diff --git a/api.ts b/api.ts
index 153481711..eef587988 100644
--- a/api.ts
+++ b/api.ts
@@ -2,7 +2,7 @@ import { app, ipcMain } from 'electron';
import { parse } from 'iptv-playlist-parser';
import axios from 'axios';
import { guid } from '@datorama/akita';
-import { Playlist } from './src/app/playlist-uploader/playlist.interface';
+import { Playlist } from './src/app/home/playlist.interface';
import Nedb from 'nedb-promises-ts';
const fs = require('fs');
@@ -26,7 +26,7 @@ export class Api {
args.url
);
this.insertToDb(playlistObject);
- event.sender.send('parse-url-response', {
+ event.sender.send('parse-response', {
payload: playlistObject,
});
});
@@ -54,7 +54,7 @@ export class Api {
ipcMain.on('playlist-by-id', async (event, args) => {
const playlist = await db.findOne({ _id: args.id });
- event.sender.send('playlist-by-id-result', {
+ event.sender.send('parse-response', {
payload: playlist,
});
});
diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts
index 491a6e8dc..a282b9c88 100644
--- a/src/app/app-routing.module.ts
+++ b/src/app/app-routing.module.ts
@@ -1,13 +1,13 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { PageNotFoundComponent } from './shared/components';
-import { PlaylistUploaderComponent } from './playlist-uploader/playlist-uploader.component';
+import { HomeComponent } from './home/home.component';
import { VideoPlayerComponent } from './video-player/video-player.component';
const routes: Routes = [
{
path: '',
- component: PlaylistUploaderComponent,
+ component: HomeComponent,
},
{
path: 'iptv',
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index e28b48a1c..d5ee2756f 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -14,14 +14,16 @@ import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { AppComponent } from './app.component';
import { CommonModule } from '@angular/common';
-import { PlaylistUploaderComponent } from './playlist-uploader/playlist-uploader.component';
+import { HomeComponent } from './home/home.component';
import { NgxUploaderModule } from 'ngx-uploader';
import { VideoPlayerComponent } from './video-player/video-player.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
-import { RecentPlaylistsComponent } from './recent-playlists/recent-playlists.component';
+import { RecentPlaylistsComponent } from './home/recent-playlists/recent-playlists.component';
import { ChannelListContainerComponent } from './channel-list-container/channel-list-container.component';
import { FilterPipeModule } from 'ngx-filter-pipe';
import { VjsPlayerComponent } from './vjs-player/vjs-player.component';
+import { UrlUploadComponent } from './home/url-upload/url-upload.component';
+import { FileUploadComponent } from './home/file-upload/file-upload.component';
// AoT requires an exported function for factories
export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader {
@@ -32,10 +34,12 @@ export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader {
declarations: [
AppComponent,
ChannelListContainerComponent,
- PlaylistUploaderComponent,
- VideoPlayerComponent,
+ FileUploadComponent,
+ HomeComponent,
RecentPlaylistsComponent,
+ VideoPlayerComponent,
VjsPlayerComponent,
+ UrlUploadComponent,
],
imports: [
CommonModule,
diff --git a/src/app/home/file-upload/file-upload.component.html b/src/app/home/file-upload/file-upload.component.html
new file mode 100644
index 000000000..075613e7d
--- /dev/null
+++ b/src/app/home/file-upload/file-upload.component.html
@@ -0,0 +1,23 @@
+
+
save_alt
+
Drag & drop files here
+
or
+
+
+
diff --git a/src/app/playlist-uploader/playlist-uploader.component.scss b/src/app/home/file-upload/file-upload.component.scss
similarity index 73%
rename from src/app/playlist-uploader/playlist-uploader.component.scss
rename to src/app/home/file-upload/file-upload.component.scss
index b6664c514..560de482b 100644
--- a/src/app/playlist-uploader/playlist-uploader.component.scss
+++ b/src/app/home/file-upload/file-upload.component.scss
@@ -1,18 +1,4 @@
-::ng-deep {
- .mat-tab-label-content {
- text-transform: uppercase;
- }
-
- .mat-tab-group.mat-background-primary .mat-tab-header {
- background: #000 !important;
- }
-
- .mat-tab-group.mat-background-primary .mat-tab-header-pagination {
- display:none;
- }
-}
-
.drop-container {
margin: 10px;
padding: 10px;
@@ -23,11 +9,6 @@
color: #666;
}
-.url-form {
- margin: 15px;
- height: 304px;
-}
-
#file input {
border-radius: 4px;
border: 1px solid #ccc;
diff --git a/src/app/playlist-uploader/playlist-uploader.component.spec.ts b/src/app/home/file-upload/file-upload.component.spec.ts
similarity index 59%
rename from src/app/playlist-uploader/playlist-uploader.component.spec.ts
rename to src/app/home/file-upload/file-upload.component.spec.ts
index 77542fd3d..2e2640462 100644
--- a/src/app/playlist-uploader/playlist-uploader.component.spec.ts
+++ b/src/app/home/file-upload/file-upload.component.spec.ts
@@ -3,21 +3,21 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
-import { PlaylistUploaderComponent } from './playlist-uploader.component';
+import { FileUploadComponent } from './file-upload.component';
-describe('PlaylistUploaderComponent', () => {
- let component: PlaylistUploaderComponent;
- let fixture: ComponentFixture;
+describe('FileUploadComponent', () => {
+ let component: FileUploadComponent;
+ let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
- declarations: [ PlaylistUploaderComponent ]
+ declarations: [ FileUploadComponent ]
})
.compileComponents();
}));
beforeEach(() => {
- fixture = TestBed.createComponent(PlaylistUploaderComponent);
+ fixture = TestBed.createComponent(FileUploadComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/src/app/home/file-upload/file-upload.component.ts b/src/app/home/file-upload/file-upload.component.ts
new file mode 100644
index 000000000..acb5bd5d2
--- /dev/null
+++ b/src/app/home/file-upload/file-upload.component.ts
@@ -0,0 +1,90 @@
+import { Component, EventEmitter, Output } from '@angular/core';
+import {
+ UploaderOptions,
+ UploadFile,
+ UploadOutput,
+ UploadInput,
+} from 'ngx-uploader';
+
+@Component({
+ selector: 'app-file-upload',
+ templateUrl: './file-upload.component.html',
+ styleUrls: ['./file-upload.component.scss'],
+})
+export class FileUploadComponent {
+ /** Array with uploaded files */
+ files: UploadFile[] = [];
+ /** Upload emitter */
+ uploadInput: EventEmitter = new EventEmitter();
+ /** Drag over flag */
+ dragOver: boolean;
+ /** ngx-uploader lib options */
+ options: UploaderOptions = {
+ allowedContentTypes: [
+ 'application/x-mpegurl',
+ 'application/octet-stream',
+ 'application/mpegurl',
+ 'application/vnd.apple.mpegurl',
+ 'application/vnd.apple.mpegurl.audio',
+ 'audio/x-mpegurl',
+ 'audio/mpegurl',
+ ],
+ concurrency: 1,
+ maxUploads: 1,
+ };
+ /** Emits on reject event */
+ @Output() fileRejected: EventEmitter = new EventEmitter();
+ /** Emits after successful file selection */
+ @Output() fileSelected: EventEmitter<{
+ uploadEvent: Event;
+ file: UploadFile;
+ }> = new EventEmitter();
+
+ /**
+ * Handles file upload
+ * @param output
+ */
+ onUploadOutput(output: UploadOutput): void {
+ if (output.type === 'allAddedToQueue') {
+ if (this.files.length > 0) {
+ const fileReader = new FileReader();
+ fileReader.onload = (uploadEvent) =>
+ this.fileSelected.emit({
+ uploadEvent,
+ file: this.files[0],
+ });
+ fileReader.readAsText(this.files[0].nativeFile);
+ }
+ } else if (
+ output.type === 'addedToQueue' &&
+ typeof output.file !== 'undefined'
+ ) {
+ this.files.push(output.file);
+ } else if (
+ output.type === 'uploading' &&
+ typeof output.file !== 'undefined'
+ ) {
+ const index = this.files.findIndex(
+ (file) =>
+ typeof output.file !== 'undefined' &&
+ file.id === output.file.id
+ );
+ this.files[index] = output.file;
+ } else if (output.type === 'cancelled' || output.type === 'removed') {
+ this.files = this.files.filter(
+ (file: UploadFile) => file !== output.file
+ );
+ } else if (output.type === 'dragOver') {
+ this.dragOver = true;
+ } else if (output.type === 'dragOut') {
+ this.dragOver = false;
+ } else if (output.type === 'drop') {
+ this.dragOver = false;
+ } else if (
+ output.type === 'rejected' &&
+ typeof output.file !== 'undefined'
+ ) {
+ this.fileRejected.emit(output.file.name);
+ }
+ }
+}
diff --git a/src/app/playlist-uploader/playlist-uploader.component.html b/src/app/home/home.component.html
similarity index 53%
rename from src/app/playlist-uploader/playlist-uploader.component.html
rename to src/app/home/home.component.html
index f32a5e409..546ead40e 100644
--- a/src/app/playlist-uploader/playlist-uploader.component.html
+++ b/src/app/home/home.component.html
@@ -1,4 +1,7 @@
+
+
+
@@ -15,19 +18,7 @@
attach_file Add via file upload
-
-
save_alt
-
- Drag & drop files here
-
-
or
-
-
-
+
@@ -35,13 +26,7 @@
link Add via URL
-
-
-
-
-
-
+
diff --git a/src/app/home/home.component.scss b/src/app/home/home.component.scss
new file mode 100644
index 000000000..be72b2070
--- /dev/null
+++ b/src/app/home/home.component.scss
@@ -0,0 +1,13 @@
+::ng-deep {
+ .mat-tab-label-content {
+ text-transform: uppercase;
+ }
+
+ .mat-tab-group.mat-background-primary .mat-tab-header {
+ background: #000 !important;
+ }
+
+ .mat-tab-group.mat-background-primary .mat-tab-header-pagination {
+ display:none;
+ }
+}
\ No newline at end of file
diff --git a/src/app/home/home.component.spec.ts b/src/app/home/home.component.spec.ts
new file mode 100644
index 000000000..6d3706366
--- /dev/null
+++ b/src/app/home/home.component.spec.ts
@@ -0,0 +1,25 @@
+/* tslint:disable:no-unused-variable */
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { HomeComponent } from './home.component';
+
+describe('HomeComponent', () => {
+ let component: HomeComponent;
+ let fixture: ComponentFixture
;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [HomeComponent],
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(HomeComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/home/home.component.ts b/src/app/home/home.component.ts
new file mode 100644
index 000000000..871804e47
--- /dev/null
+++ b/src/app/home/home.component.ts
@@ -0,0 +1,173 @@
+/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
+import { Component, NgZone } from '@angular/core';
+import { UploadFile } from 'ngx-uploader';
+import { ChannelStore, createChannel } from '../state';
+import { Router } from '@angular/router';
+import { MatSnackBar } from '@angular/material/snack-bar';
+import { Playlist } from './playlist.interface';
+import { ElectronService } from '../services/electron.service';
+
+@Component({
+ selector: 'app-home',
+ templateUrl: './home.component.html',
+ styleUrls: ['./home.component.scss'],
+})
+export class HomeComponent {
+ /** Added playlists */
+ playlists: Pick[] = [];
+ /** Loading spinner state */
+ isLoading = false;
+ /** IPC Renderer commands list with callbacks */
+ commandsList = [
+ {
+ id: 'parse-response',
+ execute: (response: any) => this.setPlaylist(response.payload),
+ },
+ {
+ id: 'playlist-all-result',
+ execute: (response: any) => (this.playlists = response.payload),
+ },
+ {
+ id: 'playlist-remove-by-id-result',
+ execute: () => {
+ this.snackBar.open('Done! Playlist was removed.', null, {
+ duration: 2000,
+ });
+ this.electronService.ipcRenderer.send('playlists-all');
+ },
+ },
+ ];
+
+ /**
+ * Creates an instanceof HomeComponent
+ * @param channelStore channels store
+ * @param electronService electron service
+ * @param ngZone angulars ngZone module
+ * @param router angulars router
+ * @param snackBar snackbars with notification messages
+ */
+ constructor(
+ private channelStore: ChannelStore,
+ private electronService: ElectronService,
+ private ngZone: NgZone,
+ private router: Router,
+ private snackBar: MatSnackBar
+ ) {
+ // get all playlists
+ this.electronService.ipcRenderer.send('playlists-all');
+ // set all renderer listeners
+ this.setRendererListeners();
+ }
+
+ /**
+ * Set electrons main process listeners
+ */
+ setRendererListeners(): void {
+ this.commandsList.forEach((command) => {
+ this.electronService.ipcRenderer.on(
+ command.id,
+ (event, response) => {
+ this.ngZone.run(() => command.execute(response));
+ }
+ );
+ });
+ }
+
+ /**
+ * Shows the filename of rejected file
+ * @param fileName name of the uploaded file
+ */
+ rejectFile(fileName: string): void {
+ this.snackBar.open(
+ `File was rejected, unsupported file format (${fileName}).`,
+ null,
+ {
+ duration: 2000,
+ }
+ );
+ this.isLoading = false;
+ }
+
+ /**
+ * Parse and store uploaded playlist
+ * @param payload
+ */
+ handlePlaylist(payload: { uploadEvent: Event; file: UploadFile }): void {
+ this.isLoading = true;
+ const result = (payload.uploadEvent.target as FileReader).result;
+ const array = (result as string).split('\n');
+ this.electronService.ipcRenderer.send('parse-playlist', {
+ title: payload.file.name,
+ playlist: array,
+ });
+ }
+
+ /**
+ * Navigates to the video player route
+ */
+ navigateToPlayer(): void {
+ this.isLoading = false;
+ this.router.navigateByUrl('/iptv', { skipLocationChange: true });
+ }
+
+ /**
+ * Sends url of the playlist to the renderer process
+ * @param playlistUrl url of the added playlist
+ */
+ sendPlaylistsUrl(playlistUrl: string): void {
+ this.electronService.ipcRenderer.send('parse-playlist-by-url', {
+ title: this.getLastUrlSegment(playlistUrl),
+ url: playlistUrl,
+ });
+ }
+
+ /**
+ * Sets the given playlist as active for the current session
+ * @param playlist playlist object
+ */
+ setPlaylist(playlist: Playlist): void {
+ this.channelStore.reset();
+ const favorites = playlist.favorites || [];
+ const channels = playlist.playlist.items.map((element) =>
+ createChannel(element, favorites)
+ );
+ this.channelStore.upsertMany(channels);
+ this.channelStore.update(() => ({
+ favorites,
+ playlistId: playlist.id,
+ }));
+ this.navigateToPlayer();
+ }
+
+ /**
+ * Removes the provided playlist from the database
+ * @param playlistId playlist id to remove
+ */
+ removePlaylist(playlistId: string): void {
+ this.electronService.ipcRenderer.send('playlist-remove-by-id', {
+ id: playlistId,
+ });
+ }
+
+ /**
+ * Requests playlist by id
+ * @param playlistId playlist id
+ */
+ getPlaylist(playlistId: string): void {
+ this.electronService.ipcRenderer.send('playlist-by-id', {
+ id: playlistId,
+ });
+ }
+
+ /**
+ * Returns last segment (part after last slash "/") of the given URL
+ * @param value URL as string
+ */
+ getLastUrlSegment(value: string): string {
+ if (value && value.length > 1) {
+ return value.substr(value.lastIndexOf('/') + 1);
+ } else {
+ return '';
+ }
+ }
+}
diff --git a/src/app/playlist-uploader/playlist.interface.ts b/src/app/home/playlist.interface.ts
similarity index 100%
rename from src/app/playlist-uploader/playlist.interface.ts
rename to src/app/home/playlist.interface.ts
diff --git a/src/app/home/recent-playlists/recent-playlists.component.html b/src/app/home/recent-playlists/recent-playlists.component.html
new file mode 100644
index 000000000..4d9801bab
--- /dev/null
+++ b/src/app/home/recent-playlists/recent-playlists.component.html
@@ -0,0 +1,33 @@
+
+
+ cloud
+ No playlists were added
+
+ Please add your first playlist from the other tab
+
+
+
+ cloud
+ folder
+ {{ item.title || item.filename }}
+
+ Channels: {{ item.count }}
+ 3">
+ | Added: {{ item.importDate | date }}
+
+
+
+
+
+
diff --git a/src/app/recent-playlists/recent-playlists.component.scss b/src/app/home/recent-playlists/recent-playlists.component.scss
similarity index 100%
rename from src/app/recent-playlists/recent-playlists.component.scss
rename to src/app/home/recent-playlists/recent-playlists.component.scss
diff --git a/src/app/recent-playlists/recent-playlists.component.ts b/src/app/home/recent-playlists/recent-playlists.component.ts
similarity index 90%
rename from src/app/recent-playlists/recent-playlists.component.ts
rename to src/app/home/recent-playlists/recent-playlists.component.ts
index 2129cfa78..a9fb9846e 100644
--- a/src/app/recent-playlists/recent-playlists.component.ts
+++ b/src/app/home/recent-playlists/recent-playlists.component.ts
@@ -1,5 +1,5 @@
import { Component, Input, Output, EventEmitter } from '@angular/core';
-import { Playlist } from 'app/playlist-uploader/playlist.interface';
+import { Playlist } from '../playlist.interface';
@Component({
selector: 'app-recent-playlists',
diff --git a/src/app/home/url-upload/url-upload.component.html b/src/app/home/url-upload/url-upload.component.html
new file mode 100644
index 000000000..36353a087
--- /dev/null
+++ b/src/app/home/url-upload/url-upload.component.html
@@ -0,0 +1,18 @@
+
diff --git a/src/app/home/url-upload/url-upload.component.scss b/src/app/home/url-upload/url-upload.component.scss
new file mode 100644
index 000000000..067df20f3
--- /dev/null
+++ b/src/app/home/url-upload/url-upload.component.scss
@@ -0,0 +1,7 @@
+form {
+ margin: 15px;
+}
+
+button {
+ text-transform: uppercase;
+}
\ No newline at end of file
diff --git a/src/app/home/url-upload/url-upload.component.spec.ts b/src/app/home/url-upload/url-upload.component.spec.ts
new file mode 100644
index 000000000..8346a673a
--- /dev/null
+++ b/src/app/home/url-upload/url-upload.component.spec.ts
@@ -0,0 +1,49 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
+
+import { UrlUploadComponent } from './url-upload.component';
+import { MockModule } from 'ng-mocks';
+import { MatInputModule } from '@angular/material/input';
+
+describe('UrlUploadComponent', () => {
+ let component: UrlUploadComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [UrlUploadComponent],
+ imports: [MockModule(MatInputModule)],
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(UrlUploadComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('submit form with playlist url', async(() => {
+ spyOn(component.urlAdded, 'emit');
+ const TEST_URL = 'http://example.org/playlist.m3u';
+ const input = fixture.debugElement.query(By.css('input'));
+ const el = input.nativeElement;
+
+ const submitButton = fixture.debugElement.nativeElement.querySelector(
+ 'button'
+ );
+ expect(submitButton.disabled).toBeTruthy();
+ el.value = TEST_URL;
+ fixture.detectChanges();
+ expect(submitButton.disabled).toBeFalsy();
+
+ const form = fixture.debugElement.query(By.css('form'));
+ form.triggerEventHandler('ngSubmit', null);
+
+ // eslint-disable-next-line @typescript-eslint/unbound-method
+ expect(component.urlAdded.emit).toHaveBeenCalledWith(TEST_URL);
+ }));
+});
diff --git a/src/app/home/url-upload/url-upload.component.ts b/src/app/home/url-upload/url-upload.component.ts
new file mode 100644
index 000000000..e7dc78a56
--- /dev/null
+++ b/src/app/home/url-upload/url-upload.component.ts
@@ -0,0 +1,11 @@
+import { Component, Output, EventEmitter } from '@angular/core';
+
+@Component({
+ selector: 'app-url-upload',
+ templateUrl: './url-upload.component.html',
+ styleUrls: ['./url-upload.component.scss'],
+})
+export class UrlUploadComponent {
+ /** Emits url string to the parent component on form submit */
+ @Output() urlAdded: EventEmitter = new EventEmitter();
+}
diff --git a/src/app/playlist-uploader/playlist-uploader.component.ts b/src/app/playlist-uploader/playlist-uploader.component.ts
deleted file mode 100644
index ab1678c14..000000000
--- a/src/app/playlist-uploader/playlist-uploader.component.ts
+++ /dev/null
@@ -1,240 +0,0 @@
-import { Component, EventEmitter, NgZone } from '@angular/core';
-import {
- UploadOutput,
- UploadInput,
- UploadFile,
- humanizeBytes,
- UploaderOptions,
-} from 'ngx-uploader';
-import { ChannelStore, createChannel } from '../state';
-import { Router } from '@angular/router';
-import { MatSnackBar } from '@angular/material/snack-bar';
-import { Playlist } from './playlist.interface';
-import { ElectronService } from 'app/services/electron.service';
-
-@Component({
- selector: 'app-playlist-uploader',
- templateUrl: './playlist-uploader.component.html',
- styleUrls: ['./playlist-uploader.component.scss'],
-})
-export class PlaylistUploaderComponent {
- playlistUrl = '';
- formData: FormData;
- files: UploadFile[] = [];
- uploadInput: EventEmitter = new EventEmitter();
- humanizeBytes: any = humanizeBytes;
- dragOver: boolean;
- options: UploaderOptions = {
- allowedContentTypes: [
- 'application/x-mpegurl',
- 'application/octet-stream',
- 'application/mpegurl',
- 'application/vnd.apple.mpegurl',
- 'application/vnd.apple.mpegurl.audio',
- 'audio/x-mpegurl',
- 'audio/mpegurl',
- ],
- concurrency: 1,
- maxUploads: 1,
- };
- playlists: { count: number; title: string; _id: string }[] = [];
- isLoading = false;
-
- /**
- * Creates an instanceof PlaylistUploaderComponent
- * @param channelStore channels store
- * @param electronService electron service
- * @param ngZone angulars ngZone module
- * @param router angulars router
- * @param snackBar snackbars with notification messages
- */
- constructor(
- private channelStore: ChannelStore,
- private electronService: ElectronService,
- private ngZone: NgZone,
- private router: Router,
- private snackBar: MatSnackBar
- ) {
- // get all playlists
- this.electronService.ipcRenderer.send('playlists-all');
- // set all renderer listeners
- this.setRendererListeners();
- }
-
- /**
- * Set electrons main process listeners
- */
- setRendererListeners(): void {
- this.electronService.ipcRenderer.on(
- 'parse-response',
- (event, response) => {
- this.ngZone.run(() => this.setPlaylist(response.payload));
- }
- );
-
- this.electronService.ipcRenderer.on(
- 'parse-url-response',
- (event, response) => {
- this.ngZone.run(() => this.setPlaylist(response.payload));
- }
- );
-
- this.electronService.ipcRenderer.on(
- 'playlist-all-result',
- (event, response) => {
- this.ngZone.run(() => (this.playlists = response.payload));
- }
- );
- this.electronService.ipcRenderer.on(
- 'playlist-by-id-result',
- (event, response) => {
- this.ngZone.run(() => this.setPlaylist(response.payload));
- }
- );
- this.electronService.ipcRenderer.on(
- 'playlist-remove-by-id-result',
- () => {
- this.ngZone.run(() => {
- this.snackBar.open('Done! Playlist was removed.', null, {
- duration: 2000,
- });
- this.electronService.ipcRenderer.send('playlists-all');
- });
- }
- );
- }
-
- /**
- * Handles file upload
- * @param output
- */
- onUploadOutput(output: UploadOutput): void {
- if (output.type === 'allAddedToQueue') {
- if (this.files.length > 0) {
- this.isLoading = true;
- const fileReader = new FileReader();
- fileReader.onload = (fileLoadedEvent) =>
- this.handlePlaylist(fileLoadedEvent);
- fileReader.readAsText(this.files[0].nativeFile);
- }
- } else if (
- output.type === 'addedToQueue' &&
- typeof output.file !== 'undefined'
- ) {
- this.files.push(output.file);
- } else if (
- output.type === 'uploading' &&
- typeof output.file !== 'undefined'
- ) {
- const index = this.files.findIndex(
- (file) =>
- typeof output.file !== 'undefined' &&
- file.id === output.file.id
- );
- this.files[index] = output.file;
- } else if (output.type === 'cancelled' || output.type === 'removed') {
- this.files = this.files.filter(
- (file: UploadFile) => file !== output.file
- );
- } else if (output.type === 'dragOver') {
- this.dragOver = true;
- } else if (output.type === 'dragOut') {
- this.dragOver = false;
- } else if (output.type === 'drop') {
- this.dragOver = false;
- } else if (
- output.type === 'rejected' &&
- typeof output.file !== 'undefined'
- ) {
- this.snackBar.open(
- `File was rejected, unsupported file format (${output.file.name}).`,
- null,
- {
- duration: 2000,
- }
- );
- this.isLoading = false;
- }
- }
-
- /**
- * Parse and store uploaded playlist
- * @param fileLoadedEvent
- */
- handlePlaylist(fileLoadedEvent: Event): void {
- const result = (fileLoadedEvent.target as FileReader).result;
- const array = (result as string).split('\n');
- this.electronService.ipcRenderer.send('parse-playlist', {
- title: this.files[0].name,
- playlist: array,
- });
- }
-
- /**
- * Navigates to the video player route
- */
- navigateToPlayer(): void {
- this.isLoading = false;
- this.router.navigateByUrl('/iptv', { skipLocationChange: true });
- }
-
- /**
- * Sends url of the playlist to the renderer process
- */
- sendPlaylistsUrl(): void {
- this.electronService.ipcRenderer.send('parse-playlist-by-url', {
- title: this.getLastUrlSegment(this.playlistUrl),
- url: this.playlistUrl,
- });
- }
-
- /**
- * Sets the given playlist as active for the current session
- * @param playlist playlist object
- */
- setPlaylist(playlist: Playlist): void {
- this.channelStore.reset();
- const favorites = playlist.favorites || [];
- const channels = playlist.playlist.items.map((element) =>
- createChannel(element, favorites)
- );
- this.channelStore.upsertMany(channels);
- this.channelStore.update(() => ({
- favorites,
- playlistId: playlist.id,
- }));
- this.navigateToPlayer();
- }
-
- /**
- * Removes the provided playlist from the database
- * @param playlistId playlist id to remove
- */
- removePlaylist(playlistId: string): void {
- this.electronService.ipcRenderer.send('playlist-remove-by-id', {
- id: playlistId,
- });
- }
-
- /**
- * Requests playlist by id
- * @param playlistId playlist id
- */
- getPlaylist(playlistId: string): void {
- this.electronService.ipcRenderer.send('playlist-by-id', {
- id: playlistId,
- });
- }
-
- /**
- * Returns last segment (part after last slash "/") of the given URL
- * @param value URL as string
- */
- getLastUrlSegment(value: string): string {
- if (value && value.length > 1) {
- return value.substr(value.lastIndexOf('/') + 1);
- } else {
- return '';
- }
- }
-}
diff --git a/src/app/recent-playlists/recent-playlists.component.html b/src/app/recent-playlists/recent-playlists.component.html
deleted file mode 100644
index f02a1d959..000000000
--- a/src/app/recent-playlists/recent-playlists.component.html
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
- cloud
- No playlists were added
- Please add your first playlist from the other tab
-
-
- cloud
- folder
- {{ item.title || item.filename }}
-
- Channels: {{ item.count }} 3">
- | Added: {{item.importDate | date}}
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/app/video-player/video-player.component.ts b/src/app/video-player/video-player.component.ts
index d591c7369..50734acc6 100644
--- a/src/app/video-player/video-player.component.ts
+++ b/src/app/video-player/video-player.component.ts
@@ -3,9 +3,9 @@ import * as Hls from 'hls.js';
import { ChannelQuery, Channel } from '../state';
import { Observable } from 'rxjs';
import { MatSidenav } from '@angular/material/sidenav';
-import { ElectronService } from 'app/services/electron.service';
+import { ElectronService } from '../services/electron.service';
import { StorageMap } from '@ngx-pwa/local-storage';
-import { Settings, VideoPlayerType } from 'app/settings/settings.interface';
+import { Settings, VideoPlayerType } from '../settings/settings.interface';
/** Settings key in storage */
export const SETTINGS_STORE_KEY = 'settings';
diff --git a/src/tsconfig.spec.json b/src/tsconfig.spec.json
new file mode 100644
index 000000000..26a878ced
--- /dev/null
+++ b/src/tsconfig.spec.json
@@ -0,0 +1,10 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../../dist/out-tsc",
+ "module": "commonjs",
+ "types": ["jest", "node"]
+ },
+ "files": ["src/test-setup.ts"],
+ "include": ["**/*.spec.ts", "**/*.d.ts"]
+}