Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Watch videos on phone or tablet #9

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/app/animations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ export const searchAnimation = trigger('searchAnimation', [
export const settingsAnimation = trigger('settingsAnimation', [
transition(
':enter', [
style({ transform: 'translate(0, 300px)' }),
style({ transform: 'translate(0, 333px)' }),
animate('300ms ease', style({ transform: 'translate(0, 0)' }))
]
),
transition(
':leave', [
style({ transform: 'translate(0, 0)' }),
animate('300ms ease', style({ transform: 'translate(0, 300px)' }))
animate('300ms ease', style({ transform: 'translate(0, 333px)' }))
]
)]
);
Expand Down
60 changes: 59 additions & 1 deletion src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,22 @@ <h1>Settings</h1>

<div>
<button (click)="toggleFontSize()" [ngClass]="{ toggled: settings.largerText }">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-type"><polyline points="4 7 4 4 20 4 20 7"></polyline><line x1="9" y1="20" x2="15" y2="20"></line><line x1="12" y1="4" x2="12" y2="20"></line></svg> </button>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-type"><polyline points="4 7 4 4 20 4 20 7"></polyline><line x1="9" y1="20" x2="15" y2="20"></line><line x1="12" y1="4" x2="12" y2="20"></line></svg>
</button>
<span>
Larger text
</span>
</div>

<div>
<button (click)="toggleVideoPlay()" [ngClass]="{ toggled: settings.playOnDevice }">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-film"><rect x="2" y="2" width="20" height="20" rx="2.18" ry="2.18"></rect><line x1="7" y1="2" x2="7" y2="22"></line><line x1="17" y1="2" x2="17" y2="22"></line><line x1="2" y1="12" x2="22" y2="12"></line><line x1="2" y1="7" x2="7" y2="7"></line><line x1="2" y1="17" x2="7" y2="17"></line><line x1="17" y1="17" x2="22" y2="17"></line><line x1="17" y1="7" x2="22" y2="7"></line></svg>
</button>
<span>
Play on device
</span>
</div>

</div>

<div
Expand Down Expand Up @@ -187,3 +197,51 @@ <h1>Welcome to Video Hub App Remote</h1>

<p>Afterwards, you can close this and open the app from your home screen!</p>
</div>

<div class="video-player-container">

<video
[src]="this.httpFile"
class="video-player"
#videoplayer
(timeupdate)="this.seekBarUpdate()"
(click)="this.playPauseVideo()"
></video>

<div class="video-controls">

<button #playButton type="button" id="play-pause" (click)="this.playPauseVideo()">

<svg *ngIf="!isPlaying; else pauseButton" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-play"><polygon points="5 3 19 12 5 21 5 3"></polygon></svg>

<ng-template #pauseButton>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-pause"><rect x="6" y="4" width="4" height="16"></rect><rect x="14" y="4" width="4" height="16"></rect></svg>
</ng-template>

</button>

<input #seekBar class="seek-bar" type="range" id="seek-bar" value="0" max="{{this.duration}}" step="1" (change)="this.seekBarChange()">

<span class="time">
{{this.currentTime | durationPipe }} / {{this.duration | durationPipe}}
</span>

<button #muteButton type="button" id="mute" (click)="this.muteUnmuteVideo()">

<svg *ngIf="isMuted; else muteButtonIcon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-volume-x"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon><line x1="23" y1="9" x2="17" y2="15"></line><line x1="17" y1="9" x2="23" y2="15"></line></svg>

<ng-template #muteButtonIcon>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-volume"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon></svg>
</ng-template>

</button>

<input #volumeBar type="range" id="volume-bar" min="0" max="1" step="0.1" value="1" (change)="this.volumeChange()">

<button type="button" id="full-screen" (click)="this.fullscreenVideo()">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-maximize"><path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"></path></svg>
</button>

</div>

</div>
41 changes: 40 additions & 1 deletion src/app/app.component.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
$menu-height: 50px;
$settings-height: 300px; // also update `settingsAnimation`
$settings-height: 333px; // also update `settingsAnimation`

.buttons-menu {
background-color: #f9f9f9;
Expand Down Expand Up @@ -271,3 +271,42 @@ app-thumbnail {
top: 10px;
}
}

.video-player-container {
background-color: #f9f9f9;
border-bottom: 1px solid #bbbbbb;
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.25);
box-sizing: border-box;
height: 65vw;
left: 0;
padding: 20px;
position: fixed;
top: 0;
width: 100vw;

button {
margin: 0;
padding-top: 7px;
}

.time {
font-family: monospace;
font-size: 12px;
padding: 12px 6px;
}

.video-player {
height: 90%;
width: 100%;
}

.video-controls {
display: flex;
justify-content: space-between;
}

.seek-bar {
flex-grow: 2;
margin: 0 10px;
}
}
81 changes: 79 additions & 2 deletions src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core';
import { Platform } from '@angular/cdk/platform';

import { VirtualScrollerComponent } from 'ngx-virtual-scroller';
Expand All @@ -12,6 +12,7 @@ interface RemoteSettings {
darkMode: boolean;
imgsPerRow: number;
largerText: boolean;
playOnDevice: boolean;
}

type SocketMessageType = 'gallery' | 'settings';
Expand All @@ -36,6 +37,7 @@ export class AppComponent implements OnInit {
darkMode: false,
imgsPerRow: 2,
largerText: true,
playOnDevice: true,
}

// variables
Expand All @@ -60,7 +62,7 @@ export class AppComponent implements OnInit {
ngOnInit() {
this.setUpSocket();
this.computePreviewWidth();
this.showInstallInstructions();
// this.showInstallInstructions();
}

/**
Expand Down Expand Up @@ -186,6 +188,16 @@ export class AppComponent implements OnInit {
}
}

/**
* Toggle if video plays on device or on PC
*/
toggleVideoPlay(): void {
this.settings.playOnDevice = !this.settings.playOnDevice;
if (!this.showSearch) {
this.searchString = '';
}
}

/**
* Request from server the current gallery view
* or refresh if not connected
Expand Down Expand Up @@ -268,4 +280,69 @@ export class AppComponent implements OnInit {
this.socketConnected = false;
}

// Video playback
// most of the code comes from Cal2195
// Thank you Cal2195

@ViewChild('muteButton', { static: false }) muteButton: any;
@ViewChild('playButton', { static: false }) playButton: any;
@ViewChild('seekBar', { static: false }) seekBar: any;
@ViewChild('videoplayer', { static: false }) videoplayer: any;
@ViewChild('volumeBar', { static: false }) volumeBar: any;

currentTime: number = 0;
httpfile: string;
isMuted: boolean = false;
isPlaying: boolean = false;
seek: number = 0;

duration = 30;
httpFile: string = "http://192.168.1.5:3000/video?file=C:\\temp\\stream.mp4";
tempVidPath: string = 'C:\\temp\\stream.mp4';

playPauseVideo() {
if (!this.videoplayer.nativeElement.paused) {
this.isPlaying = false;
this.videoplayer.nativeElement.pause();
} else {
this.isPlaying = true;
this.videoplayer.nativeElement.play();
}
}

muteUnmuteVideo() {
if (this.videoplayer.nativeElement.muted === false) {
this.isMuted = true;
this.videoplayer.nativeElement.muted = true;
} else {
this.isMuted = false;
this.videoplayer.nativeElement.muted = false;
}
}

fullscreenVideo() {
this.videoplayer.nativeElement.requestFullscreen();
}

seekBarChange() {
this.seek = parseFloat(this.seekBar.nativeElement.value);
this.seekVideo(!this.videoplayer.nativeElement.paused);
}

seekBarUpdate() {
this.currentTime = parseFloat(this.videoplayer.nativeElement.currentTime) + this.seek;
this.seekBar.nativeElement.value = this.currentTime;
}

seekVideo(play = false) {
this.httpFile = 'http://192.168.1.5:3000/video?file=' + this.tempVidPath + '&seek=' + this.seek;
if (this.videoplayer) {
this.videoplayer.nativeElement.autoplay = play;
}
}

volumeChange() {
this.videoplayer.nativeElement.volume = this.volumeBar.nativeElement.value;
}

}
9 changes: 6 additions & 3 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgModule } from '@angular/core';
import { ServiceWorkerModule } from '@angular/service-worker';

import { VirtualScrollerModule } from 'ngx-virtual-scroller';

Expand All @@ -10,13 +11,15 @@ import { FilePathService } from './file-path.service';
import { AppComponent } from './app.component';
import { ThumbnailComponent } from './thumb/thumbnail.component';

import { DurationPipe } from './duration.pipe';
import { SearchPipe } from './search.pipe';
import { ServiceWorkerModule } from '@angular/service-worker';

import { environment } from '../environments/environment';

@NgModule({
declarations: [
AppComponent,
DurationPipe,
SearchPipe,
ThumbnailComponent,
],
Expand Down
37 changes: 37 additions & 0 deletions src/app/duration.pipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
name: 'durationPipe'
})
export class DurationPipe implements PipeTransform {

/**
* Return length of video file formatted as X:XX:XX
* or if `omitSeconds` then `Xhr XXmin`
* @param numOfSec
* @param destination -- dictates the output format (TimeFormat)
*/
transform(
numOfSec: number
): string {

if (numOfSec === undefined) {
// short circuit
return '';
}

const h: string = (Math.floor(numOfSec / 3600)).toString();
const m: string = (Math.floor(numOfSec / 60) % 60).toString();
const s: string = (Math.floor(numOfSec) % 60).toString();

// file should behave thus:
// 0:00, 0:01 ... 0:59, 1:00, 1:01 ... 59:59, 1:00:00 ... 3:14:15 ...
if (h !== '0') {
return h + ':' + m.padStart(2, '0') + ':' + s.padStart(2, '0');
} else {
return m.padStart(2, '0') + ':' + s.padStart(2, '0');
}

}

}