Skip to content

Syncing a Remote Player

Austin Nunn edited this page Nov 8, 2026 · 1 revision

Syncing a Remote Player

This page connects tv-player (set up in Remote Player) to tv-server. When it's done, the player copies the server's schedule every hour and plays exactly what Emby and Jellyfin show, at the same moment.

How it works

The server is in charge. It builds every schedule. The player never schedules anything on its own. Once an hour, it copies four things from the server:

  1. The schedule database. What plays when, on every channel.
  2. The station configs. One file per channel.
  3. The catalog. The folders of links your channels play, plus bump cards and other small files.
  4. The Sources list. Live stream channels, like the weather channel.

The videos themselves are never copied. Both PCs read them from the NAS at the same path, /mnt/media.

graph TD
  subgraph tv-server
    wrapper[Station Wrapper 43 builds the schedules]
    feed[fs42-feed.sh: snapshot plus read-only copy]
  end
  subgraph tv-player
    sync[fs42-sync, every hour]
    streams[fs42-streams]
    player[FieldStation42 player]
  end
  nas[NAS videos at /mnt/media]
  wrapper --> feed
  feed -->|SSH, one locked-down key| sync
  sync --> streams --> player
  nas --> player
Loading

Why not have the player build its own schedules? Then it would pick different episodes than the server, and the TV wouldn't match Emby. Copying the server's schedule keeps them in step.

The three scripts

All three are in the repo's extras/remote-player folder.

Script Runs on Job
fs42-feed.sh the server The only thing the player's SSH key is allowed to run. It can make a fresh snapshot of the schedule, or let the player copy files. It can't change anything.
fs42-sync the player Runs every hour from cron. Copies everything over, then restarts the player if something changed.
fs42-streams the player Called by fs42-sync. Turns the server's live stream channels and guide channel into something the player can show.

Why a locked-down SSH key

The player logs in to the server over SSH to copy files. That login uses its own key, and the server only lets that key run fs42-feed.sh. So even if someone got hold of the player and its key, they couldn't run commands on the server or change any files there. All they could do is read your channel data.

The read-only copying is done by rrsync, a small helper that comes with Ubuntu's rsync package. It only allows reading, and only inside one folder.

Part 1: on the server

1. Install the feed script

cd ~/fieldstation42
curl -fsSLO https://raw.githubusercontent.com/austincnunn/station-wrapper-43/main/extras/remote-player/fs42-feed.sh
chmod +x fs42-feed.sh

Open it and check the two settings at the top:

Setting Set it to
DATA Your data folder, like /home/tv/fieldstation42/data
CONTAINER The wrapper container's name (station-wrapper-43 if you used the compose file from Server Setup)

Your user must be able to run docker without sudo, because the script reads two files through the container.

2. Check that rrsync is there

ls -l /usr/bin/rrsync

If it's missing, install rsync: sudo apt-get install -y rsync.

3. Test the snapshot

SSH_ORIGINAL_COMMAND=snapshot ~/fieldstation42/fs42-feed.sh && ls -l ~/fieldstation42/data/crt/

You should see three files: fs42_fluid.db, FS42_REF and sources.json. (The folder is called crt because this was first built for a CRT TV.)

Part 2: on the player

4. Make the SSH key

The key has no password, because cron needs to use it with no one around. That's fine, since the server limits what it can do.

ssh-keygen -q -t ed25519 -N "" -C "tv-player fs42 feed" -f ~/.ssh/fs42_feed
cat ~/.ssh/fs42_feed.pub

Copy the line it prints. You'll paste it on the server in the next step.

5. Let the key in, locked to the feed script (on the server)

On the server, add one line to ~/.ssh/authorized_keys. Put this in front of the key you copied, all on one line:

command="/home/tv/fieldstation42/fs42-feed.sh",restrict ssh-ed25519 AAAA...your key... tv-player fs42 feed
  • command="..." means this key always runs the feed script, no matter what the player asks for.
  • restrict turns off everything else: no terminal, no port forwarding, no agent forwarding.

Make sure the file's permissions are tight:

chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys

6. Give the server a short name (on the player)

Add this to ~/.ssh/config on the player:

Host fs42-feed
  HostName 192.168.1.20
  User tv
  IdentityFile ~/.ssh/fs42_feed
  IdentitiesOnly yes
  StrictHostKeyChecking accept-new
chmod 600 ~/.ssh/config

Now fs42-feed means "the server, with that key". The scripts use this name.

7. Test the connection

ssh -T fs42-feed snapshot && echo OK

You should see OK. The first time, SSH saves the server's fingerprint.

Try something the key isn't allowed to do:

ssh -T fs42-feed whoami

It won't print tv. Instead you'll see rrsync error: SSH_ORIGINAL_COMMAND does not run rsync. The server ran the feed script, which only allows copying. That's the lock working.

8. Install the sync scripts

mkdir -p ~/bin
cd ~/bin
curl -fsSLO https://raw.githubusercontent.com/austincnunn/station-wrapper-43/main/extras/remote-player/fs42-sync
curl -fsSLO https://raw.githubusercontent.com/austincnunn/station-wrapper-43/main/extras/remote-player/fs42-streams
chmod +x fs42-sync fs42-streams

Open fs42-sync and check its settings:

Setting Set it to
FS42_DIR Your FieldStation42 folder, like /home/tv/FieldStation42
FEED fs42-feed, the SSH name from step 6
STREAMS Where fs42-streams is. The default (next to fs42-sync) is right if both are in ~/bin.
SKIP_PACKAGES Packages left out when FieldStation42 is updated. Remove evdev if you installed it for a remote.

Open fs42-streams and check its two settings:

Setting Set it to
TUNER The server's tuner, http://192.168.1.20:5004. You can also set the FS42_TUNER_URL environment variable instead.
PANSCAN How widescreen video fits the TV. 0 shows the whole picture with black bars, which is right for a widescreen TV. 1.0 fills a 4:3 TV by cutting off the sides.

Then run its self-check:

~/bin/fs42-streams --test

It should print ok.

9. Run the first sync by hand

~/bin/fs42-sync

The first run copies the whole catalog of links, so it takes longer than later runs. After that, check what arrived:

ls ~/FieldStation42/confs/
ls ~/FieldStation42/runtime/

confs/ should have one .json file per channel. runtime/ should have fs42_fluid.db, server_sources.json, server_fs42_ref and fs42_ref.

If the server runs a different FieldStation42 version than you cloned, this run also switches yours to match and reinstalls its packages.

10. Run it every hour

crontab -e

Add this line:

0 * * * * /home/tv/bin/fs42-sync 2>&1 | logger -t fs42-sync

Its messages go to the system log, tagged fs42-sync.

11. Start the TV

sudo reboot

The PC logs in by itself, starts X, and the player starts on the TV.

What each sync does

Every hour, fs42-sync does this:

  1. Takes a lock, so two syncs never run at once. If the last one is still going, this one quietly skips.
  2. Asks for a snapshot. The server makes a clean copy of the schedule database. (The real database is busy all the time. Copying it as-is could catch it half-written.)
  3. Copies the catalog. rsync only sends what changed, so this is quick after the first time. Links removed on the server are removed here too.
  4. Copies the station configs. It keeps three things on the player that the server doesn't have: FieldStation42's main_config.json, its examples/ folder, and the stream-*.json stations that fs42-streams makes.
  5. Copies the schedule snapshot into runtime/fs42_fluid.db.
  6. Runs fs42-streams (see below).
  7. Checks the FieldStation42 version. The server's image records which FieldStation42 commit it runs. If the player's is different, it fetches and switches to that exact commit, then reinstalls the packages. Both sides read the same database, so they need to agree.
  8. Restarts the player if needed. If any station config or the FieldStation42 version changed, it stops the player. The autologin starts it again on the new setup a few seconds later. If nothing changed, the TV isn't touched.

What fs42-streams does

The player and the server don't handle everything the same way. fs42-streams fixes up the copied configs, on the player only:

  • Live stream channels. Each channel on the server's Sources page (a weather channel, a camera, an IPTV playlist) becomes a FieldStation42 "streaming" station on the player, with the same number and name as in Emby. Remove a source on the server, and its station goes away here at the next sync. The player opens these streams itself, so it must be able to reach them on the network.
  • The guide channel. FieldStation42's own guide is plain. The server's guide shows show names and posters. So the player plays the server's guide channel as a stream from the server's tuner (TUNER/auto/v<number>). A guide set to Standard (4:3) on the server is cropped to fill a 4:3 TV. A widescreen guide is shown whole.
  • Panscan. Every other station gets your PANSCAN setting, unless its config already has its own. This has to go in each station's config, because FieldStation42 resets mpv's panscan for every video. Putting it in mpv.conf doesn't stick.

Checking on it

The sync's log:

journalctl -t fs42-sync -n 20

The player's log:

journalctl -t fs42 -n 50

When did the last schedule copy arrive?

ls -l ~/FieldStation42/runtime/fs42_fluid.db

It should be less than an hour old.

When it doesn't work

What you see What to check
Permission denied (publickey) The key line on the server (step 5). It has to be one line, with the command= part in front of the key. Check the permissions too: ~/.ssh is 700 and authorized_keys is 600.
ssh -T fs42-feed snapshot hangs or fails Run the snapshot on the server by hand (step 3). Can your user run docker? Is DATA right?
rrsync: command not found Install rsync on the server, or fix the path at the bottom of fs42-feed.sh.
Shows play at the wrong time The two PCs' time zones don't match. See Remote Player, step 1.
Black screen, or "file not found" in the player log The player can't see the videos at /mnt/media. Run ls /mnt/media and compare with what the container sees (docker exec station-wrapper-43 ls /mnt/media on the server).
A channel shows up in Emby but not on the TV The sync hasn't run since you made it. Run ~/bin/fs42-sync by hand.
A stream channel is skipped The log says why, for example channel '47' is not a whole number free on this player. Each stream needs a whole channel number that no other station uses.
The weather channel or a stream won't play The player opens the stream's address itself. Try it with mpv <address> from the player.
The picture is cut off, or has bars Change PANSCAN in fs42-streams, then run ~/bin/fs42-sync. The configs are copied fresh from the server each time, so the new setting reaches every station at once. The only exception is a station whose config on the server sets its own panscan.

More general problems are on the Troubleshooting page.

Clone this wiki locally