fixed chapter generation
This commit is contained in:
parent
d9fe95dd25
commit
3ed664132c
@ -715,7 +715,7 @@ export async function downloadCrunchyrollPlaylist(
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const chapterPath = await createChapterFile(metadata, chapterFolder)
|
const chapterPath = await createChapterFile(metadata, chapterFolder, format)
|
||||||
|
|
||||||
return chapterPath
|
return chapterPath
|
||||||
}
|
}
|
||||||
|
@ -1,49 +1,50 @@
|
|||||||
import path from 'path'
|
import path from 'path'
|
||||||
import fs from 'fs/promises'
|
import fs from 'fs/promises'
|
||||||
import { VideoMetadata } from '../types/crunchyroll'
|
import { VideoMetadata, VideoMetadataSingle } from '../types/crunchyroll'
|
||||||
import { server } from '../api'
|
import { server } from '../api'
|
||||||
|
|
||||||
function formatTimeFFMPEG(seconds: number) {
|
function formatTimeFFMPEG(seconds: number) {
|
||||||
return seconds * 1000
|
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')
|
const filepath = path.join(dir, 'chapters.txt')
|
||||||
|
|
||||||
var chapters: string[] = []
|
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) {
|
if (rawchapters.intro && rawchapters.intro.type && rawchapters.intro.start && rawchapters.intro.end) {
|
||||||
chapters.push('[CHAPTER]')
|
addChapter(rawchapters.intro)
|
||||||
chapters.push('TIMEBASE=1/1000')
|
|
||||||
chapters.push(`START=${formatTimeFFMPEG(rawchapters.intro.start)}`)
|
|
||||||
chapters.push(`END=${formatTimeFFMPEG(rawchapters.intro.end) - 1}`)
|
|
||||||
chapters.push('title=Intro')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rawchapters.credits && rawchapters.credits.type && rawchapters.credits.start && rawchapters.credits.end) {
|
if (rawchapters.credits && rawchapters.credits.type && rawchapters.credits.start && rawchapters.credits.end) {
|
||||||
chapters.push('[CHAPTER]')
|
addChapter(rawchapters.credits)
|
||||||
chapters.push('TIMEBASE=1/1000')
|
|
||||||
chapters.push(`START=${formatTimeFFMPEG(rawchapters.credits.start)}`)
|
|
||||||
chapters.push(`END=${formatTimeFFMPEG(rawchapters.credits.end) - 1}`)
|
|
||||||
chapters.push('title=Credits')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rawchapters.preview && rawchapters.preview.type && rawchapters.preview.start && rawchapters.preview.end) {
|
if (rawchapters.preview && rawchapters.preview.type && rawchapters.preview.start && rawchapters.preview.end) {
|
||||||
chapters.push('[CHAPTER]')
|
addChapter(rawchapters.preview)
|
||||||
chapters.push('TIMEBASE=1/1000')
|
|
||||||
chapters.push(`START=${formatTimeFFMPEG(rawchapters.preview.start)}`)
|
|
||||||
chapters.push(`END=${formatTimeFFMPEG(rawchapters.preview.end) - 1}`)
|
|
||||||
chapters.push('title=Preview')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rawchapters.recap && rawchapters.recap.type && rawchapters.recap.start && rawchapters.recap.end) {
|
if (rawchapters.recap && rawchapters.recap.type && rawchapters.recap.start && rawchapters.recap.end) {
|
||||||
chapters.push('[CHAPTER]')
|
addChapter(rawchapters.recap)
|
||||||
chapters.push('TIMEBASE=1/1000')
|
|
||||||
chapters.push(`START=${formatTimeFFMPEG(rawchapters.recap.start)}`)
|
|
||||||
chapters.push(`END=${formatTimeFFMPEG(rawchapters.recap.end) - 1}`)
|
|
||||||
chapters.push('title=Recap')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -230,3 +230,13 @@ export interface VideoMetadata {
|
|||||||
lastUpdated: Date
|
lastUpdated: Date
|
||||||
mediaId: string
|
mediaId: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface VideoMetadataSingle {
|
||||||
|
approverId: string
|
||||||
|
distributionNumber: string
|
||||||
|
end: number
|
||||||
|
seriesId: string
|
||||||
|
start: number
|
||||||
|
title: string
|
||||||
|
type: string
|
||||||
|
}
|
||||||
|
@ -352,6 +352,59 @@ ipcMain.handle('dialog:getDefaultCrunchyrollLanguageTemplate', async (events) =>
|
|||||||
return seTP
|
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()
|
const openWindows = new Map()
|
||||||
|
|
||||||
|
@ -25,12 +25,12 @@ contextBridge.exposeInMainWorld('myAPI', {
|
|||||||
getSeasonTemplate: () => ipcRenderer.invoke('dialog:getSeasonTemplate'),
|
getSeasonTemplate: () => ipcRenderer.invoke('dialog:getSeasonTemplate'),
|
||||||
setSeasonEnabled: (active: boolean) => ipcRenderer.invoke('dialog:setSeasonEnabledTemplate', active),
|
setSeasonEnabled: (active: boolean) => ipcRenderer.invoke('dialog:setSeasonEnabledTemplate', active),
|
||||||
getSeasonEnabled: () => ipcRenderer.invoke('dialog:getSeasonEnabledTemplate'),
|
getSeasonEnabled: () => ipcRenderer.invoke('dialog:getSeasonEnabledTemplate'),
|
||||||
// setDefaultVideoQuality: (quality: number) => ipcRenderer.invoke('dialog:setDefaultVideoQualityTemplate', quality),
|
setDefaultVideoQuality: (quality: number) => ipcRenderer.invoke('dialog:setDefaultVideoQualityTemplate', quality),
|
||||||
// getDefaultVideoQuality: () => ipcRenderer.invoke('dialog:getDefaultVideoQualityTemplate'),
|
getDefaultVideoQuality: () => ipcRenderer.invoke('dialog:getDefaultVideoQualityTemplate'),
|
||||||
// setDefaultAudioQuality: (quality: number) => ipcRenderer.invoke('dialog:setDefaultAudioQualityTemplate', quality),
|
setDefaultAudioQuality: (quality: number) => ipcRenderer.invoke('dialog:setDefaultAudioQualityTemplate', quality),
|
||||||
// getDefaultAudioQuality: () => ipcRenderer.invoke('dialog:getDefaultAudioQualityTemplate'),
|
getDefaultAudioQuality: () => ipcRenderer.invoke('dialog:getDefaultAudioQualityTemplate'),
|
||||||
// setDefaultOutputFormat: (format: string) => ipcRenderer.invoke('dialog:setDefaultOutputFormatTemplate', format),
|
setDefaultOutputFormat: (format: string) => ipcRenderer.invoke('dialog:setDefaultOutputFormatTemplate', format),
|
||||||
// getDefaultOutputFormat: () => ipcRenderer.invoke('dialog:getDefaultOutputFormatTemplate'),
|
getDefaultOutputFormat: () => ipcRenderer.invoke('dialog:getDefaultOutputFormatTemplate'),
|
||||||
setDefaultCrunchyrollLanguage: (lang: string) => ipcRenderer.invoke('dialog:setDefaultCrunchyrollLanguageTemplate', lang),
|
setDefaultCrunchyrollLanguage: (lang: string) => ipcRenderer.invoke('dialog:setDefaultCrunchyrollLanguageTemplate', lang),
|
||||||
getDefaultCrunchyrollLanguage: () => ipcRenderer.invoke('dialog:getDefaultCrunchyrollLanguageTemplate')
|
getDefaultCrunchyrollLanguage: () => ipcRenderer.invoke('dialog:getDefaultCrunchyrollLanguageTemplate')
|
||||||
})
|
})
|
||||||
|
Reference in New Issue
Block a user