5.1 KiB · text History c20d4f7
{% extends "root.html" %}

{% block inner %}
<div class="space-y-6 text-left">
  <div class="flex justify-between items-center mb-4">
    <h2 class="text-2xl font-bold text-accent">Uploads ({{ total }})</h2>
    <a href="/uploads/new" class="btn btn-primary btn-sm">New upload</a>
  </div>

  {% if error %}
  <div class="alert alert-error">
    <i class="fas fa-circle-exclamation"></i>
    <span>{{ error }}</span>
  </div>
  {% endif %}
  {% if notice %}
  <div class="alert alert-info">
    <i class="fas fa-circle-info"></i>
    <span>{{ notice }}</span>
  </div>
  {% endif %}

  <form method="get" action="/uploads" class="flex flex-wrap gap-2 items-end">
    <label class="flex flex-col">
      <span class="text-xs">Search</span>
      <input class="input input-sm" type="text" name="q" value="{{ filter_q }}" placeholder="filename / description"/>
    </label>
    <label class="flex flex-col">
      <span class="text-xs">Tags</span>
      <input class="input input-sm" type="text" name="tags" value="{{ filter_tags }}" placeholder="desc:dog Series:Friends"/>
    </label>
    <label class="flex flex-col">
      <span class="text-xs">Namespace</span>
      <input class="input input-sm" type="text" name="namespace" value="{{ filter_namespace }}" placeholder="desc"/>
    </label>
    <button class="btn btn-sm btn-secondary" type="submit">Filter</button>
  </form>

  <form id="quick-upload-form" method="post" action="/uploads" enctype="multipart/form-data" class="card bg-base-200 p-4 space-y-2">
    <div class="flex items-center justify-between">
      <h3 class="font-bold">Quick upload</h3>
      <span class="text-xs opacity-60">
        Paste an image (<kbd class="kbd kbd-xs">Ctrl</kbd>+<kbd class="kbd kbd-xs">V</kbd>) to attach it
      </span>
    </div>
    <input id="quick-upload-file" class="file-input file-input-sm w-full" type="file" name="file" accept="image/*" data-max-bytes="{{ max_upload_bytes }}" required/>
    <div id="quick-upload-preview" class="hidden items-center gap-2 text-xs">
      <img class="h-16 w-16 object-cover rounded-sm border border-base-300" alt="pasted preview"/>
      <span class="opacity-70" data-name></span>
    </div>
    <input class="input input-sm w-full" type="text" name="item_name" placeholder="Optional name"/>
    <textarea class="textarea textarea-sm w-full" name="item_description" placeholder="Optional description"></textarea>
    <input class="input input-sm w-full" type="text" name="tags" placeholder="user:keep Series:Friends"/>
    <button class="btn btn-primary btn-sm" type="submit">Upload</button>
  </form>

  <form method="post" action="/uploads/zip" enctype="multipart/form-data" class="card bg-base-200 p-4 space-y-2">
    <div class="flex items-center justify-between">
      <h3 class="font-bold">Bulk upload (zip)</h3>
      <span class="text-xs opacity-60">Every image inside is ingested and processed</span>
    </div>
    <input class="file-input file-input-sm w-full" type="file" name="file" accept=".zip,application/zip,application/x-zip-compressed" data-max-bytes="{{ max_upload_bytes }}" required/>
    <textarea class="textarea textarea-sm w-full" name="item_description" placeholder="Optional description — applied to every image"></textarea>
    <input class="input input-sm w-full" type="text" name="tags" placeholder="Optional tags — applied to every image"/>
    <button class="btn btn-primary btn-sm" type="submit">Upload zip</button>
  </form>

  {% if uploads|length == 0 %}
    <p class="opacity-70">No uploads matched.</p>
  {% else %}
    <div class="grid grid-cols-2 md:grid-cols-4 gap-3">
      {% for u in uploads %}
        <a href="/uploads/{{ u.id }}" class="card bg-base-200 hover:bg-base-300 p-2">
          {% if u.content_type == "image/gif" or u.content_type is startingwith("video/") %}
            {# Anything that moves shows the `preview` worker's short animated WebP.
               Until that exists, fall back to the still — the original GIF, or the
               frame the `poster` worker extracted — and failing that, a label. #}
            <img class="w-full h-32 object-cover rounded-sm" loading="lazy"
                 src="/files/{{ u.item_path | derived("preview", "webp") }}"
                 data-fallback-src="/files/{% if u.content_type == "image/gif" %}{{ u.item_path }}{% else %}{{ u.item_path | derived("poster", "jpg") }}{% endif %}"
                 alt="{{ u.file_name_original }}" data-fallback="{% if u.content_type == "image/gif" %}gif{% else %}video{% endif %}"/>
          {% elif u.content_type is startingwith("image/") %}
            <img class="w-full h-32 object-cover rounded-sm" src="/files/{{ u.item_path }}" alt="{{ u.file_name_original }}"/>
          {% else %}
            <div class="w-full h-32 flex items-center justify-center bg-base-300 rounded-sm text-xs">
              {{ u.content_type or "binary" }}
            </div>
          {% endif %}
          <div class="text-xs mt-1 truncate">{{ u.item_name or u.file_name_original }}</div>
          <div class="text-[10px] opacity-60">{{ u.processing_status }}</div>
        </a>
      {% endfor %}
    </div>
  {% endif %}
</div>
{% endblock %}