"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.processVideoImport = void 0;
const tslib_1 = require("tslib");
const fs_extra_1 = require("fs-extra");
const database_utils_1 = require("../../../helpers/database-utils");
const youtube_dl_1 = require("../../../helpers/youtube-dl");
const config_1 = require("../../../initializers/config");
const moderation_1 = require("../../moderation");
const paths_1 = require("../../paths");
const hooks_1 = require("../../plugins/hooks");
const server_config_manager_1 = require("../../server-config-manager");
const user_1 = require("../../user");
const video_1 = require("../../video");
const video_path_manager_1 = require("../../video-path-manager");
const video_state_1 = require("../../video-state");
const thumbnail_1 = require("../../../models/video/thumbnail");
const core_utils_1 = require("../../../../shared/core-utils");
const extra_utils_1 = require("../../../../shared/extra-utils");
const ffmpeg_1 = require("../../../helpers/ffmpeg");
const logger_1 = require("../../../helpers/logger");
const utils_1 = require("../../../helpers/utils");
const webtorrent_1 = require("../../../helpers/webtorrent");
const constants_1 = require("../../../initializers/constants");
const database_1 = require("../../../initializers/database");
const video_2 = require("../../../models/video/video");
const video_file_1 = require("../../../models/video/video-file");
const video_import_1 = require("../../../models/video/video-import");
const videos_1 = require("../../activitypub/videos");
const notifier_1 = require("../../notifier");
const thumbnail_2 = require("../../thumbnail");
const job_queue_1 = require("../job-queue");
function processVideoImport(job) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const payload = job.data;
        const videoImport = yield getVideoImportOrDie(payload);
        if (videoImport.state === 5) {
            logger_1.logger.info('Do not process import since it has been cancelled', { payload });
            return { resultType: 'success' };
        }
        videoImport.state = 6;
        yield videoImport.save();
        try {
            if (payload.type === 'youtube-dl')
                yield processYoutubeDLImport(job, videoImport, payload);
            if (payload.type === 'magnet-uri' || payload.type === 'torrent-file')
                yield processTorrentImport(job, videoImport, payload);
            return { resultType: 'success' };
        }
        catch (err) {
            if (!payload.preventException)
                throw err;
            logger_1.logger.warn('Catch error in video import to send value to parent job.', { payload, err });
            return { resultType: 'error' };
        }
    });
}
exports.processVideoImport = processVideoImport;
function processTorrentImport(job, videoImport, payload) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        logger_1.logger.info('Processing torrent video import in job %s.', job.id);
        const options = { type: payload.type, videoImportId: payload.videoImportId };
        const target = {
            torrentName: videoImport.torrentName ? (0, utils_1.getSecureTorrentName)(videoImport.torrentName) : undefined,
            uri: videoImport.magnetUri
        };
        return processFile(() => (0, webtorrent_1.downloadWebTorrentVideo)(target, constants_1.JOB_TTL['video-import']), videoImport, options);
    });
}
function processYoutubeDLImport(job, videoImport, payload) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        logger_1.logger.info('Processing youtubeDL video import in job %s.', job.id);
        const options = { type: payload.type, videoImportId: videoImport.id };
        const youtubeDL = new youtube_dl_1.YoutubeDLWrapper(videoImport.targetUrl, server_config_manager_1.ServerConfigManager.Instance.getEnabledResolutions('vod'), config_1.CONFIG.TRANSCODING.ALWAYS_TRANSCODE_ORIGINAL_RESOLUTION);
        return processFile(() => youtubeDL.downloadVideo(payload.fileExt, constants_1.JOB_TTL['video-import']), videoImport, options);
    });
}
function getVideoImportOrDie(payload) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const videoImport = yield video_import_1.VideoImportModel.loadAndPopulateVideo(payload.videoImportId);
        if (!(videoImport === null || videoImport === void 0 ? void 0 : videoImport.Video)) {
            throw new Error(`Cannot import video ${payload.videoImportId}: the video import or video linked to this import does not exist anymore.`);
        }
        return videoImport;
    });
}
function processFile(downloader, videoImport, options) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        let tempVideoPath;
        let videoFile;
        try {
            tempVideoPath = yield downloader();
            const stats = yield (0, fs_extra_1.stat)(tempVideoPath);
            const isAble = yield (0, user_1.isAbleToUploadVideo)(videoImport.User.id, stats.size);
            if (isAble === false) {
                throw new Error('The user video quota is exceeded with this video to import.');
            }
            const probe = yield (0, ffmpeg_1.ffprobePromise)(tempVideoPath);
            const { resolution } = (yield (0, extra_utils_1.isAudioFile)(tempVideoPath, probe))
                ? { resolution: 0 }
                : yield (0, ffmpeg_1.getVideoStreamDimensionsInfo)(tempVideoPath);
            const fps = yield (0, ffmpeg_1.getVideoStreamFPS)(tempVideoPath, probe);
            const duration = yield (0, ffmpeg_1.getVideoStreamDuration)(tempVideoPath, probe);
            const fileExt = (0, core_utils_1.getLowercaseExtension)(tempVideoPath);
            const videoFileData = {
                extname: fileExt,
                resolution,
                size: stats.size,
                filename: (0, paths_1.generateWebTorrentVideoFilename)(resolution, fileExt),
                fps,
                videoId: videoImport.videoId
            };
            videoFile = new video_file_1.VideoFileModel(videoFileData);
            const hookName = options.type === 'youtube-dl'
                ? 'filter:api.video.post-import-url.accept.result'
                : 'filter:api.video.post-import-torrent.accept.result';
            const acceptParameters = {
                videoImport,
                video: videoImport.Video,
                videoFilePath: tempVideoPath,
                videoFile,
                user: videoImport.User
            };
            const acceptedResult = yield hooks_1.Hooks.wrapFun(moderation_1.isPostImportVideoAccepted, acceptParameters, hookName);
            if (acceptedResult.accepted !== true) {
                logger_1.logger.info('Refused imported video.', { acceptedResult, acceptParameters });
                videoImport.state = 4;
                yield videoImport.save();
                throw new Error(acceptedResult.errorMessage);
            }
            const videoFileLockReleaser = yield video_path_manager_1.VideoPathManager.Instance.lockFiles(videoImport.Video.uuid);
            try {
                const videoImportWithFiles = yield refreshVideoImportFromDB(videoImport, videoFile);
                const videoDestFile = video_path_manager_1.VideoPathManager.Instance.getFSVideoFileOutputPath(videoImportWithFiles.Video, videoFile);
                yield (0, fs_extra_1.move)(tempVideoPath, videoDestFile);
                tempVideoPath = null;
                let { miniatureModel: thumbnailModel, miniatureJSONSave: thumbnailSave } = yield generateMiniature(videoImportWithFiles, videoFile, 1);
                let { miniatureModel: previewModel, miniatureJSONSave: previewSave } = yield generateMiniature(videoImportWithFiles, videoFile, 2);
                yield (0, webtorrent_1.createTorrentAndSetInfoHash)(videoImportWithFiles.Video, videoFile);
                const videoFileSave = videoFile.toJSON();
                const { videoImportUpdated, video } = yield (0, database_utils_1.retryTransactionWrapper)(() => {
                    return database_1.sequelizeTypescript.transaction((t) => tslib_1.__awaiter(this, void 0, void 0, function* () {
                        const video = yield video_2.VideoModel.load(videoImportWithFiles.videoId, t);
                        if (!video)
                            throw new Error('Video linked to import ' + videoImportWithFiles.videoId + ' does not exist anymore.');
                        yield videoFile.save({ transaction: t });
                        video.duration = duration;
                        video.state = (0, video_state_1.buildNextVideoState)(video.state);
                        yield video.save({ transaction: t });
                        if (thumbnailModel)
                            yield video.addAndSaveThumbnail(thumbnailModel, t);
                        if (previewModel)
                            yield video.addAndSaveThumbnail(previewModel, t);
                        const videoForFederation = yield video_2.VideoModel.loadFull(video.uuid, t);
                        yield (0, videos_1.federateVideoIfNeeded)(videoForFederation, true, t);
                        videoImportWithFiles.state = 2;
                        const videoImportUpdated = yield videoImportWithFiles.save({ transaction: t });
                        logger_1.logger.info('Video %s imported.', video.uuid);
                        return { videoImportUpdated, video: videoForFederation };
                    })).catch(err => {
                        if (thumbnailModel)
                            thumbnailModel = new thumbnail_1.ThumbnailModel(thumbnailSave);
                        if (previewModel)
                            previewModel = new thumbnail_1.ThumbnailModel(previewSave);
                        videoFile = new video_file_1.VideoFileModel(videoFileSave);
                        throw err;
                    });
                });
                yield afterImportSuccess({ videoImport: videoImportUpdated, video, videoFile, user: videoImport.User });
            }
            finally {
                videoFileLockReleaser();
            }
        }
        catch (err) {
            yield onImportError(err, tempVideoPath, videoImport);
            throw err;
        }
    });
}
function refreshVideoImportFromDB(videoImport, videoFile) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const video = yield videoImport.Video.reload();
        const videoWithFiles = Object.assign(video, { VideoFiles: [videoFile], VideoStreamingPlaylists: [] });
        return Object.assign(videoImport, { Video: videoWithFiles });
    });
}
function generateMiniature(videoImportWithFiles, videoFile, thumbnailType) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const needsMiniature = thumbnailType === 1
            ? !videoImportWithFiles.Video.getMiniature()
            : !videoImportWithFiles.Video.getPreview();
        if (!needsMiniature) {
            return {
                miniatureModel: null,
                miniatureJSONSave: null
            };
        }
        const miniatureModel = yield (0, thumbnail_2.generateVideoMiniature)({
            video: videoImportWithFiles.Video,
            videoFile,
            type: thumbnailType
        });
        const miniatureJSONSave = miniatureModel.toJSON();
        return {
            miniatureModel,
            miniatureJSONSave
        };
    });
}
function afterImportSuccess(options) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const { video, videoFile, videoImport, user } = options;
        notifier_1.Notifier.Instance.notifyOnFinishedVideoImport({ videoImport: Object.assign(videoImport, { Video: video }), success: true });
        if (video.isBlacklisted()) {
            const videoBlacklist = Object.assign(video.VideoBlacklist, { Video: video });
            notifier_1.Notifier.Instance.notifyOnVideoAutoBlacklist(videoBlacklist);
        }
        else {
            notifier_1.Notifier.Instance.notifyOnNewVideoIfNeeded(video);
        }
        if (video.state === 6) {
            yield job_queue_1.JobQueue.Instance.createJob(yield (0, video_1.buildMoveToObjectStorageJob)({ video, previousVideoState: 3 }));
            return;
        }
        if (video.state === 2) {
            yield job_queue_1.JobQueue.Instance.createJob(yield (0, video_1.buildOptimizeOrMergeAudioJob)({ video, videoFile, user }));
        }
    });
}
function onImportError(err, tempVideoPath, videoImport) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        try {
            if (tempVideoPath)
                yield (0, fs_extra_1.remove)(tempVideoPath);
        }
        catch (errUnlink) {
            logger_1.logger.warn('Cannot cleanup files after a video import error.', { err: errUnlink });
        }
        videoImport.error = err.message;
        if (videoImport.state !== 4) {
            videoImport.state = 3;
        }
        yield videoImport.save();
        notifier_1.Notifier.Instance.notifyOnFinishedVideoImport({ videoImport, success: false });
    });
}
//# sourceMappingURL=video-import.js.map