aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/controllers
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2018-02-13 18:17:05 +0100
committerChocobozzz <me@florianbigard.com>2018-02-14 16:03:09 +0100
commitac81d1a06d57b9ae86663831e7f5edcef57b0fa4 (patch)
treeda31775c9533d3e270f68f921e146f086bf7c0b8 /server/controllers
parente883399fa6caa56bb8519c9a2e22d88001f26661 (diff)
downloadPeerTube-ac81d1a06d57b9ae86663831e7f5edcef57b0fa4.tar.gz
PeerTube-ac81d1a06d57b9ae86663831e7f5edcef57b0fa4.tar.zst
PeerTube-ac81d1a06d57b9ae86663831e7f5edcef57b0fa4.zip
Add ability to set video thumbnail/preview
Diffstat (limited to 'server/controllers')
-rw-r--r--server/controllers/api/users.ts19
-rw-r--r--server/controllers/api/videos/index.ts89
2 files changed, 77 insertions, 31 deletions
diff --git a/server/controllers/api/users.ts b/server/controllers/api/users.ts
index 6e5d09695..e3067584e 100644
--- a/server/controllers/api/users.ts
+++ b/server/controllers/api/users.ts
@@ -1,13 +1,13 @@
1import * as express from 'express' 1import * as express from 'express'
2import 'multer'
2import { extname, join } from 'path' 3import { extname, join } from 'path'
3import * as sharp from 'sharp'
4import * as uuidv4 from 'uuid/v4' 4import * as uuidv4 from 'uuid/v4'
5import { UserCreate, UserRight, UserRole, UserUpdate, UserUpdateMe, UserVideoRate as FormattedUserVideoRate } from '../../../shared' 5import { UserCreate, UserRight, UserRole, UserUpdate, UserUpdateMe, UserVideoRate as FormattedUserVideoRate } from '../../../shared'
6import { unlinkPromise } from '../../helpers/core-utils'
7import { retryTransactionWrapper } from '../../helpers/database-utils' 6import { retryTransactionWrapper } from '../../helpers/database-utils'
7import { processImage } from '../../helpers/image-utils'
8import { logger } from '../../helpers/logger' 8import { logger } from '../../helpers/logger'
9import { createReqFiles, getFormattedObjects } from '../../helpers/utils' 9import { createReqFiles, getFormattedObjects } from '../../helpers/utils'
10import { AVATAR_MIMETYPE_EXT, AVATARS_SIZE, CONFIG, sequelizeTypescript } from '../../initializers' 10import { AVATARS_SIZE, CONFIG, IMAGE_MIMETYPE_EXT, sequelizeTypescript } from '../../initializers'
11import { updateActorAvatarInstance } from '../../lib/activitypub' 11import { updateActorAvatarInstance } from '../../lib/activitypub'
12import { sendUpdateUser } from '../../lib/activitypub/send' 12import { sendUpdateUser } from '../../lib/activitypub/send'
13import { Emailer } from '../../lib/emailer' 13import { Emailer } from '../../lib/emailer'
@@ -42,7 +42,7 @@ import { UserModel } from '../../models/account/user'
42import { OAuthTokenModel } from '../../models/oauth/oauth-token' 42import { OAuthTokenModel } from '../../models/oauth/oauth-token'
43import { VideoModel } from '../../models/video/video' 43import { VideoModel } from '../../models/video/video'
44 44
45const reqAvatarFile = createReqFiles('avatarfile', CONFIG.STORAGE.AVATARS_DIR, AVATAR_MIMETYPE_EXT) 45const reqAvatarFile = createReqFiles([ 'avatarfile' ], IMAGE_MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.AVATARS_DIR })
46 46
47const usersRouter = express.Router() 47const usersRouter = express.Router()
48 48
@@ -288,17 +288,10 @@ async function updateMyAvatar (req: express.Request, res: express.Response, next
288 const user = res.locals.oauth.token.user 288 const user = res.locals.oauth.token.user
289 const actor = user.Account.Actor 289 const actor = user.Account.Actor
290 290
291 const avatarDir = CONFIG.STORAGE.AVATARS_DIR
292 const source = join(avatarDir, avatarPhysicalFile.filename)
293 const extension = extname(avatarPhysicalFile.filename) 291 const extension = extname(avatarPhysicalFile.filename)
294 const avatarName = uuidv4() + extension 292 const avatarName = uuidv4() + extension
295 const destination = join(avatarDir, avatarName) 293 const destination = join(CONFIG.STORAGE.AVATARS_DIR, avatarName)
296 294 await processImage(avatarPhysicalFile, destination, AVATARS_SIZE)
297 await sharp(source)
298 .resize(AVATARS_SIZE.width, AVATARS_SIZE.height)
299 .toFile(destination)
300
301 await unlinkPromise(source)
302 295
303 const avatar = await sequelizeTypescript.transaction(async t => { 296 const avatar = await sequelizeTypescript.transaction(async t => {
304 const updatedActor = await updateActorAvatarInstance(actor, avatarName, t) 297 const updatedActor = await updateActorAvatarInstance(actor, avatarName, t)
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts
index 459795141..1a4de081f 100644
--- a/server/controllers/api/videos/index.ts
+++ b/server/controllers/api/videos/index.ts
@@ -4,18 +4,36 @@ import { VideoCreate, VideoPrivacy, VideoUpdate } from '../../../../shared'
4import { renamePromise } from '../../../helpers/core-utils' 4import { renamePromise } from '../../../helpers/core-utils'
5import { retryTransactionWrapper } from '../../../helpers/database-utils' 5import { retryTransactionWrapper } from '../../../helpers/database-utils'
6import { getVideoFileHeight } from '../../../helpers/ffmpeg-utils' 6import { getVideoFileHeight } from '../../../helpers/ffmpeg-utils'
7import { processImage } from '../../../helpers/image-utils'
7import { logger } from '../../../helpers/logger' 8import { logger } from '../../../helpers/logger'
8import { createReqFiles, getFormattedObjects, getServerActor, resetSequelizeInstance } from '../../../helpers/utils' 9import { createReqFiles, getFormattedObjects, getServerActor, resetSequelizeInstance } from '../../../helpers/utils'
9import { 10import {
10 CONFIG, sequelizeTypescript, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_MIMETYPE_EXT, 11 CONFIG,
12 IMAGE_MIMETYPE_EXT,
13 PREVIEWS_SIZE,
14 sequelizeTypescript,
15 THUMBNAILS_SIZE,
16 VIDEO_CATEGORIES,
17 VIDEO_LANGUAGES,
18 VIDEO_LICENCES,
19 VIDEO_MIMETYPE_EXT,
11 VIDEO_PRIVACIES 20 VIDEO_PRIVACIES
12} from '../../../initializers' 21} from '../../../initializers'
13import { fetchRemoteVideoDescription, getVideoActivityPubUrl, shareVideoByServerAndChannel } from '../../../lib/activitypub' 22import { fetchRemoteVideoDescription, getVideoActivityPubUrl, shareVideoByServerAndChannel } from '../../../lib/activitypub'
14import { sendCreateVideo, sendCreateViewToOrigin, sendCreateViewToVideoFollowers, sendUpdateVideo } from '../../../lib/activitypub/send' 23import { sendCreateVideo, sendCreateViewToOrigin, sendCreateViewToVideoFollowers, sendUpdateVideo } from '../../../lib/activitypub/send'
15import { JobQueue } from '../../../lib/job-queue' 24import { JobQueue } from '../../../lib/job-queue'
16import { 25import {
17 asyncMiddleware, authenticate, paginationValidator, setDefaultSort, setDefaultPagination, videosAddValidator, videosGetValidator, 26 asyncMiddleware,
18 videosRemoveValidator, videosSearchValidator, videosSortValidator, videosUpdateValidator 27 authenticate,
28 paginationValidator,
29 setDefaultPagination,
30 setDefaultSort,
31 videosAddValidator,
32 videosGetValidator,
33 videosRemoveValidator,
34 videosSearchValidator,
35 videosSortValidator,
36 videosUpdateValidator
19} from '../../../middlewares' 37} from '../../../middlewares'
20import { TagModel } from '../../../models/video/tag' 38import { TagModel } from '../../../models/video/tag'
21import { VideoModel } from '../../../models/video/video' 39import { VideoModel } from '../../../models/video/video'
@@ -28,7 +46,23 @@ import { rateVideoRouter } from './rate'
28 46
29const videosRouter = express.Router() 47const videosRouter = express.Router()
30 48
31const reqVideoFile = createReqFiles('videofile', CONFIG.STORAGE.VIDEOS_DIR, VIDEO_MIMETYPE_EXT) 49const reqVideoFileAdd = createReqFiles(
50 [ 'videofile', 'thumbnailfile', 'previewfile' ],
51 Object.assign({}, VIDEO_MIMETYPE_EXT, IMAGE_MIMETYPE_EXT),
52 {
53 videofile: CONFIG.STORAGE.VIDEOS_DIR,
54 thumbnailfile: CONFIG.STORAGE.THUMBNAILS_DIR,
55 previewfile: CONFIG.STORAGE.PREVIEWS_DIR
56 }
57)
58const reqVideoFileUpdate = createReqFiles(
59 [ 'thumbnailfile', 'previewfile' ],
60 IMAGE_MIMETYPE_EXT,
61 {
62 thumbnailfile: CONFIG.STORAGE.THUMBNAILS_DIR,
63 previewfile: CONFIG.STORAGE.PREVIEWS_DIR
64 }
65)
32 66
33videosRouter.use('/', abuseVideoRouter) 67videosRouter.use('/', abuseVideoRouter)
34videosRouter.use('/', blacklistRouter) 68videosRouter.use('/', blacklistRouter)
@@ -58,12 +92,13 @@ videosRouter.get('/search',
58) 92)
59videosRouter.put('/:id', 93videosRouter.put('/:id',
60 authenticate, 94 authenticate,
95 reqVideoFileUpdate,
61 asyncMiddleware(videosUpdateValidator), 96 asyncMiddleware(videosUpdateValidator),
62 asyncMiddleware(updateVideoRetryWrapper) 97 asyncMiddleware(updateVideoRetryWrapper)
63) 98)
64videosRouter.post('/upload', 99videosRouter.post('/upload',
65 authenticate, 100 authenticate,
66 reqVideoFile, 101 reqVideoFileAdd,
67 asyncMiddleware(videosAddValidator), 102 asyncMiddleware(videosAddValidator),
68 asyncMiddleware(addVideoRetryWrapper) 103 asyncMiddleware(addVideoRetryWrapper)
69) 104)
@@ -150,8 +185,7 @@ async function addVideo (req: express.Request, res: express.Response, videoPhysi
150 const video = new VideoModel(videoData) 185 const video = new VideoModel(videoData)
151 video.url = getVideoActivityPubUrl(video) 186 video.url = getVideoActivityPubUrl(video)
152 187
153 const videoFilePath = join(CONFIG.STORAGE.VIDEOS_DIR, videoPhysicalFile.filename) 188 const videoFileHeight = await getVideoFileHeight(videoPhysicalFile.path)
154 const videoFileHeight = await getVideoFileHeight(videoFilePath)
155 189
156 const videoFileData = { 190 const videoFileData = {
157 extname: extname(videoPhysicalFile.filename), 191 extname: extname(videoPhysicalFile.filename),
@@ -160,21 +194,28 @@ async function addVideo (req: express.Request, res: express.Response, videoPhysi
160 } 194 }
161 const videoFile = new VideoFileModel(videoFileData) 195 const videoFile = new VideoFileModel(videoFileData)
162 const videoDir = CONFIG.STORAGE.VIDEOS_DIR 196 const videoDir = CONFIG.STORAGE.VIDEOS_DIR
163 const source = join(videoDir, videoPhysicalFile.filename)
164 const destination = join(videoDir, video.getVideoFilename(videoFile)) 197 const destination = join(videoDir, video.getVideoFilename(videoFile))
198 await renamePromise(videoPhysicalFile.path, destination)
165 199
166 await renamePromise(source, destination) 200 // Process thumbnail or create it from the video
167 // This is important in case if there is another attempt in the retry process 201 const thumbnailField = req.files['thumbnailfile']
168 videoPhysicalFile.filename = video.getVideoFilename(videoFile) 202 if (thumbnailField) {
203 const thumbnailPhysicalFile = thumbnailField[0]
204 await processImage(thumbnailPhysicalFile, join(CONFIG.STORAGE.THUMBNAILS_DIR, video.getThumbnailName()), THUMBNAILS_SIZE)
205 } else {
206 await video.createThumbnail(videoFile)
207 }
169 208
170 const tasks = [] 209 // Process preview or create it from the video
210 const previewField = req.files['previewfile']
211 if (previewField) {
212 const previewPhysicalFile = previewField[0]
213 await processImage(previewPhysicalFile, join(CONFIG.STORAGE.PREVIEWS_DIR, video.getPreviewName()), PREVIEWS_SIZE)
214 } else {
215 await video.createPreview(videoFile)
216 }
171 217
172 tasks.push( 218 await video.createTorrentAndSetInfoHash(videoFile)
173 video.createTorrentAndSetInfoHash(videoFile),
174 video.createThumbnail(videoFile),
175 video.createPreview(videoFile)
176 )
177 await Promise.all(tasks)
178 219
179 const videoCreated = await sequelizeTypescript.transaction(async t => { 220 const videoCreated = await sequelizeTypescript.transaction(async t => {
180 const sequelizeOptions = { transaction: t } 221 const sequelizeOptions = { transaction: t }
@@ -237,6 +278,18 @@ async function updateVideo (req: express.Request, res: express.Response) {
237 const videoInfoToUpdate: VideoUpdate = req.body 278 const videoInfoToUpdate: VideoUpdate = req.body
238 const wasPrivateVideo = videoInstance.privacy === VideoPrivacy.PRIVATE 279 const wasPrivateVideo = videoInstance.privacy === VideoPrivacy.PRIVATE
239 280
281 // Process thumbnail or create it from the video
282 if (req.files && req.files['thumbnailfile']) {
283 const thumbnailPhysicalFile = req.files['thumbnailfile'][0]
284 await processImage(thumbnailPhysicalFile, join(CONFIG.STORAGE.THUMBNAILS_DIR, videoInstance.getThumbnailName()), THUMBNAILS_SIZE)
285 }
286
287 // Process preview or create it from the video
288 if (req.files && req.files['previewfile']) {
289 const previewPhysicalFile = req.files['previewfile'][0]
290 await processImage(previewPhysicalFile, join(CONFIG.STORAGE.PREVIEWS_DIR, videoInstance.getPreviewName()), PREVIEWS_SIZE)
291 }
292
240 try { 293 try {
241 await sequelizeTypescript.transaction(async t => { 294 await sequelizeTypescript.transaction(async t => {
242 const sequelizeOptions = { 295 const sequelizeOptions = {