Skip to content

No Cuesheet support until now #1

@Peter0

Description

@Peter0

Hello C0decracker,

thanks for the small python script regarding letting ffmpeg do multiple jobs in order to losslessly splitting sourcefile.
I needed cuesheet support for doing non-equidistantly splits, therefore I added cuesheet-support.
I got no idea about how to operate git and the like, but I think this project is small enough to just give you my work here:

#!/usr/bin/env python

import subprocess
import re
import math
from optparse import OptionParser


length_regexp      = 'Duration: (\d{2}):(\d{2}):(\d{2})\.\d+,'
cue_reffile_regexp = 'FILE\s"(.*)"'
cue_index_regexp  = 'INDEX\s01\s(\d{2}):(\d{2}):(\d{2})'
re_length      = re.compile(length_regexp)
re_cue_reffile = re.compile(cue_reffile_regexp)
re_cue_index   = re.compile(cue_index_regexp)
#splitbin = "ffmpeg"
splitbin = "avconv"

def main():

    (filename, split_length) = parse_options()
    if split_length <= 0:
        # Cuesheet operation (individual split lengths)
        cuefile = open (filename)
        cue=cuefile.read ()
        cuefile.close ()
        filename = re_cue_reffile.search(cue).group(1)
        #This finds only the first FILE; there should be only one FILE per cuesheet for this script being and working reasonable.
        split_points = [int(indx.group(1))*60 + int(indx.group(2)) + float(indx.group(3))/75 for indx in re_cue_index.finditer(cue)]
        equal = False
    else:
        equal = True

    output = subprocess.Popen(splitbin+" -i '"+filename+"' 2>&1 | grep 'Duration'", 
                            shell = True,
                            stdout = subprocess.PIPE
                            ).stdout.read()
    print output
    matches = re_length.search(output)
    if matches:
        video_length = int(matches.group(1)) * 3600 + \
                       int(matches.group(2)) * 60 + \
                       int(matches.group(3))
        print "Video length in seconds: "+str(video_length)
    else:
        print "Can't determine video length."
        raise SystemExit

    if equal:
        split_count = int(math.ceil(video_length/float(split_length)))
        if(split_count == 1):
            print "Video length is less then the target split length."
            raise SystemExit

        split_cmd = splitbin + " -i '"+filename+"' -vcodec copy "
        for n in range(0, split_count):
            split_str = ""
            if n == 0:
                split_start = 0
            else:
                split_start = split_length * n

            split_str += " -ss "+str(split_start)+" -t "+str(split_length) + \
                        " '"+filename[:-4] + "-" + str(n) + "." + filename[-3:] + \
                        "'"
            print "About to run: "+split_cmd+split_str
            output = subprocess.Popen(split_cmd+split_str, shell = True, stdout =
                                   subprocess.PIPE).stdout.read()
    else:
        if(len (split_points) <= 1):
            print "There aren't at least two indices in cuefile."
            raise SystemExit

        split_cmd = splitbin + " -i '"+filename+"' -codec copy "  # Enable audio, too
        n = 0
        split_points.append(-1)
        split_start = split_points [0]
        for split_end in split_points[1:]:
            split_str = " -ss "+str(split_start)+((" -t "+str(split_end - split_start)) if split_end != -1 else "") + \
                        " '"+filename[:-4] + "-" + str(n) + "." + filename[-3:] + \
                        "'"   #Omit -t when last piece will be created
            print "About to run: "+split_cmd+split_str
            output = subprocess.Popen(split_cmd+split_str, shell = True, stdout =
                                   subprocess.PIPE).stdout.read()
            n += 1
            split_start = split_end


def parse_options():
    parser = OptionParser()    

    parser.add_option("-f", "--file",
                        dest = "filename",
                        help = "file to split, for example sample.avi",
                        type = "string",
                        action = "store"
                        )
    parser.add_option("-s", "--split-size",
                        dest = "split_size",
                        help = "split or chunk size in seconds, for example 10; if 0 is given, -f refers to a cuefile",
                        type = "int",
                        action = "store"
                        )
    (options, args) = parser.parse_args()

    if options.filename and (options.split_size != None):

        return (options.filename, options.split_size)

    else:

        parser.print_help()
        raise SystemExit

if __name__ == '__main__':

    try: 
        main()
    except Exception, e:
        print "Exception occured running main():"
        print str(e)

This isn't fairly tested and should give only the idea.
If the equidistantly splitting would be done in a slighty other way (namely by first building a list of split_starts and split_length or better split_ends and then doing the actual job), there could be less excess code for both if-cases.

It uses -s=0 as flag to interpret -f as cuefile; this may seem to be unaesthetic.

I used it to split m4a sound-only files, therefore I got to change from "-vcodec" to "-acodec". Because just "-codec" also worked, I just left it. I don't know what the impact would be to actual videos. I left the equidistant part, it still uses "-vcodec" for now.

Finally, as my system uses avconv instead of ffmpeg, I put its name into a variable to be able to simply change it in the source. Another idea: Retrieve file name of script (don't know if this is possible under python like it can be done under bash by retrieving argument no. 0), chop off the "-split.py" part and use the remaining as tool’s name – so you’d use "ffmpeg-split.py" whereas I’d rename it to "avconv-split.py".

I haven't tracked down it thoroughly, but I'm suspiciously wondering if your code possibly omits the last fraction of seconds of the source video, because it handles only integer seconds.

Thanks – Peter

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions