bep40 commited on
Commit
965ade4
·
verified ·
1 Parent(s): 61cd00e

restore from commit 719a835ce: static/vtv_init.js (VTV Player v6 — Professional UI + Real-time EPG)

Browse files
Files changed (1) hide show
  1. static/vtv_init.js +20 -85
static/vtv_init.js CHANGED
@@ -1,13 +1,12 @@
1
- // ===== VNEWS VTV Player v8VTV6 proxy, EPG multi-source, FPTPlay fallback =====
2
  (function() {
3
  if (window._vtvInitLoaded) return;
4
  window._vtvInitLoaded = true;
5
 
6
- // VTV6 first (ưu tiên hàng đầu)
7
  var CHANNELS = [
8
- {id:'vtv6',name:'VTV6',badge:'Thanh Niên'},{id:'vtv1',name:'VTV1',badge:'Tin tức'},
9
- {id:'vtv2',name:'VTV2',badge:'Khoa học'},{id:'vtv3',name:'VTV3',badge:'Giải trí'},
10
- {id:'vtv4',name:'VTV4',badge:'Quốc Tế'},{id:'vtv5',name:'VTV5',badge:'Miền Nam'},
11
  {id:'vtv7',name:'VTV7',badge:'Giáo Dục'},{id:'vtv8',name:'VTV8',badge:'Miền Trung'},
12
  {id:'vtv9',name:'VTV9',badge:'Miền Bắc'},{id:'vtv10',name:'VTV10',badge:'Cần Thơ'},
13
  {id:'vtvprime',name:'VTVPrime',badge:'Prime'},
@@ -19,7 +18,7 @@
19
  var _epgData = [];
20
  var _timeTimer = null;
21
 
22
- // ===== CSS =====
23
  (function injectCSS() {
24
  if (document.getElementById('vtv-pro-css')) return;
25
  var s = document.createElement('style');
@@ -75,52 +74,36 @@
75
  document.head.appendChild(s);
76
  })();
77
 
78
- // ===== URL handler =====
79
- // VTV6: vtvdigital CDN uses signed tokens + CORS blocks. Proxy through server.
80
- // Other channels: play directly from CDN (no proxy — FPTPlay blocks server).
81
- function proxyUrl(url, chId) {
82
  if (!url) return url;
83
  if (url.indexOf(location.origin) === 0) return url;
84
  if (url.indexOf('/api/proxy/') === 0) return url;
85
- // VTV6: use server proxy endpoint (handles CORS + token rewriting)
86
- if (chId === 'vtv6' || url.indexOf('vtvdigital') !== -1) {
87
- return '/api/proxy/vtv6/master.m3u8';
88
- }
89
- // FPTPlay: play directly in browser (CDN blocks server)
90
- if (url.indexOf('fptplay') !== -1) {
91
- return url;
92
- }
93
- // Other: use generic proxy
94
  return '/api/proxy/m3u8/vtv?url=' + encodeURIComponent(url);
95
  }
96
 
97
  function formatTime(d) {
98
- var h = d.getUTCHours(), m = d.getUTCMinutes();
99
  return (h < 10 ? '0' : '') + h + ':' + (m < 10 ? '0' : '') + m;
100
  }
101
 
102
- // GMT+0 (UTC)
103
  function getVNTime() {
104
- return new Date();
 
105
  }
106
 
107
- // ===== EPG =====
108
  function loadEPG(chId) {
109
  var epgEl = document.getElementById('vtv-pro-epg-body');
110
  if (!epgEl) return;
111
  epgEl.innerHTML = '<div class="vtv-pro-epg-load"><div class="vtv-pro-spinner" style="width:12px;height:12px;border-width:1.5px;flex-shrink:0"></div>Đang tải lịch...</div>';
112
  var xhr = new XMLHttpRequest();
113
  xhr.open('GET', '/api/vtv/epg/' + chId, true);
114
- xhr.timeout = 15000;
115
  xhr.onload = function() {
116
  try {
117
  var data = JSON.parse(xhr.responseText);
118
  _epgData = data.programs || [];
119
- if (_epgData.length === 0) {
120
- epgEl.innerHTML = '<div class="vtv-pro-epg-empty">Không có lịch phát sóng</div>';
121
- } else {
122
- renderEPG();
123
- }
124
  } catch(e) {
125
  epgEl.innerHTML = '<div class="vtv-pro-epg-empty">Không có lịch phát sóng</div>';
126
  }
@@ -138,11 +121,12 @@
138
  epgEl.innerHTML = '<div class="vtv-pro-epg-empty">Chưa có lịch phát sóng</div>';
139
  return;
140
  }
141
- var now = new Date();
142
- var nowStr = formatTime(now);
143
 
144
  _epgData.sort(function(a, b) { return a.time.localeCompare(b.time); });
145
 
 
146
  var foundNow = false;
147
  for (var i = 0; i < _epgData.length; i++) {
148
  _epgData[i].now = false;
@@ -155,16 +139,6 @@
155
  }
156
  }
157
 
158
- if (!foundNow) {
159
- for (var i = 0; i < _epgData.length; i++) {
160
- if (_epgData[i].time > nowStr) {
161
- _epgData[i].now = true;
162
- _epgData[i].end_time = (_epgData[i+1] || {}).time || '';
163
- break;
164
- }
165
- }
166
- }
167
-
168
  var html = '';
169
  for (var i = 0; i < _epgData.length; i++) {
170
  var p = _epgData[i];
@@ -203,8 +177,7 @@
203
  function updateClock() {
204
  var timeEl = document.getElementById('vtv-pro-time');
205
  if (timeEl) {
206
- var now = new Date();
207
- timeEl.textContent = formatTime(now) + ' GMT+0';
208
  }
209
  }
210
 
@@ -240,13 +213,8 @@
240
  frame.innerHTML = '<div class="vtv-pro-err"><div class="icon">📡</div><div class="msg">Kênh đang tạm ngừng phát sóng</div><button onclick="_vtvProSwitch(\''+chId+'\')">Thử lại</button></div>';
241
  return;
242
  }
243
- // VTV6: proxy through server, with FPTPlay fallback
244
- if (chId === 'vtv6') {
245
- playVTV6(url, frame);
246
- } else {
247
- url = proxyUrl(url, chId);
248
- playStream(url, frame);
249
- }
250
  } catch(e) {
251
  frame.innerHTML = '<div class="vtv-pro-err"><div class="icon">⚠️</div><div class="msg">Lỗi tải dữ liệu</div><button onclick="_vtvProSwitch(\''+chId+'\')">Thử lại</button></div>';
252
  }
@@ -257,33 +225,6 @@
257
  xhr.send();
258
  };
259
 
260
- // VTV6: try server proxy first, fallback to FPTPlay direct
261
- function playVTV6(vtvdigitalUrl, frame) {
262
- // Try proxy first (handles CORS + token rewriting)
263
- var proxyUrl = '/api/proxy/vtv6/master.m3u8';
264
-
265
- // Check if proxy works by doing a HEAD request
266
- var check = new XMLHttpRequest();
267
- check.open('GET', proxyUrl, true);
268
- check.timeout = 10000;
269
- check.onload = function() {
270
- if (check.status === 200) {
271
- // Proxy works! Use it
272
- playStream(proxyUrl, frame);
273
- } else {
274
- // Proxy failed (403 or 502) — try FPTPlay direct
275
- var fallback = 'https://live.fptplay53.net/fnxhd1/vtv6_vhls.smil/chunklist_b5000000.m3u8';
276
- playStream(fallback, frame);
277
- }
278
- };
279
- check.onerror = function() {
280
- // Proxy unreachable — try FPTPlay direct
281
- var fallback = 'https://live.fptplay53.net/fnxhd1/vtv6_vhls.smil/chunklist_b5000000.m3u8';
282
- playStream(fallback, frame);
283
- };
284
- check.send();
285
- }
286
-
287
  function playStream(url, frame) {
288
  var video = document.createElement('video');
289
  video.id = 'vtv-pro-video';
@@ -305,12 +246,6 @@
305
  if (data.fatal) {
306
  hls.destroy();
307
  _hlsInst = null;
308
- // If VTV6 proxy failed, try direct FPTPlay
309
- if (_currentCh === 'vtv6' && url.indexOf('/api/proxy/') === 0) {
310
- var fallback = 'https://live.fptplay53.net/fnxhd1/vtv6_vhls.smil/chunklist_b5000000.m3u8';
311
- playStream(fallback, frame);
312
- return;
313
- }
314
  frame.innerHTML = '<div class="vtv-pro-err"><div class="icon">📹</div><div class="msg">Lỗi phát video</div><button onclick="_vtvProSwitch(\''+_currentCh+'\')">Thử lại</button></div>';
315
  }
316
  });
@@ -365,10 +300,10 @@
365
  homeEl.insertAdjacentHTML('afterbegin', buildHTML());
366
  }
367
 
368
- // Auto-load VTV6 as default
369
  setTimeout(function() {
370
  if (window._vtvProSwitch) {
371
- window._vtvProSwitch('vtv6');
372
  }
373
  }, 500);
374
  }
 
1
+ // ===== VNEWS VTV Player v6Professional UI + Real-time EPG + Auto-refresh =====
2
  (function() {
3
  if (window._vtvInitLoaded) return;
4
  window._vtvInitLoaded = true;
5
 
 
6
  var CHANNELS = [
7
+ {id:'vtv1',name:'VTV1',badge:'Tin tức'},{id:'vtv2',name:'VTV2',badge:'Khoa học'},
8
+ {id:'vtv3',name:'VTV3',badge:'Giải trí'},{id:'vtv4',name:'VTV4',badge:'Quốc Tế'},
9
+ {id:'vtv5',name:'VTV5',badge:'Miền Nam'},{id:'vtv6',name:'VTV6',badge:'Thanh Niên'},
10
  {id:'vtv7',name:'VTV7',badge:'Giáo Dục'},{id:'vtv8',name:'VTV8',badge:'Miền Trung'},
11
  {id:'vtv9',name:'VTV9',badge:'Miền Bắc'},{id:'vtv10',name:'VTV10',badge:'Cần Thơ'},
12
  {id:'vtvprime',name:'VTVPrime',badge:'Prime'},
 
18
  var _epgData = [];
19
  var _timeTimer = null;
20
 
21
+ // ===== CSS chuyên nghiệp =====
22
  (function injectCSS() {
23
  if (document.getElementById('vtv-pro-css')) return;
24
  var s = document.createElement('style');
 
74
  document.head.appendChild(s);
75
  })();
76
 
77
+ function proxyUrl(url) {
 
 
 
78
  if (!url) return url;
79
  if (url.indexOf(location.origin) === 0) return url;
80
  if (url.indexOf('/api/proxy/') === 0) return url;
 
 
 
 
 
 
 
 
 
81
  return '/api/proxy/m3u8/vtv?url=' + encodeURIComponent(url);
82
  }
83
 
84
  function formatTime(d) {
85
+ var h = d.getHours(), m = d.getMinutes();
86
  return (h < 10 ? '0' : '') + h + ':' + (m < 10 ? '0' : '') + m;
87
  }
88
 
 
89
  function getVNTime() {
90
+ var now = new Date();
91
+ return new Date(now.getTime() + 7 * 3600000);
92
  }
93
 
94
+ // ===== EPG real-time =====
95
  function loadEPG(chId) {
96
  var epgEl = document.getElementById('vtv-pro-epg-body');
97
  if (!epgEl) return;
98
  epgEl.innerHTML = '<div class="vtv-pro-epg-load"><div class="vtv-pro-spinner" style="width:12px;height:12px;border-width:1.5px;flex-shrink:0"></div>Đang tải lịch...</div>';
99
  var xhr = new XMLHttpRequest();
100
  xhr.open('GET', '/api/vtv/epg/' + chId, true);
101
+ xhr.timeout = 10000;
102
  xhr.onload = function() {
103
  try {
104
  var data = JSON.parse(xhr.responseText);
105
  _epgData = data.programs || [];
106
+ renderEPG();
 
 
 
 
107
  } catch(e) {
108
  epgEl.innerHTML = '<div class="vtv-pro-epg-empty">Không có lịch phát sóng</div>';
109
  }
 
121
  epgEl.innerHTML = '<div class="vtv-pro-epg-empty">Chưa có lịch phát sóng</div>';
122
  return;
123
  }
124
+ var vnNow = getVNTime();
125
+ var nowStr = formatTime(vnNow);
126
 
127
  _epgData.sort(function(a, b) { return a.time.localeCompare(b.time); });
128
 
129
+ // Client-side now detection using real VN time
130
  var foundNow = false;
131
  for (var i = 0; i < _epgData.length; i++) {
132
  _epgData[i].now = false;
 
139
  }
140
  }
141
 
 
 
 
 
 
 
 
 
 
 
142
  var html = '';
143
  for (var i = 0; i < _epgData.length; i++) {
144
  var p = _epgData[i];
 
177
  function updateClock() {
178
  var timeEl = document.getElementById('vtv-pro-time');
179
  if (timeEl) {
180
+ timeEl.textContent = formatTime(getVNTime()) + ' GMT+7';
 
181
  }
182
  }
183
 
 
213
  frame.innerHTML = '<div class="vtv-pro-err"><div class="icon">📡</div><div class="msg">Kênh đang tạm ngừng phát sóng</div><button onclick="_vtvProSwitch(\''+chId+'\')">Thử lại</button></div>';
214
  return;
215
  }
216
+ url = proxyUrl(url);
217
+ playStream(url, frame);
 
 
 
 
 
218
  } catch(e) {
219
  frame.innerHTML = '<div class="vtv-pro-err"><div class="icon">⚠️</div><div class="msg">Lỗi tải dữ liệu</div><button onclick="_vtvProSwitch(\''+chId+'\')">Thử lại</button></div>';
220
  }
 
225
  xhr.send();
226
  };
227
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
228
  function playStream(url, frame) {
229
  var video = document.createElement('video');
230
  video.id = 'vtv-pro-video';
 
246
  if (data.fatal) {
247
  hls.destroy();
248
  _hlsInst = null;
 
 
 
 
 
 
249
  frame.innerHTML = '<div class="vtv-pro-err"><div class="icon">📹</div><div class="msg">Lỗi phát video</div><button onclick="_vtvProSwitch(\''+_currentCh+'\')">Thử lại</button></div>';
250
  }
251
  });
 
300
  homeEl.insertAdjacentHTML('afterbegin', buildHTML());
301
  }
302
 
303
+ // Auto-load VTV3 as default
304
  setTimeout(function() {
305
  if (window._vtvProSwitch) {
306
+ window._vtvProSwitch('vtv3');
307
  }
308
  }, 500);
309
  }