Fix GIF display: store gif_url separately, render from API field or fallback to URL in content

This commit is contained in:
Your Name 2026-07-22 04:43:56 +03:00
parent 75380bb5ed
commit 203713da2c
4 changed files with 24 additions and 15 deletions

View File

@ -83,6 +83,7 @@ def register_routes(app):
channel = data.get("channel", "general") channel = data.get("channel", "general")
content = data.get("message", "").strip() content = data.get("message", "").strip()
gif_url = data.get("gif_url") or None
if channel not in VALID_CHANNELS: if channel not in VALID_CHANNELS:
response.status = 400 response.status = 400
@ -97,7 +98,7 @@ def register_routes(app):
content = _strip_xss(content) content = _strip_xss(content)
ip_hash = hashlib.sha256((_salt + ip).encode()).hexdigest()[:16] 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} return {"success": True, "message_id": msg_id, "ip": ip_hash}
except Exception as e: except Exception as e:

View File

@ -320,8 +320,10 @@
return; return;
} }
gifGrid.innerHTML = results.map(gif => { gifGrid.innerHTML = results.map(gif => {
const url = gif.media_formats?.tinygif?.url || gif.url; // Use the actual media URL, not the Tenor page URL
return `<img src="${escapeHtml(url)}" alt="gif" data-gif-url="${escapeHtml(gif.url || url)}">`; const thumbUrl = gif.media_formats?.tinygif?.url || gif.media_formats?.gif?.url || gif.url;
const fullUrl = gif.media_formats?.gif?.url || thumbUrl;
return `<img src="${escapeHtml(thumbUrl)}" alt="gif" data-gif-url="${escapeHtml(fullUrl)}">`;
}).join(''); }).join('');
gifGrid.querySelectorAll('img').forEach(img => { gifGrid.querySelectorAll('img').forEach(img => {
@ -395,11 +397,16 @@
let html = ''; let html = '';
msgs.forEach(msg => { msgs.forEach(msg => {
const time = new Date(msg.timestamp).toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }); const time = new Date(msg.timestamp).toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' });
let contentHtml = escapeHtml(msg.content); let contentHtml = escapeHtml(msg.content || '');
// Render inline GIFs from stored <img> tags // Try gif_url from API first, fallback to parsing content for old messages
contentHtml = contentHtml.replace(/&lt;img class=&quot;inline-gif&quot; src=&quot;(.*?)&quot;&gt;/g, (_, url) => { let gifToShow = msg.gif_url || null;
return `<img class="inline-gif" src="${escapeHtml(url)}" alt="gif">`; if (!gifToShow) {
}); const gifMatch = msg.content.match(/https?:\/\/[^\s]+\.(gif|webp)(\?[^\s]*)?/i);
if (gifMatch) gifToShow = gifMatch[0];
}
if (gifToShow) {
contentHtml += `<img class="inline-gif" src="${escapeHtml(gifToShow)}" alt="gif">`;
}
html += `<div class="message"> html += `<div class="message">
<div class="msg-ip">🔑 ${escapeHtml(msg.ip)}</div> <div class="msg-ip">🔑 ${escapeHtml(msg.ip)}</div>
<div class="msg-content">${contentHtml}</div> <div class="msg-content">${contentHtml}</div>
@ -415,18 +422,14 @@
if (!text && !selectedGifUrl) return; if (!text && !selectedGifUrl) return;
let payload = text; let payload = text;
if (selectedGifUrl) { let gif_url = selectedGifUrl || null;
payload = text
? `${text}\n<img class="inline-gif" src="${selectedGifUrl}">`
: `<img class="inline-gif" src="${selectedGifUrl}">`;
}
sendBtn.disabled = true; sendBtn.disabled = true;
try { try {
const r = await fetch('/api/messages', { const r = await fetch('/api/messages', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, 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(); const data = await r.json();
if (data.success) { if (data.success) {

View File

@ -4,6 +4,7 @@ models/base.py — declarative base & the Messages model.
from sqlalchemy import create_engine, Integer, String, Text from sqlalchemy import create_engine, Integer, String, Text
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, sessionmaker from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, sessionmaker
from typing import Optional
class Base(DeclarativeBase): class Base(DeclarativeBase):
@ -17,6 +18,7 @@ class Messages(Base):
channel: Mapped[str] = mapped_column(String(32), nullable=False, default="general") channel: Mapped[str] = mapped_column(String(32), nullable=False, default="general")
ip: Mapped[str] = mapped_column(String(64), nullable=False) ip: Mapped[str] = mapped_column(String(64), nullable=False)
content: Mapped[str] = mapped_column(Text, 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) timestamp: Mapped[int] = mapped_column(Integer, nullable=False)

View File

@ -3,6 +3,7 @@ models/db.py — DBfrontend: encrypted message store.
""" """
import time import time
from typing import Optional
from cryptography.fernet import Fernet from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
@ -33,12 +34,13 @@ class DBfrontend:
except Exception: except Exception:
return "[Decryption failed]" 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: with self.Session() as session:
msg = Messages( msg = Messages(
channel=channel, channel=channel,
ip=ip, ip=ip,
content=self._encrypt(content), content=self._encrypt(content),
gif_url=gif_url,
timestamp=int(time.time() * 1000), timestamp=int(time.time() * 1000),
) )
session.add(msg) session.add(msg)
@ -61,6 +63,7 @@ class DBfrontend:
"channel": msg.channel, "channel": msg.channel,
"ip": msg.ip, "ip": msg.ip,
"content": self._decrypt(msg.content), "content": self._decrypt(msg.content),
"gif_url": msg.gif_url,
"timestamp": msg.timestamp, "timestamp": msg.timestamp,
} }
) )