mirror of https://github.com/ErsatzTV/ErsatzTV.git
Browse Source
* optimize emby collection scan frequency * add button to sync emby collections * update changelog * fix scanning; add progress indicatorpull/1206/head
21 changed files with 4817 additions and 28 deletions
@ -0,0 +1,82 @@ |
|||||||
|
using System.Threading.Channels; |
||||||
|
using ErsatzTV.Application.Libraries; |
||||||
|
using ErsatzTV.Core; |
||||||
|
using ErsatzTV.Core.Errors; |
||||||
|
using ErsatzTV.Core.Interfaces.Repositories; |
||||||
|
using ErsatzTV.FFmpeg.Runtime; |
||||||
|
using ErsatzTV.Infrastructure.Data; |
||||||
|
using ErsatzTV.Infrastructure.Extensions; |
||||||
|
using Microsoft.EntityFrameworkCore; |
||||||
|
|
||||||
|
namespace ErsatzTV.Application.Emby; |
||||||
|
|
||||||
|
public class CallEmbyCollectionScannerHandler : CallLibraryScannerHandler<SynchronizeEmbyCollections>, |
||||||
|
IRequestHandler<SynchronizeEmbyCollections, Either<BaseError, Unit>> |
||||||
|
{ |
||||||
|
public CallEmbyCollectionScannerHandler( |
||||||
|
IDbContextFactory<TvContext> dbContextFactory, |
||||||
|
IConfigElementRepository configElementRepository, |
||||||
|
ChannelWriter<ISearchIndexBackgroundServiceRequest> channel, |
||||||
|
IMediator mediator, |
||||||
|
IRuntimeInfo runtimeInfo) : base(dbContextFactory, configElementRepository, channel, mediator, runtimeInfo) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
protected override async Task<DateTimeOffset> GetLastScan(TvContext dbContext, SynchronizeEmbyCollections request) |
||||||
|
{ |
||||||
|
DateTime minDateTime = await dbContext.EmbyMediaSources |
||||||
|
.SelectOneAsync(l => l.Id, l => l.Id == request.EmbyMediaSourceId) |
||||||
|
.Match(l => l.LastCollectionsScan ?? SystemTime.MinValueUtc, () => SystemTime.MaxValueUtc); |
||||||
|
|
||||||
|
return new DateTimeOffset(minDateTime, TimeSpan.Zero); |
||||||
|
} |
||||||
|
|
||||||
|
protected override bool ScanIsRequired( |
||||||
|
DateTimeOffset lastScan, |
||||||
|
int libraryRefreshInterval, |
||||||
|
SynchronizeEmbyCollections request) |
||||||
|
{ |
||||||
|
if (lastScan == SystemTime.MaxValueUtc) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
DateTimeOffset nextScan = lastScan + TimeSpan.FromHours(libraryRefreshInterval); |
||||||
|
return request.ForceScan || (libraryRefreshInterval > 0 && nextScan < DateTimeOffset.Now); |
||||||
|
} |
||||||
|
|
||||||
|
public async Task<Either<BaseError, Unit>> |
||||||
|
Handle(SynchronizeEmbyCollections request, CancellationToken cancellationToken) |
||||||
|
{ |
||||||
|
Validation<BaseError, string> validation = await Validate(request); |
||||||
|
return await validation.Match( |
||||||
|
scanner => PerformScan(scanner, request, cancellationToken), |
||||||
|
error => |
||||||
|
{ |
||||||
|
foreach (ScanIsNotRequired scanIsNotRequired in error.OfType<ScanIsNotRequired>()) |
||||||
|
{ |
||||||
|
return Task.FromResult<Either<BaseError, Unit>>(scanIsNotRequired); |
||||||
|
} |
||||||
|
|
||||||
|
return Task.FromResult<Either<BaseError, Unit>>(error.Join()); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
private async Task<Either<BaseError, Unit>> PerformScan( |
||||||
|
string scanner, |
||||||
|
SynchronizeEmbyCollections request, |
||||||
|
CancellationToken cancellationToken) |
||||||
|
{ |
||||||
|
var arguments = new List<string> |
||||||
|
{ |
||||||
|
"scan-emby-collections", request.EmbyMediaSourceId.ToString() |
||||||
|
}; |
||||||
|
|
||||||
|
if (request.ForceScan) |
||||||
|
{ |
||||||
|
arguments.Add("--force"); |
||||||
|
} |
||||||
|
|
||||||
|
return await base.PerformScan(scanner, arguments, cancellationToken).MapT(_ => Unit.Default); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,6 @@ |
|||||||
|
using ErsatzTV.Core; |
||||||
|
|
||||||
|
namespace ErsatzTV.Application.Emby; |
||||||
|
|
||||||
|
public record SynchronizeEmbyCollections(int EmbyMediaSourceId, bool ForceScan) : IRequest<Either<BaseError, Unit>>, |
||||||
|
IScannerBackgroundServiceRequest; |
@ -0,0 +1,3 @@ |
|||||||
|
namespace ErsatzTV.Application.Libraries; |
||||||
|
|
||||||
|
public record GetExternalCollections : IRequest<List<LibraryViewModel>>; |
@ -0,0 +1,35 @@ |
|||||||
|
using ErsatzTV.Core.Domain; |
||||||
|
using ErsatzTV.Infrastructure.Data; |
||||||
|
using Microsoft.EntityFrameworkCore; |
||||||
|
|
||||||
|
namespace ErsatzTV.Application.Libraries; |
||||||
|
|
||||||
|
public class GetExternalCollectionsHandler : IRequestHandler<GetExternalCollections, List<LibraryViewModel>> |
||||||
|
{ |
||||||
|
private readonly IDbContextFactory<TvContext> _dbContextFactory; |
||||||
|
|
||||||
|
public GetExternalCollectionsHandler(IDbContextFactory<TvContext> dbContextFactory) |
||||||
|
{ |
||||||
|
_dbContextFactory = dbContextFactory; |
||||||
|
} |
||||||
|
|
||||||
|
public async Task<List<LibraryViewModel>> Handle( |
||||||
|
GetExternalCollections request, |
||||||
|
CancellationToken cancellationToken) |
||||||
|
{ |
||||||
|
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); |
||||||
|
List<int> mediaSourceIds = await dbContext.EmbyMediaSources |
||||||
|
.Filter(ems => ems.Libraries.Any(l => ((EmbyLibrary)l).ShouldSyncItems)) |
||||||
|
.Map(ems => ems.Id) |
||||||
|
.ToListAsync(cancellationToken: cancellationToken); |
||||||
|
|
||||||
|
return mediaSourceIds.Map( |
||||||
|
id => new LibraryViewModel( |
||||||
|
"Emby", |
||||||
|
0, |
||||||
|
"Collections", |
||||||
|
0, |
||||||
|
id)) |
||||||
|
.ToList(); |
||||||
|
} |
||||||
|
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,29 @@ |
|||||||
|
using System; |
||||||
|
using Microsoft.EntityFrameworkCore.Migrations; |
||||||
|
|
||||||
|
#nullable disable |
||||||
|
|
||||||
|
namespace ErsatzTV.Infrastructure.Migrations |
||||||
|
{ |
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class Add_EmbyMediaSource_LastCollectionScan : Migration |
||||||
|
{ |
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder) |
||||||
|
{ |
||||||
|
migrationBuilder.AddColumn<DateTime>( |
||||||
|
name: "LastCollectionsScan", |
||||||
|
table: "EmbyMediaSource", |
||||||
|
type: "TEXT", |
||||||
|
nullable: true); |
||||||
|
} |
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder) |
||||||
|
{ |
||||||
|
migrationBuilder.DropColumn( |
||||||
|
name: "LastCollectionsScan", |
||||||
|
table: "EmbyMediaSource"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue