So I had some split movies that I wanted to merge together. Since I didn’t want to search for every problematic file and manually fix it, I decided to write a script.

The script takes a top directory as argument and then goes through all sub directories looking for mp4 or avi files that have -part1 in the name. It then uses ffmpeg to convert those multi part files to a single movie file. If the doDelete variable is set, it will clean up the original multi part files. You might want to test the script on a couple of movies before enabling this.

DISCLAIMER: this script worked perfectly on my movie library, however if it eats your movies for breakfast or causes the world to explode, it is your responsibility. You ran it, didn’t you?

And for those who didn’t see it: it is Perl, so you will need Perl (and ffmpeg).

#!/usr/bin/perl

# Location of the Movies, all directories underneath
# will be scanned
my $baseDirectory = "/path/to/the/root/of/your/movies";

my $numberOfUpdates = 0;
my $doDelete = 0;

handle_dir($baseDirectory, '.avi|.mp4');

print "\nUpdated $numberOfUpdates files\n";

exit 0;

# -------------------------------------------------------
# find_extension (dir, extension)
#
# Recurse through a directory and look for an extension
# -------------------------------------------------------
sub handle_dir {
    my ($dir, $extensions) = @_;
    return unless -d $dir;
        
    my @files;
    if (opendir my $dh, $dir) {
        my @list;
        my $file;
        
        while ($file = readdir $dh) {

            if (-d "$dir/$file" && $file ne "." && $file ne "..") {
                handle_dir("$dir/$file", $extensions);
            } elsif ($file =~ /$extensions$/ && $file =~ /\-part1/) {
                handle_extension($dir, $file, $extensions);
            }
        }
        closedir $dh;
    } else {
        print "Unable to open directory: $dir\n$!";
    }
}

# -------------------------------------------------------
# handle_extension ($multipartDir, $file, $extension)
#
# When an extension has been found. Convert all parts
# to a single file and continue
# -------------------------------------------------------
sub handle_extension {
    my ($multipartDir, $file, $extension) = @_;
    
    print "\nFound multipart $extension: $file\n";

    $fileExtension = $file;
    $fileExtension =~ s/.*(\..*)/$1/;

    $baseFilename = $file;
    $baseFilename =~ s/(.*)\-part1.*/$1/;
    
    $outputFilename = "$multipartDir/$baseFilename$fileExtension";

    my $command = "/usr/bin/ffmpeg -f concat -i \"$multipartDir/multipartFiles.txt\" -c copy \"$outputFilename\"";

    # Get all the multipart $fileExtension parts from the current directory
    @multipartFiles = `ls \"$multipartDir\" | grep part | grep $fileExtension | sort`;
    
    # Write all the multipart pieces to a temp file
    open (TMPFILE, ">$multipartDir/multipartFiles.txt");
    
    foreach $partFile (@multipartFiles) {
        $partFile =~ s/(.*)\n/$1/;
        print TMPFILE "file \'$multipartDir\/$partFile\'\n";
    }   
    
    close TMPFILE;
    
    print "Extensions  : $fileExtension\n";
    print "Basefilename: $baseFilename\n";
    print "Output      : $outputFilename\n";
    print "Command     : $command\n";
    
    # Execute the ffmpeg command
    `$command`;

    # Remove our temp file
    `rm \"$multipartDir/multipartFiles.txt\"`;
    
    if ($doDelete) {    
        # Remove our partfiles
        foreach $partFile (@multipartFiles) {
            $partFile =~ s/(.*)\n/$1/;
            `rm \"$multipartDir\/$partFile\"`;
        }   
    }
    
    $numberOfUpdates++;
}