* bump to 6.1.0
* chore: updated build images
* New: add TTL setting for pushover notifications
(cherry picked from commit 317cdf15582746bd4e713d6b99e17a21dcb8abeb)
* Chore: Remove Readarr donation logo
* Skip proxy tests on MacOsX
* Fix: (#11303) collection API error when using `Movie CollectionThe` (#11304)
Co-authored-by: Bogdan <mynameisbogdan@users.noreply.github.com>
* feat: initial project branding and setup
* chore: move project config to development folder
* feat(ci): add GitHub Actions and Docker configuration
* docs: update documentation for rebrand
* chore: update Windows and macOS distribution branding
* feat(privacy): remove telemetry, analytics, fingerprinting
* docs: add privacy section and document cleanup candidates
* docs: simplify privacy section
* feat: rebrand to Logarr with teal theme
* refactor(ui): update page titles and manifest for rebrand
* fix(security): patch SQL injection, path traversal, command injection
* docs: update CHANGELOG with security fixes and branding
* fix: resolve build issues for local development
* chore: update GitHub username to cheir-mneme
* refactor: rename project from Logarr to Aletheia
* fix(ci): add disk space cleanup for Docker multi-arch builds
* refactor: remove empty housekeeping classes and commented properties
* docs: update PR template for Aletheia workflow
* fix(build): use pipe delimiter in sed for branch names with slashes
* fix(security): address pre-release security blockers
- Reject unknown sender types in certificate validation
- Disable auto-redirect in SkyHookProxy to prevent HTTPS downgrade
- Use proper JSON serialization in InitializeJsonController
- Add whitelist validation for Type.GetType in converters
* docs: update community standards with conventional commits and Aletheia branding
* docs: link README to CONTRIBUTING.md
* chore: add pre-commit hooks and CI coverage reporting
- Add pre-commit hook for JS/TS and CSS lint checks
- Add setup script to install hooks
- Add coverage reporting to CI workflow
- Add coverage threshold warning (60%)
- Update CONTRIBUTING.md with hooks setup instructions
* feat(download): add automatic archive extraction (Unpackerr absorption)
- Add SharpCompress for RAR/7z support
- Extend ArchiveService with RAR, 7z extraction via SharpCompress
- Add DownloadExtractionService for orchestrating extraction
- Add config: AutoExtractArchives (default: false)
- Add config: DeleteArchiveAfterExtraction (default: true)
- Integrate extraction into CompletedDownloadService
Note: UI settings page not yet implemented - backend foundation only.
* fix(style): use explicit HashSet type for StyleCop SA1000
* fix(style): use explicit HashSet type for StyleCop SA1000
* fix(ci): copy test DLLs to expected location for test.sh
* fix(style): use explicit JsonSerializerOptions type
* fix(style): remove unused using, use AsSpan over Substring
* chore(deps): bump js-yaml in the npm_and_yarn group across 1 directory
Bumps the npm_and_yarn group with 1 update in the / directory: [js-yaml](https://github.com/nodeca/js-yaml).
Updates `js-yaml` from 4.1.0 to 4.1.1
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/4.1.0...4.1.1)
---
updated-dependencies:
- dependency-name: js-yaml
dependency-version: 4.1.1
dependency-type: indirect
dependency-group: npm_and_yarn
...
Signed-off-by: dependabot[bot] <support@github.com>
* feat(indexer): add multi-media type foundation
Add MediaType enum and indexer support for books/audiobooks:
- MediaType enum (Movie, TV, Music, Book, Audiobook, Podcast, Comic)
- NewznabStandardCategory constants for all media types
- Database migration 243 for SupportedMediaTypes column
- Updated IndexerDefinition, IIndexer, IndexerBase
- Updated README with current project status
* feat(indexer): add book/audiobook search criteria
Add search criteria classes and update request generators:
- BookSearchCriteria (Author, Title, ISBN, Publisher, Year)
- AudiobookSearchCriteria (Author, Title, Narrator, ASIN, ISBN)
- Updated IIndexerRequestGenerator interface
- Implemented book/audiobook search in NewznabRequestGenerator
- Added stub implementations to all other request generators
* Add SonarCloud analysis workflow
This workflow triggers a SonarCloud analysis of the code and populates GitHub Code Scanning alerts with vulnerabilities found.
* fix: disable SA1200 StyleCop rule to match stylecop.json config
* Add GitHub Super Linter workflow
This workflow runs multiple linters on code changes in the 'develop' branch for both pushes and pull requests.
* Add Trivy vulnerability scanning workflow
* ci: fix workflow configs and add dependabot
- SonarCloud: add proper projectKey and organization
- Trivy: fix image reference, add schedule comment
- Super Linter: upgrade to v6, configure linter selection
- Add Dependabot for NuGet, npm, Docker, GitHub Actions
* fix(ci): correct Dockerfile path and skip SonarCloud when token missing
* fix(ci): use filesystem scan instead of image scan for Trivy
* fix(ci): use exclusion-only config for super-linter
* fix(ci): disable checkov and github_actions linters in super-linter
* ci(deps): bump actions/checkout from 4 to 6
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v6)
---
updated-dependencies:
- dependency-name: actions/checkout
dependency-version: '6'
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot] <support@github.com>
* ci(deps): bump actions/cache from 4 to 5
Bumps [actions/cache](https://github.com/actions/cache) from 4 to 5.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v4...v5)
---
updated-dependencies:
- dependency-name: actions/cache
dependency-version: '5'
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot] <support@github.com>
* ci(deps): bump codecov/codecov-action from 4 to 5
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 4 to 5.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v4...v5)
---
updated-dependencies:
- dependency-name: codecov/codecov-action
dependency-version: '5'
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot] <support@github.com>
* ci(deps): bump dessant/label-actions from 3 to 5
Bumps [dessant/label-actions](https://github.com/dessant/label-actions) from 3 to 5.
- [Release notes](https://github.com/dessant/label-actions/releases)
- [Changelog](https://github.com/dessant/label-actions/blob/main/CHANGELOG.md)
- [Commits](https://github.com/dessant/label-actions/compare/v3...v5)
---
updated-dependencies:
- dependency-name: dessant/label-actions
dependency-version: '5'
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot] <support@github.com>
* Bump the nuget group with 1 update
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>
* docs: update CLA to reference Aletheia
* ci: remove super-linter workflow
Linting covered by existing tools:
- C#: StyleCop during build
- GitHub Actions: CodeQL
- Frontend: eslint in package.json
* feat(indexer): add MyAnonamouse indexer for books and audiobooks
* feat(indexer): enable book and audiobook support in Newznab/Torznab
* feat(ui): add media type badge to poster view
* fix: address code review findings
- Fix Torznab default definition protocol (Usenet -> Torrent)
- Add try-catch around JSON deserialization in MAM parser
- Add logging for author info parse failures
- Add null check for JSON response
* fix: add timeout to regex for DoS prevention
* fix: mark React component props as Readonly
Bulk update to make all component props immutable at the type level.
This prevents accidental prop mutation and improves type safety.
Resolves ~50 SonarCloud code smells.
* refactor: replace ApplicationException with domain-specific exceptions
Create custom exception classes:
- InvalidDatabaseSchemaException for migration errors
- ServiceInstallationException for service install failures
- DataRetrievalException for repository query mismatches
- InvalidRequestException for HTTP request validation
- InvalidHeaderException for HTTP header validation
Resolves SonarCloud S3988 (ApplicationException usage).
* refactor(ui): extract PosterDateRow to reduce MovieIndexPoster complexity
Extract repetitive date display logic into PosterDateRow component.
Reduces cognitive complexity from 30 to ~20 by consolidating 4 similar
conditional blocks into reusable component calls.
* refactor: reduce MyAnonamouseParser cognitive complexity
Extract helper methods for author parsing, title flags, and freeleech
detection to simplify the main ParseResponse loop.
Addresses #30
* refactor: reduce LanguageParser cognitive complexity
Replace 40+ individual if statements with dictionary-based lookup.
Extract helper methods for keyword, case-sensitive regex, and
case-insensitive regex language detection. Original method reduced
from ~400 lines to ~17 lines while preserving all behavior.
* refactor: make methods static where instance data not used (S2325)
~243 methods converted to static where they don't access instance data.
Fixed call sites that needed to use type name instead of instance.
* refactor: seal non-derived private classes (S3260)
63 private nested classes marked as sealed since they have no derived classes.
* perf: use char overloads for StartsWith/EndsWith (S6610)
Use single character overloads instead of single-character string
overloads for better performance.
* refactor: use Number.parseInt/parseFloat/isNaN (S7773)
Use Number static methods instead of global functions for better
clarity and consistency.
* Update README for clarity and typo corrections
Corrected typos and improved clarity in the README.
* refactor: remove redundant boolean literals (S1125)
Replace == false with negation operator, remove == true comparisons
* ci: remove sonarcloud workflow (conflicts with automatic analysis)
* docs: add comprehensive technical debt tracking
* docs: remove tech debt tracking from repo (moved to wrapper)
* fix(security): sanitize user-controlled strings in log statements
Add SanitizeForLog() extension method to prevent log forging attacks
by replacing control characters (newlines, etc.) with spaces. Applied
across 30 files that log user-controlled data like paths, titles,
URLs, and usernames.
Fixes CodeQL log-forging alerts.
* fix: resolve technical debt and npm vulnerabilities
NPM Security (0 vulnerabilities remaining):
- Add yarn resolutions for cross-spawn, brace-expansion, color-string, glob, postcss
Bug fixes:
- Bug-002: Use FirstOrDefault with null check (DownloadStationTaskProxyV2)
- Bug-007: Fix inverted exception logic for magnet fallback (TorrentClientBase)
- Bug-008: Fix stale closure using ref (MovieSearchInput)
- Bug-009: Fix Number.Number.parseInt typos across 50+ files
- Bug-010: Add regex timeout and Compiled flag (RegexReplace)
- Bug-011: Add null checks for XML queries (ConfigFileProvider)
- Bug-012: Remove empty touch handler (MovieDetails)
- Bug-013: Use Path.GetFileName for safer check (InstallUpdateService)
- Bug-014: Return Ok instead of Accepted for sync PUT (MovieController)
- Bug-016: Fix double bracket typo in log message (InstallUpdateService)
- Bug-017: Add console.warn to catch block (MovieTagInput)
- Bug-018: Remove stray debug console.log (SignalRConnector)
- Bug-019: Document disabled regex with ReDoS justification (Parser)
* Fix deadlock risk in ReleasePushController with async SemaphoreSlim
* Add log sanitization for CodeQL log forging alerts
* Add custom CodeQL config to exclude log-forging false positives
* Fix CodeQL qlpack.yml - add library: true
* Trigger CI after disabling default CodeQL
* Update CodeQL config to exclude path-injection and use security-extended
* Exclude additional CodeQL false positives for single-user app
* Exclude SonarCloud S5145 false positive log injection warnings
* Suppress S5145 log injection false positive in editorconfig
* Add CI-based SonarCloud workflow with rule exclusions
* Remove sonar-project.properties - not supported by SonarScanner for .NET
* Remove SonarCloud CI workflow - conflicts with automatic analysis
* Fix CodeQL rule ID for insecure-direct-object-reference
* Fix remaining technical debt bugs
- Bug-001: Add null check for SingleOrDefault() in TorrentRssParser
- Bug-006: Replace generic Exception with PathCombinationException in OsPath
- Bug-006: Replace generic Exception with NotSupportedException in IMDbListRequestGenerator
* Fix blocking semaphore in MediaCoverService
Convert _semaphore.Wait() to async pattern with WaitAsync()
to prevent thread blocking during image resizing operations.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix CancellationTokenSource resource leaks (BLOCKER severity)
- ManagedHttpDispatcher: Dispose quickFailCts and linkedTokenSource in finally block
- CommandExecutor: Dispose _cancellationTokenSource on shutdown
- Scheduler: Dispose _cancellationTokenSource on shutdown
- IntegrationTestBase: Store CTS as field and dispose in TearDown
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* test: add IMDb list error message verification tests
* fix: thread-safe SHA1 hashing in HashConverter
* fix(ci): pin Trivy action and update branding
* fix(ci): add CODEOWNERS, enable test blocking, add pre-commit hooks
* chore: update yarn.lock with husky and lint-staged
* fix(ci): P2 improvements - editorconfig, integration tests, Prettier 3
- Remove duplicate dotnet_style_qualification rules in .editorconfig
- Update Radarr branding to Aletheia in .editorconfig
- Add integration tests step to build.yml (with continue-on-error)
- Upgrade Prettier to 3.7.4, eslint-plugin-prettier to 5.5.4
- Upgrade eslint-config-prettier to 10.1.8
- Fix pre-existing lint errors (unused vars, radix parameter)
- Reformat frontend code with Prettier 3 formatting changes
Closes#57 (SonarCloud deferred - needs org setup)
Closes#58, #62 (partial - ESLint 9 deferred), #63🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(security): address P3 vulnerabilities and add mitigations
Security fixes:
- XXE prevention: disable XmlResolver in UTorrentProxy.cs (#42)
- Path traversal: validate paths in LogFileController.cs (#44)
- Path traversal: validate paths in MediaCoverController.cs (#44)
- ReDoS mitigation: add 5s timeout to user regex patterns
Documentation:
- CORS: document security rationale in Startup.cs (#43)
Closes#42, #43, #44
Related: #59, #60, #61 (SonarCloud triage - GitHub alerts now at 0 open)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Cache regex instances as static compiled fields
- SkyHookProxy: Cache IMDB/TMDB URL regexes
- PushsaferSettings: Cache hex color validation regex
- Parser: Cache IMDB ID validation regex
* Optimize O(n*m) Contains patterns with HashSet
- MovieService.FindByTitle: Convert title lists to HashSets
- MoviesSearchService: Convert queue IDs to HashSet
* fix(security): add path validation to OpenWriteStream and regex timeouts
- DiskProviderBase: Add Ensure.That path validation to OpenWriteStream
- CleanseLogMessage: Add 5-second timeout to all 22 regex patterns to prevent ReDoS
* fix(frontend): address React and TypeScript quality issues
- Replace index-as-key antipattern with stable keys (#34)
- Remove TypeScript any types in favor of proper types (#37)
- Memoize inline style objects to prevent unnecessary re-renders (#41)
Files: 17 frontend components updated
* Migrate to ESLint 9 flat config
- Create eslint.config.mjs with ESM flat config format
- Remove legacy .eslintrc.js and .eslintignore
- Remove eslint-plugin-filenames (not ESLint 9 compatible)
- Update lint-staged to use new config
- Clean up unused eslint-disable directives
* Fix SonarCloud issues and add suppression config
Backend:
- Add regex timeout to prevent ReDoS (S6444):
- SkyHookProxy.cs: ImdbUrlRegex, TmdbUrlRegex
- PushsaferSettings.cs: HexColorRegex
- Parser.cs: ImdbIdRegex
Frontend:
- Fix sorting without localeCompare (S2871):
- MovieIndex.tsx, Collection.js, DiscoverMovie.js
Config:
- Add sonar-project.properties with documented false positive suppressions:
- S8135: TMDB public API token (not a secret)
- S6680: Directory depth iteration (naturally bounded)
- S6674: NLog structured logging placeholder syntax
- S4662: PostCSS mixin directives
- S5145: Sanitized log data
* Fix SonarCloud bugs: threading, React state, sorting
Backend:
- S2445: Make _connections readonly in MessageHub.cs to fix locking issue
Frontend:
- S6756: Use callback form of setState when referencing previous state
- Collection.js, DiscoverMovie.js, ImportMovie.js
- ImportMovieSelectMovie.js, EditQualityProfileModalContentConnector.js
- S2871: Add localeCompare for proper alphabetical sorting
- Collection.js, DiscoverMovie.js, MovieIndex.tsx
- S1764: Remove duplicate condition in QualityProfileSelectInput.tsx
* fix: SonarCloud bugs batch 2
- S2445: Make _connections readonly for thread-safe locking (MessageHub.cs)
- S6756: Use setState callbacks for 5 React components
- S1764: Remove duplicate expression in QualityProfileSelectInput.tsx
- S2583: Remove unreachable conditions in NotificationDefinition.cs
- S2259: Fix null reference in Pushcut.cs
* 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 + 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>
* 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>
* perf: cache regex patterns in Parser.ToUrlSlug and FileNameBuilder.GetEditionToken (#82)
Co-authored-by: admin <admin@ardentleatherworks.com>
* fix: add null safety to LINQ First/Single calls (#83)
Co-authored-by: admin <admin@ardentleatherworks.com>
* fix(frontend): remove index from React keys in dynamic lists (#84)
Co-authored-by: admin <admin@ardentleatherworks.com>
* chore: update GitHub Actions and consolidate .editorconfig rules (#85)
Co-authored-by: admin <admin@ardentleatherworks.com>
* 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>
* 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>
* 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>
* 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>
* 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>
* 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>
* 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>
* 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>
* 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>
* 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>
* 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>
* 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>
* fix: resolve thread safety issues in ConfigService cache (#98)
Co-authored-by: admin <admin@ardentleatherworks.com>
* fix: add empty catch comment and SingleOrDefault safety (#99)
Co-authored-by: admin <admin@ardentleatherworks.com>
* fix: add null safety to QualityProfile First/Last methods (#100)
Co-authored-by: admin <admin@ardentleatherworks.com>
* fix: avoid redundant First() calls in BasicRepository (#101)
Co-authored-by: admin <admin@ardentleatherworks.com>
* fix(security): prevent path traversal and command injection (#102)
Co-authored-by: admin <admin@ardentleatherworks.com>
* fix(frontend): use ref to avoid stale movies closure in search (#103)
Co-authored-by: admin <admin@ardentleatherworks.com>
* fix(deps): remove obsolete System.Private.Uri package (#104)
Closes#28
Co-authored-by: admin <admin@ardentleatherworks.com>
* 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>
* 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>
* 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>
* 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>
* 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>
* [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>
* 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>
* 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>
* 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>
* [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>
* 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>
* 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>
* 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>
* 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>
* 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>
* 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>
* 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>
* 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>
* 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>
* 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>
* 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>
* 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>
* 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>
* 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>
* 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>
* ci(deps): bump dessant/lock-threads from 4 to 6 (#130)
Bumps [dessant/lock-threads](https://github.com/dessant/lock-threads) from 4 to 6.
- [Release notes](https://github.com/dessant/lock-threads/releases)
- [Changelog](https://github.com/dessant/lock-threads/blob/main/CHANGELOG.md)
- [Commits](https://github.com/dessant/lock-threads/compare/v4...v6)
---
updated-dependencies:
- dependency-name: dessant/lock-threads
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>
* fix: address SonarCloud code quality issues (#131)
* fix: address SonarCloud code quality issues
- Remove unused private fields from services and repositories
- Replace Object.assign with spread operator in Redux actions
- Use structuredClone instead of _.cloneDeep
- Add exception parameters to catch clause logging
- Use Number.parseInt instead of parseInt in detail pages
- Mark React component props as readonly
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* docs: update CHANGELOG with Phase 2 multi-media work
* fix: update labeler.yml for actions/labeler v6 format
---------
Co-authored-by: admin <admin@ardentleatherworks.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* feat(monitoring): implement hierarchical monitoring for Author/Series/Book/Audiobook (#132)
* feat(monitoring): implement hierarchical monitoring for Author/Series/Book/Audiobook
- Add cascade logic: unmonitoring parent cascades to children
- Re-monitoring parent does not auto-monitor children (explicit control)
- EffectivelyMonitored computed from item AND all ancestors
- Database indexes for efficient cascade queries (migration 248)
- AuthorMonitoringChangedEvent and SeriesMonitoringChangedEvent
- EffectivelyMonitored field added to Book/Audiobook API resources
Closes#2🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor(monitoring): reduce code duplication in HierarchicalMonitoringService
- Extract common ancestor check to IsAncestorUnmonitored helper
- Consolidate monitoring context retrieval to GetMonitoringContext
- Create generic UnmonitorEntities helper for cascade operations
- Reduce code from 302 to 233 lines while preserving all functionality
* ci(sonar): exclude intentional structural duplication from CPD
* ci(codeql): exclude user-controlled-bypass for monitoring cascade logic
---------
Co-authored-by: admin <admin@ardentleatherworks.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add statistics services and analytics dashboard (#133)
- Add BookStatistics and AudiobookStatistics services
- Create unified Dashboard with media type stats
- Add statistics to Book/Audiobook controllers
- Make dashboard the default landing page
Closes#6
Co-authored-by: admin <admin@ardentleatherworks.com>
* refactor(database): unify Book/Audiobook inheritance with MediaItem (#134)
Book and Audiobook now extend MediaItem instead of ModelBase,
eliminating duplicate properties and enabling cross-media operations.
- Remove duplicate MediaType, Monitored, Path, Tags, etc. from entities
- Implement abstract GetTitle() and GetYear() methods
- Add migration 249 (documentation only - no DB changes needed)
Co-authored-by: admin <admin@ardentleatherworks.com>
* feat(database): add Music entities and tables (#135)
Add foundation for Music support:
- Artist entity (parallel to Author)
- Album entity (parallel to Series)
- Track entity (extends MediaItem)
- MusicFile entity for audio files
- Migration 250 creates Artists, Albums, Tracks, MusicFiles tables
- Register all entities in TableMapping
Co-authored-by: admin <admin@ardentleatherworks.com>
* feat(music): add repositories and services (#136)
Add data access and business logic layers for Music support:
- ArtistRepository/ArtistService for artist management
- AlbumRepository/AlbumService for album management
- TrackRepository/TrackService for track management
- MusicFileRepository for audio file management
Co-authored-by: admin <admin@ardentleatherworks.com>
* refactor: extract BaseMediaService<T> base class (#137)
* refactor: extract BaseMediaService<T> base class
Extract common CRUD operations into BaseMediaService<T>:
- Get, GetAll, Paged, Add, AddMany, Delete, DeleteMany, Update, UpdateMany
- SetAddedTimestamp with reflection for non-MediaItem types
- Virtual event hooks (OnItemAdded, OnItemDeleted, etc.)
Migrate services to use base class:
- BookService: 180 → 89 lines
- AudiobookService: 192 → 93 lines
- AlbumService: 132 → 58 lines
- ArtistService: 107 → 50 lines
- TrackService: 114 → 50 lines
Net reduction: ~385 lines
* chore: cleanup stale files and fix branding
Remove IDE/editor config files that should not be tracked:
- .vscode/, frontend/.vscode/, src/.idea/
- azure-pipelines.yml (obsolete CI)
- Empty localization files (bs, ta, et, lt, sr, es_MX)
Remove unused npm packages:
- react-addons-shallow-compare
- react-async-script
Fix remaining Radarr→Aletheia branding:
- ConsoleApp.cs error messages
- openapi.json title/description/license
- FileNameBuilder.cs default release group
---------
Co-authored-by: admin <admin@ardentleatherworks.com>
* chore: P2/P3 code cleanup batch (#138)
- Remove unused RemoveTitle() methods from AlternativeTitleService, MovieTranslationService, CreditService
- Clean commented-out code blocks in EventAggregator, Pneumatic, MovieStatisticsFixture
- Consolidate 4 duplicate TagsModalContent.css files into shared Components/Styles module
- Fix BaseMediaService StyleCop violations (SA1127, SA1502)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: admin <admin@ardentleatherworks.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: extract BaseMediaCrudController for Book/Audiobook (#139)
- Create BaseMediaCrudController with common CRUD patterns
- Extract shared validation setup (path, quality, title)
- Move Create, Update, Delete endpoints to base class
- BookController: 219 -> 168 lines (-51)
- AudiobookController: 227 -> 177 lines (-50)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: admin <admin@ardentleatherworks.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: extract BaseMediaEditorController for bulk operations (#140)
- Create IEditorResource interface for common editor properties
- Create BaseMediaEditorController with common bulk edit/delete logic
- Extract tag handling (Add/Remove/Replace) to base class
- BookEditorController: 92 -> 36 lines (-56)
- AudiobookEditorController: 92 -> 36 lines (-56)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: admin <admin@ardentleatherworks.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: extract focused config services from ConfigService (#141)
Create DownloadConfigService and ImportConfigService as focused
interfaces for download-related and import-related configuration.
Uses delegation pattern for backward compatibility.
Co-authored-by: admin <admin@ardentleatherworks.com>
* refactor: extract UIConfigService and ProxyConfigService (#142)
Continue ConfigService split with UI and proxy-related settings.
Uses delegation pattern for backward compatibility.
Co-authored-by: admin <admin@ardentleatherworks.com>
* refactor: add IMediaResource interface for resource mapping consolidation (#143)
Create common interface for media resource properties (Id, Monitored,
QualityProfileId, Path, RootFolderPath, Added, Tags). BookResource,
AudiobookResource, and AuthorResource now implement IMediaResource.
Foundation for future resource mapping consolidation.
Co-authored-by: admin <admin@ardentleatherworks.com>
* feat(music): complete Music API layer with hierarchical monitoring (#144)
* feat(music): add events, statistics, and API resources
Foundation layer for Music API support:
- Events: Artist/Album/Track added/edited/deleted events
- Statistics: MusicStatistics with album-level tracking
- Resources: Artist, Album, Track, MusicFile, MusicStatistics DTOs
* feat(music): add validators and add services for artist and album
* feat(music): add main API controllers for artist, album, track, and music files
* feat(music): add editor and lookup controllers for artist and album
* feat(music): integrate hierarchical monitoring for artist/album/track
* fix: address SonarCloud static method and indexer issues
* fix: address SonarCloud code quality issues
- Add SuppressMessage for S107 (constructor params) on DI controllers
- Add SuppressMessage for S6968 (ASP.NET validation) on resource DTOs
- Use global:: prefix to avoid namespace conflicts
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: exclude Music API from duplication checks
Music resources/controllers follow same pattern as Books/Audiobooks.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: expand duplication exclusion to core Music files
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: add MusicStats to duplication exclusions
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: expand duplication exclusions to all media type directories
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: admin <admin@ardentleatherworks.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* New: Parse Group GiLG (#145)
Co-authored-by: TRaSH <trash-pm@protonmail.ch>
Co-authored-by: admin <admin@ardentleatherworks.com>
* feat(metadata): add Music and Book metadata providers (#146)
* New: Parse Group GiLG
* feat(metadata): add Music and Book metadata providers
- Add MusicBrainz proxy for Artist/Album/Track lookups
- Implement OpenLibrary integration for Book searches
- Support ISBN, title, author lookups for books
- Support MusicBrainz ID and name searches for music
* fix: address SonarCloud issues in metadata providers
---------
Co-authored-by: TRaSH <trash-pm@protonmail.ch>
Co-authored-by: admin <admin@ardentleatherworks.com>
* feat(music): comprehensive music quality support (#147)
* feat(music): add comprehensive music quality support
- Add MUSIC to QualitySource enum
- Add Music qualities (ID 300-310): MP3-128/192/256/320, AAC-256, OGG-320, FLAC, FLAC 24bit, WAV, ALAC
- Add DefaultQualityDefinitions for music (Weight 300+)
- Add music file extensions (.mp3, .flac, .wav, .ogg, .m4a, .aac, .alac, .ape, .wv, .dsf, .dff)
- Add ebook extensions (.epub, .mobi, .azw3, .pdf, .txt, .djvu, .cbr, .cbz)
- Add audiobook extensions (.m4b, .aa, .aax)
- Add MediaFileExtensions helper properties (MusicExtensions, EbookExtensions, AudiobookExtensions)
- Create MusicQualityParser for bitrate/format/bit-depth detection
- Create BookQualityParser for ebook format detection
- Create AudiobookQualityParser for audiobook format/bitrate detection
* feat(music): comprehensive quality system with full granularity
Expand music quality system to 60+ distinct quality levels:
Lossy (IDs 300-315):
- MP3: 128/192/256/320 kbps
- AAC: 128/256/320 kbps
- OGG: 128/192/256/320 kbps
- Opus: 128/192/256 kbps
- WMA
Lossless FLAC (IDs 320-327):
- 16/44.1, 16/48, 24/44.1, 24/48, 24/88.2, 24/96, 24/176.4, 24/192
- 24/96 designated as target quality
Lossless WAV (IDs 340-347):
- Same bit-depth/sample-rate variants as FLAC
Lossless AIFF (IDs 350-357):
- Same bit-depth/sample-rate variants as FLAC
DSD (IDs 360-363):
- DSD64 (2.8MHz), DSD128 (5.6MHz), DSD256 (11.2MHz), DSD512 (22.4MHz)
Other Lossless (IDs 370-377):
- ALAC: 16/44.1, 16/48, 24/44.1, 24/48, 24/96, 24/192
- APE (Monkey's Audio), WavPack
Special (IDs 380-381):
- MQA, MQA Studio
Add MusicFileAnalyzer service:
- Uses ffprobe for accurate metadata detection
- Extracts bit depth, sample rate, codec, bitrate
- Maps to appropriate quality based on actual file properties
Update MusicQualityParser:
- Comprehensive regex patterns for all formats
- Bit-depth/sample-rate detection from filenames
- DSD variant detection
- MQA detection
- Hi-Res keyword recognition
* fix(music): address SonarCloud code quality issues
- S1172: Use unused codec/ext parameters in format detection
- S6667: Pass exceptions to logger in catch blocks
- S1192: Extract duplicate strings to constants
- S3776: Reduce cognitive complexity via method extraction
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: address additional SonarCloud issues in music module
- S1192: Extract string constants in MusicBrainzProxy and BookInfoProxy
- S2325: Make LinkAlbumStatistics static in AlbumController
- S4136: Reorder method overloads to be adjacent in Resource files
- S6964: Use nullable value types in Resource classes where appropriate
* fix(parser): reduce cognitive complexity in AudiobookQualityParser
Extract format and bitrate parsing into separate methods to reduce
cognitive complexity of ParseQualityName from 20 to under 15.
* fix(security): add regex timeout to quality parsers
Add 5-second timeout to all Regex patterns in AudiobookQualityParser,
BookQualityParser, and MusicQualityParser to prevent ReDoS attacks.
---------
Co-authored-by: admin <admin@ardentleatherworks.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* docs: update changelog with Phase 3-6 work
- Add Phase 3 (Multi-Media Foundation) entries
- Add Phase 4 (Books & Audiobooks) entries
- Add Phase 6 (Music Foundation) entries with 60+ quality definitions
- Document SonarCloud fixes from PR #147
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Robin Dadswell <19610103+RobinDadswell@users.noreply.github.com>
Co-authored-by: Stevie Robinson <stevie.robinson@gmail.com>
Co-authored-by: plz12345 <132735020+plz12345@users.noreply.github.com>
Co-authored-by: Erik Frantz <39980629+BardezAnAvatar@users.noreply.github.com>
Co-authored-by: Bogdan <mynameisbogdan@users.noreply.github.com>
Co-authored-by: admin <admin@ardentleatherworks.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: cheir-mneme <176430037+cheir-mneme@users.noreply.github.com>
Co-authored-by: TRaSH <trash-pm@protonmail.ch>
* New: Parse Group GiLG
* feat(metadata): add Music and Book metadata providers
- Add MusicBrainz proxy for Artist/Album/Track lookups
- Implement OpenLibrary integration for Book searches
- Support ISBN, title, author lookups for books
- Support MusicBrainz ID and name searches for music
* fix: address SonarCloud issues in metadata providers
---------
Co-authored-by: TRaSH <trash-pm@protonmail.ch>
Co-authored-by: admin <admin@ardentleatherworks.com>
* feat(music): add events, statistics, and API resources
Foundation layer for Music API support:
- Events: Artist/Album/Track added/edited/deleted events
- Statistics: MusicStatistics with album-level tracking
- Resources: Artist, Album, Track, MusicFile, MusicStatistics DTOs
* feat(music): add validators and add services for artist and album
* feat(music): add main API controllers for artist, album, track, and music files
* feat(music): add editor and lookup controllers for artist and album
* feat(music): integrate hierarchical monitoring for artist/album/track
* fix: address SonarCloud static method and indexer issues
* fix: address SonarCloud code quality issues
- Add SuppressMessage for S107 (constructor params) on DI controllers
- Add SuppressMessage for S6968 (ASP.NET validation) on resource DTOs
- Use global:: prefix to avoid namespace conflicts
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: exclude Music API from duplication checks
Music resources/controllers follow same pattern as Books/Audiobooks.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: expand duplication exclusion to core Music files
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: add MusicStats to duplication exclusions
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: expand duplication exclusions to all media type directories
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: admin <admin@ardentleatherworks.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Create common interface for media resource properties (Id, Monitored,
QualityProfileId, Path, RootFolderPath, Added, Tags). BookResource,
AudiobookResource, and AuthorResource now implement IMediaResource.
Foundation for future resource mapping consolidation.
Co-authored-by: admin <admin@ardentleatherworks.com>
Create DownloadConfigService and ImportConfigService as focused
interfaces for download-related and import-related configuration.
Uses delegation pattern for backward compatibility.
Co-authored-by: admin <admin@ardentleatherworks.com>
- Create IEditorResource interface for common editor properties
- Create BaseMediaEditorController with common bulk edit/delete logic
- Extract tag handling (Add/Remove/Replace) to base class
- BookEditorController: 92 -> 36 lines (-56)
- AudiobookEditorController: 92 -> 36 lines (-56)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: admin <admin@ardentleatherworks.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Add data access and business logic layers for Music support:
- ArtistRepository/ArtistService for artist management
- AlbumRepository/AlbumService for album management
- TrackRepository/TrackService for track management
- MusicFileRepository for audio file management
Co-authored-by: admin <admin@ardentleatherworks.com>
Add foundation for Music support:
- Artist entity (parallel to Author)
- Album entity (parallel to Series)
- Track entity (extends MediaItem)
- MusicFile entity for audio files
- Migration 250 creates Artists, Albums, Tracks, MusicFiles tables
- Register all entities in TableMapping
Co-authored-by: admin <admin@ardentleatherworks.com>
Book and Audiobook now extend MediaItem instead of ModelBase,
eliminating duplicate properties and enabling cross-media operations.
- Remove duplicate MediaType, Monitored, Path, Tags, etc. from entities
- Implement abstract GetTitle() and GetYear() methods
- Add migration 249 (documentation only - no DB changes needed)
Co-authored-by: admin <admin@ardentleatherworks.com>
- Add BookStatistics and AudiobookStatistics services
- Create unified Dashboard with media type stats
- Add statistics to Book/Audiobook controllers
- Make dashboard the default landing page
Closes#6
Co-authored-by: admin <admin@ardentleatherworks.com>
* feat(monitoring): implement hierarchical monitoring for Author/Series/Book/Audiobook
- Add cascade logic: unmonitoring parent cascades to children
- Re-monitoring parent does not auto-monitor children (explicit control)
- EffectivelyMonitored computed from item AND all ancestors
- Database indexes for efficient cascade queries (migration 248)
- AuthorMonitoringChangedEvent and SeriesMonitoringChangedEvent
- EffectivelyMonitored field added to Book/Audiobook API resources
Closes#2🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor(monitoring): reduce code duplication in HierarchicalMonitoringService
- Extract common ancestor check to IsAncestorUnmonitored helper
- Consolidate monitoring context retrieval to GetMonitoringContext
- Create generic UnmonitorEntities helper for cascade operations
- Reduce code from 302 to 233 lines while preserving all functionality
* ci(sonar): exclude intentional structural duplication from CPD
* ci(codeql): exclude user-controlled-bypass for monitoring cascade logic
---------
Co-authored-by: admin <admin@ardentleatherworks.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* fix: address SonarCloud code quality issues
- Remove unused private fields from services and repositories
- Replace Object.assign with spread operator in Redux actions
- Use structuredClone instead of _.cloneDeep
- Add exception parameters to catch clause logging
- Use Number.parseInt instead of parseInt in detail pages
- Mark React component props as readonly
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* docs: update CHANGELOG with Phase 2 multi-media work
* fix: update labeler.yml for actions/labeler v6 format
---------
Co-authored-by: admin <admin@ardentleatherworks.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* 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>
* 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>
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>
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>
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>
* 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>
* 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>
* 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>
* 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>
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>
* 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>
* 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>
PerlRegexFactory: static Regex field should be readonly to
prevent accidental reassignment.
Closes#36
Co-authored-by: admin <admin@ardentleatherworks.com>
Convert blocking GetAwaiter().GetResult() to proper await pattern
in the API controller method.
Partial fix for #32
Co-authored-by: admin <admin@ardentleatherworks.com>
- Remove continue-on-error from integration tests
- Set fail-on-error: true on test reporter
Closes#56
Co-authored-by: admin <admin@ardentleatherworks.com>