mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-14 21:26:15 -08:00
refactor(electron): migrate from tauri/plugin-sql to libsql
This commit is contained in:
1 parent
6dcbbadda8
commit
0a934d2ac6
31 files changed
+3458
-752
No files matched your search
@@ -1,8 +1,6 @@
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { Params } from '@angular/router';
|
||||
import { listen } from '@tauri-apps/api/event';
|
||||
import { fetch } from '@tauri-apps/plugin-http';
|
||||
import { DataService } from 'services';
|
||||
import {
|
||||
AUTO_UPDATE_PLAYLISTS,
|
||||
@@ -27,23 +25,7 @@ export class ElectronService extends DataService {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
console.log('Tauri service initialized...');
|
||||
this.setupEventListeners();
|
||||
}
|
||||
|
||||
private async setupEventListeners() {
|
||||
// Listen for player errors
|
||||
/* this.eventListeners['player-error'] = await listen(
|
||||
'player-error',
|
||||
(event) => {
|
||||
console.error('Player error:', event);
|
||||
this.snackBar.open(
|
||||
`Player Error: ${event.payload as string}`,
|
||||
'Close',
|
||||
{ duration: 5000 }
|
||||
);
|
||||
}
|
||||
); */
|
||||
console.log('Electron service initialized...');
|
||||
}
|
||||
|
||||
getAppVersion(): string {
|
||||
@@ -150,42 +132,22 @@ export class ElectronService extends DataService {
|
||||
params: Record<string, string>;
|
||||
}) {
|
||||
try {
|
||||
const url = new URL(payload.url);
|
||||
|
||||
Object.entries(payload.params).forEach(([key, value]) => {
|
||||
url.searchParams.append(key, value);
|
||||
});
|
||||
|
||||
const response = await fetch(url.toString(), {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Cookie: `mac=${payload.macAddress}`,
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Error: ${response.statusText} (Status: ${response.status})`
|
||||
);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
/* window.postMessage({
|
||||
type: 'STALKER_RESPONSE',
|
||||
payload: result,
|
||||
action: payload.params.action,
|
||||
}); */
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
// Use Electron IPC to make the Stalker request
|
||||
const response = await window.electron.stalkerRequest(payload);
|
||||
return response;
|
||||
} catch (err: any) {
|
||||
console.error('Stalker request error:', err);
|
||||
window.postMessage({
|
||||
type: ERROR,
|
||||
message: err.message ?? 'Error: not found',
|
||||
status: err.status ?? 404,
|
||||
});
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
private async fetchM3uPlaylistFromUrl(payload: Partial<Playlist>) {
|
||||
console.log(payload);
|
||||
window.electron.fetchPlaylistByUrl(payload.url).then((result) => {
|
||||
// TODO: call store and decide and store where to store based on the isTemporary flag etc
|
||||
window.postMessage({
|
||||
@@ -255,36 +217,32 @@ export class ElectronService extends DataService {
|
||||
url: string;
|
||||
params: Record<string, string>;
|
||||
}) {
|
||||
let result: any;
|
||||
const url = new URL(`${payload.url}/player_api.php`);
|
||||
Object.entries(payload.params).forEach(([key, value]) => {
|
||||
url.searchParams.append(key, value);
|
||||
});
|
||||
try {
|
||||
// Use Electron IPC to make the Xtream request
|
||||
const response = await window.electron.xtreamRequest(payload);
|
||||
|
||||
const response = await fetch(url.toString());
|
||||
|
||||
const responseBody = await response.json();
|
||||
if (!responseBody) {
|
||||
result = {
|
||||
type: ERROR,
|
||||
status: response.status,
|
||||
message: responseBody.message ?? 'Unknown error',
|
||||
};
|
||||
window.postMessage(result);
|
||||
} else {
|
||||
result = {
|
||||
const result = {
|
||||
type: XTREAM_RESPONSE,
|
||||
payload: responseBody,
|
||||
action: payload.params.action,
|
||||
payload: response.payload,
|
||||
action: response.action,
|
||||
};
|
||||
window.postMessage(result);
|
||||
return result;
|
||||
} catch (error: any) {
|
||||
console.error('Xtream request error:', error);
|
||||
const result = {
|
||||
type: ERROR,
|
||||
status: error.status ?? 500,
|
||||
message: error.message ?? 'Failed to connect to Xtream server',
|
||||
};
|
||||
window.postMessage(result);
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
removeAllListeners(type: string): void {
|
||||
if (type === 'all') {
|
||||
// Unsubscribe from all Tauri events
|
||||
// Unsubscribe from all event listeners
|
||||
Object.values(this.eventListeners).forEach((unsubscribe) =>
|
||||
unsubscribe()
|
||||
);
|
||||
@@ -296,7 +254,6 @@ export class ElectronService extends DataService {
|
||||
}
|
||||
|
||||
// Also remove any window message listeners
|
||||
// Note: This is a bit crude, but works for simple cases
|
||||
window.removeEventListener('message', this.getListenerForCommand(type));
|
||||
}
|
||||
|
||||
@@ -307,20 +264,12 @@ export class ElectronService extends DataService {
|
||||
}
|
||||
|
||||
listenOn(command: string, callback: (...args: any[]) => void): void {
|
||||
if (command.startsWith('tauri:')) {
|
||||
// For Tauri specific events, use the Tauri event system
|
||||
const tauriEvent = command.replace('tauri:', '');
|
||||
listen(tauriEvent, callback).then((unsubscribe) => {
|
||||
this.eventListeners[command] = unsubscribe;
|
||||
});
|
||||
} else {
|
||||
// For backward compatibility, use window messages
|
||||
window.addEventListener('message', callback);
|
||||
}
|
||||
// For Electron, use window message events
|
||||
window.addEventListener('message', callback);
|
||||
}
|
||||
|
||||
getAppEnvironment(): string {
|
||||
return 'tauri';
|
||||
return 'electron';
|
||||
}
|
||||
|
||||
async fetchData(url: string, queryParams: Params) {
|
||||
|
||||
@@ -63,7 +63,8 @@
|
||||
'CHANNELS.REMOVE_FAVORITE' | translate
|
||||
"
|
||||
(click)="
|
||||
$event.stopPropagation(); removeFavorite(item)
|
||||
$event.stopPropagation();
|
||||
removeFromFavorites(item)
|
||||
"
|
||||
>
|
||||
<mat-icon>delete</mat-icon>
|
||||
@@ -78,5 +79,5 @@
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<app-mpv-player-bar />
|
||||
<!-- <app-mpv-player-bar /> -->
|
||||
</div>
|
||||
@@ -6,7 +6,6 @@ import { MatIcon } from '@angular/material/icon';
|
||||
import { MatTooltip } from '@angular/material/tooltip';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
import { CategoryViewComponent } from '../../../xtream-tauri/category-view/category-view.component';
|
||||
import { MpvPlayerBarComponent } from '../mpv-player-bar/mpv-player-bar.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-favorites-layout',
|
||||
@@ -21,7 +20,7 @@ import { MpvPlayerBarComponent } from '../mpv-player-bar/mpv-player-bar.componen
|
||||
MatCardModule,
|
||||
MatIcon,
|
||||
MatIconButton,
|
||||
MpvPlayerBarComponent,
|
||||
/* MpvPlayerBarComponent, */
|
||||
MatTooltip,
|
||||
TranslatePipe,
|
||||
],
|
||||
@@ -33,15 +32,15 @@ export class FavoritesLayoutComponent {
|
||||
readonly titleTranslationString = input<string>('CHANNELS.FAVORITES');
|
||||
|
||||
readonly categoryClicked = output<any>();
|
||||
readonly removeFromFavorites = output<any>();
|
||||
readonly removeFavorite = output<any>();
|
||||
readonly openItem = output<any>();
|
||||
|
||||
setCategoryId(categoryId: any) {
|
||||
this.categoryClicked.emit({ category_id: categoryId });
|
||||
}
|
||||
|
||||
removeFavorite(item: any) {
|
||||
this.removeFromFavorites.emit(item);
|
||||
removeFromFavorites(item: any) {
|
||||
this.removeFavorite.emit(item);
|
||||
}
|
||||
|
||||
openFavorite(item: any) {
|
||||
|
||||
@@ -117,5 +117,5 @@
|
||||
<div class="scrollable-content">
|
||||
<router-outlet />
|
||||
</div>
|
||||
<app-mpv-player-bar />
|
||||
<!-- <app-mpv-player-bar /> -->
|
||||
</div>
|
||||
@@ -8,7 +8,6 @@ import { ActivatedRoute, Router, RouterOutlet } from '@angular/router';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
|
||||
import { PlaylistsService } from 'services';
|
||||
import { MpvPlayerBarComponent } from '../shared/components/mpv-player-bar/mpv-player-bar.component';
|
||||
import { CategoryViewComponent } from '../xtream-tauri/category-view/category-view.component';
|
||||
import { PlaylistErrorViewComponent } from '../xtream/playlist-error-view/playlist-error-view.component';
|
||||
import { StalkerStore } from './stalker.store';
|
||||
@@ -28,7 +27,7 @@ import { StalkerStore } from './stalker.store';
|
||||
MatIconButton,
|
||||
MatListModule,
|
||||
MatPaginatorModule,
|
||||
MpvPlayerBarComponent,
|
||||
/* MpvPlayerBarComponent, */
|
||||
NgxSkeletonLoaderModule,
|
||||
PlaylistErrorViewComponent,
|
||||
TranslatePipe,
|
||||
|
||||
@@ -99,7 +99,7 @@ export class FavoritesComponent implements OnInit {
|
||||
|
||||
async removeFromFavorites(item: any) {
|
||||
await this.favoritesService.removeFromFavorites(
|
||||
item.id,
|
||||
item.content_id,
|
||||
item.playlist_id
|
||||
);
|
||||
// Refresh favorites after removal
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
@let isEmbeddedPlayer = player() === 'videojs' || player() === 'html5';
|
||||
@let isEmbeddedPlayer =
|
||||
player() === 'videojs' || player() === 'html5' || player() === 'artplayer';
|
||||
|
||||
<div class="sidebar">
|
||||
<div class="sidebar-header">
|
||||
@@ -42,5 +43,5 @@
|
||||
{{ 'PORTALS.SELECT_CATEGORY' | translate }}
|
||||
</div>
|
||||
}
|
||||
<app-mpv-player-bar />
|
||||
<!-- <app-mpv-player-bar /> -->
|
||||
</div>
|
||||
@@ -54,3 +54,13 @@
|
||||
font-size: 1.2rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
::ng-deep {
|
||||
.video-player
|
||||
> app-web-player-view
|
||||
> app-art-player
|
||||
> .artplayer-container {
|
||||
height: 100% !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@ import { TranslatePipe } from '@ngx-translate/core';
|
||||
import { XtreamCategory } from 'shared-interfaces';
|
||||
import { EpgViewComponent, WebPlayerViewComponent } from 'shared-portals';
|
||||
import { SettingsStore } from '../../services/settings-store.service';
|
||||
import { MpvPlayerBarComponent } from '../../shared/components/mpv-player-bar/mpv-player-bar.component';
|
||||
import { CategoryViewComponent } from '../category-view/category-view.component';
|
||||
import { PortalChannelsListComponent } from '../portal-channels-list/portal-channels-list.component';
|
||||
import { FavoritesService } from '../services/favorites.service';
|
||||
@@ -34,7 +33,7 @@ import { XtreamStore } from '../xtream.store';
|
||||
MatIconButton,
|
||||
MatInputModule,
|
||||
MatListModule,
|
||||
MpvPlayerBarComponent,
|
||||
/* MpvPlayerBarComponent, */
|
||||
PortalChannelsListComponent,
|
||||
TranslatePipe,
|
||||
WebPlayerViewComponent,
|
||||
|
||||
@@ -6,4 +6,6 @@ export interface FavoriteItem {
|
||||
stream_icon?: string;
|
||||
poster_url?: string;
|
||||
added_at?: string;
|
||||
category_id: number;
|
||||
xtream_id: number;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { from, map, mergeMap, Observable } from 'rxjs';
|
||||
import { from, map, Observable } from 'rxjs';
|
||||
import { DatabaseService } from 'services';
|
||||
import { FavoriteItem } from './favorite-item.interface';
|
||||
|
||||
@@ -13,54 +13,34 @@ export class FavoritesService {
|
||||
content_id: number;
|
||||
playlist_id: string;
|
||||
}): Promise<void> {
|
||||
const db = await this.dbService.getConnection();
|
||||
await db.execute(
|
||||
`INSERT INTO favorites (content_id, playlist_id)
|
||||
VALUES (?, ?)`,
|
||||
[item.content_id, item.playlist_id]
|
||||
);
|
||||
await this.dbService.addToFavorites(item.content_id, item.playlist_id);
|
||||
}
|
||||
|
||||
async removeFromFavorites(
|
||||
contentId: number,
|
||||
playlistId: string
|
||||
): Promise<void> {
|
||||
const db = await this.dbService.getConnection();
|
||||
await db.execute(
|
||||
`DELETE FROM favorites
|
||||
WHERE content_id = ? AND playlist_id = ?`,
|
||||
[contentId, playlistId]
|
||||
);
|
||||
await this.dbService.removeFromFavorites(contentId, playlistId);
|
||||
}
|
||||
|
||||
async isFavorite(contentId: number, playlistId: string): Promise<boolean> {
|
||||
const db = await this.dbService.getConnection();
|
||||
const result = await db.select<any[]>(
|
||||
`SELECT COUNT(*) as count
|
||||
FROM favorites f
|
||||
JOIN content c ON f.content_id = c.id
|
||||
WHERE c.xtream_id = ? AND f.playlist_id = ?`,
|
||||
[contentId, playlistId]
|
||||
);
|
||||
return result[0].count > 0;
|
||||
return await this.dbService.isFavorite(contentId, playlistId);
|
||||
}
|
||||
|
||||
getFavorites(playlistId: string): Observable<FavoriteItem[]> {
|
||||
return from(this.dbService.getConnection()).pipe(
|
||||
mergeMap(async (db) => {
|
||||
return await db.select<FavoriteItem[]>(
|
||||
`SELECT
|
||||
c.*,
|
||||
f.playlist_id,
|
||||
f.added_at
|
||||
FROM favorites f
|
||||
JOIN content c ON f.content_id = c.id
|
||||
WHERE f.playlist_id = ?
|
||||
ORDER BY f.added_at DESC`,
|
||||
[playlistId]
|
||||
);
|
||||
}),
|
||||
map((results) => results)
|
||||
return from(this.dbService.getFavorites(playlistId)).pipe(
|
||||
map((items) =>
|
||||
items.map((item) => ({
|
||||
content_id: item.id,
|
||||
playlist_id: playlistId,
|
||||
type: item.type as 'live' | 'vod' | 'series',
|
||||
title: item.title,
|
||||
poster_url: item.poster_url,
|
||||
added_at: item.added_at,
|
||||
category_id: item.category_id,
|
||||
xtream_id: item.xtream_id,
|
||||
}))
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -20,45 +20,42 @@ export const withFavorites = function () {
|
||||
favoritesService = inject(FavoritesService)
|
||||
) => ({
|
||||
async toggleFavorite(xtreamId: number, playlistId: string) {
|
||||
let result = false;
|
||||
if (!xtreamId || !playlistId) return;
|
||||
|
||||
const db = await dbService.getConnection();
|
||||
|
||||
const content: any = await db.select(
|
||||
'SELECT content.id FROM content ' +
|
||||
'INNER JOIN categories ON content.category_id = categories.id ' +
|
||||
'WHERE content.xtream_id = ? AND categories.playlist_id = ?',
|
||||
[xtreamId, playlistId]
|
||||
);
|
||||
|
||||
if (!content || content.length === 0) {
|
||||
console.error('Content not found in database');
|
||||
return;
|
||||
if (!xtreamId || !playlistId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const contentId = content[0].id;
|
||||
const isFavorite = await favoritesService.isFavorite(
|
||||
// Get content by xtream ID
|
||||
const content = await dbService.getContentByXtreamId(
|
||||
xtreamId,
|
||||
playlistId
|
||||
);
|
||||
|
||||
if (isFavorite) {
|
||||
await favoritesService.removeFromFavorites(
|
||||
contentId,
|
||||
playlistId
|
||||
if (!content) {
|
||||
console.error(
|
||||
'Content not found for xtream ID:',
|
||||
xtreamId
|
||||
);
|
||||
result = false;
|
||||
} else {
|
||||
await favoritesService.addToFavorites({
|
||||
content_id: contentId,
|
||||
playlist_id: playlistId,
|
||||
});
|
||||
result = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
patchState(store, { isFavorite: !isFavorite });
|
||||
return result;
|
||||
const currentStatus = store.isFavorite();
|
||||
|
||||
if (currentStatus) {
|
||||
// Remove from favorites
|
||||
await favoritesService.removeFromFavorites(
|
||||
content.id,
|
||||
playlistId
|
||||
);
|
||||
patchState(store, { isFavorite: false });
|
||||
return false;
|
||||
} else {
|
||||
// Add to favorites
|
||||
await favoritesService.addToFavorites({
|
||||
content_id: content.id,
|
||||
playlist_id: playlistId,
|
||||
});
|
||||
patchState(store, { isFavorite: true });
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
async checkFavoriteStatus(
|
||||
@@ -70,13 +67,18 @@ export const withFavorites = function () {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!xtreamId) {
|
||||
// Get content by xtream ID
|
||||
const content = await dbService.getContentByXtreamId(
|
||||
xtreamId,
|
||||
playlistId
|
||||
);
|
||||
if (!content) {
|
||||
patchState(store, { isFavorite: false });
|
||||
return;
|
||||
}
|
||||
|
||||
const isFavorite = await favoritesService.isFavorite(
|
||||
xtreamId,
|
||||
content.id,
|
||||
playlistId
|
||||
);
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ export interface RecentlyViewedItem {
|
||||
playlist_id: string;
|
||||
viewed_at: string;
|
||||
xtream_id: number;
|
||||
category_id: number;
|
||||
}
|
||||
|
||||
export const withRecentItems = function () {
|
||||
@@ -29,30 +30,21 @@ export const withRecentItems = function () {
|
||||
loadRecentItems: rxMethod<{ id: string }>(
|
||||
pipe(
|
||||
switchMap(async (playlist) => {
|
||||
if (!playlist) return [];
|
||||
console.log(
|
||||
'Loading recent items for playlist',
|
||||
const items = await dbService.getRecentItems(
|
||||
playlist.id
|
||||
);
|
||||
const db = await dbService.getConnection();
|
||||
return db.select<RecentlyViewedItem[]>(
|
||||
`SELECT
|
||||
rv.id,
|
||||
c.title,
|
||||
c.type,
|
||||
c.poster_url,
|
||||
c.id as content_id,
|
||||
rv.playlist_id,
|
||||
rv.viewed_at,
|
||||
c.xtream_id,
|
||||
c.category_id
|
||||
FROM recently_viewed rv
|
||||
JOIN content c ON rv.content_id = c.id
|
||||
WHERE rv.playlist_id = ?
|
||||
ORDER BY rv.viewed_at DESC
|
||||
LIMIT 50`,
|
||||
[playlist.id]
|
||||
);
|
||||
// Map to RecentlyViewedItem format
|
||||
return items.map((item) => ({
|
||||
id: item.id,
|
||||
title: item.title,
|
||||
type: item.type as 'live' | 'movie' | 'series',
|
||||
poster_url: item.poster_url,
|
||||
content_id: item.id,
|
||||
playlist_id: playlist.id,
|
||||
viewed_at: item.viewed_at || '',
|
||||
xtream_id: item.xtream_id,
|
||||
category_id: item.category_id,
|
||||
}));
|
||||
}),
|
||||
tap((items: RecentlyViewedItem[]) =>
|
||||
patchState(store, { recentItems: items })
|
||||
@@ -67,50 +59,16 @@ export const withRecentItems = function () {
|
||||
}>(
|
||||
pipe(
|
||||
switchMap(async ({ contentId, playlist }) => {
|
||||
if (!playlist().id) {
|
||||
console.error('No active playlist found');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(
|
||||
'Adding to recently viewed:',
|
||||
// contentId is actually xtream_id, need to look up the database content.id
|
||||
const content = await dbService.getContentByXtreamId(
|
||||
contentId,
|
||||
playlist().id
|
||||
);
|
||||
|
||||
const db = await dbService.getConnection();
|
||||
|
||||
const content: any = await db.select(
|
||||
'SELECT content.id FROM content ' +
|
||||
'INNER JOIN categories ON content.category_id = categories.id ' +
|
||||
'WHERE content.xtream_id = ? AND categories.playlist_id = ?',
|
||||
[contentId, playlist().id]
|
||||
);
|
||||
|
||||
if (content && content.length > 0) {
|
||||
// Check if item already exists in recently_viewed
|
||||
const existing: any = await db.select(
|
||||
'SELECT recently_viewed.id FROM recently_viewed ' +
|
||||
'INNER JOIN content ON recently_viewed.content_id = content.id ' +
|
||||
'INNER JOIN categories ON content.category_id = categories.id ' +
|
||||
'WHERE content.id = ? AND categories.playlist_id = ?',
|
||||
[content[0].id, playlist().id]
|
||||
if (content) {
|
||||
await dbService.addRecentItem(
|
||||
content.id,
|
||||
playlist().id
|
||||
);
|
||||
|
||||
if (existing && existing.length > 0) {
|
||||
// Update existing record's viewed_at timestamp
|
||||
await db.execute(
|
||||
'UPDATE recently_viewed SET viewed_at = CURRENT_TIMESTAMP WHERE id = ?',
|
||||
[existing[0].id]
|
||||
);
|
||||
} else {
|
||||
// Insert new record
|
||||
await db.execute(
|
||||
`INSERT INTO recently_viewed (content_id, playlist_id)
|
||||
VALUES (?, ?)`,
|
||||
[content[0].id, playlist().id]
|
||||
);
|
||||
}
|
||||
return store.loadRecentItems({ id: playlist().id });
|
||||
}
|
||||
})
|
||||
)
|
||||
@@ -118,28 +76,30 @@ export const withRecentItems = function () {
|
||||
clearRecentItems: rxMethod<{ id: string }>(
|
||||
pipe(
|
||||
switchMap(async (playlist) => {
|
||||
console.log(
|
||||
'Clearing recent items for playlist',
|
||||
playlist.id
|
||||
);
|
||||
const db = await dbService.getConnection();
|
||||
await db.execute(
|
||||
`DELETE FROM recently_viewed WHERE playlist_id = ?`,
|
||||
[playlist.id]
|
||||
);
|
||||
return store.loadRecentItems({ id: playlist.id });
|
||||
await dbService.clearPlaylistRecentItems(playlist.id);
|
||||
patchState(store, { recentItems: [] });
|
||||
})
|
||||
)
|
||||
),
|
||||
removeRecentItem: rxMethod<{ itemId: number; playlistId: string }>(
|
||||
pipe(
|
||||
switchMap(async ({ itemId, playlistId }) => {
|
||||
const db = await dbService.getConnection();
|
||||
await db.execute(
|
||||
`DELETE FROM recently_viewed WHERE id = ? AND playlist_id = ?`,
|
||||
[itemId, playlistId]
|
||||
);
|
||||
return store.loadRecentItems({ id: playlistId });
|
||||
await dbService.removeRecentItem(itemId, playlistId);
|
||||
// Reload recent items to update UI
|
||||
const items =
|
||||
await dbService.getRecentItems(playlistId);
|
||||
const mappedItems = items.map((item) => ({
|
||||
id: item.id,
|
||||
title: item.title,
|
||||
type: item.type as 'live' | 'movie' | 'series',
|
||||
poster_url: item.poster_url,
|
||||
content_id: item.id,
|
||||
playlist_id: playlistId,
|
||||
viewed_at: item.viewed_at || '',
|
||||
xtream_id: item.xtream_id,
|
||||
category_id: item.category_id,
|
||||
}));
|
||||
patchState(store, { recentItems: mappedItems });
|
||||
})
|
||||
)
|
||||
),
|
||||
|
||||
@@ -30,5 +30,5 @@
|
||||
<div class="scrollable-content">
|
||||
<router-outlet />
|
||||
</div>
|
||||
<app-mpv-player-bar />
|
||||
<!-- <app-mpv-player-bar /> -->
|
||||
</div>
|
||||
@@ -4,7 +4,6 @@ import { MatIcon } from '@angular/material/icon';
|
||||
import { ActivatedRoute, Router, RouterOutlet } from '@angular/router';
|
||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||
import { XtreamCategory } from 'shared-interfaces';
|
||||
import { MpvPlayerBarComponent } from '../shared/components/mpv-player-bar/mpv-player-bar.component';
|
||||
import { CategoryViewComponent } from './category-view/category-view.component';
|
||||
import { XtreamStore } from './xtream.store';
|
||||
|
||||
@@ -16,7 +15,7 @@ import { XtreamStore } from './xtream.store';
|
||||
CategoryViewComponent,
|
||||
TranslateModule,
|
||||
RouterOutlet,
|
||||
MpvPlayerBarComponent,
|
||||
/* MpvPlayerBarComponent, */
|
||||
MatIcon,
|
||||
MatIconButton,
|
||||
],
|
||||
|
||||
Reference in new issue
Block a user