bep40 commited on
Commit
3e9bbf0
·
verified ·
1 Parent(s): f2982a6

Complete WC Shorts TikTok-style slideshow with autoplay, title overlay, and smooth scroll

Browse files
Files changed (1) hide show
  1. static/wc2026_v2.js +187 -162
static/wc2026_v2.js CHANGED
@@ -3,46 +3,10 @@ let _wc2026Data=null;let _wcRefreshInterval=null;
3
  const WC_TEAMS={'Mexico':'🇲🇽 Mexico','Colombia':'🇨🇴 Colombia','USA':'🇺🇸 Mỹ','United States':'🇺🇸 Mỹ','Brazil':'🇧🇷 Brazil','Argentina':'🇦🇷 Argentina','Germany':'🇩🇪 Đức','France':'🇫🇷 Pháp','Spain':'🇪🇸 Tây Ban Nha','England':'🏴󠁧󠁢󠁥󠁮󠁧󠁿 Anh','Portugal':'🇵🇹 Bồ Đào Nha','Netherlands':'🇳🇱 Hà Lan','Italy':'🇮🇹 Ý','Belgium':'🇧🇪 Bỉ','Croatia':'🇭🇷 Croatia','Uruguay':'🇺🇾 Uruguay','Japan':'🇯🇵 Nhật Bản','South Korea':'🇰🇷 Hàn Quốc','Korea Republic':'🇰🇷 Hàn Quốc','Australia':'🇦🇺 Úc','Saudi Arabia':'🇸🇦 Ả Rập Xê Út','Iran':'🇮🇷 Iran','Qatar':'🇶🇦 Qatar','Canada':'🇨🇦 Canada','Morocco':'🇲🇦 Morocco','Senegal':'🇸🇳 Senegal','Ecuador':'🇪🇨 Ecuador','Serbia':'🇷🇸 Serbia','Switzerland':'🇨🇭 Thụy Sĩ','Denmark':'🇩🇰 Đan Mạch','Poland':'🇵🇱 Ba Lan','Turkey':'🇹🇷 Thổ Nhĩ Kỳ','Türkiye':'🇹🇷 Thổ Nhĩ Kỳ','Ukraine':'🇺🇦 Ukraine','Egypt':'🇪🇬 Ai Cập','Nigeria':'🇳🇬 Nigeria','Cameroon':'🇨🇲 Cameroon','Ghana':'🇬🇭 Ghana','Tunisia':'🇹🇳 Tunisia','Algeria':'🇩🇿 Algeria','South Africa':'🇿🇦 Nam Phi','Indonesia':'🇮🇩 Indonesia','Vietnam':'🇻🇳 Việt Nam','China PR':'🇨🇳 Trung Quốc','New Zealand':'🇳🇿 New Zealand','Panama':'🇵🇦 Panama','Costa Rica':'🇨🇷 Costa Rica','Jamaica':'🇯🇲 Jamaica','Honduras':'🇭🇳 Honduras','Paraguay':'🇵🇾 Paraguay','Bolivia':'🇧🇴 Bolivia','Chile':'🇨🇱 Chile','Peru':'🇵🇪 Peru','Venezuela':'🇻🇪 Venezuela','Norway':'🇳🇴 Na Uy','Sweden':'🇸🇪 Thụy Điển','Austria':'🇦🇹 Áo','Scotland':'🏴󠁧󠁢󠁳󠁣󠁴󠁿 Scotland','Wales':'🏴󠁧󠁢󠁷󠁬󠁳󠁿 Xứ Wales','Ireland':'🇮🇪 Ireland','Romania':'🇷🇴 Romania','Hungary':'🇭🇺 Hungary','Greece':'🇬🇷 Hy Lạp','Uzbekistan':'🇺🇿 Uzbekistan','Iraq':'🇮🇶 Iraq','TBD':'🏳️ TBD'};
4
  function _t(n){return WC_TEAMS[n]||WC_TEAMS[(n||'').trim()]||'🏴 '+n;}
5
 
6
- const WC_CSS=`<style>
7
- .wc-bxh{max-height:460px;overflow-y:auto;padding:2px;display:block}
8
- .wc-bxh,.wc-bxh *{box-sizing:border-box!important;float:none!important;max-width:100%!important}
9
- .wc-bxh script,.wc-bxh style,.wc-bxh ins,.wc-bxh iframe,.wc-bxh .info,.wc-bxh .btns,.wc-bxh .form{display:none!important}
10
- .wc-bxh .title-table,.wc-bxh h6{display:block!important;background:#0b1a2a!important;color:#f0c040!important;font-size:11px!important;font-weight:800!important;padding:6px 8px!important;margin:8px 0 0!important;border-radius:5px 5px 0 0!important;border-bottom:1px solid #1a3a5a!important}
11
- .wc-bxh .bxh-head{display:flex!important;align-items:center!important;padding:4px 6px!important;background:#0a1320!important;color:#6a9fca!important;font-size:9px!important;font-weight:700!important;border-bottom:1px solid #1a3a5a!important}
12
- .wc-bxh .bxh-head .h-team{flex:1!important;text-align:left!important}
13
- .wc-bxh .bxh-head .h-stat{flex:0 0 26px!important;text-align:center!important}
14
- .wc-bxh .bxh-head .h-pts{flex:0 0 30px!important;text-align:center!important;color:#f0c040!important}
15
- .wc-bxh .item{display:flex!important;align-items:center!important;padding:5px 6px!important;border-bottom:1px solid #10202f!important;background:transparent!important}
16
- .wc-bxh .item:nth-child(even){background:#0a1320!important}
17
- .wc-bxh .item .team{flex:1!important;display:flex!important;align-items:center!important;gap:5px!important;min-width:0!important;text-align:left!important}
18
- .wc-bxh .item .team>span{flex:0 0 16px!important;color:#6a9fca!important;font-size:10px!important;text-align:center!important}
19
- .wc-bxh .item .team a{flex:1!important;display:flex!important;align-items:center!important;gap:5px!important;min-width:0!important;color:#eee!important;text-decoration:none!important}
20
- .wc-bxh .item .team a p{font-size:11px!important;color:#eee!important;white-space:nowrap!important;overflow:hidden!important;text-overflow:ellipsis!important;margin:0!important}
21
- .wc-bxh .item .copy{display:flex!important;align-items:center!important;flex:0 0 auto!important}
22
- .wc-bxh .item .copy>p{flex:0 0 26px!important;text-align:center!important;font-size:10px!important;color:#ccc!important;margin:0!important}
23
- .wc-bxh .item .copy>p:last-of-type,.wc-bxh .item .copy>p:nth-last-child(2){flex:0 0 30px!important}
24
- .wc-bxh .item .copy>p strong{color:#f0c040!important;font-weight:800!important}
25
- .wc-bxh img{width:18px!important;height:18px!important;object-fit:contain!important;vertical-align:middle!important;display:inline-block!important;flex:0 0 18px!important}
26
- .wc-bxh strong{color:#f0c040!important}
27
- .wc-bxh a{color:#8ab4d8!important;text-decoration:none!important}
28
- .wc-bxh table{width:100%!important;border-collapse:collapse!important;font-size:11px!important;display:table!important}
29
- .wc-bxh th{background:#0b1a2a!important;color:#6a9fca!important;padding:5px 3px!important;font-size:9px!important;text-align:center!important}
30
- .wc-bxh td{padding:4px 3px!important;border-bottom:1px solid #0d1a2a!important;font-size:10px!important;color:#ddd!important}
31
- </style>`;
32
-
33
  function switchWCTab(tab){
34
  document.querySelectorAll('.wc-tab').forEach(t=>t.classList.remove('active'));
35
- const tabMap = {
36
- 'news': 'Tin',
37
- 'fixtures': 'Lịch',
38
- 'standings': 'BXH',
39
- 'highlights': 'Highlight',
40
- 'shorts': 'Shorts',
41
- 'stats': 'Thống'
42
- };
43
- document.querySelectorAll('.wc-tab').forEach(t=>{
44
- if(tabMap[tab] && t.textContent.includes(tabMap[tab])) t.classList.add('active');
45
- });
46
  const el=document.getElementById('wc-content');if(!el)return;
47
  if(tab==='news')renderWCNews(el);
48
  else if(tab==='fixtures')renderWCFixtures(el);
@@ -90,34 +54,18 @@ function renderWCStandings(el){
90
  if(!_wc2026Data){el.innerHTML='<div class="loading">Đang tải...</div>';return;}
91
  const d=_wc2026Data.standings||{};
92
  if(d.html&&d.html.length>100){
93
- el.innerHTML=WC_CSS+'<div class="wc-bxh">'+d.html+'</div>';
94
- const box=el.querySelector('.wc-bxh');
95
- if(box){
96
- box.querySelectorAll('.title-table, h6').forEach(t=>{
97
- if(t.nextElementSibling && t.nextElementSibling.classList.contains('bxh-head'))return;
98
- const head=document.createElement('div');
99
- head.className='bxh-head';
100
- head.innerHTML='<span class="h-team">Đội</span><span class="h-stat">Tr</span><span class="h-stat">T</span><span class="h-stat">H</span><span class="h-stat">B</span><span class="h-stat">BT</span><span class="h-stat">BB</span><span class="h-pts">HS</span><span class="h-pts">Đ</span>';
101
- t.insertAdjacentElement('afterend',head);
102
- });
103
- }
104
  el.querySelectorAll('a').forEach(a=>a.addEventListener('click',e=>e.preventDefault()));
105
  return;
106
  }
107
- const fx=_wc2026Data.fixtures||{};const matches=fx.matches||[];
108
- const gm=matches.filter(m=>m.group&&m.group.toLowerCase().includes('group'));
109
- if(!gm.length){el.innerHTML='<div class="loading">BXH cập nhật khi giải bắt đầu 11/06/2026<br><small style="color:#6a9fca">🔴 Livescore tự động</small></div>';return;}
110
- const groups={};gm.forEach(m=>{const g=m.group;if(!groups[g])groups[g]={};[m.home,m.away].forEach(t=>{if(!groups[g][t])groups[g][t]={p:0,w:0,d:0,l:0,gf:0,ga:0,pts:0};});if(m.home_score!=null&&m.away_score!=null){const hs=+m.home_score,as=+m.away_score;if(!isNaN(hs)&&!isNaN(as)){groups[g][m.home].p++;groups[g][m.away].p++;groups[g][m.home].gf+=hs;groups[g][m.home].ga+=as;groups[g][m.away].gf+=as;groups[g][m.away].ga+=hs;if(hs>as){groups[g][m.home].w++;groups[g][m.home].pts+=3;groups[g][m.away].l++;}else if(hs<as){groups[g][m.away].w++;groups[g][m.away].pts+=3;groups[g][m.home].l++;}else{groups[g][m.home].d++;groups[g][m.home].pts++;groups[g][m.away].d++;groups[g][m.away].pts++;}}}});
111
- let h='<div style="max-height:450px;overflow-y:auto;padding:2px"><div style="font-size:10px;color:#6a9fca;text-align:center;padding:3px">🏆 BXH World Cup 2026 · 🔴 Livescore</div>';
112
- Object.keys(groups).sort().forEach(g=>{const teams=Object.entries(groups[g]).map(([n,s])=>({name:n,...s,gd:s.gf-s.ga})).sort((a,b)=>b.pts-a.pts||b.gd-a.gd);h+=`<div style="font-size:11px;font-weight:800;color:#8ab4d8;padding:6px 4px 3px;margin-top:4px;border-bottom:1px solid #1a3a5a">${esc(g)}</div><table style="width:100%;border-collapse:collapse;font-size:10px;margin:2px 0"><tr style="background:#0b1a2a"><th style="text-align:left;padding:4px;color:#6a9fca">Đội</th><th style="color:#6a9fca;padding:3px">Tr</th><th style="color:#6a9fca;padding:3px">T</th><th style="color:#6a9fca;padding:3px">H</th><th style="color:#6a9fca;padding:3px">B</th><th style="color:#6a9fca;padding:3px">HS</th><th style="color:#f0c040;padding:3px;font-weight:800">Đ</th></tr>`;teams.forEach((t,i)=>{h+=`<tr style="background:${i<2?'#0a1a10':'transparent'}"><td style="padding:3px;color:#eee;white-space:nowrap">${_t(t.name)}</td><td style="text-align:center;padding:3px;color:#999">${t.p}</td><td style="text-align:center;color:#4ade80">${t.w}</td><td style="text-align:center;color:#fbbf24">${t.d}</td><td style="text-align:center;color:#f87171">${t.l}</td><td style="text-align:center;color:${t.gd>=0?'#4ade80':'#f87171'}">${t.gd>0?'+':''}${t.gd}</td><td style="text-align:center;color:#f0c040;font-weight:800">${t.pts}</td></tr>`;});h+='</table>';});
113
- h+='</div>';el.innerHTML=h;
114
  }
115
 
116
  // === STATS ===
117
  function renderWCStats(el){
118
  if(!_wc2026Data){el.innerHTML='<div class="loading">Đang tải...</div>';return;}
119
  const d=_wc2026Data.stats||{};
120
- if(d.html&&d.html.length>100){el.innerHTML=WC_CSS+'<div class="wc-bxh">'+d.html+'</div>';el.querySelectorAll('a').forEach(a=>a.addEventListener('click',e=>e.preventDefault()));}
121
  else el.innerHTML='<div class="loading">Thống kê cập nhật khi giải bắt đầu<br><small style="color:#6a9fca">Vua phá lưới · Kiến tạo · Thẻ phạt</small></div>';
122
  }
123
 
@@ -130,60 +78,87 @@ async function renderWCHighlights(el){
130
  const seen = new Map();
131
  wcVids.forEach((v,i)=>{if(!seen.has(v.link)) seen.set(v.link, {...v, _li: seen.size});});
132
  wcVids = Array.from(seen.values());
133
- if(!wcVids.length){
134
- el.innerHTML='<div class="loading">Chưa có highlight World Cup<br><small style="color:#6a9fca">Highlight sẽ xuất hiện khi giải diễn ra</small></div>';
135
- return;
136
- }
137
  let h='<div style="display:grid;grid-template-columns:repeat(2,1fr);gap:6px;max-height:450px;overflow-y:auto;padding:4px">';
138
  wcVids.forEach((v,i)=>{
139
  h+=`<div style="cursor:pointer;border-radius:6px;overflow:hidden;background:#0a1520" onclick="openHighlightFeed('world-cup',${v._li},'${esc(v.link)}')">`;
140
  h+=`<div style="position:relative;aspect-ratio:16/9;background:#1a2a3a">${v.img?`<img src="${esc(v.img)}" loading="lazy" style="width:100%;height:100%;object-fit:cover" onerror="this.style.display='none'">`:''}<div class="card-play">▶</div></div>`;
141
- h+=`<div style="padding:4px 6px"><span style="font-size:8px;color:#f0c040">🌍 World Cup</span><div style="font-size:10px;color:#ccc;line-height:1.2;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden">${esc(v.title)}</div></div>`;
142
- h+='</div>';
143
  });
144
  h+='</div>';el.innerHTML=h;
145
- }catch(e){
146
- el.innerHTML='<div class="loading">Lỗi tải highlight: '+esc(e.message)+'</div>';
147
- }
148
  }
149
 
150
- // === WC SHORTS TAB — VTV Nam Bộ + WC moments shorts ===
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  async function renderWCShorts(el){
152
- el.innerHTML='<div class="loading" style="display:flex;flex-direction:column;align-items:center;gap:8px;padding:30px"><div class="vtv-spinner" style="width:24px;height:24px;border:2px solid #333;border-top-color:#00ccff;border-radius:50%;animation:vtvspin .8s linear infinite"></div>Đang tải shorts...</div>';
153
 
154
- // Fetch VTV Nam Bộ shorts from dedicated endpoint
155
  let wcShorts = [];
 
 
156
  try {
157
- const r = await fetch('/api/shorts/vtvnamo?count=30', {signal: AbortSignal.timeout(15000)});
158
  wcShorts = await r.json() || [];
159
- } catch(e) {
160
- console.warn('VTV shorts fetch failed:', e);
161
- }
162
 
163
- // Also check _shortsData for vtvnambo channel
164
  if(typeof _shortsData !== 'undefined' && _shortsData.length){
165
  const existingIds = new Set(wcShorts.map(s=>s.id));
166
- _shortsData.forEach(s=>{
167
- if(s.channel === 'vtvnambo' && !existingIds.has(s.id)){
168
- wcShorts.push(s);
169
- }
170
- });
171
  }
172
 
173
- // Also check _wcShortsData if available
174
  if(typeof _wcShortsData !== 'undefined' && _wcShortsData.length){
175
  const existingIds = new Set(wcShorts.map(s=>s.id));
176
  _wcShortsData.forEach(s=>{if(!existingIds.has(s.id)) wcShorts.push(s);});
177
  }
178
 
179
  if(!wcShorts.length){
180
- el.innerHTML=`<div style="text-align:center;padding:30px 16px;color:#888;font-size:12px">
181
- <div style="font-size:32px;margin-bottom:8px">📱</div>
182
- <div style="color:#ccc;font-size:14px;font-weight:700;margin-bottom:6px">Shorts World Cup</div>
183
- <div style="margin-bottom:12px">VTV Nam Bộ & khoảnh khắc World Cup</div>
184
- <div style="font-size:11px;color:#6a9fca;margin-bottom:16px">Đang cập nhật shorts từ YouTube...</div>
185
- <button onclick="openWCShortsFeed(0)" style="background:#0b6bcb;border:none;color:#fff;padding:8px 20px;border-radius:16px;font-size:12px;font-weight:700;cursor:pointer">📱 Xem Shorts</button>
186
- </div>`;
187
  return;
188
  }
189
 
@@ -191,73 +166,60 @@ async function renderWCShorts(el){
191
  const seen = new Set();
192
  wcShorts = wcShorts.filter(s=>{if(seen.has(s.id)) return false; seen.add(s.id); return true;});
193
 
194
- // Render horizontal slider of shorts
 
 
 
195
  let h = `<div style="padding:4px">
196
  <div style="display:flex;justify-content:space-between;align-items:center;padding:4px 6px 8px">
197
  <span style="font-size:13px;font-weight:800;color:#f0c040">📱 Shorts World Cup</span>
198
- <span style="font-size:10px;color:#8ab4d8;cursor:pointer" onclick="openWCShortsFeed(0)">Xem tất cả →</span>
199
  </div>
200
- <div style="display:flex;overflow-x:auto;gap:8px;padding:0 4px 8px;scrollbar-width:none" id="wc-shorts-track">`;
201
-
202
- wcShorts.slice(0,12).forEach((s,i)=>{
203
- const badge = s.channel==='vtvnambo' ? 'VTV Nam Bộ' : '⚽ WC';
204
- const badgeBg = s.channel==='vtvnambo' ? '#0b6bcb' : '#f26522';
205
- h += `<div style="flex:0 0 120px;cursor:pointer;border-radius:8px;overflow:hidden;background:#0a1520;border:1px solid #1a3a5a;transition:border-color .2s" onclick="openWCShortsFeed(${i})">`;
206
- h += `<div style="position:relative;width:100%;aspect-ratio:9/16;background:#1a2a3a;overflow:hidden">`;
207
- h += s.img ? `<img src="${esc(s.img)}" loading="lazy" style="width:100%;height:100%;object-fit:cover" onerror="this.style.display='none'">` : '';
208
- h += `<div style="position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:30px;height:30px;border-radius:50%;background:rgba(0,0,0,.55);display:flex;align-items:center;justify-content:center;color:#fff;font-size:12px">▶</div>`;
209
- h += `</div>`;
210
- h += `<div style="padding:4px 5px"><span style="font-size:8px;font-weight:700;padding:1px 4px;border-radius:3px;color:#fff;background:${badgeBg}">${badge}</span><div style="font-size:9px;color:#ccc;line-height:1.2;margin-top:2px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden">${esc(s.title)}</div></div>`;
211
  h += `</div>`;
212
  });
213
 
214
- // "View all" card
215
- h += `<div style="flex:0 0 100px;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#0b2e4a,#1a3a5a);border-radius:8px;cursor:pointer;border:1px dashed #2a5a8a;color:#8ab4d8;font-size:11px;font-weight:700;text-align:center;padding:8px;flex-shrink:0" onclick="openWCShortsFeed(0)">📱 Xem tất cả<br><small style="font-size:9px;color:#6a9fca">${wcShorts.length} shorts</small></div>`;
216
-
217
  h += `</div></div>`;
218
  el.innerHTML = h;
219
  }
220
 
221
- // === WC SHORTS TIKTOK FEED — full-screen vertical scroll, continuous autoplay ===
222
  async function openWCShortsFeed(startIdx){
223
  showView('view-tiktok');
224
  const el = document.getElementById('view-tiktok');
225
- el.innerHTML = '<div class="loading">Đang tải shorts...</div>';
226
 
227
- // Gather shorts from all available sources
228
- let wcShorts = [];
229
 
230
- // 1. Fetch from dedicated VTV Nam Bộ endpoint (most reliable)
231
- try {
232
- const r = await fetch('/api/shorts/vtvnamo?count=50', {signal: AbortSignal.timeout(15000)});
233
- const vtvShorts = await r.json() || [];
234
- wcShorts.push(...vtvShorts);
235
- } catch(e) {
236
- console.warn('VTV endpoint failed:', e);
237
  }
238
 
239
- // 2. From _shortsData (vtvnambo channel)
240
- if(typeof _shortsData !== 'undefined' && _shortsData.length){
241
- _shortsData.forEach(s=>{
242
- if(s.channel === 'vtvnambo') wcShorts.push(s);
243
- });
244
- }
245
- // 3. From _wcShortsData
246
- if(typeof _wcShortsData !== 'undefined' && _wcShortsData.length){
247
- const existingIds = new Set(wcShorts.map(s=>s.id));
248
- _wcShortsData.forEach(s=>{if(!existingIds.has(s.id)) wcShorts.push(s);});
249
  }
250
- // 4. Fetch fresh from general shorts endpoint as last resort
 
251
  if(!wcShorts.length){
252
  try{
253
  const r = await fetch('/api/shorts');
254
- const allShorts = await r.json() || [];
255
- wcShorts = allShorts.filter(s => s.channel === 'vtvnambo');
256
- // Last resort: just show all shorts
257
- if(!wcShorts.length) wcShorts = allShorts;
258
- }catch(e){
259
- wcShorts = [];
260
- }
261
  }
262
 
263
  // Deduplicate
@@ -265,42 +227,64 @@ async function openWCShortsFeed(startIdx){
265
  wcShorts = wcShorts.filter(s=>{if(seen.has(s.id)) return false; seen.add(s.id); return true;});
266
 
267
  if(!wcShorts.length){
268
- el.innerHTML = '<div class="loading">Chưa có shorts</div>';
269
  return;
270
  }
271
 
272
- // Store for reuse
273
- if(typeof _wcShortsData !== 'undefined') _wcShortsData = wcShorts;
274
 
275
  // Rotate so startIdx is first
276
  const ordered = startIdx > 0 ? [...wcShorts.slice(startIdx), ...wcShorts.slice(0,startIdx)] : [...wcShorts];
277
 
 
278
  let h = `<button class="back-btn" onclick="switchCat('home')">← World Cup Shorts</button>`;
279
- h += '<div class="tiktok-container"><div class="tiktok-feed" id="wc-shorts-feed">';
280
 
281
  ordered.forEach((s,i)=>{
282
  const id = s.id || '';
283
- // YouTube embed with autoplay on scroll
284
- const src = `https://www.youtube.com/embed/${id}?autoplay=0&rel=0&playsinline=1&mute=1&loop=1&playlist=${id}&enablejsapi=1`;
285
- const vtag = `<iframe data-yt-src="${src}" allowfullscreen allow="accelerometer;autoplay;clipboard-write;encrypted-media;gyroscope;picture-in-picture"></iframe>`;
286
- const isVTV = s.channel === 'vtvnambo';
287
- const badge = isVTV ? 'VTV Nam Bộ' : '⚽ WC';
288
- const badgeClass = isVTV ? 'badge-wc' : 'badge-fpt';
289
  const videoId = 'wcshort-' + id;
290
- h += buildTikTokSlide({vtag,title:s.title,badge,badgeClass,videoId,idx:i,total:ordered.length,shareUrl:'https://youtube.com/shorts/'+id});
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
291
  });
292
 
293
  h += '</div></div>';
294
  el.innerHTML = h;
295
 
296
- // Init continuous autoplay
297
- initWCShortsFeed();
 
 
 
298
  }
299
 
300
- function initWCShortsFeed(){
 
301
  const feed = document.getElementById('wc-shorts-feed');
302
  if(!feed) return;
303
- const slides = feed.querySelectorAll('.tiktok-slide');
304
  let cur = -1;
305
  let isManualScroll = false;
306
  let manualTimer = null;
@@ -310,21 +294,21 @@ function initWCShortsFeed(){
310
  slides.forEach((sl,idx)=>{
311
  const fr = sl.querySelector('iframe');
312
  if(idx === i){
 
313
  if(fr && !fr.src && fr.dataset.ytSrc){ fr.src = fr.dataset.ytSrc; }
314
- // Try to autoplay via postMessage
315
  if(fr && fr.src){
316
- try {
317
- fr.contentWindow.postMessage('{"event":"command","func":"playVideo","args":""}', '*');
318
- } catch(e){}
319
  }
 
320
  const vid = sl.dataset.vid;
321
- if(vid && !sl._viewed){ sl._viewed = true; doInteract(vid,'view'); }
322
  } else {
323
- // Pause other videos
324
  if(fr && fr.src){
325
- try {
326
- fr.contentWindow.postMessage('{"event":"command","func":"pauseVideo","args":""}', '*');
327
- } catch(e){}
328
  }
329
  }
330
  });
@@ -342,14 +326,15 @@ function initWCShortsFeed(){
342
  const ctr = rect.top + rect.height / 2;
343
  let best = -1, bestD = 1e9;
344
  slides.forEach((sl,i)=>{
345
- const d = Math.abs(sl.getBoundingClientRect().top + sl.getBoundingClientRect().height/2 - ctr);
 
346
  if(d < bestD){ bestD = d; best = i; }
347
  });
348
  if(best >= 0) activate(best);
349
- }, 100);
350
  });
351
 
352
- // Auto-advance every 8 seconds for continuous playback
353
  const autoTimer = setInterval(()=>{
354
  if(isManualScroll || !document.getElementById('wc-shorts-feed')) return;
355
  const next = cur + 1;
@@ -358,15 +343,55 @@ function initWCShortsFeed(){
358
  } else {
359
  slides[0].scrollIntoView({behavior:'smooth', block:'start'});
360
  }
361
- }, 8000);
362
 
363
  feed._autoTimer = autoTimer;
364
 
365
- setTimeout(() => {
 
366
  if(slides.length > 0){ slides[0].scrollIntoView({behavior:'start'}); activate(0); }
367
- }, 400);
368
  }
369
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
370
  // === LIVE AUTO-REFRESH (90s) ===
371
  function startWCLiveRefresh(){
372
  if(_wcRefreshInterval)clearInterval(_wcRefreshInterval);
 
3
  const WC_TEAMS={'Mexico':'🇲🇽 Mexico','Colombia':'🇨🇴 Colombia','USA':'🇺🇸 Mỹ','United States':'🇺🇸 Mỹ','Brazil':'🇧🇷 Brazil','Argentina':'🇦🇷 Argentina','Germany':'🇩🇪 Đức','France':'🇫🇷 Pháp','Spain':'🇪🇸 Tây Ban Nha','England':'🏴󠁧󠁢󠁥󠁮󠁧󠁿 Anh','Portugal':'🇵🇹 Bồ Đào Nha','Netherlands':'🇳🇱 Hà Lan','Italy':'🇮🇹 Ý','Belgium':'🇧🇪 Bỉ','Croatia':'🇭🇷 Croatia','Uruguay':'🇺🇾 Uruguay','Japan':'🇯🇵 Nhật Bản','South Korea':'🇰🇷 Hàn Quốc','Korea Republic':'🇰🇷 Hàn Quốc','Australia':'🇦🇺 Úc','Saudi Arabia':'🇸🇦 Ả Rập Xê Út','Iran':'🇮🇷 Iran','Qatar':'🇶🇦 Qatar','Canada':'🇨🇦 Canada','Morocco':'🇲🇦 Morocco','Senegal':'🇸🇳 Senegal','Ecuador':'🇪🇨 Ecuador','Serbia':'🇷🇸 Serbia','Switzerland':'🇨🇭 Thụy Sĩ','Denmark':'🇩🇰 Đan Mạch','Poland':'🇵🇱 Ba Lan','Turkey':'🇹🇷 Thổ Nhĩ Kỳ','Türkiye':'🇹🇷 Thổ Nhĩ Kỳ','Ukraine':'🇺🇦 Ukraine','Egypt':'🇪🇬 Ai Cập','Nigeria':'🇳🇬 Nigeria','Cameroon':'🇨🇲 Cameroon','Ghana':'🇬🇭 Ghana','Tunisia':'🇹🇳 Tunisia','Algeria':'🇩🇿 Algeria','South Africa':'🇿🇦 Nam Phi','Indonesia':'🇮🇩 Indonesia','Vietnam':'🇻🇳 Việt Nam','China PR':'🇨🇳 Trung Quốc','New Zealand':'🇳🇿 New Zealand','Panama':'🇵🇦 Panama','Costa Rica':'🇨🇷 Costa Rica','Jamaica':'🇯🇲 Jamaica','Honduras':'🇭🇳 Honduras','Paraguay':'🇵🇾 Paraguay','Bolivia':'🇧🇴 Bolivia','Chile':'🇨🇱 Chile','Peru':'🇵🇪 Peru','Venezuela':'🇻🇪 Venezuela','Norway':'🇳🇴 Na Uy','Sweden':'🇸🇪 Thụy Điển','Austria':'🇦🇹 Áo','Scotland':'🏴󠁧󠁢󠁳󠁣󠁴󠁿 Scotland','Wales':'🏴󠁧󠁢󠁷󠁬󠁳󠁿 Xứ Wales','Ireland':'🇮🇪 Ireland','Romania':'🇷🇴 Romania','Hungary':'🇭🇺 Hungary','Greece':'🇬🇷 Hy Lạp','Uzbekistan':'🇺🇿 Uzbekistan','Iraq':'🇮🇶 Iraq','TBD':'🏳️ TBD'};
4
  function _t(n){return WC_TEAMS[n]||WC_TEAMS[(n||'').trim()]||'🏴 '+n;}
5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  function switchWCTab(tab){
7
  document.querySelectorAll('.wc-tab').forEach(t=>t.classList.remove('active'));
8
+ const tabMap = {'news':'Tin','fixtures':'Lịch','standings':'BXH','highlights':'Highlight','shorts':'Shorts','stats':'Thống'};
9
+ document.querySelectorAll('.wc-tab').forEach(t=>{if(tabMap[tab]&&t.textContent.includes(tabMap[tab]))t.classList.add('active');});
 
 
 
 
 
 
 
 
 
10
  const el=document.getElementById('wc-content');if(!el)return;
11
  if(tab==='news')renderWCNews(el);
12
  else if(tab==='fixtures')renderWCFixtures(el);
 
54
  if(!_wc2026Data){el.innerHTML='<div class="loading">Đang tải...</div>';return;}
55
  const d=_wc2026Data.standings||{};
56
  if(d.html&&d.html.length>100){
57
+ el.innerHTML='<div class="wc-bxh">'+d.html+'</div>';
 
 
 
 
 
 
 
 
 
 
58
  el.querySelectorAll('a').forEach(a=>a.addEventListener('click',e=>e.preventDefault()));
59
  return;
60
  }
61
+ el.innerHTML='<div class="loading">BXH cập nhật khi giải bắt đầu 11/06/2026<br><small style="color:#6a9fca">🔴 Livescore tự động</small></div>';
 
 
 
 
 
 
62
  }
63
 
64
  // === STATS ===
65
  function renderWCStats(el){
66
  if(!_wc2026Data){el.innerHTML='<div class="loading">Đang tải...</div>';return;}
67
  const d=_wc2026Data.stats||{};
68
+ if(d.html&&d.html.length>100){el.innerHTML='<div class="wc-bxh">'+d.html+'</div>';el.querySelectorAll('a').forEach(a=>a.addEventListener('click',e=>e.preventDefault()));}
69
  else el.innerHTML='<div class="loading">Thống kê cập nhật khi giải bắt đầu<br><small style="color:#6a9fca">Vua phá lưới · Kiến tạo · Thẻ phạt</small></div>';
70
  }
71
 
 
78
  const seen = new Map();
79
  wcVids.forEach((v,i)=>{if(!seen.has(v.link)) seen.set(v.link, {...v, _li: seen.size});});
80
  wcVids = Array.from(seen.values());
81
+ if(!wcVids.length){el.innerHTML='<div class="loading">Chưa có highlight World Cup</div>';return;}
 
 
 
82
  let h='<div style="display:grid;grid-template-columns:repeat(2,1fr);gap:6px;max-height:450px;overflow-y:auto;padding:4px">';
83
  wcVids.forEach((v,i)=>{
84
  h+=`<div style="cursor:pointer;border-radius:6px;overflow:hidden;background:#0a1520" onclick="openHighlightFeed('world-cup',${v._li},'${esc(v.link)}')">`;
85
  h+=`<div style="position:relative;aspect-ratio:16/9;background:#1a2a3a">${v.img?`<img src="${esc(v.img)}" loading="lazy" style="width:100%;height:100%;object-fit:cover" onerror="this.style.display='none'">`:''}<div class="card-play">▶</div></div>`;
86
+ h+=`<div style="padding:4px 6px"><span style="font-size:8px;color:#f0c040">🌍 World Cup</span><div style="font-size:10px;color:#ccc;line-height:1.2;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden">${esc(v.title)}</div></div></div>`;
 
87
  });
88
  h+='</div>';el.innerHTML=h;
89
+ }catch(e){el.innerHTML='<div class="loading">Lỗi tải highlight</div>';}
 
 
90
  }
91
 
92
+ // ============================================================
93
+ // === WC SHORTS — TikTok-style vertical slideshow ===
94
+ // ============================================================
95
+
96
+ // Inject WC Shorts CSS
97
+ (function(){
98
+ if(document.getElementById('wc-shorts-css')) return;
99
+ const style = document.createElement('style');
100
+ style.id = 'wc-shorts-css';
101
+ style.textContent = `
102
+ .wc-shorts-container{width:100%;height:72vh;max-height:620px;min-height:380px;background:#000;border-radius:8px;overflow:hidden;position:relative}
103
+ .wc-shorts-feed{height:100%;overflow-y:scroll;scroll-snap-type:y mandatory;scrollbar-width:none;-ms-overflow-style:none}
104
+ .wc-shorts-feed::-webkit-scrollbar{display:none}
105
+ .wc-short-slide{height:72vh;max-height:620px;min-height:380px;scroll-snap-align:start;position:relative;background:#000;display:flex;align-items:center;justify-content:center;overflow:hidden}
106
+ .wc-short-slide iframe{width:100%;height:100%;border:none;background:#000}
107
+ .wc-short-overlay{position:absolute;bottom:0;left:0;right:0;padding:12px 14px 16px;background:linear-gradient(transparent 0%,rgba(0,0,0,.72) 40%,rgba(0,0,0,.92) 100%);z-index:3;pointer-events:none}
108
+ .wc-short-badge{display:inline-block;font-size:9px;font-weight:700;padding:2px 8px;border-radius:10px;color:#fff;background:#0b6bcb;margin-bottom:6px}
109
+ .wc-short-title{font-size:13px;color:#fff;font-weight:700;line-height:1.35;text-shadow:0 1px 3px rgba(0,0,0,.6);display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
110
+ .wc-short-counter{position:absolute;top:10px;right:10px;background:rgba(0,0,0,.55);font-size:10px;padding:3px 9px;border-radius:10px;color:#fff;z-index:4;font-weight:600;backdrop-filter:blur(4px)}
111
+ .wc-short-actions{position:absolute;right:10px;bottom:80px;display:flex;flex-direction:column;align-items:center;gap:16px;z-index:5}
112
+ .wc-short-action-btn{display:flex;flex-direction:column;align-items:center;gap:2px;background:none;border:0;color:#fff;cursor:pointer;font-size:10px;text-shadow:0 1px 3px rgba(0,0,0,.5)}
113
+ .wc-short-action-btn .icon{width:44px;height:44px;border-radius:50%;background:rgba(255,255,255,.15);display:flex;align-items:center;justify-content:center;font-size:20px;backdrop-filter:blur(4px);transition:background .2s}
114
+ .wc-short-action-btn:hover .icon{background:rgba(255,255,255,.28)}
115
+ .wc-short-action-btn .count{font-size:10px;color:#eee;font-weight:600}
116
+ .wc-shorts-empty{text-align:center;padding:40px 20px;color:#888;font-size:13px}
117
+ .wc-shorts-empty .icon{font-size:40px;margin-bottom:10px}
118
+ .wc-shorts-empty button{background:#0b6bcb;border:none;color:#fff;padding:10px 24px;border-radius:20px;font-size:13px;font-weight:700;cursor:pointer;margin-top:16px}
119
+ /* Horizontal slider on WC tab */
120
+ .wc-shorts-slider{display:flex;overflow-x:auto;gap:8px;padding:4px 4px 8px;scrollbar-width:none}
121
+ .wc-shorts-slider::-webkit-scrollbar{display:none}
122
+ .wc-shorts-slider-item{flex:0 0 120px;cursor:pointer;border-radius:8px;overflow:hidden;background:#0a1520;border:1px solid #1a3a5a;transition:border-color .2s,transform .2s}
123
+ .wc-shorts-slider-item:hover{border-color:#0b6bcb;transform:scale(1.03)}
124
+ .wc-shorts-slider-thumb{position:relative;width:100%;aspect-ratio:9/16;background:#1a2a3a;overflow:hidden}
125
+ .wc-shorts-slider-thumb img{width:100%;height:100%;object-fit:cover}
126
+ .wc-shorts-slider-thumb .play-overlay{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:32px;height:32px;border-radius:50%;background:rgba(0,0,0,.55);display:flex;align-items:center;justify-content:center;color:#fff;font-size:13px}
127
+ .wc-shorts-slider-info{padding:4px 5px}
128
+ .wc-shorts-slider-info .badge{font-size:8px;font-weight:700;padding:1px 5px;border-radius:3px;color:#fff;background:#0b6bcb;display:inline-block;margin-bottom:2px}
129
+ .wc-shorts-slider-info .title{font-size:9px;color:#ccc;line-height:1.2;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
130
+ .wc-shorts-view-all{flex:0 0 100px;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#0b2e4a,#1a3a5a);border-radius:8px;cursor:pointer;border:1px dashed #2a5a8a;color:#8ab4d8;font-size:11px;font-weight:700;text-align:center;padding:8px;flex-shrink:0;transition:border-color .2s}
131
+ .wc-shorts-view-all:hover{border-color:#0b6bcb;color:#fff}
132
+ `;
133
+ document.head.appendChild(style);
134
+ })();
135
+
136
+ // === WC SHORTS TAB (horizontal slider in WC section) ===
137
  async function renderWCShorts(el){
138
+ el.innerHTML='<div class="loading" style="display:flex;flex-direction:column;align-items:center;gap:8px;padding:30px"><div style="width:24px;height:24px;border:2px solid #333;border-top-color:#00ccff;border-radius:50%;animation:vtvspin .8s linear infinite"></div>Đang tải shorts...</div>';
139
 
 
140
  let wcShorts = [];
141
+
142
+ // Fetch from dedicated VTV Nam Bộ endpoint
143
  try {
144
+ const r = await fetch('/api/shorts/vtvnamo?count=50', {signal: AbortSignal.timeout(15000)});
145
  wcShorts = await r.json() || [];
146
+ } catch(e) { console.warn('VTV shorts fetch failed:', e); }
 
 
147
 
148
+ // Also from _shortsData
149
  if(typeof _shortsData !== 'undefined' && _shortsData.length){
150
  const existingIds = new Set(wcShorts.map(s=>s.id));
151
+ _shortsData.forEach(s=>{if(s.channel==='vtvnambo'&&!existingIds.has(s.id)){wcShorts.push(s);}});
 
 
 
 
152
  }
153
 
154
+ // Also from _wcShortsData
155
  if(typeof _wcShortsData !== 'undefined' && _wcShortsData.length){
156
  const existingIds = new Set(wcShorts.map(s=>s.id));
157
  _wcShortsData.forEach(s=>{if(!existingIds.has(s.id)) wcShorts.push(s);});
158
  }
159
 
160
  if(!wcShorts.length){
161
+ el.innerHTML=`<div class="wc-shorts-empty"><div class="icon">📱</div><div style="color:#ccc;font-size:14px;font-weight:700;margin-bottom:6px">Shorts World Cup</div><div>VTV Nam Bộ & khoảnh khắc World Cup</div><div style="font-size:11px;color:#6a9fca;margin-top:4px">Đang cập nhật shorts từ YouTube...</div><button onclick="openWCShortsFeed(0)">📱 Xem Shorts</button></div>`;
 
 
 
 
 
 
162
  return;
163
  }
164
 
 
166
  const seen = new Set();
167
  wcShorts = wcShorts.filter(s=>{if(seen.has(s.id)) return false; seen.add(s.id); return true;});
168
 
169
+ // Store globally
170
+ _wcShortsData = wcShorts;
171
+
172
+ // Render horizontal slider
173
  let h = `<div style="padding:4px">
174
  <div style="display:flex;justify-content:space-between;align-items:center;padding:4px 6px 8px">
175
  <span style="font-size:13px;font-weight:800;color:#f0c040">📱 Shorts World Cup</span>
176
+ <span style="font-size:10px;color:#8ab4d8;cursor:pointer;font-weight:600" onclick="openWCShortsFeed(0)">Xem tất cả →</span>
177
  </div>
178
+ <div class="wc-shorts-slider">`;
179
+
180
+ wcShorts.slice(0,15).forEach((s,i)=>{
181
+ h += `<div class="wc-shorts-slider-item" onclick="openWCShortsFeed(${i})">`;
182
+ h += `<div class="wc-shorts-slider-thumb">`;
183
+ h += s.img ? `<img src="${esc(s.img)}" loading="lazy" onerror="this.style.display='none'">` : '';
184
+ h += `<div class="play-overlay">▶</div></div>`;
185
+ h += `<div class="wc-shorts-slider-info"><span class="badge">VTV Nam Bộ</span><div class="title">${esc(s.title)}</div></div>`;
 
 
 
186
  h += `</div>`;
187
  });
188
 
189
+ h += `<div class="wc-shorts-view-all" onclick="openWCShortsFeed(0)">📱 Xem tất cả<br><small style="font-size:9px;color:#6a9fca">${wcShorts.length} shorts</small></div>`;
 
 
190
  h += `</div></div>`;
191
  el.innerHTML = h;
192
  }
193
 
194
+ // === FULL-SCREEN TIKTOK FEED ===
195
  async function openWCShortsFeed(startIdx){
196
  showView('view-tiktok');
197
  const el = document.getElementById('view-tiktok');
198
+ el.innerHTML = '<div class="loading" style="display:flex;flex-direction:column;align-items:center;gap:8px"><div style="width:24px;height:24px;border:2px solid #333;border-top-color:#00ccff;border-radius:50%;animation:vtvspin .8s linear infinite"></div>Đang tải shorts...</div>';
199
 
200
+ let wcShorts = _wcShortsData || [];
 
201
 
202
+ // If no cached data, fetch fresh
203
+ if(!wcShorts.length){
204
+ try {
205
+ const r = await fetch('/api/shorts/vtvnamo?count=50', {signal: AbortSignal.timeout(15000)});
206
+ wcShorts = await r.json() || [];
207
+ } catch(e) { console.warn('VTV fetch failed:', e); }
 
208
  }
209
 
210
+ // Fallback to _shortsData
211
+ if(!wcShorts.length && typeof _shortsData !== 'undefined' && _shortsData.length){
212
+ wcShorts = _shortsData.filter(s => s.channel === 'vtvnambo');
 
 
 
 
 
 
 
213
  }
214
+
215
+ // Fallback to general shorts
216
  if(!wcShorts.length){
217
  try{
218
  const r = await fetch('/api/shorts');
219
+ const all = await r.json() || [];
220
+ wcShorts = all.filter(s => s.channel === 'vtvnambo');
221
+ if(!wcShorts.length) wcShorts = all;
222
+ }catch(e){ wcShorts = []; }
 
 
 
223
  }
224
 
225
  // Deduplicate
 
227
  wcShorts = wcShorts.filter(s=>{if(seen.has(s.id)) return false; seen.add(s.id); return true;});
228
 
229
  if(!wcShorts.length){
230
+ el.innerHTML = '<div class="wc-shorts-empty"><div class="icon">📱</div><div>Chưa có shorts</div></div>';
231
  return;
232
  }
233
 
234
+ _wcShortsData = wcShorts;
 
235
 
236
  // Rotate so startIdx is first
237
  const ordered = startIdx > 0 ? [...wcShorts.slice(startIdx), ...wcShorts.slice(0,startIdx)] : [...wcShorts];
238
 
239
+ // Build TikTok-style feed
240
  let h = `<button class="back-btn" onclick="switchCat('home')">← World Cup Shorts</button>`;
241
+ h += '<div class="wc-shorts-container"><div class="wc-shorts-feed" id="wc-shorts-feed">';
242
 
243
  ordered.forEach((s,i)=>{
244
  const id = s.id || '';
245
+ // YouTube embed autoplay=0 initially, will be set to 1 when active
246
+ const src = `https://www.youtube.com/embed/${id}?autoplay=0&rel=0&playsinline=1&mute=0&loop=1&playlist=${id}&enablejsapi=1&origin=${encodeURIComponent(location.origin)}`;
247
+ const shareUrl = 'https://youtube.com/shorts/' + id;
 
 
 
248
  const videoId = 'wcshort-' + id;
249
+
250
+ h += `<div class="wc-short-slide" data-vid="${esc(videoId)}" data-idx="${i}">`;
251
+ h += `<iframe data-yt-src="${src}" allowfullscreen allow="accelerometer;autoplay;clipboard-write;encrypted-media;gyroscope;picture-in-picture;fullscreen"></iframe>`;
252
+
253
+ // Title overlay
254
+ h += `<div class="wc-short-overlay">`;
255
+ h += `<span class="wc-short-badge">VTV Nam Bộ</span>`;
256
+ h += `<div class="wc-short-title">${esc(s.title)}</div>`;
257
+ h += `</div>`;
258
+
259
+ // Counter
260
+ h += `<span class="wc-short-counter">${i+1}/${ordered.length}</span>`;
261
+
262
+ // Action buttons (right side)
263
+ h += `<div class="wc-short-actions">`;
264
+ h += `<button class="wc-short-action-btn" onclick="event.stopPropagation();doWCShortInteract('${esc(id)}','view',this)" title="Xem"><div class="icon">👁</div><span class="count" id="wc-vc-${i}">0</span></button>`;
265
+ h += `<button class="wc-short-action-btn" onclick="event.stopPropagation();doWCShortInteract('${esc(id)}','like',this)" title="Thích"><div class="icon">❤️</div><span class="count" id="wc-lc-${i}">0</span></button>`;
266
+ h += `<button class="wc-short-action-btn" onclick="event.stopPropagation();doWCShortInteract('${esc(id)}','share',this)" title="Chia sẻ"><div class="icon">📤</div></button>`;
267
+ h += `<button class="wc-short-action-btn" onclick="event.stopPropagation();window.open('${shareUrl}','_blank')" title="Mở trên YouTube"><div class="icon">🔗</div></button>`;
268
+ h += `</div>`;
269
+
270
+ h += `</div>`;
271
  });
272
 
273
  h += '</div></div>';
274
  el.innerHTML = h;
275
 
276
+ // Init autoplay on scroll
277
+ initWCShortsAutoplay();
278
+
279
+ // Load interaction counts
280
+ loadWCShortCounters(ordered);
281
  }
282
 
283
+ // === Autoplay logic ===
284
+ function initWCShortsAutoplay(){
285
  const feed = document.getElementById('wc-shorts-feed');
286
  if(!feed) return;
287
+ const slides = feed.querySelectorAll('.wc-short-slide');
288
  let cur = -1;
289
  let isManualScroll = false;
290
  let manualTimer = null;
 
294
  slides.forEach((sl,idx)=>{
295
  const fr = sl.querySelector('iframe');
296
  if(idx === i){
297
+ // Load iframe if not loaded
298
  if(fr && !fr.src && fr.dataset.ytSrc){ fr.src = fr.dataset.ytSrc; }
299
+ // Autoplay via postMessage
300
  if(fr && fr.src){
301
+ setTimeout(()=>{
302
+ try { fr.contentWindow.postMessage('{"event":"command","func":"playVideo","args":""}','*'); } catch(e){}
303
+ }, 300);
304
  }
305
+ // Track view
306
  const vid = sl.dataset.vid;
307
+ if(vid && !sl._viewed){ sl._viewed = true; doWCShortInteract(vid.replace('wcshort-',''),'',null); }
308
  } else {
309
+ // Pause
310
  if(fr && fr.src){
311
+ try { fr.contentWindow.postMessage('{"event":"command","func":"pauseVideo","args":""}','*'); } catch(e){}
 
 
312
  }
313
  }
314
  });
 
326
  const ctr = rect.top + rect.height / 2;
327
  let best = -1, bestD = 1e9;
328
  slides.forEach((sl,i)=>{
329
+ const r = sl.getBoundingClientRect();
330
+ const d = Math.abs(r.top + r.height/2 - ctr);
331
  if(d < bestD){ bestD = d; best = i; }
332
  });
333
  if(best >= 0) activate(best);
334
+ }, 120);
335
  });
336
 
337
+ // Auto-advance every 10 seconds
338
  const autoTimer = setInterval(()=>{
339
  if(isManualScroll || !document.getElementById('wc-shorts-feed')) return;
340
  const next = cur + 1;
 
343
  } else {
344
  slides[0].scrollIntoView({behavior:'smooth', block:'start'});
345
  }
346
+ }, 10000);
347
 
348
  feed._autoTimer = autoTimer;
349
 
350
+ // Activate first slide
351
+ setTimeout(()=>{
352
  if(slides.length > 0){ slides[0].scrollIntoView({behavior:'start'}); activate(0); }
353
+ }, 500);
354
  }
355
 
356
+ // === Interaction helpers ===
357
+ async function doWCShortInteract(videoId, type, btn){
358
+ if(type){
359
+ try{
360
+ const r = await fetch('/api/short-action',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({id:videoId,action:type})});
361
+ const j = await r.json();
362
+ if(j && j.stats){
363
+ // Update counts in the active slide
364
+ const activeSlide = document.querySelector('.wc-short-slide');
365
+ if(activeSlide){
366
+ const idx = activeSlide.dataset.idx;
367
+ if(type==='view'){ const el=document.getElementById('wc-vc-'+idx); if(el) el.textContent=fmtNum(j.stats.views); }
368
+ if(type==='like'){ const el=document.getElementById('wc-lc-'+idx); if(el) el.textContent=fmtNum(j.stats.likes); }
369
+ }
370
+ }
371
+ }catch(e){}
372
+ } else {
373
+ // Just a view count increment
374
+ try{ await fetch('/api/short-action',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({id:videoId,action:'view'})}); }catch(e){}
375
+ }
376
+ }
377
+
378
+ async function loadWCShortCounters(shorts){
379
+ for(let i=0;i<shorts.length;i++){
380
+ const id = shorts[i].id;
381
+ if(!id) continue;
382
+ try{
383
+ const r = await fetch('/api/short-stats?ids='+id);
384
+ const j = await r.json();
385
+ if(j.stats && j.stats[id]){
386
+ const vc=document.getElementById('wc-vc-'+i); if(vc) vc.textContent=fmtNum(j.stats[id].views);
387
+ const lc=document.getElementById('wc-lc-'+i); if(lc) lc.textContent=fmtNum(j.stats[id].likes);
388
+ }
389
+ }catch(e){}
390
+ }
391
+ }
392
+
393
+ function fmtNum(n){if(!n)return'0';if(n>=1e6)return(n/1e6).toFixed(1)+'M';if(n>=1e3)return(n/1e3).toFixed(1)+'K';return String(n);}
394
+
395
  // === LIVE AUTO-REFRESH (90s) ===
396
  function startWCLiveRefresh(){
397
  if(_wcRefreshInterval)clearInterval(_wcRefreshInterval);