diff --git a/make_post_sell/static/css/common.css b/make_post_sell/static/css/common.css index dfc8163..e77564c 100644 --- a/make_post_sell/static/css/common.css +++ b/make_post_sell/static/css/common.css @@ -2740,7 +2740,8 @@ textarea { .ring-header-controls { display: grid; - grid-template-columns: auto auto auto auto; + grid-auto-flow: column; + grid-auto-columns: auto; gap: 16px; justify-content: end; align-items: center; @@ -2884,6 +2885,21 @@ textarea { color: white; border-color: var(--blue-color, #5871ad); } +.ring-karaoke-btn { + font-size: 0.75em; + padding: 2px 10px; + border: 1px solid var(--border-color, #ccc); + border-radius: 12px; + background: transparent; + color: var(--text-muted, #999); + cursor: pointer; + white-space: nowrap; +} +.ring-karaoke-btn.active { + background: var(--blue-color, #5871ad); + color: white; + border-color: var(--blue-color, #5871ad); +} .related-content-row-current { background-color: var(--blue-color, #5871ad); color: white; diff --git a/make_post_sell/static/js/watch.js b/make_post_sell/static/js/watch.js index e217c99..aff5da7 100644 --- a/make_post_sell/static/js/watch.js +++ b/make_post_sell/static/js/watch.js @@ -29,6 +29,11 @@ var PROGRESS_SAVE_INTERVAL = 7000; var lastProgressSave = 0; + // --- Karaoke state --- + var karaokeMode = 0; // 0=original, 1=instrumentals, 2=vocals + var KARAOKE_LABELS = ['Original', 'Instrumentals', 'Vocals']; + var karaokeUrls = {original: null, instrumentals: null, vocals: null}; + // --- Ring state (persisted in localStorage) --- var ringProductIds = []; var ringPosition = 0; @@ -217,6 +222,69 @@ } } + // --- Karaoke: read initial URLs from data attributes --- + function initKaraokeFromDom() { + var container = document.getElementById('watch-media-container'); + karaokeUrls.instrumentals = container ? container.getAttribute('data-instrumentals-url') : null; + karaokeUrls.vocals = container ? container.getAttribute('data-vocals-url') : null; + karaokeUrls.original = activeMedia ? activeMedia.src : null; + karaokeMode = 0; + updateKaraokeButton(); + } + + function updateKaraokeButton() { + var btn = document.getElementById('karaoke-toggle-btn'); + if (!btn) return; + var hasKaraoke = !!(karaokeUrls.instrumentals || karaokeUrls.vocals); + btn.style.display = hasKaraoke ? '' : 'none'; + btn.textContent = KARAOKE_LABELS[karaokeMode]; + if (karaokeMode === 0) { + btn.classList.remove('active'); + } else { + btn.classList.add('active'); + } + } + + function getNextKaraokeMode() { + // Build list of available modes + var modes = [0]; // original is always available + if (karaokeUrls.instrumentals) modes.push(1); + if (karaokeUrls.vocals) modes.push(2); + if (modes.length <= 1) return 0; + var idx = modes.indexOf(karaokeMode); + return modes[(idx + 1) % modes.length]; + } + + function cycleKaraoke() { + if (!activeMedia) return; + var hasKaraoke = !!(karaokeUrls.instrumentals || karaokeUrls.vocals); + if (!hasKaraoke) return; + + var nextMode = getNextKaraokeMode(); + if (nextMode === karaokeMode) return; + + var savedTime = activeMedia.currentTime; + var wasPlaying = !activeMedia.paused; + + var newSrc; + if (nextMode === 1) newSrc = karaokeUrls.instrumentals; + else if (nextMode === 2) newSrc = karaokeUrls.vocals; + else newSrc = karaokeUrls.original; + + karaokeMode = nextMode; + activeMedia.src = newSrc; + + activeMedia.addEventListener('loadedmetadata', function onKLoad() { + activeMedia.removeEventListener('loadedmetadata', onKLoad); + activeMedia.currentTime = savedTime; + if (wasPlaying) activeMedia.play().catch(function() {}); + }); + + updateKaraokeButton(); + } + + initKaraokeFromDom(); + // --- Autoplay with sound (for video/audio) --- if (activeMedia) { // Restore saved progress on reload (seek + volume fade-in) @@ -585,6 +653,13 @@ syncRingPosition(data.product_id); scheduleUrlRefresh(); + // Reset karaoke state for the new product + karaokeMode = 0; + karaokeUrls.original = data.media_url || null; + karaokeUrls.instrumentals = data.instrumentals_url || null; + karaokeUrls.vocals = data.vocals_url || null; + updateKaraokeButton(); + document.title = data.title; var h1 = document.querySelector('.product-images h1'); @@ -786,6 +861,7 @@ + '' + '' + '' + + '' + '' + '' + '' @@ -920,6 +996,7 @@ } updateProgressDisplay(); + updateKaraokeButton(); } function escapeHtml(text) { @@ -1428,12 +1505,22 @@ // Only refresh if still on the same product if (data.product_id !== currentProductId) { scheduleUrlRefresh(); return; } + // Refresh karaoke URLs from the fresh response + karaokeUrls.original = data.media_url; + if (data.instrumentals_url) karaokeUrls.instrumentals = data.instrumentals_url; + if (data.vocals_url) karaokeUrls.vocals = data.vocals_url; + var wasPlaying = !activeMedia.paused; var savedTime = activeMedia.currentTime; var savedVolume = activeMedia.volume; var savedMuted = activeMedia.muted; - activeMedia.src = data.media_url; + // Use the URL matching current karaoke mode + var freshSrc; + if (karaokeMode === 1 && karaokeUrls.instrumentals) freshSrc = karaokeUrls.instrumentals; + else if (karaokeMode === 2 && karaokeUrls.vocals) freshSrc = karaokeUrls.vocals; + else freshSrc = data.media_url; + activeMedia.src = freshSrc; activeMedia.addEventListener('loadeddata', function onLoaded() { activeMedia.removeEventListener('loadeddata', onLoaded); @@ -1593,6 +1680,12 @@ return; } + if (e.target.id === 'karaoke-toggle-btn' || e.target.closest('#karaoke-toggle-btn')) { + e.preventDefault(); + cycleKaraoke(); + return; + } + if (e.target.id === 'skip-random-btn' || e.target.closest('#skip-random-btn')) { e.preventDefault(); skipToRandom(); @@ -1640,6 +1733,14 @@ } }); + // --- Keyboard shortcut: K to toggle karaoke --- + document.addEventListener('keydown', function(e) { + if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' || e.target.tagName === 'SELECT') return; + if (e.key === 'k' || e.key === 'K') { + cycleKaraoke(); + } + }); + // --- Browser back/forward --- window.addEventListener('popstate', function(e) { if (e.state && e.state.productId) { diff --git a/make_post_sell/templates/content.j2 b/make_post_sell/templates/content.j2 index cab5390..c993fbc 100644 --- a/make_post_sell/templates/content.j2 +++ b/make_post_sell/templates/content.j2 @@ -40,7 +40,7 @@ {% if request.shop.watch_mode_enabled and product.extensions.get("product") in video_extensions %} {# Watch mode: direct video render with autoplay #} {% set watch_video_url = request.app["bucket.secure_uploads.get_endpoint"] ~ "/" ~ product.s3_path ~ "/product" %} -