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
This commit is contained in:
admin 2025-12-18 13:19:16 -06:00
parent 09b1d24ce8
commit 86faa9aef7
8 changed files with 203 additions and 9 deletions

View file

@ -6,7 +6,7 @@ All-in-one media manager for movies, books, and audiobooks.
Aletheia (from Greek ἀλήθεια - "truth, disclosure") is a unified media management system forked from Radarr. It provides automated monitoring, downloading, and library management for multiple media types through a single interface.
**Current Status:** Early development. Movie functionality inherited from Radarr is working. Book and audiobook support is planned.
**Current Status:** Active development. Movie functionality inherited from Radarr is working. Multi-media foundation being implemented.
## Features
@ -15,20 +15,21 @@ Aletheia (from Greek ἀλήθεια - "truth, disclosure") is a unified media m
- Metadata and artwork management
- Integration with download clients and indexers
**Books (planned):**
**Books (in development):**
- EPUB, MOBI, PDF quality tracking
- Author and series hierarchy
- Goodreads/Hardcover metadata
**Audiobooks (planned):**
**Audiobooks (in development):**
- M4B, MP3, FLAC support
- Narrator tracking and duration metadata
- Audible metadata integration
- Narrator tracking (competitive differentiator)
- Duration metadata and Audible integration
**General:**
- Usenet and BitTorrent support
- SABnzbd, NZBGet, qBittorrent, Deluge, rTorrent, Transmission integration
- Plex and Kodi integration
- Built-in archive extraction (Unpackerr functionality)
## Privacy
@ -69,13 +70,40 @@ dotnet run --project src/Radarr
## Roadmap
1. **Foundation** - Generalize database schema, implement hierarchical monitoring (Author → Series → Item)
2. **Multi-Media** - Add book and audiobook quality profiles, port metadata providers
3. **Interface** - Unified dashboard with type filters, type-specific detail views
See [ROADMAP.md](../ROADMAP.md) for detailed phase planning.
**Completed:**
- Phase 0-1: Privacy & security fixes
- Phase 2: Foundation (fork, CI/CD, branding)
- Phase 2.5: Community standards, quality gates, Unpackerr absorption
**Current:**
- Phase 3: Multi-media foundation (database generalization, indexer management)
**Planned:**
- Phase 4: Books & audiobooks support
- Phase 5: TV shows
- Phase 6: Music (with fingerprinting and quality analysis)
- Phase 7: Subtitles (Bazarr replacement), podcasts, comics
## Key Differences from Radarr
| Feature | Radarr | Aletheia |
|---------|--------|----------|
| Media types | Movies only | Movies, books, audiobooks (planned: TV, music, podcasts) |
| Telemetry | Enabled by default | Disabled by default |
| Indexer management | External (Prowlarr) | Built-in (planned) |
| Archive extraction | External (Unpackerr) | Built-in |
| Narrator tracking | N/A | Native support for audiobooks |
## Contributing
Early development phase. See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and code guidelines.
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, code guidelines, and PR process.
**Development standards:**
- Conventional commits (`feat:`, `fix:`, `docs:`, etc.)
- Feature branches + PRs to `develop`
- Pre-commit hooks for linting
## License

View file

@ -0,0 +1,14 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(243)]
public class add_mediatype_to_indexers : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Alter.Table("Indexers").AddColumn("SupportedMediaTypes").AsString().WithDefaultValue("[\"Movie\"]");
}
}
}

View file

@ -26,6 +26,7 @@
using NzbDrone.Core.Jobs;
using NzbDrone.Core.Languages;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaTypes;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Movies.AlternativeTitles;
@ -202,6 +203,7 @@ private static void RegisterMappers()
SqlMapper.AddTypeHandler(new DapperLanguageIntConverter());
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<Language>>(new LanguageIntConverter()));
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<string>>());
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<MediaType>>());
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<ParsedMovieInfo>(new QualityIntConverter(), new LanguageIntConverter()));
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<ReleaseInfo>());
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<PendingReleaseAdditionalInfo>());

View file

@ -2,6 +2,7 @@
using System.Threading.Tasks;
using NzbDrone.Common.Http;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.MediaTypes;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.ThingiProvider;
@ -12,6 +13,7 @@ public interface IIndexer : IProvider
bool SupportsRss { get; }
bool SupportsSearch { get; }
DownloadProtocol Protocol { get; }
IEnumerable<MediaType> SupportedMediaTypes { get; }
Task<IList<ReleaseInfo>> FetchRecent();
Task<IList<ReleaseInfo>> Fetch(MovieSearchCriteria searchCriteria);

View file

@ -9,6 +9,7 @@
using NzbDrone.Core.Configuration;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Languages;
using NzbDrone.Core.MediaTypes;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.ThingiProvider;
@ -29,6 +30,7 @@ public abstract class IndexerBase<TSettings> : IIndexer
public abstract bool SupportsRss { get; }
public abstract bool SupportsSearch { get; }
public virtual IEnumerable<MediaType> SupportedMediaTypes => new[] { MediaType.Movie };
public IndexerBase(IIndexerStatusService indexerStatusService, IConfigService configService, IParsingService parsingService, Logger logger)
{

View file

@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using Equ;
using NzbDrone.Core.MediaTypes;
using NzbDrone.Core.ThingiProvider;
namespace NzbDrone.Core.Indexers
@ -13,6 +15,7 @@ public class IndexerDefinition : ProviderDefinition, IEquatable<IndexerDefinitio
public IndexerDefinition()
{
Priority = DefaultPriority;
SupportedMediaTypes = new List<MediaType> { MediaType.Movie };
}
[MemberwiseEqualityIgnore]
@ -29,6 +32,7 @@ public IndexerDefinition()
public bool EnableInteractiveSearch { get; set; }
public int DownloadClientId { get; set; }
public int Priority { get; set; }
public List<MediaType> SupportedMediaTypes { get; set; }
[MemberwiseEqualityIgnore]
public override bool Enable => EnableRss || EnableAutomaticSearch || EnableInteractiveSearch;

View file

@ -0,0 +1,128 @@
using System.Collections.Generic;
using NzbDrone.Core.MediaTypes;
namespace NzbDrone.Core.Indexers
{
public static class NewznabStandardCategory
{
public static readonly int Console = 1000;
public static readonly int ConsoleNDS = 1010;
public static readonly int ConsolePSP = 1020;
public static readonly int ConsoleWii = 1030;
public static readonly int ConsoleXbox = 1040;
public static readonly int ConsoleXbox360 = 1050;
public static readonly int ConsoleWiiware = 1060;
public static readonly int ConsoleXbox360DLC = 1070;
public static readonly int ConsolePS3 = 1080;
public static readonly int ConsoleOther = 1090;
public static readonly int Console3DS = 1110;
public static readonly int ConsolePSVita = 1120;
public static readonly int ConsoleWiiU = 1130;
public static readonly int ConsoleXboxOne = 1140;
public static readonly int ConsolePS4 = 1180;
public static readonly int Movies = 2000;
public static readonly int MoviesForeign = 2010;
public static readonly int MoviesOther = 2020;
public static readonly int MoviesSD = 2030;
public static readonly int MoviesHD = 2040;
public static readonly int MoviesUHD = 2045;
public static readonly int MoviesBluRay = 2050;
public static readonly int Movies3D = 2060;
public static readonly int MoviesDVD = 2070;
public static readonly int MoviesWEBDL = 2080;
public static readonly int Audio = 3000;
public static readonly int AudioMP3 = 3010;
public static readonly int AudioVideo = 3020;
public static readonly int AudioAudiobook = 3030;
public static readonly int AudioLossless = 3040;
public static readonly int AudioOther = 3050;
public static readonly int AudioForeign = 3060;
public static readonly int PC = 4000;
public static readonly int PC0day = 4010;
public static readonly int PCISO = 4020;
public static readonly int PCMac = 4030;
public static readonly int PCMobileOther = 4040;
public static readonly int PCGames = 4050;
public static readonly int PCMobileiOS = 4060;
public static readonly int PCMobileAndroid = 4070;
public static readonly int TV = 5000;
public static readonly int TVWEBDL = 5010;
public static readonly int TVForeign = 5020;
public static readonly int TVSD = 5030;
public static readonly int TVHD = 5040;
public static readonly int TVUHD = 5045;
public static readonly int TVOther = 5050;
public static readonly int TVSport = 5060;
public static readonly int TVAnime = 5070;
public static readonly int TVDocumentary = 5080;
public static readonly int XXX = 6000;
public static readonly int XXXDVD = 6010;
public static readonly int XXXWMV = 6020;
public static readonly int XXXXviD = 6030;
public static readonly int XXXx264 = 6040;
public static readonly int XXXOther = 6050;
public static readonly int XXXImageset = 6060;
public static readonly int XXXPacks = 6070;
public static readonly int Books = 7000;
public static readonly int BooksMags = 7010;
public static readonly int BooksEBook = 7020;
public static readonly int BooksComics = 7030;
public static readonly int BooksTechnical = 7040;
public static readonly int BooksOther = 7050;
public static readonly int BooksForeign = 7060;
public static readonly int Other = 8000;
public static readonly int OtherMisc = 8010;
public static readonly int OtherHashed = 8020;
public static IReadOnlyList<int> GetCategoriesForMediaType(MediaType mediaType)
{
return mediaType switch
{
MediaType.Movie => new[]
{
Movies, MoviesForeign, MoviesOther, MoviesSD, MoviesHD,
MoviesUHD, MoviesBluRay, Movies3D, MoviesDVD, MoviesWEBDL
},
MediaType.TV => new[]
{
TV, TVWEBDL, TVForeign, TVSD, TVHD, TVUHD,
TVOther, TVSport, TVAnime, TVDocumentary
},
MediaType.Music => new[]
{
Audio, AudioMP3, AudioVideo, AudioLossless, AudioOther, AudioForeign
},
MediaType.Audiobook => new[] { AudioAudiobook, Audio },
MediaType.Book => new[]
{
Books, BooksMags, BooksEBook, BooksTechnical, BooksOther, BooksForeign
},
MediaType.Comic => new[] { BooksComics, Books },
MediaType.Podcast => new[] { Audio, AudioOther },
_ => new[] { Movies }
};
}
public static IReadOnlyList<int> GetIgnoredCategoriesForMediaType(MediaType mediaType)
{
return mediaType switch
{
MediaType.Movie => new[] { Console, Audio, PC, XXX, Books },
MediaType.TV => new[] { Console, Audio, PC, Movies, XXX, Books },
MediaType.Music => new[] { Console, PC, Movies, XXX, Books, TV },
MediaType.Audiobook => new[] { Console, PC, Movies, XXX, TV },
MediaType.Book => new[] { Console, Audio, PC, Movies, XXX, TV },
MediaType.Comic => new[] { Console, Audio, PC, Movies, XXX, TV },
MediaType.Podcast => new[] { Console, PC, Movies, XXX, Books, TV },
_ => new[] { Console, Audio, PC, XXX, Books }
};
}
}
}

View file

@ -0,0 +1,14 @@
namespace NzbDrone.Core.MediaTypes
{
public enum MediaType
{
Unknown = 0,
Movie = 1,
TV = 2,
Music = 3,
Book = 4,
Audiobook = 5,
Podcast = 6,
Comic = 7
}
}