Ethan Dalool
da5c1ee008
Tags on photos can now have timestamps, so that if you are tagging a video or audio you can reference a specific moment with your tag. In the interface, this means the tag is clickable and seeks to that point in the media. For the user interface, I am finding I need to move away from jinja for the object cards because it is too much hassle to keep the code for jinja-based cards for static rendering and the js-based cards for dynamic rendering in sync. Rather than write the same cards in two languages I can dump the JSON into the script and render the cards on load. Which makes the static HTML worse but that's what the JSON API is for anyway.
187 lines
6.8 KiB
HTML
187 lines
6.8 KiB
HTML
{# ALBUM ######################################################################}
|
|
|
|
{% macro create_album_card(album, view="grid", unlink_parent=none, draggable=false) %}
|
|
{% set id = "album_card_root" if album == "root" else "album_card_" ~ album.id %}
|
|
{% set view = (view if view in ("list", "grid") else "grid") %}
|
|
{% set viewparam = "?view=list" if view == "list" else "" %}
|
|
<div
|
|
id="{{id}}"
|
|
class="album_card album_card_{{view}}"
|
|
data-id="{{'root' if album == 'root' else album.id}}"
|
|
ondragstart="return cards.albums.drag_start(event);"
|
|
ondragend="return cards.albums.drag_end(event);"
|
|
ondragover="return cards.albums.drag_over(event);"
|
|
ondrop="return cards.albums.drag_drop(event);"
|
|
{% if album != "root" and draggable %}
|
|
draggable=true
|
|
{% endif %}
|
|
>
|
|
{% if album == "root" %}
|
|
<a class="album_card_thumbnail" href="/albums{{viewparam}}" draggable="false">
|
|
{% else %}
|
|
<a class="album_card_thumbnail" href="/album/{{album.id}}{{viewparam}}" draggable="false">
|
|
{% endif %}
|
|
{% if album.thumbnail_photo %}
|
|
{% set thumbnail_src = "/photo/" ~ album.thumbnail_photo.id ~ "/thumbnail/" ~ album.thumbnail_photo.id ~ ".jpg" %}
|
|
{% else %}
|
|
{% set thumbnail_src = "/static/basic_thumbnails/album.png" %}
|
|
{% endif %}
|
|
<img src="{{thumbnail_src}}" loading="lazy" draggable="false"/>
|
|
</a>
|
|
|
|
<div class="album_card_title">
|
|
{% if album == "root" %}
|
|
<a href="/albums{{viewparam}}" draggable="false">Albums</a>
|
|
{% else %}
|
|
<a href="/album/{{album.id}}{{viewparam}}" draggable="false">{{album.display_name}}</a>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="album_card_metadata">
|
|
{% if album != "root" %}
|
|
{% set child_count = album.sum_children(recurse=False) %}
|
|
{% set photo_count = album.sum_photos(recurse=False) %}
|
|
<span class="album_card_child_count" title="{{child_count}} child albums">{{child_count}}</span>
|
|
{{-' | '-}}
|
|
<span class="album_card_photo_count" title="{{photo_count}} photos">{{photo_count}}</span>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="album_card_tools">
|
|
{% if unlink_parent is not none %}
|
|
<button
|
|
class="remove_child_button button_with_confirm red_button"
|
|
data-onclick="return api.albums.remove_child('{{unlink_parent.id}}', '{{album.id}}', common.refresh_or_alert);"
|
|
data-prompt="Remove child?"
|
|
data-holder-class="remove_child_button"
|
|
data-confirm-class="red_button"
|
|
data-cancel-class="gray_button"
|
|
>Unlink
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endmacro %}
|
|
|
|
{# PHOTO ######################################################################}
|
|
|
|
{# Priority: specific extensions > specific mimetypes > general mimtypes #}
|
|
{% set thumbnails =
|
|
{
|
|
"svg": "svg",
|
|
|
|
"application/zip": "archive",
|
|
"application/x-tar": "archive",
|
|
|
|
"archive": "archive",
|
|
"audio": "audio",
|
|
"image": "image",
|
|
"video": "video",
|
|
"text": "txt",
|
|
|
|
}
|
|
%}
|
|
{% macro create_photo_card(photo, view="grid") %}
|
|
{% set view = (view if view in ("list", "grid") else "grid") %}
|
|
|
|
{% set metadatas = [] %}
|
|
{% if photo.width %}
|
|
{% do metadatas.append("{w}×{h}".format(w=photo.width, h=photo.height)) %}
|
|
{% endif %}
|
|
{% if photo.duration %}
|
|
{% do metadatas.append("{d}".format(d=photo.duration_string)) %}
|
|
{% endif -%}
|
|
|
|
{% set tag_names_title = photo.get_tag_names()|sort|comma_join %}
|
|
{% set tag_names_inner = "T" if tag_names_title else "" %}
|
|
|
|
<div
|
|
id="photo_card_{{photo.id}}"
|
|
data-id="{{photo.id}}"
|
|
class="photo_card photo_card_{{view}} photo_card_unselected {%if photo.searchhidden%}photo_card_searchhidden{%endif%}"
|
|
ondragstart="return cards.photos.drag_start(event);"
|
|
ondragend="return cards.photos.drag_end(event);"
|
|
ondragover="return cards.photos.drag_over(event);"
|
|
oncontextmenu="return cards.photos.photo_rightclick(event);"
|
|
ondrop="return cards.photos.drag_drop(event);"
|
|
draggable="true"
|
|
>
|
|
<div class="photo_card_filename">
|
|
<a target="_blank" href="/photo/{{photo.id}}" draggable="false">{{photo.basename}}</a>
|
|
</div>
|
|
|
|
<span class="photo_card_metadata">
|
|
{{- metadatas|join_and_trail(", ")|safe -}}
|
|
<a target="_blank" href="{{photo|file_link}}" draggable="false">{{photo.bytes_string}}</a>
|
|
</span>
|
|
|
|
{% if view == "grid" %}
|
|
{% if photo.has_thumbnail() %}
|
|
{% set thumbnail_src = "/photo/" ~ photo.id ~ "/thumbnail/" ~ photo.id ~ ".jpg" %}
|
|
{% else %}
|
|
{% set thumbnail_src =
|
|
thumbnails.get(photo.extension, "") or
|
|
thumbnails.get(photo.mimetype, "") or
|
|
thumbnails.get(photo.simple_mimetype, "") or
|
|
"other"
|
|
%}
|
|
{% set thumbnail_src = "/static/basic_thumbnails/" ~ thumbnail_src ~ ".png" %}
|
|
{% endif -%}{# if thumbnail #}
|
|
|
|
<a class="photo_card_thumbnail" target="_blank" href="/photo/{{photo.id}}" draggable="false">
|
|
<img loading="lazy" src="{{thumbnail_src}}" draggable="false">
|
|
</a>
|
|
{% endif %}{# if grid #}
|
|
|
|
<span class="photo_card_tags" title="{{tag_names_title}}">{{tag_names_inner}}</span>
|
|
</div>
|
|
|
|
{% endmacro %}
|
|
|
|
{# TAG ########################################################################}
|
|
|
|
<!--
|
|
tag: The Tag object
|
|
extra_classes:
|
|
Space-separated string, if you want more than "tag_card".
|
|
innertext:
|
|
A string to use as the innertext.
|
|
Otherwise, will use the name based on the other parameters.
|
|
link:
|
|
None = no link, just a <span>
|
|
'search' = link to /search?tag_musts=tagname
|
|
'search_musts' = link to /search?tag_musts=tagname
|
|
'search_mays' = link to /search?tag_mays=tagname
|
|
'search_forbids' = link to /search?tag_forbids=tagname
|
|
'info' = link to /tags/tagname
|
|
with_alt_description:
|
|
True: Include the description in the alt text
|
|
-->
|
|
{%- macro create_tag_card(
|
|
tag,
|
|
photo_tag_rel_id=None,
|
|
extra_classes="",
|
|
innertext=None,
|
|
innertext_safe=None,
|
|
link='info',
|
|
onclick=None,
|
|
with_alt_description=False
|
|
) -%}
|
|
|
|
{%- set href = {
|
|
"search": "/search?tag_musts=" ~ (tag.name|urlencode),
|
|
"search_musts": "/search?tag_musts=" ~ (tag.name|urlencode),
|
|
"search_mays": "/search?tag_mays=" ~ (tag.name|urlencode),
|
|
"search_forbids": "/search?tag_forbids=" ~ (tag.name|urlencode),
|
|
"info": "/tag/" ~ tag.name,
|
|
None: None,
|
|
}.get(link, link)
|
|
-%}
|
|
{%- set class = ("tag_card" ~ " " ~ extra_classes).strip() -%}
|
|
{%- set title = (with_alt_description and tag.description) or None -%}
|
|
{%- set innertext = innertext_safe or (innertext or tag.name)|e -%}
|
|
{%- set element = "a" if (link or onclick) else "span" -%}
|
|
|
|
<{{element}} {{make_attributes(data_id=photo_tag_rel_id, class=class, title=title, href=href, onclick=onclick, **kwargs)|safe}}>{{innertext|safe}}</{{element}}>
|
|
{{-''-}}
|
|
{%- endmacro -%}
|