mirror of
https://github.com/ranaldsgift/KefinTweaks.git
synced 2026-11-05 15:52:47 +01:00
Move scripts to /scripts folder
This commit is contained in:
1 parent
584e299219
commit
216be4502a
38 files changed
+490
-112
No files matched your search
@@ -0,0 +1,248 @@
|
||||
// Jellyfin Playlist Script
|
||||
// Modifies playlist view page behavior to navigate to item details instead of playing
|
||||
// Adds play button to playlist items
|
||||
// Requires: utils.js, cardBuilder.js module to be loaded before this script
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
// Common logging function
|
||||
const LOG = (...args) => console.log('[KefinTweaks Playlist]', ...args);
|
||||
const WARN = (...args) => console.warn('[KefinTweaks Playlist]', ...args);
|
||||
const ERR = (...args) => console.error('[KefinTweaks Playlist]', ...args);
|
||||
|
||||
LOG('Initializing...');
|
||||
|
||||
/**
|
||||
* Modifies playlist item click behavior to navigate to details page
|
||||
*/
|
||||
function modifyPlaylistItemClicks() {
|
||||
const libraryPage = document.querySelector('.libraryPage:not(.hide)');
|
||||
if (!libraryPage) {
|
||||
WARN('Library page not found');
|
||||
return;
|
||||
}
|
||||
|
||||
let childrenItemsContainer = libraryPage.querySelector('.childrenItemsContainer');
|
||||
if (!childrenItemsContainer) {
|
||||
childrenItemsContainer = libraryPage.querySelector('#listChildrenCollapsible');
|
||||
if (!childrenItemsContainer) {
|
||||
WARN('Children items container not found');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const playlistItems = childrenItemsContainer.querySelectorAll('.listItem[data-playlistitemid]');
|
||||
LOG(`Found ${playlistItems.length} playlist items`);
|
||||
|
||||
playlistItems.forEach((item, index) => {
|
||||
if (item.dataset.customPlaylistButton === 'true') {
|
||||
LOG(`Playlist item ${index} already has a custom button, skipping`);
|
||||
return;
|
||||
}
|
||||
|
||||
const playlistItemId = item.getAttribute('data-playlistitemid');
|
||||
const serverId = ApiClient.serverId();
|
||||
|
||||
if (!playlistItemId) {
|
||||
WARN(`Playlist item ${index} has no data-playlistitemid`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove existing click handlers
|
||||
item.removeAttribute('data-action');
|
||||
item.style.cursor = 'pointer';
|
||||
|
||||
// Add new click handler to navigate to details page
|
||||
item.addEventListener('click', (e) => {
|
||||
handlePlaylistItemClick(e, item, playlistItemId, serverId);
|
||||
});
|
||||
|
||||
item.dataset.customPlaylistButton = 'true';
|
||||
});
|
||||
}
|
||||
|
||||
function handlePlaylistItemClick(e, item, playlistItemId, serverId) {
|
||||
// Check if the click target is within the listViewUserDataButtons container
|
||||
const buttonsContainer = item.querySelector('.listViewUserDataButtons');
|
||||
if (buttonsContainer && buttonsContainer.contains(e.target)) {
|
||||
LOG(`Click on button detected, not navigating for item: ${playlistItemId}`);
|
||||
return; // Don't navigate when clicking on buttons
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const detailsUrl = `/details?id=${playlistItemId}&serverId=${serverId}`;
|
||||
LOG(`Navigating to: ${detailsUrl}`);
|
||||
|
||||
// Use Jellyfin's navigation method
|
||||
if (Dashboard) {
|
||||
Dashboard.navigate(detailsUrl);
|
||||
} else {
|
||||
// Fallback to direct navigation
|
||||
const fullDetailsUrl = `${ApiClient.serverAddress()}/web/#${detailsUrl}`;
|
||||
window.location.href = fullDetailsUrl;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds play button to each playlist item's listViewUserDataButtons container
|
||||
*/
|
||||
function addPlayButtons() {
|
||||
const libraryPage = document.querySelector('.libraryPage:not(.hide)');
|
||||
if (!libraryPage) {
|
||||
WARN('Library page not found for play buttons');
|
||||
return;
|
||||
}
|
||||
|
||||
let childrenItemsContainer = libraryPage.querySelector('.childrenItemsContainer');
|
||||
if (!childrenItemsContainer) {
|
||||
childrenItemsContainer = libraryPage.querySelector('#listChildrenCollapsible');
|
||||
if (!childrenItemsContainer) {
|
||||
WARN('Children items container not found for play buttons');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const playlistItems = childrenItemsContainer.querySelectorAll('.listItem[data-playlistitemid]');
|
||||
LOG(`Found ${playlistItems.length} playlist items to add play buttons to`);
|
||||
|
||||
playlistItems.forEach((item, index) => {
|
||||
const playlistItemId = item.getAttribute('data-playlistitemid');
|
||||
|
||||
if (!playlistItemId) {
|
||||
WARN(`Playlist item ${index} has no data-playlistitemid`);
|
||||
return;
|
||||
}
|
||||
|
||||
const buttonsContainer = item.querySelector('.listViewUserDataButtons');
|
||||
if (!buttonsContainer) {
|
||||
WARN(`No listViewUserDataButtons container found for playlist item ${index}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if play button already exists for this item
|
||||
const existingPlayButton = buttonsContainer.querySelector('.playlist-play-button');
|
||||
if (existingPlayButton) {
|
||||
LOG(`Play button already exists for item ${index}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create play button following the same pattern as other buttons
|
||||
// Play button
|
||||
const playButton = document.createElement('button');
|
||||
playButton.setAttribute('is', 'paper-icon-button-light');
|
||||
playButton.className = 'itemAction paper-icon-button-light playBtn';
|
||||
playButton.setAttribute('data-action', 'resume');
|
||||
|
||||
const playIcon = document.createElement('span');
|
||||
playIcon.className = 'material-icons play_arrow';
|
||||
playIcon.setAttribute('aria-hidden', 'true');
|
||||
playButton.appendChild(playIcon);
|
||||
|
||||
// Prepend the play button to the container
|
||||
buttonsContainer.insertBefore(playButton, buttonsContainer.firstChild);
|
||||
});
|
||||
|
||||
LOG(`Added play buttons to ${playlistItems.length} playlist items`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function to modify the playlist page
|
||||
* @param {string} itemId - The playlist item ID
|
||||
*/
|
||||
async function modifyPlaylistPage() {
|
||||
if (document.querySelector('.libraryPage:not(.hide)')?.dataset?.kefinPlaylist === 'true') {
|
||||
LOG('Playlist page already modified, skipping');
|
||||
return;
|
||||
}
|
||||
|
||||
// Poll for page elements to be ready (every 100ms for up to 10 seconds)
|
||||
const pollInterval = 100;
|
||||
const maxAttempts = 100; // 100ms * 100 = 10 seconds
|
||||
let attempts = 0;
|
||||
|
||||
const pollForElements = () => {
|
||||
attempts++;
|
||||
|
||||
const libraryPage = document.querySelector('.libraryPage:not(.hide)');
|
||||
if (!libraryPage) {
|
||||
if (attempts < maxAttempts) {
|
||||
setTimeout(pollForElements, pollInterval);
|
||||
} else {
|
||||
WARN('Library page not found after 10 seconds');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let childrenItemsContainer = libraryPage.querySelector('.childrenItemsContainer');
|
||||
if (!childrenItemsContainer) {
|
||||
childrenItemsContainer = libraryPage.querySelector('#listChildrenCollapsible');
|
||||
if (!childrenItemsContainer) {
|
||||
if (attempts < maxAttempts) {
|
||||
setTimeout(pollForElements, pollInterval);
|
||||
} else {
|
||||
WARN('Children items container not found after 10 seconds');
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const playlistItems = childrenItemsContainer.querySelectorAll('.listItem[data-playlistitemid]');
|
||||
if (playlistItems.length === 0) {
|
||||
if (attempts < maxAttempts) {
|
||||
setTimeout(pollForElements, pollInterval);
|
||||
} else {
|
||||
WARN('No playlist items found after 10 seconds');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (libraryPage.dataset.kefinPlaylist === 'true') {
|
||||
LOG('Playlist page already modified, skipping');
|
||||
return;
|
||||
}
|
||||
|
||||
// Elements found, proceed with modifications
|
||||
libraryPage.dataset.kefinPlaylist = 'true';
|
||||
modifyPlaylistItemClicks();
|
||||
addPlayButtons();
|
||||
LOG('Playlist page modifications complete');
|
||||
};
|
||||
|
||||
pollForElements();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize playlist hook using utils
|
||||
*/
|
||||
function initializePlaylistHook() {
|
||||
if (!window.KefinTweaksUtils) {
|
||||
WARN('KefinTweaksUtils not available, retrying in 1 second');
|
||||
setTimeout(initializePlaylistHook, 1000);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG('Registering playlist handler with KefinTweaksUtils');
|
||||
|
||||
// Register handler for details pages
|
||||
window.KefinTweaksUtils.onViewPage(async (view, element, hash, itemPromise) => {
|
||||
// Await the item promise to get the actual item data
|
||||
const item = await itemPromise;
|
||||
if (item && item.Type === 'Playlist') {
|
||||
modifyPlaylistPage();
|
||||
return;
|
||||
}
|
||||
}, {
|
||||
pages: ['details']
|
||||
});
|
||||
|
||||
LOG('Playlist hook initialized successfully');
|
||||
}
|
||||
|
||||
// Initialize the hook when the script loads
|
||||
initializePlaylistHook();
|
||||
|
||||
console.log('[KefinTweaks Playlist] Module loaded and ready');
|
||||
})();
|
||||
Reference in new issue
Block a user