Skip to content

Commit a2e68e4

Browse files
FEAT: Automatically filter and remove undesired audio tracks by language in Default Audio Track Switcher
1 parent ebf76da commit a2e68e4

File tree

1 file changed

+72
-36
lines changed
  • Default Audio Track Switcher

1 file changed

+72
-36
lines changed

Default Audio Track Switcher/main.py

Lines changed: 72 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,34 +6,33 @@
66
Created : 2025-10-26
77
Description :
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
2221
Usage:
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
2928
Outputs:
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
3231
TODOs:
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
3837
Dependencies:
3938
- Python >= 3.9
@@ -73,6 +72,11 @@ class BackgroundColors: # Colors for the terminal
7372
VIDEO_FILE_EXTENSIONS = [".mkv", ".mp4", ".avi"] # List of video file extensions to process
7473
REMOVE_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:
7781
SOUND_COMMANDS = {"Darwin": "afplay", "Linux": "aplay", "Windows": "start"} # The commands to play a sound for each operating system
7882
SOUND_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+
101114
def 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

387403
def 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

416452
def play_sound():
417453
"""

0 commit comments

Comments
 (0)