Refactor to use IPFS CID as the primary content identifier: - Update database schema: content_hash -> cid, output_hash -> output_cid - Update all services, routers, and tasks to use cid terminology - Update HTML templates to display CID instead of hash - Update cache_manager parameter names - Update README documentation This completes the transition to CID-only content addressing. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
116 lines
5.2 KiB
HTML
116 lines
5.2 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Media - Art-DAG L1{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="max-w-6xl mx-auto">
|
|
<div class="flex items-center justify-between mb-6">
|
|
<h1 class="text-3xl font-bold">Media</h1>
|
|
<div class="flex items-center space-x-4">
|
|
<select id="type-filter" onchange="filterMedia()"
|
|
class="bg-gray-800 border border-gray-600 rounded px-3 py-2 text-white">
|
|
<option value="">All Types</option>
|
|
<option value="image">Images</option>
|
|
<option value="video">Videos</option>
|
|
<option value="audio">Audio</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
{% if items %}
|
|
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4" id="media-grid">
|
|
{% for item in items %}
|
|
{# Determine media category from type or filename #}
|
|
{% set is_image = item.type in ('image', 'image/jpeg', 'image/png', 'image/gif', 'image/webp') or (item.filename and item.filename.lower().endswith(('.jpg', '.jpeg', '.png', '.gif', '.webp'))) %}
|
|
{% set is_video = item.type in ('video', 'video/mp4', 'video/webm', 'video/x-matroska') or (item.filename and item.filename.lower().endswith(('.mp4', '.mkv', '.webm', '.mov'))) %}
|
|
{% set is_audio = item.type in ('audio', 'audio/mpeg', 'audio/wav', 'audio/flac') or (item.filename and item.filename.lower().endswith(('.mp3', '.wav', '.flac', '.ogg'))) %}
|
|
|
|
<a href="/cache/{{ item.cid }}"
|
|
class="media-item bg-gray-800 rounded-lg overflow-hidden hover:ring-2 hover:ring-blue-500 transition-all"
|
|
data-type="{% if is_image %}image{% elif is_video %}video{% elif is_audio %}audio{% else %}other{% endif %}">
|
|
|
|
{% if is_image %}
|
|
<img src="/cache/{{ item.cid }}/raw"
|
|
alt=""
|
|
loading="lazy"
|
|
class="w-full h-40 object-cover">
|
|
|
|
{% elif is_video %}
|
|
<div class="relative">
|
|
<video src="/cache/{{ item.cid }}/raw"
|
|
class="w-full h-40 object-cover"
|
|
muted
|
|
onmouseover="this.play()"
|
|
onmouseout="this.pause(); this.currentTime=0;">
|
|
</video>
|
|
<div class="absolute inset-0 flex items-center justify-center pointer-events-none">
|
|
<div class="bg-black bg-opacity-50 rounded-full p-2">
|
|
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20">
|
|
<path d="M6.3 2.841A1.5 1.5 0 004 4.11V15.89a1.5 1.5 0 002.3 1.269l9.344-5.89a1.5 1.5 0 000-2.538L6.3 2.84z"/>
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% elif is_audio %}
|
|
<div class="w-full h-40 bg-gray-900 flex flex-col items-center justify-center">
|
|
<svg class="w-12 h-12 text-gray-600 mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"
|
|
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>
|
|
<span class="text-gray-500 text-sm">Audio</span>
|
|
</div>
|
|
|
|
{% else %}
|
|
<div class="w-full h-40 bg-gray-900 flex items-center justify-center">
|
|
<span class="text-gray-600 text-sm">{{ item.type or 'Media' }}</span>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="p-3">
|
|
<div class="font-mono text-xs text-gray-500 truncate">{{ item.cid[:16] }}...</div>
|
|
{% if item.filename %}
|
|
<div class="text-xs text-gray-600 truncate">{{ item.filename }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</a>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
{% if has_more %}
|
|
<div hx-get="/media?offset={{ offset + limit }}"
|
|
hx-trigger="revealed"
|
|
hx-swap="beforeend"
|
|
hx-target="#media-grid"
|
|
hx-select=".media-item"
|
|
class="h-20 flex items-center justify-center text-gray-500 mt-4">
|
|
Loading more...
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% else %}
|
|
<div class="bg-gray-800 border border-gray-700 rounded-lg p-12 text-center">
|
|
<svg class="w-16 h-16 mx-auto mb-4 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"
|
|
d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"/>
|
|
</svg>
|
|
<p class="text-gray-500 mb-4">No media files yet</p>
|
|
<p class="text-gray-600 text-sm">Run a recipe to generate media artifacts.</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<script>
|
|
function filterMedia() {
|
|
const filter = document.getElementById('type-filter').value;
|
|
document.querySelectorAll('.media-item').forEach(item => {
|
|
if (!filter || item.dataset.type === filter) {
|
|
item.classList.remove('hidden');
|
|
} else {
|
|
item.classList.add('hidden');
|
|
}
|
|
});
|
|
}
|
|
</script>
|
|
{% endblock %}
|