"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.VideoViews = void 0;
const tslib_1 = require("tslib");
const core_utils_1 = require("../helpers/core-utils");
const logger_1 = require("../helpers/logger");
const constants_1 = require("../initializers/constants");
const video_1 = require("../models/video/video");
const peertube_socket_1 = require("./peertube-socket");
const redis_1 = require("./redis");
const lTags = (0, logger_1.loggerTagsFactory)('views');
class VideoViews {
    constructor() {
        this.viewersPerVideo = new Map();
    }
    init() {
        setInterval(() => this.cleanViewers(), constants_1.VIEW_LIFETIME.VIEWER);
    }
    processView(options) {
        return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
            const { video, ip, viewerExpires } = options;
            logger_1.logger.debug('Processing view for %s and ip %s.', video.url, ip, lTags());
            let success = yield this.addView(video, ip);
            if (video.isLive) {
                const successViewer = yield this.addViewer(video, ip, viewerExpires);
                success || (success = successViewer);
            }
            return success;
        });
    }
    getViewers(video) {
        const viewers = this.viewersPerVideo.get(video.id);
        if (!viewers)
            return 0;
        return viewers.length;
    }
    buildViewerExpireTime() {
        return new Date().getTime() + constants_1.VIEW_LIFETIME.VIEWER;
    }
    addView(video, ip) {
        return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
            const promises = [];
            if (ip !== null) {
                const viewExists = yield redis_1.Redis.Instance.doesVideoIPViewExist(ip, video.uuid);
                if (viewExists)
                    return false;
                promises.push(redis_1.Redis.Instance.setIPVideoView(ip, video.uuid));
            }
            if (video.isOwned()) {
                promises.push(redis_1.Redis.Instance.addLocalVideoView(video.id));
            }
            promises.push(redis_1.Redis.Instance.addVideoViewStats(video.id));
            yield Promise.all(promises);
            return true;
        });
    }
    addViewer(video, ip, viewerExpires) {
        return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
            if (ip !== null) {
                const viewExists = yield redis_1.Redis.Instance.doesVideoIPViewerExist(ip, video.uuid);
                if (viewExists)
                    return false;
                yield redis_1.Redis.Instance.setIPVideoViewer(ip, video.uuid);
            }
            let watchers = this.viewersPerVideo.get(video.id);
            if (!watchers) {
                watchers = [];
                this.viewersPerVideo.set(video.id, watchers);
            }
            const expiration = viewerExpires
                ? viewerExpires.getTime()
                : this.buildViewerExpireTime();
            watchers.push(expiration);
            yield this.notifyClients(video.id, watchers.length);
            return true;
        });
    }
    cleanViewers() {
        return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
            if (!(0, core_utils_1.isTestInstance)())
                logger_1.logger.info('Cleaning video viewers.', lTags());
            for (const videoId of this.viewersPerVideo.keys()) {
                const notBefore = new Date().getTime();
                const viewers = this.viewersPerVideo.get(videoId);
                const newViewers = viewers.filter(w => w > notBefore);
                if (newViewers.length === 0)
                    this.viewersPerVideo.delete(videoId);
                else
                    this.viewersPerVideo.set(videoId, newViewers);
                yield this.notifyClients(videoId, newViewers.length);
            }
        });
    }
    notifyClients(videoId, viewersLength) {
        return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () {
            const video = yield video_1.VideoModel.loadImmutableAttributes(videoId);
            if (!video)
                return;
            peertube_socket_1.PeerTubeSocket.Instance.sendVideoViewsUpdate(video, viewersLength);
            logger_1.logger.debug('Live video views update for %s is %d.', video.url, viewersLength, lTags());
        });
    }
    static get Instance() {
        return this.instance || (this.instance = new this());
    }
}
exports.VideoViews = VideoViews;
//# sourceMappingURL=video-views.js.map