diff options
Diffstat (limited to 'server')
20 files changed, 244 insertions, 147 deletions
diff --git a/server/controllers/activitypub/client.ts b/server/controllers/activitypub/client.ts index acce53713..1da44d096 100644 --- a/server/controllers/activitypub/client.ts +++ b/server/controllers/activitypub/client.ts | |||
@@ -159,7 +159,7 @@ activityPubClientRouter.get('/video-playlists/:playlistId', | |||
159 | asyncMiddleware(videoPlaylistsGetValidator('all')), | 159 | asyncMiddleware(videoPlaylistsGetValidator('all')), |
160 | asyncMiddleware(videoPlaylistController) | 160 | asyncMiddleware(videoPlaylistController) |
161 | ) | 161 | ) |
162 | activityPubClientRouter.get('/video-playlists/:playlistId/:videoId', | 162 | activityPubClientRouter.get('/video-playlists/:playlistId/videos/:playlistElementId', |
163 | executeIfActivityPub, | 163 | executeIfActivityPub, |
164 | asyncMiddleware(videoPlaylistElementAPGetValidator), | 164 | asyncMiddleware(videoPlaylistElementAPGetValidator), |
165 | videoPlaylistElementController | 165 | videoPlaylistElementController |
diff --git a/server/controllers/api/accounts.ts b/server/controllers/api/accounts.ts index b1c05c6c0..0a73dfcbf 100644 --- a/server/controllers/api/accounts.ts +++ b/server/controllers/api/accounts.ts | |||
@@ -1,5 +1,8 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { getServerActor } from '@server/models/application/application' | ||
3 | import { buildNSFWFilter, getCountVideos, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils' | ||
2 | import { getFormattedObjects } from '../../helpers/utils' | 4 | import { getFormattedObjects } from '../../helpers/utils' |
5 | import { JobQueue } from '../../lib/job-queue' | ||
3 | import { | 6 | import { |
4 | asyncMiddleware, | 7 | asyncMiddleware, |
5 | authenticate, | 8 | authenticate, |
@@ -8,6 +11,7 @@ import { | |||
8 | paginationValidator, | 11 | paginationValidator, |
9 | setDefaultPagination, | 12 | setDefaultPagination, |
10 | setDefaultSort, | 13 | setDefaultSort, |
14 | setDefaultVideosSort, | ||
11 | videoPlaylistsSortValidator, | 15 | videoPlaylistsSortValidator, |
12 | videoRatesSortValidator, | 16 | videoRatesSortValidator, |
13 | videoRatingValidator | 17 | videoRatingValidator |
@@ -17,18 +21,15 @@ import { | |||
17 | accountsSortValidator, | 21 | accountsSortValidator, |
18 | ensureAuthUserOwnsAccountValidator, | 22 | ensureAuthUserOwnsAccountValidator, |
19 | videoChannelsSortValidator, | 23 | videoChannelsSortValidator, |
20 | videosSortValidator, | 24 | videoChannelStatsValidator, |
21 | videoChannelStatsValidator | 25 | videosSortValidator |
22 | } from '../../middlewares/validators' | 26 | } from '../../middlewares/validators' |
27 | import { commonVideoPlaylistFiltersValidator, videoPlaylistsSearchValidator } from '../../middlewares/validators/videos/video-playlists' | ||
23 | import { AccountModel } from '../../models/account/account' | 28 | import { AccountModel } from '../../models/account/account' |
24 | import { AccountVideoRateModel } from '../../models/account/account-video-rate' | 29 | import { AccountVideoRateModel } from '../../models/account/account-video-rate' |
25 | import { VideoModel } from '../../models/video/video' | 30 | import { VideoModel } from '../../models/video/video' |
26 | import { buildNSFWFilter, getCountVideos, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils' | ||
27 | import { VideoChannelModel } from '../../models/video/video-channel' | 31 | import { VideoChannelModel } from '../../models/video/video-channel' |
28 | import { JobQueue } from '../../lib/job-queue' | ||
29 | import { VideoPlaylistModel } from '../../models/video/video-playlist' | 32 | import { VideoPlaylistModel } from '../../models/video/video-playlist' |
30 | import { commonVideoPlaylistFiltersValidator, videoPlaylistsSearchValidator } from '../../middlewares/validators/videos/video-playlists' | ||
31 | import { getServerActor } from '@server/models/application/application' | ||
32 | 33 | ||
33 | const accountsRouter = express.Router() | 34 | const accountsRouter = express.Router() |
34 | 35 | ||
@@ -49,7 +50,7 @@ accountsRouter.get('/:accountName/videos', | |||
49 | asyncMiddleware(accountNameWithHostGetValidator), | 50 | asyncMiddleware(accountNameWithHostGetValidator), |
50 | paginationValidator, | 51 | paginationValidator, |
51 | videosSortValidator, | 52 | videosSortValidator, |
52 | setDefaultSort, | 53 | setDefaultVideosSort, |
53 | setDefaultPagination, | 54 | setDefaultPagination, |
54 | optionalAuthenticate, | 55 | optionalAuthenticate, |
55 | commonVideosFiltersValidator, | 56 | commonVideosFiltersValidator, |
diff --git a/server/controllers/api/users/me.ts b/server/controllers/api/users/me.ts index 914c52e27..ba60a3d2a 100644 --- a/server/controllers/api/users/me.ts +++ b/server/controllers/api/users/me.ts | |||
@@ -17,6 +17,7 @@ import { | |||
17 | paginationValidator, | 17 | paginationValidator, |
18 | setDefaultPagination, | 18 | setDefaultPagination, |
19 | setDefaultSort, | 19 | setDefaultSort, |
20 | setDefaultVideosSort, | ||
20 | usersUpdateMeValidator, | 21 | usersUpdateMeValidator, |
21 | usersVideoRatingValidator | 22 | usersVideoRatingValidator |
22 | } from '../../../middlewares' | 23 | } from '../../../middlewares' |
@@ -60,7 +61,7 @@ meRouter.get('/me/videos', | |||
60 | authenticate, | 61 | authenticate, |
61 | paginationValidator, | 62 | paginationValidator, |
62 | videosSortValidator, | 63 | videosSortValidator, |
63 | setDefaultSort, | 64 | setDefaultVideosSort, |
64 | setDefaultPagination, | 65 | setDefaultPagination, |
65 | asyncMiddleware(getUserVideos) | 66 | asyncMiddleware(getUserVideos) |
66 | ) | 67 | ) |
diff --git a/server/controllers/api/users/my-subscriptions.ts b/server/controllers/api/users/my-subscriptions.ts index 8b88feaf3..b8c234eef 100644 --- a/server/controllers/api/users/my-subscriptions.ts +++ b/server/controllers/api/users/my-subscriptions.ts | |||
@@ -1,7 +1,11 @@ | |||
1 | import * as express from 'express' | ||
2 | import 'multer' | 1 | import 'multer' |
2 | import * as express from 'express' | ||
3 | import { VideoFilter } from '../../../../shared/models/videos/video-query.type' | ||
4 | import { buildNSFWFilter, getCountVideos } from '../../../helpers/express-utils' | ||
3 | import { getFormattedObjects } from '../../../helpers/utils' | 5 | import { getFormattedObjects } from '../../../helpers/utils' |
4 | import { WEBSERVER } from '../../../initializers/constants' | 6 | import { WEBSERVER } from '../../../initializers/constants' |
7 | import { sequelizeTypescript } from '../../../initializers/database' | ||
8 | import { JobQueue } from '../../../lib/job-queue' | ||
5 | import { | 9 | import { |
6 | asyncMiddleware, | 10 | asyncMiddleware, |
7 | asyncRetryTransactionMiddleware, | 11 | asyncRetryTransactionMiddleware, |
@@ -10,21 +14,18 @@ import { | |||
10 | paginationValidator, | 14 | paginationValidator, |
11 | setDefaultPagination, | 15 | setDefaultPagination, |
12 | setDefaultSort, | 16 | setDefaultSort, |
17 | setDefaultVideosSort, | ||
13 | userSubscriptionAddValidator, | 18 | userSubscriptionAddValidator, |
14 | userSubscriptionGetValidator | 19 | userSubscriptionGetValidator |
15 | } from '../../../middlewares' | 20 | } from '../../../middlewares' |
16 | import { | 21 | import { |
17 | areSubscriptionsExistValidator, | 22 | areSubscriptionsExistValidator, |
23 | userSubscriptionListValidator, | ||
18 | userSubscriptionsSortValidator, | 24 | userSubscriptionsSortValidator, |
19 | videosSortValidator, | 25 | videosSortValidator |
20 | userSubscriptionListValidator | ||
21 | } from '../../../middlewares/validators' | 26 | } from '../../../middlewares/validators' |
22 | import { VideoModel } from '../../../models/video/video' | ||
23 | import { buildNSFWFilter, getCountVideos } from '../../../helpers/express-utils' | ||
24 | import { VideoFilter } from '../../../../shared/models/videos/video-query.type' | ||
25 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 27 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' |
26 | import { JobQueue } from '../../../lib/job-queue' | 28 | import { VideoModel } from '../../../models/video/video' |
27 | import { sequelizeTypescript } from '../../../initializers/database' | ||
28 | 29 | ||
29 | const mySubscriptionsRouter = express.Router() | 30 | const mySubscriptionsRouter = express.Router() |
30 | 31 | ||
@@ -32,7 +33,7 @@ mySubscriptionsRouter.get('/me/subscriptions/videos', | |||
32 | authenticate, | 33 | authenticate, |
33 | paginationValidator, | 34 | paginationValidator, |
34 | videosSortValidator, | 35 | videosSortValidator, |
35 | setDefaultSort, | 36 | setDefaultVideosSort, |
36 | setDefaultPagination, | 37 | setDefaultPagination, |
37 | commonVideosFiltersValidator, | 38 | commonVideosFiltersValidator, |
38 | asyncMiddleware(getUserSubscriptionVideos) | 39 | asyncMiddleware(getUserSubscriptionVideos) |
diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts index f705034fd..45c936978 100644 --- a/server/controllers/api/video-channel.ts +++ b/server/controllers/api/video-channel.ts | |||
@@ -1,5 +1,20 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { getServerActor } from '@server/models/application/application' | ||
3 | import { MChannelAccountDefault } from '@server/types/models' | ||
4 | import { VideoChannelCreate, VideoChannelUpdate } from '../../../shared' | ||
5 | import { auditLoggerFactory, getAuditIdFromRes, VideoChannelAuditView } from '../../helpers/audit-logger' | ||
6 | import { resetSequelizeInstance } from '../../helpers/database-utils' | ||
7 | import { buildNSFWFilter, createReqFiles, getCountVideos, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils' | ||
8 | import { logger } from '../../helpers/logger' | ||
2 | import { getFormattedObjects } from '../../helpers/utils' | 9 | import { getFormattedObjects } from '../../helpers/utils' |
10 | import { CONFIG } from '../../initializers/config' | ||
11 | import { MIMETYPES } from '../../initializers/constants' | ||
12 | import { sequelizeTypescript } from '../../initializers/database' | ||
13 | import { setAsyncActorKeys } from '../../lib/activitypub/actor' | ||
14 | import { sendUpdateActor } from '../../lib/activitypub/send' | ||
15 | import { updateActorAvatarFile } from '../../lib/avatar' | ||
16 | import { JobQueue } from '../../lib/job-queue' | ||
17 | import { createLocalVideoChannel, federateAllVideosOfChannel } from '../../lib/video-channel' | ||
3 | import { | 18 | import { |
4 | asyncMiddleware, | 19 | asyncMiddleware, |
5 | asyncRetryTransactionMiddleware, | 20 | asyncRetryTransactionMiddleware, |
@@ -9,34 +24,20 @@ import { | |||
9 | paginationValidator, | 24 | paginationValidator, |
10 | setDefaultPagination, | 25 | setDefaultPagination, |
11 | setDefaultSort, | 26 | setDefaultSort, |
27 | setDefaultVideosSort, | ||
12 | videoChannelsAddValidator, | 28 | videoChannelsAddValidator, |
13 | videoChannelsRemoveValidator, | 29 | videoChannelsRemoveValidator, |
14 | videoChannelsSortValidator, | 30 | videoChannelsSortValidator, |
15 | videoChannelsUpdateValidator, | 31 | videoChannelsUpdateValidator, |
16 | videoPlaylistsSortValidator | 32 | videoPlaylistsSortValidator |
17 | } from '../../middlewares' | 33 | } from '../../middlewares' |
18 | import { VideoChannelModel } from '../../models/video/video-channel' | 34 | import { videoChannelsNameWithHostValidator, videoChannelsOwnSearchValidator, videosSortValidator } from '../../middlewares/validators' |
19 | import { videoChannelsNameWithHostValidator, videosSortValidator, videoChannelsOwnSearchValidator } from '../../middlewares/validators' | 35 | import { updateAvatarValidator } from '../../middlewares/validators/avatar' |
20 | import { sendUpdateActor } from '../../lib/activitypub/send' | 36 | import { commonVideoPlaylistFiltersValidator } from '../../middlewares/validators/videos/video-playlists' |
21 | import { VideoChannelCreate, VideoChannelUpdate } from '../../../shared' | ||
22 | import { createLocalVideoChannel, federateAllVideosOfChannel } from '../../lib/video-channel' | ||
23 | import { buildNSFWFilter, createReqFiles, getCountVideos, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils' | ||
24 | import { setAsyncActorKeys } from '../../lib/activitypub/actor' | ||
25 | import { AccountModel } from '../../models/account/account' | 37 | import { AccountModel } from '../../models/account/account' |
26 | import { MIMETYPES } from '../../initializers/constants' | ||
27 | import { logger } from '../../helpers/logger' | ||
28 | import { VideoModel } from '../../models/video/video' | 38 | import { VideoModel } from '../../models/video/video' |
29 | import { updateAvatarValidator } from '../../middlewares/validators/avatar' | 39 | import { VideoChannelModel } from '../../models/video/video-channel' |
30 | import { updateActorAvatarFile } from '../../lib/avatar' | ||
31 | import { auditLoggerFactory, getAuditIdFromRes, VideoChannelAuditView } from '../../helpers/audit-logger' | ||
32 | import { resetSequelizeInstance } from '../../helpers/database-utils' | ||
33 | import { JobQueue } from '../../lib/job-queue' | ||
34 | import { VideoPlaylistModel } from '../../models/video/video-playlist' | 40 | import { VideoPlaylistModel } from '../../models/video/video-playlist' |
35 | import { commonVideoPlaylistFiltersValidator } from '../../middlewares/validators/videos/video-playlists' | ||
36 | import { CONFIG } from '../../initializers/config' | ||
37 | import { sequelizeTypescript } from '../../initializers/database' | ||
38 | import { MChannelAccountDefault } from '@server/types/models' | ||
39 | import { getServerActor } from '@server/models/application/application' | ||
40 | 41 | ||
41 | const auditLogger = auditLoggerFactory('channels') | 42 | const auditLogger = auditLoggerFactory('channels') |
42 | const reqAvatarFile = createReqFiles([ 'avatarfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.TMP_DIR }) | 43 | const reqAvatarFile = createReqFiles([ 'avatarfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.TMP_DIR }) |
@@ -98,7 +99,7 @@ videoChannelRouter.get('/:nameWithHost/videos', | |||
98 | asyncMiddleware(videoChannelsNameWithHostValidator), | 99 | asyncMiddleware(videoChannelsNameWithHostValidator), |
99 | paginationValidator, | 100 | paginationValidator, |
100 | videosSortValidator, | 101 | videosSortValidator, |
101 | setDefaultSort, | 102 | setDefaultVideosSort, |
102 | setDefaultPagination, | 103 | setDefaultPagination, |
103 | optionalAuthenticate, | 104 | optionalAuthenticate, |
104 | commonVideosFiltersValidator, | 105 | commonVideosFiltersValidator, |
diff --git a/server/controllers/api/video-playlist.ts b/server/controllers/api/video-playlist.ts index 88a2314fb..41a0e07ff 100644 --- a/server/controllers/api/video-playlist.ts +++ b/server/controllers/api/video-playlist.ts | |||
@@ -297,7 +297,6 @@ async function addVideoInPlaylist (req: express.Request, res: express.Response) | |||
297 | const position = await VideoPlaylistElementModel.getNextPositionOf(videoPlaylist.id, t) | 297 | const position = await VideoPlaylistElementModel.getNextPositionOf(videoPlaylist.id, t) |
298 | 298 | ||
299 | const playlistElement = await VideoPlaylistElementModel.create({ | 299 | const playlistElement = await VideoPlaylistElementModel.create({ |
300 | url: getVideoPlaylistElementActivityPubUrl(videoPlaylist, video), | ||
301 | position, | 300 | position, |
302 | startTimestamp: body.startTimestamp || null, | 301 | startTimestamp: body.startTimestamp || null, |
303 | stopTimestamp: body.stopTimestamp || null, | 302 | stopTimestamp: body.stopTimestamp || null, |
@@ -305,6 +304,9 @@ async function addVideoInPlaylist (req: express.Request, res: express.Response) | |||
305 | videoId: video.id | 304 | videoId: video.id |
306 | }, { transaction: t }) | 305 | }, { transaction: t }) |
307 | 306 | ||
307 | playlistElement.url = getVideoPlaylistElementActivityPubUrl(videoPlaylist, playlistElement) | ||
308 | await playlistElement.save({ transaction: t }) | ||
309 | |||
308 | videoPlaylist.changed('updatedAt', true) | 310 | videoPlaylist.changed('updatedAt', true) |
309 | await videoPlaylist.save({ transaction: t }) | 311 | await videoPlaylist.save({ transaction: t }) |
310 | 312 | ||
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index 1dfd7c7a0..15b6f214f 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts | |||
@@ -1,11 +1,24 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { move } from 'fs-extra' | ||
2 | import { extname } from 'path' | 3 | import { extname } from 'path' |
4 | import toInt from 'validator/lib/toInt' | ||
5 | import { addOptimizeOrMergeAudioJob } from '@server/helpers/video' | ||
6 | import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent' | ||
7 | import { changeVideoChannelShare } from '@server/lib/activitypub/share' | ||
8 | import { getVideoActivityPubUrl } from '@server/lib/activitypub/url' | ||
9 | import { getVideoFilePath } from '@server/lib/video-paths' | ||
10 | import { getServerActor } from '@server/models/application/application' | ||
11 | import { MVideoDetails, MVideoFullLight } from '@server/types/models' | ||
3 | import { VideoCreate, VideoPrivacy, VideoState, VideoUpdate } from '../../../../shared' | 12 | import { VideoCreate, VideoPrivacy, VideoState, VideoUpdate } from '../../../../shared' |
13 | import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type' | ||
14 | import { VideoFilter } from '../../../../shared/models/videos/video-query.type' | ||
15 | import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' | ||
16 | import { resetSequelizeInstance } from '../../../helpers/database-utils' | ||
17 | import { buildNSFWFilter, createReqFiles, getCountVideos } from '../../../helpers/express-utils' | ||
4 | import { getMetadataFromFile, getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffmpeg-utils' | 18 | import { getMetadataFromFile, getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffmpeg-utils' |
5 | import { logger } from '../../../helpers/logger' | 19 | import { logger } from '../../../helpers/logger' |
6 | import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' | ||
7 | import { getFormattedObjects } from '../../../helpers/utils' | 20 | import { getFormattedObjects } from '../../../helpers/utils' |
8 | import { autoBlacklistVideoIfNeeded } from '../../../lib/video-blacklist' | 21 | import { CONFIG } from '../../../initializers/config' |
9 | import { | 22 | import { |
10 | DEFAULT_AUDIO_RESOLUTION, | 23 | DEFAULT_AUDIO_RESOLUTION, |
11 | MIMETYPES, | 24 | MIMETYPES, |
@@ -14,9 +27,15 @@ import { | |||
14 | VIDEO_LICENCES, | 27 | VIDEO_LICENCES, |
15 | VIDEO_PRIVACIES | 28 | VIDEO_PRIVACIES |
16 | } from '../../../initializers/constants' | 29 | } from '../../../initializers/constants' |
30 | import { sequelizeTypescript } from '../../../initializers/database' | ||
31 | import { sendView } from '../../../lib/activitypub/send/send-view' | ||
17 | import { federateVideoIfNeeded, fetchRemoteVideoDescription } from '../../../lib/activitypub/videos' | 32 | import { federateVideoIfNeeded, fetchRemoteVideoDescription } from '../../../lib/activitypub/videos' |
18 | import { JobQueue } from '../../../lib/job-queue' | 33 | import { JobQueue } from '../../../lib/job-queue' |
34 | import { Notifier } from '../../../lib/notifier' | ||
35 | import { Hooks } from '../../../lib/plugins/hooks' | ||
19 | import { Redis } from '../../../lib/redis' | 36 | import { Redis } from '../../../lib/redis' |
37 | import { createVideoMiniatureFromExisting, generateVideoMiniature } from '../../../lib/thumbnail' | ||
38 | import { autoBlacklistVideoIfNeeded } from '../../../lib/video-blacklist' | ||
20 | import { | 39 | import { |
21 | asyncMiddleware, | 40 | asyncMiddleware, |
22 | asyncRetryTransactionMiddleware, | 41 | asyncRetryTransactionMiddleware, |
@@ -26,7 +45,7 @@ import { | |||
26 | optionalAuthenticate, | 45 | optionalAuthenticate, |
27 | paginationValidator, | 46 | paginationValidator, |
28 | setDefaultPagination, | 47 | setDefaultPagination, |
29 | setDefaultSort, | 48 | setDefaultVideosSort, |
30 | videoFileMetadataGetValidator, | 49 | videoFileMetadataGetValidator, |
31 | videosAddValidator, | 50 | videosAddValidator, |
32 | videosCustomGetValidator, | 51 | videosCustomGetValidator, |
@@ -35,37 +54,18 @@ import { | |||
35 | videosSortValidator, | 54 | videosSortValidator, |
36 | videosUpdateValidator | 55 | videosUpdateValidator |
37 | } from '../../../middlewares' | 56 | } from '../../../middlewares' |
57 | import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-update' | ||
38 | import { TagModel } from '../../../models/video/tag' | 58 | import { TagModel } from '../../../models/video/tag' |
39 | import { VideoModel } from '../../../models/video/video' | 59 | import { VideoModel } from '../../../models/video/video' |
40 | import { VideoFileModel } from '../../../models/video/video-file' | 60 | import { VideoFileModel } from '../../../models/video/video-file' |
41 | import { abuseVideoRouter } from './abuse' | 61 | import { abuseVideoRouter } from './abuse' |
42 | import { blacklistRouter } from './blacklist' | 62 | import { blacklistRouter } from './blacklist' |
43 | import { videoCommentRouter } from './comment' | ||
44 | import { rateVideoRouter } from './rate' | ||
45 | import { ownershipVideoRouter } from './ownership' | ||
46 | import { VideoFilter } from '../../../../shared/models/videos/video-query.type' | ||
47 | import { buildNSFWFilter, createReqFiles, getCountVideos } from '../../../helpers/express-utils' | ||
48 | import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-update' | ||
49 | import { videoCaptionsRouter } from './captions' | 63 | import { videoCaptionsRouter } from './captions' |
64 | import { videoCommentRouter } from './comment' | ||
50 | import { videoImportsRouter } from './import' | 65 | import { videoImportsRouter } from './import' |
51 | import { resetSequelizeInstance } from '../../../helpers/database-utils' | 66 | import { ownershipVideoRouter } from './ownership' |
52 | import { move } from 'fs-extra' | 67 | import { rateVideoRouter } from './rate' |
53 | import { watchingRouter } from './watching' | 68 | import { watchingRouter } from './watching' |
54 | import { Notifier } from '../../../lib/notifier' | ||
55 | import { sendView } from '../../../lib/activitypub/send/send-view' | ||
56 | import { CONFIG } from '../../../initializers/config' | ||
57 | import { sequelizeTypescript } from '../../../initializers/database' | ||
58 | import { createVideoMiniatureFromExisting, generateVideoMiniature } from '../../../lib/thumbnail' | ||
59 | import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type' | ||
60 | import { Hooks } from '../../../lib/plugins/hooks' | ||
61 | import { MVideoDetails, MVideoFullLight } from '@server/types/models' | ||
62 | import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent' | ||
63 | import { getVideoFilePath } from '@server/lib/video-paths' | ||
64 | import toInt from 'validator/lib/toInt' | ||
65 | import { addOptimizeOrMergeAudioJob } from '@server/helpers/video' | ||
66 | import { getServerActor } from '@server/models/application/application' | ||
67 | import { changeVideoChannelShare } from '@server/lib/activitypub/share' | ||
68 | import { getVideoActivityPubUrl } from '@server/lib/activitypub/url' | ||
69 | 69 | ||
70 | const auditLogger = auditLoggerFactory('videos') | 70 | const auditLogger = auditLoggerFactory('videos') |
71 | const videosRouter = express.Router() | 71 | const videosRouter = express.Router() |
@@ -105,7 +105,7 @@ videosRouter.get('/privacies', listVideoPrivacies) | |||
105 | videosRouter.get('/', | 105 | videosRouter.get('/', |
106 | paginationValidator, | 106 | paginationValidator, |
107 | videosSortValidator, | 107 | videosSortValidator, |
108 | setDefaultSort, | 108 | setDefaultVideosSort, |
109 | setDefaultPagination, | 109 | setDefaultPagination, |
110 | optionalAuthenticate, | 110 | optionalAuthenticate, |
111 | commonVideosFiltersValidator, | 111 | commonVideosFiltersValidator, |
@@ -414,7 +414,7 @@ async function updateVideo (req: express.Request, res: express.Response) { | |||
414 | Notifier.Instance.notifyOnNewVideoIfNeeded(videoInstanceUpdated) | 414 | Notifier.Instance.notifyOnNewVideoIfNeeded(videoInstanceUpdated) |
415 | } | 415 | } |
416 | 416 | ||
417 | Hooks.runAction('action:api.video.updated', { video: videoInstanceUpdated }) | 417 | Hooks.runAction('action:api.video.updated', { video: videoInstanceUpdated, body: req.body }) |
418 | } catch (err) { | 418 | } catch (err) { |
419 | // Force fields we want to update | 419 | // Force fields we want to update |
420 | // If the transaction is retried, sequelize will think the object has not changed | 420 | // If the transaction is retried, sequelize will think the object has not changed |
diff --git a/server/controllers/feeds.ts b/server/controllers/feeds.ts index bfcd3fe36..f14c0d316 100644 --- a/server/controllers/feeds.ts +++ b/server/controllers/feeds.ts | |||
@@ -1,21 +1,21 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import * as Feed from 'pfeed' | ||
3 | import { buildNSFWFilter } from '../helpers/express-utils' | ||
4 | import { CONFIG } from '../initializers/config' | ||
2 | import { FEEDS, ROUTE_CACHE_LIFETIME, THUMBNAILS_SIZE, WEBSERVER } from '../initializers/constants' | 5 | import { FEEDS, ROUTE_CACHE_LIFETIME, THUMBNAILS_SIZE, WEBSERVER } from '../initializers/constants' |
3 | import { | 6 | import { |
4 | asyncMiddleware, | 7 | asyncMiddleware, |
5 | commonVideosFiltersValidator, | 8 | commonVideosFiltersValidator, |
6 | setDefaultSort, | 9 | feedsFormatValidator, |
10 | setDefaultVideosSort, | ||
11 | setFeedFormatContentType, | ||
7 | videoCommentsFeedsValidator, | 12 | videoCommentsFeedsValidator, |
8 | videoFeedsValidator, | 13 | videoFeedsValidator, |
9 | videosSortValidator, | 14 | videosSortValidator |
10 | feedsFormatValidator, | ||
11 | setFeedFormatContentType | ||
12 | } from '../middlewares' | 15 | } from '../middlewares' |
13 | import { VideoModel } from '../models/video/video' | ||
14 | import * as Feed from 'pfeed' | ||
15 | import { cacheRoute } from '../middlewares/cache' | 16 | import { cacheRoute } from '../middlewares/cache' |
17 | import { VideoModel } from '../models/video/video' | ||
16 | import { VideoCommentModel } from '../models/video/video-comment' | 18 | import { VideoCommentModel } from '../models/video/video-comment' |
17 | import { buildNSFWFilter } from '../helpers/express-utils' | ||
18 | import { CONFIG } from '../initializers/config' | ||
19 | 19 | ||
20 | const feedsRouter = express.Router() | 20 | const feedsRouter = express.Router() |
21 | 21 | ||
@@ -34,7 +34,7 @@ feedsRouter.get('/feeds/video-comments.:format', | |||
34 | 34 | ||
35 | feedsRouter.get('/feeds/videos.:format', | 35 | feedsRouter.get('/feeds/videos.:format', |
36 | videosSortValidator, | 36 | videosSortValidator, |
37 | setDefaultSort, | 37 | setDefaultVideosSort, |
38 | feedsFormatValidator, | 38 | feedsFormatValidator, |
39 | setFeedFormatContentType, | 39 | setFeedFormatContentType, |
40 | asyncMiddleware(cacheRoute({ | 40 | asyncMiddleware(cacheRoute({ |
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index 691d29283..c26c3a88c 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts | |||
@@ -23,7 +23,7 @@ import { CONFIG, registerConfigChangedHandler } from './config' | |||
23 | 23 | ||
24 | // --------------------------------------------------------------------------- | 24 | // --------------------------------------------------------------------------- |
25 | 25 | ||
26 | const LAST_MIGRATION_VERSION = 525 | 26 | const LAST_MIGRATION_VERSION = 530 |
27 | 27 | ||
28 | // --------------------------------------------------------------------------- | 28 | // --------------------------------------------------------------------------- |
29 | 29 | ||
@@ -417,7 +417,9 @@ const MIMETYPES = { | |||
417 | 'audio/x-ms-wma': '.wma', | 417 | 'audio/x-ms-wma': '.wma', |
418 | 'audio/wav': '.wav', | 418 | 'audio/wav': '.wav', |
419 | 'audio/x-flac': '.flac', | 419 | 'audio/x-flac': '.flac', |
420 | 'audio/flac': '.flac' | 420 | 'audio/flac': '.flac', |
421 | '‎audio/aac': '.aac', | ||
422 | 'audio/ac3': '.ac3' | ||
421 | }, | 423 | }, |
422 | EXT_MIMETYPE: null as { [ id: string ]: string } | 424 | EXT_MIMETYPE: null as { [ id: string ]: string } |
423 | }, | 425 | }, |
@@ -841,7 +843,7 @@ function buildVideoMimetypeExt () { | |||
841 | 'video/x-matroska': '.mkv', | 843 | 'video/x-matroska': '.mkv', |
842 | 844 | ||
843 | // Developed by Apple | 845 | // Developed by Apple |
844 | 'video/quicktime': '.mov', // often used as output format by editing software | 846 | 'video/quicktime': [ '.mov', '.qt', '.mqv' ], // often used as output format by editing software |
845 | 'video/x-m4v': '.m4v', | 847 | 'video/x-m4v': '.m4v', |
846 | 'video/m4v': '.m4v', | 848 | 'video/m4v': '.m4v', |
847 | 849 | ||
@@ -856,8 +858,8 @@ function buildVideoMimetypeExt () { | |||
856 | 858 | ||
857 | // Developed by 3GPP | 859 | // Developed by 3GPP |
858 | // common video formats for cell phones | 860 | // common video formats for cell phones |
859 | 'video/3gpp': '.3gp', | 861 | 'video/3gpp': [ '.3gp', '.3gpp' ], |
860 | 'video/3gpp2': '.3g2', | 862 | 'video/3gpp2': [ '.3g2', '.3gpp2' ], |
861 | 863 | ||
862 | // Developed by FFmpeg/Mplayer | 864 | // Developed by FFmpeg/Mplayer |
863 | 'application/x-nut': '.nut', | 865 | 'application/x-nut': '.nut', |
@@ -870,7 +872,8 @@ function buildVideoMimetypeExt () { | |||
870 | // Old formats reliant on MPEG-1/MPEG-2 | 872 | // Old formats reliant on MPEG-1/MPEG-2 |
871 | 'video/mpv': '.mpv', | 873 | 'video/mpv': '.mpv', |
872 | 'video/mpeg2': '.m2v', | 874 | 'video/mpeg2': '.m2v', |
873 | 'video/mpeg': '.mpeg', | 875 | 'video/mpeg': [ '.m1v', '.mpg', '.mpe', '.mpeg', '.vob' ], |
876 | 'video/dvd': '.vob', | ||
874 | 877 | ||
875 | // Could be anything | 878 | // Could be anything |
876 | 'application/octet-stream': null, | 879 | 'application/octet-stream': null, |
diff --git a/server/initializers/migrations/0530-playlist-multiple-video.ts b/server/initializers/migrations/0530-playlist-multiple-video.ts new file mode 100644 index 000000000..51a8c06b0 --- /dev/null +++ b/server/initializers/migrations/0530-playlist-multiple-video.ts | |||
@@ -0,0 +1,46 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | import { WEBSERVER } from '../constants' | ||
3 | |||
4 | async function up (utils: { | ||
5 | transaction: Sequelize.Transaction | ||
6 | queryInterface: Sequelize.QueryInterface | ||
7 | sequelize: Sequelize.Sequelize | ||
8 | }): Promise<void> { | ||
9 | { | ||
10 | const field = { | ||
11 | type: Sequelize.STRING, | ||
12 | allowNull: true | ||
13 | } | ||
14 | await utils.queryInterface.changeColumn('videoPlaylistElement', 'url', field) | ||
15 | } | ||
16 | |||
17 | { | ||
18 | await utils.sequelize.query('DROP INDEX IF EXISTS video_playlist_element_video_playlist_id_video_id;') | ||
19 | } | ||
20 | |||
21 | { | ||
22 | const selectPlaylistUUID = 'SELECT "uuid" FROM "videoPlaylist" WHERE "id" = "videoPlaylistElement"."videoPlaylistId"' | ||
23 | const url = `'${WEBSERVER.URL}' || '/video-playlists/' || (${selectPlaylistUUID}) || '/videos/' || "videoPlaylistElement"."id"` | ||
24 | |||
25 | const query = ` | ||
26 | UPDATE "videoPlaylistElement" SET "url" = ${url} WHERE id IN ( | ||
27 | SELECT "videoPlaylistElement"."id" FROM "videoPlaylistElement" | ||
28 | INNER JOIN "videoPlaylist" ON "videoPlaylist".id = "videoPlaylistElement"."videoPlaylistId" | ||
29 | INNER JOIN account ON account.id = "videoPlaylist"."ownerAccountId" | ||
30 | INNER JOIN actor ON actor.id = account."actorId" | ||
31 | WHERE actor."serverId" IS NULL | ||
32 | )` | ||
33 | |||
34 | await utils.sequelize.query(query) | ||
35 | } | ||
36 | |||
37 | } | ||
38 | |||
39 | function down (options) { | ||
40 | throw new Error('Not implemented.') | ||
41 | } | ||
42 | |||
43 | export { | ||
44 | up, | ||
45 | down | ||
46 | } | ||
diff --git a/server/lib/activitypub/url.ts b/server/lib/activitypub/url.ts index b54e038a4..58030be2c 100644 --- a/server/lib/activitypub/url.ts +++ b/server/lib/activitypub/url.ts | |||
@@ -8,7 +8,8 @@ import { | |||
8 | MVideoId, | 8 | MVideoId, |
9 | MVideoUrl, | 9 | MVideoUrl, |
10 | MVideoUUID, | 10 | MVideoUUID, |
11 | MAbuseId | 11 | MAbuseId, |
12 | MVideoPlaylistElement | ||
12 | } from '../../types/models' | 13 | } from '../../types/models' |
13 | import { MVideoPlaylist, MVideoPlaylistUUID } from '../../types/models/video/video-playlist' | 14 | import { MVideoPlaylist, MVideoPlaylistUUID } from '../../types/models/video/video-playlist' |
14 | import { MVideoFileVideoUUID } from '../../types/models/video/video-file' | 15 | import { MVideoFileVideoUUID } from '../../types/models/video/video-file' |
@@ -22,8 +23,8 @@ function getVideoPlaylistActivityPubUrl (videoPlaylist: MVideoPlaylist) { | |||
22 | return WEBSERVER.URL + '/video-playlists/' + videoPlaylist.uuid | 23 | return WEBSERVER.URL + '/video-playlists/' + videoPlaylist.uuid |
23 | } | 24 | } |
24 | 25 | ||
25 | function getVideoPlaylistElementActivityPubUrl (videoPlaylist: MVideoPlaylistUUID, video: MVideoUUID) { | 26 | function getVideoPlaylistElementActivityPubUrl (videoPlaylist: MVideoPlaylistUUID, videoPlaylistElement: MVideoPlaylistElement) { |
26 | return WEBSERVER.URL + '/video-playlists/' + videoPlaylist.uuid + '/' + video.uuid | 27 | return WEBSERVER.URL + '/video-playlists/' + videoPlaylist.uuid + '/videos/' + videoPlaylistElement.id |
27 | } | 28 | } |
28 | 29 | ||
29 | function getVideoCacheFileActivityPubUrl (videoFile: MVideoFileVideoUUID) { | 30 | function getVideoCacheFileActivityPubUrl (videoFile: MVideoFileVideoUUID) { |
diff --git a/server/middlewares/sort.ts b/server/middlewares/sort.ts index fcbb2902c..609046a46 100644 --- a/server/middlewares/sort.ts +++ b/server/middlewares/sort.ts | |||
@@ -2,6 +2,7 @@ import * as express from 'express' | |||
2 | import { SortType } from '../models/utils' | 2 | import { SortType } from '../models/utils' |
3 | 3 | ||
4 | const setDefaultSort = setDefaultSortFactory('-createdAt') | 4 | const setDefaultSort = setDefaultSortFactory('-createdAt') |
5 | const setDefaultVideosSort = setDefaultSortFactory('-publishedAt') | ||
5 | 6 | ||
6 | const setDefaultVideoRedundanciesSort = setDefaultSortFactory('name') | 7 | const setDefaultVideoRedundanciesSort = setDefaultSortFactory('name') |
7 | 8 | ||
@@ -33,6 +34,7 @@ function setBlacklistSort (req: express.Request, res: express.Response, next: ex | |||
33 | export { | 34 | export { |
34 | setDefaultSort, | 35 | setDefaultSort, |
35 | setDefaultSearchSort, | 36 | setDefaultSearchSort, |
37 | setDefaultVideosSort, | ||
36 | setDefaultVideoRedundanciesSort, | 38 | setDefaultVideoRedundanciesSort, |
37 | setBlacklistSort | 39 | setBlacklistSort |
38 | } | 40 | } |
diff --git a/server/middlewares/validators/videos/video-playlists.ts b/server/middlewares/validators/videos/video-playlists.ts index 07fd8533c..4647eae44 100644 --- a/server/middlewares/validators/videos/video-playlists.ts +++ b/server/middlewares/validators/videos/video-playlists.ts | |||
@@ -199,16 +199,6 @@ const videoPlaylistsAddVideoValidator = [ | |||
199 | if (!await doesVideoExist(req.body.videoId, res, 'only-video')) return | 199 | if (!await doesVideoExist(req.body.videoId, res, 'only-video')) return |
200 | 200 | ||
201 | const videoPlaylist = getPlaylist(res) | 201 | const videoPlaylist = getPlaylist(res) |
202 | const video = res.locals.onlyVideo | ||
203 | |||
204 | const videoPlaylistElement = await VideoPlaylistElementModel.loadByPlaylistAndVideo(videoPlaylist.id, video.id) | ||
205 | if (videoPlaylistElement) { | ||
206 | res.status(409) | ||
207 | .json({ error: 'This video in this playlist already exists' }) | ||
208 | .end() | ||
209 | |||
210 | return | ||
211 | } | ||
212 | 202 | ||
213 | if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, videoPlaylist, UserRight.UPDATE_ANY_VIDEO_PLAYLIST, res)) { | 203 | if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, videoPlaylist, UserRight.UPDATE_ANY_VIDEO_PLAYLIST, res)) { |
214 | return | 204 | return |
@@ -258,15 +248,18 @@ const videoPlaylistsUpdateOrRemoveVideoValidator = [ | |||
258 | const videoPlaylistElementAPGetValidator = [ | 248 | const videoPlaylistElementAPGetValidator = [ |
259 | param('playlistId') | 249 | param('playlistId') |
260 | .custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'), | 250 | .custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'), |
261 | param('videoId') | 251 | param('playlistElementId') |
262 | .custom(isIdOrUUIDValid).withMessage('Should have an video id/uuid'), | 252 | .custom(isIdValid).withMessage('Should have an playlist element id'), |
263 | 253 | ||
264 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | 254 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { |
265 | logger.debug('Checking videoPlaylistElementAPGetValidator parameters', { parameters: req.params }) | 255 | logger.debug('Checking videoPlaylistElementAPGetValidator parameters', { parameters: req.params }) |
266 | 256 | ||
267 | if (areValidationErrors(req, res)) return | 257 | if (areValidationErrors(req, res)) return |
268 | 258 | ||
269 | const videoPlaylistElement = await VideoPlaylistElementModel.loadByPlaylistAndVideoForAP(req.params.playlistId, req.params.videoId) | 259 | const playlistElementId = parseInt(req.params.playlistElementId + '', 10) |
260 | const playlistId = req.params.playlistId | ||
261 | |||
262 | const videoPlaylistElement = await VideoPlaylistElementModel.loadByPlaylistAndElementIdForAP(playlistId, playlistElementId) | ||
270 | if (!videoPlaylistElement) { | 263 | if (!videoPlaylistElement) { |
271 | res.status(404) | 264 | res.status(404) |
272 | .json({ error: 'Video playlist element not found' }) | 265 | .json({ error: 'Video playlist element not found' }) |
diff --git a/server/models/utils.ts b/server/models/utils.ts index d706d9ea8..6e5522346 100644 --- a/server/models/utils.ts +++ b/server/models/utils.ts | |||
@@ -129,6 +129,30 @@ function buildBlockedAccountSQL (blockerIds: number[]) { | |||
129 | 'WHERE "serverBlocklist"."accountId" IN (' + blockerIdsString + ')' | 129 | 'WHERE "serverBlocklist"."accountId" IN (' + blockerIdsString + ')' |
130 | } | 130 | } |
131 | 131 | ||
132 | function buildBlockedAccountSQLOptimized (columnNameJoin: string, blockerIds: number[]) { | ||
133 | const blockerIdsString = blockerIds.join(', ') | ||
134 | |||
135 | return [ | ||
136 | literal( | ||
137 | `NOT EXISTS (` + | ||
138 | ` SELECT 1 FROM "accountBlocklist" ` + | ||
139 | ` WHERE "targetAccountId" = ${columnNameJoin} ` + | ||
140 | ` AND "accountId" IN (${blockerIdsString})` + | ||
141 | `)` | ||
142 | ), | ||
143 | |||
144 | literal( | ||
145 | `NOT EXISTS (` + | ||
146 | ` SELECT 1 FROM "account" ` + | ||
147 | ` INNER JOIN "actor" ON account."actorId" = actor.id ` + | ||
148 | ` INNER JOIN "serverBlocklist" ON "actor"."serverId" = "serverBlocklist"."targetServerId" ` + | ||
149 | ` WHERE "account"."id" = ${columnNameJoin} ` + | ||
150 | ` AND "serverBlocklist"."accountId" IN (${blockerIdsString})` + | ||
151 | `)` | ||
152 | ) | ||
153 | ] | ||
154 | } | ||
155 | |||
132 | function buildServerIdsFollowedBy (actorId: any) { | 156 | function buildServerIdsFollowedBy (actorId: any) { |
133 | const actorIdNumber = parseInt(actorId + '', 10) | 157 | const actorIdNumber = parseInt(actorId + '', 10) |
134 | 158 | ||
@@ -201,6 +225,7 @@ function searchAttribute (sourceField?: string, targetField?: string) { | |||
201 | 225 | ||
202 | export { | 226 | export { |
203 | buildBlockedAccountSQL, | 227 | buildBlockedAccountSQL, |
228 | buildBlockedAccountSQLOptimized, | ||
204 | buildLocalActorIdsIn, | 229 | buildLocalActorIdsIn, |
205 | SortType, | 230 | SortType, |
206 | buildLocalAccountIdsIn, | 231 | buildLocalAccountIdsIn, |
diff --git a/server/models/video/video-comment.ts b/server/models/video/video-comment.ts index 1d5c7280d..de27b3d87 100644 --- a/server/models/video/video-comment.ts +++ b/server/models/video/video-comment.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import * as Bluebird from 'bluebird' | 1 | import * as Bluebird from 'bluebird' |
2 | import { uniq } from 'lodash' | 2 | import { uniq } from 'lodash' |
3 | import { FindOptions, Op, Order, ScopeOptions, Sequelize, Transaction } from 'sequelize' | 3 | import { FindOptions, Op, Order, ScopeOptions, Sequelize, Transaction, WhereOptions } from 'sequelize' |
4 | import { | 4 | import { |
5 | AllowNull, | 5 | AllowNull, |
6 | BelongsTo, | 6 | BelongsTo, |
@@ -40,7 +40,7 @@ import { | |||
40 | import { VideoCommentAbuseModel } from '../abuse/video-comment-abuse' | 40 | import { VideoCommentAbuseModel } from '../abuse/video-comment-abuse' |
41 | import { AccountModel } from '../account/account' | 41 | import { AccountModel } from '../account/account' |
42 | import { ActorModel, unusedActorAttributesForAPI } from '../activitypub/actor' | 42 | import { ActorModel, unusedActorAttributesForAPI } from '../activitypub/actor' |
43 | import { buildBlockedAccountSQL, buildLocalAccountIdsIn, getCommentSort, throwIfNotValid } from '../utils' | 43 | import { buildBlockedAccountSQL, buildBlockedAccountSQLOptimized, buildLocalAccountIdsIn, getCommentSort, throwIfNotValid } from '../utils' |
44 | import { VideoModel } from './video' | 44 | import { VideoModel } from './video' |
45 | import { VideoChannelModel } from './video-channel' | 45 | import { VideoChannelModel } from './video-channel' |
46 | 46 | ||
@@ -460,19 +460,20 @@ export class VideoCommentModel extends Model<VideoCommentModel> { | |||
460 | const serverActor = await getServerActor() | 460 | const serverActor = await getServerActor() |
461 | const { start, count, videoId, accountId, videoChannelId } = parameters | 461 | const { start, count, videoId, accountId, videoChannelId } = parameters |
462 | 462 | ||
463 | const accountExclusion = { | 463 | const whereAnd: WhereOptions[] = buildBlockedAccountSQLOptimized( |
464 | [Op.notIn]: Sequelize.literal( | 464 | '"VideoCommentModel"."accountId"', |
465 | '(' + buildBlockedAccountSQL([ serverActor.Account.id, '"Video->VideoChannel"."accountId"' ]) + ')' | 465 | [ serverActor.Account.id, '"Video->VideoChannel"."accountId"' ] |
466 | ) | 466 | ) |
467 | |||
468 | if (accountId) { | ||
469 | whereAnd.push({ | ||
470 | [Op.eq]: accountId | ||
471 | }) | ||
472 | } | ||
473 | |||
474 | const accountWhere = { | ||
475 | [Op.and]: whereAnd | ||
467 | } | 476 | } |
468 | const accountWhere = accountId | ||
469 | ? { | ||
470 | [Op.and]: { | ||
471 | ...accountExclusion, | ||
472 | [Op.eq]: accountId | ||
473 | } | ||
474 | } | ||
475 | : accountExclusion | ||
476 | 477 | ||
477 | const videoChannelWhere = videoChannelId ? { id: videoChannelId } : undefined | 478 | const videoChannelWhere = videoChannelId ? { id: videoChannelId } : undefined |
478 | 479 | ||
diff --git a/server/models/video/video-format-utils.ts b/server/models/video/video-format-utils.ts index 9b6509dfd..7a17c839f 100644 --- a/server/models/video/video-format-utils.ts +++ b/server/models/video/video-format-utils.ts | |||
@@ -78,7 +78,10 @@ function videoModelToFormattedJSON (video: MVideoFormattable, options?: VideoFor | |||
78 | 78 | ||
79 | userHistory: userHistory ? { | 79 | userHistory: userHistory ? { |
80 | currentTime: userHistory.currentTime | 80 | currentTime: userHistory.currentTime |
81 | } : undefined | 81 | } : undefined, |
82 | |||
83 | // Can be added by external plugins | ||
84 | pluginData: (video as any).pluginData | ||
82 | } | 85 | } |
83 | 86 | ||
84 | if (options) { | 87 | if (options) { |
diff --git a/server/models/video/video-playlist-element.ts b/server/models/video/video-playlist-element.ts index ba92e129a..d357766e9 100644 --- a/server/models/video/video-playlist-element.ts +++ b/server/models/video/video-playlist-element.ts | |||
@@ -44,10 +44,6 @@ import { MUserAccountId } from '@server/types/models' | |||
44 | fields: [ 'videoId' ] | 44 | fields: [ 'videoId' ] |
45 | }, | 45 | }, |
46 | { | 46 | { |
47 | fields: [ 'videoPlaylistId', 'videoId' ], | ||
48 | unique: true | ||
49 | }, | ||
50 | { | ||
51 | fields: [ 'url' ], | 47 | fields: [ 'url' ], |
52 | unique: true | 48 | unique: true |
53 | } | 49 | } |
@@ -60,8 +56,8 @@ export class VideoPlaylistElementModel extends Model<VideoPlaylistElementModel> | |||
60 | @UpdatedAt | 56 | @UpdatedAt |
61 | updatedAt: Date | 57 | updatedAt: Date |
62 | 58 | ||
63 | @AllowNull(false) | 59 | @AllowNull(true) |
64 | @Is('VideoPlaylistUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'url')) | 60 | @Is('VideoPlaylistUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'url', true)) |
65 | @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_PLAYLISTS.URL.max)) | 61 | @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_PLAYLISTS.URL.max)) |
66 | url: string | 62 | url: string |
67 | 63 | ||
@@ -185,12 +181,11 @@ export class VideoPlaylistElementModel extends Model<VideoPlaylistElementModel> | |||
185 | return VideoPlaylistElementModel.findByPk(playlistElementId) | 181 | return VideoPlaylistElementModel.findByPk(playlistElementId) |
186 | } | 182 | } |
187 | 183 | ||
188 | static loadByPlaylistAndVideoForAP ( | 184 | static loadByPlaylistAndElementIdForAP ( |
189 | playlistId: number | string, | 185 | playlistId: number | string, |
190 | videoId: number | string | 186 | playlistElementId: number |
191 | ): Bluebird<MVideoPlaylistElementVideoUrlPlaylistPrivacy> { | 187 | ): Bluebird<MVideoPlaylistElementVideoUrlPlaylistPrivacy> { |
192 | const playlistWhere = validator.isUUID('' + playlistId) ? { uuid: playlistId } : { id: playlistId } | 188 | const playlistWhere = validator.isUUID('' + playlistId) ? { uuid: playlistId } : { id: playlistId } |
193 | const videoWhere = validator.isUUID('' + videoId) ? { uuid: videoId } : { id: videoId } | ||
194 | 189 | ||
195 | const query = { | 190 | const query = { |
196 | include: [ | 191 | include: [ |
@@ -201,10 +196,12 @@ export class VideoPlaylistElementModel extends Model<VideoPlaylistElementModel> | |||
201 | }, | 196 | }, |
202 | { | 197 | { |
203 | attributes: [ 'url' ], | 198 | attributes: [ 'url' ], |
204 | model: VideoModel.unscoped(), | 199 | model: VideoModel.unscoped() |
205 | where: videoWhere | ||
206 | } | 200 | } |
207 | ] | 201 | ], |
202 | where: { | ||
203 | id: playlistElementId | ||
204 | } | ||
208 | } | 205 | } |
209 | 206 | ||
210 | return VideoPlaylistElementModel.findOne(query) | 207 | return VideoPlaylistElementModel.findOne(query) |
diff --git a/server/tests/api/check-params/video-playlists.ts b/server/tests/api/check-params/video-playlists.ts index 46ec00d46..179ae9201 100644 --- a/server/tests/api/check-params/video-playlists.ts +++ b/server/tests/api/check-params/video-playlists.ts | |||
@@ -346,11 +346,6 @@ describe('Test video playlists API validator', function () { | |||
346 | const res = await addVideoInPlaylist(params) | 346 | const res = await addVideoInPlaylist(params) |
347 | playlistElementId = res.body.videoPlaylistElement.id | 347 | playlistElementId = res.body.videoPlaylistElement.id |
348 | }) | 348 | }) |
349 | |||
350 | it('Should fail if the video was already added in the playlist', async function () { | ||
351 | const params = getBase({}, { expectedStatus: 409 }) | ||
352 | await addVideoInPlaylist(params) | ||
353 | }) | ||
354 | }) | 349 | }) |
355 | 350 | ||
356 | describe('When updating an element in a playlist', function () { | 351 | describe('When updating an element in a playlist', function () { |
diff --git a/server/tests/api/notifications/user-notifications.ts b/server/tests/api/notifications/user-notifications.ts index af4ff42b0..edc95b069 100644 --- a/server/tests/api/notifications/user-notifications.ts +++ b/server/tests/api/notifications/user-notifications.ts | |||
@@ -65,7 +65,7 @@ describe('Test user notifications', function () { | |||
65 | }) | 65 | }) |
66 | 66 | ||
67 | it('Should not send notifications if the user does not follow the video publisher', async function () { | 67 | it('Should not send notifications if the user does not follow the video publisher', async function () { |
68 | this.timeout(10000) | 68 | this.timeout(30000) |
69 | 69 | ||
70 | await uploadRandomVideoOnServers(servers, 1) | 70 | await uploadRandomVideoOnServers(servers, 1) |
71 | 71 | ||
@@ -97,7 +97,7 @@ describe('Test user notifications', function () { | |||
97 | }) | 97 | }) |
98 | 98 | ||
99 | it('Should send a new video notification on a scheduled publication', async function () { | 99 | it('Should send a new video notification on a scheduled publication', async function () { |
100 | this.timeout(20000) | 100 | this.timeout(30000) |
101 | 101 | ||
102 | // In 2 seconds | 102 | // In 2 seconds |
103 | const updateAt = new Date(new Date().getTime() + 2000) | 103 | const updateAt = new Date(new Date().getTime() + 2000) |
@@ -136,7 +136,7 @@ describe('Test user notifications', function () { | |||
136 | }) | 136 | }) |
137 | 137 | ||
138 | it('Should not send a notification before the video is published', async function () { | 138 | it('Should not send a notification before the video is published', async function () { |
139 | this.timeout(20000) | 139 | this.timeout(30000) |
140 | 140 | ||
141 | const updateAt = new Date(new Date().getTime() + 1000000) | 141 | const updateAt = new Date(new Date().getTime() + 1000000) |
142 | 142 | ||
@@ -154,7 +154,7 @@ describe('Test user notifications', function () { | |||
154 | }) | 154 | }) |
155 | 155 | ||
156 | it('Should send a new video notification when a video becomes public', async function () { | 156 | it('Should send a new video notification when a video becomes public', async function () { |
157 | this.timeout(10000) | 157 | this.timeout(30000) |
158 | 158 | ||
159 | const data = { privacy: VideoPrivacy.PRIVATE } | 159 | const data = { privacy: VideoPrivacy.PRIVATE } |
160 | const { name, uuid } = await uploadRandomVideoOnServers(servers, 1, data) | 160 | const { name, uuid } = await uploadRandomVideoOnServers(servers, 1, data) |
@@ -168,7 +168,7 @@ describe('Test user notifications', function () { | |||
168 | }) | 168 | }) |
169 | 169 | ||
170 | it('Should send a new video notification when a remote video becomes public', async function () { | 170 | it('Should send a new video notification when a remote video becomes public', async function () { |
171 | this.timeout(20000) | 171 | this.timeout(30000) |
172 | 172 | ||
173 | const data = { privacy: VideoPrivacy.PRIVATE } | 173 | const data = { privacy: VideoPrivacy.PRIVATE } |
174 | const { name, uuid } = await uploadRandomVideoOnServers(servers, 2, data) | 174 | const { name, uuid } = await uploadRandomVideoOnServers(servers, 2, data) |
@@ -182,7 +182,7 @@ describe('Test user notifications', function () { | |||
182 | }) | 182 | }) |
183 | 183 | ||
184 | it('Should not send a new video notification when a video becomes unlisted', async function () { | 184 | it('Should not send a new video notification when a video becomes unlisted', async function () { |
185 | this.timeout(20000) | 185 | this.timeout(30000) |
186 | 186 | ||
187 | const data = { privacy: VideoPrivacy.PRIVATE } | 187 | const data = { privacy: VideoPrivacy.PRIVATE } |
188 | const { name, uuid } = await uploadRandomVideoOnServers(servers, 1, data) | 188 | const { name, uuid } = await uploadRandomVideoOnServers(servers, 1, data) |
@@ -193,7 +193,7 @@ describe('Test user notifications', function () { | |||
193 | }) | 193 | }) |
194 | 194 | ||
195 | it('Should not send a new video notification when a remote video becomes unlisted', async function () { | 195 | it('Should not send a new video notification when a remote video becomes unlisted', async function () { |
196 | this.timeout(20000) | 196 | this.timeout(30000) |
197 | 197 | ||
198 | const data = { privacy: VideoPrivacy.PRIVATE } | 198 | const data = { privacy: VideoPrivacy.PRIVATE } |
199 | const { name, uuid } = await uploadRandomVideoOnServers(servers, 2, data) | 199 | const { name, uuid } = await uploadRandomVideoOnServers(servers, 2, data) |
@@ -237,7 +237,7 @@ describe('Test user notifications', function () { | |||
237 | }) | 237 | }) |
238 | 238 | ||
239 | it('Should not send a notification if transcoding is not enabled', async function () { | 239 | it('Should not send a notification if transcoding is not enabled', async function () { |
240 | this.timeout(10000) | 240 | this.timeout(30000) |
241 | 241 | ||
242 | const { name, uuid } = await uploadRandomVideoOnServers(servers, 1) | 242 | const { name, uuid } = await uploadRandomVideoOnServers(servers, 1) |
243 | await waitJobs(servers) | 243 | await waitJobs(servers) |
@@ -416,7 +416,7 @@ describe('Test user notifications', function () { | |||
416 | }) | 416 | }) |
417 | 417 | ||
418 | it('Should notify when a local channel is following one of our channel', async function () { | 418 | it('Should notify when a local channel is following one of our channel', async function () { |
419 | this.timeout(10000) | 419 | this.timeout(30000) |
420 | 420 | ||
421 | await addUserSubscription(servers[0].url, servers[0].accessToken, 'user_1_channel@localhost:' + servers[0].port) | 421 | await addUserSubscription(servers[0].url, servers[0].accessToken, 'user_1_channel@localhost:' + servers[0].port) |
422 | await waitJobs(servers) | 422 | await waitJobs(servers) |
@@ -427,7 +427,7 @@ describe('Test user notifications', function () { | |||
427 | }) | 427 | }) |
428 | 428 | ||
429 | it('Should notify when a remote channel is following one of our channel', async function () { | 429 | it('Should notify when a remote channel is following one of our channel', async function () { |
430 | this.timeout(10000) | 430 | this.timeout(30000) |
431 | 431 | ||
432 | await addUserSubscription(servers[1].url, servers[1].accessToken, 'user_1_channel@localhost:' + servers[0].port) | 432 | await addUserSubscription(servers[1].url, servers[1].accessToken, 'user_1_channel@localhost:' + servers[0].port) |
433 | await waitJobs(servers) | 433 | await waitJobs(servers) |
@@ -439,7 +439,7 @@ describe('Test user notifications', function () { | |||
439 | 439 | ||
440 | // PeerTube does not support accout -> account follows | 440 | // PeerTube does not support accout -> account follows |
441 | // it('Should notify when a local account is following one of our channel', async function () { | 441 | // it('Should notify when a local account is following one of our channel', async function () { |
442 | // this.timeout(10000) | 442 | // this.timeout(30000) |
443 | // | 443 | // |
444 | // await addUserSubscription(servers[0].url, servers[0].accessToken, 'user_1@localhost:' + servers[0].port) | 444 | // await addUserSubscription(servers[0].url, servers[0].accessToken, 'user_1@localhost:' + servers[0].port) |
445 | // | 445 | // |
@@ -449,7 +449,7 @@ describe('Test user notifications', function () { | |||
449 | // }) | 449 | // }) |
450 | 450 | ||
451 | // it('Should notify when a remote account is following one of our channel', async function () { | 451 | // it('Should notify when a remote account is following one of our channel', async function () { |
452 | // this.timeout(10000) | 452 | // this.timeout(30000) |
453 | // | 453 | // |
454 | // await addUserSubscription(servers[1].url, servers[1].accessToken, 'user_1@localhost:' + servers[0].port) | 454 | // await addUserSubscription(servers[1].url, servers[1].accessToken, 'user_1@localhost:' + servers[0].port) |
455 | // | 455 | // |
diff --git a/server/tests/api/videos/video-playlists.ts b/server/tests/api/videos/video-playlists.ts index 52b32998d..0bfb5bcd4 100644 --- a/server/tests/api/videos/video-playlists.ts +++ b/server/tests/api/videos/video-playlists.ts | |||
@@ -552,6 +552,9 @@ describe('Test video playlists', function () { | |||
552 | { | 552 | { |
553 | const res = await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 5 }) | 553 | const res = await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 5 }) |
554 | playlistElementNSFW = res.body.videoPlaylistElement.id | 554 | playlistElementNSFW = res.body.videoPlaylistElement.id |
555 | |||
556 | await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 4 }) | ||
557 | await addVideo({ videoId: nsfwVideoServer1 }) | ||
555 | } | 558 | } |
556 | 559 | ||
557 | await waitJobs(servers) | 560 | await waitJobs(servers) |
@@ -563,10 +566,10 @@ describe('Test video playlists', function () { | |||
563 | for (const server of servers) { | 566 | for (const server of servers) { |
564 | const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10) | 567 | const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10) |
565 | 568 | ||
566 | expect(res.body.total).to.equal(6) | 569 | expect(res.body.total).to.equal(8) |
567 | 570 | ||
568 | const videoElements: VideoPlaylistElement[] = res.body.data | 571 | const videoElements: VideoPlaylistElement[] = res.body.data |
569 | expect(videoElements).to.have.lengthOf(6) | 572 | expect(videoElements).to.have.lengthOf(8) |
570 | 573 | ||
571 | expect(videoElements[0].video.name).to.equal('video 0 server 1') | 574 | expect(videoElements[0].video.name).to.equal('video 0 server 1') |
572 | expect(videoElements[0].position).to.equal(1) | 575 | expect(videoElements[0].position).to.equal(1) |
@@ -598,6 +601,16 @@ describe('Test video playlists', function () { | |||
598 | expect(videoElements[5].startTimestamp).to.equal(5) | 601 | expect(videoElements[5].startTimestamp).to.equal(5) |
599 | expect(videoElements[5].stopTimestamp).to.be.null | 602 | expect(videoElements[5].stopTimestamp).to.be.null |
600 | 603 | ||
604 | expect(videoElements[6].video.name).to.equal('NSFW video') | ||
605 | expect(videoElements[6].position).to.equal(7) | ||
606 | expect(videoElements[6].startTimestamp).to.equal(4) | ||
607 | expect(videoElements[6].stopTimestamp).to.be.null | ||
608 | |||
609 | expect(videoElements[7].video.name).to.equal('NSFW video') | ||
610 | expect(videoElements[7].position).to.equal(8) | ||
611 | expect(videoElements[7].startTimestamp).to.be.null | ||
612 | expect(videoElements[7].stopTimestamp).to.be.null | ||
613 | |||
601 | const res3 = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 2) | 614 | const res3 = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 2) |
602 | expect(res3.body.data).to.have.lengthOf(2) | 615 | expect(res3.body.data).to.have.lengthOf(2) |
603 | } | 616 | } |
@@ -807,6 +820,8 @@ describe('Test video playlists', function () { | |||
807 | 'video 1 server 3', | 820 | 'video 1 server 3', |
808 | 'video 3 server 1', | 821 | 'video 3 server 1', |
809 | 'video 4 server 1', | 822 | 'video 4 server 1', |
823 | 'NSFW video', | ||
824 | 'NSFW video', | ||
810 | 'NSFW video' | 825 | 'NSFW video' |
811 | ]) | 826 | ]) |
812 | } | 827 | } |
@@ -836,6 +851,8 @@ describe('Test video playlists', function () { | |||
836 | 'video 2 server 3', | 851 | 'video 2 server 3', |
837 | 'video 1 server 3', | 852 | 'video 1 server 3', |
838 | 'video 4 server 1', | 853 | 'video 4 server 1', |
854 | 'NSFW video', | ||
855 | 'NSFW video', | ||
839 | 'NSFW video' | 856 | 'NSFW video' |
840 | ]) | 857 | ]) |
841 | } | 858 | } |
@@ -865,7 +882,9 @@ describe('Test video playlists', function () { | |||
865 | 'video 2 server 3', | 882 | 'video 2 server 3', |
866 | 'NSFW video', | 883 | 'NSFW video', |
867 | 'video 1 server 3', | 884 | 'video 1 server 3', |
868 | 'video 4 server 1' | 885 | 'video 4 server 1', |
886 | 'NSFW video', | ||
887 | 'NSFW video' | ||
869 | ]) | 888 | ]) |
870 | 889 | ||
871 | for (let i = 1; i <= elements.length; i++) { | 890 | for (let i = 1; i <= elements.length; i++) { |
@@ -1023,10 +1042,10 @@ describe('Test video playlists', function () { | |||
1023 | for (const server of servers) { | 1042 | for (const server of servers) { |
1024 | const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10) | 1043 | const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10) |
1025 | 1044 | ||
1026 | expect(res.body.total).to.equal(4) | 1045 | expect(res.body.total).to.equal(6) |
1027 | 1046 | ||
1028 | const elements: VideoPlaylistElement[] = res.body.data | 1047 | const elements: VideoPlaylistElement[] = res.body.data |
1029 | expect(elements).to.have.lengthOf(4) | 1048 | expect(elements).to.have.lengthOf(6) |
1030 | 1049 | ||
1031 | expect(elements[0].video.name).to.equal('video 0 server 1') | 1050 | expect(elements[0].video.name).to.equal('video 0 server 1') |
1032 | expect(elements[0].position).to.equal(1) | 1051 | expect(elements[0].position).to.equal(1) |
@@ -1039,6 +1058,12 @@ describe('Test video playlists', function () { | |||
1039 | 1058 | ||
1040 | expect(elements[3].video.name).to.equal('video 4 server 1') | 1059 | expect(elements[3].video.name).to.equal('video 4 server 1') |
1041 | expect(elements[3].position).to.equal(4) | 1060 | expect(elements[3].position).to.equal(4) |
1061 | |||
1062 | expect(elements[4].video.name).to.equal('NSFW video') | ||
1063 | expect(elements[4].position).to.equal(5) | ||
1064 | |||
1065 | expect(elements[5].video.name).to.equal('NSFW video') | ||
1066 | expect(elements[5].position).to.equal(6) | ||
1042 | } | 1067 | } |
1043 | }) | 1068 | }) |
1044 | 1069 | ||