bep40 commited on
Commit
5ff0481
·
verified ·
1 Parent(s): a38a3b7

Fix: No-op app_v2_shorts_fix.js — all voice/emotion/slide logic now built into app_v2.js to fix conflicts

Browse files
Files changed (1) hide show
  1. static/app_v2_shorts_fix.js +4 -381
static/app_v2_shorts_fix.js CHANGED
@@ -1,383 +1,6 @@
1
- // VNEWS Short AI Fix - Multilingual voices + emotion selector
2
- // This file patches makeShortVideo and readWallPost to support full voice list
3
-
4
  (function(){
5
- // Full voice list
6
- const VOICE_LIST = [
7
- {id:'hoaimy', label:'🎙️ Hoài My (VI)', lang:'vi'},
8
- {id:'namminh', label:'🎙️ Nam Minh (VI)', lang:'vi'},
9
- {id:'andrew', label:'🎙️ Andrew (Multilingual)', lang:'en'},
10
- {id:'jenny', label:'🎙️ Jenny (EN)', lang:'en'},
11
- {id:'thalita', label:'🎙️ Thalita (PT)', lang:'pt'},
12
- {id:'pt_francisco', label:'🎙️ Francisco (PT)', lang:'pt'},
13
- {id:'ela', label:'🎙️ Ela (ES)', lang:'es'},
14
- {id:'es_carlos', label:'🎙️ Carlos (ES)', lang:'es'},
15
- {id:'denise', label:'🎙️ Denise (FR)', lang:'fr'},
16
- {id:'katja', label:'🎙️ Katja (DE)', lang:'de'},
17
- {id:'nanami', label:'🎙️ Nanami (JA)', lang:'ja'},
18
- {id:'sunhee', label:'🎙️ SunHee (KO)', lang:'ko'},
19
- {id:'xiaochen', label:'🎙️ XiaoChen (ZH)', lang:'zh'},
20
- ];
21
-
22
- const EMOTION_LIST = [
23
- {id:'neutral', label:'😐 Trung tính'},
24
- {id:'happy', label:'😊 Vui vẻ'},
25
- {id:'excited', label:'🔥 Hào hứng'},
26
- {id:'sad', label:'😢 Buồn'},
27
- {id:'humorous', label:'😂 Hài hước'},
28
- {id:'serious', label:'⚠️ Nghiêm túc'},
29
- {id:'urgent', label:'🚨 Khẩn cấp'},
30
- {id:'warm', label:'💖 Ấm áp'},
31
- ];
32
-
33
- // Language detection (lightweight)
34
- function detectLanguage(text) {
35
- if (!text) return 'vi';
36
- var t = text.toLowerCase();
37
- var chars = new Set(t);
38
- // Vietnamese
39
- var vnChars = 'đăâêôơưàảãạáằẳẵặắầẩẫậấèẻẽẹéềễểệếìỉĩịíòỏõọóồổỗộốờởỡợớùủũụúừửữựỳỷỹỵý';
40
- var vnCount = 0;
41
- for (var c of vnChars) { if (chars.has(c)) vnCount++; }
42
- if (vnCount >= 2) return 'vi';
43
-
44
- // Spanish markers
45
- if (chars.has('ñ') || chars.has('¿') || chars.has('¡')) return 'es';
46
-
47
- // Portuguese markers
48
- if (chars.has('ã') || chars.has('õ')) return 'pt';
49
-
50
- // English default
51
- var words = t.split(/\s+/);
52
- var enWords = ['the','is','at','which','on','and','or','but','this','that','with','from','have','been'];
53
- var enCount = words.filter(function(w){return enWords.indexOf(w)>=0;}).length;
54
- if (enCount >= 2) return 'en';
55
-
56
- return 'vi';
57
- }
58
-
59
- // Auto-detect emotion
60
- function detectEmotion(text) {
61
- if (!text) return 'neutral';
62
- var t = text.toLowerCase();
63
- var kws = {
64
- happy: ['vui','hạnh phúc','tuyệt','thành công','chiến thắng','feliz','maravilloso','happy','joy','wonderful','great','amazing','love','excellent'],
65
- excited: ['hào hứng','phấn khích','đột phá','kỷ lục','đỉnh cao','emocionante','increíble','excited','thrilling','unbelievable','awesome','breakthrough'],
66
- sad: ['buồn','đau','mất','thảm họa','khủng hoảng','triste','terrible','sad','unhappy','tragic','painful','death'],
67
- humorous: ['hài hước','buồn cười','haha','đùa','engraçado','gracioso','funny','hilarious','joke','lol'],
68
- serious: ['nghiêm trọng','khẩn cấp','quan trọng','lo ngại','sério','crítico','serious','critical','urgent','severe','crisis'],
69
- urgent: ['khẩn cấp','báo động','ngay lập tức','urgent','breaking','alert','emergency'],
70
- warm: ['ấm áp','tình cảm','yêu thương','warm','love','heart','touching','beautiful story']
71
- };
72
- var bestScore = 0;
73
- var bestEmotion = 'neutral';
74
- for (var em in kws) {
75
- var score = 0;
76
- for (var kw of kws[em]) {
77
- if (t.indexOf(kw) >= 0) score++;
78
- }
79
- if (score > bestScore) { bestScore = score; bestEmotion = em; }
80
- }
81
- return bestEmotion;
82
- }
83
-
84
- // Auto-select voice based on language
85
- function getAutoVoice(lang) {
86
- var map = {vi:'hoaimy', pt:'thalita', es:'ela', en:'jenny', fr:'denise', de:'katja', ja:'nanami', ko:'sunhee', zh:'xiaochen'};
87
- return map[lang] || 'hoaimy';
88
- }
89
-
90
- // Build voice+emotion selector HTML
91
- function buildVoiceEmotionSelector(post) {
92
- var lang = post.language || detectLanguage(post.title + ' ' + post.text);
93
- var autoVoice = post.voice || getAutoVoice(lang);
94
- var autoEmotion = post.emotion || detectEmotion(post.title + ' ' + post.text);
95
-
96
- var h = '<div class="tts-selector" style="margin-top:10px;padding:10px;background:#1a1a1a;border:1px solid #2a2a2a;border-radius:10px">';
97
- h += '<div class="tts-selector-label" style="font-size:11px;color:#888;margin-bottom:6px">🎙️ Giọng đọc (ngôn ngữ: ' + lang.toUpperCase() + '):</div>';
98
- h += '<div class="tts-voice-btns" style="display:flex;flex-wrap:wrap;gap:4px;margin-bottom:8px">';
99
- VOICE_LIST.forEach(function(v){
100
- var sel = v.id === autoVoice ? 'border-color:#5cb87a;background:#1a2a1f' : 'border-color:#333;background:#222';
101
- h += '<button class="tts-voice-btn" data-voice="'+v.id+'" style="'+sel+';border:1px solid;color:#ccc;padding:4px 8px;border-radius:10px;font-size:10px;cursor:pointer">'+v.label+'</button>';
102
- });
103
- h += '</div>';
104
- h += '<div class="tts-selector-label" style="font-size:11px;color:#888;margin-bottom:6px">😊 Cảm xúc:</div>';
105
- h += '<div class="tts-emotion-btns" style="display:flex;flex-wrap:wrap;gap:4px;margin-bottom:8px">';
106
- EMOTION_LIST.forEach(function(e){
107
- var sel = e.id === autoEmotion ? 'border-color:#5cb87a;background:#1a2a1f' : 'border-color:#333;background:#222';
108
- h += '<button class="tts-emotion-btn" data-emotion="'+e.id+'" style="'+sel+';border:1px solid;color:#ccc;padding:4px 8px;border-radius:10px;font-size:10px;cursor:pointer">'+e.label+'</button>';
109
- });
110
- h += '</div>';
111
- h += '<div class="tts-speed-row" style="display:flex;align-items:center;gap:6px;margin-bottom:8px"><span style="font-size:11px;color:#888">⚡ Tốc độ:</span>';
112
- h += '<select class="tts-speed" style="background:#222;border:1px solid #333;color:#ccc;padding:3px 8px;border-radius:8px;font-size:10px">';
113
- h += '<option value="0.85">0.85x Chậm</option><option value="1.0">1.0x Bình thường</option><option value="1.2" selected>1.2x Nhanh</option><option value="1.35">1.35x Rất nhanh</option>';
114
- h += '</select></div>';
115
- h += '<button class="tts-create-btn" style="width:100%;background:#2d8659;border:0;color:#fff;padding:8px;border-radius:10px;font-size:11px;font-weight:700;cursor:pointer">🎬 Tạo Short AI</button>';
116
- h += '<input type="hidden" class="tts-selected-voice" value="' + autoVoice + '">';
117
- h += '<input type="hidden" class="tts-selected-emotion" value="' + autoEmotion + '">';
118
- h += '</div>';
119
- return h;
120
- }
121
-
122
- // Patch readWallPost to include full voice+emotion selector
123
- var origReadWallPost = window.readWallPost;
124
- window.readWallPost = function(i){
125
- var p = _wallPosts[i];
126
- if(!p) return;
127
- showView('view-article');
128
- var images = p.images || [];
129
- var imgGallery = '';
130
- if(images.length > 0){
131
- imgGallery = '<div style="display:flex;gap:4px;flex-wrap:wrap;margin:8px 0">';
132
- images.slice(0,6).forEach(function(imgUrl){
133
- imgGallery += '<img src="'+esc(imgUrl)+'" style="width:80px;height:60px;object-fit:cover;border-radius:4px" onerror="this.style.display=\'none\'">';
134
- });
135
- imgGallery += '</div>';
136
- }
137
- var hasVideo = p.video && p.video.length > 0;
138
- var voiceEmotionHtml = '';
139
- if(!hasVideo){
140
- voiceEmotionHtml = buildVoiceEmotionSelector(p);
141
- } else {
142
- voiceEmotionHtml = '<div style="margin-top:10px;padding:10px;background:#1a2a1f;border-radius:8px;font-size:11px;color:#5cb87a">🎬 Voice: ' + (p.voice||'hoaimy') + ' | 😊 Emotion: ' + (p.emotion||'neutral') + ' | ⚡ Speed: ' + (p.short_speed||'1.2') + 'x</div>';
143
- voiceEmotionHtml += buildVoiceEmotionSelector(p);
144
- }
145
-
146
- document.getElementById('view-article').innerHTML =
147
- '<button class="back-btn" onclick="switchCat(\'home\')">← Quay lại</button>' +
148
- '<div class="article-view"><span class="badge badge-ai">AI</span><h1 class="article-title">'+esc(p.title)+'</h1>' +
149
- imgGallery +
150
- '<p class="article-p" style="white-space:pre-wrap">'+esc(p.text)+'</p>' +
151
- (hasVideo ? '<video class="article-img" src="'+esc(p.video)+'" controls playsinline style="max-height:400px"></video>' : '') +
152
- '<div class="article-actions">' +
153
- (hasVideo ? '<button onclick="openShortAIFeed('+i+')">🎬 Xem Short</button>' : '') +
154
- '</div>' +
155
- voiceEmotionHtml +
156
- '</div>';
157
- window.scrollTo(0,0);
158
-
159
- // Bind voice/emotion selectors
160
- setTimeout(function(){
161
- var container = document.querySelector('.tts-selector');
162
- if(!container) return;
163
-
164
- container.querySelectorAll('.tts-voice-btn').forEach(function(btn){
165
- btn.addEventListener('click', function(){
166
- container.querySelectorAll('.tts-voice-btn').forEach(function(b){b.style.borderColor='#333';b.style.background='#222';});
167
- this.style.borderColor='#5cb87a';
168
- this.style.background='#1a2a1f';
169
- container.querySelector('.tts-selected-voice').value = this.dataset.voice;
170
- });
171
- });
172
- container.querySelectorAll('.tts-emotion-btn').forEach(function(btn){
173
- btn.addEventListener('click', function(){
174
- container.querySelectorAll('.tts-emotion-btn').forEach(function(b){b.style.borderColor='#333';b.style.background='#222';});
175
- this.style.borderColor='#5cb87a';
176
- this.style.background='#1a2a1f';
177
- container.querySelector('.tts-selected-emotion').value = this.dataset.emotion;
178
- });
179
- });
180
- var createBtn = container.querySelector('.tts-create-btn');
181
- if(createBtn){
182
- createBtn.addEventListener('click', function(){
183
- var postId = p.id;
184
- var voice = container.querySelector('.tts-selected-voice').value;
185
- var emotion = container.querySelector('.tts-selected-emotion').value;
186
- var speed = parseFloat(container.querySelector('.tts-speed').value) || 1.2;
187
- window.makeShortVideo(postId, this, voice, speed, emotion);
188
- });
189
- }
190
- }, 100);
191
- };
192
-
193
- // Patch makeShortVideo to accept emotion parameter
194
- var origMakeShortVideo = window.makeShortVideo;
195
- window.makeShortVideo = function(postId, btn, voice, speed, emotion){
196
- if(!postId) return;
197
- var origText = btn ? btn.textContent : '🎬 Tạo Video';
198
- if(btn){btn.disabled=true;btn.textContent='⏳ Đang tạo...';}
199
- toast('⏳ Đang tạo video shorts...');
200
- try{
201
- var url = '/api/ai/short/' + encodeURIComponent(postId);
202
- var params = [];
203
- if(voice) params.push('voice='+encodeURIComponent(voice));
204
- if(speed) params.push('speed='+encodeURIComponent(speed));
205
- if(emotion) params.push('emotion='+encodeURIComponent(emotion));
206
- if(params.length) url += '?' + params.join('&');
207
-
208
- fetch(url, {method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({voice:voice||'hoaimy', emotion:emotion||'neutral', speed:speed||1.2})})
209
- .then(function(r){return r.json().then(function(j){return {ok:r.ok, j:j};});})
210
- .then(function(result){
211
- if(!result.ok || result.j.error) throw new Error(result.j.error||'Lỗi tạo video');
212
- toast('✅ Đã tạo video shorts!');
213
- var p = _wallPosts.find(function(x){return String(x.id)===String(postId);});
214
- if(p){
215
- p.video = result.j.video;
216
- p.voice = result.j.voice;
217
- p.emotion = result.j.emotion;
218
- var itemId = 'wall-item-'+postId;
219
- var el = document.getElementById(itemId);
220
- if(el){
221
- var idx = _wallPosts.indexOf(p);
222
- el.outerHTML = makeWallItem(p, idx);
223
- var newEl = document.getElementById(itemId);
224
- if(newEl) newEl.className = 'wall-item wall-item-new';
225
- }
226
- }
227
- refreshShortAISlider();
228
- })
229
- .catch(function(e){
230
- toast('❌ '+e.message);
231
- if(btn){btn.disabled=false;btn.textContent=origText;}
232
- });
233
- }catch(e){
234
- toast('❌ '+e.message);
235
- if(btn){btn.disabled=false;btn.textContent=origText;}
236
- }
237
- };
238
-
239
- // Also patch rewriteArticle in app_v2.js to use rewrite_share (not rewrite_slide)
240
- window.rewriteArticle = async function(){
241
- var url = window._currentArticle && window._currentArticle.url;
242
- if(!url) return;
243
- toast('⏳ Đang tạo bài đăng Tường AI...');
244
- try {
245
- var r = await fetch('/api/rewrite_share', {
246
- method: 'POST',
247
- headers: {'Content-Type': 'application/json'},
248
- body: JSON.stringify({url: url, context: document.querySelector('.article-view') ? document.querySelector('.article-view').innerText.slice(0,14000) : ''})
249
- });
250
- var j = await r.json();
251
- if (!r.ok || j.error) throw new Error(j.error);
252
- toast('✅ Đã đăng Tường AI!');
253
- if (j.post) prependWallPost(j.post);
254
- if (j.post && typeof goToWallPost === 'function') goToWallPost(j.post.id);
255
- // Show slides if available
256
- if (j.slides && j.slides.length) {
257
- setTimeout(function(){ showSlidePreview(j.slides, j.post ? j.post.title : ''); }, 500);
258
- }
259
- // Show voice + emotion selector
260
- if (j.post && !j.post.video) {
261
- setTimeout(function(){ showVoiceEmotionSelector(j.post.id, j.post.title, j.post.text); }, 1000);
262
- }
263
- } catch(e) {
264
- toast('❌ ' + e.message);
265
- }
266
- };
267
-
268
- // Show voice+emotion selector popup (same as in rewrite_fix_v2.js)
269
- window.showVoiceEmotionSelector = function(postId, title, text) {
270
- var overlay = document.createElement('div');
271
- overlay.id = 'voice-emotion-selector';
272
- overlay.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.85);z-index:99999;display:flex;align-items:center;justify-content:center;padding:16px';
273
- var box = document.createElement('div');
274
- box.style.cssText = 'background:#1a1a1a;border:2px solid #2d8659;border-radius:16px;padding:20px;max-width:400px;width:100%;max-height:80vh;overflow-y:auto';
275
- var lang = detectLanguage(title + ' ' + text);
276
- var autoEmotion = detectEmotion(title + ' ' + text);
277
- var h = '<h3 style="color:#5cb87a;margin-bottom:12px;font-size:16px">🎬 Tạo Short AI (ngôn ngữ: '+lang.toUpperCase()+')</h3>';
278
- h += '<div style="margin-bottom:12px"><div style="color:#aaa;font-size:11px;margin-bottom:6px">🎙️ Chọn giọng đọc:</div>';
279
- VOICE_LIST.forEach(function(v){
280
- var sel = v.id === getAutoVoice(lang) ? 'border-color:#5cb87a;background:#1a2a1f' : 'border-color:#333;background:#222';
281
- h += '<button class="ve-voice-btn" data-voice="'+v.id+'" style="display:inline-block;'+sel+';border:1px solid;color:#ccc;padding:5px 10px;border-radius:12px;font-size:10px;margin:2px;cursor:pointer">'+v.label+'</button>';
282
- });
283
- h += '</div>';
284
- h += '<div style="margin-bottom:12px"><div style="color:#aaa;font-size:11px;margin-bottom:6px">😊 Chọn cảm xúc:</div>';
285
- EMOTION_LIST.forEach(function(e){
286
- var sel = e.id === autoEmotion ? 'border-color:#5cb87a;background:#1a2a1f' : 'border-color:#333;background:#222';
287
- h += '<button class="ve-emotion-btn" data-emotion="'+e.id+'" style="display:inline-block;'+sel+';border:1px solid;color:#ccc;padding:5px 10px;border-radius:12px;font-size:10px;margin:2px;cursor:pointer">'+e.label+'</button>';
288
- });
289
- h += '</div>';
290
- h += '<div style="margin-bottom:12px"><div style="color:#aaa;font-size:11px;margin-bottom:6px">⚡ Tốc độ:</div>';
291
- h += '<select id="ve-speed" style="background:#222;border:1px solid #333;color:#ccc;padding:6px 12px;border-radius:10px;font-size:11px">';
292
- h += '<option value="0.85">0.85x Chậm</option><option value="1.0">1.0x Bình thường</option><option value="1.2" selected>1.2x Nhanh</option><option value="1.35">1.35x Rất nhanh</option></select></div>';
293
- h += '<div style="display:flex;gap:8px"><button id="ve-create-btn" style="flex:1;background:#2d8659;border:0;color:#fff;padding:10px;border-radius:12px;font-size:12px;font-weight:700;cursor:pointer">🎬 Tạo Short</button>';
294
- h += '<button id="ve-cancel-btn" style="background:#333;border:0;color:#ccc;padding:10px 16px;border-radius:12px;font-size:11px;cursor:pointer">✕</button></div>';
295
- h += '<div id="ve-status" style="color:#888;font-size:10px;margin-top:8px;display:none"></div>';
296
- box.innerHTML = h;
297
- overlay.appendChild(box);
298
- document.body.appendChild(overlay);
299
- var selectedVoice = getAutoVoice(lang);
300
- var selectedEmotion = autoEmotion;
301
- box.querySelectorAll('.ve-voice-btn').forEach(function(btn){
302
- btn.addEventListener('click', function(){
303
- box.querySelectorAll('.ve-voice-btn').forEach(function(b){b.style.borderColor='#333';b.style.background='#222';});
304
- this.style.borderColor='#5cb87a';this.style.background='#1a2a1f';
305
- selectedVoice = this.dataset.voice;
306
- });
307
- });
308
- box.querySelectorAll('.ve-emotion-btn').forEach(function(btn){
309
- btn.addEventListener('click', function(){
310
- box.querySelectorAll('.ve-emotion-btn').forEach(function(b){b.style.borderColor='#333';b.style.background='#222';});
311
- this.style.borderColor='#5cb87a';this.style.background='#1a2a1f';
312
- selectedEmotion = this.dataset.emotion;
313
- });
314
- });
315
- box.querySelector('#ve-cancel-btn').addEventListener('click', function(){overlay.remove();});
316
- box.querySelector('#ve-create-btn').addEventListener('click', async function(){
317
- this.disabled = true; this.textContent = '⏳ Đang tạo...';
318
- box.querySelector('#ve-status').style.display = 'block';
319
- box.querySelector('#ve-status').textContent = 'Đang tạo video shorts...';
320
- try {
321
- var speed = parseFloat(box.querySelector('#ve-speed').value) || 1.2;
322
- var r = await fetch('/api/ai/short/' + encodeURIComponent(postId), {
323
- method: 'POST', headers: {'Content-Type': 'application/json'},
324
- body: JSON.stringify({voice: selectedVoice, emotion: selectedEmotion, speed: speed})
325
- });
326
- var j = await r.json();
327
- if (!r.ok || j.error) throw new Error(j.error || 'Lỗi tạo video');
328
- toast('✅ Đã tạo Short AI!');
329
- overlay.remove();
330
- var p = _wallPosts.find(function(x){return String(x.id)===String(postId);});
331
- if (p) { p.video = j.video; p.voice = j.voice; p.emotion = j.emotion; }
332
- var track = document.getElementById('ai-wall-track');
333
- if (track) {
334
- var idx = _wallPosts.findIndex(function(x){return String(x.id)===String(postId);});
335
- if (idx >= 0 && track.children[idx]) {
336
- track.children[idx].outerHTML = makeWallItem(p, idx);
337
- }
338
- }
339
- refreshShortAISlider();
340
- } catch(e) {
341
- this.disabled = false; this.textContent = '🎬 Tạo Short';
342
- box.querySelector('#ve-status').textContent = '❌ ' + e.message;
343
- }
344
- });
345
- };
346
-
347
- // Show slides fullscreen overlay
348
- window.showSlidePreview = function(slides, title) {
349
- if (!slides || !slides.length) return;
350
- var overlay = document.createElement('div');
351
- overlay.id = 'slide-preview';
352
- overlay.style.cssText = 'position:fixed;inset:0;background:#000;z-index:99999;display:flex;flex-direction:column;overflow:hidden';
353
- var currentSlide = 0;
354
- function renderSlide(idx) {
355
- var s = slides[idx];
356
- overlay.innerHTML = '<div style="position:absolute;top:10px;left:10px;right:10px;display:flex;justify-content:space-between;align-items:center;z-index:2">' +
357
- '<button onclick="document.getElementById(\'slide-preview\').remove()" style="background:rgba(0,0,0,.6);border:0;color:#fff;padding:8px 14px;border-radius:20px;font-size:12px;cursor:pointer">✕ Đóng</button>' +
358
- '<span style="color:#fff;font-size:11px;background:rgba(0,0,0,.6);padding:4px 10px;border-radius:10px">' + (idx+1) + '/' + slides.length + '</span>' +
359
- '</div>' +
360
- '<div style="flex:1;display:flex;align-items:center;justify-content:center;padding:20px">' +
361
- (s.image ? '<img src="' + esc(s.image) + '" style="max-width:100%;max-height:60vh;border-radius:10px;object-fit:contain" onerror="this.style.display=\'none\'">' : '') +
362
- '</div>' +
363
- '<div style="padding:16px 20px;background:linear-gradient(transparent,rgba(0,0,0,.9));min-height:100px">' +
364
- '<p style="color:#fff;font-size:14px;line-height:1.6;text-align:center">' + esc(s.text) + '</p>' +
365
- '</div>' +
366
- '<div style="display:flex;gap:10px;padding:10px 20px 20px;justify-content:center">' +
367
- '<button onclick="prevSlide()" style="background:#333;border:0;color:#fff;padding:10px 20px;border-radius:20px;font-size:12px;cursor:pointer"' + (idx===0?' disabled style="opacity:.3"':'') + '>← Trước</button>' +
368
- '<button onclick="nextSlideRip('+idx+')" id="slide-next-btn" style="background:#2d8659;border:0;color:#fff;padding:10px 20px;border-radius:20px;font-size:12px;cursor:pointer"' + (idx===slides.length-1?' disabled style="opacity:.3"':'') + '>Tiếp →</button>' +
369
- '</div>';
370
- }
371
- window.nextSlideRip = function(currentIdx) { if (currentIdx < slides.length - 1) { currentSlide = currentIdx + 1; renderSlide(currentSlide); } };
372
- window.prevSlide = function() { if (currentSlide > 0) { currentSlide--; renderSlide(currentSlide); } };
373
- renderSlide(0);
374
- document.body.appendChild(overlay);
375
- var startX = 0;
376
- overlay.addEventListener('touchstart', function(e) { startX = e.touches[0].clientX; });
377
- overlay.addEventListener('touchend', function(e) {
378
- var diff = e.changedTouches[0].clientX - startX;
379
- if (diff < -50) nextSlideRip(currentSlide);
380
- else if (diff > 50) prevSlide();
381
- });
382
- };
383
  })();
 
1
+ // app_v2_shorts_fix.js - No-op patch
2
+ // All voice+emotion+slide functionality is now built into app_v2.js directly.
3
+ // This file is kept for backward compatibility but does not override any functions.
4
  (function(){
5
+ console.log("[VNEWS] Shorts fix system loaded (built-in)");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  })();