mirror of
				https://github.com/zoriya/Kyoo.git
				synced 2025-11-03 19:17:16 -05:00 
			
		
		
		
	Fixing coding style
This commit is contained in:
		
							parent
							
								
									d36e332bcb
								
							
						
					
					
						commit
						1dbbc0a67a
					
				@ -1,10 +1,10 @@
 | 
			
		||||
import { NgModule } from '@angular/core';
 | 
			
		||||
import { NgModule } from "@angular/core";
 | 
			
		||||
import { RouteReuseStrategy, RouterModule, Routes } from "@angular/router";
 | 
			
		||||
import { ItemsGridComponent } from './components/items-grid/items-grid.component';
 | 
			
		||||
import { ItemsGridComponent } from "./components/items-grid/items-grid.component";
 | 
			
		||||
import { CustomRouteReuseStrategy } from "./misc/custom-route-reuse-strategy";
 | 
			
		||||
import { NotFoundComponent } from './pages/not-found/not-found.component';
 | 
			
		||||
import { PageResolver } from './services/page-resolver.service';
 | 
			
		||||
import { ShowDetailsComponent } from './pages/show-details/show-details.component';
 | 
			
		||||
import { NotFoundComponent } from "./pages/not-found/not-found.component";
 | 
			
		||||
import { PageResolver } from "./services/page-resolver.service";
 | 
			
		||||
import { ShowDetailsComponent } from "./pages/show-details/show-details.component";
 | 
			
		||||
import { AuthGuard } from "./auth/misc/authenticated-guard.service";
 | 
			
		||||
import { LibraryItem } from "./models/resources/library-item";
 | 
			
		||||
import {
 | 
			
		||||
@ -92,7 +92,7 @@ const routes: Routes = [
 | 
			
		||||
 | 
			
		||||
	// TODO implement an home page.
 | 
			
		||||
 | 
			
		||||
	{path: "", pathMatch: 'full', redirectTo: "/browse"},
 | 
			
		||||
	{path: "", pathMatch: "full", redirectTo: "/browse"},
 | 
			
		||||
	{path: "**", component: NotFoundComponent}
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -15,7 +15,7 @@
 | 
			
		||||
			<ul class="navbar-nav flex-row flex-nowrap ml-auto">
 | 
			
		||||
				<li class="nav-item icon searchbar">
 | 
			
		||||
					<mat-icon matTooltipPosition="below" matTooltip="Search" (click)="openSearch()">search</mat-icon>
 | 
			
		||||
					<input placeholder="Search" id="search" type="search" (input)="onUpdateValue($event)"/>
 | 
			
		||||
					<input placeholder="Search" id="search" type="search" (input)="onUpdateValue($any($event))"/>
 | 
			
		||||
				</li>
 | 
			
		||||
 | 
			
		||||
				<li class="nav-item" *ngIf="!this.isAuthenticated else accountDrop">
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import {Component} from '@angular/core';
 | 
			
		||||
import { Component } from "@angular/core";
 | 
			
		||||
import {
 | 
			
		||||
	Event,
 | 
			
		||||
	Router,
 | 
			
		||||
@ -13,19 +13,21 @@ import {AccountComponent} from "./auth/account/account.component";
 | 
			
		||||
import { AuthService } from "./auth/auth.service";
 | 
			
		||||
import { Library } from "./models/resources/library";
 | 
			
		||||
import { LibraryService } from "./services/api.service";
 | 
			
		||||
// noinspection ES6UnusedImports
 | 
			
		||||
import * as $ from "jquery";
 | 
			
		||||
import ChangeEvent = JQuery.ChangeEvent;
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
	selector: 'app-root',
 | 
			
		||||
	templateUrl: './app.component.html',
 | 
			
		||||
	styleUrls: ['./app.component.scss']
 | 
			
		||||
	selector: "app-root",
 | 
			
		||||
	templateUrl: "./app.component.html",
 | 
			
		||||
	styleUrls: ["./app.component.scss"]
 | 
			
		||||
})
 | 
			
		||||
export class AppComponent
 | 
			
		||||
{
 | 
			
		||||
	static isMobile: boolean = false;
 | 
			
		||||
	libraries: Library[];
 | 
			
		||||
	isLoading: boolean = false;
 | 
			
		||||
 | 
			
		||||
	static isMobile: boolean = false;
 | 
			
		||||
 | 
			
		||||
	constructor(private libraryService: LibraryService,
 | 
			
		||||
	            private router: Router,
 | 
			
		||||
@ -61,18 +63,23 @@ export class AppComponent
 | 
			
		||||
			document.body.classList.add("hoverEnabled");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	openSearch()
 | 
			
		||||
	get isAuthenticated(): boolean
 | 
			
		||||
	{
 | 
			
		||||
		let input: HTMLInputElement = <HTMLInputElement>document.getElementById("search");
 | 
			
		||||
		return this.authManager.isAuthenticated;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	openSearch(): void
 | 
			
		||||
	{
 | 
			
		||||
		const input: HTMLInputElement = document.getElementById("search") as HTMLInputElement;
 | 
			
		||||
 | 
			
		||||
		input.value = "";
 | 
			
		||||
		input.focus();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	onUpdateValue(event)
 | 
			
		||||
	onUpdateValue(event: ChangeEvent<HTMLInputElement>): void
 | 
			
		||||
	{
 | 
			
		||||
		let query: string = event.target.value;
 | 
			
		||||
		if (query != "")
 | 
			
		||||
		const query: string = event.target.value;
 | 
			
		||||
		if (query !== "")
 | 
			
		||||
		{
 | 
			
		||||
			event.target.classList.add("searching");
 | 
			
		||||
			this.router.navigate(["/search", query], {
 | 
			
		||||
@ -86,13 +93,8 @@ export class AppComponent
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	openAccountDialog()
 | 
			
		||||
	openAccountDialog(): void
 | 
			
		||||
	{
 | 
			
		||||
		this.dialog.open(AccountComponent, {width: "500px", data: this.authManager.account});
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	get isAuthenticated(): boolean
 | 
			
		||||
	{
 | 
			
		||||
		return this.authManager.isAuthenticated;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,45 +1,45 @@
 | 
			
		||||
import { HttpClientModule } from '@angular/common/http';
 | 
			
		||||
import { HttpClientModule } from "@angular/common/http";
 | 
			
		||||
import { APP_INITIALIZER, NgModule } from "@angular/core";
 | 
			
		||||
import { MatButtonModule } from '@angular/material/button';
 | 
			
		||||
import { MatCardModule } from '@angular/material/card';
 | 
			
		||||
import { MatRippleModule } from '@angular/material/core';
 | 
			
		||||
import { MatIconModule } from '@angular/material/icon';
 | 
			
		||||
import { MatMenuModule } from '@angular/material/menu';
 | 
			
		||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
 | 
			
		||||
import { MatSelectModule } from '@angular/material/select';
 | 
			
		||||
import { MatSliderModule } from '@angular/material/slider';
 | 
			
		||||
import { MatSnackBarModule } from '@angular/material/snack-bar';
 | 
			
		||||
import { MatTooltipModule } from '@angular/material/tooltip';
 | 
			
		||||
import { MatButtonModule } from "@angular/material/button";
 | 
			
		||||
import { MatCardModule } from "@angular/material/card";
 | 
			
		||||
import { MatRippleModule } from "@angular/material/core";
 | 
			
		||||
import { MatIconModule } from "@angular/material/icon";
 | 
			
		||||
import { MatMenuModule } from "@angular/material/menu";
 | 
			
		||||
import { MatProgressBarModule } from "@angular/material/progress-bar";
 | 
			
		||||
import { MatSelectModule } from "@angular/material/select";
 | 
			
		||||
import { MatSliderModule } from "@angular/material/slider";
 | 
			
		||||
import { MatSnackBarModule } from "@angular/material/snack-bar";
 | 
			
		||||
import { MatTooltipModule } from "@angular/material/tooltip";
 | 
			
		||||
import { BrowserModule, HammerModule } from "@angular/platform-browser";
 | 
			
		||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
 | 
			
		||||
import { AppRoutingModule } from './app-routing.module';
 | 
			
		||||
import { AppComponent } from './app.component';
 | 
			
		||||
import { ItemsGridComponent } from './components/items-grid/items-grid.component';
 | 
			
		||||
import { CollectionComponent } from './pages/collection/collection.component';
 | 
			
		||||
import { EpisodesListComponent } from './components/episodes-list/episodes-list.component';
 | 
			
		||||
import { NotFoundComponent } from './pages/not-found/not-found.component';
 | 
			
		||||
import { PeopleListComponent } from './components/people-list/people-list.component';
 | 
			
		||||
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
 | 
			
		||||
import { AppRoutingModule } from "./app-routing.module";
 | 
			
		||||
import { AppComponent } from "./app.component";
 | 
			
		||||
import { ItemsGridComponent } from "./components/items-grid/items-grid.component";
 | 
			
		||||
import { CollectionComponent } from "./pages/collection/collection.component";
 | 
			
		||||
import { EpisodesListComponent } from "./components/episodes-list/episodes-list.component";
 | 
			
		||||
import { NotFoundComponent } from "./pages/not-found/not-found.component";
 | 
			
		||||
import { PeopleListComponent } from "./components/people-list/people-list.component";
 | 
			
		||||
import {
 | 
			
		||||
	BufferToWidthPipe,
 | 
			
		||||
	FormatTimePipe,
 | 
			
		||||
	PlayerComponent, SupportedButtonPipe,
 | 
			
		||||
	VolumeToButtonPipe
 | 
			
		||||
} from "./pages/player/player.component";
 | 
			
		||||
import { SearchComponent } from './pages/search/search.component';
 | 
			
		||||
import { ShowDetailsComponent } from './pages/show-details/show-details.component';
 | 
			
		||||
import { SearchComponent } from "./pages/search/search.component";
 | 
			
		||||
import { ShowDetailsComponent } from "./pages/show-details/show-details.component";
 | 
			
		||||
import { FormsModule , ReactiveFormsModule } from "@angular/forms";
 | 
			
		||||
import { MatInputModule } from "@angular/material/input";
 | 
			
		||||
import { MatFormFieldModule } from "@angular/material/form-field";
 | 
			
		||||
import { MatTabsModule } from "@angular/material/tabs";
 | 
			
		||||
import { PasswordValidator } from "./misc/password-validator";
 | 
			
		||||
import { MatCheckboxModule } from "@angular/material/checkbox";
 | 
			
		||||
import { MatDialogModule } from '@angular/material/dialog';
 | 
			
		||||
import { MatDialogModule } from "@angular/material/dialog";
 | 
			
		||||
import { FallbackDirective } from "./misc/fallback.directive";
 | 
			
		||||
import { AuthModule } from "./auth/auth.module";
 | 
			
		||||
import { AuthRoutingModule } from "./auth/auth-routing.module";
 | 
			
		||||
import { TrailerDialogComponent } from './pages/trailer-dialog/trailer-dialog.component';
 | 
			
		||||
import { TrailerDialogComponent } from "./pages/trailer-dialog/trailer-dialog.component";
 | 
			
		||||
import { ItemsListComponent } from "./components/items-list/items-list.component";
 | 
			
		||||
import { MetadataEditComponent } from './pages/metadata-edit/metadata-edit.component';
 | 
			
		||||
import { MetadataEditComponent } from "./pages/metadata-edit/metadata-edit.component";
 | 
			
		||||
import { MatChipsModule } from "@angular/material/chips";
 | 
			
		||||
import { MatAutocompleteModule } from "@angular/material/autocomplete";
 | 
			
		||||
import { MatExpansionModule } from "@angular/material/expansion";
 | 
			
		||||
@ -47,7 +47,7 @@ import { InfiniteScrollModule } from "ngx-infinite-scroll";
 | 
			
		||||
import { ShowGridComponent } from "./components/show-grid/show-grid.component";
 | 
			
		||||
import { MatBadgeModule } from "@angular/material/badge";
 | 
			
		||||
import { StartupService } from "./services/startup.service";
 | 
			
		||||
import { LongPressDirective } from './misc/long-press.directive';
 | 
			
		||||
import { LongPressDirective } from "./misc/long-press.directive";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
@ -105,6 +105,9 @@ import { LongPressDirective } from './misc/long-press.directive';
 | 
			
		||||
		HammerModule
 | 
			
		||||
	],
 | 
			
		||||
	bootstrap: [AppComponent],
 | 
			
		||||
	exports: [
 | 
			
		||||
		FallbackDirective
 | 
			
		||||
	],
 | 
			
		||||
	providers: [
 | 
			
		||||
		StartupService,
 | 
			
		||||
		{
 | 
			
		||||
 | 
			
		||||
@ -1,13 +1,13 @@
 | 
			
		||||
import {Component, ElementRef, Inject, ViewChild} from '@angular/core';
 | 
			
		||||
import { Component, ElementRef, Inject, ViewChild } from "@angular/core";
 | 
			
		||||
import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material/dialog";
 | 
			
		||||
import { HttpClient } from "@angular/common/http";
 | 
			
		||||
import { Account } from "../../models/account";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-account',
 | 
			
		||||
  templateUrl: './account.component.html',
 | 
			
		||||
  styleUrls: ['./account.component.scss']
 | 
			
		||||
  selector: "app-account",
 | 
			
		||||
  templateUrl: "./account.component.html",
 | 
			
		||||
  styleUrls: ["./account.component.scss"]
 | 
			
		||||
})
 | 
			
		||||
export class AccountComponent
 | 
			
		||||
{
 | 
			
		||||
@ -18,9 +18,9 @@ export class AccountComponent
 | 
			
		||||
	            @Inject(MAT_DIALOG_DATA) public account: Account,
 | 
			
		||||
	            private http: HttpClient) {}
 | 
			
		||||
 | 
			
		||||
	finish()
 | 
			
		||||
	finish(): void
 | 
			
		||||
	{
 | 
			
		||||
		let data = new FormData();
 | 
			
		||||
		const data: FormData = new FormData();
 | 
			
		||||
		data.append("email", this.account.email);
 | 
			
		||||
		data.append("username", this.account.username);
 | 
			
		||||
		data.append("picture", this.selectedPicture);
 | 
			
		||||
@ -31,15 +31,15 @@ export class AccountComponent
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cancel() 
 | 
			
		||||
	cancel(): void
 | 
			
		||||
	{
 | 
			
		||||
		this.dialogRef.close();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	onPictureSelected(event: any)
 | 
			
		||||
	onPictureSelected(event: any): void
 | 
			
		||||
	{
 | 
			
		||||
		this.selectedPicture = event.target.files[0];
 | 
			
		||||
		const reader = new FileReader();
 | 
			
		||||
		const reader: FileReader = new FileReader();
 | 
			
		||||
		reader.onloadend = () =>
 | 
			
		||||
		{
 | 
			
		||||
			this.accountImg.nativeElement.src = reader.result;
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import {NgModule} from '@angular/core';
 | 
			
		||||
import { NgModule } from "@angular/core";
 | 
			
		||||
import { RouterModule, Routes } from "@angular/router";
 | 
			
		||||
import { UnauthorizedComponent } from "./unauthorized/unauthorized.component";
 | 
			
		||||
import { LogoutComponent } from "./logout/logout.component";
 | 
			
		||||
 | 
			
		||||
@ -25,7 +25,7 @@ import { AuthGuard } from "./misc/authenticated-guard.service";
 | 
			
		||||
import { AuthorizerInterceptor } from "./misc/authorizer-interceptor.service";
 | 
			
		||||
import { UnauthorizedComponent } from "./unauthorized/unauthorized.component";
 | 
			
		||||
 | 
			
		||||
export function loadConfig(oidcConfigService: OidcConfigService)
 | 
			
		||||
export function loadConfig(oidcConfigService: OidcConfigService): () => Promise<any>
 | 
			
		||||
{
 | 
			
		||||
	return () => oidcConfigService.withConfig({
 | 
			
		||||
		stsServer: window.location.origin,
 | 
			
		||||
 | 
			
		||||
@ -1,18 +1,16 @@
 | 
			
		||||
import { HttpClient } from "@angular/common/http";
 | 
			
		||||
import { Injectable } from '@angular/core';
 | 
			
		||||
import { Injectable } from "@angular/core";
 | 
			
		||||
import { OidcSecurityService } from "angular-auth-oidc-client";
 | 
			
		||||
import { Account } from "../models/account";
 | 
			
		||||
 | 
			
		||||
@Injectable({
 | 
			
		||||
	providedIn: 'root'
 | 
			
		||||
	providedIn: "root"
 | 
			
		||||
})
 | 
			
		||||
export class AuthService
 | 
			
		||||
{
 | 
			
		||||
	isAuthenticated: boolean = false;
 | 
			
		||||
	account: Account = null;
 | 
			
		||||
 | 
			
		||||
	constructor(private oidcSecurityService: OidcSecurityService,
 | 
			
		||||
	            private http: HttpClient)
 | 
			
		||||
	constructor(private oidcSecurityService: OidcSecurityService)
 | 
			
		||||
	{
 | 
			
		||||
		this.oidcSecurityService.checkAuth()
 | 
			
		||||
			.subscribe((auth: boolean) => this.isAuthenticated = auth);
 | 
			
		||||
@ -24,17 +22,17 @@ export class AuthService
 | 
			
		||||
				email: x.email,
 | 
			
		||||
				username: x.username,
 | 
			
		||||
				picture: x.picture,
 | 
			
		||||
				permissions: x.permissions.split(',')
 | 
			
		||||
				permissions: x.permissions.split(",")
 | 
			
		||||
			};
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	login()
 | 
			
		||||
	login(): void
 | 
			
		||||
	{
 | 
			
		||||
		this.oidcSecurityService.authorize();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logout() 
 | 
			
		||||
	logout(): void
 | 
			
		||||
	{
 | 
			
		||||
		// this.http.get("api/account/logout").subscribe(() =>
 | 
			
		||||
		// {
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,9 @@
 | 
			
		||||
import {Injector, Pipe, PipeTransform} from '@angular/core';
 | 
			
		||||
import { Injector, Pipe, PipeTransform } from "@angular/core";
 | 
			
		||||
import { HttpClient, HttpHeaders } from "@angular/common/http";
 | 
			
		||||
import { OidcSecurityService } from "angular-auth-oidc-client";
 | 
			
		||||
 | 
			
		||||
@Pipe({
 | 
			
		||||
	name: 'auth'
 | 
			
		||||
	name: "auth"
 | 
			
		||||
})
 | 
			
		||||
export class AuthPipe implements PipeTransform
 | 
			
		||||
{
 | 
			
		||||
@ -15,12 +15,12 @@ export class AuthPipe implements PipeTransform
 | 
			
		||||
	{
 | 
			
		||||
		if (this.oidcSecurity === undefined)
 | 
			
		||||
			this.oidcSecurity = this.injector.get(OidcSecurityService);
 | 
			
		||||
		let token = this.oidcSecurity.getToken();
 | 
			
		||||
		const token: string = this.oidcSecurity.getToken();
 | 
			
		||||
		if (!token)
 | 
			
		||||
			return uri;
 | 
			
		||||
		const headers = new HttpHeaders({"Authorization": "Bearer " + token});
 | 
			
		||||
		const img = await this.http.get(uri, {headers, responseType: 'blob'}).toPromise();
 | 
			
		||||
		const reader = new FileReader();
 | 
			
		||||
		const headers: HttpHeaders = new HttpHeaders({Authorization: "Bearer " + token});
 | 
			
		||||
		const img: Blob = await this.http.get(uri, {headers, responseType: "blob"}).toPromise();
 | 
			
		||||
		const reader: FileReader = new FileReader();
 | 
			
		||||
		return new Promise((resolve) => {
 | 
			
		||||
			reader.onloadend = () => resolve(reader.result as string);
 | 
			
		||||
			reader.readAsDataURL(img);
 | 
			
		||||
 | 
			
		||||
@ -1,14 +1,6 @@
 | 
			
		||||
import {Injectable} from '@angular/core';
 | 
			
		||||
import {
 | 
			
		||||
	CanActivate,
 | 
			
		||||
	CanLoad,
 | 
			
		||||
	Route,
 | 
			
		||||
	UrlSegment,
 | 
			
		||||
	ActivatedRouteSnapshot,
 | 
			
		||||
	RouterStateSnapshot,
 | 
			
		||||
	Router
 | 
			
		||||
} from '@angular/router';
 | 
			
		||||
import {Observable} from 'rxjs';
 | 
			
		||||
import { Injectable } from "@angular/core";
 | 
			
		||||
import { CanActivate, CanLoad, Router } from "@angular/router";
 | 
			
		||||
import { Observable } from "rxjs";
 | 
			
		||||
import { AuthService } from "../auth.service";
 | 
			
		||||
 | 
			
		||||
@Injectable({providedIn: "root"})
 | 
			
		||||
@ -18,14 +10,14 @@ export class AuthGuard
 | 
			
		||||
	public static defaultPermissions: string[];
 | 
			
		||||
	public static permissionsObservable: Observable<string[]>;
 | 
			
		||||
 | 
			
		||||
	static forPermissions(...permissions: string[])
 | 
			
		||||
	static forPermissions(...permissions: string[]): any
 | 
			
		||||
	{
 | 
			
		||||
		@Injectable()
 | 
			
		||||
		class AuthenticatedGuard implements CanActivate, CanLoad
 | 
			
		||||
		{
 | 
			
		||||
			constructor(private router: Router, private authManager: AuthService)  {}
 | 
			
		||||
 | 
			
		||||
			async canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean>
 | 
			
		||||
			async canActivate(): Promise<boolean>
 | 
			
		||||
			{
 | 
			
		||||
				if (!await this.checkPermissions())
 | 
			
		||||
				{
 | 
			
		||||
@ -35,7 +27,7 @@ export class AuthGuard
 | 
			
		||||
				return true;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			async canLoad(route: Route, segments: UrlSegment[]): Promise<boolean>
 | 
			
		||||
			async canLoad(): Promise<boolean>
 | 
			
		||||
			{
 | 
			
		||||
				if (!await this.checkPermissions())
 | 
			
		||||
				{
 | 
			
		||||
@ -50,7 +42,7 @@ export class AuthGuard
 | 
			
		||||
				if (this.authManager.isAuthenticated)
 | 
			
		||||
				{
 | 
			
		||||
					const perms: string[] = this.authManager.account.permissions;
 | 
			
		||||
					for (let perm of permissions) {
 | 
			
		||||
					for (const perm of permissions) {
 | 
			
		||||
						if (!perms.includes(perm))
 | 
			
		||||
							return false;
 | 
			
		||||
					}
 | 
			
		||||
@ -58,10 +50,10 @@ export class AuthGuard
 | 
			
		||||
				}
 | 
			
		||||
				else
 | 
			
		||||
				{
 | 
			
		||||
					if (AuthGuard.defaultPermissions == undefined)
 | 
			
		||||
						await AuthGuard.permissionsObservable.toPromise()
 | 
			
		||||
					if (!AuthGuard.defaultPermissions)
 | 
			
		||||
						await AuthGuard.permissionsObservable.toPromise();
 | 
			
		||||
 | 
			
		||||
					for (let perm of permissions)
 | 
			
		||||
					for (const perm of permissions)
 | 
			
		||||
						if (!AuthGuard.defaultPermissions.includes(perm))
 | 
			
		||||
							return false;
 | 
			
		||||
					return true;
 | 
			
		||||
 | 
			
		||||
@ -1,11 +1,11 @@
 | 
			
		||||
import {Injectable, Injector} from '@angular/core';
 | 
			
		||||
import { Injectable, Injector } from "@angular/core";
 | 
			
		||||
import {
 | 
			
		||||
	HttpRequest,
 | 
			
		||||
	HttpHandler,
 | 
			
		||||
	HttpEvent,
 | 
			
		||||
	HttpInterceptor
 | 
			
		||||
} from '@angular/common/http';
 | 
			
		||||
import { Observable } from 'rxjs';
 | 
			
		||||
} from "@angular/common/http";
 | 
			
		||||
import { Observable } from "rxjs";
 | 
			
		||||
import { OidcSecurityService } from "angular-auth-oidc-client";
 | 
			
		||||
 | 
			
		||||
@Injectable()
 | 
			
		||||
@ -20,7 +20,7 @@ export class AuthorizerInterceptor implements HttpInterceptor
 | 
			
		||||
	{
 | 
			
		||||
		if (this.oidcSecurity === undefined)
 | 
			
		||||
			this.oidcSecurity = this.injector.get(OidcSecurityService);
 | 
			
		||||
		let token = this.oidcSecurity.getToken();
 | 
			
		||||
		const token: string = this.oidcSecurity.getToken();
 | 
			
		||||
		if (token)
 | 
			
		||||
			request = request.clone({setHeaders: {Authorization: "Bearer " + token}});
 | 
			
		||||
		return next.handle(request);
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,10 @@
 | 
			
		||||
import { Component, OnInit } from '@angular/core';
 | 
			
		||||
import { Component } from "@angular/core";
 | 
			
		||||
import { AuthService } from "../auth.service";
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
 	selector: 'app-unauthorized',
 | 
			
		||||
 	templateUrl: './unauthorized.component.html',
 | 
			
		||||
 	styleUrls: ['./unauthorized.component.scss']
 | 
			
		||||
 	selector: "app-unauthorized",
 | 
			
		||||
 	templateUrl: "./unauthorized.component.html",
 | 
			
		||||
 	styleUrls: ["./unauthorized.component.scss"]
 | 
			
		||||
})
 | 
			
		||||
export class UnauthorizedComponent
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@ -30,7 +30,7 @@ export class EpisodesListComponent extends HorizontalScroller
 | 
			
		||||
 | 
			
		||||
	openMenu(index: number): void
 | 
			
		||||
	{
 | 
			
		||||
		const menu = this.menus.find((x, i) => i === index);
 | 
			
		||||
		const menu: MatMenuTrigger = this.menus.find((x, i) => i === index);
 | 
			
		||||
		menu.focus();
 | 
			
		||||
		menu.openMenu();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
import { Component, Input, OnInit } from "@angular/core";
 | 
			
		||||
import { FormControl } from "@angular/forms";
 | 
			
		||||
import { ActivatedRoute, ActivatedRouteSnapshot, Params, Router } from "@angular/router";
 | 
			
		||||
import { DomSanitizer } from '@angular/platform-browser';
 | 
			
		||||
import { DomSanitizer, SafeStyle } from "@angular/platform-browser";
 | 
			
		||||
import { Genre } from "../../models/resources/genre";
 | 
			
		||||
import { LibraryItem } from "../../models/resources/library-item";
 | 
			
		||||
import { Page } from "../../models/page";
 | 
			
		||||
@ -14,36 +14,16 @@ import { Studio } from "../../models/resources/studio";
 | 
			
		||||
import { ItemsUtils } from "../../misc/items-utils";
 | 
			
		||||
import { PeopleService, StudioService } from "../../services/api.service";
 | 
			
		||||
import { PreLoaderService } from "../../services/pre-loader.service";
 | 
			
		||||
import { Observable } from "rxjs"
 | 
			
		||||
import { Observable } from "rxjs";
 | 
			
		||||
import { catchError, filter, map, mergeAll } from "rxjs/operators";
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
	selector: 'app-items-grid',
 | 
			
		||||
	templateUrl: './items-grid.component.html',
 | 
			
		||||
	styleUrls: ['./items-grid.component.scss']
 | 
			
		||||
	selector: "app-items-grid",
 | 
			
		||||
	templateUrl: "./items-grid.component.html",
 | 
			
		||||
	styleUrls: ["./items-grid.component.scss"]
 | 
			
		||||
})
 | 
			
		||||
export class ItemsGridComponent implements OnInit
 | 
			
		||||
{
 | 
			
		||||
	@Input() page: Page<LibraryItem | Show | ShowRole | Collection>;
 | 
			
		||||
	@Input() sortEnabled: boolean = true;
 | 
			
		||||
 | 
			
		||||
	complexFiltersEnabled: boolean;
 | 
			
		||||
 | 
			
		||||
	sortType: string = "title";
 | 
			
		||||
	sortKeys: string[] = ["title", "start year", "end year"]
 | 
			
		||||
	sortUp: boolean = true;
 | 
			
		||||
 | 
			
		||||
	public static readonly showOnlyFilters: string[] = ["genres", "studio", "people"]
 | 
			
		||||
	public static readonly filters: string[] = [].concat(...ItemsGridComponent.showOnlyFilters)
 | 
			
		||||
	filters: {genres: Genre[], studio: Studio, people: People[]} = {genres: [], studio: null, people: []};
 | 
			
		||||
 | 
			
		||||
	genres: Genre[] = [];
 | 
			
		||||
 | 
			
		||||
	studioForm: FormControl = new FormControl();
 | 
			
		||||
	filteredStudios: Observable<Studio[]>;
 | 
			
		||||
 | 
			
		||||
	peopleForm: FormControl = new FormControl();
 | 
			
		||||
	filteredPeople: Observable<People[]>;
 | 
			
		||||
 | 
			
		||||
	constructor(private route: ActivatedRoute,
 | 
			
		||||
	            private sanitizer: DomSanitizer,
 | 
			
		||||
@ -70,57 +50,103 @@ export class ItemsGridComponent implements OnInit
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	updateGenresFilterFromQuery(query: Params)
 | 
			
		||||
	public static readonly showOnlyFilters: string[] = ["genres", "studio", "people"];
 | 
			
		||||
	public static readonly filters: string[] = [].concat(...ItemsGridComponent.showOnlyFilters);
 | 
			
		||||
	@Input() page: Page<LibraryItem | Show | ShowRole | Collection>;
 | 
			
		||||
	@Input() sortEnabled: boolean = true;
 | 
			
		||||
 | 
			
		||||
	complexFiltersEnabled: boolean;
 | 
			
		||||
 | 
			
		||||
	sortType: string = "title";
 | 
			
		||||
	sortKeys: string[] = ["title", "start year", "end year"];
 | 
			
		||||
	sortUp: boolean = true;
 | 
			
		||||
	filters: {genres: Genre[], studio: Studio, people: People[]} = {genres: [], studio: null, people: []};
 | 
			
		||||
 | 
			
		||||
	genres: Genre[] = [];
 | 
			
		||||
 | 
			
		||||
	studioForm: FormControl = new FormControl();
 | 
			
		||||
	filteredStudios: Observable<Studio[]>;
 | 
			
		||||
 | 
			
		||||
	peopleForm: FormControl = new FormControl();
 | 
			
		||||
	filteredPeople: Observable<People[]>;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * /browse           -> /api/items | /api/shows
 | 
			
		||||
	 * /browse/:library  -> /api/library/:slug/items | /api/library/:slug/shows
 | 
			
		||||
	 * /genre/:slug      -> /api/shows
 | 
			
		||||
	 * /studio/:slug     -> /api/shows
 | 
			
		||||
	 *
 | 
			
		||||
	 * /collection/:slug -> /api/collection/:slug/shows   |> /api/collections/:slug/shows
 | 
			
		||||
	 * /people/:slug     -> /api/people/:slug/roles       |> /api/people/:slug/roles
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	static routeMapper(route: ActivatedRouteSnapshot, endpoint: string, query: [string, string][]): string
 | 
			
		||||
	{
 | 
			
		||||
		const queryParams: [string, string][] = Object.entries(route.queryParams)
 | 
			
		||||
			.filter(x => ItemsGridComponent.filters.includes(x[0]) || x[0] === "sortBy");
 | 
			
		||||
		if (query)
 | 
			
		||||
			queryParams.push(...query);
 | 
			
		||||
 | 
			
		||||
		if (queryParams.some(x => ItemsGridComponent.showOnlyFilters.includes(x[0])))
 | 
			
		||||
			endpoint = endpoint.replace(/items?$/, "show");
 | 
			
		||||
 | 
			
		||||
		const params: string = queryParams.length > 0
 | 
			
		||||
			? "?" + queryParams.map(x => `${x[0]}=${x[1]}`).join("&")
 | 
			
		||||
			: "";
 | 
			
		||||
		return `api/${endpoint}${params}`;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	updateGenresFilterFromQuery(query: Params): void
 | 
			
		||||
	{
 | 
			
		||||
		let selectedGenres: string[] = [];
 | 
			
		||||
		if (query.genres?.startsWith("ctn:"))
 | 
			
		||||
			selectedGenres = query.genres.substr(4).split(',');
 | 
			
		||||
			selectedGenres = query.genres.substr(4).split(",");
 | 
			
		||||
		else if (query.genres != null)
 | 
			
		||||
			selectedGenres = query.genres.split(',');
 | 
			
		||||
			selectedGenres = query.genres.split(",");
 | 
			
		||||
		if (this.router.url.startsWith("/genre"))
 | 
			
		||||
			selectedGenres.push(this.route.snapshot.params.slug);
 | 
			
		||||
 | 
			
		||||
		this.filters.genres = this.genres.filter(x => selectedGenres.includes(x.slug));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	updateStudioFilterFromQuery(query: Params)
 | 
			
		||||
	updateStudioFilterFromQuery(query: Params): void
 | 
			
		||||
	{
 | 
			
		||||
		const slug: string = this.router.url.startsWith("/studio") ? this.route.snapshot.params.slug : query.studio;
 | 
			
		||||
 | 
			
		||||
		if (slug && this.filters.studio?.slug != slug)
 | 
			
		||||
		if (slug && this.filters.studio?.slug !== slug)
 | 
			
		||||
		{
 | 
			
		||||
			this.filters.studio = {id: 0, slug: slug, name: slug};
 | 
			
		||||
			this.filters.studio = {id: 0, slug, name: slug};
 | 
			
		||||
			this.studioApi.get(slug).subscribe(x => this.filters.studio = x);
 | 
			
		||||
		}
 | 
			
		||||
		else if (!slug)
 | 
			
		||||
			this.filters.studio = null;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	updatePeopleFilterFromQuery(query: Params)
 | 
			
		||||
	updatePeopleFilterFromQuery(query: Params): void
 | 
			
		||||
	{
 | 
			
		||||
		let slugs: string[] = [];
 | 
			
		||||
		if (query.people != null)
 | 
			
		||||
		{
 | 
			
		||||
			if (query.people.startsWith("ctn:"))
 | 
			
		||||
				slugs = query.people.substr(4).split(',');
 | 
			
		||||
				slugs = query.people.substr(4).split(",");
 | 
			
		||||
			else
 | 
			
		||||
				slugs = query.people.split(',');
 | 
			
		||||
				slugs = query.people.split(",");
 | 
			
		||||
		}
 | 
			
		||||
		else if (this.route.snapshot.params.slug && this.router.url.startsWith("/people"))
 | 
			
		||||
			slugs = [this.route.snapshot.params.slug];
 | 
			
		||||
 | 
			
		||||
		this.filters.people = slugs.map(x => ({slug: x, name: x} as People));
 | 
			
		||||
		for (let slug of slugs)
 | 
			
		||||
		for (const slug of slugs)
 | 
			
		||||
		{
 | 
			
		||||
			this.peopleApi.get(slug).subscribe(x =>
 | 
			
		||||
			{
 | 
			
		||||
				let i: number = this.filters.people.findIndex(x => x.slug == slug);
 | 
			
		||||
				this.filters.people[i] = x
 | 
			
		||||
				const i: number = this.filters.people.findIndex(y => y.slug === slug);
 | 
			
		||||
				this.filters.people[i] = x;
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ngOnInit()
 | 
			
		||||
	ngOnInit(): void
 | 
			
		||||
	{
 | 
			
		||||
		this.filteredStudios = this.studioForm.valueChanges
 | 
			
		||||
			.pipe(
 | 
			
		||||
@ -149,38 +175,12 @@ export class ItemsGridComponent implements OnInit
 | 
			
		||||
			);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	shouldDisplayNoneStudio()
 | 
			
		||||
	shouldDisplayNoneStudio(): boolean
 | 
			
		||||
	{
 | 
			
		||||
		return this.studioForm.value == '' || typeof this.studioForm.value != "string";
 | 
			
		||||
		return this.studioForm.value === "" || typeof this.studioForm.value !== "string";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * /browse           -> /api/items | /api/shows
 | 
			
		||||
	 * /browse/:library  -> /api/library/:slug/items | /api/library/:slug/shows
 | 
			
		||||
	 * /genre/:slug      -> /api/shows
 | 
			
		||||
	 * /studio/:slug     -> /api/shows
 | 
			
		||||
	 *
 | 
			
		||||
	 * /collection/:slug -> /api/collection/:slug/shows   |> /api/collections/:slug/shows
 | 
			
		||||
	 * /people/:slug     -> /api/people/:slug/roles       |> /api/people/:slug/roles
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	static routeMapper(route: ActivatedRouteSnapshot, endpoint: string, query: [string, string][]): string
 | 
			
		||||
	{
 | 
			
		||||
		let queryParams: [string, string][] = Object.entries(route.queryParams)
 | 
			
		||||
			.filter(x => ItemsGridComponent.filters.includes(x[0]) || x[0] == "sortBy");
 | 
			
		||||
		if (query)
 | 
			
		||||
			queryParams.push(...query)
 | 
			
		||||
 | 
			
		||||
		if (queryParams.some(x => ItemsGridComponent.showOnlyFilters.includes(x[0])))
 | 
			
		||||
			endpoint = endpoint.replace(/items?$/, "show");
 | 
			
		||||
 | 
			
		||||
		let params: string = queryParams.length > 0
 | 
			
		||||
			? '?' + queryParams.map(x => `${x[0]}=${x[1]}`).join('&')
 | 
			
		||||
			: "";
 | 
			
		||||
		return `api/${endpoint}${params}`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	getFilterCount()
 | 
			
		||||
	getFilterCount(): number
 | 
			
		||||
	{
 | 
			
		||||
		let count: number = this.filters.genres.length + this.filters.people.length;
 | 
			
		||||
		if (this.filters.studio != null)
 | 
			
		||||
@ -188,39 +188,39 @@ export class ItemsGridComponent implements OnInit
 | 
			
		||||
		return count;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	addFilter(category: string, filter: IResource, isArray: boolean = true, toggle: boolean = false)
 | 
			
		||||
	addFilter(category: string, resource: IResource, isArray: boolean = true, toggle: boolean = false): void
 | 
			
		||||
	{
 | 
			
		||||
		if (isArray)
 | 
			
		||||
		{
 | 
			
		||||
			if (this.filters[category].includes(filter) || this.filters[category].some(x => x.slug == filter.slug))
 | 
			
		||||
				this.filters[category].splice(this.filters[category].indexOf(filter), 1);
 | 
			
		||||
			if (this.filters[category].includes(resource) || this.filters[category].some(x => x.slug === resource.slug))
 | 
			
		||||
				this.filters[category].splice(this.filters[category].indexOf(resource), 1);
 | 
			
		||||
			else
 | 
			
		||||
				this.filters[category].push(filter);
 | 
			
		||||
				this.filters[category].push(resource);
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			if (filter && (this.filters[category] == filter || this.filters[category]?.slug == filter.slug))
 | 
			
		||||
			if (resource && (this.filters[category] === resource || this.filters[category]?.slug === resource.slug))
 | 
			
		||||
			{
 | 
			
		||||
				if (!toggle)
 | 
			
		||||
					return;
 | 
			
		||||
				this.filters[category] = null;
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
				this.filters[category] = filter;
 | 
			
		||||
				this.filters[category] = resource;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		let param: string = null;
 | 
			
		||||
		if (isArray && this.filters[category].length > 0)
 | 
			
		||||
			param = `${this.filters[category].map(x => x.slug).join(',')}`;
 | 
			
		||||
			param = `${this.filters[category].map(x => x.slug).join(",")}`;
 | 
			
		||||
		else if (!isArray && this.filters[category] != null)
 | 
			
		||||
			param = filter.slug;
 | 
			
		||||
			param = resource.slug;
 | 
			
		||||
 | 
			
		||||
		if (/\/browse($|\?)/.test(this.router.url)
 | 
			
		||||
			|| this.router.url.startsWith("/genre")
 | 
			
		||||
			|| this.router.url.startsWith("/studio")
 | 
			
		||||
			|| this.router.url.startsWith("/people"))
 | 
			
		||||
		{
 | 
			
		||||
			if (this.filters.genres.length == 1 && this.getFilterCount() == 1)
 | 
			
		||||
			if (this.filters.genres.length === 1 && this.getFilterCount() === 1)
 | 
			
		||||
			{
 | 
			
		||||
				this.router.navigate(["genre", this.filters.genres[0].slug], {
 | 
			
		||||
					replaceUrl: true,
 | 
			
		||||
@ -228,7 +228,7 @@ export class ItemsGridComponent implements OnInit
 | 
			
		||||
				});
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
			if (this.filters.studio != null && this.getFilterCount() == 1)
 | 
			
		||||
			if (this.filters.studio != null && this.getFilterCount() === 1)
 | 
			
		||||
			{
 | 
			
		||||
				this.router.navigate(["studio", this.filters.studio.slug], {
 | 
			
		||||
					replaceUrl: true,
 | 
			
		||||
@ -236,7 +236,7 @@ export class ItemsGridComponent implements OnInit
 | 
			
		||||
				});
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
			if (this.filters.people.length == 1 && this.getFilterCount() == 1)
 | 
			
		||||
			if (this.filters.people.length === 1 && this.getFilterCount() === 1)
 | 
			
		||||
			{
 | 
			
		||||
				this.router.navigate(["people", this.filters.people[0].slug], {
 | 
			
		||||
					replaceUrl: true,
 | 
			
		||||
@ -244,14 +244,14 @@ export class ItemsGridComponent implements OnInit
 | 
			
		||||
				});
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
 			if (this.getFilterCount() == 0 || this.router.url != "/browse")
 | 
			
		||||
 		if (this.getFilterCount() === 0 || this.router.url !== "/browse")
 | 
			
		||||
			{
 | 
			
		||||
				let params = {[category]: param}
 | 
			
		||||
				if (this.router.url.startsWith("/studio") && category != "studio")
 | 
			
		||||
				const params: {[key: string]: string} = {[category]: param};
 | 
			
		||||
				if (this.router.url.startsWith("/studio") && category !== "studio")
 | 
			
		||||
					params.studio = this.route.snapshot.params.slug;
 | 
			
		||||
				if (this.router.url.startsWith("/genre") && category != "genres")
 | 
			
		||||
				if (this.router.url.startsWith("/genre") && category !== "genres")
 | 
			
		||||
					params.genres = `${this.route.snapshot.params.slug}`;
 | 
			
		||||
				if (this.router.url.startsWith("/people") && category != "people")
 | 
			
		||||
				if (this.router.url.startsWith("/people") && category !== "people")
 | 
			
		||||
					params.people = `${this.route.snapshot.params.slug}`;
 | 
			
		||||
 | 
			
		||||
				this.router.navigate(["/browse"], {
 | 
			
		||||
@ -270,32 +270,32 @@ export class ItemsGridComponent implements OnInit
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	nameGetter(obj: Studio)
 | 
			
		||||
	nameGetter(obj: Studio): string
 | 
			
		||||
	{
 | 
			
		||||
		return obj?.name ?? "None";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	getThumb(slug: string)
 | 
			
		||||
	getThumb(slug: string): SafeStyle
 | 
			
		||||
	{
 | 
			
		||||
		return this.sanitizer.bypassSecurityTrustStyle("url(/poster/" + slug + ")");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	getDate(item: LibraryItem | Show | ShowRole | Collection)
 | 
			
		||||
	getDate(item: LibraryItem | Show | ShowRole | Collection): string
 | 
			
		||||
	{
 | 
			
		||||
		return ItemsUtils.getDate(item);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	getLink(item: LibraryItem | Show | ShowRole | Collection)
 | 
			
		||||
	getLink(item: LibraryItem | Show | ShowRole | Collection): string
 | 
			
		||||
	{
 | 
			
		||||
		return ItemsUtils.getLink(item);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sort(type: string, order: boolean)
 | 
			
		||||
	sort(type: string, order: boolean): void
 | 
			
		||||
	{
 | 
			
		||||
		this.sortType = type;
 | 
			
		||||
		this.sortUp = order;
 | 
			
		||||
 | 
			
		||||
		let param: string = `${this.sortType.replace(/\s/g, "")}:${this.sortUp ? "asc" : "desc"}`;
 | 
			
		||||
		const param: string = `${this.sortType.replace(/\s/g, "")}:${this.sortUp ? "asc" : "desc"}`;
 | 
			
		||||
		this.router.navigate([], {
 | 
			
		||||
			relativeTo: this.route,
 | 
			
		||||
			queryParams: { sortBy: param },
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
import { Component, Input } from "@angular/core";
 | 
			
		||||
import { Collection } from "../../models/resources/collection";
 | 
			
		||||
import {DomSanitizer} from "@angular/platform-browser";
 | 
			
		||||
import { DomSanitizer, SafeUrl } from "@angular/platform-browser";
 | 
			
		||||
import { HorizontalScroller } from "../../misc/horizontal-scroller";
 | 
			
		||||
import { Page } from "../../models/page";
 | 
			
		||||
import { HttpClient } from "@angular/common/http";
 | 
			
		||||
@ -9,9 +9,9 @@ import {LibraryItem} from "../../models/resources/library-item";
 | 
			
		||||
import { ItemsUtils } from "../../misc/items-utils";
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
	selector: 'app-items-list',
 | 
			
		||||
	templateUrl: './items-list.component.html',
 | 
			
		||||
	styleUrls: ['./items-list.component.scss']
 | 
			
		||||
	selector: "app-items-list",
 | 
			
		||||
	templateUrl: "./items-list.component.html",
 | 
			
		||||
	styleUrls: ["./items-list.component.scss"]
 | 
			
		||||
})
 | 
			
		||||
export class ItemsListComponent extends HorizontalScroller
 | 
			
		||||
{
 | 
			
		||||
@ -22,17 +22,17 @@ export class ItemsListComponent extends HorizontalScroller
 | 
			
		||||
		super();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	getThumb(slug: string)
 | 
			
		||||
	getThumb(slug: string): SafeUrl
 | 
			
		||||
	{
 | 
			
		||||
		return this.sanitizer.bypassSecurityTrustStyle("url(/poster/" + slug + ")");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	getDate(item: LibraryItem | Show | ShowRole | Collection)
 | 
			
		||||
	getDate(item: LibraryItem | Show | ShowRole | Collection): string
 | 
			
		||||
	{
 | 
			
		||||
		return ItemsUtils.getDate(item);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	getLink(item: LibraryItem | Show | ShowRole | Collection)
 | 
			
		||||
	getLink(item: LibraryItem | Show | ShowRole | Collection): string
 | 
			
		||||
	{
 | 
			
		||||
		return ItemsUtils.getLink(item);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -1,15 +1,14 @@
 | 
			
		||||
import { Component, ElementRef, Input, ViewChild } from '@angular/core';
 | 
			
		||||
import { MatButton } from "@angular/material/button";
 | 
			
		||||
import { DomSanitizer } from "@angular/platform-browser";
 | 
			
		||||
import { Component, Input } from "@angular/core";
 | 
			
		||||
import { DomSanitizer, SafeStyle } from "@angular/platform-browser";
 | 
			
		||||
import { People } from "../../models/resources/people";
 | 
			
		||||
import { HorizontalScroller } from "../../misc/horizontal-scroller";
 | 
			
		||||
import { Page } from "../../models/page";
 | 
			
		||||
import { HttpClient } from "@angular/common/http";
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
	selector: 'app-people-list',
 | 
			
		||||
	templateUrl: './people-list.component.html',
 | 
			
		||||
	styleUrls: ['./people-list.component.scss']
 | 
			
		||||
	selector: "app-people-list",
 | 
			
		||||
	templateUrl: "./people-list.component.html",
 | 
			
		||||
	styleUrls: ["./people-list.component.scss"]
 | 
			
		||||
})
 | 
			
		||||
export class PeopleListComponent extends HorizontalScroller
 | 
			
		||||
{
 | 
			
		||||
@ -20,7 +19,7 @@ export class PeopleListComponent extends HorizontalScroller
 | 
			
		||||
		super();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	getPeopleIcon(slug: string)
 | 
			
		||||
	getPeopleIcon(slug: string): SafeStyle
 | 
			
		||||
	{
 | 
			
		||||
		return this.sanitizer.bypassSecurityTrustStyle("url(/peopleimg/" + slug + ")");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -1,27 +1,27 @@
 | 
			
		||||
import {Component, EventEmitter, Input, Output} from '@angular/core';
 | 
			
		||||
import {DomSanitizer} from "@angular/platform-browser";
 | 
			
		||||
import { Component, EventEmitter, Input, Output } from "@angular/core";
 | 
			
		||||
import { DomSanitizer, SafeStyle } from "@angular/platform-browser";
 | 
			
		||||
import { Show } from "../../models/resources/show";
 | 
			
		||||
import { Page } from "../../models/page";
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
	selector: 'app-shows-grid',
 | 
			
		||||
	templateUrl: './show-grid.component.html',
 | 
			
		||||
	styleUrls: ['./show-grid.component.scss']
 | 
			
		||||
	selector: "app-shows-grid",
 | 
			
		||||
	templateUrl: "./show-grid.component.html",
 | 
			
		||||
	styleUrls: ["./show-grid.component.scss"]
 | 
			
		||||
})
 | 
			
		||||
export class ShowGridComponent
 | 
			
		||||
{
 | 
			
		||||
	@Input() shows: Page<Show>
 | 
			
		||||
	@Input() shows: Page<Show>;
 | 
			
		||||
	@Input() externalShows: boolean = false;
 | 
			
		||||
	@Output() clickCallback: EventEmitter<Show> = new EventEmitter();
 | 
			
		||||
 | 
			
		||||
	constructor(private sanitizer: DomSanitizer) { }
 | 
			
		||||
 | 
			
		||||
	getThumb(show: Show)
 | 
			
		||||
	getThumb(show: Show): SafeStyle
 | 
			
		||||
	{
 | 
			
		||||
		return this.sanitizer.bypassSecurityTrustStyle(`url(${show.poster})`);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	getLink(show: Show)
 | 
			
		||||
	getLink(show: Show): string
 | 
			
		||||
	{
 | 
			
		||||
		if (this.externalShows)
 | 
			
		||||
			return null;
 | 
			
		||||
 | 
			
		||||
@ -4,30 +4,30 @@ export class CustomRouteReuseStrategy extends RouteReuseStrategy
 | 
			
		||||
{
 | 
			
		||||
	shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean
 | 
			
		||||
	{
 | 
			
		||||
		if (curr.routeConfig?.path == "browse"
 | 
			
		||||
			|| curr.routeConfig?.path == "genre/:slug"
 | 
			
		||||
			|| curr.routeConfig?.path == "studio/:slug")
 | 
			
		||||
		if (curr.routeConfig?.path === "browse"
 | 
			
		||||
			|| curr.routeConfig?.path === "genre/:slug"
 | 
			
		||||
			|| curr.routeConfig?.path === "studio/:slug")
 | 
			
		||||
		{
 | 
			
		||||
			return future.routeConfig.path == "browse"
 | 
			
		||||
				|| future.routeConfig.path == "genre/:slug"
 | 
			
		||||
				|| future.routeConfig.path == "studio/:slug";
 | 
			
		||||
			return future.routeConfig.path === "browse"
 | 
			
		||||
				|| future.routeConfig.path === "genre/:slug"
 | 
			
		||||
				|| future.routeConfig.path === "studio/:slug";
 | 
			
		||||
		}
 | 
			
		||||
		return future.routeConfig === curr.routeConfig;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	shouldAttach(route: ActivatedRouteSnapshot): boolean
 | 
			
		||||
	shouldAttach(): boolean
 | 
			
		||||
	{
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	shouldDetach(route: ActivatedRouteSnapshot): boolean
 | 
			
		||||
	shouldDetach(): boolean
 | 
			
		||||
	{
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle | null): void  {}
 | 
			
		||||
	store(): void  {}
 | 
			
		||||
 | 
			
		||||
	retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null
 | 
			
		||||
	retrieve(): DetachedRouteHandle | null
 | 
			
		||||
	{
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,7 @@
 | 
			
		||||
import { Component, ElementRef, ViewChild } from "@angular/core";
 | 
			
		||||
import { ElementRef, ViewChild } from "@angular/core";
 | 
			
		||||
import { MatButton } from "@angular/material/button";
 | 
			
		||||
 | 
			
		||||
// noinspection AngularMissingOrInvalidDeclarationInModule
 | 
			
		||||
@Component({
 | 
			
		||||
	template: ""
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
export class HorizontalScroller
 | 
			
		||||
{
 | 
			
		||||
	@ViewChild("scrollView", { static: true }) private scrollView: ElementRef;
 | 
			
		||||
@ -12,35 +9,37 @@ export class HorizontalScroller
 | 
			
		||||
	@ViewChild("rightBtn", { static: false }) private rightBtn: MatButton;
 | 
			
		||||
	@ViewChild("itemsDom", { static: false }) private itemsDom: ElementRef;
 | 
			
		||||
 | 
			
		||||
	scrollLeft()
 | 
			
		||||
	scrollLeft(): void
 | 
			
		||||
	{
 | 
			
		||||
		let scroll: number = this.roundScroll(this.scrollView.nativeElement.offsetWidth * 0.80);
 | 
			
		||||
		const scroll: number = this.roundScroll(this.scrollView.nativeElement.offsetWidth * 0.80);
 | 
			
		||||
		this.scrollView.nativeElement.scrollBy({ top: 0, left: -scroll, behavior: "smooth" });
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	scrollRight()
 | 
			
		||||
	scrollRight(): void
 | 
			
		||||
	{
 | 
			
		||||
		let scroll: number = this.roundScroll(this.scrollView.nativeElement.offsetWidth * 0.80);
 | 
			
		||||
		const scroll: number = this.roundScroll(this.scrollView.nativeElement.offsetWidth * 0.80);
 | 
			
		||||
		this.scrollView.nativeElement.scrollBy({ top: 0, left: scroll, behavior: "smooth" });
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	roundScroll(offset: number): number
 | 
			
		||||
	{
 | 
			
		||||
		let itemSize: number = this.itemsDom.nativeElement.scrollWidth;
 | 
			
		||||
		const itemSize: number = this.itemsDom.nativeElement.scrollWidth;
 | 
			
		||||
 | 
			
		||||
		offset = Math.round(offset / itemSize) * itemSize;
 | 
			
		||||
		if (offset == 0)
 | 
			
		||||
		if (offset === 0)
 | 
			
		||||
			offset = itemSize;
 | 
			
		||||
		return offset;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	onScroll()
 | 
			
		||||
	onScroll(): void
 | 
			
		||||
	{
 | 
			
		||||
		if (this.scrollView.nativeElement.scrollLeft <= 0)
 | 
			
		||||
		const scroll: any = this.scrollView.nativeElement;
 | 
			
		||||
 | 
			
		||||
		if (scroll.scrollLeft <= 0)
 | 
			
		||||
			this.leftBtn._elementRef.nativeElement.classList.add("d-none");
 | 
			
		||||
		else
 | 
			
		||||
			this.leftBtn._elementRef.nativeElement.classList.remove("d-none");
 | 
			
		||||
		if (this.scrollView.nativeElement.scrollLeft >= this.scrollView.nativeElement.scrollWidth - this.scrollView.nativeElement.clientWidth)
 | 
			
		||||
		if (scroll.scrollLeft >= scroll.scrollWidth - scroll.clientWidth)
 | 
			
		||||
			this.rightBtn._elementRef.nativeElement.classList.add("d-none");
 | 
			
		||||
		else
 | 
			
		||||
			this.rightBtn._elementRef.nativeElement.classList.remove("d-none");
 | 
			
		||||
 | 
			
		||||
@ -7,7 +7,7 @@ export class ItemsUtils
 | 
			
		||||
{
 | 
			
		||||
	static getLink(item: LibraryItem | Show | ShowRole | Collection): string
 | 
			
		||||
	{
 | 
			
		||||
		if ("type" in item && item.type == ItemType.Collection)
 | 
			
		||||
		if ("type" in item && item.type === ItemType.Collection)
 | 
			
		||||
			return "/collection/" + item.slug;
 | 
			
		||||
		else
 | 
			
		||||
			return "/show/" + item.slug;
 | 
			
		||||
@ -21,13 +21,13 @@ export class ItemsUtils
 | 
			
		||||
				return `as ${item.role} (${item.type})`;
 | 
			
		||||
			return `as ${item.role}`;
 | 
			
		||||
		}
 | 
			
		||||
		if ("type" in item && item.type && typeof item.type == "string")
 | 
			
		||||
		if ("type" in item && item.type && typeof item.type === "string")
 | 
			
		||||
			return item.type;
 | 
			
		||||
 | 
			
		||||
		if (!("startYear" in item))
 | 
			
		||||
			return "";
 | 
			
		||||
		if (item.endYear && item.startYear != item.endYear)
 | 
			
		||||
			return `${item.startYear} - ${item.endYear}`
 | 
			
		||||
		if (item.endYear && item.startYear !== item.endYear)
 | 
			
		||||
			return `${item.startYear} - ${item.endYear}`;
 | 
			
		||||
		return item.startYear?.toString();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,10 @@
 | 
			
		||||
import { Directive, Output, EventEmitter, HostListener, HostBinding, ElementRef } from "@angular/core";
 | 
			
		||||
import MouseDownEvent = JQuery.MouseDownEvent;
 | 
			
		||||
import TouchStartEvent = JQuery.TouchStartEvent;
 | 
			
		||||
import ContextMenuEvent = JQuery.ContextMenuEvent;
 | 
			
		||||
import ClickEvent = JQuery.ClickEvent;
 | 
			
		||||
 | 
			
		||||
function cancelClick(event): void
 | 
			
		||||
function cancelClick(event: ClickEvent): void
 | 
			
		||||
{
 | 
			
		||||
	event.preventDefault();
 | 
			
		||||
	event.stopPropagation();
 | 
			
		||||
@ -19,7 +21,10 @@ export class LongPressDirective
 | 
			
		||||
 | 
			
		||||
	constructor(private ref: ElementRef) {}
 | 
			
		||||
 | 
			
		||||
	@HostBinding('class.longpress')
 | 
			
		||||
	@HostBinding("style.-webkit-touch-callout")
 | 
			
		||||
	defaultLongTouchEvent: string = "none";
 | 
			
		||||
 | 
			
		||||
	@HostBinding("class.longpress")
 | 
			
		||||
	get longPress(): boolean
 | 
			
		||||
	{
 | 
			
		||||
		return this._timer !== null;
 | 
			
		||||
@ -29,10 +34,10 @@ export class LongPressDirective
 | 
			
		||||
	@HostListener("mousedown", ["$event"])
 | 
			
		||||
	start(event: MouseDownEvent | TouchStartEvent): void
 | 
			
		||||
	{
 | 
			
		||||
		const startBox = event.target.getBoundingClientRect();
 | 
			
		||||
		const startBox: DOMRect = event.target.getBoundingClientRect();
 | 
			
		||||
		this._timer = setTimeout(() =>
 | 
			
		||||
		{
 | 
			
		||||
			const endBox = event.target.getBoundingClientRect();
 | 
			
		||||
			const endBox: DOMRect = event.target.getBoundingClientRect();
 | 
			
		||||
			if (startBox.top !== endBox.top || startBox.left !== endBox.left)
 | 
			
		||||
				return;
 | 
			
		||||
			this.longPressed.emit();
 | 
			
		||||
@ -63,11 +68,8 @@ export class LongPressDirective
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@HostListener("contextmenu", ["$event"])
 | 
			
		||||
	context(event): void
 | 
			
		||||
	context(event: ContextMenuEvent): void
 | 
			
		||||
	{
 | 
			
		||||
		event.preventDefault();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@HostBinding("style.-webkit-touch-callout")
 | 
			
		||||
	defaultLongTouchEvent: string = "none";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@ import {AbstractControl, NG_VALIDATORS, Validator} from "@angular/forms";
 | 
			
		||||
import { Directive } from "@angular/core";
 | 
			
		||||
 | 
			
		||||
@Directive({
 | 
			
		||||
	selector: "[passwordValidator]",
 | 
			
		||||
	selector: "[appPasswordValidator]",
 | 
			
		||||
	providers: [{provide: NG_VALIDATORS, useExisting: PasswordValidator, multi: true}]
 | 
			
		||||
})
 | 
			
		||||
export class PasswordValidator implements Validator
 | 
			
		||||
@ -12,15 +12,15 @@ export class PasswordValidator implements Validator
 | 
			
		||||
		if (!control.value)
 | 
			
		||||
			return null;
 | 
			
		||||
		if (!/[a-z]/.test(control.value))
 | 
			
		||||
			return {"passwordError": {error: "The password must contains a lowercase letter."}};
 | 
			
		||||
			return {passwordError: {error: "The password must contains a lowercase letter."}};
 | 
			
		||||
		if (!/[A-Z]/.test(control.value))
 | 
			
		||||
			return {"passwordError": {error: "The password must contains an uppercase letter."}};
 | 
			
		||||
			return {passwordError: {error: "The password must contains an uppercase letter."}};
 | 
			
		||||
		if (!/[0-9]/.test(control.value))
 | 
			
		||||
			return {"passwordError": {error: "The password must contains a digit."}};
 | 
			
		||||
			return {passwordError: {error: "The password must contains a digit."}};
 | 
			
		||||
		if (!/\W/.test(control.value))
 | 
			
		||||
			return {"passwordError": {error: "The password must contains a non-alphanumeric character."}};
 | 
			
		||||
			return {passwordError: {error: "The password must contains a non-alphanumeric character."}};
 | 
			
		||||
		if (control.value.toString().length < 6)
 | 
			
		||||
			return {"passwordError": {error: "Password must be at least 6 character long."}};
 | 
			
		||||
			return {passwordError: {error: "Password must be at least 6 character long."}};
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import {Provider} from "./provider"
 | 
			
		||||
import { Provider } from "./provider";
 | 
			
		||||
 | 
			
		||||
export interface ExternalID
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,7 @@ export class Page<T>
 | 
			
		||||
		Object.assign(this, init);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	loadNext(client: HttpClient)
 | 
			
		||||
	loadNext(client: HttpClient): void
 | 
			
		||||
	{
 | 
			
		||||
		if (this.next == null || this._isLoading)
 | 
			
		||||
			return;
 | 
			
		||||
@ -30,8 +30,8 @@ export class Page<T>
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	changeType(type: string)
 | 
			
		||||
	changeType(type: string): string
 | 
			
		||||
	{
 | 
			
		||||
		return this.first.replace(/\/\w*($|\?)/, `/${type}$1`)
 | 
			
		||||
		return this.first.replace(/\/\w*($|\?)/, `/${type}$1`);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@ export interface Collection extends IResource
 | 
			
		||||
	name: string;
 | 
			
		||||
	poster: string;
 | 
			
		||||
	overview: string;
 | 
			
		||||
	startYear: number,
 | 
			
		||||
	endYear: number,
 | 
			
		||||
	startYear: number;
 | 
			
		||||
	endYear: number;
 | 
			
		||||
	shows: Show[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,7 @@ export interface Episode extends IResource
 | 
			
		||||
	title: string;
 | 
			
		||||
	thumb: string;
 | 
			
		||||
	overview: string;
 | 
			
		||||
	releaseDate;
 | 
			
		||||
	releaseDate: string;
 | 
			
		||||
	runtime: number;
 | 
			
		||||
	showTitle: string;
 | 
			
		||||
	externalIDs: ExternalID[];
 | 
			
		||||
 | 
			
		||||
@ -9,12 +9,12 @@ export enum ItemType
 | 
			
		||||
 | 
			
		||||
export interface LibraryItem extends IResource
 | 
			
		||||
{
 | 
			
		||||
	title: string
 | 
			
		||||
	overview: string
 | 
			
		||||
	status: string
 | 
			
		||||
	trailerUrl: string
 | 
			
		||||
	startYear: number
 | 
			
		||||
	endYear: number
 | 
			
		||||
	poster: string
 | 
			
		||||
	type: ItemType
 | 
			
		||||
	title: string;
 | 
			
		||||
	overview: string;
 | 
			
		||||
	status: string;
 | 
			
		||||
	trailerUrl: string;
 | 
			
		||||
	startYear: number;
 | 
			
		||||
	endYear: number;
 | 
			
		||||
	poster: string;
 | 
			
		||||
	type: ItemType;
 | 
			
		||||
}
 | 
			
		||||
@ -8,5 +8,5 @@ export interface Season extends IResource
 | 
			
		||||
	title: string;
 | 
			
		||||
	overview: string;
 | 
			
		||||
	episodes: Episode[];
 | 
			
		||||
	externalIDs: ExternalID[]
 | 
			
		||||
	externalIDs: ExternalID[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -9,7 +9,7 @@ export interface WatchItem
 | 
			
		||||
	title: string;
 | 
			
		||||
	slug: string;
 | 
			
		||||
	duration: number;
 | 
			
		||||
	releaseDate;
 | 
			
		||||
	releaseDate: string;
 | 
			
		||||
	isMovie: boolean;
 | 
			
		||||
 | 
			
		||||
	previousEpisode: Episode;
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
import {Component} from '@angular/core';
 | 
			
		||||
import { Component } from "@angular/core";
 | 
			
		||||
import { Collection } from "../../models/resources/collection";
 | 
			
		||||
import { ActivatedRoute } from "@angular/router";
 | 
			
		||||
import {DomSanitizer} from "@angular/platform-browser";
 | 
			
		||||
import { DomSanitizer, SafeStyle } from "@angular/platform-browser";
 | 
			
		||||
import { Show, ShowRole } from "../../models/resources/show";
 | 
			
		||||
import { Page } from "../../models/page";
 | 
			
		||||
import { People } from "../../models/resources/people";
 | 
			
		||||
@ -9,9 +9,9 @@ import {LibraryItem} from "../../models/resources/library-item";
 | 
			
		||||
import { ItemsUtils } from "../../misc/items-utils";
 | 
			
		||||
 | 
			
		||||
@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
 | 
			
		||||
{
 | 
			
		||||
@ -27,12 +27,12 @@ export class CollectionComponent
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	getThumb()
 | 
			
		||||
	getThumb(): SafeStyle
 | 
			
		||||
	{
 | 
			
		||||
		return this.sanitizer.bypassSecurityTrustStyle("url(" + this.collection.poster + ")");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	getDate(item: LibraryItem | Show | ShowRole | Collection | People)
 | 
			
		||||
	getDate(item: LibraryItem | Show | ShowRole | Collection | People): string
 | 
			
		||||
	{
 | 
			
		||||
		return ItemsUtils.getDate(item);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -14,11 +14,12 @@ import { Provider } from "../../models/provider";
 | 
			
		||||
import { MatSnackBar } from "@angular/material/snack-bar";
 | 
			
		||||
import { ShowGridComponent } from "../../components/show-grid/show-grid.component";
 | 
			
		||||
import { GenreService, ShowService, StudioService } from "../../services/api.service";
 | 
			
		||||
import { ExternalID } from "../../models/external-id";
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
	selector: 'app-metadata-edit',
 | 
			
		||||
	templateUrl: './metadata-edit.component.html',
 | 
			
		||||
	styleUrls: ['./metadata-edit.component.scss']
 | 
			
		||||
	selector: "app-metadata-edit",
 | 
			
		||||
	templateUrl: "./metadata-edit.component.html",
 | 
			
		||||
	styleUrls: ["./metadata-edit.component.scss"]
 | 
			
		||||
})
 | 
			
		||||
export class MetadataEditComponent implements OnInit
 | 
			
		||||
{
 | 
			
		||||
@ -29,8 +30,8 @@ export class MetadataEditComponent implements OnInit
 | 
			
		||||
	filteredGenres: Observable<Genre[]>;
 | 
			
		||||
 | 
			
		||||
	@ViewChild("identifyGrid") private identifyGrid: ShowGridComponent;
 | 
			
		||||
	private identifying: Observable<Show[]>;
 | 
			
		||||
	private identifiedShows: [string, Show[]];
 | 
			
		||||
	private _identifying: Observable<Show[]>;
 | 
			
		||||
	private _identifiedShows: [string, Show[]];
 | 
			
		||||
	public providers: Provider[] = [];
 | 
			
		||||
 | 
			
		||||
	public metadataChanged: boolean = false;
 | 
			
		||||
@ -51,7 +52,7 @@ export class MetadataEditComponent implements OnInit
 | 
			
		||||
		this.reIdentify(this.show.title);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ngOnInit()
 | 
			
		||||
	ngOnInit(): void
 | 
			
		||||
	{
 | 
			
		||||
		this.filteredGenres = this.genreForm.valueChanges
 | 
			
		||||
			.pipe(
 | 
			
		||||
@ -85,13 +86,14 @@ export class MetadataEditComponent implements OnInit
 | 
			
		||||
		if (this.metadataChanged)
 | 
			
		||||
		{
 | 
			
		||||
			this.http.post("/api/show/re-identify/" + this.show.slug, this.show.externalIDs).subscribe(
 | 
			
		||||
				(show: Show) => 
 | 
			
		||||
				{
 | 
			
		||||
					return;
 | 
			
		||||
				},
 | 
			
		||||
				() => {},
 | 
			
		||||
				() =>
 | 
			
		||||
				{
 | 
			
		||||
					this.snackBar.open("An unknown error occurred.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
 | 
			
		||||
					this.snackBar.open("An unknown error occurred.", null, {
 | 
			
		||||
						horizontalPosition: "left",
 | 
			
		||||
						panelClass: ["snackError"],
 | 
			
		||||
						duration: 2500
 | 
			
		||||
					});
 | 
			
		||||
				}
 | 
			
		||||
			);
 | 
			
		||||
			this.dialogRef.close(this.show);
 | 
			
		||||
@ -105,27 +107,27 @@ export class MetadataEditComponent implements OnInit
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	addAlias(event: MatChipInputEvent)
 | 
			
		||||
	addAlias(event: MatChipInputEvent): void
 | 
			
		||||
	{
 | 
			
		||||
		const input = event.input;
 | 
			
		||||
		const value = event.value;
 | 
			
		||||
		const input: HTMLInputElement = event.input;
 | 
			
		||||
		const value: string = event.value;
 | 
			
		||||
 | 
			
		||||
		this.show.aliases.push(value);
 | 
			
		||||
		if (input)
 | 
			
		||||
			input.value = "";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	removeAlias(alias: string)
 | 
			
		||||
	removeAlias(alias: string): void
 | 
			
		||||
	{
 | 
			
		||||
		const i = this.show.aliases.indexOf(alias);
 | 
			
		||||
		const i: number = this.show.aliases.indexOf(alias);
 | 
			
		||||
		this.show.aliases.splice(i, 1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	addGenre(event: MatChipInputEvent)
 | 
			
		||||
	addGenre(event: MatChipInputEvent): void
 | 
			
		||||
	{
 | 
			
		||||
		const input = event.input;
 | 
			
		||||
		const value = event.value;
 | 
			
		||||
		let genre: Genre = {id: 0, slug: null, name: value};
 | 
			
		||||
		const input: HTMLInputElement = event.input;
 | 
			
		||||
		const value: string = event.value;
 | 
			
		||||
		const genre: Genre = {id: 0, slug: null, name: value};
 | 
			
		||||
 | 
			
		||||
		this.show.genres.push(genre);
 | 
			
		||||
		if (input)
 | 
			
		||||
@ -134,7 +136,7 @@ export class MetadataEditComponent implements OnInit
 | 
			
		||||
 | 
			
		||||
	removeGenre(genre: Genre): void
 | 
			
		||||
	{
 | 
			
		||||
		const i = this.show.genres.indexOf(genre);
 | 
			
		||||
		const i: number = this.show.genres.indexOf(genre);
 | 
			
		||||
		this.show.genres.splice(i, 1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -145,41 +147,42 @@ export class MetadataEditComponent implements OnInit
 | 
			
		||||
 | 
			
		||||
	identityShow(name: string): Observable<Show[]>
 | 
			
		||||
	{
 | 
			
		||||
		if (this.identifiedShows && this.identifiedShows[0] === name)
 | 
			
		||||
			return of(this.identifiedShows[1]);
 | 
			
		||||
		this.identifying = this.http.get<Show[]>("/api/show/identify/" + name + "?isMovie=" + this.show.isMovie).pipe(
 | 
			
		||||
			tap(result => this.identifiedShows = [name, result])
 | 
			
		||||
		if (this._identifiedShows && this._identifiedShows[0] === name)
 | 
			
		||||
			return of(this._identifiedShows[1]);
 | 
			
		||||
		this._identifying = this.http.get<Show[]>("/api/show/identify/" + name + "?isMovie=" + this.show.isMovie).pipe(
 | 
			
		||||
			tap(result => this._identifiedShows = [name, result])
 | 
			
		||||
		);
 | 
			
		||||
		return this.identifying;
 | 
			
		||||
		return this._identifying;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	reIdentify(search: string)
 | 
			
		||||
	reIdentify(search: string): void
 | 
			
		||||
	{
 | 
			
		||||
		// TODO implement this
 | 
			
		||||
		// this.identityShow(search).subscribe(x => this.identifyGrid.shows = x);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	getMetadataID(provider: Provider)
 | 
			
		||||
	getMetadataID(provider: Provider): ExternalID
 | 
			
		||||
	{
 | 
			
		||||
		return this.show.externalIDs.find(x => x.provider.name == provider.name);
 | 
			
		||||
		return this.show.externalIDs.find(x => x.provider.name === provider.name);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	setMetadataID(provider: Provider, id: string, link: string = undefined)
 | 
			
		||||
	setMetadataID(provider: Provider, id: string, link: string = null): void
 | 
			
		||||
	{
 | 
			
		||||
		let i = this.show.externalIDs.findIndex(x => x.provider.name == provider.name);
 | 
			
		||||
		const i: number = this.show.externalIDs.findIndex(x => x.provider.name === provider.name);
 | 
			
		||||
 | 
			
		||||
		this.metadataChanged = true;
 | 
			
		||||
		if (i != -1)
 | 
			
		||||
		if (i !== -1)
 | 
			
		||||
		{
 | 
			
		||||
			this.show.externalIDs[i].dataID = id;
 | 
			
		||||
			this.show.externalIDs[i].link = link;
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
			this.show.externalIDs.push({provider: provider, dataID: id, link: link});
 | 
			
		||||
			this.show.externalIDs.push({provider, dataID: id, link});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	identifyID(show: Show)
 | 
			
		||||
	identifyID(show: Show): void
 | 
			
		||||
	{
 | 
			
		||||
		for (let id of show.externalIDs)
 | 
			
		||||
		for (const id of show.externalIDs)
 | 
			
		||||
			this.setMetadataID(id.provider, id.dataID, id.link);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,15 +1,11 @@
 | 
			
		||||
import { Component, OnInit } from '@angular/core';
 | 
			
		||||
import { Component } 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 {
 | 
			
		||||
 | 
			
		||||
export class NotFoundComponent
 | 
			
		||||
{
 | 
			
		||||
	constructor() { }
 | 
			
		||||
 | 
			
		||||
	ngOnInit() {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { detect } from "detect-browser";
 | 
			
		||||
import { BotInfo, BrowserInfo, detect, NodeInfo, ReactNativeInfo, SearchBotDeviceInfo } from "detect-browser";
 | 
			
		||||
import { Track, WatchItem } from "../../models/watch-item";
 | 
			
		||||
 | 
			
		||||
export enum method
 | 
			
		||||
@ -31,8 +31,8 @@ export class SupportList
 | 
			
		||||
 | 
			
		||||
export function getWhatIsSupported(player: HTMLVideoElement, item: WatchItem): SupportList
 | 
			
		||||
{
 | 
			
		||||
	let supportList: SupportList = new SupportList();
 | 
			
		||||
	let browser = detect();
 | 
			
		||||
	const supportList: SupportList = new SupportList();
 | 
			
		||||
	const browser: BrowserInfo | SearchBotDeviceInfo | BotInfo | NodeInfo | ReactNativeInfo = detect();
 | 
			
		||||
 | 
			
		||||
	if (!browser)
 | 
			
		||||
	{
 | 
			
		||||
@ -54,33 +54,33 @@ function containerIsSupported(player: HTMLVideoElement, container: string, brows
 | 
			
		||||
	switch (container)
 | 
			
		||||
	{
 | 
			
		||||
		case "asf":
 | 
			
		||||
			return browser == "tizen" || browser == "orsay" || browser == "edge";
 | 
			
		||||
			return browser === "tizen" || browser === "orsay" || browser === "edge";
 | 
			
		||||
		case "avi":
 | 
			
		||||
			return browser == "tizen" || browser == "orsay" || browser == "edge";
 | 
			
		||||
			return browser === "tizen" || browser === "orsay" || browser === "edge";
 | 
			
		||||
		case "mpg":
 | 
			
		||||
		case "mpeg":
 | 
			
		||||
			return browser == "tizen" || browser == "orsay" || browser == "edge";
 | 
			
		||||
			return browser === "tizen" || browser === "orsay" || browser === "edge";
 | 
			
		||||
		case "flv":
 | 
			
		||||
			return browser == "tizen" || browser == "orsay";
 | 
			
		||||
			return browser === "tizen" || browser === "orsay";
 | 
			
		||||
		case "3gp":
 | 
			
		||||
		case "mts":
 | 
			
		||||
		case "trp":
 | 
			
		||||
		case "vob":
 | 
			
		||||
		case "vro":
 | 
			
		||||
			return browser == "tizen" || browser == "orsay";
 | 
			
		||||
			return browser === "tizen" || browser === "orsay";
 | 
			
		||||
		case "mov":
 | 
			
		||||
			return browser == "tizen" || browser == "orsay" || browser == "edge" || browser == "chrome";
 | 
			
		||||
			return browser === "tizen" || browser === "orsay" || browser === "edge" || browser === "chrome";
 | 
			
		||||
		case "m2ts":
 | 
			
		||||
			return browser == "tizen" || browser == "orsay" || browser == "edge";
 | 
			
		||||
			return browser === "tizen" || browser === "orsay" || browser === "edge";
 | 
			
		||||
		case "wmv":
 | 
			
		||||
			return browser == "tizen" || browser == "orsay" || browser == "edge";
 | 
			
		||||
			return browser === "tizen" || browser === "orsay" || browser === "edge";
 | 
			
		||||
		case "ts":
 | 
			
		||||
			return browser == "tizen" || browser == "orsay" || browser == "edge";
 | 
			
		||||
			return browser === "tizen" || browser === "orsay" || browser === "edge";
 | 
			
		||||
		case "mp4":
 | 
			
		||||
		case "m4v":
 | 
			
		||||
			return true;
 | 
			
		||||
		case "mkv":
 | 
			
		||||
			if (browser == "tizen" || browser == "orsay" || browser == "chrome" || browser == "edge")
 | 
			
		||||
			if (browser === "tizen" || browser === "orsay" || browser === "chrome" || browser === "edge")
 | 
			
		||||
				return true;
 | 
			
		||||
			return !!(player.canPlayType("video/x-matroska") || player.canPlayType("video/mkv"));
 | 
			
		||||
 | 
			
		||||
@ -95,34 +95,34 @@ function videoCodecIsSupported(player: HTMLVideoElement, codec: string, browser:
 | 
			
		||||
	switch (codec)
 | 
			
		||||
	{
 | 
			
		||||
		case "h264":
 | 
			
		||||
			return !!player.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"');
 | 
			
		||||
			return !!player.canPlayType("video/mp4; codecs=\"avc1.42E01E, mp4a.40.2\"");
 | 
			
		||||
		case "h265":
 | 
			
		||||
		case "hevc":
 | 
			
		||||
			if (browser == "tizen" || browser == "orsay" || browser == "xboxOne" || browser == "ios")
 | 
			
		||||
			if (browser === "tizen" || browser === "orsay" || browser === "xboxOne" || browser === "ios")
 | 
			
		||||
				return true;
 | 
			
		||||
			// SHOULD SUPPORT CHROMECAST ULTRA
 | 
			
		||||
			// 	if (browser.chromecast)
 | 
			
		||||
			// 	{
 | 
			
		||||
 | 
			
		||||
			//		var isChromecastUltra = userAgent.indexOf('aarch64') !== -1;
 | 
			
		||||
			// 		var isChromecastUltra = userAgent.indexOf('aarch64') !=== -1;
 | 
			
		||||
			// 		if (isChromecastUltra)
 | 
			
		||||
			// 		{
 | 
			
		||||
			// 			return true;
 | 
			
		||||
			// 		}
 | 
			
		||||
			// 	}
 | 
			
		||||
			return !!player.canPlayType('video/hevc; codecs="hevc, aac"');
 | 
			
		||||
			return !!player.canPlayType("video/hevc; codecs=\"hevc, aac\"");
 | 
			
		||||
		case "mpeg2video":
 | 
			
		||||
			return browser == "orsay" || browser == "tizen" || browser == "edge";
 | 
			
		||||
			return browser === "orsay" || browser === "tizen" || browser === "edge";
 | 
			
		||||
		case "vc1":
 | 
			
		||||
			return browser == "orsay" || browser == "tizen" || browser == "edge";
 | 
			
		||||
			return browser === "orsay" || browser === "tizen" || browser === "edge";
 | 
			
		||||
		case "msmpeg4v2":
 | 
			
		||||
			return browser == "orsay" || browser == "tizen";
 | 
			
		||||
			return browser === "orsay" || browser === "tizen";
 | 
			
		||||
		case "vp8":
 | 
			
		||||
			return !!player.canPlayType('video/webm; codecs="vp8');
 | 
			
		||||
			return !!player.canPlayType("video/webm; codecs=\"vp8");
 | 
			
		||||
		case "vp9":
 | 
			
		||||
			return !!player.canPlayType('video/webm; codecs="vp9"');
 | 
			
		||||
			return !!player.canPlayType("video/webm; codecs=\"vp9\"");
 | 
			
		||||
		case "vorbis":
 | 
			
		||||
			return browser == "orsay" || browser == "tizen" || !!player.canPlayType('video/webm; codecs="vp8');
 | 
			
		||||
			return browser === "orsay" || browser === "tizen" || !!player.canPlayType("video/webm; codecs=\"vp8");
 | 
			
		||||
		default:
 | 
			
		||||
			return false;
 | 
			
		||||
	}
 | 
			
		||||
@ -134,21 +134,21 @@ function audioCodecIsSupported(player: HTMLVideoElement, codec: string, browser:
 | 
			
		||||
	switch (codec)
 | 
			
		||||
	{
 | 
			
		||||
		case "mp3":
 | 
			
		||||
			return !!player.canPlayType('video/mp4; codecs="avc1.640029, mp4a.69"') ||
 | 
			
		||||
				!!player.canPlayType('video/mp4; codecs="avc1.640029, mp4a.6B"');
 | 
			
		||||
			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"');
 | 
			
		||||
			return !!player.canPlayType("video/mp4; codecs=\"avc1.640029, mp4a.40.2\"");
 | 
			
		||||
		case "mp2":
 | 
			
		||||
			return browser == "orsay" || browser == "tizen" || browser == "edge";
 | 
			
		||||
			return browser === "orsay" || browser === "tizen" || browser === "edge";
 | 
			
		||||
		case "pcm_s16le":
 | 
			
		||||
		case "pcm_s24le":
 | 
			
		||||
			return browser == "orsay" || browser == "tizen" || browser == "edge";
 | 
			
		||||
			return browser === "orsay" || browser === "tizen" || browser === "edge";
 | 
			
		||||
		case "aac_latm":
 | 
			
		||||
			return browser == "orsay" || browser == "tizen";
 | 
			
		||||
			return browser === "orsay" || browser === "tizen";
 | 
			
		||||
		case "opus":
 | 
			
		||||
			return !!player.canPlayType('audio/ogg; codecs="opus"');
 | 
			
		||||
			return !!player.canPlayType("audio/ogg; codecs=\"opus\"");
 | 
			
		||||
		case "flac":
 | 
			
		||||
			return browser == "orsay" || browser == "tizen" || browser == "edge";
 | 
			
		||||
			return browser === "orsay" || browser === "tizen" || browser === "edge";
 | 
			
		||||
		default:
 | 
			
		||||
			return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -75,7 +75,7 @@
 | 
			
		||||
 | 
			
		||||
		<div class="controller container-fluid" id="controller">
 | 
			
		||||
			<div class="img d-none d-sm-block">
 | 
			
		||||
				<img src="poster/{{this.item.showSlug}}" />
 | 
			
		||||
				<img src="poster/{{this.item.showSlug}}" alt="poster" />
 | 
			
		||||
			</div>
 | 
			
		||||
			<div class="content">
 | 
			
		||||
				<h3 *ngIf="!this.item.isMovie">S{{this.item.seasonNumber}}:E{{this.item.episodeNumber}} - {{this.item.title}}</h3>
 | 
			
		||||
@ -112,7 +112,7 @@
 | 
			
		||||
 | 
			
		||||
							<div id="next">
 | 
			
		||||
								<div id="main">
 | 
			
		||||
									<img src="{{this.item.nextEpisode.thumb}}" />
 | 
			
		||||
									<img src="{{this.item.nextEpisode.thumb}}" alt="next episode thumbnail" />
 | 
			
		||||
								</div>
 | 
			
		||||
								<div id="overview">
 | 
			
		||||
									<h6>S{{this.item.nextEpisode.seasonNumber}}:E{{this.item.nextEpisode.episodeNumber}} - {{this.item.nextEpisode.title}}</h6>
 | 
			
		||||
 | 
			
		||||
@ -24,7 +24,9 @@ import {
 | 
			
		||||
} from "./playbackMethodDetector";
 | 
			
		||||
import { AppComponent } from "../../app.component";
 | 
			
		||||
import { Track, WatchItem } from "../../models/watch-item";
 | 
			
		||||
import SubtitlesOctopus from "libass-wasm/dist/js/subtitles-octopus.js"
 | 
			
		||||
import SubtitlesOctopus from "libass-wasm/dist/js/subtitles-octopus.js";
 | 
			
		||||
import MouseMoveEvent = JQuery.MouseMoveEvent;
 | 
			
		||||
import TouchMoveEvent = JQuery.TouchMoveEvent;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@Pipe({
 | 
			
		||||
@ -52,7 +54,7 @@ export class BufferToWidthPipe implements PipeTransform
 | 
			
		||||
{
 | 
			
		||||
	transform(buffered: TimeRanges, duration: number): string
 | 
			
		||||
	{
 | 
			
		||||
		if (buffered.length == 0)
 | 
			
		||||
		if (buffered.length === 0)
 | 
			
		||||
			return "0";
 | 
			
		||||
		return `${buffered.end(buffered.length - 1) / duration * 100}%`;
 | 
			
		||||
	}
 | 
			
		||||
@ -66,7 +68,7 @@ export class VolumeToButtonPipe implements PipeTransform
 | 
			
		||||
{
 | 
			
		||||
	transform(volume: number, muted: boolean): string
 | 
			
		||||
	{
 | 
			
		||||
		if (volume == 0 || muted)
 | 
			
		||||
		if (volume === 0 || muted)
 | 
			
		||||
			return "volume_off";
 | 
			
		||||
		else if (volume < 25)
 | 
			
		||||
			return "volume_mute";
 | 
			
		||||
@ -167,7 +169,7 @@ export class PlayerComponent implements OnInit, OnDestroy, AfterViewInit
 | 
			
		||||
	            private startup: StartupService)
 | 
			
		||||
	{ }
 | 
			
		||||
 | 
			
		||||
	ngOnInit()
 | 
			
		||||
	ngOnInit(): void
 | 
			
		||||
	{
 | 
			
		||||
		document.getElementById("nav").classList.add("d-none");
 | 
			
		||||
		if (AppComponent.isMobile)
 | 
			
		||||
@ -223,7 +225,7 @@ export class PlayerComponent implements OnInit, OnDestroy, AfterViewInit
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ngOnDestroy()
 | 
			
		||||
	ngOnDestroy(): void
 | 
			
		||||
	{
 | 
			
		||||
		if (this.subtitlesManager)
 | 
			
		||||
			this.subtitlesManager.dispose();
 | 
			
		||||
@ -236,13 +238,13 @@ export class PlayerComponent implements OnInit, OnDestroy, AfterViewInit
 | 
			
		||||
		$(document).off();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ngAfterViewInit()
 | 
			
		||||
	ngAfterViewInit(): void
 | 
			
		||||
	{
 | 
			
		||||
		if (this.oidcSecurity === undefined)
 | 
			
		||||
			this.oidcSecurity = this.injector.get(OidcSecurityService);
 | 
			
		||||
		this.hlsPlayer.config.xhrSetup = xhr =>
 | 
			
		||||
		{
 | 
			
		||||
			const token = this.oidcSecurity.getToken();
 | 
			
		||||
			const token: string = this.oidcSecurity.getToken();
 | 
			
		||||
			if (token)
 | 
			
		||||
				xhr.setRequestHeader("Authorization", "Bearer " + token);
 | 
			
		||||
		};
 | 
			
		||||
@ -252,17 +254,17 @@ export class PlayerComponent implements OnInit, OnDestroy, AfterViewInit
 | 
			
		||||
		setTimeout(() => this.route.data.subscribe(() =>
 | 
			
		||||
		{
 | 
			
		||||
			// TODO remove the query param for the method (should be a session setting).
 | 
			
		||||
			let queryMethod: string = this.route.snapshot.queryParams["method"];
 | 
			
		||||
			const queryMethod: string = this.route.snapshot.queryParams.method;
 | 
			
		||||
			this.supportList = getWhatIsSupported(this.player, this.item);
 | 
			
		||||
			this.selectPlayMethod(queryMethod ? method[queryMethod] : this.supportList.getPlaybackMethod());
 | 
			
		||||
 | 
			
		||||
			// TODO remove this, it should be a user's setting.
 | 
			
		||||
			const subSlug: string = this.route.snapshot.queryParams["sub"];
 | 
			
		||||
			const subSlug: string = this.route.snapshot.queryParams.sub;
 | 
			
		||||
			if (subSlug != null)
 | 
			
		||||
			{
 | 
			
		||||
				const languageCode: string = subSlug.substring(0, 3);
 | 
			
		||||
				const forced: boolean = subSlug.length > 3 && subSlug.substring(4) == "for";
 | 
			
		||||
				const sub: Track = this.item.subtitles.find(x => x.language == languageCode && x.isForced == forced);
 | 
			
		||||
				const forced: boolean = subSlug.length > 3 && subSlug.substring(4) === "for";
 | 
			
		||||
				const sub: Track = this.item.subtitles.find(x => x.language === languageCode && x.isForced === forced);
 | 
			
		||||
				this.selectSubtitle(sub, false);
 | 
			
		||||
			}
 | 
			
		||||
		}));
 | 
			
		||||
@ -278,7 +280,7 @@ export class PlayerComponent implements OnInit, OnDestroy, AfterViewInit
 | 
			
		||||
		return AppComponent.isMobile;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	getTimeFromSeekbar(pageX: number)
 | 
			
		||||
	getTimeFromSeekbar(pageX: number): number
 | 
			
		||||
	{
 | 
			
		||||
		const value: number = (pageX - this.progressBar.offsetLeft) / this.progressBar.clientWidth;
 | 
			
		||||
		const percent: number = Math.max(0, Math.min(value, 1));
 | 
			
		||||
@ -308,14 +310,14 @@ export class PlayerComponent implements OnInit, OnDestroy, AfterViewInit
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@HostListener("document:touchmove", ["$event"])
 | 
			
		||||
	touchSeek(event)
 | 
			
		||||
	touchSeek(event: TouchMoveEvent): void
 | 
			
		||||
	{
 | 
			
		||||
		if (this.seeking)
 | 
			
		||||
			this.player.currentTime = this.getTimeFromSeekbar(event.changedTouches[0].pageX);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@HostListener("document:mousemove", ["$event"])
 | 
			
		||||
	mouseMove(event)
 | 
			
		||||
	mouseMove(event: MouseMoveEvent): void
 | 
			
		||||
	{
 | 
			
		||||
		if (this.seeking)
 | 
			
		||||
			this.player.currentTime = this.getTimeFromSeekbar(event.pageX);
 | 
			
		||||
@ -343,14 +345,14 @@ export class PlayerComponent implements OnInit, OnDestroy, AfterViewInit
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	selectPlayMethod(playMethod: method)
 | 
			
		||||
	selectPlayMethod(playMethod: method): void
 | 
			
		||||
	{
 | 
			
		||||
		this.playMethod = playMethod;
 | 
			
		||||
		const url: string = [
 | 
			
		||||
			"/video",
 | 
			
		||||
			this.playMethod.toLowerCase(),
 | 
			
		||||
			this.item.slug,
 | 
			
		||||
			this.playMethod != method.direct ? 'master.m3u8' : null
 | 
			
		||||
			this.playMethod !== method.direct ? "master.m3u8" : null
 | 
			
		||||
		].filter(x => x !== null).join("/");
 | 
			
		||||
		if (this.playMethod === method.direct || this.player.canPlayType("application/vnd.apple.mpegurl"))
 | 
			
		||||
			this.player.src = url;
 | 
			
		||||
@ -365,11 +367,11 @@ export class PlayerComponent implements OnInit, OnDestroy, AfterViewInit
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	back()
 | 
			
		||||
	back(): void
 | 
			
		||||
	{
 | 
			
		||||
		if (this.startup.loadedFromWatch)
 | 
			
		||||
		{
 | 
			
		||||
			this.router.navigate(["show", this.startup.show], {replaceUrl: true})
 | 
			
		||||
			this.router.navigate(["show", this.startup.show], {replaceUrl: true});
 | 
			
		||||
			this.startup.loadedFromWatch = false;
 | 
			
		||||
			this.startup.show = null;
 | 
			
		||||
		}
 | 
			
		||||
@ -377,7 +379,7 @@ export class PlayerComponent implements OnInit, OnDestroy, AfterViewInit
 | 
			
		||||
			this.location.back();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	next()
 | 
			
		||||
	next(): void
 | 
			
		||||
	{
 | 
			
		||||
		if (this.item.nextEpisode == null)
 | 
			
		||||
			return;
 | 
			
		||||
@ -387,7 +389,7 @@ export class PlayerComponent implements OnInit, OnDestroy, AfterViewInit
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	previous()
 | 
			
		||||
	previous(): void
 | 
			
		||||
	{
 | 
			
		||||
		if (this.item.previousEpisode == null)
 | 
			
		||||
			return;
 | 
			
		||||
@ -397,7 +399,7 @@ export class PlayerComponent implements OnInit, OnDestroy, AfterViewInit
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	videoClicked()
 | 
			
		||||
	videoClicked(): void
 | 
			
		||||
	{
 | 
			
		||||
		if (AppComponent.isMobile)
 | 
			
		||||
			this.showControls = !this.showControls;
 | 
			
		||||
@ -408,7 +410,7 @@ export class PlayerComponent implements OnInit, OnDestroy, AfterViewInit
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	togglePlayback()
 | 
			
		||||
	togglePlayback(): void
 | 
			
		||||
	{
 | 
			
		||||
		if (this.player.paused)
 | 
			
		||||
			this.player.play();
 | 
			
		||||
@ -416,7 +418,7 @@ export class PlayerComponent implements OnInit, OnDestroy, AfterViewInit
 | 
			
		||||
			this.player.pause();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fullscreen()
 | 
			
		||||
	fullscreen(): void
 | 
			
		||||
	{
 | 
			
		||||
		if (this.isFullScreen)
 | 
			
		||||
			document.exitFullscreen();
 | 
			
		||||
@ -424,7 +426,7 @@ export class PlayerComponent implements OnInit, OnDestroy, AfterViewInit
 | 
			
		||||
			document.body.requestFullscreen();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	async selectSubtitle(subtitle: Track | number, changeUrl: boolean = true)
 | 
			
		||||
	async selectSubtitle(subtitle: Track | number, changeUrl: boolean = true): Promise<void>
 | 
			
		||||
	{
 | 
			
		||||
		if (typeof(subtitle) === "number")
 | 
			
		||||
		{
 | 
			
		||||
@ -444,7 +446,7 @@ export class PlayerComponent implements OnInit, OnDestroy, AfterViewInit
 | 
			
		||||
					subSlug += "-for";
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			this.router.navigate([], {
 | 
			
		||||
			await this.router.navigate([], {
 | 
			
		||||
				relativeTo: this.route,
 | 
			
		||||
				queryParams: {sub: subSlug},
 | 
			
		||||
				replaceUrl: true,
 | 
			
		||||
@ -475,11 +477,11 @@ export class PlayerComponent implements OnInit, OnDestroy, AfterViewInit
 | 
			
		||||
			});
 | 
			
		||||
			this.removeHtmlTrack();
 | 
			
		||||
 | 
			
		||||
			if (subtitle.codec == "ass")
 | 
			
		||||
			if (subtitle.codec === "ass")
 | 
			
		||||
			{
 | 
			
		||||
				if (!this.subtitlesManager)
 | 
			
		||||
				{
 | 
			
		||||
					let fonts: {[key: string]: string} = await this.shows.getFonts(this.item.showSlug).toPromise();
 | 
			
		||||
					const fonts: { [key: string]: string } = await this.shows.getFonts(this.item.showSlug).toPromise();
 | 
			
		||||
					this.subtitlesManager = new SubtitlesOctopus({
 | 
			
		||||
						video: this.player,
 | 
			
		||||
						subUrl: `subtitle/${subtitle.slug}`,
 | 
			
		||||
@ -489,12 +491,12 @@ export class PlayerComponent implements OnInit, OnDestroy, AfterViewInit
 | 
			
		||||
				else
 | 
			
		||||
					this.subtitlesManager.setTrackByUrl(`subtitle/${subtitle.slug}`);
 | 
			
		||||
			}
 | 
			
		||||
			else if (subtitle.codec == "subrip")
 | 
			
		||||
			else if (subtitle.codec === "subrip")
 | 
			
		||||
			{
 | 
			
		||||
				if (this.subtitlesManager)
 | 
			
		||||
					this.subtitlesManager.freeTrack();
 | 
			
		||||
 | 
			
		||||
				let track = document.createElement("track");
 | 
			
		||||
				const track: HTMLTrackElement = document.createElement("track");
 | 
			
		||||
				track.kind = "subtitles";
 | 
			
		||||
				track.label = subtitle.displayName;
 | 
			
		||||
				track.srclang = subtitle.language;
 | 
			
		||||
@ -510,9 +512,9 @@ export class PlayerComponent implements OnInit, OnDestroy, AfterViewInit
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	removeHtmlTrack()
 | 
			
		||||
	removeHtmlTrack(): void
 | 
			
		||||
	{
 | 
			
		||||
		let elements = this.player.getElementsByTagName("track");
 | 
			
		||||
		const elements: HTMLCollectionOf<HTMLTrackElement> = this.player.getElementsByTagName("track");
 | 
			
		||||
		if (elements.length > 0)
 | 
			
		||||
			elements.item(0).remove();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -1,21 +1,21 @@
 | 
			
		||||
import { Component, OnInit, OnDestroy } from '@angular/core';
 | 
			
		||||
import { Component, OnInit, OnDestroy, AfterViewInit } from "@angular/core";
 | 
			
		||||
import { ActivatedRoute } from "@angular/router";
 | 
			
		||||
import { SearchResult } from "../../models/search-result";
 | 
			
		||||
import { Title } from "@angular/platform-browser";
 | 
			
		||||
import { Page } from "../../models/page";
 | 
			
		||||
 | 
			
		||||
@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
 | 
			
		||||
export class SearchComponent implements OnInit, OnDestroy, AfterViewInit
 | 
			
		||||
{
 | 
			
		||||
	items: SearchResult;
 | 
			
		||||
 | 
			
		||||
	constructor(private route: ActivatedRoute, private title: Title) { }
 | 
			
		||||
 | 
			
		||||
	ngOnInit()
 | 
			
		||||
	ngOnInit(): void
 | 
			
		||||
	{
 | 
			
		||||
		this.route.data.subscribe((data) =>
 | 
			
		||||
		{
 | 
			
		||||
@ -24,16 +24,16 @@ export class SearchComponent implements OnInit, OnDestroy
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ngAfterViewInit()
 | 
			
		||||
	ngAfterViewInit(): void
 | 
			
		||||
	{
 | 
			
		||||
		let searchBar: HTMLInputElement = <HTMLInputElement>document.getElementById("search");
 | 
			
		||||
		const searchBar: HTMLInputElement = document.getElementById("search") as HTMLInputElement;
 | 
			
		||||
		searchBar.classList.add("searching");
 | 
			
		||||
		searchBar.value = this.items.query;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ngOnDestroy()
 | 
			
		||||
	ngOnDestroy(): void
 | 
			
		||||
	{
 | 
			
		||||
		let searchBar: HTMLInputElement = <HTMLInputElement>document.getElementById("search");
 | 
			
		||||
		const searchBar: HTMLInputElement = document.getElementById("search") as HTMLInputElement;
 | 
			
		||||
		searchBar.classList.remove("searching");
 | 
			
		||||
		searchBar.value = "";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
<div class="backdrop">
 | 
			
		||||
	<img id="backdrop" src="backdrop/{{this.show.slug}}" />
 | 
			
		||||
	<img id="backdrop" src="backdrop/{{this.show.slug}}" alt="backdrop" />
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<div class="header container pt-sm-5">
 | 
			
		||||
 | 
			
		||||
@ -1,21 +1,25 @@
 | 
			
		||||
import {Component, Inject} from '@angular/core';
 | 
			
		||||
import { Component, Inject } from "@angular/core";
 | 
			
		||||
import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material/dialog";
 | 
			
		||||
import {DomSanitizer} from "@angular/platform-browser";
 | 
			
		||||
import { DomSanitizer, SafeUrl } from "@angular/platform-browser";
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-trailer-dialog',
 | 
			
		||||
  templateUrl: './trailer-dialog.component.html',
 | 
			
		||||
  styleUrls: ['./trailer-dialog.component.scss']
 | 
			
		||||
  selector: "app-trailer-dialog",
 | 
			
		||||
  templateUrl: "./trailer-dialog.component.html",
 | 
			
		||||
  styleUrls: ["./trailer-dialog.component.scss"]
 | 
			
		||||
})
 | 
			
		||||
export class TrailerDialogComponent
 | 
			
		||||
{
 | 
			
		||||
	constructor(public dialogRef: MatDialogRef<TrailerDialogComponent>, public sanitizer: DomSanitizer,  @Inject(MAT_DIALOG_DATA) public trailer: string) {}
 | 
			
		||||
	constructor(public dialogRef: MatDialogRef<TrailerDialogComponent>,
 | 
			
		||||
	            public sanitizer: DomSanitizer,
 | 
			
		||||
	            @Inject(MAT_DIALOG_DATA) public trailer: string)
 | 
			
		||||
	{}
 | 
			
		||||
 | 
			
		||||
	getYtTrailer()
 | 
			
		||||
	getYtTrailer(): SafeUrl
 | 
			
		||||
	{
 | 
			
		||||
		if (!this.trailer.includes("youtube.com"))
 | 
			
		||||
			return null;
 | 
			
		||||
		let uri = "https://www.youtube.com/embed/" + this.trailer.substring(this.trailer.indexOf("watch?v=") + 8) + "?autoplay=1";
 | 
			
		||||
		const ytID: string = this.trailer.substring(this.trailer.indexOf("watch?v=") + 8);
 | 
			
		||||
		const uri: string = `https://www.youtube.com/embed/${ytID}?autoplay=1`;
 | 
			
		||||
		return this.sanitizer.bypassSecurityTrustResourceUrl(uri);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
import { Injectable } from "@angular/core";
 | 
			
		||||
import { HttpClient } from "@angular/common/http";
 | 
			
		||||
import {Observable} from "rxjs"
 | 
			
		||||
import { Observable } from "rxjs";
 | 
			
		||||
import { Page } from "../models/page";
 | 
			
		||||
import { Genre } from "../models/resources/genre";
 | 
			
		||||
import { IResource } from "../models/resources/resource";
 | 
			
		||||
@ -34,7 +34,7 @@ class CrudApi<T extends IResource>
 | 
			
		||||
	{
 | 
			
		||||
		if (args == null)
 | 
			
		||||
			return "";
 | 
			
		||||
		let params: string = Object.keys(args).map(x => `${x}=${args[x]}`).join("&");
 | 
			
		||||
		const params: string = Object.keys(args).map(x => `${x}=${args[x]}`).join("&");
 | 
			
		||||
 | 
			
		||||
		return params ? `?${params}` : "";
 | 
			
		||||
	}
 | 
			
		||||
@ -67,7 +67,7 @@ class CrudApi<T extends IResource>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Injectable({
 | 
			
		||||
	providedIn: 'root'
 | 
			
		||||
	providedIn: "root"
 | 
			
		||||
})
 | 
			
		||||
export class LibraryService extends CrudApi<Library>
 | 
			
		||||
{
 | 
			
		||||
@ -78,7 +78,7 @@ export class LibraryService extends CrudApi<Library>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Injectable({
 | 
			
		||||
	providedIn: 'root'
 | 
			
		||||
	providedIn: "root"
 | 
			
		||||
})
 | 
			
		||||
export class LibraryItemService extends CrudApi<LibraryItem>
 | 
			
		||||
{
 | 
			
		||||
@ -89,7 +89,7 @@ export class LibraryItemService extends CrudApi<LibraryItem>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Injectable({
 | 
			
		||||
	providedIn: 'root'
 | 
			
		||||
	providedIn: "root"
 | 
			
		||||
})
 | 
			
		||||
export class SeasonService extends CrudApi<Season>
 | 
			
		||||
{
 | 
			
		||||
@ -106,7 +106,7 @@ export class SeasonService extends CrudApi<Season>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Injectable({
 | 
			
		||||
	providedIn: 'root'
 | 
			
		||||
	providedIn: "root"
 | 
			
		||||
})
 | 
			
		||||
export class EpisodeService extends CrudApi<Episode>
 | 
			
		||||
{
 | 
			
		||||
@ -129,7 +129,7 @@ export class EpisodeService extends CrudApi<Episode>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Injectable({
 | 
			
		||||
	providedIn: 'root'
 | 
			
		||||
	providedIn: "root"
 | 
			
		||||
})
 | 
			
		||||
export class PeopleService extends CrudApi<People>
 | 
			
		||||
{
 | 
			
		||||
@ -146,7 +146,7 @@ export class PeopleService extends CrudApi<People>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Injectable({
 | 
			
		||||
	providedIn: 'root'
 | 
			
		||||
	providedIn: "root"
 | 
			
		||||
})
 | 
			
		||||
export class GenreService extends CrudApi<Genre>
 | 
			
		||||
{
 | 
			
		||||
@ -163,7 +163,7 @@ export class GenreService extends CrudApi<Genre>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Injectable({
 | 
			
		||||
	providedIn: 'root'
 | 
			
		||||
	providedIn: "root"
 | 
			
		||||
})
 | 
			
		||||
export class ShowService extends CrudApi<Show>
 | 
			
		||||
{
 | 
			
		||||
@ -185,7 +185,7 @@ export class ShowService extends CrudApi<Show>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Injectable({
 | 
			
		||||
	providedIn: 'root'
 | 
			
		||||
	providedIn: "root"
 | 
			
		||||
})
 | 
			
		||||
export class StudioService extends CrudApi<Studio>
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@ -10,7 +10,7 @@ export class ItemResolver
 | 
			
		||||
{
 | 
			
		||||
	public static resolvers: any[] = [];
 | 
			
		||||
 | 
			
		||||
	static forResource<T>(resource: string)
 | 
			
		||||
	static forResource<T>(resource: string): any
 | 
			
		||||
	{
 | 
			
		||||
		@Injectable()
 | 
			
		||||
		class Resolver implements Resolve<T>
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,9 @@
 | 
			
		||||
import {HttpClient, HttpErrorResponse} from '@angular/common/http';
 | 
			
		||||
import {Injectable} from '@angular/core';
 | 
			
		||||
import {MatSnackBar} from '@angular/material/snack-bar';
 | 
			
		||||
import {ActivatedRouteSnapshot, Resolve} from '@angular/router';
 | 
			
		||||
import {Observable, EMPTY} from 'rxjs';
 | 
			
		||||
import {catchError, map} from 'rxjs/operators';
 | 
			
		||||
import { HttpClient, HttpErrorResponse } from "@angular/common/http";
 | 
			
		||||
import { Injectable } from "@angular/core";
 | 
			
		||||
import { MatSnackBar } from "@angular/material/snack-bar";
 | 
			
		||||
import { ActivatedRouteSnapshot, Resolve } from "@angular/router";
 | 
			
		||||
import { Observable, EMPTY } from "rxjs";
 | 
			
		||||
import { catchError, map } from "rxjs/operators";
 | 
			
		||||
import { Page } from "../models/page";
 | 
			
		||||
import { IResource } from "../models/resources/resource";
 | 
			
		||||
 | 
			
		||||
@ -16,7 +16,7 @@ export class PageResolver
 | 
			
		||||
 | 
			
		||||
	static forResource<T extends IResource>(resource: string,
 | 
			
		||||
	                                        copyParams: boolean | string[] | RouteMapper = false,
 | 
			
		||||
	                                        defaultQuery: string = null)
 | 
			
		||||
	                                        defaultQuery: string = null): any
 | 
			
		||||
	{
 | 
			
		||||
		@Injectable()
 | 
			
		||||
		class Resolver implements Resolve<Page<T>>
 | 
			
		||||
@ -27,23 +27,23 @@ export class PageResolver
 | 
			
		||||
 | 
			
		||||
			resolve(route: ActivatedRouteSnapshot): Page<T> | Observable<Page<T>> | Promise<Page<T>>
 | 
			
		||||
			{
 | 
			
		||||
				let res: string = resource.replace(/:([^:]*?)(\/|$|&)/g, (x, y, z) => `${route.paramMap.get(y)}${z}`);
 | 
			
		||||
				let query: [string, string][] = defaultQuery
 | 
			
		||||
				const res: string = resource.replace(/:([^:]*?)(\/|$|&)/g, (x, y, z) => `${route.paramMap.get(y)}${z}`);
 | 
			
		||||
				const query: [string, string][] = defaultQuery
 | 
			
		||||
					?.replace(/:([^:]*?)(\/|$|&)/g, (x, y, z) => `${route.paramMap.get(y)}${z}`)
 | 
			
		||||
					.split('&')
 | 
			
		||||
					.map(x => x.split('=') as [string, string]);
 | 
			
		||||
					.split("&")
 | 
			
		||||
					.map(x => x.split("=") as [string, string]);
 | 
			
		||||
				let uri: string;
 | 
			
		||||
				if (typeof copyParams == "function")
 | 
			
		||||
				if (typeof copyParams === "function")
 | 
			
		||||
					uri = copyParams(route, res, query);
 | 
			
		||||
				else
 | 
			
		||||
				{
 | 
			
		||||
					let entries: [string, string][] = copyParams == true
 | 
			
		||||
					const entries: [string, string][] = copyParams === true
 | 
			
		||||
						? Object.entries(route.queryParams)
 | 
			
		||||
						: Object.entries(route.queryParams).filter(x => copyParams && copyParams.includes(x[0]));
 | 
			
		||||
					if (query)
 | 
			
		||||
						entries.push(...query);
 | 
			
		||||
					let params: string = entries.length > 0
 | 
			
		||||
						? '?' + entries.map(x => `${x[0]}=${x[1]}`).join('&')
 | 
			
		||||
					const params: string = entries.length > 0
 | 
			
		||||
						? "?" + entries.map(x => `${x[0]}=${x[1]}`).join("&")
 | 
			
		||||
						: "";
 | 
			
		||||
					uri = `api/${res}${params}`;
 | 
			
		||||
				}
 | 
			
		||||
@ -56,7 +56,7 @@ export class PageResolver
 | 
			
		||||
							console.log(error.status + " - " + error.message);
 | 
			
		||||
							this.snackBar.open(`An unknown error occurred: ${error.message}.`, null, {
 | 
			
		||||
								horizontalPosition: "left",
 | 
			
		||||
								panelClass: ['snackError'],
 | 
			
		||||
								panelClass: ["snackError"],
 | 
			
		||||
								duration: 2500
 | 
			
		||||
							});
 | 
			
		||||
							return EMPTY;
 | 
			
		||||
 | 
			
		||||
@ -15,7 +15,7 @@ export class PreLoaderService
 | 
			
		||||
 | 
			
		||||
	load<T>(route: string): Observable<T[]>
 | 
			
		||||
	{
 | 
			
		||||
		let loaded = this.cache.find(x => x[0] === route);
 | 
			
		||||
		const loaded: [string, any[]] = this.cache.find(x => x[0] === route);
 | 
			
		||||
		if (loaded != null)
 | 
			
		||||
			return of(loaded[1]);
 | 
			
		||||
		return this.http.get<Page<T>>(route).pipe(map(newData =>
 | 
			
		||||
 | 
			
		||||
@ -15,7 +15,7 @@ export class StartupService
 | 
			
		||||
		if (window.location.pathname.startsWith("/watch/"))
 | 
			
		||||
		{
 | 
			
		||||
			this.loadedFromWatch = true;
 | 
			
		||||
			this.show = window.location.pathname.match(/^\/watch\/(?<show>.*)(-s\d+e\d+)+?$/).groups["show"];
 | 
			
		||||
			this.show = window.location.pathname.match(/^\/watch\/(?<show>.*)(-s\d+e\d+)+?$/).groups.show;
 | 
			
		||||
		}
 | 
			
		||||
		return Promise.resolve(null);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -1,3 +1,3 @@
 | 
			
		||||
export const environment = {
 | 
			
		||||
export const environment: any = {
 | 
			
		||||
	production: true
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@
 | 
			
		||||
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
 | 
			
		||||
// The list of file replacements can be found in `angular.json`.
 | 
			
		||||
 | 
			
		||||
export const environment = {
 | 
			
		||||
export const environment: any = {
 | 
			
		||||
	production: false
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,7 @@
 | 
			
		||||
import { enableProdMode } from '@angular/core';
 | 
			
		||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
 | 
			
		||||
 | 
			
		||||
import { AppModule } from './app/app.module';
 | 
			
		||||
import { environment } from './environments/environment';
 | 
			
		||||
import { enableProdMode } from "@angular/core";
 | 
			
		||||
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
 | 
			
		||||
import { AppModule } from "./app/app.module";
 | 
			
		||||
import { environment } from "./environments/environment";
 | 
			
		||||
 | 
			
		||||
if (environment.production) {
 | 
			
		||||
	enableProdMode();
 | 
			
		||||
 | 
			
		||||
@ -55,7 +55,7 @@
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 * 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.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/***************************************************************************************************
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										16
									
								
								tslint.json
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								tslint.json
									
									
									
									
									
								
							@ -42,14 +42,12 @@
 | 
			
		||||
            "trace"
 | 
			
		||||
        ],
 | 
			
		||||
        "no-empty": false,
 | 
			
		||||
        "no-inferrable-types": [
 | 
			
		||||
            true,
 | 
			
		||||
            "ignore-params"
 | 
			
		||||
        ],
 | 
			
		||||
        "no-inferrable-types": false,
 | 
			
		||||
        "no-non-null-assertion": true,
 | 
			
		||||
        "no-redundant-jsdoc": true,
 | 
			
		||||
		"jsdoc-format": false,
 | 
			
		||||
        "no-switch-case-fall-through": true,
 | 
			
		||||
        "no-var-requires": false,
 | 
			
		||||
        "no-var-requires": true,
 | 
			
		||||
        "object-literal-key-quotes": [
 | 
			
		||||
            true,
 | 
			
		||||
            "as-needed"
 | 
			
		||||
@ -74,7 +72,10 @@
 | 
			
		||||
        },
 | 
			
		||||
        "typedef": [
 | 
			
		||||
            true,
 | 
			
		||||
            "call-signature"
 | 
			
		||||
            "call-signature",
 | 
			
		||||
			"parameter",
 | 
			
		||||
			"property-declaration",
 | 
			
		||||
			"variable-declaration"
 | 
			
		||||
        ],
 | 
			
		||||
        "typedef-whitespace": {
 | 
			
		||||
            "options": [
 | 
			
		||||
@ -98,7 +99,8 @@
 | 
			
		||||
            "options": [
 | 
			
		||||
                "ban-keywords",
 | 
			
		||||
                "check-format",
 | 
			
		||||
                "allow-pascal-case"
 | 
			
		||||
				"allow-leading-underscore",
 | 
			
		||||
				"require-const-for-all-caps"
 | 
			
		||||
            ]
 | 
			
		||||
        },
 | 
			
		||||
        "whitespace": {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user