From 3ed664132c51046d7323fd604210e9b657bef054 Mon Sep 17 00:00:00 2001 From: stratuma Date: Fri, 21 Jun 2024 18:00:49 +0200 Subject: [PATCH] fixed chapter generation --- src/api/routes/service/service.service.ts | 2 +- src/api/services/chapter.ts | 47 ++++++++++---------- src/api/types/crunchyroll.ts | 10 +++++ src/electron/background.ts | 53 +++++++++++++++++++++++ src/electron/preload.ts | 12 ++--- 5 files changed, 94 insertions(+), 30 deletions(-) diff --git a/src/api/routes/service/service.service.ts b/src/api/routes/service/service.service.ts index 2adf47e..009a65f 100644 --- a/src/api/routes/service/service.service.ts +++ b/src/api/routes/service/service.service.ts @@ -715,7 +715,7 @@ export async function downloadCrunchyrollPlaylist( return null } - const chapterPath = await createChapterFile(metadata, chapterFolder) + const chapterPath = await createChapterFile(metadata, chapterFolder, format) return chapterPath } diff --git a/src/api/services/chapter.ts b/src/api/services/chapter.ts index b684903..1897cad 100644 --- a/src/api/services/chapter.ts +++ b/src/api/services/chapter.ts @@ -1,49 +1,50 @@ import path from 'path' import fs from 'fs/promises' -import { VideoMetadata } from '../types/crunchyroll' +import { VideoMetadata, VideoMetadataSingle } from '../types/crunchyroll' import { server } from '../api' function formatTimeFFMPEG(seconds: number) { return seconds * 1000 } -export async function createChapterFile(rawchapters: VideoMetadata, dir: string) { +export async function createChapterFile(rawchapters: VideoMetadata, dir: string, format: string) { const filepath = path.join(dir, 'chapters.txt') var chapters: string[] = [] - chapters.push(';FFMETADATA1') + const addChapter = (chapter: VideoMetadataSingle) => { + if (format === 'mkv') { + chapters.push('[CHAPTER]') + chapters.push('TIMEBASE=1/1000') + chapters.push(`START=${formatTimeFFMPEG(chapter.start)}`) + chapters.push(`title=${chapter.title}`) + chapters.push('[CHAPTER]') + chapters.push('TIMEBASE=1/1000') + chapters.push(`START=${formatTimeFFMPEG(chapter.end)}`) + chapters.push(`title=Chapter`) + } else { + chapters.push('[CHAPTER]') + chapters.push('TIMEBASE=1/1000') + chapters.push(`START=${formatTimeFFMPEG(chapter.start)}`) + chapters.push(`END=${formatTimeFFMPEG(chapter.end)}`) + chapters.push(`title=${chapter.title}`) + } + } if (rawchapters.intro && rawchapters.intro.type && rawchapters.intro.start && rawchapters.intro.end) { - chapters.push('[CHAPTER]') - chapters.push('TIMEBASE=1/1000') - chapters.push(`START=${formatTimeFFMPEG(rawchapters.intro.start)}`) - chapters.push(`END=${formatTimeFFMPEG(rawchapters.intro.end) - 1}`) - chapters.push('title=Intro') + addChapter(rawchapters.intro) } if (rawchapters.credits && rawchapters.credits.type && rawchapters.credits.start && rawchapters.credits.end) { - chapters.push('[CHAPTER]') - chapters.push('TIMEBASE=1/1000') - chapters.push(`START=${formatTimeFFMPEG(rawchapters.credits.start)}`) - chapters.push(`END=${formatTimeFFMPEG(rawchapters.credits.end) - 1}`) - chapters.push('title=Credits') + addChapter(rawchapters.credits) } if (rawchapters.preview && rawchapters.preview.type && rawchapters.preview.start && rawchapters.preview.end) { - chapters.push('[CHAPTER]') - chapters.push('TIMEBASE=1/1000') - chapters.push(`START=${formatTimeFFMPEG(rawchapters.preview.start)}`) - chapters.push(`END=${formatTimeFFMPEG(rawchapters.preview.end) - 1}`) - chapters.push('title=Preview') + addChapter(rawchapters.preview) } if (rawchapters.recap && rawchapters.recap.type && rawchapters.recap.start && rawchapters.recap.end) { - chapters.push('[CHAPTER]') - chapters.push('TIMEBASE=1/1000') - chapters.push(`START=${formatTimeFFMPEG(rawchapters.recap.start)}`) - chapters.push(`END=${formatTimeFFMPEG(rawchapters.recap.end) - 1}`) - chapters.push('title=Recap') + addChapter(rawchapters.recap) } try { diff --git a/src/api/types/crunchyroll.ts b/src/api/types/crunchyroll.ts index 95807a3..2a2135c 100644 --- a/src/api/types/crunchyroll.ts +++ b/src/api/types/crunchyroll.ts @@ -230,3 +230,13 @@ export interface VideoMetadata { lastUpdated: Date mediaId: string } + +export interface VideoMetadataSingle { + approverId: string + distributionNumber: string + end: number + seriesId: string + start: number + title: string + type: string +} diff --git a/src/electron/background.ts b/src/electron/background.ts index 2736af2..404457e 100644 --- a/src/electron/background.ts +++ b/src/electron/background.ts @@ -352,6 +352,59 @@ ipcMain.handle('dialog:getDefaultCrunchyrollLanguageTemplate', async (events) => return seTP }) +ipcMain.handle('dialog:setDefaultVideoQualityTemplate', async (events, quality: number) => { + await settings.set('CrunchyrollDefaultVideoQuality', quality) + + return quality +}) + +ipcMain.handle('dialog:getDefaultVideoQualityTemplate', async (events) => { + const seTP = await settings.get('CrunchyrollDefaultVideoQuality') + + if (!seTP) { + await settings.set('CrunchyrollDefaultVideoQuality', 1080) + + return 1080 + } + + return seTP +}) + +ipcMain.handle('dialog:setDefaultAudioQualityTemplate', async (events, quality: number) => { + await settings.set('CrunchyrollDefaultAudioQuality', quality) + + return quality +}) + +ipcMain.handle('dialog:getDefaultAudioQualityTemplate', async (events) => { + const seTP = await settings.get('CrunchyrollDefaultAudioQuality') + + if (!seTP) { + await settings.set('CrunchyrollDefaultAudioQuality', 1) + + return 1 + } + + return seTP +}) + +ipcMain.handle('dialog:setDefaultOutputFormatTemplate', async (events, format: string) => { + await settings.set('DefaultOutputFormat', format) + + return format +}) + +ipcMain.handle('dialog:getDefaultOutputFormatTemplate', async (events) => { + const seTP = await settings.get('DefaultOutputFormat') + + if (!seTP) { + await settings.set('DefaultOutputFormat', 'mkv') + + return 'mkv' + } + + return seTP +}) const openWindows = new Map() diff --git a/src/electron/preload.ts b/src/electron/preload.ts index 2e15d05..fc969f1 100644 --- a/src/electron/preload.ts +++ b/src/electron/preload.ts @@ -25,12 +25,12 @@ contextBridge.exposeInMainWorld('myAPI', { getSeasonTemplate: () => ipcRenderer.invoke('dialog:getSeasonTemplate'), setSeasonEnabled: (active: boolean) => ipcRenderer.invoke('dialog:setSeasonEnabledTemplate', active), getSeasonEnabled: () => ipcRenderer.invoke('dialog:getSeasonEnabledTemplate'), - // setDefaultVideoQuality: (quality: number) => ipcRenderer.invoke('dialog:setDefaultVideoQualityTemplate', quality), - // getDefaultVideoQuality: () => ipcRenderer.invoke('dialog:getDefaultVideoQualityTemplate'), - // setDefaultAudioQuality: (quality: number) => ipcRenderer.invoke('dialog:setDefaultAudioQualityTemplate', quality), - // getDefaultAudioQuality: () => ipcRenderer.invoke('dialog:getDefaultAudioQualityTemplate'), - // setDefaultOutputFormat: (format: string) => ipcRenderer.invoke('dialog:setDefaultOutputFormatTemplate', format), - // getDefaultOutputFormat: () => ipcRenderer.invoke('dialog:getDefaultOutputFormatTemplate'), + setDefaultVideoQuality: (quality: number) => ipcRenderer.invoke('dialog:setDefaultVideoQualityTemplate', quality), + getDefaultVideoQuality: () => ipcRenderer.invoke('dialog:getDefaultVideoQualityTemplate'), + setDefaultAudioQuality: (quality: number) => ipcRenderer.invoke('dialog:setDefaultAudioQualityTemplate', quality), + getDefaultAudioQuality: () => ipcRenderer.invoke('dialog:getDefaultAudioQualityTemplate'), + setDefaultOutputFormat: (format: string) => ipcRenderer.invoke('dialog:setDefaultOutputFormatTemplate', format), + getDefaultOutputFormat: () => ipcRenderer.invoke('dialog:getDefaultOutputFormatTemplate'), setDefaultCrunchyrollLanguage: (lang: string) => ipcRenderer.invoke('dialog:setDefaultCrunchyrollLanguageTemplate', lang), getDefaultCrunchyrollLanguage: () => ipcRenderer.invoke('dialog:getDefaultCrunchyrollLanguageTemplate') })