Adding thumbnails management (downloading, checking local versions...)

Finishing the browse/library tab of the web app.
This commit is contained in:
Zoe Roux 2019-08-19 01:34:01 +02:00
parent fe649df033
commit edc5def19a
12 changed files with 191 additions and 34 deletions

View File

@ -1,7 +1,7 @@
<div>
<div class="container justify-content-center">
<a class="show" *ngFor="let show of this.shows" routerLink="/shows/{{show.slug}}">
<img src="{{show.imgPrimary}}" />
<p>{{show.title}}</p>
<img [style.background-image]="getThumb(show.slug)"/>
<p class="title">{{show.title}}</p>
<p class="date" *ngIf="show.endYear; else elseBlock">{{show.startYear}} - {{show.endYear}}</p>
<ng-template #elseBlock><p class="date">{{show.startYear}}</p></ng-template>
</a>

View File

@ -2,14 +2,20 @@
@import "~bootstrap/scss/variables";
@import "~bootstrap/scss//mixins/breakpoints";
.container
{
display: flex;
flex-wrap: wrap;
}
.show
{
width: 33%;
list-style: none;
padding: 1em;
display: inline-block;
text-decoration: none;
color: inherit;
outline: none;
@include media-breakpoint-up(md)
{
@ -26,10 +32,26 @@
width: 18%;
}
&:focus, &:hover
{
> img
{
outline: solid var(--accentColor);
}
> .title
{
text-decoration: underline;
}
}
> img
{
max-width: 100%;
max-height: 100%;
width: 100%;
height: 0;
padding-top: 147.0588%;
background-size: cover;
background-color: #333333;
}
> p
@ -39,6 +61,12 @@
text-overflow: ellipsis;
text-align: center;
margin-bottom: 0px;
&.date
{
opacity: 0.8;
font-size: 0.8em;
}
}
&:hover
@ -46,9 +74,3 @@
cursor: pointer;
}
}
.date
{
opacity: 0.8;
font-size: 0.8em;
}

View File

@ -1,6 +1,7 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { HttpClient } from '@angular/common/http';
import { DomSanitizer, SafeStyle } from '@angular/platform-browser';
@Component({
selector: 'app-browse',
@ -13,7 +14,7 @@ export class BrowseComponent implements OnInit
private watch: any;
constructor(private http: HttpClient, private route: ActivatedRoute) { }
constructor(private http: HttpClient, private route: ActivatedRoute, private sanitizer: DomSanitizer) {}
ngOnInit()
{
@ -42,4 +43,9 @@ export class BrowseComponent implements OnInit
{
this.watch.unsubscribe();
}
getThumb(slug: string)
{
return this.sanitizer.bypassSecurityTrustStyle("url(/thumb/" + slug + ")");
}
}

View File

@ -1 +1,2 @@
<p>Should display show details of: {{this.show.title}}</p>
<p *ngIf="this.show; else elseBlock">Should display show details of: {{this.show.title}}</p>
<ng-template #elseBlock>Loading</ng-template>

View File

@ -0,0 +1,25 @@
using Kyoo.InternalAPI;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.FileProviders;
// For more information on enabling MVC for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
namespace Kyoo.Controllers
{
public class ThumbnailController : Controller
{
private ILibraryManager libraryManager;
public ThumbnailController(ILibraryManager libraryManager)
{
this.libraryManager = libraryManager;
}
[HttpGet("thumb/{showSlug}")]
public IActionResult GetShowThumb(string showSlug)
{
string thumbPath = libraryManager.GetShowBySlug(showSlug).ImgPrimary;
return new PhysicalFileResult(thumbPath, "image/jpg");
}
}
}

View File

@ -42,7 +42,7 @@ namespace Kyoo.InternalAPI
public async void Scan(string folderPath)
{
string[] files = Directory.GetFiles(folderPath);
string[] files = Directory.GetFiles(folderPath, "*", SearchOption.AllDirectories);
foreach (string file in files)
{
@ -133,10 +133,9 @@ namespace Kyoo.InternalAPI
seasonID = libraryManager.RegisterSeason(season);
}
Episode episode = await metadataProvider.GetEpisode(showProviderIDs, seasonNumber, episodeNumber);
Episode episode = await metadataProvider.GetEpisode(showProviderIDs, seasonNumber, episodeNumber, path);
episode.ShowID = showID;
episode.SeasonID = seasonID;
episode.Path = path;
libraryManager.RegisterEpisode(episode);
}
}

View File

@ -15,6 +15,6 @@ namespace Kyoo.InternalAPI
Task<string> GetSeasonImage(string showName, long seasonNumber);
//For the episodes
Task<Episode> GetEpisode(string externalIDs, long seasonNumber, long episodeNumber);
Task<Episode> GetEpisode(string externalIDs, long seasonNumber, long episodeNumber, string episodePath);
}
}

View File

@ -269,7 +269,7 @@ namespace Kyoo.InternalAPI.MetadataProvider
return null;
}
public async Task<Episode> GetEpisode(string externalIDs, long seasonNumber, long episodeNumber)
public async Task<Episode> GetEpisode(string externalIDs, long seasonNumber, long episodeNumber, string episodePath)
{
string id = GetID(externalIDs);

View File

@ -1,9 +1,11 @@
using Kyoo.InternalAPI.MetadataProvider;
using Kyoo.InternalAPI.ThumbnailsManager;
using Kyoo.Models;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
@ -12,10 +14,12 @@ namespace Kyoo.InternalAPI
public class ProviderManager : IMetadataProvider
{
private readonly List<IMetadataProvider> providers = new List<IMetadataProvider>();
private readonly IThumbnailsManager thumbnailsManager;
private readonly IConfiguration config;
public ProviderManager(IConfiguration configuration)
public ProviderManager(IThumbnailsManager thumbnailsManager, IConfiguration configuration)
{
this.thumbnailsManager = thumbnailsManager;
config = configuration;
LoadProviders();
}
@ -58,12 +62,21 @@ namespace Kyoo.InternalAPI
}
}
//public Show MergeShows(Show baseShow, Show newShow)
//{
public Show Merge(IEnumerable<Show> shows)
{
return shows.FirstOrDefault();
}
//}
public Season Merge(IEnumerable<Season> seasons)
{
return seasons.FirstOrDefault();
}
public Episode Merge(IEnumerable<Episode> episodes)
{
return episodes.FirstOrDefault();
}
//For all the following methods, it should use all providers and merge the data.
public Task<Show> GetImages(Show show)
@ -71,34 +84,69 @@ namespace Kyoo.InternalAPI
return providers[0].GetImages(show);
}
public Task<Season> GetSeason(string showName, int seasonNumber)
public async Task<Season> GetSeason(string showName, int seasonNumber)
{
return providers[0].GetSeason(showName, seasonNumber);
List<Season> datas = new List<Season>();
for (int i = 0; i < providers.Count; i++)
{
datas.Add(await providers[i].GetSeason(showName, seasonNumber));
}
return Merge(datas);
}
public Task<Show> GetShowByID(string id)
public async Task<Show> GetShowByID(string id)
{
return providers[0].GetShowByID(id);
List<Show> datas = new List<Show>();
for (int i = 0; i < providers.Count; i++)
{
datas.Add(await providers[i].GetShowByID(id));
}
return Merge(datas);
}
public Task<Show> GetShowFromName(string showName, string showPath)
public async Task<Show> GetShowFromName(string showName, string showPath)
{
return providers[0].GetShowFromName(showName, showPath);
List<Show> datas = new List<Show>();
for (int i = 0; i < providers.Count; i++)
{
datas.Add(await providers[i].GetShowFromName(showName, showPath));
}
Show show = Merge(datas);
return thumbnailsManager.Validate(show);
}
public Task<Season> GetSeason(string showName, long seasonNumber)
public async Task<Season> GetSeason(string showName, long seasonNumber)
{
return providers[0].GetSeason(showName, seasonNumber);
List<Season> datas = new List<Season>();
for (int i = 0; i < providers.Count; i++)
{
datas.Add(await providers[i].GetSeason(showName, seasonNumber));
}
return Merge(datas);
}
public Task<string> GetSeasonImage(string showName, long seasonNumber)
{
//Should select the best provider for this show.
return providers[0].GetSeasonImage(showName, seasonNumber);
}
public Task<Episode> GetEpisode(string externalIDs, long seasonNumber, long episodeNumber)
public async Task<Episode> GetEpisode(string externalIDs, long seasonNumber, long episodeNumber, string episodePath)
{
return providers[0].GetEpisode(externalIDs, seasonNumber, episodeNumber);
List<Episode> datas = new List<Episode>();
for (int i = 0; i < providers.Count; i++)
{
datas.Add(await providers[i].GetEpisode(externalIDs, seasonNumber, episodeNumber, episodePath));
}
Episode episode = Merge(datas);
episode.Path = episodePath;
return thumbnailsManager.Validate(episode);
}
}
}

View File

@ -0,0 +1,10 @@
using Kyoo.Models;
namespace Kyoo.InternalAPI.ThumbnailsManager
{
public interface IThumbnailsManager
{
Show Validate(Show show);
Episode Validate(Episode episode);
}
}

View File

@ -0,0 +1,40 @@
using Kyoo.Models;
using System;
using System.IO;
using System.Net;
namespace Kyoo.InternalAPI.ThumbnailsManager
{
public class ThumbnailsManager : IThumbnailsManager
{
public Show Validate(Show show)
{
string localThumb = Path.Combine(show.Path, "poster.jpg");
if (!File.Exists(localThumb))
{
using (WebClient client = new WebClient())
{
client.DownloadFileAsync(new Uri(show.ImgPrimary), localThumb);
}
}
show.ImgPrimary = localThumb;
return show;
}
public Episode Validate(Episode episode)
{
string localThumb = Path.ChangeExtension(episode.Path, "jpg");
if (!File.Exists(localThumb))
{
using (WebClient client = new WebClient())
{
client.DownloadFileAsync(new Uri(episode.ImgPrimary), localThumb);
}
}
episode.ImgPrimary = localThumb;
return episode;
}
}
}

View File

@ -1,5 +1,6 @@
using Kyoo.InternalAPI;
using Kyoo.InternalAPI.MetadataProvider;
using Kyoo.InternalAPI.ThumbnailsManager;
using Kyoo.Models;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
@ -7,6 +8,7 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SpaServices.AngularCli;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using System.Diagnostics;
using System.Web.Http;
@ -32,8 +34,12 @@ namespace Kyoo
configuration.RootPath = "ClientApp/dist";
});
//Services needed in the private and in the public API
services.AddSingleton<ILibraryManager, LibraryManager>();
//Services used to get informations about files and register them
services.AddHostedService<Crawler>();
services.AddSingleton<IThumbnailsManager, ThumbnailsManager>();
services.AddSingleton<IMetadataProvider, ProviderManager>();
}