make_post_sell/make_post_sell/templates/content.j2
russell@unturf.com cd5ea68fe3
fix: MPS-24 Phase 2.8c — cache-bust ALL static JS (THE root cause)
THE root cause of the entire 'still reloads / still not working' saga
across 2.7 -> 2.8 -> 2.8b: shop_tags.j2 (tag_bulk.js) and
product_edit.j2 (product_tags.js) loaded their <script> WITHOUT the
?v={{ request.git_hash }} cache-bust. routes.py serves /static with
cache_max_age=3600, so the operator's browser kept the STALE JS for up
to an hour after every deploy — the new SPA code never executed, forms
fell back to native submit = full page reload, every time. Server-side
functional tests passed throughout because they have no browser cache.

Fix: append ?v={{ request.git_hash }} to EVERY static <script> include
(the established base.j2 / offer.js / pay-countdown.js convention) —
not just the two at fault but the whole latent class: tag_bulk,
product_tags, tag_filter, auction, player, sandbox, watch, signals,
comments, shop-settings. request.git_hash shifts every deploy -> URL
changes -> fresh fetch, no hard-refresh ever needed again.

Gate (must be empty):
  grep -rnE '<script src="/static/js/[^"?]+\.js"' make_post_sell/templates/

The 2.8/2.8b JS (onTagFormClick unified click handler, AJAX focus,
drag-to-reorder) stands — it just was never being fetched by the
browser. 1131 tests pass. Docs: mps-24.md Phase 2.8c, CLAUDE.md
(new mandatory cache-bust convention section).
2026-05-16 09:39:17 -04:00

211 lines
10 KiB
Django/Jinja

{% extends "base.j2" -%}
{%- block append_to_head_tag_section %}
<link rel="canonical" href="{{ product.absolute_url(request) }}" />
<title>{{ product.title }}</title>
<meta property="og:title" content="{{ product.title }}" />
<meta name="description" content="{{ product.description|truncate(500) }}">
<meta property="og:description" content="{{ product.description|truncate(500) }}" />
<meta property="og:type" content="article" />
<meta property="og:url" content="{{ product.absolute_url(request) }}" />
<meta property="og:site_name" content="{{ request.shop.name }}" />
{%- if "thumbnail1" in product.extensions %}
<meta property="og:image" content="{{ request.shop_cdn_endpoint }}/{{ product.s3_path }}/thumbnail1?ts={{ product.updated_timestamp }}" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="{{ product.title }}" />
<meta name="twitter:description" content="{{ product.description|truncate(500) }}" />
<meta name="twitter:image" content="{{ request.shop_cdn_endpoint }}/{{ product.s3_path }}/thumbnail1?ts={{ product.updated_timestamp }}" />
{%- endif %}
{# Removed auto-refresh timer - better UX to let download links expire than interrupt reading #}
{%- endblock append_to_head_tag_section -%}
{%- block call_to_action -%}
{% if request.user and request.user.can_edit_shop(request.shop) %}
<a href="{{ product.absolute_edit_url(request) }}" class="product-edit-button mps-button mps-button-small">&#9998; Edit</a>
{% endif %}
{%- endblock call_to_action -%}
{% block content -%}
<section class="two-column{% if request.shop.watch_mode_enabled %} watch-mode{% endif %}" data-signal-product-id="{{ product.id }}" data-signal-shop-id="{{ product.shop_id }}"{% if request.user and request.user.can_edit_shop(request.shop) %} data-signal-is-owner="1"{% endif %}>
{% if request.shop.watch_mode_enabled %}<div class="watch-left" data-watch-product-id="{{ product.id }}"{% if request.user and request.user.can_edit_shop(request.shop) %} data-watch-is-mod="1"{% endif %}>{% endif %}
<div class="product-images">
{% set video_extensions = ["mp4", "avi", "mov", "wmv", "flv", "mkv", "webm", "m4v"] %}
{% set audio_extensions = ["mp3", "wav", "ogg", "m4a", "flac", "aac", "opus"] %}
{% 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.shop_cdn_endpoint ~ "/" ~ product.s3_path ~ "/product" %}
<div id="watch-media-container"{% if instrumentals_url %} data-instrumentals-url="{{ instrumentals_url }}"{% endif %}{% if vocals_url %} data-vocals-url="{{ vocals_url }}"{% endif %}{% if karaoke_eligible %} data-karaoke-eligible="1"{% endif %}>
<div class="watch-video-container">
<video id="watch-video" src="{{ watch_video_url }}" autoplay controls playsinline class="product-main"></video>
<button class="unmute-overlay">Tap to unmute</button>
</div>
</div>
<div id="watch-countdown" class="watch-countdown js-only" style="display:none">
<img id="countdown-thumb" class="countdown-thumb" />
<div class="countdown-text">
<span id="countdown-title">Up next: ...</span>
<span id="countdown-number">7</span>
</div>
<button id="countdown-play-now" class="mps-button mps-button-blue">Play Now</button>
<button id="countdown-cancel" class="mps-button">Cancel</button>
</div>
<noscript>
<a href="{{ watch_video_url }}" target="_blank">Open video</a>
</noscript>
{% elif request.shop.watch_mode_enabled and product.extensions.get("product") in audio_extensions %}
{# Watch mode: audio with album art #}
{% set watch_audio_url = request.shop_cdn_endpoint ~ "/" ~ product.s3_path ~ "/product" %}
<div id="watch-media-container"{% if instrumentals_url %} data-instrumentals-url="{{ instrumentals_url }}"{% endif %}{% if vocals_url %} data-vocals-url="{{ vocals_url }}"{% endif %}{% if karaoke_eligible %} data-karaoke-eligible="1"{% endif %}>
<div class="watch-audio-container">
{% if "thumbnail1" in product.extensions %}
<img src="{{ request.shop_cdn_endpoint }}/{{ product.s3_path }}/thumbnail1?ts={{ product.updated_timestamp }}" class="product-main audio-cover" />
{% endif %}
<audio id="watch-audio" src="{{ watch_audio_url }}" autoplay controls></audio>
</div>
</div>
<div id="watch-countdown" class="watch-countdown js-only" style="display:none">
<img id="countdown-thumb" class="countdown-thumb" />
<div class="countdown-text">
<span id="countdown-title">Up next: ...</span>
<span id="countdown-number">7</span>
</div>
<button id="countdown-play-now" class="mps-button mps-button-blue">Play Now</button>
<button id="countdown-cancel" class="mps-button">Cancel</button>
</div>
<noscript>
<a href="{{ watch_audio_url }}" target="_blank">Listen</a>
</noscript>
{% elif "thumbnail1" in product.extensions %}
{% if product.extensions.get("product") in video_extensions %}
{# Video: play button overlay, click to play inline #}
{% set video_url = request.shop_cdn_endpoint ~ "/" ~ product.s3_path ~ "/product" %}
<div id="video-container-{{ product.id }}" class="video-thumbnail-container" onclick="playInline(this, '{{ video_url }}')">
<img src="{{ request.shop_cdn_endpoint }}/{{ product.s3_path }}/thumbnail1?ts={{ product.updated_timestamp }}" class="product-main" />
<div class="video-play-overlay"></div>
</div>
<noscript>
<a href="{{ video_url }}" target="_blank">Open video</a>
</noscript>
<div class="video-click-to-play">click ▶ to play</div>
{% else %}
{# Non-video: click to open in new window #}
<a target="_blank" href="{{ request.shop_cdn_endpoint }}/{{ product.s3_path }}/product?ts={{ product.updated_timestamp }}">
<img src="{{ request.shop_cdn_endpoint }}/{{ product.s3_path }}/thumbnail1?ts={{ product.updated_timestamp }}" class="product-main" />
</a>
{% endif %}
{% endif %}
{% if request.shop.watch_mode_enabled and product.extensions.get("product") not in video_extensions and product.extensions.get("product") not in audio_extensions %}
{# Countdown overlay for static content (PDFs, images) #}
<div id="watch-countdown" class="watch-countdown js-only" style="display:none">
<img id="countdown-thumb" class="countdown-thumb" />
<div class="countdown-text">
<span id="countdown-title">Up next: ...</span>
<span id="countdown-number">7</span>
</div>
<button id="countdown-play-now" class="mps-button mps-button-blue">Play Now</button>
<button id="countdown-cancel" class="mps-button">Cancel</button>
</div>
{% endif %}
<br/>
<h1>{{ product.title }}<br/><span class="subtitle-text">uploaded to <a href="{{ product.shop.absolute_about_url(request) }}" rel="nofollow" class="shop-theme-link-color">{{ product.shop.name }}</a></span></h1>
</div>
<div class="cinema-content-stack">
<div class="product-description">
<br/>
<b>Description</b>
<section>{{ product.description_html | safe }}</section>
<br/>
<br/>
{% if product.has_product_file %}
<b>File Type</b>
<br/>
{{ product.get_content_type("product") }} {{ product_size }}
<br/>
{% endif %}
{% if request.shop.show_dates %}
<div class="product-meta-dates">
Created {{ product.human_created_timestamp }}{% if product.human_created_timestamp != product.human_updated_timestamp %} · Updated {{ product.human_updated_timestamp }}{% endif %}{% if product.human_view_count %} · <span class="product-view-count">{{ product.human_view_count }}</span>{% endif %}
</div>
{% endif %}
{% set cs = product.checksums.get("product", {}) %}
{% if cs.get("sha256") or cs.get("md5") %}
<details class="product-checksums">
<summary>Checksums</summary>
<table class="checksum-table">
{% if cs.get("sha256") %}<tr><td>SHA-256</td><td><code>{{ cs["sha256"] }}</code></td></tr>{% endif %}
{% if cs.get("md5") %}<tr><td>MD5</td><td><code>{{ cs["md5"] }}</code></td></tr>{% endif %}
</table>
</details>
{% endif %}
</div>
<div class="product-comments">
<br/>
<br/>
<!-- Comments Section -->
{% include 'snippets/comments.j2' %}
</div>
</div>
{% if request.shop.watch_mode_enabled %}</div>{% endif %}
<section class="product-right">
{% if product.has_product_file %}
<div class="well">
{% if signed_get_object_url is not none %}
<a href="{{ signed_get_object_url }}" class="product-download-button mps-button" target="_blank" download>&#11123 Download</a>
{% endif %}
{% if torrent_magnet_link %}
<a href="{{ torrent_magnet_link }}" class="mps-button" style="margin-left:.5rem">&#9697; Magnet</a>
{% endif %}
{% if torrent_file_url %}
<a href="{{ torrent_file_url }}" class="mps-button" style="margin-left:.5rem" download>&#8615; .torrent</a>
{% endif %}
</div>
{% endif %}
<div id="watch-queue" class="js-only" style="display:none">
<h4>Queue</h4>
<div id="queue-items"></div>
</div>
{% include 'snippets/related_content.j2' %}
</section>
</section>
<script>
// Replace thumbnail with inline video player
function playInline(container, videoUrl) {
// Create video element
const video = document.createElement('video');
video.src = videoUrl;
video.controls = true;
video.autoplay = true;
video.className = 'product-main';
video.style.maxWidth = '100%';
// Replace container content with video
container.innerHTML = '';
container.onclick = null;
container.appendChild(video);
}
</script>
{% if request.shop.watch_mode_enabled %}
<script src="/static/js/watch.js?v={{ request.git_hash }}"></script>
{% endif %}
<script src="/static/js/signals.js?v={{ request.git_hash }}"></script>
{%- endblock -%}