|
|
|
@ -6,6 +6,7 @@ using ErsatzTV.Core.Errors; |
|
|
|
using ErsatzTV.Core.Extensions; |
|
|
|
using ErsatzTV.Core.Extensions; |
|
|
|
using ErsatzTV.Core.Interfaces.Repositories; |
|
|
|
using ErsatzTV.Core.Interfaces.Repositories; |
|
|
|
using ErsatzTV.Core.Metadata; |
|
|
|
using ErsatzTV.Core.Metadata; |
|
|
|
|
|
|
|
using ErsatzTV.Infrastructure; |
|
|
|
using ErsatzTV.Scanner.Core.Interfaces; |
|
|
|
using ErsatzTV.Scanner.Core.Interfaces; |
|
|
|
using ErsatzTV.Scanner.Core.Interfaces.Metadata; |
|
|
|
using ErsatzTV.Scanner.Core.Interfaces.Metadata; |
|
|
|
using Microsoft.Extensions.Logging; |
|
|
|
using Microsoft.Extensions.Logging; |
|
|
|
@ -94,6 +95,8 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters, |
|
|
|
List<TEtag> existingShows = await televisionRepository.GetExistingShows(library, cancellationToken); |
|
|
|
List<TEtag> existingShows = await televisionRepository.GetExistingShows(library, cancellationToken); |
|
|
|
|
|
|
|
|
|
|
|
await foreach ((TShow incoming, int totalShowCount) in showEntries.WithCancellation(cancellationToken)) |
|
|
|
await foreach ((TShow incoming, int totalShowCount) in showEntries.WithCancellation(cancellationToken)) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
using (ScanProfiler.Measure("Scan Show")) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (cancellationToken.IsCancellationRequested) |
|
|
|
if (cancellationToken.IsCancellationRequested) |
|
|
|
{ |
|
|
|
{ |
|
|
|
@ -145,7 +148,10 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters, |
|
|
|
|
|
|
|
|
|
|
|
await televisionRepository.SetEtag(result.Item, MediaServerEtag(incoming), cancellationToken); |
|
|
|
await televisionRepository.SetEtag(result.Item, MediaServerEtag(incoming), cancellationToken); |
|
|
|
|
|
|
|
|
|
|
|
Option<int> flagResult = await televisionRepository.FlagNormal(library, result.Item, cancellationToken); |
|
|
|
Option<int> flagResult = await televisionRepository.FlagNormal( |
|
|
|
|
|
|
|
library, |
|
|
|
|
|
|
|
result.Item, |
|
|
|
|
|
|
|
cancellationToken); |
|
|
|
if (flagResult.IsSome) |
|
|
|
if (flagResult.IsSome) |
|
|
|
{ |
|
|
|
{ |
|
|
|
result.IsUpdated = true; |
|
|
|
result.IsUpdated = true; |
|
|
|
@ -161,6 +167,9 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters, |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ScanProfiler.LogStatistics(s => _logger.LogInformation("{Profile}", s)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (cleanupFileNotFoundItems) |
|
|
|
if (cleanupFileNotFoundItems) |
|
|
|
{ |
|
|
|
{ |
|
|
|
// trash shows that are no longer present on the media server
|
|
|
|
// trash shows that are no longer present on the media server
|
|
|
|
@ -284,6 +293,8 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters, |
|
|
|
List<TEtag> existingSeasons = await televisionRepository.GetExistingSeasons(library, show, cancellationToken); |
|
|
|
List<TEtag> existingSeasons = await televisionRepository.GetExistingSeasons(library, show, cancellationToken); |
|
|
|
|
|
|
|
|
|
|
|
await foreach ((TSeason incoming, int _) in seasonEntries.WithCancellation(cancellationToken)) |
|
|
|
await foreach ((TSeason incoming, int _) in seasonEntries.WithCancellation(cancellationToken)) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
using (ScanProfiler.Measure("Scan Season")) |
|
|
|
{ |
|
|
|
{ |
|
|
|
incoming.ShowId = show.Id; |
|
|
|
incoming.ShowId = show.Id; |
|
|
|
|
|
|
|
|
|
|
|
@ -333,7 +344,10 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters, |
|
|
|
|
|
|
|
|
|
|
|
await televisionRepository.SetEtag(result.Item, MediaServerEtag(incoming), cancellationToken); |
|
|
|
await televisionRepository.SetEtag(result.Item, MediaServerEtag(incoming), cancellationToken); |
|
|
|
|
|
|
|
|
|
|
|
Option<int> flagResult = await televisionRepository.FlagNormal(library, result.Item, cancellationToken); |
|
|
|
Option<int> flagResult = await televisionRepository.FlagNormal( |
|
|
|
|
|
|
|
library, |
|
|
|
|
|
|
|
result.Item, |
|
|
|
|
|
|
|
cancellationToken); |
|
|
|
if (flagResult.IsSome) |
|
|
|
if (flagResult.IsSome) |
|
|
|
{ |
|
|
|
{ |
|
|
|
result.IsUpdated = true; |
|
|
|
result.IsUpdated = true; |
|
|
|
@ -350,6 +364,7 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters, |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// trash seasons that are no longer present on the media server
|
|
|
|
// trash seasons that are no longer present on the media server
|
|
|
|
var fileNotFoundItemIds = existingSeasons.Map(s => s.MediaServerItemId).Except(incomingItemIds).ToList(); |
|
|
|
var fileNotFoundItemIds = existingSeasons.Map(s => s.MediaServerItemId).Except(incomingItemIds).ToList(); |
|
|
|
@ -378,6 +393,8 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters, |
|
|
|
List<TEtag> existingEpisodes = await televisionRepository.GetExistingEpisodes(library, season, cancellationToken); |
|
|
|
List<TEtag> existingEpisodes = await televisionRepository.GetExistingEpisodes(library, season, cancellationToken); |
|
|
|
|
|
|
|
|
|
|
|
await foreach ((TEpisode incoming, int _) in episodeEntries.WithCancellation(cancellationToken)) |
|
|
|
await foreach ((TEpisode incoming, int _) in episodeEntries.WithCancellation(cancellationToken)) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
using (ScanProfiler.Measure("Scan Episode")) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (cancellationToken.IsCancellationRequested) |
|
|
|
if (cancellationToken.IsCancellationRequested) |
|
|
|
{ |
|
|
|
{ |
|
|
|
@ -472,7 +489,10 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters, |
|
|
|
|
|
|
|
|
|
|
|
if (_fileSystem.File.Exists(result.LocalPath)) |
|
|
|
if (_fileSystem.File.Exists(result.LocalPath)) |
|
|
|
{ |
|
|
|
{ |
|
|
|
Option<int> flagResult = await televisionRepository.FlagNormal(library, result.Item, cancellationToken); |
|
|
|
Option<int> flagResult = await televisionRepository.FlagNormal( |
|
|
|
|
|
|
|
library, |
|
|
|
|
|
|
|
result.Item, |
|
|
|
|
|
|
|
cancellationToken); |
|
|
|
if (flagResult.IsSome) |
|
|
|
if (flagResult.IsSome) |
|
|
|
{ |
|
|
|
{ |
|
|
|
result.IsUpdated = true; |
|
|
|
result.IsUpdated = true; |
|
|
|
@ -480,7 +500,10 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters, |
|
|
|
} |
|
|
|
} |
|
|
|
else if (ServerSupportsRemoteStreaming) |
|
|
|
else if (ServerSupportsRemoteStreaming) |
|
|
|
{ |
|
|
|
{ |
|
|
|
Option<int> flagResult = await televisionRepository.FlagRemoteOnly(library, result.Item, cancellationToken); |
|
|
|
Option<int> flagResult = await televisionRepository.FlagRemoteOnly( |
|
|
|
|
|
|
|
library, |
|
|
|
|
|
|
|
result.Item, |
|
|
|
|
|
|
|
cancellationToken); |
|
|
|
if (flagResult.IsSome) |
|
|
|
if (flagResult.IsSome) |
|
|
|
{ |
|
|
|
{ |
|
|
|
result.IsUpdated = true; |
|
|
|
result.IsUpdated = true; |
|
|
|
@ -488,7 +511,10 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters, |
|
|
|
} |
|
|
|
} |
|
|
|
else |
|
|
|
else |
|
|
|
{ |
|
|
|
{ |
|
|
|
Option<int> flagResult = await televisionRepository.FlagUnavailable(library, result.Item, cancellationToken); |
|
|
|
Option<int> flagResult = await televisionRepository.FlagUnavailable( |
|
|
|
|
|
|
|
library, |
|
|
|
|
|
|
|
result.Item, |
|
|
|
|
|
|
|
cancellationToken); |
|
|
|
if (flagResult.IsSome) |
|
|
|
if (flagResult.IsSome) |
|
|
|
{ |
|
|
|
{ |
|
|
|
result.IsUpdated = true; |
|
|
|
result.IsUpdated = true; |
|
|
|
@ -505,6 +531,7 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters, |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// trash episodes that are no longer present on the media server
|
|
|
|
// trash episodes that are no longer present on the media server
|
|
|
|
var fileNotFoundItemIds = existingEpisodes.Map(m => m.MediaServerItemId).Except(incomingItemIds).ToList(); |
|
|
|
var fileNotFoundItemIds = existingEpisodes.Map(m => m.MediaServerItemId).Except(incomingItemIds).ToList(); |
|
|
|
@ -610,6 +637,8 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters, |
|
|
|
MediaItemScanResult<TShow> result, |
|
|
|
MediaItemScanResult<TShow> result, |
|
|
|
TShow incoming, |
|
|
|
TShow incoming, |
|
|
|
bool deepScan) |
|
|
|
bool deepScan) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
using (ScanProfiler.Measure("Update Show Metadata")) |
|
|
|
{ |
|
|
|
{ |
|
|
|
foreach (ShowMetadata fullMetadata in await GetFullMetadata( |
|
|
|
foreach (ShowMetadata fullMetadata in await GetFullMetadata( |
|
|
|
connectionParameters, |
|
|
|
connectionParameters, |
|
|
|
@ -622,6 +651,7 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters, |
|
|
|
// will have to merge JF, Emby, Plex logic
|
|
|
|
// will have to merge JF, Emby, Plex logic
|
|
|
|
return await UpdateMetadata(result, fullMetadata); |
|
|
|
return await UpdateMetadata(result, fullMetadata); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return result; |
|
|
|
return result; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -632,6 +662,8 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters, |
|
|
|
MediaItemScanResult<TSeason> result, |
|
|
|
MediaItemScanResult<TSeason> result, |
|
|
|
TSeason incoming, |
|
|
|
TSeason incoming, |
|
|
|
bool deepScan) |
|
|
|
bool deepScan) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
using (ScanProfiler.Measure("Update Season Metadata")) |
|
|
|
{ |
|
|
|
{ |
|
|
|
foreach (SeasonMetadata fullMetadata in await GetFullMetadata( |
|
|
|
foreach (SeasonMetadata fullMetadata in await GetFullMetadata( |
|
|
|
connectionParameters, |
|
|
|
connectionParameters, |
|
|
|
@ -644,6 +676,7 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters, |
|
|
|
// will have to merge JF, Emby, Plex logic
|
|
|
|
// will have to merge JF, Emby, Plex logic
|
|
|
|
return await UpdateMetadata(result, fullMetadata); |
|
|
|
return await UpdateMetadata(result, fullMetadata); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return result; |
|
|
|
return result; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -713,6 +746,8 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters, |
|
|
|
bool deepScan, |
|
|
|
bool deepScan, |
|
|
|
Option<EpisodeMetadata> maybeFullMetadata, |
|
|
|
Option<EpisodeMetadata> maybeFullMetadata, |
|
|
|
CancellationToken cancellationToken) |
|
|
|
CancellationToken cancellationToken) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
using (ScanProfiler.Measure("Update Episode Metadata")) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (maybeFullMetadata.IsNone) |
|
|
|
if (maybeFullMetadata.IsNone) |
|
|
|
{ |
|
|
|
{ |
|
|
|
@ -728,6 +763,7 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters, |
|
|
|
|
|
|
|
|
|
|
|
return result; |
|
|
|
return result; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private async Task<Either<BaseError, MediaItemScanResult<TEpisode>>> UpdateStatistics( |
|
|
|
private async Task<Either<BaseError, MediaItemScanResult<TEpisode>>> UpdateStatistics( |
|
|
|
TConnectionParameters connectionParameters, |
|
|
|
TConnectionParameters connectionParameters, |
|
|
|
@ -736,6 +772,8 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters, |
|
|
|
TEpisode incoming, |
|
|
|
TEpisode incoming, |
|
|
|
bool deepScan, |
|
|
|
bool deepScan, |
|
|
|
Option<MediaVersion> maybeMediaVersion) |
|
|
|
Option<MediaVersion> maybeMediaVersion) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
using (ScanProfiler.Measure("Update Episode Stats")) |
|
|
|
{ |
|
|
|
{ |
|
|
|
TEpisode existing = result.Item; |
|
|
|
TEpisode existing = result.Item; |
|
|
|
|
|
|
|
|
|
|
|
@ -789,12 +827,15 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters, |
|
|
|
|
|
|
|
|
|
|
|
return result; |
|
|
|
return result; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private async Task<Either<BaseError, MediaItemScanResult<TEpisode>>> UpdateSubtitles( |
|
|
|
private async Task<Either<BaseError, MediaItemScanResult<TEpisode>>> UpdateSubtitles( |
|
|
|
MediaItemScanResult<TEpisode> existing, |
|
|
|
MediaItemScanResult<TEpisode> existing, |
|
|
|
CancellationToken cancellationToken) |
|
|
|
CancellationToken cancellationToken) |
|
|
|
{ |
|
|
|
{ |
|
|
|
try |
|
|
|
try |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
using (ScanProfiler.Measure("Update Episode Subtitles")) |
|
|
|
{ |
|
|
|
{ |
|
|
|
MediaVersion version = existing.Item.GetHeadVersion(); |
|
|
|
MediaVersion version = existing.Item.GetHeadVersion(); |
|
|
|
Option<EpisodeMetadata> maybeMetadata = existing.Item.EpisodeMetadata.HeadOrNone(); |
|
|
|
Option<EpisodeMetadata> maybeMetadata = existing.Item.EpisodeMetadata.HeadOrNone(); |
|
|
|
@ -813,6 +854,7 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters, |
|
|
|
|
|
|
|
|
|
|
|
return BaseError.New("Failed to update media server subtitles"); |
|
|
|
return BaseError.New("Failed to update media server subtitles"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
catch (Exception ex) |
|
|
|
catch (Exception ex) |
|
|
|
{ |
|
|
|
{ |
|
|
|
return BaseError.New(ex.ToString()); |
|
|
|
return BaseError.New(ex.ToString()); |
|
|
|
@ -824,6 +866,8 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters, |
|
|
|
CancellationToken cancellationToken) |
|
|
|
CancellationToken cancellationToken) |
|
|
|
{ |
|
|
|
{ |
|
|
|
try |
|
|
|
try |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
using (ScanProfiler.Measure("Update Episode Chapters")) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (string.IsNullOrEmpty(existing.LocalPath)) |
|
|
|
if (string.IsNullOrEmpty(existing.LocalPath)) |
|
|
|
{ |
|
|
|
{ |
|
|
|
@ -831,13 +875,17 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters, |
|
|
|
return existing; |
|
|
|
return existing; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (await _localChaptersProvider.UpdateChapters(existing.Item, Some(existing.LocalPath), cancellationToken)) |
|
|
|
if (await _localChaptersProvider.UpdateChapters( |
|
|
|
|
|
|
|
existing.Item, |
|
|
|
|
|
|
|
Some(existing.LocalPath), |
|
|
|
|
|
|
|
cancellationToken)) |
|
|
|
{ |
|
|
|
{ |
|
|
|
existing.IsUpdated = true; |
|
|
|
existing.IsUpdated = true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return existing; |
|
|
|
return existing; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
catch (Exception ex) |
|
|
|
catch (Exception ex) |
|
|
|
{ |
|
|
|
{ |
|
|
|
return BaseError.New(ex.ToString()); |
|
|
|
return BaseError.New(ex.ToString()); |
|
|
|
|