"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.mergeAudioVideofile = exports.transcodeNewWebTorrentResolution = exports.optimizeOriginalVideofile = exports.generateHlsPlaylistResolutionFromTS = exports.generateHlsPlaylistResolution = void 0;
const tslib_1 = require("tslib");
const fs_extra_1 = require("fs-extra");
const path_1 = require("path");
const core_utils_1 = require("../../helpers/core-utils");
const database_utils_1 = require("../../helpers/database-utils");
const webtorrent_1 = require("../../helpers/webtorrent");
const database_1 = require("../../initializers/database");
const core_utils_2 = require("../../../shared/core-utils");
const ffmpeg_1 = require("../../helpers/ffmpeg");
const config_1 = require("../../initializers/config");
const video_file_1 = require("../../models/video/video-file");
const video_streaming_playlist_1 = require("../../models/video/video-streaming-playlist");
const hls_1 = require("../hls");
const paths_1 = require("../paths");
const video_path_manager_1 = require("../video-path-manager");
const default_transcoding_profiles_1 = require("./default-transcoding-profiles");
function optimizeOriginalVideofile(options) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const { video, inputVideoFile, job } = options;
        const transcodeDirectory = config_1.CONFIG.STORAGE.TMP_DIR;
        const newExtname = '.mp4';
        const inputFileMutexReleaser = yield video_path_manager_1.VideoPathManager.Instance.lockFiles(video.uuid);
        try {
            yield video.reload();
            const fileWithVideoOrPlaylist = inputVideoFile.withVideoOrPlaylist(video);
            const result = yield video_path_manager_1.VideoPathManager.Instance.makeAvailableVideoFile(fileWithVideoOrPlaylist, (videoInputPath) => tslib_1.__awaiter(this, void 0, void 0, function* () {
                const videoTranscodedPath = (0, path_1.join)(transcodeDirectory, video.id + '-transcoded' + newExtname);
                const transcodeType = (yield (0, ffmpeg_1.canDoQuickTranscode)(videoInputPath))
                    ? 'quick-transcode'
                    : 'video';
                const resolution = buildOriginalFileResolution(inputVideoFile.resolution);
                const transcodeOptions = {
                    type: transcodeType,
                    inputPath: videoInputPath,
                    outputPath: videoTranscodedPath,
                    inputFileMutexReleaser,
                    availableEncoders: default_transcoding_profiles_1.VideoTranscodingProfilesManager.Instance.getAvailableEncoders(),
                    profile: config_1.CONFIG.TRANSCODING.PROFILE,
                    resolution,
                    job
                };
                yield (0, ffmpeg_1.transcodeVOD)(transcodeOptions);
                inputVideoFile.resolution = resolution;
                inputVideoFile.extname = newExtname;
                inputVideoFile.filename = (0, paths_1.generateWebTorrentVideoFilename)(resolution, newExtname);
                inputVideoFile.storage = 0;
                const { videoFile } = yield onWebTorrentVideoFileTranscoding(video, inputVideoFile, videoTranscodedPath, inputVideoFile);
                yield (0, fs_extra_1.remove)(videoInputPath);
                return { transcodeType, videoFile };
            }));
            return result;
        }
        finally {
            inputFileMutexReleaser();
        }
    });
}
exports.optimizeOriginalVideofile = optimizeOriginalVideofile;
function transcodeNewWebTorrentResolution(options) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const { video, resolution, job } = options;
        const transcodeDirectory = config_1.CONFIG.STORAGE.TMP_DIR;
        const newExtname = '.mp4';
        const inputFileMutexReleaser = yield video_path_manager_1.VideoPathManager.Instance.lockFiles(video.uuid);
        try {
            yield video.reload();
            const file = video.getMaxQualityFile().withVideoOrPlaylist(video);
            const result = yield video_path_manager_1.VideoPathManager.Instance.makeAvailableVideoFile(file, (videoInputPath) => tslib_1.__awaiter(this, void 0, void 0, function* () {
                const newVideoFile = new video_file_1.VideoFileModel({
                    resolution,
                    extname: newExtname,
                    filename: (0, paths_1.generateWebTorrentVideoFilename)(resolution, newExtname),
                    size: 0,
                    videoId: video.id
                });
                const videoTranscodedPath = (0, path_1.join)(transcodeDirectory, newVideoFile.filename);
                const transcodeOptions = resolution === 0
                    ? {
                        type: 'only-audio',
                        inputPath: videoInputPath,
                        outputPath: videoTranscodedPath,
                        inputFileMutexReleaser,
                        availableEncoders: default_transcoding_profiles_1.VideoTranscodingProfilesManager.Instance.getAvailableEncoders(),
                        profile: config_1.CONFIG.TRANSCODING.PROFILE,
                        resolution,
                        job
                    }
                    : {
                        type: 'video',
                        inputPath: videoInputPath,
                        outputPath: videoTranscodedPath,
                        inputFileMutexReleaser,
                        availableEncoders: default_transcoding_profiles_1.VideoTranscodingProfilesManager.Instance.getAvailableEncoders(),
                        profile: config_1.CONFIG.TRANSCODING.PROFILE,
                        resolution,
                        job
                    };
                yield (0, ffmpeg_1.transcodeVOD)(transcodeOptions);
                return onWebTorrentVideoFileTranscoding(video, newVideoFile, videoTranscodedPath, newVideoFile);
            }));
            return result;
        }
        finally {
            inputFileMutexReleaser();
        }
    });
}
exports.transcodeNewWebTorrentResolution = transcodeNewWebTorrentResolution;
function mergeAudioVideofile(options) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const { video, resolution, job } = options;
        const transcodeDirectory = config_1.CONFIG.STORAGE.TMP_DIR;
        const newExtname = '.mp4';
        const inputFileMutexReleaser = yield video_path_manager_1.VideoPathManager.Instance.lockFiles(video.uuid);
        try {
            yield video.reload();
            const inputVideoFile = video.getMinQualityFile();
            const fileWithVideoOrPlaylist = inputVideoFile.withVideoOrPlaylist(video);
            const result = yield video_path_manager_1.VideoPathManager.Instance.makeAvailableVideoFile(fileWithVideoOrPlaylist, (audioInputPath) => tslib_1.__awaiter(this, void 0, void 0, function* () {
                const videoTranscodedPath = (0, path_1.join)(transcodeDirectory, video.id + '-transcoded' + newExtname);
                const previewPath = video.getPreview().getPath();
                const tmpPreviewPath = (0, path_1.join)(config_1.CONFIG.STORAGE.TMP_DIR, (0, path_1.basename)(previewPath));
                yield (0, fs_extra_1.copyFile)(previewPath, tmpPreviewPath);
                const transcodeOptions = {
                    type: 'merge-audio',
                    inputPath: tmpPreviewPath,
                    outputPath: videoTranscodedPath,
                    inputFileMutexReleaser,
                    availableEncoders: default_transcoding_profiles_1.VideoTranscodingProfilesManager.Instance.getAvailableEncoders(),
                    profile: config_1.CONFIG.TRANSCODING.PROFILE,
                    audioPath: audioInputPath,
                    resolution,
                    job
                };
                try {
                    yield (0, ffmpeg_1.transcodeVOD)(transcodeOptions);
                    yield (0, fs_extra_1.remove)(audioInputPath);
                    yield (0, fs_extra_1.remove)(tmpPreviewPath);
                }
                catch (err) {
                    yield (0, fs_extra_1.remove)(tmpPreviewPath);
                    throw err;
                }
                inputVideoFile.extname = newExtname;
                inputVideoFile.resolution = resolution;
                inputVideoFile.filename = (0, paths_1.generateWebTorrentVideoFilename)(inputVideoFile.resolution, newExtname);
                video.duration = yield (0, ffmpeg_1.getVideoStreamDuration)(videoTranscodedPath);
                yield video.save();
                return onWebTorrentVideoFileTranscoding(video, inputVideoFile, videoTranscodedPath, inputVideoFile);
            }));
            return result;
        }
        finally {
            inputFileMutexReleaser();
        }
    });
}
exports.mergeAudioVideofile = mergeAudioVideofile;
function generateHlsPlaylistResolutionFromTS(options) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        return generateHlsPlaylistCommon(Object.assign({ type: 'hls-from-ts', inputPath: options.concatenatedTsFilePath }, (0, core_utils_2.pick)(options, ['video', 'resolution', 'inputFileMutexReleaser', 'isAAC'])));
    });
}
exports.generateHlsPlaylistResolutionFromTS = generateHlsPlaylistResolutionFromTS;
function generateHlsPlaylistResolution(options) {
    return generateHlsPlaylistCommon(Object.assign({ type: 'hls', inputPath: options.videoInputPath }, (0, core_utils_2.pick)(options, ['video', 'resolution', 'copyCodecs', 'inputFileMutexReleaser', 'job'])));
}
exports.generateHlsPlaylistResolution = generateHlsPlaylistResolution;
function onWebTorrentVideoFileTranscoding(video, videoFile, transcodingPath, newVideoFile) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const mutexReleaser = yield video_path_manager_1.VideoPathManager.Instance.lockFiles(video.uuid);
        try {
            yield video.reload();
            const outputPath = video_path_manager_1.VideoPathManager.Instance.getFSVideoFileOutputPath(video, newVideoFile);
            const stats = yield (0, fs_extra_1.stat)(transcodingPath);
            const probe = yield (0, ffmpeg_1.ffprobePromise)(transcodingPath);
            const fps = yield (0, ffmpeg_1.getVideoStreamFPS)(transcodingPath, probe);
            const metadata = yield (0, ffmpeg_1.buildFileMetadata)(transcodingPath, probe);
            yield (0, fs_extra_1.move)(transcodingPath, outputPath, { overwrite: true });
            videoFile.size = stats.size;
            videoFile.fps = fps;
            videoFile.metadata = metadata;
            yield (0, webtorrent_1.createTorrentAndSetInfoHash)(video, videoFile);
            const oldFile = yield video_file_1.VideoFileModel.loadWebTorrentFile({ videoId: video.id, fps: videoFile.fps, resolution: videoFile.resolution });
            if (oldFile)
                yield video.removeWebTorrentFile(oldFile);
            yield video_file_1.VideoFileModel.customUpsert(videoFile, 'video', undefined);
            video.VideoFiles = yield video.$get('VideoFiles');
            return { video, videoFile };
        }
        finally {
            mutexReleaser();
        }
    });
}
function generateHlsPlaylistCommon(options) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const { type, video, inputPath, resolution, copyCodecs, isAAC, job, inputFileMutexReleaser } = options;
        const transcodeDirectory = config_1.CONFIG.STORAGE.TMP_DIR;
        const videoTranscodedBasePath = (0, path_1.join)(transcodeDirectory, type);
        yield (0, fs_extra_1.ensureDir)(videoTranscodedBasePath);
        const videoFilename = (0, paths_1.generateHLSVideoFilename)(resolution);
        const resolutionPlaylistFilename = (0, paths_1.getHlsResolutionPlaylistFilename)(videoFilename);
        const resolutionPlaylistFileTranscodePath = (0, path_1.join)(videoTranscodedBasePath, resolutionPlaylistFilename);
        const transcodeOptions = {
            type,
            inputPath,
            outputPath: resolutionPlaylistFileTranscodePath,
            availableEncoders: default_transcoding_profiles_1.VideoTranscodingProfilesManager.Instance.getAvailableEncoders(),
            profile: config_1.CONFIG.TRANSCODING.PROFILE,
            resolution,
            copyCodecs,
            isAAC,
            inputFileMutexReleaser,
            hlsPlaylist: {
                videoFilename
            },
            job
        };
        yield (0, ffmpeg_1.transcodeVOD)(transcodeOptions);
        const playlist = yield (0, database_utils_1.retryTransactionWrapper)(() => {
            return database_1.sequelizeTypescript.transaction((transaction) => tslib_1.__awaiter(this, void 0, void 0, function* () {
                return video_streaming_playlist_1.VideoStreamingPlaylistModel.loadOrGenerate(video, transaction);
            }));
        });
        const newVideoFile = new video_file_1.VideoFileModel({
            resolution,
            extname: (0, path_1.extname)(videoFilename),
            size: 0,
            filename: videoFilename,
            fps: -1,
            videoStreamingPlaylistId: playlist.id
        });
        const mutexReleaser = yield video_path_manager_1.VideoPathManager.Instance.lockFiles(video.uuid);
        try {
            yield video.reload();
            const videoFilePath = video_path_manager_1.VideoPathManager.Instance.getFSVideoFileOutputPath(playlist, newVideoFile);
            yield (0, fs_extra_1.ensureDir)(video_path_manager_1.VideoPathManager.Instance.getFSHLSOutputPath(video));
            const resolutionPlaylistPath = video_path_manager_1.VideoPathManager.Instance.getFSHLSOutputPath(video, resolutionPlaylistFilename);
            yield (0, fs_extra_1.move)(resolutionPlaylistFileTranscodePath, resolutionPlaylistPath, { overwrite: true });
            yield (0, fs_extra_1.move)((0, path_1.join)(videoTranscodedBasePath, videoFilename), videoFilePath, { overwrite: true });
            if (!video.duration) {
                video.duration = yield (0, ffmpeg_1.getVideoStreamDuration)(videoFilePath);
                yield video.save();
            }
            const stats = yield (0, fs_extra_1.stat)(videoFilePath);
            newVideoFile.size = stats.size;
            newVideoFile.fps = yield (0, ffmpeg_1.getVideoStreamFPS)(videoFilePath);
            newVideoFile.metadata = yield (0, ffmpeg_1.buildFileMetadata)(videoFilePath);
            yield (0, webtorrent_1.createTorrentAndSetInfoHash)(playlist, newVideoFile);
            const oldFile = yield video_file_1.VideoFileModel.loadHLSFile({
                playlistId: playlist.id,
                fps: newVideoFile.fps,
                resolution: newVideoFile.resolution
            });
            if (oldFile) {
                yield video.removeStreamingPlaylistVideoFile(playlist, oldFile);
                yield oldFile.destroy();
            }
            const savedVideoFile = yield video_file_1.VideoFileModel.customUpsert(newVideoFile, 'streaming-playlist', undefined);
            yield (0, hls_1.updatePlaylistAfterFileChange)(video, playlist);
            return { resolutionPlaylistPath, videoFile: savedVideoFile };
        }
        finally {
            mutexReleaser();
        }
    });
}
function buildOriginalFileResolution(inputResolution) {
    if (config_1.CONFIG.TRANSCODING.ALWAYS_TRANSCODE_ORIGINAL_RESOLUTION === true) {
        return (0, core_utils_1.toEven)(inputResolution);
    }
    const resolutions = (0, ffmpeg_1.computeResolutionsToTranscode)({
        input: inputResolution,
        type: 'vod',
        includeInput: false,
        strictLower: false,
        hasAudio: true
    });
    if (resolutions.length === 0) {
        return (0, core_utils_1.toEven)(inputResolution);
    }
    return Math.max(...resolutions);
}
//# sourceMappingURL=transcoding.js.map