5.6 KiB
Greyscale
Greyscale is a JavaScript API/runtime project whose primary source lives in src, while greyscale-app is retained as a legacy demo.
Project Scope
- Primary runtime source:
src - Primary API module:
src/api.js - Legacy demo (kept for reference):
greyscale-app
Current Structure
.
├── src/
│ ├── api.js
│ ├── app.js
│ ├── cache.js
│ ├── downloads.js
│ ├── lyrics.js
│ ├── player.js
│ └── utils.js
├── greyscale-app/ # legacy demo
├── monochrome/ # upstream reference submodule
└── server.js
Run
node server.js
# open http://127.0.0.1:12345
Testing
Run unit tests (offline/mocked only):
node tests/test.js
Run unit + live integration tests (real service calls):
node tests/test.js --live
Notes:
- The
--livemode calls real uptime workers, real API instances, andlrclib.net. - Some live checks may be skipped when upstream data is unavailable (for example missing IDs from search results).
- The summary at the end reports pass/fail/skip totals for unit and live suites separately.
API Documentation
GreyscaleAPI (src/api.js)
Create an API instance:
import { GreyscaleAPI } from './src/api.js';
const api = new GreyscaleAPI();
await api.initInstances();
Instance + transport
-
initInstances()- Loads healthy API and streaming instance pools from uptime workers.
- Falls back to static instance lists when workers fail.
-
fetchFromPool(poolType, path, options?)- Internal transport with failover across instance pool.
poolType:'api' | 'streaming'
Search endpoints
-
searchTracks(query)- Endpoint:
/search/?s={query} - Returns paged object:
items: Track[]limit,offset,totalNumberOfItems
- Endpoint:
-
searchAlbums(query)- Endpoint:
/search/?al={query} - Returns paged object with deduplicated album results.
- Endpoint:
-
searchArtists(query)- Endpoint:
/search/?a={query} - Returns paged object of artists.
- Endpoint:
Detail endpoints
-
getAlbum(id)- Endpoint:
/album/?id={id} - Returns:
{ album, tracks }
- Handles album track pagination (
offset/limit) for large albums.
- Endpoint:
-
getArtist(artistId, options?)- Endpoints:
/artist/?id={id}/artist/?f={id}&skip_tracks=true
- Returns normalized artist profile including:
albumsepstracks(top tracks)
- Endpoints:
-
getTrackMetadata(id)- Endpoint:
/info/?id={id} - Returns a normalized track object.
- Endpoint:
-
getTrackRecommendations(id)- Endpoint:
/recommendations/?id={id} - Returns:
Track[]
- Endpoint:
Streaming + media
-
getTrackStream(id, quality = 'HI_RES_LOSSLESS')- Endpoint:
/track/?id={id}&quality={quality} - Returns:
{ streamUrl, isDash }
- Handles direct URLs and manifest decoding.
- Endpoint:
-
downloadTrackBlob(id, quality = 'LOSSLESS', onProgress?)- Downloads stream into a
Blob. - Returns:
{ blob, filename, track }
- Progress callback shape:
{ receivedBytes, totalBytes? }
- Downloads stream into a
Artwork helpers
-
getCoverUrl(coverId, size = 320)- Builds Tidal image URL for album/track cover ids.
-
getArtistPictureUrl(id, size = 320)- Builds Tidal image URL for artist image ids.
Cache helpers
-
clearCache()- Clears API cache.
-
getCacheStats()- Returns cache stats object from
APICache.
- Returns cache stats object from
APICache (src/cache.js)
import { APICache } from './src/cache.js';
-
Constructor:
new APICache({ maxSize = 250, ttl = 20min } = {})
-
Methods:
get(namespace, key)set(namespace, key, value, ttl?)clearExpired()clear()getCacheStats()
Uses namespace-prefixed keys and timestamp-based TTL expiration.
DownloadManager (src/downloads.js)
import { DownloadManager } from './src/downloads.js';
-
Constructor:
new DownloadManager(api)
-
Methods:
setOnChange(callback)getTasks()clearFinished()cancel(taskId)downloadTrack(track, quality?)downloadAlbum(album, tracks, quality?)
Task status lifecycle:
queued -> downloading -> done- error path:
error - cancel path:
cancelled
LyricsManager (src/lyrics.js)
import { LyricsManager } from './src/lyrics.js';
-
Constructor:
new LyricsManager()
-
Methods:
fetchLyrics(track)- Fetches from LRCLIB and caches by track key.
getCurrentLineIndex(currentTime, lines)- Binary search for active synced line.
renderLyrics(payload, activeIndex?)- Produces renderable HTML for synced/plain lyrics.
Internal helper:
- LRC parsing into
{ time, text }[].
SimplePlayer (src/player.js)
import { SimplePlayer } from './src/player.js';
-
Constructor:
new SimplePlayer({ audio, progressEl, currentTimeEl, durationEl })
-
Core methods:
setResolver(resolveStreamFn)setQueue(tracks, startIndex?)getCurrentTrack()playCurrent(resolverOverride?)playNext(resolverOverride?)playPrev(resolverOverride?)togglePlayPause()
-
Events/callbacks:
onTrackChanged(track)onTimeUpdate(current, duration, track)
Supports direct audio URLs and DASH playback via dash.js.
Notes
src/app.jsis the integration shell (routing + UI wiring) for runtime modules.greyscale-appis intentionally preserved as demo history.