@ -21,6 +21,7 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters,
where TEtag : MediaServerItemEtag
where TEtag : MediaServerItemEtag
{
{
private readonly ILocalFileSystem _l ocalFileSystem ;
private readonly ILocalFileSystem _l ocalFileSystem ;
private readonly IMetadataRepository _ metadataRepository ;
private readonly ILocalStatisticsProvider _l ocalStatisticsProvider ;
private readonly ILocalStatisticsProvider _l ocalStatisticsProvider ;
private readonly ILocalSubtitlesProvider _l ocalSubtitlesProvider ;
private readonly ILocalSubtitlesProvider _l ocalSubtitlesProvider ;
private readonly ILogger _l ogger ;
private readonly ILogger _l ogger ;
@ -30,16 +31,21 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters,
ILocalStatisticsProvider localStatisticsProvider ,
ILocalStatisticsProvider localStatisticsProvider ,
ILocalSubtitlesProvider localSubtitlesProvider ,
ILocalSubtitlesProvider localSubtitlesProvider ,
ILocalFileSystem localFileSystem ,
ILocalFileSystem localFileSystem ,
IMetadataRepository metadataRepository ,
IMediator mediator ,
IMediator mediator ,
ILogger logger )
ILogger logger )
{
{
_l ocalStatisticsProvider = localStatisticsProvider ;
_l ocalStatisticsProvider = localStatisticsProvider ;
_l ocalSubtitlesProvider = localSubtitlesProvider ;
_l ocalSubtitlesProvider = localSubtitlesProvider ;
_l ocalFileSystem = localFileSystem ;
_l ocalFileSystem = localFileSystem ;
_ metadataRepository = metadataRepository ;
_ mediator = mediator ;
_ mediator = mediator ;
_l ogger = logger ;
_l ogger = logger ;
}
}
protected virtual bool ServerSupportsRemoteStreaming = > false ;
protected virtual bool ServerReturnsStatisticsWithMetadata = > false ;
protected async Task < Either < BaseError , Unit > > ScanLibrary (
protected async Task < Either < BaseError , Unit > > ScanLibrary (
IMediaServerTelevisionRepository < TLibrary , TShow , TSeason , TEpisode , TEtag > televisionRepository ,
IMediaServerTelevisionRepository < TLibrary , TShow , TSeason , TEpisode , TEtag > televisionRepository ,
TConnectionParameters connectionParameters ,
TConnectionParameters connectionParameters ,
@ -268,6 +274,18 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters,
TEpisode incoming ,
TEpisode incoming ,
bool deepScan ) ;
bool deepScan ) ;
protected virtual Task < Option < MediaVersion > > GetMediaServerStatistics (
TConnectionParameters connectionParameters ,
TLibrary library ,
MediaItemScanResult < TEpisode > result ,
TEpisode incoming ) = > Task . FromResult ( Option < MediaVersion > . None ) ;
protected abstract Task < Option < Tuple < EpisodeMetadata , MediaVersion > > > GetFullMetadataAndStatistics (
TConnectionParameters connectionParameters ,
TLibrary library ,
MediaItemScanResult < TEpisode > result ,
TEpisode incoming ) ;
protected abstract Task < Either < BaseError , MediaItemScanResult < TShow > > > UpdateMetadata (
protected abstract Task < Either < BaseError , MediaItemScanResult < TShow > > > UpdateMetadata (
MediaItemScanResult < TShow > result ,
MediaItemScanResult < TShow > result ,
ShowMetadata fullMetadata ) ;
ShowMetadata fullMetadata ) ;
@ -279,7 +297,7 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters,
protected abstract Task < Either < BaseError , MediaItemScanResult < TEpisode > > > UpdateMetadata (
protected abstract Task < Either < BaseError , MediaItemScanResult < TEpisode > > > UpdateMetadata (
MediaItemScanResult < TEpisode > result ,
MediaItemScanResult < TEpisode > result ,
EpisodeMetadata fullMetadata ) ;
EpisodeMetadata fullMetadata ) ;
private async Task < Either < BaseError , Unit > > ScanSeasons (
private async Task < Either < BaseError , Unit > > ScanSeasons (
IMediaServerTelevisionRepository < TLibrary , TShow , TSeason , TEpisode , TEtag > televisionRepository ,
IMediaServerTelevisionRepository < TLibrary , TShow , TSeason , TEpisode , TEtag > televisionRepository ,
TLibrary library ,
TLibrary library ,
@ -436,17 +454,50 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters,
incoming . SeasonId = season . Id ;
incoming . SeasonId = season . Id ;
Either < BaseError , MediaItemScanResult < TEpisode > > maybeEpisode = await televisionRepository
Either < BaseError , MediaItemScanResult < TEpisode > > maybeEpisode ;
. GetOrAdd ( library , incoming , deepScan )
. MapT (
if ( ServerReturnsStatisticsWithMetadata )
result = >
{
{
maybeEpisode = await televisionRepository
result . LocalPath = localPath ;
. GetOrAdd ( library , incoming , deepScan )
return result ;
. MapT (
} )
result = >
. BindT ( existing = > UpdateMetadata ( connectionParameters , library , existing , incoming , deepScan ) )
{
. BindT ( existing = > UpdateStatistics ( existing , incoming , ffmpegPath , ffprobePath ) )
result . LocalPath = localPath ;
. BindT ( UpdateSubtitles ) ;
return result ;
} )
. BindT (
existing = > UpdateMetadataAndStatistics (
connectionParameters ,
library ,
existing ,
incoming ,
deepScan ) )
. BindT ( UpdateSubtitles ) ;
}
else
{
maybeEpisode = await televisionRepository
. GetOrAdd ( library , incoming , deepScan )
. MapT (
result = >
{
result . LocalPath = localPath ;
return result ;
} )
. BindT (
existing = > UpdateMetadata ( connectionParameters , library , existing , incoming , deepScan , None ) )
. BindT (
existing = > UpdateStatistics (
connectionParameters ,
library ,
existing ,
incoming ,
ffmpegPath ,
ffprobePath ,
None ) )
. BindT ( UpdateSubtitles ) ;
}
if ( maybeEpisode . IsLeft )
if ( maybeEpisode . IsLeft )
{
{
@ -474,6 +525,14 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters,
result . IsUpdated = true ;
result . IsUpdated = true ;
}
}
}
}
else if ( ServerSupportsRemoteStreaming )
{
Option < int > flagResult = await televisionRepository . FlagRemoteOnly ( library , result . Item ) ;
if ( flagResult . IsSome )
{
result . IsUpdated = true ;
}
}
else
else
{
{
Option < int > flagResult = await televisionRepository . FlagUnavailable ( library , result . Item ) ;
Option < int > flagResult = await televisionRepository . FlagUnavailable ( library , result . Item ) ;
@ -542,11 +601,22 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters,
// don't scan, but mark as unavailable
// don't scan, but mark as unavailable
if ( ! _l ocalFileSystem . FileExists ( localPath ) )
if ( ! _l ocalFileSystem . FileExists ( localPath ) )
{
{
foreach ( int id in await televisionRepository . FlagUnavailable ( library , incoming ) )
if ( ServerSupportsRemoteStreaming ) {
foreach ( int id in await televisionRepository . FlagRemoteOnly ( library , incoming ) )
{
await _ mediator . Publish (
new ScannerProgressUpdate ( library . Id , null , null , new [ ] { id } , Array . Empty < int > ( ) ) ,
CancellationToken . None ) ;
}
}
else
{
{
await _ mediator . Publish (
foreach ( int id in await televisionRepository . FlagUnavailable ( library , incoming ) )
new ScannerProgressUpdate ( library . Id , null , null , new [ ] { id } , Array . Empty < int > ( ) ) ,
{
CancellationToken . None ) ;
await _ mediator . Publish (
new ScannerProgressUpdate ( library . Id , null , null , new [ ] { id } , Array . Empty < int > ( ) ) ,
CancellationToken . None ) ;
}
}
}
}
}
@ -617,19 +687,76 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters,
return result ;
return result ;
}
}
private async Task < Either < BaseError , MediaItemScanResult < TEpisode > > > UpdateMetadata (
private async Task < Either < BaseError , MediaItemScanResult < TEpisode > > > UpdateMetadataAndStatistics (
TConnectionParameters connectionParameters ,
TConnectionParameters connectionParameters ,
TLibrary library ,
TLibrary library ,
MediaItemScanResult < TEpisode > result ,
MediaItemScanResult < TEpisode > result ,
TEpisode incoming ,
TEpisode incoming ,
bool deepScan )
bool deepScan )
{
{
foreach ( EpisodeMetadata fullMetadata in await GetFullMetadata (
Option < Tuple < EpisodeMetadata , MediaVersion > > maybeMetadataAndStatistics = await GetFullMetadataAndStatistics (
connectionParameters ,
connectionParameters ,
library ,
library ,
result ,
result ,
incoming ,
incoming ) ;
deepScan ) )
foreach ( ( EpisodeMetadata fullMetadata , MediaVersion mediaVersion ) in maybeMetadataAndStatistics )
{
Either < BaseError , MediaItemScanResult < TEpisode > > metadataResult = await UpdateMetadata (
connectionParameters ,
library ,
result ,
incoming ,
deepScan ,
fullMetadata ) ;
foreach ( BaseError error in metadataResult . LeftToSeq ( ) )
{
return error ;
}
foreach ( MediaItemScanResult < TEpisode > r in metadataResult . RightToSeq ( ) )
{
result = r ;
}
Either < BaseError , MediaItemScanResult < TEpisode > > statisticsResult = await UpdateStatistics (
connectionParameters ,
library ,
result ,
incoming ,
string . Empty ,
string . Empty ,
mediaVersion ) ;
foreach ( BaseError error in statisticsResult . LeftToSeq ( ) )
{
return error ;
}
foreach ( MediaItemScanResult < TEpisode > r in metadataResult . RightToSeq ( ) )
{
result = r ;
}
}
return result ;
}
private async Task < Either < BaseError , MediaItemScanResult < TEpisode > > > UpdateMetadata (
TConnectionParameters connectionParameters ,
TLibrary library ,
MediaItemScanResult < TEpisode > result ,
TEpisode incoming ,
bool deepScan ,
Option < EpisodeMetadata > maybeFullMetadata )
{
if ( maybeFullMetadata . IsNone )
{
maybeFullMetadata = await GetFullMetadata ( connectionParameters , library , result , incoming , deepScan ) ;
}
foreach ( EpisodeMetadata fullMetadata in maybeFullMetadata )
{
{
// TODO: move some of this code into this scanner
// TODO: move some of this code into this scanner
// will have to merge JF, Emby, Plex logic
// will have to merge JF, Emby, Plex logic
@ -640,17 +767,20 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters,
}
}
private async Task < Either < BaseError , MediaItemScanResult < TEpisode > > > UpdateStatistics (
private async Task < Either < BaseError , MediaItemScanResult < TEpisode > > > UpdateStatistics (
TConnectionParameters connectionParameters ,
TLibrary library ,
MediaItemScanResult < TEpisode > result ,
MediaItemScanResult < TEpisode > result ,
TEpisode incoming ,
TEpisode incoming ,
string ffmpegPath ,
string ffmpegPath ,
string ffprobePath )
string ffprobePath ,
Option < MediaVersion > maybeMediaVersion )
{
{
TEpisode existing = result . Item ;
TEpisode existing = result . Item ;
if ( result . IsAdded | | MediaServerEtag ( existing ) ! = MediaServerEtag ( incoming ) | |
if ( result . IsAdded | | MediaServerEtag ( existing ) ! = MediaServerEtag ( incoming ) | |
existing . MediaVersions . Head ( ) . Streams . Count = = 0 )
existing . MediaVersions . Head ( ) . Streams . Count = = 0 )
{
{
if ( _l ocalFileSystem . FileExists ( result . LocalPath ) )
if ( maybeMediaVersion . IsNone & & _l ocalFileSystem . FileExists ( result . LocalPath ) )
{
{
_l ogger . LogDebug ( "Refreshing {Attribute} for {Path}" , "Statistics" , result . LocalPath ) ;
_l ogger . LogDebug ( "Refreshing {Attribute} for {Path}" , "Statistics" , result . LocalPath ) ;
Either < BaseError , bool > refreshResult =
Either < BaseError , bool > refreshResult =
@ -674,6 +804,25 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters,
result . IsUpdated = true ;
result . IsUpdated = true ;
}
}
}
}
else
{
if ( maybeMediaVersion . IsNone )
{
maybeMediaVersion = await GetMediaServerStatistics (
connectionParameters ,
library ,
result ,
incoming ) ;
}
foreach ( MediaVersion mediaVersion in maybeMediaVersion )
{
if ( await _ metadataRepository . UpdateStatistics ( result . Item , mediaVersion ) )
{
result . IsUpdated = true ;
}
}
}
}
}
return result ;
return result ;