From 26edab07b032ee48600f4b059da724c2d4979ef8 Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Mon, 26 Aug 2019 02:21:52 +0200 Subject: [PATCH] Now using the genre table inside the database. Adding menus inside the show details component of the webapp. --- Kyoo/ClientApp/src/app/app.module.ts | 6 +- .../show-details/show-details.component.html | 52 ++++++-- .../show-details/show-details.component.scss | 112 ++++++++++++++++-- .../show-details/show-details.component.ts | 5 + Kyoo/ClientApp/src/styles.scss | 6 + Kyoo/Controllers/ThumbnailController.cs | 11 +- .../LibraryManager/ILibraryManager.cs | 5 + .../LibraryManager/LibraryManager.cs | 90 +++++++++++++- .../TheTvDB/ProviderTheTvDB.cs | 2 +- .../MetadataProvider/ProviderHelper.cs | 11 ++ Kyoo/Models/Genre.cs | 31 +++++ Kyoo/Models/People.cs | 18 +++ Kyoo/Models/Show.cs | 12 +- 13 files changed, 328 insertions(+), 33 deletions(-) create mode 100644 Kyoo/Models/Genre.cs diff --git a/Kyoo/ClientApp/src/app/app.module.ts b/Kyoo/ClientApp/src/app/app.module.ts index 2c889cad..a97a78e0 100644 --- a/Kyoo/ClientApp/src/app/app.module.ts +++ b/Kyoo/ClientApp/src/app/app.module.ts @@ -10,6 +10,8 @@ import { ShowDetailsComponent } from './show-details/show-details.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { MatSnackBarModule } from '@angular/material/snack-bar'; import { MatProgressBarModule } from '@angular/material/progress-bar'; +import { MatButtonModule } from '@angular/material/button'; +import { MatDividerModule } from '@angular/material/divider'; @NgModule({ declarations: [ @@ -24,7 +26,9 @@ import { MatProgressBarModule } from '@angular/material/progress-bar'; AppRoutingModule, BrowserAnimationsModule, MatSnackBarModule, - MatProgressBarModule + MatProgressBarModule, + MatButtonModule, + MatDividerModule ], providers: [], bootstrap: [AppComponent] diff --git a/Kyoo/ClientApp/src/app/show-details/show-details.component.html b/Kyoo/ClientApp/src/app/show-details/show-details.component.html index d2a4dcec..a7dcb2d0 100644 --- a/Kyoo/ClientApp/src/app/show-details/show-details.component.html +++ b/Kyoo/ClientApp/src/app/show-details/show-details.component.html @@ -1,12 +1,48 @@
- -
-

{{this.show.title}}

-
{{show.startYear}} - {{show.endYear}}
-

{{show.startYear}}

+
+ +
+
+

{{this.show.title}}

+
{{show.startYear}} - {{show.endYear}}
+

{{show.startYear}}

+
+ +
+ + + + +
+
+
+ +
+
+

{{this.show.overview}}

+
+ +
+

Genres

+ +
+
+ +
-
-

{{this.show.overview}}

-
diff --git a/Kyoo/ClientApp/src/app/show-details/show-details.component.scss b/Kyoo/ClientApp/src/app/show-details/show-details.component.scss index 470eef4f..006c2bdd 100644 --- a/Kyoo/ClientApp/src/app/show-details/show-details.component.scss +++ b/Kyoo/ClientApp/src/app/show-details/show-details.component.scss @@ -2,18 +2,112 @@ { width: 25%; background-color: #333333; - display: inline-block; -} - -.info -{ - display: inline-block; - padding-left: 2.5em; - padding-bottom: 7em; - vertical-align: bottom; } .main { + align-self: end; + padding-bottom: 5em; + padding-left: 2.5em; + .info + { + } + + .buttons + { + > button + { + outline: none; + margin: .3em; + } + } +} + +.people-container +{ + display: flex; + overflow: hidden; +} + +.people +{ + width: 15%; + margin: 1em; + text-decoration: none; + color: inherit; + outline: none; + flex-shrink: 0; + flex-grow: 0; + + /*@include media-breakpoint-up(md) + { + width: 25%; + } + + @include media-breakpoint-up(lg) + { + width: 20%; + } + + @include media-breakpoint-up(xl) + { + width: 18%; + }*/ + /*&:focus, &:hover + { + > img + { + outline: solid var(--accentColor); + } + + > .title + { + text-decoration: underline; + } + }*/ + > img + { + width: 100%; + height: 0; + padding-top: 147.0588%; + background-size: cover; + background-color: #333333; + } + + > p + { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + text-align: center; + margin-bottom: 0px; + + &.role + { + opacity: 0.8; + font-size: 0.8em; + } + } + + &:hover + { + cursor: pointer; + } +} + +#leftBtn +{ + position: absolute; + left: 0; + top: 33%; + outline: none; +} + +#rightBtn +{ + position: absolute; + right: 0; + top: 33%; + outline: none; } diff --git a/Kyoo/ClientApp/src/app/show-details/show-details.component.ts b/Kyoo/ClientApp/src/app/show-details/show-details.component.ts index 25c6ff6f..4f7656fc 100644 --- a/Kyoo/ClientApp/src/app/show-details/show-details.component.ts +++ b/Kyoo/ClientApp/src/app/show-details/show-details.component.ts @@ -19,6 +19,11 @@ export class ShowDetailsComponent implements OnInit document.body.style.backgroundImage = "url(/backdrop/" + this.show.slug + ")"; } + getPeopleIcon(slug: string) + { + return this.sanitizer.bypassSecurityTrustStyle("url(/peopleimg/" + slug + ")"); + } + getBackdrop() { return this.sanitizer.bypassSecurityTrustStyle("url(/backdrop/" + this.show.slug + ")"); diff --git a/Kyoo/ClientApp/src/styles.scss b/Kyoo/ClientApp/src/styles.scss index 70a74a0a..4f966b2d 100644 --- a/Kyoo/ClientApp/src/styles.scss +++ b/Kyoo/ClientApp/src/styles.scss @@ -36,3 +36,9 @@ $theme: mat-light-theme($primary, $accent); background-color: theme-color("accentColor"); color: theme-color("textPrimary"); } + +.scroll-row +{ + padding-left: 0; + padding-right: 0; +} diff --git a/Kyoo/Controllers/ThumbnailController.cs b/Kyoo/Controllers/ThumbnailController.cs index 8a48a6ed..36c738c9 100644 --- a/Kyoo/Controllers/ThumbnailController.cs +++ b/Kyoo/Controllers/ThumbnailController.cs @@ -1,6 +1,5 @@ using Kyoo.InternalAPI; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.FileProviders; namespace Kyoo.Controllers { @@ -32,5 +31,15 @@ namespace Kyoo.Controllers return new PhysicalFileResult(thumbPath, "image/jpg"); } + + [HttpGet("peopleimg/{peopleSlug}")] + public IActionResult GetPeopleIcon(string peopleSlug) + { + string thumbPath = libraryManager.GetPeopleBySlug(peopleSlug)?.imgPrimary; + if (thumbPath == null) + return NotFound(); + + return new PhysicalFileResult(thumbPath, "image/jpg"); + } } } diff --git a/Kyoo/InternalAPI/LibraryManager/ILibraryManager.cs b/Kyoo/InternalAPI/LibraryManager/ILibraryManager.cs index 14726f41..b21a77c7 100644 --- a/Kyoo/InternalAPI/LibraryManager/ILibraryManager.cs +++ b/Kyoo/InternalAPI/LibraryManager/ILibraryManager.cs @@ -9,10 +9,13 @@ namespace Kyoo.InternalAPI string GetShowExternalIDs(long showID); IEnumerable QueryShows(string selection); List GetPeople(long showID); + List GetGenreForShow(long showID); //Public read IEnumerable GetLibraries(); Show GetShowBySlug(string slug); + People GetPeopleBySlug(string slug); + Genre GetGenreBySlug(string slug); //Check if value exists bool IsShowRegistered(string showPath); @@ -26,6 +29,8 @@ namespace Kyoo.InternalAPI long RegisterSeason(Season season); long RegisterEpisode(Episode episode); + long GetOrCreateGenre(Genre genre); + void RegisterShowPeople(long showID, List actors); } } diff --git a/Kyoo/InternalAPI/LibraryManager/LibraryManager.cs b/Kyoo/InternalAPI/LibraryManager/LibraryManager.cs index 15ebd471..7f66efb1 100644 --- a/Kyoo/InternalAPI/LibraryManager/LibraryManager.cs +++ b/Kyoo/InternalAPI/LibraryManager/LibraryManager.cs @@ -32,7 +32,6 @@ namespace Kyoo.InternalAPI aliases TEXT, path TEXT UNIQUE, overview TEXT, - genres TEXT, status TEXT, startYear INTEGER, endYear INTEGER, @@ -224,7 +223,7 @@ namespace Kyoo.InternalAPI SQLiteDataReader reader = cmd.ExecuteReader(); if (reader.Read()) - return Show.FromReader(reader).SetPeople(this); + return Show.FromReader(reader).SetGenres(this).SetPeople(this); else return null; } @@ -242,11 +241,61 @@ namespace Kyoo.InternalAPI List people = new List(); while (reader.Read()) - people.Add(People.FromReader(reader)); + people.Add(People.FromFullReader(reader)); return people; } } + + public People GetPeopleBySlug(string slug) + { + string query = "SELECT * FROM people WHERE slug = $slug;"; + + using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection)) + { + cmd.Parameters.AddWithValue("$slug", slug); + SQLiteDataReader reader = cmd.ExecuteReader(); + + if (reader.Read()) + return People.FromReader(reader); + else + return null; + } + } + + public List GetGenreForShow(long showID) + { + string query = "SELECT genres.id, genres.slug, genres.name FROM genres JOIN genresLinks l ON l.genreID = genres.id WHERE l.showID = $showID;"; + + using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection)) + { + cmd.Parameters.AddWithValue("$showID", showID); + SQLiteDataReader reader = cmd.ExecuteReader(); + + List genres = new List(); + + while (reader.Read()) + genres.Add(Genre.FromReader(reader)); + + return genres; + } + } + + public Genre GetGenreBySlug(string slug) + { + string query = "SELECT * FROM genres WHERE slug = $slug;"; + + using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection)) + { + cmd.Parameters.AddWithValue("$slug", slug); + SQLiteDataReader reader = cmd.ExecuteReader(); + + if (reader.Read()) + return Genre.FromReader(reader); + else + return null; + } + } #endregion #region Check if items exists @@ -308,12 +357,31 @@ namespace Kyoo.InternalAPI return cmd.ExecuteScalar() != null; } } + + public long GetOrCreateGenre(Genre genre) + { + Genre existingGenre = GetGenreBySlug(genre.Slug); + + if (existingGenre != null) + return existingGenre.id; + + string query = "INSERT INTO genres (slug, name) VALUES($slug, $name);"; + using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection)) + { + cmd.Parameters.AddWithValue("$slug", genre.Slug); + cmd.Parameters.AddWithValue("$name", genre.Name); + cmd.ExecuteNonQuery(); + + cmd.CommandText = "SELECT LAST_INSERT_ROWID()"; + return (long)cmd.ExecuteScalar(); + } + } #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);"; + string query = "INSERT INTO shows (slug, title, aliases, path, overview, startYear, endYear, imgPrimary, imgThumb, imgLogo, imgBackdrop, externalIDs) VALUES($slug, $title, $aliases, $path, $overview, $startYear, $endYear, $imgPrimary, $imgThumb, $imgLogo, $imgBackdrop, $externalIDs);"; using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection)) { cmd.Parameters.AddWithValue("$slug", show.Slug); @@ -321,7 +389,6 @@ namespace Kyoo.InternalAPI cmd.Parameters.AddWithValue("$aliases", show.GetAliases()); cmd.Parameters.AddWithValue("$path", show.Path); cmd.Parameters.AddWithValue("$overview", show.Overview); - cmd.Parameters.AddWithValue("$genres", show.GetGenres()); cmd.Parameters.AddWithValue("$status", show.Status); cmd.Parameters.AddWithValue("$startYear", show.StartYear); cmd.Parameters.AddWithValue("$endYear", show.EndYear); @@ -333,7 +400,18 @@ namespace Kyoo.InternalAPI cmd.ExecuteNonQuery(); cmd.CommandText = "SELECT LAST_INSERT_ROWID()"; - return (long)cmd.ExecuteScalar(); + long showID = (long)cmd.ExecuteScalar(); + + cmd.CommandText = "INSERT INTO genresLinks (genreID, showID) VALUES($genreID, $showID);"; + foreach (Genre genre in show.Genres) + { + long genreID = GetOrCreateGenre(genre); + cmd.Parameters.AddWithValue("$genreID", genreID); + cmd.Parameters.AddWithValue("$showID", showID); + cmd.ExecuteNonQuery(); + } + + return showID; } } diff --git a/Kyoo/InternalAPI/MetadataProvider/Implementations/TheTvDB/ProviderTheTvDB.cs b/Kyoo/InternalAPI/MetadataProvider/Implementations/TheTvDB/ProviderTheTvDB.cs index 51a25479..e13ec082 100644 --- a/Kyoo/InternalAPI/MetadataProvider/Implementations/TheTvDB/ProviderTheTvDB.cs +++ b/Kyoo/InternalAPI/MetadataProvider/Implementations/TheTvDB/ProviderTheTvDB.cs @@ -176,7 +176,7 @@ namespace Kyoo.InternalAPI.MetadataProvider data.aliases, null, //Path data.overview, - data.genre, + GetGenres(data.genre), GetStatus(data.status), GetYear(data.firstAired), null, //endYear diff --git a/Kyoo/InternalAPI/MetadataProvider/ProviderHelper.cs b/Kyoo/InternalAPI/MetadataProvider/ProviderHelper.cs index 292bc2bd..1dc538b8 100644 --- a/Kyoo/InternalAPI/MetadataProvider/ProviderHelper.cs +++ b/Kyoo/InternalAPI/MetadataProvider/ProviderHelper.cs @@ -1,4 +1,5 @@ using Kyoo.Models; +using System.Collections.Generic; using System.Text; using System.Text.RegularExpressions; @@ -68,5 +69,15 @@ namespace Kyoo.InternalAPI.MetadataProvider break; } } + + public IEnumerable GetGenres(string[] input) + { + List genres = new List(); + + foreach (string genre in input) + genres.Add(new Genre(ToSlug(genre), genre)); + + return genres; + } } } diff --git a/Kyoo/Models/Genre.cs b/Kyoo/Models/Genre.cs new file mode 100644 index 00000000..93ff9d8f --- /dev/null +++ b/Kyoo/Models/Genre.cs @@ -0,0 +1,31 @@ +using Newtonsoft.Json; + +namespace Kyoo.Models +{ + public class Genre + { + [JsonIgnore] public readonly long id; + public string Slug; + public string Name; + + public Genre(string slug, string name) + { + Slug = slug; + Name = name; + } + + public Genre(long id, string slug, string name) + { + this.id = id; + Slug = slug; + Name = name; + } + + public static Genre FromReader(System.Data.SQLite.SQLiteDataReader reader) + { + return new Genre((long)reader["id"], + reader["slug"] as string, + reader["name"] as string); + } + } +} diff --git a/Kyoo/Models/People.cs b/Kyoo/Models/People.cs index f541d8a8..79003c23 100644 --- a/Kyoo/Models/People.cs +++ b/Kyoo/Models/People.cs @@ -13,6 +13,15 @@ namespace Kyoo.Models public string externalIDs; + public People(long id, string slug, string name, string imgPrimary, string externalIDs) + { + this.id = id; + this.slug = slug; + Name = name; + this.imgPrimary = imgPrimary; + this.externalIDs = externalIDs; + } + public People(long id, string slug, string name, string role, string type, string imgPrimary, string externalIDs) { this.id = id; @@ -25,6 +34,15 @@ namespace Kyoo.Models } public static People FromReader(System.Data.SQLite.SQLiteDataReader reader) + { + return new People((long)reader["id"], + reader["slug"] as string, + reader["name"] as string, + reader["imgPrimary"] as string, + reader["externalIDs"] as string); + } + + public static People FromFullReader(System.Data.SQLite.SQLiteDataReader reader) { return new People((long)reader["id"], reader["slug"] as string, diff --git a/Kyoo/Models/Show.cs b/Kyoo/Models/Show.cs index 9044f4c2..1d2f2c70 100644 --- a/Kyoo/Models/Show.cs +++ b/Kyoo/Models/Show.cs @@ -14,7 +14,7 @@ namespace Kyoo.Models public IEnumerable Aliases; [JsonIgnore] public string Path; public string Overview; - public IEnumerable Genres; + public IEnumerable Genres; public Status? Status; public long? StartYear; @@ -49,7 +49,7 @@ namespace Kyoo.Models public Show() { } - public Show(long id, string slug, string title, IEnumerable aliases, string path, string overview, IEnumerable genres, Status? status, long? startYear, long? endYear, string externalIDs) + public Show(long id, string slug, string title, IEnumerable aliases, string path, string overview, IEnumerable genres, Status? status, long? startYear, long? endYear, string externalIDs) { this.id = id; Slug = slug; @@ -64,7 +64,7 @@ namespace Kyoo.Models ExternalIDs = externalIDs; } - public Show(long id, string slug, string title, IEnumerable aliases, string path, string overview, IEnumerable genres, Status? status, long? startYear, long? endYear, string imgPrimary, string imgThumb, string imgLogo, string imgBackdrop, string externalIDs) + public Show(long id, string slug, string title, IEnumerable aliases, string path, string overview, Status? status, long? startYear, long? endYear, string imgPrimary, string imgThumb, string imgLogo, string imgBackdrop, string externalIDs) { this.id = id; Slug = slug; @@ -72,7 +72,6 @@ namespace Kyoo.Models Aliases = aliases; Path = path; Overview = overview; - Genres = genres; Status = status; StartYear = startYear; EndYear = endYear; @@ -91,7 +90,6 @@ namespace Kyoo.Models (reader["aliases"] as string)?.Split('|') ?? null, reader["path"] as string, reader["overview"] as string, - (reader["genres"] as string)?.Split('|') ?? null, reader["status"] as Status?, reader["startYear"] as long?, reader["endYear"] as long?, @@ -109,9 +107,9 @@ namespace Kyoo.Models return this; } - public Show SetPeople(People[] people) + public Show SetGenres(ILibraryManager manager) { - this.people = people; + Genres = manager.GetGenreForShow(id); return this; }