@ -1,28 +1,35 @@
using System ;
using System ;
using System.IO ;
using System.IO ;
using System.Text.RegularExpressions ;
using System.Threading.Tasks ;
using System.Threading.Tasks ;
using System.Xml.Serialization ;
using System.Xml.Serialization ;
using ErsatzTV.Core.Domain ;
using ErsatzTV.Core.Domain ;
using ErsatzTV.Core.Interfaces.Metadata ;
using ErsatzTV.Core.Interfaces.Metadata ;
using ErsatzTV.Core.Interfaces.Repositories ;
using ErsatzTV.Core.Interfaces.Repositories ;
using LanguageExt ;
using LanguageExt ;
using Microsoft.Extensions.Logging ;
using static LanguageExt . Prelude ;
using static LanguageExt . Prelude ;
namespace ErsatzTV.Core.Metadata
namespace ErsatzTV.Core.Metadata
{
{
public class LocalMetadataProvider : ILocalMetadataProvider
public class LocalMetadataProvider : ILocalMetadataProvider
{
{
private static readonly XmlSerializer MovieSerializer = new ( typeof ( MovieNfo ) ) ;
private static readonly XmlSerializer TvShowSerializer = new ( typeof ( TvShowEpisodeNfo ) ) ;
private readonly ILogger < LocalMetadataProvider > _l ogger ;
private readonly IMediaItemRepository _ mediaItemRepository ;
private readonly IMediaItemRepository _ mediaItemRepository ;
public LocalMetadataProvider ( IMediaItemRepository mediaItemRepository ) = >
public LocalMetadataProvider ( IMediaItemRepository mediaItemRepository , ILogger < LocalMetadataProvider > logger )
{
_ mediaItemRepository = mediaItemRepository ;
_ mediaItemRepository = mediaItemRepository ;
_l ogger = logger ;
}
public async Task RefreshMetadata ( MediaItem mediaItem )
public async Task RefreshMetadata ( MediaItem mediaItem )
{
{
Option < MediaMetadata > maybeMetadata = await LoadMetadata ( mediaItem ) ;
Option < MediaMetadata > maybeMetadata = await LoadMetadata ( mediaItem ) ;
MediaMetadata metadata =
MediaMetadata metadata =
maybeMetadata . IfNone ( ( ) = > FallbackMetadataProvider . GetFallbackMetadata ( mediaItem . Path ) ) ;
maybeMetadata . IfNone ( ( ) = > FallbackMetadataProvider . GetFallbackMetadata ( mediaItem ) ) ;
await ApplyMetadataUpdate ( mediaItem , metadata ) ;
await ApplyMetadataUpdate ( mediaItem , metadata ) ;
}
}
@ -50,54 +57,72 @@ namespace ErsatzTV.Core.Metadata
string nfoFileName = Path . ChangeExtension ( mediaItem . Path , "nfo" ) ;
string nfoFileName = Path . ChangeExtension ( mediaItem . Path , "nfo" ) ;
if ( nfoFileName = = null | | ! File . Exists ( nfoFileName ) )
if ( nfoFileName = = null | | ! File . Exists ( nfoFileName ) )
{
{
_l ogger . LogDebug ( "NFO file does not exist at {Path}" , nfoFileName ) ;
return None ;
}
if ( ! ( mediaItem . Source is LocalMediaSource localMediaSource ) )
{
_l ogger . LogDebug ( "Media source {Name} is not a local media source" , mediaItem . Source . Name ) ;
return None ;
return None ;
}
}
var tvShowSerializer = new XmlSerializer ( typeof ( TvShowEpisodeNfo ) ) ;
return localMediaSource . MediaType switch
var movieSerializer = new XmlSerializer ( typeof ( MovieNfo ) ) ;
{
MediaType . Movie = > await LoadMovieMetadata ( nfoFileName ) ,
TryAsync < object > tvShowAttempt = TryAsync (
MediaType . TvShow = > await LoadTvShowMetadata ( nfoFileName ) ,
async ( ) = >
_ = > None
{
} ;
await using FileStream fileStream = File . Open ( nfoFileName , FileMode . Open ) ;
}
return tvShowSerializer . Deserialize ( fileStream ) ;
} ) ;
private async Task < Option < MediaMetadata > > LoadTvShowMetadata ( string nfoFileName )
TryAsync < object > movieAttempt = TryAsync (
{
async ( ) = >
try
{
{
await using FileStream fileStream = File . Open ( nfoFileName , FileMode . Open ) ;
await using FileStream fileStream = File . Open ( nfoFileName , FileMode . Open , FileAccess . Read ) ;
return movieSerializer . Deserialize ( fileStream ) ;
Option < TvShowEpisodeNfo > maybeNfo = TvShowSerializer . Deserialize ( fileStream ) as TvShowEpisodeNfo ;
} ) ;
return maybeNfo . Match < Option < MediaMetadata > > (
return await choice ( tvShowAttempt , movieAttempt ) . Match < object , Option < MediaMetadata > > (
nfo = > new MediaMetadata
result = >
{
switch ( result )
{
{
case TvShowEpisodeNfo nfo :
MediaType = MediaType . TvShow ,
return new MediaMetadata
Title = nfo . ShowTitle ,
{
Subtitle = nfo . Title ,
MediaType = MediaType . TvShow ,
Description = nfo . Outline ,
Title = nfo . ShowTitle ,
EpisodeNumber = nfo . Episode ,
Subtitle = nfo . Title ,
SeasonNumber = nfo . Season ,
Description = nfo . Outline ,
Aired = GetAired ( nfo . Aired )
EpisodeNumber = nfo . Episode ,
} ,
SeasonNumber = nfo . Season ,
None ) ;
Aired = GetAired ( nfo . Aired )
}
} ;
catch ( Exception ex )
case MovieNfo nfo :
{
return new MediaMetadata
_l ogger . LogDebug ( ex , "Failed to read TV nfo metadata from {Path}" , nfoFileName ) ;
{
return None ;
MediaType = MediaType . Movie ,
}
Title = nfo . Title ,
}
Description = nfo . Outline ,
ContentRating = nfo . ContentRating ,
private async Task < Option < MediaMetadata > > LoadMovieMetadata ( string nfoFileName )
Aired = GetAired ( nfo . Premiered )
{
} ;
try
default :
{
return None ;
await using FileStream fileStream = File . Open ( nfoFileName , FileMode . Open , FileAccess . Read ) ;
}
Option < MovieNfo > maybeNfo = MovieSerializer . Deserialize ( fileStream ) as MovieNfo ;
} ,
return maybeNfo . Match < Option < MediaMetadata > > (
None ) ;
nfo = > new MediaMetadata
{
MediaType = MediaType . Movie ,
Title = nfo . Title ,
Description = nfo . Outline ,
ContentRating = nfo . ContentRating ,
Aired = GetAired ( nfo . Premiered )
} ,
None ) ;
}
catch ( Exception ex )
{
_l ogger . LogDebug ( ex , "Failed to read Movie nfo metadata from {Path}" , nfoFileName ) ;
return None ;
}
}
}
private static DateTime ? GetAired ( string aired )
private static DateTime ? GetAired ( string aired )