backpack-backcrack 0.2.0__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.
- backpack_backcrack-0.2.0/LICENSE +21 -0
- backpack_backcrack-0.2.0/PKG-INFO +360 -0
- backpack_backcrack-0.2.0/README.md +341 -0
- backpack_backcrack-0.2.0/backcrack/__init__.py +0 -0
- backpack_backcrack-0.2.0/backcrack/__main__.py +3 -0
- backpack_backcrack-0.2.0/backcrack/cli.py +84 -0
- backpack_backcrack-0.2.0/backcrack/common.py +99 -0
- backpack_backcrack-0.2.0/backcrack/config.py +286 -0
- backpack_backcrack-0.2.0/backcrack/deps.py +39 -0
- backpack_backcrack-0.2.0/backcrack/disc.py +178 -0
- backpack_backcrack-0.2.0/backcrack/diskspeed.py +129 -0
- backpack_backcrack-0.2.0/backcrack/encd.py +42 -0
- backpack_backcrack-0.2.0/backcrack/encode.py +198 -0
- backpack_backcrack-0.2.0/backcrack/namer.py +178 -0
- backpack_backcrack-0.2.0/backcrack/pattern.py +135 -0
- backpack_backcrack-0.2.0/backcrack/rip.py +266 -0
- backpack_backcrack-0.2.0/backcrack/ripd.py +83 -0
- backpack_backcrack-0.2.0/backcrack/sort.py +146 -0
- backpack_backcrack-0.2.0/backcrack/sortd.py +39 -0
- backpack_backcrack-0.2.0/backcrack/status.py +59 -0
- backpack_backcrack-0.2.0/backcrack/swapd.py +69 -0
- backpack_backcrack-0.2.0/backcrack/titles.py +58 -0
- backpack_backcrack-0.2.0/backcrack/watch.py +418 -0
- backpack_backcrack-0.2.0/backpack_backcrack.egg-info/PKG-INFO +360 -0
- backpack_backcrack-0.2.0/backpack_backcrack.egg-info/SOURCES.txt +30 -0
- backpack_backcrack-0.2.0/backpack_backcrack.egg-info/dependency_links.txt +1 -0
- backpack_backcrack-0.2.0/backpack_backcrack.egg-info/entry_points.txt +2 -0
- backpack_backcrack-0.2.0/backpack_backcrack.egg-info/requires.txt +1 -0
- backpack_backcrack-0.2.0/backpack_backcrack.egg-info/top_level.txt +1 -0
- backpack_backcrack-0.2.0/pyproject.toml +32 -0
- backpack_backcrack-0.2.0/setup.cfg +4 -0
- backpack_backcrack-0.2.0/tests/test_disc_toc.py +51 -0
|
@@ -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,360 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: backpack-backcrack
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: CD/DVD/Blu-ray rip pipeline - MakeMKV/HandBrake for video, cdparanoia for audio, dynamic %token% library patterning.
|
|
5
|
+
License: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/RedEraRrow/backcrack
|
|
7
|
+
Project-URL: Issues, https://github.com/RedEraRrow/backcrack/issues
|
|
8
|
+
Classifier: Environment :: Console :: Curses
|
|
9
|
+
Classifier: Topic :: Multimedia :: Sound/Audio :: CD Audio :: CD Ripping
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: MacOS
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
14
|
+
Requires-Python: >=3.10
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: backpack-backbone<0.3,>=0.2
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
# backcrack
|
|
21
|
+
|
|
22
|
+
Insert discs, get pinged, swap discs. Encoding happens on its own in the
|
|
23
|
+
background. Works on CDs, DVDs, and Blu-rays - the disc in the drive tells
|
|
24
|
+
the pipeline which path to take.
|
|
25
|
+
|
|
26
|
+
Python, stdlib only apart from `backbone`, the library the back* tools share
|
|
27
|
+
(`watch`'s screen, process and notification helpers). Requires Python 3.10+
|
|
28
|
+
(`python3 --version` to check).
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
|
|
32
|
+
macOS only (it drives the drives through diskutil and drutil):
|
|
33
|
+
|
|
34
|
+
brew install pipx
|
|
35
|
+
pipx install backpack-backcrack
|
|
36
|
+
backcrack doctor
|
|
37
|
+
|
|
38
|
+
`backcrack doctor` lists the rippers and encoders it drives, found or how to
|
|
39
|
+
install each. For everything: `brew install --cask makemkv` and
|
|
40
|
+
`brew install handbrake cdparanoia flac cd-discid`. Paths it can't find
|
|
41
|
+
itself go in `settings.env` (`MKVCON`, `HBCLI`, …).
|
|
42
|
+
|
|
43
|
+
Every tool is a subcommand of the one command, `backcrack` (`backcrack
|
|
44
|
+
watch`, `backcrack status`), so it never takes a common word like `watch`
|
|
45
|
+
on your PATH.
|
|
46
|
+
|
|
47
|
+
To work on it from a checkout, install backbone's checkout and this one
|
|
48
|
+
editable (backbone first), so edits take effect with no reinstall:
|
|
49
|
+
|
|
50
|
+
pip3 install -e ../backbone -e .
|
|
51
|
+
|
|
52
|
+
Every command also runs from the checkout as `python3 -m backcrack <command>`.
|
|
53
|
+
|
|
54
|
+
## Before the first disc
|
|
55
|
+
|
|
56
|
+
`LIBRARY` is the folder finished discs land in, `~/Media/rips` unless you
|
|
57
|
+
set it: from `watch`'s settings screen (`s`), in `settings.env`, or as an
|
|
58
|
+
environment variable (see [Settings](#settings)).
|
|
59
|
+
|
|
60
|
+
For a push on every eject, set `NTFY_TOPIC` to a topic name of your own, and
|
|
61
|
+
`NTFY_SERVER` too if you don't use the public `https://ntfy.sh`. Anyone who
|
|
62
|
+
knows a topic name can read it on the public server, so pick one nobody will
|
|
63
|
+
guess. With `NTFY_TOPIC` unset, nothing is sent.
|
|
64
|
+
|
|
65
|
+
## Morning start
|
|
66
|
+
|
|
67
|
+
One command:
|
|
68
|
+
|
|
69
|
+
backcrack
|
|
70
|
+
|
|
71
|
+
starts `ripd` (the ripper), `encd` (the encoder) and `sortd` (which files
|
|
72
|
+
discs out of `UNSORTED/`) in the background, then opens `watch`, the live
|
|
73
|
+
view. Any of them already running is left alone. Their output goes to
|
|
74
|
+
`$LIBRARY/.ripstate/<name>.out`. `LAUNCH_RIPD`, `LAUNCH_ENCD`,
|
|
75
|
+
`LAUNCH_SORTD` and `LAUNCH_WATCH` (all on by default) turn each part off, to
|
|
76
|
+
run it by hand in its own tab instead:
|
|
77
|
+
|
|
78
|
+
backcrack ripd
|
|
79
|
+
backcrack encd # start it once and leave it; idle is normal
|
|
80
|
+
backcrack sortd
|
|
81
|
+
|
|
82
|
+
Then load both drives. Every eject sends an ntfy push. Insert the next discs.
|
|
83
|
+
|
|
84
|
+
Check progress any time:
|
|
85
|
+
|
|
86
|
+
backcrack status
|
|
87
|
+
backcrack watch # live view
|
|
88
|
+
|
|
89
|
+
In `watch`, `q` quits (and offers to stop ripd, encd and sortd with it) and
|
|
90
|
+
`s` opens the settings screen - see [Settings](#settings). Both keys, and those of every
|
|
91
|
+
list, can be changed under Key bindings on that screen.
|
|
92
|
+
|
|
93
|
+
## What it detects
|
|
94
|
+
|
|
95
|
+
- **DVD / Blu-ray** - MakeMKV rips each title to a lossless MKV, HandBrake
|
|
96
|
+
encodes the ones matching a configured duration class - see
|
|
97
|
+
`DURATION_CLASSES` below.
|
|
98
|
+
- **Audio CD** - cdparanoia extracts each track losslessly
|
|
99
|
+
(`track01.cdda.wav`...), then each is compressed to `AUDIO_FORMAT` (flac
|
|
100
|
+
by default) as `track01.flac`...
|
|
101
|
+
|
|
102
|
+
Both land in the same shape: `source/` (lossless rip) and `encoded/`
|
|
103
|
+
(compressed, ready to use); video also gets `extras/` for anything shorter
|
|
104
|
+
than the main window.
|
|
105
|
+
|
|
106
|
+
A video disc is known by its volume label. An audio CD has no useful label,
|
|
107
|
+
so it is named `AudioCD-<n>tracks-<id>`, where the id is cd-discid's disc id
|
|
108
|
+
(or, without cd-discid, a hash of cdparanoia's track table). That name is
|
|
109
|
+
what shows in the log and in `UNSORTED/`, and what a `labels.map` line for
|
|
110
|
+
the CD has to use.
|
|
111
|
+
|
|
112
|
+
### Rip mode
|
|
113
|
+
|
|
114
|
+
`RIP_MODE` picks how a video disc is ripped:
|
|
115
|
+
|
|
116
|
+
- `titles` (default): MakeMKV rips each title to its own MKV in `source/`.
|
|
117
|
+
Works for DVD and Blu-ray. A rip whose MakeMKV log reports "N titles
|
|
118
|
+
saved, M failed" is marked failed even when makemkvcon exits 0.
|
|
119
|
+
- `video_ts`: a full decrypted backup of the disc, menus included, written
|
|
120
|
+
to `VIDEO_TS/` instead of `source/`. DVD only. The partial-rip check
|
|
121
|
+
above doesn't apply; a rip counts as done when makemkvcon exits 0 and
|
|
122
|
+
`VIDEO_TS/` exists.
|
|
123
|
+
|
|
124
|
+
## Layout produced
|
|
125
|
+
|
|
126
|
+
$LIBRARY/<pattern-resolved path>/source/... lossless rip (VIDEO_TS/ in video_ts mode)
|
|
127
|
+
$LIBRARY/<pattern-resolved path>/encoded/... compressed, ready to use (default "main" class)
|
|
128
|
+
$LIBRARY/<pattern-resolved path>/extras/... video only (default "extra" class)
|
|
129
|
+
$LIBRARY/.ripstate/ logs, queue, progress
|
|
130
|
+
|
|
131
|
+
For video, `encoded` and `extras` are just the stock `DURATION_CLASSES`
|
|
132
|
+
folder names; a class's `folder` field is whatever you set it to. Audio
|
|
133
|
+
always goes to `encoded/`.
|
|
134
|
+
|
|
135
|
+
The path itself is built from `PATTERN_VIDEO` / `PATTERN_AUDIO` by
|
|
136
|
+
substituting %tokens%. Default is `Season %season%/Disc %disc%` for video
|
|
137
|
+
and `%artist%/%album%` for audio; change these for a movie shelf, a mixed
|
|
138
|
+
CD/DVD pile, whatever you're ripping this run. See `docs/pattern-tokens.md`.
|
|
139
|
+
|
|
140
|
+
Discs whose tokens can't be resolved go to `UNSORTED/<label>/`, and you get
|
|
141
|
+
a high-priority push. Add a line to `labels.map` (in your config directory,
|
|
142
|
+
see [Settings](#settings)) and, with `sortd` running, it files itself within
|
|
143
|
+
a few seconds.
|
|
144
|
+
|
|
145
|
+
## Before trusting a whole shelf of discs
|
|
146
|
+
|
|
147
|
+
Once the first disc of a kind has ripped, check what HandBrake saw:
|
|
148
|
+
|
|
149
|
+
backcrack titles "$LIBRARY/Season 1/Disc 1"
|
|
150
|
+
|
|
151
|
+
Every title in your target runtime should say `main`; menus, featurettes,
|
|
152
|
+
and any "play all" duplicate should say `extra` or `skip`. Adjust
|
|
153
|
+
`DURATION_CLASSES` if not - a 22-minute sitcom and a 150-minute film need
|
|
154
|
+
very different windows.
|
|
155
|
+
|
|
156
|
+
`DURATION_CLASSES` is a comma-separated list of
|
|
157
|
+
`name:min-max:folder:quality[:dedup]` entries, e.g. the default:
|
|
158
|
+
|
|
159
|
+
DURATION_CLASSES=extra:180-1199:extras:22,main:1200-10800:encoded:20
|
|
160
|
+
|
|
161
|
+
A title's duration must fall in a class's `min-max` (seconds) to match;
|
|
162
|
+
first match wins, and anything matching none of them isn't encoded at all.
|
|
163
|
+
`folder` is where it lands under a disc's destination, `quality` is
|
|
164
|
+
HandBrake's `--quality` (RF) for that class, and the optional `dedup` flag
|
|
165
|
+
drops a second title with that exact duration.
|
|
166
|
+
|
|
167
|
+
`dedup` is off by default, and worth turning on only for a disc that
|
|
168
|
+
really does list the same film twice ("Play Movie" plus a separate menu
|
|
169
|
+
entry of identical content). It is wrong for TV: episodes on one disc
|
|
170
|
+
naturally cluster within seconds of each other - four real titles at 2530,
|
|
171
|
+
2530, 2537 and 2533 seconds, two of them bit-for-bit different files - so
|
|
172
|
+
matching on duration alone silently drops a distinct episode. Check your own
|
|
173
|
+
discs before setting it. Add as many classes as you want: a `commentary` or
|
|
174
|
+
`featurette` tier with its own folder and quality, tighter windows for a
|
|
175
|
+
mixed sitcom/movie shelf, whatever your discs need.
|
|
176
|
+
|
|
177
|
+
`MIN_TITLE_S` is the separate rip-time floor (MakeMKV's `--minlength`) below
|
|
178
|
+
which a title is never even ripped; it defaults to the shortest configured
|
|
179
|
+
class's minimum, so set it explicitly only if you want to rip shorter junk
|
|
180
|
+
than you keep.
|
|
181
|
+
|
|
182
|
+
Then watch one encoded file and scrub a fast camera move frame by frame. If
|
|
183
|
+
you see combing, set `DEINTERLACE_ARGS` to HandBrake's deinterlace flags,
|
|
184
|
+
e.g. `DEINTERLACE_ARGS=--comb-detect --decomb`. Leave it empty for
|
|
185
|
+
progressive sources.
|
|
186
|
+
|
|
187
|
+
For audio, `cdparanoia -Q -d <device>` lists a disc's tracks directly if you
|
|
188
|
+
want to sanity-check before ripping.
|
|
189
|
+
|
|
190
|
+
## Encoding throughput
|
|
191
|
+
|
|
192
|
+
`ENCODE_JOBS` is how many HandBrake jobs run at once, `ENCODER_PRESET` is the
|
|
193
|
+
x264 preset, and `ENCODE_THREADS` caps the threads any one job may use.
|
|
194
|
+
|
|
195
|
+
The defaults (4 jobs, `fast`, threads capped at cores/jobs) were measured on
|
|
196
|
+
an M1 Pro with 8 performance cores: `medium` saturates it at 2 concurrent
|
|
197
|
+
jobs, while `fast` keeps scaling through 4, for roughly 33% more aggregate
|
|
198
|
+
throughput at about 3.6% bigger files at the same RF. Those numbers are
|
|
199
|
+
specific to that CPU and preset. Re-benchmark if you change any of it: encode
|
|
200
|
+
one short clip alone, then N of them at once, and compare the wall time.
|
|
201
|
+
|
|
202
|
+
The thread cap matters more than it looks. Jobs in a batch don't finish
|
|
203
|
+
together - a 168-minute title far outlasts a 42-minute one - so a job can end
|
|
204
|
+
up running alone, and an uncapped x264 then takes every core it can see. That
|
|
205
|
+
starved two concurrent MakeMKV rips of scheduling time for over ten minutes
|
|
206
|
+
(both sat at about 3% CPU with nothing written). The cap keeps a fixed
|
|
207
|
+
ceiling however many siblings are still going, so ripping always has room.
|
|
208
|
+
|
|
209
|
+
## Resuming
|
|
210
|
+
|
|
211
|
+
Everything is resumable. Stop any daemon with Ctrl-C whenever you like.
|
|
212
|
+
(`watch`'s live view is the exception - Ctrl-C is disabled there; press `q`
|
|
213
|
+
and it'll ask whether to also stop the daemons.)
|
|
214
|
+
|
|
215
|
+
- A disc already ripped is ejected immediately instead of redone.
|
|
216
|
+
- A failed rip is left in the drive and ripped again, up to `MAX_RETRIES`
|
|
217
|
+
attempts in all (3 by default). After the last one it is ejected and
|
|
218
|
+
`$LIBRARY/.ripstate/gaveup-<label>` is written; re-inserting it then just
|
|
219
|
+
ejects it again. To try it again, delete that file and re-insert the
|
|
220
|
+
disc: it gets a fresh `MAX_RETRIES` attempts.
|
|
221
|
+
- A file already encoded is skipped. An encode is written as
|
|
222
|
+
`<name>.part.mkv` and renamed when HandBrake finishes, so an interrupted
|
|
223
|
+
one is redone rather than kept half-written.
|
|
224
|
+
|
|
225
|
+
Before finishing, check for failures:
|
|
226
|
+
|
|
227
|
+
grep FAIL "$LIBRARY/.ripstate/rip.log" "$LIBRARY/.ripstate/encode.log"
|
|
228
|
+
grep -i WARN "$LIBRARY/.ripstate/rip.log"
|
|
229
|
+
|
|
230
|
+
`WARN` lines are worth reading. A disc routing to a folder that already holds
|
|
231
|
+
finished output is ripped alongside it as `<name> (<label>)/` rather than over
|
|
232
|
+
it, and says so there - reconcile those by hand.
|
|
233
|
+
|
|
234
|
+
Every rip also keeps MakeMKV's own output at
|
|
235
|
+
`$LIBRARY/.ripstate/logs/<label>.mkv.log`, and every encode HandBrake's at
|
|
236
|
+
`$LIBRARY/.ripstate/logs/<file>.hb.log`. That is where a read error, a
|
|
237
|
+
retried sector or a title MakeMKV gave up on shows.
|
|
238
|
+
|
|
239
|
+
## Upgrading ripd mid-run
|
|
240
|
+
|
|
241
|
+
`swapd` swaps in a new `ripd.py` without interrupting a rip: save the new
|
|
242
|
+
version as `backcrack/ripd.py.new` beside `backcrack/ripd.py` (in a checkout) and run
|
|
243
|
+
`backcrack swapd`. It waits until no
|
|
244
|
+
drive is ripping (giving up after 4 hours), swaps the file in, restarts
|
|
245
|
+
`ripd` and exits. `swap.log` in `.ripstate` records what it did.
|
|
246
|
+
|
|
247
|
+
## Settings
|
|
248
|
+
|
|
249
|
+
Each setting is read, in order, from an environment variable, then
|
|
250
|
+
`settings.env` in your config directory, then the default in
|
|
251
|
+
`backcrack/config.py`. So a one-off `LIBRARY=... ripd` overrides
|
|
252
|
+
everything, and `settings.env` holds what you want every time.
|
|
253
|
+
|
|
254
|
+
`s` in `watch` opens a settings screen over most of them and writes your
|
|
255
|
+
changes to `settings.env`. The tool paths below aren't on it; set those in
|
|
256
|
+
`settings.env` or the environment. The daemons read their settings at
|
|
257
|
+
startup, so restart `ripd`, `encd` or `sortd` to pick a change up.
|
|
258
|
+
|
|
259
|
+
The ones not covered elsewhere in this README:
|
|
260
|
+
|
|
261
|
+
| Setting | Default | What it does |
|
|
262
|
+
|---|---|---|
|
|
263
|
+
| `MKVCON`, `HBCLI`, `CDPARANOIA`, `CD_DISCID`, `FLAC`, `FFMPEG` | found on PATH (MakeMKV at `/Applications/MakeMKV.app`) | tool paths |
|
|
264
|
+
| `NOTIFY_ENCODES` | `0` | also push when a disc finishes encoding |
|
|
265
|
+
| `ACCENT` | `green` | the accent colour: `green`, `red`, `yellow`, `blue`, `magenta`, `cyan` (your terminal's own), `amber`, `coral`, `rose`, `lavender`, `sky`, `mint`, or `#RRGGBB` |
|
|
266
|
+
| `WATCH_INTERVAL` | `1` | seconds between `watch` refreshes |
|
|
267
|
+
| `SORT_INTERVAL` | `5` | seconds between `sortd` passes |
|
|
268
|
+
| `TOTAL_DISCS` | `0` | discs in this run; shows a progress bar and ETA in `watch` (0 hides them) |
|
|
269
|
+
| `WINDOW`, `ACTIVE_S`, `FALLBACK_KB` | `20`, `90`, `7340032` | `watch`: seconds behind its MB/s figure, how long an idle rip stays listed, disc size (KB) assumed until the real one is known |
|
|
270
|
+
| `READ_MB`, `SKIP_MB`, `STALL_S`, `DISC_BYTES`, `RESULTS` | `600`, `1000`, `60`, `7000000000`, `~/diskspeed.txt` | `diskspeed`: MB read per drive, MB skipped first, seconds without progress before giving up, disc size behind its minutes-per-disc estimate, results file |
|
|
271
|
+
|
|
272
|
+
An older `INTERVAL` still works as a fallback for both `WATCH_INTERVAL` and
|
|
273
|
+
`SORT_INTERVAL`.
|
|
274
|
+
|
|
275
|
+
`ONESHOT=1 watch` (print one frame and exit), `ONCE=1 sortd` (one pass) and
|
|
276
|
+
`DRYRUN=1 sortd` (say what it would do, change nothing) are per-run switches,
|
|
277
|
+
read from the environment only.
|
|
278
|
+
|
|
279
|
+
### Your files live outside the checkout
|
|
280
|
+
|
|
281
|
+
Everything personal (your saved settings, your disc overrides, your episode
|
|
282
|
+
titles) lives in a config directory, not in this repo. The checkout stays
|
|
283
|
+
code-only, and nothing of yours needs gitignoring or risks being committed.
|
|
284
|
+
|
|
285
|
+
| File | What it is |
|
|
286
|
+
|---|---|
|
|
287
|
+
| `settings.env` | saved settings, one `NAME=value` per line; the `watch` settings screen writes it |
|
|
288
|
+
| `labels.map` | manual disc → %token% overrides |
|
|
289
|
+
| `episodes.map` | per-season episode titles, for `namer` |
|
|
290
|
+
|
|
291
|
+
The directory is `$BACKCRACK_CONFIG_DIR` if set, else
|
|
292
|
+
`$XDG_CONFIG_HOME/backcrack`, else `~/.config/backcrack`. It is created on
|
|
293
|
+
first run. Point `BACKCRACK_CONFIG_DIR` somewhere else to keep separate sets
|
|
294
|
+
of overrides for separate shelves.
|
|
295
|
+
|
|
296
|
+
The same screen has an entry for adding a `labels.map` line, which is the
|
|
297
|
+
manual escape hatch for a disc whose %tokens% couldn't be resolved - pick it
|
|
298
|
+
out of `UNSORTED/`, fill in the tokens the active pattern needs, and `sortd`
|
|
299
|
+
files it within a few seconds.
|
|
300
|
+
|
|
301
|
+
## Finishing a season
|
|
302
|
+
|
|
303
|
+
Once a season's discs are all ripped and encoded, `namer` lays them out the
|
|
304
|
+
way Jellyfin and Kodi expect:
|
|
305
|
+
|
|
306
|
+
backcrack namer every season episodes.map covers
|
|
307
|
+
backcrack namer "Season 4" just one
|
|
308
|
+
|
|
309
|
+
Each disc's encoded titles are renamed to `SxxExx Title.mkv` using the titles
|
|
310
|
+
in `episodes.map`, moved up into the season folder itself, and every disc's
|
|
311
|
+
extras are merged into one season-level `featurettes/` - a recognised extra
|
|
312
|
+
type, which a bare `extras/` is not.
|
|
313
|
+
|
|
314
|
+
**namer then deletes each disc's `source/` folder, the lossless rip**, once
|
|
315
|
+
every encoded episode from that disc has moved into the season. A disc
|
|
316
|
+
whose episodes didn't all move (a name collision, or a disc with no
|
|
317
|
+
encoded episodes) keeps its `source/`, and namer says which it kept and
|
|
318
|
+
which it deleted. Empty disc folders are removed; anything it doesn't
|
|
319
|
+
recognise is left where it is.
|
|
320
|
+
|
|
321
|
+
`episodes.map` lives in your config directory (see [Settings](#settings)) and
|
|
322
|
+
is one block per season: a `# Season N` header, then one title per line in
|
|
323
|
+
broadcast order. Renaming assumes disc and title order matches
|
|
324
|
+
broadcast order, which is the normal convention for a season box set.
|
|
325
|
+
|
|
326
|
+
A season is refused outright, with nothing touched, if its encoded-file
|
|
327
|
+
count doesn't exactly match its title count, or if any encoded file is empty
|
|
328
|
+
or still being written. A mismatch means a disc is still ripping, a title
|
|
329
|
+
never encoded, or one was wrongly deduped, and guessing would mislabel every
|
|
330
|
+
episode after the gap.
|
|
331
|
+
|
|
332
|
+
namer only understands the default layout: season folders named
|
|
333
|
+
`Season N` directly under `LIBRARY`, holding one folder per disc (the
|
|
334
|
+
default `PATTERN_VIDEO`, `Season %season%/Disc %disc%`), with episodes in
|
|
335
|
+
`encoded/` and extras in `extras/` (the default `DURATION_CLASSES` folder
|
|
336
|
+
names). With another pattern or other folder names, don't use it.
|
|
337
|
+
|
|
338
|
+
## Tools it needs
|
|
339
|
+
|
|
340
|
+
- **MakeMKV** (`makemkvcon`) - for DVD/Blu-ray. `brew install --cask makemkv`.
|
|
341
|
+
- **HandBrakeCLI** - to encode video. `brew install handbrake`.
|
|
342
|
+
- **cdparanoia** - for audio CDs. `brew install cdparanoia`.
|
|
343
|
+
- **flac** (or ffmpeg) - to compress ripped audio. `brew install flac`.
|
|
344
|
+
With neither, tracks are copied to `encoded/` as `.wav`.
|
|
345
|
+
- **cd-discid** - optional, for automatic artist/album lookup on audio CDs.
|
|
346
|
+
`brew install cd-discid`. Without it, every CD goes to `UNSORTED/` until
|
|
347
|
+
you add a `labels.map` line.
|
|
348
|
+
|
|
349
|
+
`backcrack doctor` checks all of them. A missing tool also gets a warning at
|
|
350
|
+
startup rather than a silent failure: `ripd` warns about makemkvcon,
|
|
351
|
+
cdparanoia and cd-discid, and `encd` about HandBrakeCLI and flac/ffmpeg. The
|
|
352
|
+
daemon still runs; that disc kind just won't rip or encode until the tool is
|
|
353
|
+
installed.
|
|
354
|
+
|
|
355
|
+
## Storage
|
|
356
|
+
|
|
357
|
+
Video eats far more space than audio. `namer` deletes a finished TV
|
|
358
|
+
season's `source/` folders for you (see above). For anything `namer` doesn't
|
|
359
|
+
handle (audio, films, another pattern), delete a disc's `source/` by hand
|
|
360
|
+
once you've checked its encoded output.
|