cloud-ytdl 1.0.0-rc
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1063 -0
- package/lib/agents.js +114 -0
- package/lib/cache.js +45 -0
- package/lib/format-utils.js +148 -0
- package/lib/format.js +82 -0
- package/lib/index.js +169 -0
- package/lib/info.js +209 -0
- package/lib/innertube.js +1 -0
- package/lib/load.js +38 -0
- package/lib/playlist.js +57 -0
- package/lib/post.js +88 -0
- package/lib/sig-decoder.js +1 -0
- package/lib/subtitle.js +106 -0
- package/lib/url-utils.js +58 -0
- package/lib/utils.js +294 -0
- package/lib/xmlTosrt.js +49 -0
- package/package.json +80 -0
- package/types/index.d.ts +488 -0
package/lib/load.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const fs = require('fs')
|
|
4
|
+
|
|
5
|
+
function loadCookieHeader(input) {
|
|
6
|
+
if (!input) return ''
|
|
7
|
+
|
|
8
|
+
if (typeof input === 'string' && input.includes('=')
|
|
9
|
+
&& !fs.existsSync(input)) {
|
|
10
|
+
return input.trim()
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const raw = fs.readFileSync(input, 'utf8').trim()
|
|
14
|
+
|
|
15
|
+
if (raw.startsWith('[')) {
|
|
16
|
+
const list = JSON.parse(raw)
|
|
17
|
+
|
|
18
|
+
return list
|
|
19
|
+
.filter(c =>
|
|
20
|
+
c.name &&
|
|
21
|
+
c.value &&
|
|
22
|
+
(!c.expirationDate ||
|
|
23
|
+
c.expirationDate * 1000 > Date.now())
|
|
24
|
+
)
|
|
25
|
+
.map(c => `${c.name}=${c.value}`)
|
|
26
|
+
.join('; ')
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return raw
|
|
30
|
+
.split('\n')
|
|
31
|
+
.filter(l => l && !l.startsWith('#'))
|
|
32
|
+
.map(l => l.split('\t'))
|
|
33
|
+
.filter(p => p.length >= 7 && p[6])
|
|
34
|
+
.map(p => `${p[5]}=${p[6]}`)
|
|
35
|
+
.join('; ')
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
module.exports = { loadCookieHeader }
|
package/lib/playlist.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { request } = require('undici')
|
|
4
|
+
const utils = require('./utils')
|
|
5
|
+
|
|
6
|
+
async function getPlaylistInfo(url, opts = {}) {
|
|
7
|
+
const res = await request(url, {
|
|
8
|
+
headers: {
|
|
9
|
+
'user-agent':
|
|
10
|
+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
|
|
11
|
+
...(opts.agent?.jar && {
|
|
12
|
+
cookie: opts.agent.jar.getCookieStringSync(url)
|
|
13
|
+
})
|
|
14
|
+
},
|
|
15
|
+
dispatcher: opts.agent?.dispatcher
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const html = await res.body.text()
|
|
19
|
+
|
|
20
|
+
const ytInitialData =
|
|
21
|
+
html.match(/var ytInitialData = (.*?);<\/script>/)?.[1]
|
|
22
|
+
|
|
23
|
+
if (!ytInitialData)
|
|
24
|
+
throw new Error('ytInitialData not found')
|
|
25
|
+
|
|
26
|
+
const data = JSON.parse(ytInitialData)
|
|
27
|
+
|
|
28
|
+
const sidebar =
|
|
29
|
+
data?.contents?.twoColumnWatchNextResults
|
|
30
|
+
?.playlist?.playlist
|
|
31
|
+
|
|
32
|
+
if (!sidebar)
|
|
33
|
+
throw new Error('playlist data not found')
|
|
34
|
+
|
|
35
|
+
const items =
|
|
36
|
+
sidebar.contents
|
|
37
|
+
.filter(x => x.playlistPanelVideoRenderer)
|
|
38
|
+
.map(x => {
|
|
39
|
+
const v = x.playlistPanelVideoRenderer
|
|
40
|
+
return {
|
|
41
|
+
videoId: v.videoId,
|
|
42
|
+
title: v.title?.simpleText,
|
|
43
|
+
author: v.shortBylineText?.runs?.[0]?.text,
|
|
44
|
+
lengthSeconds:
|
|
45
|
+
utils.parseTime(v.lengthText?.simpleText)
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
id: sidebar.playlistId,
|
|
51
|
+
title: sidebar.title,
|
|
52
|
+
type: sidebar.isInfinite ? 'radio' : 'playlist',
|
|
53
|
+
items
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = { getPlaylistInfo }
|
package/lib/post.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
const { request } = require('undici')
|
|
2
|
+
|
|
3
|
+
const yt = {}
|
|
4
|
+
|
|
5
|
+
yt.getPostInfo = async url => {
|
|
6
|
+
const res = await request(url, {
|
|
7
|
+
headers: {
|
|
8
|
+
'user-agent':
|
|
9
|
+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/140 Safari/537.36'
|
|
10
|
+
}
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
const html = await res.body.text()
|
|
14
|
+
|
|
15
|
+
const match = html.match(/var ytInitialData = (.*?);<\/script>/s)
|
|
16
|
+
if (!match) throw new Error('ytInitialData not found')
|
|
17
|
+
|
|
18
|
+
const data = JSON.parse(match[1])
|
|
19
|
+
|
|
20
|
+
const post =
|
|
21
|
+
data.contents.twoColumnBrowseResultsRenderer.tabs[0]
|
|
22
|
+
.tabRenderer.content.sectionListRenderer.contents[0]
|
|
23
|
+
.itemSectionRenderer.contents[0]
|
|
24
|
+
.backstagePostThreadRenderer.post.backstagePostRenderer
|
|
25
|
+
|
|
26
|
+
const author = post.authorText?.runs?.[0]?.text || ''
|
|
27
|
+
const authorUrl =
|
|
28
|
+
'https://youtube.com' +
|
|
29
|
+
(post.authorEndpoint?.commandMetadata?.webCommandMetadata?.url || '')
|
|
30
|
+
|
|
31
|
+
const content =
|
|
32
|
+
post.contentText?.runs?.map(r => r.text).join('') || ''
|
|
33
|
+
|
|
34
|
+
const published = post.publishedTimeText?.runs?.[0]?.text || ''
|
|
35
|
+
const likes = post.voteCount?.simpleText || '0'
|
|
36
|
+
|
|
37
|
+
let images = []
|
|
38
|
+
let poll = null
|
|
39
|
+
|
|
40
|
+
const a = post.backstageAttachment
|
|
41
|
+
|
|
42
|
+
if (a?.postMultiImageRenderer?.images?.length) {
|
|
43
|
+
images = a.postMultiImageRenderer.images.map(
|
|
44
|
+
i => i.backstageImageRenderer.image.thumbnails.at(-1).url
|
|
45
|
+
)
|
|
46
|
+
} else if (a?.backstageImageRenderer?.images?.length) {
|
|
47
|
+
images = a.backstageImageRenderer.images.map(
|
|
48
|
+
i => i.image.thumbnails.at(-1).url
|
|
49
|
+
)
|
|
50
|
+
} else if (a?.backstageImageRenderer?.image?.thumbnails?.length) {
|
|
51
|
+
images = [a.backstageImageRenderer.image.thumbnails.at(-1).url]
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (a?.pollRenderer) {
|
|
55
|
+
const pr = a.pollRenderer
|
|
56
|
+
|
|
57
|
+
const options = (pr.choices || []).map(c => {
|
|
58
|
+
const voteText = c.voteCount?.simpleText || '0'
|
|
59
|
+
const voteCount = Number(voteText.replace(/[^\d]/g, ''))
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
text: c.text?.runs?.map(r => r.text).join('') || '',
|
|
63
|
+
voteCount,
|
|
64
|
+
isCorrect: Boolean(c.isCorrect)
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
poll = {
|
|
69
|
+
question: pr.question?.runs?.map(r => r.text).join('') || '',
|
|
70
|
+
options,
|
|
71
|
+
totalVotes: options.reduce((a, b) => a + b.voteCount, 0),
|
|
72
|
+
correctAnswer:
|
|
73
|
+
options.find(o => o.isCorrect)?.text || null
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
author,
|
|
79
|
+
authorUrl,
|
|
80
|
+
published,
|
|
81
|
+
content,
|
|
82
|
+
images,
|
|
83
|
+
likes,
|
|
84
|
+
poll
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
module.exports = yt
|