Last active
February 9, 2026 05:20
-
-
Save YamimakiReru/af7439cd32a4de77709b18d004fbd34e to your computer and use it in GitHub Desktop.
Enhance WordPress Audio Album Plugin: Sequential Track Playback and Volume Synchronization
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * Enhance WordPress Audio Album Plugin: Sequential Track Playback and Volume Synchronization | |
| * | |
| * Add the following features to the [Audio Album]( https://ja.wordpress.org/plugins/audio-album/ ) plugin. | |
| * - Start playing the next audio source automatically when the previous one ends. | |
| * - Synchronize volumes between audio elements when any one of them changes. | |
| * | |
| * To set up this script, upload it to your site and load it as a JavaScript file AFTER the Audio Album JS files. | |
| * Insert a <script> tag or use wp_enqueue_script() in functions.php. | |
| * wp_enqueue_script( 'audioalbum-r', get_template_directory_uri() . '/audioalbum-r.js', ['audioalbum'], null, true ); | |
| * | |
| * Works in my site with Audio Album v1.5.1. | |
| * | |
| * Live demo | |
| * https://symphonic.lol/en/music/audioalbum-r-js-live-demo | |
| * | |
| * SPDX-FileCopyrightText: 2026 Reru YAMIMAKI | |
| * SPDX-License-Identifier: MIT-0 | |
| */ | |
| 'use strict'; | |
| (() => { | |
| class RAudioAlbum { | |
| /** @param {HTMLElement} album */ | |
| constructor(album) { | |
| this.album = album; | |
| } | |
| /** @type {boolean} */ | |
| isSyncing = false; | |
| /** @type {HTMLAudioElement[]} */ | |
| audios = []; | |
| init() { | |
| this.audios = Array.from(this.album.querySelectorAll('audio')); | |
| for (const a of this.audios) { | |
| a.addEventListener('ended', this.onAudioEnded.bind(this)); | |
| a.addEventListener('volumechange', this.onVolumeChange.bind(this)); | |
| } | |
| } | |
| /** | |
| * Start playing the next audio source when the previous one ends. | |
| * @param {Event} ev | |
| */ | |
| onAudioEnded(ev) { | |
| /** @type {HTMLAudioElement} */ | |
| const a = ev.target; | |
| const nextIndex = (1 + this.audios.findIndex(a2 => a2 == a)) % this.audios.length; | |
| // An exception occurs unless the previous audio has completely stopped. | |
| setTimeout(() => { | |
| this.audios[nextIndex].play(); | |
| }, 100); | |
| } | |
| /** | |
| * Synchronize volumes between audio elements when any one of them changes. | |
| * @param {Event} ev | |
| */ | |
| onVolumeChange(ev) { | |
| if (this.isSyncing) { | |
| return; | |
| } | |
| this.isSyncing = true; | |
| try { | |
| /** @type {HTMLAudioElement} */ | |
| const a = ev.target; | |
| for (const a2 of this.audios) { | |
| if (a == a2) { | |
| continue; | |
| } | |
| a2.volume = a.volume; | |
| a2.muted = a.muted; | |
| } | |
| } finally { | |
| // Release the lock after event propagation ends. | |
| Promise.resolve().then(() => { | |
| this.isSyncing = false; | |
| }); | |
| } | |
| } | |
| } | |
| // Audio Album v1.5.1 does not create container elements for each album, including their <audio> elements. | |
| // -> Consider containers as outer elements whose direct children are .track elements. | |
| const audioAlbums = document.querySelectorAll('*:has(>.track)'); | |
| for (const album of audioAlbums) { | |
| new RAudioAlbum(album).init(); | |
| } | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment