diff options
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 |