From 69a8ec1c18ce9f02f3552c26939086e935ccbe54 Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Fri, 9 Aug 2019 02:05:32 +0200 Subject: [PATCH] Creating provider manager, creating season creation etc. --- Kyoo/InternalAPI/Crawler/Crawler.cs | 17 +++- .../LibraryManager/ILibraryManager.cs | 9 +- .../LibraryManager/LibraryManager.cs | 79 +++++++++++++---- .../MetadataProvider/IMetadataProvider.cs | 6 +- ...ShowProviderTvDB.cs => ProviderTheTvDB.cs} | 43 +++++----- .../MetadataProvider/MetadataAttribute.cs | 13 +++ .../MetadataProvider/ProviderHelper.cs | 2 +- .../MetadataProvider/ProviderManager.cs | 84 +++++++++++++++++++ Kyoo/Models/Season.cs | 16 ++++ Kyoo/Startup.cs | 2 +- Kyoo/config.json | 1 + 11 files changed, 229 insertions(+), 43 deletions(-) rename Kyoo/InternalAPI/MetadataProvider/Implementations/TheTvDB/{ShowProviderTvDB.cs => ProviderTheTvDB.cs} (89%) create mode 100644 Kyoo/InternalAPI/MetadataProvider/MetadataAttribute.cs create mode 100644 Kyoo/InternalAPI/MetadataProvider/ProviderManager.cs create mode 100644 Kyoo/Models/Season.cs diff --git a/Kyoo/InternalAPI/Crawler/Crawler.cs b/Kyoo/InternalAPI/Crawler/Crawler.cs index 7eb90740..01d01f8d 100644 --- a/Kyoo/InternalAPI/Crawler/Crawler.cs +++ b/Kyoo/InternalAPI/Crawler/Crawler.cs @@ -56,12 +56,12 @@ namespace Kyoo.InternalAPI string ShowPath = Path.GetDirectoryName(file); string ShowTitle = match.Groups["ShowTitle"].Value; - bool seasonSuccess = long.TryParse(match.Groups["Season"].Value, out long Season); - bool episodeSucess = long.TryParse(match.Groups["Episode"].Value, out long Episode); + bool seasonSuccess = long.TryParse(match.Groups["Season"].Value, out long seasonNumber); + bool episodeSucess = long.TryParse(match.Groups["Episode"].Value, out long episodeNumber); - Debug.WriteLine("&ShowPath: " + ShowPath + " Show: " + ShowTitle + " season: " + Season + " episode: " + Episode); + Debug.WriteLine("&ShowPath: " + ShowPath + " Show: " + ShowTitle + " season: " + seasonNumber + " episode: " + episodeNumber); - if (!libraryManager.IsShowRegistered(ShowPath, out long? showID)) + if (!libraryManager.IsShowRegistered(ShowPath, out long showID)) { Debug.WriteLine("&Should register show: " + ShowTitle); Show show = await metadataProvider.GetShowFromName(ShowTitle, ShowPath); @@ -69,6 +69,15 @@ namespace Kyoo.InternalAPI } Debug.WriteLine("&Show ID: " + showID); + + if(!libraryManager.IsSeasonRegistered(showID, seasonNumber, out long seasonID)) + { + Debug.WriteLine("&Should register season: " + ShowTitle + " - " + seasonNumber); + Season season = await metadataProvider.GetSeason(showID, seasonNumber); + showID = libraryManager.RegisterSeason(season); + } + + Debug.WriteLine("&Season ID: " + seasonID); } } } diff --git a/Kyoo/InternalAPI/LibraryManager/ILibraryManager.cs b/Kyoo/InternalAPI/LibraryManager/ILibraryManager.cs index fe87d617..e1a93d5d 100644 --- a/Kyoo/InternalAPI/LibraryManager/ILibraryManager.cs +++ b/Kyoo/InternalAPI/LibraryManager/ILibraryManager.cs @@ -5,15 +5,18 @@ namespace Kyoo.InternalAPI { public interface ILibraryManager { - //Read values + //Public value reading IEnumerable QueryShows(string selection); //Check if value exists - bool IsEpisodeRegistered(string episodePath); bool IsShowRegistered(string showPath); - bool IsShowRegistered(string showPath, out long? showID); + bool IsShowRegistered(string showPath, out long showID); + bool IsSeasonRegistered(long showID, long seasonNumber); + bool IsSeasonRegistered(long showID, long seasonNumber, out long seasonID); + bool IsEpisodeRegistered(string episodePath); //Register values long RegisterShow(Show show); + long RegisterSeason(Season season); } } diff --git a/Kyoo/InternalAPI/LibraryManager/LibraryManager.cs b/Kyoo/InternalAPI/LibraryManager/LibraryManager.cs index ba91f71b..7f25e5db 100644 --- a/Kyoo/InternalAPI/LibraryManager/LibraryManager.cs +++ b/Kyoo/InternalAPI/LibraryManager/LibraryManager.cs @@ -163,6 +163,7 @@ namespace Kyoo.InternalAPI sqlConnection.Close(); } + #region Read the database public IEnumerable QueryShows(string selection) { string query = "SELECT * FROM shows;"; @@ -179,18 +180,9 @@ namespace Kyoo.InternalAPI return shows; } } + #endregion - public bool IsEpisodeRegistered(string episodePath) - { - string query = "SELECT 1 FROM episodes WHERE path = $path;"; - using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection)) - { - cmd.Parameters.AddWithValue("$path", episodePath); - - return cmd.ExecuteScalar() != null; - } - } - + #region Check if items exists public bool IsShowRegistered(string showPath) { string query = "SELECT 1 FROM shows WHERE path = $path;"; @@ -202,18 +194,56 @@ namespace Kyoo.InternalAPI } } - public bool IsShowRegistered(string showPath, out long? showID) + public bool IsShowRegistered(string showPath, out long showID) { string query = "SELECT 1 FROM shows WHERE path = $path;"; using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection)) { cmd.Parameters.AddWithValue("$path", showPath); - showID = cmd.ExecuteScalar() as long?; + showID = cmd.ExecuteScalar() as long? ?? -1; - return showID != null; + return showID != -1; } } + public bool IsSeasonRegistered(long showID, long seasonNumber) + { + string query = "SELECT 1 FROM seasons WHERE showID = $showID AND seasonNumber = $seasonNumber;"; + using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection)) + { + cmd.Parameters.AddWithValue("$showID", showID); + cmd.Parameters.AddWithValue("$seasonNumber", seasonNumber); + + return cmd.ExecuteScalar() != null; + } + } + + public bool IsSeasonRegistered(long showID, long seasonNumber, out long seasonID) + { + string query = "SELECT 1 FROM seasons WHERE showID = $showID AND seasonNumber = $seasonNumber;"; + using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection)) + { + cmd.Parameters.AddWithValue("$showID", showID); + cmd.Parameters.AddWithValue("$seasonNumber", seasonNumber); + seasonID = cmd.ExecuteScalar() as long? ?? -1; + + return seasonID != -1; + } + } + + public bool IsEpisodeRegistered(string episodePath) + { + string query = "SELECT 1 FROM episodes WHERE path = $path;"; + using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection)) + { + cmd.Parameters.AddWithValue("$path", episodePath); + + return cmd.ExecuteScalar() != null; + } + } + #endregion + + #region Write Into The Database public long RegisterShow(Show show) { string query = "INSERT INTO shows (slug, title, aliases, path, overview, genres, startYear, endYear, imgPrimary, imgThumb, imgLogo, imgBackdrop, externalIDs) VALUES($slug, $title, $aliases, $path, $overview, $genres, $startYear, $endYear, $imgPrimary, $imgThumb, $imgLogo, $imgBackdrop, $externalIDs);"; @@ -240,5 +270,26 @@ namespace Kyoo.InternalAPI return (long)cmd.ExecuteScalar(); } } + + public long RegisterSeason(Season season) + { + string query = "INSERT INTO seasons (showID, seasonNumber, title, overview, year, imgPrimary, externalIDs) VALUES($showID, $seasonNumber, $title, $overview, $year, $imgPrimary, $externalIDs);"; + Debug.WriteLine("&SQL QUERY:: " + query); + using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection)) + { + cmd.Parameters.AddWithValue("$showID", season.ShowID); + cmd.Parameters.AddWithValue("$seasonNumber", season.seasonNumber); + cmd.Parameters.AddWithValue("$title", season.Title); + cmd.Parameters.AddWithValue("$overview", season.Overview); + cmd.Parameters.AddWithValue("$year", season.year); + cmd.Parameters.AddWithValue("$imgPrimary", season.ImgPrimary); + cmd.Parameters.AddWithValue("$externalIDs", season.ExternalIDs); + cmd.ExecuteNonQuery(); + + cmd.CommandText = "SELECT LAST_INSERT_ROWID()"; + return (long)cmd.ExecuteScalar(); + } + } + #endregion } } diff --git a/Kyoo/InternalAPI/MetadataProvider/IMetadataProvider.cs b/Kyoo/InternalAPI/MetadataProvider/IMetadataProvider.cs index ad617566..7c95cec0 100644 --- a/Kyoo/InternalAPI/MetadataProvider/IMetadataProvider.cs +++ b/Kyoo/InternalAPI/MetadataProvider/IMetadataProvider.cs @@ -5,10 +5,14 @@ namespace Kyoo.InternalAPI { public interface IMetadataProvider { - Task CompleteShow(Show show); + //For the show + Task GetShowByID(string id); Task GetShowFromName(string showName, string showPath); Task GetImages(Show show); + + //For the seasons + Task GetSeason(string showName, int seasonNumber); } } diff --git a/Kyoo/InternalAPI/MetadataProvider/Implementations/TheTvDB/ShowProviderTvDB.cs b/Kyoo/InternalAPI/MetadataProvider/Implementations/TheTvDB/ProviderTheTvDB.cs similarity index 89% rename from Kyoo/InternalAPI/MetadataProvider/Implementations/TheTvDB/ShowProviderTvDB.cs rename to Kyoo/InternalAPI/MetadataProvider/Implementations/TheTvDB/ProviderTheTvDB.cs index 6e8d7199..85c85c89 100644 --- a/Kyoo/InternalAPI/MetadataProvider/Implementations/TheTvDB/ShowProviderTvDB.cs +++ b/Kyoo/InternalAPI/MetadataProvider/Implementations/TheTvDB/ProviderTheTvDB.cs @@ -7,13 +7,13 @@ using System.Diagnostics; using System.IO; using System.Linq; using System.Net; -using System.Text; using System.Threading.Tasks; using System.Web; namespace Kyoo.InternalAPI.MetadataProvider { - public class ShowProviderTvDB : HelperTvDB, IMetadataProvider + [MetaProvider] + public class ProviderTheTvDB : HelperTvDB, IMetadataProvider { private struct SearchTbDB { @@ -122,7 +122,7 @@ namespace Kyoo.InternalAPI.MetadataProvider GetYear(data.firstAired), null, //endYear string.Format("{0}={1}|", Provider, data.id)); - return await CompleteShow(show); + return await GetShowByID(GetID(show.ExternalIDs)) ?? show; } } else @@ -140,17 +140,12 @@ namespace Kyoo.InternalAPI.MetadataProvider return new Show() { Slug = ToSlug(showName), Title = showName }; } - public async Task CompleteShow(Show show) + public async Task GetShowByID(string id) { - string id = GetId(show.ExternalIDs); - - if (id == null) - return show; - string token = await Authentificate(); if (token == null) - return show; + return null; WebRequest request = WebRequest.Create("https://api.thetvdb.com/series/" + id); request.Method = "GET"; @@ -174,12 +169,17 @@ namespace Kyoo.InternalAPI.MetadataProvider var model = new { data = new DataTvDb(), errors = new ErrorsTvDB() }; DataTvDb data = JsonConvert.DeserializeAnonymousType(content, model).data; - show.Title = data.seriesName; - show.Aliases = data.aliases; - show.Overview = data.overview; - show.Genres = data.genre; - show.Status = GetStatus(data.status); - show.StartYear = GetYear(data.firstAired); + Show show = new Show(-1, + null, //Slug + data.seriesName, + data.aliases, + null, //Path + data.overview, + data.genre, + GetStatus(data.status), + GetYear(data.firstAired), + null, //endYear + string.Format("TvDB={0}|", id)); await GetImages(show); return show; } @@ -188,20 +188,20 @@ namespace Kyoo.InternalAPI.MetadataProvider { Debug.WriteLine("&TheTvDB Provider couldn't work for the show with the id: " + id + ".\nError Code: " + response.StatusCode + " Message: " + response.StatusDescription); response.Close(); - return show; + return null; } } catch(WebException ex) { Debug.WriteLine("&TheTvDB Provider couldn't work for the show with the id: " + id + ".\nError Code: " + ex.Status); - return show; + return null; } } public async Task GetImages(Show show) { Debug.WriteLine("&Getting images for: " + show.Title); - string id = GetId(show.ExternalIDs); + string id = GetID(show.ExternalIDs); if (id == null) return show; @@ -248,5 +248,10 @@ namespace Kyoo.InternalAPI.MetadataProvider return show; } + + public Task GetSeason(string showName, int seasonNumber) + { + throw new NotImplementedException(); + } } } diff --git a/Kyoo/InternalAPI/MetadataProvider/MetadataAttribute.cs b/Kyoo/InternalAPI/MetadataProvider/MetadataAttribute.cs new file mode 100644 index 00000000..cd72a719 --- /dev/null +++ b/Kyoo/InternalAPI/MetadataProvider/MetadataAttribute.cs @@ -0,0 +1,13 @@ +using System; + +namespace Kyoo.InternalAPI.MetadataProvider +{ + [AttributeUsage(AttributeTargets.Class)] + public class MetaProvider : Attribute + { + public MetaProvider() + { + + } + } +} diff --git a/Kyoo/InternalAPI/MetadataProvider/ProviderHelper.cs b/Kyoo/InternalAPI/MetadataProvider/ProviderHelper.cs index 2ebd8c89..292bc2bd 100644 --- a/Kyoo/InternalAPI/MetadataProvider/ProviderHelper.cs +++ b/Kyoo/InternalAPI/MetadataProvider/ProviderHelper.cs @@ -8,7 +8,7 @@ namespace Kyoo.InternalAPI.MetadataProvider { public abstract string Provider { get; } - public string GetId(string externalIDs) + public string GetID(string externalIDs) { if (externalIDs.Contains(Provider)) { diff --git a/Kyoo/InternalAPI/MetadataProvider/ProviderManager.cs b/Kyoo/InternalAPI/MetadataProvider/ProviderManager.cs new file mode 100644 index 00000000..83299b26 --- /dev/null +++ b/Kyoo/InternalAPI/MetadataProvider/ProviderManager.cs @@ -0,0 +1,84 @@ +using Kyoo.InternalAPI.MetadataProvider; +using Kyoo.Models; +using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Threading.Tasks; + +namespace Kyoo.InternalAPI +{ + public class ProviderManager : IMetadataProvider + { + private readonly List providers = new List(); + private readonly IConfiguration config; + + public ProviderManager(IConfiguration configuration) + { + config = configuration; + LoadProviders(); + } + + void LoadProviders() + { + providers.Clear(); + providers.Add(new ProviderTheTvDB()); + + string[] pluginsPaths = Directory.GetFiles(config.GetValue("providerPlugins")); + List plugins = new List(); + List types = new List(); + + for (int i = 0; i < pluginsPaths.Length; i++) + { + plugins.Add(Assembly.LoadFile(pluginsPaths[i])); + types.AddRange(plugins[i].GetTypes()); + } + + List providersPlugins = types.FindAll(x => + { + object[] atr = x.GetCustomAttributes(typeof(MetaProvider), false); + + if (atr == null || atr.Length == 0) + return false; + + List interfaces = new List(x.GetInterfaces()); + + if (interfaces.Contains(typeof(IMetadataProvider))) + return true; + + return false; + }); + + providers.AddRange(providersPlugins.ConvertAll(x => Activator.CreateInstance(x) as IMetadataProvider)); + } + + //public Show MergeShows(Show baseShow, Show newShow) + //{ + + //} + + + //For all the following methods, it should use all providers and merge the data. + + public Task GetImages(Show show) + { + return providers[0].GetImages(show); + } + + public Task GetSeason(string showName, int seasonNumber) + { + return providers[0].GetSeason(showName, seasonNumber); + } + + public Task GetShowByID(string id) + { + return providers[0].GetShowByID(id); + } + + public Task GetShowFromName(string showName, string showPath) + { + return providers[0].GetShowFromName(showName, showPath); + } + } +} diff --git a/Kyoo/Models/Season.cs b/Kyoo/Models/Season.cs new file mode 100644 index 00000000..e428ce41 --- /dev/null +++ b/Kyoo/Models/Season.cs @@ -0,0 +1,16 @@ +namespace Kyoo.Models +{ + public class Season + { + public readonly long id; + public readonly long ShowID; + + public long seasonNumber; + public string Title; + public string Overview; + public long year; + + public string ImgPrimary; + public string ExternalIDs; + } +} diff --git a/Kyoo/Startup.cs b/Kyoo/Startup.cs index e8ee5a01..cc7b90aa 100644 --- a/Kyoo/Startup.cs +++ b/Kyoo/Startup.cs @@ -32,7 +32,7 @@ namespace Kyoo services.AddSingleton(); services.AddHostedService(); - services.AddSingleton(); //Shouldn't use it like that, it won't work with multiple providers. + services.AddSingleton(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. diff --git a/Kyoo/config.json b/Kyoo/config.json index 1b3356a0..d3957b02 100644 --- a/Kyoo/config.json +++ b/Kyoo/config.json @@ -1,5 +1,6 @@ { "databasePath": "C://Projects/database.db", + "providerPlugins": "C://Projects/Plugins/Providers", "libraryPaths": [ "D:\\Videos" ],