mirror of
https://github.com/A-Star100/simpliplay-desktop.git
synced 2025-09-17 22:29:38 +00:00
265 lines
9.8 KiB
JavaScript
265 lines
9.8 KiB
JavaScript
document.addEventListener('DOMContentLoaded', () => {
|
|
|
|
// --- 1. Element Grouping & State Management ---
|
|
const elements = {
|
|
media: {
|
|
player: document.getElementById('mediaPlayer'),
|
|
customControls: document.getElementById('customControls'),
|
|
playPauseBtn: document.getElementById('playPauseBtn'),
|
|
seekBar: document.getElementById('seekBar'),
|
|
timeDisplay: document.getElementById('timeDisplay'),
|
|
},
|
|
volume: {
|
|
volumeBtn: document.getElementById('volumeBtn'),
|
|
volumeBar: document.getElementById('volumeBar'),
|
|
},
|
|
dialogs: {
|
|
mainOverlay: document.getElementById('dialogOverlay'),
|
|
urlOverlay: document.getElementById('urlDialogOverlay'),
|
|
settingsOverlay: document.getElementById('settingsDialogOverlay'),
|
|
subtitlesOverlay: document.getElementById('subtitlesOverlay'),
|
|
},
|
|
buttons: {
|
|
chooseFile: document.getElementById('chooseFileBtn'),
|
|
enterUrl: document.getElementById('enterUrlBtn'),
|
|
submitUrl: document.getElementById('submitUrlBtn'),
|
|
cancelUrl: document.getElementById('cancelUrlBtn'),
|
|
settings: document.getElementById('settingsBtn'),
|
|
saveSettings: document.getElementById('saveSettingsBtn'),
|
|
cc: document.getElementById('ccBtn'),
|
|
submitSubtitles: document.getElementById('submitSubtitlesBtn'),
|
|
cancelSubtitles: document.getElementById('cancelSubtitlesBtn'),
|
|
showDialog: document.getElementById('showDialogBtn'),
|
|
hideDialog: document.getElementById('hideDialogBtn'),
|
|
fullscreen: document.getElementById('fullscreenBtn'),
|
|
},
|
|
inputs: {
|
|
fileInput: document.getElementById('fileInput'),
|
|
urlInput: document.getElementById('urlInput'),
|
|
subtitlesInput: document.getElementById('subtitlesInput'),
|
|
autoplayCheckbox: document.getElementById('autoplayCheckbox'),
|
|
loopCheckbox: document.getElementById('loopCheckbox'),
|
|
controlsCheckbox: document.getElementById('controlsCheckbox'),
|
|
colorsCheckbox: document.getElementById('colorsCheckbox'),
|
|
},
|
|
};
|
|
|
|
const state = {
|
|
hls: null,
|
|
dashPlayer: null,
|
|
previousObjectURL: null,
|
|
};
|
|
|
|
// --- 2. Utility Functions ---
|
|
const formatTime = (time) => {
|
|
const minutes = Math.floor(time / 60) || 0;
|
|
const seconds = Math.floor(time % 60) || 0;
|
|
return `${minutes}:${seconds.toString().padStart(2, '0')}`;
|
|
};
|
|
|
|
const setVisibility = (element, isVisible) => {
|
|
element.style.display = isVisible ? 'block' : 'none';
|
|
};
|
|
|
|
const toggleControls = (show) => {
|
|
elements.media.customControls.style.display = show ? 'flex' : 'none';
|
|
};
|
|
|
|
const resetMediaPlayers = () => {
|
|
if (state.hls) {
|
|
state.hls.destroy();
|
|
state.hls = null;
|
|
}
|
|
if (state.dashPlayer) {
|
|
state.dashPlayer.reset();
|
|
state.dashPlayer = null;
|
|
}
|
|
};
|
|
|
|
const updatePlayPauseButton = () => {
|
|
elements.media.playPauseBtn.textContent = elements.media.player.paused ? 'Play' : 'Pause';
|
|
};
|
|
|
|
const updateVolumeButton = () => {
|
|
const isMuted = elements.volume.volumeBar.value === 0 || elements.media.player.muted;
|
|
elements.volume.volumeBtn.textContent = isMuted ? '🔇' : '🔊';
|
|
};
|
|
|
|
const addSubtitles = (url) => {
|
|
// Remove old subtitle tracks
|
|
Array.from(elements.media.player.getElementsByTagName('track')).forEach(track => track.remove());
|
|
|
|
if (!url) return;
|
|
|
|
const track = document.createElement('track');
|
|
track.kind = 'subtitles';
|
|
track.label = 'English';
|
|
track.srclang = 'en';
|
|
track.src = url;
|
|
elements.media.player.appendChild(track);
|
|
track.track.mode = 'showing';
|
|
};
|
|
|
|
const detectStreamType = async (url) => {
|
|
try {
|
|
const response = await fetch(url, { method: 'HEAD' });
|
|
const contentType = response.headers.get('Content-Type') || '';
|
|
const isHLS = url.toLowerCase().endsWith('.m3u8') || contentType.includes('mpegurl');
|
|
const isDASH = url.toLowerCase().endsWith('.mpd') || contentType.includes('dash+xml');
|
|
return { isHLS, isDASH };
|
|
} catch (err) {
|
|
console.error("Failed to detect stream type:", err);
|
|
return { isHLS: false, isDASH: false };
|
|
}
|
|
};
|
|
|
|
const playMedia = (source) => {
|
|
elements.media.player.src = source;
|
|
elements.media.player.load();
|
|
if (elements.inputs.autoplayCheckbox.checked) {
|
|
elements.media.player.play();
|
|
}
|
|
toggleControls(true);
|
|
setVisibility(elements.dialogs.mainOverlay, false);
|
|
setVisibility(elements.dialogs.urlOverlay, false);
|
|
};
|
|
|
|
// --- 3. Event Handlers ---
|
|
const handleUrlSubmit = async () => {
|
|
let url = elements.inputs.urlInput.value;
|
|
if (url && !url.startsWith('http')) {
|
|
url = 'https://' + url;
|
|
}
|
|
|
|
if (!url) return;
|
|
|
|
resetMediaPlayers();
|
|
addSubtitles();
|
|
|
|
const { isHLS, isDASH } = await detectStreamType(url);
|
|
elements.media.player.style.display = 'flex';
|
|
|
|
if (isHLS && typeof Hls !== 'undefined' && Hls.isSupported()) {
|
|
state.hls = new Hls();
|
|
state.hls.loadSource(url);
|
|
state.hls.attachMedia(elements.media.player);
|
|
state.hls.on(Hls.Events.MANIFEST_PARSED, () => {
|
|
if (elements.inputs.autoplayCheckbox.checked) {
|
|
elements.media.player.play();
|
|
}
|
|
});
|
|
} else if (isDASH && typeof dashjs !== 'undefined') {
|
|
state.dashPlayer = dashjs.MediaPlayer().create();
|
|
state.dashPlayer.initialize(elements.media.player, url, true);
|
|
if (elements.inputs.autoplayCheckbox.checked) {
|
|
elements.media.player.play();
|
|
}
|
|
} else {
|
|
playMedia(url);
|
|
}
|
|
elements.inputs.urlInput.value = '';
|
|
toggleControls(true);
|
|
setVisibility(elements.dialogs.urlOverlay, false);
|
|
setVisibility(elements.dialogs.mainOverlay, false);
|
|
};
|
|
|
|
const handleFileChange = (event) => {
|
|
resetMediaPlayers();
|
|
addSubtitles();
|
|
if (state.previousObjectURL) {
|
|
URL.revokeObjectURL(state.previousObjectURL);
|
|
}
|
|
|
|
const file = event.target.files[0];
|
|
if (!file) return;
|
|
|
|
const fileURL = URL.createObjectURL(file);
|
|
playMedia(fileURL);
|
|
state.previousObjectURL = fileURL;
|
|
};
|
|
|
|
const handleSettingsSave = () => {
|
|
elements.media.player.autoplay = elements.inputs.autoplayCheckbox.checked;
|
|
elements.media.player.loop = elements.inputs.loopCheckbox.checked;
|
|
elements.media.player.controls = elements.inputs.controlsCheckbox.checked;
|
|
|
|
const filter = elements.inputs.colorsCheckbox.checked
|
|
? 'contrast(1.1) saturate(1.15) brightness(1.03)'
|
|
: '';
|
|
elements.media.player.style.filter = filter;
|
|
|
|
setVisibility(elements.dialogs.settingsOverlay, false);
|
|
};
|
|
|
|
// --- 4. Event Listener Setup ---
|
|
const setupEventListeners = () => {
|
|
// Media & Controls
|
|
elements.media.player.addEventListener('timeupdate', () => {
|
|
elements.media.seekBar.max = elements.media.player.duration || 0;
|
|
elements.media.seekBar.value = elements.media.player.currentTime;
|
|
elements.media.timeDisplay.textContent = `${formatTime(elements.media.player.currentTime)} / ${formatTime(elements.media.player.duration)}`;
|
|
});
|
|
|
|
elements.media.player.addEventListener('play', updatePlayPauseButton);
|
|
elements.media.player.addEventListener('pause', updatePlayPauseButton);
|
|
elements.media.player.addEventListener('volumechange', updateVolumeButton);
|
|
|
|
elements.media.playPauseBtn.addEventListener('click', () => {
|
|
elements.media.player.paused ? elements.media.player.play() : elements.media.player.pause();
|
|
});
|
|
|
|
elements.media.seekBar.addEventListener('input', () => {
|
|
elements.media.player.currentTime = elements.media.seekBar.value;
|
|
});
|
|
|
|
elements.volume.volumeBar.addEventListener('input', () => {
|
|
elements.media.player.volume = elements.volume.volumeBar.value;
|
|
});
|
|
|
|
elements.volume.volumeBtn.addEventListener('click', () => {
|
|
elements.media.player.muted = !elements.media.player.muted;
|
|
updateVolumeButton();
|
|
});
|
|
|
|
// Dialogs & Buttons
|
|
elements.buttons.chooseFile.addEventListener('click', () => elements.inputs.fileInput.click());
|
|
elements.buttons.enterUrl.addEventListener('click', () => setVisibility(elements.dialogs.urlOverlay, true));
|
|
elements.buttons.cancelUrl.addEventListener('click', () => setVisibility(elements.dialogs.urlOverlay, false));
|
|
elements.buttons.submitUrl.addEventListener('click', handleUrlSubmit);
|
|
elements.inputs.urlInput.addEventListener('keydown', (e) => {
|
|
if (e.key === 'Enter') handleUrlSubmit();
|
|
});
|
|
|
|
elements.inputs.fileInput.addEventListener('change', handleFileChange);
|
|
|
|
elements.buttons.settings.addEventListener('click', () => setVisibility(elements.dialogs.settingsOverlay, true));
|
|
elements.buttons.saveSettings.addEventListener('click', handleSettingsSave);
|
|
|
|
elements.buttons.cc.addEventListener('click', () => setVisibility(elements.dialogs.subtitlesOverlay, true));
|
|
elements.buttons.cancelSubtitles.addEventListener('click', () => setVisibility(elements.dialogs.subtitlesOverlay, false));
|
|
elements.buttons.submitSubtitles.addEventListener('click', () => {
|
|
addSubtitles(elements.inputs.subtitlesInput.value);
|
|
elements.inputs.subtitlesInput.value = '';
|
|
setVisibility(elements.dialogs.subtitlesOverlay, false);
|
|
});
|
|
elements.inputs.subtitlesInput.addEventListener('keydown', (e) => {
|
|
if (e.key === 'Enter') elements.buttons.submitSubtitles.click();
|
|
});
|
|
|
|
elements.buttons.showDialog.addEventListener('click', () => setVisibility(elements.dialogs.mainOverlay, true));
|
|
elements.buttons.hideDialog.addEventListener('click', () => setVisibility(elements.dialogs.mainOverlay, false));
|
|
elements.buttons.fullscreen.addEventListener('click', () => {
|
|
if (!document.fullscreenElement) {
|
|
elements.media.player.requestFullscreen();
|
|
} else {
|
|
document.exitFullscreen();
|
|
}
|
|
});
|
|
|
|
// Initialize
|
|
window.onload = () => setVisibility(elements.dialogs.mainOverlay, true);
|
|
};
|
|
|
|
setupEventListeners();
|
|
});
|