-
- {{show.title}}
+
+ {{show.title}}
{{show.startYear}} - {{show.endYear}}
{{show.startYear}}
diff --git a/Kyoo/ClientApp/src/app/browse/browse.component.scss b/Kyoo/ClientApp/src/app/browse/browse.component.scss
index ab3912b0..896b5959 100644
--- a/Kyoo/ClientApp/src/app/browse/browse.component.scss
+++ b/Kyoo/ClientApp/src/app/browse/browse.component.scss
@@ -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;
-}
diff --git a/Kyoo/ClientApp/src/app/browse/browse.component.ts b/Kyoo/ClientApp/src/app/browse/browse.component.ts
index ec67ea62..d51dc9b7 100644
--- a/Kyoo/ClientApp/src/app/browse/browse.component.ts
+++ b/Kyoo/ClientApp/src/app/browse/browse.component.ts
@@ -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 + ")");
+ }
}
diff --git a/Kyoo/ClientApp/src/app/show-details/show-details.component.html b/Kyoo/ClientApp/src/app/show-details/show-details.component.html
index 7dd1e78c..bb864159 100644
--- a/Kyoo/ClientApp/src/app/show-details/show-details.component.html
+++ b/Kyoo/ClientApp/src/app/show-details/show-details.component.html
@@ -1 +1,2 @@
-
Should display show details of: {{this.show.title}}
+
Should display show details of: {{this.show.title}}
+
Loading
diff --git a/Kyoo/Controllers/ThumbnailController.cs b/Kyoo/Controllers/ThumbnailController.cs
new file mode 100644
index 00000000..90d0e89f
--- /dev/null
+++ b/Kyoo/Controllers/ThumbnailController.cs
@@ -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");
+ }
+ }
+}
diff --git a/Kyoo/InternalAPI/Crawler/Crawler.cs b/Kyoo/InternalAPI/Crawler/Crawler.cs
index dd9aaa56..961daa01 100644
--- a/Kyoo/InternalAPI/Crawler/Crawler.cs
+++ b/Kyoo/InternalAPI/Crawler/Crawler.cs
@@ -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);
}
}
diff --git a/Kyoo/InternalAPI/MetadataProvider/IMetadataProvider.cs b/Kyoo/InternalAPI/MetadataProvider/IMetadataProvider.cs
index 26e0cb37..dcdb6325 100644
--- a/Kyoo/InternalAPI/MetadataProvider/IMetadataProvider.cs
+++ b/Kyoo/InternalAPI/MetadataProvider/IMetadataProvider.cs
@@ -15,6 +15,6 @@ namespace Kyoo.InternalAPI
Task
GetSeasonImage(string showName, long seasonNumber);
//For the episodes
- Task GetEpisode(string externalIDs, long seasonNumber, long episodeNumber);
+ Task GetEpisode(string externalIDs, long seasonNumber, long episodeNumber, string episodePath);
}
}
diff --git a/Kyoo/InternalAPI/MetadataProvider/Implementations/TheTvDB/ProviderTheTvDB.cs b/Kyoo/InternalAPI/MetadataProvider/Implementations/TheTvDB/ProviderTheTvDB.cs
index 16d0bfb2..03076474 100644
--- a/Kyoo/InternalAPI/MetadataProvider/Implementations/TheTvDB/ProviderTheTvDB.cs
+++ b/Kyoo/InternalAPI/MetadataProvider/Implementations/TheTvDB/ProviderTheTvDB.cs
@@ -269,7 +269,7 @@ namespace Kyoo.InternalAPI.MetadataProvider
return null;
}
- public async Task GetEpisode(string externalIDs, long seasonNumber, long episodeNumber)
+ public async Task GetEpisode(string externalIDs, long seasonNumber, long episodeNumber, string episodePath)
{
string id = GetID(externalIDs);
diff --git a/Kyoo/InternalAPI/MetadataProvider/ProviderManager.cs b/Kyoo/InternalAPI/MetadataProvider/ProviderManager.cs
index fe43254c..f8fb365b 100644
--- a/Kyoo/InternalAPI/MetadataProvider/ProviderManager.cs
+++ b/Kyoo/InternalAPI/MetadataProvider/ProviderManager.cs
@@ -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 providers = new List();
+ 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 shows)
+ {
+ return shows.FirstOrDefault();
+ }
- //}
+ public Season Merge(IEnumerable seasons)
+ {
+ return seasons.FirstOrDefault();
+ }
+
+ public Episode Merge(IEnumerable episodes)
+ {
+ return episodes.FirstOrDefault();
+ }
-
//For all the following methods, it should use all providers and merge the data.
public Task GetImages(Show show)
@@ -71,34 +84,69 @@ namespace Kyoo.InternalAPI
return providers[0].GetImages(show);
}
- public Task GetSeason(string showName, int seasonNumber)
+ public async Task GetSeason(string showName, int seasonNumber)
{
- return providers[0].GetSeason(showName, seasonNumber);
+ List datas = new List();
+ for (int i = 0; i < providers.Count; i++)
+ {
+ datas.Add(await providers[i].GetSeason(showName, seasonNumber));
+ }
+
+ return Merge(datas);
}
- public Task GetShowByID(string id)
+ public async Task GetShowByID(string id)
{
- return providers[0].GetShowByID(id);
+ List datas = new List();
+ for (int i = 0; i < providers.Count; i++)
+ {
+ datas.Add(await providers[i].GetShowByID(id));
+ }
+
+ return Merge(datas);
}
- public Task GetShowFromName(string showName, string showPath)
+ public async Task GetShowFromName(string showName, string showPath)
{
- return providers[0].GetShowFromName(showName, showPath);
+ List datas = new List();
+ 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 GetSeason(string showName, long seasonNumber)
+ public async Task GetSeason(string showName, long seasonNumber)
{
- return providers[0].GetSeason(showName, seasonNumber);
+ List datas = new List();
+ for (int i = 0; i < providers.Count; i++)
+ {
+ datas.Add(await providers[i].GetSeason(showName, seasonNumber));
+ }
+
+ return Merge(datas);
}
public Task GetSeasonImage(string showName, long seasonNumber)
{
+ //Should select the best provider for this show.
+
return providers[0].GetSeasonImage(showName, seasonNumber);
}
- public Task GetEpisode(string externalIDs, long seasonNumber, long episodeNumber)
+ public async Task GetEpisode(string externalIDs, long seasonNumber, long episodeNumber, string episodePath)
{
- return providers[0].GetEpisode(externalIDs, seasonNumber, episodeNumber);
+ List datas = new List();
+ 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);
}
}
}
diff --git a/Kyoo/InternalAPI/ThumbnailsManager/IThumbnailsManager.cs b/Kyoo/InternalAPI/ThumbnailsManager/IThumbnailsManager.cs
new file mode 100644
index 00000000..0933994f
--- /dev/null
+++ b/Kyoo/InternalAPI/ThumbnailsManager/IThumbnailsManager.cs
@@ -0,0 +1,10 @@
+using Kyoo.Models;
+
+namespace Kyoo.InternalAPI.ThumbnailsManager
+{
+ public interface IThumbnailsManager
+ {
+ Show Validate(Show show);
+ Episode Validate(Episode episode);
+ }
+}
diff --git a/Kyoo/InternalAPI/ThumbnailsManager/ThumbnailsManager.cs b/Kyoo/InternalAPI/ThumbnailsManager/ThumbnailsManager.cs
new file mode 100644
index 00000000..4600c91e
--- /dev/null
+++ b/Kyoo/InternalAPI/ThumbnailsManager/ThumbnailsManager.cs
@@ -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;
+ }
+ }
+}
diff --git a/Kyoo/Startup.cs b/Kyoo/Startup.cs
index a84a9ea7..cbbeb1b8 100644
--- a/Kyoo/Startup.cs
+++ b/Kyoo/Startup.cs
@@ -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();
+
+ //Services used to get informations about files and register them
services.AddHostedService();
+ services.AddSingleton();
services.AddSingleton();
}