+import { Mutex } from 'async-mutex'
import { remove } from 'fs-extra'
import { extname, join } from 'path'
-import { buildUUID } from '@server/helpers/uuid'
+import { logger, loggerTagsFactory } from '@server/helpers/logger'
import { extractVideo } from '@server/helpers/video'
import { CONFIG } from '@server/initializers/config'
-import {
- MStreamingPlaylistVideo,
- MVideo,
- MVideoFile,
- MVideoFileStreamingPlaylistVideo,
- MVideoFileVideo,
- MVideoUUID
-} from '@server/types/models'
+import { DIRECTORIES } from '@server/initializers/constants'
+import { MStreamingPlaylistVideo, MVideo, MVideoFile, MVideoFileStreamingPlaylistVideo, MVideoFileVideo } from '@server/types/models'
+import { buildUUID } from '@shared/extra-utils'
import { VideoStorage } from '@shared/models'
import { makeHLSFileAvailable, makeWebTorrentFileAvailable } from './object-storage'
import { getHLSDirectory, getHLSRedundancyDirectory, getHlsResolutionPlaylistFilename } from './paths'
+import { isVideoInPrivateDirectory } from './video-privacy'
type MakeAvailableCB <T> = (path: string) => Promise<T> | T
+const lTags = loggerTagsFactory('video-path-manager')
+
class VideoPathManager {
private static instance: VideoPathManager
+ // Key is a video UUID
+ private readonly videoFileMutexStore = new Map<string, Mutex>()
+
private constructor () {}
- getFSHLSOutputPath (video: MVideoUUID, filename?: string) {
+ getFSHLSOutputPath (video: MVideo, filename?: string) {
const base = getHLSDirectory(video)
if (!filename) return base
}
getFSVideoFileOutputPath (videoOrPlaylist: MVideo | MStreamingPlaylistVideo, videoFile: MVideoFile) {
- if (videoFile.isHLS()) {
- const video = extractVideo(videoOrPlaylist)
+ const video = extractVideo(videoOrPlaylist)
+ if (videoFile.isHLS()) {
return join(getHLSDirectory(video), videoFile.filename)
}
- return join(CONFIG.STORAGE.VIDEOS_DIR, videoFile.filename)
+ if (isVideoInPrivateDirectory(video.privacy)) {
+ return join(DIRECTORIES.VIDEOS.PRIVATE, videoFile.filename)
+ }
+
+ return join(DIRECTORIES.VIDEOS.PUBLIC, videoFile.filename)
}
async makeAvailableVideoFile <T> (videoFile: MVideoFileVideo | MVideoFileStreamingPlaylistVideo, cb: MakeAvailableCB<T>) {
)
}
+ async lockFiles (videoUUID: string) {
+ if (!this.videoFileMutexStore.has(videoUUID)) {
+ this.videoFileMutexStore.set(videoUUID, new Mutex())
+ }
+
+ const mutex = this.videoFileMutexStore.get(videoUUID)
+ const releaser = await mutex.acquire()
+
+ logger.debug('Locked files of %s.', videoUUID, lTags(videoUUID))
+
+ return releaser
+ }
+
+ unlockFiles (videoUUID: string) {
+ const mutex = this.videoFileMutexStore.get(videoUUID)
+
+ mutex.release()
+
+ logger.debug('Released lockfiles of %s.', videoUUID, lTags(videoUUID))
+ }
+
private async makeAvailableFactory <T> (method: () => Promise<string> | string, clean: boolean, cb: MakeAvailableCB<T>) {
let result: T