mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 17:06:15 -08:00
feat: import playlist as text
This commit is contained in:
1 parent
37f71c631e
commit
6676fa0a42
12 files changed
+153
-3
No files matched your search
@@ -19,6 +19,7 @@ import {
|
||||
PLAYLIST_PARSE,
|
||||
PLAYLIST_PARSE_BY_URL,
|
||||
PLAYLIST_PARSE_RESPONSE,
|
||||
PLAYLIST_PARSE_TEXT,
|
||||
PLAYLIST_REMOVE_BY_ID,
|
||||
PLAYLIST_REMOVE_BY_ID_RESPONSE,
|
||||
PLAYLIST_SAVE_DETAILS,
|
||||
@@ -91,6 +92,27 @@ export class Api {
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on(PLAYLIST_PARSE_TEXT, (event, args) => {
|
||||
try {
|
||||
const parsedPlaylist = this.parsePlaylist(
|
||||
args.text.split('\n')
|
||||
);
|
||||
const playlistObject = this.createPlaylistObject(
|
||||
'Imported as text',
|
||||
parsedPlaylist
|
||||
);
|
||||
this.insertToDb(playlistObject);
|
||||
event.sender.send(PLAYLIST_PARSE_RESPONSE, {
|
||||
payload: playlistObject,
|
||||
});
|
||||
} catch (err) {
|
||||
event.sender.send(ERROR, {
|
||||
message: err.response.statusText,
|
||||
status: err.response.status,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on(PLAYLIST_PARSE, (event, args) => {
|
||||
const parsedPlaylist = this.parsePlaylist(args.playlist);
|
||||
const playlistObject = this.createPlaylistObject(
|
||||
|
||||
@@ -14,6 +14,7 @@ export const PLAYLIST_GET_BY_ID = 'PLAYLIST:GET_BY_ID';
|
||||
export const PLAYLIST_SAVE_DETAILS = 'PLAYLIST:SAVE_DETAILS';
|
||||
export const PLAYLIST_PARSE = 'PLAYLIST:PARSE_PLAYLIST';
|
||||
export const PLAYLIST_PARSE_BY_URL = 'PLAYLIST:PARSE_PLAYLIST_BY_URL';
|
||||
export const PLAYLIST_PARSE_TEXT = 'PLAYLIST:PLAYLIST_PARSE_TEXT';
|
||||
export const PLAYLIST_PARSE_RESPONSE = 'PLAYLIST:PARSE_PLAYLIST_RESPONSE';
|
||||
export const PLAYLIST_UPDATE = 'PLAYLIST:UPDATE';
|
||||
export const PLAYLIST_UPDATE_RESPONSE = 'PLAYLIST:UPDATE_RESPONSE';
|
||||
|
||||
@@ -43,6 +43,17 @@
|
||||
></app-url-upload>
|
||||
</ng-template>
|
||||
</mat-tab>
|
||||
<mat-tab>
|
||||
<ng-template mat-tab-label>
|
||||
<mat-icon class="tab-icon">text_format</mat-icon>
|
||||
{{ 'HOME.TABS.TEXT_IMPORT' | translate }}
|
||||
</ng-template>
|
||||
<ng-template matTabContent>
|
||||
<app-text-import
|
||||
(textAdded)="uploadAsText($event)"
|
||||
></app-text-import>
|
||||
</ng-template>
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
|
||||
<!-- Loading spinner -->
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
PLAYLIST_PARSE,
|
||||
PLAYLIST_PARSE_BY_URL,
|
||||
PLAYLIST_PARSE_RESPONSE,
|
||||
PLAYLIST_PARSE_TEXT,
|
||||
PLAYLIST_UPDATE_RESPONSE,
|
||||
} from '../../../shared/ipc-commands';
|
||||
import { Playlist } from '../../../shared/playlist.interface';
|
||||
@@ -154,6 +155,17 @@ export class HomeComponent {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends IPC event to the renderer process to parse playlist
|
||||
* @param text playlist as string
|
||||
*/
|
||||
uploadAsText(text: string): void {
|
||||
this.isLoading = true;
|
||||
this.electronService.sendIpcEvent(PLAYLIST_PARSE_TEXT, {
|
||||
text,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns last segment (part after last slash "/") of the given URL
|
||||
* @param value URL as string
|
||||
|
||||
@@ -6,6 +6,7 @@ import { FileUploadComponent } from './file-upload/file-upload.component';
|
||||
import { HomeComponent } from './home.component';
|
||||
import { HomeRoutingModule } from './home.routing';
|
||||
import { PlaylistInfoComponent } from './recent-playlists/playlist-info/playlist-info.component';
|
||||
import { TextImportComponent } from './text-import/text-import.component';
|
||||
import { UrlUploadComponent } from './url-upload/url-upload.component';
|
||||
|
||||
@NgModule({
|
||||
@@ -14,6 +15,7 @@ import { UrlUploadComponent } from './url-upload/url-upload.component';
|
||||
HomeComponent,
|
||||
FileUploadComponent,
|
||||
PlaylistInfoComponent,
|
||||
TextImportComponent,
|
||||
UrlUploadComponent,
|
||||
],
|
||||
})
|
||||
|
||||
@@ -11,9 +11,11 @@
|
||||
(click)="getPlaylist('GLOBAL_FAVORITES')"
|
||||
>
|
||||
<mat-icon class="favorites-icon" mat-list-icon>star</mat-icon>
|
||||
<div mat-line>Global favorites</div>
|
||||
<div mat-line>
|
||||
{{ 'HOME.PLAYLISTS.GLOBAL_FAVORITES_DESCRIPTION' | translate }}
|
||||
</div>
|
||||
<div mat-line class="meta">
|
||||
Auto-generated playlist with aggregated favorites from all playlists
|
||||
{{ 'HOME.PLAYLISTS.GLOBAL_FAVORITES' | translate }}
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
</mat-list-item>
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<form (ngSubmit)="textAdded.emit(textForm.value.text)" [formGroup]="textForm">
|
||||
<mat-form-field class="full-width" appearance="fill">
|
||||
<mat-label>{{ 'HOME.TEXT_IMPORT.LABEL' | translate }}</mat-label>
|
||||
<textarea
|
||||
formControlName="text"
|
||||
matInput
|
||||
placeholder="#EXTM3U url-tvg=..."
|
||||
></textarea>
|
||||
</mat-form-field>
|
||||
<button
|
||||
mat-flat-button
|
||||
color="primary"
|
||||
type="submit"
|
||||
[disabled]="!textForm.valid"
|
||||
>
|
||||
{{ 'HOME.TEXT_IMPORT.BUTTON_LABEL' | translate }}
|
||||
</button>
|
||||
</form>
|
||||
@@ -0,0 +1,8 @@
|
||||
button {
|
||||
margin: 15px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
textarea {
|
||||
height: calc(100vh - 300px);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
|
||||
import { MockModule, MockPipe } from 'ng-mocks';
|
||||
import { TextImportComponent } from './text-import.component';
|
||||
|
||||
describe('TextImportComponent', () => {
|
||||
let component: TextImportComponent;
|
||||
let fixture: ComponentFixture<TextImportComponent>;
|
||||
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [TextImportComponent, MockPipe(TranslatePipe)],
|
||||
imports: [MockModule(MatInputModule)],
|
||||
}).compileComponents();
|
||||
})
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(TextImportComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
import { Component, EventEmitter, Output } from '@angular/core';
|
||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||
|
||||
@Component({
|
||||
selector: 'app-text-import',
|
||||
templateUrl: './text-import.component.html',
|
||||
styleUrls: ['./text-import.component.scss'],
|
||||
})
|
||||
export class TextImportComponent {
|
||||
@Output() textAdded = new EventEmitter<string>();
|
||||
|
||||
textForm = new FormGroup({
|
||||
text: new FormControl('', Validators.required),
|
||||
});
|
||||
}
|
||||
@@ -28,7 +28,7 @@
|
||||
{{ playlistTitle$ | async }}
|
||||
</ng-container>
|
||||
<ng-template #allPlaylists>
|
||||
My Playlists
|
||||
{{ 'HOME.PLAYLISTS.MY_PLAYLISTS' | translate }}
|
||||
</ng-template>
|
||||
</div>
|
||||
<div class="channels-count">
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
PLAYLIST_PARSE,
|
||||
PLAYLIST_PARSE_BY_URL,
|
||||
PLAYLIST_PARSE_RESPONSE,
|
||||
PLAYLIST_PARSE_TEXT,
|
||||
PLAYLIST_REMOVE_BY_ID,
|
||||
PLAYLIST_REMOVE_BY_ID_RESPONSE,
|
||||
PLAYLIST_SAVE_DETAILS,
|
||||
@@ -150,6 +151,34 @@ export class PwaService extends DataService {
|
||||
payload: playlist,
|
||||
});
|
||||
});
|
||||
} else if (type === PLAYLIST_PARSE_TEXT) {
|
||||
try {
|
||||
const parsedPlaylist = this.parsePlaylist(
|
||||
payload.text.split('\n')
|
||||
);
|
||||
const playlistObject = this.createPlaylistObject(
|
||||
'Imported as text',
|
||||
parsedPlaylist
|
||||
);
|
||||
|
||||
// save to db
|
||||
this.dbService
|
||||
.add(DbStores.Playlists, playlistObject)
|
||||
.subscribe(() => {
|
||||
console.log('playlist was saved to db...');
|
||||
});
|
||||
|
||||
window.postMessage({
|
||||
type: PLAYLIST_PARSE_RESPONSE,
|
||||
payload: playlistObject,
|
||||
});
|
||||
} catch (error) {
|
||||
window.postMessage({
|
||||
type: ERROR,
|
||||
status: '',
|
||||
message: 'Validation error - invalid playlist',
|
||||
});
|
||||
}
|
||||
} else if (type === PLAYLIST_GET_ALL) {
|
||||
this.dbService
|
||||
.getAll(DbStores.Playlists)
|
||||
|
||||
Reference in new issue
Block a user