MoviePilot-Frontend

repository·v3·Indexed 43 days ago

https://github.com/jxxghp/moviepilot-frontend

A modern web interface for the MoviePilot ecosystem built with Vue 3 and Vuetify 3. It features a robust plugin system supporting dynamic remote component loading via Module Federation, a comprehensive code quality toolchain using ESLint, Prettier, and Vitest, and support for static deployment via Nginx or Node.js.

Tokens
30.3K
Snippets
99
Records
132
Agent score
52%

What's inside MoviePilot

  1. Understand the plugin-component project structure

    v3

    The plugin-component project is structured to demonstrate different types of remote components that can be loaded by the main MoviePilot application. The core components include:

    • Page.vue: The plugin details page component.
    • Config.vue: The plugin configuration page component.
    • Dashboard.vue: The plugin dashboard component.
    • AppPage.vue: A sidebar full-page component (used as the main content area when nav_key=main).
    • AppPageSettings.vue: An optional second full-page component (loaded by the main application when nav_key=settings, demonstrating the 'one plugin, multiple full-page interfaces' capability).
    plugin-component/
    ├── src/
    │   ├── components/
    │   │   ├── Page.vue       # 插件详情页面组件
    │   │   ├── Config.vue     # 插件配置页面组件
    │   │   ├── Dashboard.vue  # 插件仪表板组件
    │   │   ├── AppPage.vue    # 侧栏全页(主内容区,nav_key=main)
    │   │   └── AppPageSettings.vue  # 可选第二全页(nav_key=settings)
    │   ├── App.vue            # 本地开发入口组件
    │   └── main.js            # 本地开发入口文件
    ├── vite.config.js         # Vite和模块联邦配置
    ├── index.html             # 本地开发HTML入口
    └── package.json            # 依赖配置
  2. Access Host Application Capabilities via Injection

    v3

    Remote components can access host-provided capabilities using Vue's inject. This allows plugins to use the host's UI elements (Toasts, Dialogs, Confirmations) instead of creating their own.

    CapabilityInjection KeyDescription
    Native Subscribemoviepilot:nativeSubscribeTriggers native subscription flows (Movies/TV).
    Toastmoviepilot:toastUnified notification system (success, info, warning, error).
    Public Dialogmoviepilot:dialogOpens a component in the host's SharedDialogHost.
    Confirm Dialogmoviepilot:confirmSimple confirmation dialog returning a Promise<boolean>.

    Note: Always check if the injected value exists to ensure compatibility with older host versions or environments where the capability is unavailable.

  3. Configure CSS isolation and shared dependencies

    v3

    To prevent style conflicts and manage shared libraries in your remote module, follow these patterns:

    CSS Isolation

    Use CSS Modules or <style scoped> to ensure styles do not leak into the host application.

    Shared Dependencies

    Extend the shared configuration in your Vite setup to include additional libraries. Setting requiredVersion: false is recommended for shared dependencies to avoid version mismatch errors.

    <style scoped>
    /* Component styles */
    </style>
    // vite.config.ts shared configuration
    shared: {
      vue: { requiredVersion: false },
      vuetify: { requiredVersion: false },
      '@vueuse/core': { requiredVersion: false },
      pinia: { requiredVersion: false }
    }
  4. Handle Multiple Sidebar Entries (Multi-nav_key)

    v3

    A single plugin can provide multiple sidebar entries by returning multiple items in get_sidebar_nav with different nav_key values. The host application resolves these using a specific fallback order for federated modules.

    Resolution Order for nav_key:

    1. main (or omitted): Tries ./AppPage $\rightarrow$ ./Page.
    2. Other keys (e.g., settings): Tries ./AppPage{PascalCase} $\rightarrow$ ./AppPage $\rightarrow$ ./Page.
      • Example: nav_key=settings $\rightarrow$ ./AppPageSettings.
      • Example: nav_key=my_tool $\rightarrow$ ./AppPageMyTool.

    Implementation Strategies:

    • Single File: Expose only ./AppPage and use v-if or <component :is="..."> to switch views based on the navKey prop.
    • Multi-file: Expose multiple files in vite.config.ts (e.g., ./AppPageSettings.vue). The host will attempt to load the specific module first.
    // vite.config.ts multi-exposure example
    exposes: {
      './AppPage': './src/components/AppPage.vue',
      './AppPageSettings': './src/components/AppPageSettings.vue',
    }
  5. Organize test files and directory structure

    v3

    Tests follow a co-location pattern where business-specific specs reside in a __tests__/ directory next to the source code, using the *.spec.ts suffix.

    Directory Layout

    Business Logic Specs (Co-located):

    src/
    ├── pages/discover/index.vue
    └── __tests__/index.spec.ts
    ├── stores/auth.ts
    └── __tests__/auth.spec.ts

    Shared Testing Infrastructure (Centralized): Located in the tests/ directory, accessible via the @tests/* alias.

    • tests/setup.ts: Registers DOM matchers, MSW lifecycle, browser API stubs, and per-test cleanup.
    • tests/support/render.ts: Standard entry point for rendering with Vuetify, i18n, Router, and Pinia.
    • tests/support/factories/: Minimal valid data factories for business objects.
    • tests/support/msw/handlers/: HTTP handlers defined by business domain.
    • tests/support/msw/server.ts: MSW server instance.
    • tests/support/msw/response.ts: Utilities for constructing API envelopes.
    src/
    ├── pages/
    │   ├── discover/
    │   │   ├── index.vue
    │   │   └── __tests__/index.spec.ts
    │   └── music/
    │       ├── index.vue
    │       └── __tests__/index.spec.ts
    ├── stores/
    │   ├── auth.ts
    │   └── __tests__/auth.spec.ts
    ...
    
    tests/
    ├── setup.ts
    └── support/
        ├── render.ts
        ├── factories/
        └── msw/
            ├── server.ts
            └── handlers/
  6. How Module Federation works in MoviePilot

    v3

    MoviePilot uses Module Federation to dynamically load and integrate frontend plugins. To be compatible, a remote plugin must expose specific standard components that the main application can resolve.

    When the main application navigates to a sidebar full-page route, it attempts to resolve components based on a nav_key. It first looks for a specific name (e.g., AppPageSettings), then falls back to ./AppPage, and finally to ./Page. If the nav_key is main, it only attempts to load ./AppPage or ./Page.

    |
    | 组件名称  | 暴露名           | 文件名                 | 用途                                          |
    | --------- | ---------------- | ---------------------- | --------------------------------------------- |
    | Page      | `./Page`         | Page.vue               | 插件管理中的详情弹窗                          |
    | Config    | `./Config`       | Config.vue             | 插件配置页面                                  |
    | Dashboard | `./Dashboard`    | Dashboard.vue          | 仪表盘小组件                                  |
    | AppPage   | `./AppPage`      | AppPage.vue            | 主界面侧栏独立全页(主内容区由插件完全绘制)  |
    | (可选)  | `./AppPage{Xxx}` | 如 AppPageSettings.vue | 多 `nav_key` 时按名优先加载,见下文「多界面」 |
  7. Understand the MoviePilot-Frontend Code Quality Toolchain

    v3

    The project uses a multi-layered toolchain to ensure code quality, type safety, and consistent formatting. The responsibility of each tool is strictly defined to avoid overlap:

    • ESLint: Handles JavaScript, TypeScript, and Vue code quality, framework constraints, and module boundaries. It does not handle code formatting (indentation, quotes, etc.).
    • Prettier: Ensures deterministic and repeatable code formatting. It does not handle unused variables, Vue rules, or complexity.
    • TypeScript / vue-tsc: Performs TypeScript and Vue template type checking.
    • Vitest: Executes unit and component tests and provides coverage reports.
    • Vite: Handles production builds and build-time integration verification.

    Key Principle: Editor extensions provide immediate feedback, but the project's official commands and CI environment are the single source of truth for passing or failing quality gates.

  8. Understand the SCSS directory structure and integration

    v3

    The SCSS architecture has been consolidated to simplify imports and reduce dependency on the legacy base directory.

    • Main Entry Point: src/@core/scss/index.scss
    • Functional Files: Located in src/@core/scss/template/index.scss

    All core functionalities (mixins, placeholders, Vuetify integrations, layouts, etc.) have been moved into the template directory. The system is designed to be backward compatible, but new development should rely on the consolidated structure. Once testing is complete, the base directory is intended to be removed entirely.

  9. Develop remote components with Module Federation

    v3

    MoviePilot supports Module Federation, enabling plugin developers to create and dynamically load remote components for richer plugin UIs.

    For detailed implementation details, refer to the following resources:

    • Development Guide: docs/module-federation-guide.md (How to develop remote component plugins)
    • Troubleshooting Guide: docs/federation-troubleshooting.md (Common issues and solutions)
    • Reference Example: examples/plugin-component/ (A complete example project for plugin component development)
  10. Understand the Unit Testing Architecture

    v3

    MoviePilot-Frontend uses Vitest for unit and component testing, with jsdom providing the DOM environment. Testing code is subject to TypeScript type checking but is excluded from production builds.

    Test Types

    • Unit Tests: Cover pure functions, stores, composables, routing rules, and independent modules (inputs, outputs, and side effects).
    • Component Tests: Mount Vue components or pages to cover props, emits, user interaction, visible DOM, Router, Pinia, HTTP requests, and lifecycle cleanup.
    • Browser-based Verification: PWA, Service Workers, Module Federation remote entries, real layouts, drag-and-drop, and native browser capabilities are verified in real browsers, not via jsdom.

    Tooling Responsibilities

    • Vitest: Test runner, assertions, mocking, fake timers, and coverage execution.
    • Vue Test Utils: Vue-specific control over props, emits, slots, and local components.
    • Testing Library / jest-dom / user-event: Verifying visible behavior via roles, accessible names, and user actions.
    • MSW (Mock Service Worker): Intercepts HTTP requests at the boundary. Tests must not access real backends or the internet; unhandled requests cause test failure.
    • @pinia/testing: Used for component tests that depend on stores. Stores themselves are tested using a real createPinia().