diff --git a/database.py b/database.py index 81a1583..7701584 100644 --- a/database.py +++ b/database.py @@ -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, diff --git a/serp.py b/serp.py index 25f11a1..b0a3998 100644 --- a/serp.py +++ b/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'{icon}{ext}' + 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'' elif mtype == "code": @@ -889,7 +903,8 @@ def render_detail_page( el = placeholder_svg(ext, "Aa", "#af6") else: el = f'' - media_cards.append(f'''{el}''') + filename_el = f'
{html_module.escape(filename)}
' if filename else "" + media_cards.append(f'''{el}{filename_el}''') media_count = len(media_items) media_grid = '''
@@ -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 => `${k}`).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 `
${mediaEl}
+ ${filename ? `
${filename}
` : ''} ${r.md5_hash}
${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 = ` ${mediaEl}
-
${item.alt_text || item.title || item.md5_hash.slice(0,12)}
+
${filename || item.alt_text || item.title || item.md5_hash.slice(0,12)}
${item.media_type} - ${formatSize(item.file_size)}${scoreBadge}
`;