X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=server%2Fcontrollers%2Fapi%2Fvideos%2Fupdate.ts;h=0a910379a9453b7a0821e007a0e869c08dabf829;hb=3545e72c686ff1725bbdfd8d16d693e2f4aa75a3;hp=65a7321fd22d5c9e0ee192f55585633bd20496ea;hpb=4fae2b1f300c1f027629569817262f60873a663a;p=github%2FChocobozzz%2FPeerTube.git diff --git a/server/controllers/api/videos/update.ts b/server/controllers/api/videos/update.ts index 65a7321fd..0a910379a 100644 --- a/server/controllers/api/videos/update.ts +++ b/server/controllers/api/videos/update.ts @@ -1,25 +1,24 @@ import express from 'express' import { Transaction } from 'sequelize/types' import { changeVideoChannelShare } from '@server/lib/activitypub/share' -import { JobQueue } from '@server/lib/job-queue' -import { buildVideoThumbnailsFromReq, setVideoTags } from '@server/lib/video' +import { addVideoJobsAfterUpdate, buildVideoThumbnailsFromReq, setVideoTags } from '@server/lib/video' +import { setVideoPrivacy } from '@server/lib/video-privacy' import { openapiOperationDoc } from '@server/middlewares/doc' import { FilteredModelAttributes } from '@server/types' import { MVideoFullLight } from '@server/types/models' -import { HttpStatusCode, ManageVideoTorrentPayload, VideoUpdate } from '@shared/models' +import { HttpStatusCode, VideoUpdate } from '@shared/models' import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' import { resetSequelizeInstance } from '../../../helpers/database-utils' import { createReqFiles } from '../../../helpers/express-utils' import { logger, loggerTagsFactory } from '../../../helpers/logger' import { MIMETYPES } from '../../../initializers/constants' import { sequelizeTypescript } from '../../../initializers/database' -import { federateVideoIfNeeded } from '../../../lib/activitypub/videos' -import { Notifier } from '../../../lib/notifier' import { Hooks } from '../../../lib/plugins/hooks' import { autoBlacklistVideoIfNeeded } from '../../../lib/video-blacklist' import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videosUpdateValidator } from '../../../middlewares' import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-update' import { VideoModel } from '../../../models/video/video' +import { VideoPathManager } from '@server/lib/video-path-manager' const lTags = loggerTagsFactory('api', 'video') const auditLogger = auditLoggerFactory('videos') @@ -49,8 +48,8 @@ async function updateVideo (req: express.Request, res: express.Response) { const oldVideoAuditView = new VideoAuditView(videoFromReq.toFormattedDetailsJSON()) const videoInfoToUpdate: VideoUpdate = req.body - const wasConfidentialVideo = videoFromReq.isConfidential() const hadPrivacyForFederation = videoFromReq.hasPrivacyForFederation() + const oldPrivacy = videoFromReq.privacy const [ thumbnailModel, previewModel ] = await buildVideoThumbnailsFromReq({ video: videoFromReq, @@ -59,12 +58,13 @@ async function updateVideo (req: express.Request, res: express.Response) { automaticallyGenerated: false }) + const videoFileLockReleaser = await VideoPathManager.Instance.lockFiles(videoFromReq.uuid) + try { const { videoInstanceUpdated, isNewVideo } = await sequelizeTypescript.transaction(async t => { // Refresh video since thumbnails to prevent concurrent updates const video = await VideoModel.loadFull(videoFromReq.id, t) - const sequelizeOptions = { transaction: t } const oldVideoChannel = video.VideoChannel const keysToUpdate: (keyof VideoUpdate & FilteredModelAttributes)[] = [ @@ -99,7 +99,7 @@ async function updateVideo (req: express.Request, res: express.Response) { await video.setAsRefreshed(t) } - const videoInstanceUpdated = await video.save(sequelizeOptions) as MVideoFullLight + const videoInstanceUpdated = await video.save({ transaction: t }) as MVideoFullLight // Thumbnail & preview updates? if (thumbnailModel) await videoInstanceUpdated.addAndSaveThumbnail(thumbnailModel, t) @@ -115,7 +115,9 @@ async function updateVideo (req: express.Request, res: express.Response) { await videoInstanceUpdated.$set('VideoChannel', res.locals.videoChannel, { transaction: t }) videoInstanceUpdated.VideoChannel = res.locals.videoChannel - if (hadPrivacyForFederation === true) await changeVideoChannelShare(videoInstanceUpdated, oldVideoChannel, t) + if (hadPrivacyForFederation === true) { + await changeVideoChannelShare(videoInstanceUpdated, oldVideoChannel, t) + } } // Schedule an update in the future? @@ -139,13 +141,14 @@ async function updateVideo (req: express.Request, res: express.Response) { return { videoInstanceUpdated, isNewVideo } }) - const refreshedVideo = await updateTorrentsMetadataIfNeeded(videoInstanceUpdated, videoInfoToUpdate) - - await sequelizeTypescript.transaction(t => federateVideoIfNeeded(refreshedVideo, isNewVideo, t)) + Hooks.runAction('action:api.video.updated', { video: videoInstanceUpdated, body: req.body, req, res }) - if (wasConfidentialVideo) Notifier.Instance.notifyOnNewVideoIfNeeded(refreshedVideo) - - Hooks.runAction('action:api.video.updated', { video: refreshedVideo, body: req.body, req, res }) + await addVideoJobsAfterUpdate({ + video: videoInstanceUpdated, + nameChanged: !!videoInfoToUpdate.name, + oldPrivacy, + isNewVideo + }) } catch (err) { // Force fields we want to update // If the transaction is retried, sequelize will think the object has not changed @@ -153,6 +156,8 @@ async function updateVideo (req: express.Request, res: express.Response) { resetSequelizeInstance(videoFromReq, videoFieldsSave) throw err + } finally { + videoFileLockReleaser() } return res.type('json') @@ -170,7 +175,7 @@ async function updateVideoPrivacy (options: { const isNewVideo = videoInstance.isNewVideo(videoInfoToUpdate.privacy) const newPrivacy = parseInt(videoInfoToUpdate.privacy.toString(), 10) - videoInstance.setPrivacy(newPrivacy) + setVideoPrivacy(videoInstance, newPrivacy) // Unfederate the video if the new privacy is not compatible with federation if (hadPrivacyForFederation && !videoInstance.hasPrivacyForFederation()) { @@ -191,26 +196,3 @@ function updateSchedule (videoInstance: MVideoFullLight, videoInfoToUpdate: Vide return ScheduleVideoUpdateModel.deleteByVideoId(videoInstance.id, transaction) } } - -async function updateTorrentsMetadataIfNeeded (video: MVideoFullLight, videoInfoToUpdate: VideoUpdate) { - if (video.isLive || !videoInfoToUpdate.name) return video - - for (const file of (video.VideoFiles || [])) { - const payload: ManageVideoTorrentPayload = { action: 'update-metadata', videoId: video.id, videoFileId: file.id } - - const job = await JobQueue.Instance.createJobWithPromise({ type: 'manage-video-torrent', payload }) - await job.finished() - } - - const hls = video.getHLSPlaylist() - - for (const file of (hls?.VideoFiles || [])) { - const payload: ManageVideoTorrentPayload = { action: 'update-metadata', streamingPlaylistId: hls.id, videoFileId: file.id } - - const job = await JobQueue.Instance.createJobWithPromise({ type: 'manage-video-torrent', payload }) - await job.finished() - } - - // Refresh video since files have changed - return VideoModel.loadFull(video.id) -}