Skip to content

feat: fix ios aspect ratio#86

Merged
aturret merged 4 commits into
mainfrom
fix-ios-aspect-ratio
May 29, 2026
Merged

feat: fix ios aspect ratio#86
aturret merged 4 commits into
mainfrom
fix-ios-aspect-ratio

Conversation

@aturret
Copy link
Copy Markdown
Owner

@aturret aturret commented May 29, 2026

Summary by CodeRabbit

  • New Features

    • Video files now capture and store metadata (width, height, and duration) during media processing for improved video handling.
  • Documentation

    • Updated architectural documentation with future media asset caching plans.
  • Chores

    • Added FFmpeg to the deployment environment to support video processing operations.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 29, 2026

Warning

Review limit reached

@aturret, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 46 minutes and 35 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bb7ba84b-6b74-4e3b-af77-d01d768511dd

📥 Commits

Reviewing files that changed from the base of the PR and between 3d25d0d and ecfd66e.

📒 Files selected for processing (2)
  • apps/telegram-bot/core/services/video_metadata.py
  • tests/unit/telegram_bot/test_message_sender.py
📝 Walkthrough

Walkthrough

This PR introduces comprehensive video metadata extraction and persistence across the FastFetchBot pipeline. It adds utilities for positive integer validation, extends the MediaFile model with dimensions and duration, enhances bot-side capture to forward video metadata into Redis, implements async ffprobe-based probing in message composition, and updates the worker to persist video metadata to MongoDB.

Changes

Video Metadata Capture and Persistence

Layer / File(s) Summary
Core utilities for positive integer validation
packages/shared/fastfetchbot_shared/utils/number.py, tests/unit/test_number.py
Introduces positive_int(value) helper that safely coerces inputs to strictly positive integers or None, handling float conversion, rounding, boolean rejection, and type errors.
Shared MediaFile extensions for video dimensions
packages/shared/fastfetchbot_shared/models/metadata_item.py, tests/unit/database/mongodb/test_file_id_fields.py
Dataclass MediaFile gains optional width, height, and duration fields; from_dict and to_dict methods updated to preserve video dimensions during serialization and deserialization.
Bot-side file_id capture with video metadata extraction
apps/telegram-bot/core/services/file_id_capture.py, tests/unit/telegram_bot/test_file_id_capture.py
New _video_metadata_for_update() helper extracts positive integer video fields from scraped media info; capture_and_push_file_ids() augments Redis payloads with extracted width/height/duration for video messages.
Bot-side video metadata probing and media composition
apps/telegram-bot/core/services/video_metadata.py, apps/telegram-bot/core/services/message_sender.py, tests/unit/telegram_bot/test_message_sender.py
New module implements async ffprobe-based probing with concurrent semaphore limiting, rotation normalization, and dimension swapping; message_sender.py now calls ensure_video_metadata() on uncached videos and uses video_input_kwargs() to attach metadata to both cached and uncached InputMediaVideo objects. Message composition refactored to populate uncached_media_info with derived video dimensions.
Worker-side video metadata persistence to MongoDB
apps/async-worker/async_worker/services/file_id_consumer.py, tests/unit/async_worker/test_file_id_consumer.py
_process_file_id_update() now reads video metadata fields from Redis update payloads and persists width/height/duration to MongoDB MediaFile documents using positive_int validation; increments match counter when any video field changes.
Infrastructure and documentation updates
apps/telegram-bot/Dockerfile, AGENTS.md, .gitignore
Dockerfile updated to install ffmpeg in both builder-base and production stages; AGENTS.md documents future architecture for moving media processing to worker layers; .gitignore preserves /apps/api/conf/.gitkeep and excludes JetBrains IDE files.

Sequence Diagram(s)

sequenceDiagram
  participant MessageSender
  participant VideoMetadata
  participant Ffprobe
  participant InputMediaVideo
  MessageSender->>VideoMetadata: ensure_video_metadata(media_item, io_object)
  VideoMetadata->>VideoMetadata: check ffprobe available
  VideoMetadata->>Ffprobe: async subprocess probe
  Ffprobe-->>VideoMetadata: width, height, duration, rotation
  VideoMetadata->>VideoMetadata: swap dims if rotation 90°
  VideoMetadata-->>MessageSender: populated media_item
  MessageSender->>InputMediaVideo: build with video_input_kwargs(media_item)
  InputMediaVideo-->>MessageSender: InputMediaVideo with supports_streaming=True
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • aturret/FastFetchBot#74: Related PR that extends the file_id update pipeline by modifying file_id_consumer.py and file_id_capture.py to persist video metadata alongside telegram_file_id.
  • aturret/FastFetchBot#72: Related PR that modifies message_sender.py media packaging flow, specifically wrapping video/image downloads with per-item error handling that complements this metadata integration.

Poem

🐰 A rabbit hops through metadata streams,
Capturing video width and dreams,
FFprobe whispers dimension and rotation,
MongoDB stores this grand foundation,
From bot to worker, dimensions flow free!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title 'fix ios aspect ratio' is vague and does not accurately reflect the extensive changes made throughout the codebase, which include video metadata probing, caching infrastructure, dimension tracking, and MongoDB schema updates. Use a more descriptive title that captures the main architectural change, such as 'feat: add video metadata extraction and caching pipeline' or 'feat: implement video dimension probing and storage across layers'.
Docstring Coverage ⚠️ Warning Docstring coverage is 31.03% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-ios-aspect-ratio

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 29, 2026

Codecov Report

❌ Patch coverage is 83.92857% with 27 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
apps/telegram-bot/core/services/video_metadata.py 76.34% 22 Missing ⚠️
apps/telegram-bot/core/services/message_sender.py 71.42% 4 Missing ⚠️
...c-worker/async_worker/services/file_id_consumer.py 95.00% 1 Missing ⚠️

Impacted file tree graph

@@           Coverage Diff           @@
##             main      #86   +/-   ##
=======================================
  Coverage        ?   81.13%           
=======================================
  Files           ?       68           
  Lines           ?     3785           
  Branches        ?        0           
=======================================
  Hits            ?     3071           
  Misses          ?      714           
  Partials        ?        0           
Files with missing lines Coverage Δ
apps/telegram-bot/core/services/file_id_capture.py 100.00% <100.00%> (ø)
...shared/fastfetchbot_shared/models/metadata_item.py 98.01% <100.00%> (ø)
...ackages/shared/fastfetchbot_shared/utils/number.py 100.00% <100.00%> (ø)
...c-worker/async_worker/services/file_id_consumer.py 91.15% <95.00%> (ø)
apps/telegram-bot/core/services/message_sender.py 59.15% <71.42%> (ø)
apps/telegram-bot/core/services/video_metadata.py 76.34% <76.34%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/async-worker/async_worker/services/file_id_consumer.py`:
- Around line 49-51: In the shutdown requeue exception path in
file_id_consumer.py (the block that currently calls logger.warning with
raw_payload), replace the warning call with logger.exception so the traceback is
captured; keep the existing message context (e.g. "Failed to requeue payload on
shutdown") and include raw_payload in the log call (use logger.exception("Failed
to requeue payload on shutdown: %s", raw_payload)) so errors during the requeue
in that function/path are logged with the full stack trace.

In `@apps/telegram-bot/core/services/video_metadata.py`:
- Around line 146-149: The current early-return checks only for width and height
using video_metadata_from_mapping(media_item), which skips probing when other
fields (e.g., duration) are missing; change the guard so we only return early
when all required fields (width, height, duration) are present — otherwise call
probe_video_metadata(io_object) to fill missing fields; update the conditional
around metadata/probed_metadata in the function that uses
video_metadata_from_mapping and probe_video_metadata so it checks all required
keys instead of just width and height.

In `@packages/shared/fastfetchbot_shared/utils/number.py`:
- Around line 4-8: The current positive_int() uses round(float(value)) which
silently converts fractional inputs (e.g. "720.6") into integers; change it to
strictly accept only whole integers: if value is an int accept it, if it's a
string accept only when it matches an integer pattern (no decimal point, no
exponent, optional +/-), then cast with int(), and return the int only when > 0;
otherwise return None. Update the logic in positive_int (in
packages/shared/fastfetchbot_shared/utils/number.py) to replace the float-round
path with the strict integer validation described.

In `@tests/unit/telegram_bot/test_message_sender.py`:
- Around line 300-311: Remove the redundant create=True from the AsyncMock patch
of "core.services.video_metadata.probe_video_metadata" in the test; specifically
update the patch call in tests/unit/telegram_bot/test_message_sender.py (the
patch that returns {"width": 720, "height": 1280, "duration": 14"} and is bound
to mock_probe) so it does not pass create=True, ensuring the test strictly mocks
the existing probe_video_metadata used by ensure_video_metadata and will fail if
that symbol is renamed or removed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5fc019cf-6ae0-4e9d-81d9-289d0b03b9c0

📥 Commits

Reviewing files that changed from the base of the PR and between df7756c and 3d25d0d.

📒 Files selected for processing (14)
  • .gitignore
  • AGENTS.md
  • apps/async-worker/async_worker/services/file_id_consumer.py
  • apps/telegram-bot/Dockerfile
  • apps/telegram-bot/core/services/file_id_capture.py
  • apps/telegram-bot/core/services/message_sender.py
  • apps/telegram-bot/core/services/video_metadata.py
  • packages/shared/fastfetchbot_shared/models/metadata_item.py
  • packages/shared/fastfetchbot_shared/utils/number.py
  • tests/unit/async_worker/test_file_id_consumer.py
  • tests/unit/database/mongodb/test_file_id_fields.py
  • tests/unit/telegram_bot/test_file_id_capture.py
  • tests/unit/telegram_bot/test_message_sender.py
  • tests/unit/test_number.py

Comment on lines +49 to +51
logger.warning(
f"Failed to requeue payload on shutdown: {raw_payload}"
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use logger.exception() in this exception path.

This branch is already handling a real exception during shutdown requeue, so logger.warning(...) drops the traceback exactly where you need it for queue-loss debugging.

Proposed fix
                 except Exception:
-                    logger.warning(
+                    logger.exception(
                         f"Failed to requeue payload on shutdown: {raw_payload}"
                     )

As per coding guidelines, **/*.py: Always use logger.exception() for logging errors with tracebacks and logger.error() for message-only logging; never use print() or traceback.print_exc().

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/async-worker/async_worker/services/file_id_consumer.py` around lines 49
- 51, In the shutdown requeue exception path in file_id_consumer.py (the block
that currently calls logger.warning with raw_payload), replace the warning call
with logger.exception so the traceback is captured; keep the existing message
context (e.g. "Failed to requeue payload on shutdown") and include raw_payload
in the log call (use logger.exception("Failed to requeue payload on shutdown:
%s", raw_payload)) so errors during the requeue in that function/path are logged
with the full stack trace.

Comment thread apps/telegram-bot/core/services/video_metadata.py
Comment on lines +4 to +8
try:
number = int(round(float(value)))
except (TypeError, ValueError):
return None
return number if number > 0 else None
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reject fractional values instead of rounding them.

positive_int() is being used to validate exact metadata fields, but round(float(value)) silently invents values. Inputs like "720.6" become 721 and "0.6" becomes 1, which can corrupt stored width/height/duration instead of rejecting invalid input.

Proposed fix
 def positive_int(value) -> int | None:
     if value is None or isinstance(value, bool):
         return None
     try:
-        number = int(round(float(value)))
+        number = float(value)
     except (TypeError, ValueError):
         return None
-    return number if number > 0 else None
+    if not number.is_integer():
+        return None
+    number = int(number)
+    return number if number > 0 else None
🧰 Tools
🪛 Ruff (0.15.14)

[warning] 5-5: Value being cast to int is already an integer

Remove unnecessary int call

(RUF046)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/shared/fastfetchbot_shared/utils/number.py` around lines 4 - 8, The
current positive_int() uses round(float(value)) which silently converts
fractional inputs (e.g. "720.6") into integers; change it to strictly accept
only whole integers: if value is an int accept it, if it's a string accept only
when it matches an integer pattern (no decimal point, no exponent, optional
+/-), then cast with int(), and return the int only when > 0; otherwise return
None. Update the logic in positive_int (in
packages/shared/fastfetchbot_shared/utils/number.py) to replace the float-round
path with the strict integer validation described.

Comment thread tests/unit/telegram_bot/test_message_sender.py
@aturret aturret merged commit 6b42d8f into main May 29, 2026
5 checks passed
@aturret aturret deleted the fix-ios-aspect-ratio branch May 29, 2026 04:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant