Greasy Fork

Plex web album and artist name swap (test vers)

Swaps the album and artist names so the album name is on top

目前为 2024-02-09 提交的版本。查看 最新版本

// ==UserScript==
// @name         Plex web album and artist name swap (test vers)
// @namespace    http://tampermonkey.net/
// @version      2024-02.2
// @description  Swaps the album and artist names so the album name is on top
// @author       frondonson
// @license      MIT

// @match        https://app.plex.tv/desktop/*
// @match        http://192.168.1.70:32400/web/*

// @icon         https://www.google.com/s2/favicons?sz=64&domain=plex.tv
// @grant        none
// ==/UserScript==


//I haven't found an element that is unique to a music library so anything we wait for will trigger on any recommended or library tab so the waiting method doesn't work
//So, we need a lightweight check to figure out when we open a tab with square album covers and run the init function
setInterval(checkIfMusic, 100);
function checkIfMusic()
{
	//These checks work on any recomended or library page, since there is no diffrence (that I could find) between media types except poster aspect ratio
	let onLibraryPage = document.querySelector('.DirectoryListPageContent-pageContentScroller-O3oHlt') != null;
	let onRecomendedPage = document.querySelector('.DirectoryHubsPageContent-pageContentScroller-jceJrG') != null;
	
	let isMusicLibrary = hasSquarePosters();
	
	if(onLibraryPage && isMusicLibrary)
	{ initLibrary(); }
	if(onRecomendedPage && isMusicLibrary)
	{ initRecomended(); }
	
}

function initLibrary()
{
	//Direct parrent of album [data-testid="cellItem"] divs
	let allAlbumsParent = document.querySelector('div.DirectoryListPageContent-pageContentScroller-O3oHlt>div');
	
	//Set up mutation observer
	var config = { attributes: false, childList: true, subtree: false };
	var observer = new MutationObserver(handleLibraryMutations);

	observer.observe(allAlbumsParent, config);
	
	//Do first batch of albums from page load
	let firstLoadAlbumDivs = allAlbumsParent.querySelectorAll('div[data-testid="cellItem"]');
	doSwaps(firstLoadAlbumDivs);
}

function initRecomended()
{
	//Parrent of ribbon divs
	let allRecomendedParent = document.querySelector('div.DirectoryHubsPageContent-pageContentScroller-jceJrG>div');
	
	//Set up mutation observer, subtree true becase albums are spread out in several parent divs
	var config = { attributes: false, childList: true, subtree: true };
	
	var observer = new MutationObserver(handleRecomendedMutations);

	observer.observe(allRecomendedParent, config);
	
	//Do first batch of albums from page load
	let firstLoadAlbumDivs = allRecomendedParent.querySelectorAll('div[data-testid="cellItem"]');
	doSwaps(firstLoadAlbumDivs);
}

function handleLibraryMutations(mutations)
{
	let newAlbumDivs = mutations[0].target.children;
	
	doSwaps(newAlbumDivs);
}

function handleRecomendedMutations(mutations)
{
	let newAlbumDivs = mutations[0].target.querySelectorAll('div[data-testid="cellItem"]');
	
	doSwaps(newAlbumDivs);
}

function doSwaps(nodeList)
{
	//parrent.children is a HTMLcollection not a nodeList, use iterable for-each
	//https://stackoverflow.com/questions/35969974/foreach-is-not-a-function-error-with-javascript-array
	for (const albumDiv of nodeList) 
	{
		if(albumDiv.childElementCount < 3 || albumDiv.lastChild.nodeName == ('SPAN'))
		{ continue; }
		if(albumDiv.classList.contains('SwapAlbumNameUserScript-swapped'))
		{ continue; }
		
		let artistNode = albumDiv.children[1];
		//Classlist- MetadataPosterCardTitle-centeredSingleLineTitle-EuZHlc MetadataPosterCardTitle-singleLineTitle-lPd1B2 MetadataPosterCardTitle-title-ImAmGu Link-default-bdWb1S Link-isHrefLink-nk7Aiq
		let albumNode = albumDiv.children[2];
		//Classlist- MetadataPosterCardTitle-centeredSingleLineTitle-EuZHlc MetadataPosterCardTitle-singleLineTitle-lPd1B2 MetadataPosterCardTitle-title-ImAmGu MetadataPosterCardTitle-isSecondary-gGuBpd Link-default-bdWb1S Link-isHrefLink-nk7Aiq
		
		artistNode.classList.add('MetadataPosterCardTitle-isSecondary-gGuBpd');
		albumNode.classList.remove('MetadataPosterCardTitle-isSecondary-gGuBpd');
		
		//Swap positions - (x, y) insert node X before node Y
		albumDiv.insertBefore(albumNode, artistNode);
		
		albumDiv.classList.add('SwapAlbumNameUserScript-swapped');
	}
	
}


function hasSquarePosters()
{
	let poster = document.querySelector('.MetadataPosterListItem-card-BfbXw7.PosterCard-card-BRB1k_');
	
	if(poster == null)
	{ return false; }
	
	return (poster.clientHeight == poster.clientWidth);
}