Expand convert presets and relative scaling
This commit is contained in:
parent
43eae3d17e
commit
3f356f9a74
197
main.go
197
main.go
|
|
@ -513,12 +513,12 @@ func savePersistedConvertConfig(cfg convertConfig) error {
|
||||||
|
|
||||||
// benchmarkRun represents a single benchmark test run
|
// benchmarkRun represents a single benchmark test run
|
||||||
type benchmarkRun struct {
|
type benchmarkRun struct {
|
||||||
Timestamp time.Time `json:"timestamp"`
|
Timestamp time.Time `json:"timestamp"`
|
||||||
Results []benchmark.Result `json:"results"`
|
Results []benchmark.Result `json:"results"`
|
||||||
RecommendedEncoder string `json:"recommended_encoder"`
|
RecommendedEncoder string `json:"recommended_encoder"`
|
||||||
RecommendedPreset string `json:"recommended_preset"`
|
RecommendedPreset string `json:"recommended_preset"`
|
||||||
RecommendedHWAccel string `json:"recommended_hwaccel"`
|
RecommendedHWAccel string `json:"recommended_hwaccel"`
|
||||||
RecommendedFPS float64 `json:"recommended_fps"`
|
RecommendedFPS float64 `json:"recommended_fps"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// benchmarkConfig holds benchmark history
|
// benchmarkConfig holds benchmark history
|
||||||
|
|
@ -566,47 +566,47 @@ func saveBenchmarkConfig(cfg benchmarkConfig) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
type appState struct {
|
type appState struct {
|
||||||
window fyne.Window
|
window fyne.Window
|
||||||
active string
|
active string
|
||||||
lastModule string
|
lastModule string
|
||||||
source *videoSource
|
source *videoSource
|
||||||
loadedVideos []*videoSource // Multiple loaded videos for navigation
|
loadedVideos []*videoSource // Multiple loaded videos for navigation
|
||||||
currentIndex int // Current video index in loadedVideos
|
currentIndex int // Current video index in loadedVideos
|
||||||
anim *previewAnimator
|
anim *previewAnimator
|
||||||
convert convertConfig
|
convert convertConfig
|
||||||
currentFrame string
|
currentFrame string
|
||||||
player player.Controller
|
player player.Controller
|
||||||
playerReady bool
|
playerReady bool
|
||||||
playerVolume float64
|
playerVolume float64
|
||||||
playerMuted bool
|
playerMuted bool
|
||||||
lastVolume float64
|
lastVolume float64
|
||||||
playerPaused bool
|
playerPaused bool
|
||||||
playerPos float64
|
playerPos float64
|
||||||
playerLast time.Time
|
playerLast time.Time
|
||||||
progressQuit chan struct{}
|
progressQuit chan struct{}
|
||||||
convertCancel context.CancelFunc
|
convertCancel context.CancelFunc
|
||||||
playerSurf *playerSurface
|
playerSurf *playerSurface
|
||||||
convertBusy bool
|
convertBusy bool
|
||||||
convertStatus string
|
convertStatus string
|
||||||
convertActiveIn string
|
convertActiveIn string
|
||||||
convertActiveOut string
|
convertActiveOut string
|
||||||
convertActiveLog string
|
convertActiveLog string
|
||||||
convertProgress float64
|
convertProgress float64
|
||||||
convertFPS float64
|
convertFPS float64
|
||||||
convertSpeed float64
|
convertSpeed float64
|
||||||
convertETA time.Duration
|
convertETA time.Duration
|
||||||
playSess *playSession
|
playSess *playSession
|
||||||
jobQueue *queue.Queue
|
jobQueue *queue.Queue
|
||||||
statsBar *ui.ConversionStatsBar
|
statsBar *ui.ConversionStatsBar
|
||||||
queueBtn *widget.Button
|
queueBtn *widget.Button
|
||||||
queueScroll *container.Scroll
|
queueScroll *container.Scroll
|
||||||
queueOffset fyne.Position
|
queueOffset fyne.Position
|
||||||
compareFile1 *videoSource
|
compareFile1 *videoSource
|
||||||
compareFile2 *videoSource
|
compareFile2 *videoSource
|
||||||
inspectFile *videoSource
|
inspectFile *videoSource
|
||||||
inspectInterlaceResult *interlace.DetectionResult
|
inspectInterlaceResult *interlace.DetectionResult
|
||||||
inspectInterlaceAnalyzing bool
|
inspectInterlaceAnalyzing bool
|
||||||
autoCompare bool // Auto-load Compare module after conversion
|
autoCompare bool // Auto-load Compare module after conversion
|
||||||
|
|
||||||
// Merge state
|
// Merge state
|
||||||
mergeClips []mergeClip
|
mergeClips []mergeClip
|
||||||
|
|
@ -626,8 +626,8 @@ type appState struct {
|
||||||
thumbLastOutputPath string // Path to last generated output
|
thumbLastOutputPath string // Path to last generated output
|
||||||
|
|
||||||
// Interlacing detection state
|
// Interlacing detection state
|
||||||
interlaceResult *interlace.DetectionResult
|
interlaceResult *interlace.DetectionResult
|
||||||
interlaceAnalyzing bool
|
interlaceAnalyzing bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type mergeClip struct {
|
type mergeClip struct {
|
||||||
|
|
@ -1363,10 +1363,10 @@ func (s *appState) detectHardwareEncoders() []string {
|
||||||
|
|
||||||
// Check for hardware encoders by trying to get codec info
|
// Check for hardware encoders by trying to get codec info
|
||||||
encodersToCheck := []string{
|
encodersToCheck := []string{
|
||||||
"h264_nvenc", "hevc_nvenc", // NVIDIA
|
"h264_nvenc", "hevc_nvenc", // NVIDIA
|
||||||
"h264_qsv", "hevc_qsv", // Intel QuickSync
|
"h264_qsv", "hevc_qsv", // Intel QuickSync
|
||||||
"h264_amf", "hevc_amf", // AMD AMF
|
"h264_amf", "hevc_amf", // AMD AMF
|
||||||
"h264_videotoolbox", // Apple VideoToolbox
|
"h264_videotoolbox", // Apple VideoToolbox
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, encoder := range encodersToCheck {
|
for _, encoder := range encodersToCheck {
|
||||||
|
|
@ -2089,16 +2089,16 @@ func (s *appState) showMergeView() {
|
||||||
}
|
}
|
||||||
|
|
||||||
formatMap := map[string]string{
|
formatMap := map[string]string{
|
||||||
"MKV (Copy streams)": "mkv-copy",
|
"MKV (Copy streams)": "mkv-copy",
|
||||||
"MKV (H.264)": "mkv-h264",
|
"MKV (H.264)": "mkv-h264",
|
||||||
"MKV (H.265)": "mkv-h265",
|
"MKV (H.265)": "mkv-h265",
|
||||||
"MP4 (H.264)": "mp4-h264",
|
"MP4 (H.264)": "mp4-h264",
|
||||||
"MP4 (H.265)": "mp4-h265",
|
"MP4 (H.265)": "mp4-h265",
|
||||||
"DVD NTSC 16:9": "dvd-ntsc-169",
|
"DVD NTSC 16:9": "dvd-ntsc-169",
|
||||||
"DVD NTSC 4:3": "dvd-ntsc-43",
|
"DVD NTSC 4:3": "dvd-ntsc-43",
|
||||||
"DVD PAL 16:9": "dvd-pal-169",
|
"DVD PAL 16:9": "dvd-pal-169",
|
||||||
"DVD PAL 4:3": "dvd-pal-43",
|
"DVD PAL 4:3": "dvd-pal-43",
|
||||||
"Blu-ray (H.264)": "bd-h264",
|
"Blu-ray (H.264)": "bd-h264",
|
||||||
}
|
}
|
||||||
var formatKeys []string
|
var formatKeys []string
|
||||||
for k := range formatMap {
|
for k := range formatMap {
|
||||||
|
|
@ -2210,10 +2210,10 @@ func (s *appState) showMergeView() {
|
||||||
)
|
)
|
||||||
|
|
||||||
left := container.NewBorder(
|
left := container.NewBorder(
|
||||||
leftTop, // top
|
leftTop, // top
|
||||||
nil, // bottom
|
nil, // bottom
|
||||||
nil, // left
|
nil, // left
|
||||||
nil, // right
|
nil, // right
|
||||||
ui.NewDroppable(listScroll, func(items []fyne.URI) {
|
ui.NewDroppable(listScroll, func(items []fyne.URI) {
|
||||||
var paths []string
|
var paths []string
|
||||||
for _, uri := range items {
|
for _, uri := range items {
|
||||||
|
|
@ -3297,7 +3297,7 @@ func (s *appState) executeSnippetJob(ctx context.Context, job *queue.Job, progre
|
||||||
"-i", inputPath,
|
"-i", inputPath,
|
||||||
"-t", "20",
|
"-t", "20",
|
||||||
"-c", "copy", // Copy all streams without re-encoding
|
"-c", "copy", // Copy all streams without re-encoding
|
||||||
"-map", "0", // Include all streams
|
"-map", "0", // Include all streams
|
||||||
outputPath,
|
outputPath,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3757,7 +3757,14 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
||||||
updateQualityVisibility func()
|
updateQualityVisibility func()
|
||||||
)
|
)
|
||||||
|
|
||||||
qualityOptions := []string{"Draft (CRF 28)", "Standard (CRF 23)", "High (CRF 18)", "Lossless"}
|
qualityOptions := []string{
|
||||||
|
"Draft (CRF 28)",
|
||||||
|
"Standard (CRF 23)",
|
||||||
|
"Balanced (CRF 20)",
|
||||||
|
"High (CRF 18)",
|
||||||
|
"Near-Lossless (CRF 16)",
|
||||||
|
"Lossless",
|
||||||
|
}
|
||||||
var syncingQuality bool
|
var syncingQuality bool
|
||||||
|
|
||||||
qualitySelectSimple = widget.NewSelect(qualityOptions, func(value string) {
|
qualitySelectSimple = widget.NewSelect(qualityOptions, func(value string) {
|
||||||
|
|
@ -3792,6 +3799,9 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
||||||
syncingQuality = false
|
syncingQuality = false
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if !slices.Contains(qualityOptions, state.convert.Quality) {
|
||||||
|
state.convert.Quality = "Standard (CRF 23)"
|
||||||
|
}
|
||||||
qualitySelectSimple.SetSelected(state.convert.Quality)
|
qualitySelectSimple.SetSelected(state.convert.Quality)
|
||||||
qualitySelectAdv.SetSelected(state.convert.Quality)
|
qualitySelectAdv.SetSelected(state.convert.Quality)
|
||||||
|
|
||||||
|
|
@ -3881,16 +3891,16 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
||||||
// Show results dialog
|
// Show results dialog
|
||||||
resultText := fmt.Sprintf(
|
resultText := fmt.Sprintf(
|
||||||
"Status: %s\n"+
|
"Status: %s\n"+
|
||||||
"Interlaced Frames: %.1f%%\n"+
|
"Interlaced Frames: %.1f%%\n"+
|
||||||
"Field Order: %s\n"+
|
"Field Order: %s\n"+
|
||||||
"Confidence: %s\n\n"+
|
"Confidence: %s\n\n"+
|
||||||
"Recommendation:\n%s\n\n"+
|
"Recommendation:\n%s\n\n"+
|
||||||
"Frame Counts:\n"+
|
"Frame Counts:\n"+
|
||||||
"Progressive: %d\n"+
|
"Progressive: %d\n"+
|
||||||
"Top Field First: %d\n"+
|
"Top Field First: %d\n"+
|
||||||
"Bottom Field First: %d\n"+
|
"Bottom Field First: %d\n"+
|
||||||
"Undetermined: %d\n"+
|
"Undetermined: %d\n"+
|
||||||
"Total Analyzed: %d",
|
"Total Analyzed: %d",
|
||||||
result.Status,
|
result.Status,
|
||||||
result.InterlacedPercent,
|
result.InterlacedPercent,
|
||||||
result.FieldOrder,
|
result.FieldOrder,
|
||||||
|
|
@ -4338,7 +4348,8 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
||||||
|
|
||||||
// Simple resolution selector (separate widget to avoid double-parent issues)
|
// Simple resolution selector (separate widget to avoid double-parent issues)
|
||||||
resolutionSelectSimple := widget.NewSelect([]string{
|
resolutionSelectSimple := widget.NewSelect([]string{
|
||||||
"Source", "360p", "480p", "540p", "720p", "1080p", "1440p", "4K",
|
"Source", "360p", "480p", "540p", "720p", "1080p", "1440p", "4K", "8K",
|
||||||
|
"2X (relative)", "4X (relative)",
|
||||||
"NTSC (720×480)", "PAL (720×540)", "PAL (720×576)",
|
"NTSC (720×480)", "PAL (720×540)", "PAL (720×576)",
|
||||||
}, func(value string) {
|
}, func(value string) {
|
||||||
state.convert.TargetResolution = value
|
state.convert.TargetResolution = value
|
||||||
|
|
@ -4522,7 +4533,11 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
|
||||||
updateEncodingControls()
|
updateEncodingControls()
|
||||||
|
|
||||||
// Target Resolution (advanced)
|
// Target Resolution (advanced)
|
||||||
resolutionSelect := widget.NewSelect([]string{"Source", "720p", "1080p", "1440p", "4K", "NTSC (720×480)", "PAL (720×540)", "PAL (720×576)"}, func(value string) {
|
resolutionSelect := widget.NewSelect([]string{
|
||||||
|
"Source", "720p", "1080p", "1440p", "4K", "8K",
|
||||||
|
"2X (relative)", "4X (relative)",
|
||||||
|
"NTSC (720×480)", "PAL (720×540)", "PAL (720×576)",
|
||||||
|
}, func(value string) {
|
||||||
state.convert.TargetResolution = value
|
state.convert.TargetResolution = value
|
||||||
logging.Debug(logging.CatUI, "target resolution set to %s", value)
|
logging.Debug(logging.CatUI, "target resolution set to %s", value)
|
||||||
})
|
})
|
||||||
|
|
@ -7121,10 +7136,14 @@ func (s *appState) prevVideo() {
|
||||||
|
|
||||||
func crfForQuality(q string) string {
|
func crfForQuality(q string) string {
|
||||||
switch q {
|
switch q {
|
||||||
|
case "Balanced (CRF 20)":
|
||||||
|
return "20"
|
||||||
case "Draft (CRF 28)":
|
case "Draft (CRF 28)":
|
||||||
return "28"
|
return "28"
|
||||||
case "High (CRF 18)":
|
case "High (CRF 18)":
|
||||||
return "18"
|
return "18"
|
||||||
|
case "Near-Lossless (CRF 16)":
|
||||||
|
return "16"
|
||||||
case "Lossless":
|
case "Lossless":
|
||||||
return "0"
|
return "0"
|
||||||
default:
|
default:
|
||||||
|
|
@ -7478,6 +7497,12 @@ func (s *appState) startConvert(status *widget.Label, btn, cancelBtn *widget.But
|
||||||
// Scaling/Resolution
|
// Scaling/Resolution
|
||||||
if cfg.TargetResolution != "" && cfg.TargetResolution != "Source" {
|
if cfg.TargetResolution != "" && cfg.TargetResolution != "Source" {
|
||||||
var scaleFilter string
|
var scaleFilter string
|
||||||
|
makeEven := func(v int) int {
|
||||||
|
if v%2 != 0 {
|
||||||
|
return v + 1
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
switch cfg.TargetResolution {
|
switch cfg.TargetResolution {
|
||||||
case "720p":
|
case "720p":
|
||||||
scaleFilter = "scale=-2:720"
|
scaleFilter = "scale=-2:720"
|
||||||
|
|
@ -7495,6 +7520,18 @@ func (s *appState) startConvert(status *widget.Label, btn, cancelBtn *widget.But
|
||||||
scaleFilter = "scale=720:540"
|
scaleFilter = "scale=720:540"
|
||||||
case "PAL (720×576)":
|
case "PAL (720×576)":
|
||||||
scaleFilter = "scale=720:576"
|
scaleFilter = "scale=720:576"
|
||||||
|
case "2X (relative)":
|
||||||
|
if src != nil {
|
||||||
|
w := makeEven(src.Width * 2)
|
||||||
|
h := makeEven(src.Height * 2)
|
||||||
|
scaleFilter = fmt.Sprintf("scale=%d:%d", w, h)
|
||||||
|
}
|
||||||
|
case "4X (relative)":
|
||||||
|
if src != nil {
|
||||||
|
w := makeEven(src.Width * 4)
|
||||||
|
h := makeEven(src.Height * 4)
|
||||||
|
scaleFilter = fmt.Sprintf("scale=%d:%d", w, h)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if scaleFilter != "" {
|
if scaleFilter != "" {
|
||||||
vf = append(vf, scaleFilter)
|
vf = append(vf, scaleFilter)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user