• AnyStream is having some DRM issues currently, Netflix is not available in HD for the time being.
    Situations like this will always happen with AnyStream: streaming providers are continuously improving their countermeasures while we try to catch up, it's an ongoing cat-and-mouse game. Please be patient and don't flood our support or forum with requests, we are working on it 24/7 to get it resolved. Thank you.

Subtitles

Here's the bash script I use locally, tailored to my habits and needs:
1. I always grab the "original" audio at best resolution
2. If descriptive audio is available, I'll grab that, too, at the lowest resolution, and tack " - Descriptive Audio" to the filename
3. I snag all the english subs.

This script scans the current working directory for MP4 files (ignoring descriptive audio), then matches a descriptive audio MP4 if found, then looks for forced subs, then subs, then CC subs, muxes the whole thing, then does some property edits. There are optional args to change the audio track naming (-a for normal audio, -d for descriptive).

EDIT: This doesn't handle subdirectories at all. Just a lot of errors...
EDIT: Updated to limit find to the current directory

It's pretty specific to how I like things named and ordered, but it isn't hard to customize it. Works as written in gitbash on Windows, but would probably work on linux or osx with a change to the mkvtoolnix base path.

Example - mux everything, using "Surround" as the default audio track name and "Descriptive" for any descriptive audio found:
Code:
mux.sh -a "Surround" -d "Descriptive"

Code:
#!/bin/bash

mkvtoolnix='/c/Program Files/MKVToolNix'
mkvmerge="${mkvtoolnix}/mkvmerge.exe"
mkvpropedit="${mkvtoolnix}/mkvpropedit.exe"


# Use -a {text} to override the default audio track name
default_audio="5.1 Dolby Digital"
# Use -d {text} to override the descriptive audio track name
descriptive_audio="Descriptive Audio"

while getopts "::a::d" options;
do
    case "${options}" in
        a)
            default_audio="${OPTARG}";;
        d)
            descriptive_audio="${OPTARG}";;
    esac
done

IFS=$'\n'
basenames=$(find . -maxdepth 1 -name '*.mp4' -and -not -name '* - Descriptive Audio.mp4' -exec basename {} .mp4 \;)
for f in $basenames
do
    # Assemble args for mkvmerge and mkvpropedit
    merge=("-o" "${f}.mkv" "${f}.mp4")
    propedit=("${f}.mkv" "-e" "track:a1" "-s" "name=${default_audio}" "-s" "flag-default=1")

    # Descriptive audio file, if any
    if [ -f "${f} - Descriptive Audio.mp4" ];
    then
        merge+=("-D" "${f} - Descriptive Audio.mp4")
        propedit+=("-e" "track:a2" "-s" "name=${descriptive_audio}" "-s" "flag-default=0")
    fi

    caption_codes=("en" "en-us" "en-US")
    sub_index=1

    # Forced captions - first match only
    for code in ${caption_codes[*]}
    do
        if [ -f "${f}.${code}.forced.srt" ];
        then
            merge+=("${f}.${code}.forced.srt")
            propedit+=("-e" "track:s${sub_index}" "-s" "name=Forced" "-s" "flag-default=0" "-s" "flag-forced=1" "-s" "language=${code}")
            sub_index=$((sub_index + 1))
            break
        elif [ -f "${f}.forced.${code}.srt" ];
        then
            merge+=("${f}.forced.${code}.srt")
            propedit+=("-e" "track:s${sub_index}" "-s" "name=Forced" "flag-default=0" "-s" "flag-forced=1" "-s" "language=${code}")
            sub_index=$((sub_index + 1))
            break
        fi
    done

    # Captions - include everything that matches
    for code in ${caption_codes[*]}
    do
        if [ -f "${f}.${code}.srt" ];
        then
            merge+=("${f}.${code}.srt")
            propedit+=("-e" "track:s${sub_index}" "-s" "flag-default=0" "-s" "language=${code}")
            sub_index=$((sub_index + 1))
        fi
    done

    # Closed captions - everything
    for code in ${caption_codes[*]}
    do
        if [ -f "${f}.${code}.cc.srt" ];
        then
            merge+=("${f}.${code}.cc.srt")
            propedit+=("-e" "track:s${sub_index}" "-s" "name=Closed Captions" "-s" "flag-default=0" "-s" "language=${code}")
            sub_index=$((sub_index + 1))
        fi
    done
         
    ${mkvmerge} "${merge[@]}"
    ${mkvpropedit} "${propedit[@]}"
done
 
Last edited:
Yeah I'm afraid there isn't a lot of consistency. Maybe this is going to be more work for you than I wanted it to be. :(

Looks like AP has en-us.cc.srt and en-us.forced.set
NF has en.cc.srt and en.forced.srt
D+ has en.cc.srt and en.forced.srt
HBO has en-US.cc.srt and unsure on forced, couldn't easily find an example
Hulu has en.srt

Is it possible the use wild cards like a * to cover it? I'm sure it's not that easy. LOL

But to answer your other question, being in the US and not being bilingual, everything seems come up as EN. So yeah the order I had listed should be fine: forced first, then non cc and then cc.

title.en.forced.srt
title.en.srt
title.en-us.srt
title.en.cc.srt
title.en-us.cc.srt
 
Yeah I'm afraid there isn't a lot of consistency. Maybe this is going to be more work for you than I wanted it to be. :(

Looks like AP has en-us.cc.srt and en-us.forced.set
NF has en.cc.srt and en.forced.srt
D+ has en.cc.srt and en.forced.srt
HBO has en-US.cc.srt and unsure on forced, couldn't easily find an example
Hulu has en.srt

Is it possible the use wild cards like a * to cover it? I'm sure it's not that easy. LOL

But to answer your other question, being in the US and not being bilingual, everything seems come up as EN. So yeah the order I had listed should be fine: forced first, then non cc and then cc.

title.en.forced.srt
title.en.srt
title.en-us.srt
title.en.cc.srt
title.en-us.cc.srt

Unfortunately, Windows has never been as good as Linux when it comes to file globbing (i.e., wildcards). It usually doesn't understand more than one asterisk (except for the infamous *.*). And batch files have very poor text processing abilities.

Your examples look easy enough as they all consistently use 2-letter language codes. If I stick to just English, it's trivial. Although, it hurt's my brain do hard-code stuff. :)

Let me think on it.
 
Unfortunately, Windows has never been as good as Linux when it comes to file globbing (i.e., wildcards). It usually doesn't understand more than one asterisk (except for the infamous *.*). And batch files have very poor text processing abilities.

Your examples look easy enough as they all consistently use 2-letter language codes. If I stick to just English, it's trivial. Although, it hurt's my brain do hard-code stuff. :)

Let me think on it.

I figured it wouldn't that easy. It never is. :LOL: I can totally understand hardcoding making it more difficult. Or at least narrowing things a lot.

Oh and just in case it exists, I didn't mention en-us.forced.srt LOL Hopefully it doesn't matter between en-us and en-US because then I really will feel bad. :banghead:
 
I figured it wouldn't that easy. It never is. :LOL: I can totally understand hardcoding making it more difficult. Or at least narrowing things a lot.

Oh and just in case it exists, I didn't mention en-us.forced.srt LOL Hopefully it doesn't matter between en-us and en-US because then I really will feel bad. :banghead:

Hard-coding is actually easier, but it's frown upon because it makes code less maintainable and less usable to others (reuse).

Case doesn't matter (especially since Windows' filesystems are not case sensitive).

I am making this for you, but I didn't want to make it so constrained, no one else could use it.

what about this for starters...

I could start by iterating over language codes. That's would allow you (the user) to enforce an order by configuring what codes to use and in what order (that way, it would also handle 3-letter codes). As a degenerate case, it will iterator over one: "en"

What do I do with tracks that either don't have a language code, or aren't in the set we're iterating over?

We could also iterator over country code, but I don't think that's necessary. (and it's already going to process in O(n^2) time because of the nested loops needed. I don't want to make it O(n^3). Yuck!). I'll just look for the language code being followed by a dash, and call it a day.

Also, lieu of iterating over country code, we just add various patterns to the language set: en, eng, en-us, eng-us, etc. That would basically collapse the two loops into one.

Then it could use wildcards to look for the various name patterns in the order you defined. It would require one for loop for each pattern, but I don't foresee there being more than a few files, so the inefficiency shouldn't be noticeable.

Also: I'm making a BIG assumption that mkvmerge will put the tracks in the order they are defined on the command line. It seems to, but that isn't explicating stated in the doc. I could enforce the ordering, but that would require knowing how many tracks are already in the mp4 file. Without making you install other tools besides mkvtoolnix, it would have to mux twice (another yuck!).

Thoughts?
 
Last edited:
OMG, I think I just had a epiphany.

What if I don't explicitly look the patterns, but put all the patterns, codes, etc., in one set and just iterate over that. That way, you can put whatever combos you want in it. I'll just look for them following the title, in the order you've defined. one single loop in nice, linear time.

for example, your set would be: .en.forced en en-us en.cc en-us.cc

We could also have two set, language and patterns, to keep the permutations down:

langs: en
patterns: .forced . -us .cc -us.cc

That will create langs x patterns combination (we'll use "." or some other symbol to indicate "no pattern" just the language, alone).

Again, though, the question remains of what you want to do with subtitle files with no matching pattern.

Edit:
Sandboxing this idea a little. It seems too work out nicely. Also, surprisingly, FOR seems to be able to handle multiple *s. I seem to recall that not working very well. Maybe it was an enhanced in Windows 10...
 
Last edited:
OK, give this a try. It uses a set of languages and a set of patterns to generate the subtitles file names.

The languages are currently set to "en eng" (English in both alpha-2 and alpha-3).

The patterns currently are: "{LNG}.forced {LNG} {LNG}-us {LNG}.cc {LNG}-us.cc"

Each language code is substituted into the pattern, replacing "{LNG}", and that combined with the title is checked to see if a file exists. If it does, it's added to the list of subtitles. There is a variable "subsep" that contains the string used to separate the title from the pattern. It's currently set to ".".

I also set the subtitles' track name to the resolved pattern (e.g., "en.forced"), just so it's easy to see where each track ends of in the MKV file. They seem to be right, but let me know.



Code:
@:: batch file to convert mp4 files to mkv
@echo off
setlocal EnableExtensions EnableDelayedExpansion

:: languages
set "langs=en eng"

:: subtitles file patterns
set "subpats={LNG}.forced {LNG} {LNG}-us {LNG}.cc {LNG}-us.cc"

:: separator (characters between title and subtitles pattern
set "subsep=."

:: location of mvkmerge
set "mkvmerge=C:\Program Files\MKVToolNix\mkvmerge.exe"

:: output subdirectory
set "outdir=Anystream\mkv"

:: if there are no arguments, process the directory containing the script
if "%1" == "" (
  call :DIR "%~dp0"
  goto :DONE
)

:: iterator through command line arguments
:CMDS
set "curr=%~1"

if "%curr%" == "" goto :DONE

:: ensure current arg exists
if not exist "%curr%" goto :EOF

:: determine if current arg is a dir or a file
pushd "%curr%" >NUL 2>&1
if ERRORLEVEL 1 (
  call :FILE "%curr%"
) else (
  call :DIR "%curr%"
)

shift
goto :CMDS

:DIR
echo recursing %1...
for /r %%i in ("*.mp4") do call :FILE "%%~i"
popd
goto :EOF

:FILE
:: ignore non-mp4 files
if /i "%~x1" NEQ ".mp4" goto :EOF

echo processing %1
pushd "%~dp1"
mkdir "%outdir%" >NUL 2>&1

:: process subtitles
set subs=
for %%L in (%langs%) do (
  for %%P in (%subpats%) do (
  set opts=
   
  :: substitute current language into pattern
  set pat=%%P
  set "pat=!pat:{LNG}=%%L!"
   
  :: if file exist, process it
  set "file=%~n1%subsep%!pat!.srt"
  if exist "!file!" (
  echo found subtitles file: "!file!"

  :: check for "forced" in subtitle file name
  echo !file! | find /i "forced" > NUL 2>&1
  if ERRORLEVEL 1 (
  set "opts=!opt! --default-track 0:0 --forced-track 0:0 --track-name 0:!pat!"
  ) else (
  set "opts=!opt! --default-track 0:1 --forced-track 0:1 --track-name 0:!pat!"
  )

  :: add new subtitles file to list   
  set "subs=!subs! !opts! ^"!file!^""
  )
  )
)

"%mkvmerge%" --default-language en -o "%outdir%\%~n1.mkv" "%~nx1" %subs%
popd
goto :EOF

:DONE
echo Done.
::pause
 
I understood about maybe half of what you said. :ROFLMAO: But I'm open to whatever. Honestly, if it's too much trouble, I can always just manually drop anything with forced subs in to mkvtoolnix. But it sounds like you might have already figured it out. In which case, you might be a wizard.
 
OK, give this a try. It uses a set of languages and a set of patterns to generate the subtitles file names.

The languages are currently set to "en eng" (English in both alpha-2 and alpha-3).

The patterns currently are: "{LNG}.forced {LNG} {LNG}-us {LNG}.cc {LNG}-us.cc"

Each language code is substituted into the pattern, replacing "{LNG}", and that combined with the title is checked to see if a file exists. If it does, it's added to the list of subtitles. There is a variable "subsep" that contains the string used to separate the title from the pattern. It's currently set to ".".

I also set the subtitles' track name to the resolved pattern (e.g., "en.forced"), just so it's easy to see where each track ends of in the MKV file. They seem to be right, but let me know.



Code:
@:: batch file to convert mp4 files to mkv
@echo off
setlocal EnableExtensions EnableDelayedExpansion

:: languages
set "langs=en eng"

:: subtitles file patterns
set "subpats={LNG}.forced {LNG} {LNG}-us {LNG}.cc {LNG}-us.cc"

:: separator (characters between title and subtitles pattern
set "subsep=."

:: location of mvkmerge
set "mkvmerge=C:\Program Files\MKVToolNix\mkvmerge.exe"

:: output subdirectory
set "outdir=Anystream\mkv"

:: if there are no arguments, process the directory containing the script
if "%1" == "" (
  call :DIR "%~dp0"
  goto :DONE
)

:: iterator through command line arguments
:CMDS
set "curr=%~1"

if "%curr%" == "" goto :DONE

:: ensure current arg exists
if not exist "%curr%" goto :EOF

:: determine if current arg is a dir or a file
pushd "%curr%" >NUL 2>&1
if ERRORLEVEL 1 (
  call :FILE "%curr%"
) else (
  call :DIR "%curr%"
)

shift
goto :CMDS

:DIR
echo recursing %1...
for /r %%i in ("*.mp4") do call :FILE "%%~i"
popd
goto :EOF

:FILE
:: ignore non-mp4 files
if /i "%~x1" NEQ ".mp4" goto :EOF

echo processing %1
pushd "%~dp1"
mkdir "%outdir%" >NUL 2>&1

:: process subtitles
set subs=
for %%L in (%langs%) do (
  for %%P in (%subpats%) do (
  set opts=

  :: substitute current language into pattern
  set pat=%%P
  set "pat=!pat:{LNG}=%%L!"

  :: if file exist, process it
  set "file=%~n1%subsep%!pat!.srt"
  if exist "!file!" (
  echo found subtitles file: "!file!"

  :: check for "forced" in subtitle file name
  echo !file! | find /i "forced" > NUL 2>&1
  if ERRORLEVEL 1 (
  set "opts=!opt! --default-track 0:0 --forced-track 0:0 --track-name 0:!pat!"
  ) else (
  set "opts=!opt! --default-track 0:1 --forced-track 0:1 --track-name 0:!pat!"
  )

  :: add new subtitles file to list
  set "subs=!subs! !opts! ^"!file!^""
  )
  )
)

"%mkvmerge%" --default-language en -o "%outdir%\%~n1.mkv" "%~nx1" %subs%
popd
goto :EOF

:DONE
echo Done.
::pause

So far testing works brilliantly. One minor tweak if it isn't too much trouble, if it is, don't worry about it. As shown in the picture, is there a way to remove the .en from the title? So it would just read:
English, forced [Default, Forced]
English
English, cc

And if I'm comparing correctly, this is the part of the code that handles the double-click start.

Code:
:: if there are no arguments, process the directory containing the script
if "%1" == "" (
  call :DIR "%~dp0"
  goto :DONE
)

:: iterator through command line arguments
:CMDS
set "curr=%~1"

if "%curr%" == "" goto :DONE

Can I swap it for this one and have the drag and drop? I'd honestly like to have both versions for different senarios.

Code:
:: iterator through command line arguments
:CMDS
set "curr=%~1"

if "%curr%" == "" goto :DONE
 

Attachments

  • Untitled.jpg
    Untitled.jpg
    17.4 KB · Views: 3
I stopped looking for replies after the first few and no more email notices. Wow, this thread really went way beyond my expertise. Command lines for batch processing! I haven't explored all the suggestions yet, but I decided to buy the program anyway. But now credit cards aren't working from the US. I could buy bit coin but I'm an old lady. Never, ever wanted to use bit coin. This may be a sign that I'm out of my depth!

Sorry the payment stuff isn't working but I'm sure that will working out soon. And apologies for hijacking your thread. The command stuff is not at all something you have to worry about. You can easily just download a movie or show and have the subtitles embedded in the file and it should work fine. I just make things more complicated for myself and the amazing wizard DrXenos is working some magic to help me (and probably a few others) to making it simplified.
 
Can I swap it for this one and have the drag and drop? I'd honestly like to have both versions for different senarios.

The script should handle both cases already, double-click and drag-n-drop.
 
So far testing works brilliantly. One minor tweak if it isn't too much trouble, if it is, don't worry about it. As shown in the picture, is there a way to remove the .en from the title? So it would just read:
English, forced [Default, Forced]
English
English, cc

So, if I understand you correctly, when it assigns the track name, you want it to strip-off the language code and the period after it?
 
Drag and drop doesn't seem to do anything.

Hmm, I just tested it and it seems to work fine. Do you have the actual bat file on your desktop, or a shortcut to it?

Edit: I just tried it both ways and both work fine for me.

Also, I forgot to uncomment the pause for you after testing.
 
Hmm, I just tested it and it seems to work fine. Do you have the actual bat file on your desktop, or a shortcut to it?

Edit: I just tried it both ways and both work fine for me.

Also, I forgot to uncomment the pause for you after testing.

I have in the folder with the files so that way the double click works. When I drag and drop the CMD window doesn't even open.
 
I have in the folder with the files so that way the double click works. When I drag and drop the CMD window doesn't even open.

Uncomment the pause at the end and see if it's just erroring out.
 
Uncomment the pause at the end and see if it's just erroring out.

I did and it didn't change anything. I can see a half second blip on my taskbar but no widow actually opens and when I watched Task Manager I didn't see anything pop there either.

Oh and didn't we make it non-recursive? I just noticed it's also going down folder levels.
 
Back
Top