mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-09 03:04:20 -04:00
Adding collections support.
This commit is contained in:
parent
c852d0c3b9
commit
312ab6b80b
@ -8,18 +8,24 @@ import { ShowResolverService } from './services/show-resolver.service';
|
|||||||
import { LibraryResolverService } from './services/library-resolver.service';
|
import { LibraryResolverService } from './services/library-resolver.service';
|
||||||
import { PlayerComponent } from "./player/player.component";
|
import { PlayerComponent } from "./player/player.component";
|
||||||
import { StreamResolverService } from "./services/stream-resolver.service";
|
import { StreamResolverService } from "./services/stream-resolver.service";
|
||||||
|
import { CollectionComponent } from "./collection/collection.component";
|
||||||
|
import { CollectionResolverService } from "./services/collection-resolver.service";
|
||||||
|
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{ path: "browse", component: BrowseComponent, pathMatch: "full", resolve: { shows: LibraryResolverService } },
|
{ path: "browse", component: BrowseComponent, pathMatch: "full", resolve: { shows: LibraryResolverService } },
|
||||||
{ path: "browse/:library-slug", component: BrowseComponent, resolve: { shows: LibraryResolverService } },
|
{ path: "browse/:library-slug", component: BrowseComponent, resolve: { shows: LibraryResolverService } },
|
||||||
{ path: "show/:show-slug", component: ShowDetailsComponent, resolve: { show: ShowResolverService } },
|
{ path: "show/:show-slug", component: ShowDetailsComponent, resolve: { show: ShowResolverService } },
|
||||||
|
{ path: "collection/:collection-slug", component: CollectionComponent, resolve: { shows: CollectionResolverService } },
|
||||||
{ path: "watch/:item", component: PlayerComponent, resolve: { item: StreamResolverService } },
|
{ path: "watch/:item", component: PlayerComponent, resolve: { item: StreamResolverService } },
|
||||||
{ path: "**", component: NotFoundComponent }
|
{ path: "**", component: NotFoundComponent }
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [RouterModule.forRoot(routes)],
|
imports: [RouterModule.forRoot(routes,
|
||||||
|
{
|
||||||
|
scrollPositionRestoration: "enabled"
|
||||||
|
})],
|
||||||
exports: [RouterModule],
|
exports: [RouterModule],
|
||||||
providers: [LibraryResolverService, ShowResolverService, StreamResolverService]
|
providers: [LibraryResolverService, ShowResolverService, StreamResolverService]
|
||||||
})
|
})
|
||||||
|
@ -16,10 +16,10 @@
|
|||||||
|
|
||||||
<ul class="navbar-nav flex-row ml-auto">
|
<ul class="navbar-nav flex-row ml-auto">
|
||||||
<li class="nav-item icon">
|
<li class="nav-item icon">
|
||||||
<mat-icon data-toggle="tooltip" data-placement="bottom" title="Search">search</mat-icon>
|
<mat-icon matTooltipPosition="below" matTooltip="Search">search</mat-icon>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="icon" routerLink="/login" routerLinkActive="active" data-toggle="tooltip" data-placement="bottom" title="Login">
|
<a class="icon" routerLink="/login" routerLinkActive="active" matTooltipPosition="below" matTooltip="Login">
|
||||||
<mat-icon>account_circle</mat-icon>
|
<mat-icon>account_circle</mat-icon>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { Event, Router, NavigationStart, NavigationEnd, NavigationCancel, NavigationError } from '@angular/router';
|
import { Event, Router, NavigationStart, NavigationEnd, NavigationCancel, NavigationError } from '@angular/router';
|
||||||
import * as $ from "jquery";
|
//import * as $ from "jquery";
|
||||||
import "bootstrap";
|
//import "bootstrap";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
@ -46,11 +46,6 @@ export class AppComponent
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit()
|
|
||||||
{
|
|
||||||
$('[data-toggle="tooltip"]').tooltip({ trigger: "hover" });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Library
|
interface Library
|
||||||
|
@ -7,6 +7,7 @@ import { MatProgressBarModule } from '@angular/material/progress-bar';
|
|||||||
import { MatSelectModule } from '@angular/material/select';
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
import { MatSliderModule } from '@angular/material/slider';
|
import { MatSliderModule } from '@angular/material/slider';
|
||||||
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||||
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { AppRoutingModule } from './app-routing.module';
|
import { AppRoutingModule } from './app-routing.module';
|
||||||
@ -16,6 +17,7 @@ import { EpisodesListComponent } from './episodes-list/episodes-list.component';
|
|||||||
import { NotFoundComponent } from './not-found/not-found.component';
|
import { NotFoundComponent } from './not-found/not-found.component';
|
||||||
import { PlayerComponent } from './player/player.component';
|
import { PlayerComponent } from './player/player.component';
|
||||||
import { ShowDetailsComponent } from './show-details/show-details.component';
|
import { ShowDetailsComponent } from './show-details/show-details.component';
|
||||||
|
import { CollectionComponent } from './collection/collection.component';
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@ -25,7 +27,8 @@ import { ShowDetailsComponent } from './show-details/show-details.component';
|
|||||||
BrowseComponent,
|
BrowseComponent,
|
||||||
ShowDetailsComponent,
|
ShowDetailsComponent,
|
||||||
EpisodesListComponent,
|
EpisodesListComponent,
|
||||||
PlayerComponent
|
PlayerComponent,
|
||||||
|
CollectionComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
@ -38,7 +41,8 @@ import { ShowDetailsComponent } from './show-details/show-details.component';
|
|||||||
MatIconModule,
|
MatIconModule,
|
||||||
MatSelectModule,
|
MatSelectModule,
|
||||||
MatMenuModule,
|
MatMenuModule,
|
||||||
MatSliderModule
|
MatSliderModule,
|
||||||
|
MatTooltipModule
|
||||||
],
|
],
|
||||||
providers: [],
|
providers: [],
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<div class="container-fluid justify-content-center">
|
<div class="container-fluid justify-content-center">
|
||||||
<button mat-icon-button data-toggle="tooltip" data-placement="bottom" title="Filter">
|
<button mat-icon-button matTooltipPosition="below" matTooltip="Filter">
|
||||||
<mat-icon>filter_list</mat-icon>
|
<mat-icon>filter_list</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<button mat-button data-toggle="tooltip" data-placement="bottom" title="Sort" [matMenuTriggerFor]="sortMenu">
|
<button mat-button matTooltipPosition="below" matTooltip="Sort" [matMenuTriggerFor]="sortMenu">
|
||||||
<mat-icon>sort</mat-icon> Sort by {{this.sortType}} <i *ngIf="this.sortUp" class="material-icons arrow">arrow_upward</i><i *ngIf="!this.sortUp" class="material-icons arrow">arrow_downward</i>
|
<mat-icon>sort</mat-icon> Sort by {{this.sortType}} <i *ngIf="this.sortUp" class="material-icons arrow">arrow_upward</i><i *ngIf="!this.sortUp" class="material-icons arrow">arrow_downward</i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -23,7 +23,7 @@ button
|
|||||||
width: 33%;
|
width: 33%;
|
||||||
max-width: 200px;
|
max-width: 200px;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
padding: 1em;
|
padding: .5em;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
outline: none;
|
outline: none;
|
||||||
@ -36,6 +36,7 @@ button
|
|||||||
@include media-breakpoint-up(md)
|
@include media-breakpoint-up(md)
|
||||||
{
|
{
|
||||||
width: 20%;
|
width: 20%;
|
||||||
|
padding: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@include media-breakpoint-up(lg)
|
@include media-breakpoint-up(lg)
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
<p>collection works!</p>
|
@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { CollectionComponent } from './collection.component';
|
||||||
|
|
||||||
|
describe('CollectionComponent', () => {
|
||||||
|
let component: CollectionComponent;
|
||||||
|
let fixture: ComponentFixture<CollectionComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ CollectionComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(CollectionComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
15
Kyoo/ClientApp/src/app/collection/collection.component.ts
Normal file
15
Kyoo/ClientApp/src/app/collection/collection.component.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-collection',
|
||||||
|
templateUrl: './collection.component.html',
|
||||||
|
styleUrls: ['./collection.component.scss']
|
||||||
|
})
|
||||||
|
export class CollectionComponent implements OnInit {
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -13,14 +13,6 @@
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover
|
|
||||||
{
|
|
||||||
.episodes
|
|
||||||
{
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.episodes
|
.episodes
|
||||||
@ -32,7 +24,6 @@
|
|||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
visibility: hidden;
|
|
||||||
|
|
||||||
&::-webkit-scrollbar
|
&::-webkit-scrollbar
|
||||||
{
|
{
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
<div id="hover">
|
<div id="hover">
|
||||||
<div class="back">
|
<div class="back">
|
||||||
<a mat-icon-button data-toggle="tooltip" data-placement="bottom" title="Back" href="/show/{{this.item.showSlug}}" routerLink="/show/{{this.item.showSlug}}">
|
<a mat-icon-button matTooltipPosition="below" matTooltip="Back" href="/show/{{this.item.showSlug}}" routerLink="/show/{{this.item.showSlug}}">
|
||||||
<mat-icon>arrow_back</mat-icon>
|
<mat-icon>arrow_back</mat-icon>
|
||||||
</a>
|
</a>
|
||||||
<h5>{{this.item.showTitle}}</h5>
|
<h5>{{this.item.showTitle}}</h5>
|
||||||
@ -35,10 +35,10 @@
|
|||||||
|
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<a *ngIf="this.item.previousEpisode" mat-icon-button data-toggle="tooltip" data-placement="top" title="Previous" routerLink="/watch/{{this.item.previousEpisode}}" href="/watch/{{this.item.previousEpisode}}" queryParamsHandling="merge">
|
<a *ngIf="this.item.previousEpisode" mat-icon-button matTooltipPosition="above" matTooltip="Previous" routerLink="/watch/{{this.item.previousEpisode}}" href="/watch/{{this.item.previousEpisode}}" queryParamsHandling="merge">
|
||||||
<mat-icon>skip_previous</mat-icon>
|
<mat-icon>skip_previous</mat-icon>
|
||||||
</a>
|
</a>
|
||||||
<button mat-icon-button data-toggle="tooltip" data-placement="top" title="Play" id="play" (click)="tooglePlayback()">
|
<button mat-icon-button matTooltipPosition="above" matTooltip="Play" id="play" (click)="tooglePlayback()">
|
||||||
<mat-icon>{{this.playIcon}}</mat-icon>
|
<mat-icon>{{this.playIcon}}</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<a mat-icon-button id="nextBtn" *ngIf="this.item.nextEpisode" routerLink="/watch/{{this.item.nextEpisode.link}}" href="/watch/{{this.item.nextEpisode.link}}" queryParamsHandling="merge">
|
<a mat-icon-button id="nextBtn" *ngIf="this.item.nextEpisode" routerLink="/watch/{{this.item.nextEpisode.link}}" href="/watch/{{this.item.nextEpisode.link}}" queryParamsHandling="merge">
|
||||||
@ -55,7 +55,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<div id="volume">
|
<div id="volume">
|
||||||
<button mat-icon-button data-toggle="tooltip" data-placement="top" title="Volume" (click)="toogleMute()">
|
<button mat-icon-button matTooltipPosition="above" matTooltip="Volume" (click)="toogleMute()">
|
||||||
<mat-icon>{{this.volumeIcon}}</mat-icon>
|
<mat-icon>{{this.volumeIcon}}</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@ -65,19 +65,19 @@
|
|||||||
<ng-template #elseBlock><p>{{this.minutes | number: '2.0-0'}}:{{this.seconds | number: '2.0-0'}} / {{this.maxMinutes | number: '2.0-0'}}:{{this.maxSeconds | number: '2.0-0'}}</p></ng-template>
|
<ng-template #elseBlock><p>{{this.minutes | number: '2.0-0'}}:{{this.seconds | number: '2.0-0'}} / {{this.maxMinutes | number: '2.0-0'}}:{{this.maxSeconds | number: '2.0-0'}}</p></ng-template>
|
||||||
</div>
|
</div>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<button *ngIf="this.item.audios.length > 0" mat-icon-button data-toggle="tooltip" data-placement="top" title="Select audio track">
|
<button *ngIf="this.item.audios.length > 0" mat-icon-button matTooltipPosition="above" matTooltip="Select audio track">
|
||||||
<mat-icon>music_note</mat-icon>
|
<mat-icon>music_note</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<button *ngIf="this.item.subtitles.length > 0" mat-icon-button [matMenuTriggerFor]="subtitles" data-toggle="tooltip" data-placement="top" title="Select subtitle track">
|
<button *ngIf="this.item.subtitles.length > 0" mat-icon-button [matMenuTriggerFor]="subtitles" matTooltipPosition="above" matTooltip="Select subtitle track">
|
||||||
<mat-icon>closed_caption</mat-icon>
|
<mat-icon>closed_caption</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<button mat-icon-button data-toggle="tooltip" data-placement="top" title="Cast">
|
<button mat-icon-button matTooltipPosition="above" matTooltip="Cast">
|
||||||
<mat-icon>cast</mat-icon>
|
<mat-icon>cast</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<button mat-icon-button data-toggle="tooltip" data-placement="top" title="Settings">
|
<button mat-icon-button matTooltipPosition="above" matTooltip="Settings">
|
||||||
<mat-icon>settings</mat-icon>
|
<mat-icon>settings</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<button mat-icon-button data-toggle="tooltip" data-placement="top" title="Fullscreen" id="fullscreen" (click)="fullscreen()">
|
<button mat-icon-button matTooltipPosition="above" matTooltip="Fullscreen" id="fullscreen" (click)="fullscreen()">
|
||||||
<mat-icon>{{fullscreenIcon}}</mat-icon>
|
<mat-icon>{{fullscreenIcon}}</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
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 { EMPTY, Observable } from 'rxjs';
|
||||||
|
import { catchError } from 'rxjs/operators';
|
||||||
|
import { Collection } from "../../models/collection";
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CollectionResolverService implements Resolve<Collection>
|
||||||
|
{
|
||||||
|
constructor(private http: HttpClient, private snackBar: MatSnackBar) { }
|
||||||
|
|
||||||
|
resolve(route: ActivatedRouteSnapshot): Collection | Observable<Collection> | Promise<Collection>
|
||||||
|
{
|
||||||
|
let collection: string = route.paramMap.get("collection-slug");
|
||||||
|
return this.http.get<Collection>("api/collection/" + collection).pipe(catchError((error: HttpErrorResponse) =>
|
||||||
|
{
|
||||||
|
console.log(error.status + " - " + error.message);
|
||||||
|
if (error.status == 404)
|
||||||
|
{
|
||||||
|
this.snackBar.open("Collection \"" + collection + "\" not found.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.snackBar.open("An unknow error occured.", null, { horizontalPosition: "left", panelClass: ['snackError'], duration: 2500 });
|
||||||
|
}
|
||||||
|
return EMPTY;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
@ -12,19 +12,19 @@
|
|||||||
<ng-template #elseBlock><h2 class="date">{{show.startYear}}</h2></ng-template>
|
<ng-template #elseBlock><h2 class="date">{{show.startYear}}</h2></ng-template>
|
||||||
</div>
|
</div>
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<button mat-mini-fab data-toggle="tooltip" data-placement="top" title="Play" class="mr-3">
|
<button mat-mini-fab matTooltipPosition="above" matTooltip="Play" class="mr-3">
|
||||||
<mat-icon>play_arrow</mat-icon>
|
<mat-icon>play_arrow</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<button *ngIf="this.show.trailerUrl" mat-icon-button data-toggle="tooltip" data-placement="top" title="Trailer">
|
<button *ngIf="this.show.trailerUrl" mat-icon-button matTooltipPosition="above" matTooltip="Trailer">
|
||||||
<mat-icon>local_movies</mat-icon>
|
<mat-icon>local_movies</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<button mat-icon-button data-toggle="tooltip" data-placement="top" title="Download">
|
<button mat-icon-button matTooltipPosition="above" matTooltip="Download">
|
||||||
<mat-icon>cloud_download</mat-icon>
|
<mat-icon>cloud_download</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<button mat-icon-button data-toggle="tooltip" data-placement="top" title="Watched">
|
<button mat-icon-button matTooltipPosition="above" matTooltip="Watched">
|
||||||
<mat-icon>done</mat-icon>
|
<mat-icon>done</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<button mat-icon-button data-toggle="tooltip" data-placement="top" title="More">
|
<button mat-icon-button matTooltipPosition="above" matTooltip="More">
|
||||||
<mat-icon>more_horiz</mat-icon>
|
<mat-icon>more_horiz</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -17,8 +17,13 @@ a
|
|||||||
{
|
{
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-height: 75vh;
|
max-height: 75vh;
|
||||||
min-height: 60vh;
|
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
|
min-height: 20vh;
|
||||||
|
|
||||||
|
@include media-breakpoint-up(md)
|
||||||
|
{
|
||||||
|
min-height: 60vh;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:after
|
&:after
|
||||||
@ -194,12 +199,6 @@ hr
|
|||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
visibility: hidden;
|
|
||||||
|
|
||||||
&:hover
|
|
||||||
{
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-scrollbar
|
&::-webkit-scrollbar
|
||||||
{
|
{
|
||||||
|
3
Kyoo/ClientApp/src/models/collection.js
Normal file
3
Kyoo/ClientApp/src/models/collection.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
//# sourceMappingURL=collection.js.map
|
1
Kyoo/ClientApp/src/models/collection.js.map
Normal file
1
Kyoo/ClientApp/src/models/collection.js.map
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"collection.js","sourceRoot":"","sources":["collection.ts"],"names":[],"mappings":""}
|
9
Kyoo/ClientApp/src/models/collection.ts
Normal file
9
Kyoo/ClientApp/src/models/collection.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { Show } from "./show";
|
||||||
|
|
||||||
|
export interface Collection
|
||||||
|
{
|
||||||
|
slug: string;
|
||||||
|
name: string;
|
||||||
|
overview: string;
|
||||||
|
shows: Show[];
|
||||||
|
}
|
30
Kyoo/Controllers/CollectionController.cs
Normal file
30
Kyoo/Controllers/CollectionController.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
using Kyoo.InternalAPI;
|
||||||
|
using Kyoo.Models;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Kyoo.Controllers
|
||||||
|
{
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
[ApiController]
|
||||||
|
public class CollectionController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly ILibraryManager libraryManager;
|
||||||
|
|
||||||
|
public CollectionController(ILibraryManager libraryManager)
|
||||||
|
{
|
||||||
|
this.libraryManager = libraryManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public ActionResult<Collection> GetShows(string collectionSlug)
|
||||||
|
{
|
||||||
|
Collection collection = libraryManager.GetCollection(collectionSlug);
|
||||||
|
|
||||||
|
if (collection == null)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
return collection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -15,6 +15,7 @@ namespace Kyoo.InternalAPI
|
|||||||
List<Genre> GetGenreForShow(long showID);
|
List<Genre> GetGenreForShow(long showID);
|
||||||
List<Season> GetSeasons(long showID);
|
List<Season> GetSeasons(long showID);
|
||||||
int GetSeasonCount(string showSlug, long seasonNumber);
|
int GetSeasonCount(string showSlug, long seasonNumber);
|
||||||
|
IEnumerable<Show> GetShowsInCollection(long collectionID);
|
||||||
|
|
||||||
//Internal HTML read
|
//Internal HTML read
|
||||||
(List<Track> audios, List<Track> subtitles) GetStreams(long episodeID, string showSlug);
|
(List<Track> audios, List<Track> subtitles) GetStreams(long episodeID, string showSlug);
|
||||||
@ -31,6 +32,7 @@ namespace Kyoo.InternalAPI
|
|||||||
People GetPeopleBySlug(string slug);
|
People GetPeopleBySlug(string slug);
|
||||||
Genre GetGenreBySlug(string slug);
|
Genre GetGenreBySlug(string slug);
|
||||||
Studio GetStudioBySlug(string slug);
|
Studio GetStudioBySlug(string slug);
|
||||||
|
Collection GetCollection(string slug);
|
||||||
|
|
||||||
//Check if value exists
|
//Check if value exists
|
||||||
bool IsShowRegistered(string showPath);
|
bool IsShowRegistered(string showPath);
|
||||||
@ -40,6 +42,7 @@ namespace Kyoo.InternalAPI
|
|||||||
bool IsEpisodeRegistered(string episodePath);
|
bool IsEpisodeRegistered(string episodePath);
|
||||||
|
|
||||||
//Register values
|
//Register values
|
||||||
|
long RegisterCollection(Collection collection);
|
||||||
long RegisterShow(Show show);
|
long RegisterShow(Show show);
|
||||||
long RegisterSeason(Season season);
|
long RegisterSeason(Season season);
|
||||||
long RegisterEpisode(Episode episode);
|
long RegisterEpisode(Episode episode);
|
||||||
|
@ -97,6 +97,20 @@ namespace Kyoo.InternalAPI
|
|||||||
FOREIGN KEY(showID) REFERENCES shows(id)
|
FOREIGN KEY(showID) REFERENCES shows(id)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE TABLE collections(
|
||||||
|
id INTEGER PRIMARY KEY UNIQUE,
|
||||||
|
slug TEXT UNIQUE,
|
||||||
|
name TEXT,
|
||||||
|
overview TEXT,
|
||||||
|
imgPrimary TEXT
|
||||||
|
);
|
||||||
|
CREATE TABLE collectionsLinks(
|
||||||
|
collectionID INTEGER,
|
||||||
|
showID INTEGER,
|
||||||
|
FOREIGN KEY(collectionID) REFERENCES collections(id),
|
||||||
|
FOREIGN KEY(showID) REFERENCES shows(id)
|
||||||
|
);
|
||||||
|
|
||||||
CREATE TABLE studios(
|
CREATE TABLE studios(
|
||||||
id INTEGER PRIMARY KEY UNIQUE,
|
id INTEGER PRIMARY KEY UNIQUE,
|
||||||
slug TEXT UNIQUE,
|
slug TEXT UNIQUE,
|
||||||
@ -515,6 +529,38 @@ namespace Kyoo.InternalAPI
|
|||||||
// return genres;
|
// return genres;
|
||||||
//}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Collection GetCollection(string slug)
|
||||||
|
{
|
||||||
|
string query = "SELECT * FROM collections WHERE slug = $slug;";
|
||||||
|
|
||||||
|
using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection))
|
||||||
|
{
|
||||||
|
cmd.Parameters.AddWithValue("$slug", slug);
|
||||||
|
SQLiteDataReader reader = cmd.ExecuteReader();
|
||||||
|
|
||||||
|
if (reader.Read())
|
||||||
|
return Collection.FromReader(reader).SetShows(this);
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<Show> GetShowsInCollection(long collectionID)
|
||||||
|
{
|
||||||
|
string query = "SELECT * FROM shows JOIN collectionsLink l ON l.showID = shows.id WHERE l.collectionID = $id;";
|
||||||
|
|
||||||
|
using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection))
|
||||||
|
{
|
||||||
|
cmd.Parameters.AddWithValue("$id", collectionID);
|
||||||
|
SQLiteDataReader reader = cmd.ExecuteReader();
|
||||||
|
List<Show> shows = new List<Show>();
|
||||||
|
while (reader.Read())
|
||||||
|
shows.Add(Show.FromReader(reader));
|
||||||
|
|
||||||
|
return shows;
|
||||||
|
}
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Check if items exists
|
#region Check if items exists
|
||||||
@ -638,6 +684,23 @@ namespace Kyoo.InternalAPI
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Write Into The Database
|
#region Write Into The Database
|
||||||
|
public long RegisterCollection(Collection collection)
|
||||||
|
{
|
||||||
|
string query = "INSERT INTO collections (slug, name, overview, imgPrimary) VALUES($slug, $name, $overview, $imgPrimary);";
|
||||||
|
|
||||||
|
using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection))
|
||||||
|
{
|
||||||
|
cmd.Parameters.AddWithValue("$slug", collection.Slug);
|
||||||
|
cmd.Parameters.AddWithValue("$name", collection.Name);
|
||||||
|
cmd.Parameters.AddWithValue("$overview", collection.Overview);
|
||||||
|
cmd.Parameters.AddWithValue("$imgPrimary", collection.ImgPrimary);
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
|
||||||
|
cmd.CommandText = "SELECT LAST_INSERT_ROWID()";
|
||||||
|
return (long)cmd.ExecuteScalar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public long RegisterShow(Show show)
|
public long RegisterShow(Show show)
|
||||||
{
|
{
|
||||||
string query = "INSERT INTO shows (slug, title, aliases, path, overview, trailerUrl, startYear, endYear, imgPrimary, imgThumb, imgLogo, imgBackdrop, externalIDs) VALUES($slug, $title, $aliases, $path, $overview, $trailerUrl, $startYear, $endYear, $imgPrimary, $imgThumb, $imgLogo, $imgBackdrop, $externalIDs);";
|
string query = "INSERT INTO shows (slug, title, aliases, path, overview, trailerUrl, startYear, endYear, imgPrimary, imgThumb, imgLogo, imgBackdrop, externalIDs) VALUES($slug, $title, $aliases, $path, $overview, $trailerUrl, $startYear, $endYear, $imgPrimary, $imgThumb, $imgLogo, $imgBackdrop, $externalIDs);";
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<None Remove="ClientApp\src\models\collection.ts" />
|
||||||
<None Remove="ClientApp\src\models\genre.ts" />
|
<None Remove="ClientApp\src\models\genre.ts" />
|
||||||
<None Remove="ClientApp\src\models\people.ts" />
|
<None Remove="ClientApp\src\models\people.ts" />
|
||||||
<None Remove="ClientApp\src\models\show.ts" />
|
<None Remove="ClientApp\src\models\show.ts" />
|
||||||
@ -76,6 +77,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<TypeScriptCompile Include="ClientApp\src\models\collection.ts" />
|
||||||
<TypeScriptCompile Include="ClientApp\src\models\genre.ts" />
|
<TypeScriptCompile Include="ClientApp\src\models\genre.ts" />
|
||||||
<TypeScriptCompile Include="ClientApp\src\models\people.ts" />
|
<TypeScriptCompile Include="ClientApp\src\models\people.ts" />
|
||||||
<TypeScriptCompile Include="ClientApp\src\models\show.ts" />
|
<TypeScriptCompile Include="ClientApp\src\models\show.ts" />
|
||||||
|
42
Kyoo/Models/Collection.cs
Normal file
42
Kyoo/Models/Collection.cs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
using Kyoo.InternalAPI;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Kyoo.Models
|
||||||
|
{
|
||||||
|
public class Collection
|
||||||
|
{
|
||||||
|
[JsonIgnore] public long id;
|
||||||
|
public string Slug;
|
||||||
|
public string Name;
|
||||||
|
public string Overview;
|
||||||
|
[JsonIgnore] public string ImgPrimary;
|
||||||
|
public IEnumerable<Show> Shows;
|
||||||
|
|
||||||
|
public Collection() { }
|
||||||
|
|
||||||
|
public Collection(long id, string slug, string name, string overview, string imgPrimary)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
Slug = slug;
|
||||||
|
Name = name;
|
||||||
|
Overview = overview;
|
||||||
|
ImgPrimary = imgPrimary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Collection FromReader(System.Data.SQLite.SQLiteDataReader reader)
|
||||||
|
{
|
||||||
|
return new Collection((long)reader["id"],
|
||||||
|
reader["slug"] as string,
|
||||||
|
reader["name"] as string,
|
||||||
|
reader["overview"] as string,
|
||||||
|
reader["imgPrimary"] as string);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection SetShows(ILibraryManager libraryManager)
|
||||||
|
{
|
||||||
|
Shows = libraryManager.GetShowsInCollection(id);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,8 @@ namespace Kyoo
|
|||||||
|
|
||||||
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
|
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
|
||||||
WebHost.CreateDefaultBuilder(args)
|
WebHost.CreateDefaultBuilder(args)
|
||||||
|
.UseKestrel()
|
||||||
|
.UseUrls("http://*:5000")
|
||||||
.UseStartup<Startup>();
|
.UseStartup<Startup>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"server.urls": "http://0.0.0.0",
|
"server.urls": "http://0.0.0.0:5000",
|
||||||
"https_port": 44300,
|
"https_port": 44300,
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user