diff options
Diffstat (limited to 'server/models/video/video.ts')
-rw-r--r-- | server/models/video/video.ts | 127 |
1 files changed, 62 insertions, 65 deletions
diff --git a/server/models/video/video.ts b/server/models/video/video.ts index 4516b9c7b..7a102b058 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts | |||
@@ -40,7 +40,7 @@ import { | |||
40 | isVideoDurationValid, | 40 | isVideoDurationValid, |
41 | isVideoLanguageValid, | 41 | isVideoLanguageValid, |
42 | isVideoLicenceValid, | 42 | isVideoLicenceValid, |
43 | isVideoNameValid, isVideoOriginallyPublishedAtValid, | 43 | isVideoNameValid, |
44 | isVideoPrivacyValid, | 44 | isVideoPrivacyValid, |
45 | isVideoStateValid, | 45 | isVideoStateValid, |
46 | isVideoSupportValid | 46 | isVideoSupportValid |
@@ -52,7 +52,9 @@ import { | |||
52 | ACTIVITY_PUB, | 52 | ACTIVITY_PUB, |
53 | API_VERSION, | 53 | API_VERSION, |
54 | CONFIG, | 54 | CONFIG, |
55 | CONSTRAINTS_FIELDS, HLS_PLAYLIST_DIRECTORY, HLS_REDUNDANCY_DIRECTORY, | 55 | CONSTRAINTS_FIELDS, |
56 | HLS_PLAYLIST_DIRECTORY, | ||
57 | HLS_REDUNDANCY_DIRECTORY, | ||
56 | PREVIEWS_SIZE, | 58 | PREVIEWS_SIZE, |
57 | REMOTE_SCHEME, | 59 | REMOTE_SCHEME, |
58 | STATIC_DOWNLOAD_PATHS, | 60 | STATIC_DOWNLOAD_PATHS, |
@@ -70,10 +72,17 @@ import { AccountVideoRateModel } from '../account/account-video-rate' | |||
70 | import { ActorModel } from '../activitypub/actor' | 72 | import { ActorModel } from '../activitypub/actor' |
71 | import { AvatarModel } from '../avatar/avatar' | 73 | import { AvatarModel } from '../avatar/avatar' |
72 | import { ServerModel } from '../server/server' | 74 | import { ServerModel } from '../server/server' |
73 | import { buildBlockedAccountSQL, buildTrigramSearchIndex, createSimilarityAttribute, getVideoSort, throwIfNotValid } from '../utils' | 75 | import { |
76 | buildBlockedAccountSQL, | ||
77 | buildTrigramSearchIndex, | ||
78 | buildWhereIdOrUUID, | ||
79 | createSimilarityAttribute, | ||
80 | getVideoSort, | ||
81 | throwIfNotValid | ||
82 | } from '../utils' | ||
74 | import { TagModel } from './tag' | 83 | import { TagModel } from './tag' |
75 | import { VideoAbuseModel } from './video-abuse' | 84 | import { VideoAbuseModel } from './video-abuse' |
76 | import { VideoChannelModel } from './video-channel' | 85 | import { VideoChannelModel, ScopeNames as VideoChannelScopeNames } from './video-channel' |
77 | import { VideoCommentModel } from './video-comment' | 86 | import { VideoCommentModel } from './video-comment' |
78 | import { VideoFileModel } from './video-file' | 87 | import { VideoFileModel } from './video-file' |
79 | import { VideoShareModel } from './video-share' | 88 | import { VideoShareModel } from './video-share' |
@@ -91,11 +100,11 @@ import { | |||
91 | videoModelToFormattedDetailsJSON, | 100 | videoModelToFormattedDetailsJSON, |
92 | videoModelToFormattedJSON | 101 | videoModelToFormattedJSON |
93 | } from './video-format-utils' | 102 | } from './video-format-utils' |
94 | import * as validator from 'validator' | ||
95 | import { UserVideoHistoryModel } from '../account/user-video-history' | 103 | import { UserVideoHistoryModel } from '../account/user-video-history' |
96 | import { UserModel } from '../account/user' | 104 | import { UserModel } from '../account/user' |
97 | import { VideoImportModel } from './video-import' | 105 | import { VideoImportModel } from './video-import' |
98 | import { VideoStreamingPlaylistModel } from './video-streaming-playlist' | 106 | import { VideoStreamingPlaylistModel } from './video-streaming-playlist' |
107 | import { VideoPlaylistElementModel } from './video-playlist-element' | ||
99 | 108 | ||
100 | // FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation | 109 | // FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation |
101 | const indexes: Sequelize.DefineIndexesOptions[] = [ | 110 | const indexes: Sequelize.DefineIndexesOptions[] = [ |
@@ -175,6 +184,9 @@ export enum ScopeNames { | |||
175 | 184 | ||
176 | type ForAPIOptions = { | 185 | type ForAPIOptions = { |
177 | ids: number[] | 186 | ids: number[] |
187 | |||
188 | videoPlaylistId?: number | ||
189 | |||
178 | withFiles?: boolean | 190 | withFiles?: boolean |
179 | } | 191 | } |
180 | 192 | ||
@@ -182,6 +194,7 @@ type AvailableForListIDsOptions = { | |||
182 | serverAccountId: number | 194 | serverAccountId: number |
183 | followerActorId: number | 195 | followerActorId: number |
184 | includeLocalVideos: boolean | 196 | includeLocalVideos: boolean |
197 | |||
185 | filter?: VideoFilter | 198 | filter?: VideoFilter |
186 | categoryOneOf?: number[] | 199 | categoryOneOf?: number[] |
187 | nsfw?: boolean | 200 | nsfw?: boolean |
@@ -189,9 +202,14 @@ type AvailableForListIDsOptions = { | |||
189 | languageOneOf?: string[] | 202 | languageOneOf?: string[] |
190 | tagsOneOf?: string[] | 203 | tagsOneOf?: string[] |
191 | tagsAllOf?: string[] | 204 | tagsAllOf?: string[] |
205 | |||
192 | withFiles?: boolean | 206 | withFiles?: boolean |
207 | |||
193 | accountId?: number | 208 | accountId?: number |
194 | videoChannelId?: number | 209 | videoChannelId?: number |
210 | |||
211 | videoPlaylistId?: number | ||
212 | |||
195 | trendingDays?: number | 213 | trendingDays?: number |
196 | user?: UserModel, | 214 | user?: UserModel, |
197 | historyOfUser?: UserModel | 215 | historyOfUser?: UserModel |
@@ -199,62 +217,17 @@ type AvailableForListIDsOptions = { | |||
199 | 217 | ||
200 | @Scopes({ | 218 | @Scopes({ |
201 | [ ScopeNames.FOR_API ]: (options: ForAPIOptions) => { | 219 | [ ScopeNames.FOR_API ]: (options: ForAPIOptions) => { |
202 | const accountInclude = { | ||
203 | attributes: [ 'id', 'name' ], | ||
204 | model: AccountModel.unscoped(), | ||
205 | required: true, | ||
206 | include: [ | ||
207 | { | ||
208 | attributes: [ 'id', 'uuid', 'preferredUsername', 'url', 'serverId', 'avatarId' ], | ||
209 | model: ActorModel.unscoped(), | ||
210 | required: true, | ||
211 | include: [ | ||
212 | { | ||
213 | attributes: [ 'host' ], | ||
214 | model: ServerModel.unscoped(), | ||
215 | required: false | ||
216 | }, | ||
217 | { | ||
218 | model: AvatarModel.unscoped(), | ||
219 | required: false | ||
220 | } | ||
221 | ] | ||
222 | } | ||
223 | ] | ||
224 | } | ||
225 | |||
226 | const videoChannelInclude = { | ||
227 | attributes: [ 'name', 'description', 'id' ], | ||
228 | model: VideoChannelModel.unscoped(), | ||
229 | required: true, | ||
230 | include: [ | ||
231 | { | ||
232 | attributes: [ 'uuid', 'preferredUsername', 'url', 'serverId', 'avatarId' ], | ||
233 | model: ActorModel.unscoped(), | ||
234 | required: true, | ||
235 | include: [ | ||
236 | { | ||
237 | attributes: [ 'host' ], | ||
238 | model: ServerModel.unscoped(), | ||
239 | required: false | ||
240 | }, | ||
241 | { | ||
242 | model: AvatarModel.unscoped(), | ||
243 | required: false | ||
244 | } | ||
245 | ] | ||
246 | }, | ||
247 | accountInclude | ||
248 | ] | ||
249 | } | ||
250 | |||
251 | const query: IFindOptions<VideoModel> = { | 220 | const query: IFindOptions<VideoModel> = { |
252 | where: { | 221 | where: { |
253 | id: { | 222 | id: { |
254 | [ Sequelize.Op.any ]: options.ids | 223 | [ Sequelize.Op.any ]: options.ids |
255 | } | 224 | } |
256 | }, | 225 | }, |
257 | include: [ videoChannelInclude ] | 226 | include: [ |
227 | { | ||
228 | model: VideoChannelModel.scope(VideoChannelScopeNames.SUMMARY) | ||
229 | } | ||
230 | ] | ||
258 | } | 231 | } |
259 | 232 | ||
260 | if (options.withFiles === true) { | 233 | if (options.withFiles === true) { |
@@ -264,6 +237,13 @@ type AvailableForListIDsOptions = { | |||
264 | }) | 237 | }) |
265 | } | 238 | } |
266 | 239 | ||
240 | if (options.videoPlaylistId) { | ||
241 | query.include.push({ | ||
242 | model: VideoPlaylistElementModel.unscoped(), | ||
243 | required: true | ||
244 | }) | ||
245 | } | ||
246 | |||
267 | return query | 247 | return query |
268 | }, | 248 | }, |
269 | [ ScopeNames.AVAILABLE_FOR_LIST_IDS ]: (options: AvailableForListIDsOptions) => { | 249 | [ ScopeNames.AVAILABLE_FOR_LIST_IDS ]: (options: AvailableForListIDsOptions) => { |
@@ -315,6 +295,17 @@ type AvailableForListIDsOptions = { | |||
315 | Object.assign(query.where, privacyWhere) | 295 | Object.assign(query.where, privacyWhere) |
316 | } | 296 | } |
317 | 297 | ||
298 | if (options.videoPlaylistId) { | ||
299 | query.include.push({ | ||
300 | attributes: [], | ||
301 | model: VideoPlaylistElementModel.unscoped(), | ||
302 | required: true, | ||
303 | where: { | ||
304 | videoPlaylistId: options.videoPlaylistId | ||
305 | } | ||
306 | }) | ||
307 | } | ||
308 | |||
318 | if (options.filter || options.accountId || options.videoChannelId) { | 309 | if (options.filter || options.accountId || options.videoChannelId) { |
319 | const videoChannelInclude: IIncludeOptions = { | 310 | const videoChannelInclude: IIncludeOptions = { |
320 | attributes: [], | 311 | attributes: [], |
@@ -772,6 +763,15 @@ export class VideoModel extends Model<VideoModel> { | |||
772 | }) | 763 | }) |
773 | Tags: TagModel[] | 764 | Tags: TagModel[] |
774 | 765 | ||
766 | @HasMany(() => VideoPlaylistElementModel, { | ||
767 | foreignKey: { | ||
768 | name: 'videoId', | ||
769 | allowNull: false | ||
770 | }, | ||
771 | onDelete: 'cascade' | ||
772 | }) | ||
773 | VideoPlaylistElements: VideoPlaylistElementModel[] | ||
774 | |||
775 | @HasMany(() => VideoAbuseModel, { | 775 | @HasMany(() => VideoAbuseModel, { |
776 | foreignKey: { | 776 | foreignKey: { |
777 | name: 'videoId', | 777 | name: 'videoId', |
@@ -1118,6 +1118,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1118 | accountId?: number, | 1118 | accountId?: number, |
1119 | videoChannelId?: number, | 1119 | videoChannelId?: number, |
1120 | followerActorId?: number | 1120 | followerActorId?: number |
1121 | videoPlaylistId?: number, | ||
1121 | trendingDays?: number, | 1122 | trendingDays?: number, |
1122 | user?: UserModel, | 1123 | user?: UserModel, |
1123 | historyOfUser?: UserModel | 1124 | historyOfUser?: UserModel |
@@ -1157,6 +1158,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1157 | withFiles: options.withFiles, | 1158 | withFiles: options.withFiles, |
1158 | accountId: options.accountId, | 1159 | accountId: options.accountId, |
1159 | videoChannelId: options.videoChannelId, | 1160 | videoChannelId: options.videoChannelId, |
1161 | videoPlaylistId: options.videoPlaylistId, | ||
1160 | includeLocalVideos: options.includeLocalVideos, | 1162 | includeLocalVideos: options.includeLocalVideos, |
1161 | user: options.user, | 1163 | user: options.user, |
1162 | historyOfUser: options.historyOfUser, | 1164 | historyOfUser: options.historyOfUser, |
@@ -1280,7 +1282,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1280 | } | 1282 | } |
1281 | 1283 | ||
1282 | static load (id: number | string, t?: Sequelize.Transaction) { | 1284 | static load (id: number | string, t?: Sequelize.Transaction) { |
1283 | const where = VideoModel.buildWhereIdOrUUID(id) | 1285 | const where = buildWhereIdOrUUID(id) |
1284 | const options = { | 1286 | const options = { |
1285 | where, | 1287 | where, |
1286 | transaction: t | 1288 | transaction: t |
@@ -1290,7 +1292,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1290 | } | 1292 | } |
1291 | 1293 | ||
1292 | static loadWithRights (id: number | string, t?: Sequelize.Transaction) { | 1294 | static loadWithRights (id: number | string, t?: Sequelize.Transaction) { |
1293 | const where = VideoModel.buildWhereIdOrUUID(id) | 1295 | const where = buildWhereIdOrUUID(id) |
1294 | const options = { | 1296 | const options = { |
1295 | where, | 1297 | where, |
1296 | transaction: t | 1298 | transaction: t |
@@ -1300,7 +1302,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1300 | } | 1302 | } |
1301 | 1303 | ||
1302 | static loadOnlyId (id: number | string, t?: Sequelize.Transaction) { | 1304 | static loadOnlyId (id: number | string, t?: Sequelize.Transaction) { |
1303 | const where = VideoModel.buildWhereIdOrUUID(id) | 1305 | const where = buildWhereIdOrUUID(id) |
1304 | 1306 | ||
1305 | const options = { | 1307 | const options = { |
1306 | attributes: [ 'id' ], | 1308 | attributes: [ 'id' ], |
@@ -1353,7 +1355,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1353 | } | 1355 | } |
1354 | 1356 | ||
1355 | static loadAndPopulateAccountAndServerAndTags (id: number | string, t?: Sequelize.Transaction, userId?: number) { | 1357 | static loadAndPopulateAccountAndServerAndTags (id: number | string, t?: Sequelize.Transaction, userId?: number) { |
1356 | const where = VideoModel.buildWhereIdOrUUID(id) | 1358 | const where = buildWhereIdOrUUID(id) |
1357 | 1359 | ||
1358 | const options = { | 1360 | const options = { |
1359 | order: [ [ 'Tags', 'name', 'ASC' ] ], | 1361 | order: [ [ 'Tags', 'name', 'ASC' ] ], |
@@ -1380,7 +1382,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1380 | } | 1382 | } |
1381 | 1383 | ||
1382 | static loadForGetAPI (id: number | string, t?: Sequelize.Transaction, userId?: number) { | 1384 | static loadForGetAPI (id: number | string, t?: Sequelize.Transaction, userId?: number) { |
1383 | const where = VideoModel.buildWhereIdOrUUID(id) | 1385 | const where = buildWhereIdOrUUID(id) |
1384 | 1386 | ||
1385 | const options = { | 1387 | const options = { |
1386 | order: [ [ 'Tags', 'name', 'ASC' ] ], | 1388 | order: [ [ 'Tags', 'name', 'ASC' ] ], |
@@ -1582,10 +1584,6 @@ export class VideoModel extends Model<VideoModel> { | |||
1582 | return VIDEO_STATES[ id ] || 'Unknown' | 1584 | return VIDEO_STATES[ id ] || 'Unknown' |
1583 | } | 1585 | } |
1584 | 1586 | ||
1585 | static buildWhereIdOrUUID (id: number | string) { | ||
1586 | return validator.isInt('' + id) ? { id } : { uuid: id } | ||
1587 | } | ||
1588 | |||
1589 | getOriginalFile () { | 1587 | getOriginalFile () { |
1590 | if (Array.isArray(this.VideoFiles) === false) return undefined | 1588 | if (Array.isArray(this.VideoFiles) === false) return undefined |
1591 | 1589 | ||
@@ -1598,7 +1596,6 @@ export class VideoModel extends Model<VideoModel> { | |||
1598 | } | 1596 | } |
1599 | 1597 | ||
1600 | getThumbnailName () { | 1598 | getThumbnailName () { |
1601 | // We always have a copy of the thumbnail | ||
1602 | const extension = '.jpg' | 1599 | const extension = '.jpg' |
1603 | return this.uuid + extension | 1600 | return this.uuid + extension |
1604 | } | 1601 | } |