ffdash/ui/
state.rs

1// Application state management
2
3use crate::engine::hardware::HwPreflightResult;
4use crate::stats::StatsState;
5use crate::ui::focus::ConfigFocus;
6use crate::ui::help::HelpModalState;
7use ratatui::{
8    layout::Rect,
9    widgets::{ListState, TableState},
10};
11use std::collections::VecDeque;
12use std::rc::Rc;
13use std::time::Instant;
14use sysinfo::System;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum Screen {
18    Dashboard,
19    Config,
20    Stats,
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub enum RateControlMode {
25    CQ,         // Constant quality (b:v 0)
26    CQCap,      // CQ with maxrate cap
27    TwoPassVBR, // Two-pass variable bitrate
28    CBR,        // Constant bitrate (for live/streaming)
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub enum InputMode {
33    Normal,  // Normal navigation mode - global shortcuts active
34    Editing, // Text editing mode - character input active, global shortcuts inactive
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
38pub enum CodecSelection {
39    #[default]
40    Vp9,
41    Av1,
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
45pub enum ColorSpacePreset {
46    #[default]
47    Auto,  // Passthrough: -1, -1, -1, -1
48    Sdr,   // BT709: 1, 1, 1, 0
49    Hdr10, // BT2020+PQ: 9, 9, 16, 0
50}
51
52/// Audio primary track codec selection
53/// Passthrough copies audio without re-encoding, others transcode
54#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
55pub enum AudioPrimaryCodec {
56    #[default]
57    Passthrough, // -c:a copy
58    Opus, // libopus
59    Aac,  // aac
60    Mp3,  // mp3
61    Vorbis, // vorbis
62}
63
64impl AudioPrimaryCodec {
65    pub fn is_passthrough(&self) -> bool {
66        matches!(self, Self::Passthrough)
67    }
68
69    pub fn ffmpeg_codec(&self) -> Option<&'static str> {
70        match self {
71            Self::Passthrough => None,
72            Self::Opus => Some("libopus"),
73            Self::Aac => Some("aac"),
74            Self::Mp3 => Some("mp3"),
75            Self::Vorbis => Some("vorbis"),
76        }
77    }
78
79    pub fn from_index(idx: usize) -> Self {
80        match idx {
81            0 => Self::Passthrough,
82            1 => Self::Opus,
83            2 => Self::Aac,
84            3 => Self::Mp3,
85            4 => Self::Vorbis,
86            _ => Self::Opus,
87        }
88    }
89
90    pub fn to_index(self) -> usize {
91        match self {
92            Self::Passthrough => 0,
93            Self::Opus => 1,
94            Self::Aac => 2,
95            Self::Mp3 => 3,
96            Self::Vorbis => 4,
97        }
98    }
99}
100
101/// Audio stereo compatibility track codec (no passthrough option)
102#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
103pub enum AudioStereoCodec {
104    #[default]
105    Aac,    // aac (best compatibility)
106    Opus,   // libopus
107}
108
109impl AudioStereoCodec {
110    pub fn ffmpeg_codec(&self) -> &'static str {
111        match self {
112            Self::Aac => "aac",
113            Self::Opus => "libopus",
114        }
115    }
116
117    pub fn from_index(idx: usize) -> Self {
118        match idx {
119            0 => Self::Aac,
120            1 => Self::Opus,
121            _ => Self::Aac,
122        }
123    }
124
125    pub fn to_index(self) -> usize {
126        match self {
127            Self::Aac => 0,
128            Self::Opus => 1,
129        }
130    }
131}
132
133/// State for the quit confirmation modal
134#[derive(Debug, Clone)]
135pub struct QuitConfirmationState {
136    /// Number of encodes currently in progress
137    pub running_count: usize,
138}
139
140pub struct AppState {
141    pub current_screen: Screen,
142    pub dashboard: DashboardState,
143    pub config: ConfigState,
144    pub stats: StatsState,
145    pub last_metrics_update: Instant,
146    pub viewport: Rect,
147    pub worker_pool: Option<Rc<crate::engine::worker::WorkerPool>>,
148    pub enc_state: Option<crate::engine::EncState>,
149    pub root_path: Option<std::path::PathBuf>,
150    pub help_modal: Option<HelpModalState>,
151    pub quit_confirmation: Option<QuitConfirmationState>, // Quit confirmation modal
152    pub app_version: String,
153    pub ffmpeg_version: Option<String>,
154    pub ffprobe_version: Option<String>,
155    pub hw_preflight_result: Option<HwPreflightResult>,
156    pub huc_available: Option<bool>, // HuC firmware status (for VBR/CBR modes)
157    pub scan_in_progress: bool,      // True while initial scan is running
158    pub pending_autostart: bool,     // True if we should autostart after scan completes
159}
160
161impl Default for AppState {
162    fn default() -> Self {
163        Self {
164            current_screen: Screen::Dashboard,
165            dashboard: DashboardState::default(),
166            config: ConfigState::default(),
167            stats: StatsState::default(),
168            last_metrics_update: Instant::now(),
169            viewport: Rect::default(),
170            worker_pool: None,        // Initialized when encoding starts
171            enc_state: None,          // Initialized when encoding starts
172            root_path: None,          // Set when user provides a directory to encode
173            help_modal: None,         // Opened when 'H' key is pressed
174            quit_confirmation: None,  // Opened when 'q' pressed with active encodes
175            app_version: env!("CARGO_PKG_VERSION").to_string(),
176            ffmpeg_version: None,      // Cached when help is first opened
177            ffprobe_version: None,     // Cached when help is first opened
178            hw_preflight_result: None, // Cached when help is first opened
179            huc_available: None,       // Checked when help is first opened
180            scan_in_progress: false,
181            pending_autostart: false,
182        }
183    }
184}
185
186pub struct DashboardState {
187    pub cpu_data: VecDeque<u64>,
188    pub mem_data: VecDeque<u64>,
189    pub table_state: TableState,
190    pub foreground_job_index: usize,
191    pub system: System,
192
193    // Mouse support
194    pub table_area: Option<Rect>,
195    pub table_inner_area: Option<Rect>,
196    pub hovered_row: Option<usize>,
197
198    // Job data (if available)
199    pub jobs: Vec<crate::engine::VideoJob>,
200
201    // GPU monitoring
202    pub gpu_data: VecDeque<u64>,     // GPU usage % ring buffer
203    pub gpu_mem_data: VecDeque<u64>, // GPU memory % ring buffer
204    pub gpu_available: bool,         // GPU monitoring tool detected
205    pub gpu_model: Option<String>,   // e.g., "Intel Arc A770" or "NVIDIA GeForce RTX 4060"
206    pub gpu_vendor: crate::engine::hardware::GpuVendor, // Detected GPU vendor
207
208    // Uptime tracking
209    pub start_time: Instant,
210}
211
212impl Default for DashboardState {
213    fn default() -> Self {
214        let mut table_state = TableState::default();
215        table_state.select(Some(0));
216
217        Self {
218            cpu_data: VecDeque::with_capacity(240),
219            mem_data: VecDeque::with_capacity(240),
220            table_state,
221            foreground_job_index: 0,
222            system: System::new_all(),
223            table_area: None,
224            table_inner_area: None,
225            hovered_row: None,
226            jobs: Vec::new(),
227
228            // GPU monitoring
229            gpu_data: VecDeque::with_capacity(240),
230            gpu_mem_data: VecDeque::with_capacity(240),
231            gpu_available: false,
232            gpu_model: None,
233            gpu_vendor: crate::engine::hardware::GpuVendor::Unknown,
234
235            start_time: Instant::now(),
236        }
237    }
238}
239
240pub struct ConfigState {
241    pub focus: ConfigFocus,
242    pub profile_list_state: ListState,
243    pub quality_mode_state: ListState,
244    pub profile_dropdown_state: ListState,
245    pub pix_fmt_state: ListState,
246    pub aq_mode_state: ListState,
247    pub tune_content_state: ListState,
248    pub colorspace_preset_state: ListState,
249    pub arnr_type_state: ListState,
250    pub fps_dropdown_state: ListState,
251    pub resolution_dropdown_state: ListState,
252
253    // Video codec selection (VP9 vs AV1)
254    pub video_codec_state: ListState,
255    pub codec_selection: CodecSelection,
256
257    // AV1 software settings (libsvtav1)
258    pub av1_preset: u32,           // 0-13, default 8
259    pub av1_tune_state: ListState, // Visual Quality, SSIM, VMAF
260    pub av1_film_grain: u32,       // 0-50, default 0
261    pub av1_film_grain_denoise: bool, // denoise before grain synthesis
262    pub av1_enable_overlays: bool,
263    pub av1_scd: bool,            // Scene change detection
264    pub av1_scm_state: ListState, // Screen content mode: Off, On, Auto
265    pub av1_enable_tf: bool,      // Temporal filtering
266
267    // AV1 hardware settings
268    pub av1_hw_preset: u32, // Hardware preset: 1-7 (QSV numeric, NVENC adds 'p' prefix)
269    pub av1_hw_cq: u32,     // Legacy: use per-encoder fields below
270
271    // Per-encoder quality (AV1) - these take precedence over av1_hw_cq
272    pub av1_svt_crf: u32,   // Software SVT-AV1: 0-63, lower=better
273    pub av1_qsv_cq: u32,    // Intel QSV: 1-255, lower=better
274    pub av1_nvenc_cq: u32,  // NVIDIA: 0-63, lower=better
275    pub av1_vaapi_cq: u32,  // VAAPI: 1-255, lower=better
276
277    pub av1_hw_lookahead: u32, // Lookahead frames
278    pub av1_hw_tile_cols: u32, // Tile columns
279    pub av1_hw_tile_rows: u32, // Tile rows
280
281    // Profile tracking
282    pub current_profile_name: Option<String>, // None = Custom
283    pub is_modified: bool,                    // True if settings changed after profile load
284    pub available_profiles: Vec<String>,      // Cached list of saved profiles
285
286    // General settings
287    pub output_dir: String,
288    pub overwrite: bool,
289    pub max_workers: u32, // Number of concurrent encoding jobs (1 = sequential)
290
291    // Filename customization (template-based)
292    // Supports: {filename}, {basename}, {profile}, {ext}
293    pub filename_pattern: String,
294    pub container_dropdown_state: ListState, // For selecting container extension
295
296    // Additional FFmpeg arguments (appended to command before output file)
297    pub additional_args: String,
298
299    // Video output constraints (max FPS, max resolution)
300    pub fps: u32,          // 0 = source (no limit), >0 = max fps cap
301    pub scale_width: i32,  // -2 = source, -1 = auto, >0 = max width
302    pub scale_height: i32, // -2 = source, -1 = auto, >0 = max height
303
304    // Rate control
305    pub rate_control_mode: RateControlMode,
306    pub crf: u32,
307    pub video_target_bitrate: u32,
308    pub video_min_bitrate: u32,
309    pub video_max_bitrate: u32,
310    pub video_bufsize: u32,
311    pub undershoot_pct: i32, // VBR undershoot % (-1 = auto, 0-100)
312    pub overshoot_pct: i32,  // VBR overshoot % (-1 = auto, 0-1000)
313
314    // Speed & quality
315    pub cpu_used: u32,
316    pub cpu_used_pass1: u32, // For 2-pass: Pass 1 speed (guide recommends 4)
317    pub cpu_used_pass2: u32, // For 2-pass: Pass 2 speed (guide recommends 0-2)
318    pub two_pass: bool,
319
320    // Parallelism
321    pub row_mt: bool,
322    pub tile_columns: i32,
323    pub tile_rows: i32,
324    pub threads: u32,
325    pub frame_parallel: bool,
326
327    // GOP & keyframes
328    pub gop_length: String,
329    pub keyint_min: String, // Minimum keyframe interval ("0" = auto)
330    pub fixed_gop: bool,
331    pub lag_in_frames: u32,
332    pub auto_alt_ref: u32,
333
334    // Adaptive quantization
335    // (aq_mode_state ListState above, selected index determines mode)
336
337    // Alt-ref denoising (ARNR)
338    pub arnr_max_frames: u32,
339    pub arnr_strength: u32,
340    pub arnr_type: i32, // -1=Auto, 1=Backward, 2=Forward, 3=Centered
341
342    // Advanced tuning
343    pub enable_tpl: bool,
344    pub sharpness: i32,
345    pub noise_sensitivity: u32,
346    pub static_thresh: String, // Skip encoding blocks below this threshold ("0" = disabled)
347    pub max_intra_rate: String, // Max I-frame bitrate percentage ("0" = disabled)
348
349    // Color / HDR settings
350    pub colorspace: i32,      // -1 = Auto, or specific colorspace value
351    pub color_primaries: i32, // -1 = Auto, or specific primaries value
352    pub color_trc: i32,       // -1 = Auto (transfer characteristics)
353    pub color_range: i32,     // -1 = Auto, 0 = TV/limited, 1 = PC/full
354    pub colorspace_preset: ColorSpacePreset, // UI preset combining the 4 above
355
356    // Audio settings - multi-track support
357    // Primary track: passthrough or transcode
358    pub audio_primary_codec: AudioPrimaryCodec,
359    pub audio_primary_codec_state: ListState, // For dropdown UI
360    pub audio_primary_bitrate: u32,           // Ignored when passthrough
361    pub audio_primary_downmix: bool,          // Downmix to stereo (2ch)
362
363    // Compatibility track: AC3 5.1 for legacy receivers
364    pub audio_add_ac3: bool,
365    pub audio_ac3_bitrate: u32, // 384-640, default 448
366
367    // Compatibility track: Stereo for mobile/web
368    pub audio_add_stereo: bool,
369    pub audio_stereo_codec: AudioStereoCodec,
370    pub audio_stereo_codec_state: ListState, // For dropdown UI
371    pub audio_stereo_bitrate: u32,           // 64-256, default 128
372
373    // Hardware encoding settings (Intel Arc VAAPI)
374    pub use_hardware_encoding: bool,
375    pub hw_encoding_available: Option<bool>, // None=unchecked, Some=result
376    pub hw_availability_message: Option<String>,
377    pub gpu_vendor: crate::engine::hardware::GpuVendor, // Detected GPU vendor
378    pub vaapi_rc_mode: String, // 1=CQP only (ICQ/VBR/CBR removed due to Arc driver bugs)
379    pub qsv_global_quality: u32, // 1-255 (lower=better quality/larger files, higher=worse quality/smaller files), default 70
380    pub vaapi_compression_level: String, // 0-7 (0=fastest, 7=slowest/best), default "4"
381    pub vaapi_b_frames: String,  // 0-4, default "0"
382    pub vaapi_loop_filter_level: String, // 0-63, default "16"
383    pub vaapi_loop_filter_sharpness: String, // 0-15, default "4"
384
385    // Hardware VPP filters (QSV: 0-100, VAAPI: 0-64)
386    pub hw_denoise: String, // 0=off
387    pub hw_detail: String,  // 0=off (sharpening)
388
389    // VP9 QSV-specific controls (only apply when `vp9_qsv` is used)
390    pub vp9_qsv_preset: u32, // 1-7 (1=best quality, 7=fastest)
391    pub vp9_qsv_lookahead: bool,
392    pub vp9_qsv_lookahead_depth: u32,
393
394    // Auto-VAMF settings (quality calibration)
395    pub auto_vmaf_enabled: bool,
396    pub auto_vmaf_target: String, // Target VMAF score (e.g., "93.0")
397    pub auto_vmaf_step: String,   // Quality step size per iteration (e.g., "2")
398    pub auto_vmaf_max_attempts: String, // Maximum calibration attempts (e.g., "3")
399
400    // Popup dropdown state
401    pub active_dropdown: Option<ConfigFocus>,
402
403    // Profile name input dialog (None = closed, Some(String) = open with current input)
404    pub name_input_dialog: Option<String>,
405
406    // Text input mode
407    pub input_mode: InputMode,
408
409    // Cursor position for text input fields (position in characters)
410    pub cursor_pos: usize,
411
412    // Mouse support - store widget areas
413    pub overwrite_checkbox_area: Option<Rect>,
414    pub two_pass_checkbox_area: Option<Rect>,
415    pub row_mt_checkbox_area: Option<Rect>,
416    pub frame_parallel_checkbox_area: Option<Rect>,
417    pub fixed_gop_checkbox_area: Option<Rect>,
418    pub auto_alt_ref_checkbox_area: Option<Rect>,
419    pub enable_tpl_checkbox_area: Option<Rect>,
420    pub save_button_area: Option<Rect>,
421    pub delete_button_area: Option<Rect>,
422    pub output_dir_area: Option<Rect>,
423    pub filename_pattern_area: Option<Rect>,
424    pub container_dropdown_area: Option<Rect>,
425    pub fps_area: Option<Rect>,
426    pub scale_width_area: Option<Rect>,
427    pub scale_height_area: Option<Rect>,
428    pub profile_list_area: Option<Rect>,
429    pub quality_mode_area: Option<Rect>,
430    pub vp9_profile_list_area: Option<Rect>,
431    pub pix_fmt_area: Option<Rect>,
432    pub aq_mode_area: Option<Rect>,
433    pub tune_content_area: Option<Rect>,
434    pub rate_control_mode_area: Option<Rect>,
435    pub crf_slider_area: Option<Rect>,
436    pub cpu_used_slider_area: Option<Rect>,
437    pub cpu_used_pass1_slider_area: Option<Rect>,
438    pub cpu_used_pass2_slider_area: Option<Rect>,
439    pub tile_columns_slider_area: Option<Rect>,
440    pub tile_rows_slider_area: Option<Rect>,
441    pub threads_area: Option<Rect>,
442    pub max_workers_area: Option<Rect>,
443    pub gop_length_area: Option<Rect>,
444    pub keyint_min_area: Option<Rect>,
445    pub lag_in_frames_slider_area: Option<Rect>,
446    pub arnr_max_frames_slider_area: Option<Rect>,
447    pub arnr_strength_slider_area: Option<Rect>,
448    pub sharpness_slider_area: Option<Rect>,
449    pub noise_sensitivity_slider_area: Option<Rect>,
450    pub video_target_bitrate_area: Option<Rect>,
451    pub video_min_bitrate_area: Option<Rect>,
452    pub video_max_bitrate_area: Option<Rect>,
453    pub video_bufsize_area: Option<Rect>,
454    // Audio areas
455    pub audio_primary_codec_area: Option<Rect>,
456    pub audio_primary_bitrate_area: Option<Rect>,
457    pub audio_primary_downmix_area: Option<Rect>,
458    pub audio_ac3_checkbox_area: Option<Rect>,
459    pub audio_ac3_bitrate_area: Option<Rect>,
460    pub audio_stereo_checkbox_area: Option<Rect>,
461    pub audio_stereo_codec_area: Option<Rect>,
462    pub audio_stereo_bitrate_area: Option<Rect>,
463    pub colorspace_preset_area: Option<Rect>,
464    pub arnr_type_area: Option<Rect>,
465    pub static_thresh_area: Option<Rect>,
466    pub max_intra_rate_area: Option<Rect>,
467    pub undershoot_pct_area: Option<Rect>,
468    pub overshoot_pct_area: Option<Rect>,
469
470    // Hardware encoding areas
471    pub hw_encoding_checkbox_area: Option<Rect>,
472    pub qsv_quality_slider_area: Option<Rect>,
473    pub vaapi_compression_level_slider_area: Option<Rect>,
474    pub vaapi_b_frames_area: Option<Rect>,
475    pub vaapi_loop_filter_level_area: Option<Rect>,
476    pub vaapi_loop_filter_sharpness_area: Option<Rect>,
477    pub hw_denoise_area: Option<Rect>,
478    pub hw_detail_area: Option<Rect>,
479
480    // Video codec selector area
481    pub video_codec_area: Option<Rect>,
482
483    // AV1 software areas
484    pub av1_preset_slider_area: Option<Rect>,
485    pub av1_tune_area: Option<Rect>,
486    pub av1_film_grain_slider_area: Option<Rect>,
487    pub av1_film_grain_denoise_checkbox_area: Option<Rect>,
488    pub av1_enable_overlays_checkbox_area: Option<Rect>,
489    pub av1_scd_checkbox_area: Option<Rect>,
490    pub av1_scm_area: Option<Rect>,
491    pub av1_enable_tf_checkbox_area: Option<Rect>,
492
493    // AV1 hardware areas
494    pub av1_hw_preset_area: Option<Rect>,
495    pub av1_hw_cq_slider_area: Option<Rect>,
496    pub av1_hw_lookahead_area: Option<Rect>,
497    pub av1_hw_tile_cols_area: Option<Rect>,
498    pub av1_hw_tile_rows_area: Option<Rect>,
499
500    // VP9 QSV areas
501    pub vp9_qsv_preset_area: Option<Rect>,
502    pub vp9_qsv_lookahead_checkbox_area: Option<Rect>,
503    pub vp9_qsv_lookahead_depth_area: Option<Rect>,
504
505    // Auto-VMAF areas
506    pub auto_vmaf_checkbox_area: Option<Rect>,
507    pub auto_vmaf_target_area: Option<Rect>,
508    pub auto_vmaf_step_area: Option<Rect>,
509    pub auto_vmaf_max_attempts_area: Option<Rect>,
510
511    // Additional args area
512    pub additional_args_area: Option<Rect>,
513
514    // Status message (message text, timestamp when shown)
515    pub status_message: Option<(String, Instant)>,
516}
517
518impl Default for ConfigState {
519    fn default() -> Self {
520        let mut profile_list_state = ListState::default();
521        profile_list_state.select(Some(0));
522
523        let mut quality_mode_state = ListState::default();
524        quality_mode_state.select(Some(0)); // good
525
526        let mut profile_dropdown_state = ListState::default();
527        profile_dropdown_state.select(Some(0)); // Profile 0 (8-bit)
528
529        let mut pix_fmt_state = ListState::default();
530        pix_fmt_state.select(Some(0)); // yuv420p (8-bit)
531
532        let mut aq_mode_state = ListState::default();
533        aq_mode_state.select(Some(2)); // Variance AQ (recommended for VOD)
534
535        let mut tune_content_state = ListState::default();
536        tune_content_state.select(Some(0)); // default
537
538        let mut colorspace_preset_state = ListState::default();
539        colorspace_preset_state.select(Some(0)); // Auto preset
540
541        let mut arnr_type_state = ListState::default();
542        arnr_type_state.select(Some(0)); // Auto
543
544        let mut fps_dropdown_state = ListState::default();
545        fps_dropdown_state.select(Some(0)); // Source
546
547        let mut resolution_dropdown_state = ListState::default();
548        resolution_dropdown_state.select(Some(0)); // Source
549
550        let mut container_dropdown_state = ListState::default();
551        container_dropdown_state.select(Some(0)); // webm
552
553        // Video codec selection (AV1 by default)
554        let mut video_codec_state = ListState::default();
555        video_codec_state.select(Some(1)); // AV1
556
557        // AV1 software settings
558        let mut av1_tune_state = ListState::default();
559        av1_tune_state.select(Some(0)); // Visual Quality
560
561        let mut av1_scm_state = ListState::default();
562        av1_scm_state.select(Some(0)); // Off
563
564        Self {
565            focus: ConfigFocus::default(),
566            profile_list_state,
567            quality_mode_state,
568            profile_dropdown_state,
569            pix_fmt_state,
570            aq_mode_state,
571            tune_content_state,
572            colorspace_preset_state,
573            arnr_type_state,
574            fps_dropdown_state,
575            resolution_dropdown_state,
576            container_dropdown_state,
577
578            // Video codec selection
579            video_codec_state,
580            codec_selection: CodecSelection::Av1,
581
582            // AV1 software settings
583            av1_preset: 8,
584            av1_tune_state,
585            av1_film_grain: 0,
586            av1_film_grain_denoise: false,
587            av1_enable_overlays: false,
588            av1_scd: true,
589            av1_scm_state,
590            av1_enable_tf: true,
591
592            // AV1 hardware settings
593            av1_hw_preset: 4, // Default: 4 (Balanced)
594            av1_hw_cq: 30,    // Legacy fallback
595
596            // Per-encoder quality defaults (calibrated for balanced quality)
597            av1_svt_crf: 28,   // SVT-AV1 default CRF
598            av1_qsv_cq: 65,    // Intel QSV balanced
599            av1_nvenc_cq: 16,  // NVIDIA (65/255*63 ≈ 16)
600            av1_vaapi_cq: 65,  // VAAPI same as QSV
601
602            av1_hw_lookahead: 0,
603            av1_hw_tile_cols: 0,
604            av1_hw_tile_rows: 0,
605
606            // Profile tracking
607            current_profile_name: Some("YouTube 4K".to_string()), // Default profile
608            is_modified: false,
609            available_profiles: Vec::new(), // Will be loaded on first render
610
611            // General settings
612            output_dir: std::env::current_dir()
613                .ok()
614                .and_then(|p| p.to_str().map(|s| s.to_string()))
615                .unwrap_or_else(|| ".".to_string()),
616            overwrite: true,
617            max_workers: 1, // Default to sequential processing
618
619            // Filename customization
620            filename_pattern: "{basename}".to_string(),
621
622            // Additional FFmpeg arguments (empty by default)
623            additional_args: String::new(),
624
625            // Video output constraints
626            fps: 0,           // Source (no fps limit)
627            scale_width: -2,  // Source (no resolution limit)
628            scale_height: -2, // Source (no resolution limit)
629
630            // Rate control (defaults for CQ mode)
631            rate_control_mode: RateControlMode::CQ,
632            crf: 30,
633            video_target_bitrate: 0,
634            video_min_bitrate: 0,
635            video_max_bitrate: 0,
636            video_bufsize: 0,
637            undershoot_pct: -1, // Auto
638            overshoot_pct: -1,  // Auto
639
640            // Speed & quality (good defaults for VOD)
641            cpu_used: 1,       // Used when two_pass is false
642            cpu_used_pass1: 4, // Fast analysis for pass 1
643            cpu_used_pass2: 1, // High quality for pass 2
644            two_pass: true,    // Strongly recommended for VOD quality
645
646            // Parallelism
647            row_mt: true,
648            tile_columns: 2, // Good for 1080p
649            tile_rows: 0,
650            threads: 0, // Auto
651            frame_parallel: false,
652
653            // GOP & keyframes (10 seconds at 25fps = 240)
654            gop_length: "240".to_string(),
655            keyint_min: "0".to_string(), // Auto (0 means no minimum constraint)
656            fixed_gop: false,
657            lag_in_frames: 25,
658            auto_alt_ref: 1,
659
660            // Alt-ref denoising (ARNR)
661            arnr_max_frames: 7,
662            arnr_strength: 3,
663            arnr_type: -1, // Auto
664
665            // Advanced tuning
666            enable_tpl: true, // Recommended for 2-pass efficiency
667            sharpness: -1,    // Auto
668            noise_sensitivity: 0,
669            static_thresh: "0".to_string(), // Disabled (no block skipping)
670            max_intra_rate: "0".to_string(), // Disabled (no I-frame bitrate cap)
671
672            // Color / HDR settings (all Auto by default)
673            colorspace: -1,
674            color_primaries: -1,
675            color_trc: -1,
676            color_range: -1,
677            colorspace_preset: ColorSpacePreset::Auto,
678
679            // Audio settings - multi-track
680            audio_primary_codec: AudioPrimaryCodec::Opus,
681            audio_primary_codec_state: {
682                let mut state = ListState::default();
683                state.select(Some(1)); // Opus is index 1
684                state
685            },
686            audio_primary_bitrate: 128,
687            audio_primary_downmix: false,
688            audio_add_ac3: false,
689            audio_ac3_bitrate: 448,
690            audio_add_stereo: false,
691            audio_stereo_codec: AudioStereoCodec::Aac,
692            audio_stereo_codec_state: {
693                let mut state = ListState::default();
694                state.select(Some(0)); // AAC
695                state
696            },
697            audio_stereo_bitrate: 128,
698
699            // Hardware encoding settings
700            use_hardware_encoding: false,
701            hw_encoding_available: None,
702            hw_availability_message: None,
703            gpu_vendor: crate::engine::hardware::GpuVendor::Unknown,
704            qsv_global_quality: 70,
705            vaapi_rc_mode: "1".to_string(), // CQP mode (only supported mode)
706            vaapi_compression_level: "4".to_string(),
707            vaapi_b_frames: "0".to_string(),
708            vaapi_loop_filter_level: "16".to_string(),
709            vaapi_loop_filter_sharpness: "4".to_string(),
710            hw_denoise: "0".to_string(),
711            hw_detail: "0".to_string(),
712
713            // VP9 QSV controls
714            vp9_qsv_preset: 4,
715            vp9_qsv_lookahead: true,
716            vp9_qsv_lookahead_depth: 40,
717
718            // Auto-VAMF settings (disabled by default)
719            auto_vmaf_enabled: false,
720            auto_vmaf_target: "93.0".to_string(),
721            auto_vmaf_step: "2".to_string(),
722            auto_vmaf_max_attempts: "3".to_string(),
723
724            // Popup dropdown state
725            active_dropdown: None,
726
727            // Profile name input dialog
728            name_input_dialog: None,
729
730            // Text input mode (Normal = navigation, Editing = text entry)
731            input_mode: InputMode::Normal,
732
733            // Cursor position for text inputs
734            cursor_pos: 0,
735
736            // Mouse support - all None by default
737            overwrite_checkbox_area: None,
738            two_pass_checkbox_area: None,
739            row_mt_checkbox_area: None,
740            frame_parallel_checkbox_area: None,
741            fixed_gop_checkbox_area: None,
742            auto_alt_ref_checkbox_area: None,
743            enable_tpl_checkbox_area: None,
744            save_button_area: None,
745            delete_button_area: None,
746            output_dir_area: None,
747            filename_pattern_area: None,
748            container_dropdown_area: None,
749            fps_area: None,
750            scale_width_area: None,
751            scale_height_area: None,
752            profile_list_area: None,
753            quality_mode_area: None,
754            vp9_profile_list_area: None,
755            pix_fmt_area: None,
756            aq_mode_area: None,
757            tune_content_area: None,
758            rate_control_mode_area: None,
759            crf_slider_area: None,
760            cpu_used_slider_area: None,
761            cpu_used_pass1_slider_area: None,
762            cpu_used_pass2_slider_area: None,
763            tile_columns_slider_area: None,
764            tile_rows_slider_area: None,
765            threads_area: None,
766            max_workers_area: None,
767            gop_length_area: None,
768            keyint_min_area: None,
769            lag_in_frames_slider_area: None,
770            arnr_max_frames_slider_area: None,
771            arnr_strength_slider_area: None,
772            sharpness_slider_area: None,
773            noise_sensitivity_slider_area: None,
774            video_target_bitrate_area: None,
775            video_min_bitrate_area: None,
776            video_max_bitrate_area: None,
777            video_bufsize_area: None,
778            // Audio areas
779            audio_primary_codec_area: None,
780            audio_primary_bitrate_area: None,
781            audio_primary_downmix_area: None,
782            audio_ac3_checkbox_area: None,
783            audio_ac3_bitrate_area: None,
784            audio_stereo_checkbox_area: None,
785            audio_stereo_codec_area: None,
786            audio_stereo_bitrate_area: None,
787            colorspace_preset_area: None,
788            arnr_type_area: None,
789            static_thresh_area: None,
790            max_intra_rate_area: None,
791            undershoot_pct_area: None,
792            overshoot_pct_area: None,
793
794            // Hardware encoding areas
795            hw_encoding_checkbox_area: None,
796            qsv_quality_slider_area: None,
797            vaapi_compression_level_slider_area: None,
798            vaapi_b_frames_area: None,
799            vaapi_loop_filter_level_area: None,
800            vaapi_loop_filter_sharpness_area: None,
801            hw_denoise_area: None,
802            hw_detail_area: None,
803
804            // Video codec selector area
805            video_codec_area: None,
806
807            // AV1 software areas
808            av1_preset_slider_area: None,
809            av1_tune_area: None,
810            av1_film_grain_slider_area: None,
811            av1_film_grain_denoise_checkbox_area: None,
812            av1_enable_overlays_checkbox_area: None,
813            av1_scd_checkbox_area: None,
814            av1_scm_area: None,
815            av1_enable_tf_checkbox_area: None,
816
817            // AV1 hardware areas
818            av1_hw_preset_area: None,
819            av1_hw_cq_slider_area: None,
820            av1_hw_lookahead_area: None,
821            av1_hw_tile_cols_area: None,
822            av1_hw_tile_rows_area: None,
823
824            // VP9 QSV areas
825            vp9_qsv_preset_area: None,
826            vp9_qsv_lookahead_checkbox_area: None,
827            vp9_qsv_lookahead_depth_area: None,
828
829            // Auto-VMAF areas
830            auto_vmaf_checkbox_area: None,
831            auto_vmaf_target_area: None,
832            auto_vmaf_step_area: None,
833            auto_vmaf_max_attempts_area: None,
834
835            // Additional args area
836            additional_args_area: None,
837
838            status_message: None,
839        }
840    }
841}
842
843impl ConfigState {
844    /// Load available profiles from disk and update the cached list
845    pub fn refresh_available_profiles(&mut self) {
846        use crate::engine::Profile;
847
848        if let Ok(profiles_dir) = Profile::profiles_dir() {
849            if let Ok(saved_profiles) = Profile::list_saved(&profiles_dir) {
850                self.available_profiles = saved_profiles;
851            }
852        }
853    }
854}
855
856#[cfg(test)]
857mod tests {
858    use super::*;
859
860    #[test]
861    fn test_all_list_states_initialized() {
862        let state = ConfigState::default();
863
864        // Check that all list states have valid initial selections
865        assert!(state.profile_list_state.selected().is_some());
866        assert!(state.quality_mode_state.selected().is_some());
867        assert!(state.profile_dropdown_state.selected().is_some());
868        assert!(state.audio_primary_codec_state.selected().is_some());
869        assert!(state.audio_stereo_codec_state.selected().is_some());
870        assert!(state.pix_fmt_state.selected().is_some());
871        assert!(state.aq_mode_state.selected().is_some());
872        assert!(state.tune_content_state.selected().is_some());
873    }
874
875    #[test]
876    fn test_dropdown_item_counts() {
877        // Validate that item counts match expectations
878        let profiles = vec!["YouTube 4K", "Archival", "Low Latency", "Create New..."];
879        let quality_modes = vec!["good", "realtime", "best"];
880        let vp9_profiles = vec![
881            "Profile 0 (8-bit)",
882            "Profile 1 (8-bit)",
883            "Profile 2 (10-bit)",
884            "Profile 3 (10-bit)",
885        ];
886        let primary_codecs = vec!["Passthrough", "Opus", "AAC", "MP3", "Vorbis"];
887        let stereo_codecs = vec!["AAC", "Opus"];
888        let pix_fmts = vec!["yuv420p (8-bit)", "yuv420p10le (10-bit)"];
889        let aq_modes = vec![
890            "Auto",
891            "Off",
892            "Variance",
893            "Complexity",
894            "Cyclic",
895            "360 Video",
896        ];
897        let tune_contents = vec!["default", "screen", "film"];
898
899        assert_eq!(profiles.len(), 4);
900        assert_eq!(quality_modes.len(), 3);
901        assert_eq!(vp9_profiles.len(), 4);
902        assert_eq!(primary_codecs.len(), 5);
903        assert_eq!(stereo_codecs.len(), 2);
904        assert_eq!(pix_fmts.len(), 2);
905        assert_eq!(aq_modes.len(), 6);
906        assert_eq!(tune_contents.len(), 3);
907    }
908}