@@ -62,16 +62,16 @@ def run_ffprobe_for_audio_streams(video_file):
6262 - Includes index, codec_name, and language (if available).
6363 """
6464 cmd = [
65- "ffprobe" , "-v" , "error" , "-select_streams" , "a" ,
65+ "ffprobe" , "-v" , "error" , "-select_streams" , "a" ,
6666 "-show_entries" , "stream=index,codec_name:stream_tags=language" ,
6767 "-of" , "json" , video_file
6868 ]
6969 try :
7070 output = subprocess .check_output (
71- cmd ,
72- stderr = subprocess .STDOUT ,
73- text = True ,
74- encoding = 'utf-8' ,
71+ cmd ,
72+ stderr = subprocess .STDOUT ,
73+ text = True ,
74+ encoding = 'utf-8' ,
7575 errors = 'replace'
7676 )
7777 except subprocess .CalledProcessError :
@@ -115,19 +115,20 @@ def get_video_duration(video_file):
115115 duration = frame_count / fps if fps else None
116116 cap .release ()
117117 return duration
118- def get_crop_parameters (video_file , input_width , input_height ):
118+
119+ def get_crop_parameters (video_file , input_width , input_height , limit_value ):
119120 """
120121 Detect optimal crop parameters for a video using ffmpeg.
121122 - Analyzes frames at intervals and detects cropping areas.
123+ - The 'limit_value' is determined based on HDR vs. SDR.
122124 """
123125 print ("Detecting optimal crop parameters throughout the video..." )
124126 duration = get_video_duration (video_file )
125127 if duration is None or duration < 1 :
126128 print ("Unable to determine video duration or video is too short." )
127129 return None , None , None , None
128130
129- default_limit = "48"
130- limit_value = default_limit
131+ # We no longer set 'default_limit' here directly; we use 'limit_value' passed in.
131132 default_round = "4"
132133 round_value = default_round
133134
@@ -188,6 +189,7 @@ def get_crop_parameters(video_file, input_width, input_height):
188189 w , h , x , y = input_width , input_height , 0 , 0 # Default to full frame if none detected
189190
190191 return w , h , x , y
192+
191193def add_files (file_listbox ):
192194 """
193195 Open a file dialog to add video files to the listbox.
@@ -227,11 +229,12 @@ def move_down(file_listbox):
227229 file_listbox .insert (idx + 1 , value )
228230 file_listbox .select_set (idx + 1 )
229231
230- def launch_gui (file_list , crop_params , audio_streams ):
232+ def launch_gui (file_list , crop_params , audio_streams , default_qvbr ):
231233 """
232234 Launch the GUI for configuring video processing options.
233235 - Displays the color-related metadata for selected videos.
234236 - Allows users to modify settings and start processing.
237+ - 'default_qvbr' is determined by 4K or non-4K resolution.
235238 """
236239 # Initialize main window
237240 root = tk .Tk ()
@@ -344,13 +347,15 @@ def on_file_select(event):
344347 artifact_enable = tk .BooleanVar ()
345348 tk .Checkbutton (options_frame , text = "Enable Artifact Reduction" , variable = artifact_enable ).pack (anchor = 'w' )
346349
347- qvbr = tk .StringVar (value = "20" )
350+ # Use the default_qvbr determined by resolution
351+ qvbr = tk .StringVar (value = default_qvbr )
348352 tk .Label (options_frame , text = "Enter target QVBR:" ).pack (anchor = 'w' )
349353 tk .Entry (options_frame , textvariable = qvbr ).pack (anchor = 'w' )
350354
351355 gop_len = tk .StringVar (value = "6" )
352356 tk .Label (options_frame , text = "Enter GOP length:" ).pack (anchor = 'w' )
353357 tk .Entry (options_frame , textvariable = gop_len ).pack (anchor = 'w' )
358+
354359 # Crop parameters section
355360 crop_frame = tk .LabelFrame (root , text = "Crop Parameters (Modify if needed)" )
356361 crop_frame .grid (row = 2 , column = 1 , padx = 10 , pady = 10 , sticky = "nsew" )
@@ -372,7 +377,7 @@ def on_file_select(event):
372377 tk .Label (crop_frame , text = "Y Offset:" ).grid (row = 3 , column = 0 , sticky = "w" )
373378 tk .Entry (crop_frame , textvariable = crop_y ).grid (row = 3 , column = 1 , sticky = "ew" )
374379
375- # Populate detected crop parameters
380+ # Populate detected crop parameters for the FIRST file
376381 detected_crop = crop_params [0 ] if crop_params else {}
377382 crop_w .set (detected_crop .get ("crop_w" , 0 ))
378383 crop_h .set (detected_crop .get ("crop_h" , 0 ))
@@ -508,6 +513,7 @@ def process_video(file_path, settings):
508513 if settings ['artifact_enable' ]:
509514 command .append ("--vpp-nvvfx-artifact-reduction" )
510515
516+ # Apply crop if provided
511517 if settings .get ("crop_params" ):
512518 crop = settings ["crop_params" ]
513519 left = crop ['crop_x' ]
@@ -540,6 +546,7 @@ def process_video(file_path, settings):
540546 log .write (f"Processing file: { file_path } \n " )
541547 log .write (f"Output file: { output_file } \n " )
542548 log .write (f"Status: { status } \n " )
549+
543550def process_batch (video_files , settings ):
544551 """
545552 Process a batch of video files with the provided settings.
@@ -554,28 +561,58 @@ def process_batch(video_files, settings):
554561 sys .exit ()
555562
556563 video_files = sys .argv [1 :]
557- detected_crop_params = []
564+
565+ # --- STEP A: Detect color-related metadata on the first file ---
566+ first_file = video_files [0 ]
567+ color_range , color_primaries , color_transfer , color_space , mastering_display_metadata = get_video_color_info (first_file )
568+
569+ # Decide default_limit based on HDR or SDR
570+ # For simplicity, any mention of "2020" or "2084" sets it to '64'
571+ if color_primaries and '2020' in color_primaries .lower ():
572+ default_limit = '64'
573+ elif color_transfer and '2084' in color_transfer .lower ():
574+ default_limit = '64'
575+ else :
576+ default_limit = '24'
558577
559- for video_file in video_files :
560- input_height , input_width = get_video_resolution (video_file )
561- if input_height is None or input_width is None :
562- print (f"Error: Could not retrieve resolution for { video_file } . Skipping." )
563- continue
578+ # --- STEP B: Detect video size (first file) for default QVBR ---
579+ first_input_height , first_input_width = get_video_resolution (first_file )
580+ if first_input_height is None or first_input_width is None :
581+ print (f"Error: Could not retrieve resolution for { first_file } . Exiting." )
582+ sys .exit ()
583+
584+ # If resolution is 4K or larger, default QVBR=30, otherwise 20
585+ if (first_input_height >= 2160 ) and (first_input_width >= 3840 ):
586+ default_qvbr = "30"
587+ else :
588+ default_qvbr = "20"
589+
590+ # --- STEP C: Run cropdetect only on first file, using the chosen limit value ---
591+ crop_w , crop_h , crop_x , crop_y = get_crop_parameters (
592+ first_file ,
593+ first_input_width ,
594+ first_input_height ,
595+ limit_value = default_limit
596+ )
564597
565- crop_w , crop_h , crop_x , crop_y = get_crop_parameters (video_file , input_width , input_height )
598+ # Prepare a list of dicts for each file, but all share the same crop
599+ detected_crop_params = []
600+ for vf in video_files :
566601 detected_crop_params .append ({
567- "file" : video_file ,
602+ "file" : vf ,
568603 "crop_w" : crop_w ,
569604 "crop_h" : crop_h ,
570605 "crop_x" : crop_x ,
571606 "crop_y" : crop_y
572607 })
573608
574- print ("\n Crop detection complete. Launching GUI for additional settings...\n " )
575- all_audio_streams = [
576- stream for file in detected_crop_params for stream in run_ffprobe_for_audio_streams (file ["file" ])
577- ]
578- launch_gui ([d ["file" ] for d in detected_crop_params ], detected_crop_params , all_audio_streams )
609+ print ("\n Crop detection complete (only done for the first file). Launching GUI for additional settings...\n " )
610+
611+ # --- STEP D: Run ffprobe for audio streams only on the first file
612+ all_audio_streams = run_ffprobe_for_audio_streams (first_file )
613+
614+ # --- STEP E: Launch the GUI with a single set of audio/crop defaults ---
615+ launch_gui ([d ["file" ] for d in detected_crop_params ], detected_crop_params , all_audio_streams , default_qvbr )
579616
580617 print ("All processing complete. Press any key to exit..." )
581618 input () # Cross-platform wait for any key
0 commit comments