diff --git a/src/config.py b/src/config.py
index 31efbcc..979d61a 100644
--- a/src/config.py
+++ b/src/config.py
@@ -55,11 +55,11 @@ class Settings(BaseSettings):
# Media & Video Limits
video_max_size_mb_cloud: int = 49
- video_max_size_mb_local: int = 500
- video_max_duration_sec: int = 600
+ video_max_size_mb_local: int = 2000
+ video_max_duration_sec: int = 7200
video_max_height: int = 720
- media_download_timeout_sec: int = 60
- yt_dlp_timeout_sec: int = 300
+ media_download_timeout_sec: int = 120
+ yt_dlp_timeout_sec: int = 600
# Text Styling & Decoration
header_text: str = ""
diff --git a/src/max_poster.py b/src/max_poster.py
index 833c1c7..542e8e1 100644
--- a/src/max_poster.py
+++ b/src/max_poster.py
@@ -243,15 +243,8 @@ class MAXPoster:
logger.warning("MAX bot token or chat ID is not set; skipping MAX post.")
return [], None
- link_only = [m for m in media_items if m.is_link_only]
- link_text = ""
- if link_only:
- lines = [f"- {m.media_type}: {m.original_url}" for m in link_only]
- link_text = "\n\nМедиа по ссылке:\n" + "\n".join(lines)
-
- full_raw = (raw_text + link_text).strip()
formatted_text = format_post_text(
- full_raw,
+ raw_text,
parse_mode="html",
bold_first_line=settings.format_first_line_bold,
vk_url=vk_url,
diff --git a/src/media_processor.py b/src/media_processor.py
index d55acbf..af1661d 100644
--- a/src/media_processor.py
+++ b/src/media_processor.py
@@ -131,6 +131,7 @@ class MediaProcessor:
f"best[height<={settings.video_max_height}][filesize<{max_size}]"
f"/best[height<={settings.video_max_height}]"
f"/bestvideo[height<={settings.video_max_height}][filesize<{max_size}]+bestaudio/best"
+ f"/bestvideo[height<={settings.video_max_height}]+bestaudio/best"
f"/best[filesize<{max_size}]"
f"/best"
),
diff --git a/src/text_formatter.py b/src/text_formatter.py
index a2e3543..08b5b3e 100644
--- a/src/text_formatter.py
+++ b/src/text_formatter.py
@@ -43,6 +43,35 @@ def strip_trailing_hashtags(text: str) -> str:
return "\n".join(lines).rstrip()
+def clean_vk_wiki_links(text: str, parse_mode: str = "html") -> str:
+ """
+ Converts VK wiki links to clickable links:
+ - [club12345|Name] -> Name
+ - [id12345|Name] -> Name
+ - [public12345|Name] -> Name
+ - [event12345|Name] -> Name
+ - [https://example.com|Title] -> Title
+ """
+ def _replace_vk(match: re.Match) -> str:
+ prefix = match.group(1)
+ obj_id = match.group(2)
+ title = match.group(3)
+ if parse_mode == "html":
+ return f'{html.escape(title)}'
+ return f"[{title}](https://vk.com/{prefix}{obj_id})"
+
+ def _replace_url(match: re.Match) -> str:
+ url = match.group(1)
+ title = match.group(2)
+ if parse_mode == "html":
+ return f'{html.escape(title)}'
+ return f"[{title}]({url})"
+
+ text = re.sub(r"\[(club|id|public|event)(\d+)\|([^\]]+)\]", _replace_vk, text)
+ text = re.sub(r"\[(https?://[^\s\|]+)\|([^\]]+)\]", _replace_url, text)
+ return text
+
+
def format_post_text(
raw_text: str,
*,
@@ -68,31 +97,30 @@ def format_post_text(
formatted_lines: list[str] = []
for idx, line in enumerate(lines):
- if idx == title_idx and bold_first_line and line.strip():
+ cleaned_line = clean_vk_wiki_links(line, parse_mode=parse_mode) if parse_mode == "html" else line
+ if idx == title_idx and bold_first_line and cleaned_line.strip():
if parse_mode == "html":
- escaped = html.escape(line.strip())
- formatted_lines.append(f"{escaped}")
+ # If clean_vk_wiki_links was run, keep existing tags safe
+ formatted_lines.append(f"{cleaned_line.strip()}")
else:
- formatted_lines.append(f"**{line.strip()}**")
+ formatted_lines.append(f"**{cleaned_line.strip()}**")
# Blank line after header if next line is not empty
if idx + 1 < len(lines) and lines[idx + 1].strip() != "":
formatted_lines.append("")
else:
- if parse_mode == "html":
- formatted_lines.append(html.escape(line))
- else:
- formatted_lines.append(line)
+ formatted_lines.append(cleaned_line)
- normalized_body = re.sub(r"\n{3,}", "\n\n", "\n".join(formatted_lines)).strip()
+ raw_body = "\n".join(formatted_lines)
+ normalized_body = re.sub(r"\n{3,}", "\n\n", raw_body).strip()
parts: list[str] = []
if header:
- parts.append(html.escape(header) if parse_mode == "html" else header)
+ parts.append(html.escape(header) if parse_mode == "html" and "<" not in header else header)
if normalized_body:
parts.append(normalized_body)
if footer:
- parts.append(html.escape(footer) if parse_mode == "html" else footer)
+ parts.append(html.escape(footer) if parse_mode == "html" and "<" not in footer else footer)
if tags:
- parts.append(html.escape(tags) if parse_mode == "html" else tags)
+ parts.append(html.escape(tags) if parse_mode == "html" and "<" not in tags else tags)
return "\n\n".join(part for part in parts if part).strip()
diff --git a/src/tg_poster.py b/src/tg_poster.py
index f7e8807..5acbb16 100644
--- a/src/tg_poster.py
+++ b/src/tg_poster.py
@@ -371,16 +371,8 @@ class TelegramPoster:
3. Tries sendRichMessage first.
4. If unavailable, falls back to legacy media groups / single media / text.
"""
- # Append link-only media notice if any videos/photos couldn't be downloaded
- link_only = [m for m in media_items if m.is_link_only]
- link_text = ""
- if link_only:
- lines = [f"- {m.media_type}: ссылка" for m in link_only]
- link_text = "\n\nМедиа по ссылке:\n" + "\n".join(lines)
-
- full_raw = (raw_text + link_text).strip()
formatted_text = format_post_text(
- full_raw,
+ raw_text,
parse_mode="html",
bold_first_line=settings.format_first_line_bold,
vk_url=vk_url,