aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/utils/segmentData.ts
blob: 1c2e631a6213d0a1a13210bd139392d5171819ab (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
import { DataCache } from "../../maze-utils/src/cache";
import { getHash, HashedValue } from "../../maze-utils/src/hash";
import Config from "../config";
import * as CompileConfig from "../../config.json";
import { ActionType, ActionTypes, SponsorSourceType, SponsorTime, VideoID } from "../types";
import { getHashParams } from "./pageUtils";
import { asyncRequestToServer } from "./requests";

const segmentDataCache = new DataCache<VideoID, SegmentResponse>(() => {
    return {
        segments: null,
        status: 200
    };
}, 5);

const pendingList: Record<VideoID, Promise<SegmentResponse>> = {};

export interface SegmentResponse {
    segments: SponsorTime[] | null;
    status: number;
}

export async function getSegmentsForVideo(videoID: VideoID, ignoreCache: boolean): Promise<SegmentResponse> {
    if (!ignoreCache) {
        const cachedData = segmentDataCache.getFromCache(videoID);
        if (cachedData) {
            segmentDataCache.cacheUsed(videoID);
            return cachedData;
        }
    }

    if (pendingList[videoID]) {
        return await pendingList[videoID];
    }

    const pendingData = fetchSegmentsForVideo(videoID);
    pendingList[videoID] = pendingData;

    const result = await pendingData;
    delete pendingList[videoID];

    return result;
}

async function fetchSegmentsForVideo(videoID: VideoID): Promise<SegmentResponse> {
    const categories: string[] = Config.config.categorySelections.map((category) => category.name);

    const extraRequestData: Record<string, unknown> = {};
    const hashParams = getHashParams();
    if (hashParams.requiredSegment) extraRequestData.requiredSegment = hashParams.requiredSegment;
    
    const hashPrefix = (await getHash(videoID, 1)).slice(0, 4) as VideoID & HashedValue;
    const response = await asyncRequestToServer('GET', "/api/skipSegments/" + hashPrefix, {
        categories: CompileConfig.categoryList,
        actionTypes: ActionTypes,
        ...extraRequestData
    }, {
        "X-CLIENT-NAME": `${chrome.runtime.id}/v${chrome.runtime.getManifest().version}`
    });

    if (response.ok) {
        const enabledActionTypes = getEnabledActionTypes();

        const receivedSegments: SponsorTime[] = JSON.parse(response.responseText)
                    ?.filter((video) => video.videoID === videoID)
                    ?.map((video) => video.segments)?.[0]
                    ?.filter((segment) => enabledActionTypes.includes(segment.actionType) && categories.includes(segment.category))
                    ?.map((segment) => ({
                        ...segment,
                        source: SponsorSourceType.Server
                    }))
                    ?.sort((a, b) => a.segment[0] - b.segment[0]);

        if (receivedSegments && receivedSegments.length) {
            const result = {
                segments: receivedSegments,
                status: response.status
            };

            segmentDataCache.setupCache(videoID).segments = result.segments;
            return result;
        } else {
            // Setup with null data
            segmentDataCache.setupCache(videoID);
        }
    }

    return {
        segments: null,
        status: response.status
    };
}

function getEnabledActionTypes(forceFullVideo = false): ActionType[] {
    const actionTypes = [ActionType.Skip, ActionType.Poi, ActionType.Chapter];
    if (Config.config.muteSegments) {
        actionTypes.push(ActionType.Mute);
    }
    if (Config.config.fullVideoSegments || forceFullVideo) {
        actionTypes.push(ActionType.Full);
    }

    return actionTypes;
}