import re from typing import List from .models import PostSegment def clean_segment_text(text: str) -> str: """Clean up whitespace and trailing artifacts from text.""" lines = [l.strip() for l in text.split("\n")] # Remove empty lines while preserving deliberate paragraph breaks cleaned_lines = [] prev_blank = False for line in lines: if line: cleaned_lines.append(line) prev_blank = False elif not prev_blank: cleaned_lines.append("") prev_blank = True return "\n".join(cleaned_lines).strip() def build_markdown(author_name: str, author_handle: str, canonical_url: str, segments: List[PostSegment]) -> str: """Stitches segments into a single cohesive Markdown document.""" lines = [] title = segments[0].text.split("\n")[0] if segments and segments[0].text else "Threads Post" if len(title) > 80: title = title[:77] + "..." lines.append(f"# {title}") lines.append("") lines.append(f"**Author:** [{author_name} (@{author_handle})](https://www.threads.net/@{author_handle}) ") lines.append(f"**Original Post:** [{canonical_url}]({canonical_url}) ") lines.append(f"**Thread Segments:** {len(segments)}") lines.append("") lines.append("---") lines.append("") for idx, seg in enumerate(segments): cleaned_text = clean_segment_text(seg.text) if not cleaned_text and not seg.images: continue if cleaned_text: lines.append(cleaned_text) lines.append("") # Include media images if any for img in seg.images: alt = img.alt_text or f"Thread Image {idx + 1}" lines.append(f"") lines.append("") lines.append("---") lines.append(f"*Ripped with Thread Ripper from [Threads]({canonical_url})*") return "\n".join(lines).strip() def build_plain_text(author_name: str, author_handle: str, canonical_url: str, segments: List[PostSegment]) -> str: """Stitches segments into clean plain text.""" output = [] output.append(f"{author_name} (@{author_handle})") output.append(f"Source: {canonical_url}") output.append("=" * 60) output.append("") for seg in segments: cleaned_text = clean_segment_text(seg.text) if cleaned_text: output.append(cleaned_text) output.append("") output.append("=" * 60) return "\n".join(output).strip() def build_standalone_html(author_name: str, author_handle: str, author_avatar: str, canonical_url: str, segments: List[PostSegment]) -> str: """Generates an elegant, responsive standalone HTML file.""" title = segments[0].text.split("\n")[0] if segments and segments[0].text else f"Thread by @{author_handle}" if len(title) > 80: title = title[:77] + "..." content_sections = [] for idx, seg in enumerate(segments): cleaned_text = clean_segment_text(seg.text) paragraphs = cleaned_text.split("\n") section_html = "
{p}
" if in_list: section_html += "" # Attached images for img in seg.images: section_html += f"