]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - shared/utils/videos/videos.ts
remove duplicate uploadVideo.originallyPublishedAt
[github/Chocobozzz/PeerTube.git] / shared / utils / videos / videos.ts
CommitLineData
a20399c9
C
1/* tslint:disable:no-unused-expression */
2
3import { expect } from 'chai'
62689b94 4import { existsSync, readdir, readFile } from 'fs-extra'
fdbda9e3 5import * as parseTorrent from 'parse-torrent'
1d791a26 6import { extname, join } from 'path'
c5d31dba 7import * as request from 'supertest'
ac81d1a0
C
8import {
9 buildAbsoluteFixturePath,
62689b94
C
10 getMyUserInformation,
11 immutableAssign,
ac81d1a0
C
12 makeGetRequest,
13 makePutBodyRequest,
14 makeUploadRequest,
15 root,
16 ServerInfo,
17 testImage
18} from '../'
bc22d608 19
d4681c00 20import { VideoDetails, VideoPrivacy } from '../../models/videos'
bc22d608 21import { VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../server/initializers/constants'
d175a6f7 22import { dateIsValid, webtorrentAdd } from '../miscs/miscs'
0e1dc3e7
C
23
24type VideoAttributes = {
25 name?: string
26 category?: number
27 licence?: number
9d3ef9fe 28 language?: string
0e1dc3e7 29 nsfw?: boolean
47564bbe 30 commentsEnabled?: boolean
7f2cfe3a 31 downloadEnabled?: boolean
2186386c 32 waitTranscoding?: boolean
0e1dc3e7 33 description?: string
7519127b 34 originallyPublishedAt?: string
0e1dc3e7 35 tags?: string[]
5f04dd2f 36 channelId?: number
11474c3c 37 privacy?: VideoPrivacy
0e1dc3e7 38 fixture?: string
ac81d1a0
C
39 thumbnailfile?: string
40 previewfile?: string
2baea0c7
C
41 scheduleUpdate?: {
42 updateAt: string
43 privacy?: VideoPrivacy
44 }
0e1dc3e7
C
45}
46
47function getVideoCategories (url: string) {
48 const path = '/api/v1/videos/categories'
49
eec63bbc
C
50 return makeGetRequest({
51 url,
59651eee
C
52 path,
53 statusCodeExpected: 200
eec63bbc 54 })
0e1dc3e7
C
55}
56
57function getVideoLicences (url: string) {
58 const path = '/api/v1/videos/licences'
59
eec63bbc
C
60 return makeGetRequest({
61 url,
59651eee
C
62 path,
63 statusCodeExpected: 200
eec63bbc 64 })
0e1dc3e7
C
65}
66
67function getVideoLanguages (url: string) {
68 const path = '/api/v1/videos/languages'
69
eec63bbc
C
70 return makeGetRequest({
71 url,
59651eee
C
72 path,
73 statusCodeExpected: 200
eec63bbc 74 })
0e1dc3e7
C
75}
76
11474c3c
C
77function getVideoPrivacies (url: string) {
78 const path = '/api/v1/videos/privacies'
79
eec63bbc
C
80 return makeGetRequest({
81 url,
59651eee
C
82 path,
83 statusCodeExpected: 200
eec63bbc 84 })
11474c3c
C
85}
86
11474c3c 87function getVideo (url: string, id: number | string, expectedStatus = 200) {
0e1dc3e7
C
88 const path = '/api/v1/videos/' + id
89
90 return request(url)
91 .get(path)
92 .set('Accept', 'application/json')
11474c3c
C
93 .expect(expectedStatus)
94}
95
490b595a 96function viewVideo (url: string, id: number | string, expectedStatus = 204, xForwardedFor?: string) {
1f3e9fec
C
97 const path = '/api/v1/videos/' + id + '/views'
98
490b595a 99 const req = request(url)
1f3e9fec
C
100 .post(path)
101 .set('Accept', 'application/json')
490b595a
C
102
103 if (xForwardedFor) {
104 req.set('X-Forwarded-For', xForwardedFor)
105 }
106
107 return req.expect(expectedStatus)
1f3e9fec
C
108}
109
11474c3c
C
110function getVideoWithToken (url: string, token: string, id: number | string, expectedStatus = 200) {
111 const path = '/api/v1/videos/' + id
112
113 return request(url)
114 .get(path)
115 .set('Authorization', 'Bearer ' + token)
116 .set('Accept', 'application/json')
117 .expect(expectedStatus)
0e1dc3e7
C
118}
119
9567011b
C
120function getVideoDescription (url: string, descriptionPath: string) {
121 return request(url)
122 .get(descriptionPath)
123 .set('Accept', 'application/json')
124 .expect(200)
125 .expect('Content-Type', /json/)
126}
127
0e1dc3e7
C
128function getVideosList (url: string) {
129 const path = '/api/v1/videos'
130
131 return request(url)
132 .get(path)
133 .query({ sort: 'name' })
134 .set('Accept', 'application/json')
135 .expect(200)
136 .expect('Content-Type', /json/)
137}
138
d525fc39 139function getVideosListWithToken (url: string, token: string, query: { nsfw?: boolean } = {}) {
0883b324
C
140 const path = '/api/v1/videos'
141
142 return request(url)
143 .get(path)
144 .set('Authorization', 'Bearer ' + token)
d525fc39 145 .query(immutableAssign(query, { sort: 'name' }))
0883b324
C
146 .set('Accept', 'application/json')
147 .expect(200)
148 .expect('Content-Type', /json/)
149}
150
066e94c5
C
151function getLocalVideos (url: string) {
152 const path = '/api/v1/videos'
153
154 return request(url)
155 .get(path)
156 .query({ sort: 'name', filter: 'local' })
157 .set('Accept', 'application/json')
158 .expect(200)
159 .expect('Content-Type', /json/)
160}
161
11474c3c
C
162function getMyVideos (url: string, accessToken: string, start: number, count: number, sort?: string) {
163 const path = '/api/v1/users/me/videos'
164
165 const req = request(url)
166 .get(path)
167 .query({ start: start })
168 .query({ count: count })
169
170 if (sort) req.query({ sort })
171
172 return req.set('Accept', 'application/json')
173 .set('Authorization', 'Bearer ' + accessToken)
174 .expect(200)
175 .expect('Content-Type', /json/)
176}
177
d525fc39
C
178function getAccountVideos (
179 url: string,
180 accessToken: string,
181 accountName: string,
182 start: number,
183 count: number,
184 sort?: string,
185 query: { nsfw?: boolean } = {}
186) {
ad9e39fb 187 const path = '/api/v1/accounts/' + accountName + '/videos'
6b738c7a
C
188
189 return makeGetRequest({
190 url,
191 path,
d525fc39 192 query: immutableAssign(query, {
6b738c7a
C
193 start,
194 count,
195 sort
d525fc39 196 }),
6b738c7a
C
197 token: accessToken,
198 statusCodeExpected: 200
199 })
200}
201
202function getVideoChannelVideos (
203 url: string,
204 accessToken: string,
8a19bee1 205 videoChannelName: string,
6b738c7a
C
206 start: number,
207 count: number,
d525fc39
C
208 sort?: string,
209 query: { nsfw?: boolean } = {}
6b738c7a 210) {
8a19bee1 211 const path = '/api/v1/video-channels/' + videoChannelName + '/videos'
6b738c7a
C
212
213 return makeGetRequest({
214 url,
215 path,
d525fc39 216 query: immutableAssign(query, {
6b738c7a
C
217 start,
218 count,
219 sort
d525fc39 220 }),
6b738c7a
C
221 token: accessToken,
222 statusCodeExpected: 200
223 })
224}
225
0e1dc3e7
C
226function getVideosListPagination (url: string, start: number, count: number, sort?: string) {
227 const path = '/api/v1/videos'
228
229 const req = request(url)
230 .get(path)
231 .query({ start: start })
232 .query({ count: count })
233
234 if (sort) req.query({ sort })
235
236 return req.set('Accept', 'application/json')
237 .expect(200)
238 .expect('Content-Type', /json/)
239}
240
241function getVideosListSort (url: string, sort: string) {
242 const path = '/api/v1/videos'
243
244 return request(url)
245 .get(path)
246 .query({ sort: sort })
247 .set('Accept', 'application/json')
248 .expect(200)
249 .expect('Content-Type', /json/)
250}
251
d525fc39 252function getVideosWithFilters (url: string, query: { tagsAllOf: string[], categoryOneOf: number[] | number }) {
0e1dc3e7
C
253 const path = '/api/v1/videos'
254
255 return request(url)
57c36b27 256 .get(path)
d525fc39 257 .query(query)
f3aaa9a9 258 .set('Accept', 'application/json')
d525fc39 259 .expect(200)
f3aaa9a9 260 .expect('Content-Type', /json/)
0e1dc3e7
C
261}
262
d525fc39 263function removeVideo (url: string, token: string, id: number | string, expectedStatus = 204) {
0883b324 264 const path = '/api/v1/videos'
0e1dc3e7
C
265
266 return request(url)
d525fc39 267 .delete(path + '/' + id)
0e1dc3e7 268 .set('Accept', 'application/json')
d525fc39
C
269 .set('Authorization', 'Bearer ' + token)
270 .expect(expectedStatus)
0e1dc3e7
C
271}
272
25378bc8
C
273async function checkVideoFilesWereRemoved (
274 videoUUID: string,
275 serverNumber: number,
09209296
C
276 directories = [
277 'redundancy',
278 'videos',
279 'thumbnails',
280 'torrents',
281 'previews',
282 'captions',
283 join('playlists', 'hls'),
284 join('redundancy', 'hls')
285 ]
25378bc8 286) {
f05a1c30
C
287 const testDirectory = 'test' + serverNumber
288
25378bc8 289 for (const directory of directories) {
f05a1c30
C
290 const directoryPath = join(root(), testDirectory, directory)
291
292 const directoryExists = existsSync(directoryPath)
09209296 293 if (!directoryExists) continue
f05a1c30 294
62689b94 295 const files = await readdir(directoryPath)
f05a1c30
C
296 for (const file of files) {
297 expect(file).to.not.contain(videoUUID)
298 }
299 }
300}
301
e11f68a3 302async function uploadVideo (url: string, accessToken: string, videoAttributesArg: VideoAttributes, specialStatus = 200) {
e95561cd 303 const path = '/api/v1/videos/upload'
5f04dd2f
C
304 let defaultChannelId = '1'
305
306 try {
307 const res = await getMyUserInformation(url, accessToken)
308 defaultChannelId = res.body.videoChannels[0].id
309 } catch (e) { /* empty */ }
0e1dc3e7 310
ac81d1a0
C
311 // Override default attributes
312 const attributes = Object.assign({
0e1dc3e7
C
313 name: 'my super video',
314 category: 5,
315 licence: 4,
9d3ef9fe 316 language: 'zh',
5f04dd2f 317 channelId: defaultChannelId,
0e1dc3e7 318 nsfw: true,
2186386c 319 waitTranscoding: false,
0e1dc3e7 320 description: 'my super description',
2422c46b 321 support: 'my super support text',
0e1dc3e7 322 tags: [ 'tag' ],
11474c3c 323 privacy: VideoPrivacy.PUBLIC,
47564bbe 324 commentsEnabled: true,
7f2cfe3a 325 downloadEnabled: true,
0e1dc3e7 326 fixture: 'video_short.webm'
ac81d1a0 327 }, videoAttributesArg)
0e1dc3e7
C
328
329 const req = request(url)
330 .post(path)
331 .set('Accept', 'application/json')
332 .set('Authorization', 'Bearer ' + accessToken)
333 .field('name', attributes.name)
0e1dc3e7 334 .field('nsfw', JSON.stringify(attributes.nsfw))
47564bbe 335 .field('commentsEnabled', JSON.stringify(attributes.commentsEnabled))
7f2cfe3a 336 .field('downloadEnabled', JSON.stringify(attributes.downloadEnabled))
2186386c 337 .field('waitTranscoding', JSON.stringify(attributes.waitTranscoding))
11474c3c 338 .field('privacy', attributes.privacy.toString())
5f04dd2f 339 .field('channelId', attributes.channelId)
0e1dc3e7 340
a87d467a
C
341 if (attributes.description !== undefined) {
342 req.field('description', attributes.description)
343 }
8df87ce7
C
344 if (attributes.language !== undefined) {
345 req.field('language', attributes.language.toString())
346 }
a7fea183
C
347 if (attributes.category !== undefined) {
348 req.field('category', attributes.category.toString())
349 }
350 if (attributes.licence !== undefined) {
351 req.field('licence', attributes.licence.toString())
352 }
8df87ce7 353
2422c46b
C
354 for (let i = 0; i < attributes.tags.length; i++) {
355 req.field('tags[' + i + ']', attributes.tags[i])
356 }
357
ac81d1a0
C
358 if (attributes.thumbnailfile !== undefined) {
359 req.attach('thumbnailfile', buildAbsoluteFixturePath(attributes.thumbnailfile))
360 }
361 if (attributes.previewfile !== undefined) {
362 req.attach('previewfile', buildAbsoluteFixturePath(attributes.previewfile))
0e1dc3e7
C
363 }
364
2baea0c7
C
365 if (attributes.scheduleUpdate) {
366 req.field('scheduleUpdate[updateAt]', attributes.scheduleUpdate.updateAt)
367
368 if (attributes.scheduleUpdate.privacy) {
369 req.field('scheduleUpdate[privacy]', attributes.scheduleUpdate.privacy)
370 }
371 }
372
84929846
AM
373 if (attributes.originallyPublishedAt !== undefined) {
374 req.field('originallyPublishedAt', attributes.originallyPublishedAt)
375 }
376
ac81d1a0 377 return req.attach('videofile', buildAbsoluteFixturePath(attributes.fixture))
0e1dc3e7
C
378 .expect(specialStatus)
379}
380
ac81d1a0 381function updateVideo (url: string, accessToken: string, id: number | string, attributes: VideoAttributes, statusCodeExpected = 204) {
0e1dc3e7
C
382 const path = '/api/v1/videos/' + id
383 const body = {}
384
385 if (attributes.name) body['name'] = attributes.name
386 if (attributes.category) body['category'] = attributes.category
387 if (attributes.licence) body['licence'] = attributes.licence
388 if (attributes.language) body['language'] = attributes.language
47564bbe
C
389 if (attributes.nsfw !== undefined) body['nsfw'] = JSON.stringify(attributes.nsfw)
390 if (attributes.commentsEnabled !== undefined) body['commentsEnabled'] = JSON.stringify(attributes.commentsEnabled)
7f2cfe3a 391 if (attributes.downloadEnabled !== undefined) body['downloadEnabled'] = JSON.stringify(attributes.downloadEnabled)
7519127b 392 if (attributes.originallyPublishedAt !== undefined) body['originallyPublishedAt'] = attributes.originallyPublishedAt
0e1dc3e7
C
393 if (attributes.description) body['description'] = attributes.description
394 if (attributes.tags) body['tags'] = attributes.tags
11474c3c 395 if (attributes.privacy) body['privacy'] = attributes.privacy
0f320037 396 if (attributes.channelId) body['channelId'] = attributes.channelId
2baea0c7 397 if (attributes.scheduleUpdate) body['scheduleUpdate'] = attributes.scheduleUpdate
0e1dc3e7 398
ac81d1a0
C
399 // Upload request
400 if (attributes.thumbnailfile || attributes.previewfile) {
401 const attaches: any = {}
402 if (attributes.thumbnailfile) attaches.thumbnailfile = attributes.thumbnailfile
403 if (attributes.previewfile) attaches.previewfile = attributes.previewfile
404
405 return makeUploadRequest({
406 url,
407 method: 'PUT',
408 path,
409 token: accessToken,
410 fields: body,
411 attaches,
412 statusCodeExpected
413 })
414 }
415
416 return makePutBodyRequest({
417 url,
418 path,
419 fields: body,
420 token: accessToken,
421 statusCodeExpected
422 })
0e1dc3e7
C
423}
424
425function rateVideo (url: string, accessToken: string, id: number, rating: string, specialStatus = 204) {
426 const path = '/api/v1/videos/' + id + '/rate'
427
428 return request(url)
429 .put(path)
430 .set('Accept', 'application/json')
431 .set('Authorization', 'Bearer ' + accessToken)
432 .send({ rating })
433 .expect(specialStatus)
434}
435
14d3270f 436function parseTorrentVideo (server: ServerInfo, videoUUID: string, resolution: number) {
fdbda9e3 437 return new Promise<any>((res, rej) => {
14d3270f 438 const torrentName = videoUUID + '-' + resolution + '.torrent'
2a8c5d0a 439 const torrentPath = join(root(), 'test' + server.serverNumber, 'torrents', torrentName)
fdbda9e3
C
440 readFile(torrentPath, (err, data) => {
441 if (err) return rej(err)
442
443 return res(parseTorrent(data))
444 })
445 })
446}
447
a20399c9
C
448async function completeVideoCheck (
449 url: string,
450 video: any,
451 attributes: {
452 name: string
453 category: number
454 licence: number
9d3ef9fe 455 language: string
a20399c9 456 nsfw: boolean
47564bbe 457 commentsEnabled: boolean
7f2cfe3a 458 downloadEnabled: boolean
a20399c9 459 description: string
53a61317 460 publishedAt?: string
2422c46b 461 support: string
7519127b 462 originallyPublishedAt?: string,
b64c950a
C
463 account: {
464 name: string
465 host: string
466 }
f6eebcb3
C
467 isLocal: boolean
468 tags: string[]
469 privacy: number
470 likes?: number
471 dislikes?: number
472 duration: number
a20399c9 473 channel: {
f6eebcb3
C
474 displayName: string
475 name: string
b1f5b93e 476 description
a20399c9
C
477 isLocal: boolean
478 }
f6eebcb3 479 fixture: string
a20399c9
C
480 files: {
481 resolution: number
482 size: number
ac81d1a0
C
483 }[],
484 thumbnailfile?: string
485 previewfile?: string
a20399c9
C
486 }
487) {
b1f5b93e
C
488 if (!attributes.likes) attributes.likes = 0
489 if (!attributes.dislikes) attributes.dislikes = 0
490
a20399c9 491 expect(video.name).to.equal(attributes.name)
09700934 492 expect(video.category.id).to.equal(attributes.category)
9d3ef9fe 493 expect(video.category.label).to.equal(attributes.category !== null ? VIDEO_CATEGORIES[attributes.category] : 'Misc')
09700934 494 expect(video.licence.id).to.equal(attributes.licence)
9d3ef9fe 495 expect(video.licence.label).to.equal(attributes.licence !== null ? VIDEO_LICENCES[attributes.licence] : 'Unknown')
09700934 496 expect(video.language.id).to.equal(attributes.language)
9d3ef9fe 497 expect(video.language.label).to.equal(attributes.language !== null ? VIDEO_LANGUAGES[attributes.language] : 'Unknown')
2243730c
C
498 expect(video.privacy.id).to.deep.equal(attributes.privacy)
499 expect(video.privacy.label).to.deep.equal(VIDEO_PRIVACIES[attributes.privacy])
a20399c9
C
500 expect(video.nsfw).to.equal(attributes.nsfw)
501 expect(video.description).to.equal(attributes.description)
03e12d7c
C
502 expect(video.account.id).to.be.a('number')
503 expect(video.account.uuid).to.be.a('string')
b64c950a
C
504 expect(video.account.host).to.equal(attributes.account.host)
505 expect(video.account.name).to.equal(attributes.account.name)
f6eebcb3
C
506 expect(video.channel.displayName).to.equal(attributes.channel.displayName)
507 expect(video.channel.name).to.equal(attributes.channel.name)
b1f5b93e
C
508 expect(video.likes).to.equal(attributes.likes)
509 expect(video.dislikes).to.equal(attributes.dislikes)
a20399c9 510 expect(video.isLocal).to.equal(attributes.isLocal)
b1f5b93e 511 expect(video.duration).to.equal(attributes.duration)
a20399c9 512 expect(dateIsValid(video.createdAt)).to.be.true
c49db162 513 expect(dateIsValid(video.publishedAt)).to.be.true
a20399c9
C
514 expect(dateIsValid(video.updatedAt)).to.be.true
515
53a61317 516 if (attributes.publishedAt) {
53a61317
C
517 expect(video.publishedAt).to.equal(attributes.publishedAt)
518 }
519
7519127b
C
520 if (attributes.originallyPublishedAt) {
521 expect(video.originallyPublishedAt).to.equal(attributes.originallyPublishedAt)
522 } else {
523 expect(video.originallyPublishedAt).to.be.null
524 }
525
66b16caf 526 const res = await getVideo(url, video.uuid)
0f320037 527 const videoDetails: VideoDetails = res.body
a20399c9
C
528
529 expect(videoDetails.files).to.have.lengthOf(attributes.files.length)
530 expect(videoDetails.tags).to.deep.equal(attributes.tags)
19a3b914
C
531 expect(videoDetails.account.name).to.equal(attributes.account.name)
532 expect(videoDetails.account.host).to.equal(attributes.account.host)
f6eebcb3
C
533 expect(video.channel.displayName).to.equal(attributes.channel.displayName)
534 expect(video.channel.name).to.equal(attributes.channel.name)
0f320037 535 expect(videoDetails.channel.host).to.equal(attributes.account.host)
a20399c9 536 expect(videoDetails.channel.isLocal).to.equal(attributes.channel.isLocal)
0f320037
C
537 expect(dateIsValid(videoDetails.channel.createdAt.toString())).to.be.true
538 expect(dateIsValid(videoDetails.channel.updatedAt.toString())).to.be.true
539 expect(videoDetails.commentsEnabled).to.equal(attributes.commentsEnabled)
7f2cfe3a 540 expect(videoDetails.downloadEnabled).to.equal(attributes.downloadEnabled)
a20399c9
C
541
542 for (const attributeFile of attributes.files) {
5d00a3d7 543 const file = videoDetails.files.find(f => f.resolution.id === attributeFile.resolution)
a20399c9
C
544 expect(file).not.to.be.undefined
545
b1f5b93e
C
546 let extension = extname(attributes.fixture)
547 // Transcoding enabled on server 2, extension will always be .mp4
b64c950a 548 if (attributes.account.host === 'localhost:9002') extension = '.mp4'
b1f5b93e 549
a20399c9
C
550 const magnetUri = file.magnetUri
551 expect(file.magnetUri).to.have.lengthOf.above(2)
5d00a3d7
C
552 expect(file.torrentUrl).to.equal(`http://${attributes.account.host}/static/torrents/${videoDetails.uuid}-${file.resolution.id}.torrent`)
553 expect(file.fileUrl).to.equal(`http://${attributes.account.host}/static/webseed/${videoDetails.uuid}-${file.resolution.id}${extension}`)
09700934
C
554 expect(file.resolution.id).to.equal(attributeFile.resolution)
555 expect(file.resolution.label).to.equal(attributeFile.resolution + 'p')
b1f5b93e
C
556
557 const minSize = attributeFile.size - ((10 * attributeFile.size) / 100)
558 const maxSize = attributeFile.size + ((10 * attributeFile.size) / 100)
4176e227 559 expect(file.size,
7160878c 560 'File size for resolution ' + file.resolution.label + ' outside confidence interval (' + minSize + '> size <' + maxSize + ')')
4176e227 561 .to.be.above(minSize).and.below(maxSize)
a20399c9 562
ac81d1a0 563 {
7b0956ec 564 await testImage(url, attributes.thumbnailfile || attributes.fixture, videoDetails.thumbnailPath)
ac81d1a0
C
565 }
566
567 if (attributes.previewfile) {
7b0956ec 568 await testImage(url, attributes.previewfile, videoDetails.previewPath)
ac81d1a0 569 }
a20399c9
C
570
571 const torrent = await webtorrentAdd(magnetUri, true)
572 expect(torrent.files).to.be.an('array')
573 expect(torrent.files.length).to.equal(1)
574 expect(torrent.files[0].path).to.exist.and.to.not.equal('')
575 }
576}
577
0e1dc3e7
C
578// ---------------------------------------------------------------------------
579
580export {
9567011b 581 getVideoDescription,
0e1dc3e7
C
582 getVideoCategories,
583 getVideoLicences,
11474c3c 584 getVideoPrivacies,
0e1dc3e7 585 getVideoLanguages,
11474c3c 586 getMyVideos,
6b738c7a
C
587 getAccountVideos,
588 getVideoChannelVideos,
0e1dc3e7 589 getVideo,
11474c3c 590 getVideoWithToken,
0e1dc3e7
C
591 getVideosList,
592 getVideosListPagination,
593 getVideosListSort,
594 removeVideo,
0883b324 595 getVideosListWithToken,
0e1dc3e7 596 uploadVideo,
d525fc39 597 getVideosWithFilters,
0e1dc3e7 598 updateVideo,
fdbda9e3 599 rateVideo,
1f3e9fec 600 viewVideo,
a20399c9 601 parseTorrentVideo,
066e94c5 602 getLocalVideos,
f05a1c30
C
603 completeVideoCheck,
604 checkVideoFilesWereRemoved
0e1dc3e7 605}