diff --git a/src/app/home/recent-playlists/playlist-info/playlist-info.component.ts b/src/app/home/recent-playlists/playlist-info/playlist-info.component.ts index d4b19a0df..53b30b5f5 100644 --- a/src/app/home/recent-playlists/playlist-info/playlist-info.component.ts +++ b/src/app/home/recent-playlists/playlist-info/playlist-info.component.ts @@ -9,8 +9,8 @@ import { } from '@angular/forms'; import { MAT_DIALOG_DATA } from '@angular/material/dialog'; import { PLAYLIST_SAVE_DETAILS } from '../../../../../shared/ipc-commands'; -import { ElectronService } from '../../../services/electron.service'; import { Playlist } from '../../../../../shared/playlist.interface'; +import { DataService } from '../../../services/data.service'; @Component({ selector: 'app-playlist-info', @@ -34,7 +34,7 @@ export class PlaylistInfoComponent { constructor( private datePipe: DatePipe, private formBuilder: FormBuilder, - private electronService: ElectronService, + private electronService: DataService, @Inject(MAT_DIALOG_DATA) playlist: Playlist ) { this.playlist = playlist; diff --git a/src/app/home/recent-playlists/recent-playlists.component.ts b/src/app/home/recent-playlists/recent-playlists.component.ts index 520eea343..f86a056f2 100644 --- a/src/app/home/recent-playlists/recent-playlists.component.ts +++ b/src/app/home/recent-playlists/recent-playlists.component.ts @@ -4,7 +4,7 @@ import { MatDialog } from '@angular/material/dialog'; import { PlaylistInfoComponent } from './playlist-info/playlist-info.component'; import { PlaylistMeta } from './../home.component'; import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; -import { ElectronService } from '../../services/electron.service'; +import { DataService } from '../../services/data.service'; @Component({ selector: 'app-recent-playlists', @@ -31,7 +31,7 @@ export class RecentPlaylistsComponent { */ constructor( public dialog: MatDialog, - private electronService: ElectronService + private electronService: DataService ) {} /** diff --git a/src/app/player/components/epg-list/epg-list.component.ts b/src/app/player/components/epg-list/epg-list.component.ts index 865a4df2b..1fbfff301 100644 --- a/src/app/player/components/epg-list/epg-list.component.ts +++ b/src/app/player/components/epg-list/epg-list.component.ts @@ -2,11 +2,11 @@ import { Component, NgZone } from '@angular/core'; import { EpgChannel } from '../../models/epg-channel.model'; import { EpgProgram } from '../../models/epg-program.model'; import * as moment from 'moment'; -import { ElectronService } from '../../../services/electron.service'; import { EPG_GET_PROGRAM_DONE } from '../../../../../shared/ipc-commands'; import { ChannelQuery, ChannelStore } from '../../../state'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; +import { DataService } from '../../../services/data.service'; export interface EpgData { channel: EpgChannel; @@ -58,7 +58,7 @@ export class EpgListComponent { constructor( private channelQuery: ChannelQuery, private channelStore: ChannelStore, - private electronService: ElectronService, + private electronService: DataService, private ngZone: NgZone ) { this.electronService.listenOn( diff --git a/src/app/services/data.service.ts b/src/app/services/data.service.ts index 2a7a5c54d..942aca12f 100644 --- a/src/app/services/data.service.ts +++ b/src/app/services/data.service.ts @@ -1,4 +1,7 @@ export abstract class DataService { + get isElectron(): boolean { + return !!(window && window.process && (window.process as any).type); + } abstract getAppVersion(): string; abstract sendIpcEvent(type: string, payload?: unknown): void; abstract removeAllListeners(type: string): void; diff --git a/src/app/services/electron.service.ts b/src/app/services/electron.service.ts index 1566007e6..dd3cde12b 100644 --- a/src/app/services/electron.service.ts +++ b/src/app/services/electron.service.ts @@ -18,10 +18,6 @@ export class ElectronService extends DataService { // childProcess: typeof childProcess; // fs: typeof fs; - get isElectron(): boolean { - return !!(window && window.process && window.process.type); - } - constructor() { super(); // Conditional imports diff --git a/src/app/services/pwa.service.ts b/src/app/services/pwa.service.ts index 272b9be75..22b99d782 100644 --- a/src/app/services/pwa.service.ts +++ b/src/app/services/pwa.service.ts @@ -1,11 +1,24 @@ import { Injectable } from '@angular/core'; +import { + PLAYLIST_PARSE, + PLAYLIST_PARSE_RESPONSE, +} 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 { NgxIndexedDBService } from 'ngx-indexed-db'; +import { Observable } from 'rxjs'; @Injectable({ providedIn: 'root', }) export class PwaService extends DataService { - constructor() { + playlists$: Observable = + this.dbService.getAll('playlists'); + + constructor(private dbService: NgxIndexedDBService) { super(); console.log('PWA Service'); } @@ -15,9 +28,81 @@ export class PwaService extends DataService { //throw new Error('Method not implemented.'); } - sendIpcEvent(type: string, payload?: unknown): void { + sendIpcEvent(type: string, payload?: any): void { console.log(type, payload); - // throw new Error('Method not implemented.'); + + if (type === PLAYLIST_PARSE) { + const parsedPlaylist = this.parsePlaylist(payload.playlist); + const playlistObject = this.createPlaylistObject( + payload.title, + parsedPlaylist, + payload.path, + 'FILE' + ); + + // save to db + this.dbService.add('playlists', playlistObject).subscribe((key) => { + console.log('key: ', key); + }); + + window.postMessage({ + type: PLAYLIST_PARSE_RESPONSE, + payload: playlistObject, + }); + } else if (type === 'playlist-by-id') { + this.dbService + .getByIndex('playlists', '_id', payload.id) + .subscribe((playlist) => { + window.postMessage({ + type: PLAYLIST_PARSE_RESPONSE, + payload: playlist, + }); + }); + } + } + + /** + * Saves playlist to the localStorage + * @param name name of the playlist + * @param playlist playlist to save + * @param urlOrPath absolute fs path or url of the playlist + * @param uploadType upload type - by file or via an url + */ + createPlaylistObject( + name: string, + playlist: ParsedPlaylist, + urlOrPath?: string, + uploadType?: 'URL' | 'FILE' + ): Playlist { + return { + id: guid(), + _id: guid(), + filename: name, + title: name, + count: playlist.items.length, + playlist: { + ...playlist, + items: playlist.items.map((item) => ({ + id: guid(), + ...item, + })), + }, + importDate: new Date().toISOString(), + lastUsage: new Date().toISOString(), + favorites: [], + autoRefresh: false, + ...(uploadType === 'URL' ? { url: urlOrPath } : {}), + ...(uploadType === 'FILE' ? { filePath: urlOrPath } : {}), + }; + } + + /** + * Parses string based array to playlist object + * @param m3uArray m3u playlist as array with strings + */ + parsePlaylist(m3uArray: string[]): ParsedPlaylist { + const playlistAsString = m3uArray.join('\n'); + return parse(playlistAsString); } removeAllListeners(type: string) { @@ -26,5 +111,6 @@ export class PwaService extends DataService { listenOn(command: string, callback: (...args: any[]) => void): void { console.log('listen on ' + command); + window.addEventListener('message', callback); } } diff --git a/src/app/settings/settings.component.ts b/src/app/settings/settings.component.ts index 40d269b41..639cfaa9e 100644 --- a/src/app/settings/settings.component.ts +++ b/src/app/settings/settings.component.ts @@ -9,13 +9,13 @@ import { STORE_KEY } from '../shared/enums/store-keys.enum'; import { Settings, VideoPlayer } from './settings.interface'; import { HttpClient } from '@angular/common/http'; import * as semver from 'semver'; -import { ElectronService } from '../services/electron.service'; import { ChannelQuery } from '../state'; import { EPG_FETCH } from '../../../shared/ipc-commands'; import { Language } from './language.enum'; import { Theme } from './theme.enum'; import { SettingsService } from './../services/settings.service'; import { catchError } from 'rxjs/operators'; +import { DataService } from '../services/data.service'; /** Url of the package.json file in the app repository, required to get the version of the released app */ const PACKAGE_JSON_URL = @@ -74,7 +74,7 @@ export class SettingsComponent implements OnInit { */ constructor( private channelQuery: ChannelQuery, - private electronService: ElectronService, + private electronService: DataService, private formBuilder: FormBuilder, private http: HttpClient, private router: Router, diff --git a/src/app/state/channel.store.ts b/src/app/state/channel.store.ts index 6cc530663..a81b4a32b 100644 --- a/src/app/state/channel.store.ts +++ b/src/app/state/channel.store.ts @@ -4,10 +4,10 @@ import { CHANNEL_SET_USER_AGENT, EPG_GET_PROGRAM, } from '../../../shared/ipc-commands'; -import { ElectronService } from '../services/electron.service'; import { Channel } from './channel.model'; import * as moment from 'moment'; import { EpgProgram } from '../player/models/epg-program.model'; +import { DataService } from '../services/data.service'; export interface ChannelState extends EntityState { active: Channel; @@ -24,7 +24,7 @@ export class ChannelStore extends EntityStore { * Creates an instance of ChannelStore * @param electronService electron service */ - constructor(private electronService: ElectronService) { + constructor(private electronService: DataService) { super({ active: undefined, currentEpgProgram: undefined,