While I prefer EndeavourOS Plasma on my RPI4, I found the MythTV playback of recordings to be very good and if you like the Ubuntu Gnome Desktop better than others, this might be a choice for you and your RPI4.
Here's how I installed it:
You can use Raspberry Pi Imager v1.7.2 or higher and under "Choose OS", select "Other general-purpose OS" -> Ubuntu -> Ubuntu Desktop 22.10 (64-bit). Use this specific version. I have not had success with older versions. Something really changed in 22.10 Desktop. The Imager program will download and flash the OS to your Storage device. Since you are going to need a lot of GBs to record TV, I'd say use a big SATA SSD with a USB3 to SATA adapter.
Plug in the SSD to the RPI4 and boot. The setup and update of Ubuntu is slow and not a good sign but after the upgrade it seems to be very responsive.
If your internet connection is via your RJ45 Ethernet, plug your HDHomerun tuner into your router.
If your internet connection is via your WiFi you can use your RJ45 Ethernet port to directly connect your HDHomerun tuner. If you use this method you will need to use the Network Settings to edit your Ethernet as follows:
In the IPv4 tab:
Set Method to Local-link only
Check the box for "Require IPv4 addressing for this connection to complete"
In the IPv6:
Set the Method to Disabled
Next add the MythTV PPA to your list of repositories. This worked with v32 and v33:
Code: Select all
sudo add-apt-repository ppa:mythbuntu/32
Code: Select all
sudo apt install mythtv
So now run:
Code: Select all
mythtv-setup
You'll need to setup your mythtv directories before completing setup. I use /srv/mythtv as my storage area. Here's a script to do that. Save this as create_directories.sh and make executalbe:
Code: Select all
#! /bin/bash
# Globals
mythtv_storagegroup_path=/srv/mythtv/ # using /srv/mythtv in preference to /var/lib/mythtv/,
mythtv_storagegroups="banners coverart fanart recordings streaming videos bare-client db_backups livetv sports screenshots trailers music musicart"
fn_setup_directories()
{
sudo mkdir -p $mythtv_storagegroup_path
cd $mythtv_storagegroup_path
sudo mkdir -p $mythtv_storagegroups
sudo chown -R mythtv:mythtv $mythtv_storagegroup_path
sudo chmod -R 2775 $mythtv_storagegroup_path
}
#main
# make sure we are not root
RUNNINGAS=`whoami`
if [ $RUNNINGAS = "root" ] ; then
echo "Please run as ordinary user, not with sudo"
exit 1
fi
#check mythbackend has been installed, if not abort with message
MYTHBACKEND=`which mythbackend`
if [ -z "$MYTHBACKEND" ]; then
echo -e "mythbackend not found - please install MythTV-Light package"
echo -e "For official builds (when available) 'https://www.mythtv.org/wiki/MythTV_Light'\n"
echo -e "For test builds 'https://forum.mythtv.org/viewtopic.php?f=46&t=3221&start=15'\n"
exit 1
fi
fn_setup_directories
exit 0
I use SchedulesDirect via tv_grab_zz_sdjson_sqlite. So I install XMLTV by:
Code: Select all
sudo apt install xmltv
https://www.mythtv.org/wiki/XMLTV#Setup ... son_sqlite
BUT make sure to run them as user "mythtv" by:
Code: Select all
sudo su mythtv
cd
Code: Select all
sudo apt install ssh
ssh -X $USER@localhost
#From this session run...
mythtv-setup
At this point you can test this out by restarting the backend with:
Code: Select all
sudo systemctl restart mythtv-backend.service
However, for mythtv-backend to start up properly after booting the RPI4, you are going to have to delay the startup of mythtv-backend until the HDHomerun tuners are discoverable.
Add an override to the mythtv-backend.service with:
Code: Select all
sudo --login systemctl edit mythtv-backend.service
Code: Select all
[Service]
ExecStartPre=-/usr/local/bin/hdhomerun_check.py
can watch recordings etc.) If you want to prevent the backend from starting, remove the dash.
You'll need to put the hdhomerun_check.py in /usr/local/bin and make it executable, owner/group root.
hdhomerun_check.py
Code: Select all
#!/usr/bin/python3
# -*- coding: utf-8 -*-
""" See if the HD Homerun box(s) are accessible and running
Requires Python 3.6 or later.
For backends started by systemd, use:
sudo --preserve-env systemctl edit --force mythtv-backend.service
and enter or add as needed by your service:
[Service]
ExecStartPre=-/usr/local/bin/hdhomerun_check.py
Can be called with optional IP address(s) for users that
have multiple HDHRs that have STATIC addresses.
Use --help to see all options.
If run from the command line, then output will be to the screen.
Otherwise, a log file in /tmp named hdhr_discovery.log is made.
Changable with the --logfile switch.
Exit codes:
0 = success (for *ALL* HDHRs if multiple IPs were specified)
1 = no output from the hdhomerun_config discover command
2 = IPv4 and IPv6 addresses found, disable IPv6 on NIC
3 = logfile isn't writable, delete it and try again
4 = keyboard interrupt
5 x the number of HDHRs = HDHR is most likely not up
"""
__version__ = '1.28'
import argparse
import signal
import subprocess
import sys
from datetime import datetime
from os.path import basename
from os import _exit
from time import sleep
# pylint: disable=too-many-arguments,unused-argument
def keyboard_interrupt_handler(sigint, frame):
''' Handle all KeyboardInterrupts here. And, just leave. '''
_exit(4)
# pylint: enable=unused-argument
def get_program_arguments():
''' Process the command line. '''
parser = argparse.ArgumentParser(description='HDHR Access Test',
epilog='* Default values are in ()s')
parser.add_argument('HOSTS', type=str, default=None, nargs='*',
help='optional hostname(s)/IP(s) (%(default)s)')
parser.add_argument('--attempts', default=20, type=int, metavar='<num>',
help='number of tries to find HDHRs (%(default)i)')
parser.add_argument('--debug', action='store_true',
help='output additional information (%(default)s)')
parser.add_argument('--logfile', default='/tmp/hdhomerun_check.log',
type=str, metavar='<lf>',
help='optional path + name of log file (%(default)s)')
parser.add_argument('--sleep', default=1.5, type=float, metavar='<sec>',
help='seconds betweem attempts (%(default)s)')
parser.add_argument('--version', action='version',
version='%(prog)s ' + __version__)
return parser.parse_args()
def get_elapsed_time(start):
''' Calculate the time spent waiting for the HDHR to come up. '''
delta = datetime.utcnow() - start
rounded_delta = f'{delta.seconds + (delta.microseconds / 1000000):.3f}'
return rounded_delta
def log_or_print(loglevel, message, output):
''' Add timestamp, log level then print to the selected location. '''
print(datetime.now().strftime("%F %T.%f")[:-3], f'{loglevel:8}', message,
file=output)
def last_message(loglevel, result, host, start, attempt, output):
''' Common success or failure message text. '''
log_or_print(loglevel, f'{result} {"at " + host + " " if host else ""}'
f'in {get_elapsed_time(start)} seconds '
f'and {attempt} attempt{"s"[attempt == 1:]}\n', output)
def check_one_device(host, args, output):
''' Try to discover the HDHR(s). '''
attempt = 0
command = ['hdhomerun_config', 'discover']
start = datetime.utcnow()
if host:
command.append(host)
for attempt in range(1, args.attempts+1):
try:
discovery_response = subprocess.check_output(
command, text=True, stderr=subprocess.STDOUT).split()
except subprocess.CalledProcessError:
log_or_print('WARNING', f'{command[0]}: got no response, attempt: '
f'{attempt:2}', output)
sleep(args.sleep)
continue
if not discovery_response:
log_or_print('ERROR', f'No output from {command[0]}, aborting!',
output)
sys.exit(1)
if args.debug:
log_or_print('DEBUG', f'Got: {" ".join(discovery_response)}',
output)
if discovery_response.count('hdhomerun') > 1:
log_or_print('ERROR', f'{command[0]}: more than 1 IP, aborting!',
output)
sys.exit(2)
if discovery_response[0] != 'hdhomerun':
# Consider making this an ERROR and exiting not sleeping...
log_or_print('WARNING', f'{command[0]} got an unexpected response:'
f' {" ".join(discovery_response)}',
output)
sleep(args.sleep)
else:
last_message('INFO', f'Found HDHR {discovery_response[2]}', host,
start, attempt, output)
return 0
last_message('ERROR', 'No HDHR found', host, start, attempt, output)
return 5
def main(args, output=None):
''' Control checking of one or more devices. '''
log_or_print('INFO', f'Starting {basename(__file__)} v{__version__}, '
f'attempts={args.attempts}, sleep={args.sleep:.2f}', output)
if args.HOSTS:
return_value = 0
for host in args.HOSTS:
return_value += check_one_device(host, args, output)
else:
return_value = check_one_device(None, args, output)
return return_value
if __name__ == '__main__':
signal.signal(signal.SIGINT, keyboard_interrupt_handler)
ARGS = get_program_arguments()
if sys.stdin and sys.stdin.isatty():
RETURN_VALUE = main(ARGS)
else:
try:
with open(ARGS.logfile, encoding='ascii', mode='a') as file_obj:
RETURN_VALUE = main(ARGS, output=file_obj)
except PermissionError:
print(f'Can\'t write to {ARGS.logfile}, aborting!')
sys.exit(3)
sys.exit(RETURN_VALUE)
# vim: set expandtab tabstop=4 shiftwidth=4 smartindent colorcolumn=80:
You can run mythfrontend as on any system and configure it for the RPI4 as has been documented elsewhere on this forum catefory.