8 hours ago
(This post was last modified: 8 hours ago by TheDreadPirate. Edited 2 times in total.)
(2024-10-28, 08:02 PM)SethBacon Wrote: 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'}`);
});
}
*/
Oh that looks sick. I have a custom varient of my bar which yet I didnot release as public (it's there on Bob's repo as the makd version, I have some local tweaks which I will do a public release once I get my head around), but to tackle the SPA issue, we can kind of prevent the default behaviour for all buttons (header bar home, hamburger menu home, and the jellylogo at the top of the dashboard) to route it to
Code:
window.location.href = '/web/index.html#/home.html
But @SethBacon and @Ted Hinklater for sure, look forward really to Collab for the bar again