Taylor Swift - The Fate of Ophelia (Music Video) | Apple Music 1080p

Discussion in 'Music Videos' started by tutorgirl, Oct 12, 2025.

  1. speciald

    speciald Well-Known Member

    Joined:
    Dec 18, 2015
    Threads:
    26
    Messages:
    1,334
    Likes Received:
    148
  2. russsss19

    russsss19 New Member

    Joined:
    Sep 3, 2025
    Threads:
    0
    Messages:
    7
    Likes Received:
    0
    thank you <3
     
  3. KotakstanAYE

    KotakstanAYE Member

    Joined:
    Feb 17, 2025
    Threads:
    5
    Messages:
    540
    Likes Received:
    12
    wow thanks
     
  4. ratchet

    ratchet Member

    Joined:
    Mar 4, 2016
    Threads:
    0
    Messages:
    328
    Likes Received:
    10
    thanks
     
  5. dersullana2020

    dersullana2020 Well-Known Member

    Joined:
    Apr 26, 2020
    Threads:
    7
    Messages:
    1,907
    Likes Received:
    129
  6. usersjeonghan

    usersjeonghan Member

    Joined:
    Aug 13, 2023
    Threads:
    3
    Messages:
    855
    Likes Received:
    17
  7. Imtriangle

    Imtriangle Active Member

    Joined:
    May 21, 2020
    Threads:
    13
    Messages:
    436
    Likes Received:
    40
  8. bcstoneman

    bcstoneman Member

    Joined:
    Jun 30, 2017
    Threads:
    0
    Messages:
    32
    Likes Received:
    3
  9. mike duff

    mike duff Member

    Joined:
    Oct 7, 2014
    Threads:
    3
    Messages:
    268
    Likes Received:
    18
  10. AminGold

    AminGold Well-Known Member Uploader

    Joined:
    Feb 15, 2022
    Threads:
    373
    Messages:
    3,137
    Likes Received:
    1,985
  11. nicko.mnj

    nicko.mnj Well-Known Member

    Joined:
    Apr 29, 2017
    Threads:
    118
    Messages:
    2,391
    Likes Received:
    794
    Thank you !
     
  12. harvey123

    harvey123 Well-Known Member

    Joined:
    Oct 31, 2016
    Threads:
    0
    Messages:
    1,187
    Likes Received:
    60
    Thanks!
     
  13. Gamertube

    Gamertube Member

    Joined:
    Aug 6, 2024
    Threads:
    3
    Messages:
    322
    Likes Received:
    12
    <!doctype html>
    <html lang="fr">
    <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <title>Lecteur rediffusion Twitch + Chat (instant load)</title>
    <style>
    :root{ --bg1:#070718; --bg2:#0b0f2b; --accent1:#4b3bff; --accent2:#6c2dd1; --panel:rgba(10,14,30,0.6); --glass:rgba(255,255,255,0.03); --text:#dfe7ff; --muted:#9aa6d6; }
    html,body{height:100%;margin:0;font-family:Inter,Segoe UI,system-ui,-apple-system,Roboto,"Helvetica Neue",Arial;background:linear-gradient(180deg,var(--bg1),var(--bg2));color:var(--text);}
    body{display:flex;align-items:center;justify-content:center;overflow:hidden}
    .liquid-bg{position:fixed;inset:0;pointer-events:none;mix-blend-mode:screen;opacity:0.12;filter: blur(20px) saturate(1.1);}
    .app{width:96%;height:92%;display:flex;gap:18px;align-items:center;justify-content:flex-start;position:relative}
    .side-pill{position:absolute; left:14px; top:50%; transform:translateY(-50%); width:12px; border-radius:999px; z-index:40; box-shadow: 0 6px 18px rgba(0,0,0,0.5), inset 0 -2px 8px rgba(0,0,0,0.15); overflow:visible; display:flex;align-items:center;justify-content:center; padding:6px 0; background: linear-gradient(180deg,#2a0033 0%, #3c0a4d 40%, #10203a 100% ); background-size: 300% 300%; animation: gradientShift 8s ease-in-out infinite; border:1px solid rgba(255,255,255,0.03);} @keyframes gradientShift{0%{background-position:0% 50%}50%{background-position:100% 50%}100%{background-position:0% 50%}} .side-pill .nav-dot{width:8px;height:8px;border-radius:3px;margin:8px 0;background:rgba(255,255,255,0.06);}
    .video-wrap{flex:1.4 1 0;display:flex;align-items:center;justify-content:flex-start;padding-left:64px;height:100%;box-sizing:border-box}
    .player-card{flex:1; height:86%; background:linear-gradient(180deg, rgba(255,255,255,0.02), rgba(255,255,255,0.01)); border-radius:18px; padding:12px; box-shadow: 0 10px 40px rgba(8,10,25,0.6); display:flex; flex-direction:column; gap:8px; position:relative;}
    .player-viewport{ width:100%; aspect-ratio: 16 / 9; border-radius:12px; overflow:hidden; background:#000; position:relative; }
    .player-viewport video{ width:100%; height:100%; object-fit:cover; display:block; }
    .overlay-bubbles{ position:absolute; left:12px; bottom:12px; width:48%; height:40%; pointer-events:none; display:none; overflow:visible; }
    .bubble{ display:block; padding:10px 14px; margin-bottom:8px; border-radius:14px; background: linear-gradient(90deg, rgba(75,59,255,0.14), rgba(108,45,209,0.08)); backdrop-filter: blur(6px); color:var(--text); font-size:15px; box-shadow:0 8px 30px rgba(0,0,0,0.5); transform-origin:left bottom; }
    .time-pill{ position:absolute; right:14px; bottom:14px; padding:8px 12px; border-radius:18px; background:rgba(0,0,0,0.45); color:var(--muted); font-size:13px; border:1px solid rgba(255,255,255,0.03); }
    .chat-panel{ width:360px; height:100%; border-radius:14px; background: linear-gradient(180deg, rgba(8,12,30,0.65), rgba(6,8,20,0.45)); box-shadow: inset 0 1px 0 rgba(255,255,255,0.02), 0 10px 30px rgba(0,0,0,0.6); overflow:hidden; display:flex; flex-direction:column; }
    .chat-top{ padding:12px; display:flex; gap:8px; align-items:center; border-bottom:1px solid rgba(255,255,255,0.02); }
    .search-row{ padding:10px; display:flex; align-items:center; gap:8px; border-bottom:1px solid rgba(255,255,255,0.02); }
    .search-row small{ color:var(--muted); margin-right:6px; }
    .search-row input{ flex:1; padding:8px;border-radius:8px;border:1px solid rgba(255,255,255,0.03);background:transparent;color:var(--text); }
    .chat-list{ flex:1; padding:12px; display:flex; flex-direction:column-reverse; gap:10px; align-items:flex-start; position:relative; overflow:hidden; }
    .chat-item{ display:flex; gap:10px; align-items:flex-start; padding:10px 12px; border-radius:10px; background:rgba(255,255,255,0.01); width:100%; box-sizing:border-box; }
    .chat-left{ flex:1; display:flex; flex-direction:column; gap:6px; }
    .who{ font-weight:700; color:var(--text); font-size:14px; }
    .msg{ color:var(--text); font-size:14px; line-height:1.25; white-space:pre-wrap; word-break:break-word; }
    .when{ font-size:12px; color:var(--muted); align-self:center; cursor:pointer; background:transparent; border:none; }
    .appear-up{ animation: appearUp .28s cubic-bezier(.2,.9,.2,1); }
    @keyframes appearUp{ from{ transform:translateY(8px); opacity:0 } to{ transform:translateY(0); opacity:1 } }
    @media (max-width:1200px){ .chat-panel{ width:320px } }
    @media (max-width:900px){ .app{flex-direction:column;align-items:stretch;padding-top:20px} .video-wrap{order:1;padding-left:28px} .chat-panel{order:2;width:100%;height:320px} .side-pill{left:8px} }
    </style>
    </head>
    <body>
    <svg class="liquid-bg" viewBox="0 0 1000 700" preserveAspectRatio="none"><defs><linearGradient id="g1" x1="0" x2="1"><stop offset="0" stop-color="#4b3bff"/><stop offset="1" stop-color="#6c2dd1"/></linearGradient><filter id="blur"><feGaussianBlur stdDeviation="80" /></filter></defs><g filter="url(#blur)" opacity="0.9"><path d="M0 200 C150 100 350 300 600 200 C850 100 1100 300 1400 200 L1400 700 L0 700 Z" fill="url(#g1)"></path></g></svg>
    <div class="app" id="app">
    <div class="side-pill" id="sidePill" aria-hidden="true"><div style="display:flex;flex-direction:column;align-items:center;justify-content:center;padding:6px 0;"><div class="nav-dot"></div><div class="nav-dot"></div><div class="nav-dot"></div></div></div>
    <div class="video-wrap">
    <div class="player-card" id="playerCard">
    <div class="player-viewport">
    <video id="video" controls crossorigin="anonymous" playsinline preload="metadata"></video>
    </div>
    <div class="overlay-bubbles" id="overlayBubbles" aria-hidden="true"></div>
    <div class="time-pill" id="timePill">00:00 / 00:00</div>
    </div>
    </div>
    <aside class="chat-panel" role="complementary" aria-label="Chat synchronisé">
    <div class="chat-top"><div style="flex:1;"></div></div>
    <div class="search-row"><small>Recherche :</small><input id="searchInput" placeholder="Rechercher un pseudo ou message..." /></div>
    <div class="chat-list" id="chatList" role="log" aria-live="polite"></div>
    <div style="padding:10px;border-top:1px solid rgba(255,255,255,0.02);display:flex;gap:8px;align-items:center;"><small style="color:var(--muted)">Déposez ici <strong>chat.json</strong> pour le rendre instantané, ou collez-le dans <code>&lt;script id="embeddedChat"&gt;</code>.</small></div>
    </aside>
    </div>
    <!-- Embedded chat JSON: collez ici le contenu brut de chat.json (tableau JSON) pour un chargement immédiat -->
    <script id="embeddedChat" type="application/json">[]</script>
    <script>
    // Configuration
    const DEFAULT_VIDEO_URL = "https://archive.org/download/v2578749137_720p60/v2578749137_720p60.mp4";
    const DEFAULT_CHAT_URL = "https://archive.org/download/v2578749137_720p60/chat.json";
    const CACHE_KEY = 'cached_chat_v2578749137'; // localStorage key for instant reuse
    const video = document.getElementById('video');
    const chatList = document.getElementById('chatList');
    const overlay = document.getElementById('overlayBubbles');
    const timePill = document.getElementById('timePill');
    let chatMessages = [];
    let nextIndex = 0;
    let bubblesEnabled = true;
    const MAX_VISIBLE_CHAT = 8;
    const MESSAGE_REMOVE_BUFFER = 500;
    function formatTime(s){ if (!isFinite(s)) return "00:00"; const h=Math.floor(s/3600), m=Math.floor((s%3600)/60), sec=Math.floor(s%60); return (h>0?String(h).padStart(2,'0')+':':'')+String(m).padStart(2,'0')+':'+String(sec).padStart(2,'0'); }
    function escapeHtml(s){ return String(s).replace(/[&<>\"]/g, ch=>({ '&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;' }[ch])); }
    function parseTimeString(ts){ if (typeof ts !== 'string') return NaN; const parts = ts.split(':').map(p=>p.trim()).filter(Boolean).reverse(); let seconds=0; for (let i=0;i<parts.length;i++){ const v=parseFloat(parts.replace(/[^\d.]/g,'')); if (isNaN(v)) return NaN; seconds += v*Math.pow(60,i); } return seconds; }
    function parseISOToSeconds(iso, baseDate){ const d = Date.parse(iso); if (isNaN(d)) return NaN; if (baseDate) return (d - baseDate)/1000; return d/1000; }
    function normalizeChatObjects(arr){ if (!Array.isArray(arr)) return []; let firstTimestamp=null; for (const it of arr){ if (it.timestamp||it.created_at||it.time){ const ts = it.timestamp || it.created_at || it.time; if (typeof ts === 'string' && ts.includes('T')){ const d = Date.parse(ts); if (!isNaN(d)){ if (firstTimestamp===null || d<firstTimestamp) firstTimestamp = d; } } } }
    return arr.map(obj=>{ let t = NaN; if (typeof obj.time_in_seconds === 'number') t = obj.time_in_seconds; if (typeof obj.time_in === 'number') t = obj.time_in; if (typeof obj.t === 'number') t = obj.t; if (isNaN(t) && (obj.time && typeof obj.time === 'string')) t = parseTimeString(obj.time); if (isNaN(t) && (obj.timestamp || obj.created_at)){ const ts = obj.timestamp || obj.created_at; const parsed = parseISOToSeconds(ts, firstTimestamp); if (!isNaN(parsed) && firstTimestamp) t = parsed; else if (!isNaN(parsed) && !firstTimestamp) t = parsed; } if (isNaN(t) && obj.offset) t = parseFloat(obj.offset); if (isNaN(t) && obj.duration) t = parseFloat(obj.duration); let message = obj.message || obj.text || obj.content || obj.message_body || obj.body || ''; let author = (obj.author && (obj.author.name || obj.author.display_name)) || obj.author || obj.author_name || obj.user || obj.nick || 'Unknown'; if (typeof author === 'object') author = author.name || author.display_name || JSON.stringify(author).slice(0,16); return { time: isFinite(t)?t:NaN, author: String(author||'Unknown'), message: String(message||''), raw: obj }; }).filter(m=>m.message || !isNaN(m.time)); }
    async function loadChatFromText(text){ let parsed=null; try{ parsed = JSON.parse(text); } catch(e){ const lines = text.split('
    ').map(l=>l.trim()).filter(Boolean); const objs=[]; for (const l of lines){ try{ objs.push(JSON.parse(l)); }catch(e2){} } parsed = objs; }
    chatMessages = normalizeChatObjects(parsed);
    chatMessages.sort((a,b)=>{ if (isNaN(a.time)&&isNaN(b.time)) return 0; if (isNaN(a.time)) return 1; if (isNaN(b.time)) return -1; return a.time - b.time; });
    let lastKnown = 0; for (let i=0;i<chatMessages.length;i++){ if (!isNaN(chatMessages.time)) lastKnown = Math.max(lastKnown, chatMessages.time); else chatMessages.time = lastKnown + 0.5*(i+1); }
    nextIndex = 0;
    }
    function showChatMessage(msg){ const el = document.createElement('div'); el.className = 'chat-item appear-up'; el.innerHTML = `<div class="chat-left"><div class="who">${escapeHtml(msg.author)}</div><div class="msg">${escapeHtml(msg.message)}</div></div><button class="when" data-time="${msg.time}">${formatTime(msg.time)}</button>`; el.querySelector('.when').addEventListener('click', (e)=>{ e.stopPropagation(); video.currentTime = msg.time; video.play(); }); chatList.appendChild(el); const nodes = chatList.children; if (nodes.length > MAX_VISIBLE_CHAT){ chatList.removeChild(nodes[nodes.length-1]); } const base = 2000; const perChar = 70; const maxDur = 14000; const minDur = 2200; const dur = Math.max(minDur, Math.min(maxDur, base + (msg.message.length||0)*perChar)); if (document.fullscreenElement && bubblesEnabled) createOverlayBubble(msg, dur); setTimeout(()=>{ el.animate([{opacity:1, transform:'translateY(0)'},{opacity:0, transform:'translateY(-8px)'}],{duration:320}).onfinish = ()=> el.remove(); }, dur + MESSAGE_REMOVE_BUFFER); }
    function onTimeUpdate(){ const t = video.currentTime; timePill.textContent = `${formatTime(t)} / ${formatTime(video.duration||0)}`; while (nextIndex < chatMessages.length && chatMessages[nextIndex].time <= t + 0.02){ showChatMessage(chatMessages[nextIndex]); nextIndex++; } }
    function createOverlayBubble(msg, duration){ overlay.style.display = 'block'; const b = document.createElement('div'); b.className = 'bubble'; b.innerHTML = `<strong style="display:block;margin-bottom:6px;font-size:13px">${escapeHtml(msg.author)}</strong><div style="font-size:14px">${escapeHtml(msg.message)}</div>`; overlay.appendChild(b); const floatDur = duration + 600; b.animate([{transform:'translateY(18px)', opacity:0},{transform:'translateY(0)', opacity:1}],{duration:300, easing:'cubic-bezier(.2,.9,.2,1)'}); setTimeout(()=>{ b.animate([{transform:'translateY(0)', opacity:1},{transform:'translateY(-34px)', opacity:0}],{duration:420, easing:'ease-in'}).onfinish = ()=> b.remove(); }, floatDur); }
    // fetch helper
    function fetchWithTimeout(url, options = {}, timeout = 6000) { return new Promise((resolve, reject) => { const timer = setTimeout(() => reject(new Error('timeout')), timeout); fetch(url, options).then(res => { clearTimeout(timer); resolve(res); }).catch(err => { clearTimeout(timer); reject(err); }); }); }
    async function fetchChatWithRetries(url, attempts = 3, delayMs = 700) { for (let i = 0; i < attempts; i++){ try { const res = await fetchWithTimeout(url, { mode: 'cors' }, 6000); if (!res.ok) throw new Error('status ' + res.status); const text = await res.text(); return text; } catch (err) { console.warn('Chat fetch attempt', i+1, 'failed:', err && err.message); await new Promise(r => setTimeout(r, delayMs * (i+1))); } } try { const res = await fetchWithTimeout(url, { mode: 'no-cors' }, 5000); console.warn('Received opaque response (no-cors); cannot read body due to CORS policy.'); return null; } catch (err) { console.warn('Final fallback also failed:', err && err.message); return null; } }
    // 1) Immediate loading options: embedded script or cached localStorage
    (function tryLoadFromEmbeddedOrCache(){
    // 1a: embedded <script id="embeddedChat"> with raw JSON
    try{
    const script = document.getElementById('embeddedChat');
    if (script && script.textContent && script.textContent.trim().length){
    const parsed = JSON.parse(script.textContent);
    chatMessages = normalizeChatObjects(parsed);
    chatMessages.sort((a,b)=>{ if (isNaN(a.time)&&isNaN(b.time)) return 0; if (isNaN(a.time)) return 1; if (isNaN(b.time)) return -1; return a.time - b.time; });
    let lastKnown = 0; for (let i=0;i<chatMessages.length;i++){ if (!isNaN(chatMessages.time)) lastKnown = Math.max(lastKnown, chatMessages.time); else chatMessages.time = lastKnown + 0.5*(i+1); }
    nextIndex = 0;
    console.info('Chat chargé instantanément depuis <script id="embeddedChat">');
    return;
    }
    } catch(e){ console.warn('Erreur parsing embeddedChat', e); }
    // 1b: try localStorage cache
    try{
    const cached = localStorage.getItem(CACHE_KEY);
    if (cached){ const parsed = JSON.parse(cached); chatMessages = normalizeChatObjects(parsed); chatMessages.sort((a,b)=>a.time-b.time); nextIndex=0; console.info('Chat chargé instantanément depuis localStorage.'); return; }
    }catch(e){ console.warn('Erreur lecture cache localStorage', e); }
    })();
    // startup loader (non-blocking)
    window.addEventListener('load', async ()=>{
    try{
    video.preload = 'auto';
    const existing = video.querySelector('source'); if (!existing){ const s = document.createElement('source'); s.src = DEFAULT_VIDEO_URL; s.type = 'video/mp4'; video.appendChild(s); }
    try { const pl = document.createElement('link'); pl.rel = 'preload'; pl.href = DEFAULT_VIDEO_URL; pl.as = 'video'; document.head.appendChild(pl); } catch(e){}
    video.src = DEFAULT_VIDEO_URL; video.crossOrigin = 'anonymous'; video.load(); video.play().catch(()=>{});
    // If chat not present yet, fetch remote in background and cache it for next loads
    if (!chatMessages || chatMessages.length === 0){
    const chatText = await fetchChatWithRetries(DEFAULT_CHAT_URL, 3, 800);
    if (chatText){ try{ await loadChatFromText(chatText); localStorage.setItem(CACHE_KEY, JSON.stringify(JSON.parse(chatText))); console.info('Chat distant chargé et mis en cache localStorage.'); }catch(e){ console.warn('Erreur parse/chat cache', e); } }
    else { console.warn('Impossible de charger chat distant (CORS/réseau).'); if (chatMessages.length===0){ chatMessages = [{time:0, author:'System', message:'Chat non disponible (CORS ou erreur réseau).', raw:{}}]; nextIndex=0; } }
    }
    }catch(err){ console.warn('Erreur chargement default:', err); }
    });
    // drag-and-drop support: drop chat.json to cache and load instantly
    window.addEventListener('dragover', (e)=>{ e.preventDefault(); e.dataTransfer.dropEffect = 'copy'; });
    window.addEventListener('drop', async (e)=>{
    e.preventDefault(); if (!e.dataTransfer || !e.dataTransfer.files || e.dataTransfer.files.length === 0) return; const f = e.dataTransfer.files[0]; if (!f.name || !/\.json$/i.test(f.name)) return alert('Déposez un fichier JSON valide (chat.json)'); try{ const txt = await f.text(); localStorage.setItem(CACHE_KEY, txt); await loadChatFromText(txt); alert('Chat chargé et mis en cache — rechargement instantané activé.'); }catch(err){ alert('Erreur lors du chargement du fichier: '+(err && err.message)); }
    });
    // search input
    document.getElementById('searchInput').addEventListener('input', (e)=>{ const q = e.target.value.trim().toLowerCase(); if (!q) return; const matches = chatMessages.filter(m=> (m.author||'').toLowerCase().includes(q) || (m.message||'').toLowerCase().includes(q)).slice(0,20); chatList.innerHTML = ''; for (let i=matches.length-1;i>=0;i--){ const item = matches; const el = document.createElement('div'); el.className='chat-item'; el.innerHTML = `<div class="chat-left"><div class="who">${escapeHtml(item.author)}</div><div class="msg">${escapeHtml(item.message)}</div></div><button class="when" data-time="${item.time}">${formatTime(item.time)}</button>`; el.querySelector('.when').addEventListener('click',(ev)=>{ ev.stopPropagation(); video.currentTime = item.time; video.play(); }); chatList.appendChild(el); } });
    // playback bindings
    video.addEventListener('timeupdate', onTimeUpdate);
    video.addEventListener('seeking', ()=>{ const t = video.currentTime; if (t + 0.2 < (chatMessages[nextIndex-1]?.time || 0)){ let idx = 0; while (idx < chatMessages.length && chatMessages[idx].time <= t + 0.01) idx++; nextIndex = idx; chatList.innerHTML = ''; } });
    document.addEventListener('fullscreenchange', ()=>{ const isFs = !!document.fullscreenElement; overlay.style.display = (isFs && bubblesEnabled)?'block':'none'; });
    document.getElementById('sidePill').addEventListener('click', ()=>{ video.currentTime = 0; video.play(); });
    window.__chatData = chatMessages;
    </script>
    </body>
    </html>
     
  14. withcap

    withcap Well-Known Member Uploader V . I . P.

    Joined:
    Oct 29, 2013
    Threads:
    249
    Messages:
    6,947
    Likes Received:
    3,444
    Thanks!
     
  15. SwiftieBoyTS

    SwiftieBoyTS Sam Swift

    Joined:
    Dec 19, 2024
    Threads:
    28
    Messages:
    397
    Likes Received:
    83
    Thank you so much
     
  16. Lizzy

    Lizzy Well-Known Member

    Joined:
    Mar 18, 2024
    Threads:
    9
    Messages:
    2,069
    Likes Received:
    709
    thank you
     
  17. thefateofophelia

    thefateofophelia Active Member

    Joined:
    Nov 25, 2021
    Threads:
    10
    Messages:
    831
    Likes Received:
    226
    OMG Finally thanks!
     
  18. abcde54321

    abcde54321 Well-Known Member

    Joined:
    Apr 6, 2014
    Threads:
    15
    Messages:
    2,477
    Likes Received:
    109
  19. shinpu_89

    shinpu_89 Member

    Joined:
    Apr 1, 2025
    Threads:
    0
    Messages:
    153
    Likes Received:
    8
  20. folklordeaoty

    folklordeaoty New Member

    Joined:
    Jun 19, 2025
    Threads:
    0
    Messages:
    18
    Likes Received:
    3