Stream custom live channels using your own media
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.
 
 
 

270 lines
10 KiB

using System.CommandLine;
using System.Diagnostics;
using ErsatzTV.Scanner.Application.Emby;
using ErsatzTV.Scanner.Application.Jellyfin;
using ErsatzTV.Scanner.Application.MediaSources;
using ErsatzTV.Scanner.Application.Plex;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace ErsatzTV.Scanner;
public class Worker : BackgroundService
{
private readonly IHostApplicationLifetime _appLifetime;
private readonly ILogger<Worker> _logger;
private readonly IServiceScopeFactory _serviceScopeFactory;
public Worker(
IServiceScopeFactory serviceScopeFactory,
IHostApplicationLifetime appLifetime,
ILogger<Worker> logger)
{
_serviceScopeFactory = serviceScopeFactory;
_appLifetime = appLifetime;
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
RootCommand rootCommand = ConfigureCommandLine();
// need to strip program name (head) from command line args
string[] arguments = Environment.GetCommandLineArgs().Skip(1).ToArray();
ParseResult parseResult = rootCommand.Parse(arguments);
await parseResult.InvokeAsync(stoppingToken);
_appLifetime.StopApplication();
}
private RootCommand ConfigureCommandLine()
{
var forceOption = new System.CommandLine.Option<bool>("--force")
{
AllowMultipleArgumentsPerToken = true,
Arity = ArgumentArity.Zero,
Description = "Force scanning",
DefaultValueFactory = _ => false
};
var deepOption = new System.CommandLine.Option<bool>("--deep")
{
AllowMultipleArgumentsPerToken = true,
Arity = ArgumentArity.Zero,
Description = "Deep scan",
DefaultValueFactory = _ => false
};
var libraryIdArgument = new Argument<int>("library-id")
{
Description = "The library id to scan"
};
var mediaSourceIdArgument = new Argument<int>("media-source-id")
{
Description = "The media source id to scan"
};
var scanLocalCommand = new Command("scan-local", "Scan a local library");
scanLocalCommand.Arguments.Add(libraryIdArgument);
scanLocalCommand.Options.Add(forceOption);
var scanPlexCommand = new Command("scan-plex", "Scan a Plex library");
scanPlexCommand.Arguments.Add(libraryIdArgument);
scanPlexCommand.Options.Add(forceOption);
scanPlexCommand.Options.Add(deepOption);
var scanPlexCollectionsCommand = new Command("scan-plex-collections", "Scan Plex collections");
scanPlexCollectionsCommand.Arguments.Add(mediaSourceIdArgument);
scanPlexCollectionsCommand.Options.Add(forceOption);
var scanEmbyCommand = new Command("scan-emby", "Scan an Emby library");
scanEmbyCommand.Arguments.Add(libraryIdArgument);
scanEmbyCommand.Options.Add(forceOption);
scanEmbyCommand.Options.Add(deepOption);
var scanEmbyCollectionsCommand = new Command("scan-emby-collections", "Scan Emby collections");
scanEmbyCollectionsCommand.Arguments.Add(mediaSourceIdArgument);
scanEmbyCollectionsCommand.Options.Add(forceOption);
var scanJellyfinCommand = new Command("scan-jellyfin", "Scan a Jellyfin library");
scanJellyfinCommand.Arguments.Add(libraryIdArgument);
scanJellyfinCommand.Options.Add(forceOption);
scanJellyfinCommand.Options.Add(deepOption);
var scanJellyfinCollectionsCommand = new Command("scan-jellyfin-collections", "Scan Jellyfin collections");
scanJellyfinCollectionsCommand.Arguments.Add(mediaSourceIdArgument);
scanJellyfinCollectionsCommand.Options.Add(forceOption);
scanLocalCommand.SetAction(
async (parseResult, token) =>
{
if (IsScanningEnabled())
{
bool force = parseResult.GetValue(forceOption);
SetProcessPriority(force);
int libraryId = parseResult.GetValue(libraryIdArgument);
using IServiceScope scope = _serviceScopeFactory.CreateScope();
IMediator mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
var scan = new ScanLocalLibrary(libraryId, force);
await mediator.Send(scan, token);
}
});
scanPlexCommand.SetAction(
async (parseResult, token) =>
{
if (IsScanningEnabled())
{
bool force = parseResult.GetValue(forceOption);
SetProcessPriority(force);
bool deep = parseResult.GetValue(deepOption);
int libraryId = parseResult.GetValue(libraryIdArgument);
using IServiceScope scope = _serviceScopeFactory.CreateScope();
IMediator mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
var scan = new SynchronizePlexLibraryById(libraryId, force, deep);
await mediator.Send(scan, token);
}
});
scanPlexCollectionsCommand.SetAction(
async (parseResult, token) =>
{
if (IsScanningEnabled())
{
bool force = parseResult.GetValue(forceOption);
SetProcessPriority(force);
int mediaSourceId = parseResult.GetValue(mediaSourceIdArgument);
using IServiceScope scope = _serviceScopeFactory.CreateScope();
IMediator mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
var scan = new SynchronizePlexCollections(mediaSourceId, force);
await mediator.Send(scan, token);
}
});
scanEmbyCommand.SetAction(
async (parseResult, token) =>
{
if (IsScanningEnabled())
{
bool force = parseResult.GetValue(forceOption);
SetProcessPriority(force);
bool deep = parseResult.GetValue(deepOption);
int libraryId = parseResult.GetValue(libraryIdArgument);
using IServiceScope scope = _serviceScopeFactory.CreateScope();
IMediator mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
var scan = new SynchronizeEmbyLibraryById(libraryId, force, deep);
await mediator.Send(scan, token);
}
});
scanEmbyCollectionsCommand.SetAction(
async (parseResult, token) =>
{
if (IsScanningEnabled())
{
bool force = parseResult.GetValue(forceOption);
SetProcessPriority(force);
int mediaSourceId = parseResult.GetValue(mediaSourceIdArgument);
using IServiceScope scope = _serviceScopeFactory.CreateScope();
IMediator mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
var scan = new SynchronizeEmbyCollections(mediaSourceId, force);
await mediator.Send(scan, token);
}
});
scanJellyfinCommand.SetAction(
async (parseResult, token) =>
{
if (IsScanningEnabled())
{
bool force = parseResult.GetValue(forceOption);
SetProcessPriority(force);
bool deep = parseResult.GetValue(deepOption);
int libraryId = parseResult.GetValue(libraryIdArgument);
using IServiceScope scope = _serviceScopeFactory.CreateScope();
IMediator mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
var scan = new SynchronizeJellyfinLibraryById(libraryId, force, deep);
await mediator.Send(scan, token);
}
});
scanJellyfinCollectionsCommand.SetAction(
async (parseResult, token) =>
{
if (IsScanningEnabled())
{
bool force = parseResult.GetValue(forceOption);
SetProcessPriority(force);
int mediaSourceId = parseResult.GetValue(mediaSourceIdArgument);
using IServiceScope scope = _serviceScopeFactory.CreateScope();
IMediator mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
var scan = new SynchronizeJellyfinCollections(mediaSourceId, force);
await mediator.Send(scan, token);
}
});
var rootCommand = new RootCommand();
rootCommand.Subcommands.Add(scanLocalCommand);
rootCommand.Subcommands.Add(scanPlexCommand);
rootCommand.Subcommands.Add(scanPlexCollectionsCommand);
rootCommand.Subcommands.Add(scanEmbyCommand);
rootCommand.Subcommands.Add(scanEmbyCollectionsCommand);
rootCommand.Subcommands.Add(scanJellyfinCommand);
rootCommand.Subcommands.Add(scanJellyfinCollectionsCommand);
return rootCommand;
}
private bool IsScanningEnabled()
{
#if !DEBUG_NO_SYNC
// don't want to flag the logger as unused (only used when sync is disabled)
ILogger<Worker> _ = _logger;
return true;
#else
_logger.LogInformation("Scanning is disabled via DEBUG_NO_SYNC");
return false;
#endif
}
private void SetProcessPriority(bool force)
{
if (force)
{
return;
}
try
{
using var process = Process.GetCurrentProcess();
process.PriorityClass = ProcessPriorityClass.BelowNormal;
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to set scanner priority");
}
}
}