diff --git a/src/app/services/data.service.ts b/src/app/services/data.service.ts index 942aca12f..1bfd2f7cd 100644 --- a/src/app/services/data.service.ts +++ b/src/app/services/data.service.ts @@ -1,7 +1,14 @@ +import { ipcRenderer } from 'electron'; export abstract class DataService { get isElectron(): boolean { return !!(window && window.process && (window.process as any).type); } + get remote() { + return this.isElectron ? window.require('electron').remote : null; + } + get ipcRenderer(): typeof ipcRenderer { + return this.isElectron ? window.require('electron').ipcRenderer : null; + } abstract getAppVersion(): string; abstract sendIpcEvent(type: string, payload?: unknown): void; abstract removeAllListeners(type: string): void; diff --git a/src/app/services/electron.service.stub.ts b/src/app/services/electron.service.stub.ts index 7a7aba8a4..785a781ae 100644 --- a/src/app/services/electron.service.stub.ts +++ b/src/app/services/electron.service.stub.ts @@ -14,6 +14,8 @@ export class ElectronServiceStub { }, }; + isElectron = true; + removeAllListeners(type: string) {} getAppVersion() { diff --git a/src/app/services/electron.service.ts b/src/app/services/electron.service.ts index 1df826678..c0cc62afe 100644 --- a/src/app/services/electron.service.ts +++ b/src/app/services/electron.service.ts @@ -1,22 +1,16 @@ import { Injectable } from '@angular/core'; import { DataService } from './data.service'; -import { ipcRenderer, remote } from 'electron'; - @Injectable({ providedIn: 'root', }) export class ElectronService extends DataService { - private ipcRenderer: typeof ipcRenderer; - private remote: typeof remote; - + /** + * Creates an instance of ElectronService. + */ constructor() { super(); - // Conditional imports - if (this.isElectron) { - this.ipcRenderer = window.require('electron').ipcRenderer; - this.remote = window.require('electron').remote; - } + console.log('Electron service initialized...'); } /** @@ -43,6 +37,11 @@ export class ElectronService extends DataService { this.ipcRenderer.removeAllListeners(type); } + /** + * Listens on an IPC event from the main process + * @param command command name + * @param callback callback function + */ listenOn(command: string, callback: (...args: any[]) => void): void { this.ipcRenderer.on(command, callback); } diff --git a/src/app/services/pwa.service.ts b/src/app/services/pwa.service.ts index 22b99d782..18d39e1e0 100644 --- a/src/app/services/pwa.service.ts +++ b/src/app/services/pwa.service.ts @@ -1,36 +1,61 @@ import { Injectable } from '@angular/core'; import { + PLAYLIST_GET_ALL, + PLAYLIST_GET_ALL_RESPONSE, + PLAYLIST_GET_BY_ID, PLAYLIST_PARSE, + PLAYLIST_PARSE_BY_URL, PLAYLIST_PARSE_RESPONSE, + PLAYLIST_REMOVE_BY_ID, + PLAYLIST_REMOVE_BY_ID_RESPONSE, + PLAYLIST_SAVE_DETAILS, + PLAYLIST_UPDATE, + PLAYLIST_UPDATE_FAVORITES, } from '../../../shared/ipc-commands'; import { ParsedPlaylist } from '../../typings'; import { DataService } from './data.service'; import { parse } from 'iptv-playlist-parser'; import { guid } from '@datorama/akita'; -import { Playlist } from '../../../shared/playlist.interface'; +import { + Playlist, + PlaylistUpdateState, +} from '../../../shared/playlist.interface'; import { NgxIndexedDBService } from 'ngx-indexed-db'; -import { Observable } from 'rxjs'; +import { switchMap } from 'rxjs'; +import { HttpClient } from '@angular/common/http'; +import { DbStores } from '../indexed-db.config'; +import { AppConfig } from '../../environments/environment'; @Injectable({ providedIn: 'root', }) export class PwaService extends DataService { - playlists$: Observable = - this.dbService.getAll('playlists'); - - constructor(private dbService: NgxIndexedDBService) { + /** + * Creates an instance of PwaService. + * @param dbService database service + * @param http angular http client + */ + constructor( + private dbService: NgxIndexedDBService, + private http: HttpClient + ) { super(); - console.log('PWA Service'); + console.log('PWA service initialized...'); } + /** + * Returns the current version of the app + */ getAppVersion(): string { - return '1.0.0'; - //throw new Error('Method not implemented.'); + return AppConfig.version; } + /** + * Handles incoming IPC commands + * @param type ipc command type + * @param payload payload + */ sendIpcEvent(type: string, payload?: any): void { - console.log(type, payload); - if (type === PLAYLIST_PARSE) { const parsedPlaylist = this.parsePlaylist(payload.playlist); const playlistObject = this.createPlaylistObject( @@ -41,26 +66,148 @@ export class PwaService extends DataService { ); // save to db - this.dbService.add('playlists', playlistObject).subscribe((key) => { - console.log('key: ', key); - }); + this.dbService + .add(DbStores.Playlists, playlistObject) + .subscribe((key) => { + console.log('playlist was saved to db...'); + }); window.postMessage({ type: PLAYLIST_PARSE_RESPONSE, payload: playlistObject, }); - } else if (type === 'playlist-by-id') { + } else if (type === PLAYLIST_GET_BY_ID) { this.dbService - .getByIndex('playlists', '_id', payload.id) + .getByIndex(DbStores.Playlists, '_id', payload.id) .subscribe((playlist) => { window.postMessage({ type: PLAYLIST_PARSE_RESPONSE, payload: playlist, }); }); + } else if (type === PLAYLIST_REMOVE_BY_ID) { + this.dbService + .delete(DbStores.Playlists, payload.id) + .subscribe((playlist) => { + window.postMessage({ + type: PLAYLIST_REMOVE_BY_ID_RESPONSE, + payload: playlist, + }); + }); + } else if (type === PLAYLIST_GET_ALL) { + this.dbService.getAll(DbStores.Playlists).subscribe((playlists) => { + window.postMessage({ + type: PLAYLIST_GET_ALL_RESPONSE, + payload: playlists, + }); + }); + } else if (type === PLAYLIST_SAVE_DETAILS) { + this.dbService + .getByID(DbStores.Playlists, payload._id) + .pipe( + switchMap((playlist: Playlist) => { + return this.dbService.update(DbStores.Playlists, { + ...playlist, + title: payload.title, + }); + }) + ) + .subscribe((playlists) => { + window.postMessage({ + type: PLAYLIST_GET_ALL_RESPONSE, + payload: playlists, + }); + }); + } else if (type === PLAYLIST_UPDATE_FAVORITES) { + this.dbService + .getByID(DbStores.Playlists, payload.id) + .pipe( + switchMap((playlist: Playlist) => { + return this.dbService.update(DbStores.Playlists, { + ...playlist, + favorites: payload.favorites, + }); + }) + ) + .subscribe(() => { + console.log('favorites were updated...'); + }); + } else if (type === PLAYLIST_PARSE_BY_URL) { + this.fetchFromUrl(payload); + } else if (type === PLAYLIST_UPDATE) { + this.http + .get(payload.url, { responseType: 'text' }) + .subscribe((response: any) => { + const refreshedPlaylist = + this.convertFileStringToPlaylist(response); + + this.dbService + .getByID(DbStores.Playlists, payload.id) + .pipe( + switchMap((currentPlaylist: Playlist) => { + return this.dbService.update( + DbStores.Playlists, + { + ...currentPlaylist, + ...refreshedPlaylist, + count: refreshedPlaylist.items.length, + updateDate: Date.now(), + updateState: + PlaylistUpdateState.UPDATED, + } + ); + }) + ) + .subscribe((playlists) => { + console.log('playlist was updated...'); + window.postMessage({ + type: PLAYLIST_GET_ALL_RESPONSE, + payload: playlists, + }); + }); + }); } } + /** + * Fetches playlist from the specified url + * @param payload playlist payload + */ + fetchFromUrl(payload: Partial): void { + this.http + .get(payload.url, { responseType: 'text' }) + .subscribe((response: any) => { + const parsedPlaylist = + this.convertFileStringToPlaylist(response); + const playlistObject = this.createPlaylistObject( + payload.title, + parsedPlaylist, + payload.url, + 'URL' + ); + + // save to db + this.dbService + .add(DbStores.Playlists, playlistObject) + .subscribe(() => { + console.log('playlist was added...'); + }); + + window.postMessage({ + type: PLAYLIST_PARSE_RESPONSE, + payload: playlistObject, + }); + }); + } + + /** + * Converts file string to playlist object + * @param m3uString + */ + convertFileStringToPlaylist(m3uString: string): ParsedPlaylist { + return this.parsePlaylist(m3uString.split('\n')); + } + /** * Saves playlist to the localStorage * @param name name of the playlist @@ -74,9 +221,10 @@ export class PwaService extends DataService { urlOrPath?: string, uploadType?: 'URL' | 'FILE' ): Playlist { + const id = guid(); return { - id: guid(), - _id: guid(), + id, + _id: id, filename: name, title: name, count: playlist.items.length, @@ -106,11 +254,10 @@ export class PwaService extends DataService { } removeAllListeners(type: string) { - console.log(type + ' listeners removed'); + // not implemented } listenOn(command: string, callback: (...args: any[]) => void): void { - console.log('listen on ' + command); window.addEventListener('message', callback); } }