Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Paste into Dev Tools while running Plex to automatically work through your library and assign default album art.
var albumArt = (() => {
// Helpers
// (e.g. DOM shortcuts and class-checking utilities)
var qsa = ( container, selector ) => [ ...container.querySelectorAll( selector ) ];
var nodeHasClass = ( node, str ) => node.classList.toString().indexOf( str ) > -1;
var whereNodeHasClass = ( str ) => ( node ) => nodeHasClass( node, str );
var wherePropertyLike = ( obj, str ) => {
const key = Object.keys( obj ).find( key => key.indexOf( str ) > -1 );
return key ? obj[ key ] : null;
};
// Inspectors
// (e.g. find things out about the DOM)
var needsArt = ( node ) => qsa( node, 'i' ).reduce( ( hasPlaceholder, node ) => (
hasPlaceholder || nodeHasClass( node, 'placeholderIcon' )
), false );
var getReactInstance = node => {
const key = Object.keys(node).filter(key => key.indexOf('react')>-1)[0];
return node[ key ];
};
// Interactions
// (e.g. do things to the page)
var press = ( node ) => {
const rII = getReactInstance( node );
const ownerInstance = rII._currentElement._owner._instance;
ownerInstance.props.onPress();
};
var openModal = ( item ) => {
const editButton = qsa( item, 'button' ).filter( whereNodeHasClass( 'editButton' ) )[ 0 ];
press( editButton );
};
var selectPosterTab = () => document.querySelector('a.poster-btn').click();
var selectArtwork = () => {
const artList = document.querySelector('.artwork-options-list');
const art = artList && artList.querySelector('a.artwork-option');
art && art.click();
return ! ! art;
};
var save = () => document.querySelector('.save-btn').click();
var cancel = () => document.querySelector('.cancel-btn').click();
var execute = fn => () => new Promise( resolve => {
try {
fn();
resolve();
} catch(e) { reject( e ); }
});
var wait = delay => () => new Promise( resolve => setTimeout( resolve, delay ) );
var waitFor = selector => () => {
const test = () => document.querySelector( selector ) !== null;
return new Promise( ( resolve, reject ) => {
// Nothing should take more than 5 seconds (I hope)
const rejectionTimeout = setTimeout( reject, 5000 );
const pollID = setInterval( () => {
if ( test() ) {
clearInterval( pollID );
clearTimeout( rejectionTimeout );
resolve();
}
}, 17 );
} );
};
var log = msg => () => console.log( msg );
// Main Logic
var scroller = qsa( document, 'div' ).filter( whereNodeHasClass( 'MetadataListPageContent-metadataListScroller' ) )[0];
var scrollTop = scroller.scrollTop;
var getVisibleItems = () => [ ...scroller.children[0].children ];
var runInSequence = arrOfPromises => arrOfPromises
.reduce(( lastStep, nextStep ) => lastStep.then( () => nextStep() ), Promise.resolve() );
var getAmountToScroll = () => {
const { min, max } = getVisibleItems().reduce(( minMax, item ) => {
const boundingRect = item.getBoundingClientRect();
return {
min: Math.min( boundingRect.top, minMax.min ),
max: Math.max( boundingRect.bottom, minMax.max ),
};
}, {
min: Infinity,
max: -Infinity,
});
return max - min - 200; // -200 to avoid skipping items
};
let run = true;
var populateArt = () => {
if ( ! run ) {
console.log('Processing Cancelled');
return;
}
const visibleItems = getVisibleItems();
const artlessItems = visibleItems.filter( needsArt );
var process = item => Promise.resolve( openModal( item ) )
.then( log( 'Modal open' ) )
.then( waitFor( 'a.poster-btn' ) )
.then( log( 'Modal Ready' ) )
.then( wait( 1000 ) )
.then( selectPosterTab )
.then( log( 'Selected "Poster" tab' ) )
.then( wait( 200 ) )
.then( () => {
var hasPosters = selectArtwork();
console.log( hasPosters );
if ( hasPosters ) {
return Promise.resolve()
.then( log( 'Artwork Selected' ) )
.then( wait( 1500 ) )
.then( save )
.then( log( 'Artwork saved!' ) );
}
return Promise.resolve()
.then( cancel )
.then( log( 'No artwork available' ) );
} )
.then( wait( 1000 ) )
.catch( err => console.error( err ) );
// return runInSequence( artlessItems.map( item => () => process( item ) ) )
const fnsReturningPromises = artlessItems.map( item => () => process( item ) );
return runInSequence( fnsReturningPromises )
.then(() => scroller.scrollTop += getAmountToScroll())
.then( wait( 1000 ) )
.then( populateArt );
};
return {
start: () => {
run = true;
populateArt();
},
runOne: () => {
run = true;
populateArt();
run = false;
},
stop: () => {
run = false;
},
};
})();
/**
* Run in console with a TV show season open to add an input
* which can be used to specify a custom title.
*/
(() => {
const row = document.createElement('div');
row.classList.add('row');
row.innerHTML = `
<div class="col-md-12">
<div class="form-group selectize-group">
<label for="season-title">Season Title</label>
<div class="input-group">
<input class="form-control" id="season-title" name="title" placeholder="Season Title">
</div>
</div>
</div>
`;
$('.edit-metadata-form').prepend(row);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.