From 638e3639e5e67211edbbfc3158caa2e5bcd8be77 Mon Sep 17 00:00:00 2001 From: "CATERPILLAR\\Braxen" Date: Tue, 16 Apr 2024 15:15:40 +0200 Subject: [PATCH] replace template on diretory too for exporters fixes #494 --- server/src/Controllers/Exporter.ts | 10 +++++ server/src/Exporters/FTP.ts | 15 +++---- server/src/Exporters/File.ts | 69 +++++++++++++++++++++++++++++- server/src/Exporters/RClone.ts | 10 +++-- server/src/Exporters/SFTP.ts | 14 +++--- 5 files changed, 97 insertions(+), 21 deletions(-) diff --git a/server/src/Controllers/Exporter.ts b/server/src/Controllers/Exporter.ts index 3f8f1f8f..137ee6e6 100644 --- a/server/src/Controllers/Exporter.ts +++ b/server/src/Controllers/Exporter.ts @@ -51,6 +51,11 @@ export function GetExporter( if (!options.host) throw new Error("No host set"); if (!options.username) throw new Error("No username set"); exporter = new SFTPExporter(); + if (!(exporter instanceof SFTPExporter)) { + throw new Error( + "Exporter is not an SFTPExporter (why does typescript need this?)" + ); + } exporter.setDirectory(output_directory); exporter.setHost(options.host); exporter.setUsername(options.username); @@ -86,6 +91,11 @@ export function GetExporter( if (!output_directory) throw new Error("No directory set"); if (!options.remote) throw new Error("No remote set"); exporter = new RCloneExporter(); + if (!(exporter instanceof RCloneExporter)) { + throw new Error( + "Exporter is not an RCloneExporter (why does typescript need this?)" + ); + } exporter.setDirectory(output_directory); exporter.setRemote(options.remote); } diff --git a/server/src/Exporters/FTP.ts b/server/src/Exporters/FTP.ts index 63937527..4e56d058 100644 --- a/server/src/Exporters/FTP.ts +++ b/server/src/Exporters/FTP.ts @@ -1,16 +1,16 @@ import { execSimple, startJob } from "@/Helpers/Execute"; import path from "node:path"; import sanitize from "sanitize-filename"; -import { BaseExporter } from "./Base"; +import { FileExporter } from "./File"; /** * Basic FTP exporter to transfer the VOD to a remote FTP server. * Uses curl to transfer the file. */ -export class FTPExporter extends BaseExporter { +export class FTPExporter extends FileExporter { public type = "FTP"; - public directory = ""; + // public directory = ""; public host = ""; public username = ""; public password = ""; @@ -19,10 +19,6 @@ export class FTPExporter extends BaseExporter { // public supportsDirectories = true; - public setDirectory(directory: string): void { - this.directory = directory; - } - public setHost(host: string): void { this.host = host; } @@ -51,7 +47,10 @@ export class FTPExporter extends BaseExporter { const finalFilename = sanitize(this.getFormattedTitle()) + "." + this.extension; - const filesystemPath = path.join(this.directory, finalFilename); + const filesystemPath = path.join( + this.getFormattedDirectory(), + finalFilename + ); const linuxPath = filesystemPath.replace(/\\/g, "/"); const webPath = encodeURIComponent(linuxPath); diff --git a/server/src/Exporters/File.ts b/server/src/Exporters/File.ts index 870dbca8..997d3a50 100644 --- a/server/src/Exporters/File.ts +++ b/server/src/Exporters/File.ts @@ -1,6 +1,11 @@ +import { Config } from "@/Core/Config"; import { Job } from "@/Core/Job"; import { log, LOGLEVEL } from "@/Core/Log"; import { xClearInterval, xInterval } from "@/Helpers/Timeout"; +import { isTwitchVOD } from "@/Helpers/Types"; +import { formatString } from "@common/Format"; +import type { ExporterFilenameTemplate } from "@common/Replacements"; +import { format } from "date-fns"; import fs from "node:fs"; import path from "node:path"; import sanitize from "sanitize-filename"; @@ -28,7 +33,10 @@ export class FileExporter extends BaseExporter { const finalFilename = sanitize(this.getFormattedTitle()) + "." + this.extension; - this.final_path = path.join(this.directory, finalFilename); + this.final_path = path.join( + this.getFormattedDirectory(), + finalFilename + ); if (fs.existsSync(this.final_path)) { throw new Error(`File already exists: ${this.final_path}`); @@ -79,4 +87,63 @@ export class FileExporter extends BaseExporter { }); }); } + + public getFormattedDirectory() { + if (!this.vod) { + return this.directory; // no vod loaded, return directory instead + } + if (!this.vod.video_metadata) throw new Error("No video_metadata"); + if (!this.vod.started_at) throw new Error("No started_at"); + + let title = "Title"; + // TODO: why is this done here? + if (isTwitchVOD(this.vod) && this.vod.external_vod_title) + title = this.vod.external_vod_title; + if (this.vod.chapters[0]) title = this.vod.chapters[0].title; + + const replacements: ExporterFilenameTemplate = { + login: this.vod.getChannel().internalName, + internalName: this.vod.getChannel().internalName, + displayName: this.vod.getChannel().displayName, + title: title, + date: format(this.vod.started_at, Config.getInstance().dateFormat), + year: this.vod.started_at + ? format(this.vod.started_at, "yyyy") + : "", + month: this.vod.started_at ? format(this.vod.started_at, "MM") : "", + day: this.vod.started_at ? format(this.vod.started_at, "dd") : "", + comment: this.vod.comment || "", + + stream_number: this.vod.stream_number + ? this.vod.stream_number.toString() + : "", // deprecated + episode: this.vod.stream_number?.toString() || "", + absolute_episode: this.vod.stream_absolute_number?.toString() || "", + season: this.vod.stream_season?.toString() || "", + absolute_season: this.vod.stream_absolute_season?.toString() || "", + + resolution: "", + id: this.vod.capture_id, + }; + + for (const literal in replacements) { + if ( + replacements[literal] === undefined || + replacements[literal] === null || + replacements[literal] === "" + ) { + log( + LOGLEVEL.WARNING, + "BaseExporter.getFormattedTitle", + `No value for replacement literal '${literal}', using template '${this.template_filename}'` + ); + } + } + + if (this.vod.video_metadata.type == "video") { + replacements.resolution = `${this.vod.video_metadata.height}p`; + } + + return formatString(this.directory, replacements); + } } diff --git a/server/src/Exporters/RClone.ts b/server/src/Exporters/RClone.ts index 3ec8336d..38c583d8 100644 --- a/server/src/Exporters/RClone.ts +++ b/server/src/Exporters/RClone.ts @@ -4,7 +4,7 @@ import fs from "node:fs"; import path from "node:path"; import sanitize from "sanitize-filename"; import { z } from "zod"; -import { BaseExporter } from "./Base"; +import { FileExporter } from "./File"; // export const rCloneExporterConfigSchema = z.object({ // type: z.literal("RClone"), @@ -23,10 +23,9 @@ const rCloneLsJsonSchema = z.array( }) ); -export class RCloneExporter extends BaseExporter { +export class RCloneExporter extends FileExporter { public type = "RClone"; - public directory = ""; // public host = ""; public remote_file = ""; @@ -84,7 +83,10 @@ export class RCloneExporter extends BaseExporter { const finalFilename = sanitize(this.getFormattedTitle()) + "." + this.extension; - const filesystemPath = path.join(this.directory, finalFilename); + const filesystemPath = path.join( + this.getFormattedDirectory(), + finalFilename + ); const linuxPath = filesystemPath.replace(/\\/g, "/"); // const escaped_remote_path = linux_path.includes(" ") ? `'${linux_path}'` : linux_path; const remotePath = `${this.remote}:${linuxPath}`; diff --git a/server/src/Exporters/SFTP.ts b/server/src/Exporters/SFTP.ts index 5af997b7..6389709b 100644 --- a/server/src/Exporters/SFTP.ts +++ b/server/src/Exporters/SFTP.ts @@ -1,16 +1,15 @@ import { execSimple, startJob } from "@/Helpers/Execute"; import path from "node:path"; import sanitize from "sanitize-filename"; -import { BaseExporter } from "./Base"; +import { FileExporter } from "./File"; /** * Basic SFTP exporter to transfer the VOD to a remote SFTP server. * Uses scp to transfer the file. */ -export class SFTPExporter extends BaseExporter { +export class SFTPExporter extends FileExporter { public type = "SFTP"; - public directory = ""; public host = ""; public username = ""; @@ -18,10 +17,6 @@ export class SFTPExporter extends BaseExporter { public supportsDirectories = true; - public setDirectory(directory: string): void { - this.directory = directory; - } - public setHost(host: string): void { this.host = host; } @@ -41,7 +36,10 @@ export class SFTPExporter extends BaseExporter { const finalFilename = sanitize(this.getFormattedTitle()) + "." + this.extension; - const filesystemPath = path.join(this.directory, finalFilename); + const filesystemPath = path.join( + this.getFormattedDirectory(), + finalFilename + ); const linuxPath = filesystemPath.replace(/\\/g, "/"); let remotePath = `${this.host}:'${linuxPath}'`; if (this.username) {