aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/controllers/api/videos
diff options
context:
space:
mode:
Diffstat (limited to 'server/controllers/api/videos')
-rw-r--r--server/controllers/api/videos/files.ts34
-rw-r--r--server/controllers/api/videos/import.ts7
-rw-r--r--server/controllers/api/videos/index.ts6
-rw-r--r--server/controllers/api/videos/live.ts11
-rw-r--r--server/controllers/api/videos/passwords.ts105
-rw-r--r--server/controllers/api/videos/storyboard.ts29
-rw-r--r--server/controllers/api/videos/token.ts16
-rw-r--r--server/controllers/api/videos/update.ts16
-rw-r--r--server/controllers/api/videos/upload.ts26
9 files changed, 211 insertions, 39 deletions
diff --git a/server/controllers/api/videos/files.ts b/server/controllers/api/videos/files.ts
index 6d9c0b843..67b60ff63 100644
--- a/server/controllers/api/videos/files.ts
+++ b/server/controllers/api/videos/files.ts
@@ -2,7 +2,8 @@ import express from 'express'
2import toInt from 'validator/lib/toInt' 2import toInt from 'validator/lib/toInt'
3import { logger, loggerTagsFactory } from '@server/helpers/logger' 3import { logger, loggerTagsFactory } from '@server/helpers/logger'
4import { federateVideoIfNeeded } from '@server/lib/activitypub/videos' 4import { federateVideoIfNeeded } from '@server/lib/activitypub/videos'
5import { removeAllWebTorrentFiles, removeHLSFile, removeHLSPlaylist, removeWebTorrentFile } from '@server/lib/video-file' 5import { updatePlaylistAfterFileChange } from '@server/lib/hls'
6import { removeAllWebVideoFiles, removeHLSFile, removeHLSPlaylist, removeWebVideoFile } from '@server/lib/video-file'
6import { VideoFileModel } from '@server/models/video/video-file' 7import { VideoFileModel } from '@server/models/video/video-file'
7import { HttpStatusCode, UserRight } from '@shared/models' 8import { HttpStatusCode, UserRight } from '@shared/models'
8import { 9import {
@@ -12,11 +13,10 @@ import {
12 videoFileMetadataGetValidator, 13 videoFileMetadataGetValidator,
13 videoFilesDeleteHLSFileValidator, 14 videoFilesDeleteHLSFileValidator,
14 videoFilesDeleteHLSValidator, 15 videoFilesDeleteHLSValidator,
15 videoFilesDeleteWebTorrentFileValidator, 16 videoFilesDeleteWebVideoFileValidator,
16 videoFilesDeleteWebTorrentValidator, 17 videoFilesDeleteWebVideoValidator,
17 videosGetValidator 18 videosGetValidator
18} from '../../../middlewares' 19} from '../../../middlewares'
19import { updatePlaylistAfterFileChange } from '@server/lib/hls'
20 20
21const lTags = loggerTagsFactory('api', 'video') 21const lTags = loggerTagsFactory('api', 'video')
22const filesRouter = express.Router() 22const filesRouter = express.Router()
@@ -40,17 +40,19 @@ filesRouter.delete('/:id/hls/:videoFileId',
40 asyncMiddleware(removeHLSFileController) 40 asyncMiddleware(removeHLSFileController)
41) 41)
42 42
43filesRouter.delete('/:id/webtorrent', 43filesRouter.delete(
44 [ '/:id/webtorrent', '/:id/web-videos' ], // TODO: remove webtorrent in V7
44 authenticate, 45 authenticate,
45 ensureUserHasRight(UserRight.MANAGE_VIDEO_FILES), 46 ensureUserHasRight(UserRight.MANAGE_VIDEO_FILES),
46 asyncMiddleware(videoFilesDeleteWebTorrentValidator), 47 asyncMiddleware(videoFilesDeleteWebVideoValidator),
47 asyncMiddleware(removeAllWebTorrentFilesController) 48 asyncMiddleware(removeAllWebVideoFilesController)
48) 49)
49filesRouter.delete('/:id/webtorrent/:videoFileId', 50filesRouter.delete(
51 [ '/:id/webtorrent/:videoFileId', '/:id/web-videos/:videoFileId' ], // TODO: remove webtorrent in V7
50 authenticate, 52 authenticate,
51 ensureUserHasRight(UserRight.MANAGE_VIDEO_FILES), 53 ensureUserHasRight(UserRight.MANAGE_VIDEO_FILES),
52 asyncMiddleware(videoFilesDeleteWebTorrentFileValidator), 54 asyncMiddleware(videoFilesDeleteWebVideoFileValidator),
53 asyncMiddleware(removeWebTorrentFileController) 55 asyncMiddleware(removeWebVideoFileController)
54) 56)
55 57
56// --------------------------------------------------------------------------- 58// ---------------------------------------------------------------------------
@@ -96,24 +98,24 @@ async function removeHLSFileController (req: express.Request, res: express.Respo
96 98
97// --------------------------------------------------------------------------- 99// ---------------------------------------------------------------------------
98 100
99async function removeAllWebTorrentFilesController (req: express.Request, res: express.Response) { 101async function removeAllWebVideoFilesController (req: express.Request, res: express.Response) {
100 const video = res.locals.videoAll 102 const video = res.locals.videoAll
101 103
102 logger.info('Deleting WebTorrent files of %s.', video.url, lTags(video.uuid)) 104 logger.info('Deleting Web Video files of %s.', video.url, lTags(video.uuid))
103 105
104 await removeAllWebTorrentFiles(video) 106 await removeAllWebVideoFiles(video)
105 await federateVideoIfNeeded(video, false, undefined) 107 await federateVideoIfNeeded(video, false, undefined)
106 108
107 return res.sendStatus(HttpStatusCode.NO_CONTENT_204) 109 return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
108} 110}
109 111
110async function removeWebTorrentFileController (req: express.Request, res: express.Response) { 112async function removeWebVideoFileController (req: express.Request, res: express.Response) {
111 const video = res.locals.videoAll 113 const video = res.locals.videoAll
112 114
113 const videoFileId = +req.params.videoFileId 115 const videoFileId = +req.params.videoFileId
114 logger.info('Deleting WebTorrent file %d of %s.', videoFileId, video.url, lTags(video.uuid)) 116 logger.info('Deleting Web Video file %d of %s.', videoFileId, video.url, lTags(video.uuid))
115 117
116 await removeWebTorrentFile(video, videoFileId) 118 await removeWebVideoFile(video, videoFileId)
117 await federateVideoIfNeeded(video, false, undefined) 119 await federateVideoIfNeeded(video, false, undefined)
118 120
119 return res.sendStatus(HttpStatusCode.NO_CONTENT_204) 121 return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
diff --git a/server/controllers/api/videos/import.ts b/server/controllers/api/videos/import.ts
index 6a50aaf4e..defe9efd4 100644
--- a/server/controllers/api/videos/import.ts
+++ b/server/controllers/api/videos/import.ts
@@ -14,7 +14,7 @@ import { getSecureTorrentName } from '../../../helpers/utils'
14import { CONFIG } from '../../../initializers/config' 14import { CONFIG } from '../../../initializers/config'
15import { MIMETYPES } from '../../../initializers/constants' 15import { MIMETYPES } from '../../../initializers/constants'
16import { JobQueue } from '../../../lib/job-queue/job-queue' 16import { JobQueue } from '../../../lib/job-queue/job-queue'
17import { updateVideoMiniatureFromExisting } from '../../../lib/thumbnail' 17import { updateLocalVideoMiniatureFromExisting } from '../../../lib/thumbnail'
18import { 18import {
19 asyncMiddleware, 19 asyncMiddleware,
20 asyncRetryTransactionMiddleware, 20 asyncRetryTransactionMiddleware,
@@ -120,6 +120,7 @@ async function handleTorrentImport (req: express.Request, res: express.Response,
120 videoChannel: res.locals.videoChannel, 120 videoChannel: res.locals.videoChannel,
121 tags: body.tags || undefined, 121 tags: body.tags || undefined,
122 user, 122 user,
123 videoPasswords: body.videoPasswords,
123 videoImportAttributes: { 124 videoImportAttributes: {
124 magnetUri, 125 magnetUri,
125 torrentName, 126 torrentName,
@@ -192,7 +193,7 @@ async function processThumbnail (req: express.Request, video: MVideoThumbnail) {
192 if (thumbnailField) { 193 if (thumbnailField) {
193 const thumbnailPhysicalFile = thumbnailField[0] 194 const thumbnailPhysicalFile = thumbnailField[0]
194 195
195 return updateVideoMiniatureFromExisting({ 196 return updateLocalVideoMiniatureFromExisting({
196 inputPath: thumbnailPhysicalFile.path, 197 inputPath: thumbnailPhysicalFile.path,
197 video, 198 video,
198 type: ThumbnailType.MINIATURE, 199 type: ThumbnailType.MINIATURE,
@@ -208,7 +209,7 @@ async function processPreview (req: express.Request, video: MVideoThumbnail): Pr
208 if (previewField) { 209 if (previewField) {
209 const previewPhysicalFile = previewField[0] 210 const previewPhysicalFile = previewField[0]
210 211
211 return updateVideoMiniatureFromExisting({ 212 return updateLocalVideoMiniatureFromExisting({
212 inputPath: previewPhysicalFile.path, 213 inputPath: previewPhysicalFile.path,
213 video, 214 video,
214 type: ThumbnailType.PREVIEW, 215 type: ThumbnailType.PREVIEW,
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts
index a34325e79..520d8cbbb 100644
--- a/server/controllers/api/videos/index.ts
+++ b/server/controllers/api/videos/index.ts
@@ -3,7 +3,6 @@ import { pickCommonVideoQuery } from '@server/helpers/query'
3import { doJSONRequest } from '@server/helpers/requests' 3import { doJSONRequest } from '@server/helpers/requests'
4import { openapiOperationDoc } from '@server/middlewares/doc' 4import { openapiOperationDoc } from '@server/middlewares/doc'
5import { getServerActor } from '@server/models/application/application' 5import { getServerActor } from '@server/models/application/application'
6import { guessAdditionalAttributesFromQuery } from '@server/models/video/formatter/video-format-utils'
7import { MVideoAccountLight } from '@server/types/models' 6import { MVideoAccountLight } from '@server/types/models'
8import { HttpStatusCode } from '../../../../shared/models' 7import { HttpStatusCode } from '../../../../shared/models'
9import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' 8import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger'
@@ -31,6 +30,7 @@ import {
31 videosRemoveValidator, 30 videosRemoveValidator,
32 videosSortValidator 31 videosSortValidator
33} from '../../../middlewares' 32} from '../../../middlewares'
33import { guessAdditionalAttributesFromQuery } from '../../../models/video/formatter'
34import { VideoModel } from '../../../models/video/video' 34import { VideoModel } from '../../../models/video/video'
35import { blacklistRouter } from './blacklist' 35import { blacklistRouter } from './blacklist'
36import { videoCaptionsRouter } from './captions' 36import { videoCaptionsRouter } from './captions'
@@ -41,12 +41,14 @@ import { liveRouter } from './live'
41import { ownershipVideoRouter } from './ownership' 41import { ownershipVideoRouter } from './ownership'
42import { rateVideoRouter } from './rate' 42import { rateVideoRouter } from './rate'
43import { statsRouter } from './stats' 43import { statsRouter } from './stats'
44import { storyboardRouter } from './storyboard'
44import { studioRouter } from './studio' 45import { studioRouter } from './studio'
45import { tokenRouter } from './token' 46import { tokenRouter } from './token'
46import { transcodingRouter } from './transcoding' 47import { transcodingRouter } from './transcoding'
47import { updateRouter } from './update' 48import { updateRouter } from './update'
48import { uploadRouter } from './upload' 49import { uploadRouter } from './upload'
49import { viewRouter } from './view' 50import { viewRouter } from './view'
51import { videoPasswordRouter } from './passwords'
50 52
51const auditLogger = auditLoggerFactory('videos') 53const auditLogger = auditLoggerFactory('videos')
52const videosRouter = express.Router() 54const videosRouter = express.Router()
@@ -68,6 +70,8 @@ videosRouter.use('/', updateRouter)
68videosRouter.use('/', filesRouter) 70videosRouter.use('/', filesRouter)
69videosRouter.use('/', transcodingRouter) 71videosRouter.use('/', transcodingRouter)
70videosRouter.use('/', tokenRouter) 72videosRouter.use('/', tokenRouter)
73videosRouter.use('/', videoPasswordRouter)
74videosRouter.use('/', storyboardRouter)
71 75
72videosRouter.get('/categories', 76videosRouter.get('/categories',
73 openapiOperationDoc({ operationId: 'getCategories' }), 77 openapiOperationDoc({ operationId: 'getCategories' }),
diff --git a/server/controllers/api/videos/live.ts b/server/controllers/api/videos/live.ts
index de047d4ec..e19e8c652 100644
--- a/server/controllers/api/videos/live.ts
+++ b/server/controllers/api/videos/live.ts
@@ -18,13 +18,14 @@ import { VideoLiveModel } from '@server/models/video/video-live'
18import { VideoLiveSessionModel } from '@server/models/video/video-live-session' 18import { VideoLiveSessionModel } from '@server/models/video/video-live-session'
19import { MVideoDetails, MVideoFullLight, MVideoLive } from '@server/types/models' 19import { MVideoDetails, MVideoFullLight, MVideoLive } from '@server/types/models'
20import { buildUUID, uuidToShort } from '@shared/extra-utils' 20import { buildUUID, uuidToShort } from '@shared/extra-utils'
21import { HttpStatusCode, LiveVideoCreate, LiveVideoLatencyMode, LiveVideoUpdate, UserRight, VideoState } from '@shared/models' 21import { HttpStatusCode, LiveVideoCreate, LiveVideoLatencyMode, LiveVideoUpdate, UserRight, VideoPrivacy, VideoState } from '@shared/models'
22import { logger } from '../../../helpers/logger' 22import { logger } from '../../../helpers/logger'
23import { sequelizeTypescript } from '../../../initializers/database' 23import { sequelizeTypescript } from '../../../initializers/database'
24import { updateVideoMiniatureFromExisting } from '../../../lib/thumbnail' 24import { updateLocalVideoMiniatureFromExisting } from '../../../lib/thumbnail'
25import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, optionalAuthenticate } from '../../../middlewares' 25import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, optionalAuthenticate } from '../../../middlewares'
26import { VideoModel } from '../../../models/video/video' 26import { VideoModel } from '../../../models/video/video'
27import { VideoLiveReplaySettingModel } from '@server/models/video/video-live-replay-setting' 27import { VideoLiveReplaySettingModel } from '@server/models/video/video-live-replay-setting'
28import { VideoPasswordModel } from '@server/models/video/video-password'
28 29
29const liveRouter = express.Router() 30const liveRouter = express.Router()
30 31
@@ -165,7 +166,7 @@ async function addLiveVideo (req: express.Request, res: express.Response) {
165 video, 166 video,
166 files: req.files, 167 files: req.files,
167 fallback: type => { 168 fallback: type => {
168 return updateVideoMiniatureFromExisting({ 169 return updateLocalVideoMiniatureFromExisting({
169 inputPath: ASSETS_PATH.DEFAULT_LIVE_BACKGROUND, 170 inputPath: ASSETS_PATH.DEFAULT_LIVE_BACKGROUND,
170 video, 171 video,
171 type, 172 type,
@@ -202,6 +203,10 @@ async function addLiveVideo (req: express.Request, res: express.Response) {
202 203
203 await federateVideoIfNeeded(videoCreated, true, t) 204 await federateVideoIfNeeded(videoCreated, true, t)
204 205
206 if (videoInfo.privacy === VideoPrivacy.PASSWORD_PROTECTED) {
207 await VideoPasswordModel.addPasswords(videoInfo.videoPasswords, video.id, t)
208 }
209
205 logger.info('Video live %s with uuid %s created.', videoInfo.name, videoCreated.uuid) 210 logger.info('Video live %s with uuid %s created.', videoInfo.name, videoCreated.uuid)
206 211
207 return { videoCreated } 212 return { videoCreated }
diff --git a/server/controllers/api/videos/passwords.ts b/server/controllers/api/videos/passwords.ts
new file mode 100644
index 000000000..d11cf5bcc
--- /dev/null
+++ b/server/controllers/api/videos/passwords.ts
@@ -0,0 +1,105 @@
1import express from 'express'
2
3import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
4import { getFormattedObjects } from '../../../helpers/utils'
5import {
6 asyncMiddleware,
7 asyncRetryTransactionMiddleware,
8 authenticate,
9 setDefaultPagination,
10 setDefaultSort
11} from '../../../middlewares'
12import {
13 listVideoPasswordValidator,
14 paginationValidator,
15 removeVideoPasswordValidator,
16 updateVideoPasswordListValidator,
17 videoPasswordsSortValidator
18} from '../../../middlewares/validators'
19import { VideoPasswordModel } from '@server/models/video/video-password'
20import { logger, loggerTagsFactory } from '@server/helpers/logger'
21import { Transaction } from 'sequelize'
22import { getVideoWithAttributes } from '@server/helpers/video'
23
24const lTags = loggerTagsFactory('api', 'video')
25const videoPasswordRouter = express.Router()
26
27videoPasswordRouter.get('/:videoId/passwords',
28 authenticate,
29 paginationValidator,
30 videoPasswordsSortValidator,
31 setDefaultSort,
32 setDefaultPagination,
33 asyncMiddleware(listVideoPasswordValidator),
34 asyncMiddleware(listVideoPasswords)
35)
36
37videoPasswordRouter.put('/:videoId/passwords',
38 authenticate,
39 asyncMiddleware(updateVideoPasswordListValidator),
40 asyncMiddleware(updateVideoPasswordList)
41)
42
43videoPasswordRouter.delete('/:videoId/passwords/:passwordId',
44 authenticate,
45 asyncMiddleware(removeVideoPasswordValidator),
46 asyncRetryTransactionMiddleware(removeVideoPassword)
47)
48
49// ---------------------------------------------------------------------------
50
51export {
52 videoPasswordRouter
53}
54
55// ---------------------------------------------------------------------------
56
57async function listVideoPasswords (req: express.Request, res: express.Response) {
58 const options = {
59 videoId: res.locals.videoAll.id,
60 start: req.query.start,
61 count: req.query.count,
62 sort: req.query.sort
63 }
64
65 const resultList = await VideoPasswordModel.listPasswords(options)
66
67 return res.json(getFormattedObjects(resultList.data, resultList.total))
68}
69
70async function updateVideoPasswordList (req: express.Request, res: express.Response) {
71 const videoInstance = getVideoWithAttributes(res)
72 const videoId = videoInstance.id
73
74 const passwordArray = req.body.passwords as string[]
75
76 await VideoPasswordModel.sequelize.transaction(async (t: Transaction) => {
77 await VideoPasswordModel.deleteAllPasswords(videoId, t)
78 await VideoPasswordModel.addPasswords(passwordArray, videoId, t)
79 })
80
81 logger.info(
82 `Video passwords for video with name %s and uuid %s have been updated`,
83 videoInstance.name,
84 videoInstance.uuid,
85 lTags(videoInstance.uuid)
86 )
87
88 return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
89}
90
91async function removeVideoPassword (req: express.Request, res: express.Response) {
92 const videoInstance = getVideoWithAttributes(res)
93 const password = res.locals.videoPassword
94
95 await VideoPasswordModel.deletePassword(password.id)
96 logger.info(
97 'Password with id %d of video named %s and uuid %s has been deleted.',
98 password.id,
99 videoInstance.name,
100 videoInstance.uuid,
101 lTags(videoInstance.uuid)
102 )
103
104 return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
105}
diff --git a/server/controllers/api/videos/storyboard.ts b/server/controllers/api/videos/storyboard.ts
new file mode 100644
index 000000000..47a22011d
--- /dev/null
+++ b/server/controllers/api/videos/storyboard.ts
@@ -0,0 +1,29 @@
1import express from 'express'
2import { getVideoWithAttributes } from '@server/helpers/video'
3import { StoryboardModel } from '@server/models/video/storyboard'
4import { asyncMiddleware, videosGetValidator } from '../../../middlewares'
5
6const storyboardRouter = express.Router()
7
8storyboardRouter.get('/:id/storyboards',
9 asyncMiddleware(videosGetValidator),
10 asyncMiddleware(listStoryboards)
11)
12
13// ---------------------------------------------------------------------------
14
15export {
16 storyboardRouter
17}
18
19// ---------------------------------------------------------------------------
20
21async function listStoryboards (req: express.Request, res: express.Response) {
22 const video = getVideoWithAttributes(res)
23
24 const storyboards = await StoryboardModel.listStoryboardsOf(video)
25
26 return res.json({
27 storyboards: storyboards.map(s => s.toFormattedJSON())
28 })
29}
diff --git a/server/controllers/api/videos/token.ts b/server/controllers/api/videos/token.ts
index 22387c3e8..e961ffd9e 100644
--- a/server/controllers/api/videos/token.ts
+++ b/server/controllers/api/videos/token.ts
@@ -1,13 +1,14 @@
1import express from 'express' 1import express from 'express'
2import { VideoTokensManager } from '@server/lib/video-tokens-manager' 2import { VideoTokensManager } from '@server/lib/video-tokens-manager'
3import { VideoToken } from '@shared/models' 3import { VideoPrivacy, VideoToken } from '@shared/models'
4import { asyncMiddleware, authenticate, videosCustomGetValidator } from '../../../middlewares' 4import { asyncMiddleware, optionalAuthenticate, videoFileTokenValidator, videosCustomGetValidator } from '../../../middlewares'
5 5
6const tokenRouter = express.Router() 6const tokenRouter = express.Router()
7 7
8tokenRouter.post('/:id/token', 8tokenRouter.post('/:id/token',
9 authenticate, 9 optionalAuthenticate,
10 asyncMiddleware(videosCustomGetValidator('only-video')), 10 asyncMiddleware(videosCustomGetValidator('only-video')),
11 videoFileTokenValidator,
11 generateToken 12 generateToken
12) 13)
13 14
@@ -22,12 +23,11 @@ export {
22function generateToken (req: express.Request, res: express.Response) { 23function generateToken (req: express.Request, res: express.Response) {
23 const video = res.locals.onlyVideo 24 const video = res.locals.onlyVideo
24 25
25 const { token, expires } = VideoTokensManager.Instance.create({ videoUUID: video.uuid, user: res.locals.oauth.token.User }) 26 const files = video.privacy === VideoPrivacy.PASSWORD_PROTECTED
27 ? VideoTokensManager.Instance.createForPasswordProtectedVideo({ videoUUID: video.uuid })
28 : VideoTokensManager.Instance.createForAuthUser({ videoUUID: video.uuid, user: res.locals.oauth.token.User })
26 29
27 return res.json({ 30 return res.json({
28 files: { 31 files
29 token,
30 expires
31 }
32 } as VideoToken) 32 } as VideoToken)
33} 33}
diff --git a/server/controllers/api/videos/update.ts b/server/controllers/api/videos/update.ts
index ddab428d4..28ec2cf37 100644
--- a/server/controllers/api/videos/update.ts
+++ b/server/controllers/api/videos/update.ts
@@ -2,13 +2,12 @@ import express from 'express'
2import { Transaction } from 'sequelize/types' 2import { Transaction } from 'sequelize/types'
3import { changeVideoChannelShare } from '@server/lib/activitypub/share' 3import { changeVideoChannelShare } from '@server/lib/activitypub/share'
4import { addVideoJobsAfterUpdate, buildVideoThumbnailsFromReq, setVideoTags } from '@server/lib/video' 4import { addVideoJobsAfterUpdate, buildVideoThumbnailsFromReq, setVideoTags } from '@server/lib/video'
5import { VideoPathManager } from '@server/lib/video-path-manager'
6import { setVideoPrivacy } from '@server/lib/video-privacy' 5import { setVideoPrivacy } from '@server/lib/video-privacy'
7import { openapiOperationDoc } from '@server/middlewares/doc' 6import { openapiOperationDoc } from '@server/middlewares/doc'
8import { FilteredModelAttributes } from '@server/types' 7import { FilteredModelAttributes } from '@server/types'
9import { MVideoFullLight } from '@server/types/models' 8import { MVideoFullLight } from '@server/types/models'
10import { forceNumber } from '@shared/core-utils' 9import { forceNumber } from '@shared/core-utils'
11import { HttpStatusCode, VideoUpdate } from '@shared/models' 10import { HttpStatusCode, VideoPrivacy, VideoUpdate } from '@shared/models'
12import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' 11import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger'
13import { resetSequelizeInstance } from '../../../helpers/database-utils' 12import { resetSequelizeInstance } from '../../../helpers/database-utils'
14import { createReqFiles } from '../../../helpers/express-utils' 13import { createReqFiles } from '../../../helpers/express-utils'
@@ -20,6 +19,9 @@ import { autoBlacklistVideoIfNeeded } from '../../../lib/video-blacklist'
20import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videosUpdateValidator } from '../../../middlewares' 19import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videosUpdateValidator } from '../../../middlewares'
21import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-update' 20import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-update'
22import { VideoModel } from '../../../models/video/video' 21import { VideoModel } from '../../../models/video/video'
22import { VideoPathManager } from '@server/lib/video-path-manager'
23import { VideoPasswordModel } from '@server/models/video/video-password'
24import { exists } from '@server/helpers/custom-validators/misc'
23 25
24const lTags = loggerTagsFactory('api', 'video') 26const lTags = loggerTagsFactory('api', 'video')
25const auditLogger = auditLoggerFactory('videos') 27const auditLogger = auditLoggerFactory('videos')
@@ -176,6 +178,16 @@ async function updateVideoPrivacy (options: {
176 const newPrivacy = forceNumber(videoInfoToUpdate.privacy) 178 const newPrivacy = forceNumber(videoInfoToUpdate.privacy)
177 setVideoPrivacy(videoInstance, newPrivacy) 179 setVideoPrivacy(videoInstance, newPrivacy)
178 180
181 // Delete passwords if video is not anymore password protected
182 if (videoInstance.privacy === VideoPrivacy.PASSWORD_PROTECTED && newPrivacy !== VideoPrivacy.PASSWORD_PROTECTED) {
183 await VideoPasswordModel.deleteAllPasswords(videoInstance.id, transaction)
184 }
185
186 if (newPrivacy === VideoPrivacy.PASSWORD_PROTECTED && exists(videoInfoToUpdate.videoPasswords)) {
187 await VideoPasswordModel.deleteAllPasswords(videoInstance.id, transaction)
188 await VideoPasswordModel.addPasswords(videoInfoToUpdate.videoPasswords, videoInstance.id, transaction)
189 }
190
179 // Unfederate the video if the new privacy is not compatible with federation 191 // Unfederate the video if the new privacy is not compatible with federation
180 if (hadPrivacyForFederation && !videoInstance.hasPrivacyForFederation()) { 192 if (hadPrivacyForFederation && !videoInstance.hasPrivacyForFederation()) {
181 await VideoModel.sendDelete(videoInstance, { transaction }) 193 await VideoModel.sendDelete(videoInstance, { transaction })
diff --git a/server/controllers/api/videos/upload.ts b/server/controllers/api/videos/upload.ts
index 885ac8b81..27fef0b1a 100644
--- a/server/controllers/api/videos/upload.ts
+++ b/server/controllers/api/videos/upload.ts
@@ -14,14 +14,14 @@ import { openapiOperationDoc } from '@server/middlewares/doc'
14import { VideoSourceModel } from '@server/models/video/video-source' 14import { VideoSourceModel } from '@server/models/video/video-source'
15import { MUserId, MVideoFile, MVideoFullLight } from '@server/types/models' 15import { MUserId, MVideoFile, MVideoFullLight } from '@server/types/models'
16import { uuidToShort } from '@shared/extra-utils' 16import { uuidToShort } from '@shared/extra-utils'
17import { HttpStatusCode, VideoCreate, VideoState } from '@shared/models' 17import { HttpStatusCode, VideoCreate, VideoPrivacy, VideoState } from '@shared/models'
18import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' 18import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger'
19import { createReqFiles } from '../../../helpers/express-utils' 19import { createReqFiles } from '../../../helpers/express-utils'
20import { logger, loggerTagsFactory } from '../../../helpers/logger' 20import { logger, loggerTagsFactory } from '../../../helpers/logger'
21import { MIMETYPES } from '../../../initializers/constants' 21import { MIMETYPES } from '../../../initializers/constants'
22import { sequelizeTypescript } from '../../../initializers/database' 22import { sequelizeTypescript } from '../../../initializers/database'
23import { Hooks } from '../../../lib/plugins/hooks' 23import { Hooks } from '../../../lib/plugins/hooks'
24import { generateVideoMiniature } from '../../../lib/thumbnail' 24import { generateLocalVideoMiniature } from '../../../lib/thumbnail'
25import { autoBlacklistVideoIfNeeded } from '../../../lib/video-blacklist' 25import { autoBlacklistVideoIfNeeded } from '../../../lib/video-blacklist'
26import { 26import {
27 asyncMiddleware, 27 asyncMiddleware,
@@ -33,6 +33,7 @@ import {
33} from '../../../middlewares' 33} from '../../../middlewares'
34import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-update' 34import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-update'
35import { VideoModel } from '../../../models/video/video' 35import { VideoModel } from '../../../models/video/video'
36import { VideoPasswordModel } from '@server/models/video/video-password'
36 37
37const lTags = loggerTagsFactory('api', 'video') 38const lTags = loggerTagsFactory('api', 'video')
38const auditLogger = auditLoggerFactory('videos') 39const auditLogger = auditLoggerFactory('videos')
@@ -62,13 +63,13 @@ uploadRouter.post('/upload-resumable',
62 authenticate, 63 authenticate,
63 reqVideoFileAddResumable, 64 reqVideoFileAddResumable,
64 asyncMiddleware(videosAddResumableInitValidator), 65 asyncMiddleware(videosAddResumableInitValidator),
65 uploadx.upload 66 (req, res) => uploadx.upload(req, res) // Prevent next() call, explicitely tell to uploadx it's the end
66) 67)
67 68
68uploadRouter.delete('/upload-resumable', 69uploadRouter.delete('/upload-resumable',
69 authenticate, 70 authenticate,
70 asyncMiddleware(deleteUploadResumableCache), 71 asyncMiddleware(deleteUploadResumableCache),
71 uploadx.upload 72 (req, res) => uploadx.upload(req, res) // Prevent next() call, explicitely tell to uploadx it's the end
72) 73)
73 74
74uploadRouter.put('/upload-resumable', 75uploadRouter.put('/upload-resumable',
@@ -110,7 +111,7 @@ async function addVideoLegacy (req: express.Request, res: express.Response) {
110async function addVideoResumable (req: express.Request, res: express.Response) { 111async function addVideoResumable (req: express.Request, res: express.Response) {
111 const videoPhysicalFile = res.locals.videoFileResumable 112 const videoPhysicalFile = res.locals.videoFileResumable
112 const videoInfo = videoPhysicalFile.metadata 113 const videoInfo = videoPhysicalFile.metadata
113 const files = { previewfile: videoInfo.previewfile } 114 const files = { previewfile: videoInfo.previewfile, thumbnailfile: videoInfo.thumbnailfile }
114 115
115 const response = await addVideo({ req, res, videoPhysicalFile, videoInfo, files }) 116 const response = await addVideo({ req, res, videoPhysicalFile, videoInfo, files })
116 await Redis.Instance.setUploadSession(req.query.upload_id, response) 117 await Redis.Instance.setUploadSession(req.query.upload_id, response)
@@ -152,7 +153,7 @@ async function addVideo (options: {
152 const [ thumbnailModel, previewModel ] = await buildVideoThumbnailsFromReq({ 153 const [ thumbnailModel, previewModel ] = await buildVideoThumbnailsFromReq({
153 video, 154 video,
154 files, 155 files,
155 fallback: type => generateVideoMiniature({ video, videoFile, type }) 156 fallback: type => generateLocalVideoMiniature({ video, videoFile, type })
156 }) 157 })
157 158
158 const { videoCreated } = await sequelizeTypescript.transaction(async t => { 159 const { videoCreated } = await sequelizeTypescript.transaction(async t => {
@@ -195,6 +196,10 @@ async function addVideo (options: {
195 transaction: t 196 transaction: t
196 }) 197 })
197 198
199 if (videoInfo.privacy === VideoPrivacy.PASSWORD_PROTECTED) {
200 await VideoPasswordModel.addPasswords(videoInfo.videoPasswords, video.id, t)
201 }
202
198 auditLogger.create(getAuditIdFromRes(res), new VideoAuditView(videoCreated.toFormattedDetailsJSON())) 203 auditLogger.create(getAuditIdFromRes(res), new VideoAuditView(videoCreated.toFormattedDetailsJSON()))
199 logger.info('Video with name %s and uuid %s created.', videoInfo.name, videoCreated.uuid, lTags(videoCreated.uuid)) 204 logger.info('Video with name %s and uuid %s created.', videoInfo.name, videoCreated.uuid, lTags(videoCreated.uuid))
200 205
@@ -230,6 +235,15 @@ async function addVideoJobsAfterUpload (video: MVideoFullLight, videoFile: MVide
230 }, 235 },
231 236
232 { 237 {
238 type: 'generate-video-storyboard' as 'generate-video-storyboard',
239 payload: {
240 videoUUID: video.uuid,
241 // No need to federate, we process these jobs sequentially
242 federate: false
243 }
244 },
245
246 {
233 type: 'notify', 247 type: 'notify',
234 payload: { 248 payload: {
235 action: 'new-video', 249 action: 'new-video',