Jump to content

Jellyseerr embedded in Emby via embyTabs (i.e., Home | Favorites | Requests)


Recommended Posts

KobayashiM
Posted

I was checking out Jellyfin and noticed they have a Custom Tabs plugin that can add new tabs to the Home/Favorites menu. So I added a "Requests" tab which launches Jellyseerr in an iframe and it was awesome. Unfortunately there's no such plugin for Emby that I could find. So I resorted to modifying web files and wrote a script I can run whenever the container needs to be re-built.

Here's what it looks like un-selected:

image.thumb.png.579c4ce9d3df668fc806dd04f5571608.png

Selected:

image.thumb.png.87983fe68d78a486b8d221c56031c39e.png

 

The only thing I have left to do is implement SSO so users don't have to login to Jellyseerr manually.

Instructions in next post...

  • Like 5
  • Agree 1
Posted

Thanks for sharing.

KobayashiM
Posted (edited)

Note: I run Emby and Jellyseerr containers on the same physical machine which is running Unraid OS. My reverse proxy setup is handled by Swag and domain is hosted with Cloudflare.

Pre-requisites as of today:

- Emby v4.9.1.90
- Jellyseerr v2.7.3
- Both containers can see each other on same custom network


EMBY CHANGES
Container directory: /app/emby/system/dashboard-ui/home/

Modified Files: home.html, home.js

New Files: requests.js


1. Create a backup of home.html and home.js

2. Add a new option "Requests" to the Home/Favorites button (called embyTabs)...

   a) Add a new <div> element at the bottom of home.html and assign it data-index="2":

<div class="view flex flex-direction-column withTabs">

    <div class="tabContent tabContent-positioned flex flex-grow" data-index="0" data-swapnode="sectionstab">

    </div>
    <div class="tabContent tabContent-positioned flex flex-grow" data-index="1" data-swapnode="sectionstab">

    </div>
	<div class="tabContent tabContent-positioned flex flex-grow" data-index="2" data-swapnode="sectionstab">

    </div>

</div>

 


   b) Edit home.js and search for "favorites". Copy the syntax and create a new entry for "Requests" (there are two spots):

define(["exports", "./../modules/tabbedview/tabbedview.js", "./../modules/common/globalize.js", "./../modules/maintabsmanager.js", "./../modules/layoutmanager.js", "./../modules/common/usersettings/usersettings.js", "./../modules/emby-elements/emby-scroller/emby-scroller.js", "./../modules/emby-elements/emby-button/emby-button.js"], function(_exports, _tabbedview, _globalize, _maintabsmanager, _layoutmanager, _usersettings, _embyScroller, _embyButton) {
    function HomeView(view, params) {
        _tabbedview.default.apply(this, arguments), this.enableBackMenu = !0
    }
    Object.defineProperty(_exports, "__esModule", {
        value: !0
    }), _exports.default = void 0, Object.assign(HomeView.prototype, _tabbedview.default.prototype), HomeView.prototype.getTabs = function() {
        return [{
            name: _globalize.default.translate("Home"),
            id: "home"
        }, {
            name: _globalize.default.translate("Favorites"),
            id: "favorites"
        }, {
            name: _globalize.default.translate("Requests"),
            id: "requests"
        }]
    }, HomeView.prototype.getAutoBackdropItemTypes = function() {
        return ["Movie", "Series", "Game", "Book"]
    }, HomeView.prototype.setTitle = function() {}, HomeView.prototype.supportsHorizontalTabScroll = function() {
        return !0
    }, HomeView.prototype.tabScrollDirection = function() {
        return this.supportsHorizontalTabScroll() && _layoutmanager.default.tv && "horizontal" === _usersettings.default.tvHome() ? "x" : "y"
    }, HomeView.prototype.onPause = function() {
        _tabbedview.default.prototype.onPause.call(this)
    }, HomeView.prototype.destroy = function() {
        _tabbedview.default.prototype.destroy.apply(this, arguments)
    }, HomeView.prototype.loadTabController = function(id) {
        switch (id) {
            case "home":
                return Emby.importModule("./home/hometab.js");
            case "favorites":
                return Emby.importModule("./home/favorites.js");
            case "requests":
                return Emby.importModule("./home/requests.js");
            default:
                throw new Error("tab not found: " + id)
        }
    }, HomeView.prototype.onWindowInputCommand = function(e) {
        "home" === e.detail.command ? (_maintabsmanager.default.selectedTabIndex(0), e.preventDefault()) : _tabbedview.default.prototype.onWindowInputCommand.apply(this, arguments)
    };
    _exports.default = HomeView
});

 


   c) Create a new file requests.js which will contain an iframe that can be pointed to Jellyseerr. This file is called by home.js.
      Note: the 'padding-top' parameter pushes the page down a bit so that Emby's menu doesn't overlap with Jellyseer's.

define([], function () {

    return function RequestsTab(view) {

        view.innerHTML = `
		    <div style="
                width:100%;
                height:100%;
                box-sizing:border-box;
                padding-top:60px;
            ">
				<iframe
					src="https://requests.example.net"
					style="width:100%; height:100%; border:0;"
					allowfullscreen
					>
				</iframe>
			</div>
        `;
    };

});

 


3. Upload home.html, home.js and requests.js to the container dir: /app/emby/system/dashboard-ui/home/

4. Create Unraid user script to backup original Emby files and upload custom files to the container.

Note: I keep my custom files in the appdata folder '/emby/custom-files' which is mapped in the container as '/config/custom-files'.

#!/bin/bash
#
# CURRENT AS OF Emby v4.9.1.90
#
# This script should be run any time the Emby container has been re-built.
# In the event that a newer release of Emby breaks this script due to changing file paths, 
# locate the files manually and update this script accordingly.
#
# Use "docker exec emby" at the start of each command to run it in the Emby container

# Backup and replace home.html with edited version that displays custom link at the top of the home page. This may require clearing image cache in browser.
docker exec emby cp /app/emby/system/dashboard-ui/home/home.html /app/emby/system/dashboard-ui/home/home.html.bak
docker exec emby cp /config/custom-files/dashboard-ui/home/home.html /app/emby/system/dashboard-ui/home/

# Backup and replace home.js with modified version that adds a 'Requests' tab to the main page (next to favorites)
docker exec emby cp /app/emby/system/dashboard-ui/home/home.js /app/emby/system/dashboard-ui/home/home.js.bak
docker exec emby cp /config/custom-files/dashboard-ui/home/home.js /app/emby/system/dashboard-ui/home/

# Copy new file requests.js to Emby container. This file contains an <iframe> with contents referencing https://requests.example.net
docker exec emby cp /config/custom-files/dashboard-ui/home/requests.js /app/emby/system/dashboard-ui/home/

 

Next, we need to mess with networking a bit because Jellyseerr does not work properly in an iFrame due to cross-site cookie handling being set to 'SameSite=Lax'.

To be honest, I'm not fully clear which of these changes made it work because the cookie is still 'SameSite=Lax' when I check the developer console in Chrome. There was some trial and error required to get this working.

 

JELLYSEERR

1. Add the following variables to the container template:

Name: AUTH_COOKIE_SAMESITE
Key: AUTH_COOKIE_SAMESITE
Value: None

Name: AUTH_COOKIE_SECURE
Key: AUTH_COOKIE_SECURE
Value: true


2. Enabled in UI: Settings> Network> Enable Proxy Support


NETWORKING
SWAG
1. Added the following lines in the Location block within proxy-confs file jellyseerr.subdomain.conf:

            # Not all (or any) of these may be necessary--further testing needed
            proxy_cookie_path / "/;Secure; SameSite=None";
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $host;

 

CLOUDFLARE
1. Disabled Cloudflare Proxy Status for root DNS record as well as subdomain record I'm using for Jellyseerr 
2. Set Caching Level: No query string (might not be necessary)
3. Set Browser Cache TTL: Respect Existing Headers (might not be necessary)


Done. Clear your browser cache/cookies and re-load Emby. I had some trouble with javascript files not refreshing right away so I thought it wasn't working. If you open the developer console in Chrome you can disable cache under the Network tab.

Enjoy

Edited by KobayashiM
KobayashiM
Posted (edited)

I can't edit my last post for some reason so just want to add a custom css which reduces the size of the top header so it doesn't block Jellyseerr's search bar. Also made it transparent. Figuring out as I go...

/* ------------------------ TRANSPARENT TOP BAR -------------------------------- */
div.skinHeader.skinHeader-withBackground {
  background: transparent !important;
  height: 60px;
}

 

@AdminCould we move this to the Web App CSS forum if you agree it's more appropriate there? Thanks

Edited by KobayashiM
ttgapers
Posted

Thumbs up for this @KobayashiM

Will keep following with keen interest.

KobayashiM
Posted
define([], function () {

    return function RequestsTab(view) {

        view.innerHTML = `
		    <div style="
                width:100%;
                height:100%;
                box-sizing:border-box;
                padding-top:65px;
                overflow-y: hidden;
            ">
				<iframe
					src="https://requests.example.net"
					style="width:100%; height:100%; border:0;"
					allowfullscreen
					>
				</iframe>
			</div>
        `;
    };

});

Updated requests.js to slightly increase top padding because the entire iframe scrolls up into the top header about 5 pixels when you reach the bottom of the page but the scroll wheel keeps going. Tried to hide the parent scrollbar but was only half successful as an empty vertical scrollbar is still visible.

  • 2 weeks later...
KobayashiM
Posted

I've decided to try modding the android app just as a learning experience. I kind of got this to work there but can't loging to Jellyseerr. Probably the same issue with cross-site cookies but I'm not sure what more I can do to solve that on the client end. I'll keep messing with it...

Screenshot_20260204_212543_Danflix.thumb.jpg.a531c541d691aad2591bff115b20e799.jpg

Screenshot_20260204_212559_Danflix.thumb.jpg.3940a145839ea4fb4919d272588232bc.jpg

Babatom
Posted

@LukeThat would be a killer feature if it come's to Apple TV/Android TV clients. 😍

  • Agree 1
  • Thanks 1

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...