Replacing spaces with tabs

This commit is contained in:
Zoe Roux 2020-02-11 23:17:23 +01:00
parent 03bf97dab6
commit a19bf9bce8
51 changed files with 1693 additions and 1697 deletions

View File

@ -1,35 +1,35 @@
<header id="nav" style="height: 68px;">
<div class="fixed-top">
<nav id="toolbar" class="navbar navbar-dark bg-secondary">
<a class="navbar-brand nav-item ml-3" routerLink="/">
Kyoo
</a>
<div class="fixed-top">
<nav id="toolbar" class="navbar navbar-dark bg-secondary">
<a class="navbar-brand nav-item ml-3" routerLink="/">
Kyoo
</a>
<ul class="navbar-nav flex-row">
<li class="nav-item">
<a class="nav-link" routerLink="/browse" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }">All</a>
</li>
<li class="nav-item" *ngFor="let library of this.libraries">
<a class="nav-link" routerLink="/browse/{{library.slug}}" routerLinkActive="active">{{library.name}}</a>
</li>
</ul>
<ul class="navbar-nav flex-row">
<li class="nav-item">
<a class="nav-link" routerLink="/browse" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }">All</a>
</li>
<li class="nav-item" *ngFor="let library of this.libraries">
<a class="nav-link" routerLink="/browse/{{library.slug}}" routerLinkActive="active">{{library.name}}</a>
</li>
</ul>
<ul class="navbar-nav flex-row ml-auto">
<li class="nav-item icon searchbar">
<input placeholder="Search" id="search" type="search" (input)="onUpdateValue($event)"/>
<mat-icon matTooltipPosition="below" matTooltip="Search" (click)="openSearch()">search</mat-icon>
</li>
<li class="nav-item">
<a class="icon" routerLink="/login" routerLinkActive="active" matTooltipPosition="below" matTooltip="Login">
<mat-icon>account_circle</mat-icon>
</a>
</li>
</ul>
</nav>
<mat-progress-bar *ngIf="this.isLoading" color="accent" mode="indeterminate"> </mat-progress-bar>
</div>
<ul class="navbar-nav flex-row ml-auto">
<li class="nav-item icon searchbar">
<input placeholder="Search" id="search" type="search" (input)="onUpdateValue($event)"/>
<mat-icon matTooltipPosition="below" matTooltip="Search" (click)="openSearch()">search</mat-icon>
</li>
<li class="nav-item">
<a class="icon" routerLink="/login" routerLinkActive="active" matTooltipPosition="below" matTooltip="Login">
<mat-icon>account_circle</mat-icon>
</a>
</li>
</ul>
</nav>
<mat-progress-bar *ngIf="this.isLoading" color="accent" mode="indeterminate"> </mat-progress-bar>
</div>
</header>
<main >
<router-outlet></router-outlet>
<router-outlet></router-outlet>
</main>

View File

@ -4,83 +4,83 @@
.navbar
{
justify-content: left;
justify-content: left;
}
.nav-item
{
outline: none;
outline: none;
> a
{
outline: none;
color: inherit;
}
> a
{
outline: none;
color: inherit;
}
}
.nav-link
{
padding: 12px;
color: rgba(255, 255, 255, 0.7) !important;
padding: 12px;
color: rgba(255, 255, 255, 0.7) !important;
&:host-context(.hoverEnabled) &:hover
{
color: white !important;
}
&:host-context(.hoverEnabled) &:hover
{
color: white !important;
}
&.active
{
color: var(--accentColor) !important;
}
&.active
{
color: var(--accentColor) !important;
}
}
.navbar-brand:hover
{
color: var(--accentColor);
color: var(--accentColor);
}
.searchbar
{
border-radius: 30px;
border-radius: 30px;
> input
{
background: none !important;
color: white;
outline: none;
border: none;
border-bottom: 1px solid #cfcfcf;
width: 0;
padding: 0;
transition: width 0.4s ease-in-out;
> input
{
background: none !important;
color: white;
outline: none;
border: none;
border-bottom: 1px solid #cfcfcf;
width: 0;
padding: 0;
transition: width 0.4s ease-in-out;
&:focus, &.searching
{
width: 12rem;
&:focus, &.searching
{
width: 12rem;
@include media-breakpoint-up(sm)
{
width: 20rem;
}
}
}
@include media-breakpoint-up(sm)
{
width: 20rem;
}
}
}
}
input::-webkit-search-cancel-button
{
display: none;
display: none;
}
.icon
{
padding: 8px;
display: inline-block;
opacity: 0.7;
padding: 8px;
display: inline-block;
opacity: 0.7;
&:host-context(.hoverEnabled) &:hover
{
cursor: pointer;
opacity: 1;
}
&:host-context(.hoverEnabled) &:hover
{
cursor: pointer;
opacity: 1;
}
}

View File

@ -26,35 +26,35 @@ import { ShowsListComponent } from './shows-list/shows-list.component';
@NgModule({
declarations: [
AppComponent,
NotFoundComponent,
BrowseComponent,
ShowDetailsComponent,
EpisodesListComponent,
PlayerComponent,
CollectionComponent,
SearchComponent,
PeopleListComponent,
ShowsListComponent
],
imports: [
BrowserModule,
HttpClientModule,
AppRoutingModule,
BrowserAnimationsModule,
MatSnackBarModule,
MatProgressBarModule,
MatButtonModule,
MatIconModule,
MatSelectModule,
MatMenuModule,
MatSliderModule,
MatTooltipModule,
declarations: [
AppComponent,
NotFoundComponent,
BrowseComponent,
ShowDetailsComponent,
EpisodesListComponent,
PlayerComponent,
CollectionComponent,
SearchComponent,
PeopleListComponent,
ShowsListComponent
],
imports: [
BrowserModule,
HttpClientModule,
AppRoutingModule,
BrowserAnimationsModule,
MatSnackBarModule,
MatProgressBarModule,
MatButtonModule,
MatIconModule,
MatSelectModule,
MatMenuModule,
MatSliderModule,
MatTooltipModule,
MatRippleModule,
MatCardModule
],
providers: [],
bootstrap: [AppComponent]
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

View File

@ -1,30 +1,30 @@
<div class="container-fluid justify-content-center">
<button mat-icon-button matTooltipPosition="below" matTooltip="Filter">
<mat-icon>filter_list</mat-icon>
</button>
<button mat-button matTooltipPosition="below" matTooltip="Sort" [matMenuTriggerFor]="sortMenu">
<mat-icon>sort</mat-icon> Sort by {{this.sortType}} <i *ngIf="this.sortUp" class="material-icons arrow">arrow_upward</i><i *ngIf="!this.sortUp" class="material-icons arrow">arrow_downward</i>
</button>
<button mat-icon-button matTooltipPosition="below" matTooltip="Filter">
<mat-icon>filter_list</mat-icon>
</button>
<button mat-button matTooltipPosition="below" matTooltip="Sort" [matMenuTriggerFor]="sortMenu">
<mat-icon>sort</mat-icon> Sort by {{this.sortType}} <i *ngIf="this.sortUp" class="material-icons arrow">arrow_upward</i><i *ngIf="!this.sortUp" class="material-icons arrow">arrow_downward</i>
</button>
</div>
<mat-menu #sortMenu="matMenu">
<div *ngFor="let type of this.sortTypes">
<button *ngIf="type != this.sortType; else elseBlock;" mat-menu-item (click)="sort(type, true)">
Sort by {{type}}
</button>
<ng-template #elseBlock>
<button mat-menu-item (click)="sort(type, !this.sortUp)">
Sort by {{type}} <i *ngIf="!this.sortUp" class="material-icons arrow">arrow_upward</i><i *ngIf="this.sortUp" class="material-icons arrow">arrow_downward</i>
</button>
</ng-template>
</div>
<div *ngFor="let type of this.sortTypes">
<button *ngIf="type != this.sortType; else elseBlock;" mat-menu-item (click)="sort(type, true)">
Sort by {{type}}
</button>
<ng-template #elseBlock>
<button mat-menu-item (click)="sort(type, !this.sortUp)">
Sort by {{type}} <i *ngIf="!this.sortUp" class="material-icons arrow">arrow_upward</i><i *ngIf="this.sortUp" class="material-icons arrow">arrow_downward</i>
</button>
</ng-template>
</div>
</mat-menu>
<div class="container-fluid justify-content-center">
<a class="show" *ngFor="let show of this.shows" [href]="getLink(show)" [routerLink]="getLink(show)">
<div matRipple [style.background-image]="getThumb(show.slug)" > </div>
<p class="title">{{show.title}}</p>
<p class="date" *ngIf="show.endYear; else elseBlock">{{show.startYear}} - {{show.endYear}}</p>
<ng-template #elseBlock><p class="date">{{show.startYear}}</p></ng-template>
</a>
<a class="show" *ngFor="let show of this.shows" [href]="getLink(show)" [routerLink]="getLink(show)">
<div matRipple [style.background-image]="getThumb(show.slug)" > </div>
<p class="title">{{show.title}}</p>
<p class="date" *ngIf="show.endYear; else elseBlock">{{show.startYear}} - {{show.endYear}}</p>
<ng-template #elseBlock><p class="date">{{show.startYear}}</p></ng-template>
</a>
</div>

View File

@ -4,93 +4,93 @@
button
{
outline: none;
outline: none;
}
.arrow
{
font-size: 12px;
font-size: 12px;
}
.container-fluid
{
display: flex;
flex-wrap: wrap;
display: flex;
flex-wrap: wrap;
}
.show
{
width: 33%;
min-width: 120px;
max-width: 200px;
list-style: none;
padding: .5em;
text-decoration: none;
color: inherit;
outline: none;
width: 33%;
min-width: 120px;
max-width: 200px;
list-style: none;
padding: .5em;
text-decoration: none;
color: inherit;
outline: none;
@include media-breakpoint-up(sm)
{
width: 25%;
}
@include media-breakpoint-up(sm)
{
width: 25%;
}
@include media-breakpoint-up(md)
{
width: 20%;
padding: 1em;
}
@include media-breakpoint-up(md)
{
width: 20%;
padding: 1em;
}
@include media-breakpoint-up(lg)
{
width: 18%;
}
@include media-breakpoint-up(lg)
{
width: 18%;
}
@include media-breakpoint-up(xl)
{
width: 15%;
}
@include media-breakpoint-up(xl)
{
width: 15%;
}
&:focus, &:hover
{
> div
{
outline: solid var(--accentColor);
}
&:focus, &:hover
{
> div
{
outline: solid var(--accentColor);
}
> .title
{
text-decoration: underline;
}
}
> .title
{
text-decoration: underline;
}
}
> div
{
width: 100%;
height: 0;
padding-top: 147.0588%;
background-size: cover;
background-color: #333333;
}
> div
{
width: 100%;
height: 0;
padding-top: 147.0588%;
background-size: cover;
background-color: #333333;
}
> p
{
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-align: center;
margin-bottom: 0px;
opacity: 1;
> p
{
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-align: center;
margin-bottom: 0px;
opacity: 1;
&.date
{
opacity: 0.8;
font-size: 0.8em;
}
}
&.date
{
opacity: 0.8;
font-size: 0.8em;
}
}
&:host-context(.hoverEnabled) &:hover
{
cursor: pointer;
}
&:host-context(.hoverEnabled) &:hover
{
cursor: pointer;
}
}

View File

@ -4,17 +4,17 @@ import { DomSanitizer } from '@angular/platform-browser';
import { Show } from "../../models/show";
@Component({
selector: 'app-browse',
templateUrl: './browse.component.html',
styleUrls: ['./browse.component.scss']
selector: 'app-browse',
templateUrl: './browse.component.html',
styleUrls: ['./browse.component.scss']
})
export class BrowseComponent
{
@Input() shows: Show[];
sortType: string = "title";
sortUp: boolean = true;
@Input() shows: Show[];
sortType: string = "title";
sortUp: boolean = true;
sortTypes: string[] = ["title", "release date"];
sortTypes: string[] = ["title", "release date"];
constructor(private route: ActivatedRoute, private sanitizer: DomSanitizer)
{
@ -24,37 +24,37 @@ export class BrowseComponent
});
}
getThumb(slug: string)
{
return this.sanitizer.bypassSecurityTrustStyle("url(/poster/" + slug + ")");
}
getThumb(slug: string)
{
return this.sanitizer.bypassSecurityTrustStyle("url(/poster/" + slug + ")");
}
getLink(show: Show)
{
if (show.isCollection)
return "/collection/" + show.slug;
else
return "/show/" + show.slug;
}
getLink(show: Show)
{
if (show.isCollection)
return "/collection/" + show.slug;
else
return "/show/" + show.slug;
}
sort(type: string, order: boolean)
{
this.sortType = type;
this.sortUp = order;
sort(type: string, order: boolean)
{
this.sortType = type;
this.sortUp = order;
if (type == this.sortTypes[0])
{
if (order)
this.shows.sort((a, b) => { if (a.title < b.title) return -1; else if (a.title > b.title) return 1; return 0; });
else
this.shows.sort((a, b) => { if (a.title < b.title) return 1; else if (a.title > b.title) return -1; return 0; });
}
else if (type == this.sortTypes[1])
{
if (order)
this.shows.sort((a, b) => a.startYear - b.startYear);
else
this.shows.sort((a, b) => b.startYear - a.startYear);
}
}
if (type == this.sortTypes[0])
{
if (order)
this.shows.sort((a, b) => { if (a.title < b.title) return -1; else if (a.title > b.title) return 1; return 0; });
else
this.shows.sort((a, b) => { if (a.title < b.title) return 1; else if (a.title > b.title) return -1; return 0; });
}
else if (type == this.sortTypes[1])
{
if (order)
this.shows.sort((a, b) => a.startYear - b.startYear);
else
this.shows.sort((a, b) => b.startYear - a.startYear);
}
}
}

View File

@ -1,14 +1,14 @@
<div class="container-fluid">
<div class="row justify-content-center">
<div class="col-md-4 col-lg-3 col-xl-2 collection-info">
<div [style.background-image]="getThumb()"></div>
</div>
<div class="col-md-8 col-lg-9 col-xl-10">
<h3 class="text-center text-md-left p-2 p-md-3">{{collection.name}}</h3>
<h5 class="date" *ngIf="collection.endYear; else elseBlock">{{collection.startYear}} - {{collection.endYear}}</h5>
<ng-template #elseBlock><h5 class="date">{{collection.startYear}}</h5></ng-template>
<hr />
<app-browse [shows]="collection.shows"></app-browse>
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-4 col-lg-3 col-xl-2 collection-info">
<div [style.background-image]="getThumb()"></div>
</div>
<div class="col-md-8 col-lg-9 col-xl-10">
<h3 class="text-center text-md-left p-2 p-md-3">{{collection.name}}</h3>
<h5 class="date" *ngIf="collection.endYear; else elseBlock">{{collection.startYear}} - {{collection.endYear}}</h5>
<ng-template #elseBlock><h5 class="date">{{collection.startYear}}</h5></ng-template>
<hr />
<app-browse [shows]="collection.shows"></app-browse>
</div>
</div>
</div>

View File

@ -1,23 +1,23 @@
.collection-info
{
width: 60%;
width: 60%;
> div
{
width: 100%;
height: 0;
padding-top: 147.0588%;
background-size: cover;
background-color: #333333;
margin: 10px;
}
> div
{
width: 100%;
height: 0;
padding-top: 147.0588%;
background-size: cover;
background-color: #333333;
margin: 10px;
}
}
hr
{
margin: 10px 0 10px 0;
border-top: 1px solid rgba(255, 255, 255, .60);
border-left: 0;
width: inherit;
height: 2px;
margin: 10px 0 10px 0;
border-top: 1px solid rgba(255, 255, 255, .60);
border-left: 0;
width: inherit;
height: 2px;
}

View File

@ -4,13 +4,13 @@ import { ActivatedRoute } from "@angular/router";
import { DomSanitizer } from "@angular/platform-browser";
@Component({
selector: 'app-collection',
templateUrl: './collection.component.html',
styleUrls: ['./collection.component.scss']
selector: 'app-collection',
templateUrl: './collection.component.html',
styleUrls: ['./collection.component.scss']
})
export class CollectionComponent
{
collection: Collection;
collection: Collection;
constructor(private route: ActivatedRoute, private sanitizer: DomSanitizer)
{

View File

@ -1,21 +1,21 @@
<div class="root">
<div class="episodes" #scrollView (scroll)="onScroll()">
<a class="episode" *ngFor="let episode of this.episodes" #episodeDom routerLink="/watch/{{episode.link}}" href="/watch/{{episode.link}}">
<div matRipple class="img" [style.background-image]="sanitize(episode.thumb)">
<button mat-icon-button class="playBtn"><i class="material-icons playIcon">play_circle_outline</i></button>
</div>
<ng-container *ngIf="displayShowTitle; else noTitle;">
<h6 *ngIf="episode.seasonNumber != 0; else elseBlock;" class="title">{{episode.showTitle}} - S{{episode.seasonNumber}}:E{{episode.episodeNumber}}</h6>
<ng-template #elseBlock><h6 class="title">{{episode.showTitle}}</h6></ng-template>
<p class="subtitle">{{episode.title}}</p>
</ng-container>
<ng-template #noTitle>
<h6 *ngIf="episode.seasonNumber != 0; else elseBlock;" class="title">S{{episode.seasonNumber}}:E{{episode.episodeNumber}} - {{episode.title}}</h6>
<ng-template #elseBlock><h6 class="title">{{episode.title}}</h6></ng-template>
<p class="overview">{{episode.overview}}</p>
</ng-template>
</a>
</div>
<button mat-raised-button color="accent" class="scrollBtn leftBtn d-none" #leftBtn (click)="scrollLeft()"><mat-icon>arrow_left</mat-icon></button>
<button mat-raised-button color="accent" class="scrollBtn rightBtn" #rightBtn (click)="scrollRight()"><mat-icon>arrow_right</mat-icon></button>
<div class="episodes" #scrollView (scroll)="onScroll()">
<a class="episode" *ngFor="let episode of this.episodes" #episodeDom routerLink="/watch/{{episode.link}}" href="/watch/{{episode.link}}">
<div matRipple class="img" [style.background-image]="sanitize(episode.thumb)">
<button mat-icon-button class="playBtn"><i class="material-icons playIcon">play_circle_outline</i></button>
</div>
<ng-container *ngIf="displayShowTitle; else noTitle;">
<h6 *ngIf="episode.seasonNumber != 0; else elseBlock;" class="title">{{episode.showTitle}} - S{{episode.seasonNumber}}:E{{episode.episodeNumber}}</h6>
<ng-template #elseBlock><h6 class="title">{{episode.showTitle}}</h6></ng-template>
<p class="subtitle">{{episode.title}}</p>
</ng-container>
<ng-template #noTitle>
<h6 *ngIf="episode.seasonNumber != 0; else elseBlock;" class="title">S{{episode.seasonNumber}}:E{{episode.episodeNumber}} - {{episode.title}}</h6>
<ng-template #elseBlock><h6 class="title">{{episode.title}}</h6></ng-template>
<p class="overview">{{episode.overview}}</p>
</ng-template>
</a>
</div>
<button mat-raised-button color="accent" class="scrollBtn leftBtn d-none" #leftBtn (click)="scrollLeft()"><mat-icon>arrow_left</mat-icon></button>
<button mat-raised-button color="accent" class="scrollBtn rightBtn" #rightBtn (click)="scrollRight()"><mat-icon>arrow_right</mat-icon></button>
</div>

View File

@ -4,179 +4,179 @@
.root
{
position: relative;
position: relative;
&:host-context(.hoverEnabled) &:hover
{
.scrollBtn
{
display: block;
}
}
&:host-context(.hoverEnabled) &:hover
{
.scrollBtn
{
display: block;
}
}
}
.episodes
{
display: flex;
padding-left: 15px;
padding-right: 15px;
overflow-x: auto;
min-width: 100%;
flex-shrink: 0;
flex-direction: row;
scrollbar-width: thin;
scrollbar-color: #999 transparent;
display: flex;
padding-left: 15px;
padding-right: 15px;
overflow-x: auto;
min-width: 100%;
flex-shrink: 0;
flex-direction: row;
scrollbar-width: thin;
scrollbar-color: #999 transparent;
&::-webkit-scrollbar
{
height: 4px;
background: transparent;
}
&::-webkit-scrollbar
{
height: 4px;
background: transparent;
}
&::-webkit-scrollbar-thumb
{
background-color: #999;
border-radius: 90px;
&::-webkit-scrollbar-thumb
{
background-color: #999;
border-radius: 90px;
&:host-context(.hoverEnabled) &:hover
{
background-color: rgb(134, 127, 127);
}
}
&:host-context(.hoverEnabled) &:hover
{
background-color: rgb(134, 127, 127);
}
}
}
.episode
{
visibility: visible;
display: inline-block;
padding: .25rem;
flex-shrink: 0;
width: 55%;
cursor: pointer;
color: inherit;
text-decoration: inherit;
visibility: visible;
display: inline-block;
padding: .25rem;
flex-shrink: 0;
width: 55%;
cursor: pointer;
color: inherit;
text-decoration: inherit;
@include media-breakpoint-up(sm)
{
width: 40%;
}
@include media-breakpoint-up(sm)
{
width: 40%;
}
@include media-breakpoint-up(md)
{
width: 33%;
}
@include media-breakpoint-up(md)
{
width: 33%;
}
@include media-breakpoint-up(lg)
{
width: 28%;
}
@include media-breakpoint-up(lg)
{
width: 28%;
}
@include media-breakpoint-up(xl)
{
width: 18%;
}
@include media-breakpoint-up(xl)
{
width: 18%;
}
.img
{
width: 100%;
height: 0;
padding-top: 56.25%;
background-color: #333333;
background-size: contain;
position: relative;
.img
{
width: 100%;
height: 0;
padding-top: 56.25%;
background-color: #333333;
background-size: contain;
position: relative;
> button
{
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
width: 64px;
height: 64px;
outline: none;
display: none;
}
}
> button
{
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
width: 64px;
height: 64px;
outline: none;
display: none;
}
}
.title
{
padding-top: .2rem;
font-weight: 600;
margin-bottom: 0;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
overflow: hidden;
}
.title
{
padding-top: .2rem;
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;
}
.overview
{
font-weight: 300;
display: -webkit-box;
-webkit-line-clamp: 4;
-webkit-box-orient: vertical;
overflow: hidden;
}
.subtitle
{
font-weight: 300;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
overflow: hidden;
}
.subtitle
{
font-weight: 300;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
overflow: hidden;
}
&:host-context(.hoverEnabled) &:hover
{
.img
{
outline: solid var(--accentColor);
&:host-context(.hoverEnabled) &:hover
{
.img
{
outline: solid var(--accentColor);
.playBtn
{
display: block;
}
}
.playBtn
{
display: block;
}
}
.title
{
text-decoration: underline;
}
}
.title
{
text-decoration: underline;
}
}
}
.playIcon
{
font-size: 64px;
width: 64px;
height: 64px;
line-height: 64px;
font-size: 64px;
width: 64px;
height: 64px;
line-height: 64px;
}
.scrollBtn
{
padding: 0;
outline: none;
min-width: 0;
position: absolute;
top: 20%;
bottom: 60%;
display: none;
padding: 0;
outline: none;
min-width: 0;
position: absolute;
top: 20%;
bottom: 60%;
display: none;
&.leftBtn
{
left: 0;
padding-left: 10px;
padding-right: 2px;
}
&.leftBtn
{
left: 0;
padding-left: 10px;
padding-right: 2px;
}
&.rightBtn
{
right: 0;
padding-right: 10px;
padding-left: 2px;
}
&.rightBtn
{
right: 0;
padding-right: 10px;
padding-left: 2px;
}
}

View File

@ -4,14 +4,14 @@ import { DomSanitizer } from "@angular/platform-browser";
import { Episode } from "../../models/episode";
@Component({
selector: 'app-episodes-list',
templateUrl: './episodes-list.component.html',
styleUrls: ['./episodes-list.component.scss']
selector: 'app-episodes-list',
templateUrl: './episodes-list.component.html',
styleUrls: ['./episodes-list.component.scss']
})
export class EpisodesListComponent
{
@Input() displayShowTitle: boolean = false;
@Input() episodes: Episode[];
@Input() episodes: Episode[];
@ViewChild("scrollView", { static: true }) private scrollView: ElementRef;
@ViewChild("leftBtn", { static: false }) private leftBtn: MatButton;
@ViewChild("rightBtn", { static: false }) private rightBtn: MatButton;
@ -53,8 +53,8 @@ export class EpisodesListComponent
this.rightBtn._elementRef.nativeElement.classList.remove("d-none");
}
sanitize(url: string)
{
return this.sanitizer.bypassSecurityTrustStyle("url(" + url + ")");
}
sanitize(url: string)
{
return this.sanitizer.bypassSecurityTrustStyle("url(" + url + ")");
}
}

View File

@ -4,6 +4,6 @@
<br/>
<br/>
<div class="text-center">
<h1>404 Error</h1>
<p>The page you requested was not found.</p>
<h1>404 Error</h1>
<p>The page you requested was not found.</p>
</div>

View File

@ -1,15 +1,15 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-not-found',
templateUrl: './not-found.component.html',
styleUrls: ['./not-found.component.scss']
selector: 'app-not-found',
templateUrl: './not-found.component.html',
styleUrls: ['./not-found.component.scss']
})
export class NotFoundComponent implements OnInit {
constructor() { }
constructor() { }
ngOnInit() {
}
ngOnInit() {
}
}

View File

@ -1,11 +1,11 @@
<div class="scroll-row mb-5">
<div class="people-container" #scrollView (scroll)="onScroll()">
<a class="people" *ngFor="let people of this.people" routerLink="/people/{{people.slug}}" href="/people/{{people.slug}}">
<div matRipple [style.background-image]="getPeopleIcon(people.slug)"> </div>
<h6 class="name">{{people.name}}</h6>
<p class="role">{{people.role}}</p>
</a>
</div>
<button mat-raised-button color="accent" class="scrollBtn leftBtn d-none" #leftBtn (click)="scrollLeft()"><mat-icon>arrow_left</mat-icon></button>
<button mat-raised-button color="accent" class="scrollBtn rightBtn" #rightBtn (click)="scrollRight()"><mat-icon>arrow_right</mat-icon></button>
<div class="people-container" #scrollView (scroll)="onScroll()">
<a class="people" *ngFor="let people of this.people" routerLink="/people/{{people.slug}}" href="/people/{{people.slug}}">
<div matRipple [style.background-image]="getPeopleIcon(people.slug)"> </div>
<h6 class="name">{{people.name}}</h6>
<p class="role">{{people.role}}</p>
</a>
</div>
<button mat-raised-button color="accent" class="scrollBtn leftBtn d-none" #leftBtn (click)="scrollLeft()"><mat-icon>arrow_left</mat-icon></button>
<button mat-raised-button color="accent" class="scrollBtn rightBtn" #rightBtn (click)="scrollRight()"><mat-icon>arrow_right</mat-icon></button>
</div>

View File

@ -4,138 +4,138 @@
.people-container
{
display: flex;
padding-left: 15px;
padding-right: 15px;
overflow-x: auto;
min-width: 100%;
flex-shrink: 0;
flex-direction: row;
scrollbar-width: thin;
scrollbar-color: #999 transparent;
display: flex;
padding-left: 15px;
padding-right: 15px;
overflow-x: auto;
min-width: 100%;
flex-shrink: 0;
flex-direction: row;
scrollbar-width: thin;
scrollbar-color: #999 transparent;
&::-webkit-scrollbar
{
height: 4px;
background: transparent;
}
&::-webkit-scrollbar
{
height: 4px;
background: transparent;
}
&::-webkit-scrollbar-thumb
{
background-color: #999;
border-radius: 90px;
&::-webkit-scrollbar-thumb
{
background-color: #999;
border-radius: 90px;
&:host-context(.hoverEnabled) &:hover
{
background-color: rgb(134, 127, 127);
}
}
&:host-context(.hoverEnabled) &:hover
{
background-color: rgb(134, 127, 127);
}
}
}
.people
{
visibility: visible;
margin: .25rem;
text-decoration: none;
color: inherit;
outline: none;
flex-shrink: 0;
flex-grow: 0;
width: 33%;
visibility: visible;
margin: .25rem;
text-decoration: none;
color: inherit;
outline: none;
flex-shrink: 0;
flex-grow: 0;
width: 33%;
@include media-breakpoint-up(sm)
{
width: 22%;
}
@include media-breakpoint-up(sm)
{
width: 22%;
}
@include media-breakpoint-up(md)
{
width: 20%;
}
@include media-breakpoint-up(md)
{
width: 20%;
}
@include media-breakpoint-up(lg)
{
width: 15%;
}
@include media-breakpoint-up(lg)
{
width: 15%;
}
@include media-breakpoint-up(xl)
{
width: 10%;
}
@include media-breakpoint-up(xl)
{
width: 10%;
}
> div
{
width: 100%;
height: 0;
padding-top: 147.0588%;
background-size: cover;
background-color: #333333;
}
> div
{
width: 100%;
height: 0;
padding-top: 147.0588%;
background-size: cover;
background-color: #333333;
}
> p, h6
{
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-align: center;
margin-bottom: 0px;
> p, h6
{
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-align: center;
margin-bottom: 0px;
&.role
{
font-size: 0.8em;
}
}
&.role
{
font-size: 0.8em;
}
}
&:host-context(.hoverEnabled) &:hover
{
cursor: pointer;
&:host-context(.hoverEnabled) &:hover
{
cursor: pointer;
> img
{
outline: solid var(--accentColor);
}
> img
{
outline: solid var(--accentColor);
}
.name
{
text-decoration: underline;
}
}
.name
{
text-decoration: underline;
}
}
}
.scroll-row
{
position: relative;
position: relative;
&:host-context(.hoverEnabled) &:hover
{
.scrollBtn
{
display: block;
}
}
&:host-context(.hoverEnabled) &:hover
{
.scrollBtn
{
display: block;
}
}
}
.scrollBtn
{
padding: 0;
outline: none;
min-width: 0;
position: absolute;
top: 30%;
bottom: 40%;
display: none;
padding: 0;
outline: none;
min-width: 0;
position: absolute;
top: 30%;
bottom: 40%;
display: none;
&.leftBtn
{
left: 0;
padding-left: 10px;
padding-right: 2px;
}
&.leftBtn
{
left: 0;
padding-left: 10px;
padding-right: 2px;
}
&.rightBtn
{
right: 0;
padding-right: 10px;
padding-left: 2px;
}
&.rightBtn
{
right: 0;
padding-right: 10px;
padding-left: 2px;
}
}

View File

@ -1,154 +1,154 @@
<div id="root">
<div class="player data-vjs-player">
<video id="player" poster="backdrop/{{this.item.showSlug}}" autoplay muted (click)="videoClicked()">
</video>
</div>
<div class="player data-vjs-player">
<video id="player" poster="backdrop/{{this.item.showSlug}}" autoplay muted (click)="videoClicked()">
</video>
</div>
<div id="loadIndicator">
<div class="spinner-border align-self-center" role="status"></div>
</div>
<div id="loadIndicator">
<div class="spinner-border align-self-center" role="status"></div>
</div>
<mat-card class="d-none w-25 m-5" [ngClass]="{'d-block': this.displayStats}">
<mat-card-header style="margin-bottom: 0.5rem;">
<h4 style="align-self: center; margin-bottom: 0;">Stats</h4>
<div style="flex: 1 1 auto"></div>
<button mat-icon-button aria-label="Close" (click)="this.displayStats = false" style="outline: none;">
<mat-icon>close</mat-icon>
</button>
</mat-card-header>
<mat-card-content>
Play method: <span style="float: right">{{this.playMethod}}</span>
<br />
<br />
Video Container: <span style="float: right">{{this.item.container}} <i class="material-icons" style="vertical-align: middle; font-size: 14px">{{getSupportedFeature("container")}}</i></span>
<br />
Video Codec: <span style="float: right">{{this.item.video.codec}} <i class="material-icons" style="vertical-align: middle; font-size: 14px">{{getSupportedFeature("video")}}</i></span>
<br />
Audio Codec: <span style="float: right">{{this.item.audios[0].codec}} <i class="material-icons" style="vertical-align: middle; font-size: 14px">{{getSupportedFeature("audio")}}</i></span>
<br />
Subtitle Codec: <span style="float: right">{{this.selectedSubtitle ? this.selectedSubtitle.codec : "none"}} <i class="material-icons" style="vertical-align: middle; font-size: 14px">{{getSupportedFeature("subtitle")}}</i></span>
<br />
</mat-card-content>
</mat-card>
<mat-card class="d-none w-25 m-5" [ngClass]="{'d-block': this.displayStats}">
<mat-card-header style="margin-bottom: 0.5rem;">
<h4 style="align-self: center; margin-bottom: 0;">Stats</h4>
<div style="flex: 1 1 auto"></div>
<button mat-icon-button aria-label="Close" (click)="this.displayStats = false" style="outline: none;">
<mat-icon>close</mat-icon>
</button>
</mat-card-header>
<mat-card-content>
Play method: <span style="float: right">{{this.playMethod}}</span>
<br />
<br />
Video Container: <span style="float: right">{{this.item.container}} <i class="material-icons" style="vertical-align: middle; font-size: 14px">{{getSupportedFeature("container")}}</i></span>
<br />
Video Codec: <span style="float: right">{{this.item.video.codec}} <i class="material-icons" style="vertical-align: middle; font-size: 14px">{{getSupportedFeature("video")}}</i></span>
<br />
Audio Codec: <span style="float: right">{{this.item.audios[0].codec}} <i class="material-icons" style="vertical-align: middle; font-size: 14px">{{getSupportedFeature("audio")}}</i></span>
<br />
Subtitle Codec: <span style="float: right">{{this.selectedSubtitle ? this.selectedSubtitle.codec : "none"}} <i class="material-icons" style="vertical-align: middle; font-size: 14px">{{getSupportedFeature("subtitle")}}</i></span>
<br />
</mat-card-content>
</mat-card>
<div id="hover">
<div class="back">
<a mat-icon-button matTooltipPosition="below" matTooltip="Back" (click)="back()">
<mat-icon>arrow_back</mat-icon>
</a>
<h5>{{this.item.showTitle}}</h5>
</div>
<div id="hover">
<div class="back">
<a mat-icon-button matTooltipPosition="below" matTooltip="Back" (click)="back()">
<mat-icon>arrow_back</mat-icon>
</a>
<h5>{{this.item.showTitle}}</h5>
</div>
<div class="controller container-fluid" id="controller">
<div class="img d-none d-sm-block">
<img src="poster/{{this.item.showSlug}}" />
</div>
<div class="content">
<h3>S{{this.item.seasonNumber}}:E{{this.item.episodeNumber}} - {{this.item.title}}</h3>
<div class="controller container-fluid" id="controller">
<div class="img d-none d-sm-block">
<img src="poster/{{this.item.showSlug}}" />
</div>
<div class="content">
<h3>S{{this.item.seasonNumber}}:E{{this.item.episodeNumber}} - {{this.item.title}}</h3>
<div id="progress-bar">
<div class="seek-bar">
<div id="buffered"></div>
<div id="progress"></div>
</div>
<div id="thumb"><div></div></div>
</div>
<div id="progress-bar">
<div class="seek-bar">
<div id="buffered"></div>
<div id="progress"></div>
</div>
<div id="thumb"><div></div></div>
</div>
<div class="buttons">
<div class="left">
<a *ngIf="this.item.previousEpisode" mat-icon-button matTooltipPosition="above" matTooltip="Previous" (click)="previous()">
<mat-icon>skip_previous</mat-icon>
</a>
<button mat-icon-button matTooltipPosition="above" [matTooltip]="playTooltip" id="play" (click)="tooglePlayback()">
<mat-icon>{{this.playIcon}}</mat-icon>
</button>
<a mat-icon-button id="nextBtn" *ngIf="this.item.nextEpisode" (click)="next()">
<mat-icon>skip_next</mat-icon>
<div class="buttons">
<div class="left">
<a *ngIf="this.item.previousEpisode" mat-icon-button matTooltipPosition="above" matTooltip="Previous" (click)="previous()">
<mat-icon>skip_previous</mat-icon>
</a>
<button mat-icon-button matTooltipPosition="above" [matTooltip]="playTooltip" id="play" (click)="tooglePlayback()">
<mat-icon>{{this.playIcon}}</mat-icon>
</button>
<a mat-icon-button id="nextBtn" *ngIf="this.item.nextEpisode" (click)="next()">
<mat-icon>skip_next</mat-icon>
<div id="next">
<div id="main">
<img src="{{this.item.nextEpisode.thumb}}" />
</div>
<div id="overview">
<h6>S{{this.item.nextEpisode.seasonNumber}}:E{{this.item.nextEpisode.episodeNumber}} - {{this.item.nextEpisode.title}}</h6>
<p>{{this.item.nextEpisode.overview}}</p>
</div>
</div>
</a>
<div id="volume">
<button mat-icon-button matTooltipPosition="above" matTooltip="Volume" (click)="toogleMute()">
<mat-icon>{{this.volumeIcon}}</mat-icon>
</button>
<div id="next">
<div id="main">
<img src="{{this.item.nextEpisode.thumb}}" />
</div>
<div id="overview">
<h6>S{{this.item.nextEpisode.seasonNumber}}:E{{this.item.nextEpisode.episodeNumber}} - {{this.item.nextEpisode.title}}</h6>
<p>{{this.item.nextEpisode.overview}}</p>
</div>
</div>
</a>
<div id="volume">
<button mat-icon-button matTooltipPosition="above" matTooltip="Volume" (click)="toogleMute()">
<mat-icon>{{this.volumeIcon}}</mat-icon>
</button>
<mat-slider [value]="volume" (input)="changeVolume($event.value)"></mat-slider>
</div>
<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 class="right">
<button id="volume" *ngIf="this.item.audios.length > 1" mat-icon-button matTooltipPosition="above" matTooltip="Select audio track">
<mat-icon>music_note</mat-icon>
</button>
<button *ngIf="this.item.subtitles.length > 0" mat-icon-button [matMenuTriggerFor]="subtitles" matTooltipPosition="above" matTooltip="Select subtitle track">
<mat-icon>closed_caption</mat-icon>
</button>
<button mat-icon-button matTooltipPosition="above" matTooltip="Cast">
<mat-icon>cast</mat-icon>
</button>
<button mat-icon-button matTooltipPosition="above" [matMenuTriggerFor]="settings" matTooltip="Settings">
<mat-icon>settings</mat-icon>
</button>
<button mat-icon-button matTooltipPosition="above" [matTooltip]="fullscreenTooltip" id="fullscreen" (click)="fullscreen()">
<mat-icon>{{fullscreenIcon}}</mat-icon>
</button>
</div>
</div>
</div>
<mat-slider [value]="volume" (input)="changeVolume($event.value)"></mat-slider>
</div>
<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 class="right">
<button id="volume" *ngIf="this.item.audios.length > 1" mat-icon-button matTooltipPosition="above" matTooltip="Select audio track">
<mat-icon>music_note</mat-icon>
</button>
<button *ngIf="this.item.subtitles.length > 0" mat-icon-button [matMenuTriggerFor]="subtitles" matTooltipPosition="above" matTooltip="Select subtitle track">
<mat-icon>closed_caption</mat-icon>
</button>
<button mat-icon-button matTooltipPosition="above" matTooltip="Cast">
<mat-icon>cast</mat-icon>
</button>
<button mat-icon-button matTooltipPosition="above" [matMenuTriggerFor]="settings" matTooltip="Settings">
<mat-icon>settings</mat-icon>
</button>
<button mat-icon-button matTooltipPosition="above" [matTooltip]="fullscreenTooltip" id="fullscreen" (click)="fullscreen()">
<mat-icon>{{fullscreenIcon}}</mat-icon>
</button>
</div>
</div>
</div>
<mat-menu #subtitles="matMenu">
<ng-template matMenuContent>
<button [ngClass]="{'selected': this.selectedSubtitle == null}" mat-menu-item (click)="selectSubtitle(null)">
<span>None</span>
</button>
<mat-menu #subtitles="matMenu">
<ng-template matMenuContent>
<button [ngClass]="{'selected': this.selectedSubtitle == null}" mat-menu-item (click)="selectSubtitle(null)">
<span>None</span>
</button>
<div *ngFor="let subtitle of this.item.subtitles">
<button [ngClass]="{'selected': this.selectedSubtitle == subtitle}" mat-menu-item *ngIf="subtitle.codec == 'ass' || subtitle.codec == 'subrip'; else elseBlock" (click)="selectSubtitle(subtitle)">
<span>{{subtitle.displayName}}</span>
</button>
<div *ngFor="let subtitle of this.item.subtitles">
<button [ngClass]="{'selected': this.selectedSubtitle == subtitle}" mat-menu-item *ngIf="subtitle.codec == 'ass' || subtitle.codec == 'subrip'; else elseBlock" (click)="selectSubtitle(subtitle)">
<span>{{subtitle.displayName}}</span>
</button>
<ng-template #elseBlock>
<button mat-menu-item disabled>
<span>{{subtitle.displayName}} ({{subtitle.codec}})</span>
</button>
</ng-template>
</div>
</ng-template>
</mat-menu>
<ng-template #elseBlock>
<button mat-menu-item disabled>
<span>{{subtitle.displayName}} ({{subtitle.codec}})</span>
</button>
</ng-template>
</div>
</ng-template>
</mat-menu>
<mat-menu #settings="matMenu">
<ng-template matMenuContent>
<button mat-menu-item (click)="this.displayStats = !this.displayStats">
<span>Stats</span>
</button>
<button mat-menu-item [matMenuTriggerFor]="method">
<span>Method</span>
</button>
</ng-template>
</mat-menu>
<mat-menu #settings="matMenu">
<ng-template matMenuContent>
<button mat-menu-item (click)="this.displayStats = !this.displayStats">
<span>Stats</span>
</button>
<button mat-menu-item [matMenuTriggerFor]="method">
<span>Method</span>
</button>
</ng-template>
</mat-menu>
<mat-menu #method="matMenu">
<ng-template matMenuContent>
<button mat-menu-item (click)="selectPlayMethod(methodType.direct)">
<span>Direct Play</span>
</button>
<button mat-menu-item (click)="selectPlayMethod(methodType.transmux)">
<span>Transmux</span>
</button>
<button mat-menu-item (click)="selectPlayMethod(methodType.transcode)">
<span>Transcode</span>
</button>
</ng-template>
</mat-menu>
</div>
</div>
<mat-menu #method="matMenu">
<ng-template matMenuContent>
<button mat-menu-item (click)="selectPlayMethod(methodType.direct)">
<span>Direct Play</span>
</button>
<button mat-menu-item (click)="selectPlayMethod(methodType.transmux)">
<span>Transmux</span>
</button>
<button mat-menu-item (click)="selectPlayMethod(methodType.transcode)">
<span>Transcode</span>
</button>
</ng-template>
</mat-menu>
</div>
</div>
</div>

View File

@ -3,339 +3,339 @@
.player
{
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: #000;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: #000;
> video
{
width: 100%;
height: 100%;
object-fit: contain;
}
> video
{
width: 100%;
height: 100%;
object-fit: contain;
}
}
#hover
{
transition: opacity .2s linear;
opacity: 1;
visibility: visible;
transition: opacity .2s linear;
opacity: 1;
visibility: visible;
&.idle
{
transition: opacity .6s linear, visibility 0s .6s;
opacity: 0;
visibility: hidden;
}
&.idle
{
transition: opacity .6s linear, visibility 0s .6s;
opacity: 0;
visibility: hidden;
}
}
.back
{
position: fixed;
top: 0;
left: 0;
right: 0;
background: rgba(0, 0, 0, 0.6);
padding: .33%;
display: flex;
position: fixed;
top: 0;
left: 0;
right: 0;
background: rgba(0, 0, 0, 0.6);
padding: .33%;
display: flex;
> a
{
outline: none;
color: inherit;
text-decoration: none;
}
> a
{
outline: none;
color: inherit;
text-decoration: none;
}
> h5
{
margin: 0;
margin-left: .5rem;
align-self: center;
}
> 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%;
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: 15%;
position: relative;
height: auto;
> img
{
width: 100%;
height: auto;
bottom: 0;
position: absolute;
}
}
> img
{
width: 100%;
height: auto;
bottom: 0;
position: absolute;
}
}
.content
{
width: 100%;
margin-left: 1rem;
display: flex;
flex-direction: column;
.content
{
width: 100%;
margin-left: 1rem;
display: flex;
flex-direction: column;
.buttons
{
display: flex;
flex-direction: row;
justify-content: space-between;
.buttons
{
display: flex;
flex-direction: row;
justify-content: space-between;
> div
{
&.left
{
align-self: start;
display: flex;
> div
{
&.left
{
align-self: start;
display: flex;
> p
{
margin: 0;
margin-left: 1rem;
align-self: center;
}
}
> p
{
margin: 0;
margin-left: 1rem;
align-self: center;
}
}
&.right
{
align-self: end;
}
&.right
{
align-self: end;
}
> button
{
margin-left: .3rem;
margin-right: .3rem;
outline: none;
}
> button
{
margin-left: .3rem;
margin-right: .3rem;
outline: none;
}
> a
{
margin-left: .3rem;
margin-right: .3rem;
outline: none;
color: inherit;
text-decoration: inherit;
}
}
}
}
> a
{
margin-left: .3rem;
margin-right: .3rem;
outline: none;
color: inherit;
text-decoration: inherit;
}
}
}
}
}
#progress-bar
{
width: 100%;
height: auto;
padding-top: 1rem;
padding-bottom: 1rem;
position: relative;
width: 100%;
height: auto;
padding-top: 1rem;
padding-bottom: 1rem;
position: relative;
.seek-bar
{
width: 100%;
height: 4px;
position: relative;
background-color: rgba(255, 255, 255, .2);
transform: scaleY(.6);
.seek-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;
}
#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;
}
}
#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;
#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);
}
}
> div
{
width: 12px;
height: 12px;
border-radius: 6px;
background-color: var(--accentColor);
}
}
.hoverEnabled &:hover, &.seeking
{
cursor: pointer;
.hoverEnabled &:hover, &.seeking
{
cursor: pointer;
.seek-bar
{
transform: scaleY(1);
}
.seek-bar
{
transform: scaleY(1);
}
#thumb
{
opacity: 1;
}
}
#thumb
{
opacity: 1;
}
}
}
#nextBtn
{
position: relative;
position: relative;
.hoverEnabled &:hover
{
#next
{
display: flex;
}
}
.hoverEnabled &:hover
{
#next
{
display: flex;
}
}
#next
{
position: absolute;
left: 0;
bottom: 100%;
display: none;
background-color: #212121;
white-space: normal;
line-height: normal;
cursor: default;
height: 150px;
#next
{
position: absolute;
left: 0;
bottom: 100%;
display: none;
background-color: #212121;
white-space: normal;
line-height: normal;
cursor: default;
height: 150px;
#main
{
width: auto;
height: 100%;
flex-shrink: 0;
flex-grow: 0;
#main
{
width: auto;
height: 100%;
flex-shrink: 0;
flex-grow: 0;
> img
{
width: auto;
height: 100%;
}
}
> img
{
width: auto;
height: 100%;
}
}
#overview
{
padding: 1%;
width: 50%;
min-width: 300px;
flex-shrink: 0;
display: flex;
flex-direction: column;
#overview
{
padding: 1%;
width: 50%;
min-width: 300px;
flex-shrink: 0;
display: flex;
flex-direction: column;
> p
{
text-align: justify;
font-weight: 300;
overflow: hidden;
margin: 0;
}
}
}
> p
{
text-align: justify;
font-weight: 300;
overflow: hidden;
margin: 0;
}
}
}
}
#volume
{
display: flex;
display: flex;
> button
{
outline: none;
}
> button
{
outline: none;
}
.hoverEnabled &:hover, &:focus-within
{
> mat-slider
{
width: 100px;
}
}
.hoverEnabled &: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);
> mat-slider
{
width: 0px;
min-width: 0px;
padding: 0;
height: 40px;
overflow: hidden;
transition: width .2s cubic-bezier(0.4,0, 1, 1);
> div
{
top: 19px;
left: 10px;
right: 10px;
}
}
> div
{
top: 19px;
left: 10px;
right: 10px;
}
}
}
.mat-menu-item
{
outline: none !important;
outline: none !important;
}
.selected
{
background: #595959 !important;
color: var(--accentColor);
font-weight: 900;
background: #595959 !important;
color: var(--accentColor);
font-weight: 900;
}
#loadIndicator
{
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
pointer-events: none;
background: rgba(0, 0, 0, 0.3);
display: flex;
justify-content: center;
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
pointer-events: none;
background: rgba(0, 0, 0, 0.3);
display: flex;
justify-content: center;
}
.volume
{
min-width: 0px !important;
min-width: 0px !important;
}
.info-panel
{
min-width: 250px !important;
max-width: 300px !important;
min-width: 250px !important;
max-width: 300px !important;
}

View File

@ -371,7 +371,7 @@ export class PlayerComponent implements OnInit
let queryMethod: string = this.route.snapshot.queryParams["method"];
if (queryMethod)
this.playMethod = method[queryMethod];
else
else
this.playMethod = getPlaybackMethod(this.player, this.item);
this.selectPlayMethod(this.playMethod);
@ -428,7 +428,7 @@ export class PlayerComponent implements OnInit
next()
{
if (this.item.nextEpisode != null)
this.router.navigate(["/watch/" + this.item.nextEpisode.link], { queryParamsHandling: "merge", replaceUrl: true });
this.router.navigate(["/watch/" + this.item.nextEpisode.link], { queryParamsHandling: "merge", replaceUrl: true });
}
previous()

View File

@ -1,5 +1,5 @@
::cue
{
background-color: transparent;
text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
background-color: transparent;
text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
}

View File

@ -1,12 +1,12 @@
<div *ngIf="items.shows.length > 0" class="container-fluid mt-3">
<h3>Shows</h3>
<h3>Shows</h3>
</div>
<app-shows-list [shows]="items.shows"></app-shows-list>
<div *ngIf="items.episodes.length > 0" class="container-fluid mt-5">
<h3>Episodes</h3>
<h3>Episodes</h3>
</div>
<app-episodes-list displayShowTitle="true" [episodes]="items.episodes"></app-episodes-list>
<div *ngIf="items.people.length > 0" class="container-fluid mt-5">
<h3>People</h3>
<h3>People</h3>
</div>
<app-people-list [people]="items.people"></app-people-list>

View File

@ -4,9 +4,9 @@ import { SearchResut } from "../../models/search-result";
import { Title } from "@angular/platform-browser";
@Component({
selector: 'app-search',
templateUrl: './search.component.html',
styleUrls: ['./search.component.scss']
selector: 'app-search',
templateUrl: './search.component.html',
styleUrls: ['./search.component.scss']
})
export class SearchComponent implements OnInit, OnDestroy
{

View File

@ -7,27 +7,27 @@ import { catchError } from 'rxjs/operators'
import { Collection } from "../../models/collection";
@Injectable({
providedIn: 'root'
providedIn: 'root'
})
export class CollectionResolverService implements Resolve<Collection>
{
constructor(private http: HttpClient, private snackBar: MatSnackBar) { }
constructor(private http: HttpClient, private snackBar: MatSnackBar) { }
resolve(route: ActivatedRouteSnapshot): Collection | Observable<Collection> | Promise<Collection>
{
let collection: string = route.paramMap.get("collection-slug");
return this.http.get<Collection>("api/collection/" + collection).pipe(catchError((error: HttpErrorResponse) =>
{
console.log(error.status + " - " + error.message);
if (error.status == 404)
{
this.snackBar.open("Collection \"" + collection + "\" not found.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
}
else
{
this.snackBar.open("An unknow error occured.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
}
return EMPTY;
}));
}
resolve(route: ActivatedRouteSnapshot): Collection | Observable<Collection> | Promise<Collection>
{
let collection: string = route.paramMap.get("collection-slug");
return this.http.get<Collection>("api/collection/" + collection).pipe(catchError((error: HttpErrorResponse) =>
{
console.log(error.status + " - " + error.message);
if (error.status == 404)
{
this.snackBar.open("Collection \"" + collection + "\" not found.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
}
else
{
this.snackBar.open("An unknow error occured.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
}
return EMPTY;
}));
}
}

View File

@ -10,36 +10,36 @@ import { Show } from "../../models/show";
@Injectable()
export class LibraryResolverService implements Resolve<Show[]>
{
constructor(private http: HttpClient, private snackBar: MatSnackBar) { }
constructor(private http: HttpClient, private snackBar: MatSnackBar) { }
resolve(route: ActivatedRouteSnapshot): Show[] | Observable<Show[]> | Promise<Show[]>
{
let slug: string = route.paramMap.get("library-slug");
resolve(route: ActivatedRouteSnapshot): Show[] | Observable<Show[]> | Promise<Show[]>
{
let slug: string = route.paramMap.get("library-slug");
if (slug == null)
{
return this.http.get<Show[]>("api/shows").pipe(catchError((error: HttpErrorResponse) =>
{
console.log(error.status + " - " + error.message);
this.snackBar.open("An unknow error occured.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
return EMPTY;
}));
}
else
{
return this.http.get<Show[]>("api/libraries/" + slug).pipe(catchError((error: HttpErrorResponse) =>
{
console.log(error.status + " - " + error.message);
if (error.status == 404)
{
this.snackBar.open("Library \"" + slug + "\" not found.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
}
else
{
this.snackBar.open("An unknow error occured.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
}
return EMPTY;
}));
}
}
if (slug == null)
{
return this.http.get<Show[]>("api/shows").pipe(catchError((error: HttpErrorResponse) =>
{
console.log(error.status + " - " + error.message);
this.snackBar.open("An unknow error occured.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
return EMPTY;
}));
}
else
{
return this.http.get<Show[]>("api/libraries/" + slug).pipe(catchError((error: HttpErrorResponse) =>
{
console.log(error.status + " - " + error.message);
if (error.status == 404)
{
this.snackBar.open("Library \"" + slug + "\" not found.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
}
else
{
this.snackBar.open("An unknow error occured.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
}
return EMPTY;
}));
}
}
}

View File

@ -8,7 +8,7 @@ import { Collection } from "../../models/collection";
import { People } from "../../models/people";
@Injectable({
providedIn: 'root'
providedIn: 'root'
})
export class PeopleResolverService implements Resolve<Collection>
{

View File

@ -7,20 +7,20 @@ import { catchError } from 'rxjs/operators';
import { SearchResut } from "../../models/search-result";
@Injectable({
providedIn: 'root'
providedIn: 'root'
})
export class SearchResolverService implements Resolve<SearchResut>
{
constructor(private http: HttpClient, private snackBar: MatSnackBar) { }
constructor(private http: HttpClient, private snackBar: MatSnackBar) { }
resolve(route: ActivatedRouteSnapshot): SearchResut | Observable<SearchResut> | Promise<SearchResut>
{
let query: string = route.paramMap.get("query");
{
let query: string = route.paramMap.get("query");
return this.http.get<SearchResut>("api/search/" + query).pipe(catchError((error: HttpErrorResponse) =>
{
console.log(error.status + " - " + error.message);
this.snackBar.open("An unknow error occured.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
return EMPTY;
}));
}
{
console.log(error.status + " - " + error.message);
this.snackBar.open("An unknow error occured.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
return EMPTY;
}));
}
}

View File

@ -9,23 +9,19 @@ import { Show } from "../../models/show";
@Injectable()
export class ShowResolverService implements Resolve<Show>
{
constructor(private http: HttpClient, private snackBar: MatSnackBar) { }
constructor(private http: HttpClient, private snackBar: MatSnackBar) { }
resolve(route: ActivatedRouteSnapshot): Show | Observable<Show> | Promise<Show>
{
let slug: string = route.paramMap.get("show-slug");
return this.http.get<Show>("api/shows/" + slug).pipe(catchError((error: HttpErrorResponse) =>
{
console.log(error.status + " - " + error.message);
if (error.status == 404)
{
this.snackBar.open("Show \"" + slug + "\" not found.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
}
else
{
this.snackBar.open("An unknow error occured.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
}
return EMPTY;
}));
}
resolve(route: ActivatedRouteSnapshot): Show | Observable<Show> | Promise<Show>
{
let slug: string = route.paramMap.get("show-slug");
return this.http.get<Show>("api/shows/" + slug).pipe(catchError((error: HttpErrorResponse) =>
{
console.log(error.status + " - " + error.message);
if (error.status == 404)
this.snackBar.open("Show \"" + slug + "\" not found.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
else
this.snackBar.open("An unknow error occured.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
return EMPTY;
}));
}
}

View File

@ -10,23 +10,23 @@ import { WatchItem } from "../../models/watch-item";
@Injectable()
export class StreamResolverService implements Resolve<WatchItem>
{
constructor(private http: HttpClient, private snackBar: MatSnackBar) { }
constructor(private http: HttpClient, private snackBar: MatSnackBar) { }
resolve(route: ActivatedRouteSnapshot): WatchItem | Observable<WatchItem> | Promise<WatchItem>
{
let item: string = route.paramMap.get("item");
{
let item: string = route.paramMap.get("item");
return this.http.get<WatchItem>("api/watch/" + item).pipe(catchError((error: HttpErrorResponse) =>
{
console.log(error.status + " - " + error.message);
if (error.status == 404)
{
this.snackBar.open("Episode \"" + item + "\" not found.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
}
else
{
this.snackBar.open("An unknow error occured.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
}
return EMPTY;
}));
}
{
console.log(error.status + " - " + error.message);
if (error.status == 404)
{
this.snackBar.open("Episode \"" + item + "\" not found.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
}
else
{
this.snackBar.open("An unknow error occured.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
}
return EMPTY;
}));
}
}

View File

@ -1,79 +1,79 @@
<div class="backdrop">
<img id="backdrop" src="backdrop/{{this.show.slug}}" />
<img id="backdrop" src="backdrop/{{this.show.slug}}" />
</div>
<div class="header container pt-sm-5">
<div class="row">
<img class="poster d-none d-sm-block" src="poster/{{this.show.slug}}" />
<div class="main col">
<div class="info">
<h1 class="title">{{this.show.title}}</h1>
<h2 class="date" *ngIf="show.endYear; else elseBlock">{{show.startYear}} - {{show.endYear}}</h2>
<ng-template #elseBlock><h2 class="date">{{show.startYear}}</h2></ng-template>
</div>
<div class="buttons">
<button mat-mini-fab matTooltipPosition="above" matTooltip="Play" class="mr-3">
<mat-icon>play_arrow</mat-icon>
</button>
<button *ngIf="this.show.trailerUrl" mat-icon-button matTooltipPosition="above" matTooltip="Trailer">
<mat-icon>local_movies</mat-icon>
</button>
<button mat-icon-button matTooltipPosition="above" matTooltip="Download">
<mat-icon>cloud_download</mat-icon>
</button>
<button mat-icon-button matTooltipPosition="above" matTooltip="Watched">
<mat-icon>done</mat-icon>
</button>
<button mat-icon-button matTooltipPosition="above" matTooltip="More">
<mat-icon>more_horiz</mat-icon>
</button>
</div>
</div>
<div class="col-3 secondary d-none d-md-block">
<img src="logo/{{this.show.slug}}" />
<div class="row">
<img class="poster d-none d-sm-block" src="poster/{{this.show.slug}}" />
<div class="main col">
<div class="info">
<h1 class="title">{{this.show.title}}</h1>
<h2 class="date" *ngIf="show.endYear; else elseBlock">{{show.startYear}} - {{show.endYear}}</h2>
<ng-template #elseBlock><h2 class="date">{{show.startYear}}</h2></ng-template>
</div>
<div class="buttons">
<button mat-mini-fab matTooltipPosition="above" matTooltip="Play" class="mr-3">
<mat-icon>play_arrow</mat-icon>
</button>
<button *ngIf="this.show.trailerUrl" mat-icon-button matTooltipPosition="above" matTooltip="Trailer">
<mat-icon>local_movies</mat-icon>
</button>
<button mat-icon-button matTooltipPosition="above" matTooltip="Download">
<mat-icon>cloud_download</mat-icon>
</button>
<button mat-icon-button matTooltipPosition="above" matTooltip="Watched">
<mat-icon>done</mat-icon>
</button>
<button mat-icon-button matTooltipPosition="above" matTooltip="More">
<mat-icon>more_horiz</mat-icon>
</button>
</div>
</div>
<div class="col-3 secondary d-none d-md-block">
<img src="logo/{{this.show.slug}}" />
<div>
<p>Studio: <b><a routerLink="/studio/{{this.show.studio?.slug}}">{{this.show.studio?.name}}</a></b></p>
</div>
</div>
</div>
<div>
<p>Studio: <b><a routerLink="/studio/{{this.show.studio?.slug}}">{{this.show.studio?.name}}</a></b></p>
</div>
</div>
</div>
<div class="row pt-3 d-md-none">
<div class="col">
<p class="mr-1 d-inline-block">Studio: <b><a routerLink="/studio/{{this.show.studio?.slug}}">{{this.show.studio?.name}}</a></b></p>
<div class="d-sm-none">
<p>Genres: <span *ngFor="let genre of this.show.genres; let isLast = last"><b><a routerLink="/genre/{{genre.slug}}">{{genre.name}}</a></b>{{isLast ? "" : ", "}}</span></p>
</div>
</div>
</div>
<div class="row pt-3 d-md-none">
<div class="col">
<p class="mr-1 d-inline-block">Studio: <b><a routerLink="/studio/{{this.show.studio?.slug}}">{{this.show.studio?.name}}</a></b></p>
<div class="d-sm-none">
<p>Genres: <span *ngFor="let genre of this.show.genres; let isLast = last"><b><a routerLink="/genre/{{genre.slug}}">{{genre.name}}</a></b>{{isLast ? "" : ", "}}</span></p>
</div>
</div>
</div>
<div class="row pt-3">
<div class="col">
<p class="text-justify overview">{{this.show.overview}}</p>
</div>
<hr class="d-none d-sm-block">
<div class="col-3 d-none d-sm-block">
<h3 style="opacity: .8;">Genres</h3>
<ul>
<li *ngFor="let genre of this.show.genres"><b><a class="genre" routerLink="/genre/{{genre.slug}}">{{genre.name}}</a></b></li>
</ul>
</div>
</div>
<div class="row pt-3">
<div class="col">
<p class="text-justify overview">{{this.show.overview}}</p>
</div>
<hr class="d-none d-sm-block">
<div class="col-3 d-none d-sm-block">
<h3 style="opacity: .8;">Genres</h3>
<ul>
<li *ngFor="let genre of this.show.genres"><b><a class="genre" routerLink="/genre/{{genre.slug}}">{{genre.name}}</a></b></li>
</ul>
</div>
</div>
</div>
<div class="container-fluid mt-3">
<mat-form-field>
<mat-label>Season</mat-label>
<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>
<mat-form-field>
<mat-label>Season</mat-label>
<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 mt-5">
<h3>Staff</h3>
<h3>Staff</h3>
</div>
<app-people-list [people]="show.people"></app-people-list>

View File

@ -4,149 +4,149 @@
a
{
color: #ffffff;
color: #ffffff;
}
.backdrop
{
margin-top: -68px;
position: relative;
z-index: -1;
margin-top: -68px;
position: relative;
z-index: -1;
> img
{
width: 100%;
max-height: 75vh;
object-fit: cover;
min-height: 20vh;
> img
{
width: 100%;
max-height: 75vh;
object-fit: cover;
min-height: 20vh;
@include media-breakpoint-up(md)
{
min-height: 60vh;
}
}
@include media-breakpoint-up(md)
{
min-height: 60vh;
}
}
&:after
{
content: "";
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0.6) 100%);
}
&:after
{
content: "";
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0.6) 100%);
}
}
.header
{
@include media-breakpoint-up(sm)
{
margin-top: -12rem;
}
@include media-breakpoint-up(sm)
{
margin-top: -12rem;
}
@include media-breakpoint-up(md)
{
margin-top: -13rem;
}
@include media-breakpoint-up(md)
{
margin-top: -13rem;
}
@include media-breakpoint-up(lg)
{
margin-top: -19rem;
}
@include media-breakpoint-up(lg)
{
margin-top: -19rem;
}
@include media-breakpoint-up(xl)
{
margin-top: -23rem;
}
@include media-breakpoint-up(xl)
{
margin-top: -23rem;
}
}
.poster
{
width: 33%;
background-color: #333333;
width: 33%;
background-color: #333333;
@include media-breakpoint-up(md)
{
width: 25%;
}
@include media-breakpoint-up(md)
{
width: 25%;
}
}
.main
{
align-self: center;
padding-left: 2.5em;
align-self: center;
padding-left: 2.5em;
.info
{
margin-top: -3.25rem;
.info
{
margin-top: -3.25rem;
@include media-breakpoint-up(sm)
{
margin-top: 0;
}
@include media-breakpoint-up(sm)
{
margin-top: 0;
}
.title
{
font-weight: 900 !important;
}
.title
{
font-weight: 900 !important;
}
.date
{
font-weight: 300 !important;
}
}
.date
{
font-weight: 300 !important;
}
}
.buttons
{
> button
{
outline: none;
margin: .3em;
}
}
.buttons
{
> button
{
outline: none;
margin: .3em;
}
}
}
.secondary
{
position: relative;
position: relative;
> img
{
max-width: 100%;
}
> img
{
max-width: 100%;
}
> div
{
position: absolute;
bottom: 0;
}
> div
{
position: absolute;
bottom: 0;
}
> div > p
{
opacity: .87;
}
> div > p
{
opacity: .87;
}
}
.overview
{
opacity: .87;
opacity: .87;
@include media-breakpoint-up(sm)
{
padding-top: 2.25rem;
}
@include media-breakpoint-up(sm)
{
padding-top: 2.25rem;
}
}
hr
{
margin: 0 10px 0 10px;
border-right: 1px solid rgba(255, 255, 255, .60);
border-top: 0;
height: inherit;
margin: 0 10px 0 10px;
border-right: 1px solid rgba(255, 255, 255, .60);
border-top: 0;
height: inherit;
}
.genre
{
opacity: .8;
opacity: .8;
}

View File

@ -7,76 +7,76 @@ import { Episode } from "../../models/episode";
import { Show } from "../../models/show";
@Component({
selector: 'app-show-details',
templateUrl: './show-details.component.html',
styleUrls: ['./show-details.component.scss']
selector: 'app-show-details',
templateUrl: './show-details.component.html',
styleUrls: ['./show-details.component.scss']
})
export class ShowDetailsComponent implements OnInit
{
show: Show;
episodes: Episode[] = null;
season: number;
show: Show;
episodes: Episode[] = null;
season: number;
private toolbar: HTMLElement;
private backdrop: HTMLElement;
private toolbar: HTMLElement;
private backdrop: HTMLElement;
constructor(private route: ActivatedRoute, private http: HttpClient, private snackBar: MatSnackBar, private title: Title)
{
this.route.queryParams.subscribe(params =>
{
this.season = params["season"];
});
constructor(private route: ActivatedRoute, private http: HttpClient, private snackBar: MatSnackBar, private title: Title)
{
this.route.queryParams.subscribe(params =>
{
this.season = params["season"];
});
this.route.data.subscribe(data =>
{
this.show = data.show;
this.title.setTitle(this.show.title + " - Kyoo");
this.route.data.subscribe(data =>
{
this.show = data.show;
this.title.setTitle(this.show.title + " - Kyoo");
if (this.season == null || this.show.seasons.find(x => x.seasonNumber == this.season) == null)
this.season = 1;
if (this.season == null || this.show.seasons.find(x => x.seasonNumber == this.season) == null)
this.season = 1;
this.getEpisodes();
});
}
this.getEpisodes();
});
}
ngOnInit()
{
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`);
}
ngOnInit()
{
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`);
}
ngOnDestroy()
{
window.removeEventListener("scroll", this.scroll, true);
this.title.setTitle("Kyoo");
this.toolbar.setAttribute("style", `background-color: #000000 !important`);
}
ngOnDestroy()
{
window.removeEventListener("scroll", this.scroll, true);
this.title.setTitle("Kyoo");
this.toolbar.setAttribute("style", `background-color: #000000 !important`);
}
scroll = () =>
{
let opacity: number = 2 * window.scrollY / this.backdrop.clientHeight;
this.toolbar.setAttribute("style", `background-color: rgba(0, 0, 0, ${opacity}) !important`);
}
scroll = () =>
{
let opacity: number = 2 * window.scrollY / this.backdrop.clientHeight;
this.toolbar.setAttribute("style", `background-color: rgba(0, 0, 0, ${opacity}) !important`);
}
getEpisodes()
{
if (this.show == null)
return;
getEpisodes()
{
if (this.show == null)
return;
if (this.show.seasons.find(x => x.seasonNumber == this.season).episodes != null)
this.episodes = this.show.seasons.find(x => x.seasonNumber == this.season).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.find(x => x.seasonNumber == this.season).episodes = episodes;
this.episodes = episodes;
}, error =>
{
console.log(error.status + " - " + error.message);
this.snackBar.open("An unknow error occured while getting episodes.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
});
}
this.http.get<Episode[]>("api/episodes/" + this.show.slug + "/season/" + this.season).subscribe((episodes: Episode[]) =>
{
this.show.seasons.find(x => x.seasonNumber == this.season).episodes = episodes;
this.episodes = episodes;
}, error =>
{
console.log(error.status + " - " + error.message);
this.snackBar.open("An unknow error occured while getting episodes.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
});
}
}

View File

@ -1,12 +1,12 @@
<div class="scroll-row mb-5">
<div class="shows-container" #scrollView (scroll)="onScroll()">
<a class="show" *ngFor="let show of this.shows" routerLink="/show/{{show.slug}}" href="/show/{{show.slug}}">
<div matRipple [style.background-image]="getThumb(show.slug)"> </div>
<p class="title">{{show.title}}</p>
<p class="date" *ngIf="show.endYear; else elseBlock">{{show.startYear}} - {{show.endYear}}</p>
<ng-template #elseBlock><p class="date">{{show.startYear}}</p></ng-template>
</a>
</div>
<button mat-raised-button color="accent" class="scrollBtn leftBtn d-none" #leftBtn (click)="scrollLeft()"><mat-icon>arrow_left</mat-icon></button>
<button mat-raised-button color="accent" class="scrollBtn rightBtn" #rightBtn (click)="scrollRight()"><mat-icon>arrow_right</mat-icon></button>
<div class="shows-container" #scrollView (scroll)="onScroll()">
<a class="show" *ngFor="let show of this.shows" routerLink="/show/{{show.slug}}" href="/show/{{show.slug}}">
<div matRipple [style.background-image]="getThumb(show.slug)"> </div>
<p class="title">{{show.title}}</p>
<p class="date" *ngIf="show.endYear; else elseBlock">{{show.startYear}} - {{show.endYear}}</p>
<ng-template #elseBlock><p class="date">{{show.startYear}}</p></ng-template>
</a>
</div>
<button mat-raised-button color="accent" class="scrollBtn leftBtn d-none" #leftBtn (click)="scrollLeft()"><mat-icon>arrow_left</mat-icon></button>
<button mat-raised-button color="accent" class="scrollBtn rightBtn" #rightBtn (click)="scrollRight()"><mat-icon>arrow_right</mat-icon></button>
</div>

View File

@ -4,145 +4,145 @@
.shows-container
{
display: flex;
padding-left: 15px;
padding-right: 15px;
overflow-x: auto;
min-width: 100%;
flex-shrink: 0;
flex-direction: row;
display: flex;
padding-left: 15px;
padding-right: 15px;
overflow-x: auto;
min-width: 100%;
flex-shrink: 0;
flex-direction: row;
&::-webkit-scrollbar
{
height: 4px;
background: transparent;
}
&::-webkit-scrollbar
{
height: 4px;
background: transparent;
}
&::-webkit-scrollbar-thumb
{
background-color: #999;
border-radius: 90px;
&::-webkit-scrollbar-thumb
{
background-color: #999;
border-radius: 90px;
&:host-context(.hoverEnabled) &:hover
{
background-color: rgb(134, 127, 127);
}
}
&:host-context(.hoverEnabled) &:hover
{
background-color: rgb(134, 127, 127);
}
}
}
.show
{
width: 33%;
min-width: 120px;
max-width: 200px;
list-style: none;
padding: .5em;
text-decoration: none;
color: inherit;
outline: none;
flex-shrink: 0;
flex-grow: 0;
width: 33%;
min-width: 120px;
max-width: 200px;
list-style: none;
padding: .5em;
text-decoration: none;
color: inherit;
outline: none;
flex-shrink: 0;
flex-grow: 0;
@include media-breakpoint-up(sm)
{
width: 25%;
}
@include media-breakpoint-up(sm)
{
width: 25%;
}
@include media-breakpoint-up(md)
{
width: 20%;
padding: 1em;
}
@include media-breakpoint-up(md)
{
width: 20%;
padding: 1em;
}
@include media-breakpoint-up(lg)
{
width: 18%;
}
@include media-breakpoint-up(lg)
{
width: 18%;
}
@include media-breakpoint-up(xl)
{
width: 15%;
}
@include media-breakpoint-up(xl)
{
width: 15%;
}
&:focus, &:hover
{
> div
{
outline: solid var(--accentColor);
}
&:focus, &:hover
{
> div
{
outline: solid var(--accentColor);
}
> .title
{
text-decoration: underline;
}
}
> .title
{
text-decoration: underline;
}
}
> div
{
width: 100%;
height: 0;
padding-top: 147.0588%;
background-size: cover;
background-color: #333333;
}
> div
{
width: 100%;
height: 0;
padding-top: 147.0588%;
background-size: cover;
background-color: #333333;
}
> p
{
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-align: center;
margin-bottom: 0px;
opacity: 1;
> p
{
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-align: center;
margin-bottom: 0px;
opacity: 1;
&.date
{
opacity: 0.8;
font-size: 0.8em;
}
}
&.date
{
opacity: 0.8;
font-size: 0.8em;
}
}
&:host-context(.hoverEnabled) &:hover
{
cursor: pointer;
}
&:host-context(.hoverEnabled) &:hover
{
cursor: pointer;
}
}
.scroll-row
{
position: relative;
position: relative;
&:host-context(.hoverEnabled) &:hover
{
.scrollBtn
{
display: block;
}
}
&:host-context(.hoverEnabled) &:hover
{
.scrollBtn
{
display: block;
}
}
}
.scrollBtn
{
padding: 0;
outline: none;
min-width: 0;
position: absolute;
top: 30%;
bottom: 40%;
display: none;
padding: 0;
outline: none;
min-width: 0;
position: absolute;
top: 30%;
bottom: 40%;
display: none;
&.leftBtn
{
left: 0;
padding-left: 10px;
padding-right: 2px;
}
&.leftBtn
{
left: 0;
padding-left: 10px;
padding-right: 2px;
}
&.rightBtn
{
right: 0;
padding-right: 10px;
padding-left: 2px;
}
&.rightBtn
{
right: 0;
padding-right: 10px;
padding-left: 2px;
}
}

View File

@ -1,3 +1,3 @@
export const environment = {
production: true
production: true
};

View File

@ -3,7 +3,7 @@
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false
production: false
};
/*
@ -13,4 +13,4 @@ export const environment = {
* This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown.
*/
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.

View File

@ -1,15 +1,15 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Kyoo</title>
<base href="/">
<head>
<meta charset="utf-8">
<title>Kyoo</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#000000" />
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#000000" />
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
</html>

View File

@ -72,8 +72,8 @@ let SubtitleManager = (function() {
}
var det_C0_C1 = (C[0] * C[3]) - (C[2] * C[1]);
var det_C0_X = (C[0] * X[1]) - (C[2] * X[0]);
var det_X_C1 = (X[0] * C[3]) - (X[1] * C[1]);
var det_C0_X = (C[0] * X[1]) - (C[2] * X[0]);
var det_X_C1 = (X[0] * C[3]) - (X[1] * C[1]);
var alpha_l = det_C0_C1 === 0 ? 0 : det_X_C1 / det_C0_C1;
var alpha_r = det_C0_C1 === 0 ? 0 : det_C0_X / det_C0_C1;
var segLength = norm(subtract(points[0], points[len-1]));
@ -209,8 +209,8 @@ let SubtitleManager = (function() {
} else if (curr.start_time < prev.end_time) {
let [prev_start_time,prev_start_value,prev_prev_accel] = points[points.length-2];
let last = points[points.length-1];
last[0] = curr.start_time;
last[1] = prev_start_value + (prev.end_value - prev_start_value) * Math.pow((curr.start_time - prev_start_time) / (prev.end_time - prev_start_time), last[2]);
last[0] = curr.start_time;
last[1] = prev_start_value + (prev.end_value - prev_start_value) * Math.pow((curr.start_time - prev_start_time) / (prev.end_time - prev_start_time), last[2]);
}
points.push([curr.end_time,curr.end_value,curr.accel]);
@ -251,8 +251,8 @@ let SubtitleManager = (function() {
let combineAdjacentBlocks = text => text.replace(reAdjacentBlocks,"$1$2").replace(reAdjacentBlocks,"$1$2");
// Map to convert SSAv4 alignment values to ASSv4+ values.
// 1, 2, 3, 5, 6, 7, 9, 10, 11
let SSA_ALIGNMENT_MAP = [0, 1, 2, 3, 0, 7, 8, 9, 0, 4, 5, 6];
// 1, 2, 3, 5, 6, 7, 9, 10, 11
let SSA_ALIGNMENT_MAP = [0, 1, 2, 3, 0, 7, 8, 9, 0, 4, 5, 6];
// Alias for creating SVG elements.
let createSVGElement = document.createElementNS.bind(document,"http://www.w3.org/2000/svg");
@ -803,10 +803,10 @@ let SubtitleManager = (function() {
return computedPaths[pathID];
path = path.toLowerCase();
path = path.replace(/b/g,"C"); // cubic bézier curve to point 3 using point 1 and 2 as the control points
path = path.replace(/l/g,"L"); // line-to <x>, <y>
path = path.replace(/b/g,"C"); // cubic bézier curve to point 3 using point 1 and 2 as the control points
path = path.replace(/l/g,"L"); // line-to <x>, <y>
path = path.replace(/m/g,"Z M"); // move-to <x>, <y> (closing the shape first)
path = path.replace(/n/g,"M"); // move-to <x>, <y> (without closing the shape)
path = path.replace(/n/g,"M"); // move-to <x>, <y> (without closing the shape)
// extend b-spline to <x>, <y>
// The "p" command is only supposed to be used after an "s" command,
@ -829,7 +829,7 @@ let SubtitleManager = (function() {
// done by copying the starting location and the first two points
// to the end of the spline.
// before: x0 y0 s x1 y1 x2 y2 ... c
// after: x0 y0 s x1 y1 x2 y2 ... x0 y0 x1 y1 x2 y2
// after: x0 y0 s x1 y1 x2 y2 ... x0 y0 x1 y1 x2 y2
changes = true;
while (changes) {
changes = false;
@ -842,10 +842,10 @@ let SubtitleManager = (function() {
// 3rd degree uniform b-spline
// SVG doesn't have this, so we have convert them to a series of
// Bézier curves.
// x0 y0 s x1 y1 x2 y2 x3 y3 x4 y4 x5 y5
// |-----------------------| Bézier 1
// |---------------------| Bézier 2
// |---------------------| Bézier 3
// x0 y0 s x1 y1 x2 y2 x3 y3 x4 y4 x5 y5
// |-----------------------| Bézier 1
// |---------------------| Bézier 2
// |---------------------| Bézier 3
// Since the start point for a Bézier is different from a spline,
// we also need to add a move before the first Bézier and after the
// last Bézier. However, the bounding box of b-splines is different
@ -1099,8 +1099,8 @@ let SubtitleManager = (function() {
let newPieces = [];
for (let piece of pieces) {
// convert text
// from "{overide1}some text here{overridde2}more text ..."
// to [["override1",["some"," ","text"," ","here"]], ["override2",["more"," ","text"]], ...]
// from "{overide1}some text here{overridde2}more text ..."
// to [["override1",["some"," ","text"," ","here"]], ["override2",["more"," ","text"]], ...]
// taking care not to split on non-breaking spaces or paths
let data = piece.text.split("{").slice(1).map(a => a.split("}")).map(b => [b[0], isPath(b[0]) ? [b[1]] : b[1].split(/([^\S\xA0]+)/g)]);
@ -1230,19 +1230,19 @@ let SubtitleManager = (function() {
if (slice.width) slices.push(slice);
// Bubble pieces down through the slices while keeping the constraints:
// don't make a line shorter than the one after it
// don't include whitespace at the start or end
// don't make a line shorter than the one after it
// don't include whitespace at the start or end
let bubbled, last_slice = slices.length - 1;
do {
bubbled = false;
for (let i = last_slice; i > 0; --i) {
let curr = slices[i], prev = slices[i-1];
/* prev curr
|----------| |--| Before
XXXXXXXX XX XXXX
|------| |------| After
prev curr
/* prev curr
|----------| |--| Before
XXXXXXXX XX XXXX
|------| |------| After
prev curr
*/
while (true) {
// Find the next non-whitespace piece
@ -1376,19 +1376,19 @@ let SubtitleManager = (function() {
if (slice.width) slices.unshift(slice);
// Bubble pieces up through the slices while keeping the constraints:
// don't make a line shorter than the one before it
// don't include whitespace at the start or end
// don't make a line shorter than the one before it
// don't include whitespace at the start or end
let bubbled, last_slice = slices.length - 1;
do {
bubbled = false;
for (let i = 0; i < last_slice; ++i) {
let curr = slices[i], next = slices[i+1];
/* curr next
|--| |----------| Before
XXXX XX XXXXXXXX
|------| |------| After
curr next
/* curr next
|--| |----------| Before
XXXX XX XXXXXXXX
|------| |------| After
curr next
*/
while (true) {
// Find the next non-whitespace piece
@ -2196,8 +2196,8 @@ let SubtitleManager = (function() {
this.group = null;
this.Margin = {"L" : (data.MarginL && parseInt(data.MarginL,10)) || renderer.styles[data.Style].MarginL,
"R" : (data.MarginR && parseInt(data.MarginR,10)) || renderer.styles[data.Style].MarginR,
"V" : (data.MarginV && parseInt(data.MarginV,10)) || renderer.styles[data.Style].MarginV};
"R" : (data.MarginR && parseInt(data.MarginR,10)) || renderer.styles[data.Style].MarginR,
"V" : (data.MarginV && parseInt(data.MarginV,10)) || renderer.styles[data.Style].MarginV};
this.time = {"start" : timeConvert(data.Start), "end" : timeConvert(data.End)};
this.time.milliseconds = (this.time.end - this.time.start) * 1000;
@ -2982,8 +2982,8 @@ let SubtitleManager = (function() {
// Create the font-face CSS.
css += "@font-face {\n";
css += " font-family: \"" + fontname + "\";\n";
css += " src: url(data:font/" + submime + ";charset=utf-8;base64," + btoa(fontdata) + ");\n";
css += " font-family: \"" + fontname + "\";\n";
css += " src: url(data:font/" + submime + ";charset=utf-8;base64," + btoa(fontdata) + ");\n";
css += "}\n\n";
}

View File

@ -6,8 +6,8 @@ import { environment } from './environments/environment';
import "hammerjs"
if (environment.production) {
enableProdMode();
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
.catch(err => console.error(err));

View File

@ -2,11 +2,11 @@ import { Show } from "./show";
export interface Collection
{
slug: string;
name: string;
poster: string;
overview: string;
startYear: number,
slug: string;
name: string;
poster: string;
overview: string;
startYear: number,
endYear: number,
shows: Show[];
shows: Show[];
}

View File

@ -1,13 +1,13 @@
export interface Episode
{
seasonNumber: number;
episodeNumber: number;
title: string;
thumb: string;
link: string;
overview: string;
releaseDate;
runtime: number;
seasonNumber: number;
episodeNumber: number;
title: string;
thumb: string;
link: string;
overview: string;
releaseDate;
runtime: number;
externalIDs: string;
showTitle: string;
}

View File

@ -1,5 +1,5 @@
export interface Genre
{
slug: string;
name: string;
slug: string;
name: string;
}

View File

@ -1,9 +1,9 @@
export interface People
{
slug: string;
name: string;
role: string;
type: string;
slug: string;
name: string;
role: string;
type: string;
externalIDs: string;
externalIDs: string;
}

View File

@ -2,8 +2,8 @@ import { Episode } from "./episode";
export interface Season
{
seasonNumber: number;
title: string;
overview: string;
episodes: Episode[];
seasonNumber: number;
title: string;
overview: string;
episodes: Episode[];
}

View File

@ -5,20 +5,20 @@ import { Studio } from "./studio";
export interface Show
{
slug: string;
title: string;
Aliases: string[];
overview: string;
genres: Genre[];
status: string;
studio: Studio;
people: People[];
seasons: Season[];
trailerUrl: string;
isCollection: boolean;
slug: string;
title: string;
Aliases: string[];
overview: string;
genres: Genre[];
status: string;
studio: Studio;
people: People[];
seasons: Season[];
trailerUrl: string;
isCollection: boolean;
startYear: number;
endYear : number;
startYear: number;
endYear : number;
externalIDs: string;
externalIDs: string;
}

View File

@ -1,5 +1,5 @@
export interface Studio
{
slug: string;
name: string;
slug: string;
name: string;
}

View File

@ -2,31 +2,31 @@ import { Episode } from "./episode";
export interface WatchItem
{
showTitle: string;
showSlug: string;
seasonNumber: number;
episodeNumber: number;
title: string;
link: string;
duration: number;
releaseDate;
showTitle: string;
showSlug: string;
seasonNumber: number;
episodeNumber: number;
title: string;
link: string;
duration: number;
releaseDate;
previousEpisode: string;
nextEpisode: Episode;
previousEpisode: string;
nextEpisode: Episode;
container: string;
video: Track;
audios: Track[];
subtitles: Track[];
audios: Track[];
subtitles: Track[];
}
export interface Track
{
displayName: string;
title: string;
language: string;
isDefault: boolean;
isForced: boolean;
codec: string;
link: string;
displayName: string;
title: string;
language: string;
isDefault: boolean;
isForced: boolean;
codec: string;
link: string;
}

View File

@ -3,9 +3,9 @@
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
@ -19,14 +19,14 @@
*/
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/**
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
@ -45,17 +45,17 @@
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*
* (window as any).__Zone_enable_cross_context_check = true;
* (window as any).__Zone_enable_cross_context_check = true;
*
*/
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js/dist/zone'; // Included with Angular CLI.
import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************

View File

@ -6,10 +6,10 @@
@import "~bootstrap/scss/variables";
$theme-colors: (
"primary": #0a1128,
"secondary": #000000,
"accentColor": #e23c00,
"textPrimary": #ffffff
"primary": #0a1128,
"secondary": #000000,
"accentColor": #e23c00,
"textPrimary": #ffffff
);
$body-bg: theme-color("primary");
@ -18,12 +18,12 @@ $font-family-base: "Roboto", Arial, sans-serif;
p
{
opacity: .6;
opacity: .6;
}
h6
{
opacity: .87;
opacity: .87;
}
@import "~bootstrap/scss/bootstrap";
@ -43,63 +43,63 @@ $theme: mat-dark-theme($primary, $accent);
.mat-ripple-element
{
background-color: rgba(255, 255, 255, .3) !important;
background-color: rgba(255, 255, 255, .3) !important;
}
.mat-card-header-text
{
margin: 0 5px !important;
margin: 0 5px !important;
}
//Material Icons
@font-face {
font-family: 'Material Icons';
font-style: normal;
font-weight: 400;
src: url(/iconfont/MaterialIcons-Regular.eot); /* For IE6-8 */
src: local('Material Icons'),
local('MaterialIcons-Regular'),
url(/iconfont/MaterialIcons-Regular.woff2) format('woff2'),
url(/iconfont/MaterialIcons-Regular.woff) format('woff'),
url(/iconfont/MaterialIcons-Regular.ttf) format('truetype');
font-family: 'Material Icons';
font-style: normal;
font-weight: 400;
src: url(/iconfont/MaterialIcons-Regular.eot); /* For IE6-8 */
src: local('Material Icons'),
local('MaterialIcons-Regular'),
url(/iconfont/MaterialIcons-Regular.woff2) format('woff2'),
url(/iconfont/MaterialIcons-Regular.woff) format('woff'),
url(/iconfont/MaterialIcons-Regular.ttf) format('truetype');
}
.material-icons
{
font-family: 'Material Icons';
font-weight: normal;
font-style: normal;
font-size: 24px; /* Preferred icon size */
display: inline-block;
line-height: 1;
text-transform: none;
letter-spacing: normal;
word-wrap: normal;
white-space: nowrap;
direction: ltr;
/* Support for all WebKit browsers. */
-webkit-font-smoothing: antialiased;
/* Support for Safari and Chrome. */
text-rendering: optimizeLegibility;
/* Support for Firefox. */
-moz-osx-font-smoothing: grayscale;
/* Support for IE. */
font-feature-settings: 'liga';
font-family: 'Material Icons';
font-weight: normal;
font-style: normal;
font-size: 24px; /* Preferred icon size */
display: inline-block;
line-height: 1;
text-transform: none;
letter-spacing: normal;
word-wrap: normal;
white-space: nowrap;
direction: ltr;
/* Support for all WebKit browsers. */
-webkit-font-smoothing: antialiased;
/* Support for Safari and Chrome. */
text-rendering: optimizeLegibility;
/* Support for Firefox. */
-moz-osx-font-smoothing: grayscale;
/* Support for IE. */
font-feature-settings: 'liga';
}
mat-icon
{
vertical-align: middle;
vertical-align: middle;
}
.snackError
{
background-color: theme-color("accentColor");
color: theme-color("textPrimary");
background-color: theme-color("accentColor");
color: theme-color("textPrimary");
}
.scroll-row
{
padding-left: 0;
padding-right: 0;
padding-left: 0;
padding-right: 0;
}

View File

@ -3,16 +3,16 @@
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: any;
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);

View File

@ -3,157 +3,157 @@ Object.defineProperty(exports, "__esModule", { value: true });
var detect_browser_1 = require("detect-browser");
var method;
(function (method) {
method["direct"] = "Direct Play";
method["transmux"] = "Transmux";
method["transcode"] = "Transcode";
method["direct"] = "Direct Play";
method["transmux"] = "Transmux";
method["transcode"] = "Transcode";
})(method = exports.method || (exports.method = {}));
;
var SupportList = /** @class */ (function () {
function SupportList() {
}
return SupportList;
function SupportList() {
}
return SupportList;
}());
exports.SupportList = SupportList;
function getPlaybackMethod(player, item) {
var supportList = getWhatIsSupported(player, item);
if (supportList.container) {
if (supportList.videoCodec && supportList.audioCodec)
return method.direct;
return method.transcode;
}
if (supportList.videoCodec && supportList.audioCodec)
return method.transmux;
return method.transcode;
var supportList = getWhatIsSupported(player, item);
if (supportList.container) {
if (supportList.videoCodec && supportList.audioCodec)
return method.direct;
return method.transcode;
}
if (supportList.videoCodec && supportList.audioCodec)
return method.transmux;
return method.transcode;
}
exports.getPlaybackMethod = getPlaybackMethod;
function getWhatIsSupported(player, item) {
var supportList = new SupportList();
var browser = detect_browser_1.detect();
if (!browser) {
supportList.container = false;
supportList.videoCodec = false;
supportList.audioCodec = false;
}
else {
supportList.container = containerIsSupported(player, item.container, browser.name) && item.audios.length <= 1;
supportList.videoCodec = videoCodecIsSupported(player, item.video.codec, browser.name);
supportList.audioCodec = audioCodecIsSupported(player, item.audios.map(function (value) { return value.codec; }), browser.name);
}
return (supportList);
var supportList = new SupportList();
var browser = detect_browser_1.detect();
if (!browser) {
supportList.container = false;
supportList.videoCodec = false;
supportList.audioCodec = false;
}
else {
supportList.container = containerIsSupported(player, item.container, browser.name) && item.audios.length <= 1;
supportList.videoCodec = videoCodecIsSupported(player, item.video.codec, browser.name);
supportList.audioCodec = audioCodecIsSupported(player, item.audios.map(function (value) { return value.codec; }), browser.name);
}
return (supportList);
}
exports.getWhatIsSupported = getWhatIsSupported;
function containerIsSupported(player, container, browser) {
var supported = false;
switch (container) {
case "asf":
supported = browser == "tizen" || browser == "orsay" || browser == "edge";
//videoAudioCodecs = [];
break;
case "avi":
supported = browser == "tizen" || browser == "orsay" || browser == "edge";
break;
case "mpg":
case "mpeg":
supported = browser == "tizen" || browser == "orsay" || browser == "edge";
break;
case "flv":
supported = browser == "tizen" || browser == "orsay";
break;
case "3gp":
case "mts":
case "trp":
case "vob":
case "vro":
supported = browser == "tizen" || browser == "orsay";
break;
case "mov":
supported = browser == "tizen" || browser == "orsay" || browser == "edge" || browser == "chrome";
break;
case "m2ts":
supported = browser == "tizen" || browser == "orsay" || browser == "edge";
break;
case "wmv":
supported = browser == "tizen" || browser == "orsay" || browser == "edge";
//videoAudioCodecs = [];
break;
case "ts":
supported = browser == "tizen" || browser == "orsay" || browser == "edge";
break;
case "mp4":
case "m4v":
supported = true;
break;
case "mkv":
supported = browser == "tizen" || browser == "orsay" || browser == "chrome" || browser == "edge";
if (supported)
break;
if (player.canPlayType("video/x-matroska") || player.canPlayType("video/mkv"))
supported = true;
break;
default:
break;
}
return supported;
var supported = false;
switch (container) {
case "asf":
supported = browser == "tizen" || browser == "orsay" || browser == "edge";
//videoAudioCodecs = [];
break;
case "avi":
supported = browser == "tizen" || browser == "orsay" || browser == "edge";
break;
case "mpg":
case "mpeg":
supported = browser == "tizen" || browser == "orsay" || browser == "edge";
break;
case "flv":
supported = browser == "tizen" || browser == "orsay";
break;
case "3gp":
case "mts":
case "trp":
case "vob":
case "vro":
supported = browser == "tizen" || browser == "orsay";
break;
case "mov":
supported = browser == "tizen" || browser == "orsay" || browser == "edge" || browser == "chrome";
break;
case "m2ts":
supported = browser == "tizen" || browser == "orsay" || browser == "edge";
break;
case "wmv":
supported = browser == "tizen" || browser == "orsay" || browser == "edge";
//videoAudioCodecs = [];
break;
case "ts":
supported = browser == "tizen" || browser == "orsay" || browser == "edge";
break;
case "mp4":
case "m4v":
supported = true;
break;
case "mkv":
supported = browser == "tizen" || browser == "orsay" || browser == "chrome" || browser == "edge";
if (supported)
break;
if (player.canPlayType("video/x-matroska") || player.canPlayType("video/mkv"))
supported = true;
break;
default:
break;
}
return supported;
}
//SHOULD CHECK FOR DEPTH (8bits ok but 10bits unsuported for almost every browsers)
function videoCodecIsSupported(player, codec, browser) {
switch (codec) {
case "h264":
return !!player.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"'); //The !! is used to parse the string as a bool
case "h265":
case "hevc":
if (browser == "tizen" || browser == "orsay" || browser == "xboxOne" || browser == "ios")
return true;
//SHOULD SUPPORT CHROMECAST ULTRA
// if (browser.chromecast)
// {
// var isChromecastUltra = userAgent.indexOf('aarch64') !== -1;
// if (isChromecastUltra)
// {
// return true;
// }
// }
return !!player.canPlayType('video/hevc; codecs="hevc, aac"');
case "mpeg2video":
return browser == "orsay" || browser == "tizen" || browser == "edge";
case "vc1":
return browser == "orsay" || browser == "tizen" || browser == "edge";
case "msmpeg4v2":
return browser == "orsay" || browser == "tizen";
case "vp8":
return !!player.canPlayType('video/webm; codecs="vp8');
case "vp9":
return !!player.canPlayType('video/webm; codecs="vp9"');
case "vorbis":
return browser == "orsay" || browser == "tizen" || !!player.canPlayType('video/webm; codecs="vp8');
default:
return false;
}
switch (codec) {
case "h264":
return !!player.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"'); //The !! is used to parse the string as a bool
case "h265":
case "hevc":
if (browser == "tizen" || browser == "orsay" || browser == "xboxOne" || browser == "ios")
return true;
//SHOULD SUPPORT CHROMECAST ULTRA
// if (browser.chromecast)
// {
// var isChromecastUltra = userAgent.indexOf('aarch64') !== -1;
// if (isChromecastUltra)
// {
// return true;
// }
// }
return !!player.canPlayType('video/hevc; codecs="hevc, aac"');
case "mpeg2video":
return browser == "orsay" || browser == "tizen" || browser == "edge";
case "vc1":
return browser == "orsay" || browser == "tizen" || browser == "edge";
case "msmpeg4v2":
return browser == "orsay" || browser == "tizen";
case "vp8":
return !!player.canPlayType('video/webm; codecs="vp8');
case "vp9":
return !!player.canPlayType('video/webm; codecs="vp9"');
case "vorbis":
return browser == "orsay" || browser == "tizen" || !!player.canPlayType('video/webm; codecs="vp8');
default:
return false;
}
}
//SHOULD CHECK FOR NUMBER OF AUDIO CHANNEL (2 ok but 5 not in some browsers)
function audioCodecIsSupported(player, codecs, browser) {
for (var _i = 0, codecs_1 = codecs; _i < codecs_1.length; _i++) {
var codec = codecs_1[_i];
switch (codec) {
case "mp3":
return !!player.canPlayType('video/mp4; codecs="avc1.640029, mp4a.69"') ||
!!player.canPlayType('video/mp4; codecs="avc1.640029, mp4a.6B"');
case "aac":
return !!player.canPlayType('video/mp4; codecs="avc1.640029, mp4a.40.2"');
case "mp2":
return browser == "orsay" || browser == "tizen" || browser == "edge";
case "pcm_s16le":
case "pcm_s24le":
return browser == "orsay" || browser == "tizen" || browser == "edge";
case "aac_latm":
return browser == "orsay" || browser == "tizen";
case "opus":
return !!player.canPlayType('audio/ogg; codecs="opus"');
case "flac":
return browser == "orsay" || browser == "tizen" || browser == "edge";
default:
return false;
}
}
for (var _i = 0, codecs_1 = codecs; _i < codecs_1.length; _i++) {
var codec = codecs_1[_i];
switch (codec) {
case "mp3":
return !!player.canPlayType('video/mp4; codecs="avc1.640029, mp4a.69"') ||
!!player.canPlayType('video/mp4; codecs="avc1.640029, mp4a.6B"');
case "aac":
return !!player.canPlayType('video/mp4; codecs="avc1.640029, mp4a.40.2"');
case "mp2":
return browser == "orsay" || browser == "tizen" || browser == "edge";
case "pcm_s16le":
case "pcm_s24le":
return browser == "orsay" || browser == "tizen" || browser == "edge";
case "aac_latm":
return browser == "orsay" || browser == "tizen";
case "opus":
return !!player.canPlayType('audio/ogg; codecs="opus"');
case "flac":
return browser == "orsay" || browser == "tizen" || browser == "edge";
default:
return false;
}
}
}
//# sourceMappingURL=playbackMethodDetector.js.map

View File

@ -121,16 +121,16 @@ function videoCodecIsSupported(player: HTMLVideoElement, codec: string, browser:
case "hevc":
if (browser == "tizen" || browser == "orsay" || browser == "xboxOne" || browser == "ios")
return true;
//SHOULD SUPPORT CHROMECAST ULTRA
// if (browser.chromecast)
// {
//SHOULD SUPPORT CHROMECAST ULTRA
// if (browser.chromecast)
// {
// var isChromecastUltra = userAgent.indexOf('aarch64') !== -1;
// if (isChromecastUltra)
// {
// return true;
// }
// }
// var isChromecastUltra = userAgent.indexOf('aarch64') !== -1;
// if (isChromecastUltra)
// {
// return true;
// }
// }
return !!player.canPlayType('video/hevc; codecs="hevc, aac"');
case "mpeg2video":
return browser == "orsay" || browser == "tizen" || browser == "edge";
@ -145,7 +145,7 @@ function videoCodecIsSupported(player: HTMLVideoElement, codec: string, browser:
case "vorbis":
return browser == "orsay" || browser == "tizen" || !!player.canPlayType('video/webm; codecs="vp8');
default:
return false;
return false;
}
}
@ -175,5 +175,5 @@ function audioCodecIsSupported(player: HTMLVideoElement, codecs: string[], brows
default:
return false;
}
}
}
}