feat(jellyfin): store access token in encrypted local store

This commit is contained in:
2026-02-20 03:26:37 -08:00
parent a4532a5fa0
commit 46a2ac5dc7
22 changed files with 306 additions and 13 deletions

View File

@@ -496,7 +496,7 @@ Jellyfin integration is optional and disabled by default. When enabled, SubMiner
| `enabled` | `true`, `false` | Enable Jellyfin integration and CLI commands (default: `false`) |
| `serverUrl` | string (URL) | Jellyfin server base URL |
| `username` | string | Default username used by `--jellyfin-login` |
| `accessToken` | string | Stored Jellyfin access token (treat as secret) |
| `accessToken` | string | Optional explicit Jellyfin access token override; leave empty to use stored local token |
| `userId` | string | Jellyfin user id bound to token/session |
| `deviceId` | string | Client device id sent in auth headers (default: `subminer`) |
| `clientName` | string | Client name sent in auth headers (default: `SubMiner`) |
@@ -512,6 +512,8 @@ Jellyfin integration is optional and disabled by default. When enabled, SubMiner
| `directPlayContainers` | string[] | Container allowlist for direct play decisions |
| `transcodeVideoCodec` | string | Preferred transcode video codec fallback (default: `h264`) |
When `jellyfin.accessToken` is empty, SubMiner uses the locally stored encrypted token saved from Jellyfin login/setup.
Jellyfin direct app CLI commands (`SubMiner.AppImage ...`):
- `--jellyfin`: open the in-app Jellyfin setup window (server/user/password form).

View File

@@ -149,8 +149,9 @@ User-visible errors are shown through CLI logs and mpv OSD for:
## Security Notes and Limitations
- Jellyfin access token is persisted in `config.jsonc`.
- Treat config files as secrets and avoid committing them.
- Jellyfin access token is stored in local encrypted token storage after login/setup.
- `jellyfin.accessToken` remains as an optional explicit override in `config.jsonc`.
- Treat both token storage and config files as secrets and avoid committing them.
- Password is used only for login and is not stored.
- Optional setup UI is available via `--jellyfin`; all actions are also available via CLI flags.
- `subminer` wrapper uses Jellyfin subcommands (`subminer jellyfin ...`, alias `subminer jf ...`). Use `SubMiner.AppImage` for direct `--jellyfin-libraries` and `--jellyfin-items`.

View File

@@ -273,13 +273,14 @@
// ==========================================
// Jellyfin
// Optional Jellyfin integration for auth, browsing, and playback launch.
// Access token is stored in config and should be treated as a secret.
// Access token is stored in local encrypted token storage after login/setup.
// jellyfin.accessToken below remains an optional explicit override.
// ==========================================
"jellyfin": {
"enabled": false, // Enable optional Jellyfin integration and CLI control commands. Values: true | false
"serverUrl": "", // Base Jellyfin server URL (for example: http://localhost:8096).
"username": "", // Default Jellyfin username used during CLI login.
"accessToken": "", // Access token setting.
"accessToken": "", // Optional explicit access token override; leave empty to use stored local token.
"userId": "", // User id setting.
"deviceId": "subminer", // Device id setting.
"clientName": "SubMiner", // Client name setting.

View File

@@ -18,3 +18,6 @@ Read first. Keep concise.
| `codex-release-mpv-plugin-20260220T035757Z-d4yf` | `codex-release-mpv-plugin` | `Package optional release assets bundle (mpv plugin + rofi theme), move theme to assets/themes, update install/docs` | `completed` | `docs/subagents/agents/codex-release-mpv-plugin-20260220T035757Z-d4yf.md` | `2026-02-20T04:02:26Z` |
| `codex-bundle-config-example-20260220T092408Z-a1b2` | `codex-bundle-config-example` | `Bundle config.example.jsonc in release assets tarball and align install docs` | `completed` | `docs/subagents/agents/codex-bundle-config-example-20260220T092408Z-a1b2.md` | `2026-02-20T09:26:24Z` |
| `codex-tsconfig-modernize-20260220T093035Z-68qb` | `codex-tsconfig-modernize` | `Enable noUncheckedIndexedAccess + isolatedModules in root tsconfig and fix resulting compile errors` | `completed` | `docs/subagents/agents/codex-tsconfig-modernize-20260220T093035Z-68qb.md` | `2026-02-20T09:46:26Z` |
| `codex-jellyfin-secret-store-20260220T101428Z-om4z` | `codex-jellyfin-secret-store` | `Verify whether Jellyfin token can use same secret-store path as AniList token` | `completed` | `docs/subagents/agents/codex-jellyfin-secret-store-20260220T101428Z-om4z.md` | `2026-02-20T10:22:45Z` |
| `codex-vitepress-subagents-ignore-20260220T101755Z-k2m9` | `codex-vitepress-subagents-ignore` | `Exclude docs/subagents from VitePress build` | `completed` | `docs/subagents/agents/codex-vitepress-subagents-ignore-20260220T101755Z-k2m9.md` | `2026-02-20T10:18:30Z` |
| `codex-preserve-linebreak-display-20260220T110436Z-r8f1` | `codex-preserve-linebreak-display` | `Fix visible overlay display artifact when subtitleStyle.preserveLineBreaks is disabled` | `completed` | `docs/subagents/agents/codex-preserve-linebreak-display-20260220T110436Z-r8f1.md` | `2026-02-20T11:10:51Z` |

View File

@@ -0,0 +1,65 @@
# codex-jellyfin-secret-store-20260220T101428Z-om4z
- alias: `codex-jellyfin-secret-store`
- mission: `Verify whether Jellyfin token can use same secret-store path as AniList token`
- status: `completed`
- last_update_utc: `2026-02-20T10:22:45Z`
## Intent
- compare AniList token persistence path vs Jellyfin token persistence path
- answer feasibility + current behavior from code
## Planned Files
- `src/main/runtime/anilist-token-refresh.ts`
- `src/main/runtime/*anilist*`
- `src/core/services/jellyfin.ts`
- `src/main/runtime/*jellyfin*`
- `src/config/*`
- docs refs if needed
## Assumptions
- user asking architecture/feasibility question; likely no code change requested yet
## Findings
- AniList token store present: `src/core/services/anilist/anilist-token-store.ts` (`safeStorage` encrypt/decrypt + persisted file)
- AniList runtime wiring present: `src/main.ts` creates `anilistTokenStore`; refresh/setup paths use `saveToken/loadToken`
- Jellyfin auth currently writes token directly into config via `patchRawConfig({ jellyfin: { accessToken } })`
- Docs confirm current behavior: Jellyfin token persisted in config (`docs/jellyfin-integration.md`)
## Outcome
- no code changes; answered feasibility/current state from repo
- implemented requested change: Jellyfin token now persisted in local encrypted token store with config override fallback
## Files Touched
- `src/core/services/jellyfin-token-store.ts`
- `src/main.ts`
- `src/main/runtime/jellyfin-client-info.ts`
- `src/main/runtime/jellyfin-client-info.test.ts`
- `src/main/runtime/jellyfin-client-info-main-deps.ts`
- `src/main/runtime/jellyfin-client-info-main-deps.test.ts`
- `src/main/runtime/jellyfin-cli-auth.ts`
- `src/main/runtime/jellyfin-cli-auth.test.ts`
- `src/main/runtime/jellyfin-cli-main-deps.ts`
- `src/main/runtime/jellyfin-cli-main-deps.test.ts`
- `src/main/runtime/jellyfin-setup-window.ts`
- `src/main/runtime/jellyfin-setup-window.test.ts`
- `src/main/runtime/jellyfin-setup-window-main-deps.ts`
- `src/main/runtime/jellyfin-setup-window-main-deps.test.ts`
- `docs/jellyfin-integration.md`
- `docs/configuration.md`
- `docs/public/config.example.jsonc`
- `src/config/definitions.ts`
- `backlog/tasks/task-92 - Store-Jellyfin-token-in-encrypted-local-token-store-like-AniList.md`
## Verification
- `bun run build`
- `node --test dist/main/runtime/jellyfin-client-info.test.js dist/main/runtime/jellyfin-cli-auth.test.js dist/main/runtime/jellyfin-setup-window.test.js dist/main/runtime/jellyfin-client-info-main-deps.test.js dist/main/runtime/jellyfin-cli-main-deps.test.js dist/main/runtime/jellyfin-setup-window-main-deps.test.js`
- `bun run test:fast`
- `bun run docs:build`

View File

@@ -12,3 +12,7 @@ Shared notes. Append-only.
- [2026-02-20T06:35:38Z] [codex-preserve-linebreaks-20260220T063538Z-s4nd|codex-preserve-linebreaks] overlap note: touching subtitle config + renderer render path (`src/types.ts`, `src/config/*`, `src/renderer/subtitle-render.ts`, docs/config examples) to add optional preserve-line-breaks behavior while keeping default normalization unchanged.
- [2026-02-20T06:42:51Z] [codex-preserve-linebreaks-20260220T063538Z-s4nd|codex-preserve-linebreaks] completed TASK-91; added `subtitleStyle.preserveLineBreaks` config (default false), renderer token/source alignment helper to preserve visible overlay line breaks when enabled, config+renderer tests green.
- [2026-02-20T09:24:08Z] [codex-bundle-config-example-20260220T092408Z-a1b2|codex-bundle-config-example] conflict note: target file .github/workflows/release.yml already modified by codex-release-mpv-plugin session; applying minimal additive delta for config example bundling only.
- [2026-02-20T10:22:45Z] [codex-jellyfin-secret-store-20260220T101428Z-om4z|codex-jellyfin-secret-store] completed TASK-92: Jellyfin token now stored via local encrypted token store (`safeStorage`) with config override fallback; login/setup save token to store, logout clears store; runtime/docs/tests updated.
- [2026-02-20T11:04:36Z] [codex-preserve-linebreak-display-20260220T110436Z-r8f1|codex-preserve-linebreak-display] overlap note: touching `src/renderer/subtitle-render.ts` + renderer tests to fix preserve-linebreaks disabled display artifact while preserving TASK-91 behavior.
- [2026-02-20T11:07:29Z] [codex-preserve-linebreak-display-20260220T110436Z-r8f1|codex-preserve-linebreak-display] completed follow-up for TASK-91: non-preserve mode now flattens token CR/LF to spaces instead of emitting `<br>` from token surfaces; regression test added.
- [2026-02-20T11:10:51Z] [codex-preserve-linebreak-display-20260220T110436Z-r8f1|codex-preserve-linebreak-display] second follow-up: handle overlap token streams by aligning non-preserve rendering to normalized source text and skipping unmatched tail tokens (prevents duplicated second-line phrase).