66Created : 2025-10-26
77Description :
88 This script recursively searches for video files in the specified input directory
9- and sets the default audio track, optionally removing other audio tracks.
10- When more than two audio tracks are detected, the script prompts the user to
11- manually select which one should become the default .
9+ and processes their audio tracks: keeps only tracks in desired languages (from DESIRED_LANGUAGES),
10+ sets the default audio track to English if available, otherwise the first desired track.
11+ Undesired audio tracks are automatically removed .
1212
1313 Key features include:
1414 - Recursive search for video files with specified extensions
15- - Automatic detection of audio tracks using ffprobe
16- - Setting the default audio track (prioritizing English tracks)
17- - Optional removal of other audio tracks after setting the default
18- - Manual selection of default track for videos with more than two audio tracks
15+ - Automatic detection and filtering of audio tracks by desired languages
16+ - Removal of undesired audio tracks
17+ - Setting the default audio track to English if available
1918 - Progress bar visualization for processing
2019 - Integration with ffmpeg for audio track manipulation
2120
2221Usage:
2322 1. Set the INPUT_DIR constant to the folder containing your video files.
24- 2. Optionally set REMOVE_OTHER_AUDIO_TRACKS to True to remove other audio tracks after setting the default .
23+ 2. Modify DESIRED_LANGUAGES to include the languages you want to keep .
2524 3. Execute the script:
26- $ python swap_audio_tracks .py
27- 4. Select the desired audio track when prompted (if more than two exist) .
25+ $ python main .py
26+ 4. The script will automatically keep only desired language tracks and set English as default if present .
2827
2928Outputs:
30- - Video files in the same directory with swapped or updated default audio track
29+ - Video files in the same directory with only desired language audio tracks, English set as default if present
3130
3231TODOs:
3332 - Add a dry-run mode to preview changes without modifying files
3433 - Add logging of processed files and errors
35- - Support automatic language-based selection (e.g., always set “eng” as default)
36- - Optimize for batch operations without manual input
34+ - Allow customization of default language priority
35+ - Optimize for batch operations
3736
3837Dependencies:
3938 - Python >= 3.9
@@ -73,6 +72,11 @@ class BackgroundColors: # Colors for the terminal
7372VIDEO_FILE_EXTENSIONS = [".mkv" , ".mp4" , ".avi" ] # List of video file extensions to process
7473REMOVE_OTHER_AUDIO_TRACKS = False # Set to True to remove other audio tracks after setting the default
7574
75+ DESIRED_LANGUAGES = {
76+ "English" : ["english" , "eng" , "en" ], # Languages to prioritize when selecting default audio track
77+ "Brazilian Portuguese" : ["brazilian" , "portuguese" , "pt-br" , "pt" ] # Additional languages can be added here
78+ }
79+ ,
7680# Sound Constants:
7781SOUND_COMMANDS = {"Darwin" : "afplay" , "Linux" : "aplay" , "Windows" : "start" } # The commands to play a sound for each operating system
7882SOUND_FILE = "./.assets/Sounds/NotificationSound.wav" # The path to the sound file
@@ -98,6 +102,15 @@ def verbose_output(true_string="", false_string=""):
98102 elif false_string != "" : # If the false_string is set
99103 print (false_string ) # Output the false statement string
100104
105+ def get_desired_languages ():
106+ """
107+ Get a flat list of all desired languages from DESIRED_LANGUAGES.
108+
109+ :return: List of desired language codes
110+ """
111+
112+ return [lang for langs in DESIRED_LANGUAGES .values () for lang in langs ] # Flatten the list of desired languages
113+
101114def install_chocolatey ():
102115 """
103116 Installs Chocolatey on Windows if it is not already installed.
@@ -273,7 +286,7 @@ def is_english_track_default(audio_tracks):
273286 if len (track_info ) >= 3 : # If track info has enough parts
274287 language = track_info [2 ].lower ().strip () if len (track_info [2 ].strip ()) > 0 else "und" # Get language or "und"
275288 is_default = track_info [1 ].strip () == "1" # Check if track is default
276- if is_default and language in [ "english " , "eng" ] : # If English track is already default, nothing to do
289+ if is_default and language in DESIRED_LANGUAGES . get ( "English " , []) : # If default and language is English
277290 return True # English track is already default
278291
279292 return False # English track is not default
@@ -344,33 +357,36 @@ def determine_default_track(audio_tracks, video_path):
344357
345358 return 1 if num_tracks == 2 else 0 # For 1 or 2 tracks, swap only if there are 2 tracks
346359
347- def apply_audio_track_default (video_path , audio_tracks , default_track_index ):
360+ def apply_audio_track_default (video_path , audio_tracks , default_track_index , kept_indices ):
348361 """
349- Apply the default audio track disposition to the video file using ffmpeg.
350- Optionally removes other audio tracks if REMOVE_OTHER_AUDIO_TRACKS is True.
362+ Apply the default audio track disposition to the video file using ffmpeg, keeping only desired tracks.
351363
352364 :param video_path: Path to the video file
353365 :param audio_tracks: List of audio track strings
354366 :param default_track_index: Index of the track to set as default
367+ :param kept_indices: List of indices of tracks to keep
355368 :return: None
356369 """
357370
358- num_tracks = len (audio_tracks ) # Number of audio tracks
359-
360371 root , ext = os .path .splitext (video_path ) # Split the file path and extension
361372 ext = ext .lower () # Ensure lowercase extension
362373 video_path = video_path if video_path .endswith (ext ) else os .rename (video_path , root + ext ) or (root + ext ) # Rename if needed
363374 temp_file = root + ".tmp" + ext # Temporary file path with correct extension order
364375
365- if REMOVE_OTHER_AUDIO_TRACKS : # If removing other audio tracks
366- cmd = ["ffmpeg" , "-y" , "-i" , video_path , "-map" , "0:v" , "-map" , f"0:a:{ default_track_index } " , "-c" , "copy" , temp_file ]
367- else : # If keeping all audio tracks
368- cmd = ["ffmpeg" , "-y" , "-i" , video_path , "-map" , "0" , "-c" , "copy" ] # Base ffmpeg command to copy all streams
376+ cmd = ["ffmpeg" , "-y" , "-i" , video_path , "-map" , "0:v" ] # Base command with video
369377
370- for i in range ( num_tracks ) : # For each audio track
371- cmd += ["-disposition:a:" + str ( i ), "0 " ] # Clear all dispositions
378+ for idx in kept_indices : # Map each kept audio track
379+ cmd += ["-map" , f"0:a: { idx } " ] # Map audio track
372380
373- cmd += ["-disposition:a:" + str (default_track_index ), "default" , temp_file ] # Set the selected track as default and define output file
381+ cmd += ["-c" , "copy" ] # Copy codecs
382+
383+ for i , idx in enumerate (kept_indices ): # Set dispositions
384+ if idx == default_track_index : # If this is the default track
385+ cmd += ["-disposition:a:" + str (i ), "default" ] # Set as default
386+ else : # If this is not the default track
387+ cmd += ["-disposition:a:" + str (i ), "0" ] # Unset default
388+
389+ cmd += [temp_file ] # Output file
374390
375391 verbose_output (f"{ BackgroundColors .GREEN } Executing ffmpeg command:{ BackgroundColors .CYAN } { ' ' .join (cmd )} { Style .RESET_ALL } " ) # Output the ffmpeg command if verbose is True
376392
@@ -386,32 +402,52 @@ def apply_audio_track_default(video_path, audio_tracks, default_track_index):
386402
387403def swap_audio_tracks (video_path ):
388404 """
389- Swap the default audio track in the video with the non-default track.
390- Automatically detects and prioritizes English audio tracks.
405+ Process the audio tracks in the video: keep only desired languages, set English as default if available.
391406 Requires ffmpeg and ffprobe installed and available in PATH.
392407
393408 :param video_path: Path to the video file
394409 :return: None
395410 """
396411
397- verbose_output (f"{ BackgroundColors .GREEN } Swapping audio tracks for video: { BackgroundColors .CYAN } { video_path } { Style .RESET_ALL } " ) # Output the verbose message
412+ verbose_output (f"{ BackgroundColors .GREEN } Processing audio tracks for video: { BackgroundColors .CYAN } { video_path } { Style .RESET_ALL } " ) # Output the verbose message
398413
399414 audio_tracks = get_audio_track_info (video_path ) # Get audio track information using ffprobe
400415
401416 if len (audio_tracks ) == 0 : # Check if any audio tracks were found
402417 print (f"{ BackgroundColors .YELLOW } No audio tracks found for: { BackgroundColors .CYAN } { video_path } { Style .RESET_ALL } " )
403418 return # Skip this file
404419
405- if is_english_track_default (audio_tracks ): # Check if English track is already the default
406- verbose_output (f"{ BackgroundColors .GREEN } English audio track is already default for: { BackgroundColors .CYAN } { video_path } { Style .RESET_ALL } " )
407- return # Skip this file
408-
409- default_track_index = determine_default_track (audio_tracks , video_path ) # Determine which track should be set as default
420+ desired_langs = get_desired_languages () # Get list of desired languages
421+ kept_indices = [] # Indices of tracks to keep
410422
411- if default_track_index is None : # If no valid track was selected, skip this file
423+ for i , track in enumerate (audio_tracks ): # For each audio track
424+ parts = track .split ("," ) # Split the track info
425+ if len (parts ) >= 3 : # If track info has enough parts
426+ lang = parts [2 ].lower ().strip () # Get language
427+ if lang in desired_langs : # If language is desired
428+ kept_indices .append (i ) # Keep this track
429+
430+ if not kept_indices : # If no desired tracks found
431+ print (f"{ BackgroundColors .YELLOW } No desired audio tracks found for: { BackgroundColors .CYAN } { video_path } { Style .RESET_ALL } " )
412432 return # Skip this file
413433
414- apply_audio_track_default (video_path , audio_tracks , default_track_index ) # Apply the default disposition to the selected track
434+ english_langs = DESIRED_LANGUAGES .get ("English" , []) # Get English language codes
435+ english_index = None # Index of English track if found
436+ for i in kept_indices : # For each kept track
437+ parts = audio_tracks [i ].split ("," ) # Split the track info
438+ lang = parts [2 ].lower ().strip () # Get language
439+ if lang in english_langs : # If language is English
440+ english_index = i # Set English track index
441+ break # Stop searching
442+
443+ if english_index is not None : # If English track found
444+ default_track_index = english_index # Set English as default
445+ print (f"{ BackgroundColors .GREEN } Automatically selected English audio track for: { BackgroundColors .CYAN } { video_path } { Style .RESET_ALL } " )
446+ else : # No English, use first kept
447+ default_track_index = kept_indices [0 ] # Set first desired track as default
448+ print (f"{ BackgroundColors .GREEN } Selected first desired audio track as default for: { BackgroundColors .CYAN } { video_path } { Style .RESET_ALL } " )
449+
450+ apply_audio_track_default (video_path , audio_tracks , default_track_index , kept_indices ) # Apply the changes
415451
416452def play_sound ():
417453 """
0 commit comments