refactor: code improvements, bugfixing

This commit is contained in:
4gray committed 2020-06-21 19:37:42 +02:00
1 parent d3e3c349a1
commit 973f6f64ba
13 files changed
+161 -58

No files matched your search

+4
View File
@@ -16,6 +16,9 @@ import { MaterialModule } from './material.module';
import { RecentPlaylistsComponent } from './components/recent-playlists/recent-playlists.component';
import { FilterPipeModule } from 'ngx-filter-pipe';
import { FormsModule } from '@angular/forms';
import { NgxIndexedDBModule } from 'ngx-indexed-db';
import { dbConfig } from './db.config';
@NgModule({
declarations: [
@@ -33,6 +36,7 @@ import { FormsModule } from '@angular/forms';
FilterPipeModule,
FormsModule,
HttpClientModule,
NgxIndexedDBModule.forRoot(dbConfig),
MaterialModule,
NgxUploaderModule,
ServiceWorkerModule.register('ngsw-worker.js', {
@@ -2,6 +2,15 @@
width: 100%;
}
mat-list {
margin-top: 10px;
#top-panel {
background: #eee;
}
#channels-list {
overflow-y: auto;
height: calc(100% - 65px);
}
.active {
background: #ddd;
}
@@ -1,13 +1,14 @@
<mat-list>
<mat-list id="top-panel">
<mat-list-item>
<mat-form-field class="full-width">
<input matInput placeholder="Search channel" [(ngModel)]="searchTerm.inf.title">
<input matInput placeholder="Search channel {{channelList?.length}}" [(ngModel)]="searchTerm.inf.title">
</mat-form-field>
</mat-list-item>
<mat-list-item *ngFor=" let channel of channelList | filterBy: searchTerm">
<button mat-button (click)="changeChannel.emit({ url: channel.url, title: channel.inf.title})">
{{channel?.inf?.title || 'Unnamed Channel'}}
</button>
<mat-divider></mat-divider>
<button mat-icon-button [routerLink]="'/'" matTooltip="Upload or select other playlist"><mat-icon>create_new_folder</mat-icon></button>
</mat-list-item>
</mat-list>
<mat-nav-list id="channels-list">
<mat-list-item *ngFor=" let channel of channelList | filterBy: searchTerm; index as i" [class.active]="selected?.id === channel.id" (click)="selectChannel(channel)">
{{i+1 + '. ' + channel?.inf?.title || 'Unnamed Channel'}}
<mat-divider></mat-divider>
</mat-list-item>
</mat-nav-list>
@@ -1,4 +1,5 @@
import { Component, Input, Output, EventEmitter } from '@angular/core';
import { Channel } from 'src/app/state';
@Component({
selector: 'app-channel-list-container',
@@ -9,7 +10,12 @@ export class ChannelListContainerComponent {
/**
* Channels array
*/
@Input() channelList;
@Input() channelList: Channel[];
/**
* Selected channel
*/
selected: Channel;
/**
* Emits on channel change
@@ -27,4 +33,13 @@ export class ChannelListContainerComponent {
title: '',
},
};
/**
* Sets clicked channel as selected and emits them to the parent component
* @param channel selected channel
*/
selectChannel(channel: Channel): void {
this.selected = channel;
this.changeChannel.emit({ url: channel.url, title: channel.inf.title});
}
}
@@ -1,3 +1,9 @@
:host {
max-width: 800px;
margin: 0 auto;
display: block;
}
.drop-container {
height: 200px;
border: 2px dashed #ccc;
@@ -8,7 +14,7 @@
color: #666;
}
input {
#file input {
border-radius: 4px;
border: 1px solid #ccc;
padding: 5px;
@@ -18,7 +24,7 @@ input {
font-size: 1.25em;
}
.inputfile {
.input-file {
width: 0.1px;
height: 0.1px;
opacity: 0;
@@ -27,7 +33,7 @@ input {
z-index: -1;
}
.inputfile + label {
.input-file + label {
font-size: 1.25em;
font-weight: 700;
color: white;
@@ -35,12 +41,12 @@ input {
display: inline-block;
}
.inputfile:focus + label,
.inputfile + label:hover {
.input-file:focus + label,
.input-file + label:hover {
background-color: red;
}
.inputfile + label {
.input-file + label {
cursor: pointer;
line-height: 35px;
padding: 3px 10px;
@@ -1,3 +1,10 @@
<mat-card *ngIf="(playlists).length > 0">
<mat-card-content>
<app-recent-playlists [playlists]="playlists" (playlistClicked)="setPlaylist($event)" (removeClicked)="removePlaylist($event)">
</app-recent-playlists>
</mat-card-content>
</mat-card>
<br />
<mat-card>
<mat-card-subtitle>Add playlist</mat-card-subtitle>
<div class="drop-container" ngFileDrop [options]="options" (uploadOutput)="onUploadOutput($event)"
@@ -9,20 +16,11 @@
Drag & drop files here
</p>
<p>or</p>
<input type="file" ngFileSelect [options]="options" (uploadOutput)="onUploadOutput($event)" class="inputfile"
<input type="file" ngFileSelect [options]="options" (uploadOutput)="onUploadOutput($event)" class="input-file"
name="file" id="file" [uploadInput]="uploadInput" multiple>
<label for="file">
Choose a file
</label>
</div>
</mat-card>
<br>
<ng-container *ngIf="(playlists | keyvalue)?.length > 0">
<mat-card>
<mat-card-subtitle>Recent playlists</mat-card-subtitle>
<mat-card-content>
<app-recent-playlists [playlists]="playlists" (playlistClicked)="setPlaylist($event)">
</app-recent-playlists>
</mat-card-content>
</mat-card>
</ng-container>
<mat-progress-bar *ngIf="isLoading" mode="indeterminate"></mat-progress-bar>
@@ -9,6 +9,21 @@ import {
import { ChannelStore, createChannel } from 'src/app/state';
import { M3uService } from 'src/app/services/m3u-service.service';
import { Router } from '@angular/router';
import { NgxIndexedDBService } from 'ngx-indexed-db';
import { PLAYLISTS_STORE } from 'src/app/db.config';
import {MatSnackBar} from '@angular/material/snack-bar';
/**
* Describes playlist interface
*/
export interface Playlist {
id?: number;
title: string;
filename: string;
playlist: any;
importDate: number;
lastUsage: number;
}
@Component({
selector: 'app-playlist-uploader',
@@ -22,18 +37,22 @@ export class PlaylistUploaderComponent {
humanizeBytes: Function;
dragOver: boolean;
options: UploaderOptions;
playlists: any;
playlists: Playlist[] = [];
isLoading = false;
/**
* Creates an instanceof PlaylistUploaderComponent
* @param channelStore channels store
* @param dbService indexeddb service
* @param m3uService m3u service
* @param router angulars router
*/
constructor(
private channelStore: ChannelStore,
private dbService: NgxIndexedDBService,
private m3uService: M3uService,
private router: Router
private router: Router,
private snackBar: MatSnackBar
) {
this.getPlaylists();
@@ -52,6 +71,7 @@ export class PlaylistUploaderComponent {
*/
onUploadOutput(output: UploadOutput): void {
if (output.type === 'allAddedToQueue') {
this.isLoading = true;
if (this.files.length > 0) {
const fileReader = new FileReader();
fileReader.onload = fileLoadedEvent => {
@@ -64,7 +84,7 @@ export class PlaylistUploaderComponent {
);
this.savePlaylist(
this.files[0].name,
JSON.stringify(playlist)
playlist
);
this.setPlaylist(playlist);
@@ -108,6 +128,7 @@ export class PlaylistUploaderComponent {
* Navigates to the video player route
*/
navigateToPlayer(): void {
this.isLoading = false;
this.router.navigateByUrl('/iptv', { skipLocationChange: true });
}
@@ -117,21 +138,21 @@ export class PlaylistUploaderComponent {
* @param playlist playlist to save
*/
savePlaylist(name: string, playlist: any): void {
localStorage.setItem(name, playlist);
this.dbService.add<Playlist>('playlists', { filename: name, title: name, playlist, importDate: new Date().getMilliseconds(), lastUsage: new Date().getMilliseconds() }).then(() => {
console.log('playlist saved!')
});
}
/**
* Reads all saved playlists from the localStorage
* Reads all saved playlists from the browser store
*/
getPlaylists(): void {
this.playlists = { ...localStorage };
this.playlists = Object.keys(this.playlists)
.filter(key => key.includes('.m3u'))
.reduce((obj, key) => {
obj[key] = JSON.parse(this.playlists[key]);
return obj;
}, {});
this.dbService.getAll('playlists').then(
(playlists: Playlist[]) => this.playlists = playlists,
error => {
console.error(error);
}
);
}
/**
@@ -139,9 +160,19 @@ export class PlaylistUploaderComponent {
* @param playlist m3u playlist
*/
setPlaylist(playlist: any): void {
playlist.segments.forEach(element => {
this.channelStore.add(createChannel(element));
this.navigateToPlayer();
});
this.channelStore.reset();
const channels = playlist.segments.map(element => createChannel(element));
this.channelStore.upsertMany(channels);
this.navigateToPlayer();
}
/**
* Removes the provided playlist from the indexedDb
* @param playlist playlist to remove
*/
removePlaylist(playlist: Playlist) {
this.dbService.delete(PLAYLISTS_STORE, playlist.id);
this.getPlaylists();
this.snackBar.open('Done! Playlist was removed.', null,{ duration: 2000 });
}
}
@@ -1,3 +1,15 @@
<button *ngFor="let playlist of playlists | keyvalue" mat-stroked-button (click)="playlistClicked.emit(playlist.value)">
{{playlist.key}}
</button>
<mat-nav-list>
<div mat-subheader>Recently added playlists</div>
<a mat-list-item *ngFor="let item of playlists; last as last" (click)="playlistClicked.emit(item.playlist);">
<mat-icon mat-list-icon>folder</mat-icon>
<div mat-line>{{ item.title || item.filename }}</div>
<div mat-line>Channels: {{ item.playlist?.segments.length }}</div>
<!--<button mat-icon-button color="primary" (click)="$event.stopPropagation(); renameClicked.emit(item)" matTooltip="Rename playlist">
<mat-icon>edit</mat-icon>
</button>-->
<button mat-icon-button color="primary" (click)="$event.stopPropagation(); removeClicked.emit(item)" matTooltip="Remove playlist">
<mat-icon>delete</mat-icon>
</button>
<mat-divider *ngIf="!last"></mat-divider>
</a>
</mat-nav-list>
@@ -1,3 +1,3 @@
button {
margin: 5px;
}
mat-list-item {
cursor: pointer;
}
@@ -1,4 +1,5 @@
import { Component, Input, Output, EventEmitter } from '@angular/core';
import { Playlist } from '../playlist-uploader/playlist-uploader.component';
@Component({
selector: 'app-recent-playlists',
@@ -6,13 +7,15 @@ import { Component, Input, Output, EventEmitter } from '@angular/core';
styleUrls: ['./recent-playlists.component.scss'],
})
export class RecentPlaylistsComponent {
/**
* Playlist object
*/
@Input() playlists: any;
/** All available playlists */
@Input() playlists: Playlist[];
/**
* Emits on playlist click
*/
/** Emits on playlist selection */
@Output() playlistClicked: EventEmitter<any> = new EventEmitter();
/** Emits on playlist remove click */
@Output() removeClicked: EventEmitter<Playlist> = new EventEmitter();
/** Emits on playlist rename click */
@Output() renameClicked: EventEmitter<Playlist> = new EventEmitter();
}
@@ -6,6 +6,7 @@
<mat-drawer
#drawer
mode="side"
opened
(keydown.escape)="close()"
disableClose>
<ng-container *ngIf="channels$ | async as channels">
+19
View File
@@ -0,0 +1,19 @@
import { DBConfig } from 'ngx-indexed-db';
export const PLAYLISTS_STORE = 'playlists';
export const dbConfig: DBConfig = {
name: 'MyIptvDb',
version: 1,
objectStoresMeta: [{
store: PLAYLISTS_STORE,
storeConfig: { keyPath: 'id', autoIncrement: true },
storeSchema: [
{ name: 'title', keypath: 'title', options: { unique: false } },
{ name: 'filename', keypath: 'filename', options: { unique: false } },
{ name: 'playlist', keypath: 'playlist', options: { unique: false } },
{ name: 'importDate', keypath: 'importDate', options: { unique: false } },
{ name: 'lastUsage', keypath: 'lastUsage', options: { unique: false } },
]
}]
};
+4
View File
@@ -4,8 +4,10 @@ import { MatCardModule } from '@angular/material/card';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatListModule } from '@angular/material/list';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatSidenavModule } from '@angular/material/sidenav';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatSnackBarModule } from '@angular/material/snack-bar';
@NgModule({
exports: [
@@ -14,7 +16,9 @@ import { MatToolbarModule } from '@angular/material/toolbar';
MatIconModule,
MatInputModule,
MatListModule,
MatProgressBarModule,
MatSidenavModule,
MatSnackBarModule,
MatToolbarModule,
],
})