bep40 commited on
Commit
87aa7ba
·
verified ·
1 Parent(s): 60c9b78

Upload static/shorts_fresh.js

Browse files
Files changed (1) hide show
  1. static/shorts_fresh.js +66 -71
static/shorts_fresh.js CHANGED
@@ -1,10 +1,11 @@
1
  /**
2
- * VNEWS Fresh Shorts Fetcher v8 - 24h filter for both shorts and wall
 
3
  */
4
  (function() {
5
  'use strict';
6
 
7
- const MAX_AGE_MS = 86400000; // 24h in ms
8
 
9
  function toast(msg) {
10
  let t = document.getElementById('short-progress-toast');
@@ -30,35 +31,26 @@
30
  return url;
31
  }
32
 
33
- // Filter items by 24h
34
  function filter24h(items, timeField = 'created') {
35
  const now = Date.now();
36
  return items.filter(item => {
37
- const created = item[timeField] || (typeof item.created === 'number' ? item.created * 1000 : item.created);
38
- if (created) {
39
- // Handle both timestamp (ms) and Unix timestamp (s)
40
- const ts = created > 1000000000000 ? created : created * 1000;
41
- return (now - ts) <= MAX_AGE_MS;
42
- }
43
- return true; // Keep if no timestamp
44
  });
45
  }
46
 
47
- // Fetch shorts from server API
48
  async function fetchShorts() {
49
  try {
50
  const r = await fetch('/api/shorts?t=' + Date.now());
51
  if (r.ok) {
52
  let data = await r.json();
53
  if (Array.isArray(data)) {
54
- data = filter24h(data);
55
- return data.map(item => ({
56
- id: item.id,
57
- title: item.title || 'YouTube Short',
58
  img: item.img || `https://i.ytimg.com/vi/${item.id}/hqdefault.jpg`,
59
  link: item.link || `https://www.youtube.com/shorts/${item.id}`,
60
- channel: item.channel || 'baodantri7941',
61
- source: 'yt'
62
  }));
63
  }
64
  }
@@ -66,15 +58,12 @@
66
  return [];
67
  }
68
 
69
- // Fetch shorts from YouTube RSS (fresh)
70
  async function fetchFromRSS() {
71
  try {
72
  const r = await fetch('/api/shorts/rss?t=' + Date.now());
73
  if (r.ok) {
74
  const data = await r.json();
75
- if (data.shorts && data.shorts.length > 0) {
76
- return data.shorts; // Server already filtered
77
- }
78
  }
79
  } catch(e) {}
80
  return [];
@@ -83,8 +72,7 @@
83
  function interleaveShorts(shorts) {
84
  const dt = shorts.filter(s => s.channel === 'baodantri7941');
85
  const sk = shorts.filter(s => s.channel === 'baosuckhoedoisongboyte');
86
- const result = [];
87
- let i = 0, j = 0;
88
  while (i < dt.length || j < sk.length) {
89
  if (i < dt.length) result.push(dt[i++]);
90
  if (j < sk.length) result.push(sk[j++]);
@@ -104,9 +92,7 @@
104
  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">';
105
  mixed.slice(0, 25).forEach((a, i) => {
106
  const badge = a.channel === 'baosuckhoedoisongboyte' ? 'SKĐS' : 'Dân trí';
107
- h += '<div class="slider-item shorts-item" onclick="openYTShortsFeed(' + i + ')"><div class="slider-thumb shorts-thumb">' +
108
- (a.img ? '<img src="' + esc(proxyImg(a.img)) + '" loading="lazy" onerror="this.src=\'https://i.ytimg.com/vi/' + a.id + '/hqdefault.jpg\'">' : '') +
109
- '<div class="card-play">▶</div></div><div class="slider-title"><span style="color:#f0c040;font-size:8px">' + badge + '</span> ' + esc(a.title) + '</div></div>';
110
  });
111
  h += '</div>';
112
 
@@ -131,70 +117,79 @@
131
  }
132
  };
133
 
134
- // Patch _renderWallIn to filter 24h
135
- const origRenderWallIn = window._renderWallIn;
136
- window._renderWallIn = function(afterEl) {
137
- if (!window._wallPosts || !window._wallPosts.length || !afterEl) return;
138
-
139
- // Filter to 24h
140
- const now = Date.now();
141
- const posts = window._wallPosts.filter(p => {
142
- const created = p.created || 0;
143
- const ts = created > 1000000000000 ? created : created * 1000;
144
- return (now - ts) <= MAX_AGE_MS;
145
- }).slice(0, 20);
146
-
147
- if (!posts.length) return;
148
-
149
- // Render Short AI slide (videos only)
150
- const aiShorts = posts.filter(p => p.video);
151
- if (aiShorts.length) {
152
- let wrap = document.getElementById('short-ai-section');
153
- if (!wrap) {
154
- wrap = document.createElement('div');
155
  wrap.className = 'slider-wrap';
156
  wrap.id = 'short-ai-section';
 
 
 
 
 
 
157
  afterEl.parentNode.insertBefore(wrap, afterEl);
158
  }
159
- let h = '<div class="slider-header"><span class="slider-label">🎬 Short AI (24h)</span></div><div class="slider-track">';
160
- aiShorts.forEach((p, i) => {
161
- 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>';
 
 
 
 
 
162
  });
163
  h += '</div>';
164
  wrap.innerHTML = h;
165
- }
 
166
 
167
- // Render AI Wall
168
- wrap = document.createElement('div');
169
- wrap.className = 'slider-wrap';
170
- wrap.id = 'ai-wall-wrap';
171
- let h = '<div class="slider-header"><span class="slider-label">🧱 Tường AI (24h)</span></div><div class="slider-track" id="ai-wall-track">';
172
- posts.forEach((p, i) => {
173
- h += '<div class="wall-item" id="wall-item-' + esc(p.id) + '"><div class="wall-thumb">' +
174
- (p.img ? '<img src="' + proxyImg(p.img) + '">' : (p.video ? '<video src="' + esc(p.video) + '" muted></video>' : '')) +
175
- '</div><div class="wall-title">' + esc(p.title) + '</div></div>';
176
- });
177
- h += '</div>';
178
- wrap.innerHTML = h;
179
- afterEl.parentNode.insertBefore(wrap, afterEl);
180
- };
181
 
182
  // Load cache
183
  try {
184
  const c = JSON.parse(localStorage.getItem('vnews_shorts') || '{}');
185
  if (c.data && c.data.length > 0 && (Date.now() - c.ts) < 7200000) {
186
- window._shortsData = c.data.filter((item, i, arr) => {
187
- const created = item.created || c.ts;
188
- const ts = created > 1000000000000 ? created : created * 1000;
189
- return (Date.now() - ts) <= MAX_AGE_MS;
190
- });
191
  if (window._shortsData.length > 0) {
192
  setTimeout(() => rerenderShortsSlide(window._shortsData), 2000);
193
  }
194
  }
195
  } catch(e) {}
196
 
197
- // Fetch fresh
 
 
 
198
  setTimeout(() => { window.refreshShorts().catch(() => {}); }, 3000);
199
 
200
  console.log('[Shorts] Fetcher v8 - 24h filter active');
 
1
  /**
2
+ * VNEWS Fresh Shorts Fetcher v8 - 24h filter
3
+ * Also patches wall rendering to filter old posts
4
  */
5
  (function() {
6
  'use strict';
7
 
8
+ const MAX_AGE_MS = 86400000; // 24h
9
 
10
  function toast(msg) {
11
  let t = document.getElementById('short-progress-toast');
 
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
  }
 
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 [];
 
72
  function interleaveShorts(shorts) {
73
  const dt = shorts.filter(s => s.channel === 'baodantri7941');
74
  const sk = shorts.filter(s => s.channel === 'baosuckhoedoisongboyte');
75
+ const result = []; let i = 0, j = 0;
 
76
  while (i < dt.length || j < sk.length) {
77
  if (i < dt.length) result.push(dt[i++]);
78
  if (j < sk.length) result.push(sk[j++]);
 
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
 
 
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
 
178
  // Load cache
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');