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/channel.ts196
-rw-r--r--server/controllers/api/videos/index.ts48
2 files changed, 217 insertions, 27 deletions
diff --git a/server/controllers/api/videos/channel.ts b/server/controllers/api/videos/channel.ts
new file mode 100644
index 000000000..630fc4f53
--- /dev/null
+++ b/server/controllers/api/videos/channel.ts
@@ -0,0 +1,196 @@
1import * as express from 'express'
2
3import { database as db } from '../../../initializers'
4import {
5 logger,
6 getFormattedObjects,
7 retryTransactionWrapper
8} from '../../../helpers'
9import {
10 authenticate,
11 paginationValidator,
12 videoChannelsSortValidator,
13 videoChannelsAddValidator,
14 setVideoChannelsSort,
15 setPagination,
16 videoChannelsRemoveValidator,
17 videoChannelGetValidator,
18 videoChannelsUpdateValidator,
19 listVideoAuthorChannelsValidator
20} from '../../../middlewares'
21import {
22 createVideoChannel,
23 updateVideoChannelToFriends
24} from '../../../lib'
25import { VideoChannelInstance, AuthorInstance } from '../../../models'
26import { VideoChannelCreate, VideoChannelUpdate } from '../../../../shared'
27
28const videoChannelRouter = express.Router()
29
30videoChannelRouter.get('/channels',
31 paginationValidator,
32 videoChannelsSortValidator,
33 setVideoChannelsSort,
34 setPagination,
35 listVideoChannels
36)
37
38videoChannelRouter.get('/authors/:authorId/channels',
39 listVideoAuthorChannelsValidator,
40 listVideoAuthorChannels
41)
42
43videoChannelRouter.post('/channels',
44 authenticate,
45 videoChannelsAddValidator,
46 addVideoChannelRetryWrapper
47)
48
49videoChannelRouter.put('/channels/:id',
50 authenticate,
51 videoChannelsUpdateValidator,
52 updateVideoChannelRetryWrapper
53)
54
55videoChannelRouter.delete('/channels/:id',
56 authenticate,
57 videoChannelsRemoveValidator,
58 removeVideoChannelRetryWrapper
59)
60
61videoChannelRouter.get('/channels/:id',
62 videoChannelGetValidator,
63 getVideoChannel
64)
65
66// ---------------------------------------------------------------------------
67
68export {
69 videoChannelRouter
70}
71
72// ---------------------------------------------------------------------------
73
74function listVideoChannels (req: express.Request, res: express.Response, next: express.NextFunction) {
75 db.VideoChannel.listForApi(req.query.start, req.query.count, req.query.sort)
76 .then(result => res.json(getFormattedObjects(result.data, result.total)))
77 .catch(err => next(err))
78}
79
80function listVideoAuthorChannels (req: express.Request, res: express.Response, next: express.NextFunction) {
81 db.VideoChannel.listByAuthor(res.locals.author.id)
82 .then(result => res.json(getFormattedObjects(result.data, result.total)))
83 .catch(err => next(err))
84}
85
86// Wrapper to video channel add that retry the function if there is a database error
87// We need this because we run the transaction in SERIALIZABLE isolation that can fail
88function addVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
89 const options = {
90 arguments: [ req, res ],
91 errorMessage: 'Cannot insert the video video channel with many retries.'
92 }
93
94 retryTransactionWrapper(addVideoChannel, options)
95 .then(() => {
96 // TODO : include Location of the new video channel -> 201
97 res.type('json').status(204).end()
98 })
99 .catch(err => next(err))
100}
101
102function addVideoChannel (req: express.Request, res: express.Response) {
103 const videoChannelInfo: VideoChannelCreate = req.body
104 const author: AuthorInstance = res.locals.oauth.token.User.Author
105
106 return db.sequelize.transaction(t => {
107 return createVideoChannel(videoChannelInfo, author, t)
108 })
109 .then(videoChannelUUID => logger.info('Video channel with uuid %s created.', videoChannelUUID))
110 .catch((err: Error) => {
111 logger.debug('Cannot insert the video channel.', err)
112 throw err
113 })
114}
115
116function updateVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
117 const options = {
118 arguments: [ req, res ],
119 errorMessage: 'Cannot update the video with many retries.'
120 }
121
122 retryTransactionWrapper(updateVideoChannel, options)
123 .then(() => res.type('json').status(204).end())
124 .catch(err => next(err))
125}
126
127function updateVideoChannel (req: express.Request, res: express.Response) {
128 const videoChannelInstance: VideoChannelInstance = res.locals.videoChannel
129 const videoChannelFieldsSave = videoChannelInstance.toJSON()
130 const videoChannelInfoToUpdate: VideoChannelUpdate = req.body
131
132 return db.sequelize.transaction(t => {
133 const options = {
134 transaction: t
135 }
136
137 if (videoChannelInfoToUpdate.name !== undefined) videoChannelInstance.set('name', videoChannelInfoToUpdate.name)
138 if (videoChannelInfoToUpdate.description !== undefined) videoChannelInstance.set('description', videoChannelInfoToUpdate.description)
139
140 return videoChannelInstance.save(options)
141 .then(() => {
142 const json = videoChannelInstance.toUpdateRemoteJSON()
143
144 // Now we'll update the video channel's meta data to our friends
145 return updateVideoChannelToFriends(json, t)
146 })
147 })
148 .then(() => {
149 logger.info('Video channel with name %s and uuid %s updated.', videoChannelInstance.name, videoChannelInstance.uuid)
150 })
151 .catch(err => {
152 logger.debug('Cannot update the video channel.', err)
153
154 // Force fields we want to update
155 // If the transaction is retried, sequelize will think the object has not changed
156 // So it will skip the SQL request, even if the last one was ROLLBACKed!
157 Object.keys(videoChannelFieldsSave).forEach(key => {
158 const value = videoChannelFieldsSave[key]
159 videoChannelInstance.set(key, value)
160 })
161
162 throw err
163 })
164}
165
166function removeVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
167 const options = {
168 arguments: [ req, res ],
169 errorMessage: 'Cannot remove the video channel with many retries.'
170 }
171
172 retryTransactionWrapper(removeVideoChannel, options)
173 .then(() => res.type('json').status(204).end())
174 .catch(err => next(err))
175}
176
177function removeVideoChannel (req: express.Request, res: express.Response) {
178 const videoChannelInstance: VideoChannelInstance = res.locals.videoChannel
179
180 return db.sequelize.transaction(t => {
181 return videoChannelInstance.destroy({ transaction: t })
182 })
183 .then(() => {
184 logger.info('Video channel with name %s and uuid %s deleted.', videoChannelInstance.name, videoChannelInstance.uuid)
185 })
186 .catch(err => {
187 logger.error('Errors when removed the video channel.', err)
188 throw err
189 })
190}
191
192function getVideoChannel (req: express.Request, res: express.Response, next: express.NextFunction) {
193 db.VideoChannel.loadAndPopulateAuthorAndVideos(res.locals.videoChannel.id)
194 .then(videoChannelWithVideos => res.json(videoChannelWithVideos.toFormattedJSON()))
195 .catch(err => next(err))
196}
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts
index 2b7ead954..ec855ee8e 100644
--- a/server/controllers/api/videos/index.ts
+++ b/server/controllers/api/videos/index.ts
@@ -46,6 +46,7 @@ import { VideoCreate, VideoUpdate } from '../../../../shared'
46import { abuseVideoRouter } from './abuse' 46import { abuseVideoRouter } from './abuse'
47import { blacklistRouter } from './blacklist' 47import { blacklistRouter } from './blacklist'
48import { rateVideoRouter } from './rate' 48import { rateVideoRouter } from './rate'
49import { videoChannelRouter } from './channel'
49 50
50const videosRouter = express.Router() 51const videosRouter = express.Router()
51 52
@@ -76,6 +77,7 @@ const reqFiles = multer({ storage: storage }).fields([{ name: 'videofile', maxCo
76videosRouter.use('/', abuseVideoRouter) 77videosRouter.use('/', abuseVideoRouter)
77videosRouter.use('/', blacklistRouter) 78videosRouter.use('/', blacklistRouter)
78videosRouter.use('/', rateVideoRouter) 79videosRouter.use('/', rateVideoRouter)
80videosRouter.use('/', videoChannelRouter)
79 81
80videosRouter.get('/categories', listVideoCategories) 82videosRouter.get('/categories', listVideoCategories)
81videosRouter.get('/licences', listVideoLicences) 83videosRouter.get('/licences', listVideoLicences)
@@ -161,21 +163,13 @@ function addVideo (req: express.Request, res: express.Response, videoPhysicalFil
161 let videoUUID = '' 163 let videoUUID = ''
162 164
163 return db.sequelize.transaction(t => { 165 return db.sequelize.transaction(t => {
164 const user = res.locals.oauth.token.User 166 let p: Promise<TagInstance[]>
165 167
166 const name = user.username 168 if (!videoInfo.tags) p = Promise.resolve(undefined)
167 // null because it is OUR pod 169 else p = db.Tag.findOrCreateTags(videoInfo.tags, t)
168 const podId = null
169 const userId = user.id
170 170
171 return db.Author.findOrCreateAuthor(name, podId, userId, t) 171 return p
172 .then(author => { 172 .then(tagInstances => {
173 const tags = videoInfo.tags
174 if (!tags) return { author, tagInstances: undefined }
175
176 return db.Tag.findOrCreateTags(tags, t).then(tagInstances => ({ author, tagInstances }))
177 })
178 .then(({ author, tagInstances }) => {
179 const videoData = { 173 const videoData = {
180 name: videoInfo.name, 174 name: videoInfo.name,
181 remote: false, 175 remote: false,
@@ -186,18 +180,18 @@ function addVideo (req: express.Request, res: express.Response, videoPhysicalFil
186 nsfw: videoInfo.nsfw, 180 nsfw: videoInfo.nsfw,
187 description: videoInfo.description, 181 description: videoInfo.description,
188 duration: videoPhysicalFile['duration'], // duration was added by a previous middleware 182 duration: videoPhysicalFile['duration'], // duration was added by a previous middleware
189 authorId: author.id 183 channelId: res.locals.videoChannel.id
190 } 184 }
191 185
192 const video = db.Video.build(videoData) 186 const video = db.Video.build(videoData)
193 return { author, tagInstances, video } 187 return { tagInstances, video }
194 }) 188 })
195 .then(({ author, tagInstances, video }) => { 189 .then(({ tagInstances, video }) => {
196 const videoFilePath = join(CONFIG.STORAGE.VIDEOS_DIR, videoPhysicalFile.filename) 190 const videoFilePath = join(CONFIG.STORAGE.VIDEOS_DIR, videoPhysicalFile.filename)
197 return getVideoFileHeight(videoFilePath) 191 return getVideoFileHeight(videoFilePath)
198 .then(height => ({ author, tagInstances, video, videoFileHeight: height })) 192 .then(height => ({ tagInstances, video, videoFileHeight: height }))
199 }) 193 })
200 .then(({ author, tagInstances, video, videoFileHeight }) => { 194 .then(({ tagInstances, video, videoFileHeight }) => {
201 const videoFileData = { 195 const videoFileData = {
202 extname: extname(videoPhysicalFile.filename), 196 extname: extname(videoPhysicalFile.filename),
203 resolution: videoFileHeight, 197 resolution: videoFileHeight,
@@ -205,9 +199,9 @@ function addVideo (req: express.Request, res: express.Response, videoPhysicalFil
205 } 199 }
206 200
207 const videoFile = db.VideoFile.build(videoFileData) 201 const videoFile = db.VideoFile.build(videoFileData)
208 return { author, tagInstances, video, videoFile } 202 return { tagInstances, video, videoFile }
209 }) 203 })
210 .then(({ author, tagInstances, video, videoFile }) => { 204 .then(({ tagInstances, video, videoFile }) => {
211 const videoDir = CONFIG.STORAGE.VIDEOS_DIR 205 const videoDir = CONFIG.STORAGE.VIDEOS_DIR
212 const source = join(videoDir, videoPhysicalFile.filename) 206 const source = join(videoDir, videoPhysicalFile.filename)
213 const destination = join(videoDir, video.getVideoFilename(videoFile)) 207 const destination = join(videoDir, video.getVideoFilename(videoFile))
@@ -216,10 +210,10 @@ function addVideo (req: express.Request, res: express.Response, videoPhysicalFil
216 .then(() => { 210 .then(() => {
217 // This is important in case if there is another attempt in the retry process 211 // This is important in case if there is another attempt in the retry process
218 videoPhysicalFile.filename = video.getVideoFilename(videoFile) 212 videoPhysicalFile.filename = video.getVideoFilename(videoFile)
219 return { author, tagInstances, video, videoFile } 213 return { tagInstances, video, videoFile }
220 }) 214 })
221 }) 215 })
222 .then(({ author, tagInstances, video, videoFile }) => { 216 .then(({ tagInstances, video, videoFile }) => {
223 const tasks = [] 217 const tasks = []
224 218
225 tasks.push( 219 tasks.push(
@@ -239,15 +233,15 @@ function addVideo (req: express.Request, res: express.Response, videoPhysicalFil
239 ) 233 )
240 } 234 }
241 235
242 return Promise.all(tasks).then(() => ({ author, tagInstances, video, videoFile })) 236 return Promise.all(tasks).then(() => ({ tagInstances, video, videoFile }))
243 }) 237 })
244 .then(({ author, tagInstances, video, videoFile }) => { 238 .then(({ tagInstances, video, videoFile }) => {
245 const options = { transaction: t } 239 const options = { transaction: t }
246 240
247 return video.save(options) 241 return video.save(options)
248 .then(videoCreated => { 242 .then(videoCreated => {
249 // Do not forget to add Author information to the created video 243 // Do not forget to add video channel information to the created video
250 videoCreated.Author = author 244 videoCreated.VideoChannel = res.locals.videoChannel
251 videoUUID = videoCreated.uuid 245 videoUUID = videoCreated.uuid
252 246
253 return { tagInstances, video: videoCreated, videoFile } 247 return { tagInstances, video: videoCreated, videoFile }
@@ -392,7 +386,7 @@ function getVideo (req: express.Request, res: express.Response) {
392 } 386 }
393 387
394 // Do not wait the view system 388 // Do not wait the view system
395 res.json(videoInstance.toFormattedJSON()) 389 res.json(videoInstance.toFormattedDetailsJSON())
396} 390}
397 391
398function listVideos (req: express.Request, res: express.Response, next: express.NextFunction) { 392function listVideos (req: express.Request, res: express.Response, next: express.NextFunction) {