essandess

Q: EyeTV with Commercial Skipping HD DVR Server

I use EyeTV HD and Turbo.264 HD to record and transcode h.264 shows for playback on a Mac, Apple TV, iPad, iPhone. I also use a self-modified version of etvcomskip to detect and mark commercials, which are then automatically skipped at playback. This makes tv viewing a lot more pleasurable and efficient, and is especially helpful controlling advertiser access to children. In case this is useful to others, I've outlined the steps here:

 

  1. Eric Kaashoek's website Comskip and download his HD-capable Comskip software. "Commercial" commercial-skipping companies (ReplayTV, TiVo, Dish, etc.) are quickly sued out of existence or into submission, even if they simply provide a basic "30-second skip" feature. Comskip is open source beerware that you can download, install, and use yourself, and you can't sue anyone for publishing source code. And rather than providing a simple 30-second skip feature, it detects, marks, and provides that capability for automatic commercial skipping.
  2. Comskip a Windows executable, so you'll need WINE (WINdows Emulator) on OS X to run it. First, download and install Xcode (free from the Apple App store), then download and install and install Macports, then in Terminal, run "sudo port selfupdate; sudo port install wine-devel". Finally, make sure that /Applications/Utilities/X11.app is added to your list of applications to run at login (WINE uses X11 to display Windows windows). Yes, this is windows emulation, but on a half-decent box you'll see that commercial skipping runs at around twice real-time for h.264 HD recordings.
  3. Download and install etvcomskip (and PyeTV if you want EyeTV to work with Front Row). The 2010 version of etvcomskip doesn't work with Comskip's latest HD capabilities, and the latest Comskip mpeg libraries do not allow live tv processing, so install these files to get everything working. Once you done this, you'll have a commercial-skipping HD DVR box on your Mac that can stream to Apple TV, iOS, or export to iTunes.

Comskip software

This lives in the directory /Library/Application Support/ETVcomskip.

 

Edit the file ./comskip/comskip.ini to use these HD-tuned settings, tailored for US cable:

 

detect_method=127;1=black frame, 2=logo, 4=scene change, 8=fuzzy logic, 16=closed captions, 32=aspect ration, 64=silence, 128=cutscenes, 255=all
max_avg_brightness=21;maximum average brightness for a dim frame to be considered black (scale 0 to 255) 0 means autosetting
non_uniformity=50; Set to 0 to disable cutpoints based on uniform frames
max_volume=20; any frame with sound volume larger than this will not be regarded as black frame
logo_at_bottom=1; Set to 1 to search only for logo at the lower half of the video, do not combine with subtitle setting
intelligent_brightness=1 ; Set to 1 to use a USA specific algorithm to tune some of the settings, not adviced outside the USA

volume_slip=150

output_dvrmstb=0; Set to 1 if you're running DVRMS-Toolbox
live_tv=0; set to 1 if you use parallelprocessing and need the output while recording
live_tv_retries=4; change to 16 when using live_tv in BTV, used for mpeg PS and TS

 

Edit the file ./MarkCommercials.app/Contents/Resources/MarkCommercials.py to use WINE to run comskip.exe nicely. Grep for these changes:

 

EyeTV.launch()

 

msg = 'Error: unable to communicate with %s\n' % options.app

 

#cmd = '"/Library/Application Support/ETVComskip/Wine.app/Contents/Resources/bin/wine" "/Library/Application Support/ETVComskip/comskip/comskip.exe" --ini="/Library/Application Support/ETVComskip/comskip/comskip.ini" "%s"' % MpgFile

# MacPorts 64-bit wine

#cmd = '"/Applications/Wine.app/Contents/Resources/bin/wine" "/Library/Application Support/ETVComskip/comskip/comskip.exe" --ini="/Library/Application Support/ETVComskip/comskip/comskip.ini" "%s"' % MpgFile

cmd = '"/opt/local/bin/wine" "/Library/Application Support/ETVComskip/comskip/comskip.exe" --ini="/Library/Application Support/ETVComskip/comskip/comskip.ini" "%s"' % MpgFile

 

# nice the wine command

cmd = "/usr/bin/nice -n 14 " + cmd

 

EyeTV software

EyeTV uses the triggered AppleScripts RecordingStarted and RecordingDone in the directory /Library/Application\ Support/EyeTV/Scripts/TriggeredScripts. Open these using the AppleScript Editor and use these scripts instead. You will probably have to Save As... on your desktop, then copy the updated script back into the correct directory to avoid permissions issues.

 

RecordingStarted.scpt

 

on RecordingStarted(recordingID)

    delay 10

    -- comskip81 uses ffmpeg and does not support live tv; put this in RecordingDone.scpt

    --    set cmd to "export DISPLAY=:0.0; /usr/bin/nice -n 5 '/Library/Application Support/ETVComskip/MarkCommercials.app/Contents/MacOS/MarkCommercials' --log " & recordingID & " &> /dev/null &"

    -- display dialog cmd

    -- set cmd to "env > /tmp/etv_test.log &"

    -- do shell script cmd

 

    --disable this if you do not want a logfile written

    write_to_file((short date string of (current date) & " " & time string of (current date)) & "Recording Started run for ID: " & recordingID & (ASCII character 13), (path to "logs" as string) & "EyeTV scripts.log", true)

end RecordingStarted

 

on write_to_file(this_data, target_file, append_data)

    --from http://www.apple.com/applescript/sbrt/sbrt-09.html

    try

        set the target_file to the target_file as string

        set the open_target_file to open for access file target_file with write permission

        if append_data is false then set eof of the open_target_file to 0

        write this_data to the open_target_file starting at eof

        close access the open_target_file

        return true

    on error

        try

            close access file target_file

        end try

        return false

    end try

end write_to_file

RecordingDone.scpt

 

-- Run the python MarkCommercials script for the given recording

-- this must be run with the RecordingStarted script

-- it will check if there were multiple PIDs for the recording and runs MarkCommercials for each pid

-- requires updated MarkCommercials which allows specifying the pid

-- by Ben Blake, September 2009

 

-- modified for latest Comskip, which cannot be run until after recording is finished; waits for Turbo.264 HD to stop running as well

-- S.T.Smith

 

global LogMsg

 

on RecordingDone(recordingID)

 

    set ProcessName to "Elgato H.264 Decoder"

    set DelayTime to 60

    set MaxDelays to 4 * 60 -- four hours

 

    -- delay until the process slows down or stops running

    repeat MaxDelays times

        delay DelayTime -- delay at least once to give it a chance to start

        set pcpu to CPUPercentage(ProcessName)

        if pcpu is equal to "" or pcpu < 2.0 then exit repeat -- break out of delay loop if it's not running

    end repeat

 

    delay 10

    -- comskip81 uses ffmpeg and does not support live tv; take this from RecordingStarted.scpt

    set cmd to "export DISPLAY=:0.0; /usr/bin/nice -n 5 '/Library/Application Support/ETVComskip/MarkCommercials.app/Contents/MacOS/MarkCommercials' --log " & recordingID & " &> /dev/null &"

    -- display dialog cmd

    -- set cmd to "env > /tmp/etv_test.log &"

    do shell script cmd

 

    set LogMsg to ""

    CheckMultiplePIDs(recordingID)

 

    --disable this if you do not want a logfile written

    if (count of LogMsg) > 0 then

        write_to_file((short date string of (current date) & " " & time string of (current date)) & LogMsg & (ASCII character 13), (path to "logs" as string) & "EyeTV scripts.log", true)

    end if

end RecordingDone

 

-- testing code: this will not be called when triggered from EyeTV, but only when the script is run as a stand-alone script

on run

    tell application "EyeTV.app"

        set rec to «class Unqu» of item 1 of every «class cRec»

 

        my RecordingDone(rec)

    end tell

end run

 

-- compute the percentage CPU used by ProcessName

on CPUPercentage(ProcessName)

    set ProcessPS to do shell script ("ps -axwwc | grep '" & ProcessName & "' | grep -v grep || true")

    if ProcessPS is not equal to "" then

        set ProcessID to word 1 of ProcessPS

        set ProcessPS to do shell script ("ps -xwwco pid,ppid,%cpu -p " & ProcessID & " | tail -1")

        set ProcessCPU to word 3 of ProcessPS

        return ProcessCPU as number

    else

        return ""

    end if

end CPUPercentage

 

on CheckMultiplePIDs(recordingID)

    --check if there are multiple Video PIDs in the file

 

    tell application "EyeTV.app"

        set input_text to my read_from_file((path to "logs" as string) & "ETVComskip" & ":" & recordingID & "_comskip.log")

        if (count of (input_text as string)) > 0 then

            set logdata to every paragraph of input_text

            set logdata_lastrow to (item ((count of logdata) - 1) of logdata) as string

 

            if (items 1 thru 19 of logdata_lastrow) as string = "Video PID not found" then

                --multiple Video PIDs, rerun MarkCommercials until successful

 

                set recrdingIDInteger to recordingID as integer

                set rec to «class cRec» id recrdingIDInteger

                set LogMsg to "RecordingDone found multiple PIDs for recording ID: " & recordingID & ", Channel " & («class Chnm» of rec) & " - " & («class Titl» of rec)

 

                set PIDs to (items 44 thru ((count of logdata_lastrow) - 2) of logdata_lastrow) as string

                set delims to AppleScript's text item delimiters

                set AppleScript's text item delimiters to ", "

                set PID_List to {}

                set PID_List to every word of PIDs

                set AppleScript's text item delimiters to delims

 

                repeat with pid in PID_List

                    my launchComSkip(recordingID, pid)

                    repeat while (my mcIsRunning())

                        delay 5

                    end repeat

                end repeat

 

            end if

        end if

    end tell

end CheckMultiplePIDs

 

on read_from_file(target_file)

    --return the contents of the given file

    set fileRef to (open for access (target_file))

    set txt to (read fileRef for (get eof fileRef) as «class utf8»)

    close access fileRef

    return txt

end read_from_file

 

on write_to_file(this_data, target_file, append_data)

    --from http://www.apple.com/applescript/sbrt/sbrt-09.html

    try

        set the target_file to the target_file as string

        set the open_target_file to open for access file target_file with write permission

        if append_data is false then set eof of the open_target_file to 0

        write this_data to the open_target_file starting at eof

        close access the open_target_file

        return true

    on error

        try

            close access file target_file

        end try

        return false

    end try

end write_to_file

 

on launchComSkip(recID, pid)

    if pid = "" then

        set cmd to "export DISPLAY=:0.0; /usr/bin/nice -n 5 '/Library/Application Support/ETVComskip/MarkCommercials.app/Contents/MacOS/MarkCommercials' --force --log " & recID & " &> /dev/null &"

    else

        set cmd to "export DISPLAY=:0.0; /usr/bin/nice -n 5 '/Library/Application Support/ETVComskip/MarkCommercials.app/Contents/MacOS/MarkCommercials' --force --log " & recID & " --pid=" & pid & " &> /dev/null &"

    end if

 

    do shell script cmd

end launchComSkip

 

on mcIsRunning()

    set processPaths to do shell script "ps -xww | awk -F/ 'NF >2' | awk -F/ '{print $NF}' | awk -F '-' '{print $1}' "

    return (processPaths contains "MarkCommercials")

end mcIsRunning



<Links Edited By Host>

Mac mini Server (Mid 2010), Mac OS X (10.7.4), Lion Server, EyeTV HD, Turbo.264 HD

Posted on Dec 5, 2015 1:28 PM

Close

Q: EyeTV with Commercial Skipping HD DVR Server

  • All replies
  • Helpful answers

Previous Page 2 of 10 last Next
  • by Niccum,

    Niccum Niccum Oct 13, 2012 5:28 AM in response to essandess
    Level 1 (0 points)
    Oct 13, 2012 5:28 AM in response to essandess

    Brandon,

    I am doing a similar routine as I also like to manually tweak the commercial markers.  I have found on my system that I can skip the compress step and go straight to the export.  The export to AppleTV recognizes the commercial markers on my setup.  So at least you can eliminate the compress step if you want to.

     

    For those of us that are anal about fine tuning the markers, it would be nice to be able to be able to check them off as prepared for export and then have an export routine kick in automatically in the middle of the night to do the longer export work.  That is about as automated as I can think of if still needing to fine tune the commercial marks.

     

    Eric

  • by essandess,

    essandess essandess Oct 13, 2012 6:35 AM in response to Brandon Riffel
    Level 1 (28 points)
    Applications
    Oct 13, 2012 6:35 AM in response to Brandon Riffel

    Yes, every step works great in Mountain Lion and previous OS X versions. A Turbo.264 HD dongle isn't necessary (I believe), as I wrote the RecordingDone script above so that commercial marking waits for this process to complete:

     

    set ProcessName to "Elgato H.264 Decoder"

     

    I believe that EyeTV runs this whether a Turbo.264 dongle is attached or not. Someone without a dongle—please confirm that this is correct. If that process isn't there, or isn't running at more than 2 percent CPU load, then commercial marking kicks in.

     

    That said, I sure need a dongle for a mid-2010 Mac Mini, and though the latest Mac Mini probably wont see major speed improvements from a dongle, it's nice to offload transcoding from the CPU and save it for other major tasks, like running a server and recording new shows. Elgato may be exploiting the GPU on newer models, but I've not heard this.

     

    An EyeTV HD box can be set up to save all recorded shows for streaming to iPad (720p). If you do this, then export "iPad" to iTunes (automatically or manually), then EyeTV will just copy this file without an additional transcoding step. Commercial marks in .edl files are cut out at the transcoding step. I've found that it's better to add chapter headings to h.264 files rather than delete content automatically. The steps look like:

     

    Record -> Transcode -> Mark Commercials [ -> Export to iTunes]

     

    Finally, I'd like to remind people to ask Elgato to add chapter navigation to their iOS streaming app, which will remove the extra iTunes step in many cases. Submit a ticket and make the feature request: http://tickets.kb.elgato.com/?language=en-us

  • by gardnern,

    gardnern gardnern Oct 13, 2012 1:35 PM in response to essandess
    Level 1 (0 points)
    Oct 13, 2012 1:35 PM in response to essandess

    I have an HDHomeRun which outputs MPEG-2.  The process that runs on my machine from EyeTV is "Elgato MPEG-2 Decoder" so I had to tweak the script to check that process for pausing to wait for the export to finish.

     

    Below is the MarkCommercials.py with Growl fixed up.  I know it works with Growl 2.0 but I think it should work back to 1.3.  The script has to be called with --growl on the command line.

     

    I also added a --m4vonly that will just chapter the m4v files on stuff that has been previously marked.

     

    This is the first time I've coded in python, it all works but it might be a tad crude.

    #!/usr/bin/env python
    #
    # MarkCommercials.py, EyeTV3 version
    #
    # Copyright (c) 2008, Jon A. Christopher
    # Copyright (c) 2008, TeamSTARS Dick Gordon and Rick Kier
    # Licensed under the GNU General Public License, version 2
    #
    #
    # Run comskip on the specified file and replace the markers in the .eyetvr
    # file with the new markers.
    #
    # if no arguments show id, recording name...
    #
    # If argument is 'all', process all recordings which don't
    # have markers and do not match any exclude information from the cfg file.
    #
    # If argument is'forceall', process all recordings except those that match
    # any exclude information in the cfg file.
    #
    # Otherwise, argument is treated as an EyeTV recording id, and that
    # recording is processed if it is not excluded and it does NOT have any markers.
    #
    # To be done:
    #  exporting - not needed??
    #  exclude channels
    #   Issue: some channels are 0
    #  exclude titles
    #  exclude station names
    #   Issue: some station names are blank
    #  Catch ^c when in c program - comskip throws SIGINT's away?? (see mpeg2dec.c)
    #  Catch ^c when in python program - coded
    #  EXCLUDED_TITLES and EXCLUDED_CHANNELS are NOT empty lists
    #  Handle multiple video pids.
    # Added argument for PID - Ben Blake September 2009
    
    # STS
    # Added macports wine, nice-ness, mp4chaps chapters headings at commercials
    
    import sys, os, string, os.path
    import time
    import math
    import traceback
    from optparse import OptionParser
    from ConfigParser import SafeConfigParser
    
    from os import listdir
    from os.path import isfile, join
    
    # Exit Codes
    #  Everything worked ok
    successExitCode = 0
    #  Unable to import appscript
    importExitCode = 1
    #  Unable to find the recoring specified
    noRecordingExitCode = 2
    #  Error getting recordings from EyeTV
    getRecordingsErrorExitCode = 3
    #  Unknown exit code from comskip
    unknownComskipErrorExitCode = 4
    #  Error when accessing plist file
    accessPlistFileErrorExitCode = 5
    #  Error when accessing config file
    accessConfigFileErrorExitCode = 6
    #  Keyboard interrupt (^c)
    keyboardInterruptExitCode = 7
    #  Unable to communicate with the application
    communicationsErrorExitCode = 8
    
    # provided with appscript package
    try:
        import aem
        from appscript import *
    except ImportError, e:
        sys.stderr.write('Error: importing appscript\n%s\n' % e)
        sys.exit(importExitCode)
    
    version = '0.4.0'
    # Cfg file definitions and variables
    userSectionName = 'User Section'
    listDelimiterName = 'LIST_DELIMITER'
    excludedTitlesName = 'EXCLUDED_TITLES'
    excludedChannelsName = 'EXCLUDED_CHANNELS'
    excludedStationNamesName = 'EXCLUDED_STATION_NAMES'
    excludedTitles = []
    excludedChannels = []
    excludedStationNames = []
    # General variables
    options = None
    args = None
    recordingCount = 0
    comskipLogPathName = None
    log = None
    growl = None
    eyeTV = None
    pathToComskip = None
    nameOfComskip = 'comskip'
    comskipLocations = ['.', r'/Library/Application Support/ETVComskip']
    
    # for debugging. when False, will not actually run comskip, but will do everything else
    RUN_COMSKIP = True
    
    # Get the executable directory
    ETVComskipDir = os.path.abspath(os.path.dirname(__file__))
    
    # Growl support
    commercialStart = 'Start'
    commercialStartDescription = 'Start detecting commercials'
    commercialStop = 'Stop'
    commercialStopDescription = 'Stop detecting commercials'
    programName = 'Mark Commercials'
    allNotificationsList = [commercialStart, commercialStop]
    
    def InitGrowl():
        """docstring for InitGrowl"""
        global growl
        global allNotificationsList
        
        # Should we use growl?
        if not options.growl:
            # No
            return
        growl = app('Growl')
        enabledNotificationsList = allNotificationsList
        WriteToLog('Registering with growl\n')
        try:
            growl.register(as_application=programName,
                           all_notifications=allNotificationsList,
                           default_notifications=enabledNotificationsList)
        except Exception, e:
            WriteToLog('Error: registering with growl\n  %s\n' % e)
            growl = None
    
    
    def sendGrowlNotification(name, title, description):
        """docstring for sendGrowlNotification"""
        # Send a Notification...
        if growl is not None:
            WriteToLog('Sending notification via growl (%s, %s, %s)\n' % (programName, title,description))
            try:
                growl.notify(with_name=name,
                             title=programName,
                             description=description,
                             application_name=programName)
            except Exception, e:
                WriteToLog('Error: growl notify\n  %s\n' % e)
    
    
    
    # Create the log file
    def GetLog(name=None):
        """docstring for GetLog"""
        global log
        global comskipLogPathName
        
        # Should we log?
        if not options.log:
            return
        
        # Is the log directory created?
        fullPath = os.path.expanduser('~/Library/Logs/ETVComskip')
        if not os.path.isdir(fullPath):
            # No, create it.
            os.mkdir(fullPath)
        # Create the log
        if name is None:
            name = time.strftime('%m-%d-%Y %H-%M-%S')
        comskipLogPathName = os.path.join(fullPath, name + '_comskip.log')
        log = open(os.path.join(fullPath, name + '.log'), 'w')
    
    def WriteToLog(message):
        """docstring for WriteToLog"""
        if options.log:
            if type(message) == type(u""):
                message=message.encode("utf-8")
            log.write('%s - %s' % (time.asctime(), message))
            log.flush()
    
    def CheckForApplicationCommunications(retries=3):
        """docstring for CheckForApplicationCommunications"""
        global EyeTV
        
        # launch the application
        EyeTV.launch()
        WriteToLog('Checking communications to %s with %d retries\n' % (options.app, retries))
        for attempt in range(retries):
            try:
                # Get the recordings
                EyeTV.recordings.get()
                # It worked - break out of here
                WriteToLog('  Attempt %d worked\n' % (attempt + 1))
                break
            except Exception, e:
                WriteToLog('  Attempt %d failed\n    %s\n' % ((attempt + 1), e))
                EyeTV = app(options.app)
                time.sleep(0.5)
                continue
        else:
            msg = 'Error: unable to communicate with %s\n' % options.app
            WriteToLog(msg)
            sys.stderr.write(msg)
            sys.exit(communicationsErrorExitCode)
    
    def GetRecordings(retries=0):
        """docstring for GetRecordings"""
        global EyeTV
        
        WriteToLog('Getting recordings\n')
        try:
            recordings = EyeTV.recordings.get()
        except Exception, e:
            msg = 'Error: unable to get recordings\n%s\n' % e
            WriteToLog(msg)
            sys.stderr.write(msg)
            sys.exit(getRecordingsErrorExitCode)
        WriteToLog('Recordings: %s\n' % recordings)
        return recordings
    
    # Possibly run comskip and return the name of a plist file with commercial markers in it
    def GetPlistFile(etvr_file, run_comskip=True):
        
        FileDir = os.path.dirname(etvr_file)
        dir, fil = os.path.split(etvr_file)
        FileRoot, ext = os.path.splitext(fil)
        
        MpgFile = FileRoot + ".mpg"
        PlistFile = FileRoot + ".edl"
        
        #cmd = '"/Library/Application Support/ETVComskip/Wine.app/Contents/Resources/bin/wine" "/Library/Application Support/ETVComskip/comskip/comskip.exe" --ini="/Library/Application Support/ETVComskip/comskip/comskip.ini" "%s"' % MpgFile
        # MacPorts 64-bit wine
        #cmd = '"/Applications/Wine.app/Contents/Resources/bin/wine" "/Library/Application Support/ETVComskip/comskip/comskip.exe" --ini="/Library/Application Support/ETVComskip/comskip/comskip.ini" "%s"' % MpgFile
        cmd = '"/opt/local/bin/wine" "/Library/Application Support/ETVComskip/comskip/comskip.exe" --ini="/Library/Application Support/ETVComskip/comskip/comskip.ini" "%s"' % MpgFile
        
        if options.pid <> "":
             cmd += " --pid=" + options.pid
    
        outputName = '/dev/null'
        if options.log:
            cmd += ' > %s 2>&1' % comskipLogPathName
        else:
            cmd += ' > %s 2>&1' % '/dev/null'
        if options.verbose:
            cmd += ' --verbose=%d' % options.verbose
        # nice the wine command
        cmd = "/usr/bin/nice -n 14 " + cmd
        WriteToLog('Changing directory to %s\n' % FileDir)
        os.chdir(FileDir)
        if run_comskip:
            #get the show name from the Directory Path
            showName=os.path.splitext(os.path.basename(FileDir))[0]
            # Notify the user
            sendGrowlNotification(commercialStart, commercialStart + " " + showName, commercialStartDescription + " on " + showName)
            # TBD stop comskip when ^c happens
            if not options.m4vonly:
                WriteToLog('Running: %s\n' % cmd)
                rc = os.system(cmd)
            else:
                WriteToLog('Skipped Comm Search, attempting to just add m4v Markers.')
                rc=0
            # Add the Comskip information as chapters to all m4v files
            mp4chaps_all_m4v(FileDir)
            # Notify the user
            sendGrowlNotification(commercialStop, commercialStop + " " + showName, commercialStopDescription + " on " + showName)
            WriteToLog('Return code is: %d, 0x%x\n' % (rc, rc))
            errorCode = (rc >> 8) & 0xff
            WriteToLog('Error code is: %d, 0x%x\n' % (errorCode, errorCode))
            # Error code:
            #   3 = no Video pid found
            #   2 = Can't open the mpeg2 file TBD
            #   1 = Commercials found
            #   0 = Commercials not found
            if errorCode  == 2:
                msg = 'Unable to open mpeg2 file return from "comskip": %d\n' % errorCode
                WriteToLog(msg)
                sys.stderr.write(msg)
                return None
            elif errorCode == 3:
                msg = 'No video pid found return from "comskip": %d\n' % errorCode
                WriteToLog(msg)
                sys.stderr.write(msg)
                return None
            elif errorCode == 1:
                WriteToLog('Commercials found by comskip\n')
                return PlistFile
            elif errorCode == 0:
                WriteToLog('No commercials found by comskip\n')
                return None
            else:
                msg = 'Error: unknown error code from comskip: %d, assuming it worked.\n' % errorCode
                WriteToLog(msg)
                return PlistFile
    
    # return start and ending times for the given line
    def TimeChop(line):
        fields = line.split("\t")
        start = (float(fields[0]))
        end = (float(fields[1]))
        return (start,end)
    
    # given a plist file, return a markers array suitable for adding to a recording
    def GetMarkersArray(PlistFile):
        try:
            file = open(PlistFile)
            lines = file.readlines()
            file.close()
        except Exception, e:
            msg = 'Error: accessing %s\n%s\n' % (PlistFile, e)
            WriteToLog(msg)
            sys.stderr.write(msg)
            sys.exit(accessPlistFileErrorExitCode)
    
        WriteToLog('Plist file contents: %s\n' % lines)
        markers=[]
        for line in lines:
            WriteToLog("Processing plist file line: '%s'\n" % line)
            start,end = TimeChop(line)
            WriteToLog('Adding marker, start: %d, end: %d\n' % (start, end))
            marker = {}
            marker['position'] = start
            marker[aem.AEType('leng')] = end - start
            markers.append(marker)
    
        return markers
    
    def ProcessRecording(recording, run_comskip):
        
        global recordingCount
        
        channel = recording.channel_number()
        title = recording.title()
        stationName = recording.station_name()
        
        recordingCount += 1
        msg = '%2d. Processing "%s" on [%s] channel [%s]...' % (recordingCount, title, stationName, channel)
        WriteToLog('%s\n' % msg)
        print msg.encode("utf-8")
        
        # Should excludes be allowed?
        if not options.noexclude:
            # Yes, Is this one allowed?
            #  User can exclude titles, channels and station names
            # Channel
            msg='  Channel: %s' % channel
            print msg.encode("utf-8"),
            if str(channel) in excludedChannels:
                WriteToLog('Skipped due to channel match\n')
                print ' skipped'
                return
            print ', not skipped'
            # Title
            msg='  Title: %s' % title
            print msg.encode("utf-8"),
            if title in excludedTitles:
                WriteToLog('Skipped due to title match\n')
                print ' skipped'
                return
            print ', not skipped'
            # Station name
            msg='  Station name: %s' % stationName
            print msg.encode("utf-8"),
            if stationName in excludedStationNames:
                WriteToLog('Skipped due to station name match\n')
                print ' skipped'
                return
            print ', not skipped'
    
        # Get its path
        etvr_path = recording.location.get().path
        WriteToLog('Path to recording is %s\n' % etvr_path)
        
        # get the plist file for this recording, and make a markers array for it
        Plist = GetPlistFile(etvr_path, run_comskip)
        # Did we get a plist file?
        if Plist is not None:
            # Yes, convert it.
            markers = GetMarkersArray(Plist)
            # and finally, set them
            WriteToLog('Setting markers on recording\n')
            markers_string = str(markers)
            WriteToLog('Adding marker: %s\n' % (markers_string))
            recording.markers.set(markers)
    
    mp4chaps = '/opt/local/bin/mp4chaps'
    
    def sec2hhmmss(secs):
        """Convert seconds to string HH:MM:SS.SSS format."""
        rem = secs/3600
        hh = int(rem)
        rem = (rem-hh)*60
        mm = int(rem)
        rem = (rem-mm)*60
        ss = int(rem)
        rem = (rem-ss)
        rem = '%.3f' % rem  # millisecond precision
        rem = rem.replace('0.','.')
        return '%02d:%02d:%02d%s' % (hh,mm,ss,rem)
    
    def split_whitespace_nolibs(str):
        """Split a string by whitespace without using re or shlex libraries.."""
        strs = filter(None,str.split('\t'))
        strs = map(lambda s: s.split(' '),strs)
        strs = filter(None,[item for sublist in strs for item in sublist])
        return strs
    
    def edl2mp4chaps(edl_file,file):
        """Convert an edl file into an mp4chaps file."""
        txt_file = file.replace('.m4v','.chapters.txt')
        ftxt = open(txt_file,'w')
        
        comskipno = 0
        comskipchapno = 0
        lines = [line.strip() for line in open(edl_file)]
        for line in lines:
            times = map(float,split_whitespace_nolibs(line))
            if (comskipno == 0 and times[0] != 0.0):
                comskipno += 1
            if (len(times) < 2 or times[2] == 0.0):
                if (times[0] != 0.0):
                    ftxt.write('%s Chapter %d End\n' % (sec2hhmmss(times[0]),comskipno))
                else:
                    ftxt.write('00:00:00.000 Beginning\n')
                comskipno += 1
                ftxt.write('%s Chapter %d Start\n' % (sec2hhmmss(times[1]),comskipno))
            else:
                # never seen this case, but here for logical consistency
                comskipchapno += 1
                if (times[0] != 0.0):
                    ftxt.write('%s Chapter %d Start\n' % (sec2hhmmss(times[0]),comskipchapno))
                else:
                    ftxt.write('00:00:00.000 Beginning\n')
                ftxt.write('%s Chapter %d End\n' % (sec2hhmmss(times[1]),comskipchapno))
        ftxt.close()
        return
    
    def mp4chaps_all_m4v(dir):
        """Apply an edl file's entries to all m4v files in a directory."""
        os.chdir(dir)
        edl_file = ""
        files = [ file for file in listdir(dir) if isfile(join(dir,file)) ]
        for file in files:
            if file.find('.edl') != -1:
                edl_file = file
                break
        if edl_file != "" and os.path.isfile(mp4chaps):
            for file in files:
                if file.find('.m4v') != -1:
                    # remove all chapters
                    cmd = mp4chaps + ' -r ' + file + ' > /dev/null 2>&1'
                    rc = os.system(cmd)
                    # create chapter file
                    edl2mp4chaps(edl_file,file)
                    # import chapters
                    cmd = mp4chaps + ' -i ' + file + ' > /dev/null 2>&1'
                    rc = os.system(cmd)
        return
    
    def main():
        global options
        global args
        global excludedTitles
        global excludedChannels
        global excludedStationNames
        global log
        global EyeTV
        
        # Do the options
        usage = "usage: %prog [options] [RECORDING-ID | 'all' | 'forceall']"
        parser = OptionParser(usage=usage, version=version)
        parser.add_option("--noexclude",
                          action="store_true", dest="noexclude", default=False,
                          help="Do NOT exclude recordings specified in cfg file, default=%default")
        parser.add_option("--force",
                          action="store_true", dest="force", default=False,
                          help="Force commercial marking on specified RECORDING-ID. Allows marking when markers already exist, default=%default")
        parser.add_option("--growl",
                          action="store_true", dest="growl", default=False,
                          help="Enable growl notification, default=%default")
        parser.add_option("--log",
                          action="store_true", dest="log", default=False,
                          help="Enable logging, default=%default")
        parser.add_option("--app",
                          dest="app", default='EyeTV',
                          help="Specify EyeTV application name, default=%default")
        parser.add_option("--verbose",
                          type='int',
                          dest="verbose", default=0,
                          help="Verbosity level, 0-10, default=%default")
        parser.add_option("--pid",
                          dest="pid", default='',
                          help="Specify the Video PID, default=%default")
        parser.add_option("--m4vonly",
                          dest="m4vonly", default=False,
                          help="Only chapter m4v files that have already had commercials marked, don't try to mark commercials. default=%default")
        (options, args) = parser.parse_args()
        
        if len(args):
            name = args[0]
        else:
            name = None
        GetLog(name=name)
        WriteToLog('%s, %s starting\n' % (sys.argv[0], version))
        WriteToLog('Command line: %s\n' % sys.argv)
        WriteToLog('Application name: %s\n' % options.app)
        print '\t\t%s\t%s\n' % (os.path.splitext(os.path.basename(sys.argv[0]))[0], version)
    
        # Get the app
        EyeTV = app(options.app)
    
        # Use growl notifications
        InitGrowl()
        # Get our configuration file & data
        configInput = SafeConfigParser()
        try:
            cfgFilesRead = configInput.read([os.path.join(os.path.dirname(sys.argv[0]), 'MarkCommercials.cfg'),
                                             os.path.expanduser('~/Library/Application Support/ETVComskip/MarkCommercials.cfg')])
        except Exception, e:
            msg = 'Error: reading configuration file\n%s\n' % e
            WriteToLog(msg)
            sys.stderr.write(msg)
            sys.exit(accessConfigFileErrorExitCode)
        WriteToLog('Cfg files read: %s\n' % cfgFilesRead)
        if cfgFilesRead:
            # Process the user's section
            if configInput.has_section(userSectionName):
                # Process the user's section
                #   Get the delimiter
                listDelimiter = configInput.get(userSectionName, listDelimiterName)
                if configInput.has_option(userSectionName, excludedChannelsName):
                    for channel in configInput.get(userSectionName, excludedChannelsName).split(listDelimiter):
                        excludedChannels.append(channel)
                if configInput.has_option(userSectionName, excludedTitlesName):
                    for title in configInput.get(userSectionName, excludedTitlesName).split(listDelimiter):
                        excludedTitles.append(title)
                if configInput.has_option(userSectionName, excludedStationNamesName):
                    for title in configInput.get(userSectionName, excludedStationNamesName).split(listDelimiter):
                        excludedStationNames.append(title)
            WriteToLog('List Delimiter: %s\n' % listDelimiter)
            WriteToLog('Excluded Channels: %s\n' % excludedChannels)
            WriteToLog('Excluded Titles: %s\n' % excludedTitles)
            WriteToLog('Excluded Station names: %s\n' % excludedStationNames)
    
        # Test communications with application
        CheckForApplicationCommunications()
    
        # Get the location of the commercial skipper
        #findComskip()
    
        # Show the IDs and program names when there are no arguments
        #    replace any non ascii characters with ?
        if len(args) == 0:
            for rec in GetRecordings():
                programName = os.path.split(os.path.splitext(os.path.dirname(rec.location.get().path))[0])[1]
                outputName = ''
                for char in programName:
                    # Insure the character is ascii
                    if ord(char) <= 127:
                        outputName += char
                    else:
                        outputName += '?'
                msg = '  %d = [%s], [%s], [%s]' % (rec.unique_ID.get(), outputName, rec.channel_number(), rec.station_name())
                WriteToLog('%s\n' % msg)
                print msg.encode("utf-8")
            return successExitCode
    
        if args[0] == "all" or args[0] == "forceall":
            # batch mode, process all recordings without markers
            recs = GetRecordings()
            for rec in recs:
                markerCount = len(rec.markers.get())
                WriteToLog('Marker count: %d\n' % markerCount)
                if markerCount == 0 or args[0] == "forceall":
                    ProcessRecording(rec, RUN_COMSKIP)
        else:
            # triggered mode, process just the listed recording
            recs = GetRecordings()
            recordingRequested = int(args[0])
            for rec in recs:
                if rec.unique_ID() == recordingRequested:
                    WriteToLog('Found recording %d\n' % recordingRequested)
                    break
            else:
                msg = 'Error: unable to find recording %d\n' % recordingRequested
                WriteToLog(msg)
                sys.stderr.write(msg)
                sys.exit(noRecordingExitCode)
            markerCount = len(rec.markers.get())
            WriteToLog('Marker count: %d\n' % markerCount)
            # Recording already have markers?
            if markerCount == 0 or options.m4vonly:
                # No
                try:
                    ProcessRecording(rec, RUN_COMSKIP)
                except Exception,e:
                    exc_type, exc_value, exc_traceback = sys.exc_info()
                    WriteToLog(repr(traceback.format_exception(exc_type, exc_value,exc_traceback)))
    
            # Is the recording already marked but the user wants it done again?
            elif markerCount != 0 and options.force:
                # Yes
                WriteToLog('Recording already marked - use forcing with --force option\n')
                ProcessRecording(rec, RUN_COMSKIP)
            else:
                # Recording already maked and user doesn't want it done again
                msg = 'Recording previously marked'
                WriteToLog('%s\n' % msg)
                print '  ',
                print msg.encode("utf-8")
        return successExitCode
    
    if __name__ == '__main__':
        '''
            Call main
            '''
        try:
            exitStatus = main()
        except KeyboardInterrupt, e:
            msg = 'Error: %s\n' % e
            WriteToLog(msg)
            sys.stderr.write(msg)
            sys.exit(keyboardInterruptExitCode)
            pass
        WriteToLog('Exiting\n')
        sys.exit(exitStatus)
    
  • by sheldoa,

    sheldoa sheldoa Oct 18, 2012 8:39 PM in response to essandess
    Level 1 (0 points)
    Oct 18, 2012 8:39 PM in response to essandess

    I have been super excited to attempt to get this working on my install of EyeTV.  The issue that I'm running into is that when I attempt to either run "MarkCommercials all" from the command line the result I get is this:

    1. Processing "Castle" on [KSTP HDTV] channel [431]...

      Channel: 431 , not skipped

      Title: Castle , not skipped

      Station name: KSTP HDTV , not skipped

    Error: accessing 00000000162cf6e4.edl

    (2, 'No such file or directory')

     

    However when I run comskip directly from the command line using the following command (running from inside the recording's package):

    /opt/local/bin/wine /Library/Application\ Support/ETVComskip/comskip/comskip.exe --ini=/Library/Application\ Support/ETVComskip/comskip/comskip.ini 00000000162cda88.mpg

    There is a text file that is output named "00000000162cda88.txt".  I've also been having issues with the program crashing but I'm betting that was one of my recordings being funky.  Any thoughts on how to go about getting this to work?  I'm getting the same result off of the RecordingDone.scpt as well.  Any help would be appreciated.  Thanks!

  • by essandess,

    essandess essandess Oct 19, 2012 6:58 AM in response to sheldoa
    Level 1 (28 points)
    Applications
    Oct 19, 2012 6:58 AM in response to sheldoa

    @sheldoa

     

    Looks like a mismatch between the .mpg file name and the .edl file name, whose root name should be identical. Troubleshooting:

     

    1. Cut-and-paste--posting-error on your part, or is this real?
    2. Is this a problem for all recordings or a single recording? To test (using @gardnern's MarkCommercials.py above):
      • $ /Library/Application\ Support/ETVComskip/MarkCommercials.app/Contents/MacOS/MarkCommercials  # prints out all your EyeTV recordID's
      • $ /Library/Application\ Support/ETVComskip/MarkCommercials.app/Contents/MacOS/MarkCommercials <recordID>  # rerun comskip
      • $ /Library/Application\ Support/ETVComskip/MarkCommercials.app/Contents/MacOS/MarkCommercials --m4vonly=true <recordID>
      • $ /Library/Application\ Support/ETVComskip/MarkCommercials.app/Contents/MacOS/MarkCommercials all  # mark 'em all!
    3. Use the command
      • $ find /The/Full/POSIX/Path/to/Your/EyeTV\ Archive -name '00000000162cda88.mpg'
    4. Then look at this .eyetv's directories contents. It should look like this:

     

    $ ls -1

    0000000016206100.eyetvp

    000000001630f938.edl

    000000001630f938.exported_inodes.txt

    000000001630f938.eyetvi

    000000001630f938.eyetvr

    000000001630f938.iPad.chapters.txt

    000000001630f938.iPad.m4v

    000000001630f938.log

    000000001630f938.logo.txt

    000000001630f938.mpg

    000000001630f938.thumbnail.tiff

    000000001630f938.tiff

    000000001630f938.txt

     

    @gardnern Thanks for the updated script! Works great on my system, and it's been nice to have the m4vonly option to test to improved functionality I've added.

     

    @all interested: I've written script that allows automatic, asynchronous iTunes exports and commercial marking. I'm almost done testing them and will post soon to ETVComskip's googlecode page. These scripts transparently and automatically address all the issues discussed above: you can do exports or commercial marking in any order you want, or simultaneously, and both scripts take care of adding commercial skipping information to the iTunes files independently. There is an EyeTV ExportDone.scpt that saves all exported files' inode numbers to a new file called root-name.exported_inodes.txt (see above), then imports the .edl markings as h.264 chapters if there's a corresponding .edl file. And there's an updated MarkCommercials.py script that also imports  .edl markings as h.264 chapters into all existing exports if there's a corresponding .exported_inodes.txt file. iTunes can and will rename and reorganize the exported files, and the python script finds whatever iTunes filenames that have the inode number saved by ExportDone.scpt. It all works asynchronously, and though I've only tested it on my iPad, I expect it to work with Apple TV and many individual iTunes and HTPC setups. So watch this space.

  • by essandess,

    essandess essandess Oct 19, 2012 7:20 AM in response to essandess
    Level 1 (28 points)
    Applications
    Oct 19, 2012 7:20 AM in response to essandess

    P.S. Did you update your MarkCommercials.py script?

     

    $ cd /Library/Application\ Support/ETVComskip/MarkCommercials.app/Contents/Resources

    $ sudo cp ./MarkCommercials.py ./MarkCommercials.py.orig

    $ sudo cp ~/Desktop/MarkCommercials.py ./MarkCommercials.py

     

    (We probably need to post a recap of which files must be updated.)

  • by gardnern,

    gardnern gardnern Oct 19, 2012 9:26 AM in response to essandess
    Level 1 (0 points)
    Oct 19, 2012 9:26 AM in response to essandess

    I'm not much for exporting to iTunes however pushing an appropriately named file to Plex would be pretty spiffy.

     

    Does your export process do any renameing and/or file copying to specific locations?

  • by essandess,

    essandess essandess Oct 19, 2012 11:19 AM in response to gardnern
    Level 1 (28 points)
    Applications
    Oct 19, 2012 11:19 AM in response to gardnern

    No. EyeTV takes care of all exporting. The (modified) ETVComskip setup simply does two things: (1) adds a .edl Edit Decision List file to the .eyetv directory for commercial skipping during EyeTV/Turbo.264/etc. playback; (2) adds the .edl cutpoints as chapters within the h.264 files created by EyeTV, whether in the EyeTV Archive or exported to iTunes.

     

    It would be quite simple to modify EyeTV's RecordingDone script to export files to whichever location you would like to see them in. It would also be simple to copy part of the to-be-posted ExportDone AppleScript  that remembers the inode information of each exported file, allowing MarkCommercials.py to asynchronously add .edl information to these exported files.

     

    BTW, I tried Plex once but gave up on it because Plex kept using my 720p files (created for fast streaming) rather than the full 1080p HD recordings -- there didn't seem to be a way to tell it to use the best video available. Do you have Plex working well with EyeTV? How does this work? Is it really necessary to export files from EyeTV to Plex to avoid this problem?

  • by gardnern,

    gardnern gardnern Oct 19, 2012 11:39 AM in response to essandess
    Level 1 (0 points)
    Oct 19, 2012 11:39 AM in response to essandess

    Actually after I asked I went on a bit of a research trip.  Right now I'm using a Scanner written to add links to the playable file in an EyeTV package into Plex.  This works very well and even grabs weird stuff I record that dosn't have Episode numbers on them, like PBS cooking episodes.

     

    Primarily I'm interested in the commercial skipping working within Plex itself.  Whil this doesn't appear to be an option playing the recorded .mpg file directly, perhaps it would work if I exported into the eyeTV recording package and then had Plex play the .m4v file instead.

     

    The question is if Plex will respect the chapter marks.  If it won't then I'd have to run Comskip first, then have EyeTV run an export which would leave out the flagged commercial sections.

     

    I have glanced over a lot of discussions on the Plex forums concerning its use of the improper (lower quality) stream.  I believe a lot of work has been done in this respect.  I know there is now a place to define your preferred stream quality at least for web based myPlex videos.  In their new web client you can also define the quality of stream you wish to receive.

     

    If you haven't looked into Plex within the last 6 months I would encourage you to give the latest release a try.  If I could get football to live broadcast through Plex with Pause/FF/Rewind working I probably wouldn't watch anything at all through EyeTV directly..

  • by essandess,

    essandess essandess Dec 5, 2015 2:09 PM in response to essandess
    Level 1 (28 points)
    Applications
    Dec 5, 2015 2:09 PM in response to essandess

    I've posted new and improved ETVComskip modifications at the ETVComskip google code page.

     

    These modifications allow the use of comskip for H.264 HD video and adds comskip information to EyeTV iTunes exports.

     

    Comskip .edl information is added as chapters within the H.264 .m4v files exported by EyeTV and Turbo.264, providing a mobile Comskip solution: when the ads appear, skip to the next chapter. It avoids the problem of false detections by not deleting any program material.

     

    The scripts above perform simple load balancing by making sure that transcoding and/or exports are done first, then Comskip is run, as well as setting a high UNIX nice value for the comskip process. A Turbo.264 HD dongle used to offload transcoding from the CPU may be used but is not necessary, as the RecordingDone script is written so that commercial marking waits for the process "Elgato H.264 Decoder" to complete before running Comskip. The steps look like:

     

    Record -> Transcode [ -> Export to iTunes] -> Mark Commercials

     

    Automatic, asynchronous, and transparent  commercial marking of iTunes exports is performed. There is an EyeTV ExportDone.scpt that saves all exported files' inode numbers to a new file called root-name.exported_inodes.txt, then imports the .edl markings as h.264 chapters if there's a corresponding .edl file. And there's an updated MarkCommercials.py script that also imports  .edl markings as h.264 chapters into all existing exports if there's a corresponding .exported_inodes.txt file. iTunes can and will rename and reorganize the exported files, and the python script finds whatever iTunes filenames that have the inode number saved by ExportDone.scpt. It all works asynchronously, and though I've tested it using an iPad, I expect it to work with Apple TV and many individual iTunes and HTPC setups.

     

    Zip file contents:

     

    ExportDone.scpt

    MarkCommercials.py

    RecordingDone.scpt

    RecordingStarted.scpt

    comskip.ini

    readme-and-install.sh

     

    Help string in readme-and-install.sh:

     

    Installation:

     

    1. Eric Kaashoek's website www.kaashoek.com/comskip/

       and download his HD-capable Comskip software.

     

    2. Install Xcode from the App Store and Macports from http://www.macports.org/.

       For Mountain Lion, install Xquartz.

     

    3. Install ETVComskip from code.google.com/p/etv-comskip/downloads/list.

     

    4. Install Kaashoek's comskip files within the directory

          /Library/Application\ Support/ETVComskip/comskip

       Make sure that the ownership/group/permissions are set exactly the same

       as the original ./comskip directory and files.

     

    5. Download this zip file and check that your iTunes TV Shows directory

       matches this setting at the beginning of ./MarkCommercials.py:

          iTunes_TV_Shows = '~/Music/iTunes Media/TV Shows'

       Edit ./MarkCommercials.py to match this, then run these commands as a sudoer:

     

    # Necessary Macports

    sudo port selfupdate

    sudo port install wine-devel mp4v2

     

    # Move these five files into their correct locations

    sudo install -B .orig -b -m 0644 ./comskip.ini /Library/Application\ Support/ETVComskip/comskip/comskip.ini

    sudo install -B .orig -b -m 0644 ./MarkCommercials.py /Library/Application\ Support/ETVComskip/MarkCommercials.app/Contents/Resources/MarkCommercials.py

    sudo install -B .orig -b -m 0644 ./RecordingStarted.scpt /Library/Application\ Support/EyeTV/Scripts/TriggeredScripts/RecordingStarted.scpt

    sudo install -B .orig -b -m 0644 ./RecordingDone.scpt /Library/Application\ Support/EyeTV/Scripts/TriggeredScripts/RecordingDone.scpt

    sudo install -B .orig -b -m 0644 ./ExportDone.scpt /Library/Application\ Support/EyeTV/Scripts/TriggeredScripts/ExportDone.scpt

     

    The AppleScript ExportDone.scpt is new; here is the new code to accomplish asynchronous comskip marking of EyeTV iTunes exports:

     

    -- EyeTV ExportDone script to save exported file inode numbers as the text file filename.exported_inodes.txt and import .edl files as mp4 chapters

     

    -- Copyright © 2012 Steven T. Smith <steve dot t dot smith at gmail dot com>, GPL

     

    --    This program is free software: you can redistribute it and/or modify

    --    it under the terms of the GNU General Public License as published by

    --    the Free Software Foundation, either version 3 of the License, or

    --    (at your option) any later version.

    --

    --    This program is distributed in the hope that it will be useful,

    --    but WITHOUT ANY WARRANTY; without even the implied warranty of

    --    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

    --    GNU General Public License for more details.

    --

    --    You should have received a copy of the GNU General Public License

    --    along with this program.  If not, see <http://www.gnu.org/licenses/>.

     

     

    on ExportDone(recordingID)

     

        set myid to recordingID as integer

        set mp4chaps to "/opt/local/bin/mp4chaps"

        set mp4chaps_suffix to ".chapters.txt"

        set export_suffix to ".exported_inodes.txt"

        set edl_suffix to ".edl"

        set perl_suffix to ".pl"

     

        tell application "EyeTV"

            set myshortname to get the title of recording id myid

            set eyetvr_file to get the location of recording id myid as alias

        end tell

     

        tell application "Finder"

            -- Get EyeTV's root file names and paths for the recording

            set eyetv_path to container of eyetvr_file as string

            -- fix AppleScript's strange trailing colon issue for paths

            if character -1 of eyetv_path is not ":" then set eyetv_path to eyetv_path & ":"

            set eyetv_file to name of eyetvr_file

            set eyetv_root to (RootName(eyetv_file) of me)

            set edl_file to eyetv_path & eyetv_root & edl_suffix

            set edl_file_posix to POSIX path of edl_file

            set exported_inodes_file to eyetv_path & eyetv_root & export_suffix

        end tell

     

        -- give iTunes a chance to sync

        delay 30 --if the script does not seem to work, try increasing this delay slightly.

     

        tell application "iTunes"

            set mytv to get the location of (the tracks of playlist "TV Shows" whose name is myshortname or artist is myshortname)

        end tell

     

        -- return if no exports match; this shouldn't happen!

        if the (count of mytv) is less than 1 then return

     

        tell application "Finder"

            -- find the most recent export that isn't an open file

            set mymp4 to item 1 of mytv

            set mydate to modification date of mymp4

            repeat with kk from 2 to count of mytv

                if mydate is less than (modification date of item kk of mytv) and not my IsFileOpen(POSIX path of item kk of mytv) then

                    set mymp4 to item kk of mytv

                    set mydate to modification date of mymp4

                end if

            end repeat

            set mymp4_posix to POSIX path of mymp4

            -- safely quote any single quote characters for system calls: ' --> '"'"'

            set mymp4_posix_safequotes to my replace_chars(mymp4_posix, "'", "'\"'\"'")

            set itunes_path to container of mymp4 as string

            -- fix AppleScript's strange trailing colon issue for paths

            if character -1 of itunes_path is not ":" then set itunes_path to itunes_path & ":"

            set itunes_file to name of mymp4

            set itunes_root to (RootName(itunes_file) of me)

     

            -- save the iTunes file inode to the exported files file "*.exported_inodes.txt"

            -- find the exported file with the command: find . -type f -inum <inum>

            set ef_handle to open for access exported_inodes_file with write permission

            write (my FileInode(mymp4_posix) as string) & return to ef_handle starting at eof

            close access ef_handle

     

            -- return if no .edl file

            if not (exists file edl_file) then return

     

            -- add the mp4 chapters if the .edl file exists

     

            -- define the mp4chaps chapter file

            set itunes_chapter_file to (POSIX path of itunes_path) & itunes_root & mp4chaps_suffix

     

            -- translate the .edl file into a mp4chaps chapter file using perl

     

            set perlCode to "

    #!/usr/bin/perl

     

    ########################################

    # CONVERT EDL FILES TO MP4CHAPS FILES  #

    ########################################

     

    # Copyright © 2012 Steven T. Smith <steve dot t dot smith at gmail dot com>, GPL

     

    #    This program is free software: you can redistribute it and/or modify

    #    it under the terms of the GNU General Public License as published by

    #    the Free Software Foundation, either version 3 of the License, or

    #    (at your option) any later version.

    #

    #    This program is distributed in the hope that it will be useful,

    #    but WITHOUT ANY WARRANTY; without even the implied warranty of

    #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

    #    GNU General Public License for more details.

    #

    #    You should have received a copy of the GNU General Public License

    #    along with this program.  If not, see <http://www.gnu.org/licenses/>.

     

    use strict;

     

    my $edl_file = q{" & edl_file_posix & "};

    my $txt_file = q{" & itunes_chapter_file & "};

     

    sub sec2hhmmss {

    my $rem = $_[0]/3600;

    my $hh = int($rem);

    $rem = ($rem - $hh)*60;

    my $mm = int($rem);

    $rem = ($rem - $mm)*60;

        my $ss = int($rem);

        $rem = ($rem - $ss);

        $rem = sprintf(\"%.3f\",$rem);  # millisecond precision

        $rem =~ s/^0//;

        return sprintf(\"%02d:%02d:%02d%s\",$hh,$mm,$ss,$rem);

    }

     

    open (EDL,$edl_file) || die(\"Cannot open edl file \" . $edl_file);

    open (TXT,\">\",$txt_file) || die(\"Cannot open txt file \" . $txt_file);

    my $line;

    my @times;

    my $comskipno = 0;

    my $comskipchapno = 0;

    print TXT \"00:00:00.000 Beginning\\n\";

    while ($line = <EDL>) {

        chomp $line;

        # parse the space-delimited edl ascii times into an array of numbers

        @times = split ' ', $line;

        @times = map {$_+0} @times; # (unnecessarily) convert strings to numbers

        $comskipno += 1 if ($comskipno == 0 && $times[0] != 0.0);

        if ($#times < 2 || $times[2] == 0.0) {

            if ($times[0] != 0.0) {

                print TXT sprintf(\"%s Chapter %d End\\n\",sec2hhmmss($times[0]),$comskipno);

            }

         $comskipno++;

            print TXT sprintf(\"%s Chapter %d Start\\n\",sec2hhmmss($times[1]),$comskipno);

        } else {

            # never seen this case, but here for logical consistency

            $comskipchapno++;

            if ($times[0] != 0.0) {

                print TXT sprintf(\"%s Chapter %d Start\\n\",sec2hhmmss($times[0]),$comskipchapno);

            }

            print TXT sprintf(\"%s Chapter %d End\\n\",sec2hhmmss($times[1]),$comskipchapno);

        }

    }

    close (EDL) ;

    close (TXT) ;

    "

     

            -- define the perl  script and run it and delete it

            set perl_file to eyetv_path & eyetv_root & perl_suffix

            -- safely quote any single quote characters for system calls: ' --> '"'"'

            set perl_file_safequotes to my replace_chars(POSIX path of perl_file, "'", "'\"'\"'")

     

            set pl_handle to open for access perl_file with write permission

            set eof of pl_handle to 0

            write perlCode & return to pl_handle

            close access pl_handle

            set perlRes to do shell script "perl '" & perl_file_safequotes & "' || true"

            delete file perl_file

     

            -- execute mp4chaps and delete the chapter file

            -- remove any existing chapters

            set mp4chapsRes to do shell script mp4chaps & " -r '" & mymp4_posix_safequotes & "' > /dev/null 2>&1 || true"

            -- import the comskip chapters

            set mp4chapsRes to do shell script mp4chaps & " -i '" & mymp4_posix_safequotes & "' > /dev/null 2>&1 || true"

            -- delete the chapter file

            delete file (itunes_path & itunes_root & mp4chaps_suffix)

     

        end tell

    end ExportDone

     

    -- extract the root name of a file

    on RootName(fname)

        -- http://stackoverflow.com/questions/12907517/extracting-file-extensions-from-appl escript-paths

        set root to fname as string

        set delims to AppleScript's text item delimiters

        set AppleScript's text item delimiters to "."

        if root contains "." then set root to (text items 1 thru -2 of root) as text

        set AppleScript's text item delimiters to delims

        return root

    end RootName

     

    -- get the inode of a file

    on FileInode(posix_filename)

        -- safely quote any single quote characters for system calls: ' --> '"'"'

        set posix_filename_safequotes to my replace_chars(posix_filename, "'", "'\"'\"'")

        set fi to do shell script ("ls -i '" & posix_filename_safequotes & "' || true")

        if fi is not equal to "" then

            set fi to word 1 of fi

            return fi as number

        else

            return ""

        end if

    end FileInode

     

    -- test if a file is open

    on IsFileOpen(posix_filename)

        -- safely quote any single quote characters for system calls: ' --> '"'"'

        set posix_filename_safequotes to my replace_chars(posix_filename, "'", "'\"'\"'")

        set res to do shell script ("lsof '" & posix_filename_safequotes & "' > /dev/null 2>&1 && echo 'true' || echo 'false' || true")

        if res is equal to "true" then

            set res to true

        else

            set res to false

        end if

        return res

    end IsFileOpen

     

    -- string replacement

    on replace_chars(this_text, search_string, replacement_string)

        set delims to AppleScript's text item delimiters

        set AppleScript's text item delimiters to the search_string

        set the item_list to every text item of this_text

        set AppleScript's text item delimiters to the replacement_string

        set this_text to the item_list as string

        set AppleScript's text item delimiters to delims

        return this_text

    end replace_chars

     

     

    -- testing code: this will not be called when triggered from EyeTV, but only when the script is run as a stand-alone script

    on run

        tell application "EyeTV"

            --set rec to unique ID of item 1 of recordings

            -- for all your id's, run /Library/Application\ Support/ETVComskip/MarkCommercials.app/Contents/MacOS/MarkCommercials

            set rec to 372308280

            my ExportDone(rec)

        end tell

    end run


    <Links Edited By Host>

  • by sheldoa,

    sheldoa sheldoa Oct 22, 2012 5:09 PM in response to essandess
    Level 1 (0 points)
    Oct 22, 2012 5:09 PM in response to essandess

    I installed your latest files from the code.google site and works like a charm now.  Thanks for all your help and hard work!

  • by essandess,

    essandess essandess Oct 22, 2012 6:26 PM in response to sheldoa
    Level 1 (28 points)
    Applications
    Oct 22, 2012 6:26 PM in response to sheldoa

    Thanks. It even turns steering wheel controls into commercial skipping buttons when my kid is watching tv over Bluetooth in the car. This alone makes the coding worthwhile.

  • by fracnl14,

    fracnl14 fracnl14 Nov 8, 2012 12:44 AM in response to essandess
    Level 1 (0 points)
    Nov 8, 2012 12:44 AM in response to essandess

    HI

    thank you. your instructions were very helpful for me but I live in Europe and I don't have neither HD or turbo264. Are your changes to the standard version (edit markcommercials.py, the new scripts) useful for me? my etv comskip (standard version) marks well short program (below 1 and half hour) but not the longer ones (like movies).

    if your changes are useful, could you explain better how to "edit markcommercials.py"? do I have to substitute the similar parts in the original file?

    thank you

  • by essandess,

    essandess essandess Nov 9, 2012 4:45 AM in response to fracnl14
    Level 1 (28 points)
    Applications
    Nov 9, 2012 4:45 AM in response to fracnl14

    The ETVComskip modifications allow commercial marking on HD content, so if you don't have an HD PVR, the original ETVComskip is what you want. However, the EyeTV script ExportDone.scpt above will mark commercials in iTunes exports whether they're HD or not, so that may be useful.

     

    Also, if you're having issues with Comskip, try downloading the latest free version and swapping it in as described for the contributor version above—that has a good chance of resolving little problems.

     

    Finally, google "os x text editor" for text editing questions.

  • by Ste88,

    Ste88 Ste88 Nov 15, 2012 4:24 PM in response to essandess
    Level 1 (0 points)
    Nov 15, 2012 4:24 PM in response to essandess

    Hello,

     

    Perhaps you can help me - I'm having a problem that initially was similar to sheldoa, i.e., that I was getting "No such file or directory" when running MarkCommercials from the command line. It doesn't appear to be the same problem, however, as when I look in my .eyetv file, I see no .edl file whatsoever. I came to the conclusion (correct me if I'm wrong here) that comskip didn't do it's job. So, I also tried running comskip from the command line:

     

    /opt/local/bin/wine /Library/Application\ Support/ETVComskip/comskip/comskip.exe --ini=/Library/Application\ Support/ETVComskip/comskip/comskip.ini 0000000016536aa4.mpg

     

    This fails with the error:

     

    err:module:attach_process_dlls "winspool.drv" failed to initialize, aborting

    err:module:LdrInitializeThunk Main exe initialization for L"Z:\\Library\\Application Support\\ETVComskip\\comskip\\comskip.exe" failed, status c0000005

     

    Which leads me to believe I was right. It also seems like comskip is not functioning because wine itself is not starting. Does comskip itself create the .edl file within each .eyetv package?

     

    I have carefully gone through the installation steps detailed in the readme, and have made sure I have the latest macports updates, the donor version of comskip, etc. XQuartz is installed and running, and wine-devel and mp4v2 and their dependencies install without error from macports. I also have copied your files from the zip as instructed. Is there something I'm missing?

     

    Any help would be very much appreciated!

     

    Cheers,

    S

     

    PS. This is Mountain Lion on a Mac Mini with my EyeTV Archive in a non-default location (on my external firewire Media volume)

Previous Page 2 of 10 last Next