mirror of
				https://github.com/zoriya/Kyoo.git
				synced 2025-11-03 19:17:16 -05:00 
			
		
		
		
	Adding thumbnails management (downloading, checking local versions...)
Finishing the browse/library tab of the web app.
This commit is contained in:
		
							parent
							
								
									fe649df033
								
							
						
					
					
						commit
						edc5def19a
					
				@ -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>
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
    {
 | 
			
		||||
    max-width: 100%;
 | 
			
		||||
    max-height: 100%;
 | 
			
		||||
      outline: solid var(--accentColor);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    > .title
 | 
			
		||||
    {
 | 
			
		||||
      text-decoration: underline;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  > img
 | 
			
		||||
  {
 | 
			
		||||
    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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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 + ")");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										25
									
								
								Kyoo/Controllers/ThumbnailController.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								Kyoo/Controllers/ThumbnailController.cs
									
									
									
									
									
										Normal 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");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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,11 +62,20 @@ 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.
 | 
			
		||||
 | 
			
		||||
@ -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));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        public Task<Show> GetShowByID(string id)
 | 
			
		||||
        {
 | 
			
		||||
            return providers[0].GetShowByID(id);
 | 
			
		||||
            return Merge(datas);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Task<Show> GetShowFromName(string showName, string showPath)
 | 
			
		||||
        public async Task<Show> GetShowByID(string id)
 | 
			
		||||
        {
 | 
			
		||||
            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].GetShowByID(id));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        public Task<Season> GetSeason(string showName, long seasonNumber)
 | 
			
		||||
            return Merge(datas);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task<Show> GetShowFromName(string showName, string showPath)
 | 
			
		||||
        {
 | 
			
		||||
            return providers[0].GetSeason(showName, seasonNumber);
 | 
			
		||||
            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 async Task<Season> GetSeason(string showName, long 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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										10
									
								
								Kyoo/InternalAPI/ThumbnailsManager/IThumbnailsManager.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								Kyoo/InternalAPI/ThumbnailsManager/IThumbnailsManager.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
			
		||||
using Kyoo.Models;
 | 
			
		||||
 | 
			
		||||
namespace Kyoo.InternalAPI.ThumbnailsManager
 | 
			
		||||
{
 | 
			
		||||
    public interface IThumbnailsManager
 | 
			
		||||
    {
 | 
			
		||||
        Show Validate(Show show);
 | 
			
		||||
        Episode Validate(Episode episode);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										40
									
								
								Kyoo/InternalAPI/ThumbnailsManager/ThumbnailsManager.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								Kyoo/InternalAPI/ThumbnailsManager/ThumbnailsManager.cs
									
									
									
									
									
										Normal 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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user