Skip to content
This repository was archived by the owner on Apr 6, 2026. It is now read-only.

[BUG] Fix recently-added webhook parsing#248

Merged
nwithan8 merged 3 commits intomasterfrom
webhook_parsing_fix
Jan 2, 2026
Merged

[BUG] Fix recently-added webhook parsing#248
nwithan8 merged 3 commits intomasterfrom
webhook_parsing_fix

Conversation

@nwithan8
Copy link
Copy Markdown
Owner

@nwithan8 nwithan8 commented Jan 2, 2026

  • Fix bad casting of int to string for year in Recently Added webhook data parsing
  • Update documentation for webhook payload setup

Summary by CodeRabbit

  • Documentation

    • Updated JSON example formatting in the "Recently Added" example.
  • New Features

    • Expanded webhook payload to include studio, directors, actors, genres, ratings, poster image, and duration.
    • Improved handling and validation for numeric-like fields (accepts ints or numeric strings).
  • Refactor

    • Under-the-hood model base changed for core data objects to standardize validation behavior.
  • Dependencies

    • Added typing-extensions.

✏️ Tip: You can customize this high-level summary in your review settings.

… data parsing

- Update documentation for webhook payload setup
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 2, 2026

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

Adds a Pydantic pre-validator and a lightweight base model (_Base and IntAsString), switches several models to inherit from _Base, expands Tautulli webhook data schema with new fields/properties, adds typing-extensions to requirements, and adjusts a JSON example placeholder format in docs.

Changes

Cohort / File(s) Summary
Core model utilities
modules/models/_base.py
New file: adds `int_to_string(value: Any) -> str
Model base updates
modules/models/recently_added_item.py, modules/models/webhook.py
Both classes change inheritance from BaseModel_Base; imports updated. No other field or method changes.
Webhook data expansion
modules/webhooks/tautulli_recently_added.py
RecentlyAddedWebhookData now inherits _Base; year_ field uses IntAsString; new fields added (studio, directors_, actors_, genres_, plex_id, critic_rating_, audience_rating_, poster_url) plus computed properties (year, duration, directors, actors, genres, critic_rating, audience_rating). Core parsing and webhook construction retained.
Dependencies
requirements.txt
Adds typing-extensions.
Documentation
documentation/DOCUMENTATION.md
Updates JSON example placeholders from double-brace {{title}} to single-brace {title} and adjusts indentation/quoting in the example.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title '[BUG] Fix recently-added webhook parsing' directly describes the primary change: fixing webhook parsing for recently-added data, matching the PR's core objective to fix int-to-string casting for the year field.

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d637830 and 82322b9.

📒 Files selected for processing (1)
  • modules/models/_base.py

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.

Copy link
Copy Markdown

@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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
modules/webhooks/tautulli_recently_added.py (1)

42-46: Duration parsing may fail on "HH:MM:SS" format.

The unpacking on line 45 assumes exactly two parts after splitting on :. If Tautulli ever sends duration in "HH:MM:SS" format, this will raise ValueError: too many values to unpack.

🔎 Proposed defensive fix
         if ':' not in self.duration_:
             return int(self.duration_)

-        hours, minutes = self.duration_.split(':')
-        return int(hours) * 60 + int(minutes)
+        parts = self.duration_.split(':')
+        if len(parts) == 2:
+            hours, minutes = parts
+            return int(hours) * 60 + int(minutes)
+        elif len(parts) == 3:
+            hours, minutes, _ = parts  # Ignore seconds
+            return int(hours) * 60 + int(minutes)
+        return None
🧹 Nitpick comments (3)
requirements.txt (1)

14-14: Consider adding a version constraint for typing-extensions.

The typing-extensions dependency is added without a version constraint. While the features used (Annotated) are available in older versions, specifying a minimum version ensures compatibility and prevents potential issues.

🔎 Suggested version constraint
-typing-extensions
+typing-extensions>=4.0.0
modules/models/_base.py (1)

23-23: Consider extracting error messages to exception class.

Static analysis suggests moving the error message strings into custom exception classes for better maintainability and reusability.

🔎 Optional refactor
+class InvalidIntStringError(ValueError):
+    """Raised when a value cannot be coerced to a valid int string."""
+    pass
+
+
 def int_to_string(value: Any) -> Union[str, None]:
     """
     Handle coercing ints to strings for Pydantic models.
     :param value: The value to be converted.
     :return: The string representation of the int, or None if the value is None.
     """
     if value is None:
         return None
 
     if isinstance(value, int):
         return str(value)
 
     if isinstance(value, str):
         try:
             int(value)
         except ValueError as e:
-            raise ValueError("String value cannot be converted to int") from e
+            raise InvalidIntStringError("String value cannot be converted to int") from e
         return value
 
-    raise ValueError("Input value is not an int or string")
+    raise InvalidIntStringError("Input value is not an int or string")

Also applies to: 26-26

modules/webhooks/tautulli_recently_added.py (1)

84-89: Consider narrowing the exception handling.

Catching all exceptions here could mask unexpected errors (e.g., MemoryError, RecursionError). Consider catching more specific exceptions like BadRequest or JSON-related exceptions from Flask/Werkzeug.

🔎 Suggested improvement
+from werkzeug.exceptions import BadRequest
+
         try:
             body = request.get_json()
-        except Exception:
+        except (BadRequest, TypeError, ValueError):
             # JSON data is stored in the form field 'payload_json' if files are present
             # ref: https://github.com/Tautulli/Tautulli/blob/d019efcf911b4806618761c2da48bab7d04031ec/plexpy/notifiers.py#L1225
             body = json.loads(request.form.get('payload_json', '{}'))
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1562980 and 32487f6.

📒 Files selected for processing (6)
  • documentation/DOCUMENTATION.md
  • modules/models/_base.py
  • modules/models/recently_added_item.py
  • modules/models/webhook.py
  • modules/webhooks/tautulli_recently_added.py
  • requirements.txt
🧰 Additional context used
🧬 Code graph analysis (2)
modules/models/webhook.py (2)
modules/models/_base.py (1)
  • _Base (32-33)
modules/database/models/webhooks.py (1)
  • Webhook (7-17)
modules/models/recently_added_item.py (2)
modules/models/_base.py (1)
  • _Base (32-33)
modules/database/models/recently_added_item.py (1)
  • RecentlyAddedItem (6-20)
🪛 Ruff (0.14.10)
modules/models/_base.py

23-23: Avoid specifying long messages outside the exception class

(TRY003)


26-26: Avoid specifying long messages outside the exception class

(TRY003)

🔇 Additional comments (9)
documentation/DOCUMENTATION.md (1)

373-388: LGTM! Documentation correctly updated.

The webhook JSON data format has been updated to use single-brace placeholders (e.g., {title}) instead of double-brace format (e.g., {{title}}), which aligns with standard Tautulli webhook variable syntax.

modules/models/webhook.py (1)

4-7: LGTM! Clean migration to shared base class.

The model now inherits from _Base instead of directly from BaseModel, enabling shared validation and typing utilities across the codebase while maintaining the same functionality.

modules/models/recently_added_item.py (1)

2-5: LGTM! Clean migration to shared base class.

The model now inherits from _Base instead of directly from BaseModel, consistent with the broader refactoring effort to use shared validation utilities.

modules/models/_base.py (2)

7-26: Function logic is correct for int/string coercion.

The int_to_string function properly handles the three expected cases:

  • Passes None through unchanged
  • Converts int values to str
  • Validates that str values represent valid integers before accepting them

This enables the IntAsString type to accept both integer and string inputs while ensuring type safety.


32-33: LGTM! Minimal base class provides extension point.

The empty _Base class serves as a shared foundation for models across the codebase, allowing future common validation logic or configuration to be added in one place.

modules/webhooks/tautulli_recently_added.py (4)

4-7: LGTM!

Import changes align with the migration to the custom _Base model and IntAsString type alias for handling int-to-string coercion.


48-66: LGTM!

The property implementations for directors, actors, genres, critic_rating, and audience_rating correctly handle None cases and use appropriate type conversions.


100-101: LGTM!

The _determine_trigger method correctly returns the RECENTLY_ADDED trigger constant.


17-17: > Likely an incorrect or invalid review comment.

Comment thread modules/models/_base.py Outdated
Copy link
Copy Markdown

@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: 1

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 32487f6 and d637830.

📒 Files selected for processing (1)
  • modules/models/_base.py
🧰 Additional context used
🪛 Ruff (0.14.10)
modules/models/_base.py

23-23: Avoid specifying long messages outside the exception class

(TRY003)


26-26: Avoid specifying long messages outside the exception class

(TRY003)

🔇 Additional comments (2)
modules/models/_base.py (2)

1-4: LGTM!

Imports are appropriate and necessary for the Pydantic validator implementation.


32-33: LGTM!

The minimal base class provides a clean foundation for extending Pydantic models across the codebase.

Comment thread modules/models/_base.py Outdated
@nwithan8 nwithan8 merged commit fd1b302 into master Jan 2, 2026
1 of 2 checks passed
@nwithan8 nwithan8 deleted the webhook_parsing_fix branch January 2, 2026 07:39
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant