mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-12 03:56:15 -08:00
chore(nx): enforce scoped workspace boundaries
This commit is contained in:
1 parent
8f79b25f52
commit
6de47d4b82
338 files changed
+925
-589
No files matched your search
@@ -0,0 +1,44 @@
|
||||
---
|
||||
name: iptvnator-nx-architecture
|
||||
description: Repository-specific Nx monorepo structure, library placement rules, scoped path aliases, and migration guardrails for portal/workspace/app code.
|
||||
---
|
||||
|
||||
# IPTVnator Nx Architecture
|
||||
|
||||
Use this skill when deciding where code belongs, extracting libraries, changing imports, editing project tags, or refactoring portal/workspace/app boundaries.
|
||||
|
||||
## Project Shape
|
||||
|
||||
- `apps/web`: Angular renderer application.
|
||||
- `apps/electron-backend`: Electron main process and native/runtime integration.
|
||||
- `apps/*-e2e`: Playwright E2E projects.
|
||||
- `apps/*-mock-server`: local development and E2E mock servers.
|
||||
- `libs/playlist/*`: M3U/import/shared playlist functionality.
|
||||
- `libs/portal/*`: Xtream, Stalker, and provider-neutral portal functionality.
|
||||
- `libs/workspace/*`: workspace shell and dashboard.
|
||||
- `libs/ui/*`: provider-neutral UI, playback, EPG, remote control, and pipes.
|
||||
- `libs/shared/*`: contracts, database, and pure utility code.
|
||||
|
||||
## Import Policy
|
||||
|
||||
- Use scoped aliases from `tsconfig.base.json`, for example `@iptvnator/services` and `@iptvnator/shared/interfaces`.
|
||||
- Do not reintroduce legacy bare aliases such as `services`, `components`, or `shared-interfaces`.
|
||||
- Prefer library public APIs (`src/index.ts`) over deep imports unless a sub-entrypoint is explicitly configured.
|
||||
- Keep app-only code in `apps/*`; shared behavior belongs in a domain library.
|
||||
|
||||
## Tag Policy
|
||||
|
||||
- Every project should have `scope:*`, `domain:*`, and `type:*` tags.
|
||||
- Use `type:feature` for route/component orchestration, `type:data-access` for stores/API/persistence, `type:ui` for reusable components, and `type:util` for pure helpers/contracts.
|
||||
- Keep `type:data-access` from depending on `type:feature` or `type:ui`.
|
||||
- Add or update `@nx/enforce-module-boundaries` constraints when introducing new tag families.
|
||||
|
||||
## Validation
|
||||
|
||||
```bash
|
||||
pnpm nx show projects
|
||||
pnpm nx lint <project>
|
||||
pnpm nx test <project>
|
||||
```
|
||||
|
||||
When dependencies are not installed in a fresh worktree, run `pnpm install --frozen-lockfile` first.
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
name: iptvnator-sqlite-db-worker
|
||||
description: Repository-specific guidance for Electron non-EPG SQLite worker boundaries, request-scoped DB progress events, and validation for slow DB operations.
|
||||
---
|
||||
|
||||
# IPTVnator SQLite DB Worker
|
||||
|
||||
Use this skill when changing Electron SQLite operations, worker-backed database flows, DB progress events, or Xtream/playlist import/search/delete persistence.
|
||||
|
||||
## Ownership
|
||||
|
||||
- Runtime worker client: `apps/electron-backend/src/app/services/database-worker-client.ts`
|
||||
- Worker protocol: `apps/electron-backend/src/app/workers/database-worker.types.ts`
|
||||
- Worker dispatcher: `apps/electron-backend/src/app/workers/database.worker.ts`
|
||||
- SQL operation modules: `apps/electron-backend/src/app/database/operations/`
|
||||
- Shared schema/path helpers: `libs/shared/database/src/`
|
||||
- Architecture doc: `docs/architecture/sqlite-db-worker.md`
|
||||
|
||||
## Rules
|
||||
|
||||
- Keep heavy non-EPG SQLite work off the Electron main thread.
|
||||
- Keep SQL-heavy logic in operation modules; keep the worker entrypoint as dispatcher/orchestration.
|
||||
- Emit request-scoped `DB_OPERATION_EVENT` progress for long-running operations.
|
||||
- Preserve cancellation as cooperative and chunk-based.
|
||||
- Do not break existing preload API method names without a coordinated renderer migration.
|
||||
|
||||
## Validation
|
||||
|
||||
- Run `pnpm nx test electron-backend` for worker/client/operation changes.
|
||||
- Run targeted Electron E2E for import, search, delete, backup/restore, or downloads flows when touched.
|
||||
- Use `IPTVNATOR_TRACE_DB=1` or `IPTVNATOR_TRACE_SQL=1` for manual debugging.
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
name: iptvnator-theme-style
|
||||
description: Theme architecture, design tokens, shared SCSS library, portal header/sidebar patterns, Electron drag regions, and cross-portal style consistency.
|
||||
---
|
||||
|
||||
# IPTVnator Theme Style
|
||||
|
||||
Use this skill when changing SCSS tokens, shared layout mixins, portal headers, sidebars, detail views, or Electron draggable regions.
|
||||
|
||||
## Shared Style Sources
|
||||
|
||||
- `libs/ui/styles/_index.scss`
|
||||
- `libs/ui/styles/_portal-layout.scss`
|
||||
- `libs/ui/styles/_content-grid.scss`
|
||||
- `libs/ui/styles/_portal-sidebar.scss`
|
||||
- `libs/ui/styles/_panel-header.scss`
|
||||
- `libs/ui/styles/_detail-view-actions.scss`
|
||||
- `docs/architecture/iptvnator-ui-guidelines.md`
|
||||
|
||||
## Rules
|
||||
|
||||
- Prefer shared SCSS mixins and Material system tokens over local hard-coded colors.
|
||||
- Keep Electron drag behavior explicit: interactive controls need `app-region: no-drag`.
|
||||
- Do not duplicate large SCSS files across feature libraries; extract a shared partial or mixin.
|
||||
- Use relative SCSS imports only when no stable shared entrypoint exists.
|
||||
- Check Xtream, Stalker, M3U, and workspace views for cross-portal style drift when editing shared patterns.
|
||||
|
||||
## Validation
|
||||
|
||||
- Run lint/tests for the affected UI project.
|
||||
- Manually inspect the changed screen in light and dark themes for layout, selection, and drag-region regressions.
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
name: iptvnator-ui-design
|
||||
description: Repository-specific UI design guidance for IPTVnator channel rows, EPG views, settings surfaces, shared selection styles, and light/dark theme consistency.
|
||||
---
|
||||
|
||||
# IPTVnator UI Design
|
||||
|
||||
Use this skill when changing user-visible Angular UI in the IPTVnator app, especially channel lists, EPG panels, settings, playlist surfaces, and portal shared components.
|
||||
|
||||
## Principles
|
||||
|
||||
- Prefer dense, scannable application UI over marketing-style layouts.
|
||||
- Preserve existing Material 3 token usage and light/dark theme behavior.
|
||||
- Keep repeated selection states aligned with existing `--app-selection-*` tokens.
|
||||
- Use existing shared UI components before adding local one-off markup.
|
||||
- Avoid large visual rewrites in behavior-focused changes.
|
||||
|
||||
## Checklist
|
||||
|
||||
1. Inspect the nearest existing component and shared UI library before editing.
|
||||
2. Check both light and dark theme styles when touching colors, borders, hover states, or selection states.
|
||||
3. Keep text and controls within fixed-width rows from resizing the layout.
|
||||
4. Prefer existing components from `@iptvnator/ui/components`, `@iptvnator/portal/shared/ui`, and `@iptvnator/ui/epg`.
|
||||
5. Add or update focused component tests for changed interaction states.
|
||||
|
||||
## Validation
|
||||
|
||||
- Run the affected Nx project test target.
|
||||
- For visible workflow changes, run the closest Playwright E2E target or manually verify through Electron CDP as documented in `AGENTS.md`.
|
||||
@@ -0,0 +1,30 @@
|
||||
---
|
||||
name: xtream-electron
|
||||
description: IPTVnator's Electron-first Xtream implementation, including feature/data-access boundaries, worker-backed DB flows, and Xtream loading/progress UX.
|
||||
---
|
||||
|
||||
# Xtream Electron
|
||||
|
||||
Use this skill when working on Xtream routes, stores, data sources, import/search/delete behavior, or Electron-backed Xtream playback and persistence.
|
||||
|
||||
## Key Areas
|
||||
|
||||
- Feature UI: `libs/portal/xtream/feature/src/lib/`
|
||||
- Data access: `libs/portal/xtream/data-access/src/lib/`
|
||||
- Shared portal utilities: `libs/portal/shared/util/src/lib/`
|
||||
- Shared portal UI: `libs/portal/shared/ui/src/lib/`
|
||||
- Electron DB events: `apps/electron-backend/src/app/events/database/`
|
||||
- DB worker operations: `apps/electron-backend/src/app/database/operations/`
|
||||
|
||||
## Rules
|
||||
|
||||
- Keep provider-specific API/cache behavior in `portal/xtream/data-access`.
|
||||
- Keep reusable layout and collection UI in `portal/shared/ui` or `portal/shared/util`.
|
||||
- Prefer worker-backed DB operations for large imports, global search, delete, and restore.
|
||||
- Preserve request cancellation and progress reporting for long imports.
|
||||
- Validate both PWA and Electron data-source paths when changing Xtream APIs.
|
||||
|
||||
## Validation
|
||||
|
||||
- Run `pnpm nx test portal-xtream-data-access` and `pnpm nx test portal-xtream-feature` for store/UI changes.
|
||||
- Run `pnpm nx run web-e2e:e2e-ci--src/xtream.e2e.ts` or the closest Electron E2E target for user-visible Xtream workflow changes.
|
||||
+3
-1
@@ -73,7 +73,9 @@ Thumbs.db
|
||||
.cursor
|
||||
.agent
|
||||
.claude
|
||||
.codex
|
||||
.codex/*
|
||||
!.codex/skills/
|
||||
!.codex/skills/**
|
||||
.plans
|
||||
|
||||
# Native Electron addons
|
||||
|
||||
@@ -9,6 +9,15 @@ This file provides guidance to coding agents working in this repository.
|
||||
- Use the filename pattern `YYYY-MM-DD-short-topic.md` such as `.plans/2026-03-12-channel-filtering.md`.
|
||||
- If the intended filename already exists, append a numeric suffix such as `-2`, `-3`, and so on.
|
||||
|
||||
## Agent Bootstrap
|
||||
|
||||
- In a fresh worktree, run `pnpm install --frozen-lockfile` before relying on Nx project discovery, lint, test, or build commands. Without `node_modules`, `pnpm nx show projects` will fail because the local Nx modules are unavailable.
|
||||
- After dependencies are installed, verify workspace discovery with `pnpm nx show projects`.
|
||||
- Use scoped path aliases from `tsconfig.base.json` such as `@iptvnator/services`, `@iptvnator/shared/interfaces`, and `@iptvnator/ui/components`. Do not add new imports from legacy bare aliases such as `services`, `shared-interfaces`, `components`, `m3u-state`, or `database`.
|
||||
- Every Nx project should keep `scope:*`, `domain:*`, and `type:*` tags in `project.json` so `@nx/enforce-module-boundaries` remains useful for humans and agents.
|
||||
- See `docs/architecture/nx-workspace-boundaries.md` for the current Nx tag and alias policy.
|
||||
- Repository-specific skills are committed under `.codex/skills/`. If an external agent does not support skills, treat those files as concise ownership docs.
|
||||
|
||||
## Documentation After Changes
|
||||
|
||||
- After implementing a meaningful change, agents must assess whether canonical repo docs need updates before considering the task complete.
|
||||
@@ -150,7 +159,7 @@ Key files:
|
||||
|
||||
## General Guidelines for working with Nx
|
||||
|
||||
- For navigating/exploring the workspace, invoke the `nx-workspace` skill first - it has patterns for querying projects, targets, and dependencies
|
||||
- For navigating/exploring the workspace, invoke the `nx-workspace` skill first when it is available - it has patterns for querying projects, targets, and dependencies. If it is unavailable, use `pnpm nx show projects`, `pnpm nx graph`, and project `project.json` files directly.
|
||||
- When running tasks (for example build, lint, test, e2e, etc.), always prefer running the task through `nx` (i.e. `nx run`, `nx run-many`, `nx affected`) instead of using the underlying tooling directly
|
||||
- Prefix nx commands with the workspace's package manager (e.g., `pnpm nx build`, `npm exec nx test`) - avoids using globally installed CLI
|
||||
- You have access to the Nx MCP server and its tools, use them to help the user
|
||||
|
||||
@@ -45,6 +45,20 @@ IPTVnator is a cross-platform IPTV player application built with Angular and Ele
|
||||
|
||||
## Development Commands
|
||||
|
||||
### Agent Bootstrap
|
||||
|
||||
```bash
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm nx show projects
|
||||
```
|
||||
|
||||
- Run the install step in a fresh worktree before relying on Nx discovery, lint, test, or build commands. Without `node_modules`, local Nx modules are unavailable.
|
||||
- Use scoped path aliases from `tsconfig.base.json` such as `@iptvnator/services`, `@iptvnator/shared/interfaces`, and `@iptvnator/ui/components`.
|
||||
- Do not add new imports from legacy bare aliases such as `services`, `shared-interfaces`, `components`, `m3u-state`, or `database`.
|
||||
- Every Nx project should keep `scope:*`, `domain:*`, and `type:*` tags in `project.json`.
|
||||
- See `docs/architecture/nx-workspace-boundaries.md` for the current Nx tag and alias policy.
|
||||
- Repository-specific skills are committed under `.codex/skills/`. If Claude Code does not load skills directly, treat those files as concise ownership docs.
|
||||
|
||||
### Building and Serving
|
||||
|
||||
```bash
|
||||
@@ -689,7 +703,7 @@ No formal migration system yet. Schema changes are applied via raw SQL in `conne
|
||||
|
||||
## General Guidelines for working with Nx
|
||||
|
||||
- For navigating/exploring the workspace, invoke the `nx-workspace` skill first - it has patterns for querying projects, targets, and dependencies
|
||||
- For navigating/exploring the workspace, invoke the `nx-workspace` skill first when it is available - it has patterns for querying projects, targets, and dependencies. If it is unavailable, use `pnpm nx show projects`, `pnpm nx graph`, and project `project.json` files directly.
|
||||
- When running tasks (for example build, lint, test, e2e, etc.), always prefer running the task through `nx` (i.e. `nx run`, `nx run-many`, `nx affected`) instead of using the underlying tooling directly
|
||||
- Prefix nx commands with the workspace's package manager (e.g., `pnpm nx build`, `npm exec nx test`) - avoids using globally installed CLI
|
||||
- You have access to the Nx MCP server and its tools, use them to help the user
|
||||
|
||||
@@ -12,5 +12,6 @@
|
||||
"e2e": {
|
||||
"dependsOn": ["electron-backend:build-e2e"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": ["scope:e2e", "domain:electron-main", "type:e2e"]
|
||||
}
|
||||
@@ -77,23 +77,23 @@ async function buildWorker() {
|
||||
sourcemap: !isProduction,
|
||||
minify: isProduction,
|
||||
alias: {
|
||||
'shared-interfaces': path.join(
|
||||
'@iptvnator/shared/interfaces': path.join(
|
||||
__dirname,
|
||||
'../../libs/shared/interfaces/src/index.ts'
|
||||
),
|
||||
'm3u-utils': path.join(
|
||||
'@iptvnator/shared/m3u-utils': path.join(
|
||||
__dirname,
|
||||
'../../libs/shared/m3u-utils/src/index.ts'
|
||||
),
|
||||
'database': path.join(
|
||||
'@iptvnator/shared/database': path.join(
|
||||
__dirname,
|
||||
'../../libs/shared/database/src/index.ts'
|
||||
),
|
||||
'database-schema': path.join(
|
||||
'@iptvnator/shared/database/schema': path.join(
|
||||
__dirname,
|
||||
'../../libs/shared/database/src/lib/schema.ts'
|
||||
),
|
||||
'database-path-utils': path.join(
|
||||
'@iptvnator/shared/database/path-utils': path.join(
|
||||
__dirname,
|
||||
'../../libs/shared/database/src/lib/path-utils.ts'
|
||||
),
|
||||
|
||||
@@ -179,5 +179,6 @@
|
||||
"jestConfig": "apps/electron-backend/jest.config.ts"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": ["scope:electron", "domain:electron-main", "type:app"]
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import type {
|
||||
PlaylistRefreshPayload,
|
||||
ResolvedPortalPlayback,
|
||||
XtreamCategory,
|
||||
} from 'shared-interfaces';
|
||||
} from '@iptvnator/shared/interfaces';
|
||||
import {
|
||||
DEBUG_TRACE_EVENT_CHANNEL,
|
||||
isRendererApiTraceEnabled,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { __databaseConnectionTestHooks } from 'database';
|
||||
import { __databaseConnectionTestHooks } from '@iptvnator/shared/database';
|
||||
|
||||
function statementContaining(pattern: string): string {
|
||||
const statement =
|
||||
|
||||
@@ -10,5 +10,5 @@ export {
|
||||
closeDatabase,
|
||||
type DatabaseInstance,
|
||||
type DatabaseOptions,
|
||||
} from 'database';
|
||||
} from '@iptvnator/shared/database';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { BetterSQLite3Database } from 'drizzle-orm/better-sqlite3';
|
||||
import * as schema from 'database-schema';
|
||||
import * as schema from '@iptvnator/shared/database/schema';
|
||||
|
||||
export type AppDatabase = BetterSQLite3Database<typeof schema>;
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { AppDatabase } from '../database.types';
|
||||
import * as schema from 'database-schema';
|
||||
import * as schema from '@iptvnator/shared/database/schema';
|
||||
import { getCategories, saveCategories } from './category.operations';
|
||||
|
||||
function createDbMock(existingCount = 0) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { and, eq, inArray, sql } from 'drizzle-orm';
|
||||
import * as schema from 'database-schema';
|
||||
import * as schema from '@iptvnator/shared/database/schema';
|
||||
import type { AppDatabase } from '../database.types';
|
||||
|
||||
type XtreamCategoryInput = {
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ jest.mock('drizzle-orm', () => ({
|
||||
sqlMock(strings, ...values),
|
||||
}));
|
||||
|
||||
import * as schema from 'database-schema';
|
||||
import * as schema from '@iptvnator/shared/database/schema';
|
||||
import type { AppDatabase } from '../database.types';
|
||||
import { setContentBackdropIfMissing } from './content-backdrop.operations';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { and, eq, sql } from 'drizzle-orm';
|
||||
import * as schema from 'database-schema';
|
||||
import * as schema from '@iptvnator/shared/database/schema';
|
||||
import type { AppDatabase } from '../database.types';
|
||||
|
||||
export async function persistContentBackdropIfMissing(
|
||||
|
||||
@@ -18,7 +18,7 @@ jest.mock('drizzle-orm', () => ({
|
||||
sql: jest.fn(),
|
||||
}));
|
||||
|
||||
import * as schema from 'database-schema';
|
||||
import * as schema from '@iptvnator/shared/database/schema';
|
||||
import type { AppDatabase } from '../database.types';
|
||||
import { getContentByXtreamId, saveContent } from './content.operations';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { and, asc, desc, eq, inArray, or, sql } from 'drizzle-orm';
|
||||
import * as schema from 'database-schema';
|
||||
import * as schema from '@iptvnator/shared/database/schema';
|
||||
import type { AppDatabase } from '../database.types';
|
||||
import {
|
||||
checkpointOperation,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { and, asc, desc, eq, inArray, sql } from 'drizzle-orm';
|
||||
import * as schema from 'database-schema';
|
||||
import * as schema from '@iptvnator/shared/database/schema';
|
||||
import type { AppDatabase } from '../database.types';
|
||||
import {
|
||||
chunkValues,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { and, desc, eq, sql } from 'drizzle-orm';
|
||||
import * as schema from 'database-schema';
|
||||
import * as schema from '@iptvnator/shared/database/schema';
|
||||
import type { AppDatabase } from '../database.types';
|
||||
|
||||
type PlaylistType = 'xtream' | 'stalker' | 'm3u-file' | 'm3u-text' | 'm3u-url';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { eq, inArray } from 'drizzle-orm';
|
||||
import * as schema from 'database-schema';
|
||||
import * as schema from '@iptvnator/shared/database/schema';
|
||||
import type { AppDatabase } from '../database.types';
|
||||
import {
|
||||
checkpointOperation,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { and, desc, eq, inArray, sql } from 'drizzle-orm';
|
||||
import * as schema from 'database-schema';
|
||||
import * as schema from '@iptvnator/shared/database/schema';
|
||||
import type { AppDatabase } from '../database.types';
|
||||
import { persistContentBackdropIfMissing } from './content-backdrop.operations';
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { and, eq, inArray } from 'drizzle-orm';
|
||||
import * as schema from 'database-schema';
|
||||
import * as schema from '@iptvnator/shared/database/schema';
|
||||
import type {
|
||||
XtreamBackupFavoriteItem,
|
||||
XtreamBackupHiddenCategory,
|
||||
XtreamBackupRecentlyViewedItem,
|
||||
} from 'shared-interfaces';
|
||||
} from '@iptvnator/shared/interfaces';
|
||||
import type { AppDatabase } from '../database.types';
|
||||
import {
|
||||
checkpointOperation,
|
||||
|
||||
@@ -36,4 +36,4 @@ export {
|
||||
type NewPlaybackPosition,
|
||||
type Download,
|
||||
type NewDownload,
|
||||
} from 'database';
|
||||
} from '@iptvnator/shared/database';
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
EmbeddedMpvBounds,
|
||||
EmbeddedMpvRecordingStartOptions,
|
||||
ResolvedPortalPlayback,
|
||||
} from 'shared-interfaces';
|
||||
} from '@iptvnator/shared/interfaces';
|
||||
import {
|
||||
EmbeddedMpvNativeService,
|
||||
embeddedMpvNativeService,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { and, eq, gte, inArray, lte, sql } from 'drizzle-orm';
|
||||
import { app, BrowserWindow, ipcMain } from 'electron';
|
||||
import * as path from 'path';
|
||||
import { EpgChannelMetadata, EpgProgram } from 'shared-interfaces';
|
||||
import { EpgChannelMetadata, EpgProgram } from '@iptvnator/shared/interfaces';
|
||||
import { pathToFileURL } from 'url';
|
||||
import { Worker } from 'worker_threads';
|
||||
import { getDatabase } from '../database/connection';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ExternalPlayerSession } from 'shared-interfaces';
|
||||
import { ExternalPlayerSession } from '@iptvnator/shared/interfaces';
|
||||
import { ExternalPlayerSessionRegistry } from './external-player-session-registry';
|
||||
|
||||
describe('ExternalPlayerSessionRegistry', () => {
|
||||
|
||||
@@ -2,7 +2,7 @@ import {
|
||||
ExternalPlayerName,
|
||||
ExternalPlayerSession,
|
||||
PlayerContentInfo,
|
||||
} from 'shared-interfaces';
|
||||
} from '@iptvnator/shared/interfaces';
|
||||
|
||||
interface CreateExternalPlayerSessionOptions {
|
||||
player: ExternalPlayerName;
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
ExternalPlayerSession,
|
||||
parseExternalPlayerArguments,
|
||||
type ExternalPlayerArgumentsInput,
|
||||
} from 'shared-interfaces';
|
||||
} from '@iptvnator/shared/interfaces';
|
||||
import App from '../app';
|
||||
import {
|
||||
MPV_PLAYER_ARGUMENTS,
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import axios from 'axios';
|
||||
import { app, dialog, ipcMain, WebContents } from 'electron';
|
||||
import { parse } from 'iptv-playlist-parser';
|
||||
import { createPlaylistObject, getFilenameFromUrl } from 'm3u-utils';
|
||||
import { createPlaylistObject, getFilenameFromUrl } from '@iptvnator/shared/m3u-utils';
|
||||
import { readFile, writeFile } from 'node:fs/promises';
|
||||
import { basename } from 'node:path';
|
||||
import { pathToFileURL } from 'url';
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
Playlist,
|
||||
PlaylistRefreshEvent,
|
||||
PlaylistRefreshPayload,
|
||||
} from 'shared-interfaces';
|
||||
} from '@iptvnator/shared/interfaces';
|
||||
import { resolveWorkerRuntimeBootstrap } from '../workers/worker-runtime-paths';
|
||||
import type {
|
||||
PlaylistRefreshWorkerMessage,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { BrowserWindow } from 'electron';
|
||||
import { PORTAL_DEBUG_EVENT, PortalDebugEvent } from 'shared-interfaces';
|
||||
import { PORTAL_DEBUG_EVENT, PortalDebugEvent } from '@iptvnator/shared/interfaces';
|
||||
import { environment } from '../../environments/environment';
|
||||
|
||||
const MAX_DEBUG_DEPTH = 6;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ipcMain } from 'electron';
|
||||
import { normalizeExternalPlayerArguments } from 'shared-interfaces';
|
||||
import { normalizeExternalPlayerArguments } from '@iptvnator/shared/interfaces';
|
||||
import {
|
||||
MPV_PLAYER_ARGUMENTS,
|
||||
MPV_REUSE_INSTANCE,
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import axios, { AxiosRequestConfig } from 'axios';
|
||||
import { createHash } from 'crypto';
|
||||
import { ipcMain } from 'electron';
|
||||
import { PortalDebugEvent, STALKER_REQUEST } from 'shared-interfaces';
|
||||
import { PortalDebugEvent, STALKER_REQUEST } from '@iptvnator/shared/interfaces';
|
||||
import { rememberStalkerPlaybackContext } from '../services/stalker-playback-context.service';
|
||||
import { emitPortalDebugEvent } from './portal-debug.events';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { XTREAM_CANCEL_SESSION } from 'shared-interfaces';
|
||||
import { XTREAM_CANCEL_SESSION } from '@iptvnator/shared/interfaces';
|
||||
|
||||
const registeredHandlers = new Map<string, (...args: unknown[]) => unknown>();
|
||||
const axiosMock = Object.assign(jest.fn(), {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import axios, { AxiosRequestConfig } from 'axios';
|
||||
import { ipcMain } from 'electron';
|
||||
import { PortalDebugEvent, XTREAM_CANCEL_SESSION } from 'shared-interfaces';
|
||||
import { PortalDebugEvent, XTREAM_CANCEL_SESSION } from '@iptvnator/shared/interfaces';
|
||||
import { emitPortalDebugEvent } from './portal-debug.events';
|
||||
|
||||
export default class XtreamEvents {
|
||||
|
||||
@@ -2,7 +2,7 @@ import type {
|
||||
EmbeddedMpvBounds,
|
||||
EmbeddedMpvSessionStatus,
|
||||
ResolvedPortalPlayback,
|
||||
} from 'shared-interfaces';
|
||||
} from '@iptvnator/shared/interfaces';
|
||||
import { existsSync, mkdtempSync, rmSync, writeFileSync } from 'fs';
|
||||
import { tmpdir } from 'os';
|
||||
import path from 'path';
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
EmbeddedMpvSupport,
|
||||
EMBEDDED_MPV_SESSION_UPDATE,
|
||||
ResolvedPortalPlayback,
|
||||
} from 'shared-interfaces';
|
||||
} from '@iptvnator/shared/interfaces';
|
||||
|
||||
interface NativeEmbeddedMpvSessionSnapshot {
|
||||
status: EmbeddedMpvSessionStatus;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Conf } from 'electron-conf/main';
|
||||
import { getElectronConfigDirectory } from 'database';
|
||||
import { getElectronConfigDirectory } from '@iptvnator/shared/database';
|
||||
|
||||
export const WINDOW_BOUNDS = 'WINDOW_BOUNDS';
|
||||
export const MPV_PLAYER_PATH = 'MPV_PLAYER_PATH';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type BetterSqlite3 from 'better-sqlite3';
|
||||
import * as schema from 'database-schema';
|
||||
import { getIptvnatorDatabasePath } from 'database-path-utils';
|
||||
import * as schema from '@iptvnator/shared/database/schema';
|
||||
import { getIptvnatorDatabasePath } from '@iptvnator/shared/database/path-utils';
|
||||
import { workerData } from 'worker_threads';
|
||||
import type { AppDatabase } from '../database/database.types';
|
||||
import {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { parentPort } from 'worker_threads';
|
||||
import type {
|
||||
XtreamBackupFavoriteItem,
|
||||
XtreamBackupRecentlyViewedItem,
|
||||
} from 'shared-interfaces';
|
||||
} from '@iptvnator/shared/interfaces';
|
||||
import type {
|
||||
DbOperationEvent,
|
||||
DbWorkerIncomingMessage,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type BetterSqlite3 from 'better-sqlite3';
|
||||
import { existsSync, mkdirSync } from 'fs';
|
||||
import { getIptvnatorDatabasePath } from 'database-path-utils';
|
||||
import { getIptvnatorDatabasePath } from '@iptvnator/shared/database/path-utils';
|
||||
import { Readable } from 'stream';
|
||||
import { parentPort, workerData } from 'worker_threads';
|
||||
import { createGunzip } from 'zlib';
|
||||
|
||||
@@ -2,12 +2,12 @@ import axios from 'axios';
|
||||
import { parentPort } from 'worker_threads';
|
||||
import { parse } from 'iptv-playlist-parser';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { createPlaylistObject, getFilenameFromUrl } from 'm3u-utils';
|
||||
import { createPlaylistObject, getFilenameFromUrl } from '@iptvnator/shared/m3u-utils';
|
||||
import type {
|
||||
Playlist,
|
||||
PlaylistRefreshEvent,
|
||||
PlaylistRefreshPayload,
|
||||
} from 'shared-interfaces';
|
||||
} from '@iptvnator/shared/interfaces';
|
||||
import type {
|
||||
PlaylistRefreshWorkerIncomingMessage,
|
||||
PlaylistRefreshWorkerMessage,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { PlaylistRefreshEvent, PlaylistRefreshPayload } from 'shared-interfaces';
|
||||
import type { PlaylistRefreshEvent, PlaylistRefreshPayload } from '@iptvnator/shared/interfaces';
|
||||
|
||||
export interface PlaylistRefreshWorkerRequestMessage {
|
||||
type: 'request';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { app, BrowserWindow } from 'electron';
|
||||
import { getElectronUserDataPath } from 'database';
|
||||
import { getElectronUserDataPath } from '@iptvnator/shared/database';
|
||||
import fixPath from 'fix-path';
|
||||
import App from './app/app';
|
||||
import { initDatabase } from './app/database/connection';
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"enabled": false
|
||||
}
|
||||
},
|
||||
"tags": [],
|
||||
"tags": ["scope:remote-control", "domain:playback", "type:app"],
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@angular/build:application",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Component, OnInit, inject } from '@angular/core';
|
||||
import { RemoteControlComponent } from 'remote-control';
|
||||
import { RemoteControlComponent } from '@iptvnator/ui/remote-control';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"projectType": "application",
|
||||
"sourceRoot": "apps/stalker-mock-server/src",
|
||||
"tags": ["scope:dev-tools"],
|
||||
"tags": ["scope:dev-tools", "domain:stalker", "type:dev-app"],
|
||||
"targets": {
|
||||
"serve": {
|
||||
"continuous": true,
|
||||
|
||||
@@ -11,5 +11,6 @@
|
||||
"// targets": "to see all targets run: nx show project web-e2e --web",
|
||||
"targets": {
|
||||
"e2e": {}
|
||||
}
|
||||
},
|
||||
"tags": ["scope:e2e", "domain:web", "type:e2e"]
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
"enabled": false
|
||||
}
|
||||
},
|
||||
"tags": ["scope:app", "type:app"],
|
||||
"tags": ["scope:app", "domain:web", "type:app"],
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@angular/build:application",
|
||||
|
||||
@@ -8,7 +8,7 @@ import { EpgService } from '@iptvnator/epg/data-access';
|
||||
import { WORKSPACE_SHELL_ACTIONS } from '@iptvnator/workspace/shell/util';
|
||||
import { MockProvider } from 'ng-mocks';
|
||||
import { EMPTY, of } from 'rxjs';
|
||||
import { DataService } from 'services';
|
||||
import { DataService } from '@iptvnator/services';
|
||||
import {
|
||||
Language,
|
||||
Settings,
|
||||
@@ -17,8 +17,8 @@ import {
|
||||
StreamFormat,
|
||||
Theme,
|
||||
VideoPlayer,
|
||||
} from 'shared-interfaces';
|
||||
import { PlaylistActions } from 'm3u-state';
|
||||
} from '@iptvnator/shared/interfaces';
|
||||
import { PlaylistActions } from '@iptvnator/m3u-state';
|
||||
import { AppComponent } from './app.component';
|
||||
import { ElectronServiceStub } from './services/electron.service.stub';
|
||||
import { SettingsService } from './services/settings.service';
|
||||
|
||||
@@ -7,9 +7,9 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
import { EpgService } from '@iptvnator/epg/data-access';
|
||||
import { WORKSPACE_SHELL_ACTIONS } from '@iptvnator/workspace/shell/util';
|
||||
import { EpgProgressPanelComponent } from '@iptvnator/ui/epg/progress-panel';
|
||||
import { PlaylistActions, selectAllPlaylistsMeta } from 'm3u-state';
|
||||
import { PlaylistActions, selectAllPlaylistsMeta } from '@iptvnator/m3u-state';
|
||||
import { filter, take } from 'rxjs';
|
||||
import { DataService, SettingsStore } from 'services';
|
||||
import { DataService, SettingsStore } from '@iptvnator/services';
|
||||
import {
|
||||
AUTO_UPDATE_PLAYLISTS,
|
||||
Language,
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
Settings,
|
||||
STORE_KEY,
|
||||
Theme,
|
||||
} from 'shared-interfaces';
|
||||
} from '@iptvnator/shared/interfaces';
|
||||
import { SettingsService } from './services/settings.service';
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -18,7 +18,7 @@ import { provideStore } from '@ngrx/store';
|
||||
import { provideStoreDevtools } from '@ngrx/store-devtools';
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
|
||||
import { PlaylistEffects, playlistReducer } from 'm3u-state';
|
||||
import { PlaylistEffects, playlistReducer } from '@iptvnator/m3u-state';
|
||||
import { NgxIndexedDBModule, NgxIndexedDBService } from 'ngx-indexed-db';
|
||||
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
|
||||
import {
|
||||
@@ -27,8 +27,8 @@ import {
|
||||
} from '@iptvnator/portal/shared/util';
|
||||
import { PLAYLIST_PLAYER_ACTIONS } from '@iptvnator/playlist/shared/util';
|
||||
import { provideXtreamDataSource } from '@iptvnator/portal/xtream/data-access';
|
||||
import { DataService } from 'services';
|
||||
import { dbConfig } from 'shared-interfaces';
|
||||
import { DataService } from '@iptvnator/services';
|
||||
import { dbConfig } from '@iptvnator/shared/interfaces';
|
||||
import { AppConfig } from '../environments/environment';
|
||||
import { routes } from './app.routes';
|
||||
import { ElectronService } from './services/electron.service';
|
||||
|
||||
@@ -40,7 +40,7 @@ export const routes: Routes = [
|
||||
path: 'dashboard',
|
||||
canActivate: [dashboardAccessGuard],
|
||||
loadComponent: () =>
|
||||
import('workspace-dashboard-feature').then(
|
||||
import('@iptvnator/workspace/dashboard/feature').then(
|
||||
(c) => c.WorkspaceDashboardRailsComponent
|
||||
),
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Component, signal } from '@angular/core';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { ResolvedPortalPlayback } from 'shared-interfaces';
|
||||
import { ResolvedPortalPlayback } from '@iptvnator/shared/interfaces';
|
||||
import { EmbeddedMpvOverlayVisibilityService } from '../../../../libs/ui/playback/src/lib/embedded-mpv-player/embedded-mpv-overlay-visibility.service';
|
||||
import { EmbeddedMpvPlayerComponent } from '../../../../libs/ui/playback/src/lib/embedded-mpv-player/embedded-mpv-player.component';
|
||||
import { EmbeddedMpvSessionController } from '../../../../libs/ui/playback/src/lib/embedded-mpv-player/embedded-mpv-session-controller';
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
} from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { selectAllPlaylistsMeta } from 'm3u-state';
|
||||
import { selectAllPlaylistsMeta } from '@iptvnator/m3u-state';
|
||||
import { EmptyStateComponent } from '@iptvnator/playlist/shared/ui';
|
||||
import { PlaylistContextFacade } from '@iptvnator/playlist/shared/util';
|
||||
import {
|
||||
|
||||
@@ -2,4 +2,4 @@ export {
|
||||
DownloadsService,
|
||||
type DownloadItem,
|
||||
type DownloadStatus,
|
||||
} from 'services';
|
||||
} from '@iptvnator/services';
|
||||
@@ -2,8 +2,8 @@ import { inject, Injectable } from '@angular/core';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { PlaylistActions } from 'm3u-state';
|
||||
import { DataService } from 'services';
|
||||
import { PlaylistActions } from '@iptvnator/m3u-state';
|
||||
import { DataService } from '@iptvnator/services';
|
||||
import {
|
||||
AUTO_UPDATE_PLAYLISTS,
|
||||
ERROR,
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
XTREAM_REQUEST,
|
||||
XTREAM_RESPONSE,
|
||||
XtreamCodeActions,
|
||||
} from 'shared-interfaces';
|
||||
} from '@iptvnator/shared/interfaces';
|
||||
import { AppConfig } from '../../environments/environment';
|
||||
import {
|
||||
createPortalDebugRequestContext,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ExternalPlayerSession } from 'shared-interfaces';
|
||||
import { ExternalPlayerSession } from '@iptvnator/shared/interfaces';
|
||||
import { ExternalPlaybackService } from './external-playback.service';
|
||||
|
||||
describe('ExternalPlaybackService', () => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { computed, Injectable, signal } from '@angular/core';
|
||||
import { ExternalPlayerSession, PlayerContentInfo } from 'shared-interfaces';
|
||||
import { ExternalPlayerSession, PlayerContentInfo } from '@iptvnator/shared/interfaces';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { DataService } from 'services';
|
||||
import { ExternalPlayerSession, VideoPlayer } from 'shared-interfaces';
|
||||
import { DataService } from '@iptvnator/services';
|
||||
import { ExternalPlayerSession, VideoPlayer } from '@iptvnator/shared/interfaces';
|
||||
import { SettingsStore } from './settings-store.service';
|
||||
import { PlayerService } from './player.service';
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Injectable, inject } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { ExternalPlayerInfoDialogComponent } from '@iptvnator/ui/playback/external-player-info-dialog';
|
||||
import { DataService } from 'services';
|
||||
import { DataService } from '@iptvnator/services';
|
||||
import {
|
||||
ExternalPlayerSession,
|
||||
OPEN_MPV_PLAYER,
|
||||
@@ -9,8 +9,8 @@ import {
|
||||
PlayerContentInfo,
|
||||
ResolvedPortalPlayback,
|
||||
VideoPlayer,
|
||||
} from 'shared-interfaces';
|
||||
import type { ExternalPlayerName } from 'shared-interfaces';
|
||||
} from '@iptvnator/shared/interfaces';
|
||||
import type { ExternalPlayerName } from '@iptvnator/shared/interfaces';
|
||||
import { SettingsStore } from './settings-store.service';
|
||||
|
||||
@Injectable({
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
PortalNavigationActions,
|
||||
} from '@iptvnator/portal/shared/util';
|
||||
import { XtreamStore } from '@iptvnator/portal/xtream/data-access';
|
||||
import { Playlist } from 'shared-interfaces';
|
||||
import { Playlist } from '@iptvnator/shared/interfaces';
|
||||
import { SettingsComponent } from '../settings/settings.component';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
|
||||
@@ -4,9 +4,9 @@ import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { SwUpdate } from '@angular/service-worker';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { PlaylistActions } from 'm3u-state';
|
||||
import { PlaylistActions } from '@iptvnator/m3u-state';
|
||||
import { catchError, firstValueFrom, throwError } from 'rxjs';
|
||||
import { DataService } from 'services';
|
||||
import { DataService } from '@iptvnator/services';
|
||||
import {
|
||||
ERROR,
|
||||
Playlist,
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
XtreamCodeActions,
|
||||
XTREAM_REQUEST,
|
||||
XTREAM_RESPONSE,
|
||||
} from 'shared-interfaces';
|
||||
} from '@iptvnator/shared/interfaces';
|
||||
import { AppConfig } from '../../environments/environment';
|
||||
import {
|
||||
createPortalDebugErrorEvent,
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { SettingsStore } from 'services';
|
||||
export { SettingsStore } from '@iptvnator/services';
|
||||
@@ -2,7 +2,7 @@ import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
import { inject, TestBed } from '@angular/core/testing';
|
||||
import { StorageMap } from '@ngx-pwa/local-storage';
|
||||
import { of } from 'rxjs';
|
||||
import { STORE_KEY, Theme } from 'shared-interfaces';
|
||||
import { STORE_KEY, Theme } from '@iptvnator/shared/interfaces';
|
||||
import { SettingsService } from './settings.service';
|
||||
|
||||
describe('Service: Settings', () => {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { HttpClient } from '@angular/common/http';
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { StorageMap } from '@ngx-pwa/local-storage';
|
||||
import { catchError, map, Observable } from 'rxjs';
|
||||
import { STORE_KEY, Theme } from 'shared-interfaces';
|
||||
import { STORE_KEY, Theme } from '@iptvnator/shared/interfaces';
|
||||
|
||||
const PRERELEASE_KEYWORDS = [
|
||||
'beta',
|
||||
|
||||
@@ -6,7 +6,7 @@ import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { CoverSize, Language, Theme } from 'shared-interfaces';
|
||||
import { CoverSize, Language, Theme } from '@iptvnator/shared/interfaces';
|
||||
import {
|
||||
CoverSizeOption,
|
||||
StartupBehaviorOption,
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
StartupBehavior,
|
||||
Theme,
|
||||
VideoPlayer,
|
||||
} from 'shared-interfaces';
|
||||
} from '@iptvnator/shared/interfaces';
|
||||
import {
|
||||
CoverSizeOption,
|
||||
SettingsPlayerOption,
|
||||
@@ -108,7 +108,7 @@ export function buildSettingsSectionNavItems(
|
||||
visible: isDesktop,
|
||||
},
|
||||
{
|
||||
id: 'remote-control',
|
||||
id: '@iptvnator/ui/remote-control',
|
||||
label: 'SETTINGS.NAV_REMOTE',
|
||||
icon: 'smartphone',
|
||||
visible: isDesktop,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||
import { StreamFormat, VideoPlayer } from 'shared-interfaces';
|
||||
import { StreamFormat, VideoPlayer } from '@iptvnator/shared/interfaces';
|
||||
import { SettingsPlaybackSectionComponent } from './settings-playback-section.component';
|
||||
|
||||
const MPV_PATH_DESCRIPTION =
|
||||
|
||||
@@ -8,7 +8,7 @@ import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { StreamFormat, VideoPlayer } from 'shared-interfaces';
|
||||
import { StreamFormat, VideoPlayer } from '@iptvnator/shared/interfaces';
|
||||
import { SettingsPlayerOption } from './settings.models';
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -22,14 +22,14 @@ import { EpgService } from '@iptvnator/epg/data-access';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { MockStore, provideMockStore } from '@ngrx/store/testing';
|
||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||
import { DialogService } from 'components';
|
||||
import { DialogService } from '@iptvnator/ui/components';
|
||||
import { MockModule, MockProvider } from 'ng-mocks';
|
||||
import {
|
||||
DatabaseService,
|
||||
DataService,
|
||||
PlaylistBackupService,
|
||||
PlaylistsService,
|
||||
} from 'services';
|
||||
} from '@iptvnator/services';
|
||||
import {
|
||||
EmbeddedMpvSupport,
|
||||
Language,
|
||||
@@ -38,7 +38,7 @@ import {
|
||||
StreamFormat,
|
||||
Theme,
|
||||
VideoPlayer,
|
||||
} from 'shared-interfaces';
|
||||
} from '@iptvnator/shared/interfaces';
|
||||
import { SettingsComponent } from './settings.component';
|
||||
|
||||
import { signal } from '@angular/core';
|
||||
@@ -47,7 +47,7 @@ import {
|
||||
PlaylistActions,
|
||||
selectAllPlaylistsMeta,
|
||||
selectIsEpgAvailable,
|
||||
} from 'm3u-state';
|
||||
} from '@iptvnator/m3u-state';
|
||||
import { NgxIndexedDBService } from 'ngx-indexed-db';
|
||||
import { from, of } from 'rxjs';
|
||||
import { ElectronServiceStub } from '../services/electron.service.stub';
|
||||
|
||||
@@ -28,12 +28,12 @@ import { EpgService } from '@iptvnator/epg/data-access';
|
||||
import { SettingsContextService } from '@iptvnator/workspace/shell/util';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||
import { DialogService } from 'components';
|
||||
import { DialogService } from '@iptvnator/ui/components';
|
||||
import {
|
||||
PlaylistActions,
|
||||
selectAllPlaylistsMeta,
|
||||
selectIsEpgAvailable,
|
||||
} from 'm3u-state';
|
||||
} from '@iptvnator/m3u-state';
|
||||
import { firstValueFrom, take } from 'rxjs';
|
||||
import {
|
||||
DatabaseService,
|
||||
@@ -42,7 +42,7 @@ import {
|
||||
PlaylistBackupImportSummary,
|
||||
PlaylistBackupService,
|
||||
PlaylistsService,
|
||||
} from 'services';
|
||||
} from '@iptvnator/services';
|
||||
import {
|
||||
EmbeddedMpvSupport,
|
||||
CoverSize,
|
||||
@@ -52,7 +52,7 @@ import {
|
||||
StreamFormat,
|
||||
Theme,
|
||||
VideoPlayer,
|
||||
} from 'shared-interfaces';
|
||||
} from '@iptvnator/shared/interfaces';
|
||||
import { SettingsStore } from '../services/settings-store.service';
|
||||
import { SettingsService } from './../services/settings.service';
|
||||
import { SettingsAboutSectionComponent } from './settings-about-section.component';
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
StartupBehavior,
|
||||
Theme,
|
||||
VideoPlayer,
|
||||
} from 'shared-interfaces';
|
||||
} from '@iptvnator/shared/interfaces';
|
||||
|
||||
export interface SettingsSection {
|
||||
id: string;
|
||||
|
||||
Vendored
+2
-2
@@ -23,7 +23,7 @@ import {
|
||||
XtreamBackupHiddenCategory,
|
||||
XtreamBackupRecentlyViewedItem,
|
||||
XtreamCategory,
|
||||
} from 'shared-interfaces';
|
||||
} from '@iptvnator/shared/interfaces';
|
||||
import {
|
||||
DbOperationEvent,
|
||||
GlobalFavoriteItem,
|
||||
@@ -32,7 +32,7 @@ import {
|
||||
GlobalSearchResult,
|
||||
XCategoryFromDb,
|
||||
XtreamContent,
|
||||
} from 'services';
|
||||
} from '@iptvnator/services';
|
||||
|
||||
type JsonObject = Record<string, unknown>;
|
||||
type JsonArray = unknown[];
|
||||
|
||||
+30
-30
@@ -1,33 +1,33 @@
|
||||
{
|
||||
"name": "website",
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"projectType": "application",
|
||||
"sourceRoot": "apps/website/src",
|
||||
"tags": ["type:website"],
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "nx:run-commands",
|
||||
"outputs": ["{workspaceRoot}/dist/apps/website"],
|
||||
"inputs": ["production", "^production"],
|
||||
"options": {
|
||||
"command": "pnpm exec astro build",
|
||||
"cwd": "apps/website"
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
"command": "pnpm exec astro dev --port 4321",
|
||||
"cwd": "apps/website"
|
||||
}
|
||||
},
|
||||
"preview": {
|
||||
"executor": "nx:run-commands",
|
||||
"dependsOn": ["build"],
|
||||
"options": {
|
||||
"command": "pnpm exec astro preview --port 4322",
|
||||
"cwd": "apps/website"
|
||||
}
|
||||
"name": "website",
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"projectType": "application",
|
||||
"sourceRoot": "apps/website/src",
|
||||
"tags": ["scope:website", "domain:marketing", "type:website"],
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "nx:run-commands",
|
||||
"outputs": ["{workspaceRoot}/dist/apps/website"],
|
||||
"inputs": ["production", "^production"],
|
||||
"options": {
|
||||
"command": "pnpm exec astro build",
|
||||
"cwd": "apps/website"
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
"command": "pnpm exec astro dev --port 4321",
|
||||
"cwd": "apps/website"
|
||||
}
|
||||
},
|
||||
"preview": {
|
||||
"executor": "nx:run-commands",
|
||||
"dependsOn": ["build"],
|
||||
"options": {
|
||||
"command": "pnpm exec astro preview --port 4322",
|
||||
"cwd": "apps/website"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"projectType": "application",
|
||||
"sourceRoot": "apps/xtream-mock-server/src",
|
||||
"tags": ["scope:dev-tools"],
|
||||
"tags": ["scope:dev-tools", "domain:xtream", "type:dev-app"],
|
||||
"targets": {
|
||||
"serve": {
|
||||
"continuous": true,
|
||||
|
||||
@@ -354,7 +354,7 @@ class EpgService {
|
||||
- The shared EPG UI only shows the archive replay badge when the host confirms
|
||||
that the selected M3U channel has a playable replay scheme. Archive days
|
||||
alone are not enough.
|
||||
- M3U catch-up support is resolved in `m3u-utils` from channel metadata and
|
||||
- M3U catch-up support is resolved in `@iptvnator/shared/m3u-utils` from channel metadata and
|
||||
the archived program start time.
|
||||
- Supported replay precedence:
|
||||
1. `catchup.source` if it is an HTTP(S) URL. IPTVNator rewrites or appends
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
# Nx Workspace Boundaries
|
||||
|
||||
This document records the current monorepo boundary conventions for IPTVnator.
|
||||
|
||||
## Fresh Worktree Bootstrap
|
||||
|
||||
Install dependencies before using Nx discovery or targets:
|
||||
|
||||
```bash
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm nx show projects
|
||||
```
|
||||
|
||||
`pnpm nx show projects` depends on the workspace-local Nx packages under
|
||||
`node_modules`. In a fresh worktree without dependencies it will fail before it
|
||||
can inspect project metadata.
|
||||
|
||||
## Project Tags
|
||||
|
||||
Every Nx project should carry three tag families in `project.json`:
|
||||
|
||||
1. `scope:*` - ownership area, for example `scope:portal`, `scope:workspace`,
|
||||
`scope:shared`, `scope:electron`, `scope:e2e`, or `scope:dev-tools`.
|
||||
2. `domain:*` - product/runtime domain, for example `domain:xtream`,
|
||||
`domain:stalker`, `domain:m3u`, `domain:playback`, `domain:web`, or
|
||||
`domain:shared-runtime`.
|
||||
3. `type:*` - architectural role, for example `type:app`, `type:e2e`,
|
||||
`type:dev-app`, `type:feature`, `type:ui`, `type:data-access`,
|
||||
`type:util`, `type:tool`, or `type:website`.
|
||||
|
||||
`eslint.config.mjs` uses these tags with `@nx/enforce-module-boundaries`.
|
||||
When adding a project, choose tags before adding imports so dependency direction
|
||||
is clear from the start.
|
||||
|
||||
## Import Aliases
|
||||
|
||||
Use scoped `@iptvnator/*` aliases from `tsconfig.base.json`.
|
||||
|
||||
Examples:
|
||||
|
||||
```ts
|
||||
import { SettingsStore } from '@iptvnator/services';
|
||||
import { Playlist } from '@iptvnator/shared/interfaces';
|
||||
import { DialogService } from '@iptvnator/ui/components';
|
||||
```
|
||||
|
||||
Do not introduce legacy bare aliases such as:
|
||||
|
||||
- `components`
|
||||
- `m3u-state`
|
||||
- `m3u-utils`
|
||||
- `services`
|
||||
- `shared-interfaces`
|
||||
- `shared-portals`
|
||||
- `remote-control`
|
||||
- `database`
|
||||
- `database-schema`
|
||||
- `database-path-utils`
|
||||
- `workspace-dashboard-feature`
|
||||
- `workspace-dashboard-data-access`
|
||||
|
||||
The lint config blocks these aliases so new code uses the same visible
|
||||
namespace and ownership convention.
|
||||
|
||||
Buildable libraries that have a local `package.json` should use the same public
|
||||
name as their scoped alias. Nx uses `package.json.name` when it rewrites
|
||||
buildable dependency paths to `dist/` during `@nx/js:tsc` builds.
|
||||
|
||||
## Dependency Direction
|
||||
|
||||
- `type:feature` may use `type:feature`, `type:ui`, `type:data-access`, and
|
||||
`type:util`.
|
||||
- `type:ui` may use `type:ui`, `type:data-access`, and `type:util`.
|
||||
- `type:data-access` may use `type:data-access` and `type:util`.
|
||||
- `type:util` may use only `type:util`.
|
||||
|
||||
If a change needs a dependency in the opposite direction, move the shared
|
||||
contract into a lower-level library instead of weakening boundaries.
|
||||
@@ -349,8 +349,8 @@ renderer can actually paint the loading state instead of freezing.
|
||||
|
||||
The worker build also aliases:
|
||||
|
||||
1. `database-schema`
|
||||
2. `database-path-utils`
|
||||
1. `@iptvnator/shared/database/schema`
|
||||
2. `@iptvnator/shared/database/path-utils`
|
||||
|
||||
These aliases avoid importing the shared database barrel from inside the worker,
|
||||
which would otherwise pull in runtime code that assumes the main Electron
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
import nx from '@nx/eslint-plugin';
|
||||
|
||||
const legacyBareAliases = [
|
||||
'components',
|
||||
'm3u-state',
|
||||
'm3u-utils',
|
||||
'services',
|
||||
'shared-interfaces',
|
||||
'shared-portals',
|
||||
'remote-control',
|
||||
'database',
|
||||
'database-schema',
|
||||
'database-path-utils',
|
||||
'workspace-dashboard-feature',
|
||||
'workspace-dashboard-data-access',
|
||||
];
|
||||
|
||||
export default [
|
||||
...nx.configs['flat/base'],
|
||||
...nx.configs['flat/typescript'],
|
||||
@@ -25,6 +40,28 @@ export default [
|
||||
'type:util',
|
||||
],
|
||||
},
|
||||
{
|
||||
sourceTag: 'type:e2e',
|
||||
onlyDependOnLibsWithTags: [
|
||||
'type:feature',
|
||||
'type:ui',
|
||||
'type:data-access',
|
||||
'type:util',
|
||||
],
|
||||
},
|
||||
{
|
||||
sourceTag: 'type:dev-app',
|
||||
onlyDependOnLibsWithTags: [
|
||||
'type:feature',
|
||||
'type:ui',
|
||||
'type:data-access',
|
||||
'type:util',
|
||||
],
|
||||
},
|
||||
{
|
||||
sourceTag: 'type:website',
|
||||
onlyDependOnLibsWithTags: ['type:ui', 'type:util'],
|
||||
},
|
||||
{
|
||||
sourceTag: 'type:feature',
|
||||
onlyDependOnLibsWithTags: [
|
||||
@@ -175,6 +212,21 @@ export default [
|
||||
],
|
||||
},
|
||||
],
|
||||
'no-restricted-imports': [
|
||||
'error',
|
||||
{
|
||||
paths: legacyBareAliases.map((name) => ({
|
||||
name,
|
||||
message:
|
||||
'Use the scoped @iptvnator/* path alias instead of the legacy bare alias.',
|
||||
})),
|
||||
patterns: legacyBareAliases.map((name) => ({
|
||||
group: [`${name}/*`],
|
||||
message:
|
||||
'Use the scoped @iptvnator/* path alias instead of the legacy bare alias.',
|
||||
})),
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { EpgProgram } from 'shared-interfaces';
|
||||
import { EpgProgram } from '@iptvnator/shared/interfaces';
|
||||
import { normalizeEpgPrograms } from './epg-program-normalization.util';
|
||||
|
||||
function buildProgram(overrides: Partial<EpgProgram> = {}): EpgProgram {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { EpgProgram } from 'shared-interfaces';
|
||||
import { EpgProgram } from '@iptvnator/shared/interfaces';
|
||||
|
||||
function toIsoDate(value: string | undefined): string | null {
|
||||
if (!value) {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { BehaviorSubject, forkJoin, from, Observable, of } from 'rxjs';
|
||||
import { catchError, map, tap, timeout } from 'rxjs/operators';
|
||||
import { EpgChannelMetadata, EpgProgram } from 'shared-interfaces';
|
||||
import { EpgChannelMetadata, EpgProgram } from '@iptvnator/shared/interfaces';
|
||||
import { normalizeEpgPrograms } from './epg-program-normalization.util';
|
||||
|
||||
interface CachedProgram {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export default {
|
||||
displayName: 'm3u-state',
|
||||
displayName: '@iptvnator/m3u-state',
|
||||
preset: '../../jest.preset.js',
|
||||
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
|
||||
coverageDirectory: '../../coverage/libs/m3u-state',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createActionGroup, emptyProps, props } from '@ngrx/store';
|
||||
import { Channel, EpgProgram, Playlist, PlaylistMeta } from 'shared-interfaces';
|
||||
import { Channel, EpgProgram, Playlist, PlaylistMeta } from '@iptvnator/shared/interfaces';
|
||||
|
||||
export const PlaylistActions = createActionGroup({
|
||||
source: 'Playlists',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Channel } from 'shared-interfaces';
|
||||
import { Channel } from '@iptvnator/shared/interfaces';
|
||||
import { resolveChannelEpgLookupKey } from './channel-epg-lookup.util';
|
||||
|
||||
function buildChannel(overrides: Partial<Channel> = {}): Channel {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Channel } from 'shared-interfaces';
|
||||
import { Channel } from '@iptvnator/shared/interfaces';
|
||||
|
||||
export function resolveChannelEpgLookupKey(
|
||||
channel: Channel | null | undefined
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Channel } from 'shared-interfaces';
|
||||
import { Channel } from '@iptvnator/shared/interfaces';
|
||||
import { buildExternalPlayerPayload } from './external-player-payload.util';
|
||||
|
||||
describe('buildExternalPlayerPayload', () => {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { inject, Injectable } from '@angular/core';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { Router } from '@angular/router';
|
||||
import { EpgService } from '@iptvnator/epg/data-access';
|
||||
import { resolveM3uCatchupUrl } from 'm3u-utils';
|
||||
import { resolveM3uCatchupUrl } from '@iptvnator/shared/m3u-utils';
|
||||
import { Actions, createEffect, ofType } from '@ngrx/effects';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { StorageMap } from '@ngx-pwa/local-storage';
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
tap,
|
||||
withLatestFrom,
|
||||
} from 'rxjs';
|
||||
import { DataService, PlaylistsService } from 'services';
|
||||
import { DataService, PlaylistsService } from '@iptvnator/services';
|
||||
import {
|
||||
OPEN_MPV_PLAYER,
|
||||
OPEN_VLC_PLAYER,
|
||||
@@ -26,7 +26,7 @@ import {
|
||||
Playlist,
|
||||
STORE_KEY,
|
||||
VideoPlayer,
|
||||
} from 'shared-interfaces';
|
||||
} from '@iptvnator/shared/interfaces';
|
||||
import {
|
||||
ChannelActions,
|
||||
EpgActions,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Channel } from 'shared-interfaces';
|
||||
import { Channel } from '@iptvnator/shared/interfaces';
|
||||
|
||||
export function buildExternalPlayerPayload(
|
||||
activeChannel: Channel | undefined | null,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createEntityAdapter, EntityAdapter, EntityState } from '@ngrx/entity';
|
||||
import { PlaylistMeta } from 'shared-interfaces';
|
||||
import { PlaylistMeta } from '@iptvnator/shared/interfaces';
|
||||
|
||||
export const playlistsAdapter: EntityAdapter<PlaylistMeta> =
|
||||
createEntityAdapter<PlaylistMeta>({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createReducer } from '@ngrx/store';
|
||||
import { Channel } from 'shared-interfaces';
|
||||
import { Channel } from '@iptvnator/shared/interfaces';
|
||||
import { ChannelActions } from '../actions';
|
||||
import { initialState } from '../state';
|
||||
import { channelReducers } from './channel.reducers';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { on } from '@ngrx/store';
|
||||
import { Channel } from 'shared-interfaces';
|
||||
import { Channel } from '@iptvnator/shared/interfaces';
|
||||
import { ChannelActions } from '../actions';
|
||||
import { PlaylistState } from '../state';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { on } from '@ngrx/store';
|
||||
import { Channel } from 'shared-interfaces';
|
||||
import { Channel } from '@iptvnator/shared/interfaces';
|
||||
import { EpgActions } from '../actions';
|
||||
import { PlaylistState } from '../state';
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { PlaylistActions } from '../actions';
|
||||
import { playlistsAdapter } from '../playlists.state';
|
||||
import { initialState } from '../state';
|
||||
import { playlistReducers } from './playlist.reducers';
|
||||
import { Channel, Playlist, PlaylistMeta } from 'shared-interfaces';
|
||||
import { Channel, Playlist, PlaylistMeta } from '@iptvnator/shared/interfaces';
|
||||
|
||||
const reducer = createReducer(initialState, ...playlistReducers);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { on } from '@ngrx/store';
|
||||
import { Channel } from 'shared-interfaces';
|
||||
import { Channel } from '@iptvnator/shared/interfaces';
|
||||
import { PlaylistActions } from '../actions';
|
||||
import { playlistsAdapter } from '../playlists.state';
|
||||
import { PlaylistState } from '../state';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { PlaylistMeta } from 'shared-interfaces';
|
||||
import { PlaylistMeta } from '@iptvnator/shared/interfaces';
|
||||
import { PlaylistState } from './state';
|
||||
import { selectActivePlaylist, selectChannelsLoading } from './selectors';
|
||||
|
||||
|
||||
Loaded 100 of 338 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user