From d7611f75317085884a5e799fa142e8d382789e98 Mon Sep 17 00:00:00 2001 From: Flo Date: Fri, 23 Feb 2024 14:07:10 +0100 Subject: [PATCH] first nice version --- angular.json | 1 - docker/docker-compose.yml | 4 +- docker/nginx/config/nginx.conf | 2 + docker/npm/dockerfile | 2 +- src/app/app.component.html | 4 +- src/app/app.component.ts | 39 ++++++++- src/app/app.module.ts | 45 ++-------- .../feature/video/list/list.component.html | 15 ++++ .../feature/video/list/list.component.scss | 56 +++++++++++++ src/app/feature/video/list/list.component.ts | 43 ++++++++++ src/app/feature/video/video.component.html | 15 ++++ src/app/feature/video/video.component.scss | 0 src/app/feature/video/video.component.ts | 39 +++++++++ src/app/feature/video/video.module.ts | 25 ++++++ src/app/model/VideoDetails.ts | 5 ++ src/app/model/VideoListEntry.ts | 5 ++ src/app/shared/shared.module.ts | 82 +++++++++++++++++++ src/styles.scss | 54 +++++++++++- 18 files changed, 389 insertions(+), 47 deletions(-) create mode 100644 src/app/feature/video/list/list.component.html create mode 100644 src/app/feature/video/list/list.component.scss create mode 100644 src/app/feature/video/list/list.component.ts create mode 100644 src/app/feature/video/video.component.html create mode 100644 src/app/feature/video/video.component.scss create mode 100644 src/app/feature/video/video.component.ts create mode 100644 src/app/feature/video/video.module.ts create mode 100644 src/app/model/VideoDetails.ts create mode 100644 src/app/model/VideoListEntry.ts create mode 100644 src/app/shared/shared.module.ts diff --git a/angular.json b/angular.json index 213f72b..fa037f3 100644 --- a/angular.json +++ b/angular.json @@ -20,7 +20,6 @@ "skipTests": true }, "@schematics/angular:module": { - "skipTests": true }, "@schematics/angular:pipe": { "skipTests": true diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index dce82e9..030fe98 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -10,7 +10,7 @@ services: networks: - mytube volumes: - - ./../:/var/www/ + - ./../:/var/www/html build: context: ./../ dockerfile: ./docker/npm/dockerfile @@ -21,7 +21,7 @@ services: networks: - mytube volumes: - - ./../:/var/www/html:z + - ./../:/var/www/html build: context: ./../ dockerfile: ./docker/nginx/dockerfile diff --git a/docker/nginx/config/nginx.conf b/docker/nginx/config/nginx.conf index 1400616..45b28ff 100644 --- a/docker/nginx/config/nginx.conf +++ b/docker/nginx/config/nginx.conf @@ -6,6 +6,8 @@ upstream host-backend-nginx { server{ listen 80 default_server; + client_max_body_size 1000M; + root /var/www/html/dist/mytube; index index.html; diff --git a/docker/npm/dockerfile b/docker/npm/dockerfile index 0535922..d6f8389 100644 --- a/docker/npm/dockerfile +++ b/docker/npm/dockerfile @@ -1,4 +1,4 @@ -FROM node:latest +FROM node:20-slim WORKDIR /var/www/html diff --git a/src/app/app.component.html b/src/app/app.component.html index 27cb800..90c6b64 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,3 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 9a8db0f..8366f2c 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,9 +1,44 @@ -import { Component } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; +import { RequestService } from './request.service'; +import { VideoListEntry } from './model/VideoListEntry'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) -export class AppComponent { +export class AppComponent implements OnInit { + videoList: VideoListEntry[] = []; + selectedVideoUrl: string|null = null; + + constructor( + public requestService : RequestService, + ) { + } + + ngOnInit(): void { + this.readList(); + } + + selectVideo(entry: VideoListEntry) { + if (this.selectedVideoUrl === null) { + this.selectedVideoUrl = "http://wsl-flo/api/video/stream/" + entry.id; + } else { + this.selectedVideoUrl = null; + } + } + + readList(): void { + this.requestService.post( + 'video-list/read-list', + {}, + (response:any) => { + this.videoList = response; + } + ); + } + + delay(ms: number) { + return new Promise( resolve => setTimeout(resolve, ms) ); + } } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 7bdef99..e4dc064 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,29 +1,17 @@ import { NgModule } from '@angular/core'; -import { FormsModule } from '@angular/forms'; import { BrowserModule } from '@angular/platform-browser'; import { HttpClientModule } from '@angular/common/http'; -import { MatInputModule } from '@angular/material/input'; -import { MatSelectModule } from '@angular/material/select'; - -import { MatSidenavModule } from '@angular/material/sidenav'; -import { MatToolbarModule } from '@angular/material/toolbar'; -import { MatMenuModule } from '@angular/material/menu'; -import { MatListModule } from '@angular/material/list'; -import { MatIconModule } from '@angular/material/icon'; -import { MatGridListModule } from '@angular/material/grid-list' -import { MatButtonModule } from '@angular/material/button'; -import { MatCardModule } from '@angular/material/card'; -import { MatExpansionModule } from '@angular/material/expansion'; -import { MatSlideToggleModule } from '@angular/material/slide-toggle'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatDividerModule } from '@angular/material/divider'; -import {MatDialogModule} from '@angular/material/dialog'; -import {MatSnackBarModule} from '@angular/material/snack-bar'; - import { AppComponent } from './app.component'; +import { RouterModule, Routes } from '@angular/router'; +import { SharedModule } from './shared/shared.module'; +const routes: Routes = [ + { path: 'video', loadChildren: () => import('./feature/video/video.module').then( m => m.VideoModule ) }, + { path: '', redirectTo: 'video/list', pathMatch: 'full' } +]; + @NgModule({ declarations: [ AppComponent, @@ -31,23 +19,8 @@ import { AppComponent } from './app.component'; imports: [ BrowserModule, HttpClientModule, - MatSlideToggleModule, - MatCardModule, - MatButtonModule, - MatIconModule, - MatGridListModule, - MatFormFieldModule, - FormsModule, - MatExpansionModule, - MatMenuModule, - MatListModule, - MatToolbarModule, - MatSidenavModule, - MatInputModule, - MatSelectModule, - MatDividerModule, - MatDialogModule, - MatSnackBarModule + RouterModule.forRoot(routes), + SharedModule, ], providers: [], bootstrap: [AppComponent] diff --git a/src/app/feature/video/list/list.component.html b/src/app/feature/video/list/list.component.html new file mode 100644 index 0000000..c2db882 --- /dev/null +++ b/src/app/feature/video/list/list.component.html @@ -0,0 +1,15 @@ +
+ + + + {{video.title}} + + + + Thumbnail + + +
diff --git a/src/app/feature/video/list/list.component.scss b/src/app/feature/video/list/list.component.scss new file mode 100644 index 0000000..221b568 --- /dev/null +++ b/src/app/feature/video/list/list.component.scss @@ -0,0 +1,56 @@ +mat-card { + margin: 1em; +} + +mat-card:hover { + background-color: lightgray; +} + +.mat-card-title { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.cardContainer { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + margin: 0 -10px; +} + +.mat-card { + flex-basis: calc(100% - 20px); + max-width: calc(100% - 20px); + flex: 0 0 calc(33.33% - 20px); + margin: 10px; + box-sizing: border-box; +} + +/* For tablet: */ +@media only screen and (min-width: 700px) { + .mat-card { + flex-basis: calc(50% - 20px); + max-width: calc(50% - 20px); + } +} + +/* For desktop: */ +@media only screen and (min-width: 1200px) { + .mat-card { + max-width: calc(33.33% - 20px); + flex-basis: calc(33.33% - 20px); + } +} + + + +.mat-card .mat-card-content { + height: 100%; +} + +.mat-card .mat-card-content img { + width: 100%; + height: 100%; + object-fit: cover; +} \ No newline at end of file diff --git a/src/app/feature/video/list/list.component.ts b/src/app/feature/video/list/list.component.ts new file mode 100644 index 0000000..7eebc0d --- /dev/null +++ b/src/app/feature/video/list/list.component.ts @@ -0,0 +1,43 @@ +import { Component } from '@angular/core'; +import { Router } from '@angular/router'; +import { VideoListEntry } from 'src/app/model/VideoListEntry'; +import { RequestService } from 'src/app/request.service'; + +@Component({ + selector: 'app-list', + templateUrl: './list.component.html', + styleUrls: ['./list.component.scss'] +}) +export class ListComponent { + videoList: VideoListEntry[] = []; + selectedVideoUrl: string|null = null; + + constructor( + private router: Router, + public requestService : RequestService, + ) { + } + + ngOnInit(): void { + this.readList(); + } + + selectVideo(entry: VideoListEntry) { + this.router.navigate(['/video', entry.id]); + } + + getThumbnailUrl(entry: VideoListEntry): string { + return 'http://wsl-flo/api/video/thumbnail/' + entry.id; +} + + readList(): void { + this.requestService.post( + 'video-list/read-list', + {}, + (response:any) => { + this.videoList = response; + } + ); + } + +} diff --git a/src/app/feature/video/video.component.html b/src/app/feature/video/video.component.html new file mode 100644 index 0000000..9f15798 --- /dev/null +++ b/src/app/feature/video/video.component.html @@ -0,0 +1,15 @@ +
+ + + +

{{videoDetails.title}}

+ + + +
diff --git a/src/app/feature/video/video.component.scss b/src/app/feature/video/video.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/feature/video/video.component.ts b/src/app/feature/video/video.component.ts new file mode 100644 index 0000000..4182769 --- /dev/null +++ b/src/app/feature/video/video.component.ts @@ -0,0 +1,39 @@ +import { Component } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { VideoDetails } from 'src/app/model/VideoDetails'; +import { RequestService } from 'src/app/request.service'; + +@Component({ + selector: 'app-video', + templateUrl: './video.component.html', + styleUrls: ['./video.component.scss'] +}) +export class VideoComponent { + videoId: string; + videoUrl: string|null = null; + videoDetails: VideoDetails|null = null; + + constructor( + private route: ActivatedRoute, + private requestService : RequestService, + ) { + this.videoId = this.route.snapshot.paramMap.get('id') ?? ''; + this.videoUrl = "http://wsl-flo/api/video/stream/" + this.videoId; + } + + ngOnInit(): void { + this.readDetails(); + } + + readDetails(): void { + this.requestService.post( + 'video/read-details', + { + videoId: this.videoId + }, + (response:any) => { + this.videoDetails = response; + } + ); + } +} diff --git a/src/app/feature/video/video.module.ts b/src/app/feature/video/video.module.ts new file mode 100644 index 0000000..d89b981 --- /dev/null +++ b/src/app/feature/video/video.module.ts @@ -0,0 +1,25 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { ListComponent } from './list/list.component'; +import { VideoComponent } from './video.component'; +import { SharedModule } from 'src/app/shared/shared.module'; +import { RouterModule, Routes } from '@angular/router'; + +const routes: Routes = [ + { path: 'list', component: ListComponent }, + { path: ':id', component: VideoComponent }, + { path: '', redirectTo: 'video/list', pathMatch: 'full'}, +] + +@NgModule({ + declarations: [ + ListComponent, + VideoComponent + ], + imports: [ + CommonModule, + SharedModule, + RouterModule.forChild(routes) + ] +}) +export class VideoModule { } diff --git a/src/app/model/VideoDetails.ts b/src/app/model/VideoDetails.ts new file mode 100644 index 0000000..c0569ef --- /dev/null +++ b/src/app/model/VideoDetails.ts @@ -0,0 +1,5 @@ + +export interface VideoDetails { + title: string; + id: string; +} \ No newline at end of file diff --git a/src/app/model/VideoListEntry.ts b/src/app/model/VideoListEntry.ts new file mode 100644 index 0000000..b314066 --- /dev/null +++ b/src/app/model/VideoListEntry.ts @@ -0,0 +1,5 @@ + +export interface VideoListEntry { + title: string; + id: string; +} \ No newline at end of file diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts new file mode 100644 index 0000000..76667c7 --- /dev/null +++ b/src/app/shared/shared.module.ts @@ -0,0 +1,82 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { FormsModule } from '@angular/forms'; +import { MatInputModule } from '@angular/material/input'; +import { MatSelectModule } from '@angular/material/select'; +import { MatSidenavModule } from '@angular/material/sidenav'; +import { MatToolbarModule } from '@angular/material/toolbar'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatListModule } from '@angular/material/list'; +import { MatIconModule } from '@angular/material/icon'; +import { MatGridListModule } from '@angular/material/grid-list' +import { MatButtonModule } from '@angular/material/button'; +import { MatCardModule } from '@angular/material/card'; +import { MatExpansionModule } from '@angular/material/expansion'; +import { MatSlideToggleModule } from '@angular/material/slide-toggle'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatDividerModule } from '@angular/material/divider'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatPaginatorModule } from '@angular/material/paginator'; +import { MatTabsModule } from '@angular/material/tabs'; +import { MatTableModule } from '@angular/material/table'; + + + + +@NgModule({ + declarations: [ + ], + imports: [ + CommonModule, + MatSlideToggleModule, + MatCardModule, + MatButtonModule, + MatIconModule, + MatGridListModule, + MatFormFieldModule, + FormsModule, + MatExpansionModule, + MatMenuModule, + MatListModule, + MatToolbarModule, + MatSidenavModule, + MatInputModule, + MatSelectModule, + MatDividerModule, + MatDialogModule, + MatSnackBarModule, + MatPaginatorModule, + MatCheckboxModule, + MatTabsModule, + MatTableModule + ], + exports: [ + // to be replaced + MatSlideToggleModule, + MatCardModule, + MatButtonModule, + MatIconModule, + MatGridListModule, + MatFormFieldModule, + FormsModule, + MatExpansionModule, + MatMenuModule, + MatListModule, + MatToolbarModule, + MatSidenavModule, + MatInputModule, + MatSelectModule, + MatDividerModule, + MatDialogModule, + MatSnackBarModule, + MatPaginatorModule, + MatCheckboxModule, + MatTabsModule, + MatTableModule + + ] +}) +export class SharedModule { } \ No newline at end of file diff --git a/src/styles.scss b/src/styles.scss index 79422be..eec26cc 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -3,11 +3,61 @@ body { height: 100vh; } +app-root { + height: 100%; +} + +.flex-container { + display: flex; +} + +.flex-1 { + flex: 1; +} +.flex-2 { + flex: 2; +} +.flex-3 { + flex: 3; +} +.flex-4 { + flex: 4; +} +.flex-5 { + flex: 5; +} + .container { padding: 0; max-width: unset; } -app-root { - height: 100%; + + +.svg { + filter: invert(1); +} + +.svg-1 { + filter: invert(1); + height: 1em; + width: 1em; +} + +.svg-2 { + filter: invert(1); + height: 2em; + width: 2em; +} + +.svg-3 { + filter: invert(1); + height: 3em; + width: 3em; +} + +.svg-4 { + filter: invert(1); + height: 4em; + width: 4em; } \ No newline at end of file