bep40 commited on
Commit
36390e5
·
verified ·
1 Parent(s): 87aa7ba

Upload static/shorts_fresh.js

Browse files
Files changed (1) hide show
  1. static/shorts_fresh.js +119 -93
static/shorts_fresh.js CHANGED
@@ -1,6 +1,6 @@
1
  /**
2
- * VNEWS Fresh Shorts Fetcher v8 - 24h filter
3
- * Also patches wall rendering to filter old posts
4
  */
5
  (function() {
6
  'use strict';
@@ -31,41 +31,38 @@
31
  return url;
32
  }
33
 
34
- function filter24h(items, timeField = 'created') {
 
35
  const now = Date.now();
36
- return items.filter(item => {
37
- const created = item[timeField] || 0;
 
38
  const ts = created > 1000000000000 ? created : created * 1000;
39
  return (now - ts) <= MAX_AGE_MS;
40
  });
41
  }
42
 
 
43
  async function fetchShorts() {
44
  try {
45
- const r = await fetch('/api/shorts?t=' + Date.now());
46
  if (r.ok) {
47
- let data = await r.json();
48
- if (Array.isArray(data)) {
49
- return filter24h(data).map(item => ({
50
- id: item.id, title: item.title || 'YouTube Short',
51
- img: item.img || `https://i.ytimg.com/vi/${item.id}/hqdefault.jpg`,
52
- link: item.link || `https://www.youtube.com/shorts/${item.id}`,
53
- channel: item.channel || 'baodantri7941', source: 'yt'
54
- }));
55
  }
56
  }
57
  } catch(e) {}
58
- return [];
59
- }
60
-
61
- async function fetchFromRSS() {
62
  try {
63
- const r = await fetch('/api/shorts/rss?t=' + Date.now());
64
  if (r.ok) {
65
  const data = await r.json();
66
- if (data.shorts && data.shorts.length > 0) return data.shorts;
67
  }
68
  } catch(e) {}
 
69
  return [];
70
  }
71
 
@@ -80,98 +77,123 @@
80
  return result;
81
  }
82
 
83
- function rerenderShortsSlide(shorts) {
84
- const existingSlide = document.getElementById('shorts-live-slide') ||
85
- Array.from(document.querySelectorAll('.slider-wrap')).find(el =>
86
- (el.querySelector('.slider-label')?.textContent || '').includes('Shorts')
87
- );
88
 
89
  const mixed = interleaveShorts(shorts);
90
  if (!mixed.length) return;
91
 
 
 
 
92
  let h = '<div class="slider-header"><span class="slider-label">📱 Shorts Dân trí & SKĐS (24h)</span><span class="slider-note">🟢 vừa cập nhật</span></div><div class="slider-track">';
93
  mixed.slice(0, 25).forEach((a, i) => {
94
  const badge = a.channel === 'baosuckhoedoisongboyte' ? 'SKĐS' : 'Dân trí';
95
  h += `<div class="slider-item shorts-item" onclick="openYTShortsFeed(${i})"><div class="slider-thumb shorts-thumb">${a.img ? `<img src="${esc(proxyImg(a.img))}" loading="lazy">` : ''}<div class="card-play">▶</div></div><div class="slider-title"><span style="color:#f0c040;font-size:8px">${badge}</span> ${esc(a.title)}</div></div>`;
96
  });
97
  h += '</div>';
 
98
 
99
- if (existingSlide) {
100
- existingSlide.innerHTML = h;
101
- existingSlide.id = 'shorts-live-slide';
 
102
  }
103
  }
104
 
105
- window.refreshShorts = async function() {
106
- let all = await fetchFromRSS();
107
- if (all.length === 0) all = await fetchShorts();
 
 
 
108
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  const seen = new Set();
110
  const fresh = all.filter(s => seen.has(s.id) ? false : (seen.add(s.id), true));
111
 
112
  if (fresh.length > 0) {
113
  window._shortsData = fresh;
114
- rerenderShortsSlide(fresh);
 
115
  localStorage.setItem('vnews_shorts', JSON.stringify({ ts: Date.now(), data: fresh }));
116
  toast('✓ ' + fresh.length + ' shorts (24h)!');
117
  }
118
  };
119
 
120
- // === WALL 24H PATCH ===
121
- function patchWallRender() {
122
- // Override _renderWallIn to filter 24h
123
- const origRender = window._renderWallIn;
124
- window._renderWallIn = function(afterEl) {
125
- if (!window._wallPosts || !window._wallPosts.length || !afterEl) return;
126
-
127
- // Filter to 24h
128
  const now = Date.now();
129
- const posts = window._wallPosts.filter(p => {
130
- const created = p.created || 0;
131
- const ts = created > 1000000000000 ? created : created * 1000;
132
- return (now - ts) <= MAX_AGE_MS;
133
- }).slice(0, 20);
134
-
135
- if (!posts.length) return;
136
-
137
- // Render Short AI slide (videos only)
138
- const aiShorts = posts.filter(p => p.video);
139
- if (aiShorts.length) {
140
- let wrap = document.createElement('div');
141
- wrap.className = 'slider-wrap';
142
- wrap.id = 'short-ai-section';
143
- let h = '<div class="slider-header"><span class="slider-label">🎬 Short AI (24h)</span></div><div class="slider-track">';
144
- aiShorts.forEach((p, i) => {
145
- h += `<div class="slider-item shorts-item" onclick="openShortAIFeed(${i})"><div class="slider-thumb shorts-thumb"><video src="${esc(p.video)}" muted preload="metadata"></video><div class="card-play">▶</div></div><div class="slider-title">${esc(p.title)}</div></div>`;
146
- });
147
- h += '</div>';
148
- wrap.innerHTML = h;
149
- afterEl.parentNode.insertBefore(wrap, afterEl);
150
- }
151
 
152
- // Render AI Wall
153
- wrap = document.createElement('div');
154
- wrap.className = 'slider-wrap';
155
- wrap.id = 'ai-wall-wrap';
156
- let h = '<div class="slider-header"><span class="slider-label">🧱 Tường AI (24h)</span></div><div class="slider-track" id="ai-wall-track">';
157
- posts.forEach((p, i) => {
158
- h += `<div class="wall-item" id="wall-item-${esc(p.id)}"><div class="wall-thumb">${p.img ? `<img src="${proxyImg(p.img)}">` : (p.video ? `<video src="${esc(p.video)}" muted></video>` : '')}</div><div class="wall-title">${esc(p.title)}</div></div>`;
159
- });
160
- h += '</div>';
161
- wrap.innerHTML = h;
162
- afterEl.parentNode.insertBefore(wrap, afterEl);
163
  };
164
-
165
- // Override prependWallPost to check age
166
- const origPrepend = window.prependWallPost;
167
- if (origPrepend) {
168
- window.prependWallPost = function(post) {
169
- const now = Date.now();
170
- const created = post.created || Date.now();
171
- const ts = created > 1000000000000 ? created : created * 1000;
172
- if ((now - ts) > MAX_AGE_MS) return;
173
- origPrepend.call(this, post);
174
- };
175
  }
176
  }
177
 
@@ -179,18 +201,22 @@
179
  try {
180
  const c = JSON.parse(localStorage.getItem('vnews_shorts') || '{}');
181
  if (c.data && c.data.length > 0 && (Date.now() - c.ts) < 7200000) {
182
- window._shortsData = filter24h(c.data);
183
- if (window._shortsData.length > 0) {
184
- setTimeout(() => rerenderShortsSlide(window._shortsData), 2000);
 
 
 
 
185
  }
186
  }
187
  } catch(e) {}
188
 
189
- // Patch wall rendering
190
- patchWallRender();
191
-
192
- // Fetch fresh after 3s
193
- setTimeout(() => { window.refreshShorts().catch(() => {}); }, 3000);
194
 
195
- console.log('[Shorts] Fetcher v8 - 24h filter active');
196
  })();
 
1
  /**
2
+ * VNEWS Shorts & Wall 24h Filter - v8.1
3
+ * Runs after app_v2.js to fix Short AI/Wall display
4
  */
5
  (function() {
6
  'use strict';
 
31
  return url;
32
  }
33
 
34
+ // Filter posts by 24h
35
+ function filter24h(posts) {
36
  const now = Date.now();
37
+ return posts.filter(p => {
38
+ const created = p.created || 0;
39
+ // Handle both ms and s timestamps
40
  const ts = created > 1000000000000 ? created : created * 1000;
41
  return (now - ts) <= MAX_AGE_MS;
42
  });
43
  }
44
 
45
+ // Fetch shorts from RSS (freshest)
46
  async function fetchShorts() {
47
  try {
48
+ const r = await fetch('/api/shorts/rss?t=' + Date.now());
49
  if (r.ok) {
50
+ const data = await r.json();
51
+ if (data.shorts && data.shorts.length > 0) {
52
+ return data.shorts.filter(s => s.channel && (s.channel === 'baodantri7941' || s.channel === 'baosuckhoedoisongboyte'));
 
 
 
 
 
53
  }
54
  }
55
  } catch(e) {}
56
+
57
+ // Fallback to /api/shorts
 
 
58
  try {
59
+ const r = await fetch('/api/shorts?t=' + Date.now());
60
  if (r.ok) {
61
  const data = await r.json();
62
+ if (Array.isArray(data)) return data;
63
  }
64
  } catch(e) {}
65
+
66
  return [];
67
  }
68
 
 
77
  return result;
78
  }
79
 
80
+ function renderShortsSlide(shorts, afterEl) {
81
+ // Find existing slide or create new
82
+ let existingSlide = document.getElementById('shorts-live-slide');
 
 
83
 
84
  const mixed = interleaveShorts(shorts);
85
  if (!mixed.length) return;
86
 
87
+ const wrap = document.createElement('div');
88
+ wrap.className = 'slider-wrap';
89
+ wrap.id = 'shorts-live-slide';
90
  let h = '<div class="slider-header"><span class="slider-label">📱 Shorts Dân trí & SKĐS (24h)</span><span class="slider-note">🟢 vừa cập nhật</span></div><div class="slider-track">';
91
  mixed.slice(0, 25).forEach((a, i) => {
92
  const badge = a.channel === 'baosuckhoedoisongboyte' ? 'SKĐS' : 'Dân trí';
93
  h += `<div class="slider-item shorts-item" onclick="openYTShortsFeed(${i})"><div class="slider-thumb shorts-thumb">${a.img ? `<img src="${esc(proxyImg(a.img))}" loading="lazy">` : ''}<div class="card-play">▶</div></div><div class="slider-title"><span style="color:#f0c040;font-size:8px">${badge}</span> ${esc(a.title)}</div></div>`;
94
  });
95
  h += '</div>';
96
+ wrap.innerHTML = h;
97
 
98
+ const homeEl = document.getElementById('view-home');
99
+ if (!existingSlide && homeEl && afterEl) {
100
+ // Insert after afterEl (which is #home-after-wc)
101
+ homeEl.insertBefore(wrap, afterEl.nextSibling);
102
  }
103
  }
104
 
105
+ function renderWall24h(afterEl) {
106
+ if (!window._wallPosts || !window._wallPosts.length || !afterEl) return;
107
+
108
+ // Filter to 24h
109
+ const posts = filter24h(window._wallPosts).slice(0, 20);
110
+ if (!posts.length) return;
111
 
112
+ // Short AI (videos)
113
+ const aiShorts = posts.filter(p => p.video);
114
+ if (aiShorts.length) {
115
+ let wrap = document.getElementById('short-ai-section');
116
+ if (wrap) wrap.remove();
117
+
118
+ wrap = document.createElement('div');
119
+ wrap.className = 'slider-wrap';
120
+ wrap.id = 'short-ai-section';
121
+ let h = '<div class="slider-header"><span class="slider-label">🎬 Short AI (24h)</span></div><div class="slider-track">';
122
+ aiShorts.forEach((p, i) => {
123
+ h += `<div class="slider-item shorts-item" onclick="openShortAIFeed(${i})"><div class="slider-thumb shorts-thumb"><video src="${esc(p.video)}" muted preload="metadata"></video><div class="card-play">▶</div></div><div class="slider-title">${esc(p.title)}</div></div>`;
124
+ });
125
+ h += '</div>';
126
+ wrap.innerHTML = h;
127
+ afterEl.parentNode.insertBefore(wrap, afterEl);
128
+ }
129
+
130
+ // AI Wall (text posts)
131
+ wrap = document.getElementById('ai-wall-wrap');
132
+ if (wrap) wrap.remove();
133
+
134
+ wrap = document.createElement('div');
135
+ wrap.className = 'slider-wrap';
136
+ wrap.id = 'ai-wall-wrap';
137
+ let h = '<div class="slider-header"><span class="slider-label">🧱 Tường AI (24h)</span></div><div class="slider-track" id="ai-wall-track">';
138
+ posts.forEach((p, i) => {
139
+ h += `<div class="wall-item" id="wall-item-${esc(p.id)}"><div class="wall-thumb">${p.img ? `<img src="${proxyImg(p.img)}">` : (p.video ? `<video src="${esc(p.video)}" muted></video>` : '')}</div><div class="wall-title">${esc(p.title)}</div></div>`;
140
+ });
141
+ h += '</div>';
142
+ wrap.innerHTML = h;
143
+ afterEl.parentNode.insertBefore(wrap, afterEl);
144
+ }
145
+
146
+ // Override refreshShorts
147
+ window.refreshShorts = async function() {
148
+ let all = await fetchShorts();
149
  const seen = new Set();
150
  const fresh = all.filter(s => seen.has(s.id) ? false : (seen.add(s.id), true));
151
 
152
  if (fresh.length > 0) {
153
  window._shortsData = fresh;
154
+ const afterEl = document.getElementById('home-after-wc');
155
+ if (afterEl) renderShortsSlide(fresh, afterEl);
156
  localStorage.setItem('vnews_shorts', JSON.stringify({ ts: Date.now(), data: fresh }));
157
  toast('✓ ' + fresh.length + ' shorts (24h)!');
158
  }
159
  };
160
 
161
+ // Patch prependWallPost to enforce 24h
162
+ const origPrepend = window.prependWallPost;
163
+ if (origPrepend) {
164
+ window.prependWallPost = function(post) {
 
 
 
 
165
  const now = Date.now();
166
+ const created = post.created || Date.now();
167
+ const ts = created > 1000000000000 ? created : created * 1000;
168
+ if ((now - ts) > MAX_AGE_MS) return; // Don't add old posts
169
+ origPrepend.call(this, post);
170
+ };
171
+ }
172
+
173
+ // Wait for loadHome to complete, then patch wall
174
+ const origLoadHome = window.loadHome;
175
+ if (origLoadHome) {
176
+ window.loadHome = async function() {
177
+ await origLoadHome.call(this);
 
 
 
 
 
 
 
 
 
 
178
 
179
+ // Now patch wall rendering
180
+ setTimeout(() => {
181
+ const afterEl = document.getElementById('home-after-wc');
182
+ if (afterEl && window._wallPosts) {
183
+ renderWall24h(afterEl);
184
+ }
185
+ }, 1000);
 
 
 
 
186
  };
187
+ }
188
+
189
+ // Also run on DOM ready (backup)
190
+ function initWallFix() {
191
+ if (window._wallPosts && window._wallPosts.length > 0) {
192
+ const afterEl = document.getElementById('home-after-wc');
193
+ if (afterEl) {
194
+ renderWall24h(afterEl);
195
+ toast('🧱 Tường AI đã lọc 24h');
196
+ }
 
197
  }
198
  }
199
 
 
201
  try {
202
  const c = JSON.parse(localStorage.getItem('vnews_shorts') || '{}');
203
  if (c.data && c.data.length > 0 && (Date.now() - c.ts) < 7200000) {
204
+ const filtered = filter24h(c.data);
205
+ if (filtered.length > 0) {
206
+ window._shortsData = filtered;
207
+ setTimeout(() => {
208
+ const afterEl = document.getElementById('home-after-wc');
209
+ if (afterEl) renderShortsSlide(filtered, afterEl);
210
+ }, 2000);
211
  }
212
  }
213
  } catch(e) {}
214
 
215
+ // Fetch fresh after load
216
+ setTimeout(() => {
217
+ window.refreshShorts().catch(() => {});
218
+ initWallFix();
219
+ }, 4000);
220
 
221
+ console.log('[Shorts] Fetcher v8.1 - 24h filter active');
222
  })();