from collections.abc import Awaitable, Callable from .linkify import tokenize from .own_links import parse_share_link # Each resolved link costs one backend lookup, so a single document stays bounded. MAX_LOOKUPS = 20 def _media_kind(content_type: str) -> str: value = (content_type or '').split(';')[0].strip().lower() for prefix in ('image', 'video', 'audio'): if value.startswith(prefix + '/'): return prefix if 'pdf' in value: return 'pdf' return 'other' async def build_segments( text: str, host: str, root_path: str, lookup: Callable[[str], Awaitable[dict | None]] | None = None, max_lookups: int = MAX_LOOKUPS, ) -> list[dict]: """Turn raw text into template segments: plain text, links and resolved own files.""" segments: list[dict] = [] resolved: dict[str, dict | None] = {} for kind, value in tokenize(text): if kind == 'text': segments.append({'kind': 'text', 'text': value}) continue file_id = parse_share_link(value, host=host, root_path=root_path) if lookup else None if file_id is None: segments.append({'kind': 'link', 'url': value}) continue if file_id not in resolved: if len(resolved) >= max_lookups: segments.append({'kind': 'link', 'url': value}) continue resolved[file_id] = await lookup(file_id) file_data = resolved[file_id] if not file_data: segments.append({'kind': 'link', 'url': value, 'state': 'missing'}) continue if file_data.get('expired'): segments.append({ 'kind': 'link', 'url': value, 'state': 'expired', 'label': file_data.get('file_name') or file_id, }) continue segments.append({ 'kind': 'file', 'url': value, 'state': 'ok', 'file_id': file_id, 'label': file_data.get('file_name') or file_id, 'media': _media_kind(file_data.get('content_type') or ''), }) return segments