@torrent-tv/proxy 2.107.40 → 2.107.42
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/package.json
CHANGED
|
@@ -629,7 +629,7 @@ export function wireOutputs({
|
|
|
629
629
|
viewerSecondsOn: (output, consumerId, now) => viewerSecondsOn(parts.viewers, output, consumerId, now),
|
|
630
630
|
minimumBufferSecondsFor: (output) => minimumBufferSecondsOf(output),
|
|
631
631
|
// What this proxy may keep of downloaded pieces on disk.
|
|
632
|
-
sourceStorageBytes: () => parts.machineBudget?.allowedFor
|
|
632
|
+
sourceStorageBytes: () => parts.machineBudget?.allowedFor("spilled pieces") ?? null,
|
|
633
633
|
getSourceStats: (...args) => parts.getSourceStats?.(...args) ?? null,
|
|
634
634
|
disposeSession: (...args) => parts.lifecycle.disposeSession(...args),
|
|
635
635
|
planEncodersSoon: (...args) => parts.encodeRuns.planEncodersSoon(...args),
|
package/services/storage/wire.js
CHANGED
|
@@ -194,6 +194,8 @@ export function wireMachineBudget({
|
|
|
194
194
|
|
|
195
195
|
return {
|
|
196
196
|
capacityOf: name => budget.capacityOf(name),
|
|
197
|
+
/** What one claimant was last allowed, or null before a measured division. */
|
|
198
|
+
allowedFor: name => budget.allowedFor(name),
|
|
197
199
|
/**
|
|
198
200
|
* @param {string | null} [resource] - One resource to revise; all when absent.
|
|
199
201
|
* @param {string | null} [because] - Who asked, for the revision's line.
|
|
@@ -687,16 +687,8 @@ export function predictPlaybackReadiness(input = {}) {
|
|
|
687
687
|
}
|
|
688
688
|
const tailStock = [];
|
|
689
689
|
if (trackState.some(({ tailEndSeconds }) => Number.isFinite(tailEndSeconds))) {
|
|
690
|
-
// Where each source's bytes stop being the near window's: the last byte
|
|
691
|
-
// its pieces read, in the file's own offsets.
|
|
692
|
-
const windowEnds = new Map();
|
|
693
|
-
for (const { sourceByteIntervals } of jobs) {
|
|
694
|
-
for (const [sourceId, ranges] of sourceByteIntervals) {
|
|
695
|
-
for (const range of ranges) windowEnds.set(sourceId, Math.max(windowEnds.get(sourceId) ?? 0, range.end));
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
690
|
const stop = scheduleTail({ trackState, sourceState, runStarts, trackFinish,
|
|
699
|
-
link, position, buffered, reserve, transferFinish, constrain, duration, stock: tailStock
|
|
691
|
+
link, position, buffered, reserve, transferFinish, constrain, duration, stock: tailStock });
|
|
700
692
|
if (stop) {
|
|
701
693
|
return { ...result(false, null, buffered, reserve, null, stop.reason, preparedSegments),
|
|
702
694
|
...(stop.unavailableSource ? { unavailableSource: stop.unavailableSource } : {}) };
|
|
@@ -740,12 +732,13 @@ export function predictPlaybackReadiness(input = {}) {
|
|
|
740
732
|
// share (Home Assistant 2026-10-26, torrent-tv/meta#239). Said as such, and
|
|
741
733
|
// the viewer keeps waiting rather than starting into stops.
|
|
742
734
|
const storageBytes = Number(input.sourceStorageBytes);
|
|
743
|
-
|
|
744
|
-
|
|
735
|
+
const tailParts = tailStock.filter(part => !part.skipped);
|
|
736
|
+
if (Number.isFinite(delay) && Number.isFinite(storageBytes) && storageBytes >= 0 && tailParts.length > 0) {
|
|
737
|
+
const cushionBytes = tailParts.reduce((sum, part) => sum + Math.max(0, part.totalBytes - part.missingBytes) +
|
|
745
738
|
Math.max(0, part.missingBytes - part.bytesPerSecond * Math.max(0, part.finish - delay)), 0);
|
|
746
739
|
if (cushionBytes > storageBytes) {
|
|
747
740
|
return { ...result(false, null, buffered, reserve, null, "cushion-exceeds-storage", preparedSegments),
|
|
748
|
-
cushionBytes, storageBytes };
|
|
741
|
+
cushionBytes, storageBytes, tail: tailStock };
|
|
749
742
|
}
|
|
750
743
|
}
|
|
751
744
|
const originReady = trackState.every(({ clientRanges }) => compare(contiguousEnd(clientRanges, at), at) > 0);
|
|
@@ -756,6 +749,9 @@ export function predictPlaybackReadiness(input = {}) {
|
|
|
756
749
|
return { ...forecast, measuredAt: now, predictedStartAt: Number.isFinite(delay) ? now + delay * 1000 : null,
|
|
757
750
|
firstRequiredSegmentsSeconds,
|
|
758
751
|
firstRequiredStages,
|
|
752
|
+
// What the rest of the film weighs per source, at what rate it comes and
|
|
753
|
+
// when, or why a source has none: said in the readiness line.
|
|
754
|
+
tail: tailStock,
|
|
759
755
|
// The piece whose `kind` — its production, or its delivery to the page —
|
|
760
756
|
// comes `atSeconds` from now while the media before it lasts
|
|
761
757
|
// `aheadSeconds`: the difference is the delay.
|
|
@@ -795,13 +791,17 @@ function isPiece(segment) {
|
|
|
795
791
|
* @returns {{ reason: string, unavailableSource?: object } | null} Why there is no answer, or null.
|
|
796
792
|
*/
|
|
797
793
|
function scheduleTail({ trackState, sourceState, runStarts, trackFinish, link,
|
|
798
|
-
position, buffered, reserve, transferFinish, constrain, duration, stock = null
|
|
794
|
+
position, buffered, reserve, transferFinish, constrain, duration, stock = null }) {
|
|
799
795
|
const tailStart = Math.max(position, ...trackState.map(state => state.tailStartSeconds));
|
|
800
796
|
if (!Number.isFinite(tailStart)) return null;
|
|
801
797
|
const sourceDurations = new Map();
|
|
802
798
|
for (const state of trackState) {
|
|
803
799
|
const seconds = Math.max(0, state.tailEndSeconds - tailStart) * state.tailWorkFraction;
|
|
804
|
-
if (!(seconds > 0))
|
|
800
|
+
if (!(seconds > 0)) {
|
|
801
|
+
stock?.push({ track: state.track.id, skipped: "no-work", tailStart, tailEnd: state.tailEndSeconds,
|
|
802
|
+
workFraction: state.tailWorkFraction, tailSegments: state.tailSegmentCount });
|
|
803
|
+
continue;
|
|
804
|
+
}
|
|
805
805
|
for (const sourceId of new Set(state.track.sourceIds ?? [])) {
|
|
806
806
|
sourceDurations.set(sourceId, Math.max(sourceDurations.get(sourceId) ?? 0, seconds));
|
|
807
807
|
}
|
|
@@ -811,17 +811,29 @@ function scheduleTail({ trackState, sourceState, runStarts, trackFinish, link,
|
|
|
811
811
|
for (const [sourceId, seconds] of sourceDurations) {
|
|
812
812
|
const source = sourceState.get(sourceId);
|
|
813
813
|
if (!source) return { reason: "source-measurement-unavailable" };
|
|
814
|
-
if (source.source.complete === true)
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
//
|
|
819
|
-
|
|
814
|
+
if (source.source.complete === true) {
|
|
815
|
+
stock?.push({ sourceId, skipped: "complete" });
|
|
816
|
+
continue;
|
|
817
|
+
}
|
|
818
|
+
// Held means on this proxy now, in the bytes of the film after the near
|
|
819
|
+
// window: the window's own bytes are counted above, and its planned
|
|
820
|
+
// arrivals counted again as held took 117 s of film off a 90-minute tail.
|
|
821
|
+
// Where those bytes begin is placed by the density the tail is priced at.
|
|
822
|
+
// Neither the torrent thread's plan nor the bytes the window reads are
|
|
823
|
+
// that place: both can reach the end of the file — a copied Matroska file
|
|
824
|
+
// reads its index there — and the whole of Despicable Me 3 then counted
|
|
825
|
+
// as held (Home Assistant 2026-10-26, torrent-tv/meta#239).
|
|
826
|
+
const fileLength = Number(source.source.fileLength);
|
|
827
|
+
const windowEnd = fileLength > 0 && duration > 0
|
|
828
|
+
? Math.min(fileLength, Math.max(0, fileLength * tailStart / duration)) : 0;
|
|
820
829
|
const bytes = estimateUnmappedSourceBytes(source.source, seconds, duration,
|
|
821
830
|
heldBeyond(source.source.residence, windowEnd), windowEnd);
|
|
822
831
|
if (bytes === null) return { reason: "source-measurement-unavailable",
|
|
823
832
|
unavailableSource: { sourceId, phase: "tail" } };
|
|
824
|
-
if (!(bytes > 0))
|
|
833
|
+
if (!(bytes > 0)) {
|
|
834
|
+
stock?.push({ sourceId, skipped: "held", seconds, windowEnd });
|
|
835
|
+
continue;
|
|
836
|
+
}
|
|
825
837
|
const { service } = source;
|
|
826
838
|
const rate = service.tailRate ?? service.rate;
|
|
827
839
|
if (!rate || !(rate.rateAt(0) > 0)) {
|
|
@@ -832,7 +844,7 @@ function scheduleTail({ trackState, sourceState, runStarts, trackFinish, link,
|
|
|
832
844
|
sourceArrivalAt = Math.max(sourceArrivalAt, service.finish);
|
|
833
845
|
// What this stretch weighs and what it still lacks: the stock the start
|
|
834
846
|
// asks to hold is read from these once the start's delay is known.
|
|
835
|
-
stock?.push({ totalBytes:
|
|
847
|
+
stock?.push({ sourceId, seconds, windowEnd, totalBytes:estimateUnmappedSourceBytes(source.source, seconds, duration, []) ?? bytes,
|
|
836
848
|
missingBytes: bytes, bytesPerSecond: rate.rateAt(0), finish: service.finish });
|
|
837
849
|
const state = trackState.find(candidate => candidate.track.sourceIds?.includes(sourceId));
|
|
838
850
|
const index = state?.segments.at(-1)?.index ?? 0;
|