jukette
Version:
White-label jukebox custom element for audio, SoundCloud, and MIDI playlists.
539 lines (400 loc) • 14.8 kB
Markdown
# jukette
A white-label jukebox custom element exposed as `<jukette-player>`.
Jukette v0.9.0
By Rémino Rem
<https://remino.net/>
[Docs](https://remino.net/jukette/) |
[Code Repo](https://github.com/remino/jukette) |
[npm Package](https://www.npmjs.com/package/jukette)
---
<!-- mtoc-start -->
- [Installation](#installation)
- [HTML (CDN)](#html-cdn)
- [npm](#npm)
- [Direct download](#direct-download)
- [Usage](#usage)
- [Playlist](#playlist)
- [Tracks](#tracks)
- [API](#api)
- [Styling](#styling)
- [Roadmap](#roadmap)
- [Development](#development)
- [Release](#release)
- [Contributing](#contributing)
- [Licence](#licence)
<!-- mtoc-end -->
---
## Installation
### HTML (CDN)
Register the custom element automatically from a CDN:
```html
<script src="https://unpkg.com/jukette"></script>
```
Mirrors:
- https://unpkg.com/jukette
- https://cdn.jsdelivr.net/npm/jukette
Use a pinned version in production:
```html
<script src="https://unpkg.com/jukette@0.9.0"></script>
```
If you want the API instead of auto-registration, import the ES module directly:
```html
<script type="module">
import { defineElement } from 'https://unpkg.com/jukette@0.9.0/dist/jukette.mjs'
defineElement()
</script>
```
### npm
Install the convenience package first:
```sh
npm install jukette
```
Then register the custom element automatically:
```js
import 'jukette/auto'
```
Or import the explicit API:
```js
import { defineElement } from 'jukette'
defineElement()
```
TypeScript declarations are included with the package.
Optional modular packages:
```sh
npm install jukette @remino/jukette-midi @remino/jukette-soundcloud
```
- `jukette`: convenience package with `@remino/jukette-core` and
`@remino/jukette-audio`.
- `@remino/jukette-core`: backendless player infrastructure.
- `@remino/jukette-audio` and `@remino/jukette-audio/auto`: browser-native
audio backend.
- `@remino/jukette-midi` and `@remino/jukette-midi/auto`: optional MIDI
backend.
- `@remino/jukette-soundcloud` and `@remino/jukette-soundcloud/auto`:
optional SoundCloud backend.
Typical addon usage:
```js
import 'jukette/auto'
import '@remino/jukette-midi/auto'
import '@remino/jukette-soundcloud/auto'
```
### Direct download
Download the package tarball or individual files from npm/CDN:
- https://www.npmjs.com/package/jukette
- https://unpkg.com/jukette/dist/
- https://cdn.jsdelivr.net/npm/jukette/dist/
The browser-ready auto-registration file is `dist/jukette-auto.min.js`.
Distribution files:
- `dist/jukette.mjs`: ES module library API.
- `dist/jukette.cjs`: CommonJS library API.
- `dist/jukette-auto.mjs`: ES module auto-registration entry.
- `dist/jukette-auto.cjs`: CommonJS auto-registration entry.
- `dist/jukette-auto.min.js`: minified classic browser auto-registration.
[Back to top](#)
---
## Usage
After registration, use the element with a single source:
```html
<jukette-player src="/audio/theme.mp3"></jukette-player>
```
Or pass a playlist with child track elements:
```html
<jukette-player preload-metadata prefer-media-metadata>
<jukette-track
title="Theme"
artist="Local"
src="/audio/theme.mp3"
></jukette-track>
<jukette-track
title="Sketch"
src="/midi/sketch.mid"
type="midi"
></jukette-track>
<jukette-track
title="Flickermood"
artist="Forss"
src="https://soundcloud.com/forss/flickermood"
type="soundcloud"
preload
></jukette-track>
<jukette-track
title="Reprise"
artist="Local"
src="/audio/reprise.ogg"
></jukette-track>
</jukette-player>
```
Changing the selected track prepares it for playback but does not start it
automatically. Jukette enables play, seek, and time controls once the selected
track is ready.
The default `jukette` entry includes browser-native audio support only. Import
addons for other track types:
```js
import 'jukette/auto'
import '@remino/jukette-midi/auto'
import '@remino/jukette-soundcloud/auto'
```
[Back to top](#)
---
## Playlist
Use direct `<jukette-track>` children for authored HTML. Browser HTML requires
explicit closing tags, so write `<jukette-track></jukette-track>` rather than a
self-closing tag.
For generated markup or compatibility with older usage, the `playlist`
attribute also accepts JSON. Each item can be either a URL string or a track
object.
```json
[
"/audio/one.mp3",
{
"title": "Two",
"artist": "Example",
"src": "/audio/two.ogg",
"type": "audio",
"preload": true,
"preferMediaMetadata": false
}
]
```
When JSON parsing fails, Jukette treats the attribute as a newline-separated URL
list.
For remote JSON, point `playlist-src` at an endpoint or static file that
returns the same JSON array or object shape accepted by the `playlist`
attribute.
```html
<jukette-player playlist-src="/audio/playlist.json"></jukette-player>
```
Track sources are resolved in this order:
- `player.playlist` set from JavaScript.
- Direct `<jukette-track>` children.
- `playlist` attribute JSON or newline list.
- `playlist-src` remote JSON.
- Single `src` attribute.
[Back to top](#)
---
## Tracks
`<jukette-track>` attributes and track object fields:
- `src`: required URL for a local audio file, local MIDI file, or public
SoundCloud track URL when `type="soundcloud"` is used.
- `title`: optional display title.
- `artist`: optional display artist.
- `type`: optional backend-owned track type such as `audio`, `midi`, or
`soundcloud`.
- `start-at` / `startAt`: optional per-track start offset in seconds. Accepts
fractional seconds.
- `preload` attribute / `preload` object field: optional per-track playback
preparation preference.
- `prefer-media-metadata` / `preferMediaMetadata`: optional per-track override
for the player's media metadata preference.
- `show-source-link` / `showSourceLink`: optional per-track override for
whether the selected track should expose a source-page link in the header.
If `type` is omitted, Jukette treats `.mid` / `.midi` sources as `midi`.
Everything else defaults to `audio`.
In practice, the convenience `jukette` package registers the browser-native
audio backend for you, so tracks without an explicit `type` normally behave as
audio tracks unless their source looks like MIDI.
MIDI playback uses `@tonejs/midi` for parsing and a compact Tone.js synth for
browser playback. It is intentionally simple and suitable for local MIDI
previews, not a full General MIDI instrument set.
SoundCloud playback is available through the optional
`@remino/jukette-soundcloud` addon. Selecting a SoundCloud track prepares the
hidden widget and oEmbed metadata first, then enables Play once the widget is
ready to accept playback calls. Player-level `preload-metadata` does not
prepare SoundCloud tracks by itself; use per-track `preload` when you want a
SoundCloud widget prepared before selection, and `prefer-media-metadata` when
you want authored labels to yield to fetched SoundCloud metadata.
If a selected track type has no registered backend, Jukette leaves the track
selected, keeps playback controls disabled, and surfaces that the track type is
unavailable.
[Back to top](#)
---
## API
Each element exposes:
```js
const player = document.querySelector('jukette-player')
player.play()
player.pause()
player.toggle()
player.seek(30)
player.currentTime = 30
console.log(player.currentTime)
console.log(player.currentTrack)
console.log(player.currentTrackIndex)
console.log(player.totalTracks)
player.playlist = [{ title: 'Track', src: '/track.mp3' }]
player.preloadMetadata = true
player.preferMediaMetadata = true
player.showTrackSelect = false
player.showSourceLink = true
player.midiOscillator = 'sine'
```
Core and addon imports:
```js
import { defineElement } from '@remino/jukette-core'
import { register as registerAudio } from '@remino/jukette-audio'
import { register as registerMidi } from '@remino/jukette-midi'
import { register as registerSoundCloud } from '@remino/jukette-soundcloud'
registerAudio()
registerMidi()
registerSoundCloud()
defineElement()
```
Use the `preload-metadata` attribute or `preloadMetadata` property to discover
playlist durations before tracks are played. Jukette preloads metadata for
registered backends that provide preload hooks, including browser-native audio
and local MIDI. SoundCloud stays opt-in here: player-level `preload-metadata`
alone does not fetch or prepare SoundCloud tracks.
Use `currentTime` to read the current playback position in seconds. Assigning
to `currentTime` seeks, matching native media element behavior.
Use `currentTrack`, `currentTrackIndex`, and `totalTracks` to inspect the track
selection state.
Selecting a track prepares it and resets playback to the start of that track.
Playback begins only after an explicit `play()` call or a user press on the
play button.
If the selected track backend is unavailable, Jukette keeps the selected track
visible but leaves play, seek, and time controls disabled.
Use `prefer-media-metadata` or `preferMediaMetadata` to let readable media-file
tags override authored track titles and artists. Jukette currently reads MP3
ID3 `TIT2` title and `TPE1` artist tags, plus MIDI track/sequence names as
titles. MIDI artists stay authored-only. Authored values stay in place when
tags are missing, unreadable, or unsupported.
Direct `<jukette-track>` children and JavaScript track objects can override the
player-level preference per track. Use `prefer-media-metadata` or
`preferMediaMetadata: true` to force metadata display for that track, use
`prefer-media-metadata="false"` or `preferMediaMetadata: false` to force
authored display values, or omit it to inherit the player setting.
Use `preload` or `preload: true` to ask Jukette to prepare a track for playback
when possible. The flag is track-local and does not change media metadata
preloading. For SoundCloud tracks, it also opts that track into early widget
preparation before the user selects it.
Use `midi-oscillator` or `midiOscillator` to choose the Tone.js MIDI preview
oscillator. Supported values are `auto`, `sine`, `square`, `sawtooth`, and
`triangle`. `auto` is the default and maps MIDI program changes to a simple
preview timbre; invalid values fall back to `auto`. The property matters only
when the MIDI addon is registered.
Use `display-marquee` or `displayMarquee` to control how the merged header
display scrolls. Supported values are `overflow`, `always`, and `never`.
`overflow` is the default and scrolls only when the text overflows.
Use `show-track-select` or `showTrackSelect` to control whether the track
selector row is shown. The default is on. Set `show-track-select="false"` or
`player.showTrackSelect = false` to hide it, and omit the attribute or set it
to `true` to show it.
Use `show-source-link` or `showSourceLink` to expose a narrow source-page link
beside the header display for the selected track. The default is off. When
enabled, Jukette uses the selected track's `src` as the link target. Direct
`<jukette-track>` children and JavaScript track objects can override the player
setting per track with `show-source-link`,
`show-source-link="false"`, `showSourceLink: true`, or
`showSourceLink: false`.
Jukette dispatches bubbling composed custom events from the `<jukette-player>`
host:
- `jukette:play`
- `jukette:pause`
- `jukette:seek`
- `jukette:ended`
- `jukette:trackchange`
Each event includes `event.detail` with the current `track`, `tracks`, `index`,
`type`, `currentTime`, `duration`, and `playing`.
[Back to top](#)
---
## Styling
Jukette keeps the default UI basic on purpose. It uses inherited text color and
font, a single border, and native range controls. Style the host element first:
```css
jukette-player {
color: #111;
font:
1rem/1.4 system-ui,
sans-serif;
max-inline-size: 36rem;
--jukette-control-size: 2.25rem;
}
```
The host supports these stable styling inputs:
- `color`: inherited by text, borders, buttons, and range accents.
- `font`: inherited by labels, buttons, and the track selector.
- `--jukette-control-size`: controls the square play button size. Defaults to
`2em`.
- `inline-size`, `max-inline-size`, `margin`, and other normal layout
properties on `jukette-player`.
Use host attributes for state-specific styling:
```css
jukette-player[data-kind='midi'] {
color: #164e63;
}
```
Range inputs use `accent-color: currentColor` inside the shadow DOM, so changing
the host `color` changes the seek accent in browsers that support native range
accent styling.
For deeper styling, Jukette exposes stable `::part()` hooks:
- Layout: `player`, `track`, `seek`, `time`, `controls`.
- Track display: `display`.
- Controls: `button`, `play-button`, `seek-input`, `track-select`.
```css
jukette-player::part(player) {
border: 0;
padding: 0;
}
jukette-player::part(play-button) {
border-radius: 999px;
}
jukette-player::part(track-select) {
font-size: 0.95rem;
}
```
[Back to top](#)
---
## Roadmap
Forward-looking work lives in [ROADMAP.md](./ROADMAP.md). For now, that covers
the planned SoundCloud addon direction without expanding `jukette` core again.
[Back to top](#)
---
## Development
```sh
npm install
npm run dev
npm run build
```
This repository is an npm workspaces monorepo:
- `packages/jukette`: publishes `jukette`.
- `packages/core`: publishes `@remino/jukette-core`.
- `packages/audio`: publishes `@remino/jukette-audio`.
- `packages/midi`: publishes `@remino/jukette-midi`.
- `apps/docs`: Astro docs and playground site.
Root scripts orchestrate builds in dependency order. Use `npm run build`,
`npm run typecheck`, and `npm test` from the repo root.
Common root shortcuts:
- `npm run dev` or `npm run dev:docs`: start the Astro docs site.
- `npm run build:packages`: build the publishable packages only.
- `npm run build:docs`: build the docs app only.
- `npm run preview:docs`: preview the built docs app.
[Back to top](#)
---
## Release
Release automation is available through `release-it`. A release runs checks,
bumps the root and publishable workspace package versions in lockstep, builds
the workspace packages, dry-runs publish order, publishes the scoped packages
first, publishes `jukette` last, creates the GitHub release, and then publishes
docs:
```sh
npm run release:dry-run
npm run release
```
If docs publishing fails after the package release, rerun it directly:
```sh
npm run docs:publish
```
Before running a real release, make sure `RELEASE_IT_GITHUB_TOKEN` is set and
`npm whoami --registry https://registry.npmjs.org/` passes. Release-it prompts
for an npm OTP when npm requires one.
[Back to top](#)
---
## Contributing
1. Fork the repository.
2. Create a feature branch: `git checkout -b feature/amazing-feature`.
3. Make your changes.
4. Run `npm run build` and `npm test`.
5. Commit, push, and open a pull request.
---
## Licence
ISC. See [LICENSE.md](LICENSE.md).