Browse Source

fix television artwork

pull/31/head
Jason Dove 6 years ago
parent
commit
e6c0b5b05d
  1. 18
      ErsatzTV.Application/Television/Mapper.cs
  2. 5
      ErsatzTV.Core.Tests/Fakes/FakeTelevisionRepository.cs
  3. 4
      ErsatzTV.Core/Interfaces/Repositories/ITelevisionRepository.cs
  4. 2
      ErsatzTV.Core/Metadata/LocalFolderScanner.cs
  5. 25
      ErsatzTV.Core/Metadata/LocalMetadataProvider.cs
  6. 23
      ErsatzTV.Core/Metadata/TelevisionFolderScanner.cs
  7. 14
      ErsatzTV.Infrastructure/Data/Repositories/MovieRepository.cs
  8. 59
      ErsatzTV.Infrastructure/Data/Repositories/TelevisionRepository.cs
  9. 14
      ErsatzTV/Controllers/ArtworkController.cs
  10. 2
      ErsatzTV/Pages/Movie.razor
  11. 2
      ErsatzTV/Pages/TelevisionEpisode.razor
  12. 5
      ErsatzTV/Pages/TelevisionEpisodeList.razor
  13. 2
      ErsatzTV/Pages/TelevisionSeasonList.razor
  14. 17
      ErsatzTV/Shared/MediaCard.razor

18
ErsatzTV.Application/Television/Mapper.cs

@ -1,4 +1,6 @@ @@ -1,4 +1,6 @@
using ErsatzTV.Core.Domain;
using System.Linq;
using ErsatzTV.Core.Domain;
using static LanguageExt.Prelude;
namespace ErsatzTV.Application.Television
{
@ -10,7 +12,7 @@ namespace ErsatzTV.Application.Television @@ -10,7 +12,7 @@ namespace ErsatzTV.Application.Television
show.ShowMetadata.HeadOrNone().Map(m => m.Title).IfNone(string.Empty),
show.ShowMetadata.HeadOrNone().Map(m => m.ReleaseDate?.Year.ToString()).IfNone(string.Empty),
show.ShowMetadata.HeadOrNone().Map(m => m.Plot).IfNone(string.Empty),
null); // TODO: artwork
show.ShowMetadata.HeadOrNone().Map(GetPoster).IfNone(string.Empty));
internal static TelevisionSeasonViewModel ProjectToViewModel(Season season) =>
new(
@ -19,7 +21,7 @@ namespace ErsatzTV.Application.Television @@ -19,7 +21,7 @@ namespace ErsatzTV.Application.Television
season.Show.ShowMetadata.HeadOrNone().Map(m => m.Title).IfNone(string.Empty),
season.Show.ShowMetadata.HeadOrNone().Map(m => m.ReleaseDate?.Year.ToString()).IfNone(string.Empty),
season.SeasonNumber == 0 ? "Specials" : $"Season {season.SeasonNumber}",
null); // TODO: artwork
season.SeasonMetadata.HeadOrNone().Map(GetPoster).IfNone(string.Empty));
internal static TelevisionEpisodeViewModel ProjectToViewModel(Episode episode) =>
new(
@ -28,6 +30,14 @@ namespace ErsatzTV.Application.Television @@ -28,6 +30,14 @@ namespace ErsatzTV.Application.Television
episode.EpisodeNumber,
episode.EpisodeMetadata.HeadOrNone().Map(m => m.Title).IfNone(string.Empty),
episode.EpisodeMetadata.HeadOrNone().Map(m => m.Plot).IfNone(string.Empty),
null); // TODO: artwork
episode.EpisodeMetadata.HeadOrNone().Map(GetThumbnail).IfNone(string.Empty));
private static string GetPoster(Metadata metadata) =>
Optional(metadata.Artwork.FirstOrDefault(a => a.ArtworkKind == ArtworkKind.Poster))
.Match(a => a.Path, string.Empty);
private static string GetThumbnail(Metadata metadata) =>
Optional(metadata.Artwork.FirstOrDefault(a => a.ArtworkKind == ArtworkKind.Thumbnail))
.Match(a => a.Path, string.Empty);
}
}

5
ErsatzTV.Core.Tests/Fakes/FakeTelevisionRepository.cs

@ -44,10 +44,11 @@ namespace ErsatzTV.Core.Tests.Fakes @@ -44,10 +44,11 @@ namespace ErsatzTV.Core.Tests.Fakes
public Task<List<EpisodeMetadata>> GetPagedEpisodes(int seasonId, int pageNumber, int pageSize) =>
throw new NotSupportedException();
public Task<Option<Show>> GetShowByMetadata(ShowMetadata metadata) => throw new NotSupportedException();
public Task<Option<Show>> GetShowByMetadata(int libraryPathId, ShowMetadata metadata) =>
throw new NotSupportedException();
public Task<Either<BaseError, Show>>
AddShow(int localMediaSourceId, string showFolder, ShowMetadata metadata) =>
AddShow(int libraryPathId, string showFolder, ShowMetadata metadata) =>
throw new NotSupportedException();
public Task<Either<BaseError, Season>> GetOrAddSeason(Show show, string path, int seasonNumber) =>

4
ErsatzTV.Core/Interfaces/Repositories/ITelevisionRepository.cs

@ -23,10 +23,10 @@ namespace ErsatzTV.Core.Interfaces.Repositories @@ -23,10 +23,10 @@ namespace ErsatzTV.Core.Interfaces.Repositories
Task<Option<Episode>> GetEpisode(int televisionEpisodeId);
Task<int> GetEpisodeCount(int televisionSeasonId);
Task<List<EpisodeMetadata>> GetPagedEpisodes(int seasonId, int pageNumber, int pageSize);
Task<Option<Show>> GetShowByMetadata(ShowMetadata metadata);
Task<Option<Show>> GetShowByMetadata(int libraryPathId, ShowMetadata metadata);
Task<Either<BaseError, Show>> AddShow(
int localMediaSourceId,
int libraryPathId,
string showFolder,
ShowMetadata metadata);

2
ErsatzTV.Core/Metadata/LocalFolderScanner.cs

@ -88,6 +88,8 @@ namespace ErsatzTV.Core.Metadata @@ -88,6 +88,8 @@ namespace ErsatzTV.Core.Metadata
{
DateTime lastWriteTime = _localFileSystem.GetLastWriteTime(artworkFile);
metadata.Artwork ??= new List<Artwork>();
Option<Artwork> maybePoster =
metadata.Artwork.FirstOrDefault(a => a.ArtworkKind == artworkKind);

25
ErsatzTV.Core/Metadata/LocalMetadataProvider.cs

@ -38,19 +38,22 @@ namespace ErsatzTV.Core.Metadata @@ -38,19 +38,22 @@ namespace ErsatzTV.Core.Metadata
_logger = logger;
}
public Task<ShowMetadata> GetMetadataForShow(string showFolder)
public async Task<ShowMetadata> GetMetadataForShow(string showFolder)
{
string nfoFileName = Path.Combine(showFolder, "tvshow.nfo");
return Optional(_localFileSystem.FileExists(nfoFileName))
.Filter(identity).AsTask()
.Bind(_ => LoadTelevisionShowMetadata(nfoFileName))
.IfNoneAsync(() => _fallbackMetadataProvider.GetFallbackMetadataForShow(showFolder).AsTask())
.Map(
m =>
{
m.SortTitle = GetSortTitle(m.Title);
return m;
});
Option<ShowMetadata> maybeMetadata = None;
if (_localFileSystem.FileExists(nfoFileName))
{
maybeMetadata = await LoadTelevisionShowMetadata(nfoFileName);
}
return maybeMetadata.IfNone(
() =>
{
ShowMetadata metadata = _fallbackMetadataProvider.GetFallbackMetadataForShow(showFolder);
metadata.SortTitle = GetSortTitle(metadata.Title);
return metadata;
});
}
public Task<Unit> RefreshSidecarMetadata(MediaItem mediaItem, string path) =>

23
ErsatzTV.Core/Metadata/TelevisionFolderScanner.cs

@ -76,7 +76,7 @@ namespace ErsatzTV.Core.Metadata @@ -76,7 +76,7 @@ namespace ErsatzTV.Core.Metadata
string showFolder)
{
ShowMetadata metadata = await _localMetadataProvider.GetMetadataForShow(showFolder);
Option<Show> maybeShow = await _televisionRepository.GetShowByMetadata(metadata);
Option<Show> maybeShow = await _televisionRepository.GetShowByMetadata(libraryPathId, metadata);
return await maybeShow.Match(
show => Right<BaseError, Show>(show).AsTask(),
async () => await _televisionRepository.AddShow(libraryPathId, showFolder, metadata));
@ -86,9 +86,9 @@ namespace ErsatzTV.Core.Metadata @@ -86,9 +86,9 @@ namespace ErsatzTV.Core.Metadata
LibraryPath libraryPath,
string ffprobePath,
Show show,
string showPath)
string showFolder)
{
foreach (string seasonFolder in _localFileSystem.ListSubdirectories(showPath).Filter(ShouldIncludeFolder)
foreach (string seasonFolder in _localFileSystem.ListSubdirectories(showFolder).Filter(ShouldIncludeFolder)
.OrderBy(identity))
{
Option<int> maybeSeasonNumber = SeasonNumberForFolder(seasonFolder);
@ -97,10 +97,10 @@ namespace ErsatzTV.Core.Metadata @@ -97,10 +97,10 @@ namespace ErsatzTV.Core.Metadata
{
Either<BaseError, Season> maybeSeason = await _televisionRepository
.GetOrAddSeason(show, seasonFolder, seasonNumber)
.BindT(UpdatePoster);
.BindT(season => UpdatePoster(season, seasonFolder));
await maybeSeason.Match(
season => ScanEpisodes(libraryPath, ffprobePath, season),
season => ScanEpisodes(libraryPath, ffprobePath, season, seasonFolder),
_ => Task.FromResult(Unit.Default));
});
}
@ -111,9 +111,10 @@ namespace ErsatzTV.Core.Metadata @@ -111,9 +111,10 @@ namespace ErsatzTV.Core.Metadata
private async Task<Unit> ScanEpisodes(
LibraryPath libraryPath,
string ffprobePath,
Season season)
Season season,
string seasonPath)
{
foreach (string file in _localFileSystem.ListFiles(season.Path)
foreach (string file in _localFileSystem.ListFiles(seasonPath)
.Filter(f => VideoFileExtensions.Contains(Path.GetExtension(f))).OrderBy(identity))
{
// TODO: figure out how to rebuild playlists
@ -227,11 +228,11 @@ namespace ErsatzTV.Core.Metadata @@ -227,11 +228,11 @@ namespace ErsatzTV.Core.Metadata
}
}
private async Task<Either<BaseError, Season>> UpdatePoster(Season season)
private async Task<Either<BaseError, Season>> UpdatePoster(Season season, string seasonFolder)
{
try
{
await LocatePoster(season).IfSomeAsync(
await LocatePoster(season, seasonFolder).IfSomeAsync(
async posterFile =>
{
season.SeasonMetadata ??= new List<SeasonMetadata>();
@ -293,9 +294,9 @@ namespace ErsatzTV.Core.Metadata @@ -293,9 +294,9 @@ namespace ErsatzTV.Core.Metadata
.Filter(s => _localFileSystem.FileExists(s))
.HeadOrNone();
private Option<string> LocatePoster(Season season)
private Option<string> LocatePoster(Season season, string seasonFolder)
{
string folder = Path.GetDirectoryName(season.Path) ?? string.Empty;
string folder = Path.GetDirectoryName(seasonFolder) ?? string.Empty;
return ImageFileExtensions
.Map(ext => Path.Combine(folder, $"season{season.SeasonNumber:00}-poster.{ext}"))
.Filter(s => _localFileSystem.FileExists(s))

14
ErsatzTV.Infrastructure/Data/Repositories/MovieRepository.cs

@ -17,19 +17,27 @@ namespace ErsatzTV.Infrastructure.Data.Repositories @@ -17,19 +17,27 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
{
private readonly IDbConnection _dbConnection;
private readonly TvContext _dbContext;
private readonly IDbContextFactory<TvContext> _dbContextFactory;
public MovieRepository(TvContext dbContext, IDbConnection dbConnection)
public MovieRepository(
TvContext dbContext,
IDbContextFactory<TvContext> dbContextFactory,
IDbConnection dbConnection)
{
_dbContext = dbContext;
_dbContextFactory = dbContextFactory;
_dbConnection = dbConnection;
}
public Task<Option<Movie>> GetMovie(int movieId) =>
_dbContext.Movies
public async Task<Option<Movie>> GetMovie(int movieId)
{
await using TvContext dbContext = _dbContextFactory.CreateDbContext();
return await dbContext.Movies
.Include(m => m.MovieMetadata)
.ThenInclude(m => m.Artwork)
.SingleOrDefaultAsync(m => m.Id == movieId)
.Map(Optional);
}
public async Task<Either<BaseError, Movie>> GetOrAdd(LibraryPath libraryPath, string path)
{

59
ErsatzTV.Infrastructure/Data/Repositories/TelevisionRepository.cs

@ -53,6 +53,7 @@ namespace ErsatzTV.Infrastructure.Data.Repositories @@ -53,6 +53,7 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
.AsNoTracking()
.Filter(s => s.Id == televisionShowId)
.Include(s => s.ShowMetadata)
.ThenInclude(sm => sm.Artwork)
.SingleOrDefaultAsync()
.Map(Optional);
@ -84,6 +85,8 @@ namespace ErsatzTV.Infrastructure.Data.Repositories @@ -84,6 +85,8 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
public Task<Option<Season>> GetSeason(int televisionSeasonId) =>
_dbContext.Seasons
.AsNoTracking()
.Include(s => s.SeasonMetadata)
.ThenInclude(sm => sm.Artwork)
.Include(s => s.Show)
.ThenInclude(s => s.ShowMetadata)
.SingleOrDefaultAsync(s => s.Id == televisionSeasonId)
@ -123,6 +126,7 @@ namespace ErsatzTV.Infrastructure.Data.Repositories @@ -123,6 +126,7 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
.AsNoTracking()
.Include(e => e.Season)
.Include(e => e.EpisodeMetadata)
.ThenInclude(em => em.Artwork)
.SingleOrDefaultAsync(s => s.Id == televisionEpisodeId)
.Map(Optional);
@ -146,13 +150,14 @@ namespace ErsatzTV.Infrastructure.Data.Repositories @@ -146,13 +150,14 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
.Take(pageSize)
.ToListAsync();
public async Task<Option<Show>> GetShowByMetadata(ShowMetadata metadata)
public async Task<Option<Show>> GetShowByMetadata(int libraryPathId, ShowMetadata metadata)
{
Option<int> maybeId = await _dbContext.ShowMetadata
.Where(s => s.Title == metadata.Title && s.ReleaseDate == metadata.ReleaseDate)
.Where(s => s.Show.LibraryPathId == libraryPathId)
.SingleOrDefaultAsync()
.Map(sm => sm.ShowId)
.Map(Optional);
.Map(Optional)
.MapT(sm => sm.ShowId);
return await maybeId.Match(
id =>
@ -160,22 +165,20 @@ namespace ErsatzTV.Infrastructure.Data.Repositories @@ -160,22 +165,20 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
return _dbContext.Shows
.Include(s => s.ShowMetadata)
.ThenInclude(sm => sm.Artwork)
.Filter(s => s.Id == id)
.SingleOrDefaultAsync()
.OrderBy(s => s.Id)
.SingleOrDefaultAsync(s => s.Id == id)
.Map(Optional);
},
() => Option<Show>.None.AsTask());
}
public async Task<Either<BaseError, Show>> AddShow(
int localMediaSourceId,
string showFolder,
ShowMetadata metadata)
public async Task<Either<BaseError, Show>> AddShow(int libraryPathId, string showFolder, ShowMetadata metadata)
{
try
{
var show = new Show
{
LibraryPathId = libraryPathId,
ShowMetadata = new List<ShowMetadata> { metadata },
Seasons = new List<Season>()
};
@ -191,27 +194,23 @@ namespace ErsatzTV.Infrastructure.Data.Repositories @@ -191,27 +194,23 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
}
}
public async Task<Either<BaseError, Season>> GetOrAddSeason(
Show show,
string path,
int seasonNumber)
public async Task<Either<BaseError, Season>> GetOrAddSeason(Show show, string path, int seasonNumber)
{
Option<Season> maybeExisting = await _dbContext.Seasons
.SingleOrDefaultAsync(i => i.Path == path);
.Include(s => s.SeasonMetadata)
.ThenInclude(sm => sm.Artwork)
.SingleOrDefaultAsync(s => s.ShowId == show.Id && s.SeasonNumber == seasonNumber);
return await maybeExisting.Match(
season => Right<BaseError, Season>(season).AsTask(),
() => AddSeason(show, path, seasonNumber));
}
// TODO: don't use mediaSourceId, use library path id
public async Task<Either<BaseError, Episode>> GetOrAddEpisode(
Season season,
LibraryPath libraryPath,
string path)
public async Task<Either<BaseError, Episode>> GetOrAddEpisode(Season season, LibraryPath libraryPath, string path)
{
Option<Episode> maybeExisting = await _dbContext.Episodes
.Include(i => i.EpisodeMetadata)
.ThenInclude(em => em.Artwork)
.Include(i => i.Statistics)
.SingleOrDefaultAsync(i => i.Path == path);
@ -253,17 +252,17 @@ namespace ErsatzTV.Infrastructure.Data.Repositories @@ -253,17 +252,17 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
.Filter(e => e.SeasonId == seasonId)
.ToListAsync();
private async Task<Either<BaseError, Season>> AddSeason(
Show show,
string path,
int seasonNumber)
private async Task<Either<BaseError, Season>> AddSeason(Show show, string path, int seasonNumber)
{
try
{
var season = new Season
{
ShowId = show.Id, Path = path, SeasonNumber = seasonNumber,
Episodes = new List<Episode>()
ShowId = show.Id,
Path = path,
SeasonNumber = seasonNumber,
Episodes = new List<Episode>(),
SeasonMetadata = new List<SeasonMetadata>()
};
await _dbContext.Seasons.AddAsync(season);
await _dbContext.SaveChangesAsync();
@ -275,18 +274,16 @@ namespace ErsatzTV.Infrastructure.Data.Repositories @@ -275,18 +274,16 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
}
}
private async Task<Either<BaseError, Episode>> AddEpisode(
Season season,
int mediaSourceId,
string path)
private async Task<Either<BaseError, Episode>> AddEpisode(Season season, int libraryPathId, string path)
{
try
{
var episode = new Episode
{
LibraryPathId = mediaSourceId,
LibraryPathId = libraryPathId,
SeasonId = season.Id,
Path = path
Path = path,
EpisodeMetadata = new List<EpisodeMetadata>()
};
await _dbContext.Episodes.AddAsync(episode);
await _dbContext.SaveChangesAsync();

14
ErsatzTV/Controllers/PostersController.cs → ErsatzTV/Controllers/ArtworkController.cs

@ -17,8 +17,8 @@ namespace ErsatzTV.Controllers @@ -17,8 +17,8 @@ namespace ErsatzTV.Controllers
public PostersController(IMediator mediator) => _mediator = mediator;
[HttpGet("/posters/{fileName}")]
public async Task<IActionResult> GetImage(string fileName)
[HttpGet("/artwork/posters/{fileName}")]
public async Task<IActionResult> GetPoster(string fileName)
{
Either<BaseError, ImageViewModel> imageContents =
await _mediator.Send(new GetImageContents(fileName, ArtworkKind.Poster, 440));
@ -26,5 +26,15 @@ namespace ErsatzTV.Controllers @@ -26,5 +26,15 @@ namespace ErsatzTV.Controllers
Left: _ => new NotFoundResult(),
Right: r => new FileContentResult(r.Contents, r.MimeType));
}
[HttpGet("/artwork/thumbnails/{fileName}")]
public async Task<IActionResult> GetThumbnail(string fileName)
{
Either<BaseError, ImageViewModel> imageContents =
await _mediator.Send(new GetImageContents(fileName, ArtworkKind.Thumbnail, 220));
return imageContents.Match<IActionResult>(
Left: _ => new NotFoundResult(),
Right: r => new FileContentResult(r.Contents, r.MimeType));
}
}
}

2
ErsatzTV/Pages/Movie.razor

@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
@if (!string.IsNullOrWhiteSpace(_movie.Poster))
{
<MudPaper Style="flex-shrink: 0;">
<MudCardMedia Image="@($"/posters/{_movie.Poster}")" Style="height: 440px; width: 304px;"/>
<MudCardMedia Image="@($"/artwork/posters/{_movie.Poster}")" Style="height: 440px; width: 304px;"/>
</MudPaper>
}
<MudCardContent Class="mx-3 my-3">

2
ErsatzTV/Pages/TelevisionEpisode.razor

@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
@if (!string.IsNullOrWhiteSpace(_episode.Poster))
{
<MudPaper style="display: flex; flex-direction: column">
<MudCardMedia Image="@($"/posters/{_episode.Poster}")" Style="flex-grow: 1; height: 220px; width: 392px;"/>
<MudCardMedia Image="@($"/artwork/thumbnails/{_episode.Poster}")" Style="flex-grow: 1; height: 220px; width: 392px;"/>
</MudPaper>
}
<MudCardContent Class="ml-3">

5
ErsatzTV/Pages/TelevisionEpisodeList.razor

@ -18,7 +18,7 @@ @@ -18,7 +18,7 @@
@if (!string.IsNullOrWhiteSpace(_season.Poster))
{
<MudPaper Style="flex-shrink: 0;">
<MudCardMedia Image="@($"/posters/{_season.Poster}")" Style="height: 440px; width: 304px;"/>
<MudCardMedia Image="@($"/artwork/posters/{_season.Poster}")" Style="height: 440px; width: 304px;"/>
</MudPaper>
}
<MudCardContent Class="mx-3 my-3">
@ -55,7 +55,8 @@ @@ -55,7 +55,8 @@
Link="@($"/media/tv/episodes/{card.EpisodeId}")"
DataRefreshed="@(() => RefreshData())"
ContainerClass="media-card-episode-container mx-2"
CardClass="media-card-episode"/>
CardClass="media-card-episode"
ArtworkKind="@ArtworkKind.Thumbnail"/>
}
</MudContainer>

2
ErsatzTV/Pages/TelevisionSeasonList.razor

@ -18,7 +18,7 @@ @@ -18,7 +18,7 @@
@if (!string.IsNullOrWhiteSpace(_show.Poster))
{
<MudPaper Style="flex-shrink: 0;">
<MudCardMedia Image="@($"/posters/{_show.Poster}")" Style="height: 440px; width: 304px;"/>
<MudCardMedia Image="@($"/artwork/posters/{_show.Poster}")" Style="height: 440px; width: 304px;"/>
</MudPaper>
}
<MudCardContent Class="mx-3 my-3">

17
ErsatzTV/Shared/MediaCard.razor

@ -6,7 +6,7 @@ @@ -6,7 +6,7 @@
@if (!string.IsNullOrWhiteSpace(Link))
{
<div style="position: relative">
<MudPaper Class="@($"media-card {CardClass}")" Style="@PosterForItem()">
<MudPaper Class="@($"media-card {CardClass}")" Style="@ArtworkForItem()">
@if (string.IsNullOrWhiteSpace(Data.Poster))
{
<MudText Align="Align.Center" Typo="Typo.h1" Class="media-card-poster-placeholder mud-text-disabled">
@ -29,7 +29,7 @@ @@ -29,7 +29,7 @@
}
else
{
<MudPaper Class="@($"media-card {CardClass}")" Style="@PosterForItem()">
<MudPaper Class="@($"media-card {CardClass}")" Style="@ArtworkForItem()">
@if (string.IsNullOrWhiteSpace(Data.Poster))
{
<MudText Align="Align.Center" Typo="Typo.h1" Class="media-card-poster-placeholder mud-text-disabled">
@ -72,6 +72,9 @@ @@ -72,6 +72,9 @@
[Parameter]
public string CardClass { get; set; }
[Parameter]
public ArtworkKind ArtworkKind { get; set; }
[Parameter]
public EventCallback<MediaCardViewModel> DeleteClicked { get; set; }
@ -86,8 +89,14 @@ @@ -86,8 +89,14 @@
return int.TryParse(first, out _) ? "#" : first;
}
private string PosterForItem() => string.IsNullOrWhiteSpace(Data.Poster)
private string ArtworkForItem() => string.IsNullOrWhiteSpace(Data.Poster)
? "position: relative"
: $"position: relative; background-image: url(/posters/{Data.Poster}); background-size: cover";
: $"position: relative; background-image: url(/artwork/{PathForArtwork()}/{Data.Poster}); background-size: cover";
private string PathForArtwork() => ArtworkKind switch
{
ArtworkKind.Thumbnail => "thumbnails",
_ => "posters"
};
}
Loading…
Cancel
Save