[UPDATED]How to install mythtv-light (v34) on RPI OS 64bit Bookworm

For discussion of topics specific to MythTV on Raspberry Pi devices
Post Reply
User avatar
jfabernathy
Senior
Posts: 612
Joined: Wed Feb 18, 2015 2:37 pm
Location: Raleigh, NC
United States of America

[UPDATED]How to install mythtv-light (v34) on RPI OS 64bit Bookworm

Post by jfabernathy »

V34 (Master) is not production/Released software so this is only for those wanting to experiment with master/v34 and it's new Web app for setup instead of the mythtv-setup command.

This topic will give the instructions to install MythTV Master (v34) for running on a 4GB Raspberry Pi 4 (RPI4) running the latest Bookworm 64 bit based on Debian 12 either as a Frontend only or a combination Frontend/Backend (FE/BE). The MythTV developer team and the Raspberry Pi OS team both have made huge improvments to video routines and drivers. So this doc will only concentrate on master and Bookworm 64 bit which means you must have a v34 (Master) backend or do the combination build so you will have one on the RPI4. The RPI4 has no issues being a backend. You can easily record 4 TV HD programs from the networked tuners at once while playing back a previously recorded program on the frontend section of the RPI4.

I created this document while installing a combo FE/BE so the instructions should be as accurate as possible.

The first part is common to both, so we will start there. We are going to build from source so the latest will always be available for the version we want to work with.

We will be installing 'mythtv-light' so the software is packaged as a .deb file that can be easily installed and upgraded.

You can find the version of v34 as of today(12-2-2024) . That version is at: https://drive.google.com/drive/folders/ ... bVG77UdN2X

Pick the Bookworm version,

I'll try to keep the v34 Bookworm folders up to date.

The hardware being used in this example is:
RPI4 4GB RAM
USB3 to SATA Adapter based on the ASMedia ASM1153E controller
1TB SATA SSD
USB keyboard
FHD HDMI monitor
SiliconDust HDHomeRun Quatro networked ATSC 1.0 tuner (4 tuners) (HDHR)
Ethernet connected to the same network as HDHR with Cat-5e, but WiFi can work if yours is a very good connection.

Using the Raspberry Pi Imager 1.7.5 or newer, select the Bookworm 64bit. Flash the image directly to the SATA SSD using the adapter.

The version of Bookworm that I installed had as default a locale that uses the ISO settings. Previous versions uses UTF-8. You may need to change this.

You need to download or build mythtv-light and install similar to the command below:

Code: Select all

sudo apt install ./mythtv-light_34.0-30-gc376437559-0_arm64_bookworm.deb
On Bookworm you will see some notifications and permissions issues, but they don't appear to affect the install of mythtv-light.
See the build v34 topic to see a work around to avoid the warnings if you want.

At this point the MythTV software is installed and if you only want a frontend then you are done.

To build a combo FE/BE on v34 you will use a web app, but first some setup is needed.

Install mariadb-server

Code: Select all

sudo apt install mariadb-server
Add user 'mythtv'

Code: Select all

sudo useradd -m mythtv
Add your normal user to the mythtv group (jim in my case)

Code: Select all

sudo usermod -aG mythtv jim
Setup the systemd service for mythbackend

Code: Select all

mkdir ~/build
cd ~/build
git clone https://github.com/mythtv/packaging
sudo cp -n ~/build/packaging/deb/debian/mythtv-backend.service /lib/systemd/system/
sudo systemctl enable mythtv-backend
sudo systemctl daemon-reload
Create a script to setup Storage directories for mythtv

Code: Select all

cd
nano create-dirs.sh
Add this code to script

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
Make script executable and run:

Code: Select all

sudo ./create-dirs.sh
Reboot

Now using your browser to to localhost:6544

Follow the instruction for setting up the database and select the mariadb button. Copy the code and built the sql script using the console.

Once the instructions on the database are complete, click save and go back to the top and click on Restart backend and make sure all the indicators are good and then click next.

On the General Backend Setup select the Primary IP to match your actual IP. Save

On the Job Queue (Backend-Specific) Turn off Commerical Flagging and Transcoding Jobs. Save

Next setup the Capture cards. Since my current HDHR is a 2 tuner version, I setup 2 tuners and since this test run is for over the air EIT EPG I select those options.

On Video Source I setup only a EIT source and name it OTA. You can use Schedules Direct EPG and the setup here would be about the same as standard SD setup.

On Input Connections I select the Name to MPEG2TS, Video source to OTA, and Scan Channels -> scan type to HDHR channel import. Then Start Scan. Next I select Starting Channel. Save
For the other tuner do the same except don't scan for this tuner.

Setup the Storage Directories. Setup the ones you want but the Default one is required. Since we setup the directories above in /srv/mythtv/, select the appropriate directory. The Default is usually 'recordings'.

When you are through using the Web App to setup the backend, click on the Restart Backend button and make sure it starts. You can go to the dashboard to view everything.

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.

Back to the console,

Code: Select all

sudo apt install hdhomerun-config
Add an override to the mythtv-backend.service with:

Code: Select all

sudo --login systemctl edit mythtv-backend.service
Add or adjust the override file to include:

Code: Select all

[Service]
ExecStartPre=-/usr/local/bin/hdhomerun_check.py
Note the dash in the line above. That means that if the command fails, the backend will still start (so you
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:
Reboot and make sure that mythtv-backend.service is running correctly.

If you are going to be running mythfrontend remotely from this backend you need to configure mariadb.

Create a file: /etc/mysql/mariadb.conf.d/80-mythtv,cnf
It should look like this:

Code: Select all

[mysqld]
bind-address=*
max_connections=100
binlog-ignore-db = mythconverg
You can now setup mythfrontend as normal.
Post Reply