Skip to content

adds a html page and a button to jellyfin 10.9.x for users to get more avatars

Notifications You must be signed in to change notification settings

BobHasNoSoul/jellyfin-avatars

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

73 Commits
 
 
 
 
 
 

Repository files navigation

jellyfin-avatars

keyupdate: it no longer needs the user to download the image and manually add it, it just updates the current users image with a simple tap or click

adds a html page and a button to jellyfin 10.9.x and higher for users to get more avatars

this guide was thrown together as a more in depth way of getting avatars on jellyfin from my jellyfin-mods repo.. if you like this head there and maybe you will see some other things you like.

Thanks NetfreaksShadow for the extra packs of avatars


installation

go to your web root (usually /usr/share/jellyfin/web) now run these commands

sudo wget https://github.com/BobHasNoSoul/jellyfin-avatars/archive/refs/heads/main.zip
sudo unzip main.zip
sudo mv jellyfin-avatars-main/avatars ./avatars

10.9.x version

and now we need to edit the profile tab to enable the button :D

sudo nano user-userprofile.5*.js

now replace the entire files content with the following

"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[16325,96084],{55858:function(e,r,t){t.r(r);var a=t(62540),i=t(64680),n=t(63696),o=t(9055),l=t(89100),s=t(9482),c=t(73233),d=t(22622),u=t(40532),g=t(76165),m=t(41600),f=t(56869),y=t(50764),p=t(7397),h=function(e,r){var t="function"==typeof Symbol&&e[Symbol.iterator];if(!t)return e;var a,i,n=t.call(e),o=[];try{for(;(void 0===r||r-- >0)&&!(a=n.next()).done;)o.push(a.value)}catch(e){i={error:e}}finally{try{a&&!a.done&&(t=n.return)&&t.call(n)}finally{if(i)throw i.error}}return o};r.default=function(){var e=h((0,o.ok)(),1)[0].get("userId"),r=h((0,n.useState)(""),2),t=r[0],v=r[1],A=(0,n.useRef)(null),b=(0,n.useCallback)((function(){var r=A.current;r?e?(f.Ay.show(),window.ApiClient.getUser(e).then((function(e){if(!e.Name||!e.Id)throw new Error("Unexpected null user name or id");v(e.Name),c.default.setTitle(e.Name);var t="assets/img/avatar.png";e.PrimaryImageTag&&(t=window.ApiClient.getUserImageUrl(e.Id,{tag:e.PrimaryImageTag,type:"Primary"})),r.querySelector("#image").style.backgroundImage="url("+t+")",l.default.getCurrentUser().then((function(t){var a;if(!e.Policy)throw new Error("Unexpected null user.Policy");e.PrimaryImageTag?(r.querySelector("#btnAddImage").classList.add("hide"),r.querySelector("#btnDeleteImage").classList.remove("hide")):d.g.supports("fileinput")&&((null===(a=null==t?void 0:t.Policy)||void 0===a?void 0:a.IsAdministrator)||e.Policy.EnableUserPreferenceAccess)&&(r.querySelector("#btnDeleteImage").classList.add("hide"),r.querySelector("#btnAddImage").classList.remove("hide"))})).catch((function(e){console.error("[userprofile] failed to get current user",e)})),f.Ay.hide()})).catch((function(e){console.error("[userprofile] failed to load data",e)}))):console.error("[userprofile] missing user id"):console.error("[userprofile] Unexpected null page reference")}),[e]);return(0,n.useEffect)((function(){var r=A.current;if(r){b();var t=function(e){var r,t;switch(f.Ay.hide(),null===(t=null===(r=e.target)||void 0===r?void 0:r.error)||void 0===t?void 0:t.code){case DOMException.NOT_FOUND_ERR:(0,y.A)(s.Ay.translate("FileNotFound"));break;case DOMException.ABORT_ERR:a();break;default:(0,y.A)(s.Ay.translate("FileReadError"))}},a=function(){f.Ay.hide(),(0,y.A)(s.Ay.translate("FileReadCancelled"))};r.querySelector("#btnDeleteImage").addEventListener("click",(function(){e?(0,u.A)(s.Ay.translate("DeleteImageConfirmation"),s.Ay.translate("DeleteImage")).then((function(){f.Ay.show(),window.ApiClient.deleteUserImage(e,i.y.Primary).then((function(){f.Ay.hide(),b()})).catch((function(e){console.error("[userprofile] failed to delete image",e)}))})).catch((function(){})):console.error("[userprofile] missing user id")})),r.querySelector("#btnAddImage").addEventListener("click",(function(){var e=r.querySelector("#uploadImage");e.value="",e.click()})),r.querySelector("#uploadImage").addEventListener("change",(function(n){!function(n){var o=r.querySelector("#image"),l=n.target.files[0];if(!l||!/image.*/.exec(l.type))return!1;var s=new FileReader;s.onerror=t,s.onabort=a,s.onload=function(){e?(o.style.backgroundImage="url("+s.result+")",window.ApiClient.uploadUserImage(e,i.y.Primary,l).then((function(){f.Ay.hide(),b()})).catch((function(e){console.error("[userprofile] failed to upload image",e)}))):console.error("[userprofile] missing user id")},s.readAsDataURL(l)}(n)}));

        var btnMoreAvatars = document.createElement("button");
        btnMoreAvatars.id = "btnMoreAvatars";
        btnMoreAvatars.className = "raised emby-button"; // Match the style of Delete Image button
        btnMoreAvatars.innerHTML = s.Ay.translate("More Avatars");
        btnMoreAvatars.addEventListener("click", function() {
            window.location.href = "/web/avatars/index.html";
        });

        var btnDeleteImage = r.querySelector("#btnDeleteImage");
        if (btnDeleteImage && btnDeleteImage.parentNode) {
            btnDeleteImage.parentNode.insertBefore(btnMoreAvatars, btnDeleteImage.nextSibling);
        }
    } else console.error("[userprofile] Unexpected null page reference")}),[b,e]),(0,a.jsx)(p.A,{id:"userProfilePage",title:s.Ay.translate("Profile"),className:"mainAnimatedPage libraryPage userPreferencesPage userPasswordPage noSecondaryNavPage",children:(0,a.jsxs)("div",{ref:A,className:"padded-left padded-right padded-bottom-page",children:[(0,a.jsxs)("div",{className:"readOnlyContent",style:{margin:"0 auto",marginBottom:"1.8em",padding:"0 1em",display:"flex",flexDirection:"row",alignItems:"center"},children:[(0,a.jsxs)("div",{className:"imagePlaceHolder",style:{position:"relative",display:"inline-block",maxWidth:200},children:[(0,a.jsx)("input",{id:"uploadImage",type:"file",accept:"image/*",style:{position:"absolute",right:0,width:"100%",height:"100%",opacity:0,cursor:"pointer"}}),(0,a.jsx)("div",{id:"image",style:{width:200,height:200,backgroundRepeat:"no-repeat",backgroundPosition:"center",borderRadius:"100%",backgroundSize:"cover"}})]}),(0,a.jsxs)("div",{style:{verticalAlign:"top",margin:"1em 2em",display:"flex",flexDirection:"column",alignItems:"center"},children:[(0,a.jsx)("h2",{className:"username",style:{margin:0,fontSize:"xx-large"},children:t}),(0,a.jsx)("br",{}),(0,a.jsx)(g.A,{type:"button",id:"btnAddImage",className:"raised emby-button hide",title:"ButtonAddImage"}),(0,a.jsx)(g.A,{type:"button",id:"btnDeleteImage",className:"raised emby-button hide",title:"DeleteImage"})]})]}),(0,a.jsx)(m.A,{userId:e})]})})}}}]);

now you can just simply save this file

one extra edit is needed to make this work (or you will get an api error)

simply edit index.html and add find and replace </body></html> with

<script>
// Function to save credentials to sessionStorage
function saveCredentialsToSessionStorage(credentials) {
  try {
    // Store the credentials in sessionStorage
    sessionStorage.setItem('json-credentials', JSON.stringify(credentials));
    console.log('Credentials saved to sessionStorage.');
  } catch (error) {
    console.error('Error saving credentials:', error);
  }
}

// Function to save the API key to sessionStorage
function saveApiKey(apiKey) {
  try {
    sessionStorage.setItem('api-key', apiKey);
    console.log('API key saved to sessionStorage.');
  } catch (error) {
    console.error('Error saving API key:', error);
  }
}

// Override the default console.log function
(function() {
  var originalConsoleLog = console.log;

  console.log = function(message) {
    // Call the original console.log method
    originalConsoleLog.apply(console, arguments);

    // Check if the message contains the JSON credentials
    if (typeof message === 'string' && message.startsWith('Stored JSON credentials:')) {
      try {
        // Extract the JSON credentials from the message
        var jsonString = message.substring('Stored JSON credentials: '.length);
        var credentials = JSON.parse(jsonString);

        // Save the credentials to sessionStorage
        saveCredentialsToSessionStorage(credentials);
      } catch (error) {
        console.error('Error parsing credentials:', error);
      }
    }

    // Check if the message contains the WebSocket URL with api_key
    if (typeof message === 'string' && message.startsWith('opening web socket with url:')) {
      try {
        // Extract the API key from the message
        var url = message.split('url:')[1].trim();
        var urlParams = new URL(url).searchParams;
        var apiKey = urlParams.get('api_key');

        if (apiKey) {
          saveApiKey(apiKey);
        }
      } catch (error) {
        console.error('Error extracting API key:', error);
      }
    }
  };
})();
</script>
</body></html>

now save the file and clear cache and reload in your client browser / app

all set :D

10.10.x version

for 10.10.x

sudo nano user-userprofile.9cdbcbd8b4ed7a184e73.chunk.js

find className:"raised hide",title:"DeleteImage"})

replace it with


className:"raised hide",title:"DeleteImage"}),
(0, n.jsx)("a", {
    href: "/web/avatars/index.html",
    className: "raised button-submit",
    style: { marginTop: "1em", display: "inline-block", textDecoration: "none", padding: "0.5em 1em", backgroundColor: "#007BFF", color: "#fff", borderRadius: "5px", textAlign: "center" },
    children: "More Avatars"
})

now you can just simply save this file

one extra edit is needed to make this work (or you will get an api error)

simply edit index.html and add find and replace </body></html> with

<script>
// Function to save credentials to sessionStorage
function saveCredentialsToSessionStorage(credentials) {
  try {
    // Store the credentials in sessionStorage
    sessionStorage.setItem('json-credentials', JSON.stringify(credentials));
    console.log('Credentials saved to sessionStorage.');
  } catch (error) {
    console.error('Error saving credentials:', error);
  }
}

// Function to save the API key to sessionStorage
function saveApiKey(apiKey) {
  try {
    sessionStorage.setItem('api-key', apiKey);
    console.log('API key saved to sessionStorage.');
  } catch (error) {
    console.error('Error saving API key:', error);
  }
}

// Override the default console.log function
(function() {
  var originalConsoleLog = console.log;

  console.log = function(message) {
    // Call the original console.log method
    originalConsoleLog.apply(console, arguments);

    // Check if the message contains the JSON credentials
    if (typeof message === 'string' && message.startsWith('Stored JSON credentials:')) {
      try {
        // Extract the JSON credentials from the message
        var jsonString = message.substring('Stored JSON credentials: '.length);
        var credentials = JSON.parse(jsonString);

        // Save the credentials to sessionStorage
        saveCredentialsToSessionStorage(credentials);
      } catch (error) {
        console.error('Error parsing credentials:', error);
      }
    }

    // Check if the message contains the WebSocket URL with api_key
    if (typeof message === 'string' && message.startsWith('opening web socket with url:')) {
      try {
        // Extract the API key from the message
        var url = message.split('url:')[1].trim();
        var urlParams = new URL(url).searchParams;
        var apiKey = urlParams.get('api_key');

        if (apiKey) {
          saveApiKey(apiKey);
        }
      } catch (error) {
        console.error('Error extracting API key:', error);
      }
    }
  };
})();
</script>
</body></html>

now save the file and clear cache and reload in your client browser / app

all set :D

About

adds a html page and a button to jellyfin 10.9.x for users to get more avatars

Resources

Stars

Watchers

Forks

Packages

No packages published