mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
Implementing seasons inside the web app.
Implementing episodes inside the API.
This commit is contained in:
parent
3a378a0ac9
commit
d5358ad685
@ -11,7 +11,9 @@ 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 { MatIconModule } from '@angular/material/icon';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@ -28,7 +30,8 @@ import { MatIconModule } from '@angular/material/icon';
|
||||
MatSnackBarModule,
|
||||
MatProgressBarModule,
|
||||
MatButtonModule,
|
||||
MatIconModule
|
||||
MatIconModule,
|
||||
MatSelectModule
|
||||
],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent]
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
import { Show } from "../../models/show";
|
||||
|
||||
@Component({
|
||||
selector: 'app-browse',
|
||||
|
@ -3,6 +3,7 @@ import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/r
|
||||
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
|
||||
import { Observable, EMPTY } from 'rxjs';
|
||||
import { catchError } from 'rxjs/operators';
|
||||
import { Show } from "../../models/show";
|
||||
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
|
||||
|
@ -3,6 +3,7 @@ import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/r
|
||||
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
|
||||
import { Observable, EMPTY } from 'rxjs';
|
||||
import { catchError } from 'rxjs/operators';
|
||||
import { Show } from "../../models/show";
|
||||
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
|
||||
|
@ -13,6 +13,9 @@
|
||||
<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">
|
||||
<mat-icon>local_movies</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button data-toggle="tooltip" data-placement="top" title="Download">
|
||||
<mat-icon>cloud_download</mat-icon>
|
||||
</button>
|
||||
@ -58,6 +61,19 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="container-fluid">
|
||||
<mat-form-field>
|
||||
<mat-label>Season</mat-label>
|
||||
<mat-select [(value)]="season">
|
||||
<mat-option *ngFor="let season of this.show.seasons" [value]="season.seasonNumber">{{season.title}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<p>You selected: {{season}}</p>
|
||||
</div>
|
||||
|
||||
<!--<div class="container-fluid">
|
||||
<div class="row">
|
||||
<h3 class="col">Staff</h3>
|
||||
|
@ -29,12 +29,12 @@ a
|
||||
|
||||
@include media-breakpoint-up(lg)
|
||||
{
|
||||
margin-top: -20rem;
|
||||
margin-top: -19rem;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(xl)
|
||||
{
|
||||
margin-top: -24rem;
|
||||
margin-top: -23rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,11 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
import { Show } from "../../models/show";
|
||||
import { Episode } from "../../models/episode";
|
||||
import { HttpErrorResponse, HttpClient } from "@angular/common/http";
|
||||
import { catchError } from "rxjs/operators";
|
||||
import { MatSnackBar } from "@angular/material/snack-bar";
|
||||
|
||||
@Component({
|
||||
selector: 'app-show-details',
|
||||
@ -10,20 +15,32 @@ import { DomSanitizer } from '@angular/platform-browser';
|
||||
export class ShowDetailsComponent implements OnInit
|
||||
{
|
||||
show: Show;
|
||||
season;
|
||||
|
||||
private toolbar: HTMLElement
|
||||
private backdrop: HTMLElement
|
||||
|
||||
constructor(private route: ActivatedRoute, private sanitizer: DomSanitizer) { }
|
||||
constructor(private route: ActivatedRoute, private sanitizer: DomSanitizer, private http: HttpClient, private snackBar: MatSnackBar)
|
||||
{
|
||||
this.route.queryParams.subscribe(params =>
|
||||
{
|
||||
this.season = params["season"];
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit()
|
||||
{
|
||||
this.show = this.route.snapshot.data.show;
|
||||
|
||||
if (this.season == null || this.show.seasons.find(x => x.seasonNumber == this.season) == null)
|
||||
this.season = this.show.seasons[0].seasonNumber;
|
||||
|
||||
this.toolbar = document.getElementById("toolbar");
|
||||
this.backdrop = document.getElementById("backdrop");
|
||||
window.addEventListener("scroll", this.scroll, true);
|
||||
this.toolbar.setAttribute("style", `background-color: rgba(0, 0, 0, 0) !important`);
|
||||
|
||||
this.getEpisodes();
|
||||
}
|
||||
|
||||
ngOnDestroy()
|
||||
@ -37,6 +54,22 @@ export class ShowDetailsComponent implements OnInit
|
||||
this.toolbar.setAttribute("style", `background-color: rgba(0, 0, 0, ${opacity}) !important`);
|
||||
}
|
||||
|
||||
getEpisodes()
|
||||
{
|
||||
console.log("getting episodes");
|
||||
|
||||
this.http.get<Episode[]>("api/episodes/" + this.show.slug + "/season/" + this.season).subscribe((episodes: Episode[]) =>
|
||||
{
|
||||
console.log(episodes.length);
|
||||
}, error =>
|
||||
{
|
||||
console.log(error.status + " - " + error.message);
|
||||
this.snackBar.open("An unknow error occured while getting episodes.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
|
||||
});
|
||||
|
||||
console.log("Episodes got");
|
||||
}
|
||||
|
||||
|
||||
|
||||
getPeopleIcon(slug: string)
|
||||
|
9
Kyoo/ClientApp/src/models/episode.ts
Normal file
9
Kyoo/ClientApp/src/models/episode.ts
Normal file
@ -0,0 +1,9 @@
|
||||
export interface Episode
|
||||
{
|
||||
episodeNumber: number;
|
||||
title: string;
|
||||
overview: string;
|
||||
releaseDate;
|
||||
runtimeInMinutes: number;
|
||||
externalIDs: string;
|
||||
}
|
3
Kyoo/ClientApp/src/models/season.js
Normal file
3
Kyoo/ClientApp/src/models/season.js
Normal file
@ -0,0 +1,3 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//# sourceMappingURL=season.js.map
|
1
Kyoo/ClientApp/src/models/season.js.map
Normal file
1
Kyoo/ClientApp/src/models/season.js.map
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"season.js","sourceRoot":"","sources":["season.ts"],"names":[],"mappings":""}
|
9
Kyoo/ClientApp/src/models/season.ts
Normal file
9
Kyoo/ClientApp/src/models/season.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { Episode } from "./episode";
|
||||
|
||||
export interface Season
|
||||
{
|
||||
seasonNumber: number;
|
||||
title: string;
|
||||
overview: string;
|
||||
episode: Episode[];
|
||||
}
|
@ -1 +1,3 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//# sourceMappingURL=show.js.map
|
@ -1,4 +1,6 @@
|
||||
interface Show
|
||||
import { Season } from "./season";
|
||||
|
||||
export interface Show
|
||||
{
|
||||
id: number;
|
||||
slug: string;
|
||||
@ -18,4 +20,6 @@ interface Show
|
||||
imgBackdrop: string;
|
||||
|
||||
externalIDs: string;
|
||||
|
||||
seasons: Season[];
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ $font-family-base: "Roboto", Arial, sans-serif;
|
||||
// Define the default theme (same as the example above).
|
||||
$primary: (default: #0a1128);
|
||||
$accent: (default: #e23c00, lighter: #ff9149);
|
||||
$theme: mat-light-theme($primary, $accent);
|
||||
$theme: mat-dark-theme($primary, $accent);
|
||||
|
||||
// Include the default theme styles.
|
||||
@include angular-material-theme($theme);
|
||||
|
@ -2,6 +2,8 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.InternalAPI;
|
||||
using Kyoo.Models;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
@ -11,9 +13,33 @@ namespace Kyoo.Controllers
|
||||
[ApiController]
|
||||
public class EpisodesController : ControllerBase
|
||||
{
|
||||
public string Index()
|
||||
private readonly ILibraryManager libraryManager;
|
||||
|
||||
public EpisodesController(ILibraryManager libraryManager)
|
||||
{
|
||||
return "Episode Test";
|
||||
this.libraryManager = libraryManager;
|
||||
}
|
||||
|
||||
[HttpGet("{showSlug}/season/{seasonNumber}")]
|
||||
public ActionResult<IEnumerable<Episode>> GetEpisodesForSeason(string showSlug, long seasonNumber)
|
||||
{
|
||||
List<Episode> episodes = libraryManager.GetEpisodes(showSlug, seasonNumber);
|
||||
|
||||
if(episodes == null)
|
||||
return NotFound();
|
||||
|
||||
return episodes;
|
||||
}
|
||||
|
||||
[HttpGet("{showSlug}/season/{seasonNumber}/episode/{episodeNumber}")]
|
||||
public ActionResult<Episode> GetEpisode(string showSlug, long seasonNumber, long episodeNumber)
|
||||
{
|
||||
Episode episode = libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber);
|
||||
|
||||
if (episode == null)
|
||||
return NotFound();
|
||||
|
||||
return episode;
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,6 @@
|
||||
using Kyoo.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Kyoo.Controllers
|
||||
{
|
||||
|
@ -18,6 +18,8 @@ namespace Kyoo.InternalAPI
|
||||
IEnumerable<Library> GetLibraries();
|
||||
Show GetShowBySlug(string slug);
|
||||
Season GetSeason(string showSlug, long seasonNumber);
|
||||
List<Episode> GetEpisodes(string showSlug, long seasonNumber);
|
||||
Episode GetEpisode(string showSlug, long seasonNumber, long episodeNumber);
|
||||
People GetPeopleBySlug(string slug);
|
||||
Genre GetGenreBySlug(string slug);
|
||||
Studio GetStudioBySlug(string slug);
|
||||
|
@ -56,6 +56,7 @@ namespace Kyoo.InternalAPI
|
||||
id INTEGER PRIMARY KEY UNIQUE,
|
||||
showID INTEGER,
|
||||
seasonID INTEGER,
|
||||
seasonNumber INTEGER,
|
||||
episodeNumber INTEGER,
|
||||
path TEXT,
|
||||
title TEXT,
|
||||
@ -264,6 +265,43 @@ namespace Kyoo.InternalAPI
|
||||
}
|
||||
}
|
||||
|
||||
public List<Episode> GetEpisodes(string showSlug, long seasonNumber)
|
||||
{
|
||||
string query = "SELECT * FROM episodes JOIN shows ON shows.id = episodes.showID WHERE shows.slug = $showSlug AND episodes.seasonNumber = $seasonNumber;";
|
||||
|
||||
using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("$showSlug", showSlug);
|
||||
cmd.Parameters.AddWithValue("$seasonNumber", seasonNumber);
|
||||
SQLiteDataReader reader = cmd.ExecuteReader();
|
||||
|
||||
List<Episode> episodes = new List<Episode>();
|
||||
|
||||
while (reader.Read())
|
||||
episodes.Add(Episode.FromReader(reader));
|
||||
|
||||
return episodes;
|
||||
}
|
||||
}
|
||||
|
||||
public Episode GetEpisode(string showSlug, long seasonNumber, long episodeNumber)
|
||||
{
|
||||
string query = "SELECT * FROM episodes JOIN shows ON shows.id = episodes.showID WHERE shows.slug = $showSlug AND episodes.seasonNumber = $seasonNumber AND episodes.episodeNumber = $episodeNumber;";
|
||||
|
||||
using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("$showSlug", showSlug);
|
||||
cmd.Parameters.AddWithValue("$seasonNumber", seasonNumber);
|
||||
cmd.Parameters.AddWithValue("$episodeNumber", episodeNumber);
|
||||
SQLiteDataReader reader = cmd.ExecuteReader();
|
||||
|
||||
if (reader.Read())
|
||||
return Episode.FromReader(reader);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public List<People> GetPeople(long showID)
|
||||
{
|
||||
string query = "SELECT people.id, people.slug, people.name, people.imgPrimary, people.externalIDs, l.role, l.type FROM people JOIN peopleLinks l ON l.peopleID = people.id WHERE l.showID = $showID;";
|
||||
@ -550,11 +588,12 @@ namespace Kyoo.InternalAPI
|
||||
|
||||
public long RegisterEpisode(Episode episode)
|
||||
{
|
||||
string query = "INSERT INTO episodes (showID, seasonID, episodeNumber, path, title, overview, releaseDate, runtime, imgPrimary, externalIDs) VALUES($showID, $seasonID, $episodeNumber, $path, $title, $overview, $releaseDate, $runtime, $imgPrimary, $externalIDs);";
|
||||
string query = "INSERT INTO episodes (showID, seasonID, seasonNumber, episodeNumber, path, title, overview, releaseDate, runtime, imgPrimary, externalIDs) VALUES($showID, $seasonID, $seasonNumber,$episodeNumber, $path, $title, $overview, $releaseDate, $runtime, $imgPrimary, $externalIDs);";
|
||||
using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("$showID", episode.ShowID);
|
||||
cmd.Parameters.AddWithValue("$seasonID", episode.SeasonID);
|
||||
cmd.Parameters.AddWithValue("$seasonNUmber", episode.seasonNumber);
|
||||
cmd.Parameters.AddWithValue("$episodeNumber", episode.episodeNumber);
|
||||
cmd.Parameters.AddWithValue("$path", episode.Path);
|
||||
cmd.Parameters.AddWithValue("$title", episode.Title);
|
||||
|
@ -307,7 +307,7 @@ namespace Kyoo.InternalAPI.MetadataProvider
|
||||
dynamic episode = data.data[index];
|
||||
|
||||
DateTime dateTime = DateTime.ParseExact((string)episode.firstAired, "yyyy-MM-dd", CultureInfo.InvariantCulture);
|
||||
return new Episode(episodeNumber, (string)episode.episodeName, (string)episode.overview, dateTime, -1, "https://www.thetvdb.com/banners/" + episode.filename, string.Format("TvDB={0}|", episode.id));
|
||||
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));
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -9,11 +9,12 @@ namespace Kyoo.Models
|
||||
[JsonIgnore] public long ShowID;
|
||||
[JsonIgnore] public long SeasonID;
|
||||
|
||||
public long seasonNumber;
|
||||
public long episodeNumber;
|
||||
[JsonIgnore] public string Path;
|
||||
public string Title;
|
||||
public string Overview;
|
||||
public DateTime ReleaseDate;
|
||||
public DateTime? ReleaseDate;
|
||||
|
||||
public long Runtime; //This runtime variable should be in seconds (used by the video manager so we need precisions)
|
||||
|
||||
@ -31,11 +32,12 @@ namespace Kyoo.Models
|
||||
|
||||
public Episode() { }
|
||||
|
||||
public Episode(long episodeNumber, string title, string overview, DateTime releaseDate, long runtime, string imgPrimary, string externalIDs)
|
||||
public Episode(long seasonNumber, long episodeNumber, string title, string overview, DateTime? releaseDate, long runtime, string imgPrimary, string externalIDs)
|
||||
{
|
||||
id = -1;
|
||||
ShowID = -1;
|
||||
SeasonID = -1;
|
||||
this.seasonNumber = seasonNumber;
|
||||
this.episodeNumber = episodeNumber;
|
||||
Title = title;
|
||||
Overview = overview;
|
||||
@ -45,11 +47,12 @@ namespace Kyoo.Models
|
||||
ExternalIDs = externalIDs;
|
||||
}
|
||||
|
||||
public Episode(long id, long showID, long seasonID, long episodeNumber, string path, string title, string overview, DateTime releaseDate, long runtime, string imgPrimary, string externalIDs)
|
||||
public Episode(long id, long showID, long seasonID, long seasonNumber, long episodeNumber, string path, string title, string overview, DateTime? releaseDate, long runtime, string imgPrimary, string externalIDs)
|
||||
{
|
||||
this.id = id;
|
||||
ShowID = showID;
|
||||
SeasonID = seasonID;
|
||||
this.seasonNumber = seasonNumber;
|
||||
this.episodeNumber = episodeNumber;
|
||||
Path = path;
|
||||
Title = title;
|
||||
@ -59,5 +62,21 @@ namespace Kyoo.Models
|
||||
ImgPrimary = imgPrimary;
|
||||
ExternalIDs = externalIDs;
|
||||
}
|
||||
|
||||
public static Episode FromReader(System.Data.SQLite.SQLiteDataReader reader)
|
||||
{
|
||||
return new Episode((long)reader["id"],
|
||||
(long)reader["showID"],
|
||||
(long)reader["seasonID"],
|
||||
(long)reader["seasonNumber"],
|
||||
(long)reader["episodeNumber"],
|
||||
reader["path"] as string,
|
||||
reader["title"] as string,
|
||||
reader["overview"] as string,
|
||||
reader["releaseDate"] as DateTime?,
|
||||
(long)reader["runtime"],
|
||||
reader["imgPrimary"] as string,
|
||||
reader["externalIDs"] as string);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user