Creating the provider manager

This commit is contained in:
Zoe Roux 2020-01-18 17:34:07 +01:00
parent 9d37426764
commit 3d79d41a60
18 changed files with 343 additions and 279 deletions

View File

@ -5,8 +5,8 @@ namespace Kyoo.Controllers
{
public interface ICrawler
{
Task Start(bool watch);
void Start();
Task StopAsync();
void Cancel();
}
}

View File

@ -63,9 +63,9 @@ namespace Kyoo.Controllers
long GetOrCreateGenre(Genre genre);
long GetOrCreateStudio(Studio studio);
void RegisterShowPeople(long showID, List<People> actors);
void RegisterShowPeople(long showID, IEnumerable<People> actors);
void AddShowToCollection(long showID, long collectionID);
void RegisterInLibrary(long showID, string libraryPath);
void RegisterInLibrary(long showID, Library library);
void RemoveEpisode(Episode episode);
void ClearSubtitles(long episodeID);

View File

@ -6,18 +6,18 @@ namespace Kyoo.Controllers
{
public interface IMetadataProvider
{
public string Name { get; }
//For the collection
Task<Collection> GetCollectionFromName(string name);
//For the show
Task<Show> GetShowByID(string id);
Task<Show> GetShowFromName(string showName, string showPath);
Task<Show> GetImages(Show show);
Task<List<People>> GetPeople(string id);
Task<IEnumerable<People>> GetPeople(string id);
//For the seasons
Task<Season> GetSeason(string showName, long seasonNumber);
Task<string> GetSeasonImage(string showName, long seasonNumber);
//For the episodes
Task<Episode> GetEpisode(string externalIDs, long seasonNumber, long episodeNumber, long absoluteNumber, string episodePath);

View File

@ -0,0 +1,15 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Kyoo.Models;
namespace Kyoo.Controllers
{
public interface IProviderManager
{
Task<Collection> GetCollectionFromName(string name, Library library);
Task<Show> GetShowFromName(string showName, string showPath, Library library);
Task<Season> GetSeason(string showName, long seasonNumber, Library library);
Task<Episode> GetEpisode(string externalIDs, long seasonNumber, long episodeNumber, long absoluteNumber, string episodePath, Library library);
Task<IEnumerable<People>> GetPeople(string showExternalIDs, Library library);
}
}

View File

@ -7,7 +7,7 @@ namespace Kyoo.Controllers.ThumbnailsManager
public interface IThumbnailsManager
{
Task<Show> Validate(Show show);
Task<List<People>> Validate(List<People> actors);
Task<IEnumerable<People>> Validate(IEnumerable<People> actors);
Task<Episode> Validate(Episode episode);
}
}

View File

@ -1,50 +1,76 @@
using Kyoo.Controllers;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
using Kyoo.Utility;
namespace Kyoo.Models
{
public class Collection
public class Collection : IMergable<Collection>
{
[JsonIgnore] public long id;
public string Slug;
public string Name;
public string Poster;
public string Overview;
[JsonIgnore] public string ImgPrimary;
public IEnumerable<Show> Shows;
[JsonIgnore] public long id = -1;
public string Slug;
public string Name;
public string Poster;
public string Overview;
[JsonIgnore] public string ImgPrimary;
public IEnumerable<Show> Shows;
public Collection() { }
public Collection()
{
}
public Collection(long id, string slug, string name, string overview, string imgPrimary)
{
this.id = id;
Slug = slug;
Name = name;
Overview = overview;
ImgPrimary = imgPrimary;
}
public Collection(long id, string slug, string name, string overview, string imgPrimary)
{
this.id = id;
Slug = slug;
Name = name;
Overview = overview;
ImgPrimary = imgPrimary;
}
public static Collection FromReader(System.Data.SQLite.SQLiteDataReader reader)
{
Collection col = new Collection((long)reader["id"],
reader["slug"] as string,
reader["name"] as string,
reader["overview"] as string,
reader["imgPrimary"] as string);
col.Poster = "poster/" + col.Slug;
return col;
}
public static Collection FromReader(System.Data.SQLite.SQLiteDataReader reader)
{
Collection col = new Collection((long) reader["id"],
reader["slug"] as string,
reader["name"] as string,
reader["overview"] as string,
reader["imgPrimary"] as string);
col.Poster = "poster/" + col.Slug;
return col;
}
public Show AsShow()
{
return new Show(-1, Slug, Name, null, null, Overview, null, null, null, null, null, null);
}
public Show AsShow()
{
return new Show(-1, Slug, Name, null, null, Overview, null, null, null, null, null, null);
}
public Collection SetShows(ILibraryManager libraryManager)
{
Shows = libraryManager.GetShowsInCollection(id);
return this;
}
public Collection SetShows(ILibraryManager libraryManager)
{
Shows = libraryManager.GetShowsInCollection(id);
return this;
}
public Collection Merge(Collection collection)
{
if (id == -1)
id = collection.id;
if (Slug == null)
Slug = collection.Slug;
if (Name == null)
Name = collection.Name;
if (Poster == null)
Poster = collection.Poster;
if (Overview == null)
Overview = collection.Overview;
if (ImgPrimary == null)
ImgPrimary = collection.ImgPrimary;
if (Shows == null)
Shows = collection.Shows;
else
Shows = Shows.Concat(collection.Shows);
return this;
}
}
}

View File

@ -1,9 +1,10 @@
using Newtonsoft.Json;
using System;
using Kyoo.Utility;
namespace Kyoo.Models
{
public class Episode
public class Episode : IMergable<Episode>
{
[JsonIgnore] public long id;
[JsonIgnore] public long ShowID;
@ -97,5 +98,35 @@ namespace Kyoo.Models
{
return showSlug + "-s" + seasonNumber + "e" + episodeNumber;
}
public Episode Merge(Episode other)
{
if (id == -1)
id = other.id;
if (ShowID == -1)
ShowID = other.ShowID;
if (SeasonID == -1)
SeasonID = other.SeasonID;
if (seasonNumber == -1)
seasonNumber = other.seasonNumber;
if (episodeNumber == -1)
episodeNumber = other.episodeNumber;
if (absoluteNumber == -1)
absoluteNumber = other.absoluteNumber;
if (Path == null)
Path = other.Path;
if (Title == null)
Title = other.Title;
if (Overview == null)
Overview = other.Overview;
if (ReleaseDate == null)
ReleaseDate = other.ReleaseDate;
if (Runtime == -1)
Runtime = other.Runtime;
if (ImgPrimary == null)
ImgPrimary = other.ImgPrimary;
ExternalIDs += '|' + other.ExternalIDs;
return this;
}
}
}

View File

@ -7,14 +7,16 @@ namespace Kyoo.Models
[JsonIgnore] public readonly long id;
public string Slug;
public string Name;
public string Path;
public string[] Paths;
public string[] Providers;
public Library(long id, string slug, string name, string path)
public Library(long id, string slug, string name, string[] paths, string[] providers)
{
this.id = id;
Slug = slug;
Name = name;
Path = path;
Paths = paths;
Providers = providers;
}
public static Library FromReader(System.Data.SQLite.SQLiteDataReader reader)
@ -22,7 +24,8 @@ namespace Kyoo.Models
return new Library((long)reader["id"],
reader["slug"] as string,
reader["name"] as string,
reader["path"] as string);
(reader["path"] as string)?.Split('|'),
(reader["providers"] as string)?.Split('|'));
}
}
}

View File

@ -1,8 +1,9 @@
using Newtonsoft.Json;
using Kyoo.Utility;
using Newtonsoft.Json;
namespace Kyoo.Models
{
public class People
public class People : IMergable<People>
{
[JsonIgnore] public long id;
public string slug;
@ -52,5 +53,23 @@ namespace Kyoo.Models
reader["imgPrimary"] as string,
reader["externalIDs"] as string);
}
public People Merge(People other)
{
if (id == -1)
id = other.id;
if (slug == null)
slug = other.slug;
if (Name == null)
Name = other.Name;
if (Role == null)
Role = other.Role;
if (Type == null)
Type = other.Type;
if (imgPrimary == null)
imgPrimary = other.imgPrimary;
externalIDs += '|' + other.externalIDs;
return this;
}
}
}

View File

@ -1,8 +1,10 @@
using Newtonsoft.Json;
using System.Linq;
using Kyoo.Utility;
using Newtonsoft.Json;
namespace Kyoo.Models
{
public class Season
public class Season : IMergable<Season>
{
[JsonIgnore] public readonly long id;
[JsonIgnore] public long ShowID;
@ -40,5 +42,23 @@ namespace Kyoo.Models
reader["imgPrimary"] as string,
reader["externalIDs"] as string);
}
public Season Merge(Season other)
{
if (ShowID == -1)
ShowID = other.ShowID;
if (seasonNumber == -1)
seasonNumber = other.seasonNumber;
if (Title == null)
Title = other.Title;
if (Overview == null)
Overview = other.Overview;
if (year == null)
year = other.year;
if (ImgPrimary == null)
ImgPrimary = other.ImgPrimary;
ExternalIDs += '|' + other.ExternalIDs;
return this;
}
}
}

View File

@ -1,10 +1,12 @@
using Kyoo.Controllers;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
using Kyoo.Utility;
namespace Kyoo.Models
{
public class Show
public class Show : IMergable<Show>
{
[JsonIgnore] public long id = -1;
@ -111,7 +113,7 @@ namespace Kyoo.Models
return new Show((long)reader["id"],
reader["slug"] as string,
reader["title"] as string,
(reader["aliases"] as string)?.Split('|') ?? null,
(reader["aliases"] as string)?.Split('|'),
reader["path"] as string,
reader["overview"] as string,
reader["trailerUrl"] as string,
@ -161,6 +163,46 @@ namespace Kyoo.Models
seasons = manager.GetSeasons(id);
return this;
}
public Show Merge(Show other)
{
if (id == -1)
id = other.id;
if (Slug == null)
Slug = other.Slug;
if (Title == null)
Title = other.Title;
if (Aliases == null)
Aliases = other.Aliases;
else
Aliases = Aliases.Concat(other.Aliases);
if (Genres == null)
Genres = other.Genres;
else
Genres = Genres.Concat(other.Genres);
if (Path == null)
Path = other.Path;
if (Overview == null)
Overview = other.Overview;
if (TrailerUrl == null)
TrailerUrl = other.TrailerUrl;
if (Status == null)
Status = other.Status;
if (StartYear == null)
StartYear = other.StartYear;
if (EndYear == null)
EndYear = other.EndYear;
if (ImgPrimary == null)
ImgPrimary = other.ImgPrimary;
if (ImgThumb == null)
ImgThumb = other.ImgThumb;
if (ImgLogo == null)
ImgLogo = other.ImgLogo;
if (ImgBackdrop == null)
ImgBackdrop = other.ImgBackdrop;
ExternalIDs += '|' + other.ExternalIDs;
return this;
}
}
public enum Status { Finished, Airing }

View File

@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace Kyoo.Utility
{
public interface IMergable<T>
{
public T Merge(T other);
}
}

View File

@ -14,16 +14,15 @@ namespace Kyoo.Controllers
{
public class Crawler : ICrawler
{
private static ICrawler runningCrawler;
private bool isScanning;
private bool isRunning;
private readonly CancellationTokenSource cancellation;
private readonly ILibraryManager libraryManager;
private readonly IMetadataProvider metadataProvider;
private readonly IProviderManager metadataProvider;
private readonly ITranscoder transcoder;
private readonly IConfiguration config;
public Crawler(ILibraryManager libraryManager, IMetadataProvider metadataProvider, ITranscoder transcoder, IConfiguration configuration)
public Crawler(ILibraryManager libraryManager, IProviderManager metadataProvider, ITranscoder transcoder, IConfiguration configuration)
{
this.libraryManager = libraryManager;
this.metadataProvider = metadataProvider;
@ -32,30 +31,27 @@ namespace Kyoo.Controllers
cancellation = new CancellationTokenSource();
}
public async Task Start(bool watch)
public void Start()
{
if (runningCrawler == null)
{
runningCrawler = this;
await StartAsync(watch, cancellation.Token);
}
else if (runningCrawler is Crawler crawler)
{
if (!crawler.isScanning)
{
await crawler.StopAsync();
runningCrawler = this;
await StartAsync(watch, cancellation.Token);
}
}
if (isRunning)
return;
isRunning = true;
StartAsync(cancellation.Token);
}
private Task StartAsync(bool watch, CancellationToken cancellationToken)
public void Cancel()
{
if (!isRunning)
return;
isRunning = false;
cancellation.Cancel();
}
private async void StartAsync(CancellationToken cancellationToken)
{
IEnumerable<Episode> episodes = libraryManager.GetAllEpisodes();
IEnumerable<string> libraryPaths = libraryManager.GetLibrariesPath();
IEnumerable<Library> libraries = libraryManager.GetLibraries();
isScanning = true;
Debug.WriteLine("&Crawler started");
foreach (Episode episode in episodes)
{
@ -63,106 +59,46 @@ namespace Kyoo.Controllers
libraryManager.RemoveEpisode(episode);
}
foreach (string path in libraryPaths)
{
Scan(path, cancellationToken);
foreach (Library library in libraries)
await Scan(library, cancellationToken);
if(watch)
Watch(path, cancellationToken);
}
isScanning = false;
if (watch)
while (!cancellationToken.IsCancellationRequested);
isRunning = false;
Debug.WriteLine("&Crawler stopped");
runningCrawler = null;
return null;
}
private async void Scan(string folderPath, CancellationToken cancellationToken)
private async Task Scan(Library library, CancellationToken cancellationToken)
{
string[] files = Directory.GetFiles(folderPath, "*", SearchOption.AllDirectories);
IEnumerable<string> files = new List<string>();
files = library.Paths.Aggregate(files, (current, path) =>
current.Concat(Directory.GetFiles(path, "*", SearchOption.AllDirectories)));
foreach (string file in files)
{
if (cancellationToken.IsCancellationRequested)
return;
if (IsVideo(file))
{
Debug.WriteLine("&Registering episode at: " + file);
await ExtractEpisodeData(file, folderPath);
}
if (!IsVideo(file))
continue;
await RegisterFile(file, library);
}
}
private void Watch(string folderPath, CancellationToken cancellationToken)
private async Task RegisterFile(string path, Library library)
{
Debug.WriteLine("Folder watching not implemented yet.");
//Debug.WriteLine("&Watching " + folderPath + " for changes");
//using (FileSystemWatcher watcher = new FileSystemWatcher())
//{
// watcher.Path = folderPath;
// watcher.IncludeSubdirectories = true;
// watcher.NotifyFilter = NotifyFilters.LastAccess
// | NotifyFilters.LastWrite
// | NotifyFilters.FileName
// | NotifyFilters.Size
// | NotifyFilters.DirectoryName;
// watcher.Created += FileCreated;
// watcher.Changed += FileChanged;
// watcher.Renamed += FileRenamed;
// watcher.Deleted += FileDeleted;
// watcher.EnableRaisingEvents = true;
// while (!cancellationToken.IsCancellationRequested);
//}
}
//private void FileCreated(object sender, FileSystemEventArgs e)
//{
// Debug.WriteLine("&File Created at " + e.FullPath);
// if (IsVideo(e.FullPath))
// {
// Debug.WriteLine("&Created file is a video");
// _ = TryRegisterEpisode(e.FullPath);
// }
//}
//private void FileChanged(object sender, FileSystemEventArgs e)
//{
// Debug.WriteLine("&File Changed at " + e.FullPath);
//}
//private void FileRenamed(object sender, RenamedEventArgs e)
//{
// Debug.WriteLine("&File Renamed at " + e.FullPath);
//}
//private void FileDeleted(object sender, FileSystemEventArgs e)
//{
// Debug.WriteLine("&File Deleted at " + e.FullPath);
//}
private async Task ExtractEpisodeData(string episodePath, string libraryPath)
{
if (!libraryManager.IsEpisodeRegistered(episodePath))
if (!libraryManager.IsEpisodeRegistered(path))
{
string relativePath = episodePath.Substring(libraryPath.Length);
string relativePath = path.Substring(library.Paths.Length);
string patern = config.GetValue<string>("regex");
Regex regex = new Regex(patern, RegexOptions.IgnoreCase);
Match match = regex.Match(relativePath);
string showPath = Path.GetDirectoryName(episodePath);
string showPath = Path.GetDirectoryName(path);
string collectionName = match.Groups["Collection"]?.Value;
string showName = match.Groups["ShowTitle"].Value;
bool seasonSuccess = long.TryParse(match.Groups["Season"].Value, out long seasonNumber);
bool episodeSucess = long.TryParse(match.Groups["Episode"].Value, out long episodeNumber);
long absoluteNumber = -1;
Debug.WriteLine("&Registering episode at: " + path);
if (!seasonSuccess || !episodeSucess)
{
//Considering that the episode is using absolute path.
@ -182,37 +118,37 @@ namespace Kyoo.Controllers
}
}
Show show = await RegisterOrGetShow(collectionName, showName, showPath, libraryPath);
Show show = await RegisterOrGetShow(collectionName, showName, showPath, library);
if (show != null)
await RegisterEpisode(show, seasonNumber, episodeNumber, absoluteNumber, episodePath);
await RegisterEpisode(show, seasonNumber, episodeNumber, absoluteNumber, path, library);
}
}
private async Task<Show> RegisterOrGetShow(string collectionName, string showTitle, string showPath, string libraryPath)
private async Task<Show> RegisterOrGetShow(string collectionName, string showTitle, string showPath, Library library)
{
string showProviderIDs;
if (!libraryManager.IsShowRegistered(showPath, out long showID))
{
Show show = await metadataProvider.GetShowFromName(showTitle, showPath);
Show show = await metadataProvider.GetShowFromName(showTitle, showPath, library);
showProviderIDs = show.ExternalIDs;
showID = libraryManager.RegisterShow(show);
if (showID == -1)
return null;
libraryManager.RegisterInLibrary(showID, libraryPath);
libraryManager.RegisterInLibrary(showID, library);
if (!string.IsNullOrEmpty(collectionName))
{
if (!libraryManager.IsCollectionRegistered(Slugifier.ToSlug(collectionName), out long collectionID))
{
Collection collection = await metadataProvider.GetCollectionFromName(collectionName);
Collection collection = await metadataProvider.GetCollectionFromName(collectionName, library);
collectionID = libraryManager.RegisterCollection(collection);
}
libraryManager.AddShowToCollection(showID, collectionID);
}
List<People> actors = await metadataProvider.GetPeople(show.ExternalIDs);
IEnumerable<People> actors = await metadataProvider.GetPeople(show.ExternalIDs, library);
libraryManager.RegisterShowPeople(showID, actors);
}
else
@ -221,27 +157,27 @@ namespace Kyoo.Controllers
return new Show { id = showID, ExternalIDs = showProviderIDs, Title = showTitle };
}
private async Task RegisterEpisode(Show show, long seasonNumber, long episodeNumber, long absoluteNumber, string episodePath)
private async Task RegisterEpisode(Show show, long seasonNumber, long episodeNumber, long absoluteNumber, string episodePath, Library library)
{
long seasonID = -1;
if (seasonNumber != -1)
{
if (!libraryManager.IsSeasonRegistered(show.id, seasonNumber, out seasonID))
{
Season season = await metadataProvider.GetSeason(show.Title, seasonNumber);
Season season = await metadataProvider.GetSeason(show.Title, seasonNumber, library);
season.ShowID = show.id;
seasonID = libraryManager.RegisterSeason(season);
}
}
Episode episode = await metadataProvider.GetEpisode(show.ExternalIDs, seasonNumber, episodeNumber, absoluteNumber, episodePath);
Episode episode = await metadataProvider.GetEpisode(show.ExternalIDs, seasonNumber, episodeNumber, absoluteNumber, episodePath, library);
episode.ShowID = show.id;
if (seasonID == -1)
{
if (!libraryManager.IsSeasonRegistered(show.id, episode.seasonNumber, out seasonID))
{
Season season = await metadataProvider.GetSeason(show.Title, episode.seasonNumber);
Season season = await metadataProvider.GetSeason(show.Title, episode.seasonNumber, library);
season.ShowID = show.id;
seasonID = libraryManager.RegisterSeason(season);
}
@ -263,18 +199,15 @@ namespace Kyoo.Controllers
libraryManager.RegisterTrack(track);
}
if (episode.Path.EndsWith(".mkv"))
if (episode.Path.EndsWith(".mkv") && CountExtractedSubtitles(episode) != subcount)
{
if (CountExtractedSubtitles(episode) != subcount)
Track[] subtitles = await transcoder.ExtractSubtitles(episode.Path);
if (subtitles != null)
{
Track[] subtitles = await transcoder.ExtractSubtitles(episode.Path);
if (subtitles != null)
foreach (Track track in subtitles)
{
foreach (Track track in subtitles)
{
track.episodeID = episode.id;
libraryManager.RegisterTrack(track);
}
track.episodeID = episode.id;
libraryManager.RegisterTrack(track);
}
}
}

View File

@ -29,7 +29,7 @@ namespace Kyoo.Controllers
sqlConnection = new SQLiteConnection($"Data Source={databasePath};Version=3");
sqlConnection.Open();
string createStatement = @"CREATE TABLE shows(
const string createStatement = @"CREATE TABLE shows(
id INTEGER PRIMARY KEY UNIQUE,
slug TEXT UNIQUE,
title TEXT,
@ -92,7 +92,8 @@ namespace Kyoo.Controllers
id INTEGER PRIMARY KEY UNIQUE,
slug TEXT UNIQUE,
name TEXT,
path TEXT
path TEXT,
providers TEXT
);
CREATE TABLE librariesLinks(
libraryID INTEGER,
@ -209,10 +210,11 @@ namespace Kyoo.Controllers
public string GetShowExternalIDs(long showID)
{
string query = string.Format("SELECT * FROM shows WHERE id = {0};", showID);
string query = "SELECT * FROM shows WHERE id = $showID;";
using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection))
{
cmd.Parameters.AddWithValue("$showID", showID);
SQLiteDataReader reader = cmd.ExecuteReader();
if (reader.Read())
@ -949,13 +951,14 @@ namespace Kyoo.Controllers
}
}
public void RegisterInLibrary(long showID, string libraryPath)
public void RegisterInLibrary(long showID, Library library)
{
string query = "INSERT INTO librariesLinks (libraryID, showID) SELECT id, $showID FROM libraries WHERE libraries.path = $libraryPath;";
string query =
"INSERT INTO librariesLinks (libraryID, showID) SELECT id, $showID FROM libraries WHERE libraries.id = $libraryID;";
using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection))
{
cmd.Parameters.AddWithValue("$libraryPath", libraryPath);
cmd.Parameters.AddWithValue("$libraryID", library.id);
cmd.Parameters.AddWithValue("$showID", showID);
cmd.ExecuteNonQuery();
}
@ -1102,21 +1105,21 @@ namespace Kyoo.Controllers
}
}
public void RegisterShowPeople(long showID, List<People> people)
public void RegisterShowPeople(long showID, IEnumerable<People> people)
{
if (people == null)
return;
string linkQuery = "INSERT INTO peopleLinks (peopleID, showID, role, type) VALUES($peopleID, $showID, $role, $type);";
for (int i = 0; i < people.Count; i++)
foreach (People peop in people)
{
using (SQLiteCommand cmd = new SQLiteCommand(linkQuery, sqlConnection))
{
cmd.Parameters.AddWithValue("$peopleID", GetOrCreatePeople(people[i]));
cmd.Parameters.AddWithValue("$peopleID", GetOrCreatePeople(peop));
cmd.Parameters.AddWithValue("$showID", showID);
cmd.Parameters.AddWithValue("$role", people[i].Role);
cmd.Parameters.AddWithValue("$type", people[i].Type);
cmd.Parameters.AddWithValue("$role", peop.Role);
cmd.Parameters.AddWithValue("$type", peop.Type);
cmd.ExecuteNonQuery();
}
}

View File

@ -1,124 +1,87 @@
using Kyoo.Models;
using Microsoft.Extensions.Configuration;
using System;
using System;
using Kyoo.Models;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Kyoo.Controllers.ThumbnailsManager;
using Kyoo.Utility;
namespace Kyoo.Controllers
{
public class ProviderManager : IMetadataProvider
public class ProviderManager : IProviderManager
{
private readonly IEnumerable<IMetadataProvider> providers;
private readonly IThumbnailsManager thumbnailsManager;
private readonly IConfiguration config;
public ProviderManager(IThumbnailsManager thumbnailsManager, IPluginManager pluginManager, IConfiguration config)
public ProviderManager(IThumbnailsManager thumbnailsManager, IPluginManager pluginManager)
{
this.thumbnailsManager = thumbnailsManager;
this.config = config;
providers = pluginManager.GetPlugins<IMetadataProvider>();
}
public Show Merge(IEnumerable<Show> shows)
public async Task<T> GetMetadata<T>(Func<IMetadataProvider, Task<T>> providerCall, Library library, string what) where T : IMergable<T>, new()
{
return shows.FirstOrDefault();
}
public Season Merge(IEnumerable<Season> seasons)
{
return seasons.FirstOrDefault();
}
public Episode Merge(IEnumerable<Episode> episodes)
{
return episodes.FirstOrDefault(); //Should do something if the return is null;
}
//For all the following methods, it should use all providers and merge the data.
public Task<Collection> GetCollectionFromName(string name)
{
return providers[0].GetCollectionFromName(name);
}
public Task<Show> GetImages(Show show)
{
return providers[0].GetImages(show);
}
public async Task<Season> GetSeason(string showName, int seasonNumber)
{
List<Season> datas = new List<Season>();
for (int i = 0; i < providers.Count; i++)
T ret = new T();
foreach (IMetadataProvider provider in providers.OrderBy(provider => Array.IndexOf(library.Providers, provider.Name)))
{
datas.Add(await providers[i].GetSeason(showName, seasonNumber));
try
{
if (library.Providers.Contains(provider.Name))
ret = ret.Merge(await providerCall(provider));
} catch (Exception ex) {
Console.Error.WriteLine($"The provider {provider.Name} coudln't work for {what}. (Excepetion: {ex.Message}");
}
}
return Merge(datas);
return ret;
}
public async Task<Show> GetShowByID(string id)
public async Task<IEnumerable<T>> GetMetadata<T>(Func<IMetadataProvider, Task<IEnumerable<T>>> providerCall, Library library, string what)
{
List<Show> datas = new List<Show>();
for (int i = 0; i < providers.Count; i++)
List<T> ret = new List<T>();
foreach (IMetadataProvider provider in providers.OrderBy(provider => Array.IndexOf(library.Providers, provider.Name)))
{
datas.Add(await providers[i].GetShowByID(id));
try
{
if (library.Providers.Contains(provider.Name))
ret.AddRange(await providerCall(provider));
} catch (Exception ex) {
Console.Error.WriteLine($"The provider {provider.Name} coudln't work for {what}. (Excepetion: {ex.Message}");
}
}
return Merge(datas);
return ret;
}
public async Task<Collection> GetCollectionFromName(string name, Library library)
{
return await GetMetadata(provider => provider.GetCollectionFromName(name), library, $"the collection {name}");
}
public async Task<Show> GetShowFromName(string showName, string showPath)
public async Task<Show> GetShowFromName(string showName, string showPath, Library library)
{
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 await thumbnailsManager.Validate(show);
Show show = await GetMetadata(provider => provider.GetShowFromName(showName, showPath), library, $"the show {showName}");
await thumbnailsManager.Validate(show);
return show;
}
public async Task<Season> GetSeason(string showName, long seasonNumber)
public async Task<Season> GetSeason(string showName, long seasonNumber, Library library)
{
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);
return await GetMetadata(provider => provider.GetSeason(showName, seasonNumber), library, $"the season ${seasonNumber} of {showName}");
}
public Task<string> GetSeasonImage(string showName, long seasonNumber)
public async Task<Episode> GetEpisode(string externalIDs, long seasonNumber, long episodeNumber, long absoluteNumber, string episodePath, Library library)
{
//Should select the best provider for this show.
return providers[0].GetSeasonImage(showName, seasonNumber);
Episode episode = await GetMetadata(provider => provider.GetEpisode(externalIDs, seasonNumber, episodeNumber, absoluteNumber, episodePath), library, $"the episode at {episodePath}");
await thumbnailsManager.Validate(episode);
return episode;
}
public async Task<Episode> GetEpisode(string externalIDs, long seasonNumber, long episodeNumber, long absoluteNumber, string episodePath)
public async Task<IEnumerable<People>> GetPeople(string showExternalIDs, Library library)
{
List<Episode> datas = new List<Episode>();
for (int i = 0; i < providers.Count; i++)
{
datas.Add(await providers[i].GetEpisode(externalIDs, seasonNumber, episodeNumber, absoluteNumber, episodePath));
}
Episode episode = Merge(datas);
episode.Path = episodePath;
return await thumbnailsManager.Validate(episode);
}
public async Task<List<People>> GetPeople(string id)
{
List<People> actors = await providers[0].GetPeople(id);
return await thumbnailsManager.Validate(actors);
IEnumerable<People> people = await GetMetadata(provider => provider.GetPeople(showExternalIDs), library, $"unknown data");
people = await thumbnailsManager.Validate(people);
return people;
}
}
}

View File

@ -67,20 +67,20 @@ namespace Kyoo.Controllers.ThumbnailsManager
return show;
}
public async Task<List<People>> Validate(List<People> people)
public async Task<IEnumerable<People>> Validate(IEnumerable<People> people)
{
for (int i = 0; i < people?.Count; i++)
foreach (People peop in people)
{
string root = config.GetValue<string>("peoplePath");
Directory.CreateDirectory(root);
string localThumb = root + "/" + people[i].slug + ".jpg";
if (people[i].imgPrimary != null && !File.Exists(localThumb))
string localThumb = root + "/" + peop.slug + ".jpg";
if (peop.imgPrimary != null && !File.Exists(localThumb))
{
try
{
using WebClient client = new WebClient();
await client.DownloadFileTaskAsync(new Uri(people[i].imgPrimary), localThumb);
await client.DownloadFileTaskAsync(new Uri(peop.imgPrimary), localThumb);
}
catch (WebException)
{

View File

@ -18,10 +18,10 @@ namespace Kyoo.Controllers
this.crawler = crawler;
}
[HttpGet("scan/{watch}")]
public IActionResult ScanLibrary(bool watch)
[HttpGet("scan")]
public IActionResult ScanLibrary()
{
crawler.Start(watch);
crawler.Start();
return Ok("Scanning");
}
}

View File

@ -35,7 +35,7 @@ namespace Kyoo
services.AddSingleton<ITranscoder, Transcoder>();
services.AddSingleton<ICrawler, Crawler>();
services.AddSingleton<IThumbnailsManager, ThumbnailsManager>();
services.AddSingleton<IMetadataProvider, ProviderManager>();
services.AddSingleton<IProviderManager, ProviderManager>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.