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