OS X + HandBrakeCLI + Python = convert, iTunes and e-mail

Come up with a useful post-processing script? Share it here!
imthenachoman
Jr. Member
Jr. Member
Posts: 59
Joined: March 2nd, 2009, 10:58 am
Contact:

OS X + HandBrakeCLI + Python = convert, iTunes and e-mail

Post by imthenachoman »

So I re-wrote a script I had in Python. It converts videos using HandBrakeCLI to (currently) any preset option of HandBrakeCLI and imports it into iTunes. There are some config options that let you:
  • delete original after import
  • choose prefix or postfix and extension for output files
  • choose the output format
  • strip season and episode information from tv shows
  • e-mail confirmation
The code is below. It has comments but if anyone has any questions let me know. Be sure to read the CONFIG section to make the script do what you want.

Code: Select all

#!/usr/bin/python

###############################################################################
# PLEASE DO NOT REMOVE
#
# Author: Anchal 'Nacho' Nigam (and lots of help from #python on irc.freenode.net)
# E-Mail: [email protected]
# Date:   February 1, 2010
###############################################################################

###############################################################################
# CONFIG START - read bottom of page for explanation
###############################################################################

DATE_TIME_FORMAT = "[ %Y-%m-%d @ %H:%M:%S ]  "
SEPERATOR = " : "

FILE_TYPES = [ "*.avi", "*.mkv" ]

TV_FORMAT = "^(?P<show>.*) - (?P<season_number>\d+)x(?P<episode_number>\d+) - (?P<episode_ID>.*)$"

OUTPUT_PREFIX = ""
OUTPUT_POSTFIX = " - iPhone"
OUTPUT_EXTENSION = "mp4"
#OUTPUT_DIRECTORY = "/"

DELETE_ORIGINAL = False

HANDBRAKE_PATH = "/usr/bin/HandBrakeCLI"
HANDBRAKE_PRESET = "iPhone & iPod Touch"
HANDBRAKE_COMMAND_OUTPUT = False
HANDBRAKE_COMMAND_OUTPUT_APPEND = False

EMAIL = True
EMAIL_SMTP = 'smtp.gmail.com'
EMAIL_AUTH = True
EMAIL_LOGIN = '[email protected]'
EMAIL_PASSWORD = 'H0ku5p0ku5'
EMAIL_TO = [ '[email protected]', ]
EMAIL_FROM = '[email protected]'

###############################################################################
# CONFIG END
###############################################################################

###############################################################################
# MAIN PROGRAM
###############################################################################

import sys

# This script does not work well with the current default python path SabNZBD sets
# Set it to the same python path as OS X defaults
osx_python_path = [
    sys.path[0],
    "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python26.zip",
    "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6",
    "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/plat-darwin",
    "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/plat-mac",
    "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/plat-mac/lib-scriptpackages",
    "/System/Library/Frameworks/Python.framework/Versions/2.6/Extras/lib/python",
    "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/lib-tk",
    "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/lib-old",
    "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/lib-dynload",
    "/Library/Python/2.6/site-packages",
    "/System/Library/Frameworks/Python.framework/Versions/2.6/Extras/lib/python/PyObjC",
    "/System/Library/Frameworks/Python.framework/Versions/2.6/Extras/lib/python/wx-2.8-mac-unicode"
]

sys.path = osx_python_path

import glob, os, re, subprocess, smtplib, email.utils
from email.mime.text import MIMEText
from email.utils import COMMASPACE
from time import strftime
from encodings import ascii

def date_time():
    return strftime( DATE_TIME_FORMAT )

# 1        The final directory of the job (full path)
# 2        The name of the NZB file
# 3        Clean version of the job name (no path info and ".nzb" removed)
# 4        Newzbin report number (may be empty
# 5        Newzbin or user-defined category
# 6        Group that the NZB was posted in e.g. alt.binaries.x

FINAL_DIRECTORY = len( sys.argv ) >= 2 and sys.argv[1] or ""
NZB_FILE_NAME = sys.argv[2] if len( sys.argv ) >= 3 else ""
JOB_NAME = sys.argv[3] if len( sys.argv ) >= 4 else ""
NEWZBIN_NZB_NUMBER = sys.argv[4] if len( sys.argv ) >= 5 else ""
NEWZBIN_CATEGORY = sys.argv[5] if len( sys.argv ) >= 6 else ""
NZB_USENET_CATEGORY = sys.argv[6] if len( sys.argv ) >= 7 else ""

print date_time() + ( "%-30s" + SEPERATOR + "%s" ) % ( "Final Directory", FINAL_DIRECTORY )
print date_time() + ( "%-30s" + SEPERATOR + "%s" ) % ( "NZB Filename", NZB_FILE_NAME )
print date_time() + ( "%-30s" + SEPERATOR + "%s" ) % ( "Job Name", JOB_NAME )
print date_time() + ( "%-30s" + SEPERATOR + "%s" ) % ( "NewzBin NZB ID", NEWZBIN_NZB_NUMBER )
print date_time() + ( "%-30s" + SEPERATOR + "%s" ) % ( "NewzBin Category", NEWZBIN_CATEGORY )
print date_time() + ( "%-30s" + SEPERATOR + "%s" ) % ( "Usenet Category", NZB_USENET_CATEGORY )

# set output directory to final directory if not set
try:
    OUTPUT_DIRECTORY
except NameError:
    OUTPUT_DIRECTORY = FINAL_DIRECTORY

email_message = "Results:\n"
success = 0
fail = 0

print date_time() + ( "%-30s" + SEPERATOR + "%s" ) % ( "File Types", FILE_TYPES )
print date_time() + ( "%-30s" + SEPERATOR + "%s" ) % ( "TV Format", TV_FORMAT )
print date_time() + ( "%-30s" + SEPERATOR + "%s" ) % ( "Output Directory", OUTPUT_DIRECTORY )
print date_time() + ( "%-30s" + SEPERATOR + "%s" ) % ( "Output Prefix", OUTPUT_PREFIX )
print date_time() + ( "%-30s" + SEPERATOR + "%s" ) % ( "Output Postfix", OUTPUT_POSTFIX )
print date_time() + ( "%-30s" + SEPERATOR + "%s" ) % ( "Output Extension", OUTPUT_EXTENSION )
print date_time() + ( "%-30s" + SEPERATOR + "%s" ) % ( "Handbrake Path", HANDBRAKE_PATH )
print date_time() + ( "%-30s" + SEPERATOR + "%s" ) % ( "Handbrake Preset", HANDBRAKE_PRESET )
print date_time() + ( "%-30s" + SEPERATOR + "%s" ) % ( "Handbrake Verbose", HANDBRAKE_COMMAND_OUTPUT )

# store the output of each handbrake iteration
handbrake_output = []

# iterate through all the file types
for file_type in FILE_TYPES:
    print date_time() + "Processsing file type " + file_type
    
    # for each file type find all files
    for input_file in glob.glob( FINAL_DIRECTORY + '/' + file_type ):
        
        # get directory and file name with extension for the input file and remove extension
        ( directory, filename ) = os.path.split( input_file )
        filename = re.match( '^(.*)[.]' + file_type + '$', filename ).group( 1 )

        output_file = os.path.join( OUTPUT_DIRECTORY, "%s%s%s.%s" % ( OUTPUT_PREFIX, filename, OUTPUT_POSTFIX, OUTPUT_EXTENSION ) )
        # check if the output file exists
        if( os.path.exists( output_file ) ) :
            print date_time() + "\t" + input_file + SEPERATOR + 'skipping' + SEPERATOR + output_file + ' exists'
            #email_message += "\n" + 'SKIPPED' + SEPERATOR + input_file + SEPERATOR + 'already exists'
            continue
        
        output_file_processing = os.path.join( OUTPUT_DIRECTORY, "%s%s%s_processing.%s" % ( OUTPUT_PREFIX, filename, OUTPUT_POSTFIX, OUTPUT_EXTENSION ) )
        print date_time() + "\t" + input_file
        
        print date_time() + "\t\t" + 'encoding' + SEPERATOR + output_file_processing
        p = subprocess.Popen( [ HANDBRAKE_PATH, '-i', input_file, '-o', output_file_processing, '--preset=' + HANDBRAKE_PRESET ], stdout = ( None if HANDBRAKE_COMMAND_OUTPUT else subprocess.PIPE ), stderr = ( None if HANDBRAKE_COMMAND_OUTPUT else subprocess.STDOUT ) )
        ( stdout, stderr ) = p.communicate()
        handbrake_output.append( [ input_file, stdout ] )
        
        # make sure HandBrake exited successfully
        if( p.wait() != 0 ):
            print date_time() + "\t\t" + "FAILED"
            email_message += "\n" + 'FAILED' + SEPERATOR + input_file + SEPERATOR + 'error encoding'
            fail += 1
            continue
        
        # rename file
        os.rename( output_file_processing, output_file )
        print date_time() + "\t\t" + 'done encoding' + SEPERATOR + output_file
        
        # create applescript to import file into iTunes
        applescript = """\
tell application "iTunes"
    set posix_path to "%s"
    set mac_path to posix_path as POSIX file
    set video to ( add mac_path )
""" % ( output_file )

        # if tv use TV_FORMAT to match against input file name to strip out necessary parts and add them to applescript for importing
        if( NEWZBIN_CATEGORY == "tv" ):
            print date_time() + "\t\t" + 'adding tv show to iTunes'
            tv_details = re.match( TV_FORMAT, filename ).groupdict()
            applescript += """\
    set video kind of video to TV Show
"""
            for key in tv_details:
                print date_time() + "\t\t\t" + ( "%-15s" + SEPERATOR + "%s" ) % ( key.replace( '_', ' ' ), tv_details[ key ] )
                applescript += """\
    set %s of video to "%s"
""" % ( key.replace( '_', ' ' ), tv_details[ key ] )
        # not a tv show so just set the name to the filename
        else:
            print date_time() + "\t\t" + 'adding movie to iTunes'
            applescript += """\
    set show of video to "%s"
""" % ( filename )
            print date_time() + "\t\t\t" + ( "%-15s" + SEPERATOR + "%s" ) % ( "show name", filename )
        
        applescript += """\
end tell
"""
        # run the applescript and wait till it is done
        r = subprocess.Popen( [ "osascript", "-e", applescript ] ).wait()
        
        if( r != 0 ):
            print date_time() + "\t" + "FAILED"
            fail += 1
            email_message += "\n" + 'FAILED' + SEPERATOR + input_file + SEPERATOR + 'error adding to iTunes'
            continue
        
        print date_time() + "\t\t" + 'done'
        
        # delete the original if requested
        if( DELETE_ORIGINAL ):
            sys.stdout.write( date_time() + "\t\t" + "deleting" )
            sys.stdout.flush()
            os.unlink( input_file )
            print SEPERATOR + 'done'
            
        email_message += "\n" + 'succeeded' + SEPERATOR + input_file
        success += 1

# send e-mail if requested
if( EMAIL ):
    sys.stdout.write( date_time() + "Sending e-mail" )
    sys.stdout.flush()
    if( email_message == "Results:\n" ):
        print SEPERATOR + 'skipping' + SEPERATOR + 'nothing to send'
    else:
        msg = MIMEText( email_message )
        msg['To'] = COMMASPACE.join( EMAIL_TO )
        msg['Subject'] = 'Post processing results: %s success / %s fail' % ( success, fail )
    
        server = smtplib.SMTP( EMAIL_SMTP )
        try:
            # authenticate if required
            if( EMAIL_AUTH ):
                server.ehlo()
                # start TLS if required
                if( server.has_extn( 'STARTTLS' ) ):
                    server.starttls()
                    server.ehlo()
                server.login( EMAIL_LOGIN, EMAIL_PASSWORD )
            server.sendmail( EMAIL_FROM, EMAIL_TO, msg.as_string() )
        except Exception, e:
            print SEPERATOR + 'ERROR'
        else:
            print SEPERATOR + 'done'
        finally:
            # quit/close connection
            server.quit()

# if we want to see handbrake output
if( HANDBRAKE_COMMAND_OUTPUT_APPEND ):
    for i in handbrake_output:
        print "\n\n"
        print i[0]
        print i[1]

print date_time() + 'DONE'
sys.exit( 0 )

"""
###############################################################################
# CONFIG HELP
###############################################################################

DATE_TIME_FORMAT = "[ %Y-%m-%d @ %H:%M:%S ]  "
    Use the table at http://docs.python.org/library/time.html#time.strftime to build the format for the time in the output
    Example:    "[ %Y-%m-%d @ %H:%M:%S ]  "
    Output:     ""[ 2010-02-01 @ 16:07:36 ]  "

SEPERATOR = " : "
     The string to use between sections in a line in the output
    Example:    " : "

FILE_TYPES = [ "*.avi", "*.mkv" ]
     A list of file types used to find files to encode
    Example:    [ "*.avi", "*.mkv" ]

TV_FORMAT = "^(?P<show>.*) - (?P<season_number>\d+)x(?P<episode_number>\d+) - (?P<episode_ID>.*)$"
     A named regex pattern that will match the output of the file created by SabNZBD
    The regex names must match those of applescript options (use _ for space).
    Example:    "^(?P<show>.*) - (?P<season_number>\d+)x(?P<episode_number>\d+) - (?P<episode_ID>.*)$"
    
OUTPUT_PREFIX = ""
    The string to add to the beginning of each output filename

OUTPUT_POSTFIX = " - iPhone"
    The string to add to the end of each output filename (before the extension)

OUTPUT_EXTENSION = "mp4"
    The extension to use for the output file

#OUTPUT_DIRECTORY = "/"
    The directory to save the output files in
    If this line is commented out (with a #) this script will save the file in the same directory as the input file

DELETE_ORIGINAL = False
    Set to True if you want to delete the input file
    Set to False if you do not

HANDBRAKE_PATH = "/usr/bin/HandBrakeCLI"
    The path to handbrakecli

HANDBRAKE_PRESET = "iPhone & iPod Touch"
    The preset name to use for this script
    Look at http://trac.handbrake.fr/wiki/CLIGuide#presets for a list of presets
    At some point I might modify this script to accept other HandBrake paramaters

HANDBRAKE_COMMAND_OUTPUT = False
    Set to True if you want to see the output of HandBrakeCLI as it is encoding
    Set to False if you do not

HANDBRAKE_COMMAND_OUTPUT_APPEND = False
    Set to True if you want to see the output of HandBrakeCLI at the end, after it has finished encoding all files
    Set to False if you do not

EMAIL = True
    Send e-mail upon completion of this script

EMAIL_SMTP = 'smtp.gmail.com'
    smtp address of your e-mail

EMAIL_AUTH = True
    True if your smtp provider requires authentication
    False if not

EMAIL_LOGIN = '[email protected]'
    Login for authentication if required

EMAIL_PASSWORD = 'H0ku5p0ku5'
    Password for authentication if required
    
EMAIL_TO = [ '[email protected]', ]
    A list of e-mail addressees to send the e-mail to

EMAIL_FROM = '[email protected]'
    Whow the e-mail will be freom
    Some smtp providers might require you to use your e-mail address

"""

###############################################################################
# PLEASE DO NOT REMOVE
#
# Author:    Anchal 'Nacho' Nigam (and lots of help from #python on irc.freenode.net)
# E-Mail:    [email protected]
# Date:        February 1, 2010
###############################################################################
To install the script:
  • save the attached file (below) to your scripts directory for sabnzbd
  • in Terminal or iTerm or whatever chmod +x it (chmod +x /path/to/script/to_handbrake.py)
  • set it as the script to use for whatever you want in sabnzbd
Last edited by imthenachoman on February 1st, 2010, 10:27 pm, edited 1 time in total.
randyharris
Full Member
Full Member
Posts: 146
Joined: January 21st, 2010, 5:36 pm

Re: OS X + HandBrakeCLI + Python = convert and import videos

Post by randyharris »

Great to have a script with variables! I don't know enough of this topic to understand the benefits of Python.

Q: "strip season and episode information from tv shows"

Does this mean that when it gets injected into iTunes it won't contain the season and episode information for TV series?

Any chance of getting an email notification option in the script, that emails out a notification when a job has completed?

Thanks!
imthenachoman
Jr. Member
Jr. Member
Posts: 59
Joined: March 2nd, 2009, 10:58 am
Contact:

Re: OS X + HandBrakeCLI + Python = convert and import videos

Post by imthenachoman »

randyharris wrote: Great to have a script with variables! I don't know enough of this topic to understand the benefits of Python.

Q: "strip season and episode information from tv shows"

Does this mean that when it gets injected into iTunes it won't contain the season and episode information for TV series?

Any chance of getting an email notification option in the script, that emails out a notification when a job has completed?

Thanks!
So if SabNZBD renames the TV show to "Some Show - 2x01 - This Episode Sucks" the script will import the show into iTunes and put "Some Show", "2", "1", "This Episode Sucks" as the show name, season number, episode number, and episode ID (episode name) in the tags.

I'm actually looking into how to get python to e-mail. Once I figure it out I will let you know.
imthenachoman
Jr. Member
Jr. Member
Posts: 59
Joined: March 2nd, 2009, 10:58 am
Contact:

Re: OS X + HandBrakeCLI + Python = convert and import videos

Post by imthenachoman »

So I figured out how to do e-mail for this script. I don't really care about e-mail so I'll ask y'all.

Should the e-mail contain the same output as the script or just a simple job failed, job passed kind of deal?
randyharris
Full Member
Full Member
Posts: 146
Joined: January 21st, 2010, 5:36 pm

Re: OS X + HandBrakeCLI + Python = convert and import videos

Post by randyharris »

imthenachoman wrote: So I figured out how to do e-mail for this script. I don't really care about e-mail so I'll ask y'all.

Should the e-mail contain the same output as the script or just a simple job failed, job passed kind of deal?
Success / Fail
imthenachoman
Jr. Member
Jr. Member
Posts: 59
Joined: March 2nd, 2009, 10:58 am
Contact:

Re: OS X + HandBrakeCLI + Python = convert and import videos

Post by imthenachoman »

I got e-mail working, but randyharris pointed out that it is pointless as SabNZBD lets you e-mail output of post processing scripts.

Will update in a second with new version.
randyharris
Full Member
Full Member
Posts: 146
Joined: January 21st, 2010, 5:36 pm

Re: OS X + HandBrakeCLI + Python = convert, iTunes and e-mail

Post by randyharris »

I'm not sure what happened but I am now getting this error on all items that the script runs on. I didn't test every combination, but I tried it with Sorting and Categories both being used and both not being used with the same result.

Traceback (most recent call last):
File "/Applications/SAB/scripts/AppleTV-Postfix", line 141, in
  os.rename( output_file_processing, output_file )
OSError: [Errno 2] No such file or directory
imthenachoman
Jr. Member
Jr. Member
Posts: 59
Joined: March 2nd, 2009, 10:58 am
Contact:

Re: OS X + HandBrakeCLI + Python = convert, iTunes and e-mail

Post by imthenachoman »

randyharris wrote: I'm not sure what happened but I am now getting this error on all items that the script runs on. I didn't test every combination, but I tried it with Sorting and Categories both being used and both not being used with the same result.

Traceback (most recent call last):
File "/Applications/SAB/scripts/AppleTV-Postfix", line 141, in
   os.rename( output_file_processing, output_file )
OSError: [Errno 2] No such file or directory
Can you tell me what you were downloading, and the settings you used? You can just copy and paste the settings from the script (minus the password stuff)
imthenachoman
Jr. Member
Jr. Member
Posts: 59
Joined: March 2nd, 2009, 10:58 am
Contact:

Re: OS X + HandBrakeCLI + Python = convert, iTunes and e-mail

Post by imthenachoman »

randyharris wrote: I'm not sure what happened but I am now getting this error on all items that the script runs on. I didn't test every combination, but I tried it with Sorting and Categories both being used and both not being used with the same result.

Traceback (most recent call last):
File "/Applications/SAB/scripts/AppleTV-Postfix", line 141, in
   os.rename( output_file_processing, output_file )
OSError: [Errno 2] No such file or directory
If you could send the entire output of the script too actually....minus the passwords.
randyharris
Full Member
Full Member
Posts: 146
Joined: January 21st, 2010, 5:36 pm

Re: OS X + HandBrakeCLI + Python = convert, iTunes and e-mail

Post by randyharris »

imthenachoman wrote:
randyharris wrote: I'm not sure what happened but I am now getting this error on all items that the script runs on. I didn't test every combination, but I tried it with Sorting and Categories both being used and both not being used with the same result.

Traceback (most recent call last):
File "/Applications/SAB/scripts/AppleTV-Postfix", line 141, in
   os.rename( output_file_processing, output_file )
OSError: [Errno 2] No such file or directory
If you could send the entire output of the script too actually....minus the passwords.
A little more digging makes me think the error is occurring because there are spaces in the folder name structure.

e.g.: /Users/randyharris/desktop/TV/This is a show/

I think those spaces in "This is a show" is causing grief.
randyharris
Full Member
Full Member
Posts: 146
Joined: January 21st, 2010, 5:36 pm

Re: OS X + HandBrakeCLI + Python = convert, iTunes and e-mail

Post by randyharris »

OMG - sorry - brain fade...

I had it coded to .../TV/ and it should be ../TV Shows/

Totally my fault. Sorry Nachoman
Last edited by randyharris on February 5th, 2010, 3:36 pm, edited 1 time in total.
randyharris
Full Member
Full Member
Posts: 146
Joined: January 21st, 2010, 5:36 pm

Re: OS X + HandBrakeCLI + Python = convert, iTunes and e-mail

Post by randyharris »

-
Last edited by randyharris on February 5th, 2010, 6:21 pm, edited 1 time in total.
randyharris
Full Member
Full Member
Posts: 146
Joined: January 21st, 2010, 5:36 pm

Re: OS X + HandBrakeCLI + Python = convert, iTunes and e-mail

Post by randyharris »

Very small thing, I have this set:

DELETE_ORIGINAL = True

However it does not delete the original
imthenachoman
Jr. Member
Jr. Member
Posts: 59
Joined: March 2nd, 2009, 10:58 am
Contact:

Re: OS X + HandBrakeCLI + Python = convert, iTunes and e-mail

Post by imthenachoman »

randyharris wrote: Very small thing, I have this set:

DELETE_ORIGINAL = True

However it does not delete the original
Hurmph. I am stuck away from home due to a snow storm so I cannot get to my home computer to check but I'll look at it when I get home. Not sure why that doesn't work.

And so the earlier issue is fixed now?
forrestgump
Newbie
Newbie
Posts: 4
Joined: November 25th, 2008, 5:59 am

Re: OS X + HandBrakeCLI + Python = convert, iTunes and e-mail

Post by forrestgump »

Nice script all-in-one script.

I use a different solution since my Windows PC has more processing power than my Macbook Pro.
I download via RSS Feeds with my Windows PC with TV sorting enabled. Than I use this nifty program to rename TV episode.
http://neverfear.org/blog/view/21/Autom ... V_episodes

Dexter.S04E12.nzb results into Dexter  -  4x12 - The Getaway.mkv, yes it fetches information from the internet.

Than I invoke TVconvert.exe http://code.google.com/p/sabscripts/ which will convert it to iPhone format and tags it with AtomicParsely. It deletes the original and puts the converted mp4 on my macbook pro via a netshare. On the macbook it has action folders enabled which adds it to my iTunes.

However, I like your python script, but I am not sure whether you tag the videos as "TV Shows" with the appropiate EpisodeNo and SeasonNo.
Post Reply