aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--server/controllers/activitypub/videos.ts26
-rw-r--r--server/controllers/api/users.ts8
-rw-r--r--server/controllers/api/videos/channel.ts20
-rw-r--r--server/controllers/api/videos/index.ts2
-rw-r--r--server/controllers/client.ts4
-rw-r--r--server/controllers/services.ts2
-rw-r--r--server/helpers/custom-validators/index.ts1
-rw-r--r--server/helpers/custom-validators/video-accounts.ts45
-rw-r--r--server/helpers/custom-validators/video-channels.ts4
-rw-r--r--server/helpers/custom-validators/videos.ts4
-rw-r--r--server/initializers/constants.ts2
-rw-r--r--server/initializers/database.ts4
-rw-r--r--server/initializers/installer.ts4
-rw-r--r--server/lib/cache/videos-preview-cache.ts2
-rw-r--r--server/lib/friends.ts567
-rw-r--r--server/lib/jobs/transcoding-job-scheduler/video-file-optimizer-handler.ts4
-rw-r--r--server/lib/jobs/transcoding-job-scheduler/video-file-transcoder-handler.ts4
-rw-r--r--server/lib/video-channel.ts2
-rw-r--r--server/middlewares/validators/video-channels.ts20
-rw-r--r--server/middlewares/validators/videos.ts14
-rw-r--r--server/models/account/user.ts4
-rw-r--r--server/models/index.ts1
-rw-r--r--server/models/oauth/oauth-token.ts4
-rw-r--r--server/models/request/abstract-request-interface.ts12
-rw-r--r--server/models/request/index.ts5
-rw-r--r--server/models/request/request-interface.ts46
-rw-r--r--server/models/request/request-to-pod-interface.ts23
-rw-r--r--server/models/request/request-to-pod.ts51
-rw-r--r--server/models/request/request-video-event-interface.ts50
-rw-r--r--server/models/request/request-video-event.ts191
-rw-r--r--server/models/request/request-video-qadu-interface.ts48
-rw-r--r--server/models/request/request-video-qadu.ts159
-rw-r--r--server/models/request/request.ts144
-rw-r--r--server/models/video/index.ts1
34 files changed, 111 insertions, 1367 deletions
diff --git a/server/controllers/activitypub/videos.ts b/server/controllers/activitypub/videos.ts
index 9a1868ff7..a9b31bf75 100644
--- a/server/controllers/activitypub/videos.ts
+++ b/server/controllers/activitypub/videos.ts
@@ -32,8 +32,8 @@
32// RemoteVideoChannelCreateData, 32// RemoteVideoChannelCreateData,
33// RemoteVideoChannelUpdateData, 33// RemoteVideoChannelUpdateData,
34// RemoteVideoChannelRemoveData, 34// RemoteVideoChannelRemoveData,
35// RemoteVideoAuthorRemoveData, 35// RemoteVideoAccountRemoveData,
36// RemoteVideoAuthorCreateData 36// RemoteVideoAccountCreateData
37// } from '../../../../shared' 37// } from '../../../../shared'
38// import { VideoInstance } from '../../../models/video/video-interface' 38// import { VideoInstance } from '../../../models/video/video-interface'
39// 39//
@@ -49,8 +49,8 @@
49// functionsHash[ENDPOINT_ACTIONS.UPDATE_CHANNEL] = updateRemoteVideoChannelRetryWrapper 49// functionsHash[ENDPOINT_ACTIONS.UPDATE_CHANNEL] = updateRemoteVideoChannelRetryWrapper
50// functionsHash[ENDPOINT_ACTIONS.REMOVE_CHANNEL] = removeRemoteVideoChannelRetryWrapper 50// functionsHash[ENDPOINT_ACTIONS.REMOVE_CHANNEL] = removeRemoteVideoChannelRetryWrapper
51// functionsHash[ENDPOINT_ACTIONS.REPORT_ABUSE] = reportAbuseRemoteVideoRetryWrapper 51// functionsHash[ENDPOINT_ACTIONS.REPORT_ABUSE] = reportAbuseRemoteVideoRetryWrapper
52// functionsHash[ENDPOINT_ACTIONS.ADD_AUTHOR] = addRemoteVideoAuthorRetryWrapper 52// functionsHash[ENDPOINT_ACTIONS.ADD_ACCOUNT] = addRemoteVideoAccountRetryWrapper
53// functionsHash[ENDPOINT_ACTIONS.REMOVE_AUTHOR] = removeRemoteVideoAuthorRetryWrapper 53// functionsHash[ENDPOINT_ACTIONS.REMOVE_ACCOUNT] = removeRemoteVideoAccountRetryWrapper
54// 54//
55// const remoteVideosRouter = express.Router() 55// const remoteVideosRouter = express.Router()
56// 56//
@@ -245,24 +245,24 @@
245// logger.info('Remote video with uuid %s removed.', videoToRemoveData.uuid) 245// logger.info('Remote video with uuid %s removed.', videoToRemoveData.uuid)
246// } 246// }
247// 247//
248// async function removeRemoteVideoAuthorRetryWrapper (authorAttributesToRemove: RemoteVideoAuthorRemoveData, fromPod: PodInstance) { 248// async function removeRemoteVideoAccountRetryWrapper (accountAttributesToRemove: RemoteVideoAccountRemoveData, fromPod: PodInstance) {
249// const options = { 249// const options = {
250// arguments: [ authorAttributesToRemove, fromPod ], 250// arguments: [ accountAttributesToRemove, fromPod ],
251// errorMessage: 'Cannot remove the remote video author with many retries.' 251// errorMessage: 'Cannot remove the remote video account with many retries.'
252// } 252// }
253// 253//
254// await retryTransactionWrapper(removeRemoteVideoAuthor, options) 254// await retryTransactionWrapper(removeRemoteVideoAccount, options)
255// } 255// }
256// 256//
257// async function removeRemoteVideoAuthor (authorAttributesToRemove: RemoteVideoAuthorRemoveData, fromPod: PodInstance) { 257// async function removeRemoteVideoAccount (accountAttributesToRemove: RemoteVideoAccountRemoveData, fromPod: PodInstance) {
258// logger.debug('Removing remote video author "%s".', authorAttributesToRemove.uuid) 258// logger.debug('Removing remote video account "%s".', accountAttributesToRemove.uuid)
259// 259//
260// await db.sequelize.transaction(async t => { 260// await db.sequelize.transaction(async t => {
261// const videoAuthor = await db.Author.loadAuthorByPodAndUUID(authorAttributesToRemove.uuid, fromPod.id, t) 261// const videoAccount = await db.Account.loadAccountByPodAndUUID(accountAttributesToRemove.uuid, fromPod.id, t)
262// await videoAuthor.destroy({ transaction: t }) 262// await videoAccount.destroy({ transaction: t })
263// }) 263// })
264// 264//
265// logger.info('Remote video author with uuid %s removed.', authorAttributesToRemove.uuid) 265// logger.info('Remote video account with uuid %s removed.', accountAttributesToRemove.uuid)
266// } 266// }
267// 267//
268// async function removeRemoteVideoChannelRetryWrapper (videoChannelAttributesToRemove: RemoteVideoChannelRemoveData, fromPod: PodInstance) { 268// async function removeRemoteVideoChannelRetryWrapper (videoChannelAttributesToRemove: RemoteVideoChannelRemoveData, fromPod: PodInstance) {
diff --git a/server/controllers/api/users.ts b/server/controllers/api/users.ts
index bacfc4552..9ec6feb57 100644
--- a/server/controllers/api/users.ts
+++ b/server/controllers/api/users.ts
@@ -28,7 +28,7 @@ import {
28 UserRole, 28 UserRole,
29 UserRight 29 UserRight
30} from '../../../shared' 30} from '../../../shared'
31import { createUserAuthorAndChannel } from '../../lib' 31import { createUserAccountAndChannel } from '../../lib'
32import { UserInstance } from '../../models' 32import { UserInstance } from '../../models'
33import { videosSortValidator } from '../../middlewares/validators/sort' 33import { videosSortValidator } from '../../middlewares/validators/sort'
34import { setVideosSort } from '../../middlewares/sort' 34import { setVideosSort } from '../../middlewares/sort'
@@ -142,9 +142,9 @@ async function createUser (req: express.Request, res: express.Response, next: ex
142 videoQuota: body.videoQuota 142 videoQuota: body.videoQuota
143 }) 143 })
144 144
145 await createUserAuthorAndChannel(user) 145 await createUserAccountAndChannel(user)
146 146
147 logger.info('User %s with its channel and author created.', body.username) 147 logger.info('User %s with its channel and account created.', body.username)
148} 148}
149 149
150async function registerUser (req: express.Request, res: express.Response, next: express.NextFunction) { 150async function registerUser (req: express.Request, res: express.Response, next: express.NextFunction) {
@@ -159,7 +159,7 @@ async function registerUser (req: express.Request, res: express.Response, next:
159 videoQuota: CONFIG.USER.VIDEO_QUOTA 159 videoQuota: CONFIG.USER.VIDEO_QUOTA
160 }) 160 })
161 161
162 await createUserAuthorAndChannel(user) 162 await createUserAccountAndChannel(user)
163 return res.type('json').status(204).end() 163 return res.type('json').status(204).end()
164} 164}
165 165
diff --git a/server/controllers/api/videos/channel.ts b/server/controllers/api/videos/channel.ts
index ab54eedee..4d1f03903 100644
--- a/server/controllers/api/videos/channel.ts
+++ b/server/controllers/api/videos/channel.ts
@@ -17,14 +17,14 @@ import {
17 videoChannelsRemoveValidator, 17 videoChannelsRemoveValidator,
18 videoChannelGetValidator, 18 videoChannelGetValidator,
19 videoChannelsUpdateValidator, 19 videoChannelsUpdateValidator,
20 listVideoAuthorChannelsValidator, 20 listVideoAccountChannelsValidator,
21 asyncMiddleware 21 asyncMiddleware
22} from '../../../middlewares' 22} from '../../../middlewares'
23import { 23import {
24 createVideoChannel, 24 createVideoChannel,
25 updateVideoChannelToFriends 25 updateVideoChannelToFriends
26} from '../../../lib' 26} from '../../../lib'
27import { VideoChannelInstance, AuthorInstance } from '../../../models' 27import { VideoChannelInstance, AccountInstance } from '../../../models'
28import { VideoChannelCreate, VideoChannelUpdate } from '../../../../shared' 28import { VideoChannelCreate, VideoChannelUpdate } from '../../../../shared'
29 29
30const videoChannelRouter = express.Router() 30const videoChannelRouter = express.Router()
@@ -37,9 +37,9 @@ videoChannelRouter.get('/channels',
37 asyncMiddleware(listVideoChannels) 37 asyncMiddleware(listVideoChannels)
38) 38)
39 39
40videoChannelRouter.get('/authors/:authorId/channels', 40videoChannelRouter.get('/accounts/:accountId/channels',
41 listVideoAuthorChannelsValidator, 41 listVideoAccountChannelsValidator,
42 asyncMiddleware(listVideoAuthorChannels) 42 asyncMiddleware(listVideoAccountChannels)
43) 43)
44 44
45videoChannelRouter.post('/channels', 45videoChannelRouter.post('/channels',
@@ -79,8 +79,8 @@ async function listVideoChannels (req: express.Request, res: express.Response, n
79 return res.json(getFormattedObjects(resultList.data, resultList.total)) 79 return res.json(getFormattedObjects(resultList.data, resultList.total))
80} 80}
81 81
82async function listVideoAuthorChannels (req: express.Request, res: express.Response, next: express.NextFunction) { 82async function listVideoAccountChannels (req: express.Request, res: express.Response, next: express.NextFunction) {
83 const resultList = await db.VideoChannel.listByAuthor(res.locals.author.id) 83 const resultList = await db.VideoChannel.listByAccount(res.locals.account.id)
84 84
85 return res.json(getFormattedObjects(resultList.data, resultList.total)) 85 return res.json(getFormattedObjects(resultList.data, resultList.total))
86} 86}
@@ -101,11 +101,11 @@ async function addVideoChannelRetryWrapper (req: express.Request, res: express.R
101 101
102async function addVideoChannel (req: express.Request, res: express.Response) { 102async function addVideoChannel (req: express.Request, res: express.Response) {
103 const videoChannelInfo: VideoChannelCreate = req.body 103 const videoChannelInfo: VideoChannelCreate = req.body
104 const author: AuthorInstance = res.locals.oauth.token.User.Author 104 const account: AccountInstance = res.locals.oauth.token.User.Account
105 let videoChannelCreated: VideoChannelInstance 105 let videoChannelCreated: VideoChannelInstance
106 106
107 await db.sequelize.transaction(async t => { 107 await db.sequelize.transaction(async t => {
108 videoChannelCreated = await createVideoChannel(videoChannelInfo, author, t) 108 videoChannelCreated = await createVideoChannel(videoChannelInfo, account, t)
109 }) 109 })
110 110
111 logger.info('Video channel with uuid %s created.', videoChannelCreated.uuid) 111 logger.info('Video channel with uuid %s created.', videoChannelCreated.uuid)
@@ -179,7 +179,7 @@ async function removeVideoChannel (req: express.Request, res: express.Response)
179} 179}
180 180
181async function getVideoChannel (req: express.Request, res: express.Response, next: express.NextFunction) { 181async function getVideoChannel (req: express.Request, res: express.Response, next: express.NextFunction) {
182 const videoChannelWithVideos = await db.VideoChannel.loadAndPopulateAuthorAndVideos(res.locals.videoChannel.id) 182 const videoChannelWithVideos = await db.VideoChannel.loadAndPopulateAccountAndVideos(res.locals.videoChannel.id)
183 183
184 return res.json(videoChannelWithVideos.toFormattedJSON()) 184 return res.json(videoChannelWithVideos.toFormattedJSON())
185} 185}
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts
index 964db151d..9ad84609f 100644
--- a/server/controllers/api/videos/index.ts
+++ b/server/controllers/api/videos/index.ts
@@ -395,7 +395,7 @@ async function removeVideo (req: express.Request, res: express.Response) {
395} 395}
396 396
397async function searchVideos (req: express.Request, res: express.Response, next: express.NextFunction) { 397async function searchVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
398 const resultList = await db.Video.searchAndPopulateAuthorAndPodAndTags( 398 const resultList = await db.Video.searchAndPopulateAccountAndPodAndTags(
399 req.params.value, 399 req.params.value,
400 req.query.field, 400 req.query.field,
401 req.query.start, 401 req.query.start,
diff --git a/server/controllers/client.ts b/server/controllers/client.ts
index 67ebfcf1d..40dda7584 100644
--- a/server/controllers/client.ts
+++ b/server/controllers/client.ts
@@ -110,9 +110,9 @@ async function generateWatchHtmlPage (req: express.Request, res: express.Respons
110 110
111 // Let Angular application handle errors 111 // Let Angular application handle errors
112 if (validator.isUUID(videoId, 4)) { 112 if (validator.isUUID(videoId, 4)) {
113 videoPromise = db.Video.loadByUUIDAndPopulateAuthorAndPodAndTags(videoId) 113 videoPromise = db.Video.loadByUUIDAndPopulateAccountAndPodAndTags(videoId)
114 } else if (validator.isInt(videoId)) { 114 } else if (validator.isInt(videoId)) {
115 videoPromise = db.Video.loadAndPopulateAuthorAndPodAndTags(+videoId) 115 videoPromise = db.Video.loadAndPopulateAccountAndPodAndTags(+videoId)
116 } else { 116 } else {
117 return res.sendFile(indexPath) 117 return res.sendFile(indexPath)
118 } 118 }
diff --git a/server/controllers/services.ts b/server/controllers/services.ts
index 99a33a716..0db6e5498 100644
--- a/server/controllers/services.ts
+++ b/server/controllers/services.ts
@@ -47,7 +47,7 @@ function generateOEmbed (req: express.Request, res: express.Response, next: expr
47 width: embedWidth, 47 width: embedWidth,
48 height: embedHeight, 48 height: embedHeight,
49 title: video.name, 49 title: video.name,
50 author_name: video.VideoChannel.Author.name, 50 author_name: video.VideoChannel.Account.name,
51 provider_name: 'PeerTube', 51 provider_name: 'PeerTube',
52 provider_url: webserverUrl 52 provider_url: webserverUrl
53 } 53 }
diff --git a/server/helpers/custom-validators/index.ts b/server/helpers/custom-validators/index.ts
index 58a40249b..33922b8fe 100644
--- a/server/helpers/custom-validators/index.ts
+++ b/server/helpers/custom-validators/index.ts
@@ -3,5 +3,6 @@ export * from './misc'
3export * from './pods' 3export * from './pods'
4export * from './pods' 4export * from './pods'
5export * from './users' 5export * from './users'
6export * from './video-accounts'
6export * from './video-channels' 7export * from './video-channels'
7export * from './videos' 8export * from './videos'
diff --git a/server/helpers/custom-validators/video-accounts.ts b/server/helpers/custom-validators/video-accounts.ts
new file mode 100644
index 000000000..3f3e9edd1
--- /dev/null
+++ b/server/helpers/custom-validators/video-accounts.ts
@@ -0,0 +1,45 @@
1import * as Promise from 'bluebird'
2import * as validator from 'validator'
3import * as express from 'express'
4import 'express-validator'
5
6import { database as db } from '../../initializers'
7import { AccountInstance } from '../../models'
8import { logger } from '../logger'
9
10import { isUserUsernameValid } from './users'
11
12function isVideoAccountNameValid (value: string) {
13 return isUserUsernameValid(value)
14}
15
16function checkVideoAccountExists (id: string, res: express.Response, callback: () => void) {
17 let promise: Promise<AccountInstance>
18 if (validator.isInt(id)) {
19 promise = db.Account.load(+id)
20 } else { // UUID
21 promise = db.Account.loadByUUID(id)
22 }
23
24 promise.then(account => {
25 if (!account) {
26 return res.status(404)
27 .json({ error: 'Video account not found' })
28 .end()
29 }
30
31 res.locals.account = account
32 callback()
33 })
34 .catch(err => {
35 logger.error('Error in video account request validator.', err)
36 return res.sendStatus(500)
37 })
38}
39
40// ---------------------------------------------------------------------------
41
42export {
43 checkVideoAccountExists,
44 isVideoAccountNameValid
45}
diff --git a/server/helpers/custom-validators/video-channels.ts b/server/helpers/custom-validators/video-channels.ts
index b6be557e6..acc42f4a4 100644
--- a/server/helpers/custom-validators/video-channels.ts
+++ b/server/helpers/custom-validators/video-channels.ts
@@ -26,9 +26,9 @@ function isVideoChannelUUIDValid (value: string) {
26function checkVideoChannelExists (id: string, res: express.Response, callback: () => void) { 26function checkVideoChannelExists (id: string, res: express.Response, callback: () => void) {
27 let promise: Promise<VideoChannelInstance> 27 let promise: Promise<VideoChannelInstance>
28 if (validator.isInt(id)) { 28 if (validator.isInt(id)) {
29 promise = db.VideoChannel.loadAndPopulateAuthor(+id) 29 promise = db.VideoChannel.loadAndPopulateAccount(+id)
30 } else { // UUID 30 } else { // UUID
31 promise = db.VideoChannel.loadByUUIDAndPopulateAuthor(id) 31 promise = db.VideoChannel.loadByUUIDAndPopulateAccount(id)
32 } 32 }
33 33
34 promise.then(videoChannel => { 34 promise.then(videoChannel => {
diff --git a/server/helpers/custom-validators/videos.ts b/server/helpers/custom-validators/videos.ts
index 83407f17b..487b3d646 100644
--- a/server/helpers/custom-validators/videos.ts
+++ b/server/helpers/custom-validators/videos.ts
@@ -166,9 +166,9 @@ function isVideoFileInfoHashValid (value: string) {
166function checkVideoExists (id: string, res: express.Response, callback: () => void) { 166function checkVideoExists (id: string, res: express.Response, callback: () => void) {
167 let promise: Promise<VideoInstance> 167 let promise: Promise<VideoInstance>
168 if (validator.isInt(id)) { 168 if (validator.isInt(id)) {
169 promise = db.Video.loadAndPopulateAuthorAndPodAndTags(+id) 169 promise = db.Video.loadAndPopulateAccountAndPodAndTags(+id)
170 } else { // UUID 170 } else { // UUID
171 promise = db.Video.loadByUUIDAndPopulateAuthorAndPodAndTags(id) 171 promise = db.Video.loadByUUIDAndPopulateAccountAndPodAndTags(id)
172 } 172 }
173 173
174 promise.then(video => { 174 promise.then(video => {
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index e1f877e80..e6fda88c2 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -29,7 +29,7 @@ const PAGINATION_COUNT_DEFAULT = 15
29 29
30// Sortable columns per schema 30// Sortable columns per schema
31const SEARCHABLE_COLUMNS = { 31const SEARCHABLE_COLUMNS = {
32 VIDEOS: [ 'name', 'magnetUri', 'host', 'author', 'tags' ] 32 VIDEOS: [ 'name', 'magnetUri', 'host', 'account', 'tags' ]
33} 33}
34 34
35// Sortable columns per schema 35// Sortable columns per schema
diff --git a/server/initializers/database.ts b/server/initializers/database.ts
index 52e766394..aefb6da3a 100644
--- a/server/initializers/database.ts
+++ b/server/initializers/database.ts
@@ -44,10 +44,6 @@ const database: {
44 OAuthClient?: OAuthClientModel, 44 OAuthClient?: OAuthClientModel,
45 OAuthToken?: OAuthTokenModel, 45 OAuthToken?: OAuthTokenModel,
46 Pod?: PodModel, 46 Pod?: PodModel,
47 RequestToPod?: RequestToPodModel,
48 RequestVideoEvent?: RequestVideoEventModel,
49 RequestVideoQadu?: RequestVideoQaduModel,
50 Request?: RequestModel,
51 Tag?: TagModel, 47 Tag?: TagModel,
52 AccountVideoRate?: AccountVideoRateModel, 48 AccountVideoRate?: AccountVideoRateModel,
53 AccountFollow?: AccountFollowModel, 49 AccountFollow?: AccountFollowModel,
diff --git a/server/initializers/installer.ts b/server/initializers/installer.ts
index 077472341..c8f6b3bc2 100644
--- a/server/initializers/installer.ts
+++ b/server/initializers/installer.ts
@@ -5,7 +5,7 @@ import { database as db } from './database'
5import { CONFIG, LAST_MIGRATION_VERSION, CACHE } from './constants' 5import { CONFIG, LAST_MIGRATION_VERSION, CACHE } from './constants'
6import { clientsExist, usersExist } from './checker' 6import { clientsExist, usersExist } from './checker'
7import { logger, createCertsIfNotExist, mkdirpPromise, rimrafPromise } from '../helpers' 7import { logger, createCertsIfNotExist, mkdirpPromise, rimrafPromise } from '../helpers'
8import { createUserAuthorAndChannel } from '../lib' 8import { createUserAccountAndChannel } from '../lib'
9import { UserRole } from '../../shared' 9import { UserRole } from '../../shared'
10 10
11async function installApplication () { 11async function installApplication () {
@@ -117,7 +117,7 @@ async function createOAuthAdminIfNotExist () {
117 } 117 }
118 const user = db.User.build(userData) 118 const user = db.User.build(userData)
119 119
120 await createUserAuthorAndChannel(user, validatePassword) 120 await createUserAccountAndChannel(user, validatePassword)
121 logger.info('Username: ' + username) 121 logger.info('Username: ' + username)
122 logger.info('User password: ' + password) 122 logger.info('User password: ' + password)
123 123
diff --git a/server/lib/cache/videos-preview-cache.ts b/server/lib/cache/videos-preview-cache.ts
index 0fe4d2f78..791ad1cbf 100644
--- a/server/lib/cache/videos-preview-cache.ts
+++ b/server/lib/cache/videos-preview-cache.ts
@@ -43,7 +43,7 @@ class VideosPreviewCache {
43 } 43 }
44 44
45 private async loadPreviews (key: string) { 45 private async loadPreviews (key: string) {
46 const video = await db.Video.loadByUUIDAndPopulateAuthorAndPodAndTags(key) 46 const video = await db.Video.loadByUUIDAndPopulateAccountAndPodAndTags(key)
47 if (!video) return undefined 47 if (!video) return undefined
48 48
49 if (video.isOwned()) return join(CONFIG.STORAGE.PREVIEWS_DIR, video.getPreviewName()) 49 if (video.isOwned()) return join(CONFIG.STORAGE.PREVIEWS_DIR, video.getPreviewName())
diff --git a/server/lib/friends.ts b/server/lib/friends.ts
deleted file mode 100644
index 5c9baef47..000000000
--- a/server/lib/friends.ts
+++ /dev/null
@@ -1,567 +0,0 @@
1import * as request from 'request'
2import * as Sequelize from 'sequelize'
3import * as Bluebird from 'bluebird'
4import { join } from 'path'
5
6import { database as db } from '../initializers/database'
7import {
8 API_VERSION,
9 CONFIG,
10 REQUESTS_IN_PARALLEL,
11 REQUEST_ENDPOINTS,
12 REQUEST_ENDPOINT_ACTIONS,
13 REMOTE_SCHEME,
14 STATIC_PATHS
15} from '../initializers'
16import {
17 logger,
18 getMyPublicCert,
19 makeSecureRequest,
20 makeRetryRequest
21} from '../helpers'
22import {
23 RequestScheduler,
24 RequestSchedulerOptions,
25
26 RequestVideoQaduScheduler,
27 RequestVideoQaduSchedulerOptions,
28
29 RequestVideoEventScheduler,
30 RequestVideoEventSchedulerOptions
31} from './request'
32import {
33 PodInstance,
34 VideoInstance
35} from '../models'
36import {
37 RequestEndpoint,
38 RequestVideoEventType,
39 RequestVideoQaduType,
40 RemoteVideoCreateData,
41 RemoteVideoUpdateData,
42 RemoteVideoRemoveData,
43 RemoteVideoReportAbuseData,
44 ResultList,
45 RemoteVideoRequestType,
46 Pod as FormattedPod,
47 RemoteVideoChannelCreateData,
48 RemoteVideoChannelUpdateData,
49 RemoteVideoChannelRemoveData,
50 RemoteVideoAuthorCreateData,
51 RemoteVideoAuthorRemoveData
52} from '../../shared'
53
54type QaduParam = { videoId: number, type: RequestVideoQaduType }
55type EventParam = { videoId: number, type: RequestVideoEventType }
56
57const ENDPOINT_ACTIONS = REQUEST_ENDPOINT_ACTIONS[REQUEST_ENDPOINTS.VIDEOS]
58
59const requestScheduler = new RequestScheduler()
60const requestVideoQaduScheduler = new RequestVideoQaduScheduler()
61const requestVideoEventScheduler = new RequestVideoEventScheduler()
62
63function activateSchedulers () {
64 requestScheduler.activate()
65 requestVideoQaduScheduler.activate()
66 requestVideoEventScheduler.activate()
67}
68
69function addVideoToFriends (videoData: RemoteVideoCreateData, transaction: Sequelize.Transaction) {
70 const options = {
71 type: ENDPOINT_ACTIONS.ADD_VIDEO,
72 endpoint: REQUEST_ENDPOINTS.VIDEOS,
73 data: videoData,
74 transaction
75 }
76 return createRequest(options)
77}
78
79function updateVideoToFriends (videoData: RemoteVideoUpdateData, transaction: Sequelize.Transaction) {
80 const options = {
81 type: ENDPOINT_ACTIONS.UPDATE_VIDEO,
82 endpoint: REQUEST_ENDPOINTS.VIDEOS,
83 data: videoData,
84 transaction
85 }
86 return createRequest(options)
87}
88
89function removeVideoToFriends (videoParams: RemoteVideoRemoveData, transaction?: Sequelize.Transaction) {
90 const options = {
91 type: ENDPOINT_ACTIONS.REMOVE_VIDEO,
92 endpoint: REQUEST_ENDPOINTS.VIDEOS,
93 data: videoParams,
94 transaction
95 }
96 return createRequest(options)
97}
98
99function addVideoAuthorToFriends (authorData: RemoteVideoAuthorCreateData, transaction: Sequelize.Transaction) {
100 const options = {
101 type: ENDPOINT_ACTIONS.ADD_AUTHOR,
102 endpoint: REQUEST_ENDPOINTS.VIDEOS,
103 data: authorData,
104 transaction
105 }
106 return createRequest(options)
107}
108
109function removeVideoAuthorToFriends (authorData: RemoteVideoAuthorRemoveData, transaction?: Sequelize.Transaction) {
110 const options = {
111 type: ENDPOINT_ACTIONS.REMOVE_AUTHOR,
112 endpoint: REQUEST_ENDPOINTS.VIDEOS,
113 data: authorData,
114 transaction
115 }
116 return createRequest(options)
117}
118
119function addVideoChannelToFriends (videoChannelData: RemoteVideoChannelCreateData, transaction: Sequelize.Transaction) {
120 const options = {
121 type: ENDPOINT_ACTIONS.ADD_CHANNEL,
122 endpoint: REQUEST_ENDPOINTS.VIDEOS,
123 data: videoChannelData,
124 transaction
125 }
126 return createRequest(options)
127}
128
129function updateVideoChannelToFriends (videoChannelData: RemoteVideoChannelUpdateData, transaction: Sequelize.Transaction) {
130 const options = {
131 type: ENDPOINT_ACTIONS.UPDATE_CHANNEL,
132 endpoint: REQUEST_ENDPOINTS.VIDEOS,
133 data: videoChannelData,
134 transaction
135 }
136 return createRequest(options)
137}
138
139function removeVideoChannelToFriends (videoChannelParams: RemoteVideoChannelRemoveData, transaction?: Sequelize.Transaction) {
140 const options = {
141 type: ENDPOINT_ACTIONS.REMOVE_CHANNEL,
142 endpoint: REQUEST_ENDPOINTS.VIDEOS,
143 data: videoChannelParams,
144 transaction
145 }
146 return createRequest(options)
147}
148
149function reportAbuseVideoToFriend (reportData: RemoteVideoReportAbuseData, video: VideoInstance, transaction: Sequelize.Transaction) {
150 const options = {
151 type: ENDPOINT_ACTIONS.REPORT_ABUSE,
152 endpoint: REQUEST_ENDPOINTS.VIDEOS,
153 data: reportData,
154 toIds: [ video.VideoChannel.Author.podId ],
155 transaction
156 }
157 return createRequest(options)
158}
159
160function quickAndDirtyUpdateVideoToFriends (qaduParam: QaduParam, transaction?: Sequelize.Transaction) {
161 const options = {
162 videoId: qaduParam.videoId,
163 type: qaduParam.type,
164 transaction
165 }
166 return createVideoQaduRequest(options)
167}
168
169function quickAndDirtyUpdatesVideoToFriends (qadusParams: QaduParam[], transaction: Sequelize.Transaction) {
170 const tasks = []
171
172 qadusParams.forEach(qaduParams => {
173 tasks.push(quickAndDirtyUpdateVideoToFriends(qaduParams, transaction))
174 })
175
176 return Promise.all(tasks)
177}
178
179function addEventToRemoteVideo (eventParam: EventParam, transaction?: Sequelize.Transaction) {
180 const options = {
181 videoId: eventParam.videoId,
182 type: eventParam.type,
183 transaction
184 }
185 return createVideoEventRequest(options)
186}
187
188function addEventsToRemoteVideo (eventsParams: EventParam[], transaction: Sequelize.Transaction) {
189 const tasks = []
190
191 for (const eventParams of eventsParams) {
192 tasks.push(addEventToRemoteVideo(eventParams, transaction))
193 }
194
195 return Promise.all(tasks)
196}
197
198async function hasFriends () {
199 const count = await db.Pod.countAll()
200
201 return count !== 0
202}
203
204async function makeFriends (hosts: string[]) {
205 const podsScore = {}
206
207 logger.info('Make friends!')
208 const cert = await getMyPublicCert()
209
210 for (const host of hosts) {
211 await computeForeignPodsList(host, podsScore)
212 }
213
214 logger.debug('Pods scores computed.', { podsScore: podsScore })
215
216 const podsList = computeWinningPods(hosts, podsScore)
217 logger.debug('Pods that we keep.', { podsToKeep: podsList })
218
219 return makeRequestsToWinningPods(cert, podsList)
220}
221
222async function quitFriends () {
223 // Stop pool requests
224 requestScheduler.deactivate()
225
226 try {
227 await requestScheduler.flush()
228
229 await requestVideoQaduScheduler.flush()
230
231 const pods = await db.Pod.list()
232 const requestParams = {
233 method: 'POST' as 'POST',
234 path: '/api/' + API_VERSION + '/remote/pods/remove',
235 toPod: null
236 }
237
238 // Announce we quit them
239 // We don't care if the request fails
240 // The other pod will exclude us automatically after a while
241 try {
242 await Bluebird.map(pods, pod => {
243 requestParams.toPod = pod
244
245 return makeSecureRequest(requestParams)
246 }, { concurrency: REQUESTS_IN_PARALLEL })
247 } catch (err) { // Don't stop the process
248 logger.error('Some errors while quitting friends.', err)
249 }
250
251 const tasks = []
252 for (const pod of pods) {
253 tasks.push(pod.destroy())
254 }
255 await Promise.all(pods)
256
257 logger.info('Removed all remote videos.')
258
259 requestScheduler.activate()
260 } catch (err) {
261 // Don't forget to re activate the scheduler, even if there was an error
262 requestScheduler.activate()
263
264 throw err
265 }
266}
267
268async function sendOwnedDataToPod (podId: number) {
269 // First send authors
270 await sendOwnedAuthorsToPod(podId)
271 await sendOwnedChannelsToPod(podId)
272 await sendOwnedVideosToPod(podId)
273}
274
275async function sendOwnedChannelsToPod (podId: number) {
276 const videoChannels = await db.VideoChannel.listOwned()
277
278 const tasks: Promise<any>[] = []
279 for (const videoChannel of videoChannels) {
280 const remoteVideoChannel = videoChannel.toAddRemoteJSON()
281 const options = {
282 type: 'add-channel' as 'add-channel',
283 endpoint: REQUEST_ENDPOINTS.VIDEOS,
284 data: remoteVideoChannel,
285 toIds: [ podId ],
286 transaction: null
287 }
288
289 const p = createRequest(options)
290 tasks.push(p)
291 }
292
293 await Promise.all(tasks)
294}
295
296async function sendOwnedAuthorsToPod (podId: number) {
297 const authors = await db.Author.listOwned()
298 const tasks: Promise<any>[] = []
299
300 for (const author of authors) {
301 const remoteAuthor = author.toAddRemoteJSON()
302 const options = {
303 type: 'add-author' as 'add-author',
304 endpoint: REQUEST_ENDPOINTS.VIDEOS,
305 data: remoteAuthor,
306 toIds: [ podId ],
307 transaction: null
308 }
309
310 const p = createRequest(options)
311 tasks.push(p)
312 }
313
314 await Promise.all(tasks)
315}
316
317async function sendOwnedVideosToPod (podId: number) {
318 const videosList = await db.Video.listOwnedAndPopulateAuthorAndTags()
319 const tasks: Bluebird<any>[] = []
320
321 for (const video of videosList) {
322 const promise = video.toAddRemoteJSON()
323 .then(remoteVideo => {
324 const options = {
325 type: 'add-video' as 'add-video',
326 endpoint: REQUEST_ENDPOINTS.VIDEOS,
327 data: remoteVideo,
328 toIds: [ podId ],
329 transaction: null
330 }
331 return createRequest(options)
332 })
333 .catch(err => {
334 logger.error('Cannot convert video to remote.', err)
335 // Don't break the process
336 return undefined
337 })
338
339 tasks.push(promise)
340 }
341
342 await Promise.all(tasks)
343}
344
345function fetchRemotePreview (video: VideoInstance) {
346 const host = video.VideoChannel.Author.Pod.host
347 const path = join(STATIC_PATHS.PREVIEWS, video.getPreviewName())
348
349 return request.get(REMOTE_SCHEME.HTTP + '://' + host + path)
350}
351
352function fetchRemoteDescription (video: VideoInstance) {
353 const host = video.VideoChannel.Author.Pod.host
354 const path = video.getDescriptionPath()
355
356 const requestOptions = {
357 url: REMOTE_SCHEME.HTTP + '://' + host + path,
358 json: true
359 }
360
361 return new Promise<string>((res, rej) => {
362 request.get(requestOptions, (err, response, body) => {
363 if (err) return rej(err)
364
365 return res(body.description ? body.description : '')
366 })
367 })
368}
369
370async function removeFriend (pod: PodInstance) {
371 const requestParams = {
372 method: 'POST' as 'POST',
373 path: '/api/' + API_VERSION + '/remote/pods/remove',
374 toPod: pod
375 }
376
377 try {
378 await makeSecureRequest(requestParams)
379 } catch (err) {
380 logger.warn('Cannot notify friends %s we are quitting him.', pod.host, err)
381 }
382
383 try {
384 await pod.destroy()
385
386 logger.info('Removed friend %s.', pod.host)
387 } catch (err) {
388 logger.error('Cannot destroy friend %s.', pod.host, err)
389 }
390}
391
392function getRequestScheduler () {
393 return requestScheduler
394}
395
396function getRequestVideoQaduScheduler () {
397 return requestVideoQaduScheduler
398}
399
400function getRequestVideoEventScheduler () {
401 return requestVideoEventScheduler
402}
403
404// ---------------------------------------------------------------------------
405
406export {
407 activateSchedulers,
408 addVideoToFriends,
409 removeVideoAuthorToFriends,
410 updateVideoToFriends,
411 addVideoAuthorToFriends,
412 reportAbuseVideoToFriend,
413 quickAndDirtyUpdateVideoToFriends,
414 quickAndDirtyUpdatesVideoToFriends,
415 addEventToRemoteVideo,
416 addEventsToRemoteVideo,
417 hasFriends,
418 makeFriends,
419 quitFriends,
420 removeFriend,
421 removeVideoToFriends,
422 sendOwnedDataToPod,
423 getRequestScheduler,
424 getRequestVideoQaduScheduler,
425 getRequestVideoEventScheduler,
426 fetchRemotePreview,
427 addVideoChannelToFriends,
428 fetchRemoteDescription,
429 updateVideoChannelToFriends,
430 removeVideoChannelToFriends
431}
432
433// ---------------------------------------------------------------------------
434
435async function computeForeignPodsList (host: string, podsScore: { [ host: string ]: number }) {
436 const result = await getForeignPodsList(host)
437 const foreignPodsList: { host: string }[] = result.data
438
439 // Let's give 1 point to the pod we ask the friends list
440 foreignPodsList.push({ host })
441
442 for (const foreignPod of foreignPodsList) {
443 const foreignPodHost = foreignPod.host
444
445 if (podsScore[foreignPodHost]) podsScore[foreignPodHost]++
446 else podsScore[foreignPodHost] = 1
447 }
448
449 return undefined
450}
451
452function computeWinningPods (hosts: string[], podsScore: { [ host: string ]: number }) {
453 // Build the list of pods to add
454 // Only add a pod if it exists in more than a half base pods
455 const podsList = []
456 const baseScore = hosts.length / 2
457
458 for (const podHost of Object.keys(podsScore)) {
459 // If the pod is not me and with a good score we add it
460 if (isMe(podHost) === false && podsScore[podHost] > baseScore) {
461 podsList.push({ host: podHost })
462 }
463 }
464
465 return podsList
466}
467
468function getForeignPodsList (host: string) {
469 return new Promise< ResultList<FormattedPod> >((res, rej) => {
470 const path = '/api/' + API_VERSION + '/remote/pods/list'
471
472 request.post(REMOTE_SCHEME.HTTP + '://' + host + path, (err, response, body) => {
473 if (err) return rej(err)
474
475 try {
476 const json: ResultList<FormattedPod> = JSON.parse(body)
477 return res(json)
478 } catch (err) {
479 return rej(err)
480 }
481 })
482 })
483}
484
485async function makeRequestsToWinningPods (cert: string, podsList: PodInstance[]) {
486 // Stop pool requests
487 requestScheduler.deactivate()
488 // Flush pool requests
489 requestScheduler.forceSend()
490
491 try {
492 await Bluebird.map(podsList, async pod => {
493 const params = {
494 url: REMOTE_SCHEME.HTTP + '://' + pod.host + '/api/' + API_VERSION + '/remote/pods/add',
495 method: 'POST' as 'POST',
496 json: {
497 host: CONFIG.WEBSERVER.HOST,
498 email: CONFIG.ADMIN.EMAIL,
499 publicKey: cert
500 }
501 }
502
503 const { response, body } = await makeRetryRequest(params)
504 const typedBody = body as { cert: string, email: string }
505
506 if (response.statusCode === 200) {
507 const podObj = db.Pod.build({ host: pod.host, publicKey: typedBody.cert, email: typedBody.email })
508
509 let podCreated: PodInstance
510 try {
511 podCreated = await podObj.save()
512 } catch (err) {
513 logger.error('Cannot add friend %s pod.', pod.host, err)
514 }
515
516 // Add our videos to the request scheduler
517 sendOwnedDataToPod(podCreated.id)
518 .catch(err => logger.warn('Cannot send owned data to pod %d.', podCreated.id, err))
519 } else {
520 logger.error('Status not 200 for %s pod.', pod.host)
521 }
522 }, { concurrency: REQUESTS_IN_PARALLEL })
523
524 logger.debug('makeRequestsToWinningPods finished.')
525
526 requestScheduler.activate()
527 } catch (err) {
528 // Final callback, we've ended all the requests
529 // Now we made new friends, we can re activate the pool of requests
530 requestScheduler.activate()
531 }
532}
533
534// Wrapper that populate "toIds" argument with all our friends if it is not specified
535type CreateRequestOptions = {
536 type: RemoteVideoRequestType
537 endpoint: RequestEndpoint
538 data: Object
539 toIds?: number[]
540 transaction: Sequelize.Transaction
541}
542async function createRequest (options: CreateRequestOptions) {
543 if (options.toIds !== undefined) {
544 await requestScheduler.createRequest(options as RequestSchedulerOptions)
545 return undefined
546 }
547
548 // If the "toIds" pods is not specified, we send the request to all our friends
549 const podIds = await db.Pod.listAllIds(options.transaction)
550
551 const newOptions = Object.assign(options, { toIds: podIds })
552 await requestScheduler.createRequest(newOptions)
553
554 return undefined
555}
556
557function createVideoQaduRequest (options: RequestVideoQaduSchedulerOptions) {
558 return requestVideoQaduScheduler.createRequest(options)
559}
560
561function createVideoEventRequest (options: RequestVideoEventSchedulerOptions) {
562 return requestVideoEventScheduler.createRequest(options)
563}
564
565function isMe (host: string) {
566 return host === CONFIG.WEBSERVER.HOST
567}
diff --git a/server/lib/jobs/transcoding-job-scheduler/video-file-optimizer-handler.ts b/server/lib/jobs/transcoding-job-scheduler/video-file-optimizer-handler.ts
index ccded4721..f019c28bc 100644
--- a/server/lib/jobs/transcoding-job-scheduler/video-file-optimizer-handler.ts
+++ b/server/lib/jobs/transcoding-job-scheduler/video-file-optimizer-handler.ts
@@ -7,7 +7,7 @@ import { addVideoToFriends } from '../../friends'
7import { JobScheduler } from '../job-scheduler' 7import { JobScheduler } from '../job-scheduler'
8 8
9async function process (data: { videoUUID: string }, jobId: number) { 9async function process (data: { videoUUID: string }, jobId: number) {
10 const video = await db.Video.loadByUUIDAndPopulateAuthorAndPodAndTags(data.videoUUID) 10 const video = await db.Video.loadByUUIDAndPopulateAccountAndPodAndTags(data.videoUUID)
11 // No video, maybe deleted? 11 // No video, maybe deleted?
12 if (!video) { 12 if (!video) {
13 logger.info('Do not process job %d, video does not exist.', jobId, { videoUUID: video.uuid }) 13 logger.info('Do not process job %d, video does not exist.', jobId, { videoUUID: video.uuid })
@@ -30,7 +30,7 @@ async function onSuccess (jobId: number, video: VideoInstance) {
30 logger.info('Job %d is a success.', jobId) 30 logger.info('Job %d is a success.', jobId)
31 31
32 // Maybe the video changed in database, refresh it 32 // Maybe the video changed in database, refresh it
33 const videoDatabase = await db.Video.loadByUUIDAndPopulateAuthorAndPodAndTags(video.uuid) 33 const videoDatabase = await db.Video.loadByUUIDAndPopulateAccountAndPodAndTags(video.uuid)
34 // Video does not exist anymore 34 // Video does not exist anymore
35 if (!videoDatabase) return undefined 35 if (!videoDatabase) return undefined
36 36
diff --git a/server/lib/jobs/transcoding-job-scheduler/video-file-transcoder-handler.ts b/server/lib/jobs/transcoding-job-scheduler/video-file-transcoder-handler.ts
index 853645510..397b95795 100644
--- a/server/lib/jobs/transcoding-job-scheduler/video-file-transcoder-handler.ts
+++ b/server/lib/jobs/transcoding-job-scheduler/video-file-transcoder-handler.ts
@@ -5,7 +5,7 @@ import { VideoInstance } from '../../../models'
5import { VideoResolution } from '../../../../shared' 5import { VideoResolution } from '../../../../shared'
6 6
7async function process (data: { videoUUID: string, resolution: VideoResolution }, jobId: number) { 7async function process (data: { videoUUID: string, resolution: VideoResolution }, jobId: number) {
8 const video = await db.Video.loadByUUIDAndPopulateAuthorAndPodAndTags(data.videoUUID) 8 const video = await db.Video.loadByUUIDAndPopulateAccountAndPodAndTags(data.videoUUID)
9 // No video, maybe deleted? 9 // No video, maybe deleted?
10 if (!video) { 10 if (!video) {
11 logger.info('Do not process job %d, video does not exist.', jobId, { videoUUID: video.uuid }) 11 logger.info('Do not process job %d, video does not exist.', jobId, { videoUUID: video.uuid })
@@ -28,7 +28,7 @@ async function onSuccess (jobId: number, video: VideoInstance) {
28 logger.info('Job %d is a success.', jobId) 28 logger.info('Job %d is a success.', jobId)
29 29
30 // Maybe the video changed in database, refresh it 30 // Maybe the video changed in database, refresh it
31 const videoDatabase = await db.Video.loadByUUIDAndPopulateAuthorAndPodAndTags(video.uuid) 31 const videoDatabase = await db.Video.loadByUUIDAndPopulateAccountAndPodAndTags(video.uuid)
32 // Video does not exist anymore 32 // Video does not exist anymore
33 if (!videoDatabase) return undefined 33 if (!videoDatabase) return undefined
34 34
diff --git a/server/lib/video-channel.ts b/server/lib/video-channel.ts
index a6dd4d061..f81383ce8 100644
--- a/server/lib/video-channel.ts
+++ b/server/lib/video-channel.ts
@@ -11,7 +11,7 @@ async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account
11 name: videoChannelInfo.name, 11 name: videoChannelInfo.name,
12 description: videoChannelInfo.description, 12 description: videoChannelInfo.description,
13 remote: false, 13 remote: false,
14 authorId: account.id 14 accountId: account.id
15 } 15 }
16 16
17 const videoChannel = db.VideoChannel.build(videoChannelData) 17 const videoChannel = db.VideoChannel.build(videoChannelData)
diff --git a/server/middlewares/validators/video-channels.ts b/server/middlewares/validators/video-channels.ts
index 7d611728b..53416a857 100644
--- a/server/middlewares/validators/video-channels.ts
+++ b/server/middlewares/validators/video-channels.ts
@@ -9,19 +9,19 @@ import {
9 isVideoChannelDescriptionValid, 9 isVideoChannelDescriptionValid,
10 isVideoChannelNameValid, 10 isVideoChannelNameValid,
11 checkVideoChannelExists, 11 checkVideoChannelExists,
12 checkVideoAuthorExists 12 checkVideoAccountExists
13} from '../../helpers' 13} from '../../helpers'
14import { UserInstance } from '../../models' 14import { UserInstance } from '../../models'
15import { UserRight } from '../../../shared' 15import { UserRight } from '../../../shared'
16 16
17const listVideoAuthorChannelsValidator = [ 17const listVideoAccountChannelsValidator = [
18 param('authorId').custom(isIdOrUUIDValid).withMessage('Should have a valid author id'), 18 param('accountId').custom(isIdOrUUIDValid).withMessage('Should have a valid account id'),
19 19
20 (req: express.Request, res: express.Response, next: express.NextFunction) => { 20 (req: express.Request, res: express.Response, next: express.NextFunction) => {
21 logger.debug('Checking listVideoAuthorChannelsValidator parameters', { parameters: req.body }) 21 logger.debug('Checking listVideoAccountChannelsValidator parameters', { parameters: req.body })
22 22
23 checkErrors(req, res, () => { 23 checkErrors(req, res, () => {
24 checkVideoAuthorExists(req.params.authorId, res, next) 24 checkVideoAccountExists(req.params.accountId, res, next)
25 }) 25 })
26 } 26 }
27] 27]
@@ -54,7 +54,7 @@ const videoChannelsUpdateValidator = [
54 .end() 54 .end()
55 } 55 }
56 56
57 if (res.locals.videoChannel.Author.userId !== res.locals.oauth.token.User.id) { 57 if (res.locals.videoChannel.Account.userId !== res.locals.oauth.token.User.id) {
58 return res.status(403) 58 return res.status(403)
59 .json({ error: 'Cannot update video channel of another user' }) 59 .json({ error: 'Cannot update video channel of another user' })
60 .end() 60 .end()
@@ -98,7 +98,7 @@ const videoChannelGetValidator = [
98// --------------------------------------------------------------------------- 98// ---------------------------------------------------------------------------
99 99
100export { 100export {
101 listVideoAuthorChannelsValidator, 101 listVideoAccountChannelsValidator,
102 videoChannelsAddValidator, 102 videoChannelsAddValidator,
103 videoChannelsUpdateValidator, 103 videoChannelsUpdateValidator,
104 videoChannelsRemoveValidator, 104 videoChannelsRemoveValidator,
@@ -119,8 +119,8 @@ function checkUserCanDeleteVideoChannel (res: express.Response, callback: () =>
119 119
120 // Check if the user can delete the video channel 120 // Check if the user can delete the video channel
121 // The user can delete it if s/he is an admin 121 // The user can delete it if s/he is an admin
122 // Or if s/he is the video channel's author 122 // Or if s/he is the video channel's account
123 if (user.hasRight(UserRight.REMOVE_ANY_VIDEO_CHANNEL) === false && res.locals.videoChannel.Author.userId !== user.id) { 123 if (user.hasRight(UserRight.REMOVE_ANY_VIDEO_CHANNEL) === false && res.locals.videoChannel.Account.userId !== user.id) {
124 return res.status(403) 124 return res.status(403)
125 .json({ error: 'Cannot remove video channel of another user' }) 125 .json({ error: 'Cannot remove video channel of another user' })
126 .end() 126 .end()
@@ -131,7 +131,7 @@ function checkUserCanDeleteVideoChannel (res: express.Response, callback: () =>
131} 131}
132 132
133function checkVideoChannelIsNotTheLastOne (res: express.Response, callback: () => void) { 133function checkVideoChannelIsNotTheLastOne (res: express.Response, callback: () => void) {
134 db.VideoChannel.countByAuthor(res.locals.oauth.token.User.Author.id) 134 db.VideoChannel.countByAccount(res.locals.oauth.token.User.Account.id)
135 .then(count => { 135 .then(count => {
136 if (count <= 1) { 136 if (count <= 1) {
137 return res.status(409) 137 return res.status(409)
diff --git a/server/middlewares/validators/videos.ts b/server/middlewares/validators/videos.ts
index 15b4629c1..10b426df3 100644
--- a/server/middlewares/validators/videos.ts
+++ b/server/middlewares/validators/videos.ts
@@ -48,11 +48,11 @@ const videosAddValidator = [
48 const videoFile: Express.Multer.File = req.files['videofile'][0] 48 const videoFile: Express.Multer.File = req.files['videofile'][0]
49 const user = res.locals.oauth.token.User 49 const user = res.locals.oauth.token.User
50 50
51 return db.VideoChannel.loadByIdAndAuthor(req.body.channelId, user.Author.id) 51 return db.VideoChannel.loadByIdAndAccount(req.body.channelId, user.Account.id)
52 .then(videoChannel => { 52 .then(videoChannel => {
53 if (!videoChannel) { 53 if (!videoChannel) {
54 res.status(400) 54 res.status(400)
55 .json({ error: 'Unknown video video channel for this author.' }) 55 .json({ error: 'Unknown video video channel for this account.' })
56 .end() 56 .end()
57 57
58 return undefined 58 return undefined
@@ -131,7 +131,7 @@ const videosUpdateValidator = [
131 .end() 131 .end()
132 } 132 }
133 133
134 if (video.VideoChannel.Author.userId !== res.locals.oauth.token.User.id) { 134 if (video.VideoChannel.Account.userId !== res.locals.oauth.token.User.id) {
135 return res.status(403) 135 return res.status(403)
136 .json({ error: 'Cannot update video of another user' }) 136 .json({ error: 'Cannot update video of another user' })
137 .end() 137 .end()
@@ -163,7 +163,7 @@ const videosGetValidator = [
163 if (video.privacy !== VideoPrivacy.PRIVATE) return next() 163 if (video.privacy !== VideoPrivacy.PRIVATE) return next()
164 164
165 authenticate(req, res, () => { 165 authenticate(req, res, () => {
166 if (video.VideoChannel.Author.userId !== res.locals.oauth.token.User.id) { 166 if (video.VideoChannel.Account.userId !== res.locals.oauth.token.User.id) {
167 return res.status(403) 167 return res.status(403)
168 .json({ error: 'Cannot get this private video of another user' }) 168 .json({ error: 'Cannot get this private video of another user' })
169 .end() 169 .end()
@@ -256,10 +256,10 @@ function checkUserCanDeleteVideo (userId: number, res: express.Response, callbac
256 256
257 // Check if the user can delete the video 257 // Check if the user can delete the video
258 // The user can delete it if s/he is an admin 258 // The user can delete it if s/he is an admin
259 // Or if s/he is the video's author 259 // Or if s/he is the video's account
260 const author = res.locals.video.VideoChannel.Author 260 const account = res.locals.video.VideoChannel.Account
261 const user = res.locals.oauth.token.User 261 const user = res.locals.oauth.token.User
262 if (user.hasRight(UserRight.REMOVE_ANY_VIDEO) === false && author.userId !== user.id) { 262 if (user.hasRight(UserRight.REMOVE_ANY_VIDEO) === false && account.userId !== user.id) {
263 return res.status(403) 263 return res.status(403)
264 .json({ error: 'Cannot remove video of another user' }) 264 .json({ error: 'Cannot remove video of another user' })
265 .end() 265 .end()
diff --git a/server/models/account/user.ts b/server/models/account/user.ts
index 1401762c5..7390baf91 100644
--- a/server/models/account/user.ts
+++ b/server/models/account/user.ts
@@ -164,7 +164,7 @@ toFormattedJSON = function (this: UserInstance) {
164 roleLabel: USER_ROLE_LABELS[this.role], 164 roleLabel: USER_ROLE_LABELS[this.role],
165 videoQuota: this.videoQuota, 165 videoQuota: this.videoQuota,
166 createdAt: this.createdAt, 166 createdAt: this.createdAt,
167 author: { 167 account: {
168 id: this.Account.id, 168 id: this.Account.id,
169 uuid: this.Account.uuid 169 uuid: this.Account.uuid
170 } 170 }
@@ -295,7 +295,7 @@ function getOriginalVideoFileTotalFromUser (user: UserInstance) {
295 '(SELECT MAX("VideoFiles"."size") AS "size" FROM "VideoFiles" ' + 295 '(SELECT MAX("VideoFiles"."size") AS "size" FROM "VideoFiles" ' +
296 'INNER JOIN "Videos" ON "VideoFiles"."videoId" = "Videos"."id" ' + 296 'INNER JOIN "Videos" ON "VideoFiles"."videoId" = "Videos"."id" ' +
297 'INNER JOIN "VideoChannels" ON "VideoChannels"."id" = "Videos"."channelId" ' + 297 'INNER JOIN "VideoChannels" ON "VideoChannels"."id" = "Videos"."channelId" ' +
298 'INNER JOIN "Accounts" ON "VideoChannels"."authorId" = "Accounts"."id" ' + 298 'INNER JOIN "Accounts" ON "VideoChannels"."accountId" = "Accounts"."id" ' +
299 'INNER JOIN "Users" ON "Accounts"."userId" = "Users"."id" ' + 299 'INNER JOIN "Users" ON "Accounts"."userId" = "Users"."id" ' +
300 'WHERE "Users"."id" = $userId GROUP BY "Videos"."id") t' 300 'WHERE "Users"."id" = $userId GROUP BY "Videos"."id") t'
301 301
diff --git a/server/models/index.ts b/server/models/index.ts
index 29479e067..0aec2d3b1 100644
--- a/server/models/index.ts
+++ b/server/models/index.ts
@@ -2,6 +2,5 @@ export * from './application'
2export * from './job' 2export * from './job'
3export * from './oauth' 3export * from './oauth'
4export * from './pod' 4export * from './pod'
5export * from './request'
6export * from './account' 5export * from './account'
7export * from './video' 6export * from './video'
diff --git a/server/models/oauth/oauth-token.ts b/server/models/oauth/oauth-token.ts
index dc8bcd872..c7afcc38c 100644
--- a/server/models/oauth/oauth-token.ts
+++ b/server/models/oauth/oauth-token.ts
@@ -131,7 +131,7 @@ getByTokenAndPopulateUser = function (bearerToken: string) {
131 model: OAuthToken['sequelize'].models.User, 131 model: OAuthToken['sequelize'].models.User,
132 include: [ 132 include: [
133 { 133 {
134 model: OAuthToken['sequelize'].models.Author, 134 model: OAuthToken['sequelize'].models.Account,
135 required: true 135 required: true
136 } 136 }
137 ] 137 ]
@@ -156,7 +156,7 @@ getByRefreshTokenAndPopulateUser = function (refreshToken: string) {
156 model: OAuthToken['sequelize'].models.User, 156 model: OAuthToken['sequelize'].models.User,
157 include: [ 157 include: [
158 { 158 {
159 model: OAuthToken['sequelize'].models.Author, 159 model: OAuthToken['sequelize'].models.Account,
160 required: true 160 required: true
161 } 161 }
162 ] 162 ]
diff --git a/server/models/request/abstract-request-interface.ts b/server/models/request/abstract-request-interface.ts
deleted file mode 100644
index a384f4d27..000000000
--- a/server/models/request/abstract-request-interface.ts
+++ /dev/null
@@ -1,12 +0,0 @@
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
deleted file mode 100644
index 3dd6aedc2..000000000
--- a/server/models/request/index.ts
+++ /dev/null
@@ -1,5 +0,0 @@
1export * from './abstract-request-interface'
2export * from './request-interface'
3export * from './request-to-pod-interface'
4export * from './request-video-event-interface'
5export * from './request-video-qadu-interface'
diff --git a/server/models/request/request-interface.ts b/server/models/request/request-interface.ts
deleted file mode 100644
index dae35651b..000000000
--- a/server/models/request/request-interface.ts
+++ /dev/null
@@ -1,46 +0,0 @@
1import * as Sequelize from 'sequelize'
2import * as Promise from 'bluebird'
3
4import { AbstractRequestClass } from './abstract-request-interface'
5import { PodInstance, PodAttributes } from '../pod/pod-interface'
6import { RequestEndpoint } from '../../../shared/models/request-scheduler.model'
7
8export type RequestsGrouped = {
9 [ podId: number ]: {
10 request: RequestInstance,
11 pod: PodInstance
12 }[]
13}
14
15export namespace RequestMethods {
16 export type CountTotalRequests = () => Promise<number>
17
18 export type ListWithLimitAndRandom = (limitPods: number, limitRequestsPerPod: number) => Promise<RequestsGrouped>
19
20 export type RemoveWithEmptyTo = () => Promise<number>
21
22 export type RemoveAll = () => Promise<void>
23}
24
25export interface RequestClass extends AbstractRequestClass<RequestsGrouped> {
26 countTotalRequests: RequestMethods.CountTotalRequests
27 listWithLimitAndRandom: RequestMethods.ListWithLimitAndRandom
28 removeWithEmptyTo: RequestMethods.RemoveWithEmptyTo
29 removeAll: RequestMethods.RemoveAll
30}
31
32export interface RequestAttributes {
33 request: object
34 endpoint: RequestEndpoint
35}
36
37export interface RequestInstance extends RequestClass, RequestAttributes, Sequelize.Instance<RequestAttributes> {
38 id: number
39 createdAt: Date
40 updatedAt: Date
41
42 setPods: Sequelize.HasManySetAssociationsMixin<PodAttributes, number>
43 Pods: PodInstance[]
44}
45
46export interface RequestModel extends RequestClass, Sequelize.Model<RequestInstance, RequestAttributes> {}
diff --git a/server/models/request/request-to-pod-interface.ts b/server/models/request/request-to-pod-interface.ts
deleted file mode 100644
index 7ca99f4d4..000000000
--- a/server/models/request/request-to-pod-interface.ts
+++ /dev/null
@@ -1,23 +0,0 @@
1import * as Sequelize from 'sequelize'
2import * as Promise from 'bluebird'
3
4import { AbstractRequestToPodClass } from './abstract-request-interface'
5
6export namespace RequestToPodMethods {
7 export type RemoveByRequestIdsAndPod = (requestsIds: number[], podId: number) => Promise<number>
8}
9
10export interface RequestToPodClass extends AbstractRequestToPodClass {
11 removeByRequestIdsAndPod: RequestToPodMethods.RemoveByRequestIdsAndPod
12}
13
14export interface RequestToPodAttributes {
15}
16
17export interface RequestToPodInstance extends RequestToPodClass, RequestToPodAttributes, Sequelize.Instance<RequestToPodAttributes> {
18 id: number
19 createdAt: Date
20 updatedAt: Date
21}
22
23export interface RequestToPodModel extends RequestToPodClass, Sequelize.Model<RequestToPodInstance, RequestToPodAttributes> {}
diff --git a/server/models/request/request-to-pod.ts b/server/models/request/request-to-pod.ts
deleted file mode 100644
index 4e14e918b..000000000
--- a/server/models/request/request-to-pod.ts
+++ /dev/null
@@ -1,51 +0,0 @@
1import * as Sequelize from 'sequelize'
2
3import { addMethodsToModel } from '../utils'
4import {
5 RequestToPodInstance,
6 RequestToPodAttributes,
7
8 RequestToPodMethods
9} from './request-to-pod-interface'
10
11let RequestToPod: Sequelize.Model<RequestToPodInstance, RequestToPodAttributes>
12let removeByRequestIdsAndPod: RequestToPodMethods.RemoveByRequestIdsAndPod
13
14export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
15 RequestToPod = sequelize.define<RequestToPodInstance, RequestToPodAttributes>('RequestToPod', {}, {
16 indexes: [
17 {
18 fields: [ 'requestId' ]
19 },
20 {
21 fields: [ 'podId' ]
22 },
23 {
24 fields: [ 'requestId', 'podId' ],
25 unique: true
26 }
27 ]
28 })
29
30 const classMethods = [
31 removeByRequestIdsAndPod
32 ]
33 addMethodsToModel(RequestToPod, classMethods)
34
35 return RequestToPod
36}
37
38// ---------------------------------------------------------------------------
39
40removeByRequestIdsAndPod = function (requestsIds: number[], podId: number) {
41 const query = {
42 where: {
43 requestId: {
44 [Sequelize.Op.in]: requestsIds
45 },
46 podId: podId
47 }
48 }
49
50 return RequestToPod.destroy(query)
51}
diff --git a/server/models/request/request-video-event-interface.ts b/server/models/request/request-video-event-interface.ts
deleted file mode 100644
index 26cabe3ba..000000000
--- a/server/models/request/request-video-event-interface.ts
+++ /dev/null
@@ -1,50 +0,0 @@
1import * as Sequelize from 'sequelize'
2import * as Promise from 'bluebird'
3
4import { AbstractRequestClass, AbstractRequestToPodClass } from './abstract-request-interface'
5import { VideoInstance } from '../video/video-interface'
6import { PodInstance } from '../pod/pod-interface'
7
8import { RequestVideoEventType } from '../../../shared/models/request-scheduler.model'
9
10export type RequestsVideoEventGrouped = {
11 [ podId: number ]: {
12 id: number
13 type: RequestVideoEventType
14 count: number
15 video: VideoInstance
16 pod: PodInstance
17 }[]
18}
19
20export namespace RequestVideoEventMethods {
21 export type CountTotalRequests = () => Promise<number>
22
23 export type ListWithLimitAndRandom = (limitPods: number, limitRequestsPerPod: number) => Promise<RequestsVideoEventGrouped>
24
25 export type RemoveByRequestIdsAndPod = (ids: number[], podId: number) => Promise<number>
26
27 export type RemoveAll = () => Promise<void>
28}
29
30export interface RequestVideoEventClass extends AbstractRequestClass<RequestsVideoEventGrouped>, AbstractRequestToPodClass {
31 countTotalRequests: RequestVideoEventMethods.CountTotalRequests
32 listWithLimitAndRandom: RequestVideoEventMethods.ListWithLimitAndRandom
33 removeByRequestIdsAndPod: RequestVideoEventMethods.RemoveByRequestIdsAndPod
34 removeAll: RequestVideoEventMethods.RemoveAll
35}
36
37export interface RequestVideoEventAttributes {
38 type: RequestVideoEventType
39 count: number
40}
41
42export interface RequestVideoEventInstance
43 extends RequestVideoEventClass, RequestVideoEventAttributes, Sequelize.Instance<RequestVideoEventAttributes> {
44 id: number
45
46 Video: VideoInstance
47}
48
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
deleted file mode 100644
index 8954407e1..000000000
--- a/server/models/request/request-video-event.ts
+++ /dev/null
@@ -1,191 +0,0 @@
1/*
2 Request Video events (likes, dislikes, views...)
3*/
4
5import { values } from 'lodash'
6import * as Sequelize from 'sequelize'
7
8import { database as db } from '../../initializers/database'
9import { REQUEST_VIDEO_EVENT_TYPES } from '../../initializers'
10import { isVideoEventCountValid } from '../../helpers'
11import { addMethodsToModel } from '../utils'
12import {
13 RequestVideoEventInstance,
14 RequestVideoEventAttributes,
15
16 RequestVideoEventMethods,
17 RequestsVideoEventGrouped
18} from './request-video-event-interface'
19
20let RequestVideoEvent: Sequelize.Model<RequestVideoEventInstance, RequestVideoEventAttributes>
21let countTotalRequests: RequestVideoEventMethods.CountTotalRequests
22let listWithLimitAndRandom: RequestVideoEventMethods.ListWithLimitAndRandom
23let removeByRequestIdsAndPod: RequestVideoEventMethods.RemoveByRequestIdsAndPod
24let removeAll: RequestVideoEventMethods.RemoveAll
25
26export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
27 RequestVideoEvent = sequelize.define<RequestVideoEventInstance, RequestVideoEventAttributes>('RequestVideoEvent',
28 {
29 type: {
30 type: DataTypes.ENUM(values(REQUEST_VIDEO_EVENT_TYPES)),
31 allowNull: false
32 },
33 count: {
34 type: DataTypes.INTEGER,
35 allowNull: false,
36 validate: {
37 countValid: function (value) {
38 const res = isVideoEventCountValid(value)
39 if (res === false) throw new Error('Video event count is not valid.')
40 }
41 }
42 }
43 },
44 {
45 updatedAt: false,
46 indexes: [
47 {
48 fields: [ 'videoId' ]
49 }
50 ]
51 }
52 )
53
54 const classMethods = [
55 associate,
56
57 listWithLimitAndRandom,
58 countTotalRequests,
59 removeAll,
60 removeByRequestIdsAndPod
61 ]
62 addMethodsToModel(RequestVideoEvent, classMethods)
63
64 return RequestVideoEvent
65}
66
67// ------------------------------ STATICS ------------------------------
68
69function associate (models) {
70 RequestVideoEvent.belongsTo(models.Video, {
71 foreignKey: {
72 name: 'videoId',
73 allowNull: false
74 },
75 onDelete: 'CASCADE'
76 })
77}
78
79countTotalRequests = function () {
80 const query = {}
81 return RequestVideoEvent.count(query)
82}
83
84listWithLimitAndRandom = function (limitPods: number, limitRequestsPerPod: number) {
85 const Pod = db.Pod
86
87 // We make a join between videos and authors to find the podId of our video event requests
88 const podJoins = 'INNER JOIN "VideoChannels" ON "VideoChannels"."authorId" = "Authors"."id" ' +
89 'INNER JOIN "Videos" ON "Videos"."channelId" = "VideoChannels"."id" ' +
90 'INNER JOIN "RequestVideoEvents" ON "RequestVideoEvents"."videoId" = "Videos"."id"'
91
92 return Pod.listRandomPodIdsWithRequest(limitPods, 'Authors', podJoins).then(podIds => {
93 // We don't have friends that have requests
94 if (podIds.length === 0) return []
95
96 const query = {
97 order: [
98 [ 'id', 'ASC' ]
99 ],
100 include: [
101 {
102 model: RequestVideoEvent['sequelize'].models.Video,
103 include: [
104 {
105 model: RequestVideoEvent['sequelize'].models.VideoChannel,
106 include: [
107 {
108 model: RequestVideoEvent['sequelize'].models.Author,
109 include: [
110 {
111 model: RequestVideoEvent['sequelize'].models.Pod,
112 where: {
113 id: {
114 [Sequelize.Op.in]: podIds
115 }
116 }
117 }
118 ]
119 }
120 ]
121 }
122 ]
123 }
124 ]
125 }
126
127 return RequestVideoEvent.findAll(query).then(requests => {
128 const requestsGrouped = groupAndTruncateRequests(requests, limitRequestsPerPod)
129 return requestsGrouped
130 })
131 })
132}
133
134removeByRequestIdsAndPod = function (ids: number[], podId: number) {
135 const query = {
136 where: {
137 id: {
138 [Sequelize.Op.in]: ids
139 }
140 },
141 include: [
142 {
143 model: RequestVideoEvent['sequelize'].models.Video,
144 include: [
145 {
146 model: RequestVideoEvent['sequelize'].models.VideoChannel,
147 include: [
148 {
149 model: RequestVideoEvent['sequelize'].models.Author,
150 where: {
151 podId
152 }
153 }
154 ]
155 }
156 ]
157 }
158 ]
159 }
160
161 return RequestVideoEvent.destroy(query)
162}
163
164removeAll = function () {
165 // Delete all requests
166 return RequestVideoEvent.truncate({ cascade: true })
167}
168
169// ---------------------------------------------------------------------------
170
171function groupAndTruncateRequests (events: RequestVideoEventInstance[], limitRequestsPerPod: number) {
172 const eventsGrouped: RequestsVideoEventGrouped = {}
173
174 events.forEach(event => {
175 const pod = event.Video.VideoChannel.Author.Pod
176
177 if (!eventsGrouped[pod.id]) eventsGrouped[pod.id] = []
178
179 if (eventsGrouped[pod.id].length < limitRequestsPerPod) {
180 eventsGrouped[pod.id].push({
181 id: event.id,
182 type: event.type,
183 count: event.count,
184 video: event.Video,
185 pod
186 })
187 }
188 })
189
190 return eventsGrouped
191}
diff --git a/server/models/request/request-video-qadu-interface.ts b/server/models/request/request-video-qadu-interface.ts
deleted file mode 100644
index b7b7b1ecc..000000000
--- a/server/models/request/request-video-qadu-interface.ts
+++ /dev/null
@@ -1,48 +0,0 @@
1import * as Sequelize from 'sequelize'
2import * as Promise from 'bluebird'
3
4import { AbstractRequestClass, AbstractRequestToPodClass } from './abstract-request-interface'
5import { VideoInstance } from '../video/video-interface'
6import { PodInstance } from '../pod/pod-interface'
7
8import { RequestVideoQaduType } from '../../../shared/models/request-scheduler.model'
9
10export type RequestsVideoQaduGrouped = {
11 [ podId: number ]: {
12 request: RequestVideoQaduInstance
13 video: VideoInstance
14 pod: PodInstance
15 }
16}
17
18export namespace RequestVideoQaduMethods {
19 export type CountTotalRequests = () => Promise<number>
20
21 export type ListWithLimitAndRandom = (limitPods: number, limitRequestsPerPod: number) => Promise<RequestsVideoQaduGrouped>
22
23 export type RemoveByRequestIdsAndPod = (ids: number[], podId: number) => Promise<number>
24
25 export type RemoveAll = () => Promise<void>
26}
27
28export interface RequestVideoQaduClass extends AbstractRequestClass<RequestsVideoQaduGrouped>, AbstractRequestToPodClass {
29 countTotalRequests: RequestVideoQaduMethods.CountTotalRequests
30 listWithLimitAndRandom: RequestVideoQaduMethods.ListWithLimitAndRandom
31 removeByRequestIdsAndPod: RequestVideoQaduMethods.RemoveByRequestIdsAndPod
32 removeAll: RequestVideoQaduMethods.RemoveAll
33}
34
35export interface RequestVideoQaduAttributes {
36 type: RequestVideoQaduType
37}
38
39export interface RequestVideoQaduInstance
40 extends RequestVideoQaduClass, RequestVideoQaduAttributes, Sequelize.Instance<RequestVideoQaduAttributes> {
41 id: number
42
43 Pod: PodInstance
44 Video: VideoInstance
45}
46
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
deleted file mode 100644
index 93cf249b3..000000000
--- a/server/models/request/request-video-qadu.ts
+++ /dev/null
@@ -1,159 +0,0 @@
1/*
2 Request Video for Quick And Dirty Updates like:
3 - views
4 - likes
5 - dislikes
6
7 We can't put it in the same system than basic requests for efficiency.
8 Moreover we don't want to slow down the basic requests with a lot of views/likes/dislikes requests.
9 So we put it an independant request scheduler.
10*/
11
12import { values } from 'lodash'
13import * as Sequelize from 'sequelize'
14
15import { database as db } from '../../initializers/database'
16import { REQUEST_VIDEO_QADU_TYPES } from '../../initializers'
17import { addMethodsToModel } from '../utils'
18import {
19 RequestVideoQaduInstance,
20 RequestVideoQaduAttributes,
21
22 RequestVideoQaduMethods
23} from './request-video-qadu-interface'
24
25let RequestVideoQadu: Sequelize.Model<RequestVideoQaduInstance, RequestVideoQaduAttributes>
26let countTotalRequests: RequestVideoQaduMethods.CountTotalRequests
27let listWithLimitAndRandom: RequestVideoQaduMethods.ListWithLimitAndRandom
28let removeByRequestIdsAndPod: RequestVideoQaduMethods.RemoveByRequestIdsAndPod
29let removeAll: RequestVideoQaduMethods.RemoveAll
30
31export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
32 RequestVideoQadu = sequelize.define<RequestVideoQaduInstance, RequestVideoQaduAttributes>('RequestVideoQadu',
33 {
34 type: {
35 type: DataTypes.ENUM(values(REQUEST_VIDEO_QADU_TYPES)),
36 allowNull: false
37 }
38 },
39 {
40 timestamps: false,
41 indexes: [
42 {
43 fields: [ 'podId' ]
44 },
45 {
46 fields: [ 'videoId' ]
47 }
48 ]
49 }
50 )
51
52 const classMethods = [
53 associate,
54
55 listWithLimitAndRandom,
56 countTotalRequests,
57 removeAll,
58 removeByRequestIdsAndPod
59 ]
60 addMethodsToModel(RequestVideoQadu, classMethods)
61
62 return RequestVideoQadu
63}
64
65// ------------------------------ STATICS ------------------------------
66
67function associate (models) {
68 RequestVideoQadu.belongsTo(models.Pod, {
69 foreignKey: {
70 name: 'podId',
71 allowNull: false
72 },
73 onDelete: 'CASCADE'
74 })
75
76 RequestVideoQadu.belongsTo(models.Video, {
77 foreignKey: {
78 name: 'videoId',
79 allowNull: false
80 },
81 onDelete: 'CASCADE'
82 })
83}
84
85countTotalRequests = function () {
86 const query = {}
87 return RequestVideoQadu.count(query)
88}
89
90listWithLimitAndRandom = function (limitPods: number, limitRequestsPerPod: number) {
91 const Pod = db.Pod
92 const tableJoin = ''
93
94 return Pod.listRandomPodIdsWithRequest(limitPods, 'RequestVideoQadus', tableJoin).then(podIds => {
95 // We don't have friends that have requests
96 if (podIds.length === 0) return []
97
98 const query = {
99 include: [
100 {
101 model: RequestVideoQadu['sequelize'].models.Pod,
102 where: {
103 id: {
104 [Sequelize.Op.in]: podIds
105 }
106 }
107 },
108 {
109 model: RequestVideoQadu['sequelize'].models.Video
110 }
111 ]
112 }
113
114 return RequestVideoQadu.findAll(query).then(requests => {
115 const requestsGrouped = groupAndTruncateRequests(requests, limitRequestsPerPod)
116 return requestsGrouped
117 })
118 })
119}
120
121removeByRequestIdsAndPod = function (ids: number[], podId: number) {
122 const query = {
123 where: {
124 id: {
125 [Sequelize.Op.in]: ids
126 },
127 podId
128 }
129 }
130
131 return RequestVideoQadu.destroy(query)
132}
133
134removeAll = function () {
135 // Delete all requests
136 return RequestVideoQadu.truncate({ cascade: true })
137}
138
139// ---------------------------------------------------------------------------
140
141function groupAndTruncateRequests (requests: RequestVideoQaduInstance[], limitRequestsPerPod: number) {
142 const requestsGrouped = {}
143
144 requests.forEach(request => {
145 const pod = request.Pod
146
147 if (!requestsGrouped[pod.id]) requestsGrouped[pod.id] = []
148
149 if (requestsGrouped[pod.id].length < limitRequestsPerPod) {
150 requestsGrouped[pod.id].push({
151 request: request,
152 video: request.Video,
153 pod
154 })
155 }
156 })
157
158 return requestsGrouped
159}
diff --git a/server/models/request/request.ts b/server/models/request/request.ts
deleted file mode 100644
index 71118a947..000000000
--- a/server/models/request/request.ts
+++ /dev/null
@@ -1,144 +0,0 @@
1import { values } from 'lodash'
2import * as Sequelize from 'sequelize'
3
4import { database as db } from '../../initializers/database'
5import { REQUEST_ENDPOINTS } from '../../initializers'
6import { addMethodsToModel } from '../utils'
7import {
8 RequestInstance,
9 RequestAttributes,
10
11 RequestMethods,
12 RequestsGrouped
13} from './request-interface'
14
15let Request: Sequelize.Model<RequestInstance, RequestAttributes>
16let countTotalRequests: RequestMethods.CountTotalRequests
17let listWithLimitAndRandom: RequestMethods.ListWithLimitAndRandom
18let removeWithEmptyTo: RequestMethods.RemoveWithEmptyTo
19let removeAll: RequestMethods.RemoveAll
20
21export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
22 Request = sequelize.define<RequestInstance, RequestAttributes>('Request',
23 {
24 request: {
25 type: DataTypes.JSON,
26 allowNull: false
27 },
28 endpoint: {
29 type: DataTypes.ENUM(values(REQUEST_ENDPOINTS)),
30 allowNull: false
31 }
32 }
33 )
34
35 const classMethods = [
36 associate,
37
38 listWithLimitAndRandom,
39
40 countTotalRequests,
41 removeAll,
42 removeWithEmptyTo
43 ]
44 addMethodsToModel(Request, classMethods)
45
46 return Request
47}
48
49// ------------------------------ STATICS ------------------------------
50
51function associate (models) {
52 Request.belongsToMany(models.Pod, {
53 foreignKey: {
54 name: 'requestId',
55 allowNull: false
56 },
57 through: models.RequestToPod,
58 onDelete: 'CASCADE'
59 })
60}
61
62countTotalRequests = function () {
63 // We need to include Pod because there are no cascade delete when a pod is removed
64 // So we could count requests that do not have existing pod anymore
65 const query = {
66 include: [ Request['sequelize'].models.Pod ]
67 }
68
69 return Request.count(query)
70}
71
72listWithLimitAndRandom = function (limitPods: number, limitRequestsPerPod: number) {
73 const Pod = db.Pod
74 const tableJoin = ''
75
76 return Pod.listRandomPodIdsWithRequest(limitPods, 'RequestToPods', tableJoin).then(podIds => {
77 // We don't have friends that have requests
78 if (podIds.length === 0) return []
79
80 // The first x requests of these pods
81 // It is very important to sort by id ASC to keep the requests order!
82 const query = {
83 order: [
84 [ 'id', 'ASC' ]
85 ],
86 include: [
87 {
88 model: Request['sequelize'].models.Pod,
89 where: {
90 id: {
91 [Sequelize.Op.in]: podIds
92 }
93 }
94 }
95 ]
96 }
97
98 return Request.findAll(query).then(requests => {
99
100 const requestsGrouped = groupAndTruncateRequests(requests, limitRequestsPerPod)
101 return requestsGrouped
102 })
103 })
104}
105
106removeAll = function () {
107 // Delete all requests
108 return Request.truncate({ cascade: true })
109}
110
111removeWithEmptyTo = function () {
112 const query = {
113 where: {
114 id: {
115 [Sequelize.Op.notIn]: [
116 Sequelize.literal('SELECT "requestId" FROM "RequestToPods"')
117 ]
118 }
119 }
120 }
121
122 return Request.destroy(query)
123}
124
125// ---------------------------------------------------------------------------
126
127function groupAndTruncateRequests (requests: RequestInstance[], limitRequestsPerPod: number) {
128 const requestsGrouped: RequestsGrouped = {}
129
130 requests.forEach(request => {
131 request.Pods.forEach(pod => {
132 if (!requestsGrouped[pod.id]) requestsGrouped[pod.id] = []
133
134 if (requestsGrouped[pod.id].length < limitRequestsPerPod) {
135 requestsGrouped[pod.id].push({
136 request,
137 pod
138 })
139 }
140 })
141 })
142
143 return requestsGrouped
144}
diff --git a/server/models/video/index.ts b/server/models/video/index.ts
index dba6a5590..20d97931f 100644
--- a/server/models/video/index.ts
+++ b/server/models/video/index.ts
@@ -1,4 +1,3 @@
1export * from './author-interface'
2export * from './tag-interface' 1export * from './tag-interface'
3export * from './video-abuse-interface' 2export * from './video-abuse-interface'
4export * from './video-blacklist-interface' 3export * from './video-blacklist-interface'