mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-16 22:26:16 -08:00
feat(settings): server section for self-hosted storage
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
cec1ad83a2
commit
07b942a832
14 files changed
+553
-5
No files matched your search
@@ -2,6 +2,7 @@ import { PLAYBACK_SETTINGS_SEARCH_ENTRIES } from './settings-search-entries-play
|
||||
import { SettingsSearchEntry } from './settings-search.types';
|
||||
|
||||
const DASHBOARD_KEYWORDS = ['dashboard', 'home', 'rail', 'row'];
|
||||
const SERVER_KEYWORDS = ['server', 'self-hosted', 'nas', 'sync', 'docker'];
|
||||
|
||||
function dashboardRail(
|
||||
id: string,
|
||||
@@ -244,6 +245,39 @@ export const SETTINGS_SEARCH_ENTRIES: readonly SettingsSearchEntry[] = [
|
||||
keywords: ['parental', 'lock categories', 'groups', 'right-click'],
|
||||
fallbackId: 'parental-lock-enable',
|
||||
},
|
||||
{
|
||||
id: 'server-connection',
|
||||
section: 'server',
|
||||
labelKey: 'SETTINGS.SERVER_CONNECTION',
|
||||
descriptionKey: 'SETTINGS.SERVER_CONNECTION_DESCRIPTION',
|
||||
keywords: SERVER_KEYWORDS,
|
||||
requires: ['server-storage'],
|
||||
},
|
||||
{
|
||||
id: 'server-storage',
|
||||
section: 'server',
|
||||
labelKey: 'SETTINGS.SERVER_STORAGE',
|
||||
descriptionKey: 'SETTINGS.SERVER_STORAGE_DESCRIPTION',
|
||||
keywords: [...SERVER_KEYWORDS, 'database', 'backup', 'size'],
|
||||
requires: ['server-storage'],
|
||||
},
|
||||
{
|
||||
id: 'server-import-browser',
|
||||
section: 'server',
|
||||
labelKey: 'SETTINGS.SERVER_IMPORT_BROWSER',
|
||||
descriptionKey: 'SETTINGS.SERVER_IMPORT_BROWSER_DESCRIPTION',
|
||||
keywords: [...SERVER_KEYWORDS, 'import', 'migrate', 'browser'],
|
||||
requires: ['server-storage'],
|
||||
fallbackId: 'server-storage',
|
||||
},
|
||||
{
|
||||
id: 'server-access',
|
||||
section: 'server',
|
||||
labelKey: 'SETTINGS.SERVER_ACCESS',
|
||||
descriptionKey: 'SETTINGS.SERVER_ACCESS_DESCRIPTION',
|
||||
keywords: [...SERVER_KEYWORDS, 'token', 'sign out', 'password'],
|
||||
requires: ['server-storage'],
|
||||
},
|
||||
{
|
||||
id: 'import-export-data',
|
||||
section: 'backup',
|
||||
|
||||
@@ -65,6 +65,13 @@ export const SETTINGS_SECTION_DEFINITIONS: readonly SettingsSectionDefinition[]
|
||||
requires: ['remote-control'],
|
||||
group: 'devices',
|
||||
},
|
||||
{
|
||||
id: 'server',
|
||||
navLabelKey: 'SETTINGS.NAV_SERVER',
|
||||
icon: 'dns',
|
||||
requires: ['server-storage'],
|
||||
group: 'data',
|
||||
},
|
||||
{
|
||||
id: 'backup',
|
||||
navLabelKey: 'SETTINGS.NAV_BACKUP',
|
||||
|
||||
@@ -2,7 +2,10 @@ import { TestBed } from '@angular/core/testing';
|
||||
import { Router } from '@angular/router';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { VodSourceDiscoveryService } from '@iptvnator/portal/shared/data-access';
|
||||
import { RuntimeCapabilitiesService } from '@iptvnator/services';
|
||||
import {
|
||||
RuntimeCapabilitiesService,
|
||||
ServerStorageStatusService,
|
||||
} from '@iptvnator/services';
|
||||
import { EMBEDDED_MPV_SUPPORT_RECHECK_MS } from '@iptvnator/shared/interfaces';
|
||||
import { SETTINGS_SEARCH_ENTRIES } from './settings-search-entries';
|
||||
import { SETTINGS_SECTION_DEFINITIONS } from './settings-search-sections';
|
||||
@@ -31,10 +34,14 @@ interface RuntimeStub {
|
||||
supportsEmbeddedMpv: boolean;
|
||||
}
|
||||
|
||||
function setup(runtime: Partial<RuntimeStub> = {}) {
|
||||
function setup(runtime: Partial<RuntimeStub> = {}, serverStorage = false) {
|
||||
const router = { navigate: jest.fn().mockResolvedValue(true) };
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
{
|
||||
provide: ServerStorageStatusService,
|
||||
useValue: { isActive: () => serverStorage },
|
||||
},
|
||||
{
|
||||
provide: RuntimeCapabilitiesService,
|
||||
useValue: {
|
||||
@@ -131,7 +138,25 @@ describe('SettingsSearchService', () => {
|
||||
// Embedded MPV rows wait for the support probe.
|
||||
expect(entries).not.toContain('embedded-mpv-extra-options');
|
||||
expect(entries).not.toContain('embedded-mpv-frame-copy');
|
||||
expect(entries.length).toBe(SETTINGS_SEARCH_ENTRIES.length - 4);
|
||||
// Server rows need the self-hosted PWA's server storage.
|
||||
expect(entries).not.toContain('server-connection');
|
||||
expect(entries.length).toBe(SETTINGS_SEARCH_ENTRIES.length - 8);
|
||||
});
|
||||
|
||||
it('offers the server section and rows with server storage', () => {
|
||||
const { service } = setup({}, true);
|
||||
|
||||
expect(service.visibleSections().map(({ id }) => id)).toContain(
|
||||
'server'
|
||||
);
|
||||
expect(service.visibleEntries().map(({ id }) => id)).toEqual(
|
||||
expect.arrayContaining([
|
||||
'server-connection',
|
||||
'server-storage',
|
||||
'server-import-browser',
|
||||
'server-access',
|
||||
])
|
||||
);
|
||||
});
|
||||
|
||||
describe('embedded MPV probe', () => {
|
||||
|
||||
@@ -2,7 +2,10 @@ import { inject, Injectable, signal } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { VodSourceDiscoveryService } from '@iptvnator/portal/shared/data-access';
|
||||
import { RuntimeCapabilitiesService } from '@iptvnator/services';
|
||||
import {
|
||||
RuntimeCapabilitiesService,
|
||||
ServerStorageStatusService,
|
||||
} from '@iptvnator/services';
|
||||
import {
|
||||
EmbeddedMpvSupport,
|
||||
watchEmbeddedMpvSupport,
|
||||
@@ -30,6 +33,7 @@ import {
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class SettingsSearchService {
|
||||
private readonly runtime = inject(RuntimeCapabilitiesService);
|
||||
private readonly serverStorageStatus = inject(ServerStorageStatusService);
|
||||
private readonly vodSourceDiscovery = inject(VodSourceDiscoveryService);
|
||||
private readonly router = inject(Router);
|
||||
private readonly translate = inject(TranslateService);
|
||||
@@ -61,6 +65,7 @@ export class SettingsSearchService {
|
||||
'embedded-mpv': !!this.embeddedMpvSupport()?.supported,
|
||||
'embedded-mpv-frame-copy':
|
||||
!!this.embeddedMpvSupport()?.frameCopyAvailable,
|
||||
'server-storage': this.serverStorageStatus.isActive(),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,9 @@ export type SettingsSearchRequirement =
|
||||
| 'vod-multi-source'
|
||||
/** Probed lazily; false until `ensureEmbeddedMpvSupportLoaded` resolves. */
|
||||
| 'embedded-mpv'
|
||||
| 'embedded-mpv-frame-copy';
|
||||
| 'embedded-mpv-frame-copy'
|
||||
/** The PWA runs against self-hosted server storage. */
|
||||
| 'server-storage';
|
||||
|
||||
export type SettingsSearchCapabilities = Readonly<
|
||||
Record<SettingsSearchRequirement, boolean>
|
||||
|
||||
Reference in new issue
Block a user