diff --git a/make_post_sell/models/product.py b/make_post_sell/models/product.py index ed00b19..a7b9a67 100644 --- a/make_post_sell/models/product.py +++ b/make_post_sell/models/product.py @@ -62,10 +62,11 @@ DEFAULT_BUNDLE_METADATA = unicode( VIDEO_EXTENSIONS = ["mp4", "webm", "mkv", "avi", "mov", "wmv", "flv", "m4v", "ogv"] AUDIO_EXTENSIONS = ["mp3", "wav", "ogg", "m4a", "flac", "aac", "wma", "opus"] IMAGE_EXTENSIONS = ["jpg", "jpeg", "png", "gif", "webp", "bmp", "svg"] +DOCUMENT_EXTENSIONS = ["pdf"] def get_media_type(extension): - """Returns 'video', 'audio', 'image', or None based on file extension.""" + """Returns 'video', 'audio', 'image', 'document', or None based on file extension.""" if not extension: return None ext = extension.lower() @@ -75,6 +76,8 @@ def get_media_type(extension): return "audio" elif ext in IMAGE_EXTENSIONS: return "image" + elif ext in DOCUMENT_EXTENSIONS: + return "document" return None diff --git a/make_post_sell/static/js/player.js b/make_post_sell/static/js/player.js index c05c43f..e4c12ce 100644 --- a/make_post_sell/static/js/player.js +++ b/make_post_sell/static/js/player.js @@ -302,6 +302,20 @@ e.preventDefault(); toggleAutoResize(); break; + case 'g': + case 'G': + e.preventDefault(); + if (typeof goToProductPage === 'function') { + goToProductPage(); + } + break; + case 'p': + case 'P': + e.preventDefault(); + if (typeof togglePip === 'function') { + togglePip(); + } + break; } } diff --git a/make_post_sell/templates/content.j2 b/make_post_sell/templates/content.j2 index 224dbc8..4379151 100644 --- a/make_post_sell/templates/content.j2 +++ b/make_post_sell/templates/content.j2 @@ -119,6 +119,11 @@ async function loadMedia(mediaElement, productId) { const match = scriptContent.match(/window\.playerData\s*=\s*(\{[^}]+\})/); if (match) { currentPlayerData = eval('(' + match[1] + ')'); + + // Navigate parent page to show the new product + if (currentPlayerData.productUrl) { + window.history.pushState({}, '', currentPlayerData.productUrl); + } } } } @@ -175,7 +180,7 @@ async function openMediaPlayer(productId) { } } - // Enter Picture-in-Picture mode + // Enter Picture-in-Picture mode (only for video) if (document.pictureInPictureEnabled && media.tagName === 'VIDEO') { await media.play(); await media.requestPictureInPicture(); @@ -199,9 +204,13 @@ async function openMediaPlayer(productId) { media.addEventListener('leavepictureinpicture', () => { document.body.removeChild(media); }); - } else { - // Fallback: keep small floating player on page + } else if (media.tagName === 'AUDIO') { + // Audio: keep small floating player on page media.play(); + } else { + // For non-media (PDFs, images), open in pop-out player window + document.body.removeChild(media); + window.open(`/player/${productId}`, 'mediaPlayer', 'width=800,height=600,menubar=no,toolbar=no,location=no,status=no,scrollbars=no'); } } catch (err) { console.error('Error opening media player:', err); diff --git a/make_post_sell/templates/player.j2 b/make_post_sell/templates/player.j2 index 359e41f..2307d33 100644 --- a/make_post_sell/templates/player.j2 +++ b/make_post_sell/templates/player.j2 @@ -40,6 +40,11 @@ display: block; } + /* For documents (PDFs), fill the viewport */ + iframe#media { + border: none; + } + /* Mobile optimizations */ @media (max-width: 600px) { .controls-grid { @@ -186,6 +191,10 @@ {% elif media_type == 'image' %} {{ product.title }} + {% elif media_type == 'document' %} + {% endif %}
{{ product.title }}
@@ -214,8 +223,8 @@ + -
@@ -226,8 +235,24 @@ window.playerData = { prevProductId: {{ ('"%s"' % prev_product_id) if prev_product_id else 'null' }}, nextProductId: {{ ('"%s"' % next_product_id) if next_product_id else 'null' }}, - mediaType: "{{ media_type }}" + mediaType: "{{ media_type }}", + productId: "{{ product.id }}", + productUrl: "{{ product_url }}", + prevProductUrl: {{ ('"%s"' % prev_product_url) if prev_product_url else 'null' }}, + nextProductUrl: {{ ('"%s"' % next_product_url) if next_product_url else 'null' }} }; + + // Go to the product page + function goToProductPage() { + if (window.opener && !window.opener.closed) { + // If opened from another window, navigate that window + window.opener.location.href = window.playerData.productUrl; + window.opener.focus(); + } else { + // Otherwise open in new tab + window.open(window.playerData.productUrl, '_blank'); + } + } diff --git a/make_post_sell/templates/product.j2 b/make_post_sell/templates/product.j2 index fbcdf61..b4a430a 100644 --- a/make_post_sell/templates/product.j2 +++ b/make_post_sell/templates/product.j2 @@ -197,6 +197,11 @@ async function loadMedia(mediaElement, productId) { const match = scriptContent.match(/window\.playerData\s*=\s*(\{[^}]+\})/); if (match) { currentPlayerData = eval('(' + match[1] + ')'); + + // Navigate parent page to show the new product + if (currentPlayerData.productUrl) { + window.history.pushState({}, '', currentPlayerData.productUrl); + } } } } @@ -253,7 +258,7 @@ async function openMediaPlayer(productId) { } } - // Enter Picture-in-Picture mode + // Enter Picture-in-Picture mode (only for video) if (document.pictureInPictureEnabled && media.tagName === 'VIDEO') { await media.play(); await media.requestPictureInPicture(); @@ -277,9 +282,13 @@ async function openMediaPlayer(productId) { media.addEventListener('leavepictureinpicture', () => { document.body.removeChild(media); }); - } else { - // Fallback: keep small floating player on page + } else if (media.tagName === 'AUDIO') { + // Audio: keep small floating player on page media.play(); + } else { + // For non-media (PDFs, images), open in pop-out player window + document.body.removeChild(media); + window.open(`/player/${productId}`, 'mediaPlayer', 'width=800,height=600,menubar=no,toolbar=no,location=no,status=no,scrollbars=no'); } } catch (err) { console.error('Error opening media player:', err); diff --git a/make_post_sell/views/player.py b/make_post_sell/views/player.py index 73181b0..c3a80d6 100644 --- a/make_post_sell/views/player.py +++ b/make_post_sell/views/player.py @@ -105,6 +105,17 @@ def player(request): if current_index < len(media_products) - 1: next_product_id = str(media_products[current_index + 1].id) + # Get product URLs for navigation + product_url = product.absolute_url(request) + prev_product_url = None + next_product_url = None + + if prev_product_id and current_index is not None and current_index > 0: + prev_product_url = media_products[current_index - 1].absolute_url(request) + + if next_product_id and current_index is not None and current_index < len(media_products) - 1: + next_product_url = media_products[current_index + 1].absolute_url(request) + return { "product": product, "presigned_url": presigned_url, @@ -112,4 +123,7 @@ def player(request): "extension": extension, "prev_product_id": prev_product_id, "next_product_id": next_product_id, + "product_url": product_url, + "prev_product_url": prev_product_url, + "next_product_url": next_product_url, }