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 }} + + | 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 }} - | 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"] +}