Files
iptvnator/libs/shared/m3u-utils/src/lib/playlist.utils.ts
T
4gray 1af287205f perf: optimize playlist favorite lookups
Use Map and Set lookups for M3U favorite aggregation, dashboard global favorite mapping, and channel-list favorite/group resolution.

Remove the duplicate NgxIndexedDB provider registration and add regression coverage for duplicate URL and earliest-match behavior.

Entire-Checkpoint: c2cea9c530e6
2026-05-01 20:44:30 +02:00

105 lines
2.8 KiB
TypeScript

import {
Channel,
ParsedPlaylist,
ParsedPlaylistItem,
Playlist,
} from 'shared-interfaces';
import { v4 as uuidv4 } from 'uuid';
/**
* Aggregates favorite channels as objects from all available playlists
* @param playlists all available playlists
* @returns an array with favorite channels from all playlists
*/
export function aggregateFavoriteChannels(playlists: Playlist[]): Channel[] {
const favorites: Channel[] = [];
for (const playlist of playlists) {
const favoriteIds = new Set(
(playlist.favorites ?? []).filter(
(favorite): favorite is string => typeof favorite === 'string'
)
);
if (favoriteIds.size === 0) {
continue;
}
for (const channel of playlist.playlist?.items ?? []) {
if (favoriteIds.has(channel.id) || favoriteIds.has(channel.url)) {
favorites.push(channel);
}
}
}
return favorites;
}
/**
* Creates a simplified playlist object which is used for global favorites
* @param channels channels list
* @returns simplified playlist object
*/
export function createFavoritesPlaylist(
channels: Channel[]
): Partial<Playlist> {
return {
_id: 'global-favorites',
count: channels.length,
playlist: {
items: channels,
},
favorites: channels.map((channel) => channel.url),
filename: 'Global favorites',
};
}
/**
* Returns last segment (part after last slash "/") of the given URL
* @param value URL as string
*/
export const getFilenameFromUrl = (value: string): string => {
if (value && value.length > 1) {
return value.substring(value.lastIndexOf('/') + 1);
}
return 'Untitled playlist';
};
/**
* Creates a playlist object
* @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
*/
export const createPlaylistObject = (
name: string,
playlist: ParsedPlaylist,
urlOrPath?: string,
uploadType?: 'URL' | 'FILE' | 'TEXT'
): Playlist => {
return {
_id: uuidv4(),
filename: name,
title: name,
count: playlist.items.length,
playlist: {
...playlist,
items: playlist.items.map((item: ParsedPlaylistItem) => ({
...item,
id: uuidv4(),
})),
},
importDate: new Date().toISOString(),
lastUsage: new Date().toISOString(),
favorites: [],
autoRefresh: false,
...(uploadType === 'URL' ? { url: urlOrPath } : {}),
...(uploadType === 'FILE' ? { filePath: urlOrPath } : {}),
};
};
export const getExtensionFromUrl = (url: string) => {
return url.split(/[#?]/)[0].split('.').pop()?.trim();
};