Shared components for L1 and L2 servers: - Jinja2 template system with base template and components - Middleware for auth and content negotiation - Pydantic models for requests/responses - Utility functions for pagination, media, formatting - Constants for Tailwind/HTMX/Cytoscape CDNs 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
99 lines
3.8 KiB
HTML
99 lines
3.8 KiB
HTML
{#
|
|
Media preview component for videos, images, and audio.
|
|
|
|
Usage:
|
|
{% from "components/media_preview.html" import media_preview, video_player, image_preview, audio_player %}
|
|
|
|
{{ media_preview(content_hash, media_type, title="Preview") }}
|
|
{{ video_player(src="/cache/abc123/mp4", poster="/cache/abc123/thumb") }}
|
|
#}
|
|
|
|
{% macro media_preview(content_hash, media_type, title=None, class="", show_download=True) %}
|
|
<div class="bg-dark-600 rounded-lg overflow-hidden {{ class }}">
|
|
{% if title %}
|
|
<div class="px-4 py-2 border-b border-dark-500">
|
|
<h3 class="text-sm font-medium text-gray-400">{{ title }}</h3>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="aspect-video bg-dark-700 flex items-center justify-center">
|
|
{% if media_type == "video" %}
|
|
{{ video_player("/cache/" + content_hash + "/mp4") }}
|
|
{% elif media_type == "image" %}
|
|
{{ image_preview("/cache/" + content_hash + "/raw") }}
|
|
{% elif media_type == "audio" %}
|
|
{{ audio_player("/cache/" + content_hash + "/raw") }}
|
|
{% else %}
|
|
<div class="text-gray-400 text-center p-4">
|
|
<svg class="w-12 h-12 mx-auto mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
|
|
</svg>
|
|
<p>Preview not available</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
{% if show_download %}
|
|
<div class="px-4 py-2 border-t border-dark-500">
|
|
<a href="/cache/{{ content_hash }}/raw" download
|
|
class="text-blue-400 hover:text-blue-300 text-sm">
|
|
Download original
|
|
</a>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% endmacro %}
|
|
|
|
{% macro video_player(src, poster=None, autoplay=False, muted=True, loop=False, class="") %}
|
|
<video
|
|
class="w-full h-full object-contain {{ class }}"
|
|
controls
|
|
playsinline
|
|
{% if poster %}poster="{{ poster }}"{% endif %}
|
|
{% if autoplay %}autoplay{% endif %}
|
|
{% if muted %}muted{% endif %}
|
|
{% if loop %}loop{% endif %}
|
|
>
|
|
<source src="{{ src }}" type="video/mp4">
|
|
Your browser does not support the video tag.
|
|
</video>
|
|
{% endmacro %}
|
|
|
|
{% macro image_preview(src, alt="", class="") %}
|
|
<img
|
|
src="{{ src }}"
|
|
alt="{{ alt }}"
|
|
class="w-full h-full object-contain {{ class }}"
|
|
loading="lazy"
|
|
>
|
|
{% endmacro %}
|
|
|
|
{% macro audio_player(src, class="") %}
|
|
<div class="w-full px-4 {{ class }}">
|
|
<audio controls class="w-full">
|
|
<source src="{{ src }}">
|
|
Your browser does not support the audio element.
|
|
</audio>
|
|
</div>
|
|
{% endmacro %}
|
|
|
|
{% macro thumbnail(content_hash, media_type, size="w-24 h-24", class="") %}
|
|
<div class="bg-dark-700 rounded {{ size }} flex items-center justify-center overflow-hidden {{ class }}">
|
|
{% if media_type == "image" %}
|
|
<img src="/cache/{{ content_hash }}/raw" alt="" class="w-full h-full object-cover" loading="lazy">
|
|
{% elif media_type == "video" %}
|
|
<svg class="w-8 h-8 text-gray-400" fill="currentColor" viewBox="0 0 24 24">
|
|
<path d="M8 5v14l11-7z"/>
|
|
</svg>
|
|
{% elif media_type == "audio" %}
|
|
<svg class="w-8 h-8 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19V6l12-3v13M9 19c0 1.105-1.343 2-3 2s-3-.895-3-2 1.343-2 3-2 3 .895 3 2zm12-3c0 1.105-1.343 2-3 2s-3-.895-3-2 1.343-2 3-2 3 .895 3 2zM9 10l12-3"/>
|
|
</svg>
|
|
{% else %}
|
|
<svg class="w-8 h-8 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
|
|
</svg>
|
|
{% endif %}
|
|
</div>
|
|
{% endmacro %}
|