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 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 GetOrCreateGenre(Genre genre);
long GetOrCreateStudio(Studio studio); 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 AddShowToCollection(long showID, long collectionID);
void RegisterInLibrary(long showID, string libraryPath); void RegisterInLibrary(long showID, Library library);
void RemoveEpisode(Episode episode); void RemoveEpisode(Episode episode);
void ClearSubtitles(long episodeID); void ClearSubtitles(long episodeID);

View File

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

View File

@ -1,50 +1,76 @@
using Kyoo.Controllers; using Kyoo.Controllers;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Kyoo.Utility;
namespace Kyoo.Models namespace Kyoo.Models
{ {
public class Collection public class Collection : IMergable<Collection>
{ {
[JsonIgnore] public long id; [JsonIgnore] public long id = -1;
public string Slug; public string Slug;
public string Name; public string Name;
public string Poster; public string Poster;
public string Overview; public string Overview;
[JsonIgnore] public string ImgPrimary; [JsonIgnore] public string ImgPrimary;
public IEnumerable<Show> Shows; public IEnumerable<Show> Shows;
public Collection() { } public Collection()
{
}
public Collection(long id, string slug, string name, string overview, string imgPrimary) public Collection(long id, string slug, string name, string overview, string imgPrimary)
{ {
this.id = id; this.id = id;
Slug = slug; Slug = slug;
Name = name; Name = name;
Overview = overview; Overview = overview;
ImgPrimary = imgPrimary; ImgPrimary = imgPrimary;
} }
public static Collection FromReader(System.Data.SQLite.SQLiteDataReader reader) public static Collection FromReader(System.Data.SQLite.SQLiteDataReader reader)
{ {
Collection col = new Collection((long)reader["id"], Collection col = new Collection((long) reader["id"],
reader["slug"] as string, reader["slug"] as string,
reader["name"] as string, reader["name"] as string,
reader["overview"] as string, reader["overview"] as string,
reader["imgPrimary"] as string); reader["imgPrimary"] as string);
col.Poster = "poster/" + col.Slug; col.Poster = "poster/" + col.Slug;
return col; return col;
} }
public Show AsShow() public Show AsShow()
{ {
return new Show(-1, Slug, Name, null, null, Overview, null, null, null, null, null, null); return new Show(-1, Slug, Name, null, null, Overview, null, null, null, null, null, null);
} }
public Collection SetShows(ILibraryManager libraryManager) public Collection SetShows(ILibraryManager libraryManager)
{ {
Shows = libraryManager.GetShowsInCollection(id); Shows = libraryManager.GetShowsInCollection(id);
return this; 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 Newtonsoft.Json;
using System; using System;
using Kyoo.Utility;
namespace Kyoo.Models namespace Kyoo.Models
{ {
public class Episode public class Episode : IMergable<Episode>
{ {
[JsonIgnore] public long id; [JsonIgnore] public long id;
[JsonIgnore] public long ShowID; [JsonIgnore] public long ShowID;
@ -97,5 +98,35 @@ namespace Kyoo.Models
{ {
return showSlug + "-s" + seasonNumber + "e" + episodeNumber; 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; [JsonIgnore] public readonly long id;
public string Slug; public string Slug;
public string Name; 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; this.id = id;
Slug = slug; Slug = slug;
Name = name; Name = name;
Path = path; Paths = paths;
Providers = providers;
} }
public static Library FromReader(System.Data.SQLite.SQLiteDataReader reader) public static Library FromReader(System.Data.SQLite.SQLiteDataReader reader)
@ -22,7 +24,8 @@ namespace Kyoo.Models
return new Library((long)reader["id"], return new Library((long)reader["id"],
reader["slug"] as string, reader["slug"] as string,
reader["name"] 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 namespace Kyoo.Models
{ {
public class People public class People : IMergable<People>
{ {
[JsonIgnore] public long id; [JsonIgnore] public long id;
public string slug; public string slug;
@ -52,5 +53,23 @@ namespace Kyoo.Models
reader["imgPrimary"] as string, reader["imgPrimary"] as string,
reader["externalIDs"] 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 namespace Kyoo.Models
{ {
public class Season public class Season : IMergable<Season>
{ {
[JsonIgnore] public readonly long id; [JsonIgnore] public readonly long id;
[JsonIgnore] public long ShowID; [JsonIgnore] public long ShowID;
@ -40,5 +42,23 @@ namespace Kyoo.Models
reader["imgPrimary"] as string, reader["imgPrimary"] as string,
reader["externalIDs"] 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 Kyoo.Controllers;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Kyoo.Utility;
namespace Kyoo.Models namespace Kyoo.Models
{ {
public class Show public class Show : IMergable<Show>
{ {
[JsonIgnore] public long id = -1; [JsonIgnore] public long id = -1;
@ -111,7 +113,7 @@ namespace Kyoo.Models
return new Show((long)reader["id"], return new Show((long)reader["id"],
reader["slug"] as string, reader["slug"] as string,
reader["title"] as string, reader["title"] as string,
(reader["aliases"] as string)?.Split('|') ?? null, (reader["aliases"] as string)?.Split('|'),
reader["path"] as string, reader["path"] as string,
reader["overview"] as string, reader["overview"] as string,
reader["trailerUrl"] as string, reader["trailerUrl"] as string,
@ -161,6 +163,46 @@ namespace Kyoo.Models
seasons = manager.GetSeasons(id); seasons = manager.GetSeasons(id);
return this; 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 } 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 public class Crawler : ICrawler
{ {
private static ICrawler runningCrawler; private bool isRunning;
private bool isScanning;
private readonly CancellationTokenSource cancellation; private readonly CancellationTokenSource cancellation;
private readonly ILibraryManager libraryManager; private readonly ILibraryManager libraryManager;
private readonly IMetadataProvider metadataProvider; private readonly IProviderManager metadataProvider;
private readonly ITranscoder transcoder; private readonly ITranscoder transcoder;
private readonly IConfiguration config; 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.libraryManager = libraryManager;
this.metadataProvider = metadataProvider; this.metadataProvider = metadataProvider;
@ -32,30 +31,27 @@ namespace Kyoo.Controllers
cancellation = new CancellationTokenSource(); cancellation = new CancellationTokenSource();
} }
public async Task Start(bool watch) public void Start()
{ {
if (runningCrawler == null) if (isRunning)
{ return;
runningCrawler = this; isRunning = true;
await StartAsync(watch, cancellation.Token); StartAsync(cancellation.Token);
}
else if (runningCrawler is Crawler crawler)
{
if (!crawler.isScanning)
{
await crawler.StopAsync();
runningCrawler = this;
await StartAsync(watch, 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<Episode> episodes = libraryManager.GetAllEpisodes();
IEnumerable<string> libraryPaths = libraryManager.GetLibrariesPath(); IEnumerable<Library> libraries = libraryManager.GetLibraries();
isScanning = true;
Debug.WriteLine("&Crawler started"); Debug.WriteLine("&Crawler started");
foreach (Episode episode in episodes) foreach (Episode episode in episodes)
{ {
@ -63,106 +59,46 @@ namespace Kyoo.Controllers
libraryManager.RemoveEpisode(episode); libraryManager.RemoveEpisode(episode);
} }
foreach (string path in libraryPaths) foreach (Library library in libraries)
{ await Scan(library, cancellationToken);
Scan(path, cancellationToken);
if(watch) isRunning = false;
Watch(path, cancellationToken);
}
isScanning = false;
if (watch)
while (!cancellationToken.IsCancellationRequested);
Debug.WriteLine("&Crawler stopped"); 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) foreach (string file in files)
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
return; return;
if (!IsVideo(file))
if (IsVideo(file)) continue;
{ await RegisterFile(file, library);
Debug.WriteLine("&Registering episode at: " + file);
await ExtractEpisodeData(file, folderPath);
}
} }
} }
private void Watch(string folderPath, CancellationToken cancellationToken) private async Task RegisterFile(string path, Library library)
{ {
Debug.WriteLine("Folder watching not implemented yet."); if (!libraryManager.IsEpisodeRegistered(path))
//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))
{ {
string relativePath = episodePath.Substring(libraryPath.Length); string relativePath = path.Substring(library.Paths.Length);
string patern = config.GetValue<string>("regex"); string patern = config.GetValue<string>("regex");
Regex regex = new Regex(patern, RegexOptions.IgnoreCase); Regex regex = new Regex(patern, RegexOptions.IgnoreCase);
Match match = regex.Match(relativePath); Match match = regex.Match(relativePath);
string showPath = Path.GetDirectoryName(episodePath); string showPath = Path.GetDirectoryName(path);
string collectionName = match.Groups["Collection"]?.Value; string collectionName = match.Groups["Collection"]?.Value;
string showName = match.Groups["ShowTitle"].Value; string showName = match.Groups["ShowTitle"].Value;
bool seasonSuccess = long.TryParse(match.Groups["Season"].Value, out long seasonNumber); bool seasonSuccess = long.TryParse(match.Groups["Season"].Value, out long seasonNumber);
bool episodeSucess = long.TryParse(match.Groups["Episode"].Value, out long episodeNumber); bool episodeSucess = long.TryParse(match.Groups["Episode"].Value, out long episodeNumber);
long absoluteNumber = -1; long absoluteNumber = -1;
Debug.WriteLine("&Registering episode at: " + path);
if (!seasonSuccess || !episodeSucess) if (!seasonSuccess || !episodeSucess)
{ {
//Considering that the episode is using absolute path. //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) 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; string showProviderIDs;
if (!libraryManager.IsShowRegistered(showPath, out long showID)) 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; showProviderIDs = show.ExternalIDs;
showID = libraryManager.RegisterShow(show); showID = libraryManager.RegisterShow(show);
if (showID == -1) if (showID == -1)
return null; return null;
libraryManager.RegisterInLibrary(showID, libraryPath); libraryManager.RegisterInLibrary(showID, library);
if (!string.IsNullOrEmpty(collectionName)) if (!string.IsNullOrEmpty(collectionName))
{ {
if (!libraryManager.IsCollectionRegistered(Slugifier.ToSlug(collectionName), out long collectionID)) 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); collectionID = libraryManager.RegisterCollection(collection);
} }
libraryManager.AddShowToCollection(showID, collectionID); 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); libraryManager.RegisterShowPeople(showID, actors);
} }
else else
@ -221,27 +157,27 @@ namespace Kyoo.Controllers
return new Show { id = showID, ExternalIDs = showProviderIDs, Title = showTitle }; 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; long seasonID = -1;
if (seasonNumber != -1) if (seasonNumber != -1)
{ {
if (!libraryManager.IsSeasonRegistered(show.id, seasonNumber, out seasonID)) 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; season.ShowID = show.id;
seasonID = libraryManager.RegisterSeason(season); 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; episode.ShowID = show.id;
if (seasonID == -1) if (seasonID == -1)
{ {
if (!libraryManager.IsSeasonRegistered(show.id, episode.seasonNumber, out seasonID)) 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; season.ShowID = show.id;
seasonID = libraryManager.RegisterSeason(season); seasonID = libraryManager.RegisterSeason(season);
} }
@ -263,18 +199,15 @@ namespace Kyoo.Controllers
libraryManager.RegisterTrack(track); 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); foreach (Track track in subtitles)
if (subtitles != null)
{ {
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 = new SQLiteConnection($"Data Source={databasePath};Version=3");
sqlConnection.Open(); sqlConnection.Open();
string createStatement = @"CREATE TABLE shows( const string createStatement = @"CREATE TABLE shows(
id INTEGER PRIMARY KEY UNIQUE, id INTEGER PRIMARY KEY UNIQUE,
slug TEXT UNIQUE, slug TEXT UNIQUE,
title TEXT, title TEXT,
@ -92,7 +92,8 @@ namespace Kyoo.Controllers
id INTEGER PRIMARY KEY UNIQUE, id INTEGER PRIMARY KEY UNIQUE,
slug TEXT UNIQUE, slug TEXT UNIQUE,
name TEXT, name TEXT,
path TEXT path TEXT,
providers TEXT
); );
CREATE TABLE librariesLinks( CREATE TABLE librariesLinks(
libraryID INTEGER, libraryID INTEGER,
@ -209,10 +210,11 @@ namespace Kyoo.Controllers
public string GetShowExternalIDs(long showID) 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)) using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection))
{ {
cmd.Parameters.AddWithValue("$showID", showID);
SQLiteDataReader reader = cmd.ExecuteReader(); SQLiteDataReader reader = cmd.ExecuteReader();
if (reader.Read()) 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)) using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection))
{ {
cmd.Parameters.AddWithValue("$libraryPath", libraryPath); cmd.Parameters.AddWithValue("$libraryID", library.id);
cmd.Parameters.AddWithValue("$showID", showID); cmd.Parameters.AddWithValue("$showID", showID);
cmd.ExecuteNonQuery(); 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) if (people == null)
return; return;
string linkQuery = "INSERT INTO peopleLinks (peopleID, showID, role, type) VALUES($peopleID, $showID, $role, $type);"; 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)) 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("$showID", showID);
cmd.Parameters.AddWithValue("$role", people[i].Role); cmd.Parameters.AddWithValue("$role", peop.Role);
cmd.Parameters.AddWithValue("$type", people[i].Type); cmd.Parameters.AddWithValue("$type", peop.Type);
cmd.ExecuteNonQuery(); cmd.ExecuteNonQuery();
} }
} }

View File

@ -1,124 +1,87 @@
using Kyoo.Models; using System;
using Microsoft.Extensions.Configuration; using Kyoo.Models;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using Kyoo.Controllers.ThumbnailsManager; using Kyoo.Controllers.ThumbnailsManager;
using Kyoo.Utility;
namespace Kyoo.Controllers namespace Kyoo.Controllers
{ {
public class ProviderManager : IMetadataProvider public class ProviderManager : IProviderManager
{ {
private readonly IEnumerable<IMetadataProvider> providers; private readonly IEnumerable<IMetadataProvider> providers;
private readonly IThumbnailsManager thumbnailsManager; 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.thumbnailsManager = thumbnailsManager;
this.config = config;
providers = pluginManager.GetPlugins<IMetadataProvider>(); 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(); T ret = new T();
}
foreach (IMetadataProvider provider in providers.OrderBy(provider => Array.IndexOf(library.Providers, provider.Name)))
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++)
{ {
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 ret;
return Merge(datas);
} }
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>(); List<T> ret = new List<T>();
for (int i = 0; i < providers.Count; i++)
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 ret;
return Merge(datas); }
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>(); Show show = await GetMetadata(provider => provider.GetShowFromName(showName, showPath), library, $"the show {showName}");
for (int i = 0; i < providers.Count; i++) await thumbnailsManager.Validate(show);
{ return show;
datas.Add(await providers[i].GetShowFromName(showName, showPath));
}
Show show = Merge(datas);
return await thumbnailsManager.Validate(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>(); return await GetMetadata(provider => provider.GetSeason(showName, seasonNumber), library, $"the season ${seasonNumber} of {showName}");
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) 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. Episode episode = await GetMetadata(provider => provider.GetEpisode(externalIDs, seasonNumber, episodeNumber, absoluteNumber, episodePath), library, $"the episode at {episodePath}");
await thumbnailsManager.Validate(episode);
return providers[0].GetSeasonImage(showName, seasonNumber); 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>(); IEnumerable<People> people = await GetMetadata(provider => provider.GetPeople(showExternalIDs), library, $"unknown data");
for (int i = 0; i < providers.Count; i++) people = await thumbnailsManager.Validate(people);
{ return people;
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);
} }
} }
} }

View File

@ -67,20 +67,20 @@ namespace Kyoo.Controllers.ThumbnailsManager
return show; 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"); string root = config.GetValue<string>("peoplePath");
Directory.CreateDirectory(root); Directory.CreateDirectory(root);
string localThumb = root + "/" + people[i].slug + ".jpg"; string localThumb = root + "/" + peop.slug + ".jpg";
if (people[i].imgPrimary != null && !File.Exists(localThumb)) if (peop.imgPrimary != null && !File.Exists(localThumb))
{ {
try try
{ {
using WebClient client = new WebClient(); using WebClient client = new WebClient();
await client.DownloadFileTaskAsync(new Uri(people[i].imgPrimary), localThumb); await client.DownloadFileTaskAsync(new Uri(peop.imgPrimary), localThumb);
} }
catch (WebException) catch (WebException)
{ {

View File

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

View File

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