New hme page
This commit is contained in:
parent
c697209112
commit
add7a5439d
5 changed files with 116 additions and 951 deletions
575
index.js
575
index.js
|
@ -1,558 +1,85 @@
|
|||
const express = require("express"),
|
||||
path = require("path"),
|
||||
fs = require("fs"),
|
||||
ytdl = require("ytdl-core"),
|
||||
bodyParser = require("body-parser"),
|
||||
youtube = require("scrape-youtube"),
|
||||
ytExt = require("youtube-ext"),
|
||||
cp = require("child_process"),
|
||||
ffmpeg = require('ffmpeg-static')
|
||||
ytjs = require('youtubei.js')
|
||||
|
||||
const PORT = process.env.PORT || 8080
|
||||
|
||||
const staticPath = path.join(__dirname, 'static')
|
||||
|
||||
const cssPath = path.join(staticPath, 'mainStyle.css')
|
||||
|
||||
const resources = path.join(__dirname, 'resources')
|
||||
|
||||
const cachePath = path.join(__dirname, 'cache')
|
||||
const searchCacheDur = (process.env.SEARCH_DUR || 24) * 3600000
|
||||
|
||||
const playerPath = path.join(resources, 'player.html')
|
||||
const searchPath = path.join(resources, 'searchPage.html')
|
||||
const channelPath = path.join(resources, 'channelPage.html')
|
||||
|
||||
const cssHeader = `<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css"
|
||||
integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="/mainStyle.css">`
|
||||
|
||||
const topBar = `
|
||||
<div id="titleBar" class="row container-fluid">
|
||||
<div class="col-6">
|
||||
<h1><a href="/">SimpleTube</a></h1>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<form action="/search">
|
||||
<input type="text" placeholder="Search" name="q">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
|
||||
if (fs.existsSync(cachePath)) {
|
||||
fs.rmSync(cachePath, { recursive: true, force: true })
|
||||
}
|
||||
|
||||
fs.mkdirSync(cachePath)
|
||||
|
||||
|
||||
var videoCache = {}
|
||||
var searchCache = {}
|
||||
|
||||
var app = express()
|
||||
|
||||
app.use(bodyParser.urlencoded({ extended: false }))
|
||||
|
||||
app.use(express.static(staticPath))
|
||||
var resources = path.join(__dirname, 'resources')
|
||||
var staticpath = path.join(__dirname, 'static')
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log("Simpletube is now listening on port: " + PORT)
|
||||
console.log("SimpleTube is now listening on port " + PORT)
|
||||
})
|
||||
|
||||
function resultHTML(result) {
|
||||
app.use(express.static(staticpath))
|
||||
|
||||
console.log(result)
|
||||
var thumbCount = 0
|
||||
var thumborInstances = [
|
||||
"https://thumbor-production-0e82.up.railway.app/",
|
||||
"https://enormous-book-production.up.railway.app/",
|
||||
"https://unusual-back-production.up.railway.app/",
|
||||
"https://axiomatic-hair-production.up.railway.app/"
|
||||
]
|
||||
|
||||
function thumbnailCheck() {
|
||||
if (result.thumbnails) {
|
||||
return result.thumbnails[0].url
|
||||
} else {
|
||||
return result.thumbnail
|
||||
}
|
||||
}
|
||||
function getThumbor() {
|
||||
thumbCount += 1
|
||||
return thumborInstances[thumbCount % thumborInstances.length] + "unsafe"
|
||||
}
|
||||
|
||||
function publishCheck() {
|
||||
if (result.published) {
|
||||
return result.published.text
|
||||
} else {
|
||||
return result.uploaded
|
||||
}
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="col-xxl-4 col-sm-6 resultContainer">
|
||||
<div class="videoResult container-fluid row">
|
||||
<div class="col-lg-6 thumbparent">
|
||||
<a class="videoLink" href="/watch?v=${result.id}">
|
||||
<img class="thumbnail" src="${thumbnailCheck()}">
|
||||
<p style="display: block; text-align: left;">${result.duration.pretty || result.durationString}</p>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<a class="videoLink" href="/watch?v=${result.id}">
|
||||
<p style="font-size: 1.25rem;">${result.title || "No Title Found"}</p>
|
||||
<p class="resultDescription">${(result.description || result.views.text).substring(0, 125) || "No Description"}</p>
|
||||
<p style="display: block;">${publishCheck()}</p>
|
||||
</a>
|
||||
</div>
|
||||
<div style="display: inline-block; width: 100%;">
|
||||
<a style="color: white; margin: 10px; display: inline-block;" href="/channel?q=${result.channel.id}">
|
||||
<img src="${result.channel.thumbnail}" class="minipfp">
|
||||
${result.channel.name}
|
||||
</a>
|
||||
function searchResultToHTML(results) {
|
||||
var addedHTML = ""
|
||||
for (let index = 0; index < results.length; index++) {
|
||||
const result = results[index].content;
|
||||
if (result && result.type == "Video" && result.published.text) {
|
||||
addedHTML += `
|
||||
<div class="col-xxl-4 col-sm-6 resultContainer">
|
||||
<div class="videoResult container-fluid row">
|
||||
<div class="col-lg-6 thumbparent">
|
||||
<a class="videoLink" href="/watch?v=${result.id}">
|
||||
<img class="thumbnail" src="${getThumbor()}/${result.thumbnails[0].url}">
|
||||
<p style="display: block; text-align: left;">${result.duration.text}</p>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<a class="videoLink" href="/watch?v=${result.id}">
|
||||
<p style="font-size: 1.25rem;">${result.title.text || "No Title Found"}</p>
|
||||
<p class="resultDescription">${result.description_snippet.text}</p>
|
||||
<p style="display: block;">${result.published.text}</p>
|
||||
</a>
|
||||
</div>
|
||||
<div style="display: inline-block; width: 100%;">
|
||||
<a style="color: white; margin: 10px; display: inline-block;" href="/channel?q=${result.author.id}">
|
||||
<img src="${getThumbor()}/${result.author.thumbnails[0].url}" class="minipfp">
|
||||
${result.author.name}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
function msth(ms) {
|
||||
var x = ms / 1000
|
||||
var seconds = Math.floor(x % 60)
|
||||
x /= 60
|
||||
var minutes = Math.floor(x % 60)
|
||||
x /= 60
|
||||
var hours = Math.floor(x)
|
||||
|
||||
var fs = ""
|
||||
|
||||
if (hours >= 1) {
|
||||
fs += hours
|
||||
if (hours == 1) {
|
||||
fs += " hour "
|
||||
} else {
|
||||
fs += " hours "
|
||||
`
|
||||
}
|
||||
}
|
||||
if (minutes >= 1) {
|
||||
fs += minutes
|
||||
if (minutes == 1) {
|
||||
fs += " minute "
|
||||
} else {
|
||||
fs += " minutes "
|
||||
}
|
||||
}
|
||||
fs += ` ${seconds} seconds.`
|
||||
|
||||
return fs.trim()
|
||||
}
|
||||
|
||||
async function cacher(id, quality, ready) {
|
||||
vidpath = path.join(cachePath, `${id + quality}.mp4`)
|
||||
var debounce = true
|
||||
|
||||
var dp = 0
|
||||
var vidInfo = await ytdl.getBasicInfo(id)
|
||||
|
||||
if (quality == "sd") {
|
||||
var video = ytdl(id, { filter: 'videoandaudio', quality: "highest", format: 'mp4' })
|
||||
.on("progress", (chunk, ct, et) => {
|
||||
|
||||
if (debounce && (ct / et) > 0.1) {
|
||||
debounce = false
|
||||
videoCache[id + quality] = {
|
||||
"path": vidpath,
|
||||
"size": et,
|
||||
"downloaded": false,
|
||||
"download%": 0,
|
||||
"lastUsed": Date.now(),
|
||||
"duration": (vidInfo.videoDetails.lengthSeconds + 1) * 1000
|
||||
}
|
||||
|
||||
ready(vidpath, fs.readFileSync(vidpath))
|
||||
}
|
||||
var percent = Math.round(ct / et * 100)
|
||||
if (!debounce) {
|
||||
videoCache[id + quality]["download%"] = percent
|
||||
}
|
||||
if (!debounce && percent > dp && id in videoCache && "path" in videoCache[id]) {
|
||||
dp = percent
|
||||
}
|
||||
})
|
||||
.on("finish", () => {
|
||||
if ((id + quality) in videoCache) {
|
||||
videoCache[id + quality]["downloaded"] = true
|
||||
}
|
||||
})
|
||||
video.pipe(fs.createWriteStream(vidpath))
|
||||
} else {
|
||||
var video = ytdl(id, { filter: 'videoonly', quality: "highest" })
|
||||
var audio = ytdl(id, { filter: 'audioonly', highWaterMark: 1<<25 })
|
||||
|
||||
const ffmpegProcess = cp.spawn(ffmpeg, [
|
||||
'-i', `pipe:3`,
|
||||
'-i', `pipe:4`,
|
||||
'-map','0:v',
|
||||
'-map','1:a',
|
||||
'-c:v', 'copy',
|
||||
'-c:a', 'libmp3lame',
|
||||
'-crf','27',
|
||||
'-preset','veryfast',
|
||||
'-movflags','frag_keyframe+empty_moov',
|
||||
'-f','mp4',
|
||||
'-loglevel','error',
|
||||
'-t', vidInfo.videoDetails.lengthSeconds,
|
||||
'-'
|
||||
], {
|
||||
stdio: [
|
||||
'pipe', 'pipe', 'pipe', 'pipe', 'pipe',
|
||||
],
|
||||
});
|
||||
|
||||
var debounce = false
|
||||
|
||||
function start(et) {
|
||||
if (fs.existsSync(vidpath)) {
|
||||
videoCache[id + quality] = {
|
||||
"path": vidpath,
|
||||
"size": et * 2,
|
||||
"downloaded": true,
|
||||
"download%": 0,
|
||||
"lastUsed": Date.now(),
|
||||
"duration": (vidInfo.videoDetails.lengthSeconds + 1) * 1000
|
||||
}
|
||||
ready(vidpath, fs.readFileSync(vidpath))
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
start(et)
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
video.on("progress", (chunk, ct, et) => {
|
||||
if (!debounce) {
|
||||
debounce = true
|
||||
start(et)
|
||||
}
|
||||
|
||||
videoCache[id + quality]["download%"] = Math.round(fs.statSync(vidpath).size / (et) * 100)
|
||||
// console.log(Math.round(fs.statSync(vidpath).size / (et * 2) * 100))
|
||||
})
|
||||
|
||||
video.pipe(ffmpegProcess.stdio[3])
|
||||
audio.pipe(ffmpegProcess.stdio[4])
|
||||
|
||||
|
||||
ffmpegProcess.stdio[1].pipe(fs.createWriteStream(vidpath))
|
||||
|
||||
.on("finish", () => {
|
||||
videoCache[id + quality] = {
|
||||
"path": vidpath,
|
||||
"size": fs.statSync(vidpath).size,
|
||||
"downloaded": true,
|
||||
"download%": 100,
|
||||
"lastUsed": Date.now(),
|
||||
"duration": (vidInfo.videoDetails.lengthSeconds + 1) * 1000
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return addedHTML
|
||||
}
|
||||
|
||||
app.get("/", async (req, res) => {
|
||||
var html = fs.readFileSync('resources/mainPage.html').toString()
|
||||
res.send(html.substring(0, html.indexOf("{RESULTS}")))
|
||||
youtube.search(" ", { type: "videos" })
|
||||
.then((results) => {
|
||||
addedHTML += "<h2><br>Videos:</h2>"
|
||||
var innerTube = await ytjs.Innertube.create()
|
||||
|
||||
for (let index = 0; index < videos.length; index++) {
|
||||
const result = videos[index];
|
||||
addedHTML += resultHTML(result)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
app.get("/search", async (req, res) => {
|
||||
var search = req.query.q || "How to search on SimpleTube"
|
||||
res.setHeader("Content-Type", "text/html")
|
||||
|
||||
function searchReturn(results) {
|
||||
var videos = results.videos
|
||||
|
||||
var html = fs.readFileSync(searchPath).toString()
|
||||
|
||||
html = html.replace("{SEARCH}", search)
|
||||
|
||||
var addedHTML = ""
|
||||
|
||||
var channels = results.channels
|
||||
if (channels.length > 0) {
|
||||
|
||||
addedHTML += "<h2><br>Channels:</h2>"
|
||||
|
||||
for (let index = 0; index < channels.length; index++) {
|
||||
const channel = channels[index]
|
||||
addedHTML += `
|
||||
<div class="col-xxl-4 col-sm-6 resultContainer">
|
||||
<div class="videoResult container-fluid row">
|
||||
<div class="col-lg-5 col-md-6 thumbparent">
|
||||
<a class="videoLink" href="/channel?q=${channel.id}">
|
||||
<img class="pfp" src="${channel.thumbnail}">
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-lg-7 col-md-6">
|
||||
<a class="videoLink" href="/channel?q=${channel.id}">
|
||||
<p style="font-size: 1.25rem;">${channel.name || "No Title Found"}</p>
|
||||
<p class="resultDescription">${channel.description || "No Description"}</p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
if (videos.length > 0) {
|
||||
addedHTML += "<h2><br>Videos:</h2>"
|
||||
|
||||
for (let index = 0; index < videos.length; index++) {
|
||||
const result = videos[index];
|
||||
addedHTML += resultHTML(result)
|
||||
}
|
||||
}
|
||||
if (addedHTML == "") {
|
||||
addedHTML = "<h2>No results found!</h2>"
|
||||
}
|
||||
res.send(html.replace("{RESULTS}", addedHTML))
|
||||
}
|
||||
|
||||
var tA = Object.keys(searchCache)
|
||||
|
||||
|
||||
for (let index = 0; index < tA.length; index++) {
|
||||
itemName = tA[index]
|
||||
const item = searchCache[itemName];
|
||||
|
||||
if (item[1] < Date.now()) {
|
||||
delete searchCache[search]
|
||||
}
|
||||
}
|
||||
|
||||
if (search in searchCache) {
|
||||
searchReturn(searchCache[search][0])
|
||||
searchCache[search][1] = Date.now() + searchCacheDur
|
||||
} else {
|
||||
youtube.search(search, { type: "all" })
|
||||
.then((result) => {
|
||||
searchReturn(result)
|
||||
searchCache[search] = [result, Date.now() + searchCacheDur]
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
app.get("/channel", async (req, res) => {
|
||||
var section = req.query.s || "videos"
|
||||
var channel = req.query.q || "UChcrBJNJLZucy3TPyGyAY2g"
|
||||
|
||||
var html = fs.readFileSync(channelPath).toString()
|
||||
|
||||
var info = await ytExt.channelInfo(channel, { includeVideos: true })
|
||||
|
||||
var videos = info.videos
|
||||
var addedHTML = ""
|
||||
if (section == "videos") {
|
||||
for (let index = 0; index < videos.length; index++) {
|
||||
const result = videos[index];
|
||||
if (result.title != undefined) {
|
||||
addedHTML += resultHTML(result)
|
||||
} else {
|
||||
addedHTML = "<p>Failed to load.</p>"
|
||||
}
|
||||
|
||||
}
|
||||
} else if (section == "about") {
|
||||
addedHTML += `
|
||||
<div id="description">
|
||||
<h2>Description: <br></h2>
|
||||
<p>${info.description}</p>
|
||||
<p>Subscribers: ${info.subscribers.pretty}
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
if (addedHTML == "") {
|
||||
addedHTML = `<h2>Failed to load ${section}! Sorry... T^T<h2>`
|
||||
}
|
||||
|
||||
html = html.replace("{RESULTS}", addedHTML)
|
||||
|
||||
html = html.replace("{CHANNEL_NAME}", info.name)
|
||||
html = html.replace("{CHANNEL_DESC}", info.description)
|
||||
|
||||
for (let index = 0; index < 3; index++) {
|
||||
html = html.replace("{CHANNEL_ID}", info.id)
|
||||
|
||||
}
|
||||
|
||||
html = html.replace("{CHANNEL_PFP}", info.thumbnails[0].url)
|
||||
html = html.replace("{SUB_COUNT}", info.subscribers.pretty)
|
||||
|
||||
res.send(html)
|
||||
})
|
||||
|
||||
app.get("/video", async (req, res) => {
|
||||
var id = req.query.q || req.query.v
|
||||
var quality = req.query.quality
|
||||
var range = req.headers.range
|
||||
|
||||
res.setHeader("X-Accel-Buffering", "no")
|
||||
res.setHeader("Content-Type", "video/mp4")
|
||||
|
||||
if (ytdl.validateURL(id)) {
|
||||
id = ytdl.getVideoID(id)
|
||||
}
|
||||
|
||||
if (!ytdl.validateID(id)) {
|
||||
res.setHeader("Content-Type", "text/html")
|
||||
res.write("Not a valid video id or url!")
|
||||
res.end()
|
||||
return
|
||||
}
|
||||
|
||||
if (range) {
|
||||
function ready(vidpath) {
|
||||
if (fs.existsSync(vidpath) && fs.statSync(vidpath).size > 1) {
|
||||
const fileSize = fs.statSync(vidpath).size
|
||||
const parts = range.replace(/bytes=/, "").split("-")
|
||||
const start = parseInt(parts[0], 10)
|
||||
const end = parts[1]
|
||||
? parseInt(parts[1], 10)
|
||||
: fileSize - 1
|
||||
|
||||
if (start >= fs.statSync(vidpath).size + 1) {
|
||||
return
|
||||
}
|
||||
|
||||
const chunksize = (end - start) + 1
|
||||
|
||||
const head = {
|
||||
'Content-Range': `bytes ${start}-${end}/${fileSize}`,
|
||||
'Accept-Ranges': 'bytes',
|
||||
'Content-Length': chunksize,
|
||||
'Content-Type': 'video/mp4',
|
||||
}
|
||||
|
||||
res.writeHead(206, head)
|
||||
|
||||
if (end == -1) {
|
||||
fs.createReadStream(vidpath, { start: start }).pipe(res)
|
||||
} else {
|
||||
fs.createReadStream(vidpath, { start: start, end: end }).pipe(res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((id + quality) in videoCache) {
|
||||
id = id + quality
|
||||
if ("path" in videoCache[id]) {
|
||||
ready(videoCache[id].path)
|
||||
videoCache[id].lastUsed = Date.now()
|
||||
}
|
||||
} else {
|
||||
videoCache[id + quality] = []
|
||||
cacher(id, quality, ready)
|
||||
}
|
||||
|
||||
|
||||
|
||||
} else {
|
||||
const head = {
|
||||
'Content-Length': fileSize,
|
||||
'Content-Type': 'video/mp4',
|
||||
}
|
||||
res.writeHead(200, head)
|
||||
fs.createReadStream(path).pipe(res)
|
||||
}
|
||||
|
||||
var tA = Object.keys(videoCache)
|
||||
for (let index = 0; index < tA.length; index++) {
|
||||
const itemName = tA[index];
|
||||
const item = videoCache[itemName]
|
||||
const itemPath = item.path
|
||||
|
||||
if ("lastUsed" in item && item.lastUsed + (item.duration * 2.5) < Date.now()) {
|
||||
delete videoCache[itemName]
|
||||
if (fs.existsSync(itemPath)) {
|
||||
fs.unlinkSync(itemPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
app.get("/watch", async (req, res) => {
|
||||
var id = req.query.q || req.query.v || "ubFq-wV3Eic"
|
||||
var quality = req.query.quality || "hd"
|
||||
|
||||
res.setHeader("Content-Type", "text/html")
|
||||
|
||||
if (ytdl.validateURL(id)) {
|
||||
id = ytdl.getVideoID(id)
|
||||
}
|
||||
|
||||
if (!ytdl.validateID(id)) {
|
||||
res.write(cssHeader)
|
||||
res.write(topBar)
|
||||
res.write("<h2>Not a valid video id or url!</h2>")
|
||||
res.end()
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
var vidInfo = (await ytdl.getBasicInfo(id)).videoDetails
|
||||
} catch (error) {
|
||||
res.write(cssHeader)
|
||||
res.write(topBar)
|
||||
res.write("<h2>Failed to get video info! This likely means the video is age restricted, or deleted.</h2>", () => {res.end()})
|
||||
return
|
||||
}
|
||||
var html = fs.readFileSync(path.join(resources, 'mainPage.html')).toString()
|
||||
|
||||
res.write(html.substring(0, html.indexOf("{RESULTS}")))
|
||||
|
||||
var html = fs.readFileSync(playerPath).toString()
|
||||
var results = (await innerTube.getHomeFeed()).contents.contents
|
||||
|
||||
if (quality == "sd") {
|
||||
html = html.replace(`href="?quality=sd&q={VIDEOID}"`, `style="color: white;"`)
|
||||
} else {
|
||||
html = html.replace(`href="?quality=hd&q={VIDEOID}"`, `style="color: white;"`)
|
||||
}
|
||||
var addedHTML = searchResultToHTML(results)
|
||||
|
||||
for (let index = 0; index < 2; index++) {
|
||||
html = html.replace("{VIDEOID}", id)
|
||||
}
|
||||
|
||||
html = html.replace("{VIDEO_QUALITY}", quality)
|
||||
|
||||
html = html.replace("{DURATION}", msth(vidInfo.lengthSeconds * 1000))
|
||||
|
||||
html = html.replace("{CSS_HEADER}", cssHeader)
|
||||
|
||||
for (let index = 0; index < 2; index++) {
|
||||
html = html.replace("{VIDEO_TITLE}", vidInfo.title)
|
||||
|
||||
}
|
||||
|
||||
html = html.replace("{VIDEO_DESCRIPTION}", vidInfo.description || "No Description.")
|
||||
console.log(videoCache)
|
||||
if (!((id + quality) in videoCache && videoCache[id + quality].downloaded == true)) {
|
||||
html = html.replace("{CACHE_WARNING}", `
|
||||
<p style="color: lightgray">Please note that this video has not been fully cached, and may have trouble loading!
|
||||
<br>{DOWNLOAD_PERCENT}% cached as of page load. If content fails to load after a minute, reload the page!</p>
|
||||
`)
|
||||
if ((id + quality) in videoCache && "download%" in videoCache[id + quality]) {
|
||||
html = html.replace("{DOWNLOAD_PERCENT}", videoCache[id + quality]["download%"])
|
||||
} else {
|
||||
html = html.replace("{DOWNLOAD_PERCENT}", "0")
|
||||
}
|
||||
} else {
|
||||
html = html.replace("{CACHE_WARNING}", "<p>This video is fully cached!</p>")
|
||||
}
|
||||
|
||||
var finalThumb = vidInfo.thumbnails[vidInfo.thumbnails.length - 1].url
|
||||
html = html.replace("{VIDEO_THUMBNAIL}", finalThumb)
|
||||
|
||||
res.send(html)
|
||||
res.write(addedHTML + html.substring(html.indexOf("{RESULTS}") + 9), () => {res.end()})
|
||||
})
|
||||
|
||||
process.on('uncaughtException', (err, origin) => {
|
||||
|
|
473
package-lock.json
generated
473
package-lock.json
generated
|
@ -9,28 +9,11 @@
|
|||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"body-parser": "^1.20.2",
|
||||
"express": "^4.18.2",
|
||||
"ffmpeg-static": "^5.2.0",
|
||||
"scrape-youtube": "^2.4.0",
|
||||
"youtube-ext": "^1.1.16",
|
||||
"youtubei.js": "^9.0.2",
|
||||
"ytdl-core": "^4.11.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@derhuerst/http-basic": {
|
||||
"version": "8.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@derhuerst/http-basic/-/http-basic-8.2.4.tgz",
|
||||
"integrity": "sha512-F9rL9k9Xjf5blCz8HsJRO4diy111cayL2vkY2XE4r4t3n0yPXVYy3KD3nJ1qbrSn9743UWSXH4IwuCa/HWlGFw==",
|
||||
"dependencies": {
|
||||
"caseless": "^0.12.0",
|
||||
"concat-stream": "^2.0.0",
|
||||
"http-response-object": "^3.0.1",
|
||||
"parse-cache-control": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@fastify/busboy": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.0.0.tgz",
|
||||
|
@ -39,11 +22,6 @@
|
|||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "10.17.60",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz",
|
||||
"integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw=="
|
||||
},
|
||||
"node_modules/accepts": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||
|
@ -56,71 +34,22 @@
|
|||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/agent-base": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
|
||||
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
|
||||
"dependencies": {
|
||||
"debug": "4"
|
||||
"node_modules/acorn": {
|
||||
"version": "8.11.3",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
|
||||
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6.0.0"
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/agent-base/node_modules/debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||
"dependencies": {
|
||||
"ms": "2.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/agent-base/node_modules/ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node_modules/array-flatten": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
|
||||
},
|
||||
"node_modules/body-parser": {
|
||||
"version": "1.20.2",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
|
||||
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"content-type": "~1.0.5",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"destroy": "1.2.0",
|
||||
"http-errors": "2.0.0",
|
||||
"iconv-lite": "0.4.24",
|
||||
"on-finished": "2.4.1",
|
||||
"qs": "6.11.0",
|
||||
"raw-body": "2.5.2",
|
||||
"type-is": "~1.6.18",
|
||||
"unpipe": "1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8",
|
||||
"npm": "1.2.8000 || >= 1.4.16"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer-from": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
|
||||
},
|
||||
"node_modules/bytes": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
|
@ -142,25 +71,6 @@
|
|||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/caseless": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
|
||||
"integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw=="
|
||||
},
|
||||
"node_modules/concat-stream": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz",
|
||||
"integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==",
|
||||
"engines": [
|
||||
"node >= 6.0"
|
||||
],
|
||||
"dependencies": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"inherits": "^2.0.3",
|
||||
"readable-stream": "^3.0.2",
|
||||
"typedarray": "^0.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/content-disposition": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||
|
@ -244,14 +154,6 @@
|
|||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/env-paths": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
|
||||
"integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/escape-html": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||
|
@ -343,21 +245,6 @@
|
|||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/ffmpeg-static": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ffmpeg-static/-/ffmpeg-static-5.2.0.tgz",
|
||||
"integrity": "sha512-WrM7kLW+do9HLr+H6tk7LzQ7kPqbAgLjdzNE32+u3Ff11gXt9Kkkd2nusGFrlWMIe+XaA97t+I8JS7sZIrvRgA==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@derhuerst/http-basic": "^8.2.0",
|
||||
"env-paths": "^2.2.0",
|
||||
"https-proxy-agent": "^5.0.0",
|
||||
"progress": "^2.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/finalhandler": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
|
||||
|
@ -483,47 +370,6 @@
|
|||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/http-response-object": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz",
|
||||
"integrity": "sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==",
|
||||
"dependencies": {
|
||||
"@types/node": "^10.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/https-proxy-agent": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
|
||||
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
|
||||
"dependencies": {
|
||||
"agent-base": "6",
|
||||
"debug": "4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/https-proxy-agent/node_modules/debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||
"dependencies": {
|
||||
"ms": "2.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/https-proxy-agent/node_modules/ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
|
@ -548,6 +394,17 @@
|
|||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/jintr": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jintr/-/jintr-1.1.0.tgz",
|
||||
"integrity": "sha512-Tu9wk3BpN2v+kb8yT6YBtue+/nbjeLFv4vvVC4PJ7oCidHKbifWhvORrAbQfxVIQZG+67am/mDagpiGSVtvrZg==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/LuanRT"
|
||||
],
|
||||
"dependencies": {
|
||||
"acorn": "^8.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/m3u8stream": {
|
||||
"version": "0.8.6",
|
||||
"resolved": "https://registry.npmjs.org/m3u8stream/-/m3u8stream-0.8.6.tgz",
|
||||
|
@ -651,11 +508,6 @@
|
|||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/parse-cache-control": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz",
|
||||
"integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg=="
|
||||
},
|
||||
"node_modules/parseurl": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||
|
@ -669,14 +521,6 @@
|
|||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
||||
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
|
||||
},
|
||||
"node_modules/progress": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
|
||||
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-addr": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||
|
@ -711,33 +555,6 @@
|
|||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/raw-body": {
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
|
||||
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"http-errors": "2.0.0",
|
||||
"iconv-lite": "0.4.24",
|
||||
"unpipe": "1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/readable-stream": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
|
@ -767,11 +584,6 @@
|
|||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz",
|
||||
"integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA=="
|
||||
},
|
||||
"node_modules/scrape-youtube": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/scrape-youtube/-/scrape-youtube-2.4.0.tgz",
|
||||
"integrity": "sha512-fUmtg2Fa8xKSGW3S7BvQwaHGxeFUeTtIsHU/AEQcBYQfCcJfDIVJeX1jtBuPOYqy3VaBVDqwdCpSYfMIHjGDEQ=="
|
||||
},
|
||||
"node_modules/send": {
|
||||
"version": "0.18.0",
|
||||
"resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
|
||||
|
@ -854,14 +666,6 @@
|
|||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/toidentifier": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
||||
|
@ -870,6 +674,11 @@
|
|||
"node": ">=0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
|
||||
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
|
||||
},
|
||||
"node_modules/type-is": {
|
||||
"version": "1.6.18",
|
||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
||||
|
@ -882,11 +691,6 @@
|
|||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/typedarray": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "5.27.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-5.27.0.tgz",
|
||||
|
@ -906,11 +710,6 @@
|
|||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
||||
},
|
||||
"node_modules/utils-merge": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||
|
@ -927,12 +726,17 @@
|
|||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/youtube-ext": {
|
||||
"version": "1.1.16",
|
||||
"resolved": "https://registry.npmjs.org/youtube-ext/-/youtube-ext-1.1.16.tgz",
|
||||
"integrity": "sha512-vyzHSwxlCAwqWUxZKJ/5g139BgnbmZFTy9I0nxDwqlbAh74dB1LjayCoB5BgLaaIkSMruEQwlf5bF+EeR235qA==",
|
||||
"node_modules/youtubei.js": {
|
||||
"version": "9.0.2",
|
||||
"resolved": "https://registry.npmjs.org/youtubei.js/-/youtubei.js-9.0.2.tgz",
|
||||
"integrity": "sha512-D7GoJmupYaJxTNQyHRWYw8MUdQTxRaa3c7nzM9etWQjaexepFGVlVtwl3CybLx7GopBNtBvr7RxSUUIUyNnYIg==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/LuanRT"
|
||||
],
|
||||
"dependencies": {
|
||||
"undici": "^5.26.3"
|
||||
"jintr": "^1.1.0",
|
||||
"tslib": "^2.5.0",
|
||||
"undici": "^5.19.1"
|
||||
}
|
||||
},
|
||||
"node_modules/ytdl-core": {
|
||||
|
@ -950,27 +754,11 @@
|
|||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@derhuerst/http-basic": {
|
||||
"version": "8.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@derhuerst/http-basic/-/http-basic-8.2.4.tgz",
|
||||
"integrity": "sha512-F9rL9k9Xjf5blCz8HsJRO4diy111cayL2vkY2XE4r4t3n0yPXVYy3KD3nJ1qbrSn9743UWSXH4IwuCa/HWlGFw==",
|
||||
"requires": {
|
||||
"caseless": "^0.12.0",
|
||||
"concat-stream": "^2.0.0",
|
||||
"http-response-object": "^3.0.1",
|
||||
"parse-cache-control": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"@fastify/busboy": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.0.0.tgz",
|
||||
"integrity": "sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ=="
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "10.17.60",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz",
|
||||
"integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw=="
|
||||
},
|
||||
"accepts": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||
|
@ -980,58 +768,16 @@
|
|||
"negotiator": "0.6.3"
|
||||
}
|
||||
},
|
||||
"agent-base": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
|
||||
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
|
||||
"requires": {
|
||||
"debug": "4"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||
"requires": {
|
||||
"ms": "2.1.2"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
}
|
||||
}
|
||||
"acorn": {
|
||||
"version": "8.11.3",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
|
||||
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg=="
|
||||
},
|
||||
"array-flatten": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
|
||||
},
|
||||
"body-parser": {
|
||||
"version": "1.20.2",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
|
||||
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
|
||||
"requires": {
|
||||
"bytes": "3.1.2",
|
||||
"content-type": "~1.0.5",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"destroy": "1.2.0",
|
||||
"http-errors": "2.0.0",
|
||||
"iconv-lite": "0.4.24",
|
||||
"on-finished": "2.4.1",
|
||||
"qs": "6.11.0",
|
||||
"raw-body": "2.5.2",
|
||||
"type-is": "~1.6.18",
|
||||
"unpipe": "1.0.0"
|
||||
}
|
||||
},
|
||||
"buffer-from": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
|
||||
},
|
||||
"bytes": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
|
@ -1047,22 +793,6 @@
|
|||
"set-function-length": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"caseless": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
|
||||
"integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw=="
|
||||
},
|
||||
"concat-stream": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz",
|
||||
"integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==",
|
||||
"requires": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"inherits": "^2.0.3",
|
||||
"readable-stream": "^3.0.2",
|
||||
"typedarray": "^0.0.6"
|
||||
}
|
||||
},
|
||||
"content-disposition": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||
|
@ -1124,11 +854,6 @@
|
|||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="
|
||||
},
|
||||
"env-paths": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
|
||||
"integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="
|
||||
},
|
||||
"escape-html": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||
|
@ -1209,17 +934,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"ffmpeg-static": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ffmpeg-static/-/ffmpeg-static-5.2.0.tgz",
|
||||
"integrity": "sha512-WrM7kLW+do9HLr+H6tk7LzQ7kPqbAgLjdzNE32+u3Ff11gXt9Kkkd2nusGFrlWMIe+XaA97t+I8JS7sZIrvRgA==",
|
||||
"requires": {
|
||||
"@derhuerst/http-basic": "^8.2.0",
|
||||
"env-paths": "^2.2.0",
|
||||
"https-proxy-agent": "^5.0.0",
|
||||
"progress": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"finalhandler": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
|
||||
|
@ -1306,38 +1020,6 @@
|
|||
"toidentifier": "1.0.1"
|
||||
}
|
||||
},
|
||||
"http-response-object": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz",
|
||||
"integrity": "sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==",
|
||||
"requires": {
|
||||
"@types/node": "^10.0.3"
|
||||
}
|
||||
},
|
||||
"https-proxy-agent": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
|
||||
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
|
||||
"requires": {
|
||||
"agent-base": "6",
|
||||
"debug": "4"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||
"requires": {
|
||||
"ms": "2.1.2"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
|
@ -1356,6 +1038,14 @@
|
|||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
|
||||
},
|
||||
"jintr": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jintr/-/jintr-1.1.0.tgz",
|
||||
"integrity": "sha512-Tu9wk3BpN2v+kb8yT6YBtue+/nbjeLFv4vvVC4PJ7oCidHKbifWhvORrAbQfxVIQZG+67am/mDagpiGSVtvrZg==",
|
||||
"requires": {
|
||||
"acorn": "^8.8.0"
|
||||
}
|
||||
},
|
||||
"m3u8stream": {
|
||||
"version": "0.8.6",
|
||||
"resolved": "https://registry.npmjs.org/m3u8stream/-/m3u8stream-0.8.6.tgz",
|
||||
|
@ -1426,11 +1116,6 @@
|
|||
"ee-first": "1.1.1"
|
||||
}
|
||||
},
|
||||
"parse-cache-control": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz",
|
||||
"integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg=="
|
||||
},
|
||||
"parseurl": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||
|
@ -1441,11 +1126,6 @@
|
|||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
||||
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
|
||||
},
|
||||
"progress": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
|
||||
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="
|
||||
},
|
||||
"proxy-addr": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||
|
@ -1468,27 +1148,6 @@
|
|||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
|
||||
},
|
||||
"raw-body": {
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
|
||||
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
|
||||
"requires": {
|
||||
"bytes": "3.1.2",
|
||||
"http-errors": "2.0.0",
|
||||
"iconv-lite": "0.4.24",
|
||||
"unpipe": "1.0.0"
|
||||
}
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
|
@ -1504,11 +1163,6 @@
|
|||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz",
|
||||
"integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA=="
|
||||
},
|
||||
"scrape-youtube": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/scrape-youtube/-/scrape-youtube-2.4.0.tgz",
|
||||
"integrity": "sha512-fUmtg2Fa8xKSGW3S7BvQwaHGxeFUeTtIsHU/AEQcBYQfCcJfDIVJeX1jtBuPOYqy3VaBVDqwdCpSYfMIHjGDEQ=="
|
||||
},
|
||||
"send": {
|
||||
"version": "0.18.0",
|
||||
"resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
|
||||
|
@ -1578,19 +1232,16 @@
|
|||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||
"requires": {
|
||||
"safe-buffer": "~5.2.0"
|
||||
}
|
||||
},
|
||||
"toidentifier": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
||||
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="
|
||||
},
|
||||
"tslib": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
|
||||
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
|
||||
},
|
||||
"type-is": {
|
||||
"version": "1.6.18",
|
||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
||||
|
@ -1600,11 +1251,6 @@
|
|||
"mime-types": "~2.1.24"
|
||||
}
|
||||
},
|
||||
"typedarray": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
|
||||
},
|
||||
"undici": {
|
||||
"version": "5.27.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-5.27.0.tgz",
|
||||
|
@ -1618,11 +1264,6 @@
|
|||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="
|
||||
},
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
||||
},
|
||||
"utils-merge": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||
|
@ -1633,12 +1274,14 @@
|
|||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="
|
||||
},
|
||||
"youtube-ext": {
|
||||
"version": "1.1.16",
|
||||
"resolved": "https://registry.npmjs.org/youtube-ext/-/youtube-ext-1.1.16.tgz",
|
||||
"integrity": "sha512-vyzHSwxlCAwqWUxZKJ/5g139BgnbmZFTy9I0nxDwqlbAh74dB1LjayCoB5BgLaaIkSMruEQwlf5bF+EeR235qA==",
|
||||
"youtubei.js": {
|
||||
"version": "9.0.2",
|
||||
"resolved": "https://registry.npmjs.org/youtubei.js/-/youtubei.js-9.0.2.tgz",
|
||||
"integrity": "sha512-D7GoJmupYaJxTNQyHRWYw8MUdQTxRaa3c7nzM9etWQjaexepFGVlVtwl3CybLx7GopBNtBvr7RxSUUIUyNnYIg==",
|
||||
"requires": {
|
||||
"undici": "^5.26.3"
|
||||
"jintr": "^1.1.0",
|
||||
"tslib": "^2.5.0",
|
||||
"undici": "^5.19.1"
|
||||
}
|
||||
},
|
||||
"ytdl-core": {
|
||||
|
|
|
@ -17,11 +17,8 @@
|
|||
},
|
||||
"homepage": "https://github.com/Violets-puragtory/SimpleTube#readme",
|
||||
"dependencies": {
|
||||
"body-parser": "^1.20.2",
|
||||
"express": "^4.18.2",
|
||||
"ffmpeg-static": "^5.2.0",
|
||||
"scrape-youtube": "^2.4.0",
|
||||
"youtube-ext": "^1.1.16",
|
||||
"youtubei.js": "^9.0.2",
|
||||
"ytdl-core": "^4.11.5"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,16 +27,14 @@
|
|||
<h1>Welcome to SimpleTube</h1>
|
||||
<p>SimpleTube is a Youtube client that aims to be free, fast, and javascript free. (and has no ads!) </p>
|
||||
|
||||
<p>
|
||||
<a href="https://codeberg.org/Bingus_Violet/SimpleTube">Codeberg</a>
|
||||
</p>
|
||||
|
||||
<div class="row container-fluid">
|
||||
{RESULTS}
|
||||
</div>
|
||||
|
||||
<h2>More about the project: <br></h2>
|
||||
<p>
|
||||
<a href="https://codeberg.org/Bingus_Violet/SimpleTube">Find this project on codeberg!</a>
|
||||
<a href="https://violets-purgatory.dev">More about me :D</a>
|
||||
</p>
|
||||
|
||||
</main>
|
||||
</body>
|
||||
|
||||
|
|
|
@ -31,8 +31,8 @@
|
|||
</div>
|
||||
<hr>
|
||||
<p>Duration: {DURATION}</p>
|
||||
<a href="?quality=sd&q={VIDEOID}">SD</a>
|
||||
<a href="?quality=hd&q={VIDEOID}">HD</a>
|
||||
<a href="?quality=720&q={VIDEOID}">720p</a>
|
||||
<a href="?quality=1080&q={VIDEOID}">1080p</a>
|
||||
<main>
|
||||
<h2 id="title">{VIDEO_TITLE}</h2>
|
||||
<div id="description">
|
||||
|
|
Loading…
Reference in a new issue