• Login
  • Register
  • Login Register
    Login
    Username/Email:
    Password:
    Or login with a social network below
  • Forum
  • Website
  • GitHub
  • Status
  • Translation
  • Features
  • Team
  • Rules
  • Help
  • Feeds
User Links
  • Login
  • Register
  • Login Register
    Login
    Username/Email:
    Password:
    Or login with a social network below

    Useful Links Forum Website GitHub Status Translation Features Team Rules Help Feeds
    Jellyfin Forum Development Web Development Jellyfin UI Enhancements

     
    • 0 Vote(s) - 0 Average

    Jellyfin UI Enhancements

    Injecting pages or code into jellyfin-web for pseudo-plugin functionality
    SethBacon
    Offline

    Member

    Posts: 66
    Threads: 7
    Joined: 2023 Nov
    Reputation: 8
    Country:Canada
    #2
    2024-10-28, 08:02 PM
    Some example functions. Note: for a lot of media manipulation you will want to generate an api code in jellyfin Admin settings, thenm hardcode it into your injections to make api calls. 

    Code:
    // Get the current authenticated user's info
    const getCurrentUser = () => {
        return fetch('/Users/Me', {
            method: 'GET',
            headers: {
                'Authorization': `MediaBrowser Token="${ApiClient.accessToken()}"`,
                'Content-Type': 'application/json'
            }
        })
        .then(response => response.json())
        .catch(error => {
            console.error('Error getting current user:', error);
            return null;
        });
    };

    // Get a user's profile image URL by their ID
    const getUserImageUrl = (userId) => {
        return `${ApiClient.serverAddress()}/Users/${userId}/Images/Primary?quality=90`;
    };

    // Update a media item's tags
    const updateMediaTags = (itemId, newTags) => {
        return ApiClient.getItem(ApiClient.getCurrentUserId(), itemId)
            .then(item => {
                const updatedTags = [...new Set([...item.Tags, ...newTags])];
                return ApiClient.updateItem({
                    Id: itemId,
                    Tags: updatedTags
                });
            })
            .catch(error => {
                console.error('Error updating tags:', error);
                return false;
            });
    };

    // Watch for navigation changes in Jellyfin's SPA
    const watchNavigation = (callback) => {
        const observer = new MutationObserver((mutations) => {
            mutations.forEach((mutation) => {
                if (mutation.type === 'childList' &&
                    mutation.target.classList.contains('view')) {
                    callback(window.location.hash);
                }
            });
        });
       
        observer.observe(document.querySelector('div[data-role="content"]'), {
            childList: true,
            subtree: true
        });
       
        return observer;
    };

    // Inject a custom element into a specific page section
    const injectElement = (targetSelector, htmlContent, position = 'beforeend') => {
        const target = document.querySelector(targetSelector);
        if (target) {
            target.insertAdjacentHTML(position, htmlContent);
            return true;
        }
        return false;
    };

    // Get all users and their online status
    const getOnlineUsers = () => {
        return fetch('/Users/Query', {
            method: 'GET',
            headers: {
                'Authorization': `MediaBrowser Token="${ApiClient.accessToken()}"`,
                'Content-Type': 'application/json'
            }
        })
        .then(response => response.json())
        .then(users => {
            return ApiClient.getSessions()
                .then(sessions => {
                    return users.map(user => ({
                        ...user,
                        isOnline: sessions.some(session => session.UserId === user.Id)
                    }));
                });
        })
        .catch(error => {
            console.error('Error getting online users:', error);
            return [];
        });
    };

    // Add a custom button to the media details page
    const addCustomButton = (text, onClick) => {
        const buttonHtml = `
            <button is="emby-button"
                    type="button"
                    class="button-flat btnPlay button-flat-custom raised">
                <span>${text}</span>
            </button>
        `;
       
        injectElement('.detailButtons', buttonHtml);
       
        const button = document.querySelector('.button-flat-custom');
        if (button) {
            button.addEventListener('click', onClick);
        }
    };

    // Watch for theme changes and apply custom styles
    const watchThemeChanges = (callback) => {
        const observer = new MutationObserver((mutations) => {
            mutations.forEach((mutation) => {
                if (mutation.attributeName === 'data-theme') {
                    const theme = document.documentElement.getAttribute('data-theme');
                    callback(theme);
                }
            });
        });
       
        observer.observe(document.documentElement, {
            attributes: true,
            attributeFilter: ['data-theme']
        });
       
        return observer;
    };
    // Example usage of the functions:
    /*
    // Watch for navigation changes
    const navObserver = watchNavigation((newPath) => {
        console.log('Navigation changed to:', newPath);
        if (newPath.includes('details')) {
            addCustomButton('My Custom Action', () => {
                console.log('Custom button clicked!');
            });
        }
    });

    // Watch for theme changes
    const themeObserver = watchThemeChanges((newTheme) => {
        console.log('Theme changed to:', newTheme);
        // Apply custom styles based on theme
    });

    // Get current user and update UI
    async function updateUserInfo() {
        const user = await getCurrentUser();
        if (user) {
            const userImage = getUserImageUrl(user.Id);
            console.log('Current user:', user.Name);
            console.log('User image:', userImage);
        }
    }

    // Update media tags
    async function addTagToMedia(itemId, newTag) {
        const success = await updateMediaTags(itemId, [newTag]);
        if (success) {
            console.log('Tags updated successfully');
        }
    }

    // Check online users
    async function showOnlineUsers() {
        const users = await getOnlineUsers();
        users.forEach(user => {
            console.log(`${user.Name} is ${user.isOnline ? 'online' : 'offline'}`);
        });
    }
    */
    « Next Oldest | Next Newest »

    Users browsing this thread: 1 Guest(s)


    Messages In This Thread
    Jellyfin UI Enhancements - by SethBacon - 2024-10-28, 07:57 PM
    RE: Jellyfin UI Enhancements - by SethBacon - 2024-10-28, 08:02 PM
    RE: Jellyfin UI Enhancements - by M0RPH3US - 2024-10-29, 05:27 PM
    RE: Jellyfin UI Enhancements - by Ted Hinklater - 2024-10-29, 04:48 PM
    RE: Jellyfin UI Enhancements - by TheDreadPirate - 2024-10-29, 06:07 PM
    RE: Jellyfin UI Enhancements - by Valentin - 2024-10-31, 10:24 AM
    RE: Jellyfin UI Enhancements - by locoboco - 2024-12-30, 08:27 AM
    RE: Jellyfin UI Enhancements - by OneMeanRabbit - 2025-04-08, 08:14 AM
    RE: Jellyfin UI Enhancements - by JellyHunter - 2025-04-21, 08:48 PM

    • View a Printable Version
    • Subscribe to this thread
    Forum Jump:

    Home · Team · Help · Contact
    © Designed by D&D - Powered by MyBB
    L


    Jellyfin

    The Free Software Media System

    Linear Mode
    Threaded Mode