mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
Creating a few components for the web app.
This commit is contained in:
parent
5c3547fe61
commit
e27bac2c56
@ -1,8 +1,17 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
|
||||
import { BrowseComponent } from './browse/browse.component';
|
||||
import { ShowDetailsComponent } from './show-details/show-details.component';
|
||||
import { NotFoundComponent } from './not-found/not-found.component';
|
||||
|
||||
const routes: Routes = [];
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: "browse", component: BrowseComponent, pathMatch: "full" },
|
||||
{ path: "browse/:library-slug", component: BrowseComponent },
|
||||
{ path: "shows/:show-slug", component: ShowDetailsComponent },
|
||||
{ path: "**", component: NotFoundComponent }
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes)],
|
||||
|
@ -1,18 +1,37 @@
|
||||
<nav class="navbar fixed-top navbar-dark bg-dark">
|
||||
<a class="navbar-brand" routerLink="/">
|
||||
Kyoo
|
||||
</a>
|
||||
<header style="height: 64px;">
|
||||
<nav class="navbar fixed-top navbar-dark bg-dark">
|
||||
<a class="navbar-brand ml-3" routerLink="/">
|
||||
Kyoo
|
||||
</a>
|
||||
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" routerLink="/browse" routerLinkActive="active">All</a>
|
||||
</li>
|
||||
<li class="nav-item" *ngFor="let library of this.libraries">
|
||||
<a class="nav-link" routerLink="/browse/{{library.slug}}" routerLinkActive="active">{{library.name}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<ul class="navbar-nav flex-row">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" routerLink="/browse" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }">All</a>
|
||||
</li>
|
||||
<li class="nav-item" *ngFor="let library of this.libraries">
|
||||
<a class="nav-link" routerLink="/browse/{{library.slug}}" routerLinkActive="active">{{library.name}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<main>
|
||||
<ul class="navbar-nav flex-row ml-auto">
|
||||
<li class="nav-item icon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<title>Search</title>
|
||||
<path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"></path>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="icon" routerLink="/login" routerLinkActive="active">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<title>Account</title>
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 3c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 14.2c-2.5 0-4.71-1.28-6-3.22.03-1.99 4-3.08 6-3.08 1.99 0 5.97 1.09 6 3.08-1.29 1.94-3.5 3.22-6 3.22z" />
|
||||
</svg>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main >
|
||||
<router-outlet></router-outlet>
|
||||
</main>
|
||||
|
@ -3,15 +3,28 @@
|
||||
justify-content: left;
|
||||
}
|
||||
|
||||
.navbar-nav
|
||||
.nav-link
|
||||
{
|
||||
flex-direction: row;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.nav-item
|
||||
.icon
|
||||
{
|
||||
> a
|
||||
padding: 8px;
|
||||
display: inline-block;
|
||||
|
||||
> svg
|
||||
{
|
||||
padding: 16px;
|
||||
fill: #d3d3d3;
|
||||
}
|
||||
|
||||
&:hover
|
||||
{
|
||||
cursor: pointer;
|
||||
|
||||
> svg
|
||||
{
|
||||
fill: #ffffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,10 +4,16 @@ import { NgModule } from '@angular/core';
|
||||
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
import { NotFoundComponent } from './not-found/not-found.component';
|
||||
import { BrowseComponent } from './browse/browse.component';
|
||||
import { ShowDetailsComponent } from './show-details/show-details.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent
|
||||
AppComponent,
|
||||
NotFoundComponent,
|
||||
BrowseComponent,
|
||||
ShowDetailsComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
|
8
Kyoo/ClientApp/src/app/browse/browse.component.html
Normal file
8
Kyoo/ClientApp/src/app/browse/browse.component.html
Normal file
@ -0,0 +1,8 @@
|
||||
<div>
|
||||
<a class="show" *ngFor="let show of this.shows" routerLink="/shows/{{show.slug}}">
|
||||
<img src="{{show.imgPrimary}}" />
|
||||
<p>{{show.title}}</p>
|
||||
<p class="date" *ngIf="show.endYear; else elseBlock">{{show.startYear}} - {{show.endYear}}</p>
|
||||
<ng-template #elseBlock><p class="date">{{show.startYear}}</p></ng-template>
|
||||
</a>
|
||||
</div>
|
54
Kyoo/ClientApp/src/app/browse/browse.component.scss
Normal file
54
Kyoo/ClientApp/src/app/browse/browse.component.scss
Normal file
@ -0,0 +1,54 @@
|
||||
@import "~bootstrap//scss/functions";
|
||||
@import "~bootstrap/scss/variables";
|
||||
@import "~bootstrap/scss//mixins/breakpoints";
|
||||
|
||||
.show
|
||||
{
|
||||
width: 33%;
|
||||
list-style: none;
|
||||
padding: 1em;
|
||||
display: inline-block;
|
||||
text-decoration: none;
|
||||
color: initial;
|
||||
|
||||
@include media-breakpoint-up(md)
|
||||
{
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(lg)
|
||||
{
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(xl)
|
||||
{
|
||||
width: 18%;
|
||||
}
|
||||
|
||||
> img
|
||||
{
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
> p
|
||||
{
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
text-align: center;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
&:hover
|
||||
{
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.date
|
||||
{
|
||||
opacity: 0.8;
|
||||
font-size: 0.8em;
|
||||
}
|
25
Kyoo/ClientApp/src/app/browse/browse.component.spec.ts
Normal file
25
Kyoo/ClientApp/src/app/browse/browse.component.spec.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { BrowseComponent } from './browse.component';
|
||||
|
||||
describe('BrowseComponent', () => {
|
||||
let component: BrowseComponent;
|
||||
let fixture: ComponentFixture<BrowseComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ BrowseComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(BrowseComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
45
Kyoo/ClientApp/src/app/browse/browse.component.ts
Normal file
45
Kyoo/ClientApp/src/app/browse/browse.component.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
|
||||
@Component({
|
||||
selector: 'app-browse',
|
||||
templateUrl: './browse.component.html',
|
||||
styleUrls: ['./browse.component.scss']
|
||||
})
|
||||
export class BrowseComponent implements OnInit
|
||||
{
|
||||
shows: Show[];
|
||||
|
||||
private watch: any;
|
||||
|
||||
constructor(private http: HttpClient, private route: ActivatedRoute) { }
|
||||
|
||||
ngOnInit()
|
||||
{
|
||||
this.watch = this.route.params.subscribe(params =>
|
||||
{
|
||||
var slug: string = params["library-slug"];
|
||||
|
||||
if (slug == null)
|
||||
{
|
||||
this.http.get<Show[]>("api/shows").subscribe(result =>
|
||||
{
|
||||
this.shows = result;
|
||||
}, error => console.log(error));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.http.get<Show[]>("api/library/" + slug).subscribe(result =>
|
||||
{
|
||||
this.shows = result;
|
||||
}, error => console.log(error));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy()
|
||||
{
|
||||
this.watch.unsubscribe();
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
<br/>
|
||||
<br/>
|
||||
<br/>
|
||||
<br/>
|
||||
<br/>
|
||||
<div class="text-center">
|
||||
<h1>404 Error</h1>
|
||||
<p>The page you requested was not found.</p>
|
||||
</div>
|
25
Kyoo/ClientApp/src/app/not-found/not-found.component.spec.ts
Normal file
25
Kyoo/ClientApp/src/app/not-found/not-found.component.spec.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { NotFoundComponent } from './not-found.component';
|
||||
|
||||
describe('NotFoundComponent', () => {
|
||||
let component: NotFoundComponent;
|
||||
let fixture: ComponentFixture<NotFoundComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ NotFoundComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(NotFoundComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
15
Kyoo/ClientApp/src/app/not-found/not-found.component.ts
Normal file
15
Kyoo/ClientApp/src/app/not-found/not-found.component.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-not-found',
|
||||
templateUrl: './not-found.component.html',
|
||||
styleUrls: ['./not-found.component.scss']
|
||||
})
|
||||
export class NotFoundComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1 @@
|
||||
<p>Should display show details of: {{this.show.title}}</p>
|
@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ShowDetailsComponent } from './show-details.component';
|
||||
|
||||
describe('ShowDetailsComponent', () => {
|
||||
let component: ShowDetailsComponent;
|
||||
let fixture: ComponentFixture<ShowDetailsComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ ShowDetailsComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ShowDetailsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,36 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
|
||||
@Component({
|
||||
selector: 'app-show-details',
|
||||
templateUrl: './show-details.component.html',
|
||||
styleUrls: ['./show-details.component.scss']
|
||||
})
|
||||
export class ShowDetailsComponent implements OnInit
|
||||
{
|
||||
show: Show;
|
||||
|
||||
private watch: any;
|
||||
|
||||
constructor(private http: HttpClient, private route: ActivatedRoute) { }
|
||||
|
||||
ngOnInit()
|
||||
{
|
||||
this.watch = this.route.params.subscribe(params =>
|
||||
{
|
||||
var slug: string = params["show-slug"];
|
||||
|
||||
this.http.get<Show>("api/shows/" + slug).subscribe(result =>
|
||||
{
|
||||
this.show = result;
|
||||
}, error => console.log(error));
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy()
|
||||
{
|
||||
this.watch.unsubscribe();
|
||||
}
|
||||
|
||||
}
|
21
Kyoo/ClientApp/src/models/show.ts
Normal file
21
Kyoo/ClientApp/src/models/show.ts
Normal file
@ -0,0 +1,21 @@
|
||||
interface Show
|
||||
{
|
||||
id: number;
|
||||
Slug: string;
|
||||
title: string;
|
||||
//IEnumerable < > Aliases;
|
||||
Path: string;
|
||||
Overview: string;
|
||||
//IEnumerable < > Genres;
|
||||
//Status ? Status;
|
||||
|
||||
StartYear: number;
|
||||
EndYear : number;
|
||||
|
||||
ImgPrimary: string;
|
||||
ImgThumb: string;
|
||||
ImgLogo: string;
|
||||
ImgBackdrop: string;
|
||||
|
||||
ExternalIDs: string;
|
||||
}
|
@ -46,40 +46,8 @@ namespace Kyoo.InternalAPI
|
||||
|
||||
foreach (string file in files)
|
||||
{
|
||||
if(IsVideo(file) && !libraryManager.IsEpisodeRegistered(file))
|
||||
{
|
||||
string patern = config.GetValue<string>("regex");
|
||||
Regex regex = new Regex(patern, RegexOptions.IgnoreCase);
|
||||
Match match = regex.Match(file);
|
||||
|
||||
string showPath = Path.GetDirectoryName(file);
|
||||
string showName = match.Groups["ShowTitle"].Value;
|
||||
bool seasonSuccess = long.TryParse(match.Groups["Season"].Value, out long seasonNumber);
|
||||
bool episodeSucess = long.TryParse(match.Groups["Episode"].Value, out long episodeNumber);
|
||||
|
||||
string showProviderIDs;
|
||||
if (!libraryManager.IsShowRegistered(showPath, out long showID))
|
||||
{
|
||||
Show show = await metadataProvider.GetShowFromName(showName, showPath);
|
||||
showProviderIDs = show.ExternalIDs;
|
||||
showID = libraryManager.RegisterShow(show);
|
||||
}
|
||||
else
|
||||
showProviderIDs = libraryManager.GetShowExternalIDs(showID);
|
||||
|
||||
if(!libraryManager.IsSeasonRegistered(showID, seasonNumber, out long seasonID))
|
||||
{
|
||||
Season season = await metadataProvider.GetSeason(showName, seasonNumber);
|
||||
season.ShowID = showID;
|
||||
seasonID = libraryManager.RegisterSeason(season);
|
||||
}
|
||||
|
||||
Episode episode = await metadataProvider.GetEpisode(showProviderIDs, seasonNumber, episodeNumber);
|
||||
episode.ShowID = showID;
|
||||
episode.SeasonID = seasonID;
|
||||
episode.Path = file;
|
||||
libraryManager.RegisterEpisode(episode);
|
||||
}
|
||||
if (IsVideo(file))
|
||||
await TryRegisterEpisode(file);
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,6 +79,11 @@ namespace Kyoo.InternalAPI
|
||||
private void FileCreated(object sender, FileSystemEventArgs e)
|
||||
{
|
||||
Debug.WriteLine("&File Created at " + e.FullPath);
|
||||
if (IsVideo(e.FullPath))
|
||||
{
|
||||
Debug.WriteLine("&Created file is a video");
|
||||
_ = TryRegisterEpisode(e.FullPath);
|
||||
}
|
||||
}
|
||||
|
||||
private void FileChanged(object sender, FileSystemEventArgs e)
|
||||
@ -129,6 +102,47 @@ namespace Kyoo.InternalAPI
|
||||
}
|
||||
|
||||
|
||||
|
||||
private async Task TryRegisterEpisode(string path)
|
||||
{
|
||||
if (!libraryManager.IsEpisodeRegistered(path))
|
||||
{
|
||||
string patern = config.GetValue<string>("regex");
|
||||
Regex regex = new Regex(patern, RegexOptions.IgnoreCase);
|
||||
Match match = regex.Match(path);
|
||||
|
||||
string showPath = Path.GetDirectoryName(path);
|
||||
string showName = match.Groups["ShowTitle"].Value;
|
||||
bool seasonSuccess = long.TryParse(match.Groups["Season"].Value, out long seasonNumber);
|
||||
bool episodeSucess = long.TryParse(match.Groups["Episode"].Value, out long episodeNumber);
|
||||
|
||||
string showProviderIDs;
|
||||
if (!libraryManager.IsShowRegistered(showPath, out long showID))
|
||||
{
|
||||
Show show = await metadataProvider.GetShowFromName(showName, showPath);
|
||||
showProviderIDs = show.ExternalIDs;
|
||||
showID = libraryManager.RegisterShow(show);
|
||||
}
|
||||
else
|
||||
showProviderIDs = libraryManager.GetShowExternalIDs(showID);
|
||||
|
||||
if (!libraryManager.IsSeasonRegistered(showID, seasonNumber, out long seasonID))
|
||||
{
|
||||
Season season = await metadataProvider.GetSeason(showName, seasonNumber);
|
||||
season.ShowID = showID;
|
||||
seasonID = libraryManager.RegisterSeason(season);
|
||||
}
|
||||
|
||||
Episode episode = await metadataProvider.GetEpisode(showProviderIDs, seasonNumber, episodeNumber);
|
||||
episode.ShowID = showID;
|
||||
episode.SeasonID = seasonID;
|
||||
episode.Path = path;
|
||||
libraryManager.RegisterEpisode(episode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static readonly string[] videoExtensions = { ".webm", ".mkv", ".flv", ".vob", ".ogg", ".ogv", ".avi", ".mts", ".m2ts", ".ts", ".mov", ".qt", ".asf", ".mp4", ".m4p", ".m4v", ".mpg", ".mp2", ".mpeg", ".mpe", ".mpv", ".m2v", ".3gp", ".3g2" };
|
||||
|
||||
private bool IsVideo(string filePath)
|
||||
|
@ -29,6 +29,14 @@
|
||||
<None Include="$(SpaRoot)**" Exclude="$(SpaRoot)node_modules\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="ClientApp\src\models\show.ts" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<TypeScriptCompile Include="ClientApp\src\models\show.ts" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="DebugEnsureNodeEnv" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('$(SpaRoot)node_modules') ">
|
||||
<!-- Ensure Node.js is installed -->
|
||||
<Exec Command="node --version" ContinueOnError="true">
|
||||
|
Loading…
x
Reference in New Issue
Block a user