Files
whats-that-music/desktop-client/now-playing.js
T

64 lines
2.1 KiB
JavaScript
Raw Permalink Normal View History

// 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 };