@torrent-tv/proxy 2.80.14 → 2.80.16
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.
- package/CHANGELOG.md +13 -0
- package/docs/encode-architecture.md +79 -0
- package/package.json +1 -1
- package/services/encode/EncodePlan.js +1274 -1101
- package/services/encode/run-costs.js +54 -3
- package/services/hls-session-manager.js +6 -2
- package/services/output/LiveOutputs.js +17 -7
- package/services/output/rates.js +35 -5
- package/test/encode-plan.test.js +9 -3
- package/test/move-cost.test.js +145 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
## 2.80.16
|
|
2
|
+
|
|
3
|
+
- **Fix**: THE SPINNER CARAVAN, by arithmetic alone, and the two props 2.80.15 carried are gone with it — the threshold that made a move pay for itself, and the exclusion that kept a live run off a deadline-less zone. Both were propping up a comparison that was wrong rather than indifferent. Three quantities were computed wrongly:
|
|
4
|
+
1. **A body was charged a whole piece for the one it was already making.** `arrival = delay + (index - at + 1) / rate` is right for a body that does not exist yet and wrong for a run 0.8 s into a 0.9 s piece. `delaySec` is now when a body finishes the piece it STANDS ON — for a run that has produced something, one piece; for one still warming up, the measured time to a first piece less the time it has been alive. From #58, reaching #59 costs 0.14 + 0.94 = 1.08 s against 1.88 s for a kill and a cold start: a decision by eight hundred milliseconds, where the double charge had made it a coin flip lost by ten.
|
|
5
|
+
2. **The piece was priced at the unpenalised rate** while arrivals used the penalised one, so every extra body looked cheaper than it is and the plan bought a second encoder where one served. One rate now, the one the arrangement itself puts in force, and each body states its own debt as a function of it — so there is nothing to dispatch on.
|
|
6
|
+
- **Fix**: What a move costs is `Infinity` until something has been measured, because a move is irreversible while leaving the encoder alone is always available; placing one where there is none takes the unknown the other way, since the film gets made or it does not. And a run killed before producing anything is a measurement too — a lower bound on the first output — which is the only reading a thrash can supply, since every run in one is killed before it finishes anything.
|
|
7
|
+
- **Chore**: Verified by simulation over sixty ticks against the map's real shape — one encoder placed, left alone, and moved exactly once, at the viewer's own seek. NOT yet seen in the field.
|
|
8
|
+
|
|
9
|
+
## 2.80.15
|
|
10
|
+
|
|
11
|
+
- **Fix**: THE SPINNER CARAVAN, first attempt. Field 2026-09-08: 39 encoder moves in one session, 24 of them between three adjacent numbers about 0.8 s apart, one viewer on a host affording three runs got three, and the picture stood still for 116.7 s in three interruptions, the worst 91.8 s. This release fixed the reading of `withinSeconds: null` — the map marks the film BEHIND the viewers with it, meaning nobody is waiting there, and `deadlineReaderFor` read it through `Number()`, where it is 0, so that film was due IMMEDIATELY and was the most urgent material in the file. It also made the objective the map's own rank order, as a vector compared position by position rather than three seconds-valued terms with ten ranks collapsed into two buckets.
|
|
12
|
+
- **Chore**: It also carried two props that 2.80.16 removes — a threshold requiring a move to beat staying by what moving costs, and an exclusion keeping a live run off any zone with no deadline. Both were written while the arrival arithmetic was still wrong; neither belongs in a model.
|
|
13
|
+
|
|
1
14
|
## 2.80.14
|
|
2
15
|
|
|
3
16
|
- **Fix**: What the master playlist declares a variant carries is MEASURED. It was `height * height * 3.2` — for 1080 exactly the 3 732 480 the field declared for a file carrying 18.4 Mbit/s, five times more — and the comment justifying it said no measurement existed before encoding starts. Both do: the average is the file's own length over its duration, exact at session creation, and the peak is the biggest piece produced over the span it covers, equal to the average until one exists. A re-encoded height is declared at the cap we impose; a smaller height at the pixel share of the source's rate. `BANDWIDTH` and `AVERAGE-BANDWIDTH` are both emitted, as the specification names them. This is not cosmetic: the browser sizes its cushion in BYTES from `BANDWIDTH`, so 120 s asked bought 56 MB — 26 s of that film — and the deepest it ever held was 17.1 s. One expression, no case analysis: absent measurements and absent caps are written as the identity of the operation that consumes them (`services/output/rates.js`, eight checks).
|
|
@@ -188,6 +188,85 @@ was 17.1 s. Inflating it is not the answer either: hls.js compares it against
|
|
|
188
188
|
its own estimate of the link to decide a level is unplayable, and its recovery
|
|
189
189
|
then moves level by itself, which does not honour our pinning.
|
|
190
190
|
|
|
191
|
+
## The objective is the map's own rank order
|
|
192
|
+
|
|
193
|
+
Not three terms in seconds. One pair per rank the map states, most urgent rank
|
|
194
|
+
first, compared position by position:
|
|
195
|
+
|
|
196
|
+
```
|
|
197
|
+
[ late(100), done(100), late(99), done(99), … late(1), done(1) ], bodies, wasted
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
`late(r)` is how long anybody waits past a deadline at rank `r`; `done(r)` is
|
|
201
|
+
when the last number of that rank is made. A difference at a higher rank settles
|
|
202
|
+
it and nothing lower can reopen it — which is the stated order: nobody stares at
|
|
203
|
+
a spinner; then the film in front is encoded as fast as it can be, band by band
|
|
204
|
+
as the map ranks them; then, with what is left over and only then, the film
|
|
205
|
+
behind, in case somebody seeks back.
|
|
206
|
+
|
|
207
|
+
**No weights, and none possible.** A weight would let seconds at one rank buy
|
|
208
|
+
seconds at another, and it would be a figure nobody measured. The map is the
|
|
209
|
+
source of truth about what matters and it already says so — ten ranks on a film,
|
|
210
|
+
p100 at the number a viewer is stopped on, doubling zones down to p91 for the far
|
|
211
|
+
tail, p1 for what lies behind them.
|
|
212
|
+
|
|
213
|
+
`bodies` ranks below every rank of the map, so spare capacity cannot buy an
|
|
214
|
+
encoder where the map is indifferent. `wasted` is the swarm's bill for anything
|
|
215
|
+
fetched twice.
|
|
216
|
+
|
|
217
|
+
## Why an encoder is not moved for nothing, and it is not a rule
|
|
218
|
+
|
|
219
|
+
There is no threshold here, and there must not be one. What kept an encoder
|
|
220
|
+
moving was three quantities computed wrongly, not a policy that needed tuning.
|
|
221
|
+
|
|
222
|
+
**`delaySec` is when a body finishes the piece it STANDS ON.** For a run that has
|
|
223
|
+
produced something, one piece at the rate in force; for one still warming up, the
|
|
224
|
+
measured time to a first piece less the time it has already been alive. The
|
|
225
|
+
arrival of anything further on is that plus the pieces between, and nothing else.
|
|
226
|
+
|
|
227
|
+
It used to be `delay + (index - at + 1) / rate`, which charges every body a whole
|
|
228
|
+
piece for the one it is already making. That is right for a body that does not
|
|
229
|
+
exist yet and wrong for a run 0.8 s into a 0.9 s piece — and the difference is
|
|
230
|
+
the whole fault: from #58, reaching #59 was priced at 1.89 s against 1.88 s for a
|
|
231
|
+
kill and a cold start, a coin flip lost by ten milliseconds. Priced correctly it
|
|
232
|
+
is 1.08 s against 1.88 s.
|
|
233
|
+
|
|
234
|
+
**One rate, the one the arrangement puts in force.** Concurrent encoders slow
|
|
235
|
+
each other, measured on this host, so a piece costs what it costs at the body
|
|
236
|
+
count the arrangement has. Taken from the unpenalised rate while arrivals used
|
|
237
|
+
the penalised one, an extra body looked cheaper than it is and the plan bought a
|
|
238
|
+
second encoder where one served.
|
|
239
|
+
|
|
240
|
+
**Each body states its own debt** where it is created, as a function of what one
|
|
241
|
+
piece costs — because only there is it known what the body IS, and at the point
|
|
242
|
+
of pricing only how many there are. So there is no kind, no tag and no case
|
|
243
|
+
analysis.
|
|
244
|
+
|
|
245
|
+
**What a move costs is `Infinity` until something has been measured**, because a
|
|
246
|
+
move is irreversible while leaving the encoder alone is always available. Placing
|
|
247
|
+
one where there is none takes the unknown the other way: the film gets made or it
|
|
248
|
+
does not. And a run killed before producing anything is a measurement too — a
|
|
249
|
+
lower bound on the first output, and the only reading a thrash can supply, since
|
|
250
|
+
every run in one is killed before it finishes anything.
|
|
251
|
+
|
|
252
|
+
### What that was for
|
|
253
|
+
|
|
254
|
+
Field 2026-09-08: 39 moves in one session, 24 of them between three adjacent
|
|
255
|
+
numbers about 0.8 s apart. One viewer on a host affording three runs got three.
|
|
256
|
+
The picture stood still for 116.7 s in three interruptions, the worst 91.8 s.
|
|
257
|
+
|
|
258
|
+
Compounding it, the map states `withinSeconds: null` for the film behind the
|
|
259
|
+
viewers — nobody is waiting there — and `deadlineReaderFor` read that through
|
|
260
|
+
`Number()`, where `null` is 0. So the film a viewer had already passed was due
|
|
261
|
+
IMMEDIATELY and was the most urgent material in the file: it bought encoders, and
|
|
262
|
+
it took the run standing in front of the viewer because that run was the nearest
|
|
263
|
+
body to it.
|
|
264
|
+
|
|
265
|
+
Checked by simulation over sixty ticks against the map's real shape — ten zones
|
|
266
|
+
doubling ahead of the viewer, one behind — at both one and three runs: the
|
|
267
|
+
encoder is placed once, left alone, and moved exactly once, at the viewer's own
|
|
268
|
+
seek.
|
|
269
|
+
|
|
191
270
|
## What is checked
|
|
192
271
|
|
|
193
272
|
`test/one-authority.test.js` holds the shape: one caller of `#startEncodeRun`,
|