aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/models/video
diff options
context:
space:
mode:
authorChocobozzz <florian.bigard@gmail.com>2017-07-05 13:26:25 +0200
committerChocobozzz <florian.bigard@gmail.com>2017-07-05 14:14:16 +0200
commit6fcd19ba737f1f5614a56c6925adb882dea43b8d (patch)
tree3365a96d82bc7f00ae504a568725c8e914150cf8 /server/models/video
parent5fe7e898316e18369c3e1aba307b55077adc7bfb (diff)
downloadPeerTube-6fcd19ba737f1f5614a56c6925adb882dea43b8d.tar.gz
PeerTube-6fcd19ba737f1f5614a56c6925adb882dea43b8d.tar.zst
PeerTube-6fcd19ba737f1f5614a56c6925adb882dea43b8d.zip
Move to promises
Closes https://github.com/Chocobozzz/PeerTube/issues/74
Diffstat (limited to 'server/models/video')
-rw-r--r--server/models/video/author-interface.ts9
-rw-r--r--server/models/video/author.ts23
-rw-r--r--server/models/video/tag-interface.ts4
-rw-r--r--server/models/video/tag.ts24
-rw-r--r--server/models/video/video-abuse-interface.ts5
-rw-r--r--server/models/video/video-abuse.ts11
-rw-r--r--server/models/video/video-blacklist-interface.ts24
-rw-r--r--server/models/video/video-blacklist.ts28
-rw-r--r--server/models/video/video-interface.ts70
-rw-r--r--server/models/video/video-tag.ts6
-rw-r--r--server/models/video/video.ts369
11 files changed, 243 insertions, 330 deletions
diff --git a/server/models/video/author-interface.ts b/server/models/video/author-interface.ts
index c1b30848c..dbcb85b17 100644
--- a/server/models/video/author-interface.ts
+++ b/server/models/video/author-interface.ts
@@ -1,10 +1,15 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import * as Promise from 'bluebird'
2 3
3import { PodInstance } from '../pod' 4import { PodInstance } from '../pod'
4 5
5export namespace AuthorMethods { 6export namespace AuthorMethods {
6 export type FindOrCreateAuthorCallback = (err: Error, authorInstance?: AuthorInstance) => void 7 export type FindOrCreateAuthor = (
7 export type FindOrCreateAuthor = (name: string, podId: number, userId: number, transaction: Sequelize.Transaction, callback: FindOrCreateAuthorCallback) => void 8 name: string,
9 podId: number,
10 userId: number,
11 transaction: Sequelize.Transaction
12 ) => Promise<AuthorInstance>
8} 13}
9 14
10export interface AuthorClass { 15export interface AuthorClass {
diff --git a/server/models/video/author.ts b/server/models/video/author.ts
index 4a115e328..3222c4834 100644
--- a/server/models/video/author.ts
+++ b/server/models/video/author.ts
@@ -4,7 +4,6 @@ import { isUserUsernameValid } from '../../helpers'
4 4
5import { addMethodsToModel } from '../utils' 5import { addMethodsToModel } from '../utils'
6import { 6import {
7 AuthorClass,
8 AuthorInstance, 7 AuthorInstance,
9 AuthorAttributes, 8 AuthorAttributes,
10 9
@@ -74,30 +73,18 @@ function associate (models) {
74 }) 73 })
75} 74}
76 75
77findOrCreateAuthor = function ( 76findOrCreateAuthor = function (name: string, podId: number, userId: number, transaction: Sequelize.Transaction) {
78 name: string,
79 podId: number,
80 userId: number,
81 transaction: Sequelize.Transaction,
82 callback: AuthorMethods.FindOrCreateAuthorCallback
83) {
84 const author = { 77 const author = {
85 name, 78 name,
86 podId, 79 podId,
87 userId 80 userId
88 } 81 }
89 82
90 const query: any = { 83 const query: Sequelize.FindOrInitializeOptions<AuthorAttributes> = {
91 where: author, 84 where: author,
92 defaults: author 85 defaults: author,
86 transaction
93 } 87 }
94 88
95 if (transaction !== null) query.transaction = transaction 89 return Author.findOrCreate(query).then(([ authorInstance ]) => authorInstance)
96
97 Author.findOrCreate(query).asCallback(function (err, result) {
98 if (err) return callback(err)
99
100 // [ instance, wasCreated ]
101 return callback(null, result[0])
102 })
103} 90}
diff --git a/server/models/video/tag-interface.ts b/server/models/video/tag-interface.ts
index e045e7ca5..08e5c3246 100644
--- a/server/models/video/tag-interface.ts
+++ b/server/models/video/tag-interface.ts
@@ -1,8 +1,8 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import * as Promise from 'bluebird'
2 3
3export namespace TagMethods { 4export namespace TagMethods {
4 export type FindOrCreateTagsCallback = (err: Error, tagInstances: TagInstance[]) => void 5 export type FindOrCreateTags = (tags: string[], transaction: Sequelize.Transaction) => Promise<TagInstance[]>
5 export type FindOrCreateTags = (tags: string[], transaction: Sequelize.Transaction, callback: FindOrCreateTagsCallback) => void
6} 6}
7 7
8export interface TagClass { 8export interface TagClass {
diff --git a/server/models/video/tag.ts b/server/models/video/tag.ts
index 3c657d751..d0d8353d7 100644
--- a/server/models/video/tag.ts
+++ b/server/models/video/tag.ts
@@ -1,9 +1,8 @@
1import { each } from 'async'
2import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import * as Promise from 'bluebird'
3 3
4import { addMethodsToModel } from '../utils' 4import { addMethodsToModel } from '../utils'
5import { 5import {
6 TagClass,
7 TagInstance, 6 TagInstance,
8 TagAttributes, 7 TagAttributes,
9 8
@@ -52,10 +51,9 @@ function associate (models) {
52 }) 51 })
53} 52}
54 53
55findOrCreateTags = function (tags: string[], transaction: Sequelize.Transaction, callback: TagMethods.FindOrCreateTagsCallback) { 54findOrCreateTags = function (tags: string[], transaction: Sequelize.Transaction) {
56 const tagInstances = [] 55 const tasks: Promise<TagInstance>[] = []
57 56 tags.forEach(tag => {
58 each<string, Error>(tags, function (tag, callbackEach) {
59 const query: any = { 57 const query: any = {
60 where: { 58 where: {
61 name: tag 59 name: tag
@@ -67,15 +65,9 @@ findOrCreateTags = function (tags: string[], transaction: Sequelize.Transaction,
67 65
68 if (transaction) query.transaction = transaction 66 if (transaction) query.transaction = transaction
69 67
70 Tag.findOrCreate(query).asCallback(function (err, res) { 68 const promise = Tag.findOrCreate(query).then(([ tagInstance ]) => tagInstance)
71 if (err) return callbackEach(err) 69 tasks.push(promise)
72
73 // res = [ tag, isCreated ]
74 const tag = res[0]
75 tagInstances.push(tag)
76 return callbackEach()
77 })
78 }, function (err) {
79 return callback(err, tagInstances)
80 }) 70 })
71
72 return Promise.all(tasks)
81} 73}
diff --git a/server/models/video/video-abuse-interface.ts b/server/models/video/video-abuse-interface.ts
index c85d09091..75647fe0e 100644
--- a/server/models/video/video-abuse-interface.ts
+++ b/server/models/video/video-abuse-interface.ts
@@ -1,6 +1,8 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import * as Promise from 'bluebird'
2 3
3import { PodInstance } from '../pod' 4import { PodInstance } from '../pod'
5import { ResultList } from '../../../shared'
4 6
5// Don't use barrel, import just what we need 7// Don't use barrel, import just what we need
6import { VideoAbuse as FormatedVideoAbuse } from '../../../shared/models/video-abuse.model' 8import { VideoAbuse as FormatedVideoAbuse } from '../../../shared/models/video-abuse.model'
@@ -8,8 +10,7 @@ import { VideoAbuse as FormatedVideoAbuse } from '../../../shared/models/video-a
8export namespace VideoAbuseMethods { 10export namespace VideoAbuseMethods {
9 export type ToFormatedJSON = (this: VideoAbuseInstance) => FormatedVideoAbuse 11 export type ToFormatedJSON = (this: VideoAbuseInstance) => FormatedVideoAbuse
10 12
11 export type ListForApiCallback = (err: Error, videoAbuseInstances?: VideoAbuseInstance[], total?: number) => void 13 export type ListForApi = (start: number, count: number, sort: string) => Promise< ResultList<VideoAbuseInstance> >
12 export type ListForApi = (start: number, count: number, sort: string, callback: ListForApiCallback) => void
13} 14}
14 15
15export interface VideoAbuseClass { 16export interface VideoAbuseClass {
diff --git a/server/models/video/video-abuse.ts b/server/models/video/video-abuse.ts
index bc5f01e21..ab1a3ea7d 100644
--- a/server/models/video/video-abuse.ts
+++ b/server/models/video/video-abuse.ts
@@ -5,7 +5,6 @@ import { isVideoAbuseReporterUsernameValid, isVideoAbuseReasonValid } from '../.
5 5
6import { addMethodsToModel, getSort } from '../utils' 6import { addMethodsToModel, getSort } from '../utils'
7import { 7import {
8 VideoAbuseClass,
9 VideoAbuseInstance, 8 VideoAbuseInstance,
10 VideoAbuseAttributes, 9 VideoAbuseAttributes,
11 10
@@ -109,7 +108,7 @@ function associate (models) {
109 }) 108 })
110} 109}
111 110
112listForApi = function (start: number, count: number, sort: string, callback: VideoAbuseMethods.ListForApiCallback) { 111listForApi = function (start: number, count: number, sort: string) {
113 const query = { 112 const query = {
114 offset: start, 113 offset: start,
115 limit: count, 114 limit: count,
@@ -122,11 +121,7 @@ listForApi = function (start: number, count: number, sort: string, callback: Vid
122 ] 121 ]
123 } 122 }
124 123
125 return VideoAbuse.findAndCountAll(query).asCallback(function (err, result) { 124 return VideoAbuse.findAndCountAll(query).then(({ rows, count }) => {
126 if (err) return callback(err) 125 return { total: count, data: rows }
127
128 return callback(null, result.rows, result.count)
129 }) 126 })
130} 127}
131
132
diff --git a/server/models/video/video-blacklist-interface.ts b/server/models/video/video-blacklist-interface.ts
index d4d6528d1..5ca423801 100644
--- a/server/models/video/video-blacklist-interface.ts
+++ b/server/models/video/video-blacklist-interface.ts
@@ -1,4 +1,7 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import * as Promise from 'bluebird'
3
4import { ResultList } from '../../../shared'
2 5
3// Don't use barrel, import just what we need 6// Don't use barrel, import just what we need
4import { BlacklistedVideo as FormatedBlacklistedVideo } from '../../../shared/models/video-blacklist.model' 7import { BlacklistedVideo as FormatedBlacklistedVideo } from '../../../shared/models/video-blacklist.model'
@@ -6,20 +9,15 @@ import { BlacklistedVideo as FormatedBlacklistedVideo } from '../../../shared/mo
6export namespace BlacklistedVideoMethods { 9export namespace BlacklistedVideoMethods {
7 export type ToFormatedJSON = (this: BlacklistedVideoInstance) => FormatedBlacklistedVideo 10 export type ToFormatedJSON = (this: BlacklistedVideoInstance) => FormatedBlacklistedVideo
8 11
9 export type CountTotalCallback = (err: Error, total: number) => void 12 export type CountTotal = () => Promise<number>
10 export type CountTotal = (callback: CountTotalCallback) => void
11 13
12 export type ListCallback = (err: Error, backlistedVideoInstances: BlacklistedVideoInstance[]) => void 14 export type List = () => Promise<BlacklistedVideoInstance[]>
13 export type List = (callback: ListCallback) => void
14 15
15 export type ListForApiCallback = (err: Error, blacklistedVIdeoInstances?: BlacklistedVideoInstance[], total?: number) => void 16 export type ListForApi = (start: number, count: number, sort: string) => Promise< ResultList<BlacklistedVideoInstance> >
16 export type ListForApi = (start: number, count: number, sort: string, callback: ListForApiCallback) => void
17 17
18 export type LoadByIdCallback = (err: Error, blacklistedVideoInstance: BlacklistedVideoInstance) => void 18 export type LoadById = (id: number) => Promise<BlacklistedVideoInstance>
19 export type LoadById = (id: number, callback: LoadByIdCallback) => void
20 19
21 export type LoadByVideoIdCallback = (err: Error, blacklistedVideoInstance: BlacklistedVideoInstance) => void 20 export type LoadByVideoId = (id: string) => Promise<BlacklistedVideoInstance>
22 export type LoadByVideoId = (id: string, callback: LoadByVideoIdCallback) => void
23} 21}
24 22
25export interface BlacklistedVideoClass { 23export interface BlacklistedVideoClass {
@@ -35,7 +33,8 @@ export interface BlacklistedVideoAttributes {
35 videoId: string 33 videoId: string
36} 34}
37 35
38export interface BlacklistedVideoInstance extends BlacklistedVideoClass, BlacklistedVideoAttributes, Sequelize.Instance<BlacklistedVideoAttributes> { 36export interface BlacklistedVideoInstance
37 extends BlacklistedVideoClass, BlacklistedVideoAttributes, Sequelize.Instance<BlacklistedVideoAttributes> {
39 id: number 38 id: number
40 createdAt: Date 39 createdAt: Date
41 updatedAt: Date 40 updatedAt: Date
@@ -43,4 +42,5 @@ export interface BlacklistedVideoInstance extends BlacklistedVideoClass, Blackli
43 toFormatedJSON: BlacklistedVideoMethods.ToFormatedJSON 42 toFormatedJSON: BlacklistedVideoMethods.ToFormatedJSON
44} 43}
45 44
46export interface BlacklistedVideoModel extends BlacklistedVideoClass, Sequelize.Model<BlacklistedVideoInstance, BlacklistedVideoAttributes> {} 45export interface BlacklistedVideoModel
46 extends BlacklistedVideoClass, Sequelize.Model<BlacklistedVideoInstance, BlacklistedVideoAttributes> {}
diff --git a/server/models/video/video-blacklist.ts b/server/models/video/video-blacklist.ts
index 3576c96f6..8c42dbc21 100644
--- a/server/models/video/video-blacklist.ts
+++ b/server/models/video/video-blacklist.ts
@@ -2,7 +2,6 @@ import * as Sequelize from 'sequelize'
2 2
3import { addMethodsToModel, getSort } from '../utils' 3import { addMethodsToModel, getSort } from '../utils'
4import { 4import {
5 BlacklistedVideoClass,
6 BlacklistedVideoInstance, 5 BlacklistedVideoInstance,
7 BlacklistedVideoAttributes, 6 BlacklistedVideoAttributes,
8 7
@@ -66,38 +65,39 @@ function associate (models) {
66 }) 65 })
67} 66}
68 67
69countTotal = function (callback: BlacklistedVideoMethods.CountTotalCallback) { 68countTotal = function () {
70 return BlacklistedVideo.count().asCallback(callback) 69 return BlacklistedVideo.count()
71} 70}
72 71
73list = function (callback: BlacklistedVideoMethods.ListCallback) { 72list = function () {
74 return BlacklistedVideo.findAll().asCallback(callback) 73 return BlacklistedVideo.findAll()
75} 74}
76 75
77listForApi = function (start: number, count: number, sort: string, callback: BlacklistedVideoMethods.ListForApiCallback) { 76listForApi = function (start: number, count: number, sort: string) {
78 const query = { 77 const query = {
79 offset: start, 78 offset: start,
80 limit: count, 79 limit: count,
81 order: [ getSort(sort) ] 80 order: [ getSort(sort) ]
82 } 81 }
83 82
84 return BlacklistedVideo.findAndCountAll(query).asCallback(function (err, result) { 83 return BlacklistedVideo.findAndCountAll(query).then(({ rows, count }) => {
85 if (err) return callback(err) 84 return {
86 85 data: rows,
87 return callback(null, result.rows, result.count) 86 total: count
87 }
88 }) 88 })
89} 89}
90 90
91loadById = function (id: number, callback: BlacklistedVideoMethods.LoadByIdCallback) { 91loadById = function (id: number) {
92 return BlacklistedVideo.findById(id).asCallback(callback) 92 return BlacklistedVideo.findById(id)
93} 93}
94 94
95loadByVideoId = function (id: string, callback: BlacklistedVideoMethods.LoadByIdCallback) { 95loadByVideoId = function (id: string) {
96 const query = { 96 const query = {
97 where: { 97 where: {
98 videoId: id 98 videoId: id
99 } 99 }
100 } 100 }
101 101
102 return BlacklistedVideo.find(query).asCallback(callback) 102 return BlacklistedVideo.findOne(query)
103} 103}
diff --git a/server/models/video/video-interface.ts b/server/models/video/video-interface.ts
index 4b591b9e7..c3e3365d5 100644
--- a/server/models/video/video-interface.ts
+++ b/server/models/video/video-interface.ts
@@ -1,10 +1,12 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import * as Promise from 'bluebird'
2 3
3import { AuthorInstance } from './author-interface' 4import { AuthorInstance } from './author-interface'
4import { VideoTagInstance } from './video-tag-interface' 5import { TagAttributes, TagInstance } from './tag-interface'
5 6
6// Don't use barrel, import just what we need 7// Don't use barrel, import just what we need
7import { Video as FormatedVideo } from '../../../shared/models/video.model' 8import { Video as FormatedVideo } from '../../../shared/models/video.model'
9import { ResultList } from '../../../shared/models/result-list.model'
8 10
9export type FormatedAddRemoteVideo = { 11export type FormatedAddRemoteVideo = {
10 name: string 12 name: string
@@ -56,46 +58,32 @@ export namespace VideoMethods {
56 export type IsOwned = (this: VideoInstance) => boolean 58 export type IsOwned = (this: VideoInstance) => boolean
57 export type ToFormatedJSON = (this: VideoInstance) => FormatedVideo 59 export type ToFormatedJSON = (this: VideoInstance) => FormatedVideo
58 60
59 export type ToAddRemoteJSONCallback = (err: Error, videoFormated?: FormatedAddRemoteVideo) => void 61 export type ToAddRemoteJSON = (this: VideoInstance) => Promise<FormatedAddRemoteVideo>
60 export type ToAddRemoteJSON = (this: VideoInstance, callback: ToAddRemoteJSONCallback) => void
61
62 export type ToUpdateRemoteJSON = (this: VideoInstance) => FormatedUpdateRemoteVideo 62 export type ToUpdateRemoteJSON = (this: VideoInstance) => FormatedUpdateRemoteVideo
63 63
64 export type TranscodeVideofileCallback = (err: Error) => void 64 export type TranscodeVideofile = (this: VideoInstance) => Promise<void>
65 export type TranscodeVideofile = (this: VideoInstance, callback: TranscodeVideofileCallback) => void 65
66 66 // Return thumbnail name
67 export type GenerateThumbnailFromDataCallback = (err: Error, thumbnailName?: string) => void 67 export type GenerateThumbnailFromData = (video: VideoInstance, thumbnailData: string) => Promise<string>
68 export type GenerateThumbnailFromData = (video: VideoInstance, thumbnailData: string, callback: GenerateThumbnailFromDataCallback) => void 68 export type GetDurationFromFile = (videoPath: string) => Promise<number>
69 69
70 export type GetDurationFromFileCallback = (err: Error, duration?: number) => void 70 export type List = () => Promise<VideoInstance[]>
71 export type GetDurationFromFile = (videoPath, callback) => void 71 export type ListOwnedAndPopulateAuthorAndTags = () => Promise<VideoInstance[]>
72 72 export type ListOwnedByAuthor = (author: string) => Promise<VideoInstance[]>
73 export type ListCallback = (err: Error, videoInstances: VideoInstance[]) => void 73
74 export type List = (callback: ListCallback) => void 74 export type ListForApi = (start: number, count: number, sort: string) => Promise< ResultList<VideoInstance> >
75 75 export type SearchAndPopulateAuthorAndPodAndTags = (
76 export type ListForApiCallback = (err: Error, videoInstances?: VideoInstance[], total?: number) => void 76 value: string,
77 export type ListForApi = (start: number, count: number, sort: string, callback: ListForApiCallback) => void 77 field: string,
78 78 start: number,
79 export type LoadByHostAndRemoteIdCallback = (err: Error, videoInstance: VideoInstance) => void 79 count: number,
80 export type LoadByHostAndRemoteId = (fromHost: string, remoteId: string, callback: LoadByHostAndRemoteIdCallback) => void 80 sort: string
81 81 ) => Promise< ResultList<VideoInstance> >
82 export type ListOwnedAndPopulateAuthorAndTagsCallback = (err: Error, videoInstances: VideoInstance[]) => void 82
83 export type ListOwnedAndPopulateAuthorAndTags = (callback: ListOwnedAndPopulateAuthorAndTagsCallback) => void 83 export type Load = (id: string) => Promise<VideoInstance>
84 84 export type LoadByHostAndRemoteId = (fromHost: string, remoteId: string) => Promise<VideoInstance>
85 export type ListOwnedByAuthorCallback = (err: Error, videoInstances: VideoInstance[]) => void 85 export type LoadAndPopulateAuthor = (id: string) => Promise<VideoInstance>
86 export type ListOwnedByAuthor = (author: string, callback: ListOwnedByAuthorCallback) => void 86 export type LoadAndPopulateAuthorAndPodAndTags = (id: string) => Promise<VideoInstance>
87
88 export type LoadCallback = (err: Error, videoInstance: VideoInstance) => void
89 export type Load = (id: string, callback: LoadCallback) => void
90
91 export type LoadAndPopulateAuthorCallback = (err: Error, videoInstance: VideoInstance) => void
92 export type LoadAndPopulateAuthor = (id: string, callback: LoadAndPopulateAuthorCallback) => void
93
94 export type LoadAndPopulateAuthorAndPodAndTagsCallback = (err: Error, videoInstance: VideoInstance) => void
95 export type LoadAndPopulateAuthorAndPodAndTags = (id: string, callback: LoadAndPopulateAuthorAndPodAndTagsCallback) => void
96
97 export type SearchAndPopulateAuthorAndPodAndTagsCallback = (err: Error, videoInstances?: VideoInstance[], total?: number) => void
98 export type SearchAndPopulateAuthorAndPodAndTags = (value: string, field: string, start: number, count: number, sort: string, callback: SearchAndPopulateAuthorAndPodAndTagsCallback) => void
99} 87}
100 88
101export interface VideoClass { 89export interface VideoClass {
@@ -139,7 +127,7 @@ export interface VideoAttributes {
139 dislikes?: number 127 dislikes?: number
140 128
141 Author?: AuthorInstance 129 Author?: AuthorInstance
142 Tags?: VideoTagInstance[] 130 Tags?: TagInstance[]
143} 131}
144 132
145export interface VideoInstance extends VideoClass, VideoAttributes, Sequelize.Instance<VideoAttributes> { 133export interface VideoInstance extends VideoClass, VideoAttributes, Sequelize.Instance<VideoAttributes> {
@@ -157,6 +145,8 @@ export interface VideoInstance extends VideoClass, VideoAttributes, Sequelize.In
157 toAddRemoteJSON: VideoMethods.ToAddRemoteJSON 145 toAddRemoteJSON: VideoMethods.ToAddRemoteJSON
158 toUpdateRemoteJSON: VideoMethods.ToUpdateRemoteJSON 146 toUpdateRemoteJSON: VideoMethods.ToUpdateRemoteJSON
159 transcodeVideofile: VideoMethods.TranscodeVideofile 147 transcodeVideofile: VideoMethods.TranscodeVideofile
148
149 setTags: Sequelize.HasManySetAssociationsMixin<TagAttributes, string>
160} 150}
161 151
162export interface VideoModel extends VideoClass, Sequelize.Model<VideoInstance, VideoAttributes> {} 152export interface VideoModel extends VideoClass, Sequelize.Model<VideoInstance, VideoAttributes> {}
diff --git a/server/models/video/video-tag.ts b/server/models/video/video-tag.ts
index 71ca85332..ac45374f8 100644
--- a/server/models/video/video-tag.ts
+++ b/server/models/video/video-tag.ts
@@ -1,12 +1,8 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2 2
3import { addMethodsToModel } from '../utils'
4import { 3import {
5 VideoTagClass,
6 VideoTagInstance, 4 VideoTagInstance,
7 VideoTagAttributes, 5 VideoTagAttributes
8
9 VideoTagMethods
10} from './video-tag-interface' 6} from './video-tag-interface'
11 7
12let VideoTag: Sequelize.Model<VideoTagInstance, VideoTagAttributes> 8let VideoTag: Sequelize.Model<VideoTagInstance, VideoTagAttributes>
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index e66ebee2d..629051ae4 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -1,17 +1,15 @@
1import * as safeBuffer from 'safe-buffer' 1import * as safeBuffer from 'safe-buffer'
2const Buffer = safeBuffer.Buffer 2const Buffer = safeBuffer.Buffer
3import * as createTorrent from 'create-torrent'
4import * as ffmpeg from 'fluent-ffmpeg' 3import * as ffmpeg from 'fluent-ffmpeg'
5import * as fs from 'fs'
6import * as magnetUtil from 'magnet-uri' 4import * as magnetUtil from 'magnet-uri'
7import { map, values } from 'lodash' 5import { map, values } from 'lodash'
8import { parallel, series } from 'async'
9import * as parseTorrent from 'parse-torrent' 6import * as parseTorrent from 'parse-torrent'
10import { join } from 'path' 7import { join } from 'path'
11import * as Sequelize from 'sequelize' 8import * as Sequelize from 'sequelize'
9import * as Promise from 'bluebird'
12 10
13import { database as db } from '../../initializers/database' 11import { database as db } from '../../initializers/database'
14import { VideoTagInstance } from './video-tag-interface' 12import { TagInstance } from './tag-interface'
15import { 13import {
16 logger, 14 logger,
17 isVideoNameValid, 15 isVideoNameValid,
@@ -21,7 +19,12 @@ import {
21 isVideoNSFWValid, 19 isVideoNSFWValid,
22 isVideoDescriptionValid, 20 isVideoDescriptionValid,
23 isVideoInfoHashValid, 21 isVideoInfoHashValid,
24 isVideoDurationValid 22 isVideoDurationValid,
23 readFileBufferPromise,
24 unlinkPromise,
25 renamePromise,
26 writeFilePromise,
27 createTorrentPromise
25} from '../../helpers' 28} from '../../helpers'
26import { 29import {
27 CONSTRAINTS_FIELDS, 30 CONSTRAINTS_FIELDS,
@@ -37,7 +40,6 @@ import { JobScheduler, removeVideoToFriends } from '../../lib'
37 40
38import { addMethodsToModel, getSort } from '../utils' 41import { addMethodsToModel, getSort } from '../utils'
39import { 42import {
40 VideoClass,
41 VideoInstance, 43 VideoInstance,
42 VideoAttributes, 44 VideoAttributes,
43 45
@@ -260,7 +262,7 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
260 toFormatedJSON, 262 toFormatedJSON,
261 toAddRemoteJSON, 263 toAddRemoteJSON,
262 toUpdateRemoteJSON, 264 toUpdateRemoteJSON,
263 transcodeVideofile, 265 transcodeVideofile
264 ] 266 ]
265 addMethodsToModel(Video, classMethods, instanceMethods) 267 addMethodsToModel(Video, classMethods, instanceMethods)
266 268
@@ -276,91 +278,53 @@ function beforeValidate (video: VideoInstance) {
276} 278}
277 279
278function beforeCreate (video: VideoInstance, options: { transaction: Sequelize.Transaction }) { 280function beforeCreate (video: VideoInstance, options: { transaction: Sequelize.Transaction }) {
279 return new Promise(function (resolve, reject) { 281 if (video.isOwned()) {
282 const videoPath = join(CONFIG.STORAGE.VIDEOS_DIR, video.getVideoFilename())
280 const tasks = [] 283 const tasks = []
281 284
282 if (video.isOwned()) { 285 tasks.push(
283 const videoPath = join(CONFIG.STORAGE.VIDEOS_DIR, video.getVideoFilename()) 286 createTorrentFromVideo(video, videoPath),
284 287 createThumbnail(video, videoPath),
285 tasks.push( 288 createPreview(video, videoPath)
286 function createVideoTorrent (callback) { 289 )
287 createTorrentFromVideo(video, videoPath, callback)
288 },
289
290 function createVideoThumbnail (callback) {
291 createThumbnail(video, videoPath, callback)
292 },
293
294 function createVideoPreview (callback) {
295 createPreview(video, videoPath, callback)
296 }
297 )
298
299 if (CONFIG.TRANSCODING.ENABLED === true) {
300 tasks.push(
301 function createVideoTranscoderJob (callback) {
302 const dataInput = {
303 id: video.id
304 }
305 290
306 JobScheduler.Instance.createJob(options.transaction, 'videoTranscoder', dataInput, callback) 291 if (CONFIG.TRANSCODING.ENABLED === true) {
307 } 292 const dataInput = {
308 ) 293 id: video.id
309 } 294 }
310 295
311 return parallel(tasks, function (err) { 296 tasks.push(
312 if (err) return reject(err) 297 JobScheduler.Instance.createJob(options.transaction, 'videoTranscoder', dataInput)
313 298 )
314 return resolve()
315 })
316 } 299 }
317 300
318 return resolve() 301 return Promise.all(tasks)
319 }) 302 }
303
304 return Promise.resolve()
320} 305}
321 306
322function afterDestroy (video: VideoInstance) { 307function afterDestroy (video: VideoInstance) {
323 return new Promise(function (resolve, reject) { 308 const tasks = []
324 const tasks = []
325
326 tasks.push(
327 function (callback) {
328 removeThumbnail(video, callback)
329 }
330 )
331
332 if (video.isOwned()) {
333 tasks.push(
334 function removeVideoFile (callback) {
335 removeFile(video, callback)
336 },
337 309
338 function removeVideoTorrent (callback) { 310 tasks.push(
339 removeTorrent(video, callback) 311 removeThumbnail(video)
340 }, 312 )
341
342 function removeVideoPreview (callback) {
343 removePreview(video, callback)
344 },
345
346 function notifyFriends (callback) {
347 const params = {
348 remoteId: video.id
349 }
350
351 removeVideoToFriends(params)
352 313
353 return callback() 314 if (video.isOwned()) {
354 } 315 const removeVideoToFriendsParams = {
355 ) 316 remoteId: video.id
356 } 317 }
357 318
358 parallel(tasks, function (err) { 319 tasks.push(
359 if (err) return reject(err) 320 removeFile(video),
321 removeTorrent(video),
322 removePreview(video),
323 removeVideoToFriends(removeVideoToFriendsParams)
324 )
325 }
360 326
361 return resolve() 327 return Promise.all(tasks)
362 })
363 })
364} 328}
365 329
366// ------------------------------ METHODS ------------------------------ 330// ------------------------------ METHODS ------------------------------
@@ -488,7 +452,7 @@ toFormatedJSON = function (this: VideoInstance) {
488 views: this.views, 452 views: this.views,
489 likes: this.likes, 453 likes: this.likes,
490 dislikes: this.dislikes, 454 dislikes: this.dislikes,
491 tags: map<VideoTagInstance, string>(this.Tags, 'name'), 455 tags: map<TagInstance, string>(this.Tags, 'name'),
492 thumbnailPath: join(STATIC_PATHS.THUMBNAILS, this.getThumbnailName()), 456 thumbnailPath: join(STATIC_PATHS.THUMBNAILS, this.getThumbnailName()),
493 createdAt: this.createdAt, 457 createdAt: this.createdAt,
494 updatedAt: this.updatedAt 458 updatedAt: this.updatedAt
@@ -497,15 +461,11 @@ toFormatedJSON = function (this: VideoInstance) {
497 return json 461 return json
498} 462}
499 463
500toAddRemoteJSON = function (this: VideoInstance, callback: VideoMethods.ToAddRemoteJSONCallback) { 464toAddRemoteJSON = function (this: VideoInstance) {
501 // Get thumbnail data to send to the other pod 465 // Get thumbnail data to send to the other pod
502 const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, this.getThumbnailName()) 466 const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, this.getThumbnailName())
503 fs.readFile(thumbnailPath, (err, thumbnailData) => {
504 if (err) {
505 logger.error('Cannot read the thumbnail of the video')
506 return callback(err)
507 }
508 467
468 return readFileBufferPromise(thumbnailPath).then(thumbnailData => {
509 const remoteVideo = { 469 const remoteVideo = {
510 name: this.name, 470 name: this.name,
511 category: this.category, 471 category: this.category,
@@ -518,7 +478,7 @@ toAddRemoteJSON = function (this: VideoInstance, callback: VideoMethods.ToAddRem
518 author: this.Author.name, 478 author: this.Author.name,
519 duration: this.duration, 479 duration: this.duration,
520 thumbnailData: thumbnailData.toString('binary'), 480 thumbnailData: thumbnailData.toString('binary'),
521 tags: map<VideoTagInstance, string>(this.Tags, 'name'), 481 tags: map<TagInstance, string>(this.Tags, 'name'),
522 createdAt: this.createdAt, 482 createdAt: this.createdAt,
523 updatedAt: this.updatedAt, 483 updatedAt: this.updatedAt,
524 extname: this.extname, 484 extname: this.extname,
@@ -527,7 +487,7 @@ toAddRemoteJSON = function (this: VideoInstance, callback: VideoMethods.ToAddRem
527 dislikes: this.dislikes 487 dislikes: this.dislikes
528 } 488 }
529 489
530 return callback(null, remoteVideo) 490 return remoteVideo
531 }) 491 })
532} 492}
533 493
@@ -543,7 +503,7 @@ toUpdateRemoteJSON = function (this: VideoInstance) {
543 remoteId: this.id, 503 remoteId: this.id,
544 author: this.Author.name, 504 author: this.Author.name,
545 duration: this.duration, 505 duration: this.duration,
546 tags: map<VideoTagInstance, string>(this.Tags, 'name'), 506 tags: map<TagInstance, string>(this.Tags, 'name'),
547 createdAt: this.createdAt, 507 createdAt: this.createdAt,
548 updatedAt: this.updatedAt, 508 updatedAt: this.updatedAt,
549 extname: this.extname, 509 extname: this.extname,
@@ -555,7 +515,7 @@ toUpdateRemoteJSON = function (this: VideoInstance) {
555 return json 515 return json
556} 516}
557 517
558transcodeVideofile = function (this: VideoInstance, finalCallback: VideoMethods.TranscodeVideofileCallback) { 518transcodeVideofile = function (this: VideoInstance) {
559 const video = this 519 const video = this
560 520
561 const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR 521 const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
@@ -563,78 +523,73 @@ transcodeVideofile = function (this: VideoInstance, finalCallback: VideoMethods.
563 const videoInputPath = join(videosDirectory, video.getVideoFilename()) 523 const videoInputPath = join(videosDirectory, video.getVideoFilename())
564 const videoOutputPath = join(videosDirectory, video.id + '-transcoded' + newExtname) 524 const videoOutputPath = join(videosDirectory, video.id + '-transcoded' + newExtname)
565 525
566 ffmpeg(videoInputPath) 526 return new Promise<void>((res, rej) => {
567 .output(videoOutputPath) 527 ffmpeg(videoInputPath)
568 .videoCodec('libx264') 528 .output(videoOutputPath)
569 .outputOption('-threads ' + CONFIG.TRANSCODING.THREADS) 529 .videoCodec('libx264')
570 .outputOption('-movflags faststart') 530 .outputOption('-threads ' + CONFIG.TRANSCODING.THREADS)
571 .on('error', finalCallback) 531 .outputOption('-movflags faststart')
572 .on('end', function () { 532 .on('error', rej)
573 series([ 533 .on('end', () => {
574 function removeOldFile (callback) { 534
575 fs.unlink(videoInputPath, callback) 535 return unlinkPromise(videoInputPath)
576 }, 536 .then(() => {
577 537 // Important to do this before getVideoFilename() to take in account the new file extension
578 function moveNewFile (callback) { 538 video.set('extname', newExtname)
579 // Important to do this before getVideoFilename() to take in account the new file extension 539
580 video.set('extname', newExtname) 540 const newVideoPath = join(videosDirectory, video.getVideoFilename())
581 541 return renamePromise(videoOutputPath, newVideoPath)
582 const newVideoPath = join(videosDirectory, video.getVideoFilename())
583 fs.rename(videoOutputPath, newVideoPath, callback)
584 },
585
586 function torrent (callback) {
587 const newVideoPath = join(videosDirectory, video.getVideoFilename())
588 createTorrentFromVideo(video, newVideoPath, callback)
589 },
590
591 function videoExtension (callback) {
592 video.save().asCallback(callback)
593 }
594
595 ], function (err: Error) {
596 if (err) {
597 // Autodesctruction...
598 video.destroy().asCallback(function (err) {
599 if (err) logger.error('Cannot destruct video after transcoding failure.', { error: err })
600 }) 542 })
543 .then(() => {
544 const newVideoPath = join(videosDirectory, video.getVideoFilename())
545 return createTorrentFromVideo(video, newVideoPath)
546 })
547 .then(() => {
548 return video.save()
549 })
550 .then(() => {
551 return res()
552 })
553 .catch(err => {
554 // Autodesctruction...
555 video.destroy().asCallback(function (err) {
556 if (err) logger.error('Cannot destruct video after transcoding failure.', { error: err })
557 })
601 558
602 return finalCallback(err) 559 return rej(err)
603 } 560 })
604
605 return finalCallback(null)
606 }) 561 })
607 }) 562 .run()
608 .run() 563 })
609} 564}
610 565
611// ------------------------------ STATICS ------------------------------ 566// ------------------------------ STATICS ------------------------------
612 567
613generateThumbnailFromData = function (video: VideoInstance, thumbnailData: string, callback: VideoMethods.GenerateThumbnailFromDataCallback) { 568generateThumbnailFromData = function (video: VideoInstance, thumbnailData: string) {
614 // Creating the thumbnail for a remote video 569 // Creating the thumbnail for a remote video
615 570
616 const thumbnailName = video.getThumbnailName() 571 const thumbnailName = video.getThumbnailName()
617 const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, thumbnailName) 572 const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, thumbnailName)
618 fs.writeFile(thumbnailPath, Buffer.from(thumbnailData, 'binary'), function (err) { 573 return writeFilePromise(thumbnailPath, Buffer.from(thumbnailData, 'binary')).then(() => {
619 if (err) return callback(err) 574 return thumbnailName
620
621 return callback(null, thumbnailName)
622 }) 575 })
623} 576}
624 577
625getDurationFromFile = function (videoPath: string, callback: VideoMethods.GetDurationFromFileCallback) { 578getDurationFromFile = function (videoPath: string) {
626 ffmpeg.ffprobe(videoPath, function (err, metadata) { 579 return new Promise<number>((res, rej) => {
627 if (err) return callback(err) 580 ffmpeg.ffprobe(videoPath, function (err, metadata) {
581 if (err) return rej(err)
628 582
629 return callback(null, Math.floor(metadata.format.duration)) 583 return res(Math.floor(metadata.format.duration))
584 })
630 }) 585 })
631} 586}
632 587
633list = function (callback: VideoMethods.ListCallback) { 588list = function () {
634 return Video.findAll().asCallback(callback) 589 return Video.findAll()
635} 590}
636 591
637listForApi = function (start: number, count: number, sort: string, callback: VideoMethods.ListForApiCallback) { 592listForApi = function (start: number, count: number, sort: string) {
638 // Exclude Blakclisted videos from the list 593 // Exclude Blakclisted videos from the list
639 const query = { 594 const query = {
640 distinct: true, 595 distinct: true,
@@ -652,14 +607,15 @@ listForApi = function (start: number, count: number, sort: string, callback: Vid
652 where: createBaseVideosWhere() 607 where: createBaseVideosWhere()
653 } 608 }
654 609
655 return Video.findAndCountAll(query).asCallback(function (err, result) { 610 return Video.findAndCountAll(query).then(({ rows, count }) => {
656 if (err) return callback(err) 611 return {
657 612 data: rows,
658 return callback(null, result.rows, result.count) 613 total: count
614 }
659 }) 615 })
660} 616}
661 617
662loadByHostAndRemoteId = function (fromHost: string, remoteId: string, callback: VideoMethods.LoadByHostAndRemoteIdCallback) { 618loadByHostAndRemoteId = function (fromHost: string, remoteId: string) {
663 const query = { 619 const query = {
664 where: { 620 where: {
665 remoteId: remoteId 621 remoteId: remoteId
@@ -680,10 +636,10 @@ loadByHostAndRemoteId = function (fromHost: string, remoteId: string, callback:
680 ] 636 ]
681 } 637 }
682 638
683 return Video.findOne(query).asCallback(callback) 639 return Video.findOne(query)
684} 640}
685 641
686listOwnedAndPopulateAuthorAndTags = function (callback: VideoMethods.ListOwnedAndPopulateAuthorAndTagsCallback) { 642listOwnedAndPopulateAuthorAndTags = function () {
687 // If remoteId is null this is *our* video 643 // If remoteId is null this is *our* video
688 const query = { 644 const query = {
689 where: { 645 where: {
@@ -692,10 +648,10 @@ listOwnedAndPopulateAuthorAndTags = function (callback: VideoMethods.ListOwnedAn
692 include: [ Video['sequelize'].models.Author, Video['sequelize'].models.Tag ] 648 include: [ Video['sequelize'].models.Author, Video['sequelize'].models.Tag ]
693 } 649 }
694 650
695 return Video.findAll(query).asCallback(callback) 651 return Video.findAll(query)
696} 652}
697 653
698listOwnedByAuthor = function (author: string, callback: VideoMethods.ListOwnedByAuthorCallback) { 654listOwnedByAuthor = function (author: string) {
699 const query = { 655 const query = {
700 where: { 656 where: {
701 remoteId: null 657 remoteId: null
@@ -710,22 +666,22 @@ listOwnedByAuthor = function (author: string, callback: VideoMethods.ListOwnedBy
710 ] 666 ]
711 } 667 }
712 668
713 return Video.findAll(query).asCallback(callback) 669 return Video.findAll(query)
714} 670}
715 671
716load = function (id: string, callback: VideoMethods.LoadCallback) { 672load = function (id: string) {
717 return Video.findById(id).asCallback(callback) 673 return Video.findById(id)
718} 674}
719 675
720loadAndPopulateAuthor = function (id: string, callback: VideoMethods.LoadAndPopulateAuthorCallback) { 676loadAndPopulateAuthor = function (id: string) {
721 const options = { 677 const options = {
722 include: [ Video['sequelize'].models.Author ] 678 include: [ Video['sequelize'].models.Author ]
723 } 679 }
724 680
725 return Video.findById(id, options).asCallback(callback) 681 return Video.findById(id, options)
726} 682}
727 683
728loadAndPopulateAuthorAndPodAndTags = function (id: string, callback: VideoMethods.LoadAndPopulateAuthorAndPodAndTagsCallback) { 684loadAndPopulateAuthorAndPodAndTags = function (id: string) {
729 const options = { 685 const options = {
730 include: [ 686 include: [
731 { 687 {
@@ -736,17 +692,10 @@ loadAndPopulateAuthorAndPodAndTags = function (id: string, callback: VideoMethod
736 ] 692 ]
737 } 693 }
738 694
739 return Video.findById(id, options).asCallback(callback) 695 return Video.findById(id, options)
740} 696}
741 697
742searchAndPopulateAuthorAndPodAndTags = function ( 698searchAndPopulateAuthorAndPodAndTags = function (value: string, field: string, start: number, count: number, sort: string) {
743 value: string,
744 field: string,
745 start: number,
746 count: number,
747 sort: string,
748 callback: VideoMethods.SearchAndPopulateAuthorAndPodAndTagsCallback
749) {
750 const podInclude: any = { 699 const podInclude: any = {
751 model: Video['sequelize'].models.Pod, 700 model: Video['sequelize'].models.Pod,
752 required: false 701 required: false
@@ -778,7 +727,11 @@ searchAndPopulateAuthorAndPodAndTags = function (
778 } else if (field === 'tags') { 727 } else if (field === 'tags') {
779 const escapedValue = Video['sequelize'].escape('%' + value + '%') 728 const escapedValue = Video['sequelize'].escape('%' + value + '%')
780 query.where.id.$in = Video['sequelize'].literal( 729 query.where.id.$in = Video['sequelize'].literal(
781 '(SELECT "VideoTags"."videoId" FROM "Tags" INNER JOIN "VideoTags" ON "Tags"."id" = "VideoTags"."tagId" WHERE name LIKE ' + escapedValue + ')' 730 `(SELECT "VideoTags"."videoId"
731 FROM "Tags"
732 INNER JOIN "VideoTags" ON "Tags"."id" = "VideoTags"."tagId"
733 WHERE name LIKE ${escapedValue}
734 )`
782 ) 735 )
783 } else if (field === 'host') { 736 } else if (field === 'host') {
784 // FIXME: Include our pod? (not stored in the database) 737 // FIXME: Include our pod? (not stored in the database)
@@ -810,10 +763,11 @@ searchAndPopulateAuthorAndPodAndTags = function (
810 // query.include.push([ Video['sequelize'].models.Tag ]) 763 // query.include.push([ Video['sequelize'].models.Tag ])
811 } 764 }
812 765
813 return Video.findAndCountAll(query).asCallback(function (err, result) { 766 return Video.findAndCountAll(query).then(({ rows, count }) => {
814 if (err) return callback(err) 767 return {
815 768 data: rows,
816 return callback(null, result.rows, result.count) 769 total: count
770 }
817 }) 771 })
818} 772}
819 773
@@ -829,27 +783,27 @@ function createBaseVideosWhere () {
829 } 783 }
830} 784}
831 785
832function removeThumbnail (video: VideoInstance, callback: (err: Error) => void) { 786function removeThumbnail (video: VideoInstance) {
833 const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, video.getThumbnailName()) 787 const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, video.getThumbnailName())
834 fs.unlink(thumbnailPath, callback) 788 return unlinkPromise(thumbnailPath)
835} 789}
836 790
837function removeFile (video: VideoInstance, callback: (err: Error) => void) { 791function removeFile (video: VideoInstance) {
838 const filePath = join(CONFIG.STORAGE.VIDEOS_DIR, video.getVideoFilename()) 792 const filePath = join(CONFIG.STORAGE.VIDEOS_DIR, video.getVideoFilename())
839 fs.unlink(filePath, callback) 793 return unlinkPromise(filePath)
840} 794}
841 795
842function removeTorrent (video: VideoInstance, callback: (err: Error) => void) { 796function removeTorrent (video: VideoInstance) {
843 const torrenPath = join(CONFIG.STORAGE.TORRENTS_DIR, video.getTorrentName()) 797 const torrenPath = join(CONFIG.STORAGE.TORRENTS_DIR, video.getTorrentName())
844 fs.unlink(torrenPath, callback) 798 return unlinkPromise(torrenPath)
845} 799}
846 800
847function removePreview (video: VideoInstance, callback: (err: Error) => void) { 801function removePreview (video: VideoInstance) {
848 // Same name than video thumnail 802 // Same name than video thumnail
849 fs.unlink(CONFIG.STORAGE.PREVIEWS_DIR + video.getPreviewName(), callback) 803 return unlinkPromise(CONFIG.STORAGE.PREVIEWS_DIR + video.getPreviewName())
850} 804}
851 805
852function createTorrentFromVideo (video: VideoInstance, videoPath: string, callback: (err: Error) => void) { 806function createTorrentFromVideo (video: VideoInstance, videoPath: string) {
853 const options = { 807 const options = {
854 announceList: [ 808 announceList: [
855 [ CONFIG.WEBSERVER.WS + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT + '/tracker/socket' ] 809 [ CONFIG.WEBSERVER.WS + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT + '/tracker/socket' ]
@@ -859,30 +813,27 @@ function createTorrentFromVideo (video: VideoInstance, videoPath: string, callba
859 ] 813 ]
860 } 814 }
861 815
862 createTorrent(videoPath, options, function (err, torrent) { 816 return createTorrentPromise(videoPath, options)
863 if (err) return callback(err) 817 .then(torrent => {
864 818 const filePath = join(CONFIG.STORAGE.TORRENTS_DIR, video.getTorrentName())
865 const filePath = join(CONFIG.STORAGE.TORRENTS_DIR, video.getTorrentName()) 819 return writeFilePromise(filePath, torrent).then(() => torrent)
866 fs.writeFile(filePath, torrent, function (err) { 820 })
867 if (err) return callback(err) 821 .then(torrent => {
868
869 const parsedTorrent = parseTorrent(torrent) 822 const parsedTorrent = parseTorrent(torrent)
870 video.set('infoHash', parsedTorrent.infoHash) 823 video.set('infoHash', parsedTorrent.infoHash)
871 video.validate().asCallback(callback) 824 return video.validate()
872 }) 825 })
873 })
874} 826}
875 827
876function createPreview (video: VideoInstance, videoPath: string, callback: (err: Error) => void) { 828function createPreview (video: VideoInstance, videoPath: string) {
877 generateImage(video, videoPath, CONFIG.STORAGE.PREVIEWS_DIR, video.getPreviewName(), null, callback) 829 return generateImage(video, videoPath, CONFIG.STORAGE.PREVIEWS_DIR, video.getPreviewName(), null)
878} 830}
879 831
880function createThumbnail (video: VideoInstance, videoPath: string, callback: (err: Error) => void) { 832function createThumbnail (video: VideoInstance, videoPath: string) {
881 generateImage(video, videoPath, CONFIG.STORAGE.THUMBNAILS_DIR, video.getThumbnailName(), THUMBNAILS_SIZE, callback) 833 return generateImage(video, videoPath, CONFIG.STORAGE.THUMBNAILS_DIR, video.getThumbnailName(), THUMBNAILS_SIZE)
882} 834}
883 835
884type GenerateImageCallback = (err: Error, imageName: string) => void 836function generateImage (video: VideoInstance, videoPath: string, folder: string, imageName: string, size: string) {
885function generateImage (video: VideoInstance, videoPath: string, folder: string, imageName: string, size: string, callback?: GenerateImageCallback) {
886 const options: any = { 837 const options: any = {
887 filename: imageName, 838 filename: imageName,
888 count: 1, 839 count: 1,
@@ -893,29 +844,25 @@ function generateImage (video: VideoInstance, videoPath: string, folder: string,
893 options.size = size 844 options.size = size
894 } 845 }
895 846
896 ffmpeg(videoPath) 847 return new Promise<string>((res, rej) => {
897 .on('error', callback) 848 ffmpeg(videoPath)
898 .on('end', function () { 849 .on('error', rej)
899 callback(null, imageName) 850 .on('end', function () {
900 }) 851 return res(imageName)
901 .thumbnail(options) 852 })
853 .thumbnail(options)
854 })
902} 855}
903 856
904function removeFromBlacklist (video: VideoInstance, callback: (err: Error) => void) { 857function removeFromBlacklist (video: VideoInstance) {
905 // Find the blacklisted video 858 // Find the blacklisted video
906 db.BlacklistedVideo.loadByVideoId(video.id, function (err, video) { 859 return db.BlacklistedVideo.loadByVideoId(video.id).then(video => {
907 // If an error occured, stop here 860 // Not found the video, skip
908 if (err) { 861 if (!video) {
909 logger.error('Error when fetching video from blacklist.', { error: err }) 862 return null
910 return callback(err)
911 } 863 }
912 864
913 // If we found the video, remove it from the blacklist 865 // If we found the video, remove it from the blacklist
914 if (video) { 866 return video.destroy()
915 video.destroy().asCallback(callback)
916 } else {
917 // If haven't found it, simply ignore it and do nothing
918 return callback(null)
919 }
920 }) 867 })
921} 868}