mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-09 03:04:20 -04:00
Creating the web app player view.
This commit is contained in:
parent
2462d3ad7f
commit
21002fea4a
@ -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 }
|
||||
];
|
||||
|
||||
|
@ -1 +1,58 @@
|
||||
<p>player works!</p>
|
||||
<div id="root">
|
||||
<div class="player">
|
||||
<video id="player" autoplay muted (click)="tooglePlayback()">
|
||||
<source src="/api/video/{{this.video}}" type="video/mp4" />
|
||||
</video>
|
||||
</div>
|
||||
|
||||
<div class="back">
|
||||
<button mat-icon-button data-toggle="tooltip" data-placement="bottom" title="Back" (click)="back()">
|
||||
<mat-icon>arrow_back</mat-icon>
|
||||
</button>
|
||||
<h5>{{this.item.showTitle}}</h5>
|
||||
</div>
|
||||
|
||||
<div class="controller container-fluid">
|
||||
<div class="img">
|
||||
<img src="thumb/{{this.item.showSlug}}" />
|
||||
</div>
|
||||
<div class="content">
|
||||
<h3>S{{this.item.seasonNumber}}:E{{this.item.episodeNumber}} - {{this.item.title}}</h3>
|
||||
<mat-progress-bar color="accent"></mat-progress-bar>
|
||||
<div class="buttons">
|
||||
<div class="left">
|
||||
<button *ngIf="this.item.episodeNumber != 1" mat-icon-button data-toggle="tooltip" data-placement="top" title="Previous" routerLink="/watch/{{this.item.showSlug}}-s{{this.item.seasonNumber}}e{{this.item.episodeNumber - 1}}">
|
||||
<mat-icon>skip_previous</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button data-toggle="tooltip" data-placement="top" title="Play" id="play" (click)="tooglePlayback()">
|
||||
<mat-icon>{{this.playIcon}}</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button data-toggle="tooltip" data-placement="top" title="Next" routerLink="/watch/{{this.item.showSlug}}-s{{this.item.seasonNumber}}e{{this.item.episodeNumber + 1}}">
|
||||
<mat-icon>skip_next</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button data-toggle="tooltip" data-placement="top" title="Volume">
|
||||
<mat-icon>volume_up</mat-icon>
|
||||
</button>
|
||||
<p>00:00 / --:--</p>
|
||||
</div>
|
||||
<div class="right">
|
||||
<button *ngIf="this.item.audios != null" mat-icon-button data-toggle="tooltip" data-placement="top" title="Select audio track">
|
||||
<mat-icon>music_note</mat-icon>
|
||||
</button>
|
||||
<button *ngIf="this.item.subtitles != null" mat-icon-button data-toggle="tooltip" data-placement="top" title="Select subtitle track">
|
||||
<mat-icon>closed_caption</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button data-toggle="tooltip" data-placement="top" title="Cast">
|
||||
<mat-icon>cast</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button data-toggle="tooltip" data-placement="top" title="Settings">
|
||||
<mat-icon>settings</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button data-toggle="tooltip" data-placement="top" title="Fullscreen" id="fullscreen" (click)="fullscreen()">
|
||||
<mat-icon>{{fullscreenIcon}}</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
3
Kyoo/ClientApp/src/models/watch-item.js
Normal file
3
Kyoo/ClientApp/src/models/watch-item.js
Normal file
@ -0,0 +1,3 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//# sourceMappingURL=watch-item.js.map
|
1
Kyoo/ClientApp/src/models/watch-item.js.map
Normal file
1
Kyoo/ClientApp/src/models/watch-item.js.map
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"watch-item.js","sourceRoot":"","sources":["watch-item.ts"],"names":[],"mappings":""}
|
20
Kyoo/ClientApp/src/models/watch-item.ts
Normal file
20
Kyoo/ClientApp/src/models/watch-item.ts
Normal file
@ -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;
|
||||
}
|
@ -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))
|
||||
{
|
||||
|
@ -31,6 +31,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="ClientApp\src\models\show.ts" />
|
||||
<None Remove="ClientApp\src\models\watch-item.ts" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -69,6 +70,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<TypeScriptCompile Include="ClientApp\src\models\show.ts" />
|
||||
<TypeScriptCompile Include="ClientApp\src\models\watch-item.ts" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="DebugEnsureNodeEnv" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('$(SpaRoot)node_modules') ">
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user