Show original filename under media thumbnails
- database.py: Add media_uri to get_page_media and search queries - View gallery: Show filename under each thumbnail - Search results: Show filename above hash - Live feed: Show filename as card title
This commit is contained in:
parent
aa49612f38
commit
39218893bf
2 changed files with 45 additions and 5 deletions
|
|
@ -956,6 +956,7 @@ class Database:
|
|||
first_source = (
|
||||
select(
|
||||
MediaSource.md5_hash,
|
||||
func.min(MediaSource.media_uri).label('media_uri'),
|
||||
func.min(MediaSource.page_uri).label('page_uri'),
|
||||
func.min(MediaSource.page_title).label('page_title')
|
||||
)
|
||||
|
|
@ -967,7 +968,7 @@ class Database:
|
|||
select(Media.md5_hash, Media.media_type, Media.mime_type, Media.file_size,
|
||||
Media.alt_text, Media.title, Media.first_seen_at,
|
||||
Media.score, Media.upgraded_at, Media.keywords,
|
||||
first_source.c.page_uri, first_source.c.page_title)
|
||||
first_source.c.media_uri, first_source.c.page_uri, first_source.c.page_title)
|
||||
.join(matching_hashes, Media.md5_hash == matching_hashes.c.md5_hash)
|
||||
.outerjoin(first_source, Media.md5_hash == first_source.c.md5_hash)
|
||||
.order_by(
|
||||
|
|
@ -1103,7 +1104,7 @@ class Database:
|
|||
"""Get media items from a specific page (excluding screenshots)."""
|
||||
async with self.session() as session:
|
||||
stmt = (
|
||||
select(Media.md5_hash, Media.media_type, Media.alt_text, Media.file_size)
|
||||
select(Media.md5_hash, Media.media_type, Media.mime_type, Media.alt_text, Media.file_size, MediaSource.media_uri)
|
||||
.join(MediaSource, Media.md5_hash == MediaSource.md5_hash)
|
||||
.where(and_(
|
||||
MediaSource.page_uri == page_uri,
|
||||
|
|
|
|||
45
serp.py
45
serp.py
|
|
@ -811,10 +811,15 @@ h3 { color: #ff6b6b; font-size: 16px; margin: 30px 0 15px 0; border-bottom: 1px
|
|||
overflow: hidden; display: block; transition: transform 0.2s;
|
||||
}
|
||||
.media-card:hover { transform: scale(1.02); }
|
||||
.media-card img, .media-card video {
|
||||
.media-card img, .media-card video, .media-card svg {
|
||||
width: 100%; height: 120px;
|
||||
object-fit: contain; background: #0a0a0a;
|
||||
}
|
||||
.media-card-name {
|
||||
padding: 4px 8px; font-size: 11px; color: #888;
|
||||
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
||||
background: #111; text-align: center;
|
||||
}
|
||||
.tag { background: #333; padding: 2px 8px; border-radius: 4px; font-size: 12px; margin-right: 5px; }
|
||||
"""
|
||||
|
||||
|
|
@ -873,9 +878,18 @@ def render_detail_page(
|
|||
}
|
||||
def placeholder_svg(ext, icon, color):
|
||||
return f'<svg viewBox="0 0 100 100" style="width:100%;height:100%;background:#1a1a1a;border-radius:4px;"><text x="50" y="40" text-anchor="middle" fill="{color}" font-size="24">{icon}</text><text x="50" y="65" text-anchor="middle" fill="#888" font-family="monospace" font-size="14" font-weight="bold">{ext}</text></svg>'
|
||||
def get_filename(uri):
|
||||
if not uri: return ""
|
||||
# Extract filename from URI path
|
||||
from urllib.parse import urlparse, unquote
|
||||
path = urlparse(uri).path
|
||||
name = unquote(path.split("/")[-1]) if path else ""
|
||||
# Truncate long names
|
||||
return name[:30] + "..." if len(name) > 33 else name
|
||||
for m in media_items:
|
||||
mtype = m.get("media_type", "")
|
||||
mime = m.get("mime_type", "")
|
||||
filename = get_filename(m.get("media_uri", ""))
|
||||
if mtype == "video":
|
||||
el = f'<video src="/media/{m["md5_hash"]}" muted loop preload="metadata" onmouseenter="this.play()" onmouseleave="this.pause()"></video>'
|
||||
elif mtype == "code":
|
||||
|
|
@ -889,7 +903,8 @@ def render_detail_page(
|
|||
el = placeholder_svg(ext, "Aa", "#af6")
|
||||
else:
|
||||
el = f'<img src="/media/{m["md5_hash"]}" loading="lazy">'
|
||||
media_cards.append(f'''<a href="/view/{m["md5_hash"]}" class="media-card">{el}</a>''')
|
||||
filename_el = f'<div class="media-card-name">{html_module.escape(filename)}</div>' if filename else ""
|
||||
media_cards.append(f'''<a href="/view/{m["md5_hash"]}" class="media-card">{el}{filename_el}</a>''')
|
||||
media_count = len(media_items)
|
||||
media_grid = '''
|
||||
<div class="page-media">
|
||||
|
|
@ -1270,6 +1285,7 @@ h1 { color: #ff6b6b; margin-bottom: 5px; }
|
|||
.result:hover { transform: scale(1.02); }
|
||||
.result img, .result video { width: 100%; height: 200px; object-fit: contain; background: #1a1a1a; }
|
||||
.result-info { padding: 10px; }
|
||||
.result-filename { font-size: 12px; color: #aaa; margin-bottom: 4px; word-break: break-all; }
|
||||
.result-hash { font-family: monospace; font-size: 11px; color: #ff6b6b; text-decoration: none; word-break: break-all; }
|
||||
.result-hash:hover { text-decoration: underline; }
|
||||
.result-meta { font-size: 12px; color: #888; margin-top: 5px; }
|
||||
|
|
@ -1439,12 +1455,24 @@ SEARCH_CONTENT = """
|
|||
const keywords = JSON.parse(r.keywords || '[]');
|
||||
const tagsHtml = keywords.map(k => `<span class="tag">${k}</span>`).join('');
|
||||
|
||||
// Extract filename from media_uri
|
||||
const getFilename = (uri) => {
|
||||
if (!uri) return '';
|
||||
try {
|
||||
const path = new URL(uri).pathname;
|
||||
const name = decodeURIComponent(path.split('/').pop() || '');
|
||||
return name.length > 40 ? name.slice(0, 37) + '...' : name;
|
||||
} catch { return ''; }
|
||||
};
|
||||
const filename = getFilename(r.media_uri);
|
||||
|
||||
return `
|
||||
<div class="result">
|
||||
<a href="/view/${r.md5_hash}" class="media-link">
|
||||
${mediaEl}
|
||||
</a>
|
||||
<div class="result-info">
|
||||
${filename ? `<div class="result-filename">${filename}</div>` : ''}
|
||||
<a href="/view/${r.md5_hash}" class="result-hash">${r.md5_hash}</a>
|
||||
<div class="result-meta">
|
||||
${r.media_type} · ${formatBytes(r.file_size)}
|
||||
|
|
@ -2453,12 +2481,23 @@ LIVE_HTML = """
|
|||
|
||||
const scoreBadge = getScoreBadge(item.score, item.media_type);
|
||||
|
||||
// Extract filename from media_uri
|
||||
const getFilename = (uri) => {
|
||||
if (!uri) return '';
|
||||
try {
|
||||
const path = new URL(uri).pathname;
|
||||
const name = decodeURIComponent(path.split('/').pop() || '');
|
||||
return name.length > 35 ? name.slice(0, 32) + '...' : name;
|
||||
} catch { return ''; }
|
||||
};
|
||||
const filename = getFilename(item.media_uri);
|
||||
|
||||
card.innerHTML = `
|
||||
<a href="/view/${item.md5_hash}">
|
||||
${mediaEl}
|
||||
</a>
|
||||
<div class="live-card-info">
|
||||
<div class="live-card-title">${item.alt_text || item.title || item.md5_hash.slice(0,12)}</div>
|
||||
<div class="live-card-title">${filename || item.alt_text || item.title || item.md5_hash.slice(0,12)}</div>
|
||||
<div class="live-card-source">${item.media_type} - ${formatSize(item.file_size)}${scoreBadge}</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue