From 21002fea4aba59724213812d53f07b2f4bbf1224 Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Thu, 5 Sep 2019 04:06:49 +0200 Subject: [PATCH] Creating the web app player view. --- Kyoo/ClientApp/src/app/app-routing.module.ts | 2 +- .../src/app/player/player.component.html | 59 ++++++++- .../src/app/player/player.component.scss | 116 ++++++++++++++++++ .../src/app/player/player.component.ts | 76 +++++++++++- Kyoo/ClientApp/src/models/watch-item.js | 3 + Kyoo/ClientApp/src/models/watch-item.js.map | 1 + Kyoo/ClientApp/src/models/watch-item.ts | 20 +++ .../LibraryManager/LibraryManager.cs | 2 +- Kyoo/Kyoo.csproj | 2 + Kyoo/Models/WatchItem.cs | 7 +- 10 files changed, 281 insertions(+), 7 deletions(-) create mode 100644 Kyoo/ClientApp/src/models/watch-item.js create mode 100644 Kyoo/ClientApp/src/models/watch-item.js.map create mode 100644 Kyoo/ClientApp/src/models/watch-item.ts diff --git a/Kyoo/ClientApp/src/app/app-routing.module.ts b/Kyoo/ClientApp/src/app/app-routing.module.ts index 67153c0e..723a2f82 100644 --- a/Kyoo/ClientApp/src/app/app-routing.module.ts +++ b/Kyoo/ClientApp/src/app/app-routing.module.ts @@ -14,7 +14,7 @@ const routes: Routes = [ { path: "browse", component: BrowseComponent, pathMatch: "full", resolve: { shows: LibraryResolverService } }, { path: "browse/:library-slug", component: BrowseComponent, resolve: { shows: LibraryResolverService } }, { path: "show/:show-slug", component: ShowDetailsComponent, resolve: { show: ShowResolverService } }, - { path: "watch/:item", component: PlayerComponent, resolve: { show: StreamResolverService } }, + { path: "watch/:item", component: PlayerComponent, resolve: { item: StreamResolverService }, runGuardsAndResolvers: "always" }, { path: "**", component: NotFoundComponent } ]; diff --git a/Kyoo/ClientApp/src/app/player/player.component.html b/Kyoo/ClientApp/src/app/player/player.component.html index 76f9f38f..36e26e12 100644 --- a/Kyoo/ClientApp/src/app/player/player.component.html +++ b/Kyoo/ClientApp/src/app/player/player.component.html @@ -1 +1,58 @@ -

player works!

+
+
+ +
+ +
+ +
{{this.item.showTitle}}
+
+ +
+
+ +
+
+

S{{this.item.seasonNumber}}:E{{this.item.episodeNumber}} - {{this.item.title}}

+ +
+
+ + + + +

00:00 / --:--

+
+
+ + + + + +
+
+
+
+
diff --git a/Kyoo/ClientApp/src/app/player/player.component.scss b/Kyoo/ClientApp/src/app/player/player.component.scss index e69de29b..796ca57d 100644 --- a/Kyoo/ClientApp/src/app/player/player.component.scss +++ b/Kyoo/ClientApp/src/app/player/player.component.scss @@ -0,0 +1,116 @@ +.player +{ + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; + background: #000; + + > video + { + width: 100%; + height: 100%; + object-fit: contain; + } +} + +.back +{ + position: fixed; + top: 0; + left: 0; + right: 0; + background: rgba(0, 0, 0, 0.6); + padding: .33%; + display: flex; + + > button + { + outline: none; + } + + > h5 + { + margin: 0; + margin-left: .5rem; + align-self: center; + } +} + +.controller +{ + position: fixed; + bottom: 0; + left: 0; + right: 0; + background: rgba(0, 0, 0, 0.6); + display: flex; + padding: 1%; + + .img + { + width: 15%; + position: relative; + height: auto; + + > img + { + width: 100%; + height: auto; + bottom: 0; + position: absolute; + } + } + + .content + { + width: 100%; + margin-left: 1rem; + display: flex; + flex-direction: column; + + > mat-progress-bar + { + width: 100%; + height: 2px; + margin-top: 1rem; + margin-bottom: 1rem; + } + + .buttons + { + display: flex; + flex-direction: row; + justify-content: space-between; + + > div + { + &.left + { + align-self: start; + display: flex; + + > p + { + margin: 0; + margin-left: 1rem; + align-self: center; + } + } + + &.right + { + align-self: end; + } + + > button + { + margin-left: .3rem; + margin-right: .3rem; + outline: none; + } + } + } + } +} diff --git a/Kyoo/ClientApp/src/app/player/player.component.ts b/Kyoo/ClientApp/src/app/player/player.component.ts index 99e54ecf..fa6a1871 100644 --- a/Kyoo/ClientApp/src/app/player/player.component.ts +++ b/Kyoo/ClientApp/src/app/player/player.component.ts @@ -1,21 +1,93 @@ import { Component, OnInit } from '@angular/core'; +import { WatchItem } from "../../models/watch-item"; +import { ActivatedRoute } from "@angular/router"; +import { DomSanitizer } from "@angular/platform-browser"; +import { Location } from "@angular/common"; @Component({ selector: 'app-player', templateUrl: './player.component.html', styleUrls: ['./player.component.scss'] }) -export class PlayerComponent implements OnInit { +export class PlayerComponent implements OnInit +{ + item: WatchItem; + video: string; - constructor() { } + playIcon: string = "pause"; //Icon used by the play btn. + fullscreenIcon: string = "fullscreen"; //Icon used by the fullscreen btn. + + private player: HTMLVideoElement; + + constructor(private route: ActivatedRoute, private location: Location) + { + this.video = this.route.snapshot.paramMap.get("item"); + } ngOnInit() { document.getElementById("nav").classList.add("d-none"); + this.item = this.route.snapshot.data.item; + console.log("Init"); + } + + ngAfterViewInit() + { + this.player = document.getElementById("player") as HTMLVideoElement; + this.player.controls = false; + + $('[data-toggle="tooltip"]').tooltip(); + + document.addEventListener("fullscreenchange", (event) => + { + if (document.fullscreenElement != null) + { + this.fullscreenIcon = "fullscreen_exit"; + $("#fullscreen").attr("data-original-title", "Exit fullscreen").tooltip("show"); + } + else + { + this.fullscreenIcon = "fullscreen"; + $("#fullscreen").attr("data-original-title", "Fullscreen").tooltip("show"); + } + }); } ngOnDestroy() { document.getElementById("nav").classList.remove("d-none"); } + + back() + { + this.location.back(); + } + + tooglePlayback() + { + let playBtn: HTMLElement = document.getElementById("play"); + + if (this.player.paused) + { + this.player.play(); + + this.playIcon = "pause" + $(playBtn).attr("data-original-title", "Pause").tooltip("show"); + } + else + { + this.player.pause(); + + this.playIcon = "play_arrow" + $(playBtn).attr("data-original-title", "Play").tooltip("show"); + } + } + + fullscreen() + { + if (document.fullscreenElement == null) + document.getElementById("root").requestFullscreen(); + else + document.exitFullscreen(); + } } diff --git a/Kyoo/ClientApp/src/models/watch-item.js b/Kyoo/ClientApp/src/models/watch-item.js new file mode 100644 index 00000000..d9a9328a --- /dev/null +++ b/Kyoo/ClientApp/src/models/watch-item.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=watch-item.js.map \ No newline at end of file diff --git a/Kyoo/ClientApp/src/models/watch-item.js.map b/Kyoo/ClientApp/src/models/watch-item.js.map new file mode 100644 index 00000000..3fe37e3b --- /dev/null +++ b/Kyoo/ClientApp/src/models/watch-item.js.map @@ -0,0 +1 @@ +{"version":3,"file":"watch-item.js","sourceRoot":"","sources":["watch-item.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/Kyoo/ClientApp/src/models/watch-item.ts b/Kyoo/ClientApp/src/models/watch-item.ts new file mode 100644 index 00000000..eaa07a55 --- /dev/null +++ b/Kyoo/ClientApp/src/models/watch-item.ts @@ -0,0 +1,20 @@ +export interface WatchItem +{ + showTitle: string; + showSlug: string; + seasonNumber: number; + episodeNumber: number; + title: string; + releaseDate; + audio: Stream[]; + subtitles: Stream[]; +} + +export interface Stream +{ + title: string; + language: string; + isDefault: boolean; + isForced: boolean; + format: string; +} diff --git a/Kyoo/InternalAPI/LibraryManager/LibraryManager.cs b/Kyoo/InternalAPI/LibraryManager/LibraryManager.cs index ea7fdd92..be3958fd 100644 --- a/Kyoo/InternalAPI/LibraryManager/LibraryManager.cs +++ b/Kyoo/InternalAPI/LibraryManager/LibraryManager.cs @@ -305,7 +305,7 @@ namespace Kyoo.InternalAPI public WatchItem GetWatchItem(string showSlug, long seasonNumber, long episodeNumber) { - string query = "SELECT episodes.id, shows.title as showTitle, seasonNumber, episodeNumber, episodes.title, releaseDate, episodes.path FROM episodes JOIN shows ON shows.id = episodes.showID WHERE shows.slug = $showSlug AND episodes.seasonNumber = $seasonNumber AND episodes.episodeNumber = $episodeNumber;"; + string query = "SELECT episodes.id, shows.title as showTitle, shows.slug as showSlug, seasonNumber, episodeNumber, episodes.title, releaseDate, episodes.path 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)) { diff --git a/Kyoo/Kyoo.csproj b/Kyoo/Kyoo.csproj index faff7919..0a2a60e7 100644 --- a/Kyoo/Kyoo.csproj +++ b/Kyoo/Kyoo.csproj @@ -31,6 +31,7 @@ + @@ -69,6 +70,7 @@ + diff --git a/Kyoo/Models/WatchItem.cs b/Kyoo/Models/WatchItem.cs index 73af1397..52e1dc48 100644 --- a/Kyoo/Models/WatchItem.cs +++ b/Kyoo/Models/WatchItem.cs @@ -11,6 +11,7 @@ namespace Kyoo.Models [JsonIgnore] public readonly long episodeID; public string ShowTitle; + public string ShowSlug; public long seasonNumber; public long episodeNumber; public string Title; @@ -23,10 +24,11 @@ namespace Kyoo.Models public WatchItem() { } - public WatchItem(long episodeID, string showTitle, long seasonNumber, long episodeNumber, string title, DateTime? releaseDate, string path) + public WatchItem(long episodeID, string showTitle, string showSlug, long seasonNumber, long episodeNumber, string title, DateTime? releaseDate, string path) { this.episodeID = episodeID; ShowTitle = showTitle; + ShowSlug = showSlug; this.seasonNumber = seasonNumber; this.episodeNumber = episodeNumber; Title = title; @@ -34,7 +36,7 @@ namespace Kyoo.Models Path = path; } - public WatchItem(long episodeID, string showTitle, long seasonNumber, long episodeNumber, string title, DateTime? releaseDate, string path, Stream[] audios, Stream[] subtitles) : this(episodeID, showTitle, seasonNumber, episodeNumber, title, releaseDate, path) + public WatchItem(long episodeID, string showTitle, string showSlug, long seasonNumber, long episodeNumber, string title, DateTime? releaseDate, string path, Stream[] audios, Stream[] subtitles) : this(episodeID, showTitle, showSlug, seasonNumber, episodeNumber, title, releaseDate, path) { this.audios = audios; this.subtitles = subtitles; @@ -44,6 +46,7 @@ namespace Kyoo.Models { return new WatchItem((long)reader["id"], reader["showTitle"] as string, + reader["showSlug"] as string, (long)reader["seasonNumber"], (long)reader["episodeNumber"], reader["title"] as string,