Skip to content

Instantly share code, notes, and snippets.

@muekoeff
Created March 23, 2024 17:51
Show Gist options
  • Save muekoeff/8b4c5ac2d9d36f072eb1e29965b38d3f to your computer and use it in GitHub Desktop.
Save muekoeff/8b4c5ac2d9d36f072eb1e29965b38d3f to your computer and use it in GitHub Desktop.
Favorite tracks on Jellyfin from a list of tracks
// 1. Navigate to: https://your.jellyfin.instance/web/index.html#!/search.html
// 2. Define `const tracks`, each track is an object with `title` and `artist`
// 3. Define `section_title` to match the section title for tracks
// 4. Call `await favItems(tracks);`
const section_title = 'Lieder';
const delay = 750;
async function favItems(items) {
const success = [];
const fail = [];
for (const item of items) {
await search(item);
const favSuccessful = await favItem(item);
if (favSuccessful) {
console.debug(`${item.artist} – ${item.title} succeeded`);
success.push(item);
} else {
console.debug(`${item.artist} – ${item.title} failed`);
fail.push(item);
}
}
return {
fail: fail,
success: success
};
}
async function favItem(track) {
// Find tracks-container
const containers = [...document.querySelectorAll('.searchResults .verticalSection')].filter((container) => {
return container.querySelector('.sectionTitle').textContent === section_title;
});
if (containers.length !== 1) {
return false;
}
const container = containers[0];
// Find track-card
const cards = [...container.querySelectorAll('.card')].filter((card) => {
const title = card.querySelector('.cardText-first').textContent;
const artist = card.querySelector('.cardText-secondary').textContent;
return normaliseTitle(track.title) === normaliseTitle(title) && (normaliseTitle(track.artist) === normaliseTitle(artist) || artist === 'Various Artists');
});
if (cards.length !== 1) {
return false;
}
const card = cards[0];
// Fav track
const fav = card.querySelector('.favorite');
if (!fav.matches('.ratingbutton-icon-withrating')) {
fav.click();
await (new Promise((resolve) => setTimeout(resolve, delay)));
}
return true;
}
function normaliseTitle(title) {
return title.split(' feat. ')[0].replace(/ &/g, ',');
}
async function search(item) {
const fields = document.querySelectorAll('.searchfields-txtSearch');
const field = fields[fields.length - 1];
field.value = item.title;
field.dispatchEvent(new Event('input'));
await (new Promise((resolve) => setTimeout(resolve, delay)));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment