plex-tui 0.4.2__tar.gz → 0.4.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. {plex_tui-0.4.2 → plex_tui-0.4.4}/.github/workflows/post-release-homebrew.yml +120 -3
  2. {plex_tui-0.4.2 → plex_tui-0.4.4}/AGENTS.md +9 -0
  3. {plex_tui-0.4.2 → plex_tui-0.4.4}/CHANGELOG.md +27 -0
  4. {plex_tui-0.4.2 → plex_tui-0.4.4}/PACKAGING.md +16 -6
  5. {plex_tui-0.4.2 → plex_tui-0.4.4}/PKG-INFO +28 -6
  6. {plex_tui-0.4.2 → plex_tui-0.4.4}/README.md +27 -5
  7. {plex_tui-0.4.2 → plex_tui-0.4.4}/RELEASE.md +11 -4
  8. {plex_tui-0.4.2 → plex_tui-0.4.4}/ROADMAP.md +21 -8
  9. {plex_tui-0.4.2 → plex_tui-0.4.4}/config.example.toml +9 -1
  10. plex_tui-0.4.4/docs/homebrew-install-time.md +171 -0
  11. {plex_tui-0.4.2 → plex_tui-0.4.4}/docs/plex-integration-research.md +8 -17
  12. {plex_tui-0.4.2 → plex_tui-0.4.4}/packaging/aur/.SRCINFO +3 -3
  13. {plex_tui-0.4.2 → plex_tui-0.4.4}/packaging/aur/PKGBUILD +2 -2
  14. {plex_tui-0.4.2 → plex_tui-0.4.4}/packaging/homebrew/README.md +12 -3
  15. {plex_tui-0.4.2 → plex_tui-0.4.4}/pyproject.toml +1 -1
  16. {plex_tui-0.4.2 → plex_tui-0.4.4}/scripts/check_release.py +38 -0
  17. plex_tui-0.4.4/scripts/measure_homebrew_install.py +154 -0
  18. plex_tui-0.4.4/scripts/prepare_homebrew_bottle_assets.py +70 -0
  19. {plex_tui-0.4.2 → plex_tui-0.4.4}/src/plextui/__init__.py +1 -1
  20. {plex_tui-0.4.2 → plex_tui-0.4.4}/src/plextui/app.py +295 -32
  21. {plex_tui-0.4.2 → plex_tui-0.4.4}/src/plextui/config.py +10 -0
  22. {plex_tui-0.4.2 → plex_tui-0.4.4}/src/plextui/player.py +39 -4
  23. {plex_tui-0.4.2 → plex_tui-0.4.4}/src/plextui/plex_service.py +134 -5
  24. {plex_tui-0.4.2 → plex_tui-0.4.4}/tests/test_app_helpers.py +70 -9
  25. {plex_tui-0.4.2 → plex_tui-0.4.4}/tests/test_app_navigation.py +282 -19
  26. {plex_tui-0.4.2 → plex_tui-0.4.4}/tests/test_config.py +6 -1
  27. plex_tui-0.4.4/tests/test_homebrew_bottle_assets.py +72 -0
  28. {plex_tui-0.4.2 → plex_tui-0.4.4}/tests/test_player.py +36 -0
  29. {plex_tui-0.4.2 → plex_tui-0.4.4}/tests/test_plex_service.py +71 -1
  30. {plex_tui-0.4.2 → plex_tui-0.4.4}/tests/test_release_checks.py +27 -0
  31. {plex_tui-0.4.2 → plex_tui-0.4.4}/.github/workflows/aur.yml +0 -0
  32. {plex_tui-0.4.2 → plex_tui-0.4.4}/.github/workflows/bump.yml +0 -0
  33. {plex_tui-0.4.2 → plex_tui-0.4.4}/.github/workflows/ci.yml +0 -0
  34. {plex_tui-0.4.2 → plex_tui-0.4.4}/.github/workflows/post-release-aur.yml +0 -0
  35. {plex_tui-0.4.2 → plex_tui-0.4.4}/.github/workflows/publish-aur.yml +0 -0
  36. {plex_tui-0.4.2 → plex_tui-0.4.4}/.github/workflows/publish-pypi.yml +0 -0
  37. {plex_tui-0.4.2 → plex_tui-0.4.4}/.github/workflows/publish-testpypi.yml +0 -0
  38. {plex_tui-0.4.2 → plex_tui-0.4.4}/.gitignore +0 -0
  39. {plex_tui-0.4.2 → plex_tui-0.4.4}/LICENSE +0 -0
  40. {plex_tui-0.4.2 → plex_tui-0.4.4}/Makefile +0 -0
  41. {plex_tui-0.4.2 → plex_tui-0.4.4}/docs/assets/grid-view.png +0 -0
  42. {plex_tui-0.4.2 → plex_tui-0.4.4}/docs/assets/list-view.png +0 -0
  43. {plex_tui-0.4.2 → plex_tui-0.4.4}/packaging/aur/README.md +0 -0
  44. {plex_tui-0.4.2 → plex_tui-0.4.4}/scripts/stage_release.py +0 -0
  45. {plex_tui-0.4.2 → plex_tui-0.4.4}/scripts/update_aur_package.py +0 -0
  46. {plex_tui-0.4.2 → plex_tui-0.4.4}/scripts/update_homebrew_formula.py +0 -0
  47. {plex_tui-0.4.2 → plex_tui-0.4.4}/src/plextui/__main__.py +0 -0
  48. {plex_tui-0.4.2 → plex_tui-0.4.4}/src/plextui/artwork.py +0 -0
  49. {plex_tui-0.4.2 → plex_tui-0.4.4}/src/plextui/auth.py +0 -0
  50. {plex_tui-0.4.2 → plex_tui-0.4.4}/src/plextui/models.py +0 -0
  51. {plex_tui-0.4.2 → plex_tui-0.4.4}/src/plextui/smoke.py +0 -0
  52. {plex_tui-0.4.2 → plex_tui-0.4.4}/tests/test_artwork.py +0 -0
  53. {plex_tui-0.4.2 → plex_tui-0.4.4}/tests/test_auth.py +0 -0
  54. {plex_tui-0.4.2 → plex_tui-0.4.4}/tests/test_cli.py +0 -0
@@ -13,6 +13,7 @@ on:
13
13
 
14
14
  env:
15
15
  FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
16
+ HOMEBREW_NO_REQUIRE_TAP_TRUST: "1"
16
17
 
17
18
  jobs:
18
19
  publish-tap:
@@ -133,14 +134,127 @@ jobs:
133
134
  "${{ steps.tap.outputs.path }}/Formula/plex-tui.rb" \
134
135
  "${{ steps.release.outputs.version }}"
135
136
 
136
- - name: Audit formula
137
+ - name: Check tap update need
137
138
  if: steps.release.outputs.publish == 'true'
139
+ id: tap-update
140
+ working-directory: ${{ steps.tap.outputs.path }}
141
+ env:
142
+ VERSION: ${{ steps.release.outputs.version }}
143
+ run: |
144
+ set -euo pipefail
145
+
146
+ bottle_release="plex-tui-${VERSION}"
147
+ bottle_root_url="https://github.com/so1omon563/homebrew-plex-tui/releases/download/${bottle_release}"
148
+ needed=false
149
+
150
+ if ! git diff --quiet -- Formula/plex-tui.rb; then
151
+ needed=true
152
+ fi
153
+
154
+ if ! grep -Fq "root_url \"${bottle_root_url}\"" Formula/plex-tui.rb; then
155
+ needed=true
156
+ fi
157
+
158
+ {
159
+ echo "needed=${needed}"
160
+ echo "bottle_release=${bottle_release}"
161
+ echo "bottle_root_url=${bottle_root_url}"
162
+ } >> "$GITHUB_OUTPUT"
163
+
164
+ if [ "$needed" = "false" ]; then
165
+ echo "Homebrew tap is already up to date with bottle metadata."
166
+ fi
167
+
168
+ - name: Build Homebrew bottle
169
+ if: steps.release.outputs.publish == 'true' && steps.tap-update.outputs.needed == 'true'
170
+ id: bottle
171
+ timeout-minutes: 60
172
+ working-directory: ${{ steps.tap.outputs.path }}
173
+ env:
174
+ BOTTLE_ROOT_URL: ${{ steps.tap-update.outputs.bottle_root_url }}
175
+ VERSION: ${{ steps.release.outputs.version }}
176
+ run: |
177
+ set -euo pipefail
178
+
179
+ brew --version
180
+ brew config
181
+ brew info so1omon563/plex-tui/plex-tui
182
+
183
+ (
184
+ while true; do
185
+ sleep 60
186
+ echo "Still building Homebrew bottle for plex-tui ${VERSION} at $(date -u +%Y-%m-%dT%H:%M:%SZ)."
187
+ done
188
+ ) &
189
+ heartbeat_pid=$!
190
+ trap 'kill "$heartbeat_pid" 2>/dev/null || true' EXIT
191
+
192
+ brew uninstall --ignore-dependencies so1omon563/plex-tui/plex-tui >/dev/null 2>&1 || true
193
+ brew install --verbose --build-bottle so1omon563/plex-tui/plex-tui
194
+ kill "$heartbeat_pid" 2>/dev/null || true
195
+ trap - EXIT
196
+
197
+ rm -f plex-tui--*.bottle.json plex-tui--*.bottle.tar.gz
198
+ brew bottle \
199
+ --json \
200
+ --no-rebuild \
201
+ --root-url "$BOTTLE_ROOT_URL" \
202
+ so1omon563/plex-tui/plex-tui
203
+
204
+ bottle_json="$(find "$PWD" -maxdepth 1 -name 'plex-tui--*.bottle.json' -print -quit)"
205
+ if [ -z "$bottle_json" ]; then
206
+ echo "brew bottle did not produce bottle JSON" >&2
207
+ exit 1
208
+ fi
209
+
210
+ bottle_dir="${RUNNER_TEMP}/homebrew-bottles"
211
+ rm -rf "$bottle_dir"
212
+ mkdir -p "$bottle_dir"
213
+ python "${GITHUB_WORKSPACE}/scripts/prepare_homebrew_bottle_assets.py" \
214
+ "$bottle_json" \
215
+ --output-dir "$bottle_dir"
216
+
217
+ brew bottle --merge --write --no-commit "$bottle_json"
218
+
219
+ {
220
+ echo "json_path=${bottle_json}"
221
+ echo "upload_dir=${bottle_dir}"
222
+ } >> "$GITHUB_OUTPUT"
223
+
224
+ - name: Publish Homebrew bottle assets
225
+ if: steps.release.outputs.publish == 'true' && steps.tap-update.outputs.needed == 'true'
226
+ working-directory: ${{ steps.bottle.outputs.upload_dir }}
227
+ env:
228
+ GH_TOKEN: ${{ secrets.PACKAGING_PR_TOKEN }}
229
+ VERSION: ${{ steps.release.outputs.version }}
230
+ BOTTLE_RELEASE: ${{ steps.tap-update.outputs.bottle_release }}
231
+ run: |
232
+ set -euo pipefail
233
+
234
+ if gh release view "$BOTTLE_RELEASE" --repo so1omon563/homebrew-plex-tui >/dev/null 2>&1; then
235
+ gh release edit "$BOTTLE_RELEASE" \
236
+ --repo so1omon563/homebrew-plex-tui \
237
+ --title "plex-tui ${VERSION} Homebrew bottle"
238
+ else
239
+ gh release create "$BOTTLE_RELEASE" \
240
+ --repo so1omon563/homebrew-plex-tui \
241
+ --target main \
242
+ --title "plex-tui ${VERSION} Homebrew bottle" \
243
+ --notes "Homebrew bottle assets for plex-tui ${VERSION}."
244
+ fi
245
+
246
+ gh release upload "$BOTTLE_RELEASE" ./* \
247
+ --repo so1omon563/homebrew-plex-tui \
248
+ --clobber
249
+
250
+ - name: Audit formula
251
+ if: steps.release.outputs.publish == 'true' && steps.tap-update.outputs.needed == 'true'
138
252
  working-directory: ${{ steps.tap.outputs.path }}
139
253
  run: |
140
254
  brew audit --strict --online so1omon563/plex-tui/plex-tui
141
255
 
142
256
  - name: Commit tap update
143
- if: steps.release.outputs.publish == 'true'
257
+ if: steps.release.outputs.publish == 'true' && steps.tap-update.outputs.needed == 'true'
144
258
  id: commit
145
259
  working-directory: ${{ steps.tap.outputs.path }}
146
260
  env:
@@ -172,15 +286,18 @@ jobs:
172
286
  GH_TOKEN: ${{ secrets.PACKAGING_PR_TOKEN }}
173
287
  VERSION: ${{ steps.release.outputs.version }}
174
288
  BRANCH: ${{ steps.commit.outputs.branch }}
289
+ BOTTLE_RELEASE: ${{ steps.tap-update.outputs.bottle_release }}
175
290
  run: |
176
291
  set -euo pipefail
177
292
 
293
+ pr_body="$(printf 'Automated Homebrew tap update for plex-tui %s.\n\nIncludes the generated bottle metadata for %s.\n' "$VERSION" "$BOTTLE_RELEASE")"
294
+
178
295
  pr_url="$(gh pr create \
179
296
  --repo so1omon563/homebrew-plex-tui \
180
297
  --base main \
181
298
  --head "$BRANCH" \
182
299
  --title "Update plex-tui to ${VERSION}" \
183
- --body "Automated Homebrew tap update for plex-tui ${VERSION}.")"
300
+ --body "$pr_body")"
184
301
 
185
302
  gh pr merge "$pr_url" \
186
303
  --repo so1omon563/homebrew-plex-tui \
@@ -184,6 +184,11 @@ Pull requests should include:
184
184
  Use PRs for repository changes. When publishing local commits, branch from
185
185
  `main` with a scoped name such as `codex/release-prep`, push that branch, and
186
186
  open a draft PR instead of pushing directly to `main`.
187
+ Create or switch to a scoped branch before doing repo work; do not keep feature
188
+ work in the local `main` working tree.
189
+ Repo work is not complete when a branch is merely pushed. Treat the workflow as
190
+ open until there is a draft PR for the branch or the work is explicitly
191
+ canceled.
187
192
 
188
193
  `main` is protected by repository rulesets. Changes must flow through PRs; force
189
194
  pushes and branch deletion are blocked. `plex-tui` requires the Python 3.11 and
@@ -198,6 +203,10 @@ merge should create the GitHub Release and publish package channels. Exceptions
198
203
  are allowed for packaging-only follow-ups, automation repair, docs-only
199
204
  maintenance, or other changes that should not create a new version tag; call out
200
205
  the reason in the PR body when omitting a bump marker.
206
+ When a change is judged release-worthy, do not only add `#release`: run the
207
+ scripted release prep in the same branch and keep the release marker in the PR
208
+ title. A release decision means both staged release files and a publishing
209
+ marker are required unless the release is explicitly canceled.
201
210
 
202
211
  When preparing or estimating a release version, fetch remote tags first with
203
212
  `git fetch --tags origin` and base the decision on the latest origin tag, not
@@ -2,6 +2,33 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.4.4 - 2026-06-18
6
+
7
+ - Added a keyboard action to mark the selected playable Plex item watched or
8
+ unwatched from the browse view.
9
+ - Added Plex library Categories browsing, visible movie-edition variants, and
10
+ live mpv audio/subtitle switching when the active playback exposes matching
11
+ tracks.
12
+ - Made library rows open the full Library view by default, with Space opening
13
+ the browse-mode menu as the alternate action, and added a Settings option to
14
+ swap those actions.
15
+ - Added a Backspace/Delete Continue Watching removal action for selected items
16
+ in the Continue Watching view.
17
+ - Improved episode readability by showing show, season, and episode context in
18
+ episode rows and directly under the episode title in details.
19
+
20
+ ## 0.4.3 - 2026-06-18
21
+
22
+ - Made the default `mpv` window open with `--autofit=80%`, keeping custom
23
+ Settings overrides for exact pixel sizes or other percentage presets, and
24
+ made the old `1280x720` preset cycle back to Default.
25
+ - Added Homebrew bottle publishing to the post-release tap automation so macOS
26
+ installs can pour a prebuilt `plex-tui` virtualenv instead of rebuilding
27
+ Python resources from source, with bounded verbose CI diagnostics for bottle
28
+ builds.
29
+ - Documented that Homebrew bottles are published for Apple Silicon macOS, while
30
+ Intel macOS remains supported through Homebrew's source-build path.
31
+
5
32
  ## 0.4.2 - 2026-06-18
6
33
 
7
34
  - Prefer episode stills and season posters for TV artwork while keeping show art
@@ -51,8 +51,12 @@ brew install plex-tui
51
51
  ```
52
52
 
53
53
  The formula depends on `mpv` and `python@3.13`, then installs the Python app in
54
- a Homebrew-managed virtualenv. The first install can take several minutes
55
- because native Python resources such as `pillow` are built from source.
54
+ a Homebrew-managed virtualenv. Post-release automation publishes Homebrew
55
+ bottles for Apple Silicon macOS so supported installs can pour the prebuilt app
56
+ virtualenv instead of rebuilding Python resources such as `pillow` from source.
57
+ Intel macOS remains supported through Homebrew's source install path while that
58
+ platform continues to be supported. If no matching bottle is available,
59
+ Homebrew falls back to the source install path.
56
60
  Homebrew 6 requires non-official taps to be trusted before Homebrew loads
57
61
  formulae from them. The `plex-tui` formula only uses Homebrew/core formula
58
62
  dependencies, so users do not need to trust any additional taps for `mpv`,
@@ -65,6 +69,10 @@ brew test so1omon563/plex-tui/plex-tui
65
69
  brew audit --strict --online so1omon563/plex-tui/plex-tui
66
70
  ```
67
71
 
72
+ Install-time investigation lives in `docs/homebrew-install-time.md`. Use
73
+ `scripts/measure_homebrew_install.py` to capture no-op upgrade, reinstall,
74
+ fresh-install, and bottle-pour timings before changing the tap formula.
75
+
68
76
  ### Arch AUR
69
77
 
70
78
  AUR package:
@@ -95,8 +103,8 @@ Validation is handled by `.github/workflows/aur.yml`, which runs inside
95
103
  For each new app release:
96
104
 
97
105
  1. Publish and validate PyPI.
98
- 2. Let the `Post-release Homebrew Publish` workflow update, validate, and merge
99
- the Homebrew tap formula.
106
+ 2. Let the `Post-release Homebrew Publish` workflow update, bottle, validate,
107
+ and merge the Homebrew tap formula.
100
108
  3. Let the `Post-release AUR Update` workflow update AUR metadata, open the
101
109
  packaging PR, approve it, and enable auto-merge.
102
110
  4. After the packaging PR merges and the `AUR Package` workflow passes on
@@ -111,9 +119,11 @@ The AUR automation requires:
111
119
  `ssh://aur@aur.archlinux.org/plex-tui.git`.
112
120
 
113
121
  The Homebrew automation uses `PACKAGING_PR_TOKEN`; that token must also be able
114
- to push branches and merge pull requests in `so1omon563/homebrew-plex-tui`.
122
+ to create or update GitHub Releases, upload bottle assets, push branches, and
123
+ merge pull requests in `so1omon563/homebrew-plex-tui`.
115
124
 
116
125
  ## Known Follow-Up
117
126
 
118
- - Investigate faster Homebrew installs without compromising formula quality.
127
+ - Continue validating Homebrew bottle publishing on release dry-runs and
128
+ follow-up packaging PRs.
119
129
  - Consider standalone artifacts only after the app behavior stabilizes.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plex-tui
3
- Version: 0.4.2
3
+ Version: 0.4.4
4
4
  Summary: A small standalone Plex terminal UI.
5
5
  Project-URL: Homepage, https://github.com/so1omon563/plex-tui
6
6
  Project-URL: Issues, https://github.com/so1omon563/plex-tui/issues
@@ -108,9 +108,11 @@ brew install plex-tui
108
108
  plex-tui --smoke
109
109
  ```
110
110
 
111
- The Homebrew formula installs `mpv` automatically. The first install can take
112
- several minutes because native Python dependencies such as `pillow` are built
113
- from source.
111
+ The Homebrew formula installs `mpv` automatically. Apple Silicon macOS installs
112
+ use prebuilt bottles when available so Homebrew can pour the app virtualenv
113
+ instead of rebuilding native Python dependencies such as `pillow` from source.
114
+ Intel macOS installs still use Homebrew's source-build path while that platform
115
+ continues to be supported.
114
116
  Homebrew 6 requires non-official taps to be trusted before Homebrew loads
115
117
  formulae from them. `plex-tui` only depends on formulae from Homebrew/core, so no
116
118
  additional tap trust is required for its dependencies.
@@ -204,7 +206,15 @@ preferences are applied when matching streams are available, and the details
204
206
  pane shows the effective playback choices. Playback mode defaults to
205
207
  direct/default behavior and can be changed in Settings to force Plex
206
208
  transcoding with Original, 1080p 8 Mbps, 720p 4 Mbps, or 480p 2 Mbps quality
207
- presets.
209
+ presets. The default `mpv` launch uses `--autofit=80%` so videos open at a
210
+ comfortable size on modern displays; Settings can override this with values
211
+ such as `90%`, `1280x720`, or `80%x80%`. If an older config has an exact
212
+ `mpv_window_size = "1280x720"` override, cycle the mpv window-size setting once
213
+ to return to the Default preset.
214
+ Use `w` from a playable movie or episode to toggle its Plex watched state.
215
+ When playback is active, choosing an audio or subtitle track from the picker
216
+ also asks mpv to switch the active track when the launched stream exposes a
217
+ matching track.
208
218
 
209
219
  ## Key Bindings
210
220
 
@@ -218,6 +228,7 @@ presets.
218
228
  | `tab` / `shift+tab` | Move focus |
219
229
  | `l` | Focus libraries |
220
230
  | `m` | Focus media |
231
+ | `space` | Run the alternate action for a selected library |
221
232
  | `v` | Toggle list/grid view |
222
233
  | `pageup` / `pagedown` | Move one page in grid view |
223
234
  | `,` | Show settings |
@@ -225,6 +236,8 @@ presets.
225
236
  | `enter` | Open selected item |
226
237
  | `p` | Play selected item from the beginning |
227
238
  | `r` | Resume selected item from the saved Plex position |
239
+ | `w` | Mark selected item watched / unwatched |
240
+ | `backspace` / `delete` | Remove selected item from Continue Watching |
228
241
  | `a` / `s` | Choose audio / subtitle preference |
229
242
  | `A` / `S` | Clear audio preference / cycle subtitle mode |
230
243
  | `x` | Stop launched `mpv` |
@@ -233,14 +246,23 @@ presets.
233
246
 
234
247
  - Plex PIN login and server selection.
235
248
  - Paged library browsing with automatic loading near the end of loaded items.
249
+ - Library submenu entrypoints for all items, Recommended, Collections,
250
+ Playlists, and Categories. Library rows open all items by default; Space
251
+ opens the browse-mode menu, and Settings can swap the primary and alternate
252
+ actions.
236
253
  - Current-library search and bounded global search.
237
254
  - List view plus configurable-density grid view with terminal poster artwork.
238
255
  - External subtitle support and direct playback for embedded PGS/VOBSUB tracks.
239
256
  - Audio and subtitle pickers with saved language preferences.
240
257
  - Separate play-from-start and resume actions with Plex progress reporting.
258
+ - Watched/unwatched toggling for selected playable Plex items.
259
+ - Continue Watching episode rows show their show, season, and episode context,
260
+ and items can be removed from the Continue Watching view.
261
+ - Movie editions appear as distinct variants when Plex reports multiple
262
+ editions for a selected movie.
241
263
  - Settings screen for stream preferences, playback mode and transcode quality,
242
264
  artwork modes, grid density, page size, auto-load threshold, grid artwork
243
- prefetching, media view, and `mpv` window size.
265
+ prefetching, media view, library Enter behavior, and `mpv` window size.
244
266
  - App diagnostics view for version, paths, `mpv`, Plex connection, artwork, and
245
267
  browsing settings.
246
268
 
@@ -71,9 +71,11 @@ brew install plex-tui
71
71
  plex-tui --smoke
72
72
  ```
73
73
 
74
- The Homebrew formula installs `mpv` automatically. The first install can take
75
- several minutes because native Python dependencies such as `pillow` are built
76
- from source.
74
+ The Homebrew formula installs `mpv` automatically. Apple Silicon macOS installs
75
+ use prebuilt bottles when available so Homebrew can pour the app virtualenv
76
+ instead of rebuilding native Python dependencies such as `pillow` from source.
77
+ Intel macOS installs still use Homebrew's source-build path while that platform
78
+ continues to be supported.
77
79
  Homebrew 6 requires non-official taps to be trusted before Homebrew loads
78
80
  formulae from them. `plex-tui` only depends on formulae from Homebrew/core, so no
79
81
  additional tap trust is required for its dependencies.
@@ -167,7 +169,15 @@ preferences are applied when matching streams are available, and the details
167
169
  pane shows the effective playback choices. Playback mode defaults to
168
170
  direct/default behavior and can be changed in Settings to force Plex
169
171
  transcoding with Original, 1080p 8 Mbps, 720p 4 Mbps, or 480p 2 Mbps quality
170
- presets.
172
+ presets. The default `mpv` launch uses `--autofit=80%` so videos open at a
173
+ comfortable size on modern displays; Settings can override this with values
174
+ such as `90%`, `1280x720`, or `80%x80%`. If an older config has an exact
175
+ `mpv_window_size = "1280x720"` override, cycle the mpv window-size setting once
176
+ to return to the Default preset.
177
+ Use `w` from a playable movie or episode to toggle its Plex watched state.
178
+ When playback is active, choosing an audio or subtitle track from the picker
179
+ also asks mpv to switch the active track when the launched stream exposes a
180
+ matching track.
171
181
 
172
182
  ## Key Bindings
173
183
 
@@ -181,6 +191,7 @@ presets.
181
191
  | `tab` / `shift+tab` | Move focus |
182
192
  | `l` | Focus libraries |
183
193
  | `m` | Focus media |
194
+ | `space` | Run the alternate action for a selected library |
184
195
  | `v` | Toggle list/grid view |
185
196
  | `pageup` / `pagedown` | Move one page in grid view |
186
197
  | `,` | Show settings |
@@ -188,6 +199,8 @@ presets.
188
199
  | `enter` | Open selected item |
189
200
  | `p` | Play selected item from the beginning |
190
201
  | `r` | Resume selected item from the saved Plex position |
202
+ | `w` | Mark selected item watched / unwatched |
203
+ | `backspace` / `delete` | Remove selected item from Continue Watching |
191
204
  | `a` / `s` | Choose audio / subtitle preference |
192
205
  | `A` / `S` | Clear audio preference / cycle subtitle mode |
193
206
  | `x` | Stop launched `mpv` |
@@ -196,14 +209,23 @@ presets.
196
209
 
197
210
  - Plex PIN login and server selection.
198
211
  - Paged library browsing with automatic loading near the end of loaded items.
212
+ - Library submenu entrypoints for all items, Recommended, Collections,
213
+ Playlists, and Categories. Library rows open all items by default; Space
214
+ opens the browse-mode menu, and Settings can swap the primary and alternate
215
+ actions.
199
216
  - Current-library search and bounded global search.
200
217
  - List view plus configurable-density grid view with terminal poster artwork.
201
218
  - External subtitle support and direct playback for embedded PGS/VOBSUB tracks.
202
219
  - Audio and subtitle pickers with saved language preferences.
203
220
  - Separate play-from-start and resume actions with Plex progress reporting.
221
+ - Watched/unwatched toggling for selected playable Plex items.
222
+ - Continue Watching episode rows show their show, season, and episode context,
223
+ and items can be removed from the Continue Watching view.
224
+ - Movie editions appear as distinct variants when Plex reports multiple
225
+ editions for a selected movie.
204
226
  - Settings screen for stream preferences, playback mode and transcode quality,
205
227
  artwork modes, grid density, page size, auto-load threshold, grid artwork
206
- prefetching, media view, and `mpv` window size.
228
+ prefetching, media view, library Enter behavior, and `mpv` window size.
207
229
  - App diagnostics view for version, paths, `mpv`, Plex connection, artwork, and
208
230
  browsing settings.
209
231
 
@@ -124,9 +124,15 @@ be run manually with a release tag. It updates:
124
124
  - Formula URL and sha256.
125
125
  - Python resource blocks if dependencies changed.
126
126
  - Formula test expectations if version output changed.
127
+ - Homebrew bottle assets and the generated formula `bottle do` block.
127
128
 
128
- It then runs `brew audit --strict --online`, opens a tap PR, and merges it. This
129
- uses `PACKAGING_PR_TOKEN`, which must have access to the tap repository.
129
+ It builds the formula with `brew install --build-bottle`, generates bottle
130
+ metadata with `brew bottle --json`, uploads the bottle tarball to a
131
+ `plex-tui-X.Y.Z` GitHub Release in the tap repository, merges the generated
132
+ `bottle do` block into the formula, runs `brew audit --strict --online`, opens a
133
+ tap PR, and merges it. This uses `PACKAGING_PR_TOKEN`, which must have access to
134
+ create releases, upload assets, push branches, and merge pull requests in the
135
+ tap repository.
130
136
 
131
137
  If the automated workflow cannot run, update and validate manually:
132
138
 
@@ -186,8 +192,9 @@ To verify post-release packaging without publishing a new app release, manually
186
192
  dispatch the post-release workflows with the latest real release tag, for
187
193
  example `v0.3.6`. A healthy idempotent run should:
188
194
 
189
- - update and audit the Homebrew formula, then skip tap PR creation when there is
190
- no diff;
195
+ - update, bottle, and audit the Homebrew formula when bottle metadata is
196
+ missing; then skip tap PR creation on later reruns when the formula and bottle
197
+ block are already current;
191
198
  - regenerate and validate AUR metadata, then skip packaging PR auto-merge when
192
199
  there is no diff.
193
200
 
@@ -32,8 +32,10 @@
32
32
  requires the Python 3.11 and 3.13 checks before merge. Rulesets require PRs
33
33
  with zero approving reviews so automation branches can merge after checks
34
34
  without self-approval.
35
- - Improve Homebrew install time; the current formula works but builds native
36
- Python resources such as `pillow` from source.
35
+ - Improve Homebrew install time; baseline the current install/update paths with
36
+ `scripts/measure_homebrew_install.py`, then prioritize tap bottle automation.
37
+ The current formula works but builds Python resources from source, with
38
+ native compilation concentrated in `pillow`.
37
39
  - Keep post-release package publishing fully automated across PyPI, Homebrew,
38
40
  and AUR. Periodically dry-run existing release tags through the post-release
39
41
  workflows to confirm Homebrew and AUR no-op cleanly when already current.
@@ -45,19 +47,30 @@
45
47
  [`anthonycaccese/240-MP`](https://github.com/anthonycaccese/240-MP/tree/main)
46
48
  live in `docs/plex-integration-research.md`.
47
49
  - Continue Watching now has a sidebar browse entrypoint backed by Plex on-deck
48
- data; watch for real-library edge cases around ordering and pagination.
50
+ data; selected items can be removed from Continue Watching. Watch for
51
+ real-library edge cases around ordering and pagination.
49
52
  - Selective library visibility is available in Settings so noisy Plex libraries
50
53
  can be hidden from the sidebar.
51
- - Library submenus are available for Library, Recommended, Collections, and
52
- Playlists. Revisit Categories after mapping PlexAPI support against a real
53
- server response.
54
+ - Library submenus are available for Library, Recommended, Collections,
55
+ Playlists, and Categories. Categories use PlexAPI category helpers when
56
+ present and genre filter choices as the fallback. Library rows open all items
57
+ by default, Space opens the browse-mode menu, and Settings can swap those
58
+ primary/alternate actions.
54
59
  - Alphabet navigation is available for loaded browse lists and grids with
55
60
  previous/next section jumps.
56
61
  - Explicit playback controls are available for play-from-start versus resume,
57
62
  direct/default playback versus selected transcode quality presets, and
58
63
  Plex-side resume offsets for transcoded streams.
59
- - Later design passes: profile switching with auto sign-in, visible movie
60
- edition handling, and in-playback audio/subtitle switching through mpv IPC.
64
+ - Watched/unwatched toggling is available for selected playable movies and
65
+ episodes.
66
+ - Movie editions are visible as distinct variants when Plex reports multiple
67
+ editions for a selected movie.
68
+ - In-playback audio/subtitle switching uses mpv IPC when the active stream
69
+ exposes matching tracks; Plex-transcoded streams may still require relaunching
70
+ with the saved preference.
71
+ - Design playlist management before implementation: adding/removing media from
72
+ playlists and creating playlists are intentionally out of PR #80 scope.
73
+ - Later design passes: profile switching with auto sign-in.
61
74
 
62
75
  ## Technical Follow-Up
63
76
 
@@ -14,7 +14,11 @@ client_identifier = "plex-tui-your-stable-client-id"
14
14
  # preferred_subtitle_language = "eng"
15
15
  # subtitle_mode can be "auto", "none", or "preferred".
16
16
  # subtitle_mode = "preferred"
17
- # Optional mpv window size passed as --autofit. Examples: "1280x720", "80%".
17
+ # Optional mpv window size passed as --autofit. Empty uses the app default
18
+ # "80%" for a more comfortable window on modern displays.
19
+ # Examples: "90%", "1280x720", "80%x80%".
20
+ # Existing exact-size overrides such as "1280x720" stay exact until reset or
21
+ # changed in Settings.
18
22
  # mpv_window_size = "1280x720"
19
23
  # playback_mode can be "auto" for direct/default playback or "transcode" to
20
24
  # force Plex transcoding.
@@ -48,6 +52,10 @@ client_identifier = "plex-tui-your-stable-client-id"
48
52
  # detail_artwork_mode = "list_only"
49
53
  # media_view can be "list" or "grid".
50
54
  # media_view = "list"
55
+ # library_enter_action can be "library" or "browse_modes". The default
56
+ # "library" opens all items with Enter and uses Space for browse modes.
57
+ # "browse_modes" swaps those two actions.
58
+ # library_enter_action = "library"
51
59
  # grid_density can be "compact", "comfortable", or "large".
52
60
  # grid_density = "comfortable"
53
61
  # Textual theme selected from the command palette.