/
Channel.ts
107 lines (90 loc) · 3.86 KB
/
Channel.ts
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
import fs from 'fs/promises';
import chalk from 'chalk';
import db from '@inrixia/db';
import Video from './Video.js';
import type { BlogPost } from 'floatplane/creator';
import type { ChannelOptions } from './types.js';
import type Subscription from './Subscription.js';
// e = episodeNo, d = downloaded, s = filesize in bytes, f = file
export type VideoDBEntry = { episodeNo: number; expectedSize?: number; filePath?: string; releaseDate: number };
export type ChannelDB = {
videos: { [key: string]: VideoDBEntry };
nextEpisodeNo: number;
};
export default class Channel {
public readonly title: ChannelOptions['title'];
public readonly identifiers: ChannelOptions['identifiers'];
public readonly skip: ChannelOptions['skip'];
public readonly daysToKeepVideos: ChannelOptions['daysToKeepVideos'];
public readonly ignoreBeforeTimestamp: number;
public readonly consoleColor: ChannelOptions['consoleColor'];
public subscription: Subscription;
private readonly _db: ChannelDB;
/**
* Returns a channel built from a subscription.
* @param {ChannelOptions} channel
*/
constructor(channel: ChannelOptions, subscription: Subscription) {
this.subscription = subscription;
this.title = channel.title;
this.identifiers = channel.identifiers;
this.skip = channel.skip;
this.consoleColor = channel.consoleColor;
if (channel.daysToKeepVideos === undefined) channel.daysToKeepVideos = -1;
this.daysToKeepVideos = channel.daysToKeepVideos;
this.ignoreBeforeTimestamp = Date.now() - this.daysToKeepVideos * 24 * 60 * 60 * 1000;
const databaseFilePath = `./db/channels/${subscription.creatorId}/${channel.title}.json`;
try {
this._db = db<ChannelDB>(databaseFilePath, { template: { videos: {}, nextEpisodeNo: 1 } });
} catch {
throw new Error(`Cannot load Channel database file ${databaseFilePath}! Please delete the file or fix it!`);
}
}
public deleteOldVideos = async () => {
if (this.daysToKeepVideos !== -1) {
process.stdout.write(
chalk`Checking for videos older than {cyanBright ${this.daysToKeepVideos}} days in channel {yellow ${this.title}} for {redBright deletion}...`
);
let deletedFiles = 0;
let deletedVideos = 0;
for (const video of Object.values(this._db.videos)) {
if (video.releaseDate === undefined || video.filePath === undefined) continue;
if (video.releaseDate < this.ignoreBeforeTimestamp) {
deletedVideos++;
const deletionResults = await Promise.allSettled([
fs.rm(`${video.filePath}.mp4`),
fs.rm(`${video.filePath}.partial`),
fs.rm(`${video.filePath}.nfo`),
fs.rm(`${video.filePath}.png`),
]);
for (const result of deletionResults) {
if (result.status === 'fulfilled') deletedFiles++;
}
}
}
if (deletedFiles === 0) console.log(' No files found for deletion.');
else console.log(chalk` Deleted {redBright ${deletedVideos}} videos, {redBright ${deletedFiles}} files.`);
}
};
public lookupVideoDB = (guid: string): VideoDBEntry => this._db.videos[guid];
public markVideoCompleted(guid: string, releaseDate: number): void {
// Redundant check but worth keeping
if (this.lookupVideoDB(guid) === undefined) throw new Error(`Cannot mark unknown video ${guid} as completed. Video does not exist in channel database.`);
this.subscription.updateLastSeenVideo({ guid, releaseDate });
}
public addVideo(video: BlogPost): Video | null {
const releaseDate = new Date(video.releaseDate).getTime();
if (this.daysToKeepVideos !== -1 && releaseDate < this.ignoreBeforeTimestamp) return null;
// Set db info, have to instigate the db first before setting filepath
if (this._db.videos[video.guid] === undefined) {
this._db.videos[video.guid] ??= {
episodeNo: this._db.nextEpisodeNo++,
releaseDate,
filePath: '',
};
}
const videoInstance = new Video(video, this);
this._db.videos[video.guid].filePath = videoInstance.filePath;
return videoInstance;
}
}