Skip to content
DerekParksPublic

About

Control Griffin radio shark on linux

Resources

Stars

2 stars

Watchers

1 watching

Forks

Repository files navigation

Shark records your teams' radio broadcasts with a Griffin RadioSHARK, streams
games live, and serves the recordings as a podcast.

- The schedule service pulls NFL, NHL, NBA and MLB schedules for the teams in
  teams.toml and merges them by priority: a game that overlaps one from a team
  listed above it is skipped whole.
- The recorder tunes the shark and records each scheduled game to MP3, streaming
  it to Icecast while it records.
- The web app serves a dashboard, the podcast feed (/feed.xml), the recordings,
  and the live stream (/live). Every URL it hands out is built from the host the
  request arrived on, so the same server works on a LAN address, behind a
  reverse proxy that sets X-Forwarded-Proto, or over Tailscale.


Running it
----------

Needs Docker with Compose, a RadioSHARK on USB, and a Linux host whose kernel
has the radio-shark (or shark2) and snd-usb-audio drivers. Images are built for
amd64 and arm64 (64-bit Raspberry Pi OS works).

    mkdir shark && cd shark
    curl -LO https://raw.githubusercontent.com/DerekParks/shark/master/docker-compose.yaml
    curl -L -o .env https://raw.githubusercontent.com/DerekParks/shark/master/sample.env
    mkdir config data
    curl -L -o config/teams.toml https://raw.githubusercontent.com/DerekParks/shark/master/teams.example.toml

Edit config/teams.toml (the comments explain each field) and set the passwords
and TZ in .env, then:

    docker compose up -d

Then open http://<host>:8771. Check the teams file with
`docker compose run --rm schedule python buildSchedule.py --check --teams=/config/teams.toml`.

Before trusting it with a game, check reception. AM and FM can differ a lot
depending on where the shark sits:

    docker compose run --rm recorder sh -c 'fm -q 0.85 && arecord -D hw:CARD=RadioSHARK,DEV=0 -d 10 -c 2 -f S16_LE -r 44100 /data/test.wav'

fm takes MHz, so 850 AM is 0.85. Listen to data/test.wav.


Configuration
-------------

config/teams.toml   Teams, stations and priority. See teams.example.toml.
config/cover.jpg    Optional podcast cover; set SHARK_COVER=/config/cover.jpg.
.env                Everything else; sample.env lists every setting:

  ICECAST_*_PASSWORD      Icecast passwords (required)
  COMPOSE_PROFILES        "recorder" to run the recorder (needs the shark),
                          "enhance" to clean up recordings, "adcut" to cut
                          the ad breaks out; comma-separated
  TZ                      Time zone for file names and episode dates (UTC)
  SHARK_FEED_TITLE        Feed and dashboard title (Shark Sports)
  SHARK_FEED_DESCRIPTION  Feed description
  SHARK_FEED_AUTHOR       Feed author (Shark)
  SHARK_COVER             Cover image path inside the container
  SHARK_WEB_PORT          Host port for the web app (8771)
  SHARK_ALSA_DEVICE       Capture device (hw:CARD=RadioSHARK,DEV=0)
  SHARK_RADIO_DEVICE      Tuner device on the host (/dev/radio0)
  SHARK_BITRATE           Recording bitrate, kbps (64)
  SHARK_STREAM_BITRATE    Live stream bitrate, kbps (64)
  PUID, PGID              User the containers run as (1000)
  AUDIO_GID, VIDEO_GID    Host groups owning /dev/snd and /dev/radio0 (29, 44)
  SHARK_GPU_WORKER_URL    silence-of-the-spams GPU worker that finds the breaks
  SHARK_ADCUT_MODEL       Sports BiLSTM-CRF on the worker
                          (trained_models/sports/sports-text-v1.pt)
  SHARK_ADCUT_EMBEDDER    Its embedder on the worker
                          (/data/trained_models/bge-m3-ads-v1)
  ICECAST_PORT            Icecast admin port on 127.0.0.1 (8770)

Recordings land in data/out as <team>-<date>-<HHMM>.mp3. A game the recorder
had to rejoin after a restart continues in <team>-<date>-<HHMM>-2.mp3.

The enhancer cleans up each finished recording into data/enhanced under the same
name, newest first, and the feed and dashboard serve that copy once it exists.
It notches out mains hum, reduces noise with DeepFilterNet3 by at most 12 dB, and
on recordings with no audio above about 12 kHz (AM, or old low-bitrate MP3s)
regenerates the missing highs with LavaSR v2. The raw recording is kept. It
runs on the CPU and pauses while a game is recording. Enhance a single file with
`docker compose run --rm enhance python enhance.py <in.mp3> <out.mp3>`.

Ad removal (profile adcut) sends each finished recording to a
silence-of-the-spams GPU worker, which transcribes it, scans it for music and
labels every line with a BiLSTM-CRF trained on sports radio. Ad lines within
20 s of each other become one break, and breaks under 10 s are dropped.
Sponsor lines the crew reads live stay in. The cut copy goes to data/trimmed
with a short beep where each break was, cut from the enhanced copy when there
is one and cut again when a newer enhanced copy appears. The feed serves it
ahead of the enhanced and raw copies. The worker's transcript, music timeline
and the breaks are kept in data/ads. Like the enhancer, it pauses while a game
is recording.


Development
-----------

    uv sync
    uv run pytest

archive/ holds the original command-line RadioSHARK control app. More about it:
http://javier.rodriguez.org.mx/index.php/2006/06/10/griffin-radio-shark-icecast2-on-debian-gnulinux
Its original README states a Public Domain licence.

All other code is licensed under CC0 (https://wiki.creativecommons.org/CC0).

About

Control Griffin radio shark on linux

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages