← Commit history

adom-video: private video blocked in the frame (third-party cookies) -> clear message + Play it in a new tab; F opens the file itself after a failure

John Lauer ·566b5d3a4c ·7d ago ·parent 2849c3a
1 file changed +24−4
widgets/video-widget.js+24−4
@@ -184,7 +184,15 @@   @keyframes sp{to{transform:rotate(360deg)}}   .err{position:absolute;inset:0;display:none;align-items:center;justify-content:center;text-align:center;padding:24px;background:#111;font-size:14px;line-height:1.5}   .err.on{display:flex}+  .failed .chrome,.failed .grad,.failed .big,.failed .top{display:none}+  .err{z-index:8}   .err code{background:rgba(255,255,255,.1);padding:1px 5px;border-radius:4px;word-break:break-all}+  .err>div{display:flex;flex-direction:column;align-items:center;gap:14px;max-width:560px}+  .errt{font-size:17px;font-weight:600}+  .errbtn{display:inline-flex;align-items:center;gap:8px;padding:10px 18px;border-radius:22px;background:var(--accent);color:#04201e;font-weight:600}+  .errbtn svg{fill:#04201e}+  .errbtn:hover{background:var(--accent-hi)}+  .errn{opacity:.75;font-size:13px}   /* captions */   .caps{position:absolute;left:5%;right:5%;bottom:5%;display:flex;flex-wrap:wrap;justify-content:center;pointer-events:none;transition:bottom .25s;z-index:2}   .p:not(.hide-ui) .caps{bottom:76px}@@ -341,7 +349,7 @@       this._chapters(); this._captions(); this._volUI();     } -    _error(html) { const e = this.$('.err'); e.innerHTML = `<div>${html}</div>`; e.classList.add('on'); this.p.classList.remove('wait'); }+    _error(html) { const e = this.$('.err'); e.innerHTML = `<div>${html}</div>`; e.classList.add('on'); this.p.classList.remove('wait'); this.p.classList.add('failed'); }      async _fetchText(u) {       const r = await fetch(this._abs(u)).catch(() => null);@@ -553,7 +561,8 @@     popout() {       const t = Math.floor(this.v.currentTime);       let url;-      if (location.pathname.includes('/widget/')) { const u = new URL(location.href); u.searchParams.set('start', String(t)); url = u.href; }+      if (this._failed) url = (this.v.currentSrc || this.v.src).split('#')[0] + `#t=${t}`;+      else if (location.pathname.includes('/widget/')) { const u = new URL(location.href); u.searchParams.set('start', String(t)); url = u.href; }       else url = (this.v.currentSrc || this.v.src).split('#')[0] + `#t=${t}`;       this.v.pause();       this._openTab(url);@@ -657,9 +666,20 @@       v.addEventListener('waiting', () => p.classList.add('wait'));       ['playing', 'canplay', 'seeked', 'pause'].forEach((e) => v.addEventListener(e, () => p.classList.remove('wait')));       v.addEventListener('seeked', () => { this._seekBusy = false; if (this._seekPending != null) { const t = this._seekPending; this._seekPending = null; this._seekBusy = true; v.fastSeek ? v.fastSeek(t) : (v.currentTime = t); } });+      // A private page's video 404s inside the frame when the browser blocks third-party cookies (Safari+      // always, Chrome with that setting): the sandboxed frame is opaque-origin, so the wiki login is not+      // sent. Opened as a page of its own the file request is first-party and plays, with seeking.       v.addEventListener('error', () => {-        const s = this.getAttribute('video') || this.getAttribute('src') || '';-        this._error(`This video could not be loaded.<br><code>${esc(s)}</code><br><br>Check the path is <code>/OWNER/SLUG/render/FILE.mp4</code> and that the file is on the page. On a private page the widget frame may not be signed in.`);+        if (!v.currentSrc && !v.src) return;+        this._failed = true;+        const s = this.getAttribute('video') || (this._manifest() || {}).video || this.getAttribute('src') || '';+        const direct = (v.currentSrc || v.src).split('#')[0];+        this._error(`<div class="errt">This video can't play inside the page</div>+          <button class="errbtn">${svg('play', 20)} Play it in a new tab</button>+          <div class="errn">On a private page, most browsers keep your wiki login out of embedded players, so the+          video is refused here but plays in its own tab. If it fails there too, the file is missing:+          check <code>${esc(s)}</code>.</div>`);+        const b = this.$('.errbtn'); if (b) b.onclick = () => this._openTab(direct + (v.currentTime > 1 ? `#t=${Math.floor(v.currentTime)}` : ''));       });       document.addEventListener('fullscreenchange', () => this._sync());       if (v.requestVideoFrameCallback) {