mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-12 20:16:15 -08:00
fix(ui): correct imports, guard checks, logging and settings handling
This commit is contained in:
1 parent
b56519da4b
commit
9be8dce961
21 files changed
+238
-213
No files matched your search
@@ -20,10 +20,7 @@ import moment from 'moment';
|
||||
import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { DataService, EpgService } from 'services';
|
||||
import {
|
||||
EpgChannel,
|
||||
EpgProgram,
|
||||
} from 'shared-interfaces';
|
||||
import { EpgChannel, EpgProgram } from 'shared-interfaces';
|
||||
import { EpgListItemComponent } from './epg-list-item/epg-list-item.component';
|
||||
|
||||
export interface EpgData {
|
||||
@@ -164,12 +161,12 @@ export class EpgListComponent implements OnInit {
|
||||
handleEpgData(programs: EpgProgram[]): void {
|
||||
this.timeNow = new Date().toISOString();
|
||||
this.dateToday = moment().format(DATE_FORMAT);
|
||||
|
||||
|
||||
// Dispatch EPG availability flag
|
||||
this.store.dispatch(
|
||||
setEpgAvailableFlag({ value: programs.length > 0 })
|
||||
);
|
||||
|
||||
|
||||
if (programs.length > 0) {
|
||||
this.setPlayingNow();
|
||||
} else {
|
||||
|
||||
@@ -88,11 +88,7 @@ export class HtmlVideoPlayerComponent implements OnInit, OnChanges, OnDestroy {
|
||||
Hls &&
|
||||
Hls.isSupported()
|
||||
) {
|
||||
console.log(
|
||||
'... switching channel to ',
|
||||
channel.name,
|
||||
url
|
||||
);
|
||||
console.log('... switching channel to ', channel.name, url);
|
||||
this.hls = new Hls();
|
||||
this.hls.attachMedia(this.videoPlayer.nativeElement);
|
||||
this.hls.loadSource(url);
|
||||
|
||||
@@ -33,7 +33,7 @@ export class InfoOverlayComponent implements OnChanges {
|
||||
stop: any | undefined;
|
||||
|
||||
/** Timeout for the overlay visibility */
|
||||
currentTimeout: any;
|
||||
private currentTimeout: any;
|
||||
|
||||
/**
|
||||
* Calculates the necessary information for the visualization in the overview popup
|
||||
|
||||
@@ -159,17 +159,12 @@ export class MultiEpgContainerComponent
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('Requesting EPG data:');
|
||||
console.log('- Skip:', this.channelsLowerRange);
|
||||
console.log('- Limit:', this.visibleChannels);
|
||||
|
||||
const response = await window.electron.getEpgChannelsByRange(
|
||||
this.channelsLowerRange,
|
||||
this.visibleChannels
|
||||
);
|
||||
|
||||
if (response && Array.isArray(response)) {
|
||||
console.log('Received channels:', response.length);
|
||||
this.originalEpgData = response;
|
||||
this.channels$.next(this.enrichProgramData());
|
||||
|
||||
|
||||
+11
-13
@@ -82,7 +82,7 @@
|
||||
<input matInput formControlName="count" />
|
||||
</mat-form-field>
|
||||
}
|
||||
@if (isTauri) {
|
||||
@if (isDesktop) {
|
||||
<mat-form-field class="full-width mb-10">
|
||||
<mat-label>{{
|
||||
'HOME.PLAYLISTS.INFO_DIALOG.USER_AGENT' | translate
|
||||
@@ -109,18 +109,16 @@
|
||||
}
|
||||
</mat-form-field>
|
||||
}
|
||||
@if (isTauri && (playlist.url || playlist.filePath)) {
|
||||
<ng-container>
|
||||
<mat-checkbox class="full-width" formControlName="autoRefresh">
|
||||
{{ 'HOME.PLAYLISTS.INFO_DIALOG.AUTO_UPDATE' | translate }}
|
||||
</mat-checkbox>
|
||||
<p>
|
||||
{{
|
||||
'HOME.PLAYLISTS.INFO_DIALOG.AUTO_UPDATE_DESCRIPTION'
|
||||
| translate
|
||||
}}
|
||||
</p>
|
||||
</ng-container>
|
||||
@if (isDesktop && (playlist.url || playlist.filePath)) {
|
||||
<mat-checkbox class="full-width" formControlName="autoRefresh">
|
||||
{{ 'HOME.PLAYLISTS.INFO_DIALOG.AUTO_UPDATE' | translate }}
|
||||
</mat-checkbox>
|
||||
<p>
|
||||
{{
|
||||
'HOME.PLAYLISTS.INFO_DIALOG.AUTO_UPDATE_DESCRIPTION'
|
||||
| translate
|
||||
}}
|
||||
</p>
|
||||
}
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions align="end">
|
||||
|
||||
+14
-18
@@ -1,5 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/unbound-method */
|
||||
import { CommonModule, DatePipe } from '@angular/common';
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { Component, inject } from '@angular/core';
|
||||
import {
|
||||
FormControl,
|
||||
@@ -8,16 +7,14 @@ import {
|
||||
UntypedFormGroup,
|
||||
Validators,
|
||||
} from '@angular/forms';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatButton } from '@angular/material/button';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatIcon } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { TranslatePipe, TranslateService } from '@ngx-translate/core';
|
||||
import { save } from '@tauri-apps/plugin-dialog';
|
||||
import { writeTextFile } from '@tauri-apps/plugin-fs';
|
||||
import * as PlaylistActions from 'm3u-state';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { DatabaseService, PlaylistsService } from 'services';
|
||||
@@ -35,11 +32,10 @@ import { Playlist, PlaylistMeta } from 'shared-interfaces';
|
||||
],
|
||||
providers: [DatePipe],
|
||||
imports: [
|
||||
CommonModule,
|
||||
MatButtonModule,
|
||||
MatButton,
|
||||
MatCheckboxModule,
|
||||
MatDialogModule,
|
||||
MatIconModule,
|
||||
MatIcon,
|
||||
MatInputModule,
|
||||
ReactiveFormsModule,
|
||||
TranslatePipe,
|
||||
@@ -55,7 +51,7 @@ export class PlaylistInfoComponent {
|
||||
private translate = inject(TranslateService);
|
||||
public playlistData = inject<Playlist & { id: string }>(MAT_DIALOG_DATA);
|
||||
|
||||
isTauri = !!window.electron;
|
||||
readonly isDesktop = !!window.electron;
|
||||
|
||||
/** Playlist object */
|
||||
playlist: Playlist & { id: string };
|
||||
@@ -168,20 +164,20 @@ export class PlaylistInfoComponent {
|
||||
this.playlistsService.getRawPlaylistById(this.playlist._id)
|
||||
);
|
||||
|
||||
if (this.isTauri) {
|
||||
if (this.isDesktop) {
|
||||
try {
|
||||
const savePath = await save({
|
||||
filters: [
|
||||
const savePath = await window.electron.saveFileDialog(
|
||||
`${this.playlist.title || 'exported'}.m3u8`,
|
||||
[
|
||||
{
|
||||
name: 'Playlist',
|
||||
extensions: ['m3u8'],
|
||||
extensions: ['m3u8', 'm3u'],
|
||||
},
|
||||
],
|
||||
defaultPath: `${this.playlist.title || 'exported'}.m3u8`,
|
||||
});
|
||||
]
|
||||
);
|
||||
|
||||
if (savePath) {
|
||||
await writeTextFile(savePath, playlistAsString);
|
||||
await window.electron.writeFile(savePath, playlistAsString);
|
||||
this.snackBar.open(
|
||||
this.translate.instant(
|
||||
'HOME.PLAYLISTS.INFO_DIALOG.PLAYLIST_EXPORT_SUCCESS'
|
||||
|
||||
+102
-89
@@ -1,56 +1,59 @@
|
||||
<mat-list-item (click)="playlistClicked.emit(item._id)">
|
||||
<mat-icon
|
||||
matListItemIcon
|
||||
class="upload-type-icon"
|
||||
*ngIf="item?.url"
|
||||
[matTooltip]="
|
||||
('HOME.PLAYLISTS.ADDED_VIA_URL' | translate) + ' ' + item?.url
|
||||
"
|
||||
>cloud</mat-icon
|
||||
>
|
||||
<mat-icon
|
||||
matListItemIcon
|
||||
class="upload-type-icon"
|
||||
*ngIf="!item?.url && !item.serverUrl && !item.macAddress"
|
||||
[matTooltip]="'HOME.PLAYLISTS.ADDED_VIA_FILE' | translate"
|
||||
>folder</mat-icon
|
||||
>
|
||||
<div class="icon-container" *ngIf="item?.serverUrl" matListItemIcon>
|
||||
@if (item.url) {
|
||||
<mat-icon
|
||||
matListItemIcon
|
||||
class="upload-type-icon"
|
||||
[matTooltip]="'HOME.PLAYLISTS.XTREAM_PLAYLIST' | translate"
|
||||
>public</mat-icon
|
||||
[matTooltip]="
|
||||
('HOME.PLAYLISTS.ADDED_VIA_URL' | translate) + ' ' + item.url
|
||||
"
|
||||
>cloud</mat-icon
|
||||
>
|
||||
<span
|
||||
class="status-dot"
|
||||
[class]="getStatusClass()"
|
||||
[matTooltip]="'Portal status: ' + portalStatus"
|
||||
></span>
|
||||
</div>
|
||||
<mat-icon
|
||||
matListItemIcon
|
||||
class="upload-type-icon"
|
||||
*ngIf="item?.macAddress"
|
||||
[matTooltip]="'HOME.PLAYLISTS.STALKER_PORTAL' | translate"
|
||||
>dashboard</mat-icon
|
||||
>
|
||||
}
|
||||
@if (!item.url && !item.serverUrl && !item.macAddress) {
|
||||
<mat-icon
|
||||
matListItemIcon
|
||||
class="upload-type-icon"
|
||||
[matTooltip]="'HOME.PLAYLISTS.ADDED_VIA_FILE' | translate"
|
||||
>folder</mat-icon
|
||||
>
|
||||
}
|
||||
@if (item.serverUrl) {
|
||||
<div class="icon-container" matListItemIcon>
|
||||
<mat-icon
|
||||
class="upload-type-icon"
|
||||
[matTooltip]="'HOME.PLAYLISTS.XTREAM_PLAYLIST' | translate"
|
||||
>public</mat-icon
|
||||
>
|
||||
<span
|
||||
class="status-dot"
|
||||
[class]="getStatusClass()"
|
||||
[matTooltip]="'Portal status: ' + portalStatus"
|
||||
></span>
|
||||
</div>
|
||||
}
|
||||
@if (item.macAddress) {
|
||||
<mat-icon
|
||||
matListItemIcon
|
||||
class="upload-type-icon"
|
||||
[matTooltip]="'HOME.PLAYLISTS.STALKER_PORTAL' | translate"
|
||||
>dashboard</mat-icon
|
||||
>
|
||||
}
|
||||
<div matListItemTitle>
|
||||
{{ item.title || item.filename }}
|
||||
</div>
|
||||
<div matListItemLine class="meta">
|
||||
<ng-container
|
||||
*ngIf="!item.serverUrl && !item.portalUrl; else xtreamPlaylist"
|
||||
>
|
||||
@if (!item.serverUrl && !item.portalUrl) {
|
||||
{{ 'HOME.PLAYLISTS.CHANNELS' | translate }}: {{ item.count }}
|
||||
<ng-container *ngIf="item.importDate?.length > 3">
|
||||
@if (item.importDate.length > 3) {
|
||||
| {{ 'HOME.PLAYLISTS.ADDED' | translate }}:
|
||||
{{ item.importDate | date }}
|
||||
</ng-container>
|
||||
<ng-container *ngIf="item.updateDate">
|
||||
}
|
||||
@if (item.updateDate) {
|
||||
| {{ 'HOME.PLAYLISTS.UPDATED' | translate }}:
|
||||
{{ item.updateDate | date: 'MMMM d, yyyy, HH:mm' }}
|
||||
</ng-container>
|
||||
<ng-container *ngIf="item.updateState === 2">
|
||||
}
|
||||
@if (item.updateState === 2) {
|
||||
<mat-icon
|
||||
[matTooltip]="
|
||||
'HOME.PLAYLISTS.INFO_DIALOG.UPDATE_FAILED' | translate
|
||||
@@ -58,64 +61,74 @@
|
||||
[inline]="true"
|
||||
>warning</mat-icon
|
||||
>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
<ng-template #xtreamPlaylist>
|
||||
@if (item?.macAddress) {
|
||||
}
|
||||
} @else {
|
||||
@if (item.macAddress) {
|
||||
Stalker Portal
|
||||
} @else {
|
||||
Xtream Code Playlist
|
||||
}
|
||||
<ng-container *ngIf="item.importDate?.length > 3">
|
||||
@if (item.importDate.length > 3) {
|
||||
| {{ 'HOME.PLAYLISTS.ADDED' | translate }}:
|
||||
{{ item.importDate | date }}
|
||||
</ng-container>
|
||||
<ng-container *ngIf="item.updateDate">
|
||||
}
|
||||
@if (item.updateDate) {
|
||||
| {{ 'HOME.PLAYLISTS.UPDATED' | translate }}:
|
||||
{{ item.updateDate | date: 'MMMM d, yyyy, HH:mm' }}
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
||||
<span matListItemMeta class="action-buttons" *ngIf="showActions">
|
||||
<button
|
||||
*ngIf="item.url || item.filePath"
|
||||
mat-icon-button
|
||||
class="refresh-btn"
|
||||
color="accent"
|
||||
(click)="$event.stopPropagation(); refreshClicked.emit(item)"
|
||||
[matTooltip]="'HOME.PLAYLISTS.REFRESH' | translate"
|
||||
>
|
||||
<mat-icon>sync</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
*ngIf="item.serverUrl"
|
||||
mat-icon-button
|
||||
class="refresh-btn"
|
||||
color="accent"
|
||||
(click)="$event.stopPropagation(); refreshClicked.emit(item)"
|
||||
[matTooltip]="'HOME.PLAYLISTS.REFRESH_XTREAM' | translate"
|
||||
>
|
||||
<mat-icon>sync</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
mat-icon-button
|
||||
color="accent"
|
||||
class="edit-btn"
|
||||
(click)="$event.stopPropagation(); editPlaylistClicked.emit(item)"
|
||||
[matTooltip]="'HOME.PLAYLISTS.SHOW_DETAILS' | translate"
|
||||
>
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
@if (showActions()) {
|
||||
<span matListItemMeta class="action-buttons">
|
||||
@if (item.url || item.filePath) {
|
||||
<button
|
||||
mat-icon-button
|
||||
class="refresh-btn"
|
||||
color="accent"
|
||||
(click)="
|
||||
$event.stopPropagation(); refreshClicked.emit(item)
|
||||
"
|
||||
[matTooltip]="'HOME.PLAYLISTS.REFRESH' | translate"
|
||||
>
|
||||
<mat-icon>sync</mat-icon>
|
||||
</button>
|
||||
}
|
||||
@if (item.serverUrl) {
|
||||
<button
|
||||
mat-icon-button
|
||||
class="refresh-btn"
|
||||
color="accent"
|
||||
(click)="
|
||||
$event.stopPropagation(); refreshClicked.emit(item)
|
||||
"
|
||||
[matTooltip]="'HOME.PLAYLISTS.REFRESH_XTREAM' | translate"
|
||||
>
|
||||
<mat-icon>sync</mat-icon>
|
||||
</button>
|
||||
}
|
||||
|
||||
<button
|
||||
mat-icon-button
|
||||
color="accent"
|
||||
class="delete-btn"
|
||||
(click)="$event.stopPropagation(); removeClicked.emit(item._id)"
|
||||
[matTooltip]="'HOME.PLAYLISTS.REMOVE' | translate"
|
||||
>
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</span>
|
||||
<button
|
||||
mat-icon-button
|
||||
color="accent"
|
||||
class="edit-btn"
|
||||
(click)="
|
||||
$event.stopPropagation(); editPlaylistClicked.emit(item)
|
||||
"
|
||||
[matTooltip]="'HOME.PLAYLISTS.SHOW_DETAILS' | translate"
|
||||
>
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
|
||||
<button
|
||||
mat-icon-button
|
||||
color="accent"
|
||||
class="delete-btn"
|
||||
(click)="$event.stopPropagation(); removeClicked.emit(item._id)"
|
||||
[matTooltip]="'HOME.PLAYLISTS.REMOVE' | translate"
|
||||
>
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</span>
|
||||
}
|
||||
</mat-list-item>
|
||||
+8
-2
@@ -4,7 +4,7 @@ import { MatListModule } from '@angular/material/list';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { MockModule } from 'ng-mocks';
|
||||
import { DataService } from '../../../../../../services/src/lib/data.service';
|
||||
import { DataService } from 'services';
|
||||
import { PlaylistItemComponent } from './playlist-item.component';
|
||||
|
||||
describe('PlaylistItemComponent', () => {
|
||||
@@ -27,7 +27,13 @@ describe('PlaylistItemComponent', () => {
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PlaylistItemComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.item = { title: 'Playlist', id: '1' } as any;
|
||||
component.item = {
|
||||
title: 'Playlist',
|
||||
_id: '1',
|
||||
count: 10,
|
||||
importDate: Date.now().toString(),
|
||||
autoRefresh: false,
|
||||
};
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
|
||||
+21
-17
@@ -1,9 +1,9 @@
|
||||
import { DatePipe, NgIf } from '@angular/common';
|
||||
import { Component, Input, OnInit, inject, output } from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { Component, Input, OnInit, inject, input, output } from '@angular/core';
|
||||
import { MatIconButton } from '@angular/material/button';
|
||||
import { MatIcon } from '@angular/material/icon';
|
||||
import { MatListModule } from '@angular/material/list';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { MatTooltip } from '@angular/material/tooltip';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
import { PortalStatus, PortalStatusService } from 'services';
|
||||
import { PlaylistMeta } from 'shared-interfaces';
|
||||
@@ -14,17 +14,16 @@ import { PlaylistMeta } from 'shared-interfaces';
|
||||
styleUrls: ['./playlist-item.component.scss'],
|
||||
imports: [
|
||||
DatePipe,
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
MatIconButton,
|
||||
MatIcon,
|
||||
MatListModule,
|
||||
MatTooltipModule,
|
||||
NgIf,
|
||||
MatTooltip,
|
||||
TranslatePipe,
|
||||
],
|
||||
})
|
||||
export class PlaylistItemComponent implements OnInit {
|
||||
@Input() item!: PlaylistMeta;
|
||||
@Input() showActions = true;
|
||||
readonly showActions = input(true);
|
||||
|
||||
readonly editPlaylistClicked = output<PlaylistMeta>();
|
||||
readonly playlistClicked = output<string>();
|
||||
@@ -34,7 +33,6 @@ export class PlaylistItemComponent implements OnInit {
|
||||
portalStatus: PortalStatus = 'unavailable';
|
||||
private readonly portalStatusService = inject(PortalStatusService);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
async ngOnInit() {
|
||||
if (this.item?.serverUrl) {
|
||||
await this.checkPortalStatus();
|
||||
@@ -43,12 +41,18 @@ export class PlaylistItemComponent implements OnInit {
|
||||
|
||||
private async checkPortalStatus() {
|
||||
try {
|
||||
this.portalStatus =
|
||||
await this.portalStatusService.checkPortalStatus(
|
||||
this.item.serverUrl,
|
||||
this.item.username,
|
||||
this.item.password
|
||||
);
|
||||
if (
|
||||
this.item.serverUrl &&
|
||||
this.item.username &&
|
||||
this.item.password
|
||||
) {
|
||||
this.portalStatus =
|
||||
await this.portalStatusService.checkPortalStatus(
|
||||
this.item.serverUrl,
|
||||
this.item.username,
|
||||
this.item.password
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error checking portal status:', error);
|
||||
this.portalStatus = 'unavailable';
|
||||
|
||||
@@ -10,10 +10,8 @@ import {
|
||||
Output,
|
||||
viewChild,
|
||||
} from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { MatDividerModule } from '@angular/material/divider';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatIcon } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatListModule } from '@angular/material/list';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
@@ -47,9 +45,7 @@ import { PlaylistItemComponent } from './playlist-item/playlist-item.component';
|
||||
styleUrls: ['./recent-playlists.component.scss'],
|
||||
imports: [
|
||||
AsyncPipe,
|
||||
MatButtonModule,
|
||||
MatDividerModule,
|
||||
MatIconModule,
|
||||
MatIcon,
|
||||
MatInputModule,
|
||||
MatListModule,
|
||||
NgxSkeletonLoaderComponent,
|
||||
@@ -75,7 +71,7 @@ export class RecentPlaylistsComponent implements OnInit {
|
||||
|
||||
readonly ghostElements = new Array(10);
|
||||
|
||||
playlists$ = combineLatest([
|
||||
readonly playlists$ = combineLatest([
|
||||
this.store.select(selectAllPlaylistsMeta),
|
||||
this.searchQuery,
|
||||
this.store.select(selectActiveTypeFilters),
|
||||
|
||||
@@ -13,7 +13,7 @@ export class MomentDatePipe implements PipeTransform {
|
||||
value: string,
|
||||
formatToParse: string,
|
||||
formatToReturn = 'MMMM Do, dddd'
|
||||
): any {
|
||||
): string {
|
||||
return moment(value, formatToParse).format(formatToReturn);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user