bep40 commited on
Commit
c20d68f
·
verified ·
1 Parent(s): 0f5ee19

Upload static/yt_live.js

Browse files
Files changed (1) hide show
  1. static/yt_live.js +37 -16
static/yt_live.js CHANGED
@@ -1,6 +1,7 @@
1
  // === VNEWS — VTV1-VTV10 + VTVPrime LIVE CHANNELS ===
2
  // Uses backend /api/vtv/streams for stream URLs
3
  // VTV1-VTV5,VTV7-VTV9: VTVGo failover | VTV6,VTV10: xemtv.net scraping
 
4
 
5
  (function(){
6
  if(window._ytLiveLoaded) return;
@@ -20,6 +21,9 @@
20
  {id:'vtvprime', name:'VTVPrime', badge:'Prime'},
21
  ];
22
 
 
 
 
23
  const NEEDS_PROXY = /fptplay\.net/;
24
  const STREAMS = {};
25
  let _currentCh = null;
@@ -54,12 +58,16 @@
54
  .vtv-epg-toggle{background:none;border:1px solid #2a3a4a;color:#8ab4d8;font-size:9px;padding:2px 8px;border-radius:6px;cursor:pointer}
55
  .vtv-epg-list{display:flex;gap:4px;overflow-x:auto;scrollbar-width:none;padding-bottom:4px}
56
  .vtv-epg-list::-webkit-scrollbar{display:none}
57
- .vtv-epg-item{flex:0 0 auto;padding:3px 6px;background:#1a2a3a;border-radius:4px;font-size:8px;color:#8ab4d8;white-space:nowrap}
 
58
  .vtv-epg-item.now{background:#0066cc;color:#fff;font-weight:700}
59
  .vtv-epg-item .epg-t{font-size:7px;color:#6a8aaa}
60
  .vtv-epg-item.now .epg-t{color:#aaccee}
61
  .vtv-epg-item .epg-n{color:#ccc;font-size:8px}
62
  .vtv-epg-item.now .epg-n{color:#fff}
 
 
 
63
  `;
64
  document.head.appendChild(s);
65
 
@@ -122,7 +130,7 @@
122
  '</div>' +
123
  '<div class="vtv-epg" id="vtv-epg">' +
124
  '<div class="vtv-epg-header"><span class="vtv-epg-title">📋 Lịch phát sóng</span><button class="vtv-epg-toggle" id="vtv-epg-toggle" onclick="window._vtvToggleEpg()">Ẩn</button></div>' +
125
- '<div class="vtv-epg-list" id="vtv-epg-list"><span style="color:#666;font-size:9px;padding:4px">Đang tải EPG...</span></div>' +
126
  '</div>';
127
  return w;
128
  }
@@ -130,28 +138,46 @@
130
  async function loadEpg(chId){
131
  const epgList = document.getElementById('vtv-epg-list');
132
  if(!epgList) return;
 
 
 
 
133
  try {
134
- const r = await fetch('/api/vtv/epg/' + chId, {signal: AbortSignal.timeout(8000)});
135
- if(!r.ok) throw new Error('EPG fetch failed');
136
  const data = await r.json();
137
  const programs = data.programs || [];
 
138
  if(programs.length === 0){
139
- epgList.innerHTML = '<span style="color:#666;font-size:9px;padding:4px">Chưa có lịch phát sóng</span>';
140
  return;
141
  }
 
142
  let h = '';
143
  programs.forEach(p => {
144
  const isNow = p.now ? ' now' : '';
145
- h += '<div class="vtv-epg-item' + isNow + '" title="' + (p.title || '') + '">';
 
146
  h += '<span class="epg-t">' + (p.time || '') + '</span> ';
147
  h += '<span class="epg-n">' + (p.title || '') + '</span>';
148
  h += '</div>';
149
  });
150
  epgList.innerHTML = h;
 
 
 
 
 
 
151
  } catch(e) {
152
- if(epgList) epgList.innerHTML = '<span style="color:#666;font-size:9px;padding:4px">Không tải được EPG</span>';
 
153
  }
154
  }
 
 
 
 
155
 
156
  window._vtvToggleEpg = function(){
157
  const list = document.getElementById('vtv-epg-list');
@@ -178,12 +204,8 @@
178
  _blockInserted = true;
179
  if(!_streamsLoaded){
180
  loadAllStreams().then(() => {
181
- for(const ch of CHANNELS){
182
- if(STREAMS[ch.id] && STREAMS[ch.id].length > 0){
183
- setTimeout(() => window._vtvPlay(ch.id), 300);
184
- break;
185
- }
186
- }
187
  });
188
  }
189
  }
@@ -293,8 +315,7 @@
293
  }
294
  }
295
 
296
- // FIX: Only wrap loadHome, no DOMContentLoaded path.
297
- // This ensures pinBlock runs exactly once after loadHome completes.
298
  const orig = window.loadHome;
299
  if(orig && !orig.__vtvWrapped){
300
  window.loadHome = async function(){
@@ -308,4 +329,4 @@
308
  };
309
  window.loadHome.__vtvWrapped = true;
310
  }
311
- })();
 
1
  // === VNEWS — VTV1-VTV10 + VTVPrime LIVE CHANNELS ===
2
  // Uses backend /api/vtv/streams for stream URLs
3
  // VTV1-VTV5,VTV7-VTV9: VTVGo failover | VTV6,VTV10: xemtv.net scraping
4
+ // Default channel: VTV6
5
 
6
  (function(){
7
  if(window._ytLiveLoaded) return;
 
21
  {id:'vtvprime', name:'VTVPrime', badge:'Prime'},
22
  ];
23
 
24
+ // Default channel is VTV6
25
+ const DEFAULT_CHANNEL = 'vtv6';
26
+
27
  const NEEDS_PROXY = /fptplay\.net/;
28
  const STREAMS = {};
29
  let _currentCh = null;
 
58
  .vtv-epg-toggle{background:none;border:1px solid #2a3a4a;color:#8ab4d8;font-size:9px;padding:2px 8px;border-radius:6px;cursor:pointer}
59
  .vtv-epg-list{display:flex;gap:4px;overflow-x:auto;scrollbar-width:none;padding-bottom:4px}
60
  .vtv-epg-list::-webkit-scrollbar{display:none}
61
+ .vtv-epg-item{flex:0 0 auto;padding:3px 6px;background:#1a2a3a;border-radius:4px;font-size:8px;color:#8ab4d8;white-space:nowrap;cursor:pointer;transition:all .2s}
62
+ .vtv-epg-item:hover{background:#2a4a6a}
63
  .vtv-epg-item.now{background:#0066cc;color:#fff;font-weight:700}
64
  .vtv-epg-item .epg-t{font-size:7px;color:#6a8aaa}
65
  .vtv-epg-item.now .epg-t{color:#aaccee}
66
  .vtv-epg-item .epg-n{color:#ccc;font-size:8px}
67
  .vtv-epg-item.now .epg-n{color:#fff}
68
+ .vtv-epg-empty{color:#666;font-size:9px;padding:4px}
69
+ .vtv-epg-loading{color:#00ccff;font-size:9px;padding:4px;display:flex;align-items:center;gap:6px}
70
+ .vtv-epg-spinner{width:10px;height:10px;border:1px solid #333;border-top-color:#00ccff;border-radius:50%;animation:vtvspin .8s linear infinite}
71
  `;
72
  document.head.appendChild(s);
73
 
 
130
  '</div>' +
131
  '<div class="vtv-epg" id="vtv-epg">' +
132
  '<div class="vtv-epg-header"><span class="vtv-epg-title">📋 Lịch phát sóng</span><button class="vtv-epg-toggle" id="vtv-epg-toggle" onclick="window._vtvToggleEpg()">Ẩn</button></div>' +
133
+ '<div class="vtv-epg-list" id="vtv-epg-list"><div class="vtv-epg-loading"><div class="vtv-epg-spinner"></div>Đang tải EPG...</div></div>' +
134
  '</div>';
135
  return w;
136
  }
 
138
  async function loadEpg(chId){
139
  const epgList = document.getElementById('vtv-epg-list');
140
  if(!epgList) return;
141
+
142
+ // Show loading state
143
+ epgList.innerHTML = '<div class="vtv-epg-loading"><div class="vtv-epg-spinner"></div>Đang tải lịch phát sóng...</div>';
144
+
145
  try {
146
+ const r = await fetch('/api/vtv/epg/' + chId, {signal: AbortSignal.timeout(10000)});
147
+ if(!r.ok) throw new Error('EPG fetch failed: ' + r.status);
148
  const data = await r.json();
149
  const programs = data.programs || [];
150
+
151
  if(programs.length === 0){
152
+ epgList.innerHTML = '<div class="vtv-epg-empty">Chưa có lịch phát sóng cho kênh này</div>';
153
  return;
154
  }
155
+
156
  let h = '';
157
  programs.forEach(p => {
158
  const isNow = p.now ? ' now' : '';
159
+ const desc = p.description ? ' title="' + escAttr(p.description) + '"' : '';
160
+ h += '<div class="vtv-epg-item' + isNow + '"' + desc + '>';
161
  h += '<span class="epg-t">' + (p.time || '') + '</span> ';
162
  h += '<span class="epg-n">' + (p.title || '') + '</span>';
163
  h += '</div>';
164
  });
165
  epgList.innerHTML = h;
166
+
167
+ // Scroll to current program
168
+ const nowItem = epgList.querySelector('.vtv-epg-item.now');
169
+ if(nowItem){
170
+ nowItem.scrollIntoView({behavior: 'smooth', inline: 'center', block: 'nearest'});
171
+ }
172
  } catch(e) {
173
+ console.warn('EPG load error:', e);
174
+ if(epgList) epgList.innerHTML = '<div class="vtv-epg-empty">Không tải được lịch phát sóng</div>';
175
  }
176
  }
177
+
178
+ function escAttr(s){
179
+ return String(s || '').replace(/&/g,'&amp;').replace(/"/g,'&quot;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
180
+ }
181
 
182
  window._vtvToggleEpg = function(){
183
  const list = document.getElementById('vtv-epg-list');
 
204
  _blockInserted = true;
205
  if(!_streamsLoaded){
206
  loadAllStreams().then(() => {
207
+ // Auto-play VTV6 as default channel
208
+ setTimeout(() => window._vtvPlay(DEFAULT_CHANNEL), 300);
 
 
 
 
209
  });
210
  }
211
  }
 
315
  }
316
  }
317
 
318
+ // Wrap loadHome to inject VTV block
 
319
  const orig = window.loadHome;
320
  if(orig && !orig.__vtvWrapped){
321
  window.loadHome = async function(){
 
329
  };
330
  window.loadHome.__vtvWrapped = true;
331
  }
332
+ })();