Project

General

Profile

HTPC with TVHeadEnd PVR, HDHomeRun OTA, and Kodi on FireTV stick 2019

Added by Boffo Wise about 2 months ago

Here is my guide to a functional PVR system using OTA HDHomeRun EXTEND with TVHeadEnd on Ubuntu HTPC server with Kodi displaying the media to the TV.
Table of Contents
---1--- Background/Introduction
---2--- some terminology
---3--- physical layout of the system
---4--- software versions
---5--- the software part -- HTPC PVR server install
--5.1-- Install - TVHeadEnd (DVR)
--5.2-- Install - zap2xml (EPG)
--5.3-- Install - Cron for zap2xml
--5.4-- Install - tv_grab_file (DVR <--> EPG)
--5.5-- Install - HDHomeRun IPTV M3U
--5.6-- Install - last few things
---6--- the software part -- HTPC PVR server TVHeadEnd configuration
--6.1-- Config - Connect browser
--6.2-- Config - Add IPTV Automatic Network
--6.3-- Config - Services
--6.4-- Config - Electronic Program Guide
--6.5-- Config – Channels <--> EPG
--6.6-- Config - Recordings
---7--- the software part -- Kodi on FireTV Sticks
---8--- miscellaneous extras
---9--- External Links

---1--- Background/Introduction
So, after much frustration, I finally got a functional configuration of using a HTPC to get Over the Air TV. I am using an antenna in the Washington DC, USA area with an HDHomeRun with a PVR (TVHeadEnd on Ubuntu) using a graphical frontend (Kodi) in August of 2019, original setup in March of 2016. While there are MANY guides out there of people getting a home-built DVR/PVR with cable working for them, most of them are for Europe (or some other non-USA region) and more than 8 years old. The most useful guides I found were still about two years old back then.
So, to help out myself and others that may have to work the same, I create this guide. I will try to give hyperlinks to the principle sources of information for what I got to work. I looked at hundreds of websites in this process and there is no way for me to remember which websites gave me usable information. I link them at the end of this guide, and occasionally throughout as appropriate. Also, I am an "intermediate-level" Linux user with most of my experience with Debian and Ubuntu Linux Distributions. I have no fear of the command line. I try not to compile anything so that automatic updates can happen easier. Most of my configurations I prefer to do over an SSH connection to the computer.
The key requirements were that I needed local, live TV with DVR capabilities and two TVs streaming at the same time. With my parents being "elderly" and "set in their ways", using a service like Sling TV, or just watching non-live streaming services wasn't enough. They wanted an experience similar to Comcast or Dish Network. But, they were finally ready to "Cut the Cord" and get rid of the monthly subscription fee. The hardest part of the setup was the DVR and EPG. I tried MythTV (under Mythbuntu), VDR (under OpenELEC), and TVHeadEnd (under OpenELEC). They all failed for various reasons--mostly from a failure to get the HDHomeRun to stream correctly or the EPG to integrate. Finally, I installed a plain Ubuntu (desktop) solution and got TVHeadEnd to work.
With the final configuration, I decided to make the HTPC PVR a headless machine. This isn't a requirement, but it suits my needs nicely. One benefit of configuring it that way is that I can reboot the HTPC PVR while keeping web pages open--and this can save a bunch of time.
You will need to know the IP addresses of your HTPC PVR server and HDHomeRun EXTEND. I assign fixed IP adresses to all of these devices on the router/DHCP server for easy detection/remote configuration. I assume that your HDHomeRun is already up and working, with channels already detected. If you go to the IP address of your HDHomeRun from a web browser (on the same network), you should be able to tell it to do it's own scan.
Originally, I wrote this guide for Ubuntu 16.04 in 2016, so this works (with minor modification) for the older version of the server, too. But, I just followed (and modified) these steps to work with Ubuntu 18.04.6 in 2019

---2--- some terminology
- OTA - Over the Air - a term meaning that the television signal you are receiving is from an antenna, which receives its signal from a television broadcast cast station old-school style.
- EPG - Electronic Program Guide - this is basically the TV Guide, which the TV Guide brand name is famous for.
- EIT - Event Information Table - this is the built-in EPG transmitted with OTA TV signal. Some countries have multiple days EPG transmited via EIT, but in the USA, it's usually only 12-36 hours, if it is integrated at all.
- DVR - Digital Video Recorder - a device or software that records incoming digital video streams to play back at a future time. Basically, a modern VCR.
- PVR - Personal Video Recorder - for my purposes, it is the same as DVR.
- DVB - Digital Video Broadcasting - the "standard" used for transmitting TV streams. Also, it is usually used interchangable to describe the "driver" that Linux Operating Systems use to interface with the hardware that receives said streams.
- IPTV - Internet Protocol TeleVision - another "standard" used to tranmitting TV streams, this time over the internet.

---3--- physical layout of the system
- Wireless g network router (yeah, I know, "g" is old)
- OTA Antenna (with a surge-protector and booster on the antenna signal)
- 2x HD TVs with Amazon FireTV Sticks (2nd Generation) connected wirelessly
- HTPC PVR server (HTPC): an 8-year-old HP PC, upgraded RAM & HDD connected by ethernet
HDHomeRun EXTEND connected by ethernet and to the OTA Antenna
- Another computer (Ubuntu desktop PC) (PC) that is connected by ethernet. The PC is where I do most of the configuration of the HTPC PVR server.

---4--- software versions
HTS Tvheadend 4.2.8-31~g0a60f73ee
Ubuntu 18.04.6 LTS Server amd64
HDHomeRun EXTEND HDTC-2US v20161119
Kodi 16.1 Jarvis
hdhr-listings-to-m3u.py forked by aaearon version Dec 29, 2016
zap2xml 2018-12-01

---5--- the software part -- HTPC PVR server
Now start with the hard part: the HTPC PVR server. I installed a stock Ubuntu 18.04.2 Server LTE (Bionic Beaver) on it. I included default server tools, OpenSSH server and Basic Ubuntu Server packages. (I actually had to install Ubuntu 16.04.6 Server and run a do-release-upgrade to accomplish this on my equipment for some reason.)

(If you need to install the SSH server, the command

sudo apt-get install openssh-server

will get you there.)
Then go to the other PC and try to remote into the HTPC (I SSH from Ubuntu desktop). If you are successful, then you can disconnect the monitor, keyboard, and mouse from the HTPC and be done with it! (Don't forget to change any CMOS/BIOS settings to enable booting without the keyboard/monitor if you choose to go "headless" like this.)
It would be very benefitial for you to do a system update/upgrade and reboot, but not necessary.
sudo apt-get update
sudo apt-get upgrade
sudo shutdown -r now

For the rest of the guide, most of my information comes from two guides from the TVHeadEnd forums by M Anderson and Robert Cameron (see the end of the guide for the links). I will quote them without direct citation.
--5.1-- Install - TVHeadEnd (DVR)
Now, to install TVHeadEnd. First, we need to add the repository for "bionic" (Ubuntu 18.04). I needed to add the "unstable" repository in order to get a new enough version of TVHeadEnd to work with the HDHomeRun the way I wish. Also, the "release" repository didn't have any installation candidates. Current instructions for adding the repository can be found at https://tvheadend.org/projects/tvheadend/wiki/AptRepository
sudo wget -qO- https://doozer.io/keys/tvheadend/tvheadend/pgp | sudo apt-key add -
echo "deb https://apt.tvheadend.org/stable $(lsb_release -sc) main" | tee -a /etc/apt/sources.list.d/tvheadend.list
sudo apt-get update
sudo apt-get install tvheadend

Answer all prompts "Yes".
During the install, you will be prompted for a admin username and password. Remember this, as you'll need it to login to the web interface to configure TVHeadEnd and also to configure Kodi to connect.
Note: TVHeadEnd is the program that records TV. It runs as user: hts. It stores its config files in /home/hts/.hts/tvheadend.
If you later want to change the username/password, use this command:
sudo dpkg-reconfigure tvheadend

--5.2-- Install - zap2xml (EPG)
We now download zap2xml to get the EPG data from the internet service zap2it.com. The zap2xml website (http://zap2xml.awardspace.info/) will have the current download link and command line instructions. Replace the <http://zap2it.download.link> portion with the correct download link (remove the <> also) in the commands below. (Note that mc2xml, as referred to in M Anderson's guide no longer gets current information. So, mc2xml is no longer usable.)
In order for zap2xml to work best, you will need to get a (free) zap2it.com account and configure it to your zipcode. It is fairly straight forward to do. If you do need help, zap2xml's website and Google can help you set up the zap2it account. I will use the settings I chose in the instructions below.
sudo mkdir -p /home/hts/zap2xml
cd /home/hts/zap2xml
sudo wget <http://zap2it.download.link>
sudo mv index* zap2xml.pl
sudo chmod +x zap2xml.pl
sudo mkdir -p /home/hts/.xmltv
sudo mkdir -p /home/hts/channel_icons
sudo ln -s /home/hts/zap2xml/xmltv.xml /home/hts/.xmltv/tv_grab_file.xmltv

--5.3-- Install - Cron for zap2xml
We will now set up the Cron job to download the guide data daily
sudo nano /etc/cron.daily/zap2xml

Type the following into the script, replacing <[email protected]> and <password> with your zap2it.com user name and password (remove the <> as well):
#!/bin/sh
# Update Electronic Program Guide Information Daily

cd /home/hts/zap2xml
/usr/bin/nice -n 19 /usr/bin/ionice -c2 -n7 /home/hts/zap2xml/zap2xml.pl -u <[email protected]> -p <password> -D -S 1 -b -i /home/hts/channel_icons
chown hts:video /home/hts/zap2xml/xmltv.xml
chown hts:video /home/hts/channel_icons/*

CTRL-X, Y to save and exit. I actually add the -a flag to get all channels, not just favorites. Be sure to make the cron job executable:
sudo chmod +x /etc/cron.daily/zap2xml

Note that I will use nice and ionice for non-live and non-streaming operations to help the system work better.
Be sure to run zap2xml the first time so TVHeadEnd can find the EPG data... this may take a while. This run is faster than normal daily runs, but also gets less information. Be sure to replace the user name and password as before.
sudo /home/hts/zap2xml/zap2xml.pl -u <[email protected]> -p <password> -b -i /home/hts/channel_icons

--5.4-- Install - tv_grab_file (DVR <--> EPG)
For TVHeadEnd to get the EPG data, we download a script called "TV Grab File". It's website has changed since M Anderson's guide, so I had to copy and paste the script. As of the time fo this writing, the script is found at https://github.com/Rigolo/tv-grab-file/blob/master/tv_grab_file . Paste the contents of the script into the file:
sudo nano /usr/bin/tv_grab_file

CTRL-X, Y to save and exit. Make it executable:
sudo chmod 777 /usr/bin/tv_grab_file

Note that the symbolic link (ln -s) we created earlier is so we don't have to modify the tv_grab_file code.
--5.5-- Install - HDHomeRun IPTV M3U
Before configuring TVHeadEnd, we need to get some information about the HDHomeRun. In my case, TVHeadEnd can see the HDHomeRun just fine. It just doesn't seem to get the channel information well. This is due, in part, to the aforementioned DVB/Linux kernel problem. Instead, we will set up the HDHomeRun channels ("muxes" in TVHeadEnd) as IPTV streams instead. To do so, we will need a playlist file (M3U) with all the IPTV streams from your HDHomeRun. This is described in Robert Cameron's guide. The easiest way (that I've found) of creating the M3U file is using hdhr-listings-to-m3u.py as mentioned in a comment after Robert Cameron's guide. I prefer the fork by aaearon on Dec 29, 2016, which at the time of this writing it is found at https://gist.github.com/aaearon/3e1d4958cf726066c8f55d15da7d86b4 . Copy and past the script into the file
sudo nano /home/hts/hdhr-listings-to-m3u.py

But, you need to edit the file for two reasons: paste the "shebang" at the top to make it easy to run
#!/usr/bin/env python

and modify the
  'hdhr-ip' : '192.168.1.100', 

line to have your HDHomeRun's correct IP address.
CTRL-X, Y to save and exit.
We need python to use the script:
sudo apt-get install python python-requests

Make the script executable and run it.
sudo chmod +x /home/hts/hdhr-listings-to-m3u.py
sudo /home/hts/hdhr-listings-to-m3u.py > /home/hts/hdhr1-listings.m3u

If you have multiple HDHomeRun devices, you can repeat this process (modifying the ip addess line, and running the script) with a different file name at the end (/home/hts/hdhr2-listings.m3u, /home/hts/hdhr3-listings.m3u, etc).
--5.6-- Install - last few things
Lastly, we need a location to put the recordings and timeshift buffer folders.
sudo mkdir -p /home/hts/recordings /home/hts/timeshift_buffer

With all the changes we made in the home directory of TVHeadEnd, let's change the ownership to the proper user.
sudo chown -Rh hts:video /home/hts/

It is now time to restart TVHeadEnd so it can find the EPG data. But, with all the changes made, I just did a system restart. Take your pick.
sudo service tvheadend restart

or
sudo shutdown -r now

One more thing... I do a more complete EPG update after it restarts. Just manually run (all) the Cron job(s). The ampersand (&) at the end tells it to detatch and run on its own.
sudo rm /home/hts/zap2xml/cache/*
sudo run-parts /etc/cron.daily > /dev/null &

---6--- the software part -- HTPC PVR server TVHeadEnd configuration
--6.1-- Config - Connect browser
It is now time to configure TVHeadEnd, which is the central point of this guide. To configure TVHeadEnd, you use a web browser. This is another reason I like remoting into the HTPC to configure things: much of it has to be done on a web browser anyway. So, replacing <HTPC.IP.Address.Here> with your HTPC's ip address (like 192.168.1.100), point your web browser to
http://&lt;HTPC.IP.Address.Here&gt;:9981/
and login with that username and password you set back when you installed TVHeadEnd with apt-get. If you can't remember it, you can reset it by typing on the HTPC's command line

sudo dpkg-reconfigure tvheadend

If you still need some help determining your HTPC's IP Address, the command
ifconfig

can tell you what it is.
During this process, the built-in TVHeadEnd log helped me immensely. You can see it by clicking the chevrons on the bottom right-hand corner of the web page. You can enable more (Debug) logging by clicking the gear next to said chevrons after the log is shown.
Click "Configuration", click "DVB Inputs," click "Networks."
I am not sure if it is needed or not, but I selected the "View level: Expert" from the drop down on the top right-hand corner of the page.
--6.2-- Config - Add IPTV Automatic Network
Click the little "Add" button, and choose "IPTV Automatic Network".
Network name: whatever you want: I chose "HDHr1"
Maximum # of input streams: 1 (we will change this later)
URL: file:///home/hts/hdhr1-listings.m3u
Note the triple slashes after file:
Icon base URL: file:///home/hts/channel_icons
EIT time offset: Local (server) time
The rest I left as default.
Click the "Create" button (you may have to scroll down).
Note that if you have multiple HDHomeRun tuners, you will need a separate IPTV Networks and M3U files for each one.
You should see a new network listed with the name you set (HDHr1). On the right side, there should be columns for "# Muxes" and "Scan queue length." Now, patiently wait until the "Scan queue length" field reaches 0. This may take a while. Take a break and come back.
Or, repeat this process for each physical HDHomeRun on your network. To spead this up, we could have set the "Maximum # of input streams" to the maximum number of streams this HDHomeRun can stream at one time. However, I had issues with bad detection, so I suggest leaving this field as 1 during detection.
Or, you can setup the EPG (Section 6.4) while you wait. Multitasking! (I am going to pretend you aren't doing this one for this guide.)
As the Scan queue decreases, the "# Services" should be going up--indicating how many channels were actually found. If it stays at 0, there are other problems. The log (chevron in bottom right-hand corner of screen) with debugging can be your friend.
When the "Scan queue length" is down to 0, edit the network by clicking on it (HDHr1), then clicking on the "Edit" button (near the "Add" button we clicked earlier). Change the "Maximum # of input streams" to 2 (or the maximum number of streams this HDHomeRun can stream at one time). Repeat for each HDHomeRun device your are configuring.
--6.3-- Config - Services
Now, we create the actual channels by going to Configuration --> DVB Inputs --> Services. If you used the forked python script by aaearon, the channel numbers should already be entered. Happy Day! Although, you can change them later. Click the "Map Services" button near the top, then "Map all services" dropdown. I chose the "Map encrypted services", "Create type-based tags", and "Create network name tage" options. Then click "Map services". After it is finished, we should have all our channels, complete with channel numbers.
--6.4-- Config - Electronic Program Guide
Now, to tell TVHeadEnd where to get the Program Guide data. Go to Configuration --> Channel/EPG --> EPG Grabber. Under General configuration, check "Update channel icon." I like to uncheck the others, and set "Periodically save EPG to disk (hours)" to 3. Under Over-the-air grabbers, uncheck "Force initial EPG grab at start-up." Save.
Continue with Configuration --> Channel/EPG --> EPG Grabber Modules. Make sure every EPG Grabber is disabled (red stop sign next to name) by selecting them, unchecking the "Enabled" setting on the right, the clicking "Save" near the top left. Be sure to scroll down to disable all of them. But, we want the internal tv_grab_file to be turned on. So, select "Internal: XMLTV: tv_grab_file ...", check the "Enabled" setting and click "Save." Click the "Re-run Internal EPG Grabbers" option up top.
--6.5-- Config – Channels <--> EPG
Now, we connect the channel streams to the EPG channels. I have not yet found an easy way to accomplish this.
If we go to Configuration --> Channel/EPG --> Channels, we see all our channels, complete with channel numbers. We can now watch them on Kodi. But, the Program Guide is not set up, yet. So, we connect the channel streams to the EPG channels. I have not yet found an easy way to accomplish this.
For each channel, select it, click "Edit" (up top), select the "EPG source" drop down menu, scroll to find the correct channel and check the checkbox, then click "Save" near the bottom. You can type in the drop down menu to save you some time. This process can be frustrating and time-consuming.
After all the channel streams are attached to an EPG source, go back to Configuration --> Channel/EPG --> EPG Grabber Modules and click "Re-run Internal EPG Grabbers" again. This second running should populate all of the guide data for each of the channels your linked.
--6.6-- Config - Recordings
To make the DVR operational, we need to tell TVHeadEnd where to put the recordings. Go to Configuration --> Recording --> Digital Video Recorder Profiles, select the (Default profile), then set the Recording system path to /home/hts/recordings . Similarly, to pause live TV, go to Configuration --> Recording --> Timeshift and check Enabled and set the Storage path to /home/hts/timeshift_buffer . There are other settings in these two locations you may wish to play with as well. Note: TVHeadEnd will automatically create the paths you specify (or default one for timeshift), but I prefer doing it myself.
If all goes well, TVHeadEnd should be completely configured.
Of course, you may wish to change some of the Configuration --> Stream settings. Other guides can help you with them. I had to use some information about OTA stations to get my configuration the way I wanted it. https://nocable.org/ is wonderful for that.
And, a TVHeadEnd restart or system reboot may help your situation as well.

---7--- the software part -- Kodi on FireTV Sticks
As of 2019, I no longer use Kodi, but I include my 2016 instructions for those that wish for it. Now that the PVR is set up, it is time to work on the Amazon FireTV Sticks. To install Kodi on the FireTV, you have to install it as an "Apps from Unknown Sources.” I used the guide http://troypoint.com/how-to-install-kodi-on-fire-tv/ but the guide http://kodi.wiki/view/HOW-TO:Install_Kodi_on_Fire_TV works fine as well.
Inside of Kodi, go to System --> Add-ons --> My Add-ons --> All --> Tvheadend HTSP Client --> Configure.
Give it the IP address, username, and password for TVHeadEnd. Select OK, then select Enable.
Now, go to System --> TV and check the Enabled option.
Go back to Kodi main screen; select TV and verify it works.

---8--- miscellaneous extras
While these aren't technically part of the setup, they are things I also did.
I like to convert some of my recorded programs into lower-quality files to keep for a long time. To change that, I create a new recording profile under Configuration --> Recording --> Digital Video Recorder Profiles. I change the Subdirectory options to Make subdirectories per title. I change the Filename options to include: date, time, remove unsafe, windows-compatible. I change the Full pathname specification, Format string: to $t/$t $e $50s ($c %F.%R)$n.$x Then, I create a Post-processor command: /home/hts/post_scripts/convert_low.sh "%f" "%t"
I prefer using ffmpeg, so we must install it. Note that in Ubuntu 14.04, they remove the ffmpeg package and replaced it with avconv which is installed with the package libav-tools . Also, we'll need ubuntu-restricted-extras (and/or aac-enc) to get the aac codec to work

sudo apt-get install ffmpeg ubuntu-restricted-extras aac-enc

I provide four sample scripts that work together for the low-quality conversion. I delete the file from TVHeadEnd during this process.
sudo mkdir -p /home/hts/transcode_temp/convert_low /home/hts/long-term/.log-backups/
sudo nano /home/hts/post_scripts/convert_low.sh

Edit the new script file to include
#!/bin/bash
# script to convert a recorded program to lower quality (MP4 format)

# these variables make this script more readable
INPUT_PATH_NAME="$1" 
INPUT_NAME=$(basename "$1")
INPUT_SUBFOLDER="$2" 

NICE_CMD="/usr/bin/ionice -c 3 /usr/bin/nice -n 19 " 
FFMPEG_CMD="$NICE_CMD /usr/bin/ffmpeg" 
WORKING_PATH="/home/hts/transcode_temp/convert_low/$INPUT_SUBFOLDER" 
FIXED_PATH_NAME="$WORKING_PATH/fixed_$INPUT_NAME" 
CONVERTED_PATH_NAME="$WORKING_PATH/low_$INPUT_NAME" 
CONVERTED_LOG="$CONVERTED_PATH_NAME.log" 
FINAL_COPY_CMD="$NICE_CMD /usr/bin/rsync -rlXv --bwlimit 6000 " 
FINAL_PATH="/home/hts/long-term/$INPUT_SUBFOLDER" 
FINAL_PATH_NAME="$FINAL_PATH/$INPUT_NAME.mp4" 
FINAL_LOG="/home/hts/long-term/.log-backups/low_$INPUT_NAME.log" 

# make working path
mkdir -p "$WORKING_PATH/" 
mkdir -p "$FINAL_PATH" 
cd "$WORKING_PATH/" 

# fix video format errors first
/home/hts/post_scripts/ffmpeg_fix.sh "$FFMPEG_CMD" "$INPUT_PATH_NAME" "$FIXED_PATH_NAME" "$CONVERTED_LOG" 

PROCESSED_PATH_NAME="$INPUT_PATH_NAME" 
# move the original file, replace the original file, clean up
if [ -f "$FIXED_PATH_NAME" ]; then
  INPUT_FILE_SIZE=$(stat -c%s "$INPUT_PATH_NAME")
  FIXED_FILE_SIZE=$(stat -c%s "$FIXED_PATH_NAME")
  if (( "$INPUT_FILE_SIZE" > "$FIXED_FILE_SIZE" )); then
    SIZE_DIFF_PER=$((100*(INPUT_FILE_SIZE-FIXED_FILE_SIZE)/INPUT_FILE_SIZE))
  else
    SIZE_DIFF_PER=$((100*(FIXED_FILE_SIZE-INPUT_FILE_SIZE)/INPUT_FILE_SIZE))
  fi
  echo "$(date) Percent difference in file size: $SIZE_DIFF_PER" >> "$CONVERTED_LOG" 

  if [[ $SIZE_DIFF_PER -lt 21 ]] ; then
    # small file size difference, using fixed file
    PROCESSED_PATH_NAME="$FIXED_PATH_NAME" 
    echo "$(date) Using fixed file ($FIXED_PATH_NAME)." >> "$CONVERTED_LOG" 
    echo "$(date) Deleting the original file \"$INPUT_PATH_NAME\"" >> "$CONVERTED_LOG" 
    $NICE_CMD rm "$INPUT_PATH_NAME" >> "$CONVERTED_LOG" 
  else
    # large file size difference, using original file
    PROCESSED_PATH_NAME="$INPUT_PATH_NAME" 
    echo "$(date)   $INPUT_FILE_SIZE size of $INPUT_PATH_NAME" >> "$CONVERTED_LOG" 
    echo "$(date)   $FIXED_FILE_SIZE size of $FIXED_PATH_NAME" >> "$CONVERTED_LOG" 
    echo "$(date) Using original file ($INPUT_PATH_NAME) due to percent difference in file size too large." >> "$CONVERTED_LOG" 
    echo "$(date) Deleting the (temporary) fixed file \"$FIXED_PATH_NAME\"" >> "$CONVERTED_LOG" 
    #$NICE_CMD rm "$FIXED_PATH_NAME" >> "$CONVERTED_LOG" 
  fi
else
  # file not created, using original file
  PROCESSED_PATH_NAME="$INPUT_PATH_NAME" 
  echo "$(date) Using original file ($INPUT_PATH_NAME) due to fixed file not created." >> "$CONVERTED_LOG" 
fi

# convert the media file to a lower quality format
/home/hts/post_scripts/ffmpeg_low.sh "$FFMPEG_CMD" "$PROCESSED_PATH_NAME" "$CONVERTED_PATH_NAME" "$CONVERTED_LOG" 

REDUCED_PATH_NAME="$CONVERTED_PATH_NAME" 
# move the original file, replace the original file, clean up
if [ -f "$CONVERTED_PATH_NAME" ]; then
  INPUT_FILE_SIZE=$(stat -c%s "$INPUT_PATH_NAME")
  CONVERTED_FILE_SIZE=$(stat -c%s "$CONVERTED_PATH_NAME")
  if (( "$INPUT_FILE_SIZE" > "$CONVERTED_FILE_SIZE" )); then
    SIZE_DIFF_PER=$((100*(INPUT_FILE_SIZE-CONVERTED_FILE_SIZE)/INPUT_FILE_SIZE))
  else
    SIZE_DIFF_PER=$((100*(CONVERTED_FILE_SIZE-INPUT_FILE_SIZE)/INPUT_FILE_SIZE))
  fi
  echo "$(date) Percent difference in file size: $SIZE_DIFF_PER" >> "$CONVERTED_LOG" 

  if [[ $SIZE_DIFF_PER -lt 20 ]] ; then
    # small file size difference, using original (fixed) file
    REDUCED_PATH_NAME="$PROCESSED_PATH_NAME" 
    echo "$(date)   $INPUT_FILE_SIZE size of $INPUT_PATH_NAME" >> "$CONVERTED_LOG" 
    echo "$(date)   $CONVERTED_FILE_SIZE size of $CONVERTED_PATH_NAME" >> "$CONVERTED_LOG" 
    echo "$(date) Percent difference in file size too small.  Using previous file version." >> "$CONVERTED_LOG" 
  else
    # large enough file size difference, make sure not too big (errored out)
    if [[ $SIZE_DIFF_PER -gt 95 ]] ; then
      # really large file size difference, using original (fixed) file
      REDUCED_PATH_NAME="$PROCESSED_PATH_NAME" 
      echo "$(date)   $INPUT_FILE_SIZE size of $INPUT_PATH_NAME" >> "$CONVERTED_LOG" 
      echo "$(date)   $CONVERTED_FILE_SIZE size of $CONVERTED_PATH_NAME" >> "$CONVERTED_LOG" 
      echo "$(date) Percent difference in file size too large. It must be an error.  Using previous file version." >> "$CONVERTED_LOG" 
    else
      # acceptable file size difference, using converted file
      REDUCED_PATH_NAME="$CONVERTED_PATH_NAME" 
      echo "$(date) Using converted file ($CONVERTED_PATH_NAME)." >> "$CONVERTED_LOG" 
    fi
  fi
else
  # file not created, using original (fixed) file
  REDUCED_PATH_NAME="$PROCESSED_PATH_NAME" 
  echo "$(date) Using original file ($PROCESSED_PATH_NAME) due to fixed file not created." >> "$CONVERTED_LOG" 
fi
echo

echo "$(date) Final copying \"$REDUCED_PATH_NAME\" to \"$FINAL_PATH_NAME\"" >> "$CONVERTED_LOG" 
$FINAL_COPY_CMD "$REDUCED_PATH_NAME" "$FINAL_PATH_NAME" >> "$CONVERTED_LOG" 

echo "$(date) Managing files in the final path \"$FINAL_PATH\"" >> "$CONVERTED_LOG" 
/home/hts/post_scripts/manage_full.sh "$FINAL_PATH" 8 "$CONVERTED_LOG.2" '\.mp4' >> "$CONVERTED_LOG" 
cat "$CONVERTED_LOG.2" >> "$CONVERTED_LOG" 
rm "$CONVERTED_LOG.2" >> "$CONVERTED_LOG" 

echo "$(date) Deleting the original file \"$INPUT_PATH_NAME\"" >> "$CONVERTED_LOG" 
$NICE_CMD rm "$INPUT_PATH_NAME" >> "$CONVERTED_LOG" 
echo "$(date) Deleting the (temporary) fixed file \"$FIXED_PATH_NAME\"" >> "$CONVERTED_LOG" 
$NICE_CMD rm "$FIXED_PATH_NAME" >> "$CONVERTED_LOG" 
echo "$(date) Deleting the (temporary) converted file \"$FIXED_PATH_NAME\"" >> "$CONVERTED_LOG" 
$NICE_CMD rm "$CONVERTED_PATH_NAME" >> "$CONVERTED_LOG" 

echo "$(date) Copying logfile to \"$FINAL_LOG\"" >> "$CONVERTED_LOG" 
$FINAL_COPY_CMD "$CONVERTED_LOG" "$FINAL_LOG"

sudo nano /home/hts/post_scripts/ffmpeg_fix.sh

Edit the new script file to include
#!/bin/bash
# script to fix obvious errors in a media file (MP4 format)

# these variables make this script more readable
FFMPEG_CMD="$1" 
INPUT_FILE="$2" 
OUTPUT_FILE="$3" 
LOG_FILE="$4" 

# fix any obvious errors in the input file
echo "$(date) Beginning fix on $INPUT_FILE" >> "$LOG_FILE" 
echo "" >> "$LOG_FILE" && echo "" >> "$LOG_FILE" 

/usr/bin/time -f "$(date) This command took %E seconds to complete." \
  $FFMPEG_CMD -y -hide_banner -nostats \
  -fflags +genpts+igndts \
  -i "$INPUT_FILE" \
  -level 4.1 \
  -c copy -avoid_negative_ts make_zero \
  -movflags faststart \
  -f mp4 \
  "$OUTPUT_FILE" 1> "$LOG_FILE".1 2> "$LOG_FILE".2
#  -err_detect ignore_err -fflags +genpts+igndts 
cat "$LOG_FILE".1 "$LOG_FILE".2 >> "$LOG_FILE" 
rm "$LOG_FILE".1 "$LOG_FILE".2 >> "$LOG_FILE" 

echo "" >> "$LOG_FILE" && echo "" >> "$LOG_FILE" 
echo "$(date) Finished fix on $INPUT_FILE" >> "$LOG_FILE" 

echo "$(date) Fixed file saved to $OUTPUT_FILE" >> "$LOG_FILE" 
echo "" >> "$LOG_FILE" && echo "" >> "$LOG_FILE"
sudo nano /home/hts/post_scripts/ffmpeg_low.sh

Edit the new script file to include
#!/bin/bash
# script to convert a media to lower quality (MP4 format)

# these variables make this script more readable
FFMPEG_CMD="$1" 
INPUT_FILE="$2" 
OUTPUT_FILE="$3" 
LOG_FILE="$4" 

echo "$(date) Beginning convert low (CRF 18) on $INPUT_FILE" >> "$LOG_FILE" 
echo "" >> "$LOG_FILE" && echo "" >> "$LOG_FILE" 

/usr/bin/time -f "$(date) This command took %E seconds to complete." \
  $FFMPEG_CMD -y -hide_banner -nostats \
    -i "$INPUT_FILE" \
    -codec:v libx264 -crf 18 -preset fast -maxrate 1280k -bufsize 2560k \
    -pix_fmt yuv420p \
    -profile:v high -level 4.1 \
    -r 30000/1001 \
    -filter:v "scale=-2:720" \
    -movflags faststart \
    -codec:a aac -ar 48000 -b:a 192k \
    -filter:a "dynaudnorm" \
    -strict -2 -shortest -f mp4 \
    "$OUTPUT_FILE" 1> "$LOG_FILE".1 2> "$LOG_FILE".2
cat "$LOG_FILE".1 "$LOG_FILE".2 >> "$LOG_FILE" 
rm -f "$LOG_FILE".1 "$LOG_FILE".2 >> "$LOG_FILE" 

echo "" >> "$LOG_FILE" && echo "" >> "$LOG_FILE" 
echo "$(date) Finished convert low (CRF 18) on $INPUT_FILE saved to $OUTPUT_FILE" >> "$LOG_FILE" 
echo "" >> "$LOG_FILE" && echo "" >> "$LOG_FILE"
sudo nano /home/hts/post_scripts/manage_full.sh

Edit the new script file to include
#!/bin/bash
# sub-script to delete/manage the recently recorded programs (symbolic links)
# list each file in the path, sorted by date (append / to directories)
#   ignore directories
#   only include files with the passed argument
#   ignore the newest <argument 2> files
#   for the oldest files to be deleted...
#   enter the command in the log, then run the command to delete the oldest files

# these variables make this script more readable
INPUT_PATH="$1/" 
MAX_FILES="$2" 
LOG_FILE="$3" 
FILTER_GREP="$4" 

SKIP_FILE=".notrack" 
NICE_CMD="/usr/bin/ionice -c 3 /usr/bin/nice -n 19 /usr/bin/trickle -u 6000 -d 6000 -s " 
TMP_LISTa="/tmp/hts-manage-directory-lista.txt" 
TMP_LISTb="/tmp/hts-manage-directory-listb.txt" 

# make sure we aren't skipping this folder
if [ -f "$INPUT_PATH$SKIP_FILE" ]; then
  echo The path indicates that it should be skipped. The file \""$INPUT_PATH$SKIP_FILE"\" exists. >> "$LOG_FILE" 
  exit
fi

# the actual management script
# ignore the filtered files (passed argument #4)
$NICE_CMD ls -1tp "$INPUT_PATH" | grep -v '/$' > "$TMP_LISTa" 
if [ ! -z "$FILTER_GREP" ] ; then
  cat "$TMP_LISTa" | grep "$FILTER_GREP" > "$TMP_LISTb" 
  $NICE_CMD mv "$TMP_LISTb" "$TMP_LISTa" 
fi

# actually list and remove
$NICE_CMD cat "$TMP_LISTa" | tail -n +$(($MAX_FILES+1)) | while IFS= read -r THIS_FILE; do
  echo rm -fv \""$INPUT_PATH/$THIS_FILE"\" \>\> \""$LOG_FILE"\" >> "$LOG_FILE" 
  $NICE_CMD rm -fv "$INPUT_PATH/$THIS_FILE" >> "$LOG_FILE" 
done

# remove temporary files
$NICE_CMD rm -f "$TMP_LISTa" >> "$LOG_FILE" 
$NICE_CMD rm -f "$TMP_LISTb" >> "$LOG_FILE"

And, we finish the scripts with making them executable and change ownerships
sudo chmod +x /home/hts/post_scripts/*
sudo chown -Rh hts:video /home/hts/

Note that much of the ffmpeg command line was taken from https://www.virag.si/2012/01/web-video-encoding-tutorial-with-ffmpeg-0-9/ . Also, I modified it much over the past two years, and the encoding is to make it better for PLEX to stream it.
Install and configure NTP daemon for synchronizing time. I like to use NIST servers, personally.
sudo apt-get install ntp
sudo nano /etc/ntp.conf
sudo service ntp stop
sudo ntpd -gq  # force a time update/set
sudo service ntp start

---9--- External Links
TVHeadEnd Guide 1 (by M Anderson) https://tvheadend.org/boards/4/topics/10322
TVHeadEnd mux/channel Guide 2 (by Robert Cameron) https://tvheadend.org/boards/5/topics/17543?r=22080#message-22080
TVHeadEnd Apt Repository instructions https://tvheadend.org/projects/tvheadend/wiki/AptRepository
Ubuntu 14.04 downloads http://releases.ubuntu.com/14.04/
Zap2it EPG http://tvschedule.zap2it.com/
zap2xml http://zap2xml.awardspace.info/
tv_grab_file https://github.com/Rigolo/tv-grab-file/blob/master/tv_grab_file
Antenna Broadcast Station Listings https://nocable.org/
hdhr-listings-to-m3u.py Python script for HDHomeRun M3Us (forked) https://gist.github.com/aaearon/3e1d4958cf726066c8f55d15da7d86b4
TVHeadEnd Server Access http://&lt;HTPC.IP.Address.Here&gt;:9981/
HDHomeRun EXTEND channel lineup http://&lt;HDHomeRun.IP.Address.Here&gt;/lineup.html
Install Kodi on Amazon FireTV Stick 1 http://kodi.wiki/view/HOW-TO:Install_Kodi_on_Fire_TV
Install Kodi on Amazon FireTV Stick 2 http://troypoint.com/how-to-install-kodi-on-fire-tv/

I will probably not review this post, nor see your comments. I just wanted to share my process with those in my same situation. I may have some typos, etc. But, I have reviewed it thouroughly four times...


    (1-1/1)