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")
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:

View File

@ -320,8 +320,10 @@
return;
}
gifGrid.innerHTML = results.map(gif => {
const url = gif.media_formats?.tinygif?.url || gif.url;
return `<img src="${escapeHtml(url)}" alt="gif" data-gif-url="${escapeHtml(gif.url || url)}">`;
// 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 `<img src="${escapeHtml(thumbUrl)}" alt="gif" data-gif-url="${escapeHtml(fullUrl)}">`;
}).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 <img> tags
contentHtml = contentHtml.replace(/&lt;img class=&quot;inline-gif&quot; src=&quot;(.*?)&quot;&gt;/g, (_, url) => {
return `<img class="inline-gif" src="${escapeHtml(url)}" alt="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 += `<img class="inline-gif" src="${escapeHtml(gifToShow)}" alt="gif">`;
}
html += `<div class="message">
<div class="msg-ip">🔑 ${escapeHtml(msg.ip)}</div>
<div class="msg-content">${contentHtml}</div>
@ -415,18 +422,14 @@
if (!text && !selectedGifUrl) return;
let payload = text;
if (selectedGifUrl) {
payload = text
? `${text}\n<img class="inline-gif" src="${selectedGifUrl}">`
: `<img class="inline-gif" src="${selectedGifUrl}">`;
}
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) {

View File

@ -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)

View File

@ -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,
}
)