Commit graph

13543 commits

Author SHA1 Message Date
dependabot[bot]
3fbd699546
ci(deps): bump github/codeql-action from 3 to 4 (#129)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-22 09:20:21 -06:00
dependabot[bot]
42b48aecc6
ci(deps): bump actions/setup-dotnet from 4 to 5 (#128)
Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 4 to 5.
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](https://github.com/actions/setup-dotnet/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-dotnet
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-22 09:20:08 -06:00
dependabot[bot]
e92ddd1724
ci(deps): bump actions/labeler from 4 to 6 (#127)
Bumps [actions/labeler](https://github.com/actions/labeler) from 4 to 6.
- [Release notes](https://github.com/actions/labeler/releases)
- [Commits](https://github.com/actions/labeler/compare/v4...v6)

---
updated-dependencies:
- dependency-name: actions/labeler
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-22 09:19:42 -06:00
Cody Kickertz
d467dc7f0e
feat: add Author/Series services and frontend pages (#126)
* feat(core): add AddAuthorService and AddSeriesService with validators

* feat(api): add Author and Series lookup controllers

* feat(ui): add Author and Series frontend index pages

* feat(ui): add Book and Audiobook search pages

* feat(ui): add Book and Audiobook detail pages

* feat(ui): add Author and Series detail pages

---------

Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-22 09:16:05 -06:00
Cody Kickertz
4b7b273683
feat: Phase 2 Multi-Media Infrastructure - Books/Audiobooks Backend & Frontend (#125)
* feat(api): add Book and Audiobook lookup and editor controllers

Adds search and bulk edit functionality for Books and Audiobooks:
- BookLookupController: search by ISBN, ISBN13, ASIN, ForeignId, title
- AudiobookLookupController: search by ISBN, ASIN, narrator, title
- BookEditorController: bulk update/delete books
- AudiobookEditorController: bulk update/delete audiobooks
- Editor validators and resources for both entity types

* feat(core): add AddBookService and AddAudiobookService

Adds services for adding new books and audiobooks with validation:
- AddBookService: handles book creation with path generation
- AddAudiobookService: handles audiobook creation with narrator-aware paths
- AddBookValidator: validates book additions
- AddAudiobookValidator: validates audiobook additions

* feat(core): add BookFile and AudiobookFile entities and repositories

Adds file tracking infrastructure for books and audiobooks:
- BookFile entity with format tracking
- AudiobookFile entity with audio metadata (duration, bitrate, etc.)
- BookFileRepository for book file queries
- AudiobookFileRepository for audiobook file queries
- Database migration for new tables
- Table mappings for new entities

* feat(metadata): add Book and Audiobook metadata provider interfaces

Adds metadata provider infrastructure for books and audiobooks:
- IProvideBookInfo: interface for book metadata lookups
- IProvideAudiobookInfo: interface for audiobook metadata lookups
- BookMetadata: model for book metadata from external sources
- AudiobookMetadata: model for audiobook metadata with narrator info
- BookInfoProxy: stub implementation (to be replaced with Goodreads, etc.)
- AudiobookInfoProxy: stub implementation (to be replaced with Audible, etc.)

* feat(api): add Author and Series repositories, services, and API controllers

* feat(ui): add Book and Audiobook Redux store and index pages

---------

Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-21 21:28:14 -06:00
Cody Kickertz
cc9d8cd4d0
feat(core): add Book and Audiobook repositories, services, and API controllers (#124)
Adds the complete data layer for Books and Audiobooks:
- BookRepository with query methods for ISBN, ASIN, Author, Series
- BookService with business logic and event publishing
- AudiobookRepository with narrator-aware query methods
- AudiobookService with duration and narrator support
- Domain events for both entity types
- REST API controllers with full CRUD operations

Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-21 20:31:33 -06:00
Cody Kickertz
3af9d0285b
fix(core): code quality improvements (#123)
- Remove debug Console.WriteLine from migration 170
- Fix DelayProfileService.Reorder to throw ModelNotFoundException instead of silent failure
- Remove unused using directives

Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-21 19:51:54 -06:00
Cody Kickertz
b7cd7b20e8
feat(database): add Book and Audiobook entities (#122)
Add entities for books and audiobooks with:
- Book entity: title, ISBN, ASIN, publisher, author/series links
- Audiobook entity: title, narrator, duration, abridged flag, book link
- Database migration 246 for Books and Audiobooks tables
- Entity registrations in TableMapping

Note: Depends on PR #118 (Author/Series tables) for full FK support.

Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-21 19:51:44 -06:00
Cody Kickertz
b4009132d1
feat(ui): add Books and Audiobooks navigation sections (#121)
Add navigation sidebar entries for Books and Audiobooks with:
- New BOOK and AUDIOBOOK icons in props
- Books/Audiobooks sections in PageSidebar with Add New/Import children
- Routes for /books and /audiobooks paths
- Placeholder index pages for both media types
- Translation strings for Books/Audiobooks

Part of Phase 3 UI work for Issue #7.

Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-21 19:51:31 -06:00
Cody Kickertz
82f3d2da51
feat(qualities): add book and audiobook quality definitions (#120)
* feat(db): add MediaType discriminator to Movies table

Adds foundation for multi-media support by:
- Adding MediaType column to Movies table (migration 244)
- Adding MediaType property to Movie entity, defaulting to Movie

Existing movies will have MediaType=1 (Movie) after migration.
This prepares for future Book and Audiobook media types.

Addresses Issue #1

* refactor(core): create MediaItem abstract base class

Extracts common properties from Movie into a new MediaItem base class:
- MediaType, Monitored, QualityProfileId
- Path, RootFolderPath, Added, Tags, LastSearchTime

Movie now inherits from MediaItem and implements abstract methods
GetTitle() and GetYear() while maintaining backward-compatible
Title/Year property accessors.

This prepares for Book and Audiobook entities that will share
the same base structure.

Addresses Issue #1

* feat(core): add Author and Series entities for hierarchical monitoring

Introduces hierarchical structure for books/audiobooks:
- Author entity: tracks authors with monitoring, quality profiles, paths
- Series entity: groups books/audiobooks by series, linked to Author
- MediaItem: adds AuthorId and SeriesId for hierarchy support
- Migration 245: creates Authors and Series tables, adds columns to Movies

This enables Author → Series → Item monitoring inheritance for
future book and audiobook support.

Addresses Issue #2

* feat(core): add generic IProvideMediaInfo interface

Introduces generic metadata provider interfaces:
- IProvideMediaInfo<T>: Base interface for all metadata providers
  - GetByExternalId, GetById, GetBulkInfo
  - GetTrending, GetPopular, GetChangedItems
- ISearchableMediaProvider<T>: Search capability interface
  - SearchByTitle with optional year filtering

These interfaces establish the contract for future book and
audiobook metadata providers while maintaining compatibility
with the existing IProvideMovieInfo.

Addresses Issue #3

* feat(qualities): add book and audiobook quality definitions

Add quality source types and definitions for eBooks and audiobooks:
- EBOOK source: Unknown, EPUB, MOBI, AZW3, PDF, TXT
- AUDIOBOOK source: Unknown, MP3-128, MP3-320, M4B, FLAC

Quality definitions include appropriate size limits for each format.
New qualities auto-seed to database via QualityDefinitionService on startup.

Closes #4, Closes #5

---------

Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-21 19:51:20 -06:00
Cody Kickertz
37c67d3f9a
feat(core): add generic IProvideMediaInfo interface (#119)
* feat(db): add MediaType discriminator to Movies table

Adds foundation for multi-media support by:
- Adding MediaType column to Movies table (migration 244)
- Adding MediaType property to Movie entity, defaulting to Movie

Existing movies will have MediaType=1 (Movie) after migration.
This prepares for future Book and Audiobook media types.

Addresses Issue #1

* refactor(core): create MediaItem abstract base class

Extracts common properties from Movie into a new MediaItem base class:
- MediaType, Monitored, QualityProfileId
- Path, RootFolderPath, Added, Tags, LastSearchTime

Movie now inherits from MediaItem and implements abstract methods
GetTitle() and GetYear() while maintaining backward-compatible
Title/Year property accessors.

This prepares for Book and Audiobook entities that will share
the same base structure.

Addresses Issue #1

* feat(core): add Author and Series entities for hierarchical monitoring

Introduces hierarchical structure for books/audiobooks:
- Author entity: tracks authors with monitoring, quality profiles, paths
- Series entity: groups books/audiobooks by series, linked to Author
- MediaItem: adds AuthorId and SeriesId for hierarchy support
- Migration 245: creates Authors and Series tables, adds columns to Movies

This enables Author → Series → Item monitoring inheritance for
future book and audiobook support.

Addresses Issue #2

* feat(core): add generic IProvideMediaInfo interface

Introduces generic metadata provider interfaces:
- IProvideMediaInfo<T>: Base interface for all metadata providers
  - GetByExternalId, GetById, GetBulkInfo
  - GetTrending, GetPopular, GetChangedItems
- ISearchableMediaProvider<T>: Search capability interface
  - SearchByTitle with optional year filtering

These interfaces establish the contract for future book and
audiobook metadata providers while maintaining compatibility
with the existing IProvideMovieInfo.

Addresses Issue #3

---------

Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-21 19:51:07 -06:00
Cody Kickertz
5ac92d610a
feat(core): add Author and Series entities for hierarchical monitoring (#118)
* feat(db): add MediaType discriminator to Movies table

Adds foundation for multi-media support by:
- Adding MediaType column to Movies table (migration 244)
- Adding MediaType property to Movie entity, defaulting to Movie

Existing movies will have MediaType=1 (Movie) after migration.
This prepares for future Book and Audiobook media types.

Addresses Issue #1

* refactor(core): create MediaItem abstract base class

Extracts common properties from Movie into a new MediaItem base class:
- MediaType, Monitored, QualityProfileId
- Path, RootFolderPath, Added, Tags, LastSearchTime

Movie now inherits from MediaItem and implements abstract methods
GetTitle() and GetYear() while maintaining backward-compatible
Title/Year property accessors.

This prepares for Book and Audiobook entities that will share
the same base structure.

Addresses Issue #1

* feat(core): add Author and Series entities for hierarchical monitoring

Introduces hierarchical structure for books/audiobooks:
- Author entity: tracks authors with monitoring, quality profiles, paths
- Series entity: groups books/audiobooks by series, linked to Author
- MediaItem: adds AuthorId and SeriesId for hierarchy support
- Migration 245: creates Authors and Series tables, adds columns to Movies

This enables Author → Series → Item monitoring inheritance for
future book and audiobook support.

Addresses Issue #2

---------

Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-21 19:50:55 -06:00
Cody Kickertz
10c333a7d3
refactor(core): create MediaItem abstract base class (#117)
* feat(db): add MediaType discriminator to Movies table

Adds foundation for multi-media support by:
- Adding MediaType column to Movies table (migration 244)
- Adding MediaType property to Movie entity, defaulting to Movie

Existing movies will have MediaType=1 (Movie) after migration.
This prepares for future Book and Audiobook media types.

Addresses Issue #1

* refactor(core): create MediaItem abstract base class

Extracts common properties from Movie into a new MediaItem base class:
- MediaType, Monitored, QualityProfileId
- Path, RootFolderPath, Added, Tags, LastSearchTime

Movie now inherits from MediaItem and implements abstract methods
GetTitle() and GetYear() while maintaining backward-compatible
Title/Year property accessors.

This prepares for Book and Audiobook entities that will share
the same base structure.

Addresses Issue #1

---------

Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-21 18:14:41 -06:00
Cody Kickertz
841128caee
feat(db): add MediaType discriminator to Movies table (#116)
Adds foundation for multi-media support by:
- Adding MediaType column to Movies table (migration 244)
- Adding MediaType property to Movie entity, defaulting to Movie

Existing movies will have MediaType=1 (Movie) after migration.
This prepares for future Book and Audiobook media types.

Addresses Issue #1

Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-21 15:42:10 -06:00
Cody Kickertz
cde79b6e3b
fix: resolve build errors from Copilot API response code changes (#115)
- Fix void return handling in Update() methods that Copilot incorrectly
  assumed returned the updated object
- Remove unused System.Linq using in NotificationHelpers.cs
- Fix trailing whitespace style violations

Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-21 15:17:37 -06:00
Copilot
cf490da7f2
[WIP] Fix issues introduced by recent merges (#114)
* Initial plan

* Fix inconsistent HTTP response codes: PUT endpoints return 200 OK instead of 202 Accepted

Co-authored-by: cheir-mneme <176430037+cheir-mneme@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: cheir-mneme <176430037+cheir-mneme@users.noreply.github.com>
2025-12-21 14:50:32 -06:00
Copilot
84425d2b25
docs: Update Radarr references to Aletheia and document test suite status (#113)
* Initial plan

* docs: update package.json metadata for Aletheia fork

Co-authored-by: cheir-mneme <176430037+cheir-mneme@users.noreply.github.com>

* docs: update code comments to reference Aletheia instead of Radarr

Co-authored-by: cheir-mneme <176430037+cheir-mneme@users.noreply.github.com>

* docs: add test status documentation

Co-authored-by: cheir-mneme <176430037+cheir-mneme@users.noreply.github.com>

* docs: clarify Notifiarr integration naming in comment

Co-authored-by: cheir-mneme <176430037+cheir-mneme@users.noreply.github.com>

* docs: add comprehensive documentation cleanup summary

Co-authored-by: cheir-mneme <176430037+cheir-mneme@users.noreply.github.com>

* docs: update CLEANUP_CANDIDATES.md with completed items

Co-authored-by: cheir-mneme <176430037+cheir-mneme@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: cheir-mneme <176430037+cheir-mneme@users.noreply.github.com>
2025-12-21 14:50:10 -06:00
Copilot
688a0efaa4
Standardize API response codes: PUT returns 200, DELETE returns 204 (#112)
* Initial plan

* Fix API response codes: PUT returns 200 Ok, DELETE returns 204 NoContent

Co-authored-by: cheir-mneme <176430037+cheir-mneme@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: cheir-mneme <176430037+cheir-mneme@users.noreply.github.com>
2025-12-21 14:39:47 -06:00
Copilot
b9a03f0ec3
Extract common notification provider helpers to reduce duplication (#111)
* Initial plan

* Extract common notification helper methods to reduce duplication

- Create NotificationHelpers class with BytesToString, GetLinksString, and GetTitle methods
- Update Discord notification to use shared helper methods
- Remove duplicate helper methods from Discord.cs
- Reduces ~60 lines of duplicate code

Co-authored-by: cheir-mneme <176430037+cheir-mneme@users.noreply.github.com>

* Add common message helpers and update 11 notification providers

- Add GetMovieAddedMessage and GetHealthRestoredMessage to NotificationHelpers
- Update Discord, Gotify, Join, Mailgun, Prowl, PushBullet, Pushcut, Pushover, Pushsafer, Slack, and Telegram
- Replace duplicate message strings with shared helper methods
- Reduces ~22 lines of duplicate code across 11 providers

Co-authored-by: cheir-mneme <176430037+cheir-mneme@users.noreply.github.com>

* Update 5 more notification providers with common message helpers

- Update Apprise, Email, Ntfy, Simplepush, and Signal
- Standardize movie added and health restored messages
- Total of 16 providers now using shared helper methods

Co-authored-by: cheir-mneme <176430037+cheir-mneme@users.noreply.github.com>

* Address code review feedback

- Revert Apprise to use year in movie added message (preserve original behavior)
- Return empty string instead of null in GetLinksString and GetTitle helpers
- Improves null safety for consuming code

Co-authored-by: cheir-mneme <176430037+cheir-mneme@users.noreply.github.com>

* Add null checks to GetMovieAddedMessage and GetHealthRestoredMessage

- Prevent potential null reference exceptions
- Return empty strings when parameters are null
- Maintains consistency with other helper methods

Co-authored-by: cheir-mneme <176430037+cheir-mneme@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: cheir-mneme <176430037+cheir-mneme@users.noreply.github.com>
2025-12-21 14:39:35 -06:00
Copilot
2a816e10a7
[WIP] Fix open issues after research and analysis (#110)
* Initial plan

* feat(ci): Add secret scanning with secretlint to pre-commit hooks

- Install secretlint and @secretlint/secretlint-rule-preset-recommend
- Configure secretlint with .secretlintrc.json
- Add secretlint to lint-staged configuration
- Update CONTRIBUTING.md to document secret scanning
- Resolves #55

Co-authored-by: cheir-mneme <176430037+cheir-mneme@users.noreply.github.com>

* feat(privacy): Disable telemetry and analytics by default

- Set SentryEnabled to false by default in SentryTarget
- Update English localization to clarify error reporting is opt-in
- Update README with detailed privacy information
- Machine fingerprinting already removed (returns "anonymous")
- Piwik analytics already removed
- AnalyticsEnabled defaults to false in config

This ensures no telemetry is sent without explicit user consent.

Resolves #8

Co-authored-by: cheir-mneme <176430037+cheir-mneme@users.noreply.github.com>

* refactor: Address code review feedback for pre-commit and telemetry changes

- Optimize secretlint to only scan relevant file types (not all files)
- Add ignoreFiles configuration to secretlint to exclude build artifacts
- Clarify comment in SentryTarget about reconfiguration location

Co-authored-by: cheir-mneme <176430037+cheir-mneme@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: cheir-mneme <176430037+cheir-mneme@users.noreply.github.com>
2025-12-21 14:19:46 -06:00
dependabot[bot]
a69534b56c
Bump the nuget group with 1 update (#109)
Bumps System.Private.Uri from 4.3.0 to 4.3.2

---
updated-dependencies:
- dependency-name: System.Private.Uri
  dependency-version: 4.3.2
  dependency-type: direct:production
  dependency-group: nuget
- dependency-name: System.Private.Uri
  dependency-version: 4.3.2
  dependency-type: direct:production
  dependency-group: nuget
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-21 14:06:05 -06:00
Cody Kickertz
1bf1de8618
refactor: reduce cognitive complexity in FileNameBuilder.GetLanguagesToken (#108)
Extract helper methods:
- NormalizeLanguageCode: handles ISO639B mapping and culture conversion
- ApplyLanguageFilter: handles include/exclude filter logic

Uses LINQ for cleaner initial token processing.

Closes #75

Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-21 14:00:42 -06:00
Cody Kickertz
663cc841da
fix: add readonly modifier to static regex field (#106)
PerlRegexFactory: static Regex field should be readonly to
prevent accidental reassignment.

Closes #36

Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-21 14:00:21 -06:00
Cody Kickertz
b124fd8fc0
refactor(api): use async/await in MovieController.AllMovie (#107)
Convert blocking GetAwaiter().GetResult() to proper await pattern
in the API controller method.

Partial fix for #32

Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-21 14:00:03 -06:00
Cody Kickertz
b413c390fd
fix(ci): make test failures block builds (#105)
- Remove continue-on-error from integration tests
- Set fail-on-error: true on test reporter

Closes #56

Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-21 13:59:45 -06:00
Cody Kickertz
87605c0214
fix(deps): remove obsolete System.Private.Uri package (#104)
Closes #28

Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-21 13:59:36 -06:00
Cody Kickertz
c07e5a697d
fix(frontend): use ref to avoid stale movies closure in search (#103)
Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-21 12:27:41 -06:00
Cody Kickertz
80912b7d43
fix(security): prevent path traversal and command injection (#102)
Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-21 12:21:31 -06:00
Cody Kickertz
9d7b5b5298
fix: avoid redundant First() calls in BasicRepository (#101)
Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-21 12:00:29 -06:00
Cody Kickertz
2b0f9ad03a
fix: add null safety to QualityProfile First/Last methods (#100)
Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-21 12:00:19 -06:00
Cody Kickertz
b843e777de
fix: add empty catch comment and SingleOrDefault safety (#99)
Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-21 11:59:59 -06:00
Cody Kickertz
1a5ca83f4f
fix: resolve thread safety issues in ConfigService cache (#98)
Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-21 11:58:46 -06:00
Cody Kickertz
d8c69e87e2
fix(security): add regex timeouts for ReDoS prevention (#97)
Add TimeSpan.FromSeconds(1) timeout to remaining regex patterns:
- FileNameBuilder.cs: EditionOrdinalRegex, EditionUppercaseRegex
- Parser.cs: SlugSpaceRegex, SlugInvalidCharsRegex, SlugDuplicateDefaultRegex

Clears final 5 SonarCloud security hotspots for 100% review coverage

Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-21 11:50:11 -06:00
Cody Kickertz
9bf299c196
fix: add empty checks before First() in MovieFileController (#96)
Add guard clauses to prevent InvalidOperationException when
movieFiles list is empty in bulk update/delete operations

Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-21 11:44:49 -06:00
Cody Kickertz
0081ec2aa1
fix: use SingleOrDefault() with null check in UserService (#95)
Replace .Single() with .SingleOrDefault() when reading Config element
from XML to prevent InvalidOperationException on malformed config files

Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-21 11:39:41 -06:00
Cody Kickertz
c249c20eb2
fix: add null/empty checks before First() in download clients (#94)
- FileStationProxy: throw if no file info returned from API
- NzbVortex: return outputPath if no files in response
- RTorrent: use FirstOrDefault() for validation errors

Prevents InvalidOperationException on empty collections

Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-21 11:36:28 -06:00
Cody Kickertz
67a4720ed3
perf: fix remaining regex caching and add timeouts (#93)
- XbmcNfoDetector: convert instance regex to static readonly with timeout
- Parser: add RegexOptions.Compiled and timeout to ReportMovieTitleFolderRegex

Addresses Issue #36

Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-21 11:15:46 -06:00
Cody Kickertz
739a672637
perf: replace List.Contains() with HashSet for O(1) lookups (#92)
- ReleaseSearchService: wrap wantedLanguages in HashSet<Language>
- FileNameBuilder: convert splitFilter array to HashSet<string>
- NewznabCategoryFieldOptionsConverter: use HashSet<int> for category filters

Addresses Issue #35

Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-21 11:15:37 -06:00
Cody Kickertz
f4f7253165
chore(ci): standardize branch naming to use main instead of master (#91)
- Update workflow triggers to use main instead of master
- Update CONTRIBUTING.md to reference main branch
- Aligns with documentation in CLAUDE.md

Closes #52

Note: Actual branch rename (master → main) must be done on GitHub.

Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-21 11:11:39 -06:00
Cody Kickertz
168ea24266
perf(backend): cache additional regex patterns (#89)
* perf(backend): cache regex patterns for better performance

- TransmissionBase: add static VersionRegex, share with Transmission
- SearchCriteriaBase: cache RepeatingPlusRegex
- SearchMovieComparer: cache QueryYearRegex
- XbmcMetadata: cache WatchedRegex

Avoids regex compilation on each method call.

Partially addresses #36

* fix(security): add regex timeout to prevent ReDoS vulnerabilities

All cached regex patterns now include TimeSpan.FromSeconds(1) timeout
to prevent potential denial of service from malicious input patterns.

---------

Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-21 10:38:37 -06:00
Cody Kickertz
b17381f53f
fix(frontend): replace any types with proper TypeScript types (#88)
* fix(frontend): replace any types with proper TypeScript types

- AutoSuggestInput: use Data type from popper.js for modifier callback
- Tooltip: use Data type from popper.js for computeMaxSize callback
- OverlayScroller: use ComponentPropsWithoutRef<'div'> for renderView
- index.ts: use unknown[] instead of any[] for logError parameters

Improves type safety and removes eslint-disable comments.

Partially addresses #37

* fix(frontend): use ModifierFn type and string values for Popper styles

- Use ModifierFn type from popper.js for modifier callbacks
- Calculate bottom/right from offset properties (top+height, left+width)
- Convert numeric style values to strings with 'px' suffix
- Fix typo: 'botton' -> 'bottom' in AutoSuggestInput

---------

Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-21 10:38:16 -06:00
Cody Kickertz
c89ee01b63
chore(ci): standardize branch naming to use main instead of master (#90)
- Update workflow triggers to use main instead of master
- Update CONTRIBUTING.md to reference main branch
- Aligns with documentation in CLAUDE.md

Closes #52

Note: Actual branch rename (master → main) must be done on GitHub.

Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-20 09:00:25 -06:00
Cody Kickertz
1230212df8
fix(frontend): memoize inline JSX objects for performance (#87)
- MovieIndexTable: memoize itemData, move row flex styles to CSS
- MovieIndexOverviews: memoize itemData, extract listStyle constant
- MovieIndexOverview: memoize elementStyle and infoStyle
- CircularProgressBar: memoize containerStyle and circleStyle

Reduces unnecessary re-renders in virtualized lists and frequently
rendered components.

Closes #41

Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-19 20:01:07 -06:00
Cody Kickertz
80c364110c
fix(ui): update user-facing links to Aletheia resources (#86)
- MoreInfo: point to Aletheia GitHub instead of Radarr resources
- UpdateChanges: link issue numbers to Aletheia repo
- Add "Upstream" translation key for Radarr reference link

Closes #53

Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-19 20:00:39 -06:00
Cody Kickertz
b6b1df9dfe
chore: update GitHub Actions and consolidate .editorconfig rules (#85)
Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-19 20:00:23 -06:00
Cody Kickertz
a73b82d40c
fix(frontend): remove index from React keys in dynamic lists (#84)
Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-19 19:35:56 -06:00
Cody Kickertz
c4dae9a279
fix: add null safety to LINQ First/Single calls (#83)
Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-19 19:35:45 -06:00
Cody Kickertz
934a18e9a5
perf: cache regex patterns in Parser.ToUrlSlug and FileNameBuilder.GetEditionToken (#82)
Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-19 19:35:29 -06:00
Cody Kickertz
f2fff6419d
refactor: notification provider deduplication + docs (#81)
* fix: SonarCloud null safety and struct comparison issues

- OsPath.cs: Remove ReferenceEquals checks on struct (always false)
- SkyHookProxy.cs: Add null-conditional operators for Credits.Cast/Crew

* fix: remaining React index-as-key issues and backend null safety

Frontend:
- Fix 8 remaining index-as-key violations using content-based keys
- ImportMovieSelectFolder.js: use errorMessage as key
- ImportMovieFooter.js: use errorMessage as key
- CustomFormat.js: use item.name as key
- AddSpecificationItem.js: use preset.name as key
- QualityProfileItems.js: use message as key
- QualityProfileFormatItems.js: use message as key

Backend (cherry-picked from batch-3):
- OsPath.cs: Remove ReferenceEquals on struct
- SkyHookProxy.cs: Add null-conditional for Credits

* refactor(notifications): consolidate GetPosterUrl to base class

* docs: add architectural decisions log

* fix(sonar): enable path traversal suppressions for media management app

---------

Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-19 19:35:14 -06:00
Cody Kickertz
b7d5ffb6e9
fix: remaining React index-as-key issues + backend null safety (#78)
* fix: SonarCloud null safety and struct comparison issues

- OsPath.cs: Remove ReferenceEquals checks on struct (always false)
- SkyHookProxy.cs: Add null-conditional operators for Credits.Cast/Crew

* fix: remaining React index-as-key issues and backend null safety

Frontend:
- Fix 8 remaining index-as-key violations using content-based keys
- ImportMovieSelectFolder.js: use errorMessage as key
- ImportMovieFooter.js: use errorMessage as key
- CustomFormat.js: use item.name as key
- AddSpecificationItem.js: use preset.name as key
- QualityProfileItems.js: use message as key
- QualityProfileFormatItems.js: use message as key

Backend (cherry-picked from batch-3):
- OsPath.cs: Remove ReferenceEquals on struct
- SkyHookProxy.cs: Add null-conditional for Credits

---------

Co-authored-by: admin <admin@ardentleatherworks.com>
2025-12-19 16:11:24 -06:00