64 lines
2.1 KiB
JavaScript
64 lines
2.1 KiB
JavaScript
// Extracts the current track from a YouTube Music page. `doc` and `nav` are
|
|||
|
|
// injected so this stays testable without a browser.
|
||
|
|
|
||
|
|
function largestArtwork(artwork) {
|
||
|
|
if (!artwork || !artwork.length) return null;
|
||
|
|
const area = (a) => {
|
||
|
|
const [w, h] = String(a.sizes || '').split('x').map(Number);
|
||
|
|
return (w || 0) * (h || 0);
|
||
|
|
};
|
||
|
|
return artwork.reduce((best, a) => (area(a) > area(best) ? a : best)).src || null;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Preferred source: YT Music publishes the track via the Media Session API
|
||
|
|
// even when its player-bar markup changes.
|
||
|
|
function fromMediaSession(nav) {
|
||
|
|
const session = nav && nav.mediaSession;
|
||
|
|
const meta = session && session.metadata;
|
||
|
|
if (!meta || !meta.title) return null;
|
||
|
|
return {
|
||
|
|
title: meta.title,
|
||
|
|
artist: meta.artist || null,
|
||
|
|
albumArt: largestArtwork(meta.artwork),
|
||
|
|
isPlaying: session.playbackState === 'playing',
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
// Fallback for older YT Music layouts that still render ytmusic-player-bar.
|
||
|
|
function fromPlayerBar(doc) {
|
||
|
|
const titleEl =
|
||
|
|
doc.querySelector('ytmusic-player-bar .title') ||
|
||
|
|
doc.querySelector('.ytmusic-player-bar .title');
|
||
|
|
const title = titleEl ? titleEl.textContent.trim() : null;
|
||
|
|
if (!title) return null;
|
||
|
|
|
||
|
|
const bylineEl =
|
||
|
|
doc.querySelector('ytmusic-player-bar .byline') ||
|
||
|
|
doc.querySelector('.ytmusic-player-bar .byline');
|
||
|
|
const artImg =
|
||
|
|
doc.querySelector('ytmusic-player-bar .middle-controls .thumbnail-image-wrapper img') ||
|
||
|
|
doc.querySelector('ytmusic-player-bar .image img') ||
|
||
|
|
doc.querySelector('ytmusic-player-bar img.image');
|
||
|
|
|
||
|
|
// The button's title is the action it will perform: "Pause" while playing.
|
||
|
|
const btn = doc.querySelector('#play-pause-button');
|
||
|
|
let isPlaying = false;
|
||
|
|
if (btn) {
|
||
|
|
const label = (btn.getAttribute('title') || btn.getAttribute('aria-label') || '').toLowerCase();
|
||
|
|
isPlaying = label.includes('pause');
|
||
|
|
}
|
||
|
|
|
||
|
|
return {
|
||
|
|
title,
|
||
|
|
artist: bylineEl ? bylineEl.textContent.trim() : null,
|
||
|
|
albumArt: artImg ? artImg.src : null,
|
||
|
|
isPlaying,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
function scrapeNowPlaying(doc, nav) {
|
||
|
|
return fromMediaSession(nav) || fromPlayerBar(doc);
|
||
|
|
}
|
||
|
|
|
||
|
|
module.exports = { scrapeNowPlaying };
|