mirror of
https://github.com/Alexander-D-Karpov/about.git
synced 2026-03-16 22:06:08 +03:00
417 lines
15 KiB
JavaScript
417 lines
15 KiB
JavaScript
(function() {
|
|
'use strict';
|
|
|
|
let customAudioElement = null;
|
|
let isCustomPlaying = false;
|
|
let currentLoadedTrack = null;
|
|
let musicPlayerInitialized = false;
|
|
let isLoadingTrack = false;
|
|
|
|
function initCustomMusicPlayer() {
|
|
if (musicPlayerInitialized) return;
|
|
musicPlayerInitialized = true;
|
|
customAudioElement = document.getElementById('lastfm-audio-element');
|
|
if (!customAudioElement) return;
|
|
|
|
customAudioElement.removeEventListener('loadedmetadata', updatePlayerDuration);
|
|
customAudioElement.removeEventListener('timeupdate', updatePlayerProgress);
|
|
customAudioElement.removeEventListener('play', onCustomPlay);
|
|
customAudioElement.removeEventListener('pause', onCustomPause);
|
|
customAudioElement.removeEventListener('ended', onCustomEnded);
|
|
|
|
customAudioElement.addEventListener('loadedmetadata', updatePlayerDuration);
|
|
customAudioElement.addEventListener('timeupdate', updatePlayerProgress);
|
|
customAudioElement.addEventListener('play', onCustomPlay);
|
|
customAudioElement.addEventListener('pause', onCustomPause);
|
|
customAudioElement.addEventListener('ended', onCustomEnded);
|
|
|
|
const progressBar = document.getElementById('player-progress-bar');
|
|
const volumeSlider = document.getElementById('player-volume');
|
|
|
|
if (progressBar && !progressBar.dataset.hasListener) {
|
|
progressBar.dataset.hasListener = 'true';
|
|
progressBar.addEventListener('click', seekToPosition);
|
|
}
|
|
|
|
if (volumeSlider && !volumeSlider.dataset.hasListener) {
|
|
volumeSlider.dataset.hasListener = 'true';
|
|
volumeSlider.addEventListener('input', (e) => {
|
|
if (customAudioElement) {
|
|
customAudioElement.volume = e.target.value / 100;
|
|
}
|
|
});
|
|
customAudioElement.volume = 0.8;
|
|
}
|
|
}
|
|
|
|
function playCurrentLastFMTrack() {
|
|
const currentTrackEl = document.querySelector('.current-track');
|
|
if (!currentTrackEl) {
|
|
console.error('No current track element found');
|
|
return;
|
|
}
|
|
|
|
const artist = currentTrackEl.dataset.artist ||
|
|
document.getElementById('lastfm-track-artist')?.textContent?.replace(/^by\s+/, '') || '';
|
|
const track = currentTrackEl.dataset.track ||
|
|
document.getElementById('lastfm-track-title')?.textContent || '';
|
|
|
|
if (!artist || !track) {
|
|
console.error('Could not get current track info');
|
|
return;
|
|
}
|
|
|
|
const searchQuery = `${artist} ${track}`;
|
|
console.log('Playing current track:', searchQuery);
|
|
playTrack(searchQuery);
|
|
}
|
|
|
|
async function playTrack(searchInfo) {
|
|
if (isLoadingTrack) return;
|
|
isLoadingTrack = true;
|
|
|
|
let searchQuery = "";
|
|
let trackDetails = {};
|
|
|
|
if (typeof searchInfo === 'string') {
|
|
searchQuery = searchInfo;
|
|
} else if (typeof searchInfo === 'object' && searchInfo !== null) {
|
|
searchQuery = `${searchInfo.artist} - ${searchInfo.track}`;
|
|
trackDetails = searchInfo;
|
|
}
|
|
|
|
console.log('Playing track query:', searchQuery);
|
|
|
|
try {
|
|
const url = new URL('https://new.akarpov.ru/api/v1/music/search/');
|
|
url.searchParams.append('query', searchQuery);
|
|
|
|
const response = await fetch(url);
|
|
if (!response.ok) throw new Error('Search failed');
|
|
const data = await response.json();
|
|
|
|
if (!data.songs || data.songs.length === 0) {
|
|
showNotification('No tracks found', 'error');
|
|
return;
|
|
}
|
|
|
|
let track = data.songs[0];
|
|
if (trackDetails.artist && trackDetails.track && data.songs.length > 1) {
|
|
const targetArtist = trackDetails.artist.toLowerCase();
|
|
const targetTrack = trackDetails.track.toLowerCase();
|
|
|
|
const exactMatch = data.songs.find(s =>
|
|
s.name.toLowerCase() === targetTrack &&
|
|
s.authors.some(a => a.name.toLowerCase() === targetArtist)
|
|
);
|
|
|
|
if (exactMatch) {
|
|
track = exactMatch;
|
|
}
|
|
}
|
|
|
|
let imageURL = track.image_cropped || '';
|
|
if (imageURL && !imageURL.startsWith('http')) {
|
|
imageURL = 'https://new.akarpov.ru' + imageURL;
|
|
}
|
|
if (!imageURL && track.album && track.album.image_cropped) {
|
|
imageURL = track.album.image_cropped;
|
|
if (!imageURL.startsWith('http')) {
|
|
imageURL = 'https://new.akarpov.ru' + imageURL;
|
|
}
|
|
}
|
|
|
|
const normalizedTrack = {
|
|
name: track.name,
|
|
slug: track.slug,
|
|
file: track.file,
|
|
image_cropped: imageURL,
|
|
length: track.length,
|
|
album: track.album,
|
|
authors: track.authors
|
|
};
|
|
|
|
loadAndPlayCustomTrack(normalizedTrack);
|
|
} catch (error) {
|
|
console.error('Error playing track:', error);
|
|
showNotification('Failed to load track', 'error');
|
|
} finally {
|
|
isLoadingTrack = false;
|
|
}
|
|
}
|
|
|
|
function loadAndPlayCustomTrack(track) {
|
|
if (!customAudioElement) return;
|
|
|
|
if (currentLoadedTrack && currentLoadedTrack.file === track.file) {
|
|
if (customAudioElement.paused) {
|
|
customAudioElement.play().catch(err => {
|
|
console.error('Resume failed:', err);
|
|
showNotification('Playback failed', 'error');
|
|
});
|
|
}
|
|
return;
|
|
}
|
|
|
|
customAudioElement.pause();
|
|
customAudioElement.currentTime = 0;
|
|
|
|
currentLoadedTrack = track;
|
|
|
|
const player = document.getElementById('custom-music-player');
|
|
const artworkMini = document.getElementById('player-artwork-mini');
|
|
const trackName = document.getElementById('player-track-name');
|
|
const artistName = document.getElementById('player-artist-name');
|
|
const progressFill = document.getElementById('player-progress-fill');
|
|
|
|
if (player) player.style.display = 'flex';
|
|
|
|
if (progressFill) progressFill.style.width = '0%';
|
|
|
|
if (artworkMini) {
|
|
artworkMini.src = track.image_cropped || track.album?.image_cropped || '';
|
|
}
|
|
|
|
if (trackName) trackName.textContent = track.name;
|
|
if (artistName) {
|
|
const artists = track.authors?.map(a => a.name).join(', ') || 'Unknown Artist';
|
|
artistName.textContent = artists;
|
|
}
|
|
|
|
customAudioElement.src = track.file;
|
|
customAudioElement.load();
|
|
|
|
const handleCanPlay = () => {
|
|
customAudioElement.removeEventListener('canplay', handleCanPlay);
|
|
customAudioElement.removeEventListener('error', handleError);
|
|
|
|
customAudioElement.play().catch(err => {
|
|
console.error('Playback failed:', err);
|
|
showNotification('Click play to start', 'info');
|
|
});
|
|
};
|
|
|
|
const handleError = () => {
|
|
customAudioElement.removeEventListener('canplay', handleCanPlay);
|
|
customAudioElement.removeEventListener('error', handleError);
|
|
showNotification('Failed to load track', 'error');
|
|
};
|
|
|
|
customAudioElement.addEventListener('canplay', handleCanPlay, {once: true});
|
|
customAudioElement.addEventListener('error', handleError, {once: true});
|
|
|
|
showNotification(`Loading: ${track.name}`, 'success');
|
|
}
|
|
|
|
function toggleMusicPlayPause() {
|
|
if (!customAudioElement) return;
|
|
|
|
if (customAudioElement.paused) {
|
|
customAudioElement.play().catch(err => {
|
|
console.error('Play failed:', err);
|
|
showNotification('Playback failed', 'error');
|
|
});
|
|
} else {
|
|
customAudioElement.pause();
|
|
}
|
|
}
|
|
|
|
function stopMusicPlayback() {
|
|
if (!customAudioElement) return;
|
|
|
|
customAudioElement.pause();
|
|
customAudioElement.currentTime = 0;
|
|
|
|
const player = document.getElementById('custom-music-player');
|
|
if (player) player.style.display = 'none';
|
|
|
|
currentLoadedTrack = null;
|
|
isCustomPlaying = false;
|
|
updatePlayPauseIcon(false);
|
|
}
|
|
|
|
function updatePlayerProgress() {
|
|
if (!customAudioElement || !currentLoadedTrack) return;
|
|
|
|
const currentTime = customAudioElement.currentTime;
|
|
const duration = customAudioElement.duration;
|
|
|
|
if (!isFinite(currentTime) || !isFinite(duration) || duration === 0) return;
|
|
|
|
const progressFill = document.getElementById('player-progress-fill');
|
|
if (progressFill) {
|
|
const percent = Math.min(100, (currentTime / duration) * 100);
|
|
progressFill.style.width = percent + '%';
|
|
}
|
|
|
|
const currentTimeEl = document.getElementById('player-current-time');
|
|
if (currentTimeEl) {
|
|
currentTimeEl.textContent = formatTime(currentTime);
|
|
}
|
|
}
|
|
|
|
function updatePlayerDuration() {
|
|
if (!customAudioElement || !currentLoadedTrack) return;
|
|
|
|
const duration = customAudioElement.duration;
|
|
|
|
if (!isFinite(duration) || duration === 0) return;
|
|
|
|
const durationEl = document.getElementById('player-duration');
|
|
if (durationEl) {
|
|
durationEl.textContent = formatTime(duration);
|
|
}
|
|
|
|
const currentTimeEl = document.getElementById('player-current-time');
|
|
if (currentTimeEl && customAudioElement.currentTime === 0) {
|
|
currentTimeEl.textContent = '0:00';
|
|
}
|
|
|
|
const progressFill = document.getElementById('player-progress-fill');
|
|
if (progressFill && customAudioElement.currentTime === 0) {
|
|
progressFill.style.width = '0%';
|
|
}
|
|
}
|
|
|
|
function seekToPosition(e) {
|
|
if (!customAudioElement) return;
|
|
|
|
const bar = e.currentTarget;
|
|
const rect = bar.getBoundingClientRect();
|
|
const percent = (e.clientX - rect.left) / rect.width;
|
|
customAudioElement.currentTime = percent * customAudioElement.duration;
|
|
}
|
|
|
|
function onCustomPlay() {
|
|
isCustomPlaying = true;
|
|
updatePlayPauseIcon(true);
|
|
}
|
|
|
|
function onCustomPause() {
|
|
isCustomPlaying = false;
|
|
updatePlayPauseIcon(false);
|
|
}
|
|
|
|
function onCustomEnded() {
|
|
isCustomPlaying = false;
|
|
updatePlayPauseIcon(false);
|
|
|
|
const progressFill = document.getElementById('player-progress-fill');
|
|
if (progressFill) {
|
|
progressFill.style.width = '100%';
|
|
}
|
|
|
|
const currentTimeEl = document.getElementById('player-current-time');
|
|
const durationEl = document.getElementById('player-duration');
|
|
if (currentTimeEl && durationEl && customAudioElement) {
|
|
currentTimeEl.textContent = durationEl.textContent;
|
|
}
|
|
|
|
showNotification('Track finished', 'info');
|
|
|
|
setTimeout(() => {
|
|
if (progressFill) {
|
|
progressFill.style.width = '0%';
|
|
}
|
|
if (currentTimeEl) {
|
|
currentTimeEl.textContent = '0:00';
|
|
}
|
|
}, 1000);
|
|
}
|
|
|
|
function updatePlayPauseIcon(playing) {
|
|
const icon = document.getElementById('play-pause-icon');
|
|
if (!icon) return;
|
|
|
|
if (playing) {
|
|
icon.innerHTML = '<path fill="currentColor" d="M6 4h4v16H6V4zm8 0h4v16h-4V4z"/>';
|
|
} else {
|
|
icon.innerHTML = '<path fill="currentColor" d="M8 5v14l11-7z"/>';
|
|
}
|
|
}
|
|
|
|
function formatTime(seconds) {
|
|
if (!isFinite(seconds)) return '0:00';
|
|
const mins = Math.floor(seconds / 60);
|
|
const secs = Math.floor(seconds % 60);
|
|
return `${mins}:${secs.toString().padStart(2, '0')}`;
|
|
}
|
|
|
|
function showNotification(message, type) {
|
|
const notification = document.createElement('div');
|
|
notification.className = `notification ${type}`;
|
|
notification.textContent = message;
|
|
|
|
Object.assign(notification.style, {
|
|
position: 'fixed',
|
|
top: '20px',
|
|
right: '20px',
|
|
padding: '8px 16px',
|
|
borderRadius: '8px',
|
|
color: 'white',
|
|
fontWeight: '600',
|
|
zIndex: '10000',
|
|
transform: 'translateX(400px)',
|
|
transition: 'transform 0.3s ease',
|
|
maxWidth: '300px',
|
|
boxShadow: '0 4px 16px rgba(0,0,0,.3)'
|
|
});
|
|
|
|
switch (type) {
|
|
case 'success': notification.style.background = '#3ad38b'; break;
|
|
case 'error': notification.style.background = '#ff6b6b'; break;
|
|
case 'info': notification.style.background = '#7aa2ff'; break;
|
|
default: notification.style.background = '#333';
|
|
}
|
|
|
|
document.body.appendChild(notification);
|
|
setTimeout(() => notification.style.transform = 'translateX(0)', 100);
|
|
|
|
setTimeout(() => {
|
|
notification.style.transform = 'translateX(400px)';
|
|
setTimeout(() => notification.remove(), 300);
|
|
}, 3000);
|
|
}
|
|
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', initCustomMusicPlayer, { once: true });
|
|
} else {
|
|
initCustomMusicPlayer();
|
|
}
|
|
|
|
window.playTrack = playTrack;
|
|
window.playCurrentLastFMTrack = playCurrentLastFMTrack;
|
|
window.toggleMusicPlayPause = toggleMusicPlayPause;
|
|
window.stopMusicPlayback = stopMusicPlayback;
|
|
|
|
window.musicPlayer = {
|
|
playTrack,
|
|
playCurrentLastFMTrack,
|
|
getCurrentTrack: () => currentLoadedTrack,
|
|
isPlaying: () => isCustomPlaying,
|
|
handleMusicUpdate: function(message) {
|
|
if (message.type === 'music_play' && message.data) {
|
|
let imageURL = message.data.image || '';
|
|
if (imageURL && !imageURL.startsWith('http')) {
|
|
imageURL = 'https://new.akarpov.ru' + imageURL;
|
|
}
|
|
|
|
const track = {
|
|
name: message.data.name || 'Unknown Track',
|
|
file: message.data.file,
|
|
image_cropped: imageURL,
|
|
album: {
|
|
name: message.data.album || '',
|
|
image_cropped: imageURL
|
|
},
|
|
authors: message.data.artists ?
|
|
message.data.artists.map(name => ({name})) :
|
|
[{name: 'Unknown Artist'}],
|
|
length: message.data.length || 0
|
|
};
|
|
|
|
loadAndPlayCustomTrack(track);
|
|
}
|
|
}
|
|
};
|
|
})(); |