From 054a2718b7a355891bfbeef53d0c28fe0a45b1fe Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Mon, 5 Aug 2019 00:11:58 +0200 Subject: [PATCH] Creating file crawler and the metadata provider interface. --- Kyoo/InternalAPI/Crawler/Crawler.cs | 131 ++++++++++++++++++ .../LibraryManager/ILibraryManager.cs | 4 + .../LibraryManager/LibraryManager.cs | 33 ++++- .../MetadataProvider/IMetadataProvider.cs | 9 ++ .../Implementations/ProviderTheTvDB.cs | 12 ++ Kyoo/Models/Episode.cs | 29 ++++ Kyoo/Program.cs | 10 +- Kyoo/Startup.cs | 3 + Kyoo/config.json | 6 + 9 files changed, 230 insertions(+), 7 deletions(-) create mode 100644 Kyoo/InternalAPI/Crawler/Crawler.cs create mode 100644 Kyoo/InternalAPI/MetadataProvider/IMetadataProvider.cs create mode 100644 Kyoo/InternalAPI/MetadataProvider/Implementations/ProviderTheTvDB.cs create mode 100644 Kyoo/Models/Episode.cs create mode 100644 Kyoo/config.json diff --git a/Kyoo/InternalAPI/Crawler/Crawler.cs b/Kyoo/InternalAPI/Crawler/Crawler.cs new file mode 100644 index 00000000..3b63fab5 --- /dev/null +++ b/Kyoo/InternalAPI/Crawler/Crawler.cs @@ -0,0 +1,131 @@ +using Kyoo.Models; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; + +namespace Kyoo.InternalAPI +{ + public class Crawler : IHostedService + { + private readonly IConfiguration config; + private readonly ILibraryManager libraryManager; + private readonly IMetadataProvider metadataProvider; + + public Crawler(IConfiguration configuration, ILibraryManager libraryManager, IMetadataProvider metadataProvider) + { + config = configuration; + this.libraryManager = libraryManager; + this.metadataProvider = metadataProvider; + } + + public Task StartAsync(CancellationToken cancellationToken) + { + Debug.WriteLine("&Crawler started"); + string[] paths = config.GetSection("libraryPaths").Get(); + + foreach (string path in paths) + { + Scan(path); + Watch(path, cancellationToken); + } + + while (!cancellationToken.IsCancellationRequested) ; + + Debug.WriteLine("&Crawler stopped"); + return null; + } + + public void Scan(string folderPath) + { + string[] files = Directory.GetFiles(folderPath); + + foreach (string file in files) + { + if(IsVideo(file) && !libraryManager.IsEpisodeRegistered(file)) + { + Debug.WriteLine("&Should insert this: " + file); + + string patern = @".*\\(?.+?) S(?\d+)E(?\d+)"; + Regex regex = new Regex(patern, RegexOptions.IgnoreCase); + Match match = regex.Match(file); + + 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); + + Debug.WriteLine("&ShowPath: " + ShowPath + " Show: " + ShowTitle + " season: " + Season + " episode: " + Episode); + + if(!libraryManager.IsShowRegistered(ShowPath)) + { + Show show = metadataProvider.GetShowFromName(ShowTitle); + } + } + } + } + + public void Watch(string folderPath, CancellationToken cancellationToken) + { + 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); + } + + 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 static readonly string[] videoExtensions = { ".webm", ".mkv", ".flv", ".vob", ".ogg", ".ogv", ".avi", ".mts", ".m2ts", ".ts", ".mov", ".qt", ".asf", ".mp4", ".m4p", ".m4v", ".mpg", ".mp2", ".mpeg", ".mpe", ".mpv", ".m2v", ".3gp", ".3g2" }; + + private bool IsVideo(string filePath) + { + return videoExtensions.Contains(Path.GetExtension(filePath)); + } + + + public Task StopAsync(CancellationToken cancellationToken) + { + return null; + } + } +} diff --git a/Kyoo/InternalAPI/LibraryManager/ILibraryManager.cs b/Kyoo/InternalAPI/LibraryManager/ILibraryManager.cs index e8915cb6..f4d4f55c 100644 --- a/Kyoo/InternalAPI/LibraryManager/ILibraryManager.cs +++ b/Kyoo/InternalAPI/LibraryManager/ILibraryManager.cs @@ -6,5 +6,9 @@ namespace Kyoo.InternalAPI public interface ILibraryManager { IEnumerable QueryShows(string selection); + + bool IsEpisodeRegistered(string episodePath); + + bool IsShowRegistered(string showPath); } } diff --git a/Kyoo/InternalAPI/LibraryManager/LibraryManager.cs b/Kyoo/InternalAPI/LibraryManager/LibraryManager.cs index 53306b6d..2792d34c 100644 --- a/Kyoo/InternalAPI/LibraryManager/LibraryManager.cs +++ b/Kyoo/InternalAPI/LibraryManager/LibraryManager.cs @@ -1,4 +1,5 @@ using Kyoo.Models; +using Microsoft.Extensions.Configuration; using System.Collections.Generic; using System.Data.SQLite; using System.Diagnostics; @@ -11,13 +12,15 @@ namespace Kyoo.InternalAPI private readonly SQLiteConnection sqlConnection; - public LibraryManager() + public LibraryManager(IConfiguration configuration) { - Debug.WriteLine("&Library Manager init"); + string databasePath = configuration.GetValue("databasePath"); - string databasePath = @"C://Projects/database.db"; + Debug.WriteLine("&Library Manager init, databasePath: " + databasePath); if (!File.Exists(databasePath)) { + Debug.WriteLine("&Database doesn't exist, creating one."); + SQLiteConnection.CreateFile(databasePath); sqlConnection = new SQLiteConnection(string.Format("Data Source={0};Version=3", databasePath)); sqlConnection.Open(); @@ -27,6 +30,7 @@ namespace Kyoo.InternalAPI uri TEXT UNIQUE, title TEXT, aliases TEXT, + path TEXT, overview TEXT, status TEXT, startYear INTEGER, @@ -54,6 +58,7 @@ namespace Kyoo.InternalAPI showID INTEGER, seasonID INTEGER, episodeNumber INTEGER, + path TEXT, title TEXT, overview TEXT, imgPrimary TEXT, @@ -158,7 +163,7 @@ namespace Kyoo.InternalAPI public IEnumerable QueryShows(string selection) { - string query = "SELECT * FROM shows"; + string query = "SELECT * FROM shows;"; SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection); SQLiteDataReader reader = cmd.ExecuteReader(); @@ -170,5 +175,25 @@ namespace Kyoo.InternalAPI return shows; } + + public bool IsEpisodeRegistered(string episodePath) + { + string query = "SELECT 1 FROM episodes WHERE path = $path;"; + SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection); + + cmd.Parameters.AddWithValue("$path", episodePath); + + return cmd.ExecuteScalar() != null; + } + + public bool IsShowRegistered(string showPath) + { + string query = "SELECT 1 FROM shows WHERE path = $path;"; + SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection); + + cmd.Parameters.AddWithValue("$path", showPath); + + return cmd.ExecuteScalar() != null; + } } } diff --git a/Kyoo/InternalAPI/MetadataProvider/IMetadataProvider.cs b/Kyoo/InternalAPI/MetadataProvider/IMetadataProvider.cs new file mode 100644 index 00000000..13a308b6 --- /dev/null +++ b/Kyoo/InternalAPI/MetadataProvider/IMetadataProvider.cs @@ -0,0 +1,9 @@ +using Kyoo.Models; + +namespace Kyoo.InternalAPI +{ + public interface IMetadataProvider + { + Show GetShowFromName(string showName); + } +} diff --git a/Kyoo/InternalAPI/MetadataProvider/Implementations/ProviderTheTvDB.cs b/Kyoo/InternalAPI/MetadataProvider/Implementations/ProviderTheTvDB.cs new file mode 100644 index 00000000..b53920d9 --- /dev/null +++ b/Kyoo/InternalAPI/MetadataProvider/Implementations/ProviderTheTvDB.cs @@ -0,0 +1,12 @@ +using Kyoo.Models; + +namespace Kyoo.InternalAPI.MetadataProvider +{ + public class ProviderTheTvDB : IMetadataProvider + { + public Show GetShowFromName(string showName) + { + throw new System.NotImplementedException(); + } + } +} diff --git a/Kyoo/Models/Episode.cs b/Kyoo/Models/Episode.cs new file mode 100644 index 00000000..a79f717a --- /dev/null +++ b/Kyoo/Models/Episode.cs @@ -0,0 +1,29 @@ +using System; + +namespace Kyoo.Models +{ + public class Episode + { + public readonly long id; + public readonly long ShowID; + public readonly long SeasonID; + + public long episodeNumber; + public string Title; + public string Overview; + public DateTime ReleaseDate; + + public long Runtime; //This runtime variable should be in seconds (used by the video manager so we need precisions) + + public string ImgPrimary; + public string ExternalIDs; + + public long RuntimeInMinutes + { + get + { + return Runtime / 60; + } + } + } +} diff --git a/Kyoo/Program.cs b/Kyoo/Program.cs index f751e9c2..df75a2f9 100644 --- a/Kyoo/Program.cs +++ b/Kyoo/Program.cs @@ -1,8 +1,7 @@ -using Kyoo.InternalAPI; -using Kyoo.Models; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; -using System.Diagnostics; +using Microsoft.Extensions.Configuration; +using System.IO; namespace Kyoo { @@ -15,6 +14,11 @@ namespace Kyoo public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) + .ConfigureAppConfiguration((hostingContext, config) => + { + config.SetBasePath(Directory.GetCurrentDirectory()); + config.AddJsonFile("config.json", false, true); + }) .UseStartup(); } } diff --git a/Kyoo/Startup.cs b/Kyoo/Startup.cs index ff48428b..7df95135 100644 --- a/Kyoo/Startup.cs +++ b/Kyoo/Startup.cs @@ -1,4 +1,5 @@ using Kyoo.InternalAPI; +using Kyoo.InternalAPI.MetadataProvider; using Kyoo.Models; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; @@ -30,6 +31,8 @@ namespace Kyoo }); services.AddSingleton(); + services.AddHostedService(); + services.AddSingleton(); //Shouldn't use it like that, it won't work with multiple providers. } // 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 new file mode 100644 index 00000000..714f8dfa --- /dev/null +++ b/Kyoo/config.json @@ -0,0 +1,6 @@ +{ + "databasePath": "C://Projects/database.db", + "libraryPaths": [ + "D:\\Videos" + ] +}