diff options
| author | Britney Fransen <brfransen@gmail.com> | 2014-10-27 20:28:27 (GMT) | 
|---|---|---|
| committer | Britney Fransen <brfransen@gmail.com> | 2014-10-27 20:28:27 (GMT) | 
| commit | f7b823f55a80b1498c30e9f3a47692a2329c4f09 (patch) | |
| tree | 6de557714e6336282fbc0ab16c6578386dfc6654 /abs/core/LinHES-system | |
| parent | 4c2a2e3504f472628285a989c7c9a76be6855f54 (diff) | |
| parent | 01c1a60f3b7f93b3ed7404196c2cf798c4d8c674 (diff) | |
| download | linhes_pkgbuild-f7b823f55a80b1498c30e9f3a47692a2329c4f09.zip linhes_pkgbuild-f7b823f55a80b1498c30e9f3a47692a2329c4f09.tar.gz linhes_pkgbuild-f7b823f55a80b1498c30e9f3a47692a2329c4f09.tar.bz2 | |
Merge branch 'testing'
Diffstat (limited to 'abs/core/LinHES-system')
| -rwxr-xr-x | abs/core/LinHES-system/LinHES-session | 62 | ||||
| -rwxr-xr-x | abs/core/LinHES-system/PKGBUILD | 36 | ||||
| -rwxr-xr-x | abs/core/LinHES-system/add_storage.py | 4 | ||||
| -rw-r--r-- | abs/core/LinHES-system/add_storage.readme | 2 | ||||
| -rw-r--r-- | abs/core/LinHES-system/create_media_dirs.sh | 2 | ||||
| -rwxr-xr-x | abs/core/LinHES-system/idle.py | 352 | ||||
| -rwxr-xr-x | abs/core/LinHES-system/jobqueue_helper.py | 63 | ||||
| -rw-r--r-- | abs/core/LinHES-system/lh_system_host_update | 13 | ||||
| -rw-r--r-- | abs/core/LinHES-system/linhes_update.sh | 81 | ||||
| -rw-r--r-- | abs/core/LinHES-system/linhes_update2.sh | 17 | ||||
| -rw-r--r-- | abs/core/LinHES-system/myth2mkv | 105 | ||||
| -rwxr-xr-x | abs/core/LinHES-system/myth_mtc.py | 114 | ||||
| -rwxr-xr-x | abs/core/LinHES-system/mythvidexport.py | 439 | ||||
| -rwxr-xr-x | abs/core/LinHES-system/mythwelcome-config.py | 7 | ||||
| -rw-r--r-- | abs/core/LinHES-system/system.install | 6 | 
15 files changed, 1096 insertions, 207 deletions
| diff --git a/abs/core/LinHES-system/LinHES-session b/abs/core/LinHES-system/LinHES-session index 14606fb..ae702cf 100755 --- a/abs/core/LinHES-system/LinHES-session +++ b/abs/core/LinHES-system/LinHES-session @@ -55,13 +55,13 @@ function keylaunch(){          fi          echo "Checking if keylaunch is running" -        if [ "$(pidof keylaunch)" ]  +        if [ "$(pidof keylaunch)" ]          then              echo "keylaunch is already running"          else              echo "Starting keylaunch"              /usr/bin/keylaunch & -        fi  +        fi      fi  } @@ -198,7 +198,6 @@ function first_mythwelcome_config(){  function first_configure(){ -    first_mythwelcome_config      if [ ! x$STARTUP_STYLE = xlegacy ]      then          first_configure_mv @@ -209,6 +208,7 @@ function first_configure(){  function first_configure_default(){ +    echo "First configure: legacy..."      if [ !  $SystemType = "Frontend_only" ]      then          if [  -f ~/.configure ] @@ -224,19 +224,18 @@ function first_configure_default(){              else                  rm -f ~/.no_meth              fi - -        sudo sv stop mythbackend -        xterm -fn *18* -e sudo taskset -c 0 /usr/bin/mythtv-setup -        sudo sv start mythbackend -        sudo chown -R mythtv.mythtv /home/mythtv -        # only run mythfilldatabase for masterbackends -        if [ $SystemType = "Master_backend" ] -        then -            nice -n 19 mythfilldatabase --quiet & -            msg "Guide data is being loaded.\nUntil this completes\nsome shows will appear as "unknown"\nin the program guide." -        fi - -        rm ~/.configure +            first_mythwelcome_config +            sudo sv stop mythbackend +            xterm -fn *18* -e sudo taskset -c 0 /usr/bin/mythtv-setup +            sudo sv start mythbackend +            sudo chown -R mythtv.mythtv /home/mythtv +            # only run mythfilldatabase for masterbackends +            if [ $SystemType = "Master_backend" ] +            then +                nice -n 19 mythfilldatabase --quiet & +                msg "Guide data is being loaded.\nUntil this completes\nsome shows will appear as unknown\nin the program guide." +            fi +            rm ~/.configure          fi      else          if [  -f ~/.configure ] @@ -248,6 +247,7 @@ function first_configure_default(){  function first_configure_mv(){ +    echo "First configure..."      if [ !  $SystemType = "Frontend_only" ]      then          if [  -f ~/.configure ] @@ -259,9 +259,10 @@ function first_configure_mv(){              #0 yes              #1 yes/autodetech              #2  no +            first_mythwelcome_config              while [ -e '/var/run/autocard.lock' ]              do -                msg "Waiting for capture card detection to complete" +                msg "Waiting for capture card detection to complete..."                  sleep 5              done              STATUS=$? @@ -273,13 +274,13 @@ function first_configure_mv(){                      /usr/bin/taskset -c 0 /usr/bin/mythtv-setup                      /usr/MythVantage/bin/backend_control.sh start BE 127.0.0.1              fi -         # only run mythfilldatabase for masterbackends -         if [ $SystemType = "Master_backend" ] -         then -            nice -n 19 mythfilldatabase --quiet & -            msg "Guide data is being loaded.\nUntil this completes\nsome shows will appear as "unknown"\nin the program guide." -         fi -         rm -f ~/.configure +            # only run mythfilldatabase for masterbackends +            if [ $SystemType = "Master_backend" ] +            then +                nice -n 19 mythfilldatabase --quiet & +                msg "Guide data is being loaded.\nUntil this completes\nsome shows will appear as unknown\nin the program guide." +            fi +            rm -f ~/.configure          fi      else          if [  -f ~/.configure ] @@ -317,7 +318,6 @@ function start_myth() {              fi          done          sleep 3 #give time for MBE to be fully operational -              fi      #continue on with starting mythtv @@ -396,6 +396,17 @@ function set_background()  } +function stop_plymouth() +{ +    #pause plymouthd and continue in /etc/sv/frontend/finish +    pidfile=/run/plymouth.pid +    if [ -e "$pidfile" ]; then +        plypid=`cat "$pidfile"` +        if [ -n "$plypid" ]; then +            sudo kill -s STOP "$plypid" +        fi +    fi +}  if [ x"$WM" = "fluxbox" ] @@ -418,6 +429,7 @@ start_screensaver  notify_scan &  auto_start &  update_apple_trailers +stop_plymouth &  first_configure  show_help_tip  start_myth diff --git a/abs/core/LinHES-system/PKGBUILD b/abs/core/LinHES-system/PKGBUILD index 94b1604..ad23218 100755 --- a/abs/core/LinHES-system/PKGBUILD +++ b/abs/core/LinHES-system/PKGBUILD @@ -1,6 +1,6 @@  pkgname=LinHES-system -pkgver=8.1 -pkgrel=12 +pkgver=8.2 +pkgrel=3  arch=('i686' 'x86_64')  install=system.install  pkgdesc="Everything that makes LinHES an automated system" @@ -9,7 +9,7 @@ depends=('linhes-sounds' 'xdotool' 'tilda' 'keylaunch'          'python_aosd' 'udisks' 'LinHES-config>=2.3-59' 'sudo'          'python2-dbus' 'unclutter' 'wmctrl' 'archlinux-xdg-menu'          'ethtool' 'gnu-netcat' 'normalize' 'ttf-overlock' -        'handbrake-cli' 'mkvtoolnix' 'mplayer') +        'handbrake-cli' 'mkvtoolnix' 'mplayer' 'runit-scripts>=8.1-12')  backup=('etc/modprobe.d/alsa-base.conf')  binfiles="LinHES-start optimize_mythdb.py myth_mtc.py   load-modules-mythvantage.sh unclutter-toggle.sh tvterm.sh @@ -18,12 +18,12 @@ binfiles="LinHES-start optimize_mythdb.py myth_mtc.py   lh_system_backup lh_system_backup_job lh_system_restore_job   lh_system_host_update lh_system_all_host_update   add_storage.py diskspace.sh cacheclean lh_backend_control.sh - create_media_dirs.sh msg_client.py msg_daemon.py + create_media_dirs.sh msg_client.py msg_daemon.py mythvidexport.py   gen_is_xml.py gen_lib_xml.py gen_light_include.py gen_game_xml.py   misc_recent_recordings.pl misc_status_config.py misc_status_info.sh - misc_upcoming_recordings.pl misc_which_recorder.pl + misc_upcoming_recordings.pl misc_which_recorder.pl jobqueue_helper.py   change_channel.sh change_channel_wrapper.sh stop_xss.sh - be_check.py checkXFSfrag.sh find_orphans.py idle.sh xwin_find.sh + be_check.py checkXFSfrag.sh find_orphans.py idle.py xwin_find.sh   linhes_update.sh linhes_update2.sh myth2mkv myth2mp3 ripD_eject.sh   mythwelcome-config.py mythwelcome-set-alarm.sh mythwelcome-test-wakeup.sh" @@ -75,11 +75,11 @@ package() {  } -md5sums=('7ab2a2c643d2b286811d8303d08982ad' +md5sums=('a10cb96850951ae835c46f41f92463b2'           'de32a1c50101265dd7f6ca5037f7a26a'           '301884fb60521627fffd1160b2cf5181'           '76b2637cac0452b3acdbeeb4e8a5474b' -         '22807bd1e37d2a07bc0bd3f2a9fd2bb4' +         '0149e77a12d30f1050dd3990f6f1f123'           'dc3eef2a624754e16805d72bbe488b67'           '617af86b901538817ebdcaf646248dc5'           '542e670e78d117657f93141e9689f54d' @@ -93,15 +93,16 @@ md5sums=('7ab2a2c643d2b286811d8303d08982ad'           '3edef50a49a47694bf8add39cc160add'           'b376c30f95892b8682fad84f81685f75'           'bc69a520add58ede9b060c73e67ace13' -         '2e5e1b3187f75154366bffe806ff8ae9' +         '74e17d6f7453c52d56fecaed5c3f6ad5'           '47e093e8cfe4b5b96602358e1f540832' -         '4d6a6e88b519caf917ebe6c85c32c45b' +         '63bbc62240f46a9997eaae4a84b09b76'           '2c005d95312018bef80092136f80f254'           '6519d209e151cdc39ae7e1091d189c3e'           'ca63946920ba33de1f15abda83e74e40' -         '7f7c49d859abdaa0b5fca399241d4998' +         '84c0ea62550b0bf9d84ab48eddc08276'           '3e60b17892e5b8214d47dcfddf5215a4'           '57ec994cc3964a10c00580e89ebcae35' +         '7ba5e774bfebc3ec2469c9fe9a76e2ce'           '0c3509b48f11af0dc1bf989721fe9ca7'           'ac61cc460d9e97ba1f5ef69e92cdfbe5'           'f3502bb7c665750da0ecdf6918f7c838' @@ -111,20 +112,21 @@ md5sums=('7ab2a2c643d2b286811d8303d08982ad'           '3300ea8b02e4fb8bd3409df348de6e16'           '145b1da6ce501b3ce38ea415a576bf2d'           'b51c93ad9f3717a616d92899f6bfde76' +         'd8f30983055dcfe2f53197d0f9a62158'           '17f678d37187be0f12d67f64e40429c6'           'bacc813b48bafcc6fe906e5969930501'           '8e02efe1ad0df9a179075147eebb05b9'           '8b0298f70f97cc1dc2a58b9a73c64bd3'           '911b0fbc8d9178dac1a193346c9decaf'           '34fc1f58ad1eabf4eff4979d420760c0' -         'c3ada01d3a739abe3f920b02d4ea3f6e' +         '69ecfe682f1b61c2c4c95d890b9f523c'           'a94fe6d980f4b810f2e2ae5352084b39' -         '2c56266a79d058bf01f0de19c2cd042a' -         'c27d3fdf59b211f9d3cd76a81f6257dc' -         '503df99218373dfc75e7e7f5e449a44e' +         '384956b4fd9bf40907856bab0e67b169' +         'f4ce577e2ab1b6dbf9def46a8ab86e2d' +         '28f29578e5b3ba84fdf2aa57cf475bcf'           '4a1fda884dcd7d65fb2690fbdbd92a83'           '2b7fe3b57592823a4c7e3ec132dcb7f4' -         '92950f0ffb1faf1ed64c6be2b8fbc3f6' +         '6e95aa86413abdb568ed157cf11dd69f'           '95c092f67036a361ef7a57436f44332e'           '410795ef9039e4c6c0484e706ecfd567'           'eb879fee9603a05d5420d4ce8ed9e450' @@ -133,6 +135,6 @@ md5sums=('7ab2a2c643d2b286811d8303d08982ad'           '474d5c6883fcfffae4f199aceb3b1356'           '36bdfa8d877cea20ca3870faf08e08d5'           '59d0602bac7e06f11abba1894acc8bb0' -         'da432d68962e109e09beb2b71c8c8571' +         'b12bbf3e028f51b08acef2799a5708bc'           '366ccb097d1c221389709485c3b2d159'           'bdd37627c9effd745a051fd2d279975b') diff --git a/abs/core/LinHES-system/add_storage.py b/abs/core/LinHES-system/add_storage.py index 61ce0ea..2518927 100755 --- a/abs/core/LinHES-system/add_storage.py +++ b/abs/core/LinHES-system/add_storage.py @@ -960,7 +960,9 @@ def usage():      --new_init:         Erase the list of known disks and rescan.      --no_destruction:   Will not partition or format the disk.                             All other normal operations will be performed. -                           Can be used to import disks from other systems. +                           Can be used to import disks from other systems +                           however, add_storage.py only works with the first +                           partition on a disk and ignores all others.      --no_mount:         Do not mount the disk.                             All other normal operations will be performed.      --reconstruct:      Recreate mount point, /myth symlink, fstab entry, diff --git a/abs/core/LinHES-system/add_storage.readme b/abs/core/LinHES-system/add_storage.readme index 226b14d..d4435d5 100644 --- a/abs/core/LinHES-system/add_storage.readme +++ b/abs/core/LinHES-system/add_storage.readme @@ -1,3 +1,3 @@  Files here are autogenerated by add_storage.py. -They will be used by systemconfig to generate nfs,smb and mountpoint recovery. +They will be used by systemconfig to generate nfs, smb and mountpoint recovery. diff --git a/abs/core/LinHES-system/create_media_dirs.sh b/abs/core/LinHES-system/create_media_dirs.sh index 6af6eb7..f56377b 100644 --- a/abs/core/LinHES-system/create_media_dirs.sh +++ b/abs/core/LinHES-system/create_media_dirs.sh @@ -50,7 +50,7 @@ media/games  media/video  media/tmp  media/archive -media/pretty +media/recordings  media/streaming  media/artwork/trailers  media/artwork/coverart diff --git a/abs/core/LinHES-system/idle.py b/abs/core/LinHES-system/idle.py new file mode 100755 index 0000000..4575e7d --- /dev/null +++ b/abs/core/LinHES-system/idle.py @@ -0,0 +1,352 @@ +#!/usr/bin/python2 + +import argparse, os, re, subprocess, sys, time +from datetime import datetime, date, timedelta + +def msg(cmdargs,msg): +    if cmdargs.silent is False: +        print "%s" %msg + +def mythshutdownlock_check(cmdargs,cursor): +    if (cmdargs.lock): +        msg(cmdargs,"    Checking mythshutdown for lock...") +        try: +            cursor.execute("select data from settings where value = 'MythShutdownLock'") +            results=cursor.fetchone() +        except: +            return True +        lock=results[0] +        if int(lock) == 0 : +            msg(cmdargs,"        mythshutdown is NOT locked.") +            return True +        else: +            msg(cmdargs,"        mythshutdown is locked.") +            return False +    else: +        return True + +def dailywake_check(cmdargs,cursor): +    if (cmdargs.daily): +        msg(cmdargs,"    Checking if in a daily wake period...") +        dailyWake=False +        today = date.today() +        now = datetime.now() +        try: +            cursor.execute("select data from settings where value = 'DailyWakeupStartPeriod1'") +            results=cursor.fetchone() +            p1Start=datetime.strptime(' '.join([str(today), results[0]]), "%Y-%m-%d %H:%M") +            cursor.execute("select data from settings where value = 'DailyWakeupEndPeriod1'") +            results=cursor.fetchone() +            p1End=datetime.strptime(' '.join([str(today), results[0]]), "%Y-%m-%d %H:%M") +            cursor.execute("select data from settings where value = 'DailyWakeupStartPeriod2'") +            results=cursor.fetchone() +            p2Start=datetime.strptime(' '.join([str(today), results[0]]), "%Y-%m-%d %H:%M") +            cursor.execute("select data from settings where value = 'DailyWakeupEndPeriod2'") +            results=cursor.fetchone() +            p2End=datetime.strptime(' '.join([str(today), results[0]]), "%Y-%m-%d %H:%M") +        except: +            print "error" +            return True + +        # Check for time periods that cross midnight +        if (p1End < p1Start): +            if (now > p1End): +                p1End = p1End + timedelta(days=1) +            else: +                p1Start = p1Start + timedelta(days=-1) +        if (p2End < p2Start): +            if (now > p2End): +                p2End = p2End + timedelta(days=1) +            else: +                p2Start = p2Start + timedelta(days=-1) + +        #Check for one of the daily wakeup periods +        if (p1Start != p1End): +            if (now >= p1Start and now <= p1End): +                msg(cmdargs,"        Currently in daily wake period 1.") +                return False +        if (p2Start != p2End): +            if (now >= p2Start and now <= p2End): +                msg(cmdargs,"        Currently in daily wake period 2.") +                return False + +        #Are we about to start a daily wakeup period using the -t TIME var +        if (p1Start != p1End): +            delta=p1Start-now +            if (delta.seconds >= 0 and delta.seconds <= cmdargs.time * 60): +                msg(cmdargs,"        Daily wake period 1 will start in less than %s minutes." %cmdargs.time) +                return False +        if (p2Start != p2End): +            delta=p2Start-now +            if (delta.seconds >= 0 and delta.seconds <= cmdargs.time * 60): +                msg(cmdargs,"        Daily wake period 2 will start in less than %s minutes." %cmdargs.time) +                return False + +        msg(cmdargs,"        Currently NOT in a daily wake period.") +        return True +    else: +        return True + +def schemalock_check(cmdargs,cursor): +    msg(cmdargs,"    Checking if the schema is locked...") +    try: +        cursor.execute("select count(*) from schemalock") +        results=cursor.fetchone() +    except: +        return True +    schemalock=results[0] +    if schemalock == 0: +        msg(cmdargs,"        The schema is NOT locked.") +        return True +    else: +        msg(cmdargs,"        The schema is locked.") +        return False + +def in_use(cmdargs,cursor): +    msg(cmdargs,"    Checking if programs are in use...") +    try: +        cursor.execute("select count(*) from inuseprograms") +        results=cursor.fetchone() +    except: +        return True +    prginuse=results[0] +    if prginuse == 0 : +        msg(cmdargs,"        Programs are NOT in use.") +        return True +    else: +        msg(cmdargs,"        Programs are in use.") +        return False + +def job_check(cmdargs,cursor): +    msg(cmdargs,"    Checking jobqueue for active jobs...") +    try: +        cursor.execute("select count(*) from jobqueue where status between 2 and 5") +        results=cursor.fetchone() +    except: +        return True +    jobs=results[0] +    if jobs == 0 : +        msg(cmdargs,"        No jobs are active.") +        return True +    else: +        msg(cmdargs,"        Jobs are active.") +        return False + +def upcoming_check(cmdargs,mythBE): +    msg(cmdargs,"    Checking for recordings in the next %s minutes..." %cmdargs.time) +    try: +        upcoming = mythBE.getUpcomingRecordings() +    except: +        msg(cmdargs,"        Could not get upcoming recordings.") +        return True +    time_diff=10000 +    r=0 +    for i in upcoming: +        r += 1 +        if r > 1: +            break +        show=str(i) +        show=show.strip() +        showtime=re.split("[-+]\d\d:\d\d",str(i.starttime))[0] +        now=time.time() +        rec_time=time.strptime( showtime ,"%Y-%m-%d %H:%M:%S" ) +        r=time.mktime(rec_time) +        time_diff = ( r - now ) / 60 + +    if ( time_diff > cmdargs.time) : +        msg(cmdargs,"        No recordings starting in %s minutes." %cmdargs.time) +        return True +    else: +        msg(cmdargs,"        A recording is starting in %s minutes." %int(time_diff)) +        return False + +def mfd_check(cmdargs): +    msg(cmdargs,"    Checking if mythfilldatabase is running...") +    with open(os.devnull, "w") as fnull: +        mythfilldatabase_ret = subprocess.call(["pidof", "mythfilldatabase"], stdout=fnull) +    if mythfilldatabase_ret == 0 : +        msg(cmdargs,"        mythfilldatabase is running.") +        return False +    else: +        msg(cmdargs,"        mythfilldatabase is NOT running.") +        return True + +def mythtvsetup_check(cmdargs): +    msg(cmdargs,"    Checking if mythtv-setup is running...") +    with open(os.devnull, "w") as fnull: +        mythsetup_ret = subprocess.call(["pidof", "mythtv-setup"], stdout=fnull) +    if mythsetup_ret == 0 : +        msg(cmdargs,"        mythtv-setup is running.") +        return False +    else: +        msg(cmdargs,"        mythtv-setup is NOT running.") +        return True + +def userlogins_check(cmdargs): +    if (cmdargs.logins): +        u=False +        msg(cmdargs,"    Checking for users logged in...") +        users=subprocess.check_output("who") +        names=([x.split() for x in users.splitlines()]) +        for i in names: +            if (i[0] == "mythtv" and i[4] == "(:0)"): +                msg(cmdargs,"        Ignoring %s %s" %(i[0],i[4])) +            else: +                msg(cmdargs,"        User logged in: %s %s" %(i[0],i[4])) +                u=True +        if u: +            return False +        else: +            return True +    else: +        return True + +def mythfe_check(cmdargs,cursor,mythDB): +    #checks to see if a frontend is considered idle +    # True means FE is idle + +    if ( cmdargs.runningfe ): +        msg(cmdargs,"    Checking for running and playing mythfrontends...") +    else: +        msg(cmdargs,"    Checking for playing mythfrontends...") +    try: +        #frontends = mythDB.getFrontends() #use cursor instead so it doesn't test connection +        cursor.execute("select hostname from settings where value = 'FrontendIdleTimeout'") +        frontends=cursor.fetchall() +    except: +        return True + +    for i in frontends: +        try: +            msg(cmdargs,"        Checking %s's mythfrontend status..." %i) +            frontend = mythDB.getFrontend(''.join(i)) +            if ( cmdargs.runningfe ): +                msg(cmdargs,"            %s's mythfrontend is RUNNING." %i) +                return False +            location = frontend.sendQuery('Location') + +            if location == "standbymode": +                msg(cmdargs,"            %s's mythfrontend is in StandbyMode." %i) +                return True + +            if ( location.startswith('Playback ') ): +                msg(cmdargs,"            %s's mythfrontend is PLAYING." %i) +                return False +            else: +                msg(cmdargs,"            %s's mythfrontend is NOT playing." %i) +            if '.xml' in location or 'mainmenu' in location: +                msg(cmdargs,"            %s's mythfrontend is in MENUS." %i) +            else: +                #FE is not in menus, so it must be active in a plugin +                msg(cmdargs,"            %s's mythfrontend is NOT in menus." %i) +                return False +        except: +            msg(cmdargs,"            Could not connect to %s's mythfrontend." %i) + +    if ( cmdargs.runningfe ): +        msg(cmdargs,"        mythfrontends are not running or playing or are in menus.") +    else: +        msg(cmdargs,"        mythfrontends are not playing or are in menus.") + +    return True + +def usage(): +    line = ''' +    idle.py checks if the system is idle. +    Use idle.py -h to see options. + +    idle.py checks these parts of the system in this order to +    determine if it is idle: +    - (option -g) users are logged in return busy +        ignores mythtv (:0) for busy +    - (option -l) mythshutdown is locked return busy +    - (option -d) in a daily wake period or +        about to start a daily wake period return busy +        checks the next 15 minutes. -t TIME changes time +    - schema is locked return busy +    - there are in use programs return busy +    - there are active jobs in the job queue return busy +    - mythfilldatabase is running return busy +    - mythtv-setup is running return busy +    - there are upcoming recordings return busy +        checks the next 15 minutes. -t TIME changes time +    - (option -r) mythfrontends running return busy +    - mythfrontends playing back a recording or video return busy +    - mythfrontends not in menus return busy + +    idle.py stops checking and returns false (busy) when the first busy is found. +    ''' +    print line +    sys.exit(0) + +def main(args=[False]): +    parser = argparse.ArgumentParser() +    parser.add_argument('-d', '--daily', action='store_true', help='Include daily wake & about to start wake in system busy. (default: daily wake & about to start wake is system idle)') +    parser.add_argument('-g', '--logins', action='store_true', help='Include user logins in system busy. ignores mythtv (:0) in system busy.') +    parser.add_argument('-l', '--lock', action='store_true', help='Include mythshutdown lock in system busy. (default: mythshutdown lock is system idle)') +    parser.add_argument('-r', '--runningfe', action='store_true', help='Include running mythfrontends in system busy. (default: running mythfrontends are system idle)') +    parser.add_argument('-s', '--silent', action='store_true', help='Run without printing output. Recommended for use in cron jobs or scripts.') +    parser.add_argument('-t', '--time', type=int, default=15, help='Minutes of idle time needed to return idle for upcoming recordings and daily wake.') +    parser.add_argument('-u', '--usage', action='store_true', help='Print usage instructions.') +    if args[0] is False: +        cmdargs = parser.parse_args() +    else: +        cmdargs = parser.parse_args(args) + +    if cmdargs.usage: +        usage() +    idle=True +    msg(cmdargs,"Checking system idle...") + +    if (userlogins_check(cmdargs)): +        idle = True +    else: +        idle = False + +    try: +        from MythTV import MythDB +        mythDB = MythDB() +        cursor = mythDB.cursor() +        db_conn=True +    except: +        msg(cmdargs,"Couldn't connect to MythTV database.") +        db_conn=False + +    try: +        from MythTV import MythBE +        mythBE = MythBE() +        be_conn=True +    except: +        msg(cmdargs,"Couldn't connect to MythTV backend.") +        be_conn=False + +    if ( db_conn and idle ): +        if (mythshutdownlock_check(cmdargs,cursor) and dailywake_check(cmdargs,cursor) and schemalock_check(cmdargs,cursor) and in_use(cmdargs,cursor) and job_check(cmdargs,cursor)): +            idle=True +        else: +            idle=False + +    if ( be_conn and idle ): +        if (mfd_check(cmdargs) and mythtvsetup_check(cmdargs) and upcoming_check(cmdargs,mythBE)): +            idle=True +        else: +            idle=False + +    if ( db_conn and idle ): +        if (mythfe_check(cmdargs,cursor,mythDB)): +            idle=True +        else: +            idle=False + +    if ( idle ): +        msg(cmdargs,"System is idle.") +    else: +        msg(cmdargs,"System is busy.") +    return idle + +if __name__ == "__main__": +    idle=main() +    if ( idle ): +        exit(0) +    else: +        exit(1) diff --git a/abs/core/LinHES-system/jobqueue_helper.py b/abs/core/LinHES-system/jobqueue_helper.py new file mode 100755 index 0000000..d27e693 --- /dev/null +++ b/abs/core/LinHES-system/jobqueue_helper.py @@ -0,0 +1,63 @@ +#!/usr/bin/python2 + +import argparse, os, re, subprocess, sys, time +from MythTV import MythDB, Job + +mythDB = MythDB() +cursor = mythDB.cursor() + +def set_cmds(cmdargs,job): +    #print "Setting cmds on job %s to %s" %(cmdargs.jobid,cmdargs.cmd) +    cursor.execute("update jobqueue set cmds = '%s' where id = '%s'" %(cmdargs.cmd,cmdargs.jobid)) + +def set_comment(cmdargs,job): +    #print "Setting comment on job %s to %s" %(cmdargs.jobid,cmdargs.comment) +    job.setComment("%s" %cmdargs.comment) + +def set_status(cmdargs,job): +    #print "Setting status on job %s to %s" %(cmdargs.jobid,cmdargs.status) +    job.setStatus("%s" %cmdargs.status) + +def run_cursor(cmdargs): +    cursor.execute("%s" %cmdargs.man_cursor) +    results=cursor.fetchone() +    print results[0] + +def usage(): +    line = ''' +    jobqueue_helper.py provides MythTV job queue functions +    using python bindings for bash scripts. +    Use jobqueue_helper.py -h to see options. +    ''' +    print line +    sys.exit(0) + +if __name__ == "__main__": +    parser = argparse.ArgumentParser() +    parser.add_argument('-cs', '--comment_set', action='store', dest='comment', help='Set the comment of the jobid') +    parser.add_argument('-cmds', '--cmd_set', type=int, default=77777, action='store', dest='cmd', help='Set the cmd of the jobid') +    parser.add_argument('-ss', '--status_set', type=int, action='store', dest='status', help='Set the status of the jobid') +    action = parser.add_mutually_exclusive_group(required=True) +    action.add_argument('-j', '--jobid', type=int, help='jobid of the job to control') +    action.add_argument('-m', '--man_cursor', action='store', dest='man_cursor', help='Manual mysql cursor command') +    action.add_argument('-u', '--usage', action='store_true', help='Print usage instructions.') + +    cmdargs = parser.parse_args() + +    if cmdargs.usage: +        usage() + +    if cmdargs.jobid: +        job = Job(cmdargs.jobid) + +    if cmdargs.comment: +        set_comment(cmdargs, job) + +    if cmdargs.status: +        set_status(cmdargs, job) + +    if cmdargs.cmd != 77777: +        set_cmds(cmdargs, job) + +    if cmdargs.man_cursor: +        run_cursor(cmdargs) diff --git a/abs/core/LinHES-system/lh_system_host_update b/abs/core/LinHES-system/lh_system_host_update index ca6aeff..0a63e0a 100644 --- a/abs/core/LinHES-system/lh_system_host_update +++ b/abs/core/LinHES-system/lh_system_host_update @@ -37,17 +37,22 @@ case $Hostupdateplan in                  		echo  "$pkg is up to date"                  	fi  	        done -	;; +        ;;  	"All") -		echo ""  +		echo ""  		echo "Updating ALL packages"  		pacman -Sy  		pacman --noconfirm -Su --force -	;; +        ;;  	"None")  		echo ""  		echo "Updating NO packages" -		;; +        ;; +    "Notify") +        echo "" +        echo "Notify if there are updates" +        /usr/LH/bin/linhes_update.sh -s +        ;;      *)  echo ""          echo "Unknown  $Hostupdateplan, skipping updates"          ;; diff --git a/abs/core/LinHES-system/linhes_update.sh b/abs/core/LinHES-system/linhes_update.sh index afaf6d5..d563504 100644 --- a/abs/core/LinHES-system/linhes_update.sh +++ b/abs/core/LinHES-system/linhes_update.sh @@ -1,27 +1,63 @@  #!/bin/bash  #Called from LinHES Service Menu Check for Updates Menu +usage () { +    echo "Usage: $0 [-h] [-s]" +    echo +    echo "-h - Print this help/usage message and quit" +    echo "-s - Run silently, no OSD messages" +    exit $1 +} + +restart_mfe () { +    #check if started from mythfrontend-start and if so just kill mfe +    #otherwise reload mfe +    if [[ `pgrep -fx "/bin/bash /usr/LH/bin/mythfrontend-start"` ]] +    then +        killall mythfrontend +    else +        pkill -USR1 mythfrontend +    fi +} +  dsply ()  { -    if grep -q Updates\ Available /usr/share/mythtv/themes/defaultmenu/linhes.xml +    if grep -q "Updates Available" /usr/share/mythtv/themes/defaultmenu/linhes.xml      then -        msg_client.py --clear --tag "checkUpdates" -        msg_client.py --kill -        msg_client.py --msg "Updates available!\nGo to the Service Menu to install the updates.|middle" +        if [ "$1" = false ]; then +            msg_client.py --clear --tag "checkUpdates" +            msg_client.py --kill +            msg_client.py --msg "Updates available!\nGo to the Service Menu to install the updates.|middle" +        else +            restart_mfe +        fi +        echo "    Updates available!"  		exit      else -        msg_client.py --msg "Checking for updates...|middle" --timeout 600 --tag "checkUpdates" +        [ "$1" = false ] && { msg_client.py --msg "Checking for updates...|middle" --timeout 600 --tag "checkUpdates"; } +        echo "    Checking for updates..."      fi  }  chck ()  {  	sudo pacman -Sy +    [ -f /tmp/to_be_upgraded ] && sudo rm /tmp/to_be_upgraded  	sudo pacman -Qu > /tmp/to_be_upgraded  	if [[ ! -s /tmp/to_be_upgraded ]]  	then -        msg_client.py --clear --tag "checkUpdates" -        msg_client.py --kill -		msg_client.py --msg "No updates available.\nReturning to Main Menu.|middle" +        #hide LinHES update message on main menu +        echo "    Hiding update message in LinHES theme." +        sudo sed -i '/LinHES UPDATE MESSAGE/c\        <!--LinHES UPDATE MESSAGE' /usr/share/mythtv/themes/LinHES/menu-ui.xml + +        if [ "$1" = false ]; then +            msg_client.py --clear --tag "checkUpdates" +            msg_client.py --kill +		    msg_client.py --msg "No updates available.\nReturning to Main Menu.|middle" +        else +            restart_mfe +        fi +        echo "    No updates available."  	else +        echo "    Update menu to show Updates Available."  		mv /usr/share/mythtv/themes/defaultmenu/linhes.xml /tmp/linhes.xml.tmp  		sed -e '/\#Check/,/\#Check/d' < /tmp/linhes.xml.tmp > /usr/share/mythtv/themes/defaultmenu/linhes.xml  		mv /usr/share/mythtv/themes/defaultmenu/linhes.xml /tmp @@ -38,7 +74,7 @@ chck ()  {  		mv /tmp/linhes.xml.tmp /usr/share/mythtv/themes/defaultmenu/linhes.xml  		echo "<mythmenu name=\"LH_UPDATE\">" > /tmp/update3.xml.tmp -		 +          #check for kernel update and warn reboot is required          if grep linux /tmp/to_be_upgraded || grep nvidia /tmp/to_be_upgraded   		then @@ -50,7 +86,7 @@ chck ()  {              echo "     <action>NONE</action>" >> /tmp/update3.xml.tmp              echo "   </button>" >> /tmp/update3.xml.tmp  		fi -         +          #create menu items for each pkg to be upgraded  		while read line; do              echo "" >> /tmp/update3.xml.tmp @@ -63,9 +99,28 @@ chck ()  {          done < "/tmp/to_be_upgraded"          echo "</mythmenu>" >> /tmp/update3.xml.tmp          mv /tmp/update3.xml.tmp /usr/share/mythtv/themes/defaultmenu/update3.xml -  		dsply + +        #show LinHES update message on main menu +        echo "    Showing update message in LinHES theme." +        sudo sed -i '/LinHES UPDATE MESSAGE/c\        <!--LinHES UPDATE MESSAGE-->' /usr/share/mythtv/themes/LinHES/menu-ui.xml + +  		dsply $1  	fi  	} -dsply -chck +# Command line argument handling +SILENT=false +while getopts "hs" OPT ; do +    case $OPT in +    h) usage 0 ;; +    s) SILENT=true ;; +    *) usage 1 ;; +    esac +done +# Check for extra cruft on the command line... +shift $(($OPTIND - 1)) +[ -n "$*" ] && usage 1 + +dsply $SILENT +chck $SILENT +#pkill -USR1 mythfrontend diff --git a/abs/core/LinHES-system/linhes_update2.sh b/abs/core/LinHES-system/linhes_update2.sh index 6087729..5c05c99 100644 --- a/abs/core/LinHES-system/linhes_update2.sh +++ b/abs/core/LinHES-system/linhes_update2.sh @@ -1,5 +1,5 @@  #!/bin/bash - +#Called from LinHES Service Menu Updates Available Menu  if [[ "$1" = "install" ]]  then @@ -16,7 +16,7 @@ else      msg_client.py --msg "Updates canceled.|middle"  fi -rm -fr /tmp/to_be_upgraded +sudo rm -fr /tmp/to_be_upgraded  if grep -q Updates\ Available /usr/share/mythtv/themes/defaultmenu/linhes.xml  then @@ -36,3 +36,16 @@ then  	echo "</mythmenu>" >> /tmp/linhes.xml.tmp  	mv /tmp/linhes.xml.tmp /usr/share/mythtv/themes/defaultmenu/linhes.xml  fi + +#hide LinHES update message on main menu +sudo sed -i '/LinHES UPDATE MESSAGE/c\        <!--LinHES UPDATE MESSAGE' /usr/share/mythtv/themes/LinHES/menu-ui.xml + +#reload theme +#check if started from mythfrontend-start and if so just kill mfe +#otherwise reload mfe +if [[ `pgrep -fx "/bin/bash /usr/LH/bin/mythfrontend-start"` ]] +then +    killall mythfrontend +else +    pkill -USR1 mythfrontend +fi diff --git a/abs/core/LinHES-system/myth2mkv b/abs/core/LinHES-system/myth2mkv index aedccff..1352476 100644 --- a/abs/core/LinHES-system/myth2mkv +++ b/abs/core/LinHES-system/myth2mkv @@ -2,13 +2,14 @@  #  # Convert video to AVC-1 / h264  # -# version 0.25-004 +# version 0.27-001  #  # Prerequisites: -#   - mythtv >= 0.25 +#   - mythtv >= 0.27  #   - handbrake-cli  #   - mplayer  #   - mkvtoolnix +#   - jobqueue_helper.py  #  # Arguments  # $1 must be the directory/file of the recording @@ -20,7 +21,9 @@  # $7 must be quality of encode  #  # As a MythTV user job: -# myth2mkv "%DIR%/%FILE%" "%CHANID%" "%STARTTIME%" "%TITLE%" "%SUBTITLE%" "%JOBID%" HQ|MQ|LQ +# myth2mkv "%DIR%/%FILE%" "%CHANID%" "%STARTTIME%" "%TITLE%" "%SUBTITLE%" "%JOBID%" "HP|HQ|MQ|LQ" +# Select only 1 quality setting +# HP is similar to the HandBrake built-in preset High Profile  ########################  #                      # @@ -47,12 +50,13 @@ TUNING=""  # Crop 160 pixels off the left and right for 4:3 image in 1280x720 frame  # <T:B:L:R>  # i.e. 0:0:240:240 -# Default: In HQ: CROP="0:0:0:0" (no cropping). +# Default: In HP and HQ: CROP="0:0:0:0" (no cropping).  #          IN MQ and LQ: autocrop.  CROP=""  # Force custom output resolution.  # Default: Keep same resolution as input file (less any cropping). +# The HP quality setting always keeps the same resolution as the input file.  WIDTH=""  HEIGHT="" @@ -69,15 +73,6 @@ DEINT="G"  #                          #  ############################ -if [[ -e $HOME/.mythtv/mysql.txt ]] ; then -  . $HOME/.mythtv/mysql.txt -else -  DBHostName=${DBHostName:-"localhost"} -  DBUserName=${DBUserName:-"mythtv"} -  DBPassword=${DBPassword:-"mythtv"} -  DBName=${DBName:-"mythconverg"} -fi -  if [[ ! -d ${TMPDIR} ]] ; then    mkdir -p ${TMPDIR}  fi @@ -91,8 +86,7 @@ update_comment()  # Arg_1 = COMMENT  {  if [ ${NO_JOBID} -eq 0 ]; then -   SQL_CMD="update jobqueue set comment=\"${1}\" where id=\"${JOBID}\";" -   `${MYSQLCMD} "${SQL_CMD}"` +    `jobqueue_helper.py -j ${JOBID} -cs "${1}"`  fi  } @@ -101,17 +95,16 @@ check_background_progress()  {  while [ `tail -2 ${STATUSFILE} | grep -c "^HandBrake has exited"` = 0 ]  do -    sleep 10 +    sleep 15      check_myth_jobcmds -    pass=`tail -1 ${STATUSFILE} | egrep -o -e 'task [0-9]' | tail -1 | sed 's/task\ //g'` +    pass=`tail -1 ${STATUSFILE} | egrep -o -e 'task [0-9] of [0-9], ' | tail -1 | sed 's/task\ /Pass\ /g'`      prog_percent=`tail -1 ${STATUSFILE} | egrep -o -e '[0-9]*\.[0-9]. %' | tail -1 | sed 's/\ %//g'`      current_FPS=`tail -1 ${STATUSFILE} | egrep -o -e 'avg [0-9.]*\.[0-9]* fps' | tail -1 | sed -e 's/avg\ //g' -e 's/\ fps//g'`      current_ETA=`tail -1 ${STATUSFILE} | egrep -o -e 'ETA [0-9.][0-9.]h[0-9.][0-9.]m[0-9.][0-9.]s' | tail -1`      if [ -n "$prog_percent" ]; then -        echo "Pass ${pass}, ${prog_percent}% @ ${current_FPS} fps. ${current_ETA}" -        update_comment "Pass ${pass} of 2, ${prog_percent}% @ ${current_FPS} fps. ${current_ETA}" +        echo "${pass}${prog_percent}% @ ${current_FPS} fps. ${current_ETA}" +        update_comment "${pass}${prog_percent}% @ ${current_FPS} fps. ${current_ETA}"      fi -    sleep 10  done  } @@ -119,24 +112,24 @@ check_myth_jobcmds()  # check the myth database for stop pause or resume commands  {  if [[ ${NO_JOBID} -eq 0 ]] ; then -    CURRENT_CMD=`${MYSQLCMD} "select cmds from jobqueue where id = \"${JOBID}\";"` +    CURRENT_CMD=`jobqueue_helper.py -m "select cmds from jobqueue where id = ${JOBID}"`      case "${CURRENT_CMD}" in          # JOB_RUN          0) ;;          # JOB_PAUSE -        1) `${MYSQLCMD} "update jobqueue set status=\"6\" where id=\"${JOBID}\";"` +        1) `jobqueue_helper.py -j ${JOBID} -ss 6`             kill -s STOP ${handbrake_pid} ;;          # JOB_RESUME -        2) `${MYSQLCMD} "update jobqueue set status=\"4\" where id=\"${JOBID}\";"` -           `${MYSQLCMD} "update jobqueue set cmds=\"0\" where id=\"${JOBID}\";"` +        2) `jobqueue_helper.py -j ${JOBID} -ss 4` +           `jobqueue_helper.py -j ${JOBID} -cmds 0`             kill -s CONT ${handbrake_pid} ;;          # JOB_STOP -        4) `${MYSQLCMD} "update jobqueue set status=\"5\" where id=\"${JOBID}\";"` -           `${MYSQLCMD} "update jobqueue set cmds=\"0\" where id=\"${JOBID}\";"` +        4) `jobqueue_helper.py -j ${JOBID} -ss 5` +           `jobqueue_helper.py -j ${JOBID} -cmds 0`             kill -9 ${handbrake_pid} ${command_pid}             clean_up_files             echo "Encode Cancelled" >> ${LOGFILE} -            `${MYSQLCMD} "update jobqueue set status=\"320\" where id=\"${JOBID}\";"` +           `jobqueue_helper.py -j ${JOBID} -ss 320`             exit ;;      esac  fi @@ -200,7 +193,14 @@ if [[ -n ${HEIGHT} ]] ; then    HEIGHT="-l ${HEIGHT} -Y ${HEIGHT}"  fi -if [[ ${QUALITY} = HQ ]] ; then +if [[ ${QUALITY} = HP ]] ; then +  if [[ -n ${CROP} ]] ; then +    CROP="--crop ${CROP}" +  else +    CROP="--crop 0:0:0:0 --strict-anamorphic" +  fi +  HB_OPTS="-o ${TMPFILE} -e x264 ${TUNING} -q 20.0 -a 1,1 -E copy:ac3,faac -B 160,160 -6 dpl2,auto -R Auto,Auto -D 0.0,0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mkv --decomb --loose-anamorphic --modulus 2 -m --x264-preset medium --h264-profile high --h264-level 4.1 ${CROP} -s 1" +elif [[ ${QUALITY} = HQ ]] ; then    if [[ -n ${CROP} ]] ; then      CROP="--crop ${CROP}"    else @@ -260,10 +260,18 @@ echo "       <Simple>" >> "${TAG_FILE}"  echo "         <Name>DATE_RELEASED</Name>" >> "${TAG_FILE}"  echo "         <String>${OAD}</String>" >> "${TAG_FILE}"  echo "       </Simple>" >> "${TAG_FILE}" +echo "       <Simple>" >> "${TAG_FILE}" +echo "         <Name>SEASON</Name>" >> "${TAG_FILE}" +echo "         <String>${SEASON}</String>" >> "${TAG_FILE}" +echo "       </Simple>" >> "${TAG_FILE}" +echo "       <Simple>" >> "${TAG_FILE}" +echo "         <Name>EPISODE</Name>" >> "${TAG_FILE}" +echo "         <String>${EPISODE}</String>" >> "${TAG_FILE}" +echo "       </Simple>" >> "${TAG_FILE}"  echo "     </Simple>" >> "${TAG_FILE}"  echo "   </Simple>" >> "${TAG_FILE}"  echo "   <Simple>" >> "${TAG_FILE}" -echo "       <Name>ENCODER</Name>" >> "${TAG_FILE}" +echo "       <Name>ENCODED_BY</Name>" >> "${TAG_FILE}"  echo "       <String>HandBrakeCLI ${HBCLIVER}</String>" >> "${TAG_FILE}"  echo "   </Simple>" >> "${TAG_FILE}"  echo "   <Simple>" >> "${TAG_FILE}" @@ -308,9 +316,12 @@ STATUSFILE=/tmp/${TMPNAME}-status.log  HB_RETURN_CODE=/tmp/${TMPNAME}-hb_return_code  IDFILE=/tmp/${TMPNAME}-id.txt  TAG_FILE=/tmp/${TMPNAME}.xml -MYSQLCMD="mysql -B --skip-column-names -u ${DBUserName} -p${DBPassword} -h ${DBHostName} -D ${DBName} -e" -OAD=`${MYSQLCMD} "select originalairdate from recorded where basename LIKE '${BASE}';"` -DESCR=`${MYSQLCMD} "select description from recorded where basename LIKE '${BASE}';" | sed 's/\&/and/g'` +SEASON=`jobqueue_helper.py -m "select season from recorded where basename LIKE '${BASE}'"` +SEASON=`printf "%02d" $SEASON` +EPISODE=`jobqueue_helper.py -m "select episode from recorded where basename LIKE '${BASE}'"` +EPISODE=`printf "%02d" $EPISODE` +OAD=`jobqueue_helper.py -m "select originalairdate from recorded where basename LIKE '${BASE}'"` +DESCR=`jobqueue_helper.py -m "select description from recorded where basename LIKE '${BASE}'" | sed 's/\&/and/g'`  USER=`whoami`  # check if %JOBID% is passed from command line @@ -392,7 +403,11 @@ fi  # make output filename unique and do not clobber an existing file  # Build a final file name -FILE=$( echo "${TITLE,,} ${OAD} ${SUBTITLE,,}" | tr -d [:punct:] | tr [:blank:] "_" | tr -s "_" ) +if [[ $SEASON != "00" && $EPISODE != "00" ]]; then +    FILE=$( echo "${TITLE,,} s${SEASON}e${EPISODE} ${SUBTITLE,,}" | tr -d [:punct:] | tr [:blank:] "_" | tr -s "_" ) +else +    FILE=$( echo "${TITLE,,} ${OAD} ${SUBTITLE,,}" | tr -d [:punct:] | tr [:blank:] "_" | tr -s "_" ) +fi  OUTPUTFILE="${OUTDIR}/${FILE}.mkv"  i=1  while [ -e "${OUTPUTFILE}" ] @@ -422,9 +437,27 @@ if [ ${ERROR} -eq 0 ]; then      seconds=$((seconds % 3600))      minutes=$((seconds / 60))      seconds=$((seconds % 60)) -    echo "Encoding took ${hours} hour\(s\) ${minutes} minute\(s\) ${seconds} second\(s\) @ ${current_FPS} fps." >> ${LOGFILE} -    `${MYSQLCMD} "update jobqueue set status = \"272\" where id = \"${JOBID}\";"` -    update_comment "Encode Successful. Encoding Time: ${hours} hour\(s\) ${minutes} minute\(s\) ${seconds} second\(s\)" +    if [ $hours -eq 0 ]; then +        hours="" +    elif [ $hours -eq 1 ]; then +        hours=" $hours hour" +    else +        hours=" $hours hours" +    fi +    if [ $minutes -eq 1 ]; then +        minutes="$minutes minute" +    else +        minutes="$minutes minutes" +    fi +    if [ $seconds -eq 1 ]; then +        seconds="$seconds second" +    else +        seconds="$seconds seconds" +    fi + +    echo "Encoding took${hours} ${minutes} ${seconds} @ ${current_FPS} fps." >> ${LOGFILE} +    `jobqueue_helper.py -j ${JOBID} -ss 272` +    update_comment "Encode Successful. Encoding Time:${hours} ${minutes} ${seconds}"  else      echo "ERROR: ${ERROR}" >> ${LOGFILE}  fi diff --git a/abs/core/LinHES-system/myth_mtc.py b/abs/core/LinHES-system/myth_mtc.py index 7847313..b1ec75d 100755 --- a/abs/core/LinHES-system/myth_mtc.py +++ b/abs/core/LinHES-system/myth_mtc.py @@ -6,6 +6,8 @@ import socket  import os  import datetime,time  import shlex +sys.dont_write_bytecode = True +import idle  try:      from MythTV import MythBE @@ -21,15 +23,13 @@ def get_timestamp():      date = (now.strftime('%Y-%m-%d %H:%M'))       return date -  def optimize(): -         try:          cursor = mythtv.db.cursor()          cursor.execute("SHOW tables")          result = cursor.fetchall()      except: -        print "    Problem getting tables from database" +        print "\n%s Problem getting tables from the database" %(get_timestamp())  	return      ops=["REPAIR","OPTIMIZE","ANALYZE"]      for row in result: @@ -39,95 +39,15 @@ def optimize():              cmd= "%s  table %s" %(op,ctable)              cursor.execute(cmd) - -def upcoming_check(): -    print "    Checking for upcoming shows" -    try: -        upcoming = mythtv.getUpcomingRecordings() -    except: -        return True -    try: -        show=str(upcoming[0]) -        show=show.strip() -        showtime=show.partition("(")[2].strip(")") -        now=time.time() -        rec_time=time.strptime( showtime ,"%Y-%m-%d %H:%M:%S" ) -        r=time.mktime(rec_time) -        time_diff= ( r - now ) / 60 -    except: -        time_diff=100 -        show="No show" -    if ( time_diff  >  30) : -        return True -    else: -        print "      %s is upcoming in %s" %(show,time_diff) -        return False - - -def schemalock_check(): -    print "    Checking if schema is locked" -    try: -        c = mythtv.db.cursor() -        c.execute("select count(*) from schemalock") -        results=c.fetchone() -        schemalock=results[0] -    except: -        return True - -    if schemalock == 0: -        return True -    else: -        print "      schema is locked" -        return False - -def job_check(): -    print "    Checking jobqueue" +def cleanup_inuseprograms(): +    fourHoursAgo=datetime.datetime.today() - datetime.timedelta(hours=4) +    cmd="DELETE FROM inuseprograms WHERE lastupdatetime < '%s';" %fourHoursAgo      try: -        c = mythtv.db.cursor() -        c.execute("select count(*) from jobqueue where status = 4") -        results=c.fetchone() +        cursor = mythtv.db.cursor() +        cursor.execute(cmd)      except: -        return True -    jobs= results[0] -    if jobs == 0 : -        return True -    else: -        print "      jobs are running" -        return False - +        print "\n%s Problem cleaning inuseprograms in database" %(get_timestamp()) -def in_use(): -    print "    Checking if programs are in use" -    try: -        c = mythtv.db.cursor() -        c.execute("select count(*) from inuseprograms") -        results=c.fetchone() -    except: -        return True -    prginuse=results[0] -    if prginuse == 0 : -        return True -    else: -        print "      Programs in use" -        return False - -def mfd_check(): -    print "    Checking is mythfilldatabase is running" -    ps = subprocess.Popen("ps ax -o pid= -o args= ", shell=True, stdout=subprocess.PIPE) -    ps_pid = ps.pid -    output = ps.stdout.read() -    ps.stdout.close() -    ps.wait() -    proc_name="mythfilldatabase" -    for line in output.split("\n"): -        res = re.findall("(\d+) (.*)", line) -        if res: -            pid = int(res[0][0]) -            if proc_name in res[0][1] and pid != os.getpid() and pid != ps_pid: -                print "      mythfilldatabase is running" -                return False - -    return True  def bail_if_another_is_running():      cmd = shlex.split("pgrep -u {} -f {}".format(os.getuid(), __file__)) @@ -138,19 +58,9 @@ def bail_if_another_is_running():              __file__, " ".join(pids))          raise SystemExit(1) - -def idle_check(): -    print "\n%s Checking Idle" %(get_timestamp()) -    if  (   upcoming_check() and schemalock_check() and job_check()  and in_use() and  mfd_check()  ): -        idle=True -        print "\n%s Myth is idle" %(get_timestamp()) -    else: -        idle=False -        print "\n%s Myth is NOT idle" %(get_timestamp()) -    return idle -  def run_stuff(): -    if idle_check(): +    print "\n%s" %get_timestamp() +    if idle.main():          print "\n#######################################"          print "\n%s Running Optimize" %(get_timestamp())          optimize() @@ -174,6 +84,7 @@ bail_if_another_is_running()  starttime=time.time()  ctin=True  while ctin: +    cleanup_inuseprograms()      ctin=run_stuff()      if  ctin:          print "\n%s Waiting 10 minutes before trying again." %(get_timestamp()) @@ -184,4 +95,3 @@ while ctin:          ctin = False          print "\n%s Time Exceeded 50 minutes. Quitting.)" %(get_timestamp())          exit(1) - diff --git a/abs/core/LinHES-system/mythvidexport.py b/abs/core/LinHES-system/mythvidexport.py new file mode 100755 index 0000000..5fdfb6d --- /dev/null +++ b/abs/core/LinHES-system/mythvidexport.py @@ -0,0 +1,439 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- +#--------------------------- +# Name: mythvidexport.py +# Python Script +# Author: Raymond Wagner +# Purpose +#   This python script is intended to function as a user job, run through +#   mythjobqueue, capable of exporting recordings into MythVideo. +# https://github.com/wagnerrp/mythtv-scripts/blob/master/python/mythvidexport.py +# http://www.mythtv.org/wiki/Mythvidexport.py +#--------------------------- +__title__  = "MythVidExport" +__author__ = "Raymond Wagner" +__version__= "v0.7.5" + +from MythTV import MythDB, Job, Recorded, Video, VideoGrabber,\ +                   MythLog, MythError, static, MythBE +from optparse import OptionParser, OptionGroup +from socket import gethostname + +import os +import re +import sys +import time +import hashlib + +def create_dummy_video(db=None): +    db = MythDB(db) + +def hashfile(fd): +    hasher = hashlib.sha1() +    while True: +        buff = fd.read(2**16) +        if len(buff) == 0: +            break +        hasher.update(buff) +    return hasher.hexdigest() + +class VIDEO: +    def __init__(self, opts, jobid=None): +        if jobid: +            self.job = Job(jobid) +            self.chanid = self.job.chanid +            self.starttime = self.job.starttime +            self.job.update(status=Job.STARTING) +        else: +            self.job = None +            self.chanid = opts.chanid +            self.starttime = opts.starttime + +        self.opts = opts +        self.db = MythDB() +        self.log = MythLog(module='mythvidexport.py', db=self.db) + +        # load setting strings +        self.get_format() + +        # prep objects +        self.rec = Recorded((self.chanid,self.starttime), db=self.db) +        self.log(MythLog.GENERAL, MythLog.INFO, 'Using recording', +                        '%s - %s' % (self.rec.title.encode('utf-8'),  +                                     self.rec.subtitle.encode('utf-8'))) +        self.vid = Video(db=self.db).create({'title':'', 'filename':'', +                                             'host':gethostname()}) + +        # process data +        self.get_meta() +        self.get_dest() +        # bug fix to work around limitation in the bindings where DBDataRef classes +        # are mapped to the filename at time of Video element creation. since the +        # filename is specified as blank when the video is created, the markup +        # handler is not properly initialized +        self.vid.markup._refdat = (self.vid.filename,) + +        # save file +        self.copy() +        if opts.seekdata: +            self.copy_seek() +        if opts.skiplist: +            self.copy_markup(static.MARKUP.MARK_COMM_START, +                             static.MARKUP.MARK_COMM_END) +        if opts.cutlist: +            self.copy_markup(static.MARKUP.MARK_CUT_START, +                             static.MARKUP.MARK_CUT_END) +        self.vid.update() + +        # delete old file +        if opts.delete: +            self.rec.delete() + +    def get_format(self): +        host = self.db.gethostname() +        # TV Format +        if self.opts.tformat: +            self.tfmt = self.opts.tformat +        elif self.db.settings[host]['mythvideo.TVexportfmt']: +            self.tfmt = self.db.settings[host]['mythvideo.TVexportfmt'] +        else: +            self.tfmt = 'Television/%TITLE%/Season %SEASON%/'+\ +                            '%TITLE% - S%SEASON%E%EPISODEPAD% - %SUBTITLE%' + +        # Movie Format +        if self.opts.mformat: +            self.mfmt = self.opts.mformat +        elif self.db.settings[host]['mythvideo.MOVIEexportfmt']: +            self.mfmt = self.db.settings[host]['mythvideo.MOVIEexportfmt'] +        else: +            self.mfmt = 'Movies/%TITLE%' + +        # Generic Format +        if self.opts.gformat: +            self.gfmt = self.opts.gformat +        elif self.db.settings[host]['mythvideo.GENERICexportfmt']: +            self.gfmt = self.db.settings[host]['mythvideo.GENERICexportfmt'] +        else: +            self.gfmt = 'Videos/%TITLE%' + +    def get_meta(self): +        self.vid.hostname = self.db.gethostname() +        if self.rec.inetref: +            # good data is available, use it +            if self.rec.season > 0 or self.rec.episode > 0: +                self.log(self.log.GENERAL, self.log.INFO, +                        'Performing TV export with local data.') +                self.type = 'TV' +                grab = VideoGrabber(self.type) +                metadata = grab.grabInetref(self.rec.inetref, self.rec.season, self.rec.episode) +            else: +                self.log(self.log.GENERAL, self.log.INFO, +                        'Performing Movie export with local data.') +                self.type = 'MOVIE' +                grab = VideoGrabber(self.type) +                metadata = grab.grabInetref(self.rec.inetref) +        elif self.opts.listingonly: +            # force use of local data +            if self.rec.subtitle: +                self.log(self.log.GENERAL, self.log.INFO, +                        'Forcing TV export with local data.') +                self.type = 'TV' +            else: +                self.log(self.log.GENERAL, self.log.INFO, +                        'Forcing Movie export with local data.') +                self.type = 'MOVIE' +            metadata = self.rec.exportMetadata() +        else: +            if self.rec.subtitle: +                # subtitle exists, assume tv show +                self.type = 'TV' +                self.log(self.log.GENERAL, self.log.INFO, +                        'Attempting TV export.') +                grab = VideoGrabber(self.type) +                match = grab.sortedSearch(self.rec.title, self.rec.subtitle) +            else:                   # assume movie +                self.type = 'MOVIE' +                self.log(self.log.GENERAL, self.log.INFO, +                        'Attempting Movie export.') +                grab = VideoGrabber(self.type) +                match = grab.sortedSearch(self.rec.title) + +            if len(match) == 0: +                # no match found +                self.log(self.log.GENERAL, self.log.INFO, +                        'Falling back to generic export.') +                self.type = 'GENERIC' +                metadata = self.rec.exportMetadata() +            elif (len(match) > 1) & (match[0].levenshtein > 0): +                # multiple matches found, and closest is not exact +                self.vid.delete() +                raise MythError('Multiple metadata matches found: '\ +                                                   +self.rec.title) +            else: +                self.log(self.log.GENERAL, self.log.INFO, +                        'Importing content from', match[0].inetref) +                metadata = grab.grabInetref(match[0]) + +        self.vid.importMetadata(metadata) +        self.log(self.log.GENERAL, self.log.INFO, 'Import complete') + +    def get_dest(self): +        if self.type == 'TV': +            self.vid.filename = self.process_fmt(self.tfmt) +        elif self.type == 'MOVIE': +            self.vid.filename = self.process_fmt(self.mfmt) +        elif self.type == 'GENERIC': +            self.vid.filename = self.process_fmt(self.gfmt) + +    def process_fmt(self, fmt): +        # replace fields from viddata +        #print self.vid.data +        ext = '.'+self.rec.basename.rsplit('.',1)[1] +        rep = ( ('%TITLE%','title','%s'),   ('%SUBTITLE%','subtitle','%s'), +            ('%SEASON%','season','%d'),     ('%SEASONPAD%','season','%02d'), +            ('%EPISODE%','episode','%d'),   ('%EPISODEPAD%','episode','%02d'), +            ('%YEAR%','year','%s'),         ('%DIRECTOR%','director','%s')) +        for tag, data, format in rep: +            if self.vid[data]: +                fmt = fmt.replace(tag,format % self.vid[data]) +            else: +                fmt = fmt.replace(tag,'') + +        # replace fields from program data +        rep = ( ('%HOSTNAME%',    'hostname',    '%s'), +                ('%STORAGEGROUP%','storagegroup','%s')) +        for tag, data, format in rep: +            data = getattr(self.rec, data) +            fmt = fmt.replace(tag,format % data) + +#       fmt = fmt.replace('%CARDID%',self.rec.cardid) +#       fmt = fmt.replace('%CARDNAME%',self.rec.cardid) +#       fmt = fmt.replace('%SOURCEID%',self.rec.cardid) +#       fmt = fmt.replace('%SOURCENAME%',self.rec.cardid) +#       fmt = fmt.replace('%CHANNUM%',self.rec.channum) +#       fmt = fmt.replace('%CHANNAME%',self.rec.cardid) + +        if len(self.vid.genre): +            fmt = fmt.replace('%GENRE%',self.vid.genre[0].genre) +        else: +            fmt = fmt.replace('%GENRE%','') +#       if len(self.country): +#           fmt = fmt.replace('%COUNTRY%',self.country[0]) +#       else: +#           fmt = fmt.replace('%COUNTRY%','') +        return fmt+ext + +    def copy(self): +        stime = time.time() +        srcsize = self.rec.filesize +        htime = [stime,stime,stime,stime] + +        self.log(MythLog.GENERAL|MythLog.FILE, MythLog.INFO, "Copying myth://%s@%s/%s"\ +               % (self.rec.storagegroup, self.rec.hostname, self.rec.basename)\ +                                                    +" to myth://Videos@%s/%s"\ +                                          % (self.vid.host, self.vid.filename)) +        srcfp = self.rec.open('r') +        dstfp = self.vid.open('w', nooverwrite=True) + + +        if self.job: +            self.job.setStatus(Job.RUNNING) +        tsize = 2**24 +        while tsize == 2**24: +            tsize = min(tsize, srcsize - dstfp.tell()) +            dstfp.write(srcfp.read(tsize)) +            htime.append(time.time()) +            rate = float(tsize*4)/(time.time()-htime.pop(0)) +            remt = (srcsize-dstfp.tell())/rate +            if self.job: +                self.job.setComment("%02d%% complete - %d seconds remaining" %\ +                            (dstfp.tell()*100/srcsize, remt)) +        srcfp.close() +        dstfp.close() + +        self.vid.hash = self.vid.getHash() + +        self.log(MythLog.GENERAL|MythLog.FILE, MythLog.INFO, "Transfer Complete", +                            "%d seconds elapsed" % int(time.time()-stime)) + +        if self.opts.reallysafe: +            if self.job: +                self.job.setComment("Checking file hashes") +            self.log(MythLog.GENERAL|MythLog.FILE, MythLog.INFO, "Checking file hashes.") +            srchash = hashfile(self.rec.open('r')) +            dsthash = hashfile(self.vid.open('r')) +            if srchash != dsthash: +                raise MythError('Source hash (%s) does not match destination hash (%s)' \ +                            % (srchash, dsthash)) +        elif self.opts.safe: +            self.log(MythLog.GENERAL|MythLog.FILE, MythLog.INFO, "Checking file sizes.") +            be = MythBE(db=self.vid._db) +            try: +                srcsize = be.getSGFile(self.rec.hostname, self.rec.storagegroup, \ +                                       self.rec.basename)[1] +                dstsize = be.getSGFile(self.vid.host, 'Videos', self.vid.filename)[1] +            except: +                raise MythError('Could not query file size from backend') +            if srcsize != dstsize: +                raise MythError('Source size (%d) does not match destination size (%d)' \ +                            % (srcsize, dstsize)) + +        if self.job: +            self.job.setComment("Complete - %d seconds elapsed" % \ +                            (int(time.time()-stime))) +            self.job.setStatus(Job.FINISHED) + +    def copy_seek(self): +        for seek in self.rec.seek: +            self.vid.markup.add(seek.mark, seek.offset, seek.type) + +    def copy_markup(self, start, stop): +        for mark in self.rec.markup: +            if mark.type in (start, stop): +                self.vid.markup.add(mark.mark, 0, mark.type) + +def usage_format(): +    usagestr = """The default strings are: +    Television: Television/%TITLE%/Season %SEASON%/%TITLE% - S%SEASON%E%EPISODEPAD% - %SUBTITLE% +    Movie:      Movies/%TITLE% +    Generic:    Videos/%TITLE% + +Available strings: +    %TITLE%:         series title +    %SUBTITLE%:      episode title +    %SEASON%:        season number +    %SEASONPAD%:     season number, padded to 2 digits +    %EPISODE%:       episode number +    %EPISODEPAD%:    episode number, padded to 2 digits +    %YEAR%:          year +    %DIRECTOR%:      director +    %HOSTNAME%:      backend used to record show +    %STORAGEGROUP%:  storage group containing recorded show +    %GENRE%:         first genre listed for recording +""" +#    %CARDID%:        ID of tuner card used to record show +#    %CARDNAME%:      name of tuner card used to record show +#    %SOURCEID%:      ID of video source used to record show +#    %SOURCENAME%:    name of video source used to record show +#    %CHANNUM%:       ID of channel used to record show +#    %CHANNAME%:      name of channel used to record show +#    %COUNTRY%:       first country listed for recording +    print usagestr + +def print_format(): +    db = MythDB() +    host = gethostname() +    tfmt = db.settings[host]['mythvideo.TVexportfmt'] +    if not tfmt: +        tfmt = 'Television/%TITLE%/Season %SEASON%/%TITLE% - S%SEASON%E%EPISODEPAD% - %SUBTITLE%' +    mfmt = db.settings[host]['mythvideo.MOVIEexportfmt'] +    if not mfmt: +        mfmt = 'Movies/%TITLE%' +    gfmt = db.settings[host]['mythvideo.GENERICexportfmt'] +    if not gfmt: +        gfmt = 'Videos/%TITLE%' +    print "Current output formats:" +    print "    TV:      "+tfmt +    print "    Movies:  "+mfmt +    print "    Generic: "+gfmt + +def main(): +    parser = OptionParser(usage="usage: %prog [options] [jobid]") + +    formatgroup = OptionGroup(parser, "Formatting Options", +                    "These options are used to display and manipulate the output file formats.") +    formatgroup.add_option("-f", "--helpformat", action="store_true", default=False, dest="fmthelp", +            help="Print explination of file format string.") +    formatgroup.add_option("-p", "--printformat", action="store_true", default=False, dest="fmtprint", +            help="Print current file format string.") +    formatgroup.add_option("--tformat", action="store", type="string", dest="tformat", +            help="Use TV format for current task. If no task, store in database.") +    formatgroup.add_option("--mformat", action="store", type="string", dest="mformat", +            help="Use Movie format for current task. If no task, store in database.") +    formatgroup.add_option("--gformat", action="store", type="string", dest="gformat", +            help="Use Generic format for current task. If no task, store in database.") +    formatgroup.add_option("--listingonly", action="store_true", default=False, dest="listingonly", +            help="Use data from listing provider, rather than grabber") +    parser.add_option_group(formatgroup) + +    sourcegroup = OptionGroup(parser, "Source Definition", +                    "These options can be used to manually specify a recording to operate on "+\ +                    "in place of the job id.") +    sourcegroup.add_option("--chanid", action="store", type="int", dest="chanid", +            help="Use chanid for manual operation") +    sourcegroup.add_option("--starttime", action="store", type="string", dest="starttime", +            help="Use starttime for manual operation") +    parser.add_option_group(sourcegroup) + +    actiongroup = OptionGroup(parser, "Additional Actions", +                    "These options perform additional actions after the recording has been exported.") +    actiongroup.add_option('--safe', action='store_true', default=False, dest='safe', +            help='Perform quick sanity check of exported file using file size.') +    actiongroup.add_option('--really-safe', action='store_true', default=False, dest='reallysafe', +            help='Perform slow sanity check of exported file using SHA1 hash.') +    actiongroup.add_option("--delete", action="store_true", default=False, +            help="Delete source recording after successful export. Enforces use of --safe.") +    parser.add_option_group(actiongroup) + +    othergroup = OptionGroup(parser, "Other Data", +                    "These options copy additional information from the source recording.") +    othergroup.add_option("--seekdata", action="store_true", default=False, dest="seekdata", +            help="Copy seekdata from source recording.") +    othergroup.add_option("--skiplist", action="store_true", default=False, dest="skiplist", +            help="Copy commercial detection from source recording.") +    othergroup.add_option("--cutlist", action="store_true", default=False, dest="cutlist", +            help="Copy manual commercial cuts from source recording.") +    parser.add_option_group(othergroup) + +    MythLog.loadOptParse(parser) + +    opts, args = parser.parse_args() + +    if opts.verbose: +        if opts.verbose == 'help': +            print MythLog.helptext +            sys.exit(0) +        MythLog._setlevel(opts.verbose) + +    if opts.fmthelp: +        usage_format() +        sys.exit(0) + +    if opts.fmtprint: +        print_format() +        sys.exit(0) + +    if opts.delete: +        opts.safe = True + +    if opts.chanid and opts.starttime: +        export = VIDEO(opts) +    elif len(args) == 1: +        try: +            export = VIDEO(opts,int(args[0])) +        except Exception, e: +            Job(int(args[0])).update({'status':Job.ERRORED, +                                      'comment':'ERROR: '+e.args[0]}) +            MythLog(module='mythvidexport.py').logTB(MythLog.GENERAL) +            sys.exit(1) +    else: +        if opts.tformat or opts.mformat or opts.gformat: +            db = MythDB() +            host = gethostname() +            if opts.tformat: +                print "Changing TV format to: "+opts.tformat +                db.settings[host]['mythvideo.TVexportfmt'] = opts.tformat +            if opts.mformat: +                print "Changing Movie format to: "+opts.mformat +                db.settings[host]['mythvideo.MOVIEexportfmt'] = opts.mformat +            if opts.gformat: +                print "Changing Generic format to: "+opts.gformat +                db.settings[host]['mythvideo.GENERICexportfmt'] = opts.gformat +            sys.exit(0) +        else: +            parser.print_help() +            sys.exit(2) + +if __name__ == "__main__": +    main() + diff --git a/abs/core/LinHES-system/mythwelcome-config.py b/abs/core/LinHES-system/mythwelcome-config.py index 5ab24b0..01638a2 100755 --- a/abs/core/LinHES-system/mythwelcome-config.py +++ b/abs/core/LinHES-system/mythwelcome-config.py @@ -16,12 +16,11 @@ mythdb.settings[localhostname].MythShutdownPowerOff = u'sudo /sbin/poweroff'  mythdb.settings[localhostname].MythShutdownXTermCmd = u'xterm'  mythdb.settings[localhostname].MythWelcomeStartFECmd = u'/usr/LH/bin/mythfrontend-start' -  # mythtv-setup Shutdown/Wakeup options -if mythdb.settings[localhostname].idleTimeoutSecs == u'0': -    mythdb.settings[localhostname].idleTimeoutSecs = u'40' +if mythdb.settings.NULL.idleTimeoutSecs == u'0' or mythdb.settings.NULL.idleTimeoutSecs == None: +    mythdb.settings.NULL.idleTimeoutSecs = u'40'  mythdb.settings.NULL.WakeupTimeFormat = u'yyyy-MM-ddThh:mm:ss'  mythdb.settings.NULL.SetWakeuptimeCommand = u'/usr/bin/mythshutdown --setwakeup $time'  mythdb.settings.NULL.ServerHaltCommand = u'/usr/bin/mythshutdown --shutdown' -mythdb.settings.NULL.preSDWUCheckCommand = u'/usr/LH/bin/idle.sh -s -m -r' +mythdb.settings.NULL.preSDWUCheckCommand = u'/usr/LH/bin/idle.py -s -d -l -r' diff --git a/abs/core/LinHES-system/system.install b/abs/core/LinHES-system/system.install index 880ea02..6975070 100644 --- a/abs/core/LinHES-system/system.install +++ b/abs/core/LinHES-system/system.install @@ -13,13 +13,17 @@ post_install() {          rm -f /etc/cron.daily/myth_mtc      fi +    #run mythwelcome-config.py to update shutdown check to use idle.py +    /usr/LH/bin/mythwelcome-config.py +      sv restart msg_daemon  }  # arg 1:  the new package version  # arg 2:  the old package version  pre_upgrade() { -    /bin/true +    msg_client.py --kill +    sv stop msg_daemon  }  # arg 1:  the new package version | 
