WEB Media player test - Help EN/FR

in LaTaverne13 days ago

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>

Sort:  

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


You just got DUO from @servelle.
They have 1/1 DUO calls left.
duo_logo
Learn all about DUO here.

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


You just got DUO from @itharagaian.
They have 1/1 DUO calls left.
duo_logo
Learn all about DUO here.

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

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

Did you know that protons have mass?
I didn't even know they were catholic.

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

(1/10)

PLAY & EARN $DOOM

Google nest c'est ceci :
untitled.gif
Comme noté dans le post c'est la raison principal de mon outil. :)

Bonne soirée
!PIZZA
!LOL
!HUG

journalier
!hivebits

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.

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

fini ca amrche
!LOL

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.

PIZZA!

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

Come get MOONed!

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

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

non là c'est obn hihi
!LOL

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

Repos jusque demain !
!PIZZA
!LOL
!HUG

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.

je tente sa ce weekend
!LOLZ
!HUG

Plus besoin c'est opérationnel :)

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

It works for me. 👍

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

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

Yeah, I know, but just my thought - when I upload to your HTML file, it should contain the URL or IP and path, similar to P2P exchanges, and if I allow access, someone else should stream from my disk :)
But you need to have a DB of all uploads somewhere...
!INDEED


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

thank you
!PIZZA