update-5, update-6
This commit is contained in:
parent
9bd0ab2e72
commit
324cd8a959
26
docs/update-5.md
Normal file
26
docs/update-5.md
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# Обновление 5 — Нормализация URL картинок
|
||||||
|
|
||||||
|
## Проблема
|
||||||
|
|
||||||
|
Клиент Conversations (и его форки) не отображает OOB-картинки, если URL содержит символ `:`
|
||||||
|
в path-части (например, `…/1579780545_0:182:3048:1897_…jpg`).
|
||||||
|
|
||||||
|
При парсинге OOB `<url>` клиент интерпретирует `:` как разделитель схемы или порта
|
||||||
|
и обрезает URL — ссылка становится невалидной, картинка не загружается.
|
||||||
|
|
||||||
|
URL без двоеточий в пути отображаются корректно.
|
||||||
|
|
||||||
|
## Решение
|
||||||
|
|
||||||
|
URL-энкодировать path-часть URL картинки перед сохранением в `NewsItem`.
|
||||||
|
Символ `:` кодируется как `%3A`. Согласно RFC 3986, percent-encoded символы
|
||||||
|
эквивалентны оригинальным — CDN-серверы обязаны принимать такие URL.
|
||||||
|
|
||||||
|
## Техническая реализация
|
||||||
|
|
||||||
|
1. В `adapters/sources/rss/fetcher.py` добавить функцию `_normalize_image_url(url: str) -> str`
|
||||||
|
2. Функция использует `urllib.parse` (стандартная библиотека, без новых зависимостей):
|
||||||
|
- разобрать URL через `urlparse`
|
||||||
|
- перекодировать path через `quote(path, safe='/')` — слэши остаются, `:` кодируется
|
||||||
|
- собрать обратно через `urlunparse`
|
||||||
|
3. Вызывать `_normalize_image_url` в `_extract_image_url` перед возвратом URL
|
||||||
21
docs/update-6.md
Normal file
21
docs/update-6.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Обновление 6 — Убрать картинку из тела сообщения
|
||||||
|
|
||||||
|
## Контекст
|
||||||
|
|
||||||
|
В update-2 картинка добавлялась двумя способами:
|
||||||
|
- в XHTML-теле через `<p><img src="..." /></p>`
|
||||||
|
- в plain-text fallback через URL картинки первым блоком
|
||||||
|
|
||||||
|
В update-4 добавлен OOB (XEP-0066), который обеспечивает отображение картинки
|
||||||
|
в клиентах семейства Conversations. Таким образом, `<img>` и URL в fallback
|
||||||
|
стали избыточными и создают визуальный мусор в клиентах, не рендерящих XHTML.
|
||||||
|
|
||||||
|
## Задача
|
||||||
|
|
||||||
|
Убрать блок картинки из тела сообщения:
|
||||||
|
|
||||||
|
1. В `_build_xhtml` — убрать `<p><img src="..." /></p><br/><br/>`
|
||||||
|
2. В `_build_plain` — убрать добавление `image_url` в список блоков
|
||||||
|
|
||||||
|
Поле `image_url` в `NewsItem` и логика его извлечения в fetcher'е остаются —
|
||||||
|
оно по-прежнему используется для OOB.
|
||||||
@ -22,10 +22,7 @@ class JabberNewsPublisher(NewsPublisher):
|
|||||||
return await self._connection.send_to_room_xhtml(room_jid, plain, xhtml, item.image_url)
|
return await self._connection.send_to_room_xhtml(room_jid, plain, xhtml, item.image_url)
|
||||||
|
|
||||||
def _build_plain(self, item: NewsItem) -> str:
|
def _build_plain(self, item: NewsItem) -> str:
|
||||||
blocks = []
|
blocks = [item.title]
|
||||||
if item.image_url:
|
|
||||||
blocks.append(item.image_url)
|
|
||||||
blocks.append(item.title)
|
|
||||||
if item.summary:
|
if item.summary:
|
||||||
blocks.append(item.summary)
|
blocks.append(item.summary)
|
||||||
blocks.append(item.link)
|
blocks.append(item.link)
|
||||||
@ -33,10 +30,7 @@ class JabberNewsPublisher(NewsPublisher):
|
|||||||
|
|
||||||
def _build_xhtml(self, item: NewsItem) -> str:
|
def _build_xhtml(self, item: NewsItem) -> str:
|
||||||
SEP = "<br/><br/>"
|
SEP = "<br/><br/>"
|
||||||
parts = []
|
parts = [f'<p>{item.title}</p>{SEP}']
|
||||||
if item.image_url:
|
|
||||||
parts.append(f'<p><img src="{item.image_url}" alt="" /></p>{SEP}')
|
|
||||||
parts.append(f'<p>{item.title}</p>{SEP}')
|
|
||||||
if item.summary:
|
if item.summary:
|
||||||
parts.append(f'<p>{item.summary}</p>{SEP}')
|
parts.append(f'<p>{item.summary}</p>{SEP}')
|
||||||
parts.append(f'<p><a href="{item.link}">{item.link}</a></p>')
|
parts.append(f'<p><a href="{item.link}">{item.link}</a></p>')
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import logging
|
|||||||
import re
|
import re
|
||||||
from html import unescape
|
from html import unescape
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
from urllib.parse import quote, urlparse, urlunparse
|
||||||
|
|
||||||
import feedparser
|
import feedparser
|
||||||
|
|
||||||
@ -19,11 +20,18 @@ def _strip_html(text: str) -> str:
|
|||||||
return unescape(text).strip()
|
return unescape(text).strip()
|
||||||
|
|
||||||
|
|
||||||
|
def _normalize_image_url(url: str) -> str:
|
||||||
|
"""URL-энкодирует path-часть, чтобы символы вроде ':' не ломали парсеры клиентов."""
|
||||||
|
p = urlparse(url)
|
||||||
|
return urlunparse(p._replace(path=quote(p.path, safe='/')))
|
||||||
|
|
||||||
|
|
||||||
def _extract_image_url(entry) -> Optional[str]:
|
def _extract_image_url(entry) -> Optional[str]:
|
||||||
"""Возвращает URL первого enclosure с type image/*."""
|
"""Возвращает нормализованный URL первого enclosure с type image/*."""
|
||||||
for enc in entry.get("enclosures", []):
|
for enc in entry.get("enclosures", []):
|
||||||
if enc.get("type", "").startswith("image/"):
|
if enc.get("type", "").startswith("image/"):
|
||||||
return enc.get("href") or enc.get("url")
|
raw = enc.get("href") or enc.get("url")
|
||||||
|
return _normalize_image_url(raw) if raw else None
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user