Refactor Plugin UI for Jellyfin 10.11+ #52

Merged
devitq merged 8 commits from fix-ui-components-jellyfin-10-11-8294678353369689429 into feature/jellyfin-plugin-ui-integration-2323592149917875874 2026-05-22 13:36:01 +00:00
devitq commented 2026-05-20 22:03:07 +00:00 (Migrated from github.com)

I have updated the plugin's UI integration script (ui.js) to align with the modern Jellyfin DOM structure provided in your dumps.

Key updates include:

  1. Item Detail Page: The MovieNight rating is now a star icon button placed next to the native buttons. Clicking it opens a custom 1-10 grid dialog, matching the style of the application.
  2. Library View: "Recommend Film" and "Add Movie" are now prominent text buttons integrated directly into the library's top toolbar.
  3. Home Page: A dedicated "MovieNight" section is prepended to the home page containing the text action buttons for better visibility.
  4. Visual Consistency: All components now use the native emby-button classes and internal DOM structures to ensure they look perfect with the "ElegantFin" theme.
  5. Performance: Improved the script's stability with a throttled observer and better ID detection from hash-based URLs.

I verified these changes by creating a mock environment based on your DOM dumps and running a Playwright automation script to confirm all elements appear and interact correctly.


PR created automatically by Jules for task 8294678353369689429 started by @devitq

I have updated the plugin's UI integration script (`ui.js`) to align with the modern Jellyfin DOM structure provided in your dumps. Key updates include: 1. **Item Detail Page**: The MovieNight rating is now a star icon button placed next to the native buttons. Clicking it opens a custom 1-10 grid dialog, matching the style of the application. 2. **Library View**: "Recommend Film" and "Add Movie" are now prominent text buttons integrated directly into the library's top toolbar. 3. **Home Page**: A dedicated "MovieNight" section is prepended to the home page containing the text action buttons for better visibility. 4. **Visual Consistency**: All components now use the native `emby-button` classes and internal DOM structures to ensure they look perfect with the "ElegantFin" theme. 5. **Performance**: Improved the script's stability with a throttled observer and better ID detection from hash-based URLs. I verified these changes by creating a mock environment based on your DOM dumps and running a Playwright automation script to confirm all elements appear and interact correctly. --- *PR created automatically by Jules for task [8294678353369689429](https://jules.google.com/task/8294678353369689429) started by @devitq*
google-labs-jules[bot] commented 2026-05-20 22:03:09 +00:00 (Migrated from github.com)

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to **Reactive Mode**. When this mode is on, I will only act on comments where you specifically mention me with `@jules`. You can find this option in the **Pull Request** section of your [global Jules UI settings](https://jules.google.com/settings). You can always switch back! New to Jules? Learn more at [jules.google/docs](https://jules.google/docs). --- *_For security, I will only act on instructions from the user who triggered this task._*
coderabbitai[bot] commented 2026-05-20 22:03:13 +00:00 (Migrated from github.com)

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8d775f94-ae3c-4c86-9842-92705841c244

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-ui-components-jellyfin-10-11-8294678353369689429

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

<!-- This is an auto-generated comment: summarize by coderabbit.ai --> <!-- This is an auto-generated comment: skip review by coderabbit.ai --> > [!IMPORTANT] > ## Review skipped > > Auto reviews are disabled on base/target branches other than the default branch. > > > > Please check the settings in the CodeRabbit UI or the `.coderabbit.yaml` file in this repository. To trigger a single review, invoke the `@coderabbitai review` command. > > <details> > <summary>⚙️ Run configuration</summary> > > **Configuration used**: Organization UI > > **Review profile**: ASSERTIVE > > **Plan**: Pro > > **Run ID**: `8d775f94-ae3c-4c86-9842-92705841c244` > > </details> > > You can disable this status message by setting the `reviews.review_status` to `false` in the CodeRabbit configuration file. > > Use the checkbox below for a quick retry: > - [ ] <!-- {"checkboxId": "e9bb8d72-00e8-4f67-9cb2-caf3b22574fe"} --> 🔍 Trigger review <!-- end of auto-generated comment: skip review by coderabbit.ai --> <!-- finishing_touch_checkbox_start --> <details> <summary>✨ Finishing Touches</summary> <details> <summary>🧪 Generate unit tests (beta)</summary> - [ ] <!-- {"checkboxId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} --> Create PR with unit tests - [ ] <!-- {"checkboxId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} --> Commit unit tests in branch `fix-ui-components-jellyfin-10-11-8294678353369689429` </details> </details> <!-- finishing_touch_checkbox_end --> <!-- tips_start --> --- Thanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=devitq/movienight-backend&utm_content=52)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. <details> <summary>❤️ Share</summary> - [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai) - [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai) - [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai) - [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code) </details> <sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub> <!-- tips_end -->
copilot-pull-request-reviewer[bot] (Migrated from github.com) reviewed 2026-05-20 22:05:48 +00:00
copilot-pull-request-reviewer[bot] (Migrated from github.com) left a comment

Pull request overview

This PR refactors the Jellyfin plugin’s injected UI (ui.js) to match Jellyfin 10.11+ DOM structure: adding a MovieNight rating control on item detail pages, adding action buttons to library/home views, and throttling MutationObserver-driven reinjection for stability.

Changes:

  • Replaces the item-detail rating dropdown with an icon button that opens a custom 1–10 rating dialog.
  • Moves “Recommend Film” / “Add Movie (STRM)” into the library toolbar and adds a dedicated “MovieNight” home section.
  • Adds hash-based URL ID detection and throttles UI reinjection via a debounced MutationObserver callback.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

## Pull request overview This PR refactors the Jellyfin plugin’s injected UI (`ui.js`) to match Jellyfin 10.11+ DOM structure: adding a MovieNight rating control on item detail pages, adding action buttons to library/home views, and throttling MutationObserver-driven reinjection for stability. **Changes:** - Replaces the item-detail rating dropdown with an icon button that opens a custom 1–10 rating dialog. - Moves “Recommend Film” / “Add Movie (STRM)” into the library toolbar and adds a dedicated “MovieNight” home section. - Adds hash-based URL ID detection and throttles UI reinjection via a debounced MutationObserver callback. --- 💡 <a href="/devitq/movienight-backend/new/feature/jellyfin-plugin-ui-integration-2323592149917875874?filename=.github/instructions/*.instructions.md" class="Link--inTextBlock" target="_blank" rel="noopener noreferrer">Add Copilot custom instructions</a> for smarter, more guided reviews. <a href="https://docs.github.com/en/copilot/customizing-copilot/adding-repository-custom-instructions-for-github-copilot" class="Link--inTextBlock" target="_blank" rel="noopener noreferrer">Learn how to get started</a>.
@@ -13,66 +13,335 @@
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-05-20 22:05:46 +00:00

createIconButton treats icon (e.g. star_rate) as an extra CSS class on the <span class="material-icons ...">, but Material Icons expects the glyph name as the element text content. As written, the star icon is likely to render blank. Set the span’s textContent/innerText to the icon name (and keep material-icons as the class), or use the same DOM structure Jellyfin uses for detail buttons.

`createIconButton` treats `icon` (e.g. `star_rate`) as an extra CSS class on the `<span class="material-icons ...">`, but Material Icons expects the glyph name as the element text content. As written, the star icon is likely to render blank. Set the span’s textContent/innerText to the icon name (and keep `material-icons` as the class), or use the same DOM structure Jellyfin uses for detail buttons.
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-05-20 22:05:47 +00:00

The guard !document.querySelector('.btnMovieNightRecommend') is global, but this script now renders Recommend/Add buttons in multiple places (library toolbar and home section). If the home section is present, this condition will prevent the library toolbar buttons from being injected (and vice versa). Scope the checks to the intended container (e.g., !toolBar.querySelector(...)) and consider checking each button independently so one missing button doesn’t block the other.

The guard `!document.querySelector('.btnMovieNightRecommend')` is global, but this script now renders Recommend/Add buttons in multiple places (library toolbar and home section). If the home section is present, this condition will prevent the library toolbar buttons from being injected (and vice versa). Scope the checks to the intended container (e.g., `!toolBar.querySelector(...)`) and consider checking each button independently so one missing button doesn’t block the other.
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-05-20 22:05:47 +00:00

showRatingDialog hard-codes dialog colors (#222 background, white text) and several layout styles inline. This will likely clash with non-default themes and undermines the goal of using native Jellyfin styling. Prefer using Jellyfin dialog/backdrop classes (or CSS variables like --theme-*) and minimal inline styles so the dialog inherits theme styling.

`showRatingDialog` hard-codes dialog colors (`#222` background, `white` text) and several layout styles inline. This will likely clash with non-default themes and undermines the goal of using native Jellyfin styling. Prefer using Jellyfin dialog/backdrop classes (or CSS variables like `--theme-*`) and minimal inline styles so the dialog inherits theme styling.
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-05-20 22:05:47 +00:00

createTextButton uses innerHTML to insert the button label even though the content is plain text. Using textContent (and creating the <span> node) avoids unnecessary HTML parsing and reduces XSS risk if this helper is ever reused with non-constant text.

`createTextButton` uses `innerHTML` to insert the button label even though the content is plain text. Using `textContent` (and creating the `<span>` node) avoids unnecessary HTML parsing and reduces XSS risk if this helper is ever reused with non-constant text.
@@ -28,0 +31,4 @@
btn.is = 'emby-button';
btn.className = `button-flat detailButton emby-button ${className}`;
btn.title = title;
btn.innerHTML = `
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-05-20 22:05:46 +00:00

Buttons are created with btn.is = 'emby-button', but is is an HTML attribute (for customized built-in elements) and is not reliably reflected as a JS property. This may result in the element not being upgraded/styled as an emby-button. Prefer setting the attribute (btn.setAttribute('is','emby-button')) or creating the element with the is option, and apply the same fix to the rating-grid buttons.

Buttons are created with `btn.is = 'emby-button'`, but `is` is an HTML attribute (for customized built-in elements) and is not reliably reflected as a JS property. This may result in the element not being upgraded/styled as an `emby-button`. Prefer setting the attribute (`btn.setAttribute('is','emby-button')`) or creating the element with the `is` option, and apply the same fix to the rating-grid buttons.
@@ -36,0 +51,4 @@
if (itemId) {
// MovieNight Rating
if (!document.querySelector('.btnMovieNightRate')) {
const rateBtn = createIconButton('star_rate', 'Rate on MovieNight', 'btnMovieNightRate', (e) => {
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-05-20 22:05:47 +00:00

The rating button is icon-only; relying on title alone is not sufficient for accessible naming in many screen readers. Add an explicit aria-label (and/or visually-hidden text) so the control is discoverable to assistive tech.

The rating button is icon-only; relying on `title` alone is not sufficient for accessible naming in many screen readers. Add an explicit `aria-label` (and/or visually-hidden text) so the control is discoverable to assistive tech.
Sign in to join this conversation.