"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.videoChannelRouter = void 0;
const tslib_1 = require("tslib");
const express_1 = tslib_1.__importDefault(require("express"));
const query_1 = require("../../helpers/query");
const actor_image_1 = require("../../lib/actor-image");
const hooks_1 = require("../../lib/plugins/hooks");
const actor_follow_1 = require("../../models/actor/actor-follow");
const application_1 = require("../../models/application/application");
const video_format_utils_1 = require("../../models/video/formatter/video-format-utils");
const audit_logger_1 = require("../../helpers/audit-logger");
const database_utils_1 = require("../../helpers/database-utils");
const express_utils_1 = require("../../helpers/express-utils");
const logger_1 = require("../../helpers/logger");
const utils_1 = require("../../helpers/utils");
const constants_1 = require("../../initializers/constants");
const database_1 = require("../../initializers/database");
const send_1 = require("../../lib/activitypub/send");
const job_queue_1 = require("../../lib/job-queue");
const local_actor_1 = require("../../lib/local-actor");
const video_channel_1 = require("../../lib/video-channel");
const middlewares_1 = require("../../middlewares");
const validators_1 = require("../../middlewares/validators");
const actor_image_2 = require("../../middlewares/validators/actor-image");
const video_playlists_1 = require("../../middlewares/validators/videos/video-playlists");
const account_1 = require("../../models/account/account");
const video_1 = require("../../models/video/video");
const video_channel_2 = require("../../models/video/video-channel");
const video_playlist_1 = require("../../models/video/video-playlist");
const auditLogger = (0, audit_logger_1.auditLoggerFactory)('channels');
const reqAvatarFile = (0, express_utils_1.createReqFiles)(['avatarfile'], constants_1.MIMETYPES.IMAGE.MIMETYPE_EXT);
const reqBannerFile = (0, express_utils_1.createReqFiles)(['bannerfile'], constants_1.MIMETYPES.IMAGE.MIMETYPE_EXT);
const videoChannelRouter = express_1.default.Router();
exports.videoChannelRouter = videoChannelRouter;
videoChannelRouter.get('/', middlewares_1.paginationValidator, middlewares_1.videoChannelsSortValidator, middlewares_1.setDefaultSort, middlewares_1.setDefaultPagination, validators_1.videoChannelsListValidator, (0, middlewares_1.asyncMiddleware)(listVideoChannels));
videoChannelRouter.post('/', middlewares_1.authenticate, (0, middlewares_1.asyncMiddleware)(middlewares_1.videoChannelsAddValidator), (0, middlewares_1.asyncRetryTransactionMiddleware)(addVideoChannel));
videoChannelRouter.post('/:nameWithHost/avatar/pick', middlewares_1.authenticate, reqAvatarFile, (0, middlewares_1.asyncMiddleware)(validators_1.videoChannelsNameWithHostValidator), validators_1.ensureIsLocalChannel, middlewares_1.ensureCanManageChannelOrAccount, actor_image_2.updateAvatarValidator, (0, middlewares_1.asyncMiddleware)(updateVideoChannelAvatar));
videoChannelRouter.post('/:nameWithHost/banner/pick', middlewares_1.authenticate, reqBannerFile, (0, middlewares_1.asyncMiddleware)(validators_1.videoChannelsNameWithHostValidator), validators_1.ensureIsLocalChannel, middlewares_1.ensureCanManageChannelOrAccount, actor_image_2.updateBannerValidator, (0, middlewares_1.asyncMiddleware)(updateVideoChannelBanner));
videoChannelRouter.delete('/:nameWithHost/avatar', middlewares_1.authenticate, (0, middlewares_1.asyncMiddleware)(validators_1.videoChannelsNameWithHostValidator), validators_1.ensureIsLocalChannel, middlewares_1.ensureCanManageChannelOrAccount, (0, middlewares_1.asyncMiddleware)(deleteVideoChannelAvatar));
videoChannelRouter.delete('/:nameWithHost/banner', middlewares_1.authenticate, (0, middlewares_1.asyncMiddleware)(validators_1.videoChannelsNameWithHostValidator), validators_1.ensureIsLocalChannel, middlewares_1.ensureCanManageChannelOrAccount, (0, middlewares_1.asyncMiddleware)(deleteVideoChannelBanner));
videoChannelRouter.put('/:nameWithHost', middlewares_1.authenticate, (0, middlewares_1.asyncMiddleware)(validators_1.videoChannelsNameWithHostValidator), validators_1.ensureIsLocalChannel, middlewares_1.ensureCanManageChannelOrAccount, middlewares_1.videoChannelsUpdateValidator, (0, middlewares_1.asyncRetryTransactionMiddleware)(updateVideoChannel));
videoChannelRouter.delete('/:nameWithHost', middlewares_1.authenticate, (0, middlewares_1.asyncMiddleware)(validators_1.videoChannelsNameWithHostValidator), validators_1.ensureIsLocalChannel, middlewares_1.ensureCanManageChannelOrAccount, (0, middlewares_1.asyncMiddleware)(middlewares_1.videoChannelsRemoveValidator), (0, middlewares_1.asyncRetryTransactionMiddleware)(removeVideoChannel));
videoChannelRouter.get('/:nameWithHost', (0, middlewares_1.asyncMiddleware)(validators_1.videoChannelsNameWithHostValidator), (0, middlewares_1.asyncMiddleware)(getVideoChannel));
videoChannelRouter.get('/:nameWithHost/video-playlists', (0, middlewares_1.asyncMiddleware)(validators_1.videoChannelsNameWithHostValidator), middlewares_1.paginationValidator, middlewares_1.videoPlaylistsSortValidator, middlewares_1.setDefaultSort, middlewares_1.setDefaultPagination, video_playlists_1.commonVideoPlaylistFiltersValidator, (0, middlewares_1.asyncMiddleware)(listVideoChannelPlaylists));
videoChannelRouter.get('/:nameWithHost/videos', (0, middlewares_1.asyncMiddleware)(validators_1.videoChannelsNameWithHostValidator), middlewares_1.paginationValidator, validators_1.videosSortValidator, middlewares_1.setDefaultVideosSort, middlewares_1.setDefaultPagination, middlewares_1.optionalAuthenticate, middlewares_1.commonVideosFiltersValidator, (0, middlewares_1.asyncMiddleware)(listVideoChannelVideos));
videoChannelRouter.get('/:nameWithHost/followers', middlewares_1.authenticate, (0, middlewares_1.asyncMiddleware)(validators_1.videoChannelsNameWithHostValidator), middlewares_1.ensureCanManageChannelOrAccount, middlewares_1.paginationValidator, validators_1.videoChannelsFollowersSortValidator, middlewares_1.setDefaultSort, middlewares_1.setDefaultPagination, (0, middlewares_1.asyncMiddleware)(listVideoChannelFollowers));
videoChannelRouter.post('/:nameWithHost/import-videos', middlewares_1.authenticate, (0, middlewares_1.asyncMiddleware)(validators_1.videoChannelsNameWithHostValidator), (0, middlewares_1.asyncMiddleware)(validators_1.videoChannelImportVideosValidator), validators_1.ensureIsLocalChannel, middlewares_1.ensureCanManageChannelOrAccount, (0, middlewares_1.asyncMiddleware)(validators_1.ensureChannelOwnerCanUpload), (0, middlewares_1.asyncMiddleware)(importVideosInChannel));
function listVideoChannels(req, res) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const serverActor = yield (0, application_1.getServerActor)();
        const apiOptions = yield hooks_1.Hooks.wrapObject({
            actorId: serverActor.id,
            start: req.query.start,
            count: req.query.count,
            sort: req.query.sort
        }, 'filter:api.video-channels.list.params');
        const resultList = yield hooks_1.Hooks.wrapPromiseFun(video_channel_2.VideoChannelModel.listForApi, apiOptions, 'filter:api.video-channels.list.result');
        return res.json((0, utils_1.getFormattedObjects)(resultList.data, resultList.total));
    });
}
function updateVideoChannelBanner(req, res) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const bannerPhysicalFile = req.files['bannerfile'][0];
        const videoChannel = res.locals.videoChannel;
        const oldVideoChannelAuditKeys = new audit_logger_1.VideoChannelAuditView(videoChannel.toFormattedJSON());
        const banners = yield (0, local_actor_1.updateLocalActorImageFiles)(videoChannel, bannerPhysicalFile, 2);
        auditLogger.update((0, audit_logger_1.getAuditIdFromRes)(res), new audit_logger_1.VideoChannelAuditView(videoChannel.toFormattedJSON()), oldVideoChannelAuditKeys);
        return res.json({
            banner: (0, actor_image_1.getBiggestActorImage)(banners).toFormattedJSON(),
            banners: banners.map(b => b.toFormattedJSON())
        });
    });
}
function updateVideoChannelAvatar(req, res) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const avatarPhysicalFile = req.files['avatarfile'][0];
        const videoChannel = res.locals.videoChannel;
        const oldVideoChannelAuditKeys = new audit_logger_1.VideoChannelAuditView(videoChannel.toFormattedJSON());
        const avatars = yield (0, local_actor_1.updateLocalActorImageFiles)(videoChannel, avatarPhysicalFile, 1);
        auditLogger.update((0, audit_logger_1.getAuditIdFromRes)(res), new audit_logger_1.VideoChannelAuditView(videoChannel.toFormattedJSON()), oldVideoChannelAuditKeys);
        return res.json({
            avatar: (0, actor_image_1.getBiggestActorImage)(avatars).toFormattedJSON(),
            avatars: avatars.map(a => a.toFormattedJSON())
        });
    });
}
function deleteVideoChannelAvatar(req, res) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const videoChannel = res.locals.videoChannel;
        yield (0, local_actor_1.deleteLocalActorImageFile)(videoChannel, 1);
        return res.status(204).end();
    });
}
function deleteVideoChannelBanner(req, res) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const videoChannel = res.locals.videoChannel;
        yield (0, local_actor_1.deleteLocalActorImageFile)(videoChannel, 2);
        return res.status(204).end();
    });
}
function addVideoChannel(req, res) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const videoChannelInfo = req.body;
        const videoChannelCreated = yield database_1.sequelizeTypescript.transaction((t) => tslib_1.__awaiter(this, void 0, void 0, function* () {
            const account = yield account_1.AccountModel.load(res.locals.oauth.token.User.Account.id, t);
            return (0, video_channel_1.createLocalVideoChannel)(videoChannelInfo, account, t);
        }));
        const payload = { actorId: videoChannelCreated.actorId };
        yield job_queue_1.JobQueue.Instance.createJob({ type: 'actor-keys', payload });
        auditLogger.create((0, audit_logger_1.getAuditIdFromRes)(res), new audit_logger_1.VideoChannelAuditView(videoChannelCreated.toFormattedJSON()));
        logger_1.logger.info('Video channel %s created.', videoChannelCreated.Actor.url);
        hooks_1.Hooks.runAction('action:api.video-channel.created', { videoChannel: videoChannelCreated, req, res });
        return res.json({
            videoChannel: {
                id: videoChannelCreated.id
            }
        });
    });
}
function updateVideoChannel(req, res) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const videoChannelInstance = res.locals.videoChannel;
        const videoChannelFieldsSave = videoChannelInstance.toJSON();
        const oldVideoChannelAuditKeys = new audit_logger_1.VideoChannelAuditView(videoChannelInstance.toFormattedJSON());
        const videoChannelInfoToUpdate = req.body;
        let doBulkVideoUpdate = false;
        try {
            yield database_1.sequelizeTypescript.transaction((t) => tslib_1.__awaiter(this, void 0, void 0, function* () {
                if (videoChannelInfoToUpdate.displayName !== undefined)
                    videoChannelInstance.name = videoChannelInfoToUpdate.displayName;
                if (videoChannelInfoToUpdate.description !== undefined)
                    videoChannelInstance.description = videoChannelInfoToUpdate.description;
                if (videoChannelInfoToUpdate.support !== undefined) {
                    const oldSupportField = videoChannelInstance.support;
                    videoChannelInstance.support = videoChannelInfoToUpdate.support;
                    if (videoChannelInfoToUpdate.bulkVideosSupportUpdate === true && oldSupportField !== videoChannelInfoToUpdate.support) {
                        doBulkVideoUpdate = true;
                        yield video_1.VideoModel.bulkUpdateSupportField(videoChannelInstance, t);
                    }
                }
                const videoChannelInstanceUpdated = yield videoChannelInstance.save({ transaction: t });
                yield (0, send_1.sendUpdateActor)(videoChannelInstanceUpdated, t);
                auditLogger.update((0, audit_logger_1.getAuditIdFromRes)(res), new audit_logger_1.VideoChannelAuditView(videoChannelInstanceUpdated.toFormattedJSON()), oldVideoChannelAuditKeys);
                hooks_1.Hooks.runAction('action:api.video-channel.updated', { videoChannel: videoChannelInstanceUpdated, req, res });
                logger_1.logger.info('Video channel %s updated.', videoChannelInstance.Actor.url);
            }));
        }
        catch (err) {
            logger_1.logger.debug('Cannot update the video channel.', { err });
            (0, database_utils_1.resetSequelizeInstance)(videoChannelInstance, videoChannelFieldsSave);
            throw err;
        }
        res.type('json').status(204).end();
        if (doBulkVideoUpdate) {
            yield (0, video_channel_1.federateAllVideosOfChannel)(videoChannelInstance);
        }
    });
}
function removeVideoChannel(req, res) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const videoChannelInstance = res.locals.videoChannel;
        yield database_1.sequelizeTypescript.transaction((t) => tslib_1.__awaiter(this, void 0, void 0, function* () {
            yield video_playlist_1.VideoPlaylistModel.resetPlaylistsOfChannel(videoChannelInstance.id, t);
            yield videoChannelInstance.destroy({ transaction: t });
            hooks_1.Hooks.runAction('action:api.video-channel.deleted', { videoChannel: videoChannelInstance, req, res });
            auditLogger.delete((0, audit_logger_1.getAuditIdFromRes)(res), new audit_logger_1.VideoChannelAuditView(videoChannelInstance.toFormattedJSON()));
            logger_1.logger.info('Video channel %s deleted.', videoChannelInstance.Actor.url);
        }));
        return res.type('json').status(204).end();
    });
}
function getVideoChannel(req, res) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const id = res.locals.videoChannel.id;
        const videoChannel = yield hooks_1.Hooks.wrapObject(res.locals.videoChannel, 'filter:api.video-channel.get.result', { id });
        if (videoChannel.isOutdated()) {
            job_queue_1.JobQueue.Instance.createJobAsync({ type: 'activitypub-refresher', payload: { type: 'actor', url: videoChannel.Actor.url } });
        }
        return res.json(videoChannel.toFormattedJSON());
    });
}
function listVideoChannelPlaylists(req, res) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const serverActor = yield (0, application_1.getServerActor)();
        const resultList = yield video_playlist_1.VideoPlaylistModel.listForApi({
            followerActorId: serverActor.id,
            start: req.query.start,
            count: req.query.count,
            sort: req.query.sort,
            videoChannelId: res.locals.videoChannel.id,
            type: req.query.playlistType
        });
        return res.json((0, utils_1.getFormattedObjects)(resultList.data, resultList.total));
    });
}
function listVideoChannelVideos(req, res) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const serverActor = yield (0, application_1.getServerActor)();
        const videoChannelInstance = res.locals.videoChannel;
        const displayOnlyForFollower = (0, express_utils_1.isUserAbleToSearchRemoteURI)(res)
            ? null
            : {
                actorId: serverActor.id,
                orLocalVideos: true
            };
        const countVideos = (0, express_utils_1.getCountVideos)(req);
        const query = (0, query_1.pickCommonVideoQuery)(req.query);
        const apiOptions = yield hooks_1.Hooks.wrapObject(Object.assign(Object.assign({}, query), { displayOnlyForFollower, nsfw: (0, express_utils_1.buildNSFWFilter)(res, query.nsfw), videoChannelId: videoChannelInstance.id, user: res.locals.oauth ? res.locals.oauth.token.User : undefined, countVideos }), 'filter:api.video-channels.videos.list.params');
        const resultList = yield hooks_1.Hooks.wrapPromiseFun(video_1.VideoModel.listForApi, apiOptions, 'filter:api.video-channels.videos.list.result');
        return res.json((0, utils_1.getFormattedObjects)(resultList.data, resultList.total, (0, video_format_utils_1.guessAdditionalAttributesFromQuery)(query)));
    });
}
function listVideoChannelFollowers(req, res) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const channel = res.locals.videoChannel;
        const resultList = yield actor_follow_1.ActorFollowModel.listFollowersForApi({
            actorIds: [channel.actorId],
            start: req.query.start,
            count: req.query.count,
            sort: req.query.sort,
            search: req.query.search,
            state: 'accepted'
        });
        return res.json((0, utils_1.getFormattedObjects)(resultList.data, resultList.total));
    });
}
function importVideosInChannel(req, res) {
    var _a;
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const { externalChannelUrl } = req.body;
        yield job_queue_1.JobQueue.Instance.createJob({
            type: 'video-channel-import',
            payload: {
                externalChannelUrl,
                videoChannelId: res.locals.videoChannel.id,
                partOfChannelSyncId: (_a = res.locals.videoChannelSync) === null || _a === void 0 ? void 0 : _a.id
            }
        });
        logger_1.logger.info('Video import job for channel "%s" with url "%s" created.', res.locals.videoChannel.name, externalChannelUrl);
        return res.type('json').status(204).end();
    });
}
//# sourceMappingURL=video-channel.js.map