Add Author module skeleton with tabbed interface
Renamed "DVD Author" to "Author" for broader disc production workflow. Created foundation for complete authoring pipeline with three main tasks: **Module Structure:** - Tabbed interface with Chapters, Rip DVD/ISO, and Author Disc tabs - Added authorChapter struct (timestamp, title, auto-detected flag) - Added author module state fields (file, chapters, threshold, detecting) **Chapters Tab (Basic UI):** - File selection with video probing integration - Scene detection sensitivity slider (0.1-0.9 threshold) - Placeholder UI for chapter list and controls - Add Chapter and Export Chapters buttons (placeholders) - Foundation for FFmpeg scdet scene detection **Rip DVD/ISO Tab:** - Placeholder for high-quality disc extraction - Will support lossless ripping (like FLAC from CD) - Preserve all audio/subtitle tracks **Author Disc Tab:** - Placeholder for VIDEO_TS/ISO creation - Will support burn-ready output, NTSC/PAL, menus Changes: - Modified main.go: Added authorChapter struct, author state fields, showAuthorView(), buildAuthorView(), buildChaptersTab(), buildRipTab(), buildAuthorDiscTab() - Modified internal/modules/handlers.go: Renamed HandleDVDAuthor to HandleAuthor with updated comment - Updated DONE.md with Author module skeleton details Next steps: Implement FFmpeg scene detection, chapter list UI, and DVD/ISO ripping functionality. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
364d2099f5
commit
a9804b3ad3
15
DONE.md
15
DONE.md
|
|
@ -114,6 +114,21 @@ This file tracks completed features, fixes, and milestones.
|
|||
- Maintains existing options (Source, Mono, Stereo, 5.1)
|
||||
- Solves problem of videos with music in one ear and vocals in the other
|
||||
|
||||
- ✅ **Author Module Skeleton** (2025-12-20 continuation)
|
||||
- Renamed "DVD Author" module to "Author" for broader scope
|
||||
- Created tabbed interface structure with 3 tabs:
|
||||
- **Chapters Tab** - Scene detection and chapter management
|
||||
- **Rip DVD/ISO Tab** - High-quality disc extraction (like FLAC from CD)
|
||||
- **Author Disc Tab** - VIDEO_TS/ISO creation for burning
|
||||
- Implemented basic Chapters tab UI:
|
||||
- File selection with video probing
|
||||
- Scene detection sensitivity slider (0.1-0.9 threshold)
|
||||
- Placeholder chapter list
|
||||
- Add/Export chapter buttons (to be implemented)
|
||||
- Added authorChapter struct for storing chapter data
|
||||
- Added author module state fields to appState
|
||||
- Foundation for complete disc production workflow
|
||||
|
||||
### Features (2025-12-18 Session)
|
||||
- ✅ **History Sidebar Enhancements**
|
||||
- Delete button ("×") on each history entry
|
||||
|
|
|
|||
|
|
@ -44,10 +44,10 @@ func HandleAudio(files []string) {
|
|||
fmt.Println("audio", files)
|
||||
}
|
||||
|
||||
// HandleDVDAuthor handles the DVD authoring module (placeholder)
|
||||
func HandleDVDAuthor(files []string) {
|
||||
logging.Debug(logging.CatModule, "dvd author handler invoked with %v", files)
|
||||
fmt.Println("dvd author", files)
|
||||
// HandleAuthor handles the disc authoring module (DVD/Blu-ray) (placeholder)
|
||||
func HandleAuthor(files []string) {
|
||||
logging.Debug(logging.CatModule, "author handler invoked with %v", files)
|
||||
fmt.Println("author", files)
|
||||
}
|
||||
|
||||
// HandleSubtitles handles the subtitles module (placeholder)
|
||||
|
|
|
|||
146
main.go
146
main.go
|
|
@ -88,7 +88,7 @@ var (
|
|||
{"filters", "Filters", utils.MustHex("#44FF88"), "Convert", modules.HandleFilters}, // Green
|
||||
{"upscale", "Upscale", utils.MustHex("#AAFF44"), "Advanced", modules.HandleUpscale}, // Yellow-Green
|
||||
{"audio", "Audio", utils.MustHex("#FFD744"), "Convert", modules.HandleAudio}, // Yellow
|
||||
{"dvd-author", "DVD Author", utils.MustHex("#FFAA44"), "Convert", modules.HandleDVDAuthor}, // Orange
|
||||
{"author", "Author", utils.MustHex("#FFAA44"), "Convert", modules.HandleAuthor}, // Orange
|
||||
{"subtitles", "Subtitles", utils.MustHex("#44A6FF"), "Convert", modules.HandleSubtitles}, // Azure
|
||||
{"thumb", "Thumb", utils.MustHex("#FF8844"), "Screenshots", modules.HandleThumb}, // Orange
|
||||
{"compare", "Compare", utils.MustHex("#FF44AA"), "Inspect", modules.HandleCompare}, // Pink
|
||||
|
|
@ -845,6 +845,12 @@ type appState struct {
|
|||
// History sidebar state
|
||||
historyEntries []ui.HistoryEntry
|
||||
sidebarVisible bool
|
||||
|
||||
// Author module state
|
||||
authorFile *videoSource
|
||||
authorChapters []authorChapter
|
||||
authorSceneThreshold float64
|
||||
authorDetecting bool
|
||||
}
|
||||
|
||||
type mergeClip struct {
|
||||
|
|
@ -853,6 +859,12 @@ type mergeClip struct {
|
|||
Duration float64
|
||||
}
|
||||
|
||||
type authorChapter struct {
|
||||
Timestamp float64 // Timestamp in seconds
|
||||
Title string // Chapter title/name
|
||||
Auto bool // True if auto-detected, false if manual
|
||||
}
|
||||
|
||||
func (s *appState) persistConvertConfig() {
|
||||
if err := savePersistedConvertConfig(s.convert); err != nil {
|
||||
logging.Debug(logging.CatSystem, "failed to persist convert config: %v", err)
|
||||
|
|
@ -2583,6 +2595,19 @@ func (s *appState) showUpscaleView() {
|
|||
s.setContent(buildUpscaleView(s))
|
||||
}
|
||||
|
||||
func (s *appState) showAuthorView() {
|
||||
s.stopPreview()
|
||||
s.lastModule = s.active
|
||||
s.active = "author"
|
||||
|
||||
// Initialize scene detection threshold if not set
|
||||
if s.authorSceneThreshold == 0 {
|
||||
s.authorSceneThreshold = 0.3
|
||||
}
|
||||
|
||||
s.setContent(buildAuthorView(s))
|
||||
}
|
||||
|
||||
func (s *appState) showMergeView() {
|
||||
s.stopPreview()
|
||||
s.lastModule = s.active
|
||||
|
|
@ -13846,6 +13871,125 @@ func parseResolutionPreset(preset string, srcW, srcH int) (width, height int, pr
|
|||
}
|
||||
|
||||
// buildUpscaleFilter builds the FFmpeg scale filter string with the selected method
|
||||
func buildAuthorView(state *appState) fyne.CanvasObject {
|
||||
authorColor := moduleColor("author")
|
||||
|
||||
// Back button
|
||||
backBtn := widget.NewButton("< BACK", func() {
|
||||
state.showMainMenu()
|
||||
})
|
||||
backBtn.Importance = widget.LowImportance
|
||||
|
||||
// Title
|
||||
title := canvas.NewText("AUTHOR", authorColor)
|
||||
title.TextStyle = fyne.TextStyle{Monospace: true, Bold: true}
|
||||
title.TextSize = 20
|
||||
|
||||
header := container.NewBorder(nil, nil, backBtn, nil, container.NewCenter(title))
|
||||
|
||||
// Create tabs for different authoring tasks
|
||||
tabs := container.NewAppTabs(
|
||||
container.NewTabItem("Chapters", buildChaptersTab(state)),
|
||||
container.NewTabItem("Rip DVD/ISO", buildRipTab(state)),
|
||||
container.NewTabItem("Author Disc", buildAuthorDiscTab(state)),
|
||||
)
|
||||
tabs.SetTabLocation(container.TabLocationTop)
|
||||
|
||||
return container.NewBorder(header, nil, nil, nil, tabs)
|
||||
}
|
||||
|
||||
func buildChaptersTab(state *appState) fyne.CanvasObject {
|
||||
// File selection
|
||||
var fileLabel *widget.Label
|
||||
if state.authorFile != nil {
|
||||
fileLabel = widget.NewLabel(fmt.Sprintf("File: %s", filepath.Base(state.authorFile.Path)))
|
||||
fileLabel.TextStyle = fyne.TextStyle{Bold: true}
|
||||
} else {
|
||||
fileLabel = widget.NewLabel("No file loaded")
|
||||
}
|
||||
|
||||
selectBtn := widget.NewButton("Select Video", func() {
|
||||
dialog.ShowFileOpen(func(uc fyne.URIReadCloser, err error) {
|
||||
if err != nil || uc == nil {
|
||||
return
|
||||
}
|
||||
defer uc.Close()
|
||||
path := uc.URI().Path()
|
||||
src, err := probeVideo(path)
|
||||
if err != nil {
|
||||
dialog.ShowError(fmt.Errorf("failed to load video: %w", err), state.window)
|
||||
return
|
||||
}
|
||||
state.authorFile = src
|
||||
fileLabel.SetText(fmt.Sprintf("File: %s", filepath.Base(src.Path)))
|
||||
}, state.window)
|
||||
})
|
||||
|
||||
// Scene detection threshold
|
||||
thresholdLabel := widget.NewLabel(fmt.Sprintf("Detection Sensitivity: %.2f", state.authorSceneThreshold))
|
||||
thresholdSlider := widget.NewSlider(0.1, 0.9)
|
||||
thresholdSlider.Value = state.authorSceneThreshold
|
||||
thresholdSlider.Step = 0.05
|
||||
thresholdSlider.OnChanged = func(v float64) {
|
||||
state.authorSceneThreshold = v
|
||||
thresholdLabel.SetText(fmt.Sprintf("Detection Sensitivity: %.2f", v))
|
||||
}
|
||||
|
||||
// Detect scenes button
|
||||
detectBtn := widget.NewButton("Detect Scenes", func() {
|
||||
if state.authorFile == nil {
|
||||
dialog.ShowInformation("No File", "Please select a video file first", state.window)
|
||||
return
|
||||
}
|
||||
// TODO: Implement scene detection
|
||||
dialog.ShowInformation("Scene Detection", "Scene detection will be implemented in the next step", state.window)
|
||||
})
|
||||
detectBtn.Importance = widget.HighImportance
|
||||
|
||||
// Chapter list (placeholder)
|
||||
chapterList := widget.NewLabel("No chapters detected yet")
|
||||
|
||||
// Add manual chapter button
|
||||
addChapterBtn := widget.NewButton("+ Add Chapter", func() {
|
||||
// TODO: Implement manual chapter addition
|
||||
dialog.ShowInformation("Add Chapter", "Manual chapter addition will be implemented soon", state.window)
|
||||
})
|
||||
|
||||
// Export chapters button
|
||||
exportBtn := widget.NewButton("Export Chapters", func() {
|
||||
// TODO: Implement chapter export
|
||||
dialog.ShowInformation("Export", "Chapter export will be implemented soon", state.window)
|
||||
})
|
||||
|
||||
controls := container.NewVBox(
|
||||
fileLabel,
|
||||
selectBtn,
|
||||
widget.NewSeparator(),
|
||||
widget.NewLabel("Scene Detection:"),
|
||||
thresholdLabel,
|
||||
thresholdSlider,
|
||||
detectBtn,
|
||||
widget.NewSeparator(),
|
||||
widget.NewLabel("Chapters:"),
|
||||
container.NewScroll(chapterList),
|
||||
container.NewHBox(addChapterBtn, exportBtn),
|
||||
)
|
||||
|
||||
return container.NewPadded(controls)
|
||||
}
|
||||
|
||||
func buildRipTab(state *appState) fyne.CanvasObject {
|
||||
placeholder := widget.NewLabel("DVD/ISO ripping will be implemented here.\n\nFeatures:\n• Mount and scan DVD/ISO\n• Select titles and tracks\n• Rip at highest quality (like FLAC from CD)\n• Preserve all audio and subtitle tracks")
|
||||
placeholder.Wrapping = fyne.TextWrapWord
|
||||
return container.NewCenter(placeholder)
|
||||
}
|
||||
|
||||
func buildAuthorDiscTab(state *appState) fyne.CanvasObject {
|
||||
placeholder := widget.NewLabel("Disc authoring will be implemented here.\n\nFeatures:\n• Create VIDEO_TS folder structure\n• Generate burn-ready ISO\n• NTSC/PAL selection\n• Menu creation\n• Chapter integration")
|
||||
placeholder.Wrapping = fyne.TextWrapWord
|
||||
return container.NewCenter(placeholder)
|
||||
}
|
||||
|
||||
func buildUpscaleFilter(targetWidth, targetHeight int, method string, preserveAspect bool) string {
|
||||
// Ensure even dimensions for encoders
|
||||
makeEven := func(v int) int {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user