diff options
Diffstat (limited to 'server/tools/peertube-import-videos.ts')
-rw-r--r-- | server/tools/peertube-import-videos.ts | 145 |
1 files changed, 89 insertions, 56 deletions
diff --git a/server/tools/peertube-import-videos.ts b/server/tools/peertube-import-videos.ts index 9a366dbbd..f9cd3106a 100644 --- a/server/tools/peertube-import-videos.ts +++ b/server/tools/peertube-import-videos.ts | |||
@@ -14,8 +14,10 @@ import { sha256 } from '../helpers/core-utils' | |||
14 | import { buildOriginallyPublishedAt, safeGetYoutubeDL } from '../helpers/youtube-dl' | 14 | import { buildOriginallyPublishedAt, safeGetYoutubeDL } from '../helpers/youtube-dl' |
15 | import { getNetrc, getRemoteObjectOrDie, getSettings } from './cli' | 15 | import { getNetrc, getRemoteObjectOrDie, getSettings } from './cli' |
16 | 16 | ||
17 | let accessToken: string | 17 | type UserInfo = { |
18 | let client: { id: string, secret: string } | 18 | username: string |
19 | password: string | ||
20 | } | ||
19 | 21 | ||
20 | const processOptions = { | 22 | const processOptions = { |
21 | cwd: __dirname, | 23 | cwd: __dirname, |
@@ -28,13 +30,14 @@ program | |||
28 | .option('-U, --username <username>', 'Username') | 30 | .option('-U, --username <username>', 'Username') |
29 | .option('-p, --password <token>', 'Password') | 31 | .option('-p, --password <token>', 'Password') |
30 | .option('-t, --target-url <targetUrl>', 'Video target URL') | 32 | .option('-t, --target-url <targetUrl>', 'Video target URL') |
33 | .option('-C, --channel-id <channel_id>', 'Channel ID') | ||
31 | .option('-l, --language <languageCode>', 'Language ISO 639 code (fr or en...)') | 34 | .option('-l, --language <languageCode>', 'Language ISO 639 code (fr or en...)') |
32 | .option('-v, --verbose', 'Verbose mode') | 35 | .option('-v, --verbose', 'Verbose mode') |
33 | .parse(process.argv) | 36 | .parse(process.argv) |
34 | 37 | ||
35 | Promise.all([ getSettings(), getNetrc() ]) | 38 | Promise.all([ getSettings(), getNetrc() ]) |
36 | .then(([ settings, netrc ]) => { | 39 | .then(([ settings, netrc ]) => { |
37 | const { url, username, password } = getRemoteObjectOrDie(program, settings) | 40 | const { url, username, password } = getRemoteObjectOrDie(program, settings, netrc) |
38 | 41 | ||
39 | if (!program[ 'targetUrl' ]) { | 42 | if (!program[ 'targetUrl' ]) { |
40 | console.error('--targetUrl field is required.') | 43 | console.error('--targetUrl field is required.') |
@@ -45,56 +48,20 @@ Promise.all([ getSettings(), getNetrc() ]) | |||
45 | removeEndSlashes(url) | 48 | removeEndSlashes(url) |
46 | removeEndSlashes(program[ 'targetUrl' ]) | 49 | removeEndSlashes(program[ 'targetUrl' ]) |
47 | 50 | ||
48 | const user = { | 51 | const user = { username, password } |
49 | username: username, | ||
50 | password: password | ||
51 | } | ||
52 | 52 | ||
53 | run(user, url) | 53 | run(url, user) |
54 | .catch(err => { | 54 | .catch(err => { |
55 | console.error(err) | 55 | console.error(err) |
56 | process.exit(-1) | 56 | process.exit(-1) |
57 | }) | 57 | }) |
58 | }) | 58 | }) |
59 | 59 | ||
60 | async function promptPassword () { | 60 | async function run (url: string, user: UserInfo) { |
61 | return new Promise((res, rej) => { | ||
62 | prompt.start() | ||
63 | const schema = { | ||
64 | properties: { | ||
65 | password: { | ||
66 | hidden: true, | ||
67 | required: true | ||
68 | } | ||
69 | } | ||
70 | } | ||
71 | prompt.get(schema, function (err, result) { | ||
72 | if (err) { | ||
73 | return rej(err) | ||
74 | } | ||
75 | return res(result.password) | ||
76 | }) | ||
77 | }) | ||
78 | } | ||
79 | |||
80 | async function run (user, url: string) { | ||
81 | if (!user.password) { | 61 | if (!user.password) { |
82 | user.password = await promptPassword() | 62 | user.password = await promptPassword() |
83 | } | 63 | } |
84 | 64 | ||
85 | const res = await getClient(url) | ||
86 | client = { | ||
87 | id: res.body.client_id, | ||
88 | secret: res.body.client_secret | ||
89 | } | ||
90 | |||
91 | try { | ||
92 | const res = await login(program[ 'url' ], client, user) | ||
93 | accessToken = res.body.access_token | ||
94 | } catch (err) { | ||
95 | throw new Error('Cannot authenticate. Please check your username/password.') | ||
96 | } | ||
97 | |||
98 | const youtubeDL = await safeGetYoutubeDL() | 65 | const youtubeDL = await safeGetYoutubeDL() |
99 | 66 | ||
100 | const options = [ '-j', '--flat-playlist', '--playlist-reverse' ] | 67 | const options = [ '-j', '--flat-playlist', '--playlist-reverse' ] |
@@ -115,7 +82,12 @@ async function run (user, url: string) { | |||
115 | console.log('Will download and upload %d videos.\n', infoArray.length) | 82 | console.log('Will download and upload %d videos.\n', infoArray.length) |
116 | 83 | ||
117 | for (const info of infoArray) { | 84 | for (const info of infoArray) { |
118 | await processVideo(info, program[ 'language' ], processOptions.cwd, url, user) | 85 | await processVideo({ |
86 | cwd: processOptions.cwd, | ||
87 | url, | ||
88 | user, | ||
89 | youtubeInfo: info | ||
90 | }) | ||
119 | } | 91 | } |
120 | 92 | ||
121 | console.log('Video/s for user %s imported: %s', program[ 'username' ], program[ 'targetUrl' ]) | 93 | console.log('Video/s for user %s imported: %s', program[ 'username' ], program[ 'targetUrl' ]) |
@@ -123,11 +95,18 @@ async function run (user, url: string) { | |||
123 | }) | 95 | }) |
124 | } | 96 | } |
125 | 97 | ||
126 | function processVideo (info: any, languageCode: string, cwd: string, url: string, user) { | 98 | function processVideo (parameters: { |
99 | cwd: string, | ||
100 | url: string, | ||
101 | user: { username: string, password: string }, | ||
102 | youtubeInfo: any | ||
103 | }) { | ||
104 | const { youtubeInfo, cwd, url, user } = parameters | ||
105 | |||
127 | return new Promise(async res => { | 106 | return new Promise(async res => { |
128 | if (program[ 'verbose' ]) console.log('Fetching object.', info) | 107 | if (program[ 'verbose' ]) console.log('Fetching object.', youtubeInfo) |
129 | 108 | ||
130 | const videoInfo = await fetchObject(info) | 109 | const videoInfo = await fetchObject(youtubeInfo) |
131 | if (program[ 'verbose' ]) console.log('Fetched object.', videoInfo) | 110 | if (program[ 'verbose' ]) console.log('Fetched object.', videoInfo) |
132 | 111 | ||
133 | const result = await searchVideoWithSort(url, videoInfo.title, '-match') | 112 | const result = await searchVideoWithSort(url, videoInfo.title, '-match') |
@@ -153,7 +132,13 @@ function processVideo (info: any, languageCode: string, cwd: string, url: string | |||
153 | } | 132 | } |
154 | 133 | ||
155 | console.log(output.join('\n')) | 134 | console.log(output.join('\n')) |
156 | await uploadVideoOnPeerTube(normalizeObject(videoInfo), path, cwd, url, user, languageCode) | 135 | await uploadVideoOnPeerTube({ |
136 | cwd, | ||
137 | url, | ||
138 | user, | ||
139 | videoInfo: normalizeObject(videoInfo), | ||
140 | videoPath: path | ||
141 | }) | ||
157 | return res() | 142 | return res() |
158 | }) | 143 | }) |
159 | } catch (err) { | 144 | } catch (err) { |
@@ -163,7 +148,15 @@ function processVideo (info: any, languageCode: string, cwd: string, url: string | |||
163 | }) | 148 | }) |
164 | } | 149 | } |
165 | 150 | ||
166 | async function uploadVideoOnPeerTube (videoInfo: any, videoPath: string, cwd: string, url: string, user, language?: string) { | 151 | async function uploadVideoOnPeerTube (parameters: { |
152 | videoInfo: any, | ||
153 | videoPath: string, | ||
154 | cwd: string, | ||
155 | url: string, | ||
156 | user: { username: string; password: string } | ||
157 | }) { | ||
158 | const { videoInfo, videoPath, cwd, url, user } = parameters | ||
159 | |||
167 | const category = await getCategory(videoInfo.categories, url) | 160 | const category = await getCategory(videoInfo.categories, url) |
168 | const licence = getLicence(videoInfo.license) | 161 | const licence = getLicence(videoInfo.license) |
169 | let tags = [] | 162 | let tags = [] |
@@ -194,7 +187,7 @@ async function uploadVideoOnPeerTube (videoInfo: any, videoPath: string, cwd: st | |||
194 | }), | 187 | }), |
195 | category, | 188 | category, |
196 | licence, | 189 | licence, |
197 | language, | 190 | language: program[ 'language' ], |
198 | nsfw: isNSFW(videoInfo), | 191 | nsfw: isNSFW(videoInfo), |
199 | waitTranscoding: true, | 192 | waitTranscoding: true, |
200 | commentsEnabled: true, | 193 | commentsEnabled: true, |
@@ -209,15 +202,21 @@ async function uploadVideoOnPeerTube (videoInfo: any, videoPath: string, cwd: st | |||
209 | originallyPublishedAt: originallyPublishedAt ? originallyPublishedAt.toISOString() : null | 202 | originallyPublishedAt: originallyPublishedAt ? originallyPublishedAt.toISOString() : null |
210 | } | 203 | } |
211 | 204 | ||
205 | if (program[ 'channelId' ]) { | ||
206 | Object.assign(videoAttributes, { channelId: program['channelId'] }) | ||
207 | } | ||
208 | |||
212 | console.log('\nUploading on PeerTube video "%s".', videoAttributes.name) | 209 | console.log('\nUploading on PeerTube video "%s".', videoAttributes.name) |
210 | |||
211 | let accessToken = await getAccessTokenOrDie(url, user) | ||
212 | |||
213 | try { | 213 | try { |
214 | await uploadVideo(url, accessToken, videoAttributes) | 214 | await uploadVideo(url, accessToken, videoAttributes) |
215 | } catch (err) { | 215 | } catch (err) { |
216 | if (err.message.indexOf('401') !== -1) { | 216 | if (err.message.indexOf('401') !== -1) { |
217 | console.log('Got 401 Unauthorized, token may have expired, renewing token and retry.') | 217 | console.log('Got 401 Unauthorized, token may have expired, renewing token and retry.') |
218 | 218 | ||
219 | const res = await login(url, client, user) | 219 | accessToken = await getAccessTokenOrDie(url, user) |
220 | accessToken = res.body.access_token | ||
221 | 220 | ||
222 | await uploadVideo(url, accessToken, videoAttributes) | 221 | await uploadVideo(url, accessToken, videoAttributes) |
223 | } else { | 222 | } else { |
@@ -232,6 +231,8 @@ async function uploadVideoOnPeerTube (videoInfo: any, videoPath: string, cwd: st | |||
232 | console.log('Uploaded video "%s"!\n', videoAttributes.name) | 231 | console.log('Uploaded video "%s"!\n', videoAttributes.name) |
233 | } | 232 | } |
234 | 233 | ||
234 | /* ---------------------------------------------------------- */ | ||
235 | |||
235 | async function getCategory (categories: string[], url: string) { | 236 | async function getCategory (categories: string[], url: string) { |
236 | if (!categories) return undefined | 237 | if (!categories) return undefined |
237 | 238 | ||
@@ -250,8 +251,6 @@ async function getCategory (categories: string[], url: string) { | |||
250 | return undefined | 251 | return undefined |
251 | } | 252 | } |
252 | 253 | ||
253 | /* ---------------------------------------------------------- */ | ||
254 | |||
255 | function getLicence (licence: string) { | 254 | function getLicence (licence: string) { |
256 | if (!licence) return undefined | 255 | if (!licence) return undefined |
257 | 256 | ||
@@ -305,9 +304,7 @@ function buildUrl (info: any) { | |||
305 | } | 304 | } |
306 | 305 | ||
307 | function isNSFW (info: any) { | 306 | function isNSFW (info: any) { |
308 | if (info.age_limit && info.age_limit >= 16) return true | 307 | return info.age_limit && info.age_limit >= 16 |
309 | |||
310 | return false | ||
311 | } | 308 | } |
312 | 309 | ||
313 | function removeEndSlashes (url: string) { | 310 | function removeEndSlashes (url: string) { |
@@ -315,3 +312,39 @@ function removeEndSlashes (url: string) { | |||
315 | url.slice(0, -1) | 312 | url.slice(0, -1) |
316 | } | 313 | } |
317 | } | 314 | } |
315 | |||
316 | async function promptPassword () { | ||
317 | return new Promise<string>((res, rej) => { | ||
318 | prompt.start() | ||
319 | const schema = { | ||
320 | properties: { | ||
321 | password: { | ||
322 | hidden: true, | ||
323 | required: true | ||
324 | } | ||
325 | } | ||
326 | } | ||
327 | prompt.get(schema, function (err, result) { | ||
328 | if (err) { | ||
329 | return rej(err) | ||
330 | } | ||
331 | return res(result.password) | ||
332 | }) | ||
333 | }) | ||
334 | } | ||
335 | |||
336 | async function getAccessTokenOrDie (url: string, user: UserInfo) { | ||
337 | const resClient = await getClient(url) | ||
338 | const client = { | ||
339 | id: resClient.body.client_id, | ||
340 | secret: resClient.body.client_secret | ||
341 | } | ||
342 | |||
343 | try { | ||
344 | const res = await login(url, client, user) | ||
345 | return res.body.access_token | ||
346 | } catch (err) { | ||
347 | console.error('Cannot authenticate. Please check your username/password.') | ||
348 | process.exit(-1) | ||
349 | } | ||
350 | } | ||