From 203713da2cf098703ea45da359015047ce770c66 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 22 Jul 2026 04:43:56 +0300 Subject: [PATCH] Fix GIF display: store gif_url separately, render from API field or fallback to URL in content --- controllers/chat.py | 3 ++- index.html | 29 ++++++++++++++++------------- models/base.py | 2 ++ models/db.py | 5 ++++- 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/controllers/chat.py b/controllers/chat.py index 6477833..4a35de9 100644 --- a/controllers/chat.py +++ b/controllers/chat.py @@ -83,6 +83,7 @@ def register_routes(app): channel = data.get("channel", "general") content = data.get("message", "").strip() + gif_url = data.get("gif_url") or None if channel not in VALID_CHANNELS: response.status = 400 @@ -97,7 +98,7 @@ def register_routes(app): content = _strip_xss(content) ip_hash = hashlib.sha256((_salt + ip).encode()).hexdigest()[:16] - msg_id = _db.add_message(channel, ip_hash, content) + msg_id = _db.add_message(channel, ip_hash, content, gif_url=gif_url) return {"success": True, "message_id": msg_id, "ip": ip_hash} except Exception as e: diff --git a/index.html b/index.html index 4e452bf..1796142 100644 --- a/index.html +++ b/index.html @@ -320,8 +320,10 @@ return; } gifGrid.innerHTML = results.map(gif => { - const url = gif.media_formats?.tinygif?.url || gif.url; - return `gif`; + // Use the actual media URL, not the Tenor page URL + const thumbUrl = gif.media_formats?.tinygif?.url || gif.media_formats?.gif?.url || gif.url; + const fullUrl = gif.media_formats?.gif?.url || thumbUrl; + return `gif`; }).join(''); gifGrid.querySelectorAll('img').forEach(img => { @@ -395,11 +397,16 @@ let html = ''; msgs.forEach(msg => { const time = new Date(msg.timestamp).toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }); - let contentHtml = escapeHtml(msg.content); - // Render inline GIFs from stored tags - contentHtml = contentHtml.replace(/<img class="inline-gif" src="(.*?)">/g, (_, url) => { - return `gif`; - }); + let contentHtml = escapeHtml(msg.content || ''); + // Try gif_url from API first, fallback to parsing content for old messages + let gifToShow = msg.gif_url || null; + if (!gifToShow) { + const gifMatch = msg.content.match(/https?:\/\/[^\s]+\.(gif|webp)(\?[^\s]*)?/i); + if (gifMatch) gifToShow = gifMatch[0]; + } + if (gifToShow) { + contentHtml += `gif`; + } html += `
🔑 ${escapeHtml(msg.ip)}
${contentHtml}
@@ -415,18 +422,14 @@ if (!text && !selectedGifUrl) return; let payload = text; - if (selectedGifUrl) { - payload = text - ? `${text}\n` - : ``; - } + let gif_url = selectedGifUrl || null; sendBtn.disabled = true; try { const r = await fetch('/api/messages', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ channel: currentChannel, message: payload }) + body: JSON.stringify({ channel: currentChannel, message: payload, gif_url: gif_url }) }); const data = await r.json(); if (data.success) { diff --git a/models/base.py b/models/base.py index 9ea29df..28de686 100644 --- a/models/base.py +++ b/models/base.py @@ -4,6 +4,7 @@ models/base.py — declarative base & the Messages model. from sqlalchemy import create_engine, Integer, String, Text from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, sessionmaker +from typing import Optional class Base(DeclarativeBase): @@ -17,6 +18,7 @@ class Messages(Base): channel: Mapped[str] = mapped_column(String(32), nullable=False, default="general") ip: Mapped[str] = mapped_column(String(64), nullable=False) content: Mapped[str] = mapped_column(Text, nullable=False) + gif_url: Mapped[Optional[str]] = mapped_column(Text, nullable=True, default=None) timestamp: Mapped[int] = mapped_column(Integer, nullable=False) diff --git a/models/db.py b/models/db.py index 15fd939..cbf073a 100644 --- a/models/db.py +++ b/models/db.py @@ -3,6 +3,7 @@ models/db.py — DBfrontend: encrypted message store. """ import time +from typing import Optional from cryptography.fernet import Fernet from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC @@ -33,12 +34,13 @@ class DBfrontend: except Exception: return "[Decryption failed]" - def add_message(self, channel: str, ip: str, content: str) -> int: + def add_message(self, channel: str, ip: str, content: str, gif_url: Optional[str] = None) -> int: with self.Session() as session: msg = Messages( channel=channel, ip=ip, content=self._encrypt(content), + gif_url=gif_url, timestamp=int(time.time() * 1000), ) session.add(msg) @@ -61,6 +63,7 @@ class DBfrontend: "channel": msg.channel, "ip": msg.ip, "content": self._decrypt(msg.content), + "gif_url": msg.gif_url, "timestamp": msg.timestamp, } )