mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-02 02:42:28 +00:00
Handle torrent cleanup before hosted playback
This commit is contained in:
1 parent
0e5571d5a8
commit
b0cc773315
2 files changed
+176
-4
No files matched your search
@@ -1434,6 +1434,21 @@ class Application {
|
||||
|
||||
if (!needsRehydrate) {
|
||||
this.modalVideo = existingVideoElement;
|
||||
|
||||
if (
|
||||
existingVideoElement &&
|
||||
this.videoModal &&
|
||||
typeof this.videoModal.setVideoElement === "function"
|
||||
) {
|
||||
const modalVideo =
|
||||
typeof this.videoModal.getVideoElement === "function"
|
||||
? this.videoModal.getVideoElement()
|
||||
: null;
|
||||
if (modalVideo !== existingVideoElement) {
|
||||
this.videoModal.setVideoElement(existingVideoElement);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
root: existingRoot,
|
||||
videoElement: existingVideoElement,
|
||||
@@ -1468,6 +1483,12 @@ class Application {
|
||||
|
||||
if (readyVideoConnected) {
|
||||
this.modalVideo = readyVideoElement;
|
||||
if (
|
||||
this.videoModal &&
|
||||
typeof this.videoModal.setVideoElement === "function"
|
||||
) {
|
||||
this.videoModal.setVideoElement(readyVideoElement);
|
||||
}
|
||||
} else {
|
||||
this.modalVideo = null;
|
||||
}
|
||||
@@ -6231,6 +6252,7 @@ class Application {
|
||||
}
|
||||
const sanitizedUrl = typeof url === "string" ? url.trim() : "";
|
||||
const trimmedMagnet = typeof magnet === "string" ? magnet.trim() : "";
|
||||
const previousSource = this.playSource || null;
|
||||
const requestSignature = JSON.stringify({
|
||||
url: sanitizedUrl,
|
||||
magnet: trimmedMagnet,
|
||||
@@ -6256,7 +6278,35 @@ class Application {
|
||||
await this.waitForCleanup();
|
||||
this.cancelPendingViewLogging();
|
||||
|
||||
if (
|
||||
previousSource === "torrent" &&
|
||||
sanitizedUrl &&
|
||||
this.playbackService &&
|
||||
this.playbackService.torrentClient &&
|
||||
typeof this.playbackService.torrentClient.cleanup === "function"
|
||||
) {
|
||||
try {
|
||||
this.log(
|
||||
"[playVideoWithFallback] Previous playback used WebTorrent; cleaning up before preparing hosted session.",
|
||||
);
|
||||
await this.playbackService.torrentClient.cleanup();
|
||||
} catch (error) {
|
||||
devLogger.warn(
|
||||
"[playVideoWithFallback] Pre-playback torrent cleanup threw:",
|
||||
error,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let modalVideoEl = this.modalVideo;
|
||||
const modalVideoFromController =
|
||||
this.videoModal && typeof this.videoModal.getVideoElement === "function"
|
||||
? this.videoModal.getVideoElement()
|
||||
: null;
|
||||
if (modalVideoFromController && modalVideoFromController !== modalVideoEl) {
|
||||
modalVideoEl = modalVideoFromController;
|
||||
this.modalVideo = modalVideoEl;
|
||||
}
|
||||
if (!modalVideoEl) {
|
||||
try {
|
||||
const { videoElement } = await this.ensureVideoModalReady({
|
||||
|
||||
@@ -183,16 +183,28 @@ function createVideoModalPlaybackStub(document) {
|
||||
}
|
||||
|
||||
function createPlaybackService(records) {
|
||||
const torrentClient = {
|
||||
cleanupCalls: 0,
|
||||
async cleanup() {
|
||||
this.cleanupCalls += 1;
|
||||
return undefined;
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
torrentClient,
|
||||
createSession(options) {
|
||||
const record = {
|
||||
options,
|
||||
videoElement: options.videoElement,
|
||||
isConnectedDuringStart: false,
|
||||
source: null,
|
||||
};
|
||||
records.push(record);
|
||||
|
||||
return {
|
||||
const listeners = new Map();
|
||||
|
||||
const session = {
|
||||
matchesRequestSignature(signature) {
|
||||
return signature === options.requestSignature;
|
||||
},
|
||||
@@ -200,16 +212,56 @@ function createPlaybackService(records) {
|
||||
getMagnetForPlayback: () => "",
|
||||
getFallbackMagnet: () => "",
|
||||
getMagnetProvided: () => Boolean(options.magnet),
|
||||
on: () => () => {},
|
||||
start: () => {
|
||||
on(eventName, handler) {
|
||||
if (typeof handler !== "function") {
|
||||
return () => {};
|
||||
}
|
||||
if (!listeners.has(eventName)) {
|
||||
listeners.set(eventName, new Set());
|
||||
}
|
||||
const handlers = listeners.get(eventName);
|
||||
handlers.add(handler);
|
||||
return () => {
|
||||
handlers.delete(handler);
|
||||
if (!handlers.size) {
|
||||
listeners.delete(eventName);
|
||||
}
|
||||
};
|
||||
},
|
||||
emit(eventName, detail) {
|
||||
const handlers = listeners.get(eventName);
|
||||
if (!handlers || !handlers.size) {
|
||||
return;
|
||||
}
|
||||
for (const handler of Array.from(handlers)) {
|
||||
try {
|
||||
handler(detail);
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
`[tests] session listener for "${eventName}" threw`,
|
||||
error,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
async start() {
|
||||
const element = options.videoElement || null;
|
||||
record.videoElement = element;
|
||||
record.isConnectedDuringStart = Boolean(
|
||||
element && element.isConnected,
|
||||
);
|
||||
return Promise.resolve({ source: "url" });
|
||||
const hasUrl = Boolean(options.url && options.url.trim());
|
||||
const hasMagnet = Boolean(options.magnet && options.magnet.trim());
|
||||
record.source = hasUrl ? "url" : hasMagnet ? "torrent" : null;
|
||||
this.emit("video-prepared", { videoElement: element });
|
||||
if (record.source) {
|
||||
this.emit("sourcechange", { source: record.source });
|
||||
}
|
||||
return { source: record.source };
|
||||
},
|
||||
};
|
||||
|
||||
return session;
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -409,6 +461,76 @@ test(
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
"torrent-only playback resets the modal video before the next hosted session",
|
||||
async (t) => {
|
||||
const { app, modalStub, playbackRecords, cleanup } =
|
||||
await setupPlaybackHarness();
|
||||
t.after(cleanup);
|
||||
|
||||
const magnet = `magnet:?xt=urn:btih:${"a".repeat(40)}`;
|
||||
await app.playVideoWithFallback({ magnet });
|
||||
|
||||
assert.equal(
|
||||
playbackRecords.length,
|
||||
1,
|
||||
"magnet playback should create the first session",
|
||||
);
|
||||
const torrentRecord = playbackRecords[0];
|
||||
assert.equal(
|
||||
torrentRecord.source,
|
||||
"torrent",
|
||||
"first session should report torrent playback",
|
||||
);
|
||||
const torrentVideo = torrentRecord.videoElement;
|
||||
assert.ok(torrentVideo, "torrent session should supply a video element");
|
||||
assert.equal(
|
||||
Boolean(torrentVideo?.isConnected),
|
||||
true,
|
||||
"torrent playback should start with a connected element",
|
||||
);
|
||||
|
||||
const playbackUrl = "https://cdn.example.com/next-video.mp4";
|
||||
await app.playVideoWithFallback({ url: playbackUrl });
|
||||
|
||||
assert.equal(
|
||||
playbackRecords.length,
|
||||
2,
|
||||
"subsequent hosted playback should create a second session",
|
||||
);
|
||||
const hostedRecord = playbackRecords[1];
|
||||
assert.equal(
|
||||
hostedRecord.source,
|
||||
"url",
|
||||
"second session should report hosted playback",
|
||||
);
|
||||
assert.ok(hostedRecord.videoElement, "hosted playback needs a video element");
|
||||
assert.equal(
|
||||
Boolean(hostedRecord.videoElement?.isConnected),
|
||||
true,
|
||||
"hosted playback should reuse a connected element",
|
||||
);
|
||||
assert.notStrictEqual(
|
||||
hostedRecord.videoElement,
|
||||
torrentVideo,
|
||||
"hosted playback should swap in a refreshed video element",
|
||||
);
|
||||
assert.strictEqual(
|
||||
modalStub.getVideoElement(),
|
||||
hostedRecord.videoElement,
|
||||
"modal should expose the refreshed hosted video element",
|
||||
);
|
||||
assert.ok(
|
||||
modalStub.setVideoElementCalls.includes(hostedRecord.videoElement),
|
||||
"modal controller should receive the refreshed hosted element",
|
||||
);
|
||||
assert.ok(
|
||||
app.playbackService.torrentClient.cleanupCalls >= 1,
|
||||
"torrent cleanup should run before the hosted playback begins",
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test("backdrop data-dismiss closes the modal and restores focus", async (t) => {
|
||||
const { window, document, modal, playerModal, trigger, cleanup } =
|
||||
await setupModal();
|
||||
|
||||
Reference in new issue
Block a user