From e09439b06ef0ad8e3784b1a698596a561838a75c Mon Sep 17 00:00:00 2001
From: Britney Fransen <brfransen@gmail.com>
Date: Sat, 11 Feb 2023 23:53:27 -0500
Subject: linhes-system: add lh_myth_status.py

myth_mtc.py: fix some paths

myth2videos: initial inclusion

myth2mkv & myth2mp3: change /myth paths
---
 linhes/linhes-system/PKGBUILD          |  16 +-
 linhes/linhes-system/lh_myth_status.py | 271 +++++++++++++++++++++++++++++++++
 linhes/linhes-system/myth2mkv          |   4 +-
 linhes/linhes-system/myth2mp3          |   6 +-
 linhes/linhes-system/myth2videos       | 148 ++++++++++++++++++
 linhes/linhes-system/myth_mtc.py       |   4 +-
 6 files changed, 435 insertions(+), 14 deletions(-)
 create mode 100755 linhes/linhes-system/lh_myth_status.py
 create mode 100755 linhes/linhes-system/myth2videos

diff --git a/linhes/linhes-system/PKGBUILD b/linhes/linhes-system/PKGBUILD
index 88194ce..966184b 100755
--- a/linhes/linhes-system/PKGBUILD
+++ b/linhes/linhes-system/PKGBUILD
@@ -1,21 +1,21 @@
 pkgname=linhes-system
 pkgver=9.0.0
-pkgrel=42
+pkgrel=43
 arch=('x86_64')
 #install=$pkgname.install
 pkgdesc="Everything that makes LinHES a system"
 license=('GPL2')
-depends=('cronie' 'dbus-python' 'dvb-firmware' 'flatpak' 'firefox' 'glances' 'kdialog' 'libnotify'
+depends=('cronie' 'dbus-python' 'dvb-firmware' 'expect' 'flatpak' 'firefox' 'glances' 'kdialog' 'libnotify'
          'logrotate' 'linhes-templates' 'linhes-theme' 'mlocate' 'ncdu'
          'openssh' 'pacman-contrib' 'rsyslog' 'ttf-overlock' 'wget' 'x11vnc')
 binfiles="add_storage.py balance_storage_groups.py empty_storage_groups.py remove_storage.py
           checkXFSfrag.sh enableIRWake.sh idle.py lh_system_start.sh lh_notify-send
-          lh_home_check.sh jobqueue_helper.py gen_lib_xml.py
+          lh_home_check.sh lh_myth_status.py jobqueue_helper.py gen_lib_xml.py
           diskspace.sh find_orphans.py optimize_mythdb.py myth_mtc.py
           misc_recent_recordings.pl misc_status_config.py misc_status_info.sh
           misc_upcoming_recordings.pl misc_which_recorder.pl
           create_media_dirs.sh be_check.py
-          myth2mkv myth2mp3 udev_link.sh"
+          myth2mkv myth2mp3 myth2videos udev_link.sh"
 source=($binfiles
     'myth_mtc.cron' 'paccache.cron' 'flatpak_update.cron' 'xfs_defrag.cron'
     'readme_is_xml' 'add_storage.readme' 'LinHES-release' 'lh_log_care.cron'
@@ -33,12 +33,13 @@ sha256sums=('525bfe29b63d3ec5a17a32fa29745e24070020490c3f5b6dd6b03250348fb324'
             '644414148e514e4a56d68959cb427ddde4129a961cbf09cd1a0a39129d58a0b1'
             '6d4fb0ed1a5ed961b3a3884dce093118e50c2981a9cd5837d20abc5a6d4fd8aa'
             '87875d9e5f5ce18208f419698ce69b6bcbcd08955a57a4a13940e715af58b787'
+            '3b3d774701ee52054f6592258b98b9f18c3f7f9a93982ef07c7a3c7cf9445b82'
             '91bdec992bb2c933e15625c181f2195c402060b879168ebf35944cb064c904b9'
             '5cacfdd02833e5a3130d765573e772e6bd5030336ba86239c5e4db5ffa36fc69'
             'ebdb3ee0212e0cc72526bb5e50a032573e1894acb7bf75617243b0b49aa1f8f2'
             '4d006f0fe3b13e67de1b961d611e81911905a30d140849dfdb8e5c0dc4da2f7c'
             'e371c6a289c68fe200d7da856c20a8c579efa23178f4d62235f7359d7f6e49a1'
-            'd63ce11809600e77ff187eee3751a8635045ef14c6333f1584f5e35f15a679a1'
+            '29bf341afcbd54ce9a040596fa6a86622bbfe08addad0c673f68661fb93642b4'
             'd2d69b2bf6315bd37ff5f5b2f0cde8ab2fb89bae18f8796dc5208ffc1a9d743e'
             'a3f8ba840853e4a189dd52520a6958f4030e1cc3391200a6aeef055fb469f0b4'
             '1819085bd2c9106482c5f243b95fddf3dae69212330ab76cb493add5c26a45a4'
@@ -46,8 +47,9 @@ sha256sums=('525bfe29b63d3ec5a17a32fa29745e24070020490c3f5b6dd6b03250348fb324'
             '1d12a128a01dbf6327a80daab9edfdc57d393d02074d19c6a5bd54560cc6b0c0'
             'bffcc13e4b480f720feb2b3c781bc4247c63303250c3d885022c699573d45a33'
             '51093acab5e5a4de51a55f4bdf7b935f4f69edf3d84f1c37db710853ec95eca8'
-            '9ea1b5583cd38f53bb79d9e4ccae91a028db0b6850162d7991b19122c564b9c9'
-            'a81fdb81d8890e73b7891756623d536a133410ea43205b7152a295ad9ab8f3c9'
+            'ad4ddabbc34d5e1b308ece33cabf91d750f44894c52a18325762dea026152973'
+            'a961cfdc6f02b12fb445777dd2c144fed96306ca2f430cc8853ae307c759c1ad'
+            'd8574104b75c6d41284488612ec5583c50a8dab438492fa42c47231add4cfc54'
             '6bdbf593d3e1348d1a8f7c4c17cb2e893f7e18ae355daf978173e669cfe3be80'
             '9e97b4d68c9e8988daacadd40f1de9f0b5945d870eba596a2ceb5e0c023fa9c0'
             '186203d3c0520bb3d611da99d33a7713e9c1563814285f1f101097234f214b2f'
diff --git a/linhes/linhes-system/lh_myth_status.py b/linhes/linhes-system/lh_myth_status.py
new file mode 100755
index 0000000..ea8c6f2
--- /dev/null
+++ b/linhes/linhes-system/lh_myth_status.py
@@ -0,0 +1,271 @@
+#!/usr/bin/python
+#This program is called on login to display the status of mythtv tuners & recording status
+#Also will display alerts generated by xymon.  If the location of xymon changes, this script needs to be updated.
+
+from MythTV import MythBE,MythDB,MythLog
+import datetime,pytz,re,socket,subprocess,sys,time
+from dateutil.parser import parse
+from tzlocal import get_localzone
+
+import os,glob
+from socket import gethostname;
+
+def formatTD(td):
+    days = td.days
+    hours = td.seconds // 3600
+    minutes = (td.seconds % 3600) // 60
+    seconds = td.seconds % 60
+
+    if days == 0:
+      day_string = ""
+    elif days > 1:
+      day_string = "%s days, " %days
+    else:
+      day_string = "%s day, " %days
+
+    if hours > 1:
+      hour_string = "%s hours, " %hours
+    else:
+      hour_string = "%s hour, " %hours
+
+    if minutes > 1:
+      minute_string = "%s minutes, " %minutes
+    else:
+      minute_string = "%s minute, " %minutes
+
+    if seconds > 1:
+      second_string = "%s seconds" %seconds
+    else:
+      second_string = "%s second" %seconds
+
+    return_string = '%s%s%s%s' % (day_string, hour_string, minute_string, second_string)
+    return return_string
+
+def print_alerts():
+    dir_name = "/home/xymon/var/login_alerts"
+    out_alert=""
+    try:
+        os.chdir(dir_name)
+    except:
+        pass
+        #print "    myth_status: Couldn't change dir to %s" %dir_name
+
+    file_list=glob.glob("*")
+
+    if len(file_list) == 0:
+        #print "    myth_status:  no alert files found"
+        pass
+    else:
+        for alert_file in file_list:
+            out_line=''
+            datahost = ''
+            dataservice = ''
+            datacolor = ''
+            datadown = ''
+            try:
+                #print "    myth_staus: reading in %s" %alert_file
+                f=open(alert_file,'r')
+                lines=f.readlines()
+                f.close()
+            except:
+                #print "    myth_status: Couldn't open %s for reading" %alert_file
+                continue
+
+            for line in lines:
+                try:
+                    data,value=line.split(":")
+                except:
+                    continue  #exception occured try the next line
+
+                if data == 'HOST':
+                    datahost = value.strip()
+                elif data == 'SERVICE':
+                    dataservice = value.strip()
+                elif data == 'COLOR':
+                    datacolor = value.strip()
+                elif data == 'DOWN':
+                    datadown = value.strip()
+                    sec=int(datadown)
+                    td_sec = datetime.timedelta(seconds=sec)
+                    td_sec_formated = formatTD(td_sec)
+
+            out_line ="    %s on %s %s for %s \n" %(dataservice,
+                                                           datahost,datacolor.upper(),
+                                                           td_sec_formated)
+            out_alert += out_line
+
+        print("System Alerts:")
+        print("--------------")
+        if len(out_alert) > 0:
+            print(out_alert)
+            print("    Go to http://%s and click Health & Maintenance for more information." %gethostname())
+        else:
+            print("    All systems OK")
+
+    return
+
+
+#-------------------------------------------
+
+
+class tuner_recording_status:
+    def __init__ (self,num_upcoming):
+
+        self.now = datetime.datetime.now(pytz.utc)
+        self.currTZ = get_localzone()
+        self.farout=99999999
+        self.next_start_diff=datetime.timedelta(self.farout)
+        self.num_upcoming=num_upcoming
+        self.tuner_status_list=[]
+        self.conflict_list=[]
+        self.upcoming_list=[]
+        self.ur=0
+        self.db_connection_status = self.check_database_connection()
+        if self.db_connection_status == 0:
+            self.tuner_status()
+            self.conflicts()
+            self.upcoming_recordings()
+
+    def get_db_check_status(self):
+        return self.db_connection_status
+
+    def check_database_connection(self):
+        rc=0
+        try:
+            self.be = MythBE()
+            self.db = MythDB()
+            self.cursor = self.db.cursor()
+        except:
+            print("\nCouldn't connect to MythTV service for status")
+            rc=1
+        return rc
+#-----
+    def tuner_status(self):
+        a=self.be.getRecorderList()
+        for i in a:
+            outline=''
+            cmd="select cardtype,hostname,displayname  from capturecard where cardid=%s;" %i
+            self.cursor.execute(cmd)
+            results=self.cursor.fetchall()
+            type = results[0][0]
+            hostname = results[0][1]
+            displayname = results[0][2]
+            id = i
+            try:
+                c=self.be.getCurrentRecording(i)
+                if c.title ==  None:
+                    current_recording = "Idle"
+                else:
+                    current_recording = "Recording %s" %c.title
+                outline = "    Tuner %s - %s (%s) on %s : %s " %(id, displayname, type, hostname, current_recording)
+                self.tuner_status_list.append(outline)
+            except:
+                outline = "    Tuner %s - %s (%s) on %s : %s " %(id, displayname, type, hostname, "Tuner Error")
+                self.tuner_status_list.append(outline)
+
+    def get_tuner_status(self):
+        return self.tuner_status
+
+    def print_tuner_status(self):
+        print("Tuner Status:")
+        print("-------------")
+        if len(self.tuner_status_list) > 0 :
+            for line in self.tuner_status_list:
+                print(line)
+        else:
+            print("    No tuners found")
+#--------
+    def upcoming_recordings(self):
+
+        a=self.be.getUpcomingRecordings()
+        r=0
+        for i in a:
+            r += 1
+            if r > self.num_upcoming:
+                break
+            title_chan="%s (%s)" %(i.title, i.channame)
+            # convert timezone to local timezone
+            start_time=parse(str(i.starttime))
+            start_time=start_time.astimezone(self.currTZ)
+            start_time_out=start_time.strftime("%a %b %d %I:%M%p")
+            self.upcoming_list.append([start_time_out,i.hostname, title_chan])
+
+            diff = start_time - self.now
+            if diff < self.next_start_diff :
+                self.next_start_diff = diff
+
+        if self.next_start_diff == datetime.timedelta(self.farout):
+            self.ur="No recordings are scheduled"
+        else:
+            self.ur=formatTD(self.next_start_diff)
+
+    def get_upcoming_recordings(self):
+        return self.upcoming_list
+
+    def print_upcoming_recordings(self):
+        #print self.get_upcoming_recordings()
+        print("")
+        print("Upcoming Recordings (Next %s Scheduled):" %(self.num_upcoming))
+        print("----------------------------------------")
+        if len(self.get_upcoming_recordings()) > 0:
+            for i in self.get_upcoming_recordings():
+                #print "    %s - %s - %s" %(start_time_out,i.hostname, title_chan)
+                print("    %s - %s - %s" %(i[0],i[1],i[2]))
+        else:
+            print("    No upcoming recordings")
+        pass
+
+    def get_next_start_time(self):
+        return self.ur
+
+    def print_next_start_time(self):
+        print("")
+        print("The next recording starts in:")
+        print("-----------------------------")
+        print("    %s" %(self.get_next_start_time()))
+        print("")
+
+#-----
+
+    def conflicts(self):
+        a=self.be.getConflictedRecordings()
+        for i in a:
+            out_line=''
+            title_chan="%s (%s)" %(i.title, i.channame)
+            # convert timezone to local timezone
+            start_time=parse(str(i.starttime))
+            start_time=start_time.astimezone(self.currTZ)
+            start_time_out=start_time.strftime("%a %b %d %I:%M%p")
+            out_line=(start_time_out,i.hostname,title_chan)
+            self.conflict_list.append(out_line)
+
+    def get_conflict_list(self):
+        return self.conflict_list
+
+    def print_conflict_list(self):
+        print("")
+        print("Recording Conflicts:")
+        print("--------------------")
+        if len(self.get_conflict_list()) > 0:
+            for i in self.get_conflict_list():
+                print("    %s - %s - %s" %(i[0],i[1],i[2]))
+        else:
+            print("    No conflicts")
+
+#header="#"*60
+
+
+def go():
+    welcomeFile=open("/etc/LinHES-release", "r")
+    print("Welcome to %s on %s\n" %(welcomeFile.readline().rstrip(), socket.gethostname()))
+    tuner = tuner_recording_status(12)
+    if tuner.get_db_check_status() == 0:
+        tuner.print_tuner_status()
+        tuner.print_upcoming_recordings()
+        tuner.print_conflict_list()
+        tuner.print_next_start_time()
+    print_alerts()
+
+
+if __name__ == "__main__":
+    go()
diff --git a/linhes/linhes-system/myth2mkv b/linhes/linhes-system/myth2mkv
index 51a0c9b..6edb804 100755
--- a/linhes/linhes-system/myth2mkv
+++ b/linhes/linhes-system/myth2mkv
@@ -31,12 +31,12 @@
 #                      #
 ########################
 
-OUTDIR=/myth/video
+OUTDIR=/data/storage/disk0/media/video
 LOGPATH=/var/log/mythtv
 LOGFILE=${LOGPATH}/myth2mkv-$$.log
 
 # TMPDIR is for large transient files
-TMPDIR=/myth/tmp
+TMPDIR=/data/storage/disk0/media/tmp
 
 # x264 tuning:
 # Tune x264 based on content. Valid options for TUNING are:
diff --git a/linhes/linhes-system/myth2mp3 b/linhes/linhes-system/myth2mp3
index ebc588b..e5960be 100755
--- a/linhes/linhes-system/myth2mp3
+++ b/linhes/linhes-system/myth2mp3
@@ -16,12 +16,12 @@ BITRATE=256k		#ie. 128k, 160k, 192k, 224k, 256k
 USECUTLIST=Y		#Y or N
 
 # where the converted audio is stored
-OUT_DIR=/myth/music
+OUT_DIR=/data/storage/disk0/media/music
 
 # create temp filename so multiple instances won't conflict
 TMPNAME=toMP3-$$
-TMPFILE=/myth/tmp/$TMPNAME
-TMPCUTFILE=/myth/tmp/$TMPNAME.mpg
+TMPFILE=/data/storage/disk0/media/tmp/$TMPNAME
+TMPCUTFILE=/data/storage/disk0/media/tmp/$TMPNAME.mpg
 FFINPUTFILE=$1
 TITLE=`echo $2 | sed 's/\//_/g'`
 
diff --git a/linhes/linhes-system/myth2videos b/linhes/linhes-system/myth2videos
new file mode 100755
index 0000000..c4d3246
--- /dev/null
+++ b/linhes/linhes-system/myth2videos
@@ -0,0 +1,148 @@
+#!/bin/sh
+# copy recording to videos
+# version 0.3
+
+# usage:
+# first parameter must be %DIR%/%FILE% of the recording
+# second parameter must be the desired base name of the output
+# third parameter must be %CHANID% if you set USECUTLIST=Y
+# fourth parameter must be %STARTTIME% if you set USECUTLIST=Y
+# fifth parameter must be %JOBID% for the User Job status to be updated in MythTV
+# in the mythtv setup screen invoke this script like this:
+# MYTHTV User Job Command:
+# /usr/bin/myth2videos "%DIR%/%FILE%" "%TITLE% - %SUBTITLE%" "%CHANID%" "%STARTTIME%" "%JOBID%"
+
+# options:
+USECUTLIST=Y		# Y or N
+
+# where the video is stored
+OUT_DIR=/data/storage/disk0/media/video
+
+
+#------FUNCTIONS---------------
+update_comment()
+# Arg_1 = COMMENT
+{
+if [ $NO_JOBID -eq 0 ]; then
+    `jobqueue_helper.py -j ${JOBID} -cs "${1}"`
+fi
+}
+
+update_status()
+# Arg_1 = status code
+{
+if [ $NO_JOBID -eq 0 ]; then
+    `jobqueue_helper.py -j ${JOBID} -cs "${1}"`
+fi
+}
+
+check_myth_jobcmds()
+# check the myth database for stop pause or resume commands
+{
+if [ $NO_JOBID -eq 0 ]; then
+    CURRENT_CMD=`jobqueue_helper.py -m "select cmds from jobqueue where id = ${JOBID}"`
+    case "$CURRENT_CMD" in
+	# JOB_RUN
+	0) ;;
+	# JOB_PAUSE
+    1) `jobqueue_helper.py -j ${JOBID} -ss 6`;;
+	# JOB_RESUME
+    2) `jobqueue_helper.py -j ${JOBID} -ss 4`
+       `jobqueue_helper.py -j ${JOBID} -cmds 0`;;
+	# JOB_STOP
+    4) `jobqueue_helper.py -j ${JOBID} -ss 5`
+       `jobqueue_helper.py -j ${JOBID} -cmds 0`
+       clean_up_files
+       echo "Copy Cancelled" >> $LOGFILE
+       `jobqueue_helper.py -j ${JOBID} -ss 320`
+ 	   exit ;;
+    esac
+fi
+}
+
+clean_up_files()
+# clean up left over files
+{
+unlink $TMPFILE 2> /dev/null
+unlink $TMPFILE.map 2> /dev/null
+}
+
+#-------MAIN SCRIPT------------
+
+# check if %JOBID% is passed from command line
+JOBID=${5}
+if [ -z "$JOBID" ]; then
+    NO_JOBID=1
+else
+    NO_JOBID=0
+fi
+
+# create temp filename so multiple instances won't conflict
+TMPNAME=toVIDEOS-$$
+TMPFILE=/data/storage/disk0/media/tmp/$TMPNAME.mpg
+MENINPUTFILE=$1
+TITLE=`echo $2 | sed 's/\//_/g'`
+
+# log file location
+LOGFILE=/var/log/mythtv/myth2videos.log
+CDate="`date`"
+echo "" >> $LOGFILE
+echo $CDate >> $LOGFILE
+echo "File to copy: $MENINPUTFILE     Name: $TITLE" >> $LOGFILE
+echo "$2   $3   $4   $5" >> $LOGFILE
+
+# start timer
+beforetime="$(date +%s)"
+
+check_myth_jobcmds
+
+# check if using cutlist
+if [ $USECUTLIST = Y ]; then
+    MYTHCOMMFRAMES=`mythutil --getcutlist --chanid "$3" --starttime "$4" | grep 'Cutlist:' | cut -d \  -f 2`
+    echo $MYTHCOMMFRAMES
+    if [ -n "$MYTHCOMMFRAMES" ]; then
+        echo "Extracting Cutlist..." >> $LOGFILE
+        update_comment "Extracting Cutlist..."
+        /usr/bin/nice -n19 /usr/bin/mythtranscode --chanid "$3" --starttime "$4" --outfile "$TMPFILE" --mpeg2 --honorcutlist
+    else
+        update_comment "Copying Recording..."
+        cp "$MENINPUTFILE" "$TMPFILE"
+    fi
+elif [ $USECUTLIST = N ]; then
+    update_comment "Copying Recording..."
+    cp "$MENINPUTFILE" "$TMPFILE"
+fi
+
+# make output filename unique
+OUTPUTFILE=$OUT_DIR/$TITLE.mpg
+i=1
+while [ -e "$OUTPUTFILE" ]
+do
+    OUTPUTFILE=$OUT_DIR/$TITLE-$i.mpg
+    i=`expr $i + 1`
+done
+
+# move temp file to output location
+chown mythtv "$TMPFILE" && mv "$TMPFILE" "$OUTPUTFILE"
+
+# stop timer
+aftertime="$(date +%s)"
+seconds="$(expr $aftertime - $beforetime)"
+
+ERROR=$?
+if [ $ERROR -eq 0 ]; then
+    echo "File Encoded Successfully: $OUTPUTFILE" >> $LOGFILE
+    hours=$((seconds / 3600))
+    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
+    update_status 272
+    update_comment "Encode Successful. Encoding Time: $hours hour(s) $minutes minute(s) $seconds second(s)"
+else
+    update_status 304
+    update_comment "Encode Failed.  Exit status: $ERROR"
+    echo "ERROR: $ERROR" >> $LOGFILE
+fi
+
+clean_up_files
diff --git a/linhes/linhes-system/myth_mtc.py b/linhes/linhes-system/myth_mtc.py
index 953fb96..3cfbfb1 100755
--- a/linhes/linhes-system/myth_mtc.py
+++ b/linhes/linhes-system/myth_mtc.py
@@ -81,7 +81,7 @@ def cleanup_inuseprograms():
 
 def bail_if_another_is_running():
     cmd = shlex.split("pgrep -u {} -f {}".format(os.getuid(), __file__))
-    pids = subprocess.check_output(cmd).strip().split('\n')
+    pids = subprocess.check_output(cmd).decode('utf-8').strip().split('\n')
     if len(pids) > 1:
         pids.remove("{}".format(os.getpid()))
         print("Exiting! Found {} is already running (pids): {}".format(
@@ -100,7 +100,7 @@ def run_stuff():
         print("No system idle check will be done.")
         idle = 0
     else:
-        idle = subprocess.call(["/usr/bin/python2", "/usr/LH/bin/idle.py"])
+        idle = subprocess.call(["/usr/bin/python", "/usr/bin/idle.py"])
 
     if not idle:
         if ("--check_home" in sys.argv) or runall:
-- 
cgit v0.12