From 19ef7e18bfe018f0fd468e53c6bbadfe05b94cb6 Mon Sep 17 00:00:00 2001 From: 4gray Date: Fri, 3 Jul 2020 08:55:51 +0200 Subject: [PATCH] refactor: switch to iptv-playlist-parser --- package-lock.json | 41 +++++++++++---- package.json | 3 +- .../channel-list-container.component.html | 11 ++-- .../channel-list-container.component.ts | 8 ++- .../playlist-uploader.component.html | 2 +- .../playlist-uploader.component.ts | 50 +++++++++++-------- .../recent-playlists.component.html | 7 +-- src/app/services/m3u-service.service.ts | 24 ++------- src/app/state/channel.model.ts | 20 ++++++-- 9 files changed, 97 insertions(+), 69 deletions(-) diff --git a/package-lock.json b/package-lock.json index 11e04e02a..80bbec2fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7426,6 +7426,9 @@ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "dev": true }, + "iptv-playlist-parser": { + "version": "git+https://github.com/4gray/iptv-playlist-parser.git#519d10a58128496e246f24bc2cc36dd0ec288c6b" + }, "is-absolute-url": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", @@ -9014,11 +9017,6 @@ } } }, - "m3u8-file-parser": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/m3u8-file-parser/-/m3u8-file-parser-0.2.3.tgz", - "integrity": "sha512-Iz5aPr9J9PYBKyKK8+HYlg4Oqcwcg+TsxfoKVD1Mn0OqWES4Y82Na61XgMeLmKCujz+lxS/ygCNEdodgRLiOiQ==" - }, "magic-string": { "version": "0.25.7", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", @@ -12168,7 +12166,14 @@ "safe-buffer": "5.1.2", "tough-cookie": "2.4.3", "tunnel-agent": "0.6.0", - "uuid": "3.3.3" + "uuid": "3.4.0" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + } } }, "require-directory": { @@ -14324,7 +14329,7 @@ "requires": { "debug": "3.2.6", "request": "2.88.0", - "uuid": "3.3.3" + "uuid": "3.4.0" }, "dependencies": { "debug": { @@ -14335,6 +14340,12 @@ "requires": { "ms": "2.1.2" } + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true } } }, @@ -14526,9 +14537,9 @@ "dev": true }, "uuid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", - "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.2.0.tgz", + "integrity": "sha512-CYpGiFTUrmI6OBMkAdjSDM0k5h8SkkiTP4WAjQgDgNB1S3Ou9VBEvr6q0Kv2H1mMk7IWfxYGpMH5sd5AvcIV2Q==" }, "validate-npm-package-license": { "version": "3.0.4", @@ -15183,7 +15194,15 @@ "dev": true, "requires": { "ansi-colors": "3.2.4", - "uuid": "3.3.3" + "uuid": "3.4.0" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + } } }, "webpack-merge": { diff --git a/package.json b/package.json index 4be34e5b6..d88e45d7f 100644 --- a/package.json +++ b/package.json @@ -26,13 +26,14 @@ "@angular/service-worker": "~9.1.11", "@datorama/akita": "^4.23.2", "hls.js": "^0.12.4", - "m3u8-file-parser": "^0.2.3", + "iptv-playlist-parser": "git+https://github.com/4gray/iptv-playlist-parser.git", "material-icons": "^0.3.1", "ngx-filter-pipe": "^2.1.2", "ngx-indexed-db": "^5.0.4", "ngx-uploader": "^7.1.0", "rxjs": "~6.5.5", "tslib": "^1.13.0", + "uuid": "^8.2.0", "zone.js": "~0.10.2" }, "devDependencies": { diff --git a/src/app/components/channel-list-container/channel-list-container.component.html b/src/app/components/channel-list-container/channel-list-container.component.html index 2a8dfcadf..bf763fca0 100644 --- a/src/app/components/channel-list-container/channel-list-container.component.html +++ b/src/app/components/channel-list-container/channel-list-container.component.html @@ -1,14 +1,17 @@ - + - + - - {{i+1 + '. ' + channel?.inf?.title || 'Unnamed Channel'}} + + {{i+1 + '. ' + channel?.name || 'Unnamed Channel'}} diff --git a/src/app/components/channel-list-container/channel-list-container.component.ts b/src/app/components/channel-list-container/channel-list-container.component.ts index 43b607d32..4c8658645 100644 --- a/src/app/components/channel-list-container/channel-list-container.component.ts +++ b/src/app/components/channel-list-container/channel-list-container.component.ts @@ -22,16 +22,14 @@ export class ChannelListContainerComponent { */ @Output() changeChannel: EventEmitter<{ url: string; - title: string; + name: string; }> = new EventEmitter(); /** * Search term for channel filter */ searchTerm: any = { - inf: { - title: '', - }, + name: '', }; /** @@ -40,6 +38,6 @@ export class ChannelListContainerComponent { */ selectChannel(channel: Channel): void { this.selected = channel; - this.changeChannel.emit({ url: channel.url, title: channel.inf.title}); + this.changeChannel.emit({ url: channel.url, name: channel.name }); } } diff --git a/src/app/components/playlist-uploader/playlist-uploader.component.html b/src/app/components/playlist-uploader/playlist-uploader.component.html index 95aaeba6b..a72d90c24 100644 --- a/src/app/components/playlist-uploader/playlist-uploader.component.html +++ b/src/app/components/playlist-uploader/playlist-uploader.component.html @@ -6,7 +6,7 @@
- Add playlist + Add playlist via file upload
diff --git a/src/app/components/playlist-uploader/playlist-uploader.component.ts b/src/app/components/playlist-uploader/playlist-uploader.component.ts index f616ba0d3..399bef1f7 100644 --- a/src/app/components/playlist-uploader/playlist-uploader.component.ts +++ b/src/app/components/playlist-uploader/playlist-uploader.component.ts @@ -6,12 +6,12 @@ import { humanizeBytes, UploaderOptions, } from 'ngx-uploader'; -import { ChannelStore, createChannel } from 'src/app/state'; +import { ChannelStore, createChannel, Channel } 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'; +import { MatSnackBar } from '@angular/material/snack-bar'; /** * Describes playlist interface @@ -74,18 +74,13 @@ export class PlaylistUploaderComponent { this.isLoading = true; if (this.files.length > 0) { const fileReader = new FileReader(); - fileReader.onload = fileLoadedEvent => { + fileReader.onload = (fileLoadedEvent) => { const result = (fileLoadedEvent.target as FileReader) .result; const array = (result as string).split('\n'); - const playlist = this.m3uService.convertArrayToPlaylist( - array - ); - this.savePlaylist( - this.files[0].name, - playlist - ); + const playlist = this.m3uService.parsePlaylist(array); + this.savePlaylist(this.files[0].name, playlist); this.setPlaylist(playlist); }; @@ -101,7 +96,7 @@ export class PlaylistUploaderComponent { typeof output.file !== 'undefined' ) { const index = this.files.findIndex( - file => + (file) => typeof output.file !== 'undefined' && file.id === output.file.id ); @@ -138,9 +133,17 @@ export class PlaylistUploaderComponent { * @param playlist playlist to save */ savePlaylist(name: string, playlist: any): void { - this.dbService.add('playlists', { filename: name, title: name, playlist, importDate: new Date().getMilliseconds(), lastUsage: new Date().getMilliseconds() }).then(() => { - console.log('playlist saved!') - }); + this.dbService + .add('playlists', { + filename: name, + title: name, + playlist, + importDate: new Date().getMilliseconds(), + lastUsage: new Date().getMilliseconds(), + }) + .then(() => { + console.log('playlist saved!'); + }); } /** @@ -148,8 +151,8 @@ export class PlaylistUploaderComponent { */ getPlaylists(): void { this.dbService.getAll('playlists').then( - (playlists: Playlist[]) => this.playlists = playlists, - error => { + (playlists: Playlist[]) => (this.playlists = playlists), + (error) => { console.error(error); } ); @@ -158,10 +161,13 @@ export class PlaylistUploaderComponent { /** * Sets the given playlist as active for the current session * @param playlist m3u playlist + * // TODO: use typings from iptv-parser lib */ - setPlaylist(playlist: any): void { - this.channelStore.reset(); - const channels = playlist.segments.map(element => createChannel(element)); + setPlaylist(playlist: { header: any; items: Channel[] }): void { + this.channelStore.reset(); + const channels = playlist.items.map((element) => + createChannel(element) + ); this.channelStore.upsertMany(channels); this.navigateToPlayer(); } @@ -170,9 +176,11 @@ export class PlaylistUploaderComponent { * Removes the provided playlist from the indexedDb * @param playlist playlist to remove */ - removePlaylist(playlist: Playlist) { + removePlaylist(playlist: Playlist): void { this.dbService.delete(PLAYLISTS_STORE, playlist.id); this.getPlaylists(); - this.snackBar.open('Done! Playlist was removed.', null,{ duration: 2000 }); + this.snackBar.open('Done! Playlist was removed.', null, { + duration: 2000, + }); } } diff --git a/src/app/components/recent-playlists/recent-playlists.component.html b/src/app/components/recent-playlists/recent-playlists.component.html index 92da2c9f4..58ea921e4 100644 --- a/src/app/components/recent-playlists/recent-playlists.component.html +++ b/src/app/components/recent-playlists/recent-playlists.component.html @@ -1,13 +1,14 @@ -
Recently added playlists
+
Recently added playlists ({{playlists.length}})
folder
{{ item.title || item.filename }}
-
Channels: {{ item.playlist?.segments.length }}
+
Channels: {{ item.playlist?.items?.length }}
- diff --git a/src/app/services/m3u-service.service.ts b/src/app/services/m3u-service.service.ts index 59f3a4537..a8294c389 100644 --- a/src/app/services/m3u-service.service.ts +++ b/src/app/services/m3u-service.service.ts @@ -1,30 +1,16 @@ import { Injectable } from '@angular/core'; -import * as M3U8FileParser from 'm3u8-file-parser'; +import { parse } from 'iptv-playlist-parser'; @Injectable({ providedIn: 'root', }) export class M3uService { /** - * m3u8 parser - */ - m3u8FileParser = new M3U8FileParser(); - - /** - * Converts string based array to playlist object + * Parses string based array to playlist object * @param m3uArray m3u playlist as array with strings */ - convertArrayToPlaylist(m3uArray: any[]): any { - this.m3u8FileParser.read(m3uArray.join('\n')); - return this.m3u8FileParser.getResult(); - } - - /** - * Converts string to playlist structure - * @param m3uString playlist as string - */ - convertStringToPlaylist(m3uString: string): any { - this.m3u8FileParser.read(m3uString); - return this.m3u8FileParser.getResult(); + parsePlaylist(m3uArray: any[]): any { + const playlistAsString = m3uArray.join('\n'); + return parse(playlistAsString); } } diff --git a/src/app/state/channel.model.ts b/src/app/state/channel.model.ts index d5d8f9c61..5b54c902e 100755 --- a/src/app/state/channel.model.ts +++ b/src/app/state/channel.model.ts @@ -1,15 +1,27 @@ import { ID, guid } from '@datorama/akita'; +/** + * Represents channel object + * TODO: define channel interface in iptv-parser library + */ export interface Channel { - id: ID; - inf: any; + id: string; url: string; + name: string; + group: { + title: string; + }; } +/** + * Creates new channel object based on the given fields + * @param params partial channel object + */ export function createChannel(params: Partial) { return { id: guid(), - inf: params.inf, - url: params.url + name: params.name, + group: params.group, + url: params.url, } as Channel; }