mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-09 03:04:20 -04:00
Creating web-app player seek-bar and volume controls.
This commit is contained in:
parent
44c8451735
commit
3ef233a1b6
@ -19,7 +19,15 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<h3>S{{this.item.seasonNumber}}:E{{this.item.episodeNumber}} - {{this.item.title}}</h3>
|
<h3>S{{this.item.seasonNumber}}:E{{this.item.episodeNumber}} - {{this.item.title}}</h3>
|
||||||
<mat-progress-bar color="accent"></mat-progress-bar>
|
|
||||||
|
<div class="seek-bar">
|
||||||
|
<div id="progress-bar">
|
||||||
|
<div id="buffered"></div>
|
||||||
|
<div id="progress"></div>
|
||||||
|
</div>
|
||||||
|
<div id="thumb"><div></div></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<button *ngIf="this.item.previousEpisode" mat-icon-button data-toggle="tooltip" data-placement="top" title="Previous" routerLink="/watch/{{this.item.previousEpisode}}">
|
<button *ngIf="this.item.previousEpisode" mat-icon-button data-toggle="tooltip" data-placement="top" title="Previous" routerLink="/watch/{{this.item.previousEpisode}}">
|
||||||
@ -41,12 +49,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
<button mat-icon-button data-toggle="tooltip" data-placement="top" title="Volume">
|
<div id="volume">
|
||||||
<mat-icon>volume_up</mat-icon>
|
<button mat-icon-button data-toggle="tooltip" data-placement="top" title="Volume">
|
||||||
|
<mat-icon>{{this.volumeIcon}}</mat-icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
<mat-slider></mat-slider>
|
<mat-slider [value]="volume" (input)="changeVolume($event)"></mat-slider>
|
||||||
</button>
|
</div>
|
||||||
<p>{{this.minutes | number: '2.0-0'}}:{{this.seconds | number: '2.0-0'}} / --:--</p>
|
<p *ngIf="this.maxHours; else elseBlock">{{this.hours | number: '2.0-0'}}:{{this.minutes | number: '2.0-0'}}:{{this.seconds | number: '2.0-0'}} / {{this.maxHours | number: '2.0-0'}}:{{this.maxMinutes | number: '2.0-0'}}:{{this.maxSeconds | number: '2.0-0'}}</p>
|
||||||
|
<ng-template #elseBlock><p>{{this.minutes | number: '2.0-0'}}:{{this.seconds | number: '2.0-0'}} / {{this.maxMinutes | number: '2.0-0'}}:{{this.maxSeconds | number: '2.0-0'}}</p></ng-template>
|
||||||
</div>
|
</div>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<button *ngIf="this.item.audios != null" mat-icon-button data-toggle="tooltip" data-placement="top" title="Select audio track">
|
<button *ngIf="this.item.audios != null" mat-icon-button data-toggle="tooltip" data-placement="top" title="Select audio track">
|
||||||
|
@ -70,14 +70,6 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
> mat-progress-bar
|
|
||||||
{
|
|
||||||
width: 100%;
|
|
||||||
height: 2px;
|
|
||||||
margin-top: 1rem;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttons
|
.buttons
|
||||||
{
|
{
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -115,6 +107,79 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.seek-bar
|
||||||
|
{
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
padding-top: 1rem;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
#progress-bar
|
||||||
|
{
|
||||||
|
width: 100%;
|
||||||
|
height: 4px;
|
||||||
|
position: relative;
|
||||||
|
background-color: rgba(255, 255, 255, .2);
|
||||||
|
transform: scaleY(.6);
|
||||||
|
|
||||||
|
#progress
|
||||||
|
{
|
||||||
|
width: 0;
|
||||||
|
height: 100%;
|
||||||
|
background-color: var(--accentColor);
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#buffered
|
||||||
|
{
|
||||||
|
width: 0;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(255, 255, 255, .5);
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#thumb
|
||||||
|
{
|
||||||
|
width: 100%;
|
||||||
|
height: 12px;
|
||||||
|
position: absolute;
|
||||||
|
left: -6px;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
margin: auto;
|
||||||
|
opacity: 0;
|
||||||
|
|
||||||
|
> div
|
||||||
|
{
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
background-color: var(--accentColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
{
|
||||||
|
#progress-bar
|
||||||
|
{
|
||||||
|
transform: scaleY(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#thumb
|
||||||
|
{
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#nextBtn
|
#nextBtn
|
||||||
{
|
{
|
||||||
@ -173,3 +238,38 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#volume
|
||||||
|
{
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
> button
|
||||||
|
{
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover, &:focus-within
|
||||||
|
{
|
||||||
|
> mat-slider
|
||||||
|
{
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> mat-slider
|
||||||
|
{
|
||||||
|
width: 0px;
|
||||||
|
min-width: 0px;
|
||||||
|
padding: 0;
|
||||||
|
height: 40px;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: width .2s cubic-bezier(0.4,0, 1, 1);
|
||||||
|
|
||||||
|
&::ng-deep > div
|
||||||
|
{
|
||||||
|
top: 19px;
|
||||||
|
left: 10px;
|
||||||
|
right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||||
import { WatchItem } from "../../models/watch-item";
|
import { WatchItem } from "../../models/watch-item";
|
||||||
import { ActivatedRoute } from "@angular/router";
|
import { ActivatedRoute } from "@angular/router";
|
||||||
import { DomSanitizer } from "@angular/platform-browser";
|
import { DomSanitizer, Title } from "@angular/platform-browser";
|
||||||
import { Location } from "@angular/common";
|
import { Location } from "@angular/common";
|
||||||
|
import { MatSliderChange } from "@angular/material/slider";
|
||||||
|
import { HtmlAstPath } from "@angular/compiler";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-player',
|
selector: 'app-player',
|
||||||
@ -13,16 +15,26 @@ export class PlayerComponent implements OnInit
|
|||||||
{
|
{
|
||||||
item: WatchItem;
|
item: WatchItem;
|
||||||
|
|
||||||
|
volume: number = 100;
|
||||||
|
|
||||||
hours: number;
|
hours: number;
|
||||||
minutes: number;
|
minutes: number;
|
||||||
seconds: number;
|
seconds: number;
|
||||||
|
|
||||||
|
maxHours: number;
|
||||||
|
maxMinutes: number;
|
||||||
|
maxSeconds: number;
|
||||||
|
|
||||||
playIcon: string = "pause"; //Icon used by the play btn.
|
playIcon: string = "pause"; //Icon used by the play btn.
|
||||||
|
volumeIcon: string = "volume_up"; //Icon used by the volume btn.
|
||||||
fullscreenIcon: string = "fullscreen"; //Icon used by the fullscreen btn.
|
fullscreenIcon: string = "fullscreen"; //Icon used by the fullscreen btn.
|
||||||
|
|
||||||
private player: HTMLVideoElement;
|
private player: HTMLVideoElement;
|
||||||
|
private thumb: HTMLElement;
|
||||||
|
private progress: HTMLElement;
|
||||||
|
private buffered: HTMLElement;
|
||||||
|
|
||||||
constructor(private route: ActivatedRoute, private sanitizer: DomSanitizer, private location: Location) { }
|
constructor(private route: ActivatedRoute, private sanitizer: DomSanitizer, private location: Location, private title: Title) { }
|
||||||
|
|
||||||
ngOnInit()
|
ngOnInit()
|
||||||
{
|
{
|
||||||
@ -30,20 +42,32 @@ export class PlayerComponent implements OnInit
|
|||||||
this.route.data.subscribe((data) =>
|
this.route.data.subscribe((data) =>
|
||||||
{
|
{
|
||||||
this.item = data.item;
|
this.item = data.item;
|
||||||
|
this.item.duration = 20 * 60;
|
||||||
|
|
||||||
if (this.player)
|
if (this.player)
|
||||||
{
|
{
|
||||||
this.player.load();
|
this.player.load();
|
||||||
this.initPlayBtn();
|
this.initPlayBtn();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.maxSeconds = Math.round(this.item.duration % 60);
|
||||||
|
this.maxMinutes = Math.round(this.item.duration / 60 % 60);
|
||||||
|
this.maxHours = Math.round(this.item.duration / 3600);
|
||||||
|
|
||||||
|
this.title.setTitle(this.item.showTitle + " S" + this.item.seasonNumber + ":E" + this.item.episodeNumber + " - Kyoo");
|
||||||
});
|
});
|
||||||
console.log("Init");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit()
|
ngAfterViewInit()
|
||||||
{
|
{
|
||||||
this.player = document.getElementById("player") as HTMLVideoElement;
|
this.player = document.getElementById("player") as HTMLVideoElement;
|
||||||
|
this.thumb = document.getElementById("thumb") as HTMLElement;
|
||||||
|
this.progress = document.getElementById("progress") as HTMLElement;
|
||||||
|
this.buffered = document.getElementById("buffered") as HTMLElement;
|
||||||
this.player.controls = false;
|
this.player.controls = false;
|
||||||
|
//console.log(this.player.volume * 100);
|
||||||
|
//this.volume = this.player.volume * 100;
|
||||||
|
//this.changeVolumeBtn();
|
||||||
|
|
||||||
this.player.onplay = () =>
|
this.player.onplay = () =>
|
||||||
{
|
{
|
||||||
@ -58,8 +82,18 @@ export class PlayerComponent implements OnInit
|
|||||||
|
|
||||||
this.player.ontimeupdate = () =>
|
this.player.ontimeupdate = () =>
|
||||||
{
|
{
|
||||||
|
this.hours = Math.round(this.player.currentTime / 60 % 60);
|
||||||
this.seconds = Math.round(this.player.currentTime % 60);
|
this.seconds = Math.round(this.player.currentTime % 60);
|
||||||
this.minutes = Math.round(this.player.currentTime / 60);
|
this.minutes = Math.round(this.player.currentTime / 60);
|
||||||
|
|
||||||
|
this.thumb.style.transform = "translateX(" + (this.player.currentTime / this.item.duration * 100) + "%)";
|
||||||
|
this.progress.style.width = (this.player.currentTime / this.item.duration * 100) + "%";
|
||||||
|
};
|
||||||
|
|
||||||
|
this.player.onprogress = () =>
|
||||||
|
{
|
||||||
|
if (this.player.buffered.length > 0)
|
||||||
|
this.buffered.style.width = (this.player.buffered.end(this.player.buffered.length - 1) / this.item.duration * 100) + "%";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -83,6 +117,7 @@ export class PlayerComponent implements OnInit
|
|||||||
ngOnDestroy()
|
ngOnDestroy()
|
||||||
{
|
{
|
||||||
document.getElementById("nav").classList.remove("d-none");
|
document.getElementById("nav").classList.remove("d-none");
|
||||||
|
this.title.setTitle("Kyoo");
|
||||||
}
|
}
|
||||||
|
|
||||||
back()
|
back()
|
||||||
@ -112,6 +147,26 @@ export class PlayerComponent implements OnInit
|
|||||||
document.exitFullscreen();
|
document.exitFullscreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
changeVolume(event: MatSliderChange)
|
||||||
|
{
|
||||||
|
this.player.volume = event.value / 100;
|
||||||
|
this.volume = event.value;
|
||||||
|
|
||||||
|
this.changeVolumeBtn();
|
||||||
|
}
|
||||||
|
|
||||||
|
changeVolumeBtn()
|
||||||
|
{
|
||||||
|
if (this.volume == 0)
|
||||||
|
this.volumeIcon = "volume_off";
|
||||||
|
else if (this.volume < 25)
|
||||||
|
this.volumeIcon = "volume_mute";
|
||||||
|
else if (this.volume < 65)
|
||||||
|
this.volumeIcon = "volume_down";
|
||||||
|
else
|
||||||
|
this.volumeIcon = "volume_up";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
getThumb(url: string)
|
getThumb(url: string)
|
||||||
{
|
{
|
||||||
|
@ -9,6 +9,7 @@ export interface WatchItem
|
|||||||
video: string;
|
video: string;
|
||||||
title: string;
|
title: string;
|
||||||
link: string;
|
link: string;
|
||||||
|
duration: number;
|
||||||
releaseDate;
|
releaseDate;
|
||||||
|
|
||||||
previousEpisode: string;
|
previousEpisode: string;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user