Add NFO parsing for channel and episode metadata #4

Merged
coding-agent-marvin8 merged 0 commits from refs/pull/4/head into main 2026-06-17 04:04:55 +00:00
coding-agent-marvin8 commented 2026-06-17 02:35:24 +00:00 (Migrated from codeberg.org)

Implements Phase 2 of the roadmap: ChannelInfo and EpisodeInfo dataclasses, parsers for tvshow.nfo and Kodi episodedetails NFO sidecars, and a regex filename fallback for episodes without a sidecar.

Also bundles the bootstrap cleanup from Phase 1: adds .gitignore and commits the previously-staged .pre-commit-config.yaml, .woodpecker/ai-review.yml, pylock.toml, pyproject.toml, and uv.lock.

Closes #3

Implements Phase 2 of the roadmap: ChannelInfo and EpisodeInfo dataclasses, parsers for tvshow.nfo and Kodi episodedetails NFO sidecars, and a regex filename fallback for episodes without a sidecar. Also bundles the bootstrap cleanup from Phase 1: adds .gitignore and commits the previously-staged .pre-commit-config.yaml, .woodpecker/ai-review.yml, pylock.toml, pyproject.toml, and uv.lock. Closes #3
coding-agent-marvin8 commented 2026-06-17 03:23:28 +00:00 (Migrated from codeberg.org)

src/sub2pod/nfo.py line 7@marvin8

Comment typo: 'ty: ignore' should be 'type: ignore'.

🔴 Not actioning. # ty: ignore[unresolved-import] is the correct suppression syntax for Ruff's ty type checker — it is distinct from mypy's # type: ignore. Both are present on the same codebase; using type: ignore here would suppress the diagnostic only in mypy-compatible checkers and leave ty reporting the error. The annotation is intentional.

[`src/sub2pod/nfo.py` line 7](https://codeberg.org/marvin8/sub2pod/pulls/4#issuecomment-17603159) — @marvin8 > Comment typo: 'ty: ignore' should be 'type: ignore'. 🔴 Not actioning. `# ty: ignore[unresolved-import]` is the correct suppression syntax for Ruff's `ty` type checker — it is distinct from mypy's `# type: ignore`. Both are present on the same codebase; using `type: ignore` here would suppress the diagnostic only in mypy-compatible checkers and leave `ty` reporting the error. The annotation is intentional.
coding-agent-marvin8 commented 2026-06-17 03:23:35 +00:00 (Migrated from codeberg.org)

src/sub2pod/nfo.py line 26@marvin8

Missing error handling: etree.parse will raise an exception if the file does not exist or is invalid XML.

🔴 Not actioning at this layer. The parsing functions are intentionally thin: they receive a Path that the caller already located, and they propagate parse failures as exceptions. Deciding whether to skip a malformed file, log a warning, or abort belongs to the episode-discovery caller (Phase 3). Swallowing XMLSyntaxError here would silently drop corrupted channel data. Note: this comment appears to be a duplicate of #issuecomment-17603165.

[`src/sub2pod/nfo.py` line 26](https://codeberg.org/marvin8/sub2pod/pulls/4#issuecomment-17603162) — @marvin8 > Missing error handling: etree.parse will raise an exception if the file does not exist or is invalid XML. 🔴 Not actioning at this layer. The parsing functions are intentionally thin: they receive a `Path` that the caller already located, and they propagate parse failures as exceptions. Deciding whether to skip a malformed file, log a warning, or abort belongs to the episode-discovery caller (Phase 3). Swallowing `XMLSyntaxError` here would silently drop corrupted channel data. Note: this comment appears to be a duplicate of #issuecomment-17603165.
coding-agent-marvin8 commented 2026-06-17 03:23:41 +00:00 (Migrated from codeberg.org)

src/sub2pod/nfo.py line 26@marvin8

Missing error handling: etree.parse will raise an exception if the file does not exist or is invalid XML.

🔴 Not actioning — same reasoning as #issuecomment-17603162 (duplicate comment). Error handling for missing/corrupt files belongs at the Phase 3 discovery layer, not inside the parser.

[`src/sub2pod/nfo.py` line 26](https://codeberg.org/marvin8/sub2pod/pulls/4#issuecomment-17603165) — @marvin8 > Missing error handling: etree.parse will raise an exception if the file does not exist or is invalid XML. 🔴 Not actioning — same reasoning as #issuecomment-17603162 (duplicate comment). Error handling for missing/corrupt files belongs at the Phase 3 discovery layer, not inside the parser.
coding-agent-marvin8 commented 2026-06-17 03:23:48 +00:00 (Migrated from codeberg.org)

src/sub2pod/nfo.py line 40@marvin8

Potential ValueError: date.fromisoformat(aired_text) will crash if aired_text is present but not a valid ISO date.

Fixed in commit fa1ebd6. parse_episode_nfo now wraps the date.fromisoformat call in try/except ValueError and returns None for aired if the value is malformed. A test covering this case (returns None aired when value is not a valid ISO date) was also added.

[`src/sub2pod/nfo.py` line 40](https://codeberg.org/marvin8/sub2pod/pulls/4#issuecomment-17603168) — @marvin8 > Potential ValueError: date.fromisoformat(aired_text) will crash if aired_text is present but not a valid ISO date. ✅ Fixed in commit `fa1ebd6`. `parse_episode_nfo` now wraps the `date.fromisoformat` call in `try/except ValueError` and returns `None` for `aired` if the value is malformed. A test covering this case (`returns None aired when value is not a valid ISO date`) was also added.
coding-agent-marvin8 commented 2026-06-17 03:23:55 +00:00 (Migrated from codeberg.org)

src/sub2pod/nfo.py line 41@marvin8

Potential ValueError: int(runtime_text) will crash if runtime_text is present but not a valid integer.

Fixed in commit fa1ebd6. parse_episode_nfo now wraps the int() conversion in try/except ValueError and returns None for runtime if the value is malformed. A test covering this case (returns None runtime when value is not a valid integer) was also added.

[`src/sub2pod/nfo.py` line 41](https://codeberg.org/marvin8/sub2pod/pulls/4#issuecomment-17603171) — @marvin8 > Potential ValueError: int(runtime_text) will crash if runtime_text is present but not a valid integer. ✅ Fixed in commit `fa1ebd6`. `parse_episode_nfo` now wraps the `int()` conversion in `try/except ValueError` and returns `None` for `runtime` if the value is malformed. A test covering this case (`returns None runtime when value is not a valid integer`) was also added.
coding-agent-marvin8 commented 2026-06-17 03:24:01 +00:00 (Migrated from codeberg.org)

src/sub2pod/nfo.py line 53@marvin8

Potential ValueError: date.fromisoformat(match.group(1)) will crash if the date portion of the filename is not a valid ISO date (e.g. '2024-02-30').

Fixed in commit fa1ebd6. parse_episode_filename now wraps date.fromisoformat in try/except ValueError and returns None for filenames whose date portion is calendar-invalid (the regex enforces the YYYY-MM-DD format, but cannot reject impossible dates like Feb 30). A test covering this (returns None for a filename with a calendar-invalid date) was also added.

[`src/sub2pod/nfo.py` line 53](https://codeberg.org/marvin8/sub2pod/pulls/4#issuecomment-17603174) — @marvin8 > Potential ValueError: date.fromisoformat(match.group(1)) will crash if the date portion of the filename is not a valid ISO date (e.g. '2024-02-30'). ✅ Fixed in commit `fa1ebd6`. `parse_episode_filename` now wraps `date.fromisoformat` in `try/except ValueError` and returns `None` for filenames whose date portion is calendar-invalid (the regex enforces the `YYYY-MM-DD` format, but cannot reject impossible dates like Feb 30). A test covering this (`returns None for a filename with a calendar-invalid date`) was also added.
coding-agent-marvin8 commented 2026-06-17 03:28:36 +00:00 (Migrated from codeberg.org)

tests/test_nfo.py line 150@marvin8

Test coverage gap: No test for a missing title element in episode NFO. Also no tests for invalid date/runtime values in NFO or invalid filename dates.

Addressed in commit fa1ebd6:

  • Missing title → returns empty string: added returns empty string title when title element is absent
  • Invalid aired date in NFO: added returns None aired when value is not a valid ISO date (and fixed the guard)
  • Invalid runtime in NFO: added returns None runtime when value is not a valid integer (and fixed the guard)
  • Calendar-invalid filename date: added returns None for a filename with a calendar-invalid date (and fixed the guard)
[`tests/test_nfo.py` line 150](https://codeberg.org/marvin8/sub2pod/pulls/4#issuecomment-17603177) — @marvin8 > Test coverage gap: No test for a missing title element in episode NFO. Also no tests for invalid date/runtime values in NFO or invalid filename dates. ✅ Addressed in commit `fa1ebd6`: - Missing title → returns empty string: added `returns empty string title when title element is absent` - Invalid aired date in NFO: added `returns None aired when value is not a valid ISO date` (and fixed the guard) - Invalid runtime in NFO: added `returns None runtime when value is not a valid integer` (and fixed the guard) - Calendar-invalid filename date: added `returns None for a filename with a calendar-invalid date` (and fixed the guard)
Sign in to join this conversation.
No reviewers
No labels
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
marvin8/sub2pod!4
No description provided.