mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 17:06:15 -08:00
fix(m3u): use custom User-Agent for URL import and refresh (#1535)
This commit is contained in:
1 parent
79f3f6c897
commit
0ba5107561
34 files changed
+508
-27
No files matched your search
@@ -217,6 +217,36 @@ describe('playlistReducers', () => {
|
||||
expect(nextState.channelsLoading).toBe(false);
|
||||
});
|
||||
|
||||
it.each([undefined, '', 'Replacement/2.0'])(
|
||||
'preserves the saved User-Agent unless refresh explicitly replaces it: %s',
|
||||
(userAgent) => {
|
||||
const existing = {
|
||||
_id: 'playlist-1',
|
||||
userAgent: 'IPTVnator-Test/1.0',
|
||||
} as PlaylistMeta;
|
||||
const state = {
|
||||
...initialState,
|
||||
playlists: playlistsAdapter.addOne(
|
||||
existing,
|
||||
initialState.playlists
|
||||
),
|
||||
};
|
||||
const nextState = reducer(
|
||||
state,
|
||||
PlaylistActions.updatePlaylist({
|
||||
playlistId: existing._id,
|
||||
playlist: {
|
||||
playlist: { items: [] },
|
||||
...(userAgent !== undefined ? { userAgent } : {}),
|
||||
} as Playlist,
|
||||
})
|
||||
);
|
||||
expect(nextState.playlists.entities[existing._id]?.userAgent).toBe(
|
||||
userAgent ?? existing.userAgent
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
it('keeps hiddenGroupTitles on playlist refresh when the refreshed payload omits them', () => {
|
||||
const existingPlaylist: PlaylistMeta = {
|
||||
_id: 'playlist-1',
|
||||
|
||||
@@ -60,7 +60,9 @@ export const playlistReducers = [
|
||||
_id: action.playlistId,
|
||||
updateDate: Date.now(),
|
||||
count: action.playlist.playlist.items.length,
|
||||
userAgent: action.playlist.userAgent,
|
||||
userAgent:
|
||||
action.playlist.userAgent ??
|
||||
currentPlaylist?.userAgent,
|
||||
favorites: currentPlaylist?.favorites ?? [],
|
||||
epgUrls: epgSourceState.epgUrls,
|
||||
detectedEpgUrls: epgSourceState.detectedEpgUrls,
|
||||
|
||||
+3
@@ -72,6 +72,7 @@ describe('AddPlaylistDialogComponent', () => {
|
||||
form: {
|
||||
getRawValue: () => ({
|
||||
playlistName: ' My Playlist ',
|
||||
userAgent: ' IPTVnator-Test/1.0 ',
|
||||
playlistUrl: ' https://example.com/list.m3u ',
|
||||
}),
|
||||
},
|
||||
@@ -83,6 +84,7 @@ describe('AddPlaylistDialogComponent', () => {
|
||||
PLAYLIST_PARSE_BY_URL,
|
||||
{
|
||||
title: 'My Playlist',
|
||||
userAgent: 'IPTVnator-Test/1.0',
|
||||
url: 'https://example.com/list.m3u',
|
||||
}
|
||||
);
|
||||
@@ -94,6 +96,7 @@ describe('AddPlaylistDialogComponent', () => {
|
||||
form: {
|
||||
getRawValue: () => ({
|
||||
playlistName: ' ',
|
||||
userAgent: ' ',
|
||||
playlistUrl: 'https://example.com/list.m3u',
|
||||
}),
|
||||
},
|
||||
|
||||
+3
@@ -278,9 +278,12 @@ export class AddPlaylistDialogComponent {
|
||||
formValue?.playlistName
|
||||
);
|
||||
|
||||
const userAgent = this.normalizeOptionalValue(formValue?.userAgent);
|
||||
|
||||
this.dataService.sendIpcEvent(PLAYLIST_PARSE_BY_URL, {
|
||||
url: playlistUrl,
|
||||
...(playlistName ? { title: playlistName } : {}),
|
||||
...(userAgent ? { userAgent } : {}),
|
||||
});
|
||||
this.closeDialog();
|
||||
}
|
||||
|
||||
@@ -7,6 +7,15 @@
|
||||
<mat-label>{{ 'HOME.XTREAM_PLAYLIST.TITLE' | translate }}</mat-label>
|
||||
<input type="text" matInput formControlName="playlistName" />
|
||||
</mat-form-field>
|
||||
<mat-form-field class="w-full">
|
||||
<mat-label>{{
|
||||
'HOME.PLAYLISTS.INFO_DIALOG.USER_AGENT' | translate
|
||||
}}</mat-label>
|
||||
<input type="text" matInput formControlName="userAgent" />
|
||||
<mat-hint>{{
|
||||
'HOME.PLAYLISTS.INFO_DIALOG.CUSTOM_USER_AGENT' | translate
|
||||
}}</mat-hint>
|
||||
</mat-form-field>
|
||||
@if (!isDesktop) {
|
||||
<p class="cors-note">
|
||||
{{ 'HOME.URL_UPLOAD.CORS_NOTE' | translate }}
|
||||
|
||||
@@ -67,6 +67,7 @@ describe('UrlUploadComponent', () => {
|
||||
const testUrl = 'http://example.org/playlist.m3u';
|
||||
|
||||
component.form.setValue({
|
||||
userAgent: '',
|
||||
playlistName: ' Custom Playlist ',
|
||||
playlistUrl: 'wrong url here',
|
||||
});
|
||||
@@ -74,6 +75,7 @@ describe('UrlUploadComponent', () => {
|
||||
expect(component.form.valid).toBeFalsy();
|
||||
|
||||
component.form.setValue({
|
||||
userAgent: '',
|
||||
playlistName: '',
|
||||
playlistUrl: testUrl,
|
||||
});
|
||||
@@ -81,6 +83,7 @@ describe('UrlUploadComponent', () => {
|
||||
expect(component.form.valid).toBeTruthy();
|
||||
|
||||
component.form.setValue({
|
||||
userAgent: '',
|
||||
playlistName: ' ',
|
||||
playlistUrl: testUrl,
|
||||
});
|
||||
@@ -90,6 +93,7 @@ describe('UrlUploadComponent', () => {
|
||||
|
||||
it('clears the url playlist form', () => {
|
||||
component.form.setValue({
|
||||
userAgent: 'IPTVnator-Test/1.0',
|
||||
playlistName: 'News',
|
||||
playlistUrl: 'http://example.org/playlist.m3u',
|
||||
});
|
||||
@@ -100,6 +104,7 @@ describe('UrlUploadComponent', () => {
|
||||
expect(component.form.getRawValue()).toEqual({
|
||||
playlistName: '',
|
||||
playlistUrl: '',
|
||||
userAgent: '',
|
||||
});
|
||||
expect(component.form.pristine).toBeTruthy();
|
||||
});
|
||||
|
||||
@@ -28,6 +28,7 @@ export class UrlUploadComponent implements OnInit {
|
||||
form!: FormGroup<{
|
||||
playlistName: FormControl<string>;
|
||||
playlistUrl: FormControl<string>;
|
||||
userAgent: FormControl<string>;
|
||||
}>;
|
||||
readonly isDesktop = this.runtime.isElectron;
|
||||
|
||||
@@ -39,6 +40,7 @@ export class UrlUploadComponent implements OnInit {
|
||||
[Validators.required, Validators.pattern(urlRegex)],
|
||||
],
|
||||
playlistName: [''],
|
||||
userAgent: [''],
|
||||
});
|
||||
}
|
||||
|
||||
@@ -46,6 +48,7 @@ export class UrlUploadComponent implements OnInit {
|
||||
this.form.reset({
|
||||
playlistName: '',
|
||||
playlistUrl: '',
|
||||
userAgent: '',
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -306,6 +306,7 @@ describe('PlaylistRefreshActionService', () => {
|
||||
username: undefined,
|
||||
password: undefined,
|
||||
url: 'https://example.com/playlist.m3u',
|
||||
userAgent: 'IPTVnator-Test/1.0',
|
||||
});
|
||||
|
||||
service.refresh(playlist);
|
||||
@@ -314,6 +315,7 @@ describe('PlaylistRefreshActionService', () => {
|
||||
id: 'playlist-url',
|
||||
title: 'URL playlist',
|
||||
url: 'https://example.com/playlist.m3u',
|
||||
userAgent: 'IPTVnator-Test/1.0',
|
||||
});
|
||||
expect(playlistRefreshService.refreshPlaylist).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -326,6 +328,7 @@ describe('PlaylistRefreshActionService', () => {
|
||||
username: undefined,
|
||||
password: undefined,
|
||||
url: 'https://playlist.local/list.m3u',
|
||||
userAgent: 'IPTVnator-Test/1.0',
|
||||
});
|
||||
playlistRefreshService.refreshPlaylist.mockResolvedValue({
|
||||
_id: playlist._id,
|
||||
@@ -338,6 +341,7 @@ describe('PlaylistRefreshActionService', () => {
|
||||
expect(playlistRefreshService.refreshPlaylist).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
url: 'https://playlist.local/list.m3u',
|
||||
userAgent: 'IPTVnator-Test/1.0',
|
||||
trustedInsecureTlsHosts: ['playlist.local'],
|
||||
})
|
||||
);
|
||||
|
||||
@@ -89,6 +89,9 @@ export class PlaylistRefreshActionService {
|
||||
id: playlist._id,
|
||||
title: playlist.title,
|
||||
url: playlist.url,
|
||||
...(playlist.userAgent
|
||||
? { userAgent: playlist.userAgent }
|
||||
: {}),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -150,6 +153,7 @@ export class PlaylistRefreshActionService {
|
||||
playlistId: item._id,
|
||||
title: item.title,
|
||||
url: item.url,
|
||||
...(item.userAgent ? { userAgent: item.userAgent } : {}),
|
||||
filePath: item.filePath,
|
||||
trustedInsecureTlsHosts:
|
||||
this.settingsStore.getTrustOptions()
|
||||
|
||||
+1
-1
@@ -152,7 +152,7 @@
|
||||
<input matInput formControlName="count" />
|
||||
</mat-form-field>
|
||||
}
|
||||
@if (isDesktop) {
|
||||
@if (isDesktop || playlist.url) {
|
||||
<mat-form-field class="w-full mb-10">
|
||||
<mat-label>{{
|
||||
'HOME.PLAYLISTS.INFO_DIALOG.USER_AGENT' | translate
|
||||
|
||||
@@ -619,6 +619,7 @@ describe('RecentPlaylistsComponent busy state', () => {
|
||||
password: undefined,
|
||||
filePath: undefined,
|
||||
url: 'https://example.com/test.m3u',
|
||||
userAgent: 'IPTVnator-Test/1.0',
|
||||
});
|
||||
|
||||
component.refreshPlaylist(item);
|
||||
@@ -627,6 +628,7 @@ describe('RecentPlaylistsComponent busy state', () => {
|
||||
id: item._id,
|
||||
title: item.title,
|
||||
url: item.url,
|
||||
userAgent: item.userAgent,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -658,6 +660,7 @@ describe('RecentPlaylistsComponent busy state', () => {
|
||||
username: undefined,
|
||||
password: undefined,
|
||||
filePath: '/tmp/test.m3u',
|
||||
userAgent: 'IPTVnator-Test/1.0',
|
||||
});
|
||||
|
||||
lateComponent.refreshPlaylist(item);
|
||||
@@ -668,6 +671,7 @@ describe('RecentPlaylistsComponent busy state', () => {
|
||||
playlistId: item._id,
|
||||
title: item.title,
|
||||
url: item.url,
|
||||
userAgent: item.userAgent,
|
||||
filePath: item.filePath,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -331,6 +331,7 @@ export class RecentPlaylistsComponent {
|
||||
id: item._id,
|
||||
title: item.title,
|
||||
url: item.url,
|
||||
...(item.userAgent ? { userAgent: item.userAgent } : {}),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -385,6 +386,9 @@ export class RecentPlaylistsComponent {
|
||||
playlistId: item._id,
|
||||
title: item.title,
|
||||
url: item.url,
|
||||
...(item.userAgent
|
||||
? { userAgent: item.userAgent }
|
||||
: {}),
|
||||
filePath: item.filePath,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -340,6 +340,11 @@ export interface ElectronBridgeTrustOptions {
|
||||
trustedInsecureTlsHosts?: string[];
|
||||
}
|
||||
|
||||
export interface ElectronBridgePlaylistFetchOptions
|
||||
extends ElectronBridgeTrustOptions {
|
||||
userAgent?: string;
|
||||
}
|
||||
|
||||
export interface ElectronBridgeEpgFreshnessResult {
|
||||
staleUrls: string[];
|
||||
freshUrls: string[];
|
||||
@@ -718,7 +723,7 @@ export interface ElectronBridgeApi {
|
||||
fetchPlaylistByUrl: (
|
||||
url: string,
|
||||
title?: string,
|
||||
options?: ElectronBridgeTrustOptions
|
||||
options?: ElectronBridgePlaylistFetchOptions
|
||||
) => Promise<Playlist>;
|
||||
updatePlaylistFromFilePath: (
|
||||
filePath: string,
|
||||
|
||||
@@ -25,6 +25,7 @@ export interface PlaylistRefreshPayload {
|
||||
title: string;
|
||||
filePath?: string;
|
||||
url?: string;
|
||||
userAgent?: string;
|
||||
trustedInsecureTlsHosts?: string[];
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user