mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-08 02:34:16 -04:00
Making the show details page use the new api for shows, seasons & episodes
This commit is contained in:
parent
b8d5265316
commit
09f8328900
@ -1,20 +1,14 @@
|
|||||||
import {NgModule} from '@angular/core';
|
import {NgModule} from '@angular/core';
|
||||||
import {RouterModule, Routes} from '@angular/router';
|
import {RouterModule, Routes} from '@angular/router';
|
||||||
import {LibraryItemGridComponent} from './components/library-item-grid/library-item-grid.component';
|
import {LibraryItemGridComponent} from './components/library-item-grid/library-item-grid.component';
|
||||||
import {CollectionComponent} from "./collection/collection.component";
|
|
||||||
import {NotFoundComponent} from './not-found/not-found.component';
|
import {NotFoundComponent} from './not-found/not-found.component';
|
||||||
import {PlayerComponent} from "./pages/player/player.component";
|
|
||||||
import {SearchComponent} from "./pages/search/search.component";
|
|
||||||
import {CollectionResolverService} from "./services/resolvers/collection-resolver.service";
|
|
||||||
import {PageResolver} from './services/resolvers/page-resolver.service';
|
import {PageResolver} from './services/resolvers/page-resolver.service';
|
||||||
import {PeopleResolverService} from "./services/resolvers/people-resolver.service";
|
|
||||||
import {SearchResolverService} from "./services/resolvers/search-resolver.service";
|
|
||||||
import {ShowResolverService} from './services/resolvers/show-resolver.service';
|
|
||||||
import {StreamResolverService} from "./services/resolvers/stream-resolver.service";
|
|
||||||
import {ShowDetailsComponent} from './pages/show-details/show-details.component';
|
import {ShowDetailsComponent} from './pages/show-details/show-details.component';
|
||||||
import {AuthGuard} from "./auth/misc/authenticated-guard.service";
|
import {AuthGuard} from "./auth/misc/authenticated-guard.service";
|
||||||
import {LibraryItem} from "../models/library-item";
|
import {LibraryItem} from "../models/library-item";
|
||||||
import {LibraryItemService, LibraryService} from "./services/api.service";
|
import {LibraryItemService, LibraryService} from "./services/api.service";
|
||||||
|
import {Show} from "../models/show";
|
||||||
|
import {ItemResolver} from "./services/resolvers/item-resolver.service";
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{path: "browse", component: LibraryItemGridComponent, pathMatch: "full",
|
{path: "browse", component: LibraryItemGridComponent, pathMatch: "full",
|
||||||
@ -28,11 +22,16 @@ const routes: Routes = [
|
|||||||
canActivate: [AuthGuard.forPermissions("read")]
|
canActivate: [AuthGuard.forPermissions("read")]
|
||||||
},
|
},
|
||||||
|
|
||||||
{path: "show/:show-slug", component: ShowDetailsComponent, resolve: { show: ShowResolverService }, canLoad: [AuthGuard.forPermissions("read")], canActivate: [AuthGuard.forPermissions("read")]},
|
{path: "show/:slug", component: ShowDetailsComponent,
|
||||||
{path: "collection/:collection-slug", component: CollectionComponent, resolve: { collection: CollectionResolverService }, canLoad: [AuthGuard.forPermissions("read")], canActivate: [AuthGuard.forPermissions("read")]},
|
resolve: { show: ItemResolver.forResource<Show>("shows/:slug") },
|
||||||
{path: "people/:people-slug", component: CollectionComponent, resolve: { collection: PeopleResolverService }, canLoad: [AuthGuard.forPermissions("read")], canActivate: [AuthGuard.forPermissions("read")]},
|
canLoad: [AuthGuard.forPermissions("read")],
|
||||||
{path: "watch/:item", component: PlayerComponent, resolve: { item: StreamResolverService }, canLoad: [AuthGuard.forPermissions("play")], canActivate: [AuthGuard.forPermissions("play")]},
|
canActivate: [AuthGuard.forPermissions("read")]
|
||||||
{path: "search/:query", component: SearchComponent, resolve: { items: SearchResolverService }, canLoad: [AuthGuard.forPermissions("read")], canActivate: [AuthGuard.forPermissions("read")]},
|
},
|
||||||
|
// {path: "collection/:collection-slug", component: CollectionComponent, resolve: { collection: CollectionResolverService }, canLoad: [AuthGuard.forPermissions("read")], canActivate: [AuthGuard.forPermissions("read")]},
|
||||||
|
//
|
||||||
|
// {path: "people/:people-slug", component: CollectionComponent, resolve: { collection: PeopleResolverService }, canLoad: [AuthGuard.forPermissions("read")], canActivate: [AuthGuard.forPermissions("read")]},
|
||||||
|
// {path: "watch/:item", component: PlayerComponent, resolve: { item: StreamResolverService }, canLoad: [AuthGuard.forPermissions("play")], canActivate: [AuthGuard.forPermissions("play")]},
|
||||||
|
// {path: "search/:query", component: SearchComponent, resolve: { items: SearchResolverService }, canLoad: [AuthGuard.forPermissions("read")], canActivate: [AuthGuard.forPermissions("read")]},
|
||||||
{path: "**", component: NotFoundComponent}
|
{path: "**", component: NotFoundComponent}
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -46,11 +45,7 @@ const routes: Routes = [
|
|||||||
LibraryService,
|
LibraryService,
|
||||||
LibraryItemService,
|
LibraryItemService,
|
||||||
PageResolver.resolvers,
|
PageResolver.resolvers,
|
||||||
ShowResolverService,
|
ItemResolver.resolvers,
|
||||||
CollectionResolverService,
|
|
||||||
PeopleResolverService,
|
|
||||||
StreamResolverService,
|
|
||||||
SearchResolverService
|
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class AppRoutingModule { }
|
export class AppRoutingModule { }
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<div class="root">
|
<div class="root">
|
||||||
<div class="episodes" #scrollView (scroll)="onScroll()">
|
<div class="episodes" #scrollView (scroll)="onScroll()">
|
||||||
<div class="episode" *ngFor="let episode of this.episodes" #episodeDom >
|
<div class="episode" *ngFor="let episode of this.episodes?.items" #itemsDom >
|
||||||
<button mat-icon-button class="moreBtn" [matMenuTriggerFor]="more" [matMenuTriggerData]="{episode: episode}"><i class="material-icons">more_vert</i></button>
|
<button mat-icon-button class="moreBtn" [matMenuTriggerFor]="more" [matMenuTriggerData]="{episode: episode}"><i class="material-icons">more_vert</i></button>
|
||||||
<a routerLink="/watch/{{episode.slug}}" href="/watch/{{episode.slug}}">
|
<a routerLink="/watch/{{episode.slug}}" href="/watch/{{episode.slug}}">
|
||||||
<div matRipple class="img" [style.background-image]="sanitize(episode.thumb)">
|
<div matRipple class="img" [style.background-image]="sanitize(episode.thumb)">
|
||||||
|
@ -1,56 +1,22 @@
|
|||||||
import { Component, ElementRef, Input, ViewChild } from '@angular/core';
|
import { Component, Input} from '@angular/core';
|
||||||
import { MatButton } from "@angular/material/button";
|
|
||||||
import { DomSanitizer } from "@angular/platform-browser";
|
import { DomSanitizer } from "@angular/platform-browser";
|
||||||
import { Episode } from "../../../models/episode";
|
import { Episode } from "../../../models/episode";
|
||||||
|
import {HorizontalScroller} from "../../misc/horizontal-scroller";
|
||||||
|
import {Page} from "../../../models/page";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-episodes-list',
|
selector: 'app-episodes-list',
|
||||||
templateUrl: './episodes-list.component.html',
|
templateUrl: './episodes-list.component.html',
|
||||||
styleUrls: ['./episodes-list.component.scss']
|
styleUrls: ['./episodes-list.component.scss']
|
||||||
})
|
})
|
||||||
export class EpisodesListComponent
|
export class EpisodesListComponent extends HorizontalScroller
|
||||||
{
|
{
|
||||||
@Input() displayShowTitle: boolean = false;
|
@Input() displayShowTitle: boolean = false;
|
||||||
@Input() episodes: Episode[];
|
@Input() episodes: Page<Episode>;
|
||||||
@ViewChild("scrollView", { static: true }) private scrollView: ElementRef;
|
|
||||||
@ViewChild("leftBtn", { static: false }) private leftBtn: MatButton;
|
|
||||||
@ViewChild("rightBtn", { static: false }) private rightBtn: MatButton;
|
|
||||||
@ViewChild("episodeDom", { static: false }) private episode: ElementRef;
|
|
||||||
|
|
||||||
constructor(private sanitizer: DomSanitizer) { }
|
constructor(private sanitizer: DomSanitizer)
|
||||||
|
|
||||||
scrollLeft()
|
|
||||||
{
|
{
|
||||||
let scroll: number = this.roundScroll(this.scrollView.nativeElement.offsetWidth * 0.80);
|
super();
|
||||||
this.scrollView.nativeElement.scrollBy({ top: 0, left: -scroll, behavior: "smooth" });
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollRight()
|
|
||||||
{
|
|
||||||
let 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 episodeSize: number = this.episode.nativeElement.scrollWidth;
|
|
||||||
|
|
||||||
offset = Math.round(offset / episodeSize) * episodeSize;
|
|
||||||
if (offset == 0)
|
|
||||||
offset = episodeSize;
|
|
||||||
return offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
onScroll()
|
|
||||||
{
|
|
||||||
if (this.scrollView.nativeElement.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)
|
|
||||||
this.rightBtn._elementRef.nativeElement.classList.add("d-none");
|
|
||||||
else
|
|
||||||
this.rightBtn._elementRef.nativeElement.classList.remove("d-none");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sanitize(url: string)
|
sanitize(url: string)
|
||||||
|
@ -16,7 +16,7 @@ export class LibraryItemGridComponent
|
|||||||
@Input() page: Page<LibraryItem>;
|
@Input() page: Page<LibraryItem>;
|
||||||
@Input() sortEnabled: boolean = true;
|
@Input() sortEnabled: boolean = true;
|
||||||
sortType: string = "title";
|
sortType: string = "title";
|
||||||
sortKeys: string[] = ["title", "start year", "end year", "status", "type"]
|
sortKeys: string[] = ["title", "start year", "end year"]
|
||||||
sortUp: boolean = true;
|
sortUp: boolean = true;
|
||||||
|
|
||||||
constructor(private route: ActivatedRoute,
|
constructor(private route: ActivatedRoute,
|
||||||
@ -48,7 +48,7 @@ export class LibraryItemGridComponent
|
|||||||
this.sortType = type;
|
this.sortType = type;
|
||||||
this.sortUp = order;
|
this.sortUp = order;
|
||||||
|
|
||||||
this.items.getAll({sort: `${this.sortType.replace(/\s/g, "")}:${this.sortUp ? "asc" : "desc"}`})
|
this.items.getAll({sortBy: `${this.sortType.replace(/\s/g, "")}:${this.sortUp ? "asc" : "desc"}`})
|
||||||
.subscribe(x => this.page = x);
|
.subscribe(x => this.page = x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
44
src/app/misc/horizontal-scroller.ts
Normal file
44
src/app/misc/horizontal-scroller.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import {ElementRef, ViewChild} from "@angular/core";
|
||||||
|
import {MatButton} from "@angular/material/button";
|
||||||
|
|
||||||
|
export class HorizontalScroller
|
||||||
|
{
|
||||||
|
@ViewChild("scrollView", { static: true }) private scrollView: ElementRef;
|
||||||
|
@ViewChild("leftBtn", { static: false }) private leftBtn: MatButton;
|
||||||
|
@ViewChild("rightBtn", { static: false }) private rightBtn: MatButton;
|
||||||
|
@ViewChild("itemsDom", { static: false }) private items: ElementRef;
|
||||||
|
|
||||||
|
scrollLeft()
|
||||||
|
{
|
||||||
|
let scroll: number = this.roundScroll(this.scrollView.nativeElement.offsetWidth * 0.80);
|
||||||
|
this.scrollView.nativeElement.scrollBy({ top: 0, left: -scroll, behavior: "smooth" });
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollRight()
|
||||||
|
{
|
||||||
|
let 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.items.nativeElement.scrollWidth;
|
||||||
|
|
||||||
|
offset = Math.round(offset / itemSize) * itemSize;
|
||||||
|
if (offset == 0)
|
||||||
|
offset = itemSize;
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
onScroll()
|
||||||
|
{
|
||||||
|
if (this.scrollView.nativeElement.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)
|
||||||
|
this.rightBtn._elementRef.nativeElement.classList.add("d-none");
|
||||||
|
else
|
||||||
|
this.rightBtn._elementRef.nativeElement.classList.remove("d-none");
|
||||||
|
}
|
||||||
|
}
|
@ -78,12 +78,12 @@
|
|||||||
<div class="container-fluid mt-3">
|
<div class="container-fluid mt-3">
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<mat-label>Season</mat-label>
|
<mat-label>Season</mat-label>
|
||||||
<mat-select [(value)]="season" (selectionChange)="getEpisodes()">
|
<mat-select [(value)]="season" (selectionChange)="getEpisodes(season.seasonNumber)">
|
||||||
<mat-option *ngFor="let season of this.show.seasons" [value]="season.seasonNumber">{{season.title}}</mat-option>
|
<mat-option *ngFor="let season of this.seasons" [value]="season.seasonNumber">{{season.title}}</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
<app-episodes-list [episodes]="episodes"></app-episodes-list>
|
<app-episodes-list [episodes]="episodes[season]"></app-episodes-list>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container-fluid mt-5">
|
<div class="container-fluid mt-5">
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { HttpClient } from "@angular/common/http";
|
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { MatSnackBar } from "@angular/material/snack-bar";
|
import { MatSnackBar } from "@angular/material/snack-bar";
|
||||||
import { Title } from '@angular/platform-browser';
|
import { Title } from '@angular/platform-browser';
|
||||||
@ -8,7 +7,9 @@ import { Show } from "../../../models/show";
|
|||||||
import {MatDialog} from "@angular/material/dialog";
|
import {MatDialog} from "@angular/material/dialog";
|
||||||
import {TrailerDialogComponent} from "../trailer-dialog/trailer-dialog.component";
|
import {TrailerDialogComponent} from "../trailer-dialog/trailer-dialog.component";
|
||||||
import {MetadataEditComponent} from "../metadata-edit/metadata-edit.component";
|
import {MetadataEditComponent} from "../metadata-edit/metadata-edit.component";
|
||||||
import {Account} from "../../../models/account";
|
import {Season} from "../../../models/season";
|
||||||
|
import {EpisodeService, SeasonService} from "../../services/api.service";
|
||||||
|
import {Page} from "../../../models/page";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-show-details',
|
selector: 'app-show-details',
|
||||||
@ -18,17 +19,24 @@ import {Account} from "../../../models/account";
|
|||||||
export class ShowDetailsComponent implements OnInit
|
export class ShowDetailsComponent implements OnInit
|
||||||
{
|
{
|
||||||
show: Show;
|
show: Show;
|
||||||
episodes: Episode[] = null;
|
seasons: Season[];
|
||||||
season: number;
|
season: number = 1;
|
||||||
|
episodes: Page<Episode>[] = [];
|
||||||
|
|
||||||
private toolbar: HTMLElement;
|
private toolbar: HTMLElement;
|
||||||
private backdrop: HTMLElement;
|
private backdrop: HTMLElement;
|
||||||
|
|
||||||
constructor(private route: ActivatedRoute, private http: HttpClient, private snackBar: MatSnackBar, private title: Title, private router: Router, private dialog: MatDialog)
|
constructor(private route: ActivatedRoute,
|
||||||
|
private snackBar: MatSnackBar,
|
||||||
|
private title: Title,
|
||||||
|
private router: Router,
|
||||||
|
private dialog: MatDialog,
|
||||||
|
private seasonService: SeasonService,
|
||||||
|
private episodeService: EpisodeService)
|
||||||
{
|
{
|
||||||
this.route.queryParams.subscribe(params =>
|
this.route.queryParams.subscribe(params =>
|
||||||
{
|
{
|
||||||
this.season = params["season"];
|
this.season = params["season"] ?? 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.route.data.subscribe(data =>
|
this.route.data.subscribe(data =>
|
||||||
@ -36,10 +44,19 @@ export class ShowDetailsComponent implements OnInit
|
|||||||
this.show = data.show;
|
this.show = data.show;
|
||||||
this.title.setTitle(this.show.title + " - Kyoo");
|
this.title.setTitle(this.show.title + " - Kyoo");
|
||||||
|
|
||||||
if (this.season == undefined || this.show.seasons == undefined || this.show.seasons.find(x => x.seasonNumber == this.season) == null)
|
if (this.show.isMovie)
|
||||||
this.season = 1;
|
return;
|
||||||
|
|
||||||
this.getEpisodes();
|
this.seasonService.getForShow(this.show.slug, {limit: 0}).subscribe(x =>
|
||||||
|
{
|
||||||
|
this.seasons = x.items;
|
||||||
|
if (x.items.find(x => x.seasonNumber == this.season) == null)
|
||||||
|
{
|
||||||
|
this.season = 1;
|
||||||
|
this.getEpisodes(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.getEpisodes(this.season);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,23 +89,17 @@ export class ShowDetailsComponent implements OnInit
|
|||||||
this.router.navigate(["/watch/" + this.show.slug + "-s1e1"]);
|
this.router.navigate(["/watch/" + this.show.slug + "-s1e1"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
getEpisodes()
|
getEpisodes(season: number)
|
||||||
{
|
{
|
||||||
if (this.show == undefined || this.show.seasons == undefined)
|
if (season < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (this.show.seasons.find(x => x.seasonNumber == this.season).episodes != null)
|
if (this.episodes[season] != undefined)
|
||||||
this.episodes = this.show.seasons.find(x => x.seasonNumber == this.season).episodes;
|
return;
|
||||||
|
|
||||||
|
this.episodeService.getFromSeasonNumber(this.show.slug, this.season).subscribe(x =>
|
||||||
this.http.get<Episode[]>("api/episodes/" + this.show.slug + "/season/" + this.season).subscribe((episodes: Episode[]) =>
|
|
||||||
{
|
{
|
||||||
this.show.seasons.find(x => x.seasonNumber == this.season).episodes = episodes;
|
this.episodes[season] = x;
|
||||||
this.episodes = episodes;
|
|
||||||
}, error =>
|
|
||||||
{
|
|
||||||
console.log(error.status + " - " + error.message);
|
|
||||||
this.snackBar.open("An unknow error occured while getting episodes.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,10 +119,10 @@ export class ShowDetailsComponent implements OnInit
|
|||||||
|
|
||||||
redownloadImages()
|
redownloadImages()
|
||||||
{
|
{
|
||||||
this.http.post("api/show/download-images/" + this.show.slug, undefined).subscribe(() => { }, error =>
|
// this.http.post("api/show/download-images/" + this.show.slug, undefined).subscribe(() => { }, error =>
|
||||||
{
|
// {
|
||||||
console.log(error.status + " - " + error.message);
|
// console.log(error.status + " - " + error.message);
|
||||||
this.snackBar.open("An unknown error occured while re-downloading images.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
|
// this.snackBar.open("An unknown error occured while re-downloading images.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
|
||||||
});
|
// });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,24 +6,38 @@ import {IResource} from "../../models/resources/resource";
|
|||||||
import {Library} from "../../models/library";
|
import {Library} from "../../models/library";
|
||||||
import {LibraryItem} from "../../models/library-item";
|
import {LibraryItem} from "../../models/library-item";
|
||||||
import {map} from "rxjs/operators";
|
import {map} from "rxjs/operators";
|
||||||
|
import {Season} from "../../models/season";
|
||||||
|
import {Episode} from "../../models/episode";
|
||||||
|
|
||||||
|
export interface ApiArgs
|
||||||
|
{
|
||||||
|
sortBy?: string;
|
||||||
|
limit?: number;
|
||||||
|
afterID?: number;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
class CrudApi<T extends IResource>
|
class CrudApi<T extends IResource>
|
||||||
{
|
{
|
||||||
constructor(private client: HttpClient, private route: string) {}
|
constructor(protected client: HttpClient, private route: string) {}
|
||||||
|
|
||||||
get(id: number | string): Observable<T>
|
get(id: number | string): Observable<T>
|
||||||
{
|
{
|
||||||
return this.client.get<T>(`/api/${this.route}/${id}`);
|
return this.client.get<T>(`/api/${this.route}/${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
getAll(args: {sort: string} = null): Observable<Page<T>>
|
protected ArgsAsQuery(args: ApiArgs): string
|
||||||
{
|
{
|
||||||
let params: string = "?";
|
if (args == null)
|
||||||
if (args && args.sort)
|
return "";
|
||||||
params += "sortBy=" + args.sort;
|
let params: string = Object.keys(args).map(x => `${x}=${args[x]}`).join("&");
|
||||||
if (params == "?")
|
|
||||||
params = "";
|
return params ? `?${params}` : "";
|
||||||
return this.client.get<Page<T>>(`/api/${this.route}${params}`)
|
}
|
||||||
|
|
||||||
|
getAll(args?: ApiArgs): Observable<Page<T>>
|
||||||
|
{
|
||||||
|
return this.client.get<Page<T>>(`/api/${this.route}${this.ArgsAsQuery(args)}`)
|
||||||
.pipe(map(x => Object.assign(new Page<T>(), x)));
|
.pipe(map(x => Object.assign(new Page<T>(), x)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,3 +78,43 @@ export class LibraryItemService extends CrudApi<LibraryItem>
|
|||||||
super(client, "items");
|
super(client, "items");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class SeasonService extends CrudApi<Season>
|
||||||
|
{
|
||||||
|
constructor(client: HttpClient)
|
||||||
|
{
|
||||||
|
super(client, "seasons");
|
||||||
|
}
|
||||||
|
|
||||||
|
getForShow(show: string | number, args?: ApiArgs): Observable<Page<Season>>
|
||||||
|
{
|
||||||
|
return this.client.get(`/api/show/${show}/seasons${this.ArgsAsQuery(args)}`)
|
||||||
|
.pipe(map(x => Object.assign(new Page<Season>(), x)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class EpisodeService extends CrudApi<Episode>
|
||||||
|
{
|
||||||
|
constructor(client: HttpClient)
|
||||||
|
{
|
||||||
|
super(client, "episodes");
|
||||||
|
}
|
||||||
|
|
||||||
|
getFromSeason(season: string | number, args?: ApiArgs): Observable<Page<Episode>>
|
||||||
|
{
|
||||||
|
return this.client.get(`/api/seasons/${season}/episodes${this.ArgsAsQuery(args)}`)
|
||||||
|
.pipe(map(x => Object.assign(new Page<Episode>(), x)));
|
||||||
|
}
|
||||||
|
|
||||||
|
getFromSeasonNumber(show: string | number, seasonNumber: number, args?: ApiArgs): Observable<Page<Episode>>
|
||||||
|
{
|
||||||
|
return this.client.get(`/api/seasons/${show}-${seasonNumber}/episodes${this.ArgsAsQuery(args)}`)
|
||||||
|
.pipe(map(x => Object.assign(new Page<Episode>(), x)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
44
src/app/services/resolvers/item-resolver.service.ts
Normal file
44
src/app/services/resolvers/item-resolver.service.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
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} from 'rxjs/operators';
|
||||||
|
import {IResource} from "../../../models/resources/resource";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ItemResolver
|
||||||
|
{
|
||||||
|
public static resolvers: any[] = [];
|
||||||
|
|
||||||
|
static forResource<T extends IResource>(resource: string)
|
||||||
|
{
|
||||||
|
@Injectable()
|
||||||
|
class Resolver implements Resolve<T>
|
||||||
|
{
|
||||||
|
constructor(private http: HttpClient,
|
||||||
|
private snackBar: MatSnackBar)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
resolve(route: ActivatedRouteSnapshot): T | Observable<T> | Promise<T>
|
||||||
|
{
|
||||||
|
let res: string = resource.replace(/:(.*?)(\/|$)/, (x, y) => `${route.paramMap.get(y)}/`);
|
||||||
|
|
||||||
|
return this.http.get<T>(`api/${res}`)
|
||||||
|
.pipe(
|
||||||
|
catchError((error: HttpErrorResponse) =>
|
||||||
|
{
|
||||||
|
console.log(error.status + " - " + error.message);
|
||||||
|
this.snackBar.open(`An unknown error occurred: ${error.message}.`, null, {
|
||||||
|
horizontalPosition: "left",
|
||||||
|
panelClass: ['snackError'],
|
||||||
|
duration: 2500
|
||||||
|
});
|
||||||
|
return EMPTY;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ItemResolver.resolvers.push(Resolver);
|
||||||
|
return Resolver;
|
||||||
|
}
|
||||||
|
}
|
@ -23,7 +23,7 @@ export class PageResolver
|
|||||||
|
|
||||||
resolve(route: ActivatedRouteSnapshot): Page<T> | Observable<Page<T>> | Promise<Page<T>>
|
resolve(route: ActivatedRouteSnapshot): Page<T> | Observable<Page<T>> | Promise<Page<T>>
|
||||||
{
|
{
|
||||||
let res: string = resource.replace(/:(.*?)\//, (x, y) => `${route.paramMap.get(y)}/`);
|
let res: string = resource.replace(/:(.*?)(\/|$)/, (x, y) => `${route.paramMap.get(y)}/`);
|
||||||
|
|
||||||
return this.http.get<Page<T>>(`api/${res}`)
|
return this.http.get<Page<T>>(`api/${res}`)
|
||||||
.pipe(
|
.pipe(
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import {ExternalID} from "./external-id";
|
import {ExternalID} from "./external-id";
|
||||||
|
import {IResource} from "./resources/resource";
|
||||||
|
|
||||||
export interface Episode
|
export interface Episode extends IResource
|
||||||
{
|
{
|
||||||
seasonNumber: number;
|
seasonNumber: number;
|
||||||
episodeNumber: number;
|
episodeNumber: number;
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { Episode } from "./episode";
|
import { Episode } from "./episode";
|
||||||
import {ExternalID} from "./external-id";
|
import {ExternalID} from "./external-id";
|
||||||
|
import {IResource} from "./resources/resource";
|
||||||
|
|
||||||
export interface Season
|
export interface Season extends IResource
|
||||||
{
|
{
|
||||||
seasonNumber: number;
|
seasonNumber: number;
|
||||||
title: string;
|
title: string;
|
||||||
|
@ -3,10 +3,10 @@ import { Genre } from "./genre";
|
|||||||
import {People} from "./people";
|
import {People} from "./people";
|
||||||
import {Studio} from "./studio";
|
import {Studio} from "./studio";
|
||||||
import {ExternalID} from "./external-id";
|
import {ExternalID} from "./external-id";
|
||||||
|
import {IResource} from "./resources/resource";
|
||||||
|
|
||||||
export interface Show
|
export interface Show extends IResource
|
||||||
{
|
{
|
||||||
slug: string;
|
|
||||||
title: string;
|
title: string;
|
||||||
aliases: string[];
|
aliases: string[];
|
||||||
overview: string;
|
overview: string;
|
||||||
@ -16,7 +16,6 @@ export interface Show
|
|||||||
people: People[];
|
people: People[];
|
||||||
seasons: Season[];
|
seasons: Season[];
|
||||||
trailerUrl: string;
|
trailerUrl: string;
|
||||||
isCollection: boolean;
|
|
||||||
isMovie: boolean;
|
isMovie: boolean;
|
||||||
startYear: number;
|
startYear: number;
|
||||||
endYear : number;
|
endYear : number;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user