Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Commands

```bash
# Build
go build epgo

# Build with version info stripped (production)
go build -ldflags="-s -w" -o epgo

# Cross-compile (used by CI)
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o epgo_linux_amd64

# Tidy dependencies
go mod tidy

# Run (interactive config)
./epgo -configure myconfig.yaml

# Run (fetch EPG data and generate XMLTV)
./epgo -config myconfig.yaml
```

There are no tests in this project.

## Architecture

EPGo fetches EPG data from the [Schedules Direct](https://schedulesdirect.org) API and generates XMLTV files for use with media servers (Plex, Jellyfin, etc.).

### Two execution modes

**`-configure`** → `Configure()` in `configure.go`
Interactive TUI for managing credentials, lineups, and channel selection. Writes the YAML config file and cache. Uses `promptui` for menus.

**`-config`** → `sd.Update()` in `data.go`
Headless pipeline: open cache → login → fetch status → fetch lineup/schedule/program/metadata from SD → write cache → generate XMLTV file.

### The `SD` struct (`struct_sd.go`, `sd.go`)

Central API client. Uses a function-field pattern — `sd.Login`, `sd.Status`, `sd.Lineups`, etc. are closures assigned in `sd.Init()`. All HTTP calls funnel through `sd.Connect()`. The SD API base URL is `https://json.schedulesdirect.org/20141201/`.

Token caching: on login, the token and its expiry timestamp are stored in `Cache.Token` / `Cache.TokenExpires` and persisted to the cache JSON file. On subsequent runs, the cached token is reused if it expires more than 5 minutes from now.

### The `cache` struct (`struct_cache.go`, `cache.go`)

Single JSON file (default: `config_cache.json`) that persists between runs to avoid re-downloading unchanged data. Contains:

- `Channel` — `map[stationID]EPGoCache` with station metadata (name, callsign, logo, LCN)
- `Schedule` — `map[stationID][]EPGoCache` with airtimes
- `Program` — `map[programID]EPGoCache` with program details
- `Metadata` — `map[programID]EPGoCache` with artwork URLs
- `Token` / `TokenExpires` — cached SD auth token

`Cache.Init()` reinitializes only the Channel/Schedule/Program/Metadata maps without touching the token fields.

### Config file (`struct_config.go`, `configure.go`)

YAML file managed by `config.Open()` / `config.Save()`. When opened, the code detects missing option blocks by scanning the raw YAML bytes and injects defaults — this is the config migration mechanism (no version field, purely string-matching based).

The `channel` struct is dual-purpose: serialized to YAML for the config file, and also encoded directly to XML for XMLTV output (via Go struct tags). The `Lcn` field is XML-only (`yaml:"-"`), populated from the SD lineup map's `atscMajor.atscMinor` for OTA or the `channel` string for cable/satellite.

### XMLTV generation (`xmltv.go`)

Streams XML directly to file using `xml.NewEncoder`. Channels are written first (from `Cache.Channel`), then programmes (from `Cache.Schedule` joined with `Cache.Program` and `Cache.Metadata`). Channel IDs follow the format `epgo.<stationID>.schedulesdirect.org`.

The `dd_progid` episode number is formatted as `EP01938443.3118` — the first 10 characters of the program ID followed by a dot and the remaining digits.

### Image server (`server.go`)

Optional HTTP file server started with `-serve dir:port` or automatically after XMLTV generation when `Config.Server.Enable = true`. Serves downloaded artwork from the configured image path.

### Key globals

- `Config` — the parsed YAML config (`config` struct)
- `Cache` — the JSON cache (`cache` struct, includes `sync.RWMutex`)
- `Token` — the current SD auth token string (mirrors `Cache.Token` during a run)
- `logger` — `*slog.Logger` using text handler to stdout

### Branch workflow

Development happens on the `development` branch. Changes are merged to `master` for releases. The `development-release.yaml` CI workflow publishes a rolling pre-release on every push to `development`; `release.yaml` publishes versioned releases on `v*` tags.
121 changes: 97 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,49 +12,66 @@ subredit: [epgo](https://www.reddit.com/r/EpGo/)

- [Schedules Direct](https://www.schedulesdirect.org/ "Schedules Direct") Account
- Computer with 1-2 GB memory
- [Go](https://golang.org/ "Golang") to build the binary
- [Optional] Docker to run it in a dockerize environment
- [Go](https://golang.org/ "Golang") 1.21+ to build the binary
- [Optional] Docker to run it in a containerized environment

## Installation

### Option 1 -- Build Binary

The following command must be executed with the terminal / command prompt inside the source code folder.
Run the following commands inside the source code folder:

```bash
go mod tidy
go build epgo
```

This will spit out a binary for your OS named `epgo`
This produces a binary named `epgo` for your OS.

### Option 2 -- Docker
For a smaller production binary:

I need to set up the docker image. However, you can build your own by running:
```bash
go build -ldflags="-s -w" -o epgo
```

#### **docker-compose**
To cross-compile (e.g., for Linux on AMD64):

clone this repo:
```bash
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o epgo_linux_amd64
```

### Option 2 -- Docker

Clone the repo and use the provided `docker-compose.yaml`:

```bash
git clone https://github.com/Chuchodavids/EpGo.git
```

```docker-compose
```yaml
# docker-compose.yaml
services:
epgo:
container_name: epgo
build: .
environment:
- TZ=America/Chicago
volumes:
- YOUR_CONFIG.YAML_FOLDER:/app/
restart: always
epgo:
container_name: epgo
build:
context: .
args:
# Or amd64
- TARGETARCH=arm64
environment:
- TZ=America/Chicago
volumes:
# Assumes config.yaml is in the same directory as this docker-compose.yaml
- ./config.yaml:/app/config.yaml
command: -config /app/config.yaml
restart: never
```

### Option 3 -- download binary
The Docker image is built from the included `Dockerfile`, which downloads the latest release binary from GitHub.

go to [releases](https://github.com/Chuchodavids/EpGo/releases) and download the needed version
### Option 3 -- Download Binary

Go to [releases](https://github.com/Chuchodavids/EpGo/releases) and download the binary for your platform. You can also use the `download.sh` script included in the repository.

## Using the APP

Expand All @@ -65,8 +82,10 @@ go to [releases](https://github.com/Chuchodavids/EpGo/releases) and download the
= Get data from Schedules Direct with configuration file. [filename.yaml]
-configure string
= Create or modify the configuration file. [filename.yaml]
-serve string
= Start a local HTTP server to serve files from the specified directory. [directory:port]
-version
= shows the current version
= shows the current version (v3.2.1)
-h : Show help
```

Expand Down Expand Up @@ -134,7 +153,7 @@ Account:
Files:
Cache: config_cache.json
XMLTV: config.xml
The MovieDB cache file: imdb_image_cache.json
The MovieDB cache file: config_tmdb_cache.json
Server:
Enable: false
Address: localhost
Expand All @@ -145,7 +164,7 @@ Options:
Subtitle into Description: false
Insert credits tag into XML file: false
Images:
Download Images: false
Download Images from Schedules Direct: false
Image Path: ""
The MovieDB:
Enable: false
Expand Down Expand Up @@ -197,6 +216,14 @@ EPG data for the specified days. Schedules Direct has EPG data for the next 12-1

---

```yaml
Live and New icons: false
```

**true:** Appends a unicode superscript "LIVE" or "New" badge to the programme title for live broadcasts and first-run episodes, respectively.

---

```yaml
Subtitle into Description: false
```
Expand Down Expand Up @@ -225,11 +252,11 @@ Alan zieht aus, da seine Freundin Kandi und er in Las Vegas eine Million Dollar
### Images: (Can be customized)

```yaml
Download Images: false
Download Images from Schedules Direct: false
Image Path: ""
```

- **Download Images**: `true` or `false`. If `true`, images will be downloaded to the `Image Path`. If `Image Path` is not set, it will default to a folder named `images`.
- **Download Images from Schedules Direct**: `true` or `false`. If `true`, images will be downloaded to the `Image Path`. If `Image Path` is not set, it will default to a folder named `images`.
- **Image Path**: The path where the images will be downloaded.

#### The MovieDB
Expand All @@ -243,6 +270,8 @@ The MovieDB:
- **Enable**: `true` or `false` to enable or disable The MovieDB as a fallback image source.
- **Api Key**: Your The MovieDB API key.

The TMDB cache file (default: `config_tmdb_cache.json`) persists search results to avoid redundant API calls.

---

```yaml
Expand Down Expand Up @@ -393,6 +422,50 @@ Example:
2020/07/18 19:10:53 [ERROR] Could not find requested image. Post message to http://forums.schedulesdirect.org/viewforum.php?f=6 if you are having issues. [SD API Error Code: 5000] Program ID: EP03481925
```

---

## LCN (Logical Channel Number)

Each channel in the XMLTV output includes an `lcn` attribute for subchannel auto-matching in media servers like Plex and Jellyfin. The LCN is populated from the Schedules Direct lineup map — for OTA channels it uses `atscMajor.atscMinor`, and for cable/satellite it uses the channel number string.

```xml
<channel id="epgo.42635.schedulesdirect.org">
<display-name>WGBO-DT</display-name>
<display-name>66.1</display-name>
<lcn>66.1</lcn>
<icon src="https://...png" width="360" height="270"/>
</channel>
```

---

## Image Server

When `Server.Enable` is set to `true` in the config, the HTTP image server starts automatically after XMLTV generation in `-config` mode. You can also start it independently with the `-serve` flag:

```bash
./epgo -serve /path/to/images:8080
```

---

## Cron Scheduling

To keep EPG data up to date, run EPGo on a schedule. Example crontab entries:

```
@reboot /app/epgo --config /data/livetv/config.yaml
00 * * * * /app/epgo --config /data/livetv/config.yaml
```

A `cronjob` file with these examples is included in the repository.

---

## Cache Cleanup

After each XMLTV generation, stale program and metadata entries are automatically purged from the cache file. This keeps the cache size manageable without manual intervention.

### Create the XMLTV file using the command line (CLI):

```bash
Expand Down
3 changes: 2 additions & 1 deletion sample-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ Server:
Address: localhost
Port: "80"
Options:
Live and New icons: false
Schedule Days: 1
Subtitle into Description: false
Insert credits tag into XML file: false
Images:
Download Images: false
Download Images from Schedules Direct: false
Image Path: ""
The MovieDB:
Enable: false
Expand Down
Loading