Skip to content

Instantly share code, notes, and snippets.

@kadamwhite
Last active June 18, 2018 17:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kadamwhite/fda8a2c9b94f0ed372f2e401ca1e82b9 to your computer and use it in GitHub Desktop.
Save kadamwhite/fda8a2c9b94f0ed372f2e401ca1e82b9 to your computer and use it in GitHub Desktop.
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