LRC-And-YouTube-Player / index2.html
abdelhaqueidali's picture
Upload 12 files
d50155f verified
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Previous head content remains the same -->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LRC & YouTube Player</title>
<style>
/* Previous styles remain the same */
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: #f5f5f5;
}
h1 {
color: #333;
text-align: center;
}
#songList {
list-style: none;
padding: 0;
background: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
margin-top: 20px;
}
#songList li {
cursor: pointer;
padding: 12px 20px;
margin: 0;
border-bottom: 1px solid #eee;
transition: background-color 0.2s;
}
#songList li:last-child {
border-bottom: none;
}
#songList li:hover {
background-color: #f0f0f0;
}
#playerContainer {
margin-top: 20px;
display: none;
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
#youtubePlayer {
width: 100%;
max-width: 640px;
height: 360px;
margin: 0 auto;
display: block;
}
#lyricsDisplay {
margin-top: 20px;
white-space: pre-wrap;
border: 1px solid #ddd;
padding: 15px;
border-radius: 4px;
overflow-y: auto;
height: 300px;
font-size: 16px;
line-height: 1.6;
background: #fff;
}
.highlighted-lyric {
background-color: #fff3cd;
padding: 2px 5px;
border-radius: 3px;
transition: background-color 0.3s;
}
#errorDisplay {
color: red;
margin: 10px 0;
padding: 10px;
border: 1px solid red;
display: none;
}
#manualInput {
width: 100%;
height: 100px;
margin: 10px 0;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-family: monospace;
}
#processButton {
background-color: #4CAF50;
color: white;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
#processButton:hover {
background-color: #45a049;
}
.input-section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
</style>
</head>
<body>
<h1>LRC & YouTube Player</h1>
<div id="errorDisplay"></div>
<div class="input-section">
<h3>Data Input (filename.lrc|youtubeURL format)</h3>
<textarea id="manualInput" placeholder="Enter data in format:&#10;filename1.lrc|https://youtube.com/watch?v=...&#10;filename2.lrc|https://youtube.com/watch?v=..."></textarea>
<button id="processButton">Process Input</button>
</div>
<ul id="songList"></ul>
<div id="playerContainer">
<div id="youtubePlayer"></div>
<div id="lyricsDisplay"></div>
</div>
<script>
let player;
let currentLyrics = [];
let currentLyricIndex = -1;
let syncInterval;
function showError(message) {
const errorDisplay = document.getElementById('errorDisplay');
errorDisplay.textContent = message;
errorDisplay.style.display = 'block';
console.error(message);
}
// Try loading from file first
console.log('Starting to load data.txt...');
fetch('data.txt')
.then(response => {
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
return response.text();
})
.then(data => {
document.getElementById('manualInput').value = data;
processInput(data);
})
.catch(error => {
console.log('Could not load data.txt, waiting for manual input');
});
// Process button click handler
document.getElementById('processButton').onclick = () => {
const input = document.getElementById('manualInput').value;
processInput(input);
};
function processInput(data) {
console.log('Processing input data');
const songList = document.getElementById('songList');
songList.innerHTML = ''; // Clear existing list
if (!data.trim()) {
showError('No data provided');
return;
}
data.trim().split('\n').forEach((line, index) => {
console.log(`Processing line ${index}:`, line);
const [filename, youtubeLink] = line.split('|');
if (!filename || !youtubeLink) {
showError(`Invalid line format: ${line}`);
return;
}
const li = document.createElement('li');
li.textContent = filename.replace('.lrc', '');
li.dataset.filename = filename;
li.dataset.youtubeLink = youtubeLink.trim();
li.onclick = () => loadSongFromFile(filename, youtubeLink.trim());
songList.appendChild(li);
});
}
function loadSongFromFile(filename, youtubeLink) {
console.log('Attempting to load LRC file:', filename);
fetch(filename)
.then(response => {
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
return response.text();
})
.then(lrcContent => {
loadSong(filename, youtubeLink, lrcContent);
})
.catch(error => {
console.error('Error loading LRC file:', error);
showError(`Could not load ${filename}. Make sure the file exists in the same directory.`);
});
}
// Load the YouTube IFrame Player API
const tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
document.head.appendChild(tag);
window.onYouTubeIframeAPIReady = function() {
console.log('YouTube IFrame API Ready');
player = new YT.Player('youtubePlayer', {
height: '360',
width: '640',
videoId: '',
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange,
'onError': onPlayerError
}
});
};
function onPlayerReady(event) {
console.log('YouTube player ready');
}
function onPlayerError(event) {
console.error('YouTube player error:', event.data);
showError('Error loading YouTube video');
}
function loadSong(filename, youtubeLink, lrcContent) {
console.log('Loading song:', filename, youtubeLink);
const videoId = youtubeLink.match(/[?&]v=([^&]+)/)?.[1] ||
youtubeLink.match(/youtu\.be\/([^?]+)/)?.[1];
if (!videoId) {
showError('Invalid YouTube URL');
return;
}
try {
currentLyrics = parseLRC(lrcContent);
console.log('Parsed lyrics:', currentLyrics);
displayLyrics();
currentLyricIndex = -1;
if (player && player.loadVideoById) {
console.log('Loading YouTube video:', videoId);
player.loadVideoById(videoId);
document.getElementById('playerContainer').style.display = 'block';
} else {
showError('YouTube player not ready');
}
} catch (error) {
showError(`Error processing lyrics: ${error.message}`);
}
}
function parseLRC(text) {
const lines = text.split('\n');
const lyrics = [];
const timeRegex = /\[(\d+):(\d+)\.(\d+)\](.*)/;
lines.forEach((line, index) => {
const match = line.match(timeRegex);
if (match) {
const minutes = parseInt(match[1]);
const seconds = parseInt(match[2]);
const milliseconds = parseInt(match[3]);
const time = minutes * 60 + seconds + milliseconds / 1000;
const text = match[4].trim();
if (text) {
lyrics.push({ time, text });
}
}
});
return lyrics.sort((a, b) => a.time - b.time);
}
function displayLyrics() {
const container = document.getElementById('lyricsDisplay');
container.innerHTML = currentLyrics
.map((lyric, index) => `<p data-index="${index}">${lyric.text}</p>`)
.join('');
}
function syncLyrics() {
if (!player || !player.getCurrentTime) return;
const currentTime = player.getCurrentTime();
let index = currentLyrics.findIndex(lyric => lyric.time > currentTime) - 1;
if (index !== currentLyricIndex) {
const prevHighlight = document.querySelector('.highlighted-lyric');
if (prevHighlight) {
prevHighlight.classList.remove('highlighted-lyric');
}
if (index >= 0) {
const element = document.querySelector(`p[data-index="${index}"]`);
if (element) {
element.classList.add('highlighted-lyric');
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
}
currentLyricIndex = index;
}
}
function onPlayerStateChange(event) {
console.log('Player state changed:', event.data);
if (event.data === YT.PlayerState.PLAYING) {
if (syncInterval) clearInterval(syncInterval);
syncInterval = setInterval(syncLyrics, 100);
} else if (event.data === YT.PlayerState.PAUSED || event.data === YT.PlayerState.ENDED) {
if (syncInterval) clearInterval(syncInterval);
}
}
</script>
</body>
</html>