diff options
author | Chocobozzz <me@florianbigard.com> | 2018-02-20 18:01:38 +0100 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2018-02-20 18:16:13 +0100 |
commit | 61b3e146e16e997ea539cd4610af10d4b681e04a (patch) | |
tree | 65937b83e8d01a6401b8cecd1fcf34de15aed1da /server/tools | |
parent | 71578f317e881f35ec905e9136f77740bbd7e7aa (diff) | |
download | PeerTube-61b3e146e16e997ea539cd4610af10d4b681e04a.tar.gz PeerTube-61b3e146e16e997ea539cd4610af10d4b681e04a.tar.zst PeerTube-61b3e146e16e997ea539cd4610af10d4b681e04a.zip |
Add ability to import videos from all supported youtube-dl sites
Diffstat (limited to 'server/tools')
-rw-r--r-- | server/tools/import-videos.ts (renamed from server/tools/import-youtube.ts) | 112 |
1 files changed, 73 insertions, 39 deletions
diff --git a/server/tools/import-youtube.ts b/server/tools/import-videos.ts index 20b4b0179..268101b41 100644 --- a/server/tools/import-youtube.ts +++ b/server/tools/import-videos.ts | |||
@@ -11,15 +11,16 @@ program | |||
11 | .option('-u, --url <url>', 'Server url') | 11 | .option('-u, --url <url>', 'Server url') |
12 | .option('-U, --username <username>', 'Username') | 12 | .option('-U, --username <username>', 'Username') |
13 | .option('-p, --password <token>', 'Password') | 13 | .option('-p, --password <token>', 'Password') |
14 | .option('-y, --youtube-url <youtubeUrl>', 'Youtube URL') | 14 | .option('-t, --target-url <targetUrl>', 'Video target URL') |
15 | .option('-l, --language <languageCode>', 'Language code') | 15 | .option('-l, --language <languageCode>', 'Language code') |
16 | .option('-v, --verbose', 'Verbose mode') | ||
16 | .parse(process.argv) | 17 | .parse(process.argv) |
17 | 18 | ||
18 | if ( | 19 | if ( |
19 | !program['url'] || | 20 | !program['url'] || |
20 | !program['username'] || | 21 | !program['username'] || |
21 | !program['password'] || | 22 | !program['password'] || |
22 | !program['youtubeUrl'] | 23 | !program['targetUrl'] |
23 | ) { | 24 | ) { |
24 | console.error('All arguments are required.') | 25 | console.error('All arguments are required.') |
25 | process.exit(-1) | 26 | process.exit(-1) |
@@ -28,6 +29,13 @@ if ( | |||
28 | run().catch(err => console.error(err)) | 29 | run().catch(err => console.error(err)) |
29 | 30 | ||
30 | let accessToken: string | 31 | let accessToken: string |
32 | let client: { id: string, secret: string } | ||
33 | |||
34 | const user = { | ||
35 | username: program['username'], | ||
36 | password: program['password'] | ||
37 | } | ||
38 | |||
31 | const processOptions = { | 39 | const processOptions = { |
32 | cwd: __dirname, | 40 | cwd: __dirname, |
33 | maxBuffer: Infinity | 41 | maxBuffer: Infinity |
@@ -35,74 +43,72 @@ const processOptions = { | |||
35 | 43 | ||
36 | async function run () { | 44 | async function run () { |
37 | const res = await getClient(program['url']) | 45 | const res = await getClient(program['url']) |
38 | const client = { | 46 | client = { |
39 | id: res.body.client_id, | 47 | id: res.body.client_id, |
40 | secret: res.body.client_secret | 48 | secret: res.body.client_secret |
41 | } | 49 | } |
42 | 50 | ||
43 | const user = { | ||
44 | username: program['username'], | ||
45 | password: program['password'] | ||
46 | } | ||
47 | |||
48 | const res2 = await login(program['url'], client, user) | 51 | const res2 = await login(program['url'], client, user) |
49 | accessToken = res2.body.access_token | 52 | accessToken = res2.body.access_token |
50 | 53 | ||
51 | const options = [ '-j', '--flat-playlist', '--playlist-reverse' ] | 54 | const options = [ '-j', '--flat-playlist', '--playlist-reverse' ] |
52 | youtubeDL.getInfo(program['youtubeUrl'], options, processOptions, async (err, info) => { | 55 | youtubeDL.getInfo(program['targetUrl'], options, processOptions, async (err, info) => { |
53 | if (err) throw err | 56 | if (err) throw err |
54 | 57 | ||
55 | // Normalize utf8 fields | 58 | let infoArray: any[] |
56 | info = info.map(i => normalizeObject(i)) | ||
57 | |||
58 | const videos = info.map(i => { | ||
59 | return { url: 'https://www.youtube.com/watch?v=' + i.id, name: i.title } | ||
60 | }) | ||
61 | 59 | ||
62 | console.log('Will download and upload %d videos.\n', videos.length) | 60 | // Normalize utf8 fields |
61 | if (Array.isArray(info) === true) { | ||
62 | infoArray = info.map(i => normalizeObject(i)) | ||
63 | } else { | ||
64 | infoArray = [ normalizeObject(info) ] | ||
65 | } | ||
66 | console.log('Will download and upload %d videos.\n', infoArray.length) | ||
63 | 67 | ||
64 | for (const video of videos) { | 68 | for (const info of infoArray) { |
65 | await processVideo(video, program['language'], client, user) | 69 | await processVideo(info, program['language']) |
66 | } | 70 | } |
67 | 71 | ||
68 | console.log('I have finished!') | 72 | // https://www.youtube.com/watch?v=2Upx39TBc1s |
73 | console.log('I\'m finished!') | ||
69 | process.exit(0) | 74 | process.exit(0) |
70 | }) | 75 | }) |
71 | } | 76 | } |
72 | 77 | ||
73 | function processVideo (video: { name: string, url: string }, languageCode: number, client: { id: string, secret: string }, user: { username: string, password: string }) { | 78 | function processVideo (info: any, languageCode: number) { |
74 | return new Promise(async res => { | 79 | return new Promise(async res => { |
75 | const result = await searchVideo(program['url'], video.name) | 80 | if (program['verbose']) console.log('Fetching object.', info) |
81 | |||
82 | const videoInfo = await fetchObject(info) | ||
83 | if (program['verbose']) console.log('Fetched object.', videoInfo) | ||
84 | |||
85 | const result = await searchVideo(program['url'], videoInfo.title) | ||
76 | 86 | ||
77 | console.log('############################################################\n') | 87 | console.log('############################################################\n') |
78 | 88 | ||
79 | if (result.body.data.find(v => v.name === video.name)) { | 89 | if (result.body.data.find(v => v.name === videoInfo.title)) { |
80 | console.log('Video "%s" already exists, don\'t reupload it.\n', video.name) | 90 | console.log('Video "%s" already exists, don\'t reupload it.\n', videoInfo.title) |
81 | return res() | 91 | return res() |
82 | } | 92 | } |
83 | 93 | ||
84 | const path = join(__dirname, new Date().getTime() + '.mp4') | 94 | const path = join(__dirname, new Date().getTime() + '.mp4') |
85 | 95 | ||
86 | console.log('Downloading video "%s"...', video.name) | 96 | console.log('Downloading video "%s"...', videoInfo.title) |
87 | 97 | ||
88 | const options = [ '-f', 'bestvideo[ext=mp4]+bestaudio[ext=m4a]', '-o', path ] | 98 | const options = [ '-f', 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best', '-o', path ] |
89 | youtubeDL.exec(video.url, options, processOptions, async (err, output) => { | 99 | youtubeDL.exec(videoInfo.url, options, processOptions, async (err, output) => { |
90 | if (err) return console.error(err) | 100 | if (err) return console.error(err) |
91 | 101 | ||
92 | console.log(output.join('\n')) | 102 | console.log(output.join('\n')) |
93 | 103 | ||
94 | youtubeDL.getInfo(video.url, undefined, processOptions, async (err, videoInfo) => { | 104 | await uploadVideoOnPeerTube(normalizeObject(videoInfo), path, languageCode) |
95 | if (err) return console.error(err) | ||
96 | |||
97 | await uploadVideoOnPeerTube(normalizeObject(videoInfo), path, client, user, languageCode) | ||
98 | 105 | ||
99 | return res() | 106 | return res() |
100 | }) | ||
101 | }) | 107 | }) |
102 | }) | 108 | }) |
103 | } | 109 | } |
104 | 110 | ||
105 | async function uploadVideoOnPeerTube (videoInfo: any, videoPath: string, client: { id: string, secret: string }, user: { username: string, password: string }, language?: number) { | 111 | async function uploadVideoOnPeerTube (videoInfo: any, videoPath: string, language?: number) { |
106 | const category = await getCategory(videoInfo.categories) | 112 | const category = await getCategory(videoInfo.categories) |
107 | const licence = getLicence(videoInfo.license) | 113 | const licence = getLicence(videoInfo.license) |
108 | let tags = [] | 114 | let tags = [] |
@@ -141,13 +147,16 @@ async function uploadVideoOnPeerTube (videoInfo: any, videoPath: string, client: | |||
141 | console.log('\nUploading on PeerTube video "%s".', videoAttributes.name) | 147 | console.log('\nUploading on PeerTube video "%s".', videoAttributes.name) |
142 | try { | 148 | try { |
143 | await uploadVideo(program['url'], accessToken, videoAttributes) | 149 | await uploadVideo(program['url'], accessToken, videoAttributes) |
144 | } | 150 | } catch (err) { |
145 | catch (err) { | 151 | if (err.message.indexOf('401')) { |
146 | if ((err.message).search("401")) { | 152 | console.log('Got 401 Unauthorized, token may have expired, renewing token and retry.') |
147 | console.log("Get 401 Unauthorized, token may have expired, renewing token and retry.") | 153 | |
148 | const res2 = await login(program['url'], client, user) | 154 | const res = await login(program['url'], client, user) |
149 | accessToken = res2.body.access_token | 155 | accessToken = res.body.access_token |
156 | |||
150 | await uploadVideo(program['url'], accessToken, videoAttributes) | 157 | await uploadVideo(program['url'], accessToken, videoAttributes) |
158 | } else { | ||
159 | throw err | ||
151 | } | 160 | } |
152 | } | 161 | } |
153 | 162 | ||
@@ -160,6 +169,8 @@ async function uploadVideoOnPeerTube (videoInfo: any, videoPath: string, client: | |||
160 | } | 169 | } |
161 | 170 | ||
162 | async function getCategory (categories: string[]) { | 171 | async function getCategory (categories: string[]) { |
172 | if (!categories) return undefined | ||
173 | |||
163 | const categoryString = categories[0] | 174 | const categoryString = categories[0] |
164 | 175 | ||
165 | if (categoryString === 'News & Politics') return 11 | 176 | if (categoryString === 'News & Politics') return 11 |
@@ -176,6 +187,8 @@ async function getCategory (categories: string[]) { | |||
176 | } | 187 | } |
177 | 188 | ||
178 | function getLicence (licence: string) { | 189 | function getLicence (licence: string) { |
190 | if (!licence) return undefined | ||
191 | |||
179 | if (licence.indexOf('Creative Commons Attribution licence') !== -1) return 1 | 192 | if (licence.indexOf('Creative Commons Attribution licence') !== -1) return 1 |
180 | 193 | ||
181 | return undefined | 194 | return undefined |
@@ -199,3 +212,24 @@ function normalizeObject (obj: any) { | |||
199 | 212 | ||
200 | return newObj | 213 | return newObj |
201 | } | 214 | } |
215 | |||
216 | function fetchObject (info: any) { | ||
217 | const url = buildUrl(info) | ||
218 | |||
219 | return new Promise<any>(async (res, rej) => { | ||
220 | youtubeDL.getInfo(url, undefined, processOptions, async (err, videoInfo) => { | ||
221 | if (err) return rej(err) | ||
222 | |||
223 | const videoInfoWithUrl = Object.assign(videoInfo, { url }) | ||
224 | return res(normalizeObject(videoInfoWithUrl)) | ||
225 | }) | ||
226 | }) | ||
227 | } | ||
228 | |||
229 | function buildUrl (info: any) { | ||
230 | const url = info.url as string | ||
231 | if (url && url.match(/^https?:\/\//)) return info.url | ||
232 | |||
233 | // It seems youtube-dl does not return the video url | ||
234 | return 'https://www.youtube.com/watch?v=' + info.id | ||
235 | } | ||