aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--server/controllers/api/video-playlist.ts16
-rw-r--r--server/helpers/custom-validators/video-channels.ts6
-rw-r--r--server/lib/activitypub/index.ts1
-rw-r--r--server/lib/activitypub/url.ts2
-rw-r--r--server/middlewares/validators/videos/video-playlists.ts44
-rw-r--r--server/models/video/video-channel.ts2
-rw-r--r--server/models/video/video-playlist-element.ts3
-rw-r--r--server/models/video/video-playlist.ts4
-rw-r--r--server/tests/api/check-params/video-playlists.ts780
-rw-r--r--server/tests/api/check-params/videos-filter.ts33
-rw-r--r--server/tests/api/redundancy/redundancy.ts14
-rw-r--r--shared/models/videos/playlist/video-playlist-create.model.ts4
-rw-r--r--shared/models/videos/playlist/video-playlist-element-create.model.ts2
-rw-r--r--shared/models/videos/playlist/video-playlist-update.model.ts4
-rw-r--r--shared/utils/index.ts3
-rw-r--r--shared/utils/videos/video-playlists.ts30
16 files changed, 883 insertions, 65 deletions
diff --git a/server/controllers/api/video-playlist.ts b/server/controllers/api/video-playlist.ts
index 709c58beb..e026b4d16 100644
--- a/server/controllers/api/video-playlist.ts
+++ b/server/controllers/api/video-playlist.ts
@@ -4,7 +4,7 @@ import {
4 asyncMiddleware, 4 asyncMiddleware,
5 asyncRetryTransactionMiddleware, 5 asyncRetryTransactionMiddleware,
6 authenticate, 6 authenticate,
7 commonVideosFiltersValidator, 7 commonVideosFiltersValidator, optionalAuthenticate,
8 paginationValidator, 8 paginationValidator,
9 setDefaultPagination, 9 setDefaultPagination,
10 setDefaultSort 10 setDefaultSort
@@ -31,12 +31,14 @@ import { processImage } from '../../helpers/image-utils'
31import { join } from 'path' 31import { join } from 'path'
32import { UserModel } from '../../models/account/user' 32import { UserModel } from '../../models/account/user'
33import { 33import {
34 getVideoPlaylistActivityPubUrl,
35 getVideoPlaylistElementActivityPubUrl,
36 sendCreateVideoPlaylist, 34 sendCreateVideoPlaylist,
37 sendDeleteVideoPlaylist, 35 sendDeleteVideoPlaylist,
38 sendUpdateVideoPlaylist 36 sendUpdateVideoPlaylist
39} from '../../lib/activitypub' 37} from '../../lib/activitypub/send'
38import {
39 getVideoPlaylistActivityPubUrl,
40 getVideoPlaylistElementActivityPubUrl
41} from '../../lib/activitypub/url'
40import { VideoPlaylistUpdate } from '../../../shared/models/videos/playlist/video-playlist-update.model' 42import { VideoPlaylistUpdate } from '../../../shared/models/videos/playlist/video-playlist-update.model'
41import { VideoModel } from '../../models/video/video' 43import { VideoModel } from '../../models/video/video'
42import { VideoPlaylistElementModel } from '../../models/video/video-playlist-element' 44import { VideoPlaylistElementModel } from '../../models/video/video-playlist-element'
@@ -85,6 +87,7 @@ videoPlaylistRouter.get('/:playlistId/videos',
85 asyncMiddleware(videoPlaylistsGetValidator), 87 asyncMiddleware(videoPlaylistsGetValidator),
86 paginationValidator, 88 paginationValidator,
87 setDefaultPagination, 89 setDefaultPagination,
90 optionalAuthenticate,
88 commonVideosFiltersValidator, 91 commonVideosFiltersValidator,
89 asyncMiddleware(getVideoPlaylistVideos) 92 asyncMiddleware(getVideoPlaylistVideos)
90) 93)
@@ -95,7 +98,7 @@ videoPlaylistRouter.post('/:playlistId/videos',
95 asyncRetryTransactionMiddleware(addVideoInPlaylist) 98 asyncRetryTransactionMiddleware(addVideoInPlaylist)
96) 99)
97 100
98videoPlaylistRouter.put('/:playlistId/videos', 101videoPlaylistRouter.post('/:playlistId/videos/reorder',
99 authenticate, 102 authenticate,
100 asyncMiddleware(videoPlaylistsReorderVideosValidator), 103 asyncMiddleware(videoPlaylistsReorderVideosValidator),
101 asyncRetryTransactionMiddleware(reorderVideosPlaylist) 104 asyncRetryTransactionMiddleware(reorderVideosPlaylist)
@@ -168,6 +171,7 @@ async function addVideoPlaylist (req: express.Request, res: express.Response) {
168 const videoPlaylistCreated: VideoPlaylistModel = await sequelizeTypescript.transaction(async t => { 171 const videoPlaylistCreated: VideoPlaylistModel = await sequelizeTypescript.transaction(async t => {
169 const videoPlaylistCreated = await videoPlaylist.save({ transaction: t }) 172 const videoPlaylistCreated = await videoPlaylist.save({ transaction: t })
170 173
174 videoPlaylistCreated.OwnerAccount = user.Account
171 await sendCreateVideoPlaylist(videoPlaylistCreated, t) 175 await sendCreateVideoPlaylist(videoPlaylistCreated, t)
172 176
173 return videoPlaylistCreated 177 return videoPlaylistCreated
@@ -349,7 +353,7 @@ async function reorderVideosPlaylist (req: express.Request, res: express.Respons
349 const videoPlaylist: VideoPlaylistModel = res.locals.videoPlaylist 353 const videoPlaylist: VideoPlaylistModel = res.locals.videoPlaylist
350 354
351 const start: number = req.body.startPosition 355 const start: number = req.body.startPosition
352 const insertAfter: number = req.body.insertAfter 356 const insertAfter: number = req.body.insertAfterPosition
353 const reorderLength: number = req.body.reorderLength || 1 357 const reorderLength: number = req.body.reorderLength || 1
354 358
355 if (start === insertAfter) { 359 if (start === insertAfter) {
diff --git a/server/helpers/custom-validators/video-channels.ts b/server/helpers/custom-validators/video-channels.ts
index cbf150e53..3792bbdcc 100644
--- a/server/helpers/custom-validators/video-channels.ts
+++ b/server/helpers/custom-validators/video-channels.ts
@@ -26,12 +26,12 @@ async function isLocalVideoChannelNameExist (name: string, res: express.Response
26 return processVideoChannelExist(videoChannel, res) 26 return processVideoChannelExist(videoChannel, res)
27} 27}
28 28
29async function isVideoChannelIdExist (id: string, res: express.Response) { 29async function isVideoChannelIdExist (id: number | string, res: express.Response) {
30 let videoChannel: VideoChannelModel 30 let videoChannel: VideoChannelModel
31 if (validator.isInt(id)) { 31 if (validator.isInt('' + id)) {
32 videoChannel = await VideoChannelModel.loadAndPopulateAccount(+id) 32 videoChannel = await VideoChannelModel.loadAndPopulateAccount(+id)
33 } else { // UUID 33 } else { // UUID
34 videoChannel = await VideoChannelModel.loadByUUIDAndPopulateAccount(id) 34 videoChannel = await VideoChannelModel.loadByUUIDAndPopulateAccount('' + id)
35 } 35 }
36 36
37 return processVideoChannelExist(videoChannel, res) 37 return processVideoChannelExist(videoChannel, res)
diff --git a/server/lib/activitypub/index.ts b/server/lib/activitypub/index.ts
index 6906bf9d3..d8c7d83b7 100644
--- a/server/lib/activitypub/index.ts
+++ b/server/lib/activitypub/index.ts
@@ -2,6 +2,7 @@ export * from './process'
2export * from './send' 2export * from './send'
3export * from './actor' 3export * from './actor'
4export * from './share' 4export * from './share'
5export * from './playlist'
5export * from './videos' 6export * from './videos'
6export * from './video-comments' 7export * from './video-comments'
7export * from './video-rates' 8export * from './video-rates'
diff --git a/server/lib/activitypub/url.ts b/server/lib/activitypub/url.ts
index 00bbbba2d..c80b09436 100644
--- a/server/lib/activitypub/url.ts
+++ b/server/lib/activitypub/url.ts
@@ -5,10 +5,8 @@ import { VideoModel } from '../../models/video/video'
5import { VideoAbuseModel } from '../../models/video/video-abuse' 5import { VideoAbuseModel } from '../../models/video/video-abuse'
6import { VideoCommentModel } from '../../models/video/video-comment' 6import { VideoCommentModel } from '../../models/video/video-comment'
7import { VideoFileModel } from '../../models/video/video-file' 7import { VideoFileModel } from '../../models/video/video-file'
8import { VideoStreamingPlaylist } from '../../../shared/models/videos/video-streaming-playlist.model'
9import { VideoStreamingPlaylistModel } from '../../models/video/video-streaming-playlist' 8import { VideoStreamingPlaylistModel } from '../../models/video/video-streaming-playlist'
10import { VideoPlaylistModel } from '../../models/video/video-playlist' 9import { VideoPlaylistModel } from '../../models/video/video-playlist'
11import { VideoPlaylistElementModel } from '../../models/video/video-playlist-element'
12 10
13function getVideoActivityPubUrl (video: VideoModel) { 11function getVideoActivityPubUrl (video: VideoModel) {
14 return CONFIG.WEBSERVER.URL + '/videos/watch/' + video.uuid 12 return CONFIG.WEBSERVER.URL + '/videos/watch/' + video.uuid
diff --git a/server/middlewares/validators/videos/video-playlists.ts b/server/middlewares/validators/videos/video-playlists.ts
index ef8d0b851..0e97c8dc0 100644
--- a/server/middlewares/validators/videos/video-playlists.ts
+++ b/server/middlewares/validators/videos/video-playlists.ts
@@ -6,7 +6,7 @@ import { UserModel } from '../../../models/account/user'
6import { areValidationErrors } from '../utils' 6import { areValidationErrors } from '../utils'
7import { isVideoExist, isVideoImage } from '../../../helpers/custom-validators/videos' 7import { isVideoExist, isVideoImage } from '../../../helpers/custom-validators/videos'
8import { CONSTRAINTS_FIELDS } from '../../../initializers' 8import { CONSTRAINTS_FIELDS } from '../../../initializers'
9import { isIdOrUUIDValid, toValueOrNull } from '../../../helpers/custom-validators/misc' 9import { isIdOrUUIDValid, isUUIDValid, toValueOrNull } from '../../../helpers/custom-validators/misc'
10import { 10import {
11 isVideoPlaylistDescriptionValid, 11 isVideoPlaylistDescriptionValid,
12 isVideoPlaylistExist, 12 isVideoPlaylistExist,
@@ -43,10 +43,19 @@ const videoPlaylistsUpdateValidator = getCommonPlaylistEditAttributes().concat([
43 if (areValidationErrors(req, res)) return cleanUpReqFiles(req) 43 if (areValidationErrors(req, res)) return cleanUpReqFiles(req)
44 44
45 if (!await isVideoPlaylistExist(req.params.playlistId, res)) return cleanUpReqFiles(req) 45 if (!await isVideoPlaylistExist(req.params.playlistId, res)) return cleanUpReqFiles(req)
46
47 const videoPlaylist = res.locals.videoPlaylist
48
46 if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, res.locals.videoPlaylist, UserRight.REMOVE_ANY_VIDEO_PLAYLIST, res)) { 49 if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, res.locals.videoPlaylist, UserRight.REMOVE_ANY_VIDEO_PLAYLIST, res)) {
47 return cleanUpReqFiles(req) 50 return cleanUpReqFiles(req)
48 } 51 }
49 52
53 if (videoPlaylist.privacy !== VideoPlaylistPrivacy.PRIVATE && req.body.privacy === VideoPlaylistPrivacy.PRIVATE) {
54 cleanUpReqFiles(req)
55 return res.status(409)
56 .json({ error: 'Cannot set "private" a video playlist that was not private.' })
57 }
58
50 if (req.body.videoChannelId && !await isVideoChannelIdExist(req.body.videoChannelId, res)) return cleanUpReqFiles(req) 59 if (req.body.videoChannelId && !await isVideoChannelIdExist(req.body.videoChannelId, res)) return cleanUpReqFiles(req)
51 60
52 return next() 61 return next()
@@ -83,6 +92,14 @@ const videoPlaylistsGetValidator = [
83 if (!await isVideoPlaylistExist(req.params.playlistId, res)) return 92 if (!await isVideoPlaylistExist(req.params.playlistId, res)) return
84 93
85 const videoPlaylist: VideoPlaylistModel = res.locals.videoPlaylist 94 const videoPlaylist: VideoPlaylistModel = res.locals.videoPlaylist
95
96 // Video is unlisted, check we used the uuid to fetch it
97 if (videoPlaylist.privacy === VideoPlaylistPrivacy.UNLISTED) {
98 if (isUUIDValid(req.params.playlistId)) return next()
99
100 return res.status(404).end()
101 }
102
86 if (videoPlaylist.privacy === VideoPlaylistPrivacy.PRIVATE) { 103 if (videoPlaylist.privacy === VideoPlaylistPrivacy.PRIVATE) {
87 await authenticatePromiseIfNeeded(req, res) 104 await authenticatePromiseIfNeeded(req, res)
88 105
@@ -121,7 +138,7 @@ const videoPlaylistsAddVideoValidator = [
121 if (areValidationErrors(req, res)) return 138 if (areValidationErrors(req, res)) return
122 139
123 if (!await isVideoPlaylistExist(req.params.playlistId, res)) return 140 if (!await isVideoPlaylistExist(req.params.playlistId, res)) return
124 if (!await isVideoExist(req.body.videoId, res, 'id')) return 141 if (!await isVideoExist(req.body.videoId, res, 'only-video')) return
125 142
126 const videoPlaylist: VideoPlaylistModel = res.locals.videoPlaylist 143 const videoPlaylist: VideoPlaylistModel = res.locals.videoPlaylist
127 const video: VideoModel = res.locals.video 144 const video: VideoModel = res.locals.video
@@ -161,7 +178,7 @@ const videoPlaylistsUpdateOrRemoveVideoValidator = [
161 if (areValidationErrors(req, res)) return 178 if (areValidationErrors(req, res)) return
162 179
163 if (!await isVideoPlaylistExist(req.params.playlistId, res)) return 180 if (!await isVideoPlaylistExist(req.params.playlistId, res)) return
164 if (!await isVideoExist(req.params.playlistId, res, 'id')) return 181 if (!await isVideoExist(req.params.videoId, res, 'id')) return
165 182
166 const videoPlaylist: VideoPlaylistModel = res.locals.videoPlaylist 183 const videoPlaylist: VideoPlaylistModel = res.locals.videoPlaylist
167 const video: VideoModel = res.locals.video 184 const video: VideoModel = res.locals.video
@@ -233,6 +250,27 @@ const videoPlaylistsReorderVideosValidator = [
233 const videoPlaylist: VideoPlaylistModel = res.locals.videoPlaylist 250 const videoPlaylist: VideoPlaylistModel = res.locals.videoPlaylist
234 if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, videoPlaylist, UserRight.UPDATE_ANY_VIDEO_PLAYLIST, res)) return 251 if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, videoPlaylist, UserRight.UPDATE_ANY_VIDEO_PLAYLIST, res)) return
235 252
253 const nextPosition = await VideoPlaylistElementModel.getNextPositionOf(videoPlaylist.id)
254 const startPosition: number = req.body.startPosition
255 const insertAfterPosition: number = req.body.insertAfterPosition
256 const reorderLength: number = req.body.reorderLength
257
258 if (startPosition >= nextPosition || insertAfterPosition >= nextPosition) {
259 res.status(400)
260 .json({ error: `Start position or insert after position exceed the playlist limits (max: ${nextPosition - 1})` })
261 .end()
262
263 return
264 }
265
266 if (reorderLength && reorderLength + startPosition > nextPosition) {
267 res.status(400)
268 .json({ error: `Reorder length with this start position exceeds the playlist limits (max: ${nextPosition - startPosition})` })
269 .end()
270
271 return
272 }
273
236 return next() 274 return next()
237 } 275 }
238] 276]
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts
index 112abf8cf..c077fb518 100644
--- a/server/models/video/video-channel.ts
+++ b/server/models/video/video-channel.ts
@@ -223,7 +223,7 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
223 223
224 @HasMany(() => VideoPlaylistModel, { 224 @HasMany(() => VideoPlaylistModel, {
225 foreignKey: { 225 foreignKey: {
226 allowNull: false 226 allowNull: true
227 }, 227 },
228 onDelete: 'cascade', 228 onDelete: 'cascade',
229 hooks: true 229 hooks: true
diff --git a/server/models/video/video-playlist-element.ts b/server/models/video/video-playlist-element.ts
index d76149d12..5530e0492 100644
--- a/server/models/video/video-playlist-element.ts
+++ b/server/models/video/video-playlist-element.ts
@@ -188,7 +188,8 @@ export class VideoPlaylistElementModel extends Model<VideoPlaylistElementModel>
188 [Sequelize.Op.lte]: endPosition 188 [Sequelize.Op.lte]: endPosition
189 } 189 }
190 }, 190 },
191 transaction 191 transaction,
192 validate: false // We use a literal to update the position
192 } 193 }
193 194
194 return VideoPlaylistElementModel.update({ position: Sequelize.literal(`${newPosition} + "position" - ${firstPosition}`) }, query) 195 return VideoPlaylistElementModel.update({ position: Sequelize.literal(`${newPosition} + "position" - ${firstPosition}`) }, query)
diff --git a/server/models/video/video-playlist.ts b/server/models/video/video-playlist.ts
index 93b8c2f58..397887ebf 100644
--- a/server/models/video/video-playlist.ts
+++ b/server/models/video/video-playlist.ts
@@ -197,7 +197,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> {
197 197
198 @BelongsTo(() => VideoChannelModel, { 198 @BelongsTo(() => VideoChannelModel, {
199 foreignKey: { 199 foreignKey: {
200 allowNull: false 200 allowNull: true
201 }, 201 },
202 onDelete: 'CASCADE' 202 onDelete: 'CASCADE'
203 }) 203 })
@@ -351,7 +351,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> {
351 updatedAt: this.updatedAt, 351 updatedAt: this.updatedAt,
352 352
353 ownerAccount: this.OwnerAccount.toFormattedSummaryJSON(), 353 ownerAccount: this.OwnerAccount.toFormattedSummaryJSON(),
354 videoChannel: this.VideoChannel.toFormattedSummaryJSON() 354 videoChannel: this.VideoChannel ? this.VideoChannel.toFormattedSummaryJSON() : null
355 } 355 }
356 } 356 }
357 357
diff --git a/server/tests/api/check-params/video-playlists.ts b/server/tests/api/check-params/video-playlists.ts
index 7004badac..68fe362e9 100644
--- a/server/tests/api/check-params/video-playlists.ts
+++ b/server/tests/api/check-params/video-playlists.ts
@@ -1,35 +1,35 @@
1/* tslint:disable:no-unused-expression */ 1/* tslint:disable:no-unused-expression */
2 2
3import { omit } from 'lodash'
4import 'mocha' 3import 'mocha'
5import { join } from 'path'
6import { VideoPrivacy } from '../../../../shared/models/videos/video-privacy.enum'
7import { 4import {
8 createUser, 5 createUser,
6 createVideoPlaylist,
7 deleteVideoPlaylist,
9 flushTests, 8 flushTests,
10 getMyUserInformation, 9 getVideoPlaylist,
11 immutableAssign, 10 immutableAssign,
12 killallServers, 11 killallServers,
13 makeGetRequest, 12 makeGetRequest,
14 makePostBodyRequest,
15 makeUploadRequest,
16 runServer, 13 runServer,
17 ServerInfo, 14 ServerInfo,
18 setAccessTokensToServers, 15 setAccessTokensToServers,
19 updateCustomSubConfig, 16 updateVideoPlaylist,
20 userLogin 17 userLogin,
18 addVideoInPlaylist, uploadVideo, updateVideoPlaylistElement, removeVideoFromPlaylist, reorderVideosPlaylist
21} from '../../../../shared/utils' 19} from '../../../../shared/utils'
22import { 20import {
23 checkBadCountPagination, 21 checkBadCountPagination,
24 checkBadSortPagination, 22 checkBadSortPagination,
25 checkBadStartPagination 23 checkBadStartPagination
26} from '../../../../shared/utils/requests/check-api-params' 24} from '../../../../shared/utils/requests/check-api-params'
27import { getMagnetURI, getYoutubeVideoUrl } from '../../../../shared/utils/videos/video-imports' 25import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
28 26
29describe('Test video playlists API validator', function () { 27describe('Test video playlists API validator', function () {
30 const path = '/api/v1/videos/video-playlists'
31 let server: ServerInfo 28 let server: ServerInfo
32 let userAccessToken = '' 29 let userAccessToken = ''
30 let playlistUUID: string
31 let videoId: number
32 let videoId2: number
33 33
34 // --------------------------------------------------------------- 34 // ---------------------------------------------------------------
35 35
@@ -46,9 +46,31 @@ describe('Test video playlists API validator', function () {
46 const password = 'my super password' 46 const password = 'my super password'
47 await createUser(server.url, server.accessToken, username, password) 47 await createUser(server.url, server.accessToken, username, password)
48 userAccessToken = await userLogin(server, { username, password }) 48 userAccessToken = await userLogin(server, { username, password })
49
50 {
51 const res = await uploadVideo(server.url, server.accessToken, { name: 'video 1' })
52 videoId = res.body.video.id
53 }
54
55 {
56 const res = await uploadVideo(server.url, server.accessToken, { name: 'video 2' })
57 videoId2 = res.body.video.id
58 }
59
60 {
61 const res = await createVideoPlaylist({
62 url: server.url,
63 token: server.accessToken,
64 playlistAttrs: {
65 displayName: 'super playlist',
66 privacy: VideoPlaylistPrivacy.PUBLIC
67 }
68 })
69 playlistUUID = res.body.videoPlaylist.uuid
70 }
49 }) 71 })
50 72
51 describe('When listing video playlists', function () { 73 describe('When listing playlists', function () {
52 const globalPath = '/api/v1/video-playlists' 74 const globalPath = '/api/v1/video-playlists'
53 const accountPath = '/api/v1/accounts/root/video-playlists' 75 const accountPath = '/api/v1/accounts/root/video-playlists'
54 const videoChannelPath = '/api/v1/video-channels/root_channel/video-playlists' 76 const videoChannelPath = '/api/v1/video-channels/root_channel/video-playlists'
@@ -90,7 +112,7 @@ describe('Test video playlists API validator', function () {
90 }) 112 })
91 }) 113 })
92 114
93 describe('When listing videos of a playlist', async function () { 115 describe('When listing videos of a playlist', function () {
94 const path = '/api/v1/video-playlists' 116 const path = '/api/v1/video-playlists'
95 117
96 it('Should fail with a bad start pagination', async function () { 118 it('Should fail with a bad start pagination', async function () {
@@ -101,11 +123,743 @@ describe('Test video playlists API validator', function () {
101 await checkBadCountPagination(server.url, path, server.accessToken) 123 await checkBadCountPagination(server.url, path, server.accessToken)
102 }) 124 })
103 125
104 it('Should fail with an incorrect sort', async function () { 126 it('Should fail with a bad filter', async function () {
105 await checkBadSortPagination(server.url, path, server.accessToken) 127 await checkBadSortPagination(server.url, path, server.accessToken)
106 }) 128 })
107 }) 129 })
108 130
131 describe('When getting a video playlist', function () {
132 it('Should fail with a bad id or uuid', async function () {
133 await getVideoPlaylist(server.url, 'toto', 400)
134 })
135
136 it('Should fail with an unknown playlist', async function () {
137 await getVideoPlaylist(server.url, 42, 404)
138 })
139
140 it('Should fail to get an unlisted playlist with the number id', async function () {
141 const res = await createVideoPlaylist({
142 url: server.url,
143 token: server.accessToken,
144 playlistAttrs: {
145 displayName: 'super playlist',
146 privacy: VideoPlaylistPrivacy.UNLISTED
147 }
148 })
149 const playlist = res.body.videoPlaylist
150
151 await getVideoPlaylist(server.url, playlist.id, 404)
152 await getVideoPlaylist(server.url, playlist.uuid, 200)
153 })
154
155 it('Should succeed with the correct params', async function () {
156 await getVideoPlaylist(server.url, playlistUUID, 200)
157 })
158 })
159
160 describe('When creating/updating a video playlist', function () {
161
162 it('Should fail with an unauthenticated user', async function () {
163 const baseParams = {
164 url: server.url,
165 token: null,
166 playlistAttrs: {
167 displayName: 'super playlist',
168 privacy: VideoPlaylistPrivacy.PUBLIC
169 },
170 expectedStatus: 401
171 }
172
173 await createVideoPlaylist(baseParams)
174 await updateVideoPlaylist(immutableAssign(baseParams, { playlistId: playlistUUID }))
175 })
176
177 it('Should fail without displayName', async function () {
178 const baseParams = {
179 url: server.url,
180 token: server.accessToken,
181 playlistAttrs: {
182 privacy: VideoPlaylistPrivacy.PUBLIC
183 } as any,
184 expectedStatus: 400
185 }
186
187 await createVideoPlaylist(baseParams)
188 await updateVideoPlaylist(immutableAssign(baseParams, { playlistId: playlistUUID }))
189 })
190
191 it('Should fail with an incorrect display name', async function () {
192 const baseParams = {
193 url: server.url,
194 token: server.accessToken,
195 playlistAttrs: {
196 displayName: 's'.repeat(300),
197 privacy: VideoPlaylistPrivacy.PUBLIC
198 },
199 expectedStatus: 400
200 }
201
202 await createVideoPlaylist(baseParams)
203 await updateVideoPlaylist(immutableAssign(baseParams, { playlistId: playlistUUID }))
204 })
205
206 it('Should fail with an incorrect description', async function () {
207 const baseParams = {
208 url: server.url,
209 token: server.accessToken,
210 playlistAttrs: {
211 displayName: 'display name',
212 privacy: VideoPlaylistPrivacy.PUBLIC,
213 description: 't'
214 },
215 expectedStatus: 400
216 }
217
218 await createVideoPlaylist(baseParams)
219 await updateVideoPlaylist(immutableAssign(baseParams, { playlistId: playlistUUID }))
220 })
221
222 it('Should fail with an incorrect privacy', async function () {
223 const baseParams = {
224 url: server.url,
225 token: server.accessToken,
226 playlistAttrs: {
227 displayName: 'display name',
228 privacy: 45
229 } as any,
230 expectedStatus: 400
231 }
232
233 await createVideoPlaylist(baseParams)
234 await updateVideoPlaylist(immutableAssign(baseParams, { playlistId: playlistUUID }))
235 })
236
237 it('Should fail with an unknown video channel id', async function () {
238 const baseParams = {
239 url: server.url,
240 token: server.accessToken,
241 playlistAttrs: {
242 displayName: 'display name',
243 privacy: VideoPlaylistPrivacy.PUBLIC,
244 videoChannelId: 42
245 },
246 expectedStatus: 404
247 }
248
249 await createVideoPlaylist(baseParams)
250 await updateVideoPlaylist(immutableAssign(baseParams, { playlistId: playlistUUID }))
251 })
252
253 it('Should fail with an incorrect thumbnail file', async function () {
254 const baseParams = {
255 url: server.url,
256 token: server.accessToken,
257 playlistAttrs: {
258 displayName: 'display name',
259 privacy: VideoPlaylistPrivacy.PUBLIC,
260 thumbnailfile: 'avatar.png'
261 },
262 expectedStatus: 400
263 }
264
265 await createVideoPlaylist(baseParams)
266 await updateVideoPlaylist(immutableAssign(baseParams, { playlistId: playlistUUID }))
267 })
268
269 it('Should fail with an unknown playlist to update', async function () {
270 await updateVideoPlaylist({
271 url: server.url,
272 token: server.accessToken,
273 playlistId: 42,
274 playlistAttrs: {
275 displayName: 'display name',
276 privacy: VideoPlaylistPrivacy.PUBLIC
277 },
278 expectedStatus: 404
279 })
280 })
281
282 it('Should fail to update a playlist of another user', async function () {
283 await updateVideoPlaylist({
284 url: server.url,
285 token: userAccessToken,
286 playlistId: playlistUUID,
287 playlistAttrs: {
288 displayName: 'display name',
289 privacy: VideoPlaylistPrivacy.PUBLIC
290 },
291 expectedStatus: 403
292 })
293 })
294
295 it('Should fail to update to private a public/unlisted playlist', async function () {
296 const res = await createVideoPlaylist({
297 url: server.url,
298 token: server.accessToken,
299 playlistAttrs: {
300 displayName: 'super playlist',
301 privacy: VideoPlaylistPrivacy.PUBLIC
302 }
303 })
304 const playlist = res.body.videoPlaylist
305
306 await updateVideoPlaylist({
307 url: server.url,
308 token: server.accessToken,
309 playlistId: playlist.id,
310 playlistAttrs: {
311 displayName: 'display name',
312 privacy: VideoPlaylistPrivacy.PRIVATE
313 },
314 expectedStatus: 409
315 })
316 })
317
318 it('Should succeed with the correct params', async function () {
319 const baseParams = {
320 url: server.url,
321 token: server.accessToken,
322 playlistAttrs: {
323 displayName: 'display name',
324 privacy: VideoPlaylistPrivacy.UNLISTED,
325 thumbnailfile: 'thumbnail.jpg'
326 }
327 }
328
329 await createVideoPlaylist(baseParams)
330 await updateVideoPlaylist(immutableAssign(baseParams, { playlistId: playlistUUID }))
331 })
332 })
333
334 describe('When adding an element in a playlist', function () {
335 it('Should fail with an unauthenticated user', async function () {
336 await addVideoInPlaylist({
337 url: server.url,
338 token: null,
339 elementAttrs: {
340 videoId: videoId
341 },
342 playlistId: playlistUUID,
343 expectedStatus: 401
344 })
345 })
346
347 it('Should fail with the playlist of another user', async function () {
348 await addVideoInPlaylist({
349 url: server.url,
350 token: userAccessToken,
351 elementAttrs: {
352 videoId: videoId
353 },
354 playlistId: playlistUUID,
355 expectedStatus: 403
356 })
357 })
358
359 it('Should fail with an unknown or incorrect playlist id', async function () {
360 await addVideoInPlaylist({
361 url: server.url,
362 token: server.accessToken,
363 elementAttrs: {
364 videoId: videoId
365 },
366 playlistId: 'toto',
367 expectedStatus: 400
368 })
369
370 await addVideoInPlaylist({
371 url: server.url,
372 token: server.accessToken,
373 elementAttrs: {
374 videoId: videoId
375 },
376 playlistId: 42,
377 expectedStatus: 404
378 })
379 })
380
381 it('Should fail with an unknown or incorrect video id', async function () {
382 await addVideoInPlaylist({
383 url: server.url,
384 token: server.accessToken,
385 elementAttrs: {
386 videoId: 'toto' as any
387 },
388 playlistId: playlistUUID,
389 expectedStatus: 400
390 })
391
392 await addVideoInPlaylist({
393 url: server.url,
394 token: server.accessToken,
395 elementAttrs: {
396 videoId: 42
397 },
398 playlistId: playlistUUID,
399 expectedStatus: 404
400 })
401 })
402
403 it('Should fail with a bad start/stop timestamp', async function () {
404 await addVideoInPlaylist({
405 url: server.url,
406 token: server.accessToken,
407 elementAttrs: {
408 videoId: videoId,
409 startTimestamp: -42
410 },
411 playlistId: playlistUUID,
412 expectedStatus: 400
413 })
414
415 await addVideoInPlaylist({
416 url: server.url,
417 token: server.accessToken,
418 elementAttrs: {
419 videoId: videoId,
420 stopTimestamp: 'toto' as any
421 },
422 playlistId: playlistUUID,
423 expectedStatus: 400
424 })
425 })
426
427 it('Succeed with the correct params', async function () {
428 await addVideoInPlaylist({
429 url: server.url,
430 token: server.accessToken,
431 elementAttrs: {
432 videoId: videoId,
433 stopTimestamp: 3
434 },
435 playlistId: playlistUUID,
436 expectedStatus: 200
437 })
438 })
439
440 it('Should fail if the video was already added in the playlist', async function () {
441 await addVideoInPlaylist({
442 url: server.url,
443 token: server.accessToken,
444 elementAttrs: {
445 videoId: videoId,
446 stopTimestamp: 3
447 },
448 playlistId: playlistUUID,
449 expectedStatus: 409
450 })
451 })
452 })
453
454 describe('When updating an element in a playlist', function () {
455 it('Should fail with an unauthenticated user', async function () {
456 await updateVideoPlaylistElement({
457 url: server.url,
458 token: null,
459 elementAttrs: { },
460 videoId: videoId,
461 playlistId: playlistUUID,
462 expectedStatus: 401
463 })
464 })
465
466 it('Should fail with the playlist of another user', async function () {
467 await updateVideoPlaylistElement({
468 url: server.url,
469 token: userAccessToken,
470 elementAttrs: { },
471 videoId: videoId,
472 playlistId: playlistUUID,
473 expectedStatus: 403
474 })
475 })
476
477 it('Should fail with an unknown or incorrect playlist id', async function () {
478 await updateVideoPlaylistElement({
479 url: server.url,
480 token: server.accessToken,
481 elementAttrs: { },
482 videoId: videoId,
483 playlistId: 'toto',
484 expectedStatus: 400
485 })
486
487 await updateVideoPlaylistElement({
488 url: server.url,
489 token: server.accessToken,
490 elementAttrs: { },
491 videoId: videoId,
492 playlistId: 42,
493 expectedStatus: 404
494 })
495 })
496
497 it('Should fail with an unknown or incorrect video id', async function () {
498 await updateVideoPlaylistElement({
499 url: server.url,
500 token: server.accessToken,
501 elementAttrs: { },
502 videoId: 'toto',
503 playlistId: playlistUUID,
504 expectedStatus: 400
505 })
506
507 await updateVideoPlaylistElement({
508 url: server.url,
509 token: server.accessToken,
510 elementAttrs: { },
511 videoId: 42,
512 playlistId: playlistUUID,
513 expectedStatus: 404
514 })
515 })
516
517 it('Should fail with a bad start/stop timestamp', async function () {
518 await updateVideoPlaylistElement({
519 url: server.url,
520 token: server.accessToken,
521 elementAttrs: {
522 startTimestamp: 'toto' as any
523 },
524 videoId: videoId,
525 playlistId: playlistUUID,
526 expectedStatus: 400
527 })
528
529 await updateVideoPlaylistElement({
530 url: server.url,
531 token: server.accessToken,
532 elementAttrs: {
533 stopTimestamp: -42
534 },
535 videoId: videoId,
536 playlistId: playlistUUID,
537 expectedStatus: 400
538 })
539 })
540
541 it('Should fail with an unknown element', async function () {
542 await updateVideoPlaylistElement({
543 url: server.url,
544 token: server.accessToken,
545 elementAttrs: {
546 stopTimestamp: 2
547 },
548 videoId: videoId2,
549 playlistId: playlistUUID,
550 expectedStatus: 404
551 })
552 })
553
554 it('Succeed with the correct params', async function () {
555 await updateVideoPlaylistElement({
556 url: server.url,
557 token: server.accessToken,
558 elementAttrs: {
559 stopTimestamp: 2
560 },
561 videoId: videoId,
562 playlistId: playlistUUID,
563 expectedStatus: 204
564 })
565 })
566 })
567
568 describe('When reordering elements of a playlist', function () {
569 let videoId3: number
570 let videoId4: number
571
572 before(async function () {
573 {
574 const res = await uploadVideo(server.url, server.accessToken, { name: 'video 3' })
575 videoId3 = res.body.video.id
576 }
577
578 {
579 const res = await uploadVideo(server.url, server.accessToken, { name: 'video 4' })
580 videoId4 = res.body.video.id
581 }
582
583 await addVideoInPlaylist({
584 url: server.url,
585 token: server.accessToken,
586 playlistId: playlistUUID,
587 elementAttrs: { videoId: videoId3 }
588 })
589
590 await addVideoInPlaylist({
591 url: server.url,
592 token: server.accessToken,
593 playlistId: playlistUUID,
594 elementAttrs: { videoId: videoId4 }
595 })
596 })
597
598 it('Should fail with an unauthenticated user', async function () {
599 await reorderVideosPlaylist({
600 url: server.url,
601 token: null,
602 playlistId: playlistUUID,
603 elementAttrs: {
604 startPosition: 1,
605 insertAfterPosition: 2
606 },
607 expectedStatus: 401
608 })
609 })
610
611 it('Should fail with the playlist of another user', async function () {
612 await reorderVideosPlaylist({
613 url: server.url,
614 token: userAccessToken,
615 playlistId: playlistUUID,
616 elementAttrs: {
617 startPosition: 1,
618 insertAfterPosition: 2
619 },
620 expectedStatus: 403
621 })
622 })
623
624 it('Should fail with an invalid playlist', async function () {
625 await reorderVideosPlaylist({
626 url: server.url,
627 token: server.accessToken,
628 playlistId: 'toto',
629 elementAttrs: {
630 startPosition: 1,
631 insertAfterPosition: 2
632 },
633 expectedStatus: 400
634 })
635
636 await reorderVideosPlaylist({
637 url: server.url,
638 token: server.accessToken,
639 playlistId: 42,
640 elementAttrs: {
641 startPosition: 1,
642 insertAfterPosition: 2
643 },
644 expectedStatus: 404
645 })
646 })
647
648 it('Should fail with an invalid start position', async function () {
649 await reorderVideosPlaylist({
650 url: server.url,
651 token: server.accessToken,
652 playlistId: playlistUUID,
653 elementAttrs: {
654 startPosition: -1,
655 insertAfterPosition: 2
656 },
657 expectedStatus: 400
658 })
659
660 await reorderVideosPlaylist({
661 url: server.url,
662 token: server.accessToken,
663 playlistId: playlistUUID,
664 elementAttrs: {
665 startPosition: 'toto' as any,
666 insertAfterPosition: 2
667 },
668 expectedStatus: 400
669 })
670
671 await reorderVideosPlaylist({
672 url: server.url,
673 token: server.accessToken,
674 playlistId: playlistUUID,
675 elementAttrs: {
676 startPosition: 42,
677 insertAfterPosition: 2
678 },
679 expectedStatus: 400
680 })
681 })
682
683 it('Should fail with an invalid insert after position', async function () {
684 await reorderVideosPlaylist({
685 url: server.url,
686 token: server.accessToken,
687 playlistId: playlistUUID,
688 elementAttrs: {
689 startPosition: 1,
690 insertAfterPosition: 'toto' as any
691 },
692 expectedStatus: 400
693 })
694
695 await reorderVideosPlaylist({
696 url: server.url,
697 token: server.accessToken,
698 playlistId: playlistUUID,
699 elementAttrs: {
700 startPosition: 1,
701 insertAfterPosition: -2
702 },
703 expectedStatus: 400
704 })
705
706 await reorderVideosPlaylist({
707 url: server.url,
708 token: server.accessToken,
709 playlistId: playlistUUID,
710 elementAttrs: {
711 startPosition: 1,
712 insertAfterPosition: 42
713 },
714 expectedStatus: 400
715 })
716 })
717
718 it('Should fail with an invalid reorder length', async function () {
719 await reorderVideosPlaylist({
720 url: server.url,
721 token: server.accessToken,
722 playlistId: playlistUUID,
723 elementAttrs: {
724 startPosition: 1,
725 insertAfterPosition: 2,
726 reorderLength: 'toto' as any
727 },
728 expectedStatus: 400
729 })
730
731 await reorderVideosPlaylist({
732 url: server.url,
733 token: server.accessToken,
734 playlistId: playlistUUID,
735 elementAttrs: {
736 startPosition: 1,
737 insertAfterPosition: 2,
738 reorderLength: -1
739 },
740 expectedStatus: 400
741 })
742
743 await reorderVideosPlaylist({
744 url: server.url,
745 token: server.accessToken,
746 playlistId: playlistUUID,
747 elementAttrs: {
748 startPosition: 1,
749 insertAfterPosition: 2,
750 reorderLength: 4
751 },
752 expectedStatus: 400
753 })
754 })
755
756 it('Succeed with the correct params', async function () {
757 await reorderVideosPlaylist({
758 url: server.url,
759 token: server.accessToken,
760 playlistId: playlistUUID,
761 elementAttrs: {
762 startPosition: 1,
763 insertAfterPosition: 2,
764 reorderLength: 3
765 },
766 expectedStatus: 204
767 })
768 })
769 })
770
771 describe('When deleting an element in a playlist', function () {
772 it('Should fail with an unauthenticated user', async function () {
773 await removeVideoFromPlaylist({
774 url: server.url,
775 token: null,
776 videoId,
777 playlistId: playlistUUID,
778 expectedStatus: 401
779 })
780 })
781
782 it('Should fail with the playlist of another user', async function () {
783 await removeVideoFromPlaylist({
784 url: server.url,
785 token: userAccessToken,
786 videoId,
787 playlistId: playlistUUID,
788 expectedStatus: 403
789 })
790 })
791
792 it('Should fail with an unknown or incorrect playlist id', async function () {
793 await removeVideoFromPlaylist({
794 url: server.url,
795 token: server.accessToken,
796 videoId,
797 playlistId: 'toto',
798 expectedStatus: 400
799 })
800
801 await removeVideoFromPlaylist({
802 url: server.url,
803 token: server.accessToken,
804 videoId,
805 playlistId: 42,
806 expectedStatus: 404
807 })
808 })
809
810 it('Should fail with an unknown or incorrect video id', async function () {
811 await removeVideoFromPlaylist({
812 url: server.url,
813 token: server.accessToken,
814 videoId: 'toto',
815 playlistId: playlistUUID,
816 expectedStatus: 400
817 })
818
819 await removeVideoFromPlaylist({
820 url: server.url,
821 token: server.accessToken,
822 videoId: 42,
823 playlistId: playlistUUID,
824 expectedStatus: 404
825 })
826 })
827
828 it('Should fail with an unknown element', async function () {
829 await removeVideoFromPlaylist({
830 url: server.url,
831 token: server.accessToken,
832 videoId: videoId2,
833 playlistId: playlistUUID,
834 expectedStatus: 404
835 })
836 })
837
838 it('Succeed with the correct params', async function () {
839 await removeVideoFromPlaylist({
840 url: server.url,
841 token: server.accessToken,
842 videoId: videoId,
843 playlistId: playlistUUID,
844 expectedStatus: 204
845 })
846 })
847 })
848
849 describe('When deleting a playlist', function () {
850 it('Should fail with an unknown playlist', async function () {
851 await deleteVideoPlaylist(server.url, server.accessToken, 42, 404)
852 })
853
854 it('Should fail with a playlist of another user', async function () {
855 await deleteVideoPlaylist(server.url, userAccessToken, playlistUUID, 403)
856 })
857
858 it('Should succeed with the correct params', async function () {
859 await deleteVideoPlaylist(server.url, server.accessToken, playlistUUID)
860 })
861 })
862
109 after(async function () { 863 after(async function () {
110 killallServers([ server ]) 864 killallServers([ server ])
111 865
diff --git a/server/tests/api/check-params/videos-filter.ts b/server/tests/api/check-params/videos-filter.ts
index e998c8a3d..cc2f35069 100644
--- a/server/tests/api/check-params/videos-filter.ts
+++ b/server/tests/api/check-params/videos-filter.ts
@@ -1,9 +1,9 @@
1/* tslint:disable:no-unused-expression */ 1/* tslint:disable:no-unused-expression */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
5import { 4import {
6 createUser, 5 createUser,
6 createVideoPlaylist,
7 flushTests, 7 flushTests,
8 killallServers, 8 killallServers,
9 makeGetRequest, 9 makeGetRequest,
@@ -13,15 +13,15 @@ import {
13 userLogin 13 userLogin
14} from '../../../../shared/utils' 14} from '../../../../shared/utils'
15import { UserRole } from '../../../../shared/models/users' 15import { UserRole } from '../../../../shared/models/users'
16import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
16 17
17const expect = chai.expect 18async function testEndpoints (server: ServerInfo, token: string, filter: string, playlistUUID: string, statusCodeExpected: number) {
18
19async function testEndpoints (server: ServerInfo, token: string, filter: string, statusCodeExpected: number) {
20 const paths = [ 19 const paths = [
21 '/api/v1/video-channels/root_channel/videos', 20 '/api/v1/video-channels/root_channel/videos',
22 '/api/v1/accounts/root/videos', 21 '/api/v1/accounts/root/videos',
23 '/api/v1/videos', 22 '/api/v1/videos',
24 '/api/v1/search/videos' 23 '/api/v1/search/videos',
24 '/api/v1/video-playlists/' + playlistUUID + '/videos'
25 ] 25 ]
26 26
27 for (const path of paths) { 27 for (const path of paths) {
@@ -41,6 +41,7 @@ describe('Test videos filters', function () {
41 let server: ServerInfo 41 let server: ServerInfo
42 let userAccessToken: string 42 let userAccessToken: string
43 let moderatorAccessToken: string 43 let moderatorAccessToken: string
44 let playlistUUID: string
44 45
45 // --------------------------------------------------------------- 46 // ---------------------------------------------------------------
46 47
@@ -68,28 +69,38 @@ describe('Test videos filters', function () {
68 UserRole.MODERATOR 69 UserRole.MODERATOR
69 ) 70 )
70 moderatorAccessToken = await userLogin(server, moderator) 71 moderatorAccessToken = await userLogin(server, moderator)
72
73 const res = await createVideoPlaylist({
74 url: server.url,
75 token: server.accessToken,
76 playlistAttrs: {
77 displayName: 'super playlist',
78 privacy: VideoPlaylistPrivacy.PUBLIC
79 }
80 })
81 playlistUUID = res.body.videoPlaylist.uuid
71 }) 82 })
72 83
73 describe('When setting a video filter', function () { 84 describe('When setting a video filter', function () {
74 85
75 it('Should fail with a bad filter', async function () { 86 it('Should fail with a bad filter', async function () {
76 await testEndpoints(server, server.accessToken, 'bad-filter', 400) 87 await testEndpoints(server, server.accessToken, 'bad-filter', playlistUUID, 400)
77 }) 88 })
78 89
79 it('Should succeed with a good filter', async function () { 90 it('Should succeed with a good filter', async function () {
80 await testEndpoints(server, server.accessToken,'local', 200) 91 await testEndpoints(server, server.accessToken,'local', playlistUUID, 200)
81 }) 92 })
82 93
83 it('Should fail to list all-local with a simple user', async function () { 94 it('Should fail to list all-local with a simple user', async function () {
84 await testEndpoints(server, userAccessToken, 'all-local', 401) 95 await testEndpoints(server, userAccessToken, 'all-local', playlistUUID, 401)
85 }) 96 })
86 97
87 it('Should succeed to list all-local with a moderator', async function () { 98 it('Should succeed to list all-local with a moderator', async function () {
88 await testEndpoints(server, moderatorAccessToken, 'all-local', 200) 99 await testEndpoints(server, moderatorAccessToken, 'all-local', playlistUUID, 200)
89 }) 100 })
90 101
91 it('Should succeed to list all-local with an admin', async function () { 102 it('Should succeed to list all-local with an admin', async function () {
92 await testEndpoints(server, server.accessToken, 'all-local', 200) 103 await testEndpoints(server, server.accessToken, 'all-local', playlistUUID, 200)
93 }) 104 })
94 105
95 // Because we cannot authenticate the user on the RSS endpoint 106 // Because we cannot authenticate the user on the RSS endpoint
@@ -104,7 +115,7 @@ describe('Test videos filters', function () {
104 }) 115 })
105 }) 116 })
106 117
107 it('Should succed on the feeds endpoint with the local filter', async function () { 118 it('Should succeed on the feeds endpoint with the local filter', async function () {
108 await makeGetRequest({ 119 await makeGetRequest({
109 url: server.url, 120 url: server.url,
110 path: '/feeds/videos.json', 121 path: '/feeds/videos.json',
diff --git a/server/tests/api/redundancy/redundancy.ts b/server/tests/api/redundancy/redundancy.ts
index 778611fff..fc5ffbad7 100644
--- a/server/tests/api/redundancy/redundancy.ts
+++ b/server/tests/api/redundancy/redundancy.ts
@@ -4,20 +4,26 @@ import * as chai from 'chai'
4import 'mocha' 4import 'mocha'
5import { VideoDetails } from '../../../../shared/models/videos' 5import { VideoDetails } from '../../../../shared/models/videos'
6import { 6import {
7 checkSegmentHash,
8 checkVideoFilesWereRemoved,
7 doubleFollow, 9 doubleFollow,
8 flushAndRunMultipleServers, 10 flushAndRunMultipleServers,
9 getFollowingListPaginationAndSort, 11 getFollowingListPaginationAndSort,
10 getVideo, 12 getVideo,
13 getVideoWithToken,
11 immutableAssign, 14 immutableAssign,
12 killallServers, makeGetRequest, 15 killallServers,
16 makeGetRequest,
17 removeVideo,
18 reRunServer,
13 root, 19 root,
14 ServerInfo, 20 ServerInfo,
15 setAccessTokensToServers, unfollow, 21 setAccessTokensToServers,
22 unfollow,
16 uploadVideo, 23 uploadVideo,
17 viewVideo, 24 viewVideo,
18 wait, 25 wait,
19 waitUntilLog, 26 waitUntilLog
20 checkVideoFilesWereRemoved, removeVideo, getVideoWithToken, reRunServer, checkSegmentHash
21} from '../../../../shared/utils' 27} from '../../../../shared/utils'
22import { waitJobs } from '../../../../shared/utils/server/jobs' 28import { waitJobs } from '../../../../shared/utils/server/jobs'
23 29
diff --git a/shared/models/videos/playlist/video-playlist-create.model.ts b/shared/models/videos/playlist/video-playlist-create.model.ts
index 386acbb96..67a33fa35 100644
--- a/shared/models/videos/playlist/video-playlist-create.model.ts
+++ b/shared/models/videos/playlist/video-playlist-create.model.ts
@@ -2,10 +2,10 @@ import { VideoPlaylistPrivacy } from './video-playlist-privacy.model'
2 2
3export interface VideoPlaylistCreate { 3export interface VideoPlaylistCreate {
4 displayName: string 4 displayName: string
5 description: string
6 privacy: VideoPlaylistPrivacy 5 privacy: VideoPlaylistPrivacy
7 6
7 description?: string
8 videoChannelId?: number 8 videoChannelId?: number
9 9
10 thumbnailfile?: Blob 10 thumbnailfile?: any
11} 11}
diff --git a/shared/models/videos/playlist/video-playlist-element-create.model.ts b/shared/models/videos/playlist/video-playlist-element-create.model.ts
index 9bd56a8ca..c31702892 100644
--- a/shared/models/videos/playlist/video-playlist-element-create.model.ts
+++ b/shared/models/videos/playlist/video-playlist-element-create.model.ts
@@ -1,4 +1,6 @@
1export interface VideoPlaylistElementCreate { 1export interface VideoPlaylistElementCreate {
2 videoId: number
3
2 startTimestamp?: number 4 startTimestamp?: number
3 stopTimestamp?: number 5 stopTimestamp?: number
4} 6}
diff --git a/shared/models/videos/playlist/video-playlist-update.model.ts b/shared/models/videos/playlist/video-playlist-update.model.ts
index c7a15c550..0ff5bcb0f 100644
--- a/shared/models/videos/playlist/video-playlist-update.model.ts
+++ b/shared/models/videos/playlist/video-playlist-update.model.ts
@@ -2,9 +2,9 @@ import { VideoPlaylistPrivacy } from './video-playlist-privacy.model'
2 2
3export interface VideoPlaylistUpdate { 3export interface VideoPlaylistUpdate {
4 displayName: string 4 displayName: string
5 description: string
6 privacy: VideoPlaylistPrivacy 5 privacy: VideoPlaylistPrivacy
7 6
7 description?: string
8 videoChannelId?: number 8 videoChannelId?: number
9 thumbnailfile?: Blob 9 thumbnailfile?: any
10} 10}
diff --git a/shared/utils/index.ts b/shared/utils/index.ts
index 156901372..c09565d95 100644
--- a/shared/utils/index.ts
+++ b/shared/utils/index.ts
@@ -13,12 +13,13 @@ export * from './requests/requests'
13export * from './requests/check-api-params' 13export * from './requests/check-api-params'
14export * from './server/servers' 14export * from './server/servers'
15export * from './videos/services' 15export * from './videos/services'
16export * from './videos/video-playlists'
16export * from './users/users' 17export * from './users/users'
17export * from './videos/video-abuses' 18export * from './videos/video-abuses'
18export * from './videos/video-blacklist' 19export * from './videos/video-blacklist'
19export * from './videos/video-channels' 20export * from './videos/video-channels'
20export * from './videos/video-comments' 21export * from './videos/video-comments'
21export * from './videos/video-playlists' 22export * from './videos/video-streaming-playlists'
22export * from './videos/videos' 23export * from './videos/videos'
23export * from './videos/video-change-ownership' 24export * from './videos/video-change-ownership'
24export * from './feeds/feeds' 25export * from './feeds/feeds'
diff --git a/shared/utils/videos/video-playlists.ts b/shared/utils/videos/video-playlists.ts
index 5186d9c4f..21285688a 100644
--- a/shared/utils/videos/video-playlists.ts
+++ b/shared/utils/videos/video-playlists.ts
@@ -31,7 +31,7 @@ function getVideoPlaylist (url: string, playlistId: number | string, statusCodeE
31 }) 31 })
32} 32}
33 33
34function deleteVideoPlaylist (url: string, token: string, playlistId: number | string, statusCodeExpected = 200) { 34function deleteVideoPlaylist (url: string, token: string, playlistId: number | string, statusCodeExpected = 204) {
35 const path = '/api/v1/video-playlists/' + playlistId 35 const path = '/api/v1/video-playlists/' + playlistId
36 36
37 return makeDeleteRequest({ 37 return makeDeleteRequest({
@@ -46,7 +46,7 @@ function createVideoPlaylist (options: {
46 url: string, 46 url: string,
47 token: string, 47 token: string,
48 playlistAttrs: VideoPlaylistCreate, 48 playlistAttrs: VideoPlaylistCreate,
49 expectedStatus: number 49 expectedStatus?: number
50}) { 50}) {
51 const path = '/api/v1/video-playlists/' 51 const path = '/api/v1/video-playlists/'
52 52
@@ -63,7 +63,7 @@ function createVideoPlaylist (options: {
63 token: options.token, 63 token: options.token,
64 fields, 64 fields,
65 attaches, 65 attaches,
66 statusCodeExpected: options.expectedStatus 66 statusCodeExpected: options.expectedStatus || 200
67 }) 67 })
68} 68}
69 69
@@ -71,9 +71,10 @@ function updateVideoPlaylist (options: {
71 url: string, 71 url: string,
72 token: string, 72 token: string,
73 playlistAttrs: VideoPlaylistUpdate, 73 playlistAttrs: VideoPlaylistUpdate,
74 expectedStatus: number 74 playlistId: number | string,
75 expectedStatus?: number
75}) { 76}) {
76 const path = '/api/v1/video-playlists/' 77 const path = '/api/v1/video-playlists/' + options.playlistId
77 78
78 const fields = omit(options.playlistAttrs, 'thumbnailfile') 79 const fields = omit(options.playlistAttrs, 'thumbnailfile')
79 80
@@ -88,7 +89,7 @@ function updateVideoPlaylist (options: {
88 token: options.token, 89 token: options.token,
89 fields, 90 fields,
90 attaches, 91 attaches,
91 statusCodeExpected: options.expectedStatus 92 statusCodeExpected: options.expectedStatus || 204
92 }) 93 })
93} 94}
94 95
@@ -97,7 +98,7 @@ function addVideoInPlaylist (options: {
97 token: string, 98 token: string,
98 playlistId: number | string, 99 playlistId: number | string,
99 elementAttrs: VideoPlaylistElementCreate 100 elementAttrs: VideoPlaylistElementCreate
100 expectedStatus: number 101 expectedStatus?: number
101}) { 102}) {
102 const path = '/api/v1/video-playlists/' + options.playlistId + '/videos' 103 const path = '/api/v1/video-playlists/' + options.playlistId + '/videos'
103 104
@@ -106,7 +107,7 @@ function addVideoInPlaylist (options: {
106 path, 107 path,
107 token: options.token, 108 token: options.token,
108 fields: options.elementAttrs, 109 fields: options.elementAttrs,
109 statusCodeExpected: options.expectedStatus 110 statusCodeExpected: options.expectedStatus || 200
110 }) 111 })
111} 112}
112 113
@@ -116,7 +117,7 @@ function updateVideoPlaylistElement (options: {
116 playlistId: number | string, 117 playlistId: number | string,
117 videoId: number | string, 118 videoId: number | string,
118 elementAttrs: VideoPlaylistElementUpdate, 119 elementAttrs: VideoPlaylistElementUpdate,
119 expectedStatus: number 120 expectedStatus?: number
120}) { 121}) {
121 const path = '/api/v1/video-playlists/' + options.playlistId + '/videos/' + options.videoId 122 const path = '/api/v1/video-playlists/' + options.playlistId + '/videos/' + options.videoId
122 123
@@ -125,7 +126,7 @@ function updateVideoPlaylistElement (options: {
125 path, 126 path,
126 token: options.token, 127 token: options.token,
127 fields: options.elementAttrs, 128 fields: options.elementAttrs,
128 statusCodeExpected: options.expectedStatus 129 statusCodeExpected: options.expectedStatus || 204
129 }) 130 })
130} 131}
131 132
@@ -142,7 +143,7 @@ function removeVideoFromPlaylist (options: {
142 url: options.url, 143 url: options.url,
143 path, 144 path,
144 token: options.token, 145 token: options.token,
145 statusCodeExpected: options.expectedStatus 146 statusCodeExpected: options.expectedStatus || 204
146 }) 147 })
147} 148}
148 149
@@ -152,14 +153,14 @@ function reorderVideosPlaylist (options: {
152 playlistId: number | string, 153 playlistId: number | string,
153 elementAttrs: { 154 elementAttrs: {
154 startPosition: number, 155 startPosition: number,
155 insertAfter: number, 156 insertAfterPosition: number,
156 reorderLength?: number 157 reorderLength?: number
157 }, 158 },
158 expectedStatus: number 159 expectedStatus: number
159}) { 160}) {
160 const path = '/api/v1/video-playlists/' + options.playlistId + '/videos' 161 const path = '/api/v1/video-playlists/' + options.playlistId + '/videos/reorder'
161 162
162 return makePutBodyRequest({ 163 return makePostBodyRequest({
163 url: options.url, 164 url: options.url,
164 path, 165 path,
165 token: options.token, 166 token: options.token,
@@ -179,6 +180,7 @@ export {
179 deleteVideoPlaylist, 180 deleteVideoPlaylist,
180 181
181 addVideoInPlaylist, 182 addVideoInPlaylist,
183 updateVideoPlaylistElement,
182 removeVideoFromPlaylist, 184 removeVideoFromPlaylist,
183 185
184 reorderVideosPlaylist 186 reorderVideosPlaylist