bep40 commited on
Commit
4750386
·
verified ·
1 Parent(s): b958003

Upload static/vtv_init.js

Browse files
Files changed (1) hide show
  1. static/vtv_init.js +106 -24
static/vtv_init.js CHANGED
@@ -1,4 +1,4 @@
1
- // ===== VNEWS VTV Player v4 - ALWAYS proxy stream URLs + fix CSS =====
2
  (function() {
3
  if (window._vtvInitLoaded) return;
4
  window._vtvInitLoaded = true;
@@ -15,26 +15,85 @@
15
  var _hlsInst = null;
16
  var _currentCh = null;
17
 
18
- // ===== Inject minimal CSS (fix conflicts with yt_live.js styles) =====
19
  (function injectCSS() {
20
  if (document.getElementById('vtv-init-css')) return;
21
  var style = document.createElement('style');
22
  style.id = 'vtv-init-css';
23
  style.textContent =
24
- '#vtv-player-section{display:block !important;position:relative;margin:6px 4px;background:#111;border:1px solid #06c;border-radius:10px;overflow:hidden}' +
25
- '#vtv-player-section .vtwrap-inner{width:100%}' +
26
- '#vtv-player-section .vtv-tab{cursor:pointer !important}' +
27
- '#vtv-player-section video{width:100% !important;height:100% !important;max-width:100% !important;max-height:100% !important;object-fit:contain;position:absolute;inset:0}';
 
 
 
 
 
 
 
 
 
 
28
  document.head.appendChild(style);
29
  })();
30
 
31
  function proxyUrl(url) {
32
- // Always proxy all stream URLs to avoid CORS
33
  if (url.indexOf(location.origin) === 0) return url;
34
  if (url.indexOf('/api/proxy/') === 0) return url;
35
  return '/api/proxy/m3u8/vtv?url=' + encodeURIComponent(url);
36
  }
37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  window._vtvSwitch = function(chId) {
39
  if (_hlsInst) { try { _hlsInst.destroy(); } catch(e){} _hlsInst = null; }
40
  _currentCh = chId;
@@ -44,9 +103,23 @@
44
  tabs[i].className = 'vtv-tab' + (tabs[i].getAttribute('data-ch') === chId ? ' on' : ' off');
45
  }
46
 
 
 
 
 
 
 
 
 
 
 
 
47
  var frame = document.getElementById('vtv-frame');
48
  if (!frame) return;
49
- frame.innerHTML = '<div class="vtv-load" style="display:flex;align-items:center;justify-content:center;min-height:180px;color:#0cf;font-size:12px;flex-direction:column;gap:8px"><div style="width:24px;height:24px;border:2px solid #333;border-top-color:#0cf;border-radius:50%;animation:vtvspin .8s linear infinite"></div><span>Đang tải ' + chId.toUpperCase() + '...</span></div>';
 
 
 
50
 
51
  var xhr = new XMLHttpRequest();
52
  xhr.open('GET', '/api/vtv/stream/' + chId, true);
@@ -56,17 +129,17 @@
56
  var data = JSON.parse(xhr.responseText);
57
  var url = data.stream_url;
58
  if (!url) {
59
- frame.innerHTML = '<div class="vtv-err" style="display:flex;align-items:center;justify-content:center;min-height:180px;color:#888;font-size:12px;text-align:center;padding:20px;flex-direction:column;gap:8px"><span style="color:#e74c3c;font-size:24px">⚠️</span><span>Kênh đang tạm ngắt</span><button onclick="_vtvSwitch(\''+chId+'\')" style="background:#06c;border:0;color:#fff;padding:6px 14px;border-radius:8px;font-size:11px;cursor:pointer">🔄 Thử lại</button></div>';
60
  return;
61
  }
62
  url = proxyUrl(url);
63
- playStream(url, frame);
64
  } catch(e) {
65
- frame.innerHTML = '<div class="vtv-err" style="display:flex;align-items:center;justify-content:center;min-height:180px;color:#888;padding:20px;flex-direction:column;gap:8px"><span style="color:#e74c3c">⚠️ Lỗi dữ liệu</span><button onclick="_vtvSwitch(\''+chId+'\')" style="background:#06c;border:0;color:#fff;padding:6px 14px;border-radius:8px;font-size:11px;cursor:pointer">Thử lại</button></div>';
66
  }
67
  };
68
  xhr.onerror = xhr.ontimeout = function() {
69
- frame.innerHTML = '<div class="vtv-err" style="display:flex;align-items:center;justify-content:center;min-height:180px;color:#888;padding:20px;flex-direction:column;gap:8px"><span style="color:#e74c3c">⚠️ Mất kết nối</span><button onclick="_vtvSwitch(\''+chId+'\')" style="background:#06c;border:0;color:#fff;padding:6px 14px;border-radius:8px;font-size:11px;cursor:pointer">Thử lại</button></div>';
70
  };
71
  xhr.send();
72
  };
@@ -74,14 +147,13 @@
74
  function playStream(url, frame) {
75
  var video = document.createElement('video');
76
  video.id = 'vtv-video';
77
- video.style.cssText = 'width:100% !important;height:100% !important;position:absolute;inset:0;object-fit:contain';
78
  video.setAttribute('controls', '');
79
  video.setAttribute('autoplay', '');
80
  video.setAttribute('playsinline', '');
81
  frame.innerHTML = '';
82
  frame.appendChild(video);
83
 
84
- // Try HLS.js first
85
  if (typeof Hls !== 'undefined' && Hls.isSupported()) {
86
  var hls = new Hls({ debug: false });
87
  hls.loadSource(url);
@@ -92,14 +164,14 @@
92
  hls.on(Hls.Events.ERROR, function(ev, data) {
93
  if (data.fatal) {
94
  hls.destroy();
95
- frame.innerHTML = '<div class="vtv-err" style="display:flex;align-items:center;justify-content:center;min-height:180px;color:#888;padding:20px;flex-direction:column;gap:8px"><span style="color:#e74c3c">⚠️ Lỗi phát</span><button onclick="_vtvSwitch(\''+_currentCh+'\')" style="background:#06c;border:0;color:#fff;padding:6px 14px;border-radius:8px;font-size:11px;cursor:pointer">🔄 Thử lại sau 3s</button></div>';
96
  }
97
  });
98
  _hlsInst = hls;
99
  } else if (video.canPlayType('application/vnd.apple.mpegurl')) {
100
  video.src = url;
101
  } else {
102
- frame.innerHTML = '<div class="vtv-err" style="display:flex;align-items:center;justify-content:center;min-height:180px;color:#888;padding:20px;flex-direction:column;gap:8px"><span>⚠️ Trình duyệt không hỗ trợ HLS</span></div>';
103
  }
104
  }
105
 
@@ -109,14 +181,24 @@
109
  tabs += '<span class="vtv-tab off" data-ch="'+CHANNELS[i].id+'" onclick="_vtvSwitch(\''+CHANNELS[i].id+'\')">'+CHANNELS[i].name+'</span>';
110
  }
111
  return '<div class="vtv-wrap" id="vtv-player-section">' +
112
- '<div class="vtv-head" style="display:flex;align-items:center;gap:8px;padding:8px 10px;background:linear-gradient(90deg,#036,#1a1a1a)">' +
113
- '<span class="vtv-title" style="font-size:13px;font-weight:800;color:#0cf">📺 VTV Player</span>' +
114
- '<span class="vtv-badge" style="font-size:10px;font-weight:800;color:#0cf;animation:vtvp 1.3s infinite">● LIVE</span></div>' +
115
- '<div class="vtv-tabs" style="display:flex;flex-wrap:wrap;gap:3px;padding:6px 8px;overflow-x:auto;background:#0d1a2a">'+tabs+'</div>' +
116
- '<div class="vtv-frame" id="vtv-frame" style="position:relative;width:100%;aspect-ratio:16/9;background:#000;min-height:180px">' +
117
- '<div class="vtv-load" style="display:flex;align-items:center;justify-content:center;min-height:180px;color:#0cf;font-size:12px;flex-direction:column;gap:8px">' +
118
- '<div style="width:24px;height:24px;border:2px solid #333;border-top-color:#0cf;border-radius:50%;animation:vtvspin .8s linear infinite"></div>' +
119
- '<span>📺 Chọn kênh để xem</span></div></div></div>';
 
 
 
 
 
 
 
 
 
 
120
  }
121
 
122
  function inject() {
 
1
+ // ===== VNEWS VTV Player v5 - uses yt_live.js CSS + EPG =====
2
  (function() {
3
  if (window._vtvInitLoaded) return;
4
  window._vtvInitLoaded = true;
 
15
  var _hlsInst = null;
16
  var _currentCh = null;
17
 
18
+ // ===== Minimal fix CSS (does NOT override yt_live.js classes) =====
19
  (function injectCSS() {
20
  if (document.getElementById('vtv-init-css')) return;
21
  var style = document.createElement('style');
22
  style.id = 'vtv-init-css';
23
  style.textContent =
24
+ '#vtv-player-section{display:block !important;}' +
25
+ '#vtv-player-section .vtv-frame{position:relative;width:100%;aspect-ratio:16/9;background:#000;min-height:180px}' +
26
+ '#vtv-player-section .vtv-frame video{position:absolute;inset:0;width:100% !important;height:100% !important;object-fit:contain}' +
27
+ '#vtv-player-section .vtv-epg-toggle{margin-left:auto}' +
28
+ '.vtv-tab{cursor:pointer !important}' +
29
+ '.vtv-tab.on{opacity:1 !important}' +
30
+ '.vtv-tab.off{opacity:.35 !important}' +
31
+ '.vtv-load{display:flex;align-items:center;justify-content:center;height:180px;color:#0cc;font-size:12px;flex-direction:column;gap:8px}' +
32
+ '.vtv-err{display:flex;align-items:center;justify-content:center;min-height:180px;color:#888;font-size:12px;text-align:center;padding:20px;flex-direction:column;gap:8px}' +
33
+ '.vtv-err button{background:#06c;border:none;color:#fff;padding:6px 14px;border-radius:8px;font-size:11px;cursor:pointer}' +
34
+ '.vtv-err button:hover{background:#08e}' +
35
+ '.vtv-live-tag{font-size:10px;font-weight:800;color:#0cf;animation:vtvp 1.3s infinite;margin-left:6px}' +
36
+ // EPG styles (complementing yt_live.js which already has .vtv-epg* styles)
37
+ '';
38
  document.head.appendChild(style);
39
  })();
40
 
41
  function proxyUrl(url) {
42
+ if (!url) return url;
43
  if (url.indexOf(location.origin) === 0) return url;
44
  if (url.indexOf('/api/proxy/') === 0) return url;
45
  return '/api/proxy/m3u8/vtv?url=' + encodeURIComponent(url);
46
  }
47
 
48
+ // ===== EPG =====
49
+ var _epgData = null;
50
+
51
+ function loadEPG(chId) {
52
+ var epgEl = document.getElementById('vtv-epg-body');
53
+ if (!epgEl) return;
54
+ epgEl.innerHTML = '<div class="vtv-epg-loading"><div class="vtv-epg-sp"></div>Đang tải lịch...</div>';
55
+ var xhr = new XMLHttpRequest();
56
+ xhr.open('GET', '/api/vtv/epg/' + chId, true);
57
+ xhr.timeout = 10000;
58
+ xhr.onload = function() {
59
+ try {
60
+ var data = JSON.parse(xhr.responseText);
61
+ _epgData = data.programs || [];
62
+ renderEPG(epgEl);
63
+ } catch(e) {
64
+ epgEl.innerHTML = '<div class="vtv-epg-empty">Không có lịch phát sóng</div>';
65
+ }
66
+ };
67
+ xhr.onerror = function() {
68
+ epgEl.innerHTML = '<div class="vtv-epg-empty">Không tải được lịch phát sóng</div>';
69
+ };
70
+ xhr.ontimeout = function() {
71
+ epgEl.innerHTML = '<div class="vtv-epg-empty">Hết thời gian tải</div>';
72
+ };
73
+ xhr.send();
74
+ }
75
+
76
+ function renderEPG(epgEl) {
77
+ if (!_epgData || !_epgData.length) {
78
+ epgEl.innerHTML = '<div class="vtv-epg-empty">Không có lịch phát sóng</div>';
79
+ return;
80
+ }
81
+ var html = '';
82
+ for (var i = 0; i < _epgData.length; i++) {
83
+ var p = _epgData[i];
84
+ var cls = 'vtv-epg-item' + (p.now ? ' now' : '');
85
+ var extra = '';
86
+ if (p.now && p.end_time) {
87
+ extra = ' &ndash; ' + p.end_time;
88
+ }
89
+ html += '<div class="' + cls + '">' +
90
+ '<span class="t">' + p.time + extra + '</span>' +
91
+ ' <span class="n">' + p.title + '</span>' +
92
+ '</div>';
93
+ }
94
+ epgEl.innerHTML = html;
95
+ }
96
+
97
  window._vtvSwitch = function(chId) {
98
  if (_hlsInst) { try { _hlsInst.destroy(); } catch(e){} _hlsInst = null; }
99
  _currentCh = chId;
 
103
  tabs[i].className = 'vtv-tab' + (tabs[i].getAttribute('data-ch') === chId ? ' on' : ' off');
104
  }
105
 
106
+ // Update EPG header channel name
107
+ var epgTitle = document.getElementById('vtv-epg-ch-name');
108
+ if (epgTitle) {
109
+ for (var j = 0; j < CHANNELS.length; j++) {
110
+ if (CHANNELS[j].id === chId) {
111
+ epgTitle.textContent = CHANNELS[j].name + ' - Lịch phát sóng';
112
+ break;
113
+ }
114
+ }
115
+ }
116
+
117
  var frame = document.getElementById('vtv-frame');
118
  if (!frame) return;
119
+ frame.innerHTML = '<div class="vtv-load"><div class="vtv-spinner"></div><span>Đang tải ' + chId.toUpperCase() + '...</span></div>';
120
+
121
+ // Load EPG
122
+ loadEPG(chId);
123
 
124
  var xhr = new XMLHttpRequest();
125
  xhr.open('GET', '/api/vtv/stream/' + chId, true);
 
129
  var data = JSON.parse(xhr.responseText);
130
  var url = data.stream_url;
131
  if (!url) {
132
+ frame.innerHTML = '<div class="vtv-err"><span style="color:#e74c3c;font-size:24px">⚠️</span><span>Kênh đang tạm ngắt</span><button onclick="_vtvSwitch(\''+chId+'\')">🔄 Thử lại</button></div>';
133
  return;
134
  }
135
  url = proxyUrl(url);
136
+ playStream(url, frame, chId);
137
  } catch(e) {
138
+ frame.innerHTML = '<div class="vtv-err"><span style="color:#e74c3c">⚠️ Lỗi dữ liệu</span><button onclick="_vtvSwitch(\''+chId+'\')">Thử lại</button></div>';
139
  }
140
  };
141
  xhr.onerror = xhr.ontimeout = function() {
142
+ frame.innerHTML = '<div class="vtv-err"><span style="color:#e74c3c">⚠️ Mất kết nối</span><button onclick="_vtvSwitch(\''+chId+'\')">Thử lại</button></div>';
143
  };
144
  xhr.send();
145
  };
 
147
  function playStream(url, frame) {
148
  var video = document.createElement('video');
149
  video.id = 'vtv-video';
150
+ video.style.cssText = 'width:100%;height:100%;object-fit:contain';
151
  video.setAttribute('controls', '');
152
  video.setAttribute('autoplay', '');
153
  video.setAttribute('playsinline', '');
154
  frame.innerHTML = '';
155
  frame.appendChild(video);
156
 
 
157
  if (typeof Hls !== 'undefined' && Hls.isSupported()) {
158
  var hls = new Hls({ debug: false });
159
  hls.loadSource(url);
 
164
  hls.on(Hls.Events.ERROR, function(ev, data) {
165
  if (data.fatal) {
166
  hls.destroy();
167
+ frame.innerHTML = '<div class="vtv-err"><span style="color:#e74c3c">⚠️ Lỗi phát</span><button onclick="_vtvSwitch(\''+_currentCh+'\')">🔄 Thử lại</button></div>';
168
  }
169
  });
170
  _hlsInst = hls;
171
  } else if (video.canPlayType('application/vnd.apple.mpegurl')) {
172
  video.src = url;
173
  } else {
174
+ frame.innerHTML = '<div class="vtv-err"><span>⚠️ Trình duyệt không hỗ trợ HLS</span></div>';
175
  }
176
  }
177
 
 
181
  tabs += '<span class="vtv-tab off" data-ch="'+CHANNELS[i].id+'" onclick="_vtvSwitch(\''+CHANNELS[i].id+'\')">'+CHANNELS[i].name+'</span>';
182
  }
183
  return '<div class="vtv-wrap" id="vtv-player-section">' +
184
+ '<div class="vtv-head">' +
185
+ '<span class="vtv-title">📺 VTV Player</span>' +
186
+ '<span class="vtv-badge">● LIVE</span>' +
187
+ '</div>' +
188
+ '<div class="vtv-tabs">'+tabs+'</div>' +
189
+ '<div class="vtv-frame" id="vtv-frame">' +
190
+ '<div class="vtv-load"><div class="vtv-spinner"></div><span>📺 Chọn kênh để xem</span></div>' +
191
+ '</div>' +
192
+ // EPG section
193
+ '<div class="vtv-epg" id="vtv-epg-section">' +
194
+ '<div class="vtv-epg-header">' +
195
+ '<span class="vtv-epg-title" id="vtv-epg-ch-name">Lịch phát sóng</span>' +
196
+ '</div>' +
197
+ '<div class="vtv-epg-list" id="vtv-epg-body">' +
198
+ '<div class="vtv-epg-empty">Chọn kênh để xem lịch phát sóng</div>' +
199
+ '</div>' +
200
+ '</div>' +
201
+ '</div>';
202
  }
203
 
204
  function inject() {