I was having some issues with installing MythTV Master on EndeavourOS (EOS), which is Archlinux underneath. So I started over on a clean VM to test all of the instructions below. Now it's documented for me moving forward and maybe it will help someone else.
EDIT: this has been tested on both VM and real Intel PC with Intel GFX.
I created the VM on a Linux system using KVM/QEMU and libvirt-manager. The system that hosts the VM has a network bridge installed and configured so the VMs can be on the same subnet as my home network which contains my HDHomerun tuners.
1. Install EOS using defaults, ext4. no swap, KDE Plasma.
2. After reboot, install zramd with
Code: Select all
yay -S zramd
3. Edit /etc/default/zramd and set swap size as needed.
3. Enable zramd with
Code: Select all
sudo systemctl enable --now zramd.service
Code: Select all
sudo systemctl disable --now firewalld.service
Code: Select all
sudo useradd -m --groups optical,audio,video --home /home/mythtv --shell /bin/bash mythtv
Code: Select all
sudo usermod -aG mythtv jim
Code: Select all
hosts: mymachines mdns_minimal [NOTFOUND=return] resolve [!UNAVAIL=return] files myhostname dns
9. Fix makepkg options for maximum parallel compiles by editing /etc/makepkg.conf. Set MAKEFLAGS="-j$(nproc)"
10. Install and build the mythtv-git AUR package
Code: Select all
yay -S mythtv-git --editmenu
Change the source= for
"git+https://github.com/MythTV/mythtv#branch=fixes/33"
and change to
"git+https://github.com/MythTV/mythtv#branch=master"
save and exit to continue build. Take defaults and enter password when required.
11. Setup mariadb per the Archlinux wiki
Code: Select all
sudo pacman -S --needed mariadb
sudo mariadb-install-db --user=mysql --basedir=/usr --datadir=/var/lib/mysql
sudo systemctl enable --now mariadb.service
Code: Select all
sudo mysql_secure_installation
mysql_tzinfo_to_sql /usr/share/zoneinfo | sudo mysql -uroot mysql
sudo systemctl restart mysql
Code: Select all
#! /bin/bash
# Globals
mythtv_storagegroup_path=/srv/mythtv/
mythtv_storagegroups="banners coverart fanart recordings streaming videos bare-client db_backups livetv sports screenshots trailers music musicart"
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
13. We now need to setup mythtv-backend.service and make it run as user 'mythtv'. Below is a script that will do that by copying the one that MythTV Debian packaging created. Copy the text below to a .sh file, make it executable, and run it.
Code: Select all
#! /bin/bash
# Globals
mythtv_git_directory=/tmp/build
mythtv_git_branch=master
# this gets git source for mythtv/packaging
fn_get_git()
{
mkdir -p $mythtv_git_directory
cd $mythtv_git_directory
#mythtv packaging - we only need a few files
git clone -b $mythtv_git_branch https://github.com/MythTV/packaging.git
}
fn_setup_mythtv_backend_service()
{
sudo cp -n $mythtv_git_directory/packaging/deb/debian/mythtv-backend.service /lib/systemd/system/
sudo systemctl enable --now mythtv-backend
sudo systemctl daemon-reload
echo -e "\nmythtv-backend.service has been setup and enabled \n"
}
fn_tidy_up()
{
# remove local packaging repositories
rm -fr $mythtv_git_directory/packaging
}
#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
fn_get_git
fn_setup_mythtv_backend_service
fn_tidy_up
exit 0
15. Click on Test Database Connection. Under Instructions, select Mariadb.
16. Create a file called setup.sql and paste in the content of the copy/paste from the browser window. save and exit.
17. Run
Code: Select all
sudo mysql -u root < setup.sql
Code: Select all
mysql_tzinfo_to_sql /usr/share/zoneinfo | sudo mysql -u root mysql
20. Some key settings:
General -> Host Address backend setup -> Primary IP address. Select from drop down.
Capture Card: in my case for the VM setup I created a couple of HDHomerun tuners to match my dual tuner hardware. I selected the IP of the tuner I wanted and clicked on EIT scan for this test.
Video Source: I create one named OTA using the Transmitted Guide only (EIT) grabber.
Input Connection: For Input Name I selected MPEG2TS. For Video Source I selected OTA. On Scan for Channels I selected Import HDHomerun channels since my Favorites are all properly setup on my HDHR. On the subsequent tuners I do not do the Scan for channels. Lastly I select Starting channel.
This is repeated for all tuners but the Scan for channels is only done on the first tuner.
21. On Storage Groups, I matched up all the Storage groups to the directories I setup earlier.
22. Click Restart Backend.
23. If this was on real hardware instead of a VM, we would need to account for the delay needed to allow the tuner to get itself stable prior to mythbackend to start running. The method below is one way of doing this:
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:
At this point you can run mythfrontend and test it out with EIT program guide information.