backpack-backtrack 0.2.0__py3-none-any.whl

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 (69) hide show
  1. backpack_backtrack-0.2.0.dist-info/METADATA +460 -0
  2. backpack_backtrack-0.2.0.dist-info/RECORD +69 -0
  3. backpack_backtrack-0.2.0.dist-info/WHEEL +5 -0
  4. backpack_backtrack-0.2.0.dist-info/entry_points.txt +2 -0
  5. backpack_backtrack-0.2.0.dist-info/licenses/LICENSE +21 -0
  6. backpack_backtrack-0.2.0.dist-info/top_level.txt +1 -0
  7. backtrack/__init__.py +8 -0
  8. backtrack/__main__.py +3 -0
  9. backtrack/album_art.py +173 -0
  10. backtrack/bulk_pattern.py +350 -0
  11. backtrack/cli.py +511 -0
  12. backtrack/cli_commands.py +2242 -0
  13. backtrack/config.py +204 -0
  14. backtrack/deps.py +51 -0
  15. backtrack/feed.py +465 -0
  16. backtrack/history.py +73 -0
  17. backtrack/id3/__init__.py +0 -0
  18. backtrack/id3/browser.py +1167 -0
  19. backtrack/id3/bulk_art.py +374 -0
  20. backtrack/id3/bulk_assign.py +616 -0
  21. backtrack/id3/bulk_common.py +130 -0
  22. backtrack/id3/bulk_menu.py +823 -0
  23. backtrack/id3/bulk_names.py +468 -0
  24. backtrack/id3/bulk_ops.py +670 -0
  25. backtrack/id3/bulk_sort.py +402 -0
  26. backtrack/id3/cover_matcher.py +537 -0
  27. backtrack/id3/file_namer.py +299 -0
  28. backtrack/id3/filename_parser.py +517 -0
  29. backtrack/id3/tag_handler.py +1207 -0
  30. backtrack/id3/tag_registry.py +410 -0
  31. backtrack/id3/tag_writer.py +534 -0
  32. backtrack/lyrics/__init__.py +0 -0
  33. backtrack/lyrics/editor.py +821 -0
  34. backtrack/lyrics/editor_keys.py +678 -0
  35. backtrack/lyrics/editor_view.py +830 -0
  36. backtrack/lyrics/formats.py +1286 -0
  37. backtrack/lyrics/lyric_pane.py +289 -0
  38. backtrack/lyrics/md_overlay.py +390 -0
  39. backtrack/lyrics/sync_doc.py +317 -0
  40. backtrack/lyrics/text.py +521 -0
  41. backtrack/lyrics/time_fields.py +145 -0
  42. backtrack/lyrics/verify.py +372 -0
  43. backtrack/main.py +279 -0
  44. backtrack/menus/__init__.py +36 -0
  45. backtrack/menus/activity.py +79 -0
  46. backtrack/menus/browse.py +471 -0
  47. backtrack/menus/common.py +145 -0
  48. backtrack/menus/history.py +143 -0
  49. backtrack/menus/play.py +272 -0
  50. backtrack/menus/search.py +371 -0
  51. backtrack/menus/settings.py +520 -0
  52. backtrack/menus/sorting.py +199 -0
  53. backtrack/music_library.py +1215 -0
  54. backtrack/playback/__init__.py +0 -0
  55. backtrack/playback/ipc.py +387 -0
  56. backtrack/playback/libvlc.py +13 -0
  57. backtrack/playback/now_playing_box.py +156 -0
  58. backtrack/playback/player.py +744 -0
  59. backtrack/playback/player_art.py +330 -0
  60. backtrack/playback/player_geom.py +48 -0
  61. backtrack/playback/player_ui.py +1039 -0
  62. backtrack/playback/queue_pane.py +307 -0
  63. backtrack/playback/session.py +1096 -0
  64. backtrack/search.py +627 -0
  65. backtrack/trim/__init__.py +0 -0
  66. backtrack/trim/bulk.py +700 -0
  67. backtrack/trim/editor.py +847 -0
  68. backtrack/trim/engine.py +839 -0
  69. backtrack/tuning.py +134 -0
@@ -0,0 +1,460 @@
1
+ Metadata-Version: 2.4
2
+ Name: backpack-backtrack
3
+ Version: 0.2.0
4
+ Summary: Terminal music player with VLC playback, lyrics sync, and metadata editing.
5
+ License: MIT
6
+ Project-URL: Homepage, https://github.com/RedEraRrow/backtrack
7
+ Project-URL: Issues, https://github.com/RedEraRrow/backtrack/issues
8
+ Classifier: Environment :: Console :: Curses
9
+ Classifier: Topic :: Multimedia :: Sound/Audio :: Players
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: MacOS
12
+ Classifier: Operating System :: POSIX :: Linux
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3 :: Only
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: Pillow>=10.0
19
+ Requires-Dist: mutagen>=1.47.0
20
+ Requires-Dist: numpy>=1.24
21
+ Requires-Dist: pyperclip>=1.11.0
22
+ Requires-Dist: python-vlc>=3.0.21203
23
+ Requires-Dist: colorama>=0.4.6
24
+ Requires-Dist: backpack-backbone<0.3,>=0.2
25
+ Dynamic: license-file
26
+
27
+ # Backtrack
28
+
29
+ A terminal music player and tag editor for macOS and Linux. Backtrack plays your library with
30
+ VLC, draws album art as Unicode half-blocks in the terminal, shows synced and unsynced lyrics,
31
+ and has an ID3/MP4 tag editor with bulk operations for whole albums.
32
+
33
+ > **Status:** in active development. Core playback, browsing, search, lyrics, and the tag editor
34
+ > (including bulk operations) are working; expect rough edges and changing internals.
35
+
36
+ ---
37
+
38
+ ## Features
39
+
40
+ **Library & browsing**
41
+ - Browse by **artist**, **album**, **genre** and more, with an A-Z letter index for large collections.
42
+ - Track rows show a featured-artist marker and cached durations, with disc and work separators.
43
+ - A background sync keeps the library fresh: it re-scans on an interval and reconciles against the
44
+ filesystem, picking up adds, deletes, renames and moves made outside the app.
45
+
46
+ **Search**
47
+ - Fuzzy live search that re-ranks on every keystroke and highlights the matched characters.
48
+
49
+ **Playback**
50
+ - VLC/libvlc-backed audio with transport controls and a live progress bar.
51
+ - Album art drawn in the terminal as half-blocks, or as a real image in iTerm2 (opt-in).
52
+ - A full-height **volume bar** beside the art, and a side panel for **lyrics**, the up-next
53
+ **queue**, and cast/crew **credits**.
54
+ - **Equaliser**: 24 presets applied during playback via libvlc, stored per file as an `EQU2` tag.
55
+ - Several terminal windows can share one session (see [Several windows](#several-windows)).
56
+
57
+ **Lyrics**
58
+ - Shows **synced (`SYLT`)** and **unsynced (`USLT`)** lyrics, and markdown dialogue scripts for
59
+ spoken-word tracks. A lyric editor times un-timed lyrics as the track plays.
60
+
61
+ **Tag editing (MP3)**
62
+ - Edit every ID3 frame through a widget suited to its type: date/time with a **world-map timezone
63
+ picker**, track/disc **fractions**, **people/credit lists**, a **star-rating** editor (`POPM`),
64
+ a **graphic equaliser** (`EQU2`) and **dB gain** meter (`RVA2`), **numeric spinners**, and
65
+ enum/bool pickers (musical key, media type, a validated ISRC field, a compilation toggle).
66
+ - **Multi-value** frames (artists, composers, genres…), automatic **sort-order** generation, and a
67
+ **plain-text** mode.
68
+
69
+ **Bulk operations** (with a preview before anything is written)
70
+ - Tag add / set / rename / delete across a selection, plus automations: derive tags from file
71
+ names, rename files from tags, set album art, assign by range or schedule, sort orders,
72
+ renumbering and more (see [Metadata editing](#metadata-editing)).
73
+ - Track/disc **number pairs** edit in bulk without collateral damage: whichever half the files
74
+ already share is editable, and the half that differs shows as a greyed `──` and is left alone.
75
+
76
+ **Trimming** (needs ffmpeg)
77
+ - Cut the start or end off an MP3 losslessly, one track at a time (`t` in the tag editor) or
78
+ across an album (bulk *Trim tracks…*). The original is backed up and a trim can be undone.
79
+ - Measure loudness and set ReplayGain across a selection.
80
+
81
+ **History & settings**
82
+ - Listening history with relative timestamps, and a sectioned settings screen.
83
+
84
+ ---
85
+
86
+ ## Installation
87
+
88
+ macOS:
89
+ ```bash
90
+ brew install --cask vlc
91
+ pipx install backpack-backtrack
92
+ backtrack
93
+ ```
94
+
95
+ Ubuntu / Debian:
96
+ ```bash
97
+ sudo apt install vlc pipx
98
+ pipx install backpack-backtrack
99
+ backtrack
100
+ ```
101
+
102
+ That's all it needs: Python 3.10 or later and VLC. The first run asks for your music folder.
103
+ `backtrack doctor` lists what it uses and how to install anything missing, including the optional
104
+ tools: **ffmpeg** for trimming and ReplayGain (without it those options are hidden), and on Linux
105
+ **wl-clipboard** or **xclip** for copying paths and tags. (`pipx` keeps it in its own environment;
106
+ `pip install backpack-backtrack` works too.)
107
+
108
+ ### From a checkout, to work on it
109
+
110
+ Clone backbone beside backtrack and install both editable, backbone first, so edits to either take
111
+ effect with no reinstall:
112
+ ```bash
113
+ python3 -m pip install -e ../backbone -e .
114
+ ```
115
+
116
+ ## Running
117
+
118
+ ```bash
119
+ backtrack
120
+ ```
121
+ or, without the command on your PATH, `python3 -m backtrack` from the checkout.
122
+
123
+ On first run, Backtrack asks for a music directory and builds a cached library for faster
124
+ startups after that.
125
+
126
+ **With arguments, `backtrack` is an ordinary command-line tool** instead: see
127
+ [Command line](#command-line) below. Everything the menus can do is reachable from there.
128
+
129
+ ---
130
+
131
+ ## Usage
132
+
133
+ ### Main menu
134
+
135
+ Browse · Search · Listening History · Settings · Exit.
136
+
137
+ Navigation is the same everywhere: `↑↓` move, `→`/`Enter` confirm, `←`/`b`/`Esc` go back, and
138
+ `q` quits the app from anywhere (it never just closes a widget). In a field you type into, `q` is
139
+ typed as a letter; in the live search, Ctrl-C quits instead. Lists never wrap, keep the cursor on
140
+ the same item after a re-sort or an edit, restore it when you back out, and support mouse clicks;
141
+ `a` selects all in a multi-select list. A list in sections (Settings, Key bindings) that is much
142
+ taller than the window opens as its section titles: `Enter` opens one, `Esc` goes back to the
143
+ titles, and `/` switches to the whole list and back. The hint bar (pinned to the bottom of the screen) is
144
+ clickable too: click any highlighted key to trigger it. When audio is playing, the mini-player's
145
+ ⏯/⏭ icons are clickable, and clicking anywhere else on it reopens the player. In the full player,
146
+ the ⏮/⏯/⏭ controls, the hint bar, and the vertical volume bar are all clickable (click the volume
147
+ bar at the height you want).
148
+
149
+ If a file has been moved or renamed since the library was scanned, Backtrack notices when you act
150
+ on it, re-syncs the library and says so. If a whole music folder has moved, the message asks you to
151
+ update it in Settings → Music directories.
152
+
153
+ ### Browse
154
+
155
+ Explore by **Artist**, **Album**, **Genre** and more (composer, lyricist, people, year, decade,
156
+ grouping, work: choose which appear, and their order, in Settings → Browse menu), across
157
+ everything or within one music directory (Browse → Libraries, each under the name you give it in
158
+ Settings → Music directories). Drilling into a letter in the A-Z index and backing out returns you
159
+ to the index. Play all (`p`), shuffle (`x`), album shuffle (`X`) and edit all (`E`) are in the hint
160
+ bar at the bottom of each list; `e` edits just the highlighted row. `n` plays the highlighted row
161
+ next and `a` adds it to the queue (with nothing playing, either starts it). `o` lists everything
162
+ you can do with the highlighted row, each with its key: for a track, play it, edit its tags, play
163
+ from here, play next, play after the album that's playing, add it to the queue (shuffled or not),
164
+ add its whole album; for an album, artist or other group, the same for all its tracks. `O` lists
165
+ the same for the whole list: play, shuffle, edit, sort, and play next or queue everything listed.
166
+ Any of these can be given its own key in Settings → Key bindings. Shuffling, clearing and undoing
167
+ the queue itself are in the player's queue panel.
168
+
169
+ Albums and tracks follow one sort order, set with `s` in any list or in Settings → Sorting: a
170
+ chain of levels (album, album year, disc, track, title, date…), each ascending or descending, with
171
+ presets such as broadcast order. A music directory can have its own. Selecting a track plays it;
172
+ what follows is Settings → *After a picked track*: nothing, the rest of the list it was picked from
173
+ (the default), or the queue that was already playing. The queue is kept between runs: opening backtrack
174
+ again offers to **Resume** it at the track and moment you left, or start fresh.
175
+
176
+ ### Search
177
+
178
+ Fuzzy search across title, artist, album, composer, lyricist, genre and people, and by disc
179
+ ("disc 2"). Type to filter; results re-rank live with the matched characters highlighted. `^f`
180
+ cycles the scope (all / title / artist / album / composer / lyricist / genre / people) and `Tab`
181
+ jumps between result sections; `^e` edits the highlighted track and `^a` every result; `^k` lists
182
+ everything you can do with a result (as `o` does in a list); `Enter` opens a result or plays a track (what follows it is the
183
+ After a picked track setting); `Esc` backs out.
184
+
185
+ ### Playback controls
186
+
187
+ These are the default keys. Every key in the app can be changed in Settings → Key bindings (screen
188
+ by screen, with more than one key per action if you like), and every hint bar shows the keys you set.
189
+
190
+ | Key | Action |
191
+ |-----|--------|
192
+ | `space` / `p` | Play / pause |
193
+ | `←` / `→` | Seek ∓5 s |
194
+ | `j` / `l` | Seek ∓1 s |
195
+ | `,` / `.` | Seek ∓30 s |
196
+ | `+` / `-` | Volume up / down |
197
+ | `m` | Show or hide the track details line (year · genre · disc/track …); remembered. Also Settings → Track details in player |
198
+ | `w` | Cycle the side panel: off → lyrics → queue → lyrics+credits. Views with nothing in them are skipped |
199
+ | `↑` / `↓` | With the queue panel showing: a cursor through the queue. `↵` plays the track at it, `J` / `K` move it up / down, `d` removes it, `x` shuffles what's coming, `c` clears what's coming, `u` undoes the last queue change |
200
+ | `?` | Show or hide the key hints, on every screen: they start hidden. Each screen's top line ends in `[?] help`; in a text field, where `?` is typed, it says `[^/] help` and the key is Ctrl+/ (which works everywhere). Clicking the key in the corner works too. In the player it stays in the hint bar. Also Settings → Key hints |
201
+ | `[` / `]` | Previous / next track |
202
+ | `e` | Jump to the last 35 s (only with Settings → Diagnostics log on) |
203
+ | `b` / `Esc` | Minimise: leave the player but keep the audio playing in the background (pinned while another window is attached) |
204
+ | `s` | Stop playback |
205
+ | `q` | Quit the application |
206
+
207
+ From any menu while audio is playing: **Ctrl-O** reopens the player, and **Ctrl-P** / **Ctrl-N** /
208
+ **Ctrl-B** control play-pause / next / previous.
209
+
210
+ ### Several windows
211
+
212
+ Start a second `backtrack` while one is playing and it offers **Start a new session** (this window
213
+ plays its own audio) or **Join** the running one. A joined window browses and queues as normal and
214
+ controls the host's audio; its player shows what the host is playing. Only one window has the
215
+ player open at a time, and while another window is attached, `b` won't leave the player.
216
+
217
+ ### Listening history
218
+
219
+ Recent tracks in aligned columns (title · artist · album · when · listened), with relative times
220
+ (`just now`, `40m ago`, `2w ago`). Replay any entry.
221
+
222
+ ### Lyrics
223
+
224
+ Tracks with `SYLT`/`USLT` lyrics, or a transcript and markdown script, show them during playback.
225
+ To time or fix them, open the track's tag editor and choose the **Lyrics** row, which appears when
226
+ the track has lyrics or a transcript (and Settings → Lyrics editor is on). The lyric editor taps in
227
+ timings as the track plays, adds Music by / Words by credits (`c`), and for spoken-word tracks
228
+ checks the script against the transcript (`V`). An `.lrc` file can be imported from a `SYLT` or
229
+ `USLT` tag's actions. For writing dialogue scripts, see
230
+ [script etiquette](docs/script-etiquette.md).
231
+
232
+ ### Metadata editing
233
+
234
+ From a track, choose **Edit tags** to open the single-track editor; from **Browse**, choose
235
+ *Edit tags* on an album (or press `e`) for the **bulk** editor. The bulk editor has the tag
236
+ operations (add, set, rename, delete) and an **Automation…** menu:
237
+
238
+ - **Derive from filename**: fill tags from file and folder names.
239
+ - **Rename files from tags**: the inverse, collision-safe.
240
+ - **Set album art from files**: embed per-track or per-disc/series covers found beside the tracks.
241
+ - **Assign by range / schedule**: including a per-range schedule where each disc/series carries
242
+ its own start date and cadence, entered in a split date/time cell where you type only the digits.
243
+ - **Apply sort orders**.
244
+ - **Renumber tracks** (disc ↔ continuous).
245
+ - **Reflow disc numbering**: renumber discs to a dense 1…N after inserting a `1.5`, deleting a
246
+ disc, or appending one, and fix the totals.
247
+ - **Remove single-disc numbering**: drop `1/1` disc numbers.
248
+ - **Strip stale length tags**: remove stale `TLEN` and non-zero `TDLY` frames.
249
+ - **Trim tracks…** and **Measure loudness / set ReplayGain…** (need ffmpeg).
250
+ - **Set picture type**: retype embedded art (for example to front cover) without touching the image.
251
+ - **Copy from first track**.
252
+
253
+ Every operation previews its changes and, by default, only fills blank tags. In the tidy-up
254
+ previews (renumber, reflow, remove single-disc numbering, strip length tags, set picture type),
255
+ rows that wouldn't change are greyed out.
256
+
257
+ Disc and track numbering is read from the files themselves rather than the library cache, so
258
+ renumbering and reflowing stay correct even right after you have hand-numbered a disc.
259
+
260
+ The guides under [Documentation](#documentation) cover tagging practice and what *Derive from
261
+ filename* recognises.
262
+
263
+ ---
264
+
265
+ ## Command line
266
+
267
+ `backtrack` with no arguments opens the app. With arguments it is a normal CLI: noun, then
268
+ verb.
269
+
270
+ ```bash
271
+ backtrack library scan # rebuild the cache
272
+ backtrack track list --artist "Duran Duran"
273
+ backtrack search "hungry wolf" -n 5
274
+ backtrack tag read track.mp3 --tag TIT2
275
+ backtrack bulk stripdisc --album Rio
276
+ backtrack play --album Rio --repeat all
277
+ backtrack feed sync --name comedy
278
+ ```
279
+
280
+ `backtrack --help` lists the groups; `backtrack <group> <verb> --help` documents one command
281
+ and shows a worked example. `backtrack schema` prints the whole tree (commands, flags, output
282
+ shapes and exit codes) as JSON. The parser, the schema and the shell completions are all generated
283
+ from the same command definitions, so they always agree.
284
+
285
+ ### Command groups
286
+
287
+ | Group | Verbs |
288
+ |---|---|
289
+ | `library` | `scan` `list` `stat` `verify` `dirs` |
290
+ | `track` | `list` `show` |
291
+ | `tag` | `read` `write` `rename` `delete` `copy` |
292
+ | `bulk` | `derive` `rename` `art` `pictype` `renumber` `reflow` `stripdisc` `striplength` `sortorders` `assign` |
293
+ | `play` | (takes tracks or a filter) |
294
+ | `queue` | `show` `add` `next` |
295
+ | `session` | `list` `status` `pause` `next` `prev` `stop` `seek` `volume` |
296
+ | `lyrics` | `show` `import` `export` `verify` |
297
+ | `trim` | `detect` `cut` `list` `restore` |
298
+ | `feed` | `add` `list` `remove` `sync` `fetch` |
299
+ | `history` | `list` `clear` |
300
+ | `config` | `list` `get` `set` |
301
+ | `search`, `schema`, `completion` | |
302
+
303
+ Three things stay in the app, because they are "mark this by ear while it plays" and need a
304
+ person: **lyric tap-sync and audition**, the **trim marking screen**, and the **rendered
305
+ player view**. Their non-interactive halves all have commands: `lyrics import/export`,
306
+ `trim detect`, `trim cut --start --end`, and the `session` transport.
307
+
308
+ ### Global flags
309
+
310
+ | Flag | What it does |
311
+ |---|---|
312
+ | `--json` | Structured output; NDJSON, one event per line, for long operations |
313
+ | `-y`, `--yes` | Accept every confirmation; never prompt |
314
+ | `--dry-run` | Print the plan in the same event shape a real run emits, change nothing |
315
+ | `-L`, `--library DIR` | Work in this music directory (repeatable) |
316
+ | `-o`, `--output DIR` | Where files this command writes should go |
317
+ | `-q`, `--quiet` | No human output; the exit code still reports |
318
+ | `--no-colour` | Never colour the output |
319
+
320
+ They work on either side of the verb: `backtrack --json library list` and
321
+ `backtrack library list --json` are the same.
322
+
323
+ ### Output
324
+
325
+ Human by default: aligned columns, and colour **only** when stdout is a terminal. `NO_COLOR`
326
+ is honoured.
327
+
328
+ When stdout is **not** a terminal, a list command prints one path per line instead of a
329
+ table, so commands compose without a flag:
330
+
331
+ ```bash
332
+ backtrack track list --artist Darude | backtrack tag read
333
+ backtrack search wolf | backtrack bulk stripdisc
334
+ ```
335
+
336
+ `--json` overrides both. Every JSON object carries a `schema` version. Long operations emit
337
+ one event per line, flushed as it happens, so `backtrack bulk derive --json | jq` reports
338
+ each file as it is written rather than everything at the end.
339
+
340
+ Errors go to stderr; under `--json` they are `{"error": {"code", "message", "context"}}`.
341
+
342
+ ### Exit codes
343
+
344
+ | Code | Meaning |
345
+ |---|---|
346
+ | 0 | It worked |
347
+ | 1 | It didn't, for a reason with no more specific code |
348
+ | 2 | The arguments were wrong |
349
+ | 3 | The thing asked for isn't there |
350
+ | 4 | The thing asked for is there already |
351
+ | 5 | A required external tool (ffmpeg, VLC) is missing |
352
+
353
+ ### Nothing blocks
354
+
355
+ `--yes` accepts every confirmation. When stdin is not a terminal, a confirmation takes its
356
+ default rather than waiting, so an agent with no human attached never hangs on a read that
357
+ will never come. `--dry-run` works on every command that writes.
358
+
359
+ ### Shell completion
360
+
361
+ ```bash
362
+ backtrack completion zsh > ~/.zfunc/_backtrack
363
+ backtrack completion bash > /usr/local/etc/bash_completion.d/backtrack
364
+ backtrack completion fish > ~/.config/fish/completions/backtrack.fish
365
+ ```
366
+
367
+ ### Defaults in the config
368
+
369
+ A flag beats the config file; the config file beats the value compiled in. The keys are
370
+ `cli_output_dir`, `cli_rename_pattern`, `cli_art_strategy`, `cli_search_limit`,
371
+ `cli_history_limit` and `trim_scan_window_s`, plus `music_directories` for `--library`. A
372
+ falsy value means "no preference".
373
+
374
+ ```bash
375
+ backtrack config set cli_rename_pattern "%artist% - %track% - %title%"
376
+ backtrack bulk rename --album Rio # uses it
377
+ ```
378
+
379
+ ### Podcast feeds
380
+
381
+ ```bash
382
+ backtrack feed add https://example.com/rss --name comedy --filter-title "News Quiz"
383
+ backtrack feed sync --name comedy --output ~/Music/Podcasts
384
+ ```
385
+
386
+ `sync` downloads what is new, dedupes on GUID (falling back to the enclosure URL), and tags
387
+ each episode from its title (show, series, episode, title and date), keeping the raw title
388
+ verbatim in a comment. Re-running it downloads nothing and duplicates nothing.
389
+
390
+ `pubDate` is usually an upload time rather than a broadcast date, so a date found in the
391
+ title wins; where the title gives a day and month but no year, the year comes from `pubDate`
392
+ and each episode records which happened.
393
+
394
+ Downloads enter the library the way any other new file does: written into a music
395
+ directory and handed to the same refresh the app uses.
396
+
397
+ ---
398
+
399
+ ## Documentation
400
+
401
+ - **[Tag etiquette](docs/tag-etiquette.md)**: good ID3 practice, and how Backtrack reads each tag.
402
+ - **[Filesystem etiquette](docs/filesystem-etiquette.md)**: how to organise a library on disk.
403
+ - **[Library layout & naming](docs/library-layout.md)**: the exact folder/name patterns the
404
+ *Derive from filename* parser recognises, including template and regex overrides.
405
+ - **[Script etiquette](docs/script-etiquette.md)**: writing a dialogue script a transcript can be
406
+ matched to.
407
+ - **[Developer notes](docs/DEVELOPER.md)**: internals and architecture.
408
+
409
+ ---
410
+
411
+ ## Configuration
412
+
413
+ Settings are managed in-app under **Settings**, in seven sections:
414
+
415
+ - **Playback**: lyric lead-in, after a picked track, key hints, image album art (iTerm2), track
416
+ details in player.
417
+ - **Appearance**: accent colour (colours from your terminal's palette, fixed colours, or a custom
418
+ hex value).
419
+ - **Library**: music directories, the activity centre, the hidden file filter, the Browse menu.
420
+ - **Sorting**: the sort order, whether to use sort-order tags, and ignored leading words.
421
+ - **Editors**: metadata editor, lyrics editor, plain-text editing, tag name preferences, sort
422
+ list delimiter.
423
+ - **Diagnostics**: the diagnostics log, which writes `~/.config/backtrack/backtrack.log`.
424
+ - **History**: listening history on or off, and clearing the log.
425
+
426
+ They are stored in a JSON config created on first run. `music_directories` is a list (add or remove
427
+ them under Settings → Music directories; the older single `music_directory` key is migrated
428
+ automatically and kept in step with the first entry), and `volume` is restored at launch and saved
429
+ whenever you change it. Prefer the Settings screen or `backtrack config set` over hand-editing the
430
+ file.
431
+
432
+ ## Supported formats
433
+
434
+ - **Audio:** MP3, M4A, MP4, M4P and AAC. Raw `.aac` plays but can't be tagged.
435
+ - **Tags:** ID3v2 (MP3) and MP4 atoms (`.m4a`/`.mp4`/`.m4p`). The single-track editor is MP3 only.
436
+ In bulk, derive, rename files, album art, renumber, reflow and remove single-disc numbering
437
+ write both MP3 and MP4; the tag operations, assign, sort orders and the rest are MP3 only.
438
+ - **Lyrics:** `USLT` (unsynced) and `SYLT` (synced).
439
+ - **Album art:** embedded MP3 `APIC` and MP4 `covr` (JPEG/PNG), drawn in the terminal as half-blocks.
440
+
441
+ ## Troubleshooting
442
+
443
+ **Playback fails**: check that VLC / libvlc is installed, the file is a supported format, and the
444
+ terminal can read your music directory.
445
+
446
+ **Album art doesn't render**: check the file actually has embedded art; very narrow terminals
447
+ shrink or omit the art.
448
+
449
+ **Lyrics don't appear**: not all files have embedded lyrics. Import an `.lrc` from the tag editor,
450
+ or time existing lyrics in the lyric editor.
451
+
452
+ **Tag editing says "MP3 only"**: the single-track editor edits ID3/MP3; use the bulk Automation
453
+ tools for MP4 tag changes.
454
+
455
+ **Something else went wrong**: turn on Settings → Diagnostics log, repeat what you did, and look
456
+ in `~/.config/backtrack/backtrack.log`.
457
+
458
+ ## License
459
+
460
+ MIT.
@@ -0,0 +1,69 @@
1
+ backpack_backtrack-0.2.0.dist-info/licenses/LICENSE,sha256=GYuXCU6db-Xi7yUsUcRXZgYcfoe-Ss7NRcwhkZtWbhc,1067
2
+ backtrack/__init__.py,sha256=-bSHrHE9uzds5R3b7sQ_OT1hG13agc7tmCzKAecjyIE,203
3
+ backtrack/__main__.py,sha256=utrVKxoVQV9sM90-9l7G-UAQiJJh4_gNNk_3A0IxRio,58
4
+ backtrack/album_art.py,sha256=p4yYTB_MvxdpcqfUrH2QJBqaUiDmuly9NOEjlwMxd0c,6940
5
+ backtrack/bulk_pattern.py,sha256=rMIk2bAa38tX4LmVroNlmkWdWGtB2JRXMaA4dioaNRY,14370
6
+ backtrack/cli.py,sha256=Y1JD47tfbkZpEeEgFD4pXaV01ZyLpn0Ytq_RBbTbwJM,20746
7
+ backtrack/cli_commands.py,sha256=6ygAp5qBwCrvxvWlVvC2CJul8HO57nU9x_1Y1X52oFI,94097
8
+ backtrack/config.py,sha256=lBptZJc9d62WtDmGJSVPl4Gz2SoIx2Lgne1ttYhRIyI,8815
9
+ backtrack/deps.py,sha256=DKgmyIUlTJstFAdnpdLsJszOnMRc2y8wUcUPvgmLiEY,1892
10
+ backtrack/feed.py,sha256=xRVBT8OJB8jUTiJMc0f5tDvzFLiMDdA8Z5vGtg5NDyM,17618
11
+ backtrack/history.py,sha256=24imNM_Pf4EQON6xdLKJbONL7pp4W22XNY2dzRF-M-M,2258
12
+ backtrack/main.py,sha256=lW9xwYdejdxT5nxPV1ACv30yhaBKeH_YaEENLwqcCK4,10939
13
+ backtrack/music_library.py,sha256=a2U8qbY38v-_gWHOxE1A7dr8RxGw7LCzoqsu6xFpPFE,47830
14
+ backtrack/search.py,sha256=AzYFNUsRFOLVwTYZHtqKypNeVVAZq56uJW-sPmnf6zA,25570
15
+ backtrack/tuning.py,sha256=qsAFEwcwoeECnburkbmn3shqXyFcXAt455um2DVqa0k,6559
16
+ backtrack/id3/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
+ backtrack/id3/browser.py,sha256=XyNuuxkNUJEYjSW038OFOfqmfqmpK-oMtCRDhcRYDJ0,51012
18
+ backtrack/id3/bulk_art.py,sha256=kRu0iUtOVo_qwD9iR_iMilv1S-rIjUr4GMgu7b1kKRg,17037
19
+ backtrack/id3/bulk_assign.py,sha256=S-l7eYL_FS6RfqHAP_g_2ZdU0DuOB6myO3iaQBmsYxU,26475
20
+ backtrack/id3/bulk_common.py,sha256=eT261LmVrAT0C3mpzT1zyhQKDeADOfBVPE3PqkQMi-c,5321
21
+ backtrack/id3/bulk_menu.py,sha256=okLlNC6aiiwHp7XSu6RdLbd8UThc_ORXhYwBderS-8k,36172
22
+ backtrack/id3/bulk_names.py,sha256=N0_j-TpYKZ9WCzxOnRnhO0Cp9ShuXXeECnf9WgyyVFc,21896
23
+ backtrack/id3/bulk_ops.py,sha256=r5R-Q6IqwzGE0r1OP0empWVcJvN0YpQxOYaBxw1VFcE,26597
24
+ backtrack/id3/bulk_sort.py,sha256=q4g6ynBzkkOCrrcQT_6YBXGTpyusAmroK3OFU39ZUcE,17737
25
+ backtrack/id3/cover_matcher.py,sha256=CjcLIqw2r9H4PffRy8BV8EV085X6e5sg8moY365Mrbs,21478
26
+ backtrack/id3/file_namer.py,sha256=Tvjf6uaBk1iey5yaH6Ukhnx3j3VLW1SrTtyVbuUixPM,11314
27
+ backtrack/id3/filename_parser.py,sha256=EyeKyD4l72yxlpwUiWyZEfx8K0FR6Y31F2o4i-4METY,21534
28
+ backtrack/id3/tag_handler.py,sha256=JPH_c5Yv7AJFo1uFESMpyz3-eiXmsjksZb1CPXxIg-I,50341
29
+ backtrack/id3/tag_registry.py,sha256=OJfaiSCAsfGY5TH_PefOPZSzZ8vB0akyPcdxsTweT8M,18811
30
+ backtrack/id3/tag_writer.py,sha256=rg57BqxlsXJvSmuFJu5_Bt9njA9ttT1IixIFqj6z024,21527
31
+ backtrack/lyrics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
+ backtrack/lyrics/editor.py,sha256=B4XyrIUzQNiM8ASqqsPVOrF0j0gqHErafPwCXUrDSRY,42177
33
+ backtrack/lyrics/editor_keys.py,sha256=fN5v0zPJmAhxibQqqcbQwm-ybJIqrFl_jGXyAtSZTwM,34790
34
+ backtrack/lyrics/editor_view.py,sha256=55d4chS0EDwOmtI5CwWsw0PDfqUGGuxjMyglyll34rE,42066
35
+ backtrack/lyrics/formats.py,sha256=sgHQVW-hmUTuOoo5Jx7sftdSP0gFhZfyFZUhrhejCVo,55614
36
+ backtrack/lyrics/lyric_pane.py,sha256=p2oHWophl97zM3Jkk8BhK1Jk9S3qdS4bfa_sKkTca9k,12278
37
+ backtrack/lyrics/md_overlay.py,sha256=nT7Fj_8-VJ7et0e0WW-VhTnHMRBhl3dkiL3PZUs6by4,20512
38
+ backtrack/lyrics/sync_doc.py,sha256=LjC8z6tjEXp_WTelPY_17ErNUD4N628z_BEGAgCU1vc,13822
39
+ backtrack/lyrics/text.py,sha256=SBWJmkYu3hn80nB5SUghnDjkKER427v5EB0NSvg2p7Q,24770
40
+ backtrack/lyrics/time_fields.py,sha256=lB--OEK3-CoNl5GvKKyo0KtwC6bynpKNJrHRjOEbxcw,5948
41
+ backtrack/lyrics/verify.py,sha256=qYMXacG5PTt6Z6MbW-FrwwHQK2XlpyZbsaZj42s3W14,18858
42
+ backtrack/menus/__init__.py,sha256=al4acphRkw-tSbr2zoTb4jvTGqee9N5VnKsaQk2ao0c,1307
43
+ backtrack/menus/activity.py,sha256=Q1J5XfL1CzYk-pURLC0lh3l2Hg3Ok2YrsO5_51PaGHw,3548
44
+ backtrack/menus/browse.py,sha256=qljmfDweyi6hJH0UtF59OIWrtucOtmYDt60pZYrV1hI,24221
45
+ backtrack/menus/common.py,sha256=gtHcqC4BwkyQsmTpk1yh_MKR3Ez9uqbuU51c7BCpevQ,5992
46
+ backtrack/menus/history.py,sha256=60nIhfCB0-53MmvUIZD5DiFY4gPhU6-rjBapYVVZo10,5502
47
+ backtrack/menus/play.py,sha256=O2YOntDU1l0MTG9XNU0LxGetDUisYfEnKHWu7DX5-50,12721
48
+ backtrack/menus/search.py,sha256=VGeoMfuwa88dVcFgcWkPLGluTw7IxTlPuCZ8vF67G8w,17884
49
+ backtrack/menus/settings.py,sha256=FvsIplytEwcqjSoTbJg19cK9hkZgJbaQDsZcP7I0h8E,23563
50
+ backtrack/menus/sorting.py,sha256=UYG9BpwJJshDhapvKNM1ykfaL_Ce7Pejn9jfVQTBWDM,8982
51
+ backtrack/playback/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
52
+ backtrack/playback/ipc.py,sha256=4-p0NNYf2X1DTTRJ_X4dUmU4HKz9o0o7FKJ9AeDaKo0,13955
53
+ backtrack/playback/libvlc.py,sha256=aCXsFxxGkUcEQqy99rdzvIj8kZdqh0u1wEWeIO_SMIM,669
54
+ backtrack/playback/now_playing_box.py,sha256=x2fzloacgf1yHnBSDWkhRA4jpm6eIvLe2iRa5BItV0Y,7452
55
+ backtrack/playback/player.py,sha256=NdldqBAcArfHx38vUr2yaF_YVRAX-Fa-nz7_L-vIDL0,35676
56
+ backtrack/playback/player_art.py,sha256=BQhO9hkAAVzJ99tCjjS2H3WlUMoShIrO7fibgL_y04w,15069
57
+ backtrack/playback/player_geom.py,sha256=_kl1aULdCeaz-XvD3Si9hh_p8vGwGLeCUEgcmeEJtz4,2443
58
+ backtrack/playback/player_ui.py,sha256=-JgKTfs9Er6MgBcbenBTa1xk4zrvSem0YWB7Zp7gseI,45532
59
+ backtrack/playback/queue_pane.py,sha256=gZQw00ro1Zg35eQIzWqAapjIKnSKnomsR1_DXi4HwhM,12478
60
+ backtrack/playback/session.py,sha256=KVM205VXbQovkJz6H6m6eg_BWMu231cB2wLedXSk4EI,46604
61
+ backtrack/trim/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
62
+ backtrack/trim/bulk.py,sha256=F0mIkCxwPag6lqN0Ssol16cThtUquef3VRKXM5tkEsQ,29615
63
+ backtrack/trim/editor.py,sha256=kxxvjuNJLwcjT-9VNF3CtZXJ6lpCZrEaRDpIxlnufjU,37374
64
+ backtrack/trim/engine.py,sha256=8mV2t6XFSE7oMeBTx2dz5najj19-7Cp_DKdAvFyGQjQ,36539
65
+ backpack_backtrack-0.2.0.dist-info/METADATA,sha256=oMcJotG10N59LIsisFvnS5GqrcAT5BYw1W0fozWNj2w,21939
66
+ backpack_backtrack-0.2.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
67
+ backpack_backtrack-0.2.0.dist-info/entry_points.txt,sha256=fAHBgC_8urY414TwKEM9TRIvw2rcfPsuNguP4ksqlhc,50
68
+ backpack_backtrack-0.2.0.dist-info/top_level.txt,sha256=9cEv2CoDT6qG_ZBKWO57jdt6IMvqGEYKn62e6-La2mA,10
69
+ backpack_backtrack-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ backtrack = backtrack.main:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 RedEraRrow
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ backtrack
backtrack/__init__.py ADDED
@@ -0,0 +1,8 @@
1
+ """
2
+ Backtrack package entry point.
3
+
4
+ Windows ANSI support is initialised once in ``backtrack.main.main()`` via
5
+ ``colorama.just_fix_windows_console()``; no package-import-time init here.
6
+ """
7
+
8
+ __all__ = []
backtrack/__main__.py ADDED
@@ -0,0 +1,3 @@
1
+ from backtrack.main import main
2
+
3
+ raise SystemExit(main())