Solving a few bugs and styling more.

This commit is contained in:
Zoe Roux 2019-09-02 16:45:40 +02:00
parent 37fdcfa2a3
commit bc695560ae
13 changed files with 252 additions and 95 deletions

View File

@ -1,6 +1,6 @@
<div class="root">
<div class="episodes" id="episodes">
<div class="episode" *ngFor="let episode of this.episodes" id="{{episode.episodeNumber}}">
<div class="episode" *ngFor="let episode of this.episodes" id="el-{{episode.episodeNumber}}">
<div class="img" [style.background-image]="sanitize(episode.thumb)">
<button mat-icon-button id="playBtn"><i class="material-icons playIcon">play_circle_outline</i></button>
</div>
@ -8,6 +8,6 @@
<p class="overview">{{episode.overview}}</p>
</div>
</div>
<button mat-raised-button color="accent" class="scrollBtn d-none" id="leftBtn" (click)="scrollLeft()"><mat-icon>arrow_left</mat-icon></button>
<button mat-raised-button color="accent" class="scrollBtn" id="rightBtn" (click)="scrollRight()"><mat-icon>arrow_right</mat-icon></button>
<button mat-raised-button color="accent" class="scrollBtn d-none" id="el-leftBtn" (click)="scrollLeft()"><mat-icon>arrow_left</mat-icon></button>
<button mat-raised-button color="accent" class="scrollBtn" id="el-rightBtn" (click)="scrollRight()"><mat-icon>arrow_right</mat-icon></button>
</div>

View File

@ -24,10 +24,34 @@
min-width: 100%;
flex-shrink: 0;
flex-direction: row;
visibility: hidden;
&:hover
{
visibility: visible;
}
&::-webkit-scrollbar
{
height: 4px;
background: transparent;
}
&::-webkit-scrollbar-thumb
{
background-color: #999;
border-radius: 90px;
&:hover
{
background-color: rgb(134, 127, 127);
}
}
}
.episode
{
visibility: visible;
display: inline-block;
padding: .25rem;
flex-shrink: 0;
@ -82,15 +106,28 @@
{
font-weight: 600;
margin-bottom: 0;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
overflow: hidden;
}
.overview
{
font-weight: 300;
display: -webkit-box;
-webkit-line-clamp: 4;
-webkit-box-orient: vertical;
overflow: hidden;
}
&:hover
{
.img
{
outline: solid var(--accentColor);
}
.title
{
text-decoration: underline;
@ -121,14 +158,14 @@
bottom: 60%;
display: none;
&#leftBtn
&#el-leftBtn
{
left: 0;
padding-left: 10px;
padding-right: 2px;
}
&#rightBtn
&#el-rightBtn
{
right: 0;
padding-right: 10px;

View File

@ -29,25 +29,25 @@ export class EpisodesListComponent implements OnInit
let scroll: number = this.roundScroll(this.root.offsetWidth * 0.80);
this.root.scrollBy({ top: 0, left: -scroll, behavior: "smooth" });
document.getElementById("rightBtn").classList.remove("d-none");
document.getElementById("el-rightBtn").classList.remove("d-none");
if (this.root.scrollLeft - scroll <= 0)
document.getElementById("leftBtn").classList.add("d-none");
document.getElementById("el-leftBtn").classList.add("d-none");
}
scrollRight()
{
let scroll: number = this.roundScroll(this.root.offsetWidth * 0.80);
this.root.scrollBy({ top: 0, left: scroll, behavior: "smooth" });
document.getElementById("leftBtn").classList.remove("d-none");
document.getElementById("el-leftBtn").classList.remove("d-none");
if (this.root.scrollLeft + scroll >= this.root.scrollWidth - this.root.clientWidth)
document.getElementById("rightBtn").classList.add("d-none");
document.getElementById("el-rightBtn").classList.add("d-none");
}
roundScroll(offset: number): number
{
let episodeSize: number = document.getElementById("1").scrollWidth;
let episodeSize: number = document.getElementById("el-1").scrollWidth;
offset = Math.round(offset / episodeSize) * episodeSize;
if (offset == 0)

View File

@ -13,7 +13,7 @@
<button mat-mini-fab data-toggle="tooltip" data-placement="top" title="Play" class="mr-3">
<mat-icon>play_arrow</mat-icon>
</button>
<button mat-icon-button data-toggle="tooltip" data-placement="top" title="Trailer">
<button *ngIf="this.show.trailerUrl" mat-icon-button data-toggle="tooltip" data-placement="top" title="Trailer">
<mat-icon>local_movies</mat-icon>
</button>
<button mat-icon-button data-toggle="tooltip" data-placement="top" title="Download">
@ -63,29 +63,27 @@
<div class="container-fluid">
<div class="container-fluid mt-3">
<mat-form-field>
<mat-label>Season</mat-label>
<mat-select [(value)]="season">
<mat-select [(value)]="season" (selectionChange)="getEpisodes()">
<mat-option *ngFor="let season of this.show.seasons" [value]="season.seasonNumber">{{season.title}}</mat-option>
</mat-select>
</mat-form-field>
</div>
<app-episodes-list [episodes]="episodes"></app-episodes-list>
<!--<div class="container-fluid">
<div class="row">
<h3 class="col">Staff</h3>
<div class="scroll-row" style="position: relative;">
<button mat-raised-button color="accent" id="leftBtn"><mat-icon>arrow_left</mat-icon></button>
<div class="justify-content-left people-container">
<a class="people" *ngFor="let people of this.show.people" routerLink="/people/{{people.slug}}">
<img [style.background-image]="getPeopleIcon(people.slug)" />
<p class="title">{{people.name}}</p>
<p class="role">{{people.role}}</p>
</a>
</div>
<button mat-fab id="rightBtn"></button>
</div>
<div class="container-fluid mt-5">
<h3>Staff</h3>
</div>
<div class="scroll-row mb-5">
<div class="people-container" id="peopleScroll">
<a class="people" *ngFor="let people of this.show.people" routerLink="/people/{{people.slug}}">
<img [style.background-image]="getPeopleIcon(people.slug)" />
<p class="name">{{people.name}}</p>
<p class="role">{{people.role}}</p>
</a>
</div>
</div>-->
<button mat-raised-button color="accent" class="scrollBtn d-none" id="pl-leftBtn" (click)="scrollLeft()"><mat-icon>arrow_left</mat-icon></button>
<button mat-raised-button color="accent" class="scrollBtn" id="pl-rightBtn" (click)="scrollRight()"><mat-icon>arrow_right</mat-icon></button>
</div>

View File

@ -116,47 +116,110 @@ hr
height: inherit;
}
.scroll-row
{
position: relative;
&:hover
{
.scrollBtn
{
display: block;
}
}
}
.scrollBtn
{
padding: 0;
outline: none;
min-width: 0;
position: absolute;
top: 30%;
bottom: 40%;
display: none;
&#pl-leftBtn
{
left: 0;
padding-left: 10px;
padding-right: 2px;
}
&#pl-rightBtn
{
right: 0;
padding-right: 10px;
padding-left: 2px;
}
}
.people-container
{
display: flex;
overflow: hidden;
padding-left: 15px;
padding-right: 15px;
overflow-x: auto;
min-width: 100%;
flex-shrink: 0;
flex-direction: row;
visibility: hidden;
&:hover
{
visibility: visible;
}
&::-webkit-scrollbar
{
height: 4px;
background: transparent;
}
&::-webkit-scrollbar-thumb
{
background-color: #999;
border-radius: 90px;
&:hover
{
background-color: rgb(134, 127, 127);
}
}
}
.people
{
width: 15%;
margin: 1em;
visibility: visible;
margin: .25rem;
text-decoration: none;
color: inherit;
outline: none;
flex-shrink: 0;
flex-grow: 0;
/*@include media-breakpoint-up(md)
{
width: 25%;
}
width: 33%;
@include media-breakpoint-up(lg)
{
width: 20%;
}
@include media-breakpoint-up(xl)
{
width: 18%;
}*/
/*&:focus, &:hover
{
> img
@include media-breakpoint-up(sm)
{
outline: solid var(--accentColor);
width: 22%;
}
> .title
@include media-breakpoint-up(md)
{
text-decoration: underline;
width: 20%;
}
}*/
@include media-breakpoint-up(lg)
{
width: 15%;
}
@include media-breakpoint-up(xl)
{
width: 10%;
}
> img
{
width: 100%;
@ -184,6 +247,16 @@ hr
&:hover
{
cursor: pointer;
> img
{
outline: solid var(--accentColor);
}
.name
{
text-decoration: underline;
}
}
}

View File

@ -17,8 +17,9 @@ export class ShowDetailsComponent implements OnInit
episodes: Episode[] = null;
season: number;
private toolbar: HTMLElement
private backdrop: HTMLElement
private toolbar: HTMLElement;
private backdrop: HTMLElement;
private peopleScroll: HTMLElement;
constructor(private route: ActivatedRoute, private sanitizer: DomSanitizer, private http: HttpClient, private snackBar: MatSnackBar, private title: Title)
{
@ -34,10 +35,11 @@ export class ShowDetailsComponent implements OnInit
this.title.setTitle(this.show.title + " - Kyoo");
if (this.season == null || this.show.seasons.find(x => x.seasonNumber == this.season) == null)
this.season = this.show.seasons[0].seasonNumber;
this.season = 1;
this.toolbar = document.getElementById("toolbar");
this.backdrop = document.getElementById("backdrop");
this.peopleScroll = document.getElementById("peopleScroll");
window.addEventListener("scroll", this.scroll, true);
this.toolbar.setAttribute("style", `background-color: rgba(0, 0, 0, 0) !important`);
@ -48,6 +50,7 @@ export class ShowDetailsComponent implements OnInit
{
window.removeEventListener("scroll", this.scroll, true);
this.title.setTitle("Kyoo");
this.toolbar.setAttribute("style", `background-color: #000000 !important`);
}
scroll = () =>
@ -61,13 +64,13 @@ export class ShowDetailsComponent implements OnInit
if (this.show == null)
return;
if (this.show.seasons[this.season - 1].episodes != null)
this.episodes = this.show.seasons[this.season - 1].episodes;
if (this.show.seasons.find(x => x.seasonNumber == this.season).episodes != null)
this.episodes = this.show.seasons.find(x => x.seasonNumber == this.season).episodes;
this.http.get<Episode[]>("api/episodes/" + this.show.slug + "/season/" + this.season).subscribe((episodes: Episode[]) =>
{
this.show.seasons[this.season - 1].episodes = episodes;
this.show.seasons.find(x => x.seasonNumber == this.season).episodes = episodes;
this.episodes = episodes;
}, error =>
{
@ -77,6 +80,27 @@ export class ShowDetailsComponent implements OnInit
}
scrollLeft()
{
let scroll: number = this.peopleScroll.offsetWidth * 0.80;
this.peopleScroll.scrollBy({ top: 0, left: -scroll, behavior: "smooth" });
document.getElementById("pl-rightBtn").classList.remove("d-none");
if (this.peopleScroll.scrollLeft - scroll <= 0)
document.getElementById("pl-leftBtn").classList.add("d-none");
}
scrollRight()
{
let scroll: number = this.peopleScroll.offsetWidth * 0.80;
console.log("Scroll: " + scroll);
this.peopleScroll.scrollBy({ top: 0, left: scroll, behavior: "smooth" });
document.getElementById("pl-leftBtn").classList.remove("d-none");
if (this.peopleScroll.scrollLeft + scroll >= this.peopleScroll.scrollWidth - this.peopleScroll.clientWidth)
document.getElementById("pl-rightBtn").classList.add("d-none");
}
getPeopleIcon(slug: string)
{

View File

@ -8,6 +8,7 @@ export interface Show
//IEnumerable < > Aliases;
path: string;
overview: string;
trailer: string;
//IEnumerable < > Genres;
//Status ? Status;

View File

@ -7,7 +7,7 @@
$theme-colors: (
"primary": #0a1128,
"secondary": #080708,
"secondary": #000000,
"accentColor": #e23c00,
"textPrimary": #ffffff
);

View File

@ -1,52 +1,72 @@
using Kyoo.InternalAPI;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using System.IO;
namespace Kyoo.Controllers
{
public class ThumbnailController : Controller
{
private ILibraryManager libraryManager;
private readonly ILibraryManager libraryManager;
private readonly string peoplePath;
public ThumbnailController(ILibraryManager libraryManager)
public ThumbnailController(ILibraryManager libraryManager, IConfiguration config)
{
this.libraryManager = libraryManager;
peoplePath = config.GetValue<string>("peoplePath");
}
[HttpGet("thumb/{showSlug}")]
public IActionResult GetShowThumb(string showSlug)
{
string thumbPath = libraryManager.GetShowBySlug(showSlug)?.ImgPrimary;
if (thumbPath == null)
string path = libraryManager.GetShowBySlug(showSlug)?.Path;
if (path == null)
return NotFound();
return new PhysicalFileResult(thumbPath, "image/jpg");
string thumb = Path.Combine(path, "poster.jpg");
if (System.IO.File.Exists(thumb))
return new PhysicalFileResult(thumb, "image/jpg");
else
return NotFound();
}
[HttpGet("logo/{showSlug}")]
public IActionResult GetShowLogo(string showSlug)
{
string thumbPath = libraryManager.GetShowBySlug(showSlug)?.ImgLogo;
if (thumbPath == null)
string path = libraryManager.GetShowBySlug(showSlug)?.Path;
if (path == null)
return NotFound();
return new PhysicalFileResult(thumbPath, "image/png");
string thumb = Path.Combine(path, "logo.png");
if (System.IO.File.Exists(thumb))
return new PhysicalFileResult(thumb, "image/jpg");
else
return NotFound();
}
[HttpGet("backdrop/{showSlug}")]
public IActionResult GetShowBackground(string showSlug)
public IActionResult GetShowBackdrop(string showSlug)
{
string thumbPath = libraryManager.GetShowBySlug(showSlug)?.ImgBackdrop;
if (thumbPath == null)
string path = libraryManager.GetShowBySlug(showSlug)?.Path;
if (path == null)
return NotFound();
return new PhysicalFileResult(thumbPath, "image/jpg");
string thumb = Path.Combine(path, "backdrop.jpg");
if (System.IO.File.Exists(thumb))
return new PhysicalFileResult(thumb, "image/jpg");
else
return NotFound();
}
[HttpGet("peopleimg/{peopleSlug}")]
public IActionResult GetPeopleIcon(string peopleSlug)
{
string thumbPath = libraryManager.GetPeopleBySlug(peopleSlug)?.imgPrimary;
if (thumbPath == null)
string thumbPath = Path.Combine(peoplePath, peopleSlug + ".jpg");
if (!System.IO.File.Exists(thumbPath))
return NotFound();
return new PhysicalFileResult(thumbPath, "image/jpg");
@ -55,11 +75,16 @@ namespace Kyoo.Controllers
[HttpGet("thumb/{showSlug}/s{seasonNumber}/e{episodeNumber}")]
public IActionResult GetEpisodeThumb(string showSlug, long seasonNumber, long episodeNumber)
{
string thumbPath = libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber)?.ImgPrimary;
if (thumbPath == null)
string path = libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber)?.Path;
if (path == null)
return NotFound();
return new PhysicalFileResult(thumbPath, "image/jpg");
string thumb = Path.ChangeExtension(path, "jpg");
if (System.IO.File.Exists(thumb))
return new PhysicalFileResult(thumb, "image/jpg");
else
return NotFound();
}
}
}

View File

@ -31,7 +31,8 @@ namespace Kyoo.InternalAPI
title TEXT,
aliases TEXT,
path TEXT UNIQUE,
overview TEXT,
overview TEXT,
trailerUrl TEXT,
status TEXT,
startYear INTEGER,
endYear INTEGER,
@ -425,7 +426,7 @@ namespace Kyoo.InternalAPI
#region Check if items exists
public bool IsShowRegistered(string showPath)
{
string query = "SELECT 1 FROM shows WHERE path = $path;";
string query = "SELECT (id) FROM shows WHERE path = $path;";
using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection))
{
cmd.Parameters.AddWithValue("$path", showPath);
@ -436,7 +437,7 @@ namespace Kyoo.InternalAPI
public bool IsShowRegistered(string showPath, out long showID)
{
string query = "SELECT 1 FROM shows WHERE path = $path;";
string query = "SELECT (id) FROM shows WHERE path = $path;";
using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection))
{
cmd.Parameters.AddWithValue("$path", showPath);
@ -448,7 +449,7 @@ namespace Kyoo.InternalAPI
public bool IsSeasonRegistered(long showID, long seasonNumber)
{
string query = "SELECT 1 FROM seasons WHERE showID = $showID AND seasonNumber = $seasonNumber;";
string query = "SELECT (id) FROM seasons WHERE showID = $showID AND seasonNumber = $seasonNumber;";
using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection))
{
cmd.Parameters.AddWithValue("$showID", showID);
@ -460,7 +461,7 @@ namespace Kyoo.InternalAPI
public bool IsSeasonRegistered(long showID, long seasonNumber, out long seasonID)
{
string query = "SELECT 1 FROM seasons WHERE showID = $showID AND seasonNumber = $seasonNumber;";
string query = "SELECT (id) FROM seasons WHERE showID = $showID AND seasonNumber = $seasonNumber;";
using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection))
{
cmd.Parameters.AddWithValue("$showID", showID);
@ -473,7 +474,7 @@ namespace Kyoo.InternalAPI
public bool IsEpisodeRegistered(string episodePath)
{
string query = "SELECT 1 FROM episodes WHERE path = $path;";
string query = "SELECT (id) FROM episodes WHERE path = $path;";
using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection))
{
cmd.Parameters.AddWithValue("$path", episodePath);
@ -524,7 +525,7 @@ namespace Kyoo.InternalAPI
#region Write Into The Database
public long RegisterShow(Show show)
{
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);";
string query = "INSERT INTO shows (slug, title, aliases, path, overview, trailerUrl, startYear, endYear, imgPrimary, imgThumb, imgLogo, imgBackdrop, externalIDs) VALUES($slug, $title, $aliases, $path, $overview, $trailerUrl, $startYear, $endYear, $imgPrimary, $imgThumb, $imgLogo, $imgBackdrop, $externalIDs);";
using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection))
{
cmd.Parameters.AddWithValue("$slug", show.Slug);
@ -532,6 +533,7 @@ namespace Kyoo.InternalAPI
cmd.Parameters.AddWithValue("$aliases", show.GetAliases());
cmd.Parameters.AddWithValue("$path", show.Path);
cmd.Parameters.AddWithValue("$overview", show.Overview);
cmd.Parameters.AddWithValue("$trailerUrl", show.TrailerUrl);
cmd.Parameters.AddWithValue("$status", show.Status);
cmd.Parameters.AddWithValue("$startYear", show.StartYear);
cmd.Parameters.AddWithValue("$endYear", show.EndYear);

View File

@ -118,6 +118,7 @@ namespace Kyoo.InternalAPI.MetadataProvider
data.aliases,
showPath,
data.overview,
null, //trailer
null, //genres (no info with this request)
GetStatus(data.status),
GetYear(data.firstAired),
@ -176,6 +177,7 @@ namespace Kyoo.InternalAPI.MetadataProvider
data.aliases,
null, //Path
data.overview,
null, //Trailer
GetGenres(data.genre),
GetStatus(data.status),
GetYear(data.firstAired),
@ -281,10 +283,7 @@ namespace Kyoo.InternalAPI.MetadataProvider
if (token == null)
return null;
int page = (int)episodeNumber / 100 + 1;
int index = (int)episodeNumber % 100 - 1; //The -1 is for array binding
WebRequest request = WebRequest.Create("https://api.thetvdb.com/series/" + id + "/episodes?page=" + page);
WebRequest request = WebRequest.Create("https://api.thetvdb.com/series/" + id + "/episodes/query?airedSeason=" + seasonNumber + "&airedEpisode=" + episodeNumber);
request.Method = "GET";
request.Timeout = 12000;
request.ContentType = "application/json";
@ -304,7 +303,7 @@ namespace Kyoo.InternalAPI.MetadataProvider
response.Close();
dynamic data = JsonConvert.DeserializeObject(content);
dynamic episode = data.data[index];
dynamic episode = data.data[0];
DateTime dateTime = DateTime.ParseExact((string)episode.firstAired, "yyyy-MM-dd", CultureInfo.InvariantCulture);
return new Episode(seasonNumber, episodeNumber, (string)episode.episodeName, (string)episode.overview, dateTime, -1, "https://www.thetvdb.com/banners/" + episode.filename, string.Format("TvDB={0}|", episode.id));

View File

@ -34,7 +34,6 @@ namespace Kyoo.InternalAPI.ThumbnailsManager
}
}
}
show.ImgPrimary = localThumb;
if (show.ImgLogo != null)
{
@ -46,7 +45,6 @@ namespace Kyoo.InternalAPI.ThumbnailsManager
}
}
}
show.ImgLogo = localLogo;
if (show.ImgBackdrop != null)
{
@ -58,7 +56,6 @@ namespace Kyoo.InternalAPI.ThumbnailsManager
}
}
}
show.ImgBackdrop = localBackdrop;
return show;
}
@ -79,8 +76,6 @@ namespace Kyoo.InternalAPI.ThumbnailsManager
client.DownloadFileAsync(new Uri(people[i].imgPrimary), localThumb);
}
}
people[i].imgPrimary = localThumb;
}
return people;
@ -97,7 +92,6 @@ namespace Kyoo.InternalAPI.ThumbnailsManager
}
}
episode.ImgPrimary = localThumb;
return episode;
}
}

View File

@ -16,6 +16,7 @@ namespace Kyoo.Models
public string Overview;
public IEnumerable<Genre> Genres;
public Status? Status;
public string TrailerUrl;
public long? StartYear;
public long? EndYear;
@ -53,7 +54,7 @@ namespace Kyoo.Models
public Show() { }
public Show(long id, string slug, string title, IEnumerable<string> aliases, string path, string overview, IEnumerable<Genre> genres, Status? status, long? startYear, long? endYear, string externalIDs)
public Show(long id, string slug, string title, IEnumerable<string> aliases, string path, string overview, string trailerUrl, IEnumerable<Genre> genres, Status? status, long? startYear, long? endYear, string externalIDs)
{
this.id = id;
Slug = slug;
@ -61,6 +62,7 @@ namespace Kyoo.Models
Aliases = aliases;
Path = path;
Overview = overview;
TrailerUrl = trailerUrl;
Genres = genres;
Status = status;
StartYear = startYear;
@ -68,7 +70,7 @@ namespace Kyoo.Models
ExternalIDs = externalIDs;
}
public Show(long id, string slug, string title, IEnumerable<string> aliases, string path, string overview, 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<string> aliases, string path, string overview, string trailerUrl, Status? status, long? startYear, long? endYear, string imgPrimary, string imgThumb, string imgLogo, string imgBackdrop, string externalIDs)
{
this.id = id;
Slug = slug;
@ -76,6 +78,7 @@ namespace Kyoo.Models
Aliases = aliases;
Path = path;
Overview = overview;
TrailerUrl = trailerUrl;
Status = status;
StartYear = startYear;
EndYear = endYear;
@ -94,6 +97,7 @@ namespace Kyoo.Models
(reader["aliases"] as string)?.Split('|') ?? null,
reader["path"] as string,
reader["overview"] as string,
reader["trailerUrl"] as string,
reader["status"] as Status?,
reader["startYear"] as long?,
reader["endYear"] as long?,