aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/models
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
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')
-rw-r--r--server/models/application/application-interface.ts10
-rw-r--r--server/models/application/application.ts13
-rw-r--r--server/models/job/job-interface.ts4
-rw-r--r--server/models/job/job.ts5
-rw-r--r--server/models/oauth/oauth-client-interface.ts9
-rw-r--r--server/models/oauth/oauth-client.ts9
-rw-r--r--server/models/oauth/oauth-token-interface.ts11
-rw-r--r--server/models/oauth/oauth-token.ts5
-rw-r--r--server/models/pod/pod-interface.ts28
-rw-r--r--server/models/pod/pod.ts115
-rw-r--r--server/models/request/abstract-request-interface.ts12
-rw-r--r--server/models/request/index.ts1
-rw-r--r--server/models/request/request-interface.ts16
-rw-r--r--server/models/request/request-to-pod-interface.ts8
-rw-r--r--server/models/request/request-to-pod.ts7
-rw-r--r--server/models/request/request-video-event-interface.ts22
-rw-r--r--server/models/request/request-video-event.ts27
-rw-r--r--server/models/request/request-video-qadu-interface.ts22
-rw-r--r--server/models/request/request-video-qadu.ts27
-rw-r--r--server/models/request/request.ts28
-rw-r--r--server/models/user/user-interface.ts26
-rw-r--r--server/models/user/user-video-rate-interface.ts4
-rw-r--r--server/models/user/user-video-rate.ts5
-rw-r--r--server/models/user/user.ts47
-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
35 files changed, 445 insertions, 589 deletions
diff --git a/server/models/application/application-interface.ts b/server/models/application/application-interface.ts
index c03513db1..33254ba2d 100644
--- a/server/models/application/application-interface.ts
+++ b/server/models/application/application-interface.ts
@@ -1,11 +1,13 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import * as Promise from 'bluebird'
2 3
3export namespace ApplicationMethods { 4export namespace ApplicationMethods {
4 export type LoadMigrationVersionCallback = (err: Error, version: number) => void 5 export type LoadMigrationVersion = () => Promise<number>
5 export type LoadMigrationVersion = (callback: LoadMigrationVersionCallback) => void
6 6
7 export type UpdateMigrationVersionCallback = (err: Error, applicationInstance: ApplicationAttributes) => void 7 export type UpdateMigrationVersion = (
8 export type UpdateMigrationVersion = (newVersion: number, transaction: Sequelize.Transaction, callback: UpdateMigrationVersionCallback) => void 8 newVersion: number,
9 transaction: Sequelize.Transaction
10 ) => Promise<[ number, ApplicationInstance[] ]>
9} 11}
10 12
11export interface ApplicationClass { 13export interface ApplicationClass {
diff --git a/server/models/application/application.ts b/server/models/application/application.ts
index 0e9a1ebb3..507b7a843 100644
--- a/server/models/application/application.ts
+++ b/server/models/application/application.ts
@@ -2,7 +2,6 @@ import * as Sequelize from 'sequelize'
2 2
3import { addMethodsToModel } from '../utils' 3import { addMethodsToModel } from '../utils'
4import { 4import {
5 ApplicationClass,
6 ApplicationAttributes, 5 ApplicationAttributes,
7 ApplicationInstance, 6 ApplicationInstance,
8 7
@@ -35,23 +34,19 @@ export default function defineApplication (sequelize: Sequelize.Sequelize, DataT
35 34
36// --------------------------------------------------------------------------- 35// ---------------------------------------------------------------------------
37 36
38loadMigrationVersion = function (callback: ApplicationMethods.LoadMigrationVersionCallback) { 37loadMigrationVersion = function () {
39 const query = { 38 const query = {
40 attributes: [ 'migrationVersion' ] 39 attributes: [ 'migrationVersion' ]
41 } 40 }
42 41
43 return Application.findOne(query).asCallback(function (err, data) { 42 return Application.findOne(query).then(data => data ? data.migrationVersion : null)
44 const version = data ? data.migrationVersion : null
45
46 return callback(err, version)
47 })
48} 43}
49 44
50updateMigrationVersion = function (newVersion: number, transaction: Sequelize.Transaction, callback: ApplicationMethods.UpdateMigrationVersionCallback) { 45updateMigrationVersion = function (newVersion: number, transaction: Sequelize.Transaction) {
51 const options: Sequelize.UpdateOptions = { 46 const options: Sequelize.UpdateOptions = {
52 where: {}, 47 where: {},
53 transaction: transaction 48 transaction: transaction
54 } 49 }
55 50
56 return Application.update({ migrationVersion: newVersion }, options).asCallback(callback) 51 return Application.update({ migrationVersion: newVersion }, options)
57} 52}
diff --git a/server/models/job/job-interface.ts b/server/models/job/job-interface.ts
index 31b377367..ba5622977 100644
--- a/server/models/job/job-interface.ts
+++ b/server/models/job/job-interface.ts
@@ -1,10 +1,10 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import * as Promise from 'bluebird'
2 3
3import { JobState } from '../../../shared/models/job.model' 4import { JobState } from '../../../shared/models/job.model'
4 5
5export namespace JobMethods { 6export namespace JobMethods {
6 export type ListWithLimitCallback = (err: Error, jobInstances: JobInstance[]) => void 7 export type ListWithLimit = (limit: number, state: JobState) => Promise<JobInstance[]>
7 export type ListWithLimit = (limit: number, state: JobState, callback: ListWithLimitCallback) => void
8} 8}
9 9
10export interface JobClass { 10export interface JobClass {
diff --git a/server/models/job/job.ts b/server/models/job/job.ts
index 38e4e8f30..968f9d71d 100644
--- a/server/models/job/job.ts
+++ b/server/models/job/job.ts
@@ -5,7 +5,6 @@ import { JOB_STATES } from '../../initializers'
5 5
6import { addMethodsToModel } from '../utils' 6import { addMethodsToModel } from '../utils'
7import { 7import {
8 JobClass,
9 JobInstance, 8 JobInstance,
10 JobAttributes, 9 JobAttributes,
11 10
@@ -49,7 +48,7 @@ export default function defineJob (sequelize: Sequelize.Sequelize, DataTypes: Se
49 48
50// --------------------------------------------------------------------------- 49// ---------------------------------------------------------------------------
51 50
52listWithLimit = function (limit: number, state: JobState, callback: JobMethods.ListWithLimitCallback) { 51listWithLimit = function (limit: number, state: JobState) {
53 const query = { 52 const query = {
54 order: [ 53 order: [
55 [ 'id', 'ASC' ] 54 [ 'id', 'ASC' ]
@@ -60,5 +59,5 @@ listWithLimit = function (limit: number, state: JobState, callback: JobMethods.L
60 } 59 }
61 } 60 }
62 61
63 return Job.findAll(query).asCallback(callback) 62 return Job.findAll(query)
64} 63}
diff --git a/server/models/oauth/oauth-client-interface.ts b/server/models/oauth/oauth-client-interface.ts
index 3b4325740..3526e4159 100644
--- a/server/models/oauth/oauth-client-interface.ts
+++ b/server/models/oauth/oauth-client-interface.ts
@@ -1,13 +1,12 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import * as Promise from 'bluebird'
2 3
3export namespace OAuthClientMethods { 4export namespace OAuthClientMethods {
4 export type CountTotalCallback = (err: Error, total: number) => void 5 export type CountTotal = () => Promise<number>
5 export type CountTotal = (callback: CountTotalCallback) => void
6 6
7 export type LoadFirstClientCallback = (err: Error, client: OAuthClientInstance) => void 7 export type LoadFirstClient = () => Promise<OAuthClientInstance>
8 export type LoadFirstClient = (callback: LoadFirstClientCallback) => void
9 8
10 export type GetByIdAndSecret = (clientId, clientSecret) => void 9 export type GetByIdAndSecret = (clientId: string, clientSecret: string) => Promise<OAuthClientInstance>
11} 10}
12 11
13export interface OAuthClientClass { 12export interface OAuthClientClass {
diff --git a/server/models/oauth/oauth-client.ts b/server/models/oauth/oauth-client.ts
index fbc2a3393..9cc68771d 100644
--- a/server/models/oauth/oauth-client.ts
+++ b/server/models/oauth/oauth-client.ts
@@ -2,7 +2,6 @@ import * as Sequelize from 'sequelize'
2 2
3import { addMethodsToModel } from '../utils' 3import { addMethodsToModel } from '../utils'
4import { 4import {
5 OAuthClientClass,
6 OAuthClientInstance, 5 OAuthClientInstance,
7 OAuthClientAttributes, 6 OAuthClientAttributes,
8 7
@@ -67,12 +66,12 @@ function associate (models) {
67 }) 66 })
68} 67}
69 68
70countTotal = function (callback: OAuthClientMethods.CountTotalCallback) { 69countTotal = function () {
71 return OAuthClient.count().asCallback(callback) 70 return OAuthClient.count()
72} 71}
73 72
74loadFirstClient = function (callback: OAuthClientMethods.LoadFirstClientCallback) { 73loadFirstClient = function () {
75 return OAuthClient.findOne().asCallback(callback) 74 return OAuthClient.findOne()
76} 75}
77 76
78getByIdAndSecret = function (clientId: string, clientSecret: string) { 77getByIdAndSecret = function (clientId: string, clientSecret: string) {
diff --git a/server/models/oauth/oauth-token-interface.ts b/server/models/oauth/oauth-token-interface.ts
index 815ad5eef..f2ddafa54 100644
--- a/server/models/oauth/oauth-token-interface.ts
+++ b/server/models/oauth/oauth-token-interface.ts
@@ -1,5 +1,5 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import * as Bluebird from 'bluebird' 2import * as Promise from 'bluebird'
3 3
4import { UserModel } from '../user' 4import { UserModel } from '../user'
5 5
@@ -15,12 +15,11 @@ export type OAuthTokenInfo = {
15} 15}
16 16
17export namespace OAuthTokenMethods { 17export namespace OAuthTokenMethods {
18 export type GetByRefreshTokenAndPopulateClient = (refreshToken: string) => Bluebird<OAuthTokenInfo> 18 export type GetByRefreshTokenAndPopulateClient = (refreshToken: string) => Promise<OAuthTokenInfo>
19 export type GetByTokenAndPopulateUser = (bearerToken: string) => Bluebird<OAuthTokenInstance> 19 export type GetByTokenAndPopulateUser = (bearerToken: string) => Promise<OAuthTokenInstance>
20 export type GetByRefreshTokenAndPopulateUser = (refreshToken: string) => Bluebird<OAuthTokenInstance> 20 export type GetByRefreshTokenAndPopulateUser = (refreshToken: string) => Promise<OAuthTokenInstance>
21 21
22 export type RemoveByUserIdCallback = (err: Error) => void 22 export type RemoveByUserId = (userId) => Promise<number>
23 export type RemoveByUserId = (userId, callback) => void
24} 23}
25 24
26export interface OAuthTokenClass { 25export interface OAuthTokenClass {
diff --git a/server/models/oauth/oauth-token.ts b/server/models/oauth/oauth-token.ts
index eab9cf858..8c6883465 100644
--- a/server/models/oauth/oauth-token.ts
+++ b/server/models/oauth/oauth-token.ts
@@ -4,7 +4,6 @@ import { logger } from '../../helpers'
4 4
5import { addMethodsToModel } from '../utils' 5import { addMethodsToModel } from '../utils'
6import { 6import {
7 OAuthTokenClass,
8 OAuthTokenInstance, 7 OAuthTokenInstance,
9 OAuthTokenAttributes, 8 OAuthTokenAttributes,
10 9
@@ -149,12 +148,12 @@ getByRefreshTokenAndPopulateUser = function (refreshToken: string) {
149 }) 148 })
150} 149}
151 150
152removeByUserId = function (userId, callback) { 151removeByUserId = function (userId: number) {
153 const query = { 152 const query = {
154 where: { 153 where: {
155 userId: userId 154 userId: userId
156 } 155 }
157 } 156 }
158 157
159 return OAuthToken.destroy(query).asCallback(callback) 158 return OAuthToken.destroy(query)
160} 159}
diff --git a/server/models/pod/pod-interface.ts b/server/models/pod/pod-interface.ts
index d88847c45..f6963d47e 100644
--- a/server/models/pod/pod-interface.ts
+++ b/server/models/pod/pod-interface.ts
@@ -1,4 +1,5 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import * as Promise from 'bluebird'
2 3
3// Don't use barrel, import just what we need 4// Don't use barrel, import just what we need
4import { Pod as FormatedPod } from '../../../shared/models/pod.model' 5import { Pod as FormatedPod } from '../../../shared/models/pod.model'
@@ -6,32 +7,23 @@ import { Pod as FormatedPod } from '../../../shared/models/pod.model'
6export namespace PodMethods { 7export namespace PodMethods {
7 export type ToFormatedJSON = (this: PodInstance) => FormatedPod 8 export type ToFormatedJSON = (this: PodInstance) => FormatedPod
8 9
9 export type CountAllCallback = (err: Error, total: number) => void 10 export type CountAll = () => Promise<number>
10 export type CountAll = (callback) => void
11 11
12 export type IncrementScoresCallback = (err: Error) => void 12 export type IncrementScores = (ids: number[], value: number) => Promise<[ number, PodInstance[] ]>
13 export type IncrementScores = (ids: number[], value: number, callback?: IncrementScoresCallback) => void
14 13
15 export type ListCallback = (err: Error, podInstances?: PodInstance[]) => void 14 export type List = () => Promise<PodInstance[]>
16 export type List = (callback: ListCallback) => void
17 15
18 export type ListAllIdsCallback = (err: Error, ids?: number[]) => void 16 export type ListAllIds = (transaction: Sequelize.Transaction) => Promise<number[]>
19 export type ListAllIds = (transaction: Sequelize.Transaction, callback: ListAllIdsCallback) => void
20 17
21 export type ListRandomPodIdsWithRequestCallback = (err: Error, podInstanceIds?: number[]) => void 18 export type ListRandomPodIdsWithRequest = (limit: number, tableWithPods: string, tableWithPodsJoins: string) => Promise<number[]>
22 export type ListRandomPodIdsWithRequest = (limit: number, tableWithPods: string, tableWithPodsJoins: string, callback: ListRandomPodIdsWithRequestCallback) => void
23 19
24 export type ListBadPodsCallback = (err: Error, podInstances?: PodInstance[]) => void 20 export type ListBadPods = () => Promise<PodInstance[]>
25 export type ListBadPods = (callback: ListBadPodsCallback) => void
26 21
27 export type LoadCallback = (err: Error, podInstance: PodInstance) => void 22 export type Load = (id: number) => Promise<PodInstance>
28 export type Load = (id: number, callback: LoadCallback) => void
29 23
30 export type LoadByHostCallback = (err: Error, podInstance: PodInstance) => void 24 export type LoadByHost = (host: string) => Promise<PodInstance>
31 export type LoadByHost = (host: string, callback: LoadByHostCallback) => void
32 25
33 export type RemoveAllCallback = (err: Error) => void 26 export type RemoveAll = () => Promise<number>
34 export type RemoveAll = (callback: RemoveAllCallback) => void
35 27
36 export type UpdatePodsScore = (goodPods: number[], badPods: number[]) => void 28 export type UpdatePodsScore = (goodPods: number[], badPods: number[]) => void
37} 29}
diff --git a/server/models/pod/pod.ts b/server/models/pod/pod.ts
index 4fe7fda1c..9209380fc 100644
--- a/server/models/pod/pod.ts
+++ b/server/models/pod/pod.ts
@@ -1,4 +1,3 @@
1import { each, waterfall } from 'async'
2import { map } from 'lodash' 1import { map } from 'lodash'
3import * as Sequelize from 'sequelize' 2import * as Sequelize from 'sequelize'
4 3
@@ -7,7 +6,6 @@ import { logger, isHostValid } from '../../helpers'
7 6
8import { addMethodsToModel } from '../utils' 7import { addMethodsToModel } from '../utils'
9import { 8import {
10 PodClass,
11 PodInstance, 9 PodInstance,
12 PodAttributes, 10 PodAttributes,
13 11
@@ -118,13 +116,11 @@ function associate (models) {
118 }) 116 })
119} 117}
120 118
121countAll = function (callback: PodMethods.CountAllCallback) { 119countAll = function () {
122 return Pod.count().asCallback(callback) 120 return Pod.count()
123} 121}
124 122
125incrementScores = function (ids: number[], value: number, callback?: PodMethods.IncrementScoresCallback) { 123incrementScores = function (ids: number[], value: number) {
126 if (!callback) callback = function () { /* empty */ }
127
128 const update = { 124 const update = {
129 score: Sequelize.literal('score +' + value) 125 score: Sequelize.literal('score +' + value)
130 } 126 }
@@ -139,33 +135,28 @@ incrementScores = function (ids: number[], value: number, callback?: PodMethods.
139 validate: false 135 validate: false
140 } 136 }
141 137
142 return Pod.update(update, options).asCallback(callback) 138 return Pod.update(update, options)
143} 139}
144 140
145list = function (callback: PodMethods.ListCallback) { 141list = function () {
146 return Pod.findAll().asCallback(callback) 142 return Pod.findAll()
147} 143}
148 144
149listAllIds = function (transaction: Sequelize.Transaction, callback: PodMethods.ListAllIdsCallback) { 145listAllIds = function (transaction: Sequelize.Transaction) {
150 const query: any = { 146 const query: Sequelize.FindOptions = {
151 attributes: [ 'id' ] 147 attributes: [ 'id' ],
148 transaction
152 } 149 }
153 150
154 if (transaction !== null) query.transaction = transaction 151 return Pod.findAll(query).then(pods => {
155 152 return map(pods, 'id')
156 return Pod.findAll(query).asCallback(function (err: Error, pods) {
157 if (err) return callback(err)
158
159 return callback(null, map(pods, 'id'))
160 }) 153 })
161} 154}
162 155
163listRandomPodIdsWithRequest = function (limit: number, tableWithPods: string, tableWithPodsJoins: string, callback: PodMethods.ListRandomPodIdsWithRequestCallback) { 156listRandomPodIdsWithRequest = function (limit: number, tableWithPods: string, tableWithPodsJoins: string) {
164 Pod.count().asCallback(function (err, count) { 157 return Pod.count().then(count => {
165 if (err) return callback(err)
166
167 // Optimization... 158 // Optimization...
168 if (count === 0) return callback(null, []) 159 if (count === 0) return []
169 160
170 let start = Math.floor(Math.random() * count) - limit 161 let start = Math.floor(Math.random() * count) - limit
171 if (start < 0) start = 0 162 if (start < 0) start = 0
@@ -186,56 +177,55 @@ listRandomPodIdsWithRequest = function (limit: number, tableWithPods: string, ta
186 } 177 }
187 } 178 }
188 179
189 return Pod.findAll(query).asCallback(function (err, pods) { 180 return Pod.findAll(query).then(pods => {
190 if (err) return callback(err) 181 return map(pods, 'id')
191
192 return callback(null, map(pods, 'id'))
193 }) 182 })
194 }) 183 })
195} 184}
196 185
197listBadPods = function (callback: PodMethods.ListBadPodsCallback) { 186listBadPods = function () {
198 const query = { 187 const query = {
199 where: { 188 where: {
200 score: { $lte: 0 } 189 score: { $lte: 0 }
201 } 190 }
202 } 191 }
203 192
204 return Pod.findAll(query).asCallback(callback) 193 return Pod.findAll(query)
205} 194}
206 195
207load = function (id: number, callback: PodMethods.LoadCallback) { 196load = function (id: number) {
208 return Pod.findById(id).asCallback(callback) 197 return Pod.findById(id)
209} 198}
210 199
211loadByHost = function (host: string, callback: PodMethods.LoadByHostCallback) { 200loadByHost = function (host: string) {
212 const query = { 201 const query = {
213 where: { 202 where: {
214 host: host 203 host: host
215 } 204 }
216 } 205 }
217 206
218 return Pod.findOne(query).asCallback(callback) 207 return Pod.findOne(query)
219} 208}
220 209
221removeAll = function (callback: PodMethods.RemoveAllCallback) { 210removeAll = function () {
222 return Pod.destroy().asCallback(callback) 211 return Pod.destroy()
223} 212}
224 213
225updatePodsScore = function (goodPods: number[], badPods: number[]) { 214updatePodsScore = function (goodPods: number[], badPods: number[]) {
226 logger.info('Updating %d good pods and %d bad pods scores.', goodPods.length, badPods.length) 215 logger.info('Updating %d good pods and %d bad pods scores.', goodPods.length, badPods.length)
227 216
228 if (goodPods.length !== 0) { 217 if (goodPods.length !== 0) {
229 incrementScores(goodPods, PODS_SCORE.BONUS, function (err) { 218 incrementScores(goodPods, PODS_SCORE.BONUS).catch(err => {
230 if (err) logger.error('Cannot increment scores of good pods.', { error: err }) 219 logger.error('Cannot increment scores of good pods.', { error: err })
231 }) 220 })
232 } 221 }
233 222
234 if (badPods.length !== 0) { 223 if (badPods.length !== 0) {
235 incrementScores(badPods, PODS_SCORE.MALUS, function (err) { 224 incrementScores(badPods, PODS_SCORE.MALUS)
236 if (err) logger.error('Cannot decrement scores of bad pods.', { error: err }) 225 .then(() => removeBadPods())
237 removeBadPods() 226 .catch(err => {
238 }) 227 if (err) logger.error('Cannot decrement scores of bad pods.', { error: err })
228 })
239 } 229 }
240} 230}
241 231
@@ -243,32 +233,19 @@ updatePodsScore = function (goodPods: number[], badPods: number[]) {
243 233
244// Remove pods with a score of 0 (too many requests where they were unreachable) 234// Remove pods with a score of 0 (too many requests where they were unreachable)
245function removeBadPods () { 235function removeBadPods () {
246 waterfall([ 236 return listBadPods()
247 function findBadPods (callback) { 237 .then(pods => {
248 listBadPods(function (err, pods) { 238 const podsRemovePromises = pods.map(pod => pod.destroy())
249 if (err) { 239 return Promise.all(podsRemovePromises).then(() => pods.length)
250 logger.error('Cannot find bad pods.', { error: err }) 240 })
251 return callback(err) 241 .then(numberOfPodsRemoved => {
252 } 242 if (numberOfPodsRemoved) {
253 243 logger.info('Removed %d pods.', numberOfPodsRemoved)
254 return callback(null, pods) 244 } else {
255 }) 245 logger.info('No need to remove bad pods.')
256 }, 246 }
257 247 })
258 function removeTheseBadPods (pods, callback) { 248 .catch(err => {
259 each(pods, function (pod: any, callbackEach) {
260 pod.destroy().asCallback(callbackEach)
261 }, function (err) {
262 return callback(err, pods.length)
263 })
264 }
265 ], function (err, numberOfPodsRemoved) {
266 if (err) {
267 logger.error('Cannot remove bad pods.', { error: err }) 249 logger.error('Cannot remove bad pods.', { error: err })
268 } else if (numberOfPodsRemoved) { 250 })
269 logger.info('Removed %d pods.', numberOfPodsRemoved)
270 } else {
271 logger.info('No need to remove bad pods.')
272 }
273 })
274} 251}
diff --git a/server/models/request/abstract-request-interface.ts b/server/models/request/abstract-request-interface.ts
new file mode 100644
index 000000000..a384f4d27
--- /dev/null
+++ b/server/models/request/abstract-request-interface.ts
@@ -0,0 +1,12 @@
1import * as Promise from 'bluebird'
2
3export interface AbstractRequestClass <T> {
4 countTotalRequests: () => Promise<number>
5 listWithLimitAndRandom: (limitPods: number, limitRequestsPerPod: number) => Promise<T>
6 removeWithEmptyTo: () => Promise<number>
7 removeAll: () => Promise<void>
8}
9
10export interface AbstractRequestToPodClass {
11 removeByRequestIdsAndPod: (ids: number[], podId: number) => Promise<number>
12}
diff --git a/server/models/request/index.ts b/server/models/request/index.ts
index 824c140f5..3dd6aedc2 100644
--- a/server/models/request/index.ts
+++ b/server/models/request/index.ts
@@ -1,3 +1,4 @@
1export * from './abstract-request-interface'
1export * from './request-interface' 2export * from './request-interface'
2export * from './request-to-pod-interface' 3export * from './request-to-pod-interface'
3export * from './request-video-event-interface' 4export * from './request-video-event-interface'
diff --git a/server/models/request/request-interface.ts b/server/models/request/request-interface.ts
index 483850633..7b0ee4df9 100644
--- a/server/models/request/request-interface.ts
+++ b/server/models/request/request-interface.ts
@@ -1,5 +1,7 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import * as Promise from 'bluebird'
2 3
4import { AbstractRequestClass } from './abstract-request-interface'
3import { PodInstance, PodAttributes } from '../pod' 5import { PodInstance, PodAttributes } from '../pod'
4import { RequestEndpoint } from '../../../shared/models/request-scheduler.model' 6import { RequestEndpoint } from '../../../shared/models/request-scheduler.model'
5 7
@@ -11,20 +13,16 @@ export type RequestsGrouped = {
11} 13}
12 14
13export namespace RequestMethods { 15export namespace RequestMethods {
14 export type CountTotalRequestsCallback = (err: Error, total: number) => void 16 export type CountTotalRequests = () => Promise<number>
15 export type CountTotalRequests = (callback: CountTotalRequestsCallback) => void
16 17
17 export type ListWithLimitAndRandomCallback = (err: Error, requestsGrouped?: RequestsGrouped) => void 18 export type ListWithLimitAndRandom = (limitPods: number, limitRequestsPerPod: number) => Promise<RequestsGrouped>
18 export type ListWithLimitAndRandom = (limitPods, limitRequestsPerPod, callback: ListWithLimitAndRandomCallback) => void
19 19
20 export type RemoveWithEmptyToCallback = (err: Error) => void 20 export type RemoveWithEmptyTo = () => Promise<number>
21 export type RemoveWithEmptyTo = (callback: RemoveWithEmptyToCallback) => void
22 21
23 export type RemoveAllCallback = (err: Error) => void 22 export type RemoveAll = () => Promise<void>
24 export type RemoveAll = (callback: RemoveAllCallback) => void
25} 23}
26 24
27export interface RequestClass { 25export interface RequestClass extends AbstractRequestClass<RequestsGrouped> {
28 countTotalRequests: RequestMethods.CountTotalRequests 26 countTotalRequests: RequestMethods.CountTotalRequests
29 listWithLimitAndRandom: RequestMethods.ListWithLimitAndRandom 27 listWithLimitAndRandom: RequestMethods.ListWithLimitAndRandom
30 removeWithEmptyTo: RequestMethods.RemoveWithEmptyTo 28 removeWithEmptyTo: RequestMethods.RemoveWithEmptyTo
diff --git a/server/models/request/request-to-pod-interface.ts b/server/models/request/request-to-pod-interface.ts
index 6d75ca6e5..7ca99f4d4 100644
--- a/server/models/request/request-to-pod-interface.ts
+++ b/server/models/request/request-to-pod-interface.ts
@@ -1,11 +1,13 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import * as Promise from 'bluebird'
3
4import { AbstractRequestToPodClass } from './abstract-request-interface'
2 5
3export namespace RequestToPodMethods { 6export namespace RequestToPodMethods {
4 export type RemoveByRequestIdsAndPodCallback = (err: Error) => void 7 export type RemoveByRequestIdsAndPod = (requestsIds: number[], podId: number) => Promise<number>
5 export type RemoveByRequestIdsAndPod = (requestsIds: number[], podId: number, callback?: RemoveByRequestIdsAndPodCallback) => void
6} 8}
7 9
8export interface RequestToPodClass { 10export interface RequestToPodClass extends AbstractRequestToPodClass {
9 removeByRequestIdsAndPod: RequestToPodMethods.RemoveByRequestIdsAndPod 11 removeByRequestIdsAndPod: RequestToPodMethods.RemoveByRequestIdsAndPod
10} 12}
11 13
diff --git a/server/models/request/request-to-pod.ts b/server/models/request/request-to-pod.ts
index 67331be1d..6678ed290 100644
--- a/server/models/request/request-to-pod.ts
+++ b/server/models/request/request-to-pod.ts
@@ -2,7 +2,6 @@ import * as Sequelize from 'sequelize'
2 2
3import { addMethodsToModel } from '../utils' 3import { addMethodsToModel } from '../utils'
4import { 4import {
5 RequestToPodClass,
6 RequestToPodInstance, 5 RequestToPodInstance,
7 RequestToPodAttributes, 6 RequestToPodAttributes,
8 7
@@ -38,9 +37,7 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
38 37
39// --------------------------------------------------------------------------- 38// ---------------------------------------------------------------------------
40 39
41removeByRequestIdsAndPod = function (requestsIds: number[], podId: number, callback?: RequestToPodMethods.RemoveByRequestIdsAndPodCallback) { 40removeByRequestIdsAndPod = function (requestsIds: number[], podId: number) {
42 if (!callback) callback = function () { /* empty */ }
43
44 const query = { 41 const query = {
45 where: { 42 where: {
46 requestId: { 43 requestId: {
@@ -50,5 +47,5 @@ removeByRequestIdsAndPod = function (requestsIds: number[], podId: number, callb
50 } 47 }
51 } 48 }
52 49
53 RequestToPod.destroy(query).asCallback(callback) 50 return RequestToPod.destroy(query)
54} 51}
diff --git a/server/models/request/request-video-event-interface.ts b/server/models/request/request-video-event-interface.ts
index 3ed03339a..a5032e1b1 100644
--- a/server/models/request/request-video-event-interface.ts
+++ b/server/models/request/request-video-event-interface.ts
@@ -1,5 +1,7 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import * as Promise from 'bluebird'
2 3
4import { AbstractRequestClass, AbstractRequestToPodClass } from './abstract-request-interface'
3import { VideoInstance } from '../video' 5import { VideoInstance } from '../video'
4import { PodInstance } from '../pod' 6import { PodInstance } from '../pod'
5 7
@@ -16,20 +18,16 @@ export type RequestsVideoEventGrouped = {
16} 18}
17 19
18export namespace RequestVideoEventMethods { 20export namespace RequestVideoEventMethods {
19 export type CountTotalRequestsCallback = (err: Error, total: number) => void 21 export type CountTotalRequests = () => Promise<number>
20 export type CountTotalRequests = (callback: CountTotalRequestsCallback) => void
21 22
22 export type ListWithLimitAndRandomCallback = (err: Error, requestsGrouped?: RequestsVideoEventGrouped) => void 23 export type ListWithLimitAndRandom = (limitPods: number, limitRequestsPerPod: number) => Promise<RequestsVideoEventGrouped>
23 export type ListWithLimitAndRandom = (limitPods: number, limitRequestsPerPod: number, callback: ListWithLimitAndRandomCallback) => void
24 24
25 export type RemoveByRequestIdsAndPodCallback = () => void 25 export type RemoveByRequestIdsAndPod = (ids: number[], podId: number) => Promise<number>
26 export type RemoveByRequestIdsAndPod = (ids: number[], podId: number, callback: RemoveByRequestIdsAndPodCallback) => void
27 26
28 export type RemoveAllCallback = () => void 27 export type RemoveAll = () => Promise<void>
29 export type RemoveAll = (callback: RemoveAllCallback) => void
30} 28}
31 29
32export interface RequestVideoEventClass { 30export interface RequestVideoEventClass extends AbstractRequestClass<RequestsVideoEventGrouped>, AbstractRequestToPodClass {
33 countTotalRequests: RequestVideoEventMethods.CountTotalRequests 31 countTotalRequests: RequestVideoEventMethods.CountTotalRequests
34 listWithLimitAndRandom: RequestVideoEventMethods.ListWithLimitAndRandom 32 listWithLimitAndRandom: RequestVideoEventMethods.ListWithLimitAndRandom
35 removeByRequestIdsAndPod: RequestVideoEventMethods.RemoveByRequestIdsAndPod 33 removeByRequestIdsAndPod: RequestVideoEventMethods.RemoveByRequestIdsAndPod
@@ -41,10 +39,12 @@ export interface RequestVideoEventAttributes {
41 count: number 39 count: number
42} 40}
43 41
44export interface RequestVideoEventInstance extends RequestVideoEventClass, RequestVideoEventAttributes, Sequelize.Instance<RequestVideoEventAttributes> { 42export interface RequestVideoEventInstance
43 extends RequestVideoEventClass, RequestVideoEventAttributes, Sequelize.Instance<RequestVideoEventAttributes> {
45 id: number 44 id: number
46 45
47 Video: VideoInstance 46 Video: VideoInstance
48} 47}
49 48
50export interface RequestVideoEventModel extends RequestVideoEventClass, Sequelize.Model<RequestVideoEventInstance, RequestVideoEventAttributes> {} 49export interface RequestVideoEventModel
50 extends RequestVideoEventClass, Sequelize.Model<RequestVideoEventInstance, RequestVideoEventAttributes> {}
diff --git a/server/models/request/request-video-event.ts b/server/models/request/request-video-event.ts
index f552ef50b..90ea15702 100644
--- a/server/models/request/request-video-event.ts
+++ b/server/models/request/request-video-event.ts
@@ -10,7 +10,6 @@ import { REQUEST_VIDEO_EVENT_TYPES } from '../../initializers'
10import { isVideoEventCountValid } from '../../helpers' 10import { isVideoEventCountValid } from '../../helpers'
11import { addMethodsToModel } from '../utils' 11import { addMethodsToModel } from '../utils'
12import { 12import {
13 RequestVideoEventClass,
14 RequestVideoEventInstance, 13 RequestVideoEventInstance,
15 RequestVideoEventAttributes, 14 RequestVideoEventAttributes,
16 15
@@ -77,23 +76,21 @@ function associate (models) {
77 }) 76 })
78} 77}
79 78
80countTotalRequests = function (callback: RequestVideoEventMethods.CountTotalRequestsCallback) { 79countTotalRequests = function () {
81 const query = {} 80 const query = {}
82 return RequestVideoEvent.count(query).asCallback(callback) 81 return RequestVideoEvent.count(query)
83} 82}
84 83
85listWithLimitAndRandom = function (limitPods: number, limitRequestsPerPod: number, callback: RequestVideoEventMethods.ListWithLimitAndRandomCallback) { 84listWithLimitAndRandom = function (limitPods: number, limitRequestsPerPod: number) {
86 const Pod = db.Pod 85 const Pod = db.Pod
87 86
88 // We make a join between videos and authors to find the podId of our video event requests 87 // We make a join between videos and authors to find the podId of our video event requests
89 const podJoins = 'INNER JOIN "Videos" ON "Videos"."authorId" = "Authors"."id" ' + 88 const podJoins = 'INNER JOIN "Videos" ON "Videos"."authorId" = "Authors"."id" ' +
90 'INNER JOIN "RequestVideoEvents" ON "RequestVideoEvents"."videoId" = "Videos"."id"' 89 'INNER JOIN "RequestVideoEvents" ON "RequestVideoEvents"."videoId" = "Videos"."id"'
91 90
92 Pod.listRandomPodIdsWithRequest(limitPods, 'Authors', podJoins, function (err, podIds) { 91 return Pod.listRandomPodIdsWithRequest(limitPods, 'Authors', podJoins).then(podIds => {
93 if (err) return callback(err)
94
95 // We don't have friends that have requests 92 // We don't have friends that have requests
96 if (podIds.length === 0) return callback(null, []) 93 if (podIds.length === 0) return []
97 94
98 const query = { 95 const query = {
99 order: [ 96 order: [
@@ -121,16 +118,14 @@ listWithLimitAndRandom = function (limitPods: number, limitRequestsPerPod: numbe
121 ] 118 ]
122 } 119 }
123 120
124 RequestVideoEvent.findAll(query).asCallback(function (err, requests) { 121 return RequestVideoEvent.findAll(query).then(requests => {
125 if (err) return callback(err)
126
127 const requestsGrouped = groupAndTruncateRequests(requests, limitRequestsPerPod) 122 const requestsGrouped = groupAndTruncateRequests(requests, limitRequestsPerPod)
128 return callback(err, requestsGrouped) 123 return requestsGrouped
129 }) 124 })
130 }) 125 })
131} 126}
132 127
133removeByRequestIdsAndPod = function (ids: number[], podId: number, callback: RequestVideoEventMethods.RemoveByRequestIdsAndPodCallback) { 128removeByRequestIdsAndPod = function (ids: number[], podId: number) {
134 const query = { 129 const query = {
135 where: { 130 where: {
136 id: { 131 id: {
@@ -152,12 +147,12 @@ removeByRequestIdsAndPod = function (ids: number[], podId: number, callback: Req
152 ] 147 ]
153 } 148 }
154 149
155 RequestVideoEvent.destroy(query).asCallback(callback) 150 return RequestVideoEvent.destroy(query)
156} 151}
157 152
158removeAll = function (callback: RequestVideoEventMethods.RemoveAllCallback) { 153removeAll = function () {
159 // Delete all requests 154 // Delete all requests
160 RequestVideoEvent.truncate({ cascade: true }).asCallback(callback) 155 return RequestVideoEvent.truncate({ cascade: true })
161} 156}
162 157
163// --------------------------------------------------------------------------- 158// ---------------------------------------------------------------------------
diff --git a/server/models/request/request-video-qadu-interface.ts b/server/models/request/request-video-qadu-interface.ts
index 805771cda..9a172a4d4 100644
--- a/server/models/request/request-video-qadu-interface.ts
+++ b/server/models/request/request-video-qadu-interface.ts
@@ -1,5 +1,7 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import * as Promise from 'bluebird'
2 3
4import { AbstractRequestClass, AbstractRequestToPodClass } from './abstract-request-interface'
3import { VideoInstance } from '../video' 5import { VideoInstance } from '../video'
4import { PodInstance } from '../pod' 6import { PodInstance } from '../pod'
5 7
@@ -14,20 +16,16 @@ export type RequestsVideoQaduGrouped = {
14} 16}
15 17
16export namespace RequestVideoQaduMethods { 18export namespace RequestVideoQaduMethods {
17 export type CountTotalRequestsCallback = (err: Error, total: number) => void 19 export type CountTotalRequests = () => Promise<number>
18 export type CountTotalRequests = (callback: CountTotalRequestsCallback) => void
19 20
20 export type ListWithLimitAndRandomCallback = (err: Error, requestsGrouped?: RequestsVideoQaduGrouped) => void 21 export type ListWithLimitAndRandom = (limitPods: number, limitRequestsPerPod: number) => Promise<RequestsVideoQaduGrouped>
21 export type ListWithLimitAndRandom = (limitPods: number, limitRequestsPerPod: number, callback: ListWithLimitAndRandomCallback) => void
22 22
23 export type RemoveByRequestIdsAndPodCallback = () => void 23 export type RemoveByRequestIdsAndPod = (ids: number[], podId: number) => Promise<number>
24 export type RemoveByRequestIdsAndPod = (ids: number[], podId: number, callback: RemoveByRequestIdsAndPodCallback) => void
25 24
26 export type RemoveAllCallback = () => void 25 export type RemoveAll = () => Promise<void>
27 export type RemoveAll = (callback: RemoveAllCallback) => void
28} 26}
29 27
30export interface RequestVideoQaduClass { 28export interface RequestVideoQaduClass extends AbstractRequestClass<RequestsVideoQaduGrouped>, AbstractRequestToPodClass {
31 countTotalRequests: RequestVideoQaduMethods.CountTotalRequests 29 countTotalRequests: RequestVideoQaduMethods.CountTotalRequests
32 listWithLimitAndRandom: RequestVideoQaduMethods.ListWithLimitAndRandom 30 listWithLimitAndRandom: RequestVideoQaduMethods.ListWithLimitAndRandom
33 removeByRequestIdsAndPod: RequestVideoQaduMethods.RemoveByRequestIdsAndPod 31 removeByRequestIdsAndPod: RequestVideoQaduMethods.RemoveByRequestIdsAndPod
@@ -38,11 +36,13 @@ export interface RequestVideoQaduAttributes {
38 type: RequestVideoQaduType 36 type: RequestVideoQaduType
39} 37}
40 38
41export interface RequestVideoQaduInstance extends RequestVideoQaduClass, RequestVideoQaduAttributes, Sequelize.Instance<RequestVideoQaduAttributes> { 39export interface RequestVideoQaduInstance
40 extends RequestVideoQaduClass, RequestVideoQaduAttributes, Sequelize.Instance<RequestVideoQaduAttributes> {
42 id: number 41 id: number
43 42
44 Pod: PodInstance 43 Pod: PodInstance
45 Video: VideoInstance 44 Video: VideoInstance
46} 45}
47 46
48export interface RequestVideoQaduModel extends RequestVideoQaduClass, Sequelize.Model<RequestVideoQaduInstance, RequestVideoQaduAttributes> {} 47export interface RequestVideoQaduModel
48 extends RequestVideoQaduClass, Sequelize.Model<RequestVideoQaduInstance, RequestVideoQaduAttributes> {}
diff --git a/server/models/request/request-video-qadu.ts b/server/models/request/request-video-qadu.ts
index da62239f5..74e28f129 100644
--- a/server/models/request/request-video-qadu.ts
+++ b/server/models/request/request-video-qadu.ts
@@ -16,7 +16,6 @@ import { database as db } from '../../initializers/database'
16import { REQUEST_VIDEO_QADU_TYPES } from '../../initializers' 16import { REQUEST_VIDEO_QADU_TYPES } from '../../initializers'
17import { addMethodsToModel } from '../utils' 17import { addMethodsToModel } from '../utils'
18import { 18import {
19 RequestVideoQaduClass,
20 RequestVideoQaduInstance, 19 RequestVideoQaduInstance,
21 RequestVideoQaduAttributes, 20 RequestVideoQaduAttributes,
22 21
@@ -83,20 +82,18 @@ function associate (models) {
83 }) 82 })
84} 83}
85 84
86countTotalRequests = function (callback: RequestVideoQaduMethods.CountTotalRequestsCallback) { 85countTotalRequests = function () {
87 const query = {} 86 const query = {}
88 return RequestVideoQadu.count(query).asCallback(callback) 87 return RequestVideoQadu.count(query)
89} 88}
90 89
91listWithLimitAndRandom = function (limitPods: number, limitRequestsPerPod: number, callback: RequestVideoQaduMethods.ListWithLimitAndRandomCallback) { 90listWithLimitAndRandom = function (limitPods: number, limitRequestsPerPod: number) {
92 const Pod = db.Pod 91 const Pod = db.Pod
93 const tableJoin = '' 92 const tableJoin = ''
94 93
95 Pod.listRandomPodIdsWithRequest(limitPods, 'RequestVideoQadus', tableJoin, function (err, podIds) { 94 return Pod.listRandomPodIdsWithRequest(limitPods, 'RequestVideoQadus', tableJoin).then(podIds => {
96 if (err) return callback(err)
97
98 // We don't have friends that have requests 95 // We don't have friends that have requests
99 if (podIds.length === 0) return callback(null, []) 96 if (podIds.length === 0) return []
100 97
101 const query = { 98 const query = {
102 include: [ 99 include: [
@@ -114,16 +111,14 @@ listWithLimitAndRandom = function (limitPods: number, limitRequestsPerPod: numbe
114 ] 111 ]
115 } 112 }
116 113
117 RequestVideoQadu.findAll(query).asCallback(function (err, requests) { 114 return RequestVideoQadu.findAll(query).then(requests => {
118 if (err) return callback(err)
119
120 const requestsGrouped = groupAndTruncateRequests(requests, limitRequestsPerPod) 115 const requestsGrouped = groupAndTruncateRequests(requests, limitRequestsPerPod)
121 return callback(err, requestsGrouped) 116 return requestsGrouped
122 }) 117 })
123 }) 118 })
124} 119}
125 120
126removeByRequestIdsAndPod = function (ids: number[], podId: number, callback: RequestVideoQaduMethods.RemoveByRequestIdsAndPodCallback) { 121removeByRequestIdsAndPod = function (ids: number[], podId: number) {
127 const query = { 122 const query = {
128 where: { 123 where: {
129 id: { 124 id: {
@@ -133,12 +128,12 @@ removeByRequestIdsAndPod = function (ids: number[], podId: number, callback: Req
133 } 128 }
134 } 129 }
135 130
136 RequestVideoQadu.destroy(query).asCallback(callback) 131 return RequestVideoQadu.destroy(query)
137} 132}
138 133
139removeAll = function (callback: RequestVideoQaduMethods.RemoveAllCallback) { 134removeAll = function () {
140 // Delete all requests 135 // Delete all requests
141 RequestVideoQadu.truncate({ cascade: true }).asCallback(callback) 136 return RequestVideoQadu.truncate({ cascade: true })
142} 137}
143 138
144// --------------------------------------------------------------------------- 139// ---------------------------------------------------------------------------
diff --git a/server/models/request/request.ts b/server/models/request/request.ts
index 66e7da845..c3ce2cd4e 100644
--- a/server/models/request/request.ts
+++ b/server/models/request/request.ts
@@ -5,7 +5,6 @@ import { database as db } from '../../initializers/database'
5import { REQUEST_ENDPOINTS } from '../../initializers' 5import { REQUEST_ENDPOINTS } from '../../initializers'
6import { addMethodsToModel } from '../utils' 6import { addMethodsToModel } from '../utils'
7import { 7import {
8 RequestClass,
9 RequestInstance, 8 RequestInstance,
10 RequestAttributes, 9 RequestAttributes,
11 10
@@ -60,25 +59,23 @@ function associate (models) {
60 }) 59 })
61} 60}
62 61
63countTotalRequests = function (callback: RequestMethods.CountTotalRequestsCallback) { 62countTotalRequests = function () {
64 // We need to include Pod because there are no cascade delete when a pod is removed 63 // We need to include Pod because there are no cascade delete when a pod is removed
65 // So we could count requests that do not have existing pod anymore 64 // So we could count requests that do not have existing pod anymore
66 const query = { 65 const query = {
67 include: [ Request['sequelize'].models.Pod ] 66 include: [ Request['sequelize'].models.Pod ]
68 } 67 }
69 68
70 return Request.count(query).asCallback(callback) 69 return Request.count(query)
71} 70}
72 71
73listWithLimitAndRandom = function (limitPods: number, limitRequestsPerPod: number, callback: RequestMethods.ListWithLimitAndRandomCallback) { 72listWithLimitAndRandom = function (limitPods: number, limitRequestsPerPod: number) {
74 const Pod = db.Pod 73 const Pod = db.Pod
75 const tableJoin = '' 74 const tableJoin = ''
76 75
77 Pod.listRandomPodIdsWithRequest(limitPods, 'RequestToPods', '', function (err, podIds) { 76 return Pod.listRandomPodIdsWithRequest(limitPods, 'RequestToPods', tableJoin).then(podIds => {
78 if (err) return callback(err)
79
80 // We don't have friends that have requests 77 // We don't have friends that have requests
81 if (podIds.length === 0) return callback(null, []) 78 if (podIds.length === 0) return []
82 79
83 // The first x requests of these pods 80 // The first x requests of these pods
84 // It is very important to sort by id ASC to keep the requests order! 81 // It is very important to sort by id ASC to keep the requests order!
@@ -98,23 +95,20 @@ listWithLimitAndRandom = function (limitPods: number, limitRequestsPerPod: numbe
98 ] 95 ]
99 } 96 }
100 97
101 Request.findAll(query).asCallback(function (err, requests) { 98 return Request.findAll(query).then(requests => {
102 if (err) return callback(err)
103 99
104 const requestsGrouped = groupAndTruncateRequests(requests, limitRequestsPerPod) 100 const requestsGrouped = groupAndTruncateRequests(requests, limitRequestsPerPod)
105 return callback(err, requestsGrouped) 101 return requestsGrouped
106 }) 102 })
107 }) 103 })
108} 104}
109 105
110removeAll = function (callback: RequestMethods.RemoveAllCallback) { 106removeAll = function () {
111 // Delete all requests 107 // Delete all requests
112 Request.truncate({ cascade: true }).asCallback(callback) 108 return Request.truncate({ cascade: true })
113} 109}
114 110
115removeWithEmptyTo = function (callback?: RequestMethods.RemoveWithEmptyToCallback) { 111removeWithEmptyTo = function () {
116 if (!callback) callback = function () { /* empty */ }
117
118 const query = { 112 const query = {
119 where: { 113 where: {
120 id: { 114 id: {
@@ -125,7 +119,7 @@ removeWithEmptyTo = function (callback?: RequestMethods.RemoveWithEmptyToCallbac
125 } 119 }
126 } 120 }
127 121
128 Request.destroy(query).asCallback(callback) 122 return Request.destroy(query)
129} 123}
130 124
131// --------------------------------------------------------------------------- 125// ---------------------------------------------------------------------------
diff --git a/server/models/user/user-interface.ts b/server/models/user/user-interface.ts
index 48c67678b..f743945f8 100644
--- a/server/models/user/user-interface.ts
+++ b/server/models/user/user-interface.ts
@@ -1,35 +1,29 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import * as Bluebird from 'bluebird' 2import * as Promise from 'bluebird'
3 3
4// Don't use barrel, import just what we need 4// Don't use barrel, import just what we need
5import { UserRole, User as FormatedUser } from '../../../shared/models/user.model' 5import { UserRole, User as FormatedUser } from '../../../shared/models/user.model'
6import { ResultList } from '../../../shared/models/result-list.model'
6 7
7export namespace UserMethods { 8export namespace UserMethods {
8 export type IsPasswordMatchCallback = (err: Error, same: boolean) => void 9 export type IsPasswordMatch = (this: UserInstance, password: string) => Promise<boolean>
9 export type IsPasswordMatch = (this: UserInstance, password: string, callback: IsPasswordMatchCallback) => void
10 10
11 export type ToFormatedJSON = (this: UserInstance) => FormatedUser 11 export type ToFormatedJSON = (this: UserInstance) => FormatedUser
12 export type IsAdmin = (this: UserInstance) => boolean 12 export type IsAdmin = (this: UserInstance) => boolean
13 13
14 export type CountTotalCallback = (err: Error, total: number) => void 14 export type CountTotal = () => Promise<number>
15 export type CountTotal = (callback: CountTotalCallback) => void
16 15
17 export type GetByUsername = (username: string) => Bluebird<UserInstance> 16 export type GetByUsername = (username: string) => Promise<UserInstance>
18 17
19 export type ListCallback = (err: Error, userInstances: UserInstance[]) => void 18 export type List = () => Promise<UserInstance[]>
20 export type List = (callback: ListCallback) => void
21 19
22 export type ListForApiCallback = (err: Error, userInstances?: UserInstance[], total?: number) => void 20 export type ListForApi = (start: number, count: number, sort: string) => Promise< ResultList<UserInstance> >
23 export type ListForApi = (start: number, count: number, sort: string, callback: ListForApiCallback) => void
24 21
25 export type LoadByIdCallback = (err: Error, userInstance: UserInstance) => void 22 export type LoadById = (id: number) => Promise<UserInstance>
26 export type LoadById = (id: number, callback: LoadByIdCallback) => void
27 23
28 export type LoadByUsernameCallback = (err: Error, userInstance: UserInstance) => void 24 export type LoadByUsername = (username: string) => Promise<UserInstance>
29 export type LoadByUsername = (username: string, callback: LoadByUsernameCallback) => void
30 25
31 export type LoadByUsernameOrEmailCallback = (err: Error, userInstance: UserInstance) => void 26 export type LoadByUsernameOrEmail = (username: string, email: string) => Promise<UserInstance>
32 export type LoadByUsernameOrEmail = (username: string, email: string, callback: LoadByUsernameOrEmailCallback) => void
33} 27}
34 28
35export interface UserClass { 29export interface UserClass {
diff --git a/server/models/user/user-video-rate-interface.ts b/server/models/user/user-video-rate-interface.ts
index a726639b1..e0b65a13d 100644
--- a/server/models/user/user-video-rate-interface.ts
+++ b/server/models/user/user-video-rate-interface.ts
@@ -1,10 +1,10 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import * as Promise from 'bluebird'
2 3
3import { VideoRateType } from '../../../shared/models/user-video-rate.model' 4import { VideoRateType } from '../../../shared/models/user-video-rate.model'
4 5
5export namespace UserVideoRateMethods { 6export namespace UserVideoRateMethods {
6 export type LoadCallback = (err: Error, userVideoRateInstance: UserVideoRateInstance) => void 7 export type Load = (userId: number, videoId: string, transaction: Sequelize.Transaction) => Promise<UserVideoRateInstance>
7 export type Load = (userId: number, videoId: string, transaction: Sequelize.Transaction, callback: LoadCallback) => void
8} 8}
9 9
10export interface UserVideoRateClass { 10export interface UserVideoRateClass {
diff --git a/server/models/user/user-video-rate.ts b/server/models/user/user-video-rate.ts
index 4bdd35bc9..37d0222cf 100644
--- a/server/models/user/user-video-rate.ts
+++ b/server/models/user/user-video-rate.ts
@@ -8,7 +8,6 @@ import { VIDEO_RATE_TYPES } from '../../initializers'
8 8
9import { addMethodsToModel } from '../utils' 9import { addMethodsToModel } from '../utils'
10import { 10import {
11 UserVideoRateClass,
12 UserVideoRateInstance, 11 UserVideoRateInstance,
13 UserVideoRateAttributes, 12 UserVideoRateAttributes,
14 13
@@ -66,7 +65,7 @@ function associate (models) {
66 }) 65 })
67} 66}
68 67
69load = function (userId: number, videoId: string, transaction: Sequelize.Transaction, callback: UserVideoRateMethods.LoadCallback) { 68load = function (userId: number, videoId: string, transaction: Sequelize.Transaction) {
70 const options: Sequelize.FindOptions = { 69 const options: Sequelize.FindOptions = {
71 where: { 70 where: {
72 userId, 71 userId,
@@ -75,5 +74,5 @@ load = function (userId: number, videoId: string, transaction: Sequelize.Transac
75 } 74 }
76 if (transaction) options.transaction = transaction 75 if (transaction) options.transaction = transaction
77 76
78 return UserVideoRate.findOne(options).asCallback(callback) 77 return UserVideoRate.findOne(options)
79} 78}
diff --git a/server/models/user/user.ts b/server/models/user/user.ts
index 6b2410259..5ff81e741 100644
--- a/server/models/user/user.ts
+++ b/server/models/user/user.ts
@@ -13,7 +13,6 @@ import {
13 13
14import { addMethodsToModel } from '../utils' 14import { addMethodsToModel } from '../utils'
15import { 15import {
16 UserClass,
17 UserInstance, 16 UserInstance,
18 UserAttributes, 17 UserAttributes,
19 18
@@ -118,21 +117,16 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
118} 117}
119 118
120function beforeCreateOrUpdate (user: UserInstance) { 119function beforeCreateOrUpdate (user: UserInstance) {
121 return new Promise(function (resolve, reject) { 120 return cryptPassword(user.password).then(hash => {
122 cryptPassword(user.password, function (err, hash) { 121 user.password = hash
123 if (err) return reject(err) 122 return undefined
124
125 user.password = hash
126
127 return resolve()
128 })
129 }) 123 })
130} 124}
131 125
132// ------------------------------ METHODS ------------------------------ 126// ------------------------------ METHODS ------------------------------
133 127
134isPasswordMatch = function (this: UserInstance, password: string, callback: UserMethods.IsPasswordMatchCallback) { 128isPasswordMatch = function (this: UserInstance, password: string) {
135 return comparePassword(password, this.password, callback) 129 return comparePassword(password, this.password)
136} 130}
137 131
138toFormatedJSON = function (this: UserInstance) { 132toFormatedJSON = function (this: UserInstance) {
@@ -164,8 +158,8 @@ function associate (models) {
164 }) 158 })
165} 159}
166 160
167countTotal = function (callback: UserMethods.CountTotalCallback) { 161countTotal = function () {
168 return this.count().asCallback(callback) 162 return this.count()
169} 163}
170 164
171getByUsername = function (username: string) { 165getByUsername = function (username: string) {
@@ -178,44 +172,45 @@ getByUsername = function (username: string) {
178 return User.findOne(query) 172 return User.findOne(query)
179} 173}
180 174
181list = function (callback: UserMethods.ListCallback) { 175list = function () {
182 return User.find().asCallback(callback) 176 return User.findAll()
183} 177}
184 178
185listForApi = function (start: number, count: number, sort: string, callback: UserMethods.ListForApiCallback) { 179listForApi = function (start: number, count: number, sort: string) {
186 const query = { 180 const query = {
187 offset: start, 181 offset: start,
188 limit: count, 182 limit: count,
189 order: [ getSort(sort) ] 183 order: [ getSort(sort) ]
190 } 184 }
191 185
192 return User.findAndCountAll(query).asCallback(function (err, result) { 186 return User.findAndCountAll(query).then(({ rows, count }) => {
193 if (err) return callback(err) 187 return {
194 188 data: rows,
195 return callback(null, result.rows, result.count) 189 total: count
190 }
196 }) 191 })
197} 192}
198 193
199loadById = function (id: number, callback: UserMethods.LoadByIdCallback) { 194loadById = function (id: number) {
200 return User.findById(id).asCallback(callback) 195 return User.findById(id)
201} 196}
202 197
203loadByUsername = function (username: string, callback: UserMethods.LoadByUsernameCallback) { 198loadByUsername = function (username: string) {
204 const query = { 199 const query = {
205 where: { 200 where: {
206 username: username 201 username: username
207 } 202 }
208 } 203 }
209 204
210 return User.findOne(query).asCallback(callback) 205 return User.findOne(query)
211} 206}
212 207
213loadByUsernameOrEmail = function (username: string, email: string, callback: UserMethods.LoadByUsernameOrEmailCallback) { 208loadByUsernameOrEmail = function (username: string, email: string) {
214 const query = { 209 const query = {
215 where: { 210 where: {
216 $or: [ { username }, { email } ] 211 $or: [ { username }, { email } ]
217 } 212 }
218 } 213 }
219 214
220 return User.findOne(query).asCallback(callback) 215 return User.findOne(query)
221} 216}
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}