WEB Media player test - Help EN/FR

Français, voir plus bas
Source at very end

ENGLISH

Copilot_20250905_161639.png

separator3.png

Since I upgraded to Windows 11, I've had trouble easily casting the songs I want to my Google Nest, to the point that it's annoying me...

Furthermore, I sometimes have to stream games or activities where I clearly need a soundscape in the background.
That's why I'm currently creating as many songs as possible with varied rhythms, but with themes related to my universe, so that viewers can instantly find their way around and say: Ah, this music, we're definitely at Ithara's!!!

Want to listen to them ? Here they go : http://principalityofbastion.org/musiques/

So I asked Claude AI to create a web page that could play all the music I uploaded to a folder, so I could play it, on repeat, randomly.

A few seconds of thought and, he did this for me:
https://www.principalityofbastion.org/musicplayer.html
Feel free to copy the source code for your own use if you want to try modifying it, because it's not perfect. I'll explain below...

I just tested it... I can load songs from the hard drive, I can change the sound, I can navigate through the song, change the music speed, I can configure it to shuffle the songs.

However, as soon as I get to the end of a song, it loads the next one and waits for me to press play again...
Here, however, Claude is having trouble finding a solution...

@deadzy, @invest-time, would you like to test your respective AIs?
You can see the full source code below... for fun if you want, I'll have to wait until tomorrow for the rest.

(FULL SOURCE AT THE END OF THE POST)

separator3.png

Best regards,
𝕴𝖙𝖍𝖆𝖗𝖆 𝕲𝖆ï𝖆𝖓≋ Prince of Principality of Bastion
≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋
Discord : https://discord.principalityofbastion.org
Website : https://principalityofbastion.org / https://nft.bastion.city
Social Networks: See on our website.

Founder of the Principality of Bastion

Want to be seen by our manual curation services?

Interact with us by joining our discord, use our #BASTION, or #FR IF you speak french in your post.


Français

Copilot_20250905_161639.png

separator3.png

Depuis que je suis passé en windows 11, j'ai du mal à caster facilement les chansons que je veux sur mon Google nest, au point que cela m'énervait ...

De plus, je suis parfois obligé de streamer des jeux ou des activités où j'ai clairement besoin d'un univers sonore en arrière plan.
C'est d'ailleurs pour cela que je crée, en ce moment, un maximum de chansons sur des rythmes variés, mais aux thèmes liés à mon univers, pour que les viewers puissent se repérer instantanément en se disant : Ah, cette musique, on est forcément chez Ithara !!!

Envie de les écouter ? Les voici : http://principalityofbastion.org/musiques/

Alors j'ai demandé à Claude IA de me faire une page web pouvant jouer toutes les musiques que je chargerais dans un répertoire, pour pouvoir les lire, en boucle, de façon aléatoire.

Quelques secondes de réflexion et zouh, il m'a fait ceci:
https://www.principalityofbastion.org/musicplayer.html
N'hésitez pas à copier le code source pour votre propre usage si vous voulez tenter de le modifier, car il n'est pas parfait, je vous explique ci-dessous...

Je viens de le tester ... Je peux loader les chansons depuis le disque dur, je peux modifier le son, je peux surfer sur la chanson, changer la vitesse de la musique, je peux faire le setup pour mélanger les chansons.

Par contre dès que j'arrive à la fin d'une chanson, ils charge la suivante et attend qu'on appuie à nouveau sur play...
Là, par contre, claude à du mal me trouve rune solution...

@deadzy , @invest-time auriez-vous envie de tester vos IA respective?
Vous pouvez voir la source complète ci-dessous... pour vous amuser si vous voulez, moi je dois attendre demain pour la suite.

(SOURCE COMPLETE EN FIN DE POST)

separator3.png

Best regards,
𝕴𝖙𝖍𝖆𝖗𝖆 𝕲𝖆ï𝖆𝖓≋ Prince of Principality of Bastion
≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋≋
Discord : https://discord.principalityofbastion.org
Website : https://principalityofbastion.org / https://nft.bastion.city
Social Networks: See on our website.

Founder of the Principality of Bastion

Want to be seen by our manual curation services?

Interact with us by joining our discord, use our #BASTION, or #FR IF you speak french in your post.


Source

language html & JS

<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Lecteur MP3 Minimaliste</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background: linear-gradient(135deg, #1a1a2e, #16213e);
            color: #fff;
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            padding: 20px;
        }

        .player-container {
            background: rgba(255, 255, 255, 0.1);
            backdrop-filter: blur(10px);
            border-radius: 20px;
            padding: 30px;
            max-width: 500px;
            width: 100%;
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
            border: 1px solid rgba(255, 255, 255, 0.1);
        }

        .file-input-container {
            margin-bottom: 30px;
            text-align: center;
        }

        .file-input-label {
            display: inline-block;
            padding: 12px 24px;
            background: linear-gradient(45deg, #667eea, #764ba2);
            border-radius: 50px;
            cursor: pointer;
            transition: all 0.3s ease;
            font-weight: 500;
            text-transform: uppercase;
            letter-spacing: 1px;
            font-size: 12px;
        }

        .file-input-label:hover {
            transform: translateY(-2px);
            box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
        }

        #musicFiles {
            display: none;
        }

        .current-track {
            text-align: center;
            margin-bottom: 20px;
            padding: 20px;
            background: rgba(255, 255, 255, 0.05);
            border-radius: 15px;
            border: 1px solid rgba(255, 255, 255, 0.1);
        }

        .track-title {
            font-size: 16px;
            font-weight: 600;
            margin-bottom: 5px;
            color: #667eea;
        }

        .track-info {
            font-size: 12px;
            opacity: 0.7;
            text-transform: uppercase;
            letter-spacing: 1px;
        }

        .audio-player {
            width: 100%;
            margin-bottom: 20px;
            border-radius: 10px;
            overflow: hidden;
        }

        .controls {
            display: flex;
            justify-content: center;
            gap: 15px;
            margin-bottom: 20px;
        }

        .control-btn {
            background: rgba(255, 255, 255, 0.1);
            border: 1px solid rgba(255, 255, 255, 0.2);
            color: #fff;
            width: 50px;
            height: 50px;
            border-radius: 50%;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: all 0.3s ease;
            font-size: 18px;
        }

        .control-btn:hover {
            background: rgba(255, 255, 255, 0.2);
            transform: scale(1.1);
        }

        .control-btn:active {
            transform: scale(0.95);
        }

        .progress-container {
            display: flex;
            align-items: center;
            gap: 10px;
            margin-bottom: 15px;
        }

        .time {
            font-size: 12px;
            font-weight: 500;
            min-width: 40px;
            text-align: center;
        }

        .progress-bar {
            flex: 1;
            height: 6px;
            background: rgba(255, 255, 255, 0.1);
            border-radius: 3px;
            cursor: pointer;
            position: relative;
            overflow: hidden;
        }

        .progress-fill {
            height: 100%;
            background: linear-gradient(90deg, #667eea, #764ba2);
            border-radius: 3px;
            width: 0%;
            transition: width 0.1s ease;
        }

        .playlist {
            max-height: 200px;
            overflow-y: auto;
            background: rgba(0, 0, 0, 0.2);
            border-radius: 10px;
            padding: 10px;
        }

        .playlist-item {
            padding: 8px 12px;
            cursor: pointer;
            border-radius: 5px;
            transition: all 0.2s ease;
            font-size: 14px;
            margin-bottom: 2px;
        }

        .playlist-item:hover {
            background: rgba(255, 255, 255, 0.1);
        }

        .playlist-item.active {
            background: linear-gradient(90deg, rgba(102, 126, 234, 0.3), rgba(118, 75, 162, 0.3));
            border-left: 3px solid #667eea;
        }

        .status {
            text-align: center;
            margin-top: 15px;
            font-size: 12px;
            opacity: 0.8;
        }

        ::-webkit-scrollbar {
            width: 6px;
        }

        ::-webkit-scrollbar-track {
            background: rgba(255, 255, 255, 0.1);
            border-radius: 3px;
        }

        ::-webkit-scrollbar-thumb {
            background: rgba(102, 126, 234, 0.5);
            border-radius: 3px;
        }

        ::-webkit-scrollbar-thumb:hover {
            background: rgba(102, 126, 234, 0.7);
        }
    </style>
</head>
<body>
    <div class="player-container">
        <div class="file-input-container">
            <label for="musicFiles" class="file-input-label">
                📁 Chargez vos fichiers MP3
            </label>
            <input type="file" id="musicFiles" accept="audio/mp3,audio/mpeg" multiple webkitdirectory>
        </div>

        <div class="current-track">
            <div class="track-title">Aucun fichier sélectionné</div>
            <div class="track-info">Choisissez un dossier contenant vos MP3</div>
        </div>

        <audio class="audio-player" controls id="audioPlayer">
            Votre navigateur ne supporte pas l'audio HTML5.
        </audio>

        <div class="controls">
            <button class="control-btn" id="prevBtn" title="Précédent">⏮</button>
            <button class="control-btn" id="playPauseBtn" title="Lecture/Pause">▶</button>
            <button class="control-btn" id="nextBtn" title="Suivant">⏭</button>
            <button class="control-btn" id="shuffleBtn" title="Aléatoire">🔀</button>
        </div>

        <div class="progress-container">
            <span class="time" id="currentTime">0:00</span>
            <div class="progress-bar" id="progressBar">
                <div class="progress-fill" id="progressFill"></div>
            </div>
            <span class="time" id="duration">0:00</span>
        </div>

        <div class="playlist" id="playlist"></div>
        
        <div class="status" id="status">🔄 Lecture en boucle activée</div>
    </div>

    <script>
        class MP3Player {
            constructor() {
                this.audioPlayer = document.getElementById('audioPlayer');
                this.playlist = [];
                this.currentIndex = 0;
                this.isPlaying = false;
                this.isShuffled = false;
                this.originalPlaylist = [];
                
                this.initializeElements();
                this.bindEvents();
            }

            initializeElements() {
                this.playPauseBtn = document.getElementById('playPauseBtn');
                this.prevBtn = document.getElementById('prevBtn');
                this.nextBtn = document.getElementById('nextBtn');
                this.shuffleBtn = document.getElementById('shuffleBtn');
                this.progressBar = document.getElementById('progressBar');
                this.progressFill = document.getElementById('progressFill');
                this.currentTimeEl = document.getElementById('currentTime');
                this.durationEl = document.getElementById('duration');
                this.playlistEl = document.getElementById('playlist');
                this.trackTitle = document.querySelector('.track-title');
                this.trackInfo = document.querySelector('.track-info');
                this.fileInput = document.getElementById('musicFiles');
            }

            bindEvents() {
                // Contrôles
                this.playPauseBtn.addEventListener('click', () => this.togglePlayPause());
                this.prevBtn.addEventListener('click', () => this.previousTrack());
                this.nextBtn.addEventListener('click', () => this.nextTrack());
                this.shuffleBtn.addEventListener('click', () => this.toggleShuffle());

                // Barre de progression
                this.progressBar.addEventListener('click', (e) => this.seek(e));

                // Audio events
                this.audioPlayer.addEventListener('loadedmetadata', () => this.updateDuration());
                this.audioPlayer.addEventListener('timeupdate', () => this.updateProgress());
                this.audioPlayer.addEventListener('ended', () => this.nextTrack());
                this.audioPlayer.addEventListener('play', () => this.onPlay());
                this.audioPlayer.addEventListener('pause', () => this.onPause());

                // Chargement des fichiers
                this.fileInput.addEventListener('change', (e) => this.loadFiles(e));
            }

            loadFiles(event) {
                const files = Array.from(event.target.files);
                const mp3Files = files.filter(file => 
                    file.type === 'audio/mp3' || file.type === 'audio/mpeg' || file.name.toLowerCase().endsWith('.mp3')
                );

                if (mp3Files.length === 0) {
                    alert('Aucun fichier MP3 trouvé dans le dossier sélectionné.');
                    return;
                }

                this.playlist = mp3Files.map((file, index) => ({
                    file: file,
                    name: this.extractFileName(file.name),
                    path: file.webkitRelativePath || file.name,
                    url: URL.createObjectURL(file),
                    index: index
                }));

                this.originalPlaylist = [...this.playlist];
                this.currentIndex = 0;
                this.renderPlaylist();
                this.loadTrack(0);
                this.updateTrackInfo();
            }

            extractFileName(fullPath) {
                return fullPath.split('/').pop().replace('.mp3', '');
            }

            renderPlaylist() {
                this.playlistEl.innerHTML = '';
                this.playlist.forEach((track, index) => {
                    const item = document.createElement('div');
                    item.className = 'playlist-item';
                    item.textContent = `${track.name}`;
                    item.addEventListener('click', () => this.playTrack(index));
                    
                    if (index === this.currentIndex) {
                        item.classList.add('active');
                    }
                    
                    this.playlistEl.appendChild(item);
                });
            }

            loadTrack(index) {
                if (index >= 0 && index < this.playlist.length) {
                    this.currentIndex = index;
                    this.audioPlayer.src = this.playlist[index].url;
                    this.updateTrackInfo();
                    this.updateActivePlaylistItem();
                }
            }

            playTrack(index) {
                this.loadTrack(index);
                this.play();
            }

            updateTrackInfo() {
                if (this.playlist.length > 0) {
                    const track = this.playlist[this.currentIndex];
                    this.trackTitle.textContent = track.name;
                    this.trackInfo.textContent = `${this.currentIndex + 1} / ${this.playlist.length}`;
                }
            }

            updateActivePlaylistItem() {
                const items = this.playlistEl.querySelectorAll('.playlist-item');
                items.forEach((item, index) => {
                    item.classList.toggle('active', index === this.currentIndex);
                });
            }

            togglePlayPause() {
                if (this.audioPlayer.paused) {
                    this.play();
                } else {
                    this.pause();
                }
            }

            play() {
                if (this.playlist.length > 0) {
                    this.audioPlayer.play();
                }
            }

            pause() {
                this.audioPlayer.pause();
            }

            onPlay() {
                this.isPlaying = true;
                this.playPauseBtn.innerHTML = '⏸';
            }

            onPause() {
                this.isPlaying = false;
                this.playPauseBtn.innerHTML = '▶';
            }

            nextTrack() {
                if (this.playlist.length === 0) return;
                
                let nextIndex = this.currentIndex + 1;
                if (nextIndex >= this.playlist.length) {
                    nextIndex = 0; // Boucle au début
                }
                
                this.loadTrack(nextIndex);
                if (this.isPlaying) {
                    this.play();
                }
            }

            previousTrack() {
                if (this.playlist.length === 0) return;
                
                // Mémoriser si on était en train de jouer
                const wasPlaying = !this.audioPlayer.paused;
                
                let prevIndex = this.currentIndex - 1;
                if (prevIndex < 0) {
                    prevIndex = this.playlist.length - 1; // Boucle à la fin
                }
                
                this.loadTrack(prevIndex);
                
                // Si on était en train de jouer, continuer la lecture
                if (wasPlaying) {
                    this.shouldAutoPlay = true;
                    setTimeout(() => {
                        this.audioPlayer.play().catch(e => {
                            console.log('Erreur de lecture auto:', e);
                        });
                    }, 100);
                }
            }

            toggleShuffle() {
                this.isShuffled = !this.isShuffled;
                
                if (this.isShuffled) {
                    this.shuffleBtn.style.background = 'linear-gradient(45deg, #667eea, #764ba2)';
                    this.shuffleArray(this.playlist);
                } else {
                    this.shuffleBtn.style.background = 'rgba(255, 255, 255, 0.1)';
                    this.playlist = [...this.originalPlaylist];
                }
                
                this.currentIndex = 0;
                this.renderPlaylist();
                this.loadTrack(0);
            }

            shuffleArray(array) {
                for (let i = array.length - 1; i > 0; i--) {
                    const j = Math.floor(Math.random() * (i + 1));
                    [array[i], array[j]] = [array[j], array[i]];
                }
            }

            seek(event) {
                const rect = this.progressBar.getBoundingClientRect();
                const percent = (event.clientX - rect.left) / rect.width;
                const newTime = percent * this.audioPlayer.duration;
                this.audioPlayer.currentTime = newTime;
            }

            updateProgress() {
                if (this.audioPlayer.duration) {
                    const percent = (this.audioPlayer.currentTime / this.audioPlayer.duration) * 100;
                    this.progressFill.style.width = percent + '%';
                    this.currentTimeEl.textContent = this.formatTime(this.audioPlayer.currentTime);
                }
            }

            updateDuration() {
                this.durationEl.textContent = this.formatTime(this.audioPlayer.duration);
            }

            formatTime(seconds) {
                if (isNaN(seconds)) return '0:00';
                const mins = Math.floor(seconds / 60);
                const secs = Math.floor(seconds % 60);
                return `${mins}:${secs.toString().padStart(2, '0')}`;
            }
        }

        // Initialisation du lecteur
        document.addEventListener('DOMContentLoaded', () => {
            new MP3Player();
        });
    </script>
</body>
</html>

0.35741879 BEE
8 comments

Pour l'audio j'utilise VLC, il répond à mes besoins
!DUO
!BBH

0.00000000 BEE


You just got DUO from @servelle.
They have 1/1 DUO calls left.


duo_logo
Learn all about DUO here.
0.00000000 BEE
(edited)

certes, mais c'est un soft en plus à lancer et parfois pour le live c'est déjà gourmand en ressources :) Est-ce qu'il permet de caster vers le NEST ???
!LOL
!DUO

0.00000000 BEE


You just got DUO from @itharagaian.
They have 1/1 DUO calls left.


duo_logo
Learn all about DUO here.
0.00000000 BEE

Why do math teachers have so many babies?
Because they know how to multiply.

Credit: theabsolute
@servelle, I sent you an $LOLZ on behalf of itharagaian

(1/10)

PLAY & EARN $DOOM

0.00000000 BEE

Est-ce qu'il permet de caster vers le NEST ??

Je ne sais même pas ce qu'est le NEST 😅 mais c'est un open source assez complet je crois
!LOLZ

0.00000000 BEE

Bonne soirée
!PIZZA
!LOL
!HUG

0.00000000 BEE

journalier
!hivebits

0.00000000 BEE

florenceboens, you mined 1.0 🟧 HBIT If you had replied to another Hive user, the HBIT would have been split: 0.9 for you and 0.1 for them as a tip. When you mine HBIT, you're also playing the Wusang: Isle of Blaq game. 🏴‍☠️ | tools | wallet | discord | community | daily <><

What's more, you found 1.0 ⚪ BLAQ pearl as a bonus treasure token!


Your random number was 0.2917383381934253, also viewable in the Discord server, #hbit-wusang-log channel. Check for bonus treasure tokens by entering your username at block explorer A, explorer B, or take a look at your wallet.

There is a treasure chest of bitcoin sats hidden in Wusang: Isle of Blaq. Happy treasure hunting! 😃 Read about Hivebits (HBIT) or read the story of Wusang: Isle of Blaq.

0.00000000 BEE

Cartoonist found dead in home.
Details are sketchy.

Credit: reddit
@itharagaian, I sent you an $LOLZ on behalf of florenceboens

(3/10)

PLAY & EARN $DOOM

0.00000000 BEE

fini ca amrche
!LOL

0.00000000 BEE

I went to visit the wife's grave today.
She still thinks it's going to be a fishpond.

Credit: reddit
@florenceboens, I sent you an $LOLZ on behalf of itharagaian

(3/10)
Delegate Hive Tokens to Farm $LOLZ and earn 110% Rewards. Learn more.

0.00000000 BEE
(edited)

PIZZA!

$PIZZA slices delivered:
faustine.books tipped itharagaian
florenceboens tipped itharagaian
@itharagaian(2/20) tipped @seckorama (x2)
isiksenpalvoja tipped itharagaian

Come get MOONed!

0.00000000 BEE

Suite des essais demain donc !
!PIZZA
!LOL
!HUG

0.00000000 BEE

Where do elephants hide?
They paint their toenails red and hide in strawberry patches!

Credit: reddit
@itharagaian, I sent you an $LOLZ on behalf of isiksenpalvoja

(2/10)
Farm LOLZ tokens when you Delegate Hive or Hive Tokens.
Click to delegate: 10 - 20 - 50 - 100 HP

0.00000000 BEE

non là c'est obn hihi
!LOL

0.00000000 BEE

How often should you tell chemistry jokes?
Periodically.

Credit: reddit
@isiksenpalvoja, I sent you an $LOLZ on behalf of itharagaian

(4/10)
NEW: Join LOLZ's Daily Earn and Burn Contest and win $LOLZ

0.00000000 BEE

Repos jusque demain !
!PIZZA
!LOL
!HUG

0.00000000 BEE

Why did everyone enjoy the volcano?
It was just so lava-able.

Credit: belhaven14
@itharagaian, I sent you an $LOLZ on behalf of faustine.books

(2/10)
Delegate Hive Tokens to Farm $LOLZ and earn 110% Rewards. Learn more.

0.00000000 BEE

je tente sa ce weekend
!LOLZ
!HUG

0.00000000 BEE

Plus besoin c'est opérationnel :)

0.00000000 BEE

IT WORKS, it's LIVE
Ca marche c'est en ligne : http://principalityofbastion.org/mp3.html

0.00000000 BEE

It works for me. 👍

0.00000000 BEE

I guess the main point of this app should be that someone else can listen to my music and I can listen to theirs :)

0.00000000 BEE
(edited)

Nope, here the goal is simply to make my life easier to play the music for my live sessions :)
To play the music of others, we would need access to their files.

!PIZZA

0.00000000 BEE


Delegate your Hive Power to Ecency and
earn daily curation rewards in $Hive!

0.00000000 BEE

thank you
!PIZZA

0.00000000 BEE