mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-29 17:02:28 +00:00
Merge pull request #1223 from PR0M3TH3AN/video-source-toggle-2596229844924260490
Add CDN/P2P Source Toggle to Video Player
This commit is contained in:
6 files changed
+243
-3
No files matched your search
@@ -129,6 +129,32 @@
|
||||
class="video-modal__stats rounded-2xl border border-border border-opacity-20 bg-surface px-6 py-5 shadow-sm"
|
||||
data-video-stats
|
||||
>
|
||||
<div class="video-modal__source-toggle-container mb-4" data-source-toggle-container hidden>
|
||||
<div class="inline-flex rounded-lg bg-overlay-panel-soft p-1">
|
||||
<button
|
||||
type="button"
|
||||
class="px-3 py-1.5 text-xs font-medium rounded-md transition-colors focus-ring"
|
||||
data-source-toggle="url"
|
||||
aria-pressed="true"
|
||||
>
|
||||
<span class="flex items-center gap-2">
|
||||
<span>CDN</span>
|
||||
<span class="text-xs opacity-70" data-active-servers-count>1 server</span>
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="px-3 py-1.5 text-xs font-medium rounded-md transition-colors focus-ring"
|
||||
data-source-toggle="torrent"
|
||||
aria-pressed="false"
|
||||
>
|
||||
<span class="flex items-center gap-2">
|
||||
<span>P2P</span>
|
||||
<span class="text-xs opacity-70" data-active-peers-count>0 peers</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="video-modal__progress">
|
||||
<progress
|
||||
id="modalProgress"
|
||||
|
||||
@@ -5,4 +5,4 @@
|
||||
--player-modal-inline-padding,clamp(var(--space-md),4vw,var(--space-xl-plus))
|
||||
);overflow:visible}#playerModal .watch-container.video-modal__similar{padding-inline:clamp(var(--space-3xs),.6vw,var(--space-2xs))}#playerModal .video-modal__primary>.watch-container:first-child{padding-block-start:var(--player-modal-vertical-padding)}#playerModal .video-modal__primary>.watch-container:last-child,#playerModal .video-modal__secondary>.watch-container:last-child{padding-block-end:var(--player-modal-vertical-padding)}#playerModal .modal-nav .watch-container{max-width:none;margin-inline:0}#playerModal .modal-info-panel{display:flex;flex-direction:column;gap:var(--space-md-plus)}#playerModal h2#videoTitle{margin:0;font-size:clamp(var(--font-size-xl),calc(var(--font-size-modal-title-base) + .5vw),var(--font-size-xl-plus));font-weight:600;line-height:1.3;color:var(--color-text);min-width:0;overflow-wrap:break-word;word-break:break-word}@media (min-width:1024px){.video-modal__headline #videoTitle.video-modal__title{flex:1 1 0%;min-width:0;max-width:calc(100% - var(--space-3xl) - var(--space-md))}.video-modal__headline .video-modal__status{flex:0 0 auto;max-width:clamp(var(--space-xl),25%,var(--space-3xl));margin-left:auto}}#playerModal .modal-meta{font-size:var(--font-size-sm);line-height:1.4;color:var(--color-text-muted)}#playerModal .modal-view-count{font-weight:600;color:var(--color-text)}#playerModal .channel-card{align-items:center;gap:var(--space-md);padding:var(--space-md) var(--space-lg)}#playerModal .channel-card,#playerModal .player-modal__module{display:flex;border-radius:var(--radius-xl);border:var(--border-width-hairline) solid var(--color-border-translucent);background-color:var(--color-surface);box-shadow:var(--shadow-sm)}#playerModal .player-modal__module{flex-direction:column;gap:clamp(var(--space-2xs),1vw,var(--space-sm));width:100%;padding:clamp(var(--space-xs),1vw,var(--space-sm))}#playerModal .player-modal__module-title{margin:0;font-size:var(--font-size-lg);font-weight:600;line-height:1.3;color:var(--color-text)}#playerModal .player-modal__module-list{display:flex;flex-direction:column;gap:var(--space-sm);width:100%;margin:0;padding:0;list-style:none}#playerModal .video-modal__similar .player-modal__module{gap:clamp(var(--space-3xs),.6vw,var(--space-xs));padding:clamp(var(--space-2xs),.6vw,var(--space-xs));border-width:0;box-shadow:none;background-color:transparent}#playerModal .video-modal__similar .player-modal__module-title{padding-inline:clamp(var(--space-3xs),.6vw,var(--space-2xs))}#playerModal .video-modal__similar .player-modal__module-list{gap:clamp(var(--space-3xs),.6vw,var(--space-xs));padding-inline:clamp(var(--space-3xs),.6vw,var(--space-2xs));padding-bottom:clamp(var(--space-3xs),.6vw,var(--space-2xs))}#playerModal .player-modal__module-scroll{max-height:clamp(var(--player-modal-module-scroll-min-height),calc(100vh - var(--player-modal-vertical-padding)*2 - var(--space-2xl)),var(--player-modal-module-scroll-max-height));overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;scrollbar-gutter:stable;padding-inline-end:var(--space-xs)}#playerModal .video-modal__similar .player-modal__module-scroll{max-height:none;overflow:visible;padding-inline-end:0;scrollbar-gutter:auto}#playerModal .player-modal__module-scroll>li{scroll-margin-block:var(--space-xs)}@media (max-width:30rem){#playerModal .modal-nav .icon-button{width:var(--space-xl);height:var(--space-xl);min-width:var(--space-xl);min-height:var(--space-xl)}#playerModal .watch-container{padding-inline:var(--space-sm)}#playerModal .channel-card{gap:var(--space-sm);padding:var(--space-sm) var(--space-md)}}#playerModal .channel-card__avatar{flex-shrink:0;width:var(--avatar-size-lg);height:var(--avatar-size-lg);border-radius:var(--radius-full);overflow:hidden;background-color:var(--color-border-subtle)}#playerModal .channel-card__avatar img{display:block;width:100%;height:100%;-o-object-fit:cover;object-fit:cover}#playerModal .channel-card__name{margin:0;font-size:var(--font-size-lg);font-weight:600;color:var(--color-text)}#playerModal .channel-card__npub{margin:0;font-size:var(--font-size-sm);color:var(--color-text-muted)}#playerModal .description-panel{padding:var(--space-md-plus) var(--space-lg);border-radius:var(--radius-xl);border:var(--border-width-hairline) solid var(--color-border-translucent);background-color:var(--color-surface);box-shadow:var(--shadow-sm)}#playerModal .description-panel p{margin:0;font-size:var(--font-size-sm);line-height:1.6;color:var(--color-text)}.player-modal__content{position:relative;z-index:var(--z-modal-content);display:flex;flex:1 1 auto;width:100%;min-height:100%;flex-direction:column;overflow:visible;padding:0;margin:0;border:none;border-radius:0;background-color:var(--color-surface);box-shadow:none}@media (prefers-reduced-motion:reduce){.player-modal__content{transition-property:none}}#playerModal .modal-nav{position:sticky;top:0;z-index:var(--z-overlay-nav);--tw-translate-y:0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) sLine truncated
|
||||
--player-modal-primary-inline-padding-lg,clamp(var(--space-sm),2.75vw,var(--space-lg))
|
||||
)}}.revert-modal__layout{display:grid;gap:1.5rem}@media (min-width:1024px){.revert-modal__layout{grid-template-columns:minmax(var(--revert-modal-sidebar-min),var(--revert-modal-sidebar-width)) minmax(0,1fr)}}#playerModal.video-modal__primary{display:block}.video-modal__primary{display:flex;flex-direction:column;gap:1.5rem;min-width:0;min-height:0}#playerModal.video-modal__secondary{display:block}.video-modal__secondary{display:flex;flex-direction:column;gap:1rem;min-width:0;min-height:0;width:100%;align-self:stretch}.video-modal__similar{display:none}@media (min-width:1024px){#playerModal.video-modal__similar{display:block}.video-modal__similar{display:flex;flex-direction:column;gap:1.5rem;align-self:stretch;flex:1 1 auto;min-height:0}#playerModal .video-modal__similar .player-modal__module{height:100%;display:flex;flex:1 1 auto;min-height:0;flex-direction:column}#playerModal .video-modal__similar .player-modal__module-list{flex:1 1 auto;min-height:0}#playerModal .video-modal__similar .player-modal__module-scroll{height:100%;max-height:none;min-height:0}}#playerModal .player-modal__module-item{width:100%}#playerModal#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card){display:block}#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card){display:flex;width:100%;flex-direction:row;flex-wrap:nowrap;align-items:stretch;gap:var(--space-md)}#playerModal#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card){display:block}#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card){display:flex;width:100%;flex-direction:row;flex-wrap:nowrap;align-items:stretch;gap:var(--space-md)}#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card) .video-card__media{width:clamp(var(--video-modal-item-media-min),18vw,var(--video-modal-item-media-max))}#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card) .video-card__media{width:clamp(var(--video-modal-item-media-min),18vw,var(--video-modal-item-media-max))!important}#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card) .video-card__media,#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card) .video-card__media{flex-shrink:0;overflow:hidden;border-radius:var(--radius-lg)}#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card) .video-card__content{padding:clamp(var(--space-sm),1.25vw,var(--space-md))}#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card) .video-card__content{padding:clamp(var(--space-sm),1.25vw,var(--space-md))!important}#playerModal#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card) .video-card__content{display:block}#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card) .video-card__content{display:flex;min-width:0;flex:1 1 0%;flex-direction:column;gap:var(--space-sm)}#playerModal#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card) .video-card__content{display:block}#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card) .video-card__content{display:flex;min-width:0;flex:1 1 0%;flex-direction:column;gap:var(--space-sm)}#playerModal#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card) .video-card__meta{display:block}#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card) .video-card__meta{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem;font-size:var(--font-size-xs);line-height:var(--line-height-tight);color:var(--text-muted-strong)}#playerModal#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card) .video-card__meta{display:block}#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card) .video-card__meta{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem;font-size:var(--font-size-xs);line-height:var(--line-height-tight);color:var(--text-muted-strong)}#playerModal .video-modal__similar .player-modal__module-list{gap:clamp(var(--space-2xs),.85vw,var(--space-sm))}.player-modal__similar-card{outline:2px solid transparent;outline-offset:2px}.player-modal__similar-card:focus-visible{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-opacity:0.7;--tw-ring-offset-width:2px;--tw-ring-offset-color:var(--surface-base);transition-property:box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s;transition-duration:var(--motion-duration-fast);transition-timing-function:cubic-bezier(0,0,.2,1);--tw-ring-color:var(--color-accent)}#playerModal.player-modal__similar-card{display:block}.player-modal__similar-card{position:relative;displayLine truncated
|
||||
)}}.revert-modal__layout{display:grid;gap:1.5rem}@media (min-width:1024px){.revert-modal__layout{grid-template-columns:minmax(var(--revert-modal-sidebar-min),var(--revert-modal-sidebar-width)) minmax(0,1fr)}}#playerModal.video-modal__primary{display:block}.video-modal__primary{display:flex;flex-direction:column;gap:1.5rem;min-width:0;min-height:0}#playerModal.video-modal__secondary{display:block}.video-modal__secondary{display:flex;flex-direction:column;gap:1rem;min-width:0;min-height:0;width:100%;align-self:stretch}.video-modal__similar{display:none}@media (min-width:1024px){#playerModal.video-modal__similar{display:block}.video-modal__similar{display:flex;flex-direction:column;gap:1.5rem;align-self:stretch;flex:1 1 auto;min-height:0}#playerModal .video-modal__similar .player-modal__module{height:100%;display:flex;flex:1 1 auto;min-height:0;flex-direction:column}#playerModal .video-modal__similar .player-modal__module-list{flex:1 1 auto;min-height:0}#playerModal .video-modal__similar .player-modal__module-scroll{height:100%;max-height:none;min-height:0}}#playerModal .player-modal__module-item{width:100%}#playerModal#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card){display:block}#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card){display:flex;width:100%;flex-direction:row;flex-wrap:nowrap;align-items:stretch;gap:var(--space-md)}#playerModal#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card){display:block}#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card){display:flex;width:100%;flex-direction:row;flex-wrap:nowrap;align-items:stretch;gap:var(--space-md)}#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card) .video-card__media{width:clamp(var(--video-modal-item-media-min),18vw,var(--video-modal-item-media-max))}#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card) .video-card__media{width:clamp(var(--video-modal-item-media-min),18vw,var(--video-modal-item-media-max))!important}#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card) .video-card__media,#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card) .video-card__media{flex-shrink:0;overflow:hidden;border-radius:var(--radius-lg)}#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card) .video-card__content{padding:clamp(var(--space-sm),1.25vw,var(--space-md))}#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card) .video-card__content{padding:clamp(var(--space-sm),1.25vw,var(--space-md))!important}#playerModal#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card) .video-card__content{display:block}#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card) .video-card__content{display:flex;min-width:0;flex:1 1 0%;flex-direction:column;gap:var(--space-sm)}#playerModal#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card) .video-card__content{display:block}#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card) .video-card__content{display:flex;min-width:0;flex:1 1 0%;flex-direction:column;gap:var(--space-sm)}#playerModal#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card) .video-card__meta{display:block}#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card) .video-card__meta{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem;font-size:var(--font-size-xs);line-height:var(--line-height-tight);color:var(--text-muted-strong)}#playerModal#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card) .video-card__meta{display:block}#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card) .video-card__meta{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem;font-size:var(--font-size-xs);line-height:var(--line-height-tight);color:var(--text-muted-strong)}#playerModal .video-modal__similar .player-modal__module-list{gap:clamp(var(--space-2xs),.85vw,var(--space-sm))}.player-modal__similar-card{outline:2px solid transparent;outline-offset:2px}.player-modal__similar-card:focus-visible{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-opacity:0.7;--tw-ring-offset-width:2px;--tw-ring-offset-color:var(--surface-base);transition-property:box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s;transition-duration:var(--motion-duration-fast);transition-timing-function:cubic-bezier(0,0,.2,1);--tw-ring-color:var(--color-accent)}#playerModal.player-modal__similar-card{display:block}.player-modal__similar-card{position:relative;displayLine truncated
|
||||
@@ -390,6 +390,30 @@ class Application {
|
||||
if (modalRoot) {
|
||||
this.attachMoreMenuHandlers(modalRoot);
|
||||
}
|
||||
if (
|
||||
this.videoModal &&
|
||||
typeof this.videoModal.addEventListener === "function"
|
||||
) {
|
||||
this.videoModal.addEventListener("playback:switch-source", (event) => {
|
||||
const detail = event?.detail || {};
|
||||
const { source } = detail;
|
||||
if (!source) {
|
||||
return;
|
||||
}
|
||||
if (this.currentVideo) {
|
||||
this.playVideoWithFallback({
|
||||
url: this.currentVideo.url,
|
||||
magnet: this.currentVideo.magnet,
|
||||
forcedSource: source,
|
||||
}).catch((error) => {
|
||||
devLogger.warn(
|
||||
"[app] Failed to switch playback source:",
|
||||
error,
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const uploadModalPromise = this.uploadModal
|
||||
@@ -8209,7 +8233,7 @@ class Application {
|
||||
* and falls back to WebTorrent when needed.
|
||||
*/
|
||||
async playVideoWithFallback(options = {}) {
|
||||
const { url = "", magnet = "", trigger } = options || {};
|
||||
const { url = "", magnet = "", trigger, forcedSource } = options || {};
|
||||
const hasTrigger = Object.prototype.hasOwnProperty.call(
|
||||
options || {},
|
||||
"trigger"
|
||||
@@ -8223,6 +8247,7 @@ class Application {
|
||||
const requestSignature = JSON.stringify({
|
||||
url: sanitizedUrl,
|
||||
magnet: trimmedMagnet,
|
||||
forcedSource,
|
||||
});
|
||||
|
||||
const modalVideoIsConnected = (() => {
|
||||
@@ -8374,6 +8399,7 @@ class Application {
|
||||
this.playViaWebTorrent(magnetUri, options),
|
||||
autoplay: () => this.autoplayModalVideo(),
|
||||
unsupportedBtihMessage: UNSUPPORTED_BTITH_MESSAGE,
|
||||
forcedSource,
|
||||
});
|
||||
|
||||
this.activePlaybackSession = session;
|
||||
|
||||
@@ -409,6 +409,7 @@ class PlaybackSession extends SimpleEventEmitter {
|
||||
playViaWebTorrent,
|
||||
autoplay,
|
||||
unsupportedBtihMessage = DEFAULT_UNSUPPORTED_BTITH_MESSAGE,
|
||||
forcedSource = null,
|
||||
} = this.options;
|
||||
|
||||
this.result = { source: null };
|
||||
@@ -555,7 +556,11 @@ class PlaybackSession extends SimpleEventEmitter {
|
||||
return torrentInstance;
|
||||
};
|
||||
|
||||
if (this.service.urlFirstEnabled && httpsUrl) {
|
||||
if (
|
||||
this.service.urlFirstEnabled &&
|
||||
httpsUrl &&
|
||||
forcedSource !== "torrent"
|
||||
) {
|
||||
this.emit("status", { message: "Checking hosted URL..." });
|
||||
this.service.log(
|
||||
`[playVideoWithFallback] Probing hosted URL ${httpsUrl} (readyState=${activeVideoEl.readyState} networkState=${activeVideoEl.networkState}).`
|
||||
|
||||
@@ -585,6 +585,14 @@ export class VideoModal {
|
||||
this.modalSpeed = playerModal.querySelector("#modalSpeed") || null;
|
||||
this.modalDownloaded =
|
||||
playerModal.querySelector("#modalDownloaded") || null;
|
||||
this.sourceToggleContainer =
|
||||
playerModal.querySelector("[data-source-toggle-container]") || null;
|
||||
this.sourceToggleButtons =
|
||||
playerModal.querySelectorAll("[data-source-toggle]") || [];
|
||||
this.activeServersLabel =
|
||||
playerModal.querySelector("[data-active-servers-count]") || null;
|
||||
this.activePeersLabel =
|
||||
playerModal.querySelector("[data-active-peers-count]") || null;
|
||||
this.setTorrentStatsVisibility(false);
|
||||
this.videoTitle = playerModal.querySelector("#videoTitle") || null;
|
||||
this.videoDescription =
|
||||
@@ -2244,6 +2252,19 @@ export class VideoModal {
|
||||
this.modalMoreBtn.addEventListener("click", this.handleModalMoreButtonClick);
|
||||
}
|
||||
|
||||
if (this.sourceToggleButtons.length > 0) {
|
||||
this.sourceToggleButtons.forEach((btn) => {
|
||||
btn.addEventListener("click", (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const mode = btn.dataset.sourceToggle;
|
||||
if (mode) {
|
||||
this.handleSourceToggle(mode);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (this.modalZapCloseBtn) {
|
||||
this.modalZapCloseBtn.addEventListener("click", (event) => {
|
||||
event?.preventDefault?.();
|
||||
@@ -2562,6 +2583,67 @@ export class VideoModal {
|
||||
this.dispatch("video:copy-magnet", { video: this.activeVideo });
|
||||
}
|
||||
|
||||
handleSourceToggle(mode) {
|
||||
if (!this.activeVideo || (mode !== "url" && mode !== "torrent")) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.dispatch("playback:switch-source", {
|
||||
video: this.activeVideo,
|
||||
source: mode,
|
||||
});
|
||||
}
|
||||
|
||||
updateSourceAvailability(video) {
|
||||
if (!this.sourceToggleContainer) {
|
||||
return;
|
||||
}
|
||||
|
||||
const hasUrl = Boolean(
|
||||
typeof video?.url === "string" && video.url.trim()
|
||||
);
|
||||
const hasMagnet = Boolean(
|
||||
(typeof video?.magnet === "string" && video.magnet.trim()) ||
|
||||
video?.torrentSupported
|
||||
);
|
||||
|
||||
if (hasUrl || hasMagnet) {
|
||||
this.sourceToggleContainer.removeAttribute("hidden");
|
||||
this.sourceToggleContainer.classList.remove("hidden");
|
||||
} else {
|
||||
this.sourceToggleContainer.setAttribute("hidden", "");
|
||||
this.sourceToggleContainer.classList.add("hidden");
|
||||
}
|
||||
|
||||
this.sourceToggleButtons.forEach((btn) => {
|
||||
const mode = btn.dataset.sourceToggle;
|
||||
if (mode === "url") {
|
||||
btn.disabled = !hasUrl;
|
||||
} else if (mode === "torrent") {
|
||||
btn.disabled = !hasMagnet;
|
||||
}
|
||||
});
|
||||
|
||||
if (this.activeServersLabel) {
|
||||
const count = hasUrl ? 1 : 0;
|
||||
this.activeServersLabel.textContent =
|
||||
count === 1 ? "1 server" : `${count} servers`;
|
||||
}
|
||||
}
|
||||
|
||||
updateSourceToggleState(activeSource) {
|
||||
if (!this.sourceToggleButtons.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const current = activeSource === "torrent" ? "torrent" : "url";
|
||||
this.sourceToggleButtons.forEach((btn) => {
|
||||
const mode = btn.dataset.sourceToggle;
|
||||
const isActive = mode === current;
|
||||
btn.setAttribute("aria-pressed", isActive ? "true" : "false");
|
||||
});
|
||||
}
|
||||
|
||||
handleShareRequest(event) {
|
||||
event?.preventDefault?.();
|
||||
if (this.shareBtn?.disabled) {
|
||||
@@ -2676,6 +2758,7 @@ export class VideoModal {
|
||||
}
|
||||
this.setGlobalModalState("player", true);
|
||||
this.applyLoadingPoster();
|
||||
this.updateSourceAvailability(this.activeVideo);
|
||||
this.refreshActiveVideoModeration({ video: this.activeVideo });
|
||||
}
|
||||
|
||||
@@ -3262,6 +3345,8 @@ export class VideoModal {
|
||||
|
||||
setTorrentStatsVisibility(shouldShow) {
|
||||
const visible = Boolean(shouldShow);
|
||||
this.updateSourceToggleState(visible ? "torrent" : "url");
|
||||
|
||||
const container = this.modalStatsContainer;
|
||||
const stats = this.modalStats;
|
||||
const toggle = (element, show) => {
|
||||
@@ -3300,6 +3385,12 @@ export class VideoModal {
|
||||
if (this.modalPeers) {
|
||||
this.modalPeers.textContent = text || "";
|
||||
}
|
||||
if (this.activePeersLabel) {
|
||||
const match = (text || "").match(/\d+/);
|
||||
const count = match ? parseInt(match[0], 10) : 0;
|
||||
this.activePeersLabel.textContent =
|
||||
count === 1 ? "1 peer" : `${count} peers`;
|
||||
}
|
||||
}
|
||||
|
||||
updateSpeed(text) {
|
||||
@@ -4076,6 +4167,7 @@ export class VideoModal {
|
||||
this.updateCreator(creator);
|
||||
}
|
||||
|
||||
this.updateSourceAvailability(this.activeVideo);
|
||||
this.refreshActiveVideoModeration({ video: this.activeVideo });
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
from playwright.sync_api import sync_playwright
|
||||
import time
|
||||
|
||||
def verify_source_toggle():
|
||||
with sync_playwright() as p:
|
||||
browser = p.chromium.launch(headless=True)
|
||||
page = browser.new_page()
|
||||
|
||||
# Navigate to the local server
|
||||
page.goto("http://localhost:8080/index.html")
|
||||
|
||||
# Wait for the app to initialize (give it a moment)
|
||||
page.wait_for_load_state("networkidle")
|
||||
|
||||
# We need to simulate opening the modal with a video that has both sources
|
||||
# We'll use page.evaluate to interact with the Javascript context
|
||||
# Assuming 'app' is globally available or we can instantiate VideoModal
|
||||
|
||||
page.evaluate("""
|
||||
async () => {
|
||||
// Mock a video object with both URL and Magnet
|
||||
const mockVideo = {
|
||||
id: 'test-video-123',
|
||||
title: 'Test Video with Source Toggle',
|
||||
url: 'https://example.com/video.mp4',
|
||||
magnet: 'magnet:?xt=urn:btih:example',
|
||||
torrentSupported: true,
|
||||
moderation: {}
|
||||
};
|
||||
|
||||
// Access the global app instance or find the video modal
|
||||
// Looking at js/app.js, it might attach to window.app
|
||||
// If not, we might need to find the modal instance another way.
|
||||
|
||||
// Let's assume window.app exists as is common in these setups,
|
||||
// or we try to find the modal controller.
|
||||
|
||||
if (window.app && window.app.videoModal) {
|
||||
await window.app.videoModal.open(mockVideo);
|
||||
} else {
|
||||
console.error("Could not find window.app.videoModal");
|
||||
// Fallback: Try to find the modal element and manually unhide it
|
||||
// to verify the toggle structure exists, though open() is better.
|
||||
|
||||
// Note: If window.app isn't exposed, we might struggle to invoke .open() directly
|
||||
// without module injection.
|
||||
}
|
||||
}
|
||||
""")
|
||||
|
||||
# Wait for the modal to appear
|
||||
try:
|
||||
page.wait_for_selector("#playerModal", state="visible", timeout=5000)
|
||||
print("Modal opened successfully.")
|
||||
except:
|
||||
print("Modal did not open via app.videoModal. attempting manual check...")
|
||||
|
||||
# specific check for the toggle container
|
||||
# The toggle container has attribute [data-source-toggle-container]
|
||||
toggle_selector = "[data-source-toggle-container]"
|
||||
|
||||
# Expect it to be visible
|
||||
try:
|
||||
page.wait_for_selector(toggle_selector, state="visible", timeout=2000)
|
||||
print("Source toggle container is visible!")
|
||||
except:
|
||||
print("Source toggle container NOT visible or not found.")
|
||||
# If it's not visible, maybe the video didn't load right.
|
||||
# Let's try to inspect the DOM to see if the element exists at all.
|
||||
content = page.content()
|
||||
if "data-source-toggle-container" in content:
|
||||
print("Element exists in DOM but might be hidden.")
|
||||
else:
|
||||
print("Element does NOT exist in DOM.")
|
||||
|
||||
# Take a screenshot of the modal
|
||||
# We can focus on the stats container where the toggle is
|
||||
stats_selector = "[data-video-stats-container]"
|
||||
try:
|
||||
page.wait_for_selector(stats_selector, state="visible")
|
||||
# Screenshot the whole page or just the modal
|
||||
page.screenshot(path="/home/jules/verification/verification.png")
|
||||
print("Screenshot saved.")
|
||||
except Exception as e:
|
||||
print(f"Could not take targeted screenshot: {e}")
|
||||
page.screenshot(path="/home/jules/verification/verification_fallback.png")
|
||||
|
||||
browser.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
verify_source_toggle()
|
||||
Reference in new issue
Block a user