Adding collection support, only need to make the component for the visual interface.

This commit is contained in:
Zoe Roux 2019-10-07 00:13:26 +02:00
parent 725c6c4b88
commit 5940ccfc9d
15 changed files with 170 additions and 134 deletions

View File

@ -21,7 +21,7 @@
</mat-menu>
<div class="container-fluid justify-content-center">
<a class="show" *ngFor="let show of this.shows" routerLink="/show/{{show.slug}}">
<a class="show" *ngFor="let show of this.shows" [href]="getLink(show)" [routerLink]="getLink(show)">
<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>

View File

@ -28,6 +28,14 @@ export class BrowseComponent implements OnInit
return this.sanitizer.bypassSecurityTrustStyle("url(/poster/" + slug + ")");
}
getLink(show: Show)
{
if (show.isCollection)
return "/collection/" + show.slug;
else
return "/show/" + show.slug;
}
sort(type: string, order: boolean)
{
this.sortType = type;

View File

@ -16,10 +16,10 @@ export interface Show
people: People[];
seasons: Season[];
trailerUrl: string;
isCollection: boolean;
startYear: number;
endYear : number;
externalIDs: string;
}

View File

@ -1,4 +1,5 @@
using Kyoo.Models;
using Kyoo.InternalAPI.Utility;
using Kyoo.Models;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using System.Collections.Generic;
@ -15,17 +16,17 @@ namespace Kyoo.InternalAPI
{
private readonly CancellationTokenSource cancellation;
private readonly IConfiguration config;
private readonly ILibraryManager libraryManager;
private readonly IMetadataProvider metadataProvider;
private readonly ITranscoder transcoder;
private readonly IConfiguration config;
public Crawler(IConfiguration configuration, ILibraryManager libraryManager, IMetadataProvider metadataProvider, ITranscoder transcoder)
public Crawler(ILibraryManager libraryManager, IMetadataProvider metadataProvider, ITranscoder transcoder, IConfiguration configuration)
{
config = configuration;
this.libraryManager = libraryManager;
this.metadataProvider = metadataProvider;
this.transcoder = transcoder;
config = configuration;
cancellation = new CancellationTokenSource();
}
@ -129,12 +130,13 @@ namespace Kyoo.InternalAPI
Match match = regex.Match(path);
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;
if(!seasonSuccess || !episodeSucess)
if (!seasonSuccess || !episodeSucess)
{
//Considering that the episode is using absolute path.
seasonNumber = -1;
@ -160,6 +162,16 @@ namespace Kyoo.InternalAPI
showProviderIDs = show.ExternalIDs;
showID = libraryManager.RegisterShow(show);
if (collectionName != null)
{
if (!libraryManager.IsCollectionRegistered(Slugifier.ToSlug(collectionName), out long collectionID))
{
Collection collection = await metadataProvider.GetCollectionFromName(collectionName);
collectionID = libraryManager.RegisterCollection(collection);
}
libraryManager.AddShowToCollection(showID, collectionID);
}
List<People> actors = await metadataProvider.GetPeople(show.ExternalIDs);
libraryManager.RegisterShowPeople(showID, actors);
}
@ -180,7 +192,7 @@ namespace Kyoo.InternalAPI
Episode episode = await metadataProvider.GetEpisode(showProviderIDs, seasonNumber, episodeNumber, absoluteNumber, path);
episode.ShowID = showID;
if(seasonID == -1)
if (seasonID == -1)
{
if (!libraryManager.IsSeasonRegistered(showID, episode.seasonNumber, out seasonID))
{

View File

@ -35,6 +35,8 @@ namespace Kyoo.InternalAPI
Collection GetCollection(string slug);
//Check if value exists
bool IsCollectionRegistered(string collectionSlug);
bool IsCollectionRegistered(string collectionSlug, out long collectionID);
bool IsShowRegistered(string showPath);
bool IsShowRegistered(string showPath, out long showID);
bool IsSeasonRegistered(long showID, long seasonNumber);
@ -52,6 +54,7 @@ namespace Kyoo.InternalAPI
long GetOrCreateStudio(Studio studio);
void RegisterShowPeople(long showID, List<People> actors);
void AddShowToCollection(long showID, long collectionID);
void ClearSubtitles(long episodeID);
}

View File

@ -102,6 +102,8 @@ namespace Kyoo.InternalAPI
slug TEXT UNIQUE,
name TEXT,
overview TEXT,
starYear INTEGER,
endYear INTEGER,
imgPrimary TEXT
);
CREATE TABLE collectionsLinks(
@ -253,19 +255,17 @@ namespace Kyoo.InternalAPI
public IEnumerable<Show> QueryShows(string selection)
{
string query = "SELECT * FROM shows ORDER BY title;";
List<Show> shows = new List<Show>();
SQLiteDataReader reader;
string query = "SELECT slug, title, startYear, endYear, '0' FROM shows LEFT JOIN collectionsLinks l ON l.showID = shows.id WHERE l.showID IS NULL UNION SELECT slug, name, startYear, endYear, '1' FROM collections ORDER BY title;";
using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection))
{
SQLiteDataReader reader = cmd.ExecuteReader();
List<Show> shows = new List<Show>();
reader = cmd.ExecuteReader();
while (reader.Read())
shows.Add(Show.FromReader(reader));
return shows;
shows.Add(Show.FromQueryReader(reader));
}
return shows;
}
public Show GetShowBySlug(string slug)
@ -564,6 +564,31 @@ namespace Kyoo.InternalAPI
#endregion
#region Check if items exists
public bool IsCollectionRegistered(string collectionSlug)
{
string query = "SELECT (id) FROM collections WHERE slug = $slug;";
using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection))
{
cmd.Parameters.AddWithValue("$slug", collectionSlug);
return cmd.ExecuteScalar() != null;
}
}
public bool IsCollectionRegistered(string collectionSlug, out long collectionID)
{
string query = "SELECT (id) FROM collections WHERE slug = $slug;";
using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection))
{
cmd.Parameters.AddWithValue("$slug", collectionSlug);
collectionID = cmd.ExecuteScalar() as long? ?? -1;
return collectionID != -1;
}
}
public bool IsShowRegistered(string showPath)
{
string query = "SELECT (id) FROM shows WHERE path = $path;";
@ -831,6 +856,18 @@ namespace Kyoo.InternalAPI
}
}
public void AddShowToCollection(long showID, long collectionID)
{
string linkQuery = "INSERT INTO collectionsLinks (collectionID, showID) VALUES($collectionID, $showID);";
using (SQLiteCommand cmd = new SQLiteCommand(linkQuery, sqlConnection))
{
cmd.Parameters.AddWithValue("$collectionID", collectionID);
cmd.Parameters.AddWithValue("$showID", showID);
cmd.ExecuteNonQuery();
}
}
public void ClearSubtitles(long episodeID)
{
string query = "DELETE FROM tracks WHERE episodeID = $episodeID;";

View File

@ -6,6 +6,9 @@ namespace Kyoo.InternalAPI
{
public interface IMetadataProvider
{
//For the collection
Task<Collection> GetCollectionFromName(string name);
//For the show
Task<Show> GetShowByID(string id);
Task<Show> GetShowFromName(string showName, string showPath);

View File

@ -1,10 +1,8 @@
using Kyoo.Models;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

View File

@ -1,6 +1,8 @@
using Kyoo.InternalAPI.MetadataProvider.TheTvDB;
using Kyoo.InternalAPI.Utility;
using Kyoo.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@ -16,74 +18,13 @@ namespace Kyoo.InternalAPI.MetadataProvider
[MetaProvider]
public class ProviderTheTvDB : HelperTvDB, IMetadataProvider
{
private struct SearchTbDB
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
public async Task<Collection> GetCollectionFromName(string name)
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
{
public string seriesName;
public string overview;
public string slug;
public string network;
public string status;
public int id;
public string firstAired;
public string banner;
public string[] aliases;
return new Collection(-1, Slugifier.ToSlug(name), name, null, null);
}
private struct DataTvDb
{
public string seriesName;
public string overview;
public string slug;
public string network;
public string status;
public int id;
public string seriesId;
public string imdbId;
public string zap2itId;
public string firstAired;
public string banner;
public string[] aliases;
public string[] genre;
public string added;
public string airsDayOfWeek;
public string airsTime;
public string lastUpdated;
public string runtime;
public string networkId;
public string rating;
public float siteRating;
public int siteRatingCount;
}
private struct RatingInfo
{
public float average;
public int count;
}
private struct ImageTvDb
{
public string fileName;
public int id;
public string keyType;
public int languageId;
public RatingInfo ratingsInfo;
public string resolution;
public string subKey;
public string thumbnail;
}
private struct ErrorsTvDB
{
public string[] invalidFilters;
public string invalidLanguage;
public string[] invalidQueryParams;
}
public async Task<Show> GetShowFromName(string showName, string showPath)
{
string token = await Authentificate();
@ -109,21 +50,21 @@ namespace Kyoo.InternalAPI.MetadataProvider
stream.Close();
response.Close();
var model = new { data = new SearchTbDB[0] };
SearchTbDB data = JsonConvert.DeserializeAnonymousType(content, model).data[0];
dynamic obj = JsonConvert.DeserializeObject(content);
dynamic data = obj.data[0];
Show show = new Show(-1,
ToSlug(showName),
data.seriesName,
data.aliases,
(string)data.seriesName,
((JArray)data.aliases).ToObject<IEnumerable<string>>(),
showPath,
data.overview,
(string)data.overview,
null, //trailer
null, //genres (no info with this request)
GetStatus(data.status),
GetYear(data.firstAired),
GetStatus((string)data.status),
GetYear((string)data.firstAired),
null, //endYear
string.Format("{0}={1}|", Provider, data.id));
string.Format("{0}={1}|", Provider, (string)data.id));
return (await GetShowByID(GetID(show.ExternalIDs))).Set(show.Slug, show.Path) ?? show;
}
}
@ -168,19 +109,19 @@ namespace Kyoo.InternalAPI.MetadataProvider
stream.Close();
response.Close();
var model = new { data = new DataTvDb(), errors = new ErrorsTvDB() };
DataTvDb data = JsonConvert.DeserializeAnonymousType(content, model).data;
dynamic model = JsonConvert.DeserializeObject(content);
dynamic data = model.data;
Show show = new Show(-1,
null, //Slug
data.seriesName,
data.aliases,
(string)data.seriesName,
((JArray)data.aliases).ToObject<IEnumerable<string>>(),
null, //Path
data.overview,
(string)data.overview,
null, //Trailer
GetGenres(data.genre),
GetStatus(data.status),
GetYear(data.firstAired),
GetGenres(((JArray)data.genre).ToObject<string[]>()),
GetStatus((string)data.status),
GetYear((string)data.firstAired),
null, //endYear
string.Format("TvDB={0}|", id));
await GetImages(show);
@ -237,10 +178,9 @@ namespace Kyoo.InternalAPI.MetadataProvider
stream.Close();
response.Close();
var model = new { data = new ImageTvDb[0], error = new ErrorsTvDB() };
dynamic model = JsonConvert.DeserializeObject(content);
//Should implement language selection here
ImageTvDb data = JsonConvert.DeserializeAnonymousType(content, model).data.OrderByDescending(x => x.ratingsInfo.average).ThenByDescending(x => x.ratingsInfo.count).FirstOrDefault();
IEnumerable<ImageTvDb> datas = JsonConvert.DeserializeAnonymousType(content, model).data.OrderByDescending(x => x.ratingsInfo.average).ThenByDescending(x => x.ratingsInfo.count);
dynamic data = ((IEnumerable<dynamic>)model.data).OrderByDescending(x => x.ratingsInfo.average).ThenByDescending(x => x.ratingsInfo.count).FirstOrDefault();
SetImage(show, "https://www.thetvdb.com/banners/" + data.fileName, type.Key);
}
}

View File

@ -1,7 +1,6 @@
using Kyoo.Models;
using Kyoo.InternalAPI.Utility;
using Kyoo.Models;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
namespace Kyoo.InternalAPI.MetadataProvider
{
@ -22,29 +21,7 @@ namespace Kyoo.InternalAPI.MetadataProvider
public string ToSlug(string showTitle)
{
if (showTitle == null)
return null;
//First to lower case
showTitle = showTitle.ToLowerInvariant();
//Remove all accents
//var bytes = Encoding.GetEncoding("Cyrillic").GetBytes(showTitle);
//showTitle = Encoding.ASCII.GetString(bytes);
//Replace spaces
showTitle = Regex.Replace(showTitle, @"\s", "-", RegexOptions.Compiled);
//Remove invalid chars
showTitle = Regex.Replace(showTitle, @"[^\w\s\p{Pd}]", "", RegexOptions.Compiled);
//Trim dashes from end
showTitle = showTitle.Trim('-', '_');
//Replace double occurences of - or \_
showTitle = Regex.Replace(showTitle, @"([-_]){2,}", "$1", RegexOptions.Compiled);
return showTitle;
return Slugifier.ToSlug(showTitle);
}
public enum ImageType { Poster, Background, Thumbnail, Logo }

View File

@ -79,6 +79,11 @@ namespace Kyoo.InternalAPI
//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);

View File

@ -0,0 +1,34 @@
using System.Text.RegularExpressions;
namespace Kyoo.InternalAPI.Utility
{
public class Slugifier
{
public static string ToSlug(string showTitle)
{
if (showTitle == null)
return null;
//First to lower case
showTitle = showTitle.ToLowerInvariant();
//Remove all accents
//var bytes = Encoding.GetEncoding("Cyrillic").GetBytes(showTitle);
//showTitle = Encoding.ASCII.GetString(bytes);
//Replace spaces
showTitle = Regex.Replace(showTitle, @"\s", "-", RegexOptions.Compiled);
//Remove invalid chars
showTitle = Regex.Replace(showTitle, @"[^\w\s\p{Pd}]", "", RegexOptions.Compiled);
//Trim dashes from end
showTitle = showTitle.Trim('-', '_');
//Replace double occurences of - or \_
showTitle = Regex.Replace(showTitle, @"([-_]){2,}", "$1", RegexOptions.Compiled);
return showTitle;
}
}
}

View File

@ -33,6 +33,11 @@ namespace Kyoo.Models
reader["imgPrimary"] as string);
}
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);

View File

@ -1,7 +1,6 @@
using Kyoo.InternalAPI;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
namespace Kyoo.Models
{
@ -33,6 +32,7 @@ namespace Kyoo.Models
public IEnumerable<People> directors;
public IEnumerable<People> people;
public IEnumerable<Season> seasons;
public bool IsCollection;
public string GetAliases()
@ -68,6 +68,7 @@ namespace Kyoo.Models
StartYear = startYear;
EndYear = endYear;
ExternalIDs = externalIDs;
IsCollection = false;
}
public Show(long id, string slug, string title, IEnumerable<string> aliases, string path, string overview, string trailerUrl, Status? status, long? startYear, long? endYear, string imgPrimary, string imgThumb, string imgLogo, string imgBackdrop, string externalIDs)
@ -87,6 +88,19 @@ namespace Kyoo.Models
ImgLogo = imgLogo;
ImgBackdrop = imgBackdrop;
ExternalIDs = externalIDs;
IsCollection = false;
}
public static Show FromQueryReader(System.Data.SQLite.SQLiteDataReader reader)
{
return new Show()
{
Slug = reader["slug"] as string,
Title = reader["title"] as string,
StartYear = reader["startYear"] as long?,
EndYear = reader["endYear"] as long?,
IsCollection = reader[4] as string == "1"
};
}
public static Show FromReader(System.Data.SQLite.SQLiteDataReader reader)

View File

@ -16,6 +16,6 @@
"libraryPaths": [
"\\\\sdg\\video\\Anime"
],
"regex": ".*\\\\(?<ShowTitle>.+?) S(?<Season>\\d+)E(?<Episode>\\d+)",
"regex": ".*\\\\(?<Collection>.+?)?\\\\.*\\\\(?<ShowTitle>.+?) S(?<Season>\\d+)E(?<Episode>\\d+)",
"absoluteRegex": ".*\\\\(?<ShowTitle>.+?) (?<AbsoluteNumber>\\d+)"
}