HTML-ModPlayer/index.html
mueller_minki 6299885329 SEO update
2025-07-26 05:38:10 +02:00

454 lines
13 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=11">
<meta property="og:image" content="./banner.png">
<meta name="description" content="Minki's Module Collection is a selection of tracker music from various sources. This page offers the ability to play back these modules.">
<meta name="keywords" content="mueller_minki, Mueller's Software, Amiga, Amiga Module, Tracker, Music Tracker, Music, Minki's Module Collection, Module Collection, Music Collection">
<title>ModPlayer</title>
<style>
body {
font-family: Arial, sans-serif;
background: #111;
color: #f4f4f4;
margin: 0;
padding: 0;
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.sidebar {
width: 200px;
background: #1a1a1a;
padding: 20px;
box-sizing: border-box;
flex-shrink: 0;
height: 100vh;
}
.sidebar h2 {
font-size: 1.1rem;
margin: 0 0 10px;
}
.sidebar ul {
list-style: none;
padding-left: 10px;
margin-bottom: 15px;
}
.sidebar li {
cursor: pointer;
padding: 5px;
margin: 2px 0;
}
.sidebar li:hover {
background-color: #333;
}
.content {
flex: 1;
min-width: 300px;
padding: 20px;
box-sizing: border-box;
}
.player {
max-width: 600px;
margin: auto;
background: #1e1e1e;
padding: 20px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0,0,0,0.5);
}
h1 {
font-size: 1.5rem;
margin-bottom: 10px;
}
a#play {
display: inline-block;
background: #2196F3;
color: white;
padding: 8px 16px;
text-decoration: none;
border-radius: 5px;
margin: 10px 0;
transition: background 0.3s;
}
a#play:hover {
background: #1976D2;
}
input[type="range"] {
width: 100%;
margin: 10px 0;
}
select {
width: 100%;
padding: 5px;
background: #333;
color: white;
border: 1px solid #555;
border-radius: 4px;
}
small {
color: #888;
display: block;
margin-top: 20px;
}
#playlist {
margin-top: 20px;
padding-left: 0;
}
#playlist li {
cursor: pointer;
padding: 8px 10px;
list-style: none;
border-bottom: 1px solid #333;
}
#playlist li:hover {
background-color: #333;
}
#playlist li.active {
background-color: #444;
font-weight: bold;
}
@media (max-width: 700px) {
body {
flex-direction: column;
}
.sidebar {
width: 100%;
height: auto; /* Reset height for mobile */
}
.content {
padding: 10px;
}
}
</style>
</head>
<body>
<div class="sidebar">
<h2>Mod Collections</h2>
<div id="collectionList"></div>
</div>
<div class="content">
<div class="player">
<h1>Playing: <span id="modfilename">Select a module...</span></h1>
<strong><a href="javascript:pauseButton()" id="play">Load a module</a></strong><br>
Title: <span id="title">&nbsp;</span><br>
Duration: <span id="duration">&nbsp;</span>
<input id="seekbar" type="range" min="0" max="100" value="0" onchange="player.seek(this.value);" />
<div id="subsongs" style="display:none">
Subsongs:
<select id="subsong" onchange="selectSubsong()"></select>
</div>
<ul id="playlist"></ul>
<small id="library-version">&nbsp;</small>
<small>ModPlayer web project version: 0.1.7 (2025-07-26T03:08:30.629763Z)<br><br>(c) 2025 Minki the Avali - <a href="https://www.wtfpl.net/txt/copying/">WTFPL-2</a></small>
</div>
</div>
<script src="./chiptune2.js"></script>
<script src="./libopenmpt.js" async onload="initPlayer()"></script>
<script>
let moduleCollections = {
"Volume_A": {
"1": [
"Volume_A/1/2010.s3m",
"Volume_A/1/aaaa.mod",
"Volume_A/1/afloat.mod",
"Volume_A/1/gtasa.it",
"Volume_A/1/music1.s3m",
"Volume_A/1/MYPT2.MOD",
"Volume_A/1/NOWHAT.MOD",
"Volume_A/1/silence.mod",
"Volume_A/1/TRANSISR.IT",
"Volume_A/1/TRPNEVER.MOD",
"Volume_A/1/validanc.mod",
"Volume_A/1/whatname.s3m",
"Volume_A/1/yosheek.it"
],
"2": [
"Volume_A/2/5kb.mod",
"Volume_A/2/Allnite.mod",
"Volume_A/2/bid.mod",
"Volume_A/2/deflcrap.mod",
"Volume_A/2/droppy.it",
"Volume_A/2/fastfunk.xm",
"Volume_A/2/freehugs.xm",
"Volume_A/2/IhmeLuon.mod",
"Volume_A/2/Party.mod",
"Volume_A/2/Spacdeli.mod",
"Volume_A/2/Winners.mod"
],
"3": [
"Volume_A/3/1990chip.mod",
"Volume_A/3/chippie.xm",
"Volume_A/3/dotcom.mod",
"Volume_A/3/eek.it",
"Volume_A/3/golden4.mod",
"Volume_A/3/groove.mod",
"Volume_A/3/k21.mod",
"Volume_A/3/ohbdisco.it",
"Volume_A/3/rustruin.it",
"Volume_A/3/techno2.mod",
"Volume_A/3/wtaf.mod",
"Volume_A/3/z.xm"
]
},
"Volume_B": {
"1": [
"Volume_B/1/asm98.xm",
"Volume_B/1/chip-meltdown.mod",
"Volume_B/1/chirpychip.mod",
"Volume_B/1/enchantment-tsp.mod",
"Volume_B/1/funky_blend.mod",
"Volume_B/1/lizardking_-_master_and_servant.mod",
"Volume_B/1/monkeydroid.mod",
"Volume_B/1/mortal_mario.mod",
"Volume_B/1/online_pizza.mod",
"Volume_B/1/spill_the_beans.mod",
"Volume_B/1/technix_-_too_late.mod",
"Volume_B/1/wonderfull_summer.xm"
],
"2": [
"Volume_B/2/cyborgjeff-91groove.mod",
"Volume_B/2/cyborg_jeff_-_paradise.mod",
"Volume_B/2/realize.mod",
"Volume_B/2/smooth_dancer.mod",
"Volume_B/2/technix-bubble_bobble.mod",
"Volume_B/2/technix_-_cabbage.mod",
"Volume_B/2/technix-moonwalk.mod",
"Volume_B/2/technix_-_outrun.mod",
"Volume_B/2/trade.xm",
"Volume_B/2/unsuspected_o.mod",
"Volume_B/2/what_is_love_g.s3m"
],
"3": [
"Volume_B/3/28.mod",
"Volume_B/3/an_empty_cup_of_tea.mod",
"Volume_B/3/april.mod",
"Volume_B/3/chippy_nr_152.mod",
"Volume_B/3/hi-del.mod",
"Volume_B/3/high3.s3m",
"Volume_B/3/kaos_och_dekadens.mod",
"Volume_B/3/mastodontti.mod",
"Volume_B/3/noone_51_1998.mod",
"Volume_B/3/nv1bb.s3m",
"Volume_B/3/PHASE.MOD",
"Volume_B/3/tce_msx1.mod",
"Volume_B/3/_warhawk.xm"
]
},
"Volume_C": {
"1": [
"Volume_C/1/afraid_.mod",
"Volume_C/1/bsx-ten8.xm",
"Volume_C/1/captain_america.xm",
"Volume_C/1/dance_mania.mod",
"Volume_C/1/dubmood_-_a_message_to_you_mo.xm",
"Volume_C/1/full_metal_trousers.mod",
"Volume_C/1/hitmix2.xm",
"Volume_C/1/itspheno.mod"
],
"2": [
"Volume_C/2/12th_street_rag.mod",
"Volume_C/2/afterglow.mod",
"Volume_C/2/ida_ohb-idk.it",
"Volume_C/2/megatec_bestechno.mod",
"Volume_C/2/mexy_-_i_like_to_move_it_remix.mod",
"Volume_C/2/omen_no_-_funkypiano.mod",
"Volume_C/2/popcorn_world_remix.xm"
],
"3": [
"Volume_C/3/64_alive.mod",
"Volume_C/3/bevcop1.s3m",
"Volume_C/3/doop_remix.s3m",
"Volume_C/3/external.xm",
"Volume_C/3/mip4_intro.mod",
"Volume_C/3/oistein_eide_-_illusions.mod",
"Volume_C/3/pink_n_fluffy.xm",
"Volume_C/3/rg-random.xm",
"Volume_C/3/still_chippy.mod",
"Volume_C/3/subways.mod",
"Volume_C/3/techlort.s3m"
]
}
};
let moduleList = [];
let currentIndex = 0;
let player;
let progressInterval = null;
function pauseButton() {
const button = document.getElementById('play');
if (player.togglePause()) {
button.innerHTML = "Pause";
} else {
button.innerHTML = "Play";
}
}
function selectSubsong() {
const select = document.getElementById('subsong');
const subsong = select.options[select.selectedIndex].value;
player.selectSubsong(subsong);
updateDuration();
}
function updateDuration() {
const sec_num = player.duration();
const minutes = Math.floor(sec_num / 60);
let seconds = Math.floor(sec_num % 60);
if (seconds < 10) seconds = '0' + seconds;
document.getElementById('duration').innerHTML = `${minutes}:${seconds}`;
document.getElementById('seekbar').max = sec_num;
}
function setMetadata(filename) {
const metadata = player.metadata();
document.getElementById('title').innerHTML = metadata.title || filename;
const subsongs = player.subsongs();
const select = document.getElementById('subsong');
select.innerHTML = '';
document.getElementById('subsongs').style.display = (subsongs.length > 1) ? 'block' : 'none';
if (subsongs.length > 1) {
const allOption = document.createElement('option');
allOption.textContent = 'Play all subsongs';
allOption.value = -1;
select.appendChild(allOption);
subsongs.forEach((s, i) => {
const opt = document.createElement('option');
opt.textContent = s;
opt.value = i;
select.appendChild(opt);
});
select.selectedIndex = 0;
player.selectSubsong(-1);
}
updateDuration();
const stack = stackSave();
document.getElementById('library-version').innerHTML =
'Library version: libopenmpt ' + UTF8ToString(libopenmpt._openmpt_get_string(asciiToStack('library_version')))
+ ' (' + UTF8ToString(libopenmpt._openmpt_get_string(asciiToStack('build'))) + ')';
stackRestore(stack);
}
function loadAndPlay(index) {
if (progressInterval) clearInterval(progressInterval);
currentIndex = index;
const path = moduleList[index];
document.getElementById('modfilename').innerText = path;
highlightPlaylistItem(index);
player.load(path, function(buffer) {
if (player.play(buffer)) {
document.getElementById('play').innerHTML = "Pause";
} else {
document.getElementById('play').innerHTML = "Play";
}
setMetadata(path);
progressInterval = setInterval(() => {
const currentPos = player.getPosition();
const max = parseFloat(document.getElementById('seekbar').max);
document.getElementById('seekbar').value = currentPos;
if (currentPos >= max - 0.5) {
clearInterval(progressInterval);
if (currentIndex + 1 < moduleList.length) {
loadAndPlay(currentIndex + 1);
} else {
player.stop();
document.getElementById('play').innerText = "Play";
}
}
}, 500);
});
}
function highlightPlaylistItem(index) {
const items = document.querySelectorAll('#playlist li');
items.forEach((item, i) => {
item.classList.toggle('active', i === index);
});
}
function updatePlaylist(list) {
moduleList = list;
const playlist = document.getElementById('playlist');
playlist.innerHTML = '';
moduleList.forEach((filename, index) => {
const li = document.createElement('li');
li.textContent = filename;
li.onclick = () => loadAndPlay(index);
playlist.appendChild(li);
});
loadAndPlay(0);
}
function renderCollections() {
const container = document.getElementById('collectionList');
for (let collection in moduleCollections) {
const title = document.createElement('div');
title.innerHTML = `<strong>${collection}</strong>`;
container.appendChild(title);
const diskList = document.createElement('ul');
for (let disk in moduleCollections[collection]) {
const li = document.createElement('li');
li.textContent = "Disk " + disk;
li.onclick = () => updatePlaylist(moduleCollections[collection][disk]);
diskList.appendChild(li);
}
container.appendChild(diskList);
}
}
function initPlayer() {
libopenmpt.onRuntimeInitialized = () => {
player = new ChiptuneJsPlayer(new ChiptuneJsConfig(-1));
renderCollections();
};
}
</script>
</body>
</html>