menteith
menteith

Reputation: 678

Looping over all files of certain extension in a directory

I wrote a small script that unzips all the *.zip files in the current directory to extract only *.srt files directory to a newly created directory. It then loops over all the *.mkv files in the current directory to get their name and then changes subs/*.srt file name to produce a new file name that is exactly as *.mkv file name. The script works when there is one zip file and one mkv file but when there are more files it produces bad filenames. I cannot track why this is the case. Now I figured out when this is the case.

EDIT

I managed to narrow down the scenarios when file names are changed in erroneous way. Let's say in a current directory we have three *.mkv files: (sorted alphabetically)

$ ls -1a *.mkv
Home.S06E10.1080p.BluRay.x264-PRINTER.mkv 
Home.S06E11.1080p.BluRay.x264-PRINTER.mkv
Home.S06E12.1080p.BluRay.x264-PRINTER.mkv

and three *.srt files:

$ ls -1a *.srt
Home.S06E10.srt 
Home.S06E11.BDRip.X264-PRINTER.srt 
Home.S06E12.BDRip.X264-PRINTER.srt

When I run the script, I get:

subs/Home.S06E10.srt -> subs/Home.S06E10.1080p.BluRay.x264-PRINTER.srt
subs/Home.S06E10.1080p.BluRay.x264-PRINTER.srt -> subs/Home.S06E11.1080p.BluRay.x264-PRINTER.srt
subs/Home.S06E11.1080p.BluRay.x264-PRINTER.srt -> subs/Home.S06E12.1080p.BluRay.x264-PRINTER.srt

As you see, Home.S06E10.srt is used twice

#!/usr/bin/env bash

mkdir -p subs
mkdir -p mkv-out
mkdir -p subs-bak
# unzip files, maybe there are subtitles in it...
for zip in *.zip; do
    if [ -f "$zip" ]; then
        unzip "$zip" -d subs "*.srt" >/dev/null
    fi
done

# move all subtitles to subs catalog
for srt in *.srt; do
    if [ -f "$srt" ]; then
        mv "*.srt" subs
fi
done

mkvCount=(*.mkv)
srtCount=(subs/*.srt)

if [ ${#mkvCount[@]} != ${#srtCount[@]} ]; then
    echo "Different number of srt and mkv files!"
    exit 1
fi

for MOVIE in *.mkv; do
    for SUBTITLE in subs/*.srt; do
        NAME=$(basename "$MOVIE" .mkv)
        SRT="subs/$NAME.srt"
        if [ ! -f "$SRT" ]; then
            echo "$SUBTITLE -> ${SRT}"
            mv "$SUBTITLE" "$SRT"
        fi
    done
done

Upvotes: 1

Views: 217

Answers (1)

giusti
giusti

Reputation: 3548

You seem to be relying on the lexicographical order of the files to associate one SRT with one MKV. If all you have are season-episode files for the same series, then I suggest a completely different approach: iterate a season and an episode counters, then generate masks in the form S##E## and find a movie and a subtitle files. If you find them, you move them.

for season in {01..06}; do
  for episode in {01..24}; do
    # Count how many movies and subtitles we have in the form S##E##
    nummovies=$(find -name "*S${season}E${episode}*.mkv" | wc -l)
    numsubs=$(find -name "*S${season}E${episode}*.srt" | wc -l)
    if [[ $nummovies -gt 1 || $numsubs -gt 1 ]]; then
      echo "Multiple movies/subtitles for S${season}E${episode}"
      exit 1
    fi

    # Skip if there is no movie or subtitle for this particular
    # season/episode combination
    if [[ $nummovies -eq 0 ]]; then
      continue
    fi
    if [[ $numsubs -eq 0 ]]; then
      echo "No subtitle for S${season}E${episode}"
      continue
    fi

    # Now actually take the MKV file, get its basename, then find the
    # SRT file with the same S##E## and move it
    moviename=$(find -name "*S${season}E${episode}*.mkv")
    basename=$(basename -s .mkv "$moviename")
    subfile=$(find -name "*S${season}E${episode}*.srt")
    mv "${subfile}" "${basename}.mkv"
  done
done

If you don't want to rewrite everything, just change your last loop:

  1. Drop the inner loop
  2. Take the movie name instead and use sed to find the particular S##E## substring
  3. Use find to find one SRT file like in my code
  4. Move it

This has the benefit of not relying on hard-coded number of seasons/episodes. I guessed six seasons and no season with more than 26 episodes. However I thought my code would do and would look more simple.

Make certain that there will be exactly one SRT file. Having zero or more than one file will probably just give an error from mv, but it's better to be safe. In my code I used a separate call to find with wc to count the number of lines, but if you are more knowledgeable in bash-foo, then perhaps there's a way to treat the output of find as an array instead.

In both my suggestions you can also drop that check for # movies = # subtitles. This gives you more flexibility. The subtitles can be in whatever directories you want, but the movies are assumed to the in the CWDIR. With find you can also use the -or operator to accept other extensions, such as AVI and MPG.

Upvotes: 1

Related Questions