Create professional DVD structure with automatic chapter markers

Major improvements to DVD authoring for professional results:

- Always concatenate multiple videos into single title (not multiple titles)
- Automatically generate chapter markers from video clips
- Chapter markers created at each scene boundary regardless of checkbox
- One title with navigable chapters instead of separate titles
- Better logging showing chapter structure and timestamps

Before: 4 videos → 4 separate titles with no chapters
After: 4 videos → 1 title with 4 chapter markers

This creates a professional DVD that matches commercial disc structure
with proper chapter navigation.

Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Stu Leak 2025-12-30 12:05:21 -05:00
parent 03569cd813
commit 8896206b68

View File

@ -1744,27 +1744,35 @@ func (s *appState) runAuthoringPipeline(ctx context.Context, paths []string, reg
mpgPaths = append(mpgPaths, remuxPath)
}
if len(chapters) == 0 && treatAsChapters && len(clips) > 1 {
// Generate chapters from clips if available (for professional DVD navigation)
if len(chapters) == 0 && len(clips) > 1 {
chapters = chaptersFromClips(clips)
if logFn != nil {
logFn(fmt.Sprintf("Generated %d chapter markers from video clips", len(chapters)))
}
}
// Try to extract embedded chapters from single file
if len(chapters) == 0 && len(mpgPaths) == 1 {
if embed, err := extractChaptersFromFile(paths[0]); err == nil && len(embed) > 0 {
chapters = embed
if logFn != nil {
logFn(fmt.Sprintf("Extracted %d embedded chapters from source", len(chapters)))
}
}
if treatAsChapters && len(mpgPaths) > 1 {
}
// For professional DVD: always concatenate multiple files into one title with chapters
if len(mpgPaths) > 1 {
concatPath := filepath.Join(workDir, "titles_joined.mpg")
if logFn != nil {
logFn("Concatenating chapters into a single title...")
logFn(fmt.Sprintf("Combining %d videos into single title with chapter markers...", len(mpgPaths)))
}
if err := concatDVDMpg(mpgPaths, concatPath); err != nil {
return err
return fmt.Errorf("failed to concatenate videos: %w", err)
}
mpgPaths = []string{concatPath}
}
if len(mpgPaths) > 1 {
chapters = nil
}
// Log details about encoded MPG files
if logFn != nil {
@ -1783,6 +1791,16 @@ func (s *appState) runAuthoringPipeline(ctx context.Context, paths []string, reg
return err
}
// Log chapter information
if len(chapters) > 0 {
if logFn != nil {
logFn(fmt.Sprintf("Final DVD structure: 1 title with %d chapters", len(chapters)))
for i, ch := range chapters {
logFn(fmt.Sprintf(" Chapter %d: %s at %s", i+1, ch.Title, formatChapterTime(ch.Timestamp)))
}
}
}
// Log the XML content for debugging
if xmlContent, err := os.ReadFile(xmlPath); err == nil {
logFn("Generated DVD XML:")