mirror of https://github.com/ErsatzTV/ErsatzTV.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
284 lines
14 KiB
284 lines
14 KiB
using Bugsnag; |
|
using Bugsnag.Payload; |
|
using Dapper; |
|
using ErsatzTV.Core; |
|
using ErsatzTV.Core.Emby; |
|
using ErsatzTV.Core.FFmpeg; |
|
using ErsatzTV.Core.Interfaces.Emby; |
|
using ErsatzTV.Core.Interfaces.FFmpeg; |
|
using ErsatzTV.Core.Interfaces.Images; |
|
using ErsatzTV.Core.Interfaces.Jellyfin; |
|
using ErsatzTV.Core.Interfaces.Metadata; |
|
using ErsatzTV.Core.Interfaces.Plex; |
|
using ErsatzTV.Core.Interfaces.Repositories; |
|
using ErsatzTV.Core.Interfaces.Repositories.Caching; |
|
using ErsatzTV.Core.Interfaces.Search; |
|
using ErsatzTV.Core.Jellyfin; |
|
using ErsatzTV.Core.Metadata; |
|
using ErsatzTV.Core.Plex; |
|
using ErsatzTV.FFmpeg.Runtime; |
|
using ErsatzTV.Infrastructure.Data; |
|
using ErsatzTV.Infrastructure.Data.Repositories; |
|
using ErsatzTV.Infrastructure.Data.Repositories.Caching; |
|
using ErsatzTV.Infrastructure.Emby; |
|
using ErsatzTV.Infrastructure.Images; |
|
using ErsatzTV.Infrastructure.Jellyfin; |
|
using ErsatzTV.Infrastructure.Metadata; |
|
using ErsatzTV.Infrastructure.Plex; |
|
using ErsatzTV.Infrastructure.Runtime; |
|
using ErsatzTV.Infrastructure.Search; |
|
using ErsatzTV.Infrastructure.Sqlite.Data; |
|
using ErsatzTV.Scanner.Core.Emby; |
|
using ErsatzTV.Scanner.Core.FFmpeg; |
|
using ErsatzTV.Scanner.Core.Interfaces.FFmpeg; |
|
using ErsatzTV.Scanner.Core.Interfaces.Metadata; |
|
using ErsatzTV.Scanner.Core.Interfaces.Metadata.Nfo; |
|
using ErsatzTV.Scanner.Core.Jellyfin; |
|
using ErsatzTV.Scanner.Core.Metadata; |
|
using ErsatzTV.Scanner.Core.Metadata.Nfo; |
|
using ErsatzTV.Scanner.Core.Plex; |
|
using Microsoft.EntityFrameworkCore; |
|
using Microsoft.Extensions.Configuration; |
|
using Microsoft.Extensions.DependencyInjection; |
|
using Microsoft.Extensions.Hosting; |
|
using Microsoft.IO; |
|
using Serilog; |
|
using Serilog.Events; |
|
using Serilog.Formatting.Compact; |
|
using Exception = System.Exception; |
|
using IConfiguration = Bugsnag.IConfiguration; |
|
|
|
namespace ErsatzTV.Scanner; |
|
|
|
public class Program |
|
{ |
|
public static async Task<int> Main(string[] args) |
|
{ |
|
Log.Logger = new LoggerConfiguration() |
|
.MinimumLevel.Debug() |
|
.MinimumLevel.Override("Microsoft", LogEventLevel.Error) |
|
.Enrich.FromLogContext() |
|
.WriteTo.Console(new CompactJsonFormatter(), standardErrorFromLevel: LogEventLevel.Debug) |
|
.CreateLogger(); |
|
|
|
try |
|
{ |
|
await CreateHostBuilder(args).Build().RunAsync(); |
|
return 0; |
|
} |
|
catch (Exception ex) |
|
{ |
|
Log.Fatal(ex, "ErsatzTV.Scanner host terminated unexpectedly"); |
|
return 1; |
|
} |
|
finally |
|
{ |
|
await Log.CloseAndFlushAsync(); |
|
} |
|
} |
|
|
|
private static IHostBuilder CreateHostBuilder(string[] args) => |
|
Host.CreateDefaultBuilder(args) |
|
.ConfigureServices( |
|
(context, services) => |
|
{ |
|
string databaseProvider = context.Configuration.GetValue("provider", Provider.Sqlite.Name) ?? |
|
string.Empty; |
|
var sqliteConnectionString = $"Data Source={FileSystemLayout.DatabasePath};foreign keys=true;"; |
|
string mySqlConnectionString = |
|
context.Configuration.GetValue<string>("MySql:ConnectionString") ?? string.Empty; |
|
|
|
services.AddDbContext<TvContext>( |
|
options => |
|
{ |
|
if (databaseProvider == Provider.Sqlite.Name) |
|
{ |
|
options.UseSqlite( |
|
sqliteConnectionString, |
|
o => |
|
{ |
|
o.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery); |
|
o.MigrationsAssembly("ErsatzTV.Infrastructure.Sqlite"); |
|
}); |
|
} |
|
|
|
if (databaseProvider == Provider.MySql.Name) |
|
{ |
|
options.UseMySql( |
|
mySqlConnectionString, |
|
ServerVersion.AutoDetect(mySqlConnectionString), |
|
o => |
|
{ |
|
o.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery); |
|
o.MigrationsAssembly("ErsatzTV.Infrastructure.MySql"); |
|
} |
|
); |
|
} |
|
}, |
|
ServiceLifetime.Scoped, |
|
ServiceLifetime.Singleton); |
|
|
|
services.AddDbContextFactory<TvContext>( |
|
options => |
|
{ |
|
if (databaseProvider == Provider.Sqlite.Name) |
|
{ |
|
options.UseSqlite( |
|
sqliteConnectionString, |
|
o => |
|
{ |
|
o.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery); |
|
o.MigrationsAssembly("ErsatzTV.Infrastructure.Sqlite"); |
|
}); |
|
} |
|
|
|
if (databaseProvider == Provider.MySql.Name) |
|
{ |
|
options.UseMySql( |
|
mySqlConnectionString, |
|
ServerVersion.AutoDetect(mySqlConnectionString), |
|
o => |
|
{ |
|
o.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery); |
|
o.MigrationsAssembly("ErsatzTV.Infrastructure.MySql"); |
|
} |
|
); |
|
} |
|
}); |
|
|
|
if (databaseProvider == Provider.Sqlite.Name) |
|
{ |
|
TvContext.LastInsertedRowId = "last_insert_rowid()"; |
|
TvContext.CaseInsensitiveCollation = "NOCASE"; |
|
|
|
SqlMapper.AddTypeHandler(new DateTimeOffsetHandler()); |
|
SqlMapper.AddTypeHandler(new GuidHandler()); |
|
SqlMapper.AddTypeHandler(new TimeSpanHandler()); |
|
} |
|
|
|
if (databaseProvider == Provider.MySql.Name) |
|
{ |
|
TvContext.LastInsertedRowId = "last_insert_id()"; |
|
TvContext.CaseInsensitiveCollation = "utf8mb4_general_ci"; |
|
} |
|
|
|
services.AddScoped<IConfigElementRepository, ConfigElementRepository>(); |
|
services.AddScoped<IMetadataRepository, MetadataRepository>(); |
|
services.AddScoped<IMediaSourceRepository, MediaSourceRepository>(); |
|
services.AddScoped<IMediaItemRepository, MediaItemRepository>(); |
|
services.AddScoped<IMovieRepository, MovieRepository>(); |
|
services.AddScoped<ITelevisionRepository, TelevisionRepository>(); |
|
services.AddScoped<IArtistRepository, ArtistRepository>(); |
|
services.AddScoped<IMusicVideoRepository, MusicVideoRepository>(); |
|
services.AddScoped<IOtherVideoRepository, OtherVideoRepository>(); |
|
services.AddScoped<ISongRepository, SongRepository>(); |
|
services.AddScoped<IImageRepository, ImageRepository>(); |
|
services.AddScoped<ILibraryRepository, LibraryRepository>(); |
|
services.AddScoped<ISearchRepository, SearchRepository>(); |
|
services.AddScoped<ICachingSearchRepository, CachingSearchRepository>(); |
|
services.AddScoped<ILocalMetadataProvider, LocalMetadataProvider>(); |
|
services.AddScoped<IFallbackMetadataProvider, FallbackMetadataProvider>(); |
|
services.AddScoped<ILocalStatisticsProvider, LocalStatisticsProvider>(); |
|
services.AddScoped<ILocalSubtitlesProvider, LocalSubtitlesProvider>(); |
|
services.AddScoped<IImageCache, ImageCache>(); |
|
services.AddScoped<ILocalFileSystem, LocalFileSystem>(); |
|
services.AddScoped<IMovieFolderScanner, MovieFolderScanner>(); |
|
services.AddScoped<ITelevisionFolderScanner, TelevisionFolderScanner>(); |
|
services.AddScoped<IMusicVideoFolderScanner, MusicVideoFolderScanner>(); |
|
services.AddScoped<IOtherVideoFolderScanner, OtherVideoFolderScanner>(); |
|
services.AddScoped<ISongFolderScanner, SongFolderScanner>(); |
|
services.AddScoped<IImageFolderScanner, ImageFolderScanner>(); |
|
services.AddScoped<IEpisodeNfoReader, EpisodeNfoReader>(); |
|
services.AddScoped<IMovieNfoReader, MovieNfoReader>(); |
|
services.AddScoped<IArtistNfoReader, ArtistNfoReader>(); |
|
services.AddScoped<IMusicVideoNfoReader, MusicVideoNfoReader>(); |
|
services.AddScoped<IShowNfoReader, ShowNfoReader>(); |
|
services.AddScoped<IOtherVideoNfoReader, OtherVideoNfoReader>(); |
|
services.AddScoped<IFFmpegPngService, FFmpegPngService>(); |
|
services.AddScoped<IRuntimeInfo, RuntimeInfo>(); |
|
|
|
services.AddScoped<IPlexMovieLibraryScanner, PlexMovieLibraryScanner>(); |
|
services.AddScoped<IPlexOtherVideoLibraryScanner, PlexOtherVideoLibraryScanner>(); |
|
services.AddScoped<IPlexTelevisionLibraryScanner, PlexTelevisionLibraryScanner>(); |
|
services.AddScoped<IPlexCollectionScanner, PlexCollectionScanner>(); |
|
services.AddScoped<IPlexServerApiClient, PlexServerApiClient>(); |
|
services.AddScoped<IPlexCollectionRepository, PlexCollectionRepository>(); |
|
services.AddScoped<IPlexMovieRepository, PlexMovieRepository>(); |
|
services.AddScoped<IPlexOtherVideoRepository, PlexOtherVideoRepository>(); |
|
services.AddScoped<IPlexTelevisionRepository, PlexTelevisionRepository>(); |
|
services.AddScoped<IPlexPathReplacementService, PlexPathReplacementService>(); |
|
services.AddScoped<PlexEtag>(); |
|
|
|
services.AddScoped<IEmbyMovieLibraryScanner, EmbyMovieLibraryScanner>(); |
|
services.AddScoped<IEmbyTelevisionLibraryScanner, EmbyTelevisionLibraryScanner>(); |
|
services.AddScoped<IEmbyCollectionScanner, EmbyCollectionScanner>(); |
|
services.AddScoped<IEmbyApiClient, EmbyApiClient>(); |
|
services.AddScoped<IEmbyCollectionRepository, EmbyCollectionRepository>(); |
|
services.AddScoped<IEmbyMovieRepository, EmbyMovieRepository>(); |
|
services.AddScoped<IEmbyTelevisionRepository, EmbyTelevisionRepository>(); |
|
services.AddScoped<IEmbyPathReplacementService, EmbyPathReplacementService>(); |
|
|
|
services.AddScoped<IJellyfinMovieLibraryScanner, JellyfinMovieLibraryScanner>(); |
|
services.AddScoped<IJellyfinTelevisionLibraryScanner, JellyfinTelevisionLibraryScanner>(); |
|
services.AddScoped<IJellyfinCollectionScanner, JellyfinCollectionScanner>(); |
|
services.AddScoped<IJellyfinApiClient, JellyfinApiClient>(); |
|
services.AddScoped<IJellyfinCollectionRepository, JellyfinCollectionRepository>(); |
|
services.AddScoped<IJellyfinMovieRepository, JellyfinMovieRepository>(); |
|
services.AddScoped<IJellyfinTelevisionRepository, JellyfinTelevisionRepository>(); |
|
services.AddScoped<IJellyfinPathReplacementService, JellyfinPathReplacementService>(); |
|
|
|
services.AddSingleton<ITempFilePool, TempFilePool>(); |
|
services.AddSingleton<IPlexSecretStore, PlexSecretStore>(); |
|
services.AddSingleton<IEmbySecretStore, EmbySecretStore>(); |
|
services.AddSingleton<IJellyfinSecretStore, JellyfinSecretStore>(); |
|
services.AddSingleton<ISearchIndex, LuceneSearchIndex>(); |
|
services.AddSingleton<RecyclableMemoryStreamManager>(); |
|
// TODO: real bugsnag? |
|
services.AddSingleton<IClient>(_ => new BugsnagNoopClient()); |
|
|
|
services.AddMediatR(config => config.RegisterServicesFromAssemblyContaining<Worker>()); |
|
services.AddMemoryCache(); |
|
|
|
services.AddHostedService<Worker>(); |
|
}) |
|
.UseSerilog(); |
|
|
|
private class BugsnagNoopClient : IClient |
|
{ |
|
public void Notify(Exception exception) |
|
{ |
|
} |
|
|
|
public void Notify(Exception exception, Middleware callback) |
|
{ |
|
} |
|
|
|
public void Notify(Exception exception, Severity severity) |
|
{ |
|
} |
|
|
|
public void Notify(Exception exception, Severity severity, Middleware callback) |
|
{ |
|
} |
|
|
|
public void Notify(Exception exception, HandledState handledState) |
|
{ |
|
} |
|
|
|
public void Notify(Exception exception, HandledState handledState, Middleware callback) |
|
{ |
|
} |
|
|
|
public void Notify(Report report, Middleware callback) |
|
{ |
|
} |
|
|
|
public void BeforeNotify(Middleware middleware) |
|
{ |
|
} |
|
|
|
public IBreadcrumbs Breadcrumbs => new Breadcrumbs(Configuration); |
|
public ISessionTracker SessionTracking => new SessionTracker(Configuration); |
|
public IConfiguration Configuration => new Configuration(); |
|
} |
|
}
|
|
|