Spaces:
Running
Running
v8: auto-refresh wall UI after VTV Short AI upload
Browse files- static/yt_live.js +24 -13
static/yt_live.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
| 1 |
-
// === VNEWS — VTV LIVE + Inline Recorder
|
| 2 |
// Features: PiP, mini-player, INLINE RECORDER with ratio crop + AI title + Short AI upload
|
| 3 |
-
// FIX
|
|
|
|
| 4 |
|
| 5 |
(function(){
|
| 6 |
if(window._ytLiveLoaded) return;
|
|
@@ -171,7 +172,21 @@
|
|
| 171 |
if (el) el.style.display = 'none';
|
| 172 |
}
|
| 173 |
|
| 174 |
-
// =====
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 175 |
function uploadToWall(blob, title, source) {
|
| 176 |
const formData = new FormData();
|
| 177 |
formData.append('video', blob, `vtv-${_currentCh}-${Date.now()}.webm`);
|
|
@@ -181,7 +196,6 @@
|
|
| 181 |
return fetch('/api/wall', { method: 'POST', body: formData })
|
| 182 |
.then(r => {
|
| 183 |
console.log('[VTV Upload] HTTP status:', r.status, r.statusText);
|
| 184 |
-
// Read as text first, then parse JSON manually for robustness
|
| 185 |
return r.text().then(text => {
|
| 186 |
console.log('[VTV Upload] Response text:', text.substring(0, 200));
|
| 187 |
let data;
|
|
@@ -197,6 +211,10 @@
|
|
| 197 |
console.log('[VTV Upload] Parsed data:', data);
|
| 198 |
if (data && data.ok === true) {
|
| 199 |
recStatus('✅ Đã tạo Short AI! Xem trong mục Tường AI.', 'ok');
|
|
|
|
|
|
|
|
|
|
|
|
|
| 200 |
return data;
|
| 201 |
} else {
|
| 202 |
throw new Error(data && data.error ? data.error : 'Upload thất bại (ok=false)');
|
|
@@ -454,7 +472,6 @@
|
|
| 454 |
_rec.chunks = [];
|
| 455 |
_rec.isRecording = true;
|
| 456 |
|
| 457 |
-
// CRITICAL: Unmute video so captureStream includes audio tracks
|
| 458 |
const wasMuted = video.muted;
|
| 459 |
video.muted = false;
|
| 460 |
const wasVolume = video.volume;
|
|
@@ -577,10 +594,7 @@
|
|
| 577 |
video.preload = 'auto';
|
| 578 |
video.playsInline = true;
|
| 579 |
|
| 580 |
-
function cleanup() {
|
| 581 |
-
// Don't revoke blobUrl here — audioVideo may still need it
|
| 582 |
-
// It will be revoked after recording stops
|
| 583 |
-
}
|
| 584 |
|
| 585 |
function onReady() {
|
| 586 |
video.removeEventListener('loadedmetadata', onReady);
|
|
@@ -608,7 +622,6 @@
|
|
| 608 |
const ctx = canvas.getContext('2d');
|
| 609 |
const canvasStream = canvas.captureStream(30);
|
| 610 |
|
| 611 |
-
// Parallel video to capture audio from blob
|
| 612 |
let recordStream = canvasStream;
|
| 613 |
const audioVideo = document.createElement('video');
|
| 614 |
audioVideo.src = blobUrl;
|
|
@@ -676,7 +689,6 @@
|
|
| 676 |
video.onended = finish;
|
| 677 |
audioVideo.onended = finish;
|
| 678 |
|
| 679 |
-
// TIMEOUT FALLBACK: if video.ended doesn't fire within expected duration + 3s
|
| 680 |
const expectedDur = (video.duration || 0) * 1000;
|
| 681 |
if (expectedDur > 0 && isFinite(expectedDur)) {
|
| 682 |
setTimeout(() => {
|
|
@@ -686,7 +698,6 @@
|
|
| 686 |
}
|
| 687 |
}, expectedDur + 3000);
|
| 688 |
} else {
|
| 689 |
-
// If duration is unknown (e.g. blob), use a generous timeout
|
| 690 |
console.warn('[VTV Crop] Duration unknown, using 60s timeout');
|
| 691 |
setTimeout(() => {
|
| 692 |
if (!finished) {
|
|
@@ -863,4 +874,4 @@
|
|
| 863 |
window.loadHome=async function(){const old=document.getElementById('vtv-block');if(old)old.remove();_blockInserted=false;const r=await _origLoadHome.apply(this,arguments);try{pinBlock();}catch(e){}return r;};
|
| 864 |
window.loadHome.__vtvWrapped=true;
|
| 865 |
}
|
| 866 |
-
})();
|
|
|
|
| 1 |
+
// === VNEWS — VTV LIVE + Inline Recorder v8 ===
|
| 2 |
// Features: PiP, mini-player, INLINE RECORDER with ratio crop + AI title + Short AI upload
|
| 3 |
+
// FIX v8: onUploadSuccess auto-refreshes wall UI after VTV upload
|
| 4 |
+
// V8 CHANGE: Added onUploadSuccess() + call after uploadToWall success
|
| 5 |
|
| 6 |
(function(){
|
| 7 |
if(window._ytLiveLoaded) return;
|
|
|
|
| 172 |
if (el) el.style.display = 'none';
|
| 173 |
}
|
| 174 |
|
| 175 |
+
// ===== V8: onUploadSuccess — auto-refresh wall UI after Short AI upload =====
|
| 176 |
+
function onUploadSuccess(post) {
|
| 177 |
+
console.log('[VTV Upload] onUploadSuccess called with:', post);
|
| 178 |
+
if (typeof _wallPosts !== 'undefined' && _wallPosts) {
|
| 179 |
+
_wallPosts.unshift(post);
|
| 180 |
+
}
|
| 181 |
+
if (typeof prependWallPost === 'function') {
|
| 182 |
+
try { prependWallPost(post); } catch(e) { console.warn('[VTV Upload] prependWallPost error:', e); }
|
| 183 |
+
}
|
| 184 |
+
if (typeof refreshShortAISlider === 'function') {
|
| 185 |
+
try { refreshShortAISlider(); } catch(e) { console.warn('[VTV Upload] refreshShortAISlider error:', e); }
|
| 186 |
+
}
|
| 187 |
+
}
|
| 188 |
+
|
| 189 |
+
// ===== UPLOAD TO WALL (Short AI) — v8: auto-refresh wall UI =====
|
| 190 |
function uploadToWall(blob, title, source) {
|
| 191 |
const formData = new FormData();
|
| 192 |
formData.append('video', blob, `vtv-${_currentCh}-${Date.now()}.webm`);
|
|
|
|
| 196 |
return fetch('/api/wall', { method: 'POST', body: formData })
|
| 197 |
.then(r => {
|
| 198 |
console.log('[VTV Upload] HTTP status:', r.status, r.statusText);
|
|
|
|
| 199 |
return r.text().then(text => {
|
| 200 |
console.log('[VTV Upload] Response text:', text.substring(0, 200));
|
| 201 |
let data;
|
|
|
|
| 211 |
console.log('[VTV Upload] Parsed data:', data);
|
| 212 |
if (data && data.ok === true) {
|
| 213 |
recStatus('✅ Đã tạo Short AI! Xem trong mục Tường AI.', 'ok');
|
| 214 |
+
// v8: auto-update wall UI after successful upload
|
| 215 |
+
if (typeof onUploadSuccess === 'function') {
|
| 216 |
+
try { onUploadSuccess(data.post || data); } catch(e) { console.warn('[VTV Upload] onUploadSuccess error:', e); }
|
| 217 |
+
}
|
| 218 |
return data;
|
| 219 |
} else {
|
| 220 |
throw new Error(data && data.error ? data.error : 'Upload thất bại (ok=false)');
|
|
|
|
| 472 |
_rec.chunks = [];
|
| 473 |
_rec.isRecording = true;
|
| 474 |
|
|
|
|
| 475 |
const wasMuted = video.muted;
|
| 476 |
video.muted = false;
|
| 477 |
const wasVolume = video.volume;
|
|
|
|
| 594 |
video.preload = 'auto';
|
| 595 |
video.playsInline = true;
|
| 596 |
|
| 597 |
+
function cleanup() {}
|
|
|
|
|
|
|
|
|
|
| 598 |
|
| 599 |
function onReady() {
|
| 600 |
video.removeEventListener('loadedmetadata', onReady);
|
|
|
|
| 622 |
const ctx = canvas.getContext('2d');
|
| 623 |
const canvasStream = canvas.captureStream(30);
|
| 624 |
|
|
|
|
| 625 |
let recordStream = canvasStream;
|
| 626 |
const audioVideo = document.createElement('video');
|
| 627 |
audioVideo.src = blobUrl;
|
|
|
|
| 689 |
video.onended = finish;
|
| 690 |
audioVideo.onended = finish;
|
| 691 |
|
|
|
|
| 692 |
const expectedDur = (video.duration || 0) * 1000;
|
| 693 |
if (expectedDur > 0 && isFinite(expectedDur)) {
|
| 694 |
setTimeout(() => {
|
|
|
|
| 698 |
}
|
| 699 |
}, expectedDur + 3000);
|
| 700 |
} else {
|
|
|
|
| 701 |
console.warn('[VTV Crop] Duration unknown, using 60s timeout');
|
| 702 |
setTimeout(() => {
|
| 703 |
if (!finished) {
|
|
|
|
| 874 |
window.loadHome=async function(){const old=document.getElementById('vtv-block');if(old)old.remove();_blockInserted=false;const r=await _origLoadHome.apply(this,arguments);try{pinBlock();}catch(e){}return r;};
|
| 875 |
window.loadHome.__vtvWrapped=true;
|
| 876 |
}
|
| 877 |
+
})();
|