Skip to content

Instantly share code, notes, and snippets.

@mjtorn
Last active May 14, 2020 12:56
Show Gist options
  • Save mjtorn/00e5dbb1435c7dad7571a061b63dbb3d to your computer and use it in GitHub Desktop.
Save mjtorn/00e5dbb1435c7dad7571a061b63dbb3d to your computer and use it in GitHub Desktop.

Revisions

  1. mjtorn revised this gist May 14, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion dvdwhine.py
    Original file line number Diff line number Diff line change
    @@ -5,7 +5,7 @@
    Mapfile format:
    ## More than meets the eye
    # -a2 -q19
    @ -a2 -q19
    1 1x09 - Fire on the Mountain
    ^ ^
  2. mjtorn created this gist May 13, 2020.
    137 changes: 137 additions & 0 deletions dvdwhine.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,137 @@
    #!/usr/bin/env python3

    """
    Mapfile format:
    ## More than meets the eye
    # -a2 -q19
    1 1x09 - Fire on the Mountain
    ^ ^
    | +- Output file name (sans .mp4)
    +---- Title number
    Argument lines starting with `@` contain extra
    arguments passed to HandBrakeCLI. Each one
    affects all the output lines until a new
    such line is seen.
    Actual comments starting with `#` and empty
    lines are ignored.
    Greetz and thankz to Con Kolivas for
    the HandBrake options!
    """

    import multiprocessing
    import os
    import subprocess
    import sys

    DVDW_DEVICE = os.environ.get('DVDW_DEVICE', '/dev/sr0')
    DVDW_CPUCOUNT = os.environ.get('DVDW_CPUCOUNT', multiprocessing.cpu_count())
    DVDW_MODE = os.environ.get('DVDW_MODE', 'pal_4_3')

    OPT_720P = 'nombtree:keyint=1000:scenecut=10:ref=9:ip_factor=1:pb_factor=1:' \
    'direct_pred=auto:weight_b:analyse=all:me=umh:mixed_refs:' \
    'trellis=0:nofast_pskip:level_idc=41:threads={}:' \
    'aq_mode=0:nodeterministic:psy-rd=0|0:b-adapt=2:level=4.1:' \
    'direct=auto:fast-pskip=0:ipratio=1.00:pbratio=1.00:aq-mode=0' \
    ':mbtree=0:cabac=0:no-dct-decimate=1:aq-strength=0:bframes=16'.format(DVDW_CPUCOUNT)

    BASE_MODES = {
    'pal_4_3': {
    'width': 720,
    'height': 576,
    'fps': 25,
    },
    'ntsc_4_3': {
    'width': 720,
    'height': 480,
    'fps': 30,
    }
    }

    BASE_ARGS = '-i {DVDW_DEVICE} -e x264 -x {OPT} ' \
    '-w {width} -l {height} -r {fps} ' \
    '--no-loose-crop --crop 0:0:0:0 ' \
    '--comb-detect ' \
    '-t {tracknum} ' \
    '{extra_args}'


    def find_handbrake():
    """Do a simple search in $PATH for HandBrakeCLI
    """

    for path in os.environ['PATH'].split(':'):
    candidate = os.path.join(path, 'HandBrakeCLI')
    if os.path.exists(candidate):
    return candidate


    def get_arguments(mapfile, mode):
    """Parse `mapfile` and combine its arguments
    with the ones in `mode`.
    """

    fmt = mode.copy()
    fmt['DVDW_DEVICE'] = DVDW_DEVICE
    fmt['extra_args'] = ''

    args = []
    for line in open(mapfile, 'r'):
    split_line = line.strip().split()

    if not split_line:
    continue

    if line.startswith('#'):
    continue
    elif split_line[0] == '@':
    fmt['extra_args'] = ' '.join(split_line[1:])
    continue

    fmt['tracknum'] = split_line[0]
    trackname = ' '.join(split_line[1:])

    args_list = BASE_ARGS.format(**fmt)

    args.append((args_list, trackname))

    return args


    def main(mapfile):
    """Parse and rip
    """

    hb = find_handbrake()
    if not hb:
    print('HandBrakeCLI not found')
    return 1

    mode = BASE_MODES.get(DVDW_MODE)
    if mode['width'] <= 720:
    mode['OPT'] = OPT_720P
    else:
    raise NotImplementedError('Not here yet')

    args = get_arguments(mapfile, mode)
    for arg, trackname in args:
    cmd = '{} {}'.format(hb, arg)
    cmd = cmd.split()
    cmd.extend(('-o', '{}.mp4'.format(trackname)))

    ret = subprocess.run(cmd)

    return ret.returncode


    if __name__ == '__main__':
    if len(sys.argv) == 1:
    print('USAGE: {} MAPFILE'.format(sys.argv[0]))
    sys.exit(1)

    sys.exit(main(sys.argv[1]))