Add full integration for the Nextcloud News (RSS/Atom reader) app: - Add NewsClient with complete CRUD operations for folders, feeds, and items - Add 8 read-only MCP tools for listing/getting folders, feeds, items - Add Pydantic models for News entities with camelCase alias support - Add vector sync support for starred + unread items - Add HTML to Markdown converter using markdownify for better embeddings - Add Docker post-install hook to enable News app - Add 25 unit tests for NewsClient API methods Vector sync indexes starred and unread items, providing a balanced approach that captures important (starred) and current (unread) content without indexing the entire article history. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
50 lines
1.6 KiB
Python
50 lines
1.6 KiB
Python
"""HTML to Markdown conversion utilities for vector sync."""
|
|
|
|
import logging
|
|
|
|
from markdownify import markdownify as md
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def html_to_markdown(html_content: str | None) -> str:
|
|
"""Convert HTML content to Markdown, preserving semantic structure.
|
|
|
|
This function converts HTML (typically from RSS/Atom feed items) to Markdown
|
|
for better text embedding. Markdown preserves:
|
|
- Heading hierarchy (important for document structure)
|
|
- Lists (bullet and numbered)
|
|
- Links (as [text](url))
|
|
- Bold/italic emphasis
|
|
- Paragraphs and line breaks
|
|
|
|
Args:
|
|
html_content: HTML string to convert (may be None or empty)
|
|
|
|
Returns:
|
|
Markdown string, or empty string if input is None/empty
|
|
|
|
Example:
|
|
>>> html_to_markdown("<h1>Title</h1><p>Content with <b>bold</b>.</p>")
|
|
'# Title\\n\\nContent with **bold**.\\n\\n'
|
|
"""
|
|
if not html_content:
|
|
return ""
|
|
|
|
try:
|
|
markdown = md(
|
|
html_content,
|
|
heading_style="ATX", # Use # style headings
|
|
strip=["script", "style", "iframe", "noscript"], # Remove unsafe elements
|
|
bullets="-", # Use - for unordered lists
|
|
code_language="", # Don't add language hints to code blocks
|
|
)
|
|
return markdown.strip()
|
|
except Exception as e:
|
|
logger.warning(f"Failed to convert HTML to Markdown: {e}")
|
|
# Fallback: strip all HTML tags as a last resort
|
|
import re
|
|
|
|
text = re.sub(r"<[^>]+>", " ", html_content)
|
|
return " ".join(text.split()) # Normalize whitespace
|