mirror of
https://github.com/steredbits/pelagia.git
synced 2026-10-17 15:01:26 +02:00
85 lines
5.6 KiB
Markdown
85 lines
5.6 KiB
Markdown
# Architecture
|
|
|
|
Pelagia is a native Jellyfin client for jailbroken PS5 consoles, written in C++17. The goal is a simple,
|
|
reliable player; the PS5-specific layer is kept as thin as possible and everything else is portable code
|
|
that is developed and tested on Linux.
|
|
|
|
## Layout
|
|
|
|
```
|
|
core/ portable code (C++17), no platform dependency
|
|
api/ Jellyfin REST client (libcurl + cJSON): authentication, libraries, items, stream URLs,
|
|
audio/subtitle tracks (model, default choice, labels)
|
|
player/ playback pipeline: ffmpeg demux/decode, frame queues, A/V sync, network source
|
|
ui/ screens and navigation, driven by abstract input events; track menus, text subtitles
|
|
util/ logs, config, image cache, track languages, SRT subtitles, version, app identity,
|
|
interface localization (i18n.h, catalogs in util/i18n/strings_{en,fr}.def)
|
|
platform/
|
|
platform.h abstract interface: window/rendering, audio, input, network, files, time
|
|
sdl/ rendering, audio, input with SDL2, shared by Linux and PS5
|
|
posix/ files (POSIX), shared by Linux and PS5
|
|
linux/ Linux specifics (XDG folders); development and tests
|
|
ps5/ PS5 specifics (folders, logs, fullscreen)
|
|
tests/ unit and integration tests (ctest); mocked API, JSON parsing, queues, UI navigation
|
|
tools/ fake Jellyfin server, release and maintenance scripts
|
|
assets/ third_party/ embedded fonts (Noto Sans, OFL); stb_truetype, cJSON
|
|
pkg/ ci/ cmake/ packaging resources, build scripts
|
|
docs/screenshots/ screenshots of the interface (generated headlessly)
|
|
```
|
|
|
|
Rule: `core/` never includes a header from `platform/sdl`, `platform/linux` or `platform/ps5`, only
|
|
`platform/platform.h`. The platform layer may use `util/log`.
|
|
|
|
## Technical choices
|
|
|
|
- C++17 without exceptions or RTTI in the core (portability to the PS5 toolchain), built with CMake for two
|
|
targets: `-DPLATFORM=linux` and `-DPLATFORM=ps5` (cross-compilation with the open-source
|
|
[ps5-payload-dev SDK](https://github.com/ps5-payload-dev/sdk) and PacBrew libraries; no Sony SDK or
|
|
leaked header is ever used).
|
|
- Dependencies: libcurl, cJSON (vendored), ffmpeg (libavformat/libavcodec/libswscale/libswresample), SDL2.
|
|
- Video: software decoding. The server is asked for H.264 1080p + AAC stereo through the stream parameters,
|
|
so only one pair of codecs needs to be handled; direct streaming is used when the server can.
|
|
- Rendering: SDL software renderer with an IYUV texture, linear scaling, letterboxing. The UI is drawn with
|
|
a small canvas interface (SDL on Linux/PS5, a CPU rasteriser for screenshots and tests).
|
|
- Input: gamepad events are mapped to abstract events (`Up`, `Down`, `Ok`, `Back`, `PlayPause`, seek…).
|
|
- The UI never blocks (network, disk, decoding, opening a stream): work goes through worker threads and
|
|
results are applied on the main thread.
|
|
|
|
## On the console
|
|
|
|
The app is a [websrv](https://github.com/ps5-payload-dev/websrv) homebrew: `eboot.elf` +
|
|
`sce_sys/icon0.png` in `/data/homebrew/Pelagia/` or in `/mnt/usbN/homebrew/Pelagia/`. Session, poster cache
|
|
and logs are always under `/data/homebrew/Pelagia/` (`platform/ps5/ps5_paths.h`); `pelagia.conf` is only
|
|
read, from the USB stick first, then from `/data`. Logs go to stdout and to `logs/pelagia.log`.
|
|
|
|
## Jellyfin API notes
|
|
|
|
- **Authentication**: the general API only accepts the `Authorization: MediaBrowser …, Token="…"` header.
|
|
The stream accepts that header too, and the client uses it alone by default (`--stream-auth header`) so
|
|
the token appears in no URL. `query` and `both` remain available as fallbacks; the client never switches
|
|
on its own.
|
|
- **Seeking** is not possible inside the progressive stream (`Accept-Ranges: none`): the stream is reopened
|
|
with `startTimeTicks` and a fresh `playSessionId`, after `DELETE /Videos/ActiveEncodings` for the previous
|
|
session (otherwise the server reuses the job and ignores the new position).
|
|
- **Direct streaming**: depending on the file the server copies the video and only transcodes the audio; the
|
|
first audio packet of the MPEG-TS can arrive after several MB of video, so the probe goes up to 32 MB or
|
|
10 s, and the player never stays stuck (clock locked to the video after 3 s without audio, give up after
|
|
20 s with nothing readable).
|
|
- **Tracks**: the server computes `DefaultAudioStreamIndex` / `DefaultSubtitleStreamIndex` per media source
|
|
for the user profile and the client follows them (local fallback: `api/track_selection`). The stream URL
|
|
carries `audioStreamIndex` and, only for burned-in subtitles, `subtitleStreamIndex` +
|
|
`subtitleMethod=Encode`. Text subtitles are downloaded separately as SRT and drawn by the client, so the
|
|
stream is unchanged. The server only remembers tracks from `Progress` reports.
|
|
- **Logs never contain the token**: `util/log` masks registered secrets and the `api_key=` / `Token="…"`
|
|
patterns. Do not write to stderr/stdout around it, and let ffmpeg logs go through `player/av_log_bridge`.
|
|
|
|
## Testing
|
|
|
|
- `ctest --test-dir build-linux --output-on-failure`: unit tests plus end-to-end tests against
|
|
`tools/fake_jellyfin_server.py` (a small Jellyfin imitation with a media test pattern).
|
|
- `./ci/build-sanitize.sh`: Debug build with libstdc++ assertions, ASan and UBSan, all tests (also a CI job).
|
|
- `tools/update_screenshots.sh`: regenerates `docs/screenshots` headlessly after a visible UI change.
|
|
- `tools/scan-secrets.sh`: gitleaks scan (CI job).
|
|
- PS5 builds can only be checked by cross-compiling (`ci/build-ps5.sh`); behaviour on the console can only
|
|
be checked on a console, so keep PS5-specific code minimal and log what it does.
|