enable plex for television (#73)
* add plex show, season sync * sync plex episodes * sync plex episode statistics * update plex artwork as needed * code cleanup * add note about tests
This commit is contained in:
@@ -25,5 +25,12 @@ namespace ErsatzTV.Infrastructure.Plex
|
||||
string key,
|
||||
[Query] [AliasAs("X-Plex-Token")]
|
||||
string token);
|
||||
|
||||
[Get("/library/metadata/{key}/children")]
|
||||
public Task<PlexMediaContainerResponse<PlexMediaContainerMetadataContent<PlexMetadataResponse>>>
|
||||
GetChildren(
|
||||
string key,
|
||||
[Query] [AliasAs("X-Plex-Token")]
|
||||
string token);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace ErsatzTV.Infrastructure.Plex.Models
|
||||
public string OriginallyAvailableAt { get; set; }
|
||||
public int AddedAt { get; set; }
|
||||
public int UpdatedAt { get; set; }
|
||||
public int Index { get; set; }
|
||||
public List<PlexMediaResponse> Media { get; set; }
|
||||
public List<PlexGenreResponse> Genre { get; set; }
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace ErsatzTV.Infrastructure.Plex
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Either<BaseError, List<PlexMovie>>> GetLibraryContents(
|
||||
public async Task<Either<BaseError, List<PlexMovie>>> GetMovieLibraryContents(
|
||||
PlexLibrary library,
|
||||
PlexConnection connection,
|
||||
PlexServerAuthToken token)
|
||||
@@ -60,15 +60,71 @@ namespace ErsatzTV.Infrastructure.Plex
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Either<BaseError, MediaVersion>> GetStatistics(
|
||||
PlexMovie movie,
|
||||
public async Task<Either<BaseError, List<PlexShow>>> GetShowLibraryContents(
|
||||
PlexLibrary library,
|
||||
PlexConnection connection,
|
||||
PlexServerAuthToken token)
|
||||
{
|
||||
try
|
||||
{
|
||||
IPlexServerApi service = RestService.For<IPlexServerApi>(connection.Uri);
|
||||
return await service.GetMetadata(movie.Key.Split("/").Last(), token.AuthToken)
|
||||
return await service.GetLibrarySectionContents(library.Key, token.AuthToken)
|
||||
.Map(r => r.MediaContainer.Metadata)
|
||||
.Map(list => list.Map(metadata => ProjectToShow(metadata, library.MediaSourceId)).ToList());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BaseError.New(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Either<BaseError, List<PlexSeason>>> GetShowSeasons(
|
||||
PlexLibrary library,
|
||||
PlexShow show,
|
||||
PlexConnection connection,
|
||||
PlexServerAuthToken token)
|
||||
{
|
||||
try
|
||||
{
|
||||
IPlexServerApi service = RestService.For<IPlexServerApi>(connection.Uri);
|
||||
return await service.GetChildren(show.Key.Split("/").Reverse().Skip(1).Head(), token.AuthToken)
|
||||
.Map(r => r.MediaContainer.Metadata)
|
||||
.Map(list => list.Map(metadata => ProjectToSeason(metadata, library.MediaSourceId)).ToList());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BaseError.New(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Either<BaseError, List<PlexEpisode>>> GetSeasonEpisodes(
|
||||
PlexLibrary library,
|
||||
PlexSeason season,
|
||||
PlexConnection connection,
|
||||
PlexServerAuthToken token)
|
||||
{
|
||||
try
|
||||
{
|
||||
IPlexServerApi service = RestService.For<IPlexServerApi>(connection.Uri);
|
||||
return await service.GetChildren(season.Key.Split("/").Reverse().Skip(1).Head(), token.AuthToken)
|
||||
.Map(r => r.MediaContainer.Metadata.Filter(m => m.Media.Count > 0 && m.Media[0].Part.Count > 0))
|
||||
.Map(list => list.Map(metadata => ProjectToEpisode(metadata, library.MediaSourceId)).ToList());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BaseError.New(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Either<BaseError, MediaVersion>> GetStatistics(
|
||||
string key,
|
||||
PlexConnection connection,
|
||||
PlexServerAuthToken token)
|
||||
{
|
||||
try
|
||||
{
|
||||
IPlexServerApi service = RestService.For<IPlexServerApi>(connection.Uri);
|
||||
return await service.GetMetadata(key, token.AuthToken)
|
||||
.Map(
|
||||
r => r.MediaContainer.Metadata.Filter(m => m.Media.Count > 0 && m.Media[0].Part.Count > 0)
|
||||
.HeadOrNone())
|
||||
@@ -202,5 +258,187 @@ namespace ErsatzTV.Infrastructure.Plex
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private PlexShow ProjectToShow(PlexMetadataResponse response, int mediaSourceId)
|
||||
{
|
||||
DateTime dateAdded = DateTimeOffset.FromUnixTimeSeconds(response.AddedAt).DateTime;
|
||||
DateTime lastWriteTime = DateTimeOffset.FromUnixTimeSeconds(response.UpdatedAt).DateTime;
|
||||
|
||||
var metadata = new ShowMetadata
|
||||
{
|
||||
Title = response.Title,
|
||||
SortTitle = _fallbackMetadataProvider.GetSortTitle(response.Title),
|
||||
Plot = response.Summary,
|
||||
ReleaseDate = DateTime.Parse(response.OriginallyAvailableAt),
|
||||
Year = response.Year,
|
||||
Tagline = response.Tagline,
|
||||
DateAdded = dateAdded,
|
||||
DateUpdated = lastWriteTime,
|
||||
Genres = Optional(response.Genre).Flatten().Map(g => new Genre { Name = g.Tag }).ToList()
|
||||
};
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(response.Thumb))
|
||||
{
|
||||
var path = $"plex/{mediaSourceId}{response.Thumb}";
|
||||
var artwork = new Artwork
|
||||
{
|
||||
ArtworkKind = ArtworkKind.Poster,
|
||||
Path = path,
|
||||
DateAdded = dateAdded,
|
||||
DateUpdated = lastWriteTime
|
||||
};
|
||||
|
||||
metadata.Artwork ??= new List<Artwork>();
|
||||
metadata.Artwork.Add(artwork);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(response.Art))
|
||||
{
|
||||
var path = $"plex/{mediaSourceId}{response.Art}";
|
||||
var artwork = new Artwork
|
||||
{
|
||||
ArtworkKind = ArtworkKind.FanArt,
|
||||
Path = path,
|
||||
DateAdded = dateAdded,
|
||||
DateUpdated = lastWriteTime
|
||||
};
|
||||
|
||||
metadata.Artwork ??= new List<Artwork>();
|
||||
metadata.Artwork.Add(artwork);
|
||||
}
|
||||
|
||||
var show = new PlexShow
|
||||
{
|
||||
Key = response.Key,
|
||||
ShowMetadata = new List<ShowMetadata> { metadata }
|
||||
};
|
||||
|
||||
return show;
|
||||
}
|
||||
|
||||
private PlexSeason ProjectToSeason(PlexMetadataResponse response, int mediaSourceId)
|
||||
{
|
||||
DateTime dateAdded = DateTimeOffset.FromUnixTimeSeconds(response.AddedAt).DateTime;
|
||||
DateTime lastWriteTime = DateTimeOffset.FromUnixTimeSeconds(response.UpdatedAt).DateTime;
|
||||
|
||||
var metadata = new SeasonMetadata
|
||||
{
|
||||
Title = response.Title,
|
||||
SortTitle = _fallbackMetadataProvider.GetSortTitle(response.Title),
|
||||
Year = response.Year,
|
||||
DateAdded = dateAdded,
|
||||
DateUpdated = lastWriteTime
|
||||
};
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(response.Thumb))
|
||||
{
|
||||
var path = $"plex/{mediaSourceId}{response.Thumb}";
|
||||
var artwork = new Artwork
|
||||
{
|
||||
ArtworkKind = ArtworkKind.Poster,
|
||||
Path = path,
|
||||
DateAdded = dateAdded,
|
||||
DateUpdated = lastWriteTime
|
||||
};
|
||||
|
||||
metadata.Artwork ??= new List<Artwork>();
|
||||
metadata.Artwork.Add(artwork);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(response.Art))
|
||||
{
|
||||
var path = $"plex/{mediaSourceId}{response.Art}";
|
||||
var artwork = new Artwork
|
||||
{
|
||||
ArtworkKind = ArtworkKind.FanArt,
|
||||
Path = path,
|
||||
DateAdded = dateAdded,
|
||||
DateUpdated = lastWriteTime
|
||||
};
|
||||
|
||||
metadata.Artwork ??= new List<Artwork>();
|
||||
metadata.Artwork.Add(artwork);
|
||||
}
|
||||
|
||||
var season = new PlexSeason
|
||||
{
|
||||
Key = response.Key,
|
||||
SeasonNumber = response.Index,
|
||||
SeasonMetadata = new List<SeasonMetadata> { metadata }
|
||||
};
|
||||
|
||||
return season;
|
||||
}
|
||||
|
||||
private PlexEpisode ProjectToEpisode(PlexMetadataResponse response, int mediaSourceId)
|
||||
{
|
||||
PlexMediaResponse media = response.Media.Head();
|
||||
PlexPartResponse part = media.Part.Head();
|
||||
DateTime dateAdded = DateTimeOffset.FromUnixTimeSeconds(response.AddedAt).DateTime;
|
||||
DateTime lastWriteTime = DateTimeOffset.FromUnixTimeSeconds(response.UpdatedAt).DateTime;
|
||||
|
||||
var metadata = new EpisodeMetadata
|
||||
{
|
||||
Title = response.Title,
|
||||
SortTitle = _fallbackMetadataProvider.GetSortTitle(response.Title),
|
||||
Plot = response.Summary,
|
||||
Year = response.Year,
|
||||
Tagline = response.Tagline,
|
||||
DateAdded = dateAdded,
|
||||
DateUpdated = lastWriteTime
|
||||
};
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(response.OriginallyAvailableAt))
|
||||
{
|
||||
metadata.ReleaseDate = DateTime.Parse(response.OriginallyAvailableAt);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(response.Thumb))
|
||||
{
|
||||
var path = $"plex/{mediaSourceId}{response.Thumb}";
|
||||
var artwork = new Artwork
|
||||
{
|
||||
ArtworkKind = ArtworkKind.Thumbnail,
|
||||
Path = path,
|
||||
DateAdded = dateAdded,
|
||||
DateUpdated = lastWriteTime
|
||||
};
|
||||
|
||||
metadata.Artwork ??= new List<Artwork>();
|
||||
metadata.Artwork.Add(artwork);
|
||||
}
|
||||
|
||||
var version = new MediaVersion
|
||||
{
|
||||
Name = "Main",
|
||||
Duration = TimeSpan.FromMilliseconds(media.Duration),
|
||||
Width = media.Width,
|
||||
Height = media.Height,
|
||||
AudioCodec = media.AudioCodec,
|
||||
VideoCodec = media.VideoCodec,
|
||||
VideoProfile = media.VideoProfile,
|
||||
// specifically omit sample aspect ratio
|
||||
DateUpdated = lastWriteTime,
|
||||
MediaFiles = new List<MediaFile>
|
||||
{
|
||||
new PlexMediaFile
|
||||
{
|
||||
PlexId = part.Id,
|
||||
Key = part.Key,
|
||||
Path = part.File
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var episode = new PlexEpisode
|
||||
{
|
||||
Key = response.Key,
|
||||
EpisodeNumber = response.Index,
|
||||
EpisodeMetadata = new List<EpisodeMetadata> { metadata },
|
||||
MediaVersions = new List<MediaVersion> { version }
|
||||
};
|
||||
|
||||
return episode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user