aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/utils/exporter.ts
blob: 40d0f2575c8ebab25444b1c14ca103b414ab5f0f (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
105
106
107
108
109
110
import { ActionType, Category, SegmentUUID, SponsorSourceType, SponsorTime } from "../types";
import { shortCategoryName } from "./categoryUtils";
import * as CompileConfig from "../../config.json";
import { getFormattedTime, getFormattedTimeToSeconds } from "../../maze-utils/src/formating";
import { generateUserID } from "../../maze-utils/src/setup";

const inTest = typeof chrome === "undefined";

const chapterNames = CompileConfig.categoryList.filter((code) => code !== "chapter")
    .map((code) => ({
        code,
        names: !inTest ? [chrome.i18n.getMessage("category_" + code), shortCategoryName(code)] : [code]
    }));

export function exportTimes(segments: SponsorTime[]): string {
    let result = "";
    for (const segment of segments) {
        if (![ActionType.Full, ActionType.Mute].includes(segment.actionType)
                && segment.source !== SponsorSourceType.YouTube) {
            result += exportTime(segment) + "\n";
        }
    }

    return result.replace(/\n$/, "");
}

function exportTime(segment: SponsorTime): string {
    const name = segment.description || shortCategoryName(segment.category);

    return `${getFormattedTime(segment.segment[0], true)}${
        segment.segment[1] && segment.segment[0] !== segment.segment[1] 
            ? ` - ${getFormattedTime(segment.segment[1], true)}` : ""} ${name}`;
}

export function importTimes(data: string, videoDuration: number): SponsorTime[] {
    const lines = data.split("\n");
    const result: SponsorTime[] = [];
    for (const line of lines) {
        const match = line.match(/(?:((?:\d+:)?\d+:\d+)+(?:\.\d+)?)|(?:\d+(?=s| second))/g);
        if (match) {
            const startTime = getFormattedTimeToSeconds(match[0]);
            if (startTime !== null) {
                // Remove "seconds", "at", special characters, and ")" if there was a "("
                const specialCharMatchers = [{
                    matcher: /^(?:\s+seconds?)?[-:()\s]*|(?:\s+at)?[-:(\s]+$/g
                }, {
                    matcher: /[-:()\s]*$/g,
                    condition: (value) => !!value.match(/^\s*\(/)
                }];
                const titleLeft = removeIf(line.split(match[0])[0], specialCharMatchers);
                let titleRight = null;
                const split2 = line.split(match[1] || match[0]);
                titleRight = removeIf(split2[split2.length - 1], specialCharMatchers)

                const title = titleLeft?.length > titleRight?.length ? titleLeft : titleRight;
                if (title) {
                    const determinedCategory = chapterNames.find(c => c.names.includes(title))?.code as Category;

                    const segment: SponsorTime = {
                        segment: [startTime, getFormattedTimeToSeconds(match[1])],
                        category: determinedCategory ?? ("chapter" as Category),
                        actionType: determinedCategory ? ActionType.Skip : ActionType.Chapter,
                        description: title,
                        source: SponsorSourceType.Local,
                        UUID: generateUserID() as SegmentUUID
                    };

                    if (result.length > 0 && result[result.length - 1].segment[1] === null) {
                        result[result.length - 1].segment[1] = segment.segment[0];
                    }

                    result.push(segment);
                }
            }
        }
    }

    if (result.length > 0 && result[result.length - 1].segment[1] === null) {
        result[result.length - 1].segment[1] = videoDuration;
    }

    return result;
}

function removeIf(value: string, matchers: Array<{ matcher: RegExp; condition?: (value: string) => boolean }>): string {
    let result = value;
    for (const matcher of matchers) {
        if (!matcher.condition || matcher.condition(value)) {
            result = result.replace(matcher.matcher, "");
        }
    }

    return result;
}

export function exportTimesAsHashParam(segments: SponsorTime[]): string {
    const hashparamSegments = segments.map(segment => ({
        actionType: segment.actionType,
        category: segment.category,
        segment: segment.segment,
        ...(segment.description ? {description: segment.description} : {})  // don't include the description param if empty
    }));

    return `#segments=${JSON.stringify(hashparamSegments)}`;
}


export function normalizeChapterName(description: string): string {
    return description.toLowerCase().replace(/[.:'’`‛‘"‟”-]/ug, "").replace(/\s+/g, " ");
}