Grayjay plugins in one repository. Install an active plugin by opening its config URL in Grayjay.
| Plugin | Source | Config |
|---|---|---|
| MP3Quran | mp3quran.net | Install |
| tvQuran | tvquran.com | Install |
| Archive.org | archive.org | Install |
| Mixlr | mixlr.com | Install |
| AlBadr | al-badr.net | Install |
| Quran.com | quran.com | Install |
| Midad | midad.com | Install |
| QuranicAudio | quranicaudio.com | Install |
| EveryAyah | everyayah.com | Install |
| AlQuran.cloud | alquran.cloud | Install |
| BinBaz | binbaz.org.sa | Install |
| Tilawat Al Haramain | tilawatalharamain.com | Install |
| Jeman | j-eman.net | Install |
| Saleh Al Sheikh | saleh.af.org.sa | Install |
| DrosQ8 | drosq8.com | Install |
| DrosUAE | drosuae.com | Install |
| AlQasim | a-alqasim.com | Install |
| BinOthaimeen | binothaimeen.net | Install |
| Shkhudheir | shkhudheir.com | Install |
| AlFiqh | alfiqh.net | Install |
| Alukah | alukah.net | Install |
| Baheth | baheth.ieasybooks.com | Install |
| IslamHouse | islamhouse.com | Install |
| IslamWeb Audio | audio.islamweb.net | Install |
| Sunnah Wadeha | sunnahwadeha.com | Install |
- MP3Quran reciter channels expose one playlist per moshaf. Global playlist search also exposes tafsir-source playlists and video-type playlists.
- tvQuran category channels expose audio selection playlists, and reciter channels expose moshaf/collection playlists parsed from the reciter profile. tvQuran video categories stay as channel/content results because Grayjay playlist details require video items, while these entries are nested YouTube links.
- Direct tvQuran collection URLs are recognized as both channel URLs and playlist URLs, so shared collection links can open into either navigation surface depending on what Grayjay asks for.
- Archive.org exposes items as playlists, with each playable audio/video file represented as a playlist entry. It does not provide a home feed; use search to discover items.
- Mixlr is live-only in v1; it intentionally does not expose playlists until recordings or collections are implemented as concrete playable lists.
- AlBadr exposes each
/sub/{id}lesson series as a playlist, category channels expose their series through channel playlists, and the site live page is represented as nested Mixlr content. - Quran.com exposes each reciter as a timed full-surah playlist containing that reciter's surahs. Older ayah-style Quran.com URLs are accepted for compatibility but resolve to the full surah item.
MP3Quran, Quran.com, and QuranicAudio expose continuous full-surah media. EveryAyah and AlQuran.cloud expose each ayah as a playable item and group those items into reciter/edition surah playlists; they do not synthesize continuous surah files.
MP3Quran surah tracks use MP3Quran's ayah timing API for timed chapters. Quran text is fetched lazily per surah from fawazahmed0/quran-api using ara-quransimple.
Android clients that support plugin subtitle sources can use the generated WebVTT subtitle source. Grayjay Desktop currently plays direct MP3 audio through a path that does not attach external subtitles, so the plugin also includes the ayah text in chapter labels as the Desktop fallback.
Run every live integration test:
pnpm run test:livePass a plugin registry value to run one test file:
pnpm run test:live -- midadLive tests call the upstream provider websites. Set LIVE_TEST_DEEP=1 to include the slower MP3Quran catalog fallback test.
Use Grayjay's Android DevServer while serving this repository from your computer on the same network.
-
Enable developer mode in Grayjay:
- Open Grayjay on Android.
- Go to
More->Settings. - Scroll to the bottom and tap
Version Coderepeatedly.
-
Start Grayjay DevServer:
- In Grayjay settings, open
Developer Settings. - Tap
Start Server. - Note the phone IP address from Android Wi-Fi/network settings.
- In Grayjay settings, open
-
Serve this repo from your computer:
cd <repo> mise run dev
The dev server rebuilds plugin scripts when
src/**orplugins/registry.jsonchanges, then serves the generated files fromplugins/**. -
Open the DevServer in your computer browser:
http://PHONE_IP:11337/dev -
Load a plugin using your computer LAN IP, not
localhost:Replace the raw GitHub origin in any config link from the active plugin table with
http://COMPUTER_IP:3000. -
In the DevServer UI:
- Click
Load Pluginto test individual methods. - Use the
Integrationtab and clickInject Pluginto test inside the Android app. - Click refresh or inject again after local edits.
- Click
The phone must be able to reach your computer over the LAN, and your firewall must allow inbound connections to port 3000.
Android DevServer API helpers:
pnpm run grayjay:dev-api -- logs --server-url http://PHONE_IP:11337
pnpm run grayjay:dev-api -- logs --server-url http://PHONE_IP:11337 --follow
pnpm run grayjay:dev-api -- http --server-url http://PHONE_IP:11337
pnpm run grayjay:dev-api -- remote-test --server-url http://PHONE_IP:11337 --method getContentDetails --args '["CONTENT_URL"]'
pnpm run grayjay:dev-api -- load --plugin mp3quran --server-url http://PHONE_IP:11337 --dev-url http://COMPUTER_IP:3000 --followRun the Android smoke suite for the released Quran plugins with:
pnpm run grayjay:android-smoke -- --server-url http://PHONE_IP:11337 --dev-url http://COMPUTER_IP:3000This loads MP3Quran, tvQuran, AlBadr, and Archive.org as Android test plugins and verifies search capabilities, home/search feeds, filters, content details, chapters where supported, channels, channel search, and URL recognition. Android remoteTest currently cannot serialize playlist detail objects because Grayjay's Gson path sees duplicate inherited contentType fields on playlist classes, so playlist checks assert URL recognition plus that known platform serializer limitation for getPlaylist and searchPlaylists.
For a running AVD, install the ABI-matching Grayjay APK, start Grayjay once, enable developer mode in the app, then start Grayjay's DevServer from Developer Settings.
The default emulator serial used by the mise tasks is emulator-5554. Override it with ANDROID_SERIAL when needed:
ANDROID_SERIAL=emulator-5556 mise run grayjay-avd-forwardInstall and launch Grayjay:
mise run grayjay-avd-install
mise run grayjay-avd-launchgrayjay-avd-install defaults to tmp/app-arm64-v8a-release.apk. Override the APK path with:
GRAYJAY_APK=tmp/app-universal-release.apk mise run grayjay-avd-installForward the emulator DevServer port to the host:
mise run grayjay-avd-forwardServe plugin configs/scripts from the host:
mise run devRun the Android smoke suite against the AVD:
mise run grayjay-avd-smokeThe AVD smoke task uses:
--server-url http://127.0.0.1:11337, becauseadb forwardexposes Grayjay's emulator DevServer on the host loopback.--dev-url http://10.0.2.2:3000, because Android Emulator reaches the host machine through10.0.2.2.
For a non-default dev server or forwarded port:
GRAYJAY_SERVER_URL=http://127.0.0.1:11338 GRAYJAY_DEV_URL=http://10.0.2.2:3001 mise run grayjay-avd-smokeTo expose optional plugin smoke tests in Grayjay's TestSystem tab, build with:
pnpm run build:debug-tests -- --plugin mp3quranRebuild normally before signing or release builds.
Use Grayjay Desktop's Developer Portal while serving this repository from your computer.
-
Enable developer mode by creating a
DEVfile in Grayjay Desktop's data directory.On macOS:
mise run grayjay-desktop-dev-mode
Restart Grayjay Desktop after creating the file.
-
Serve this repo from your computer:
cd <repo> pnpm run dev
The dev server rebuilds plugin scripts when
src/**orplugins/registry.jsonchanges, then serves the generated files fromplugins/**. -
Find Grayjay Desktop's local server port:
mise run grayjay-desktop-port
-
Open the Developer Portal in your browser:
mise run grayjay-desktop-dev
-
Load a plugin using the local config URL:
Replace the raw GitHub origin in any config link from the active plugin table with
http://127.0.0.1:3000. -
In the Developer Portal:
- Click
Load Pluginto test individual methods. - Use the
Integrationtab and clickInject Pluginto test inside Grayjay Desktop. - Click refresh or inject again after local edits.
- Click
Signing is not needed during local development. Grayjay Desktop normally listens on loopback with a random port written to the port file. Desktop also has a --server mode that binds on port 11338.
Desktop has two useful log surfaces, and they show different failures:
tail -n 200 "$HOME/Library/Application Support/Grayjay/log.txt"
curl -sS "http://127.0.0.1:$(cat "$HOME/Library/Application Support/Grayjay/port")/Developer/GetDevLogs?index=0"log.txt is the backend log. The Developer log endpoint is available without the private UI token and shows plugin method calls such as search, isContentDetailsUrl, and getContentDetails. A successful getContentDetails only proves the plugin returned a details object; it does not prove Desktop selected a source or the CEF media element started playback.
The normal player endpoints, including /details/SourceAuto and /details/SourceProxy, require Grayjay Desktop's private _token header. Direct curl calls without that token fail with No valid token, which is expected and not a plugin failure.
For endpoint debugging without copying the UI token, start Desktop server mode:
mise run grayjay-desktop-server-unsafeThat launches Grayjay Desktop with --server --ignore-security and exposes debug endpoints at:
http://127.0.0.1:11338
Then inject the local dev plugin into that server-mode process:
pnpm run grayjay:dev-api -- load --target desktop --plugin archiveorgThis posts the local config from http://127.0.0.1:3000/plugins/archiveorg/ArchiveOrgConfig.json, rewrites the script URL to the local dev server, and enables the injected plugin as DEV.
Desktop API quick check:
base=http://127.0.0.1:11338
identifier='ARCHIVE_IDENTIFIER'
url="https%3A%2F%2Farchive.org%2Fdetails%2F$identifier"
curl -sS "$base/details/VideoLoad?url=$url"
curl -sS "$base/details/SourceAuto"
curl -sS "$base/Developer/GetDevLogs?index=0"The helper script can also read Desktop developer logs:
pnpm run grayjay:dev-api -- logs --target desktop
pnpm run grayjay:dev-api -- load --target desktop --plugin archiveorg --followVideoLoad asks the enabled plugin for content details and stores that item as Desktop's current video. SourceAuto asks Desktop to choose the source it would hand to the player. For audio-only Archive items it should return audioIndex: 0 and a direct audio/* URL; for video items it should return videoIndex: 0 and a direct video/* URL.
To inspect a specific source index explicitly:
curl -sS "$base/details/SourceProxy?videoIndex=-1&audioIndex=0&subtitleIndex=-1&videoIsLocal=false&audioIsLocal=false&subtitleIsLocal=false&tag=debug"When playback fails after getContentDetails succeeds:
-
Open Grayjay Desktop devtools and check the browser console for
source auto,Direct url,Player error,HLS player error, orDashJSmessages. These are emitted by the Desktop web player and do not appear in the Developer log. -
Check whether the loaded config is the local dev URL in
lastDevUrl:cat "$HOME/Library/Application Support/Grayjay/lastDevUrl" -
Rebuild before reinjecting; generated scripts are what Desktop loads:
pnpm run build -- --plugin archiveorg
-
Prefer direct Archive file hosts from metadata (
https://ia*.archive.org/.../items/...) overhttps://archive.org/download/...redirect URLs for media sources. Desktop's player path is easier to debug when the final media URL is already resolved. -
If inspecting Desktop behavior, fetch the missing engine submodule/source separately. The Desktop repo references
Grayjay.Engine; that source contains the JS-to-model conversion forVideoSourceDescriptor,UnMuxVideoSourceDescriptor,VideoUrlSource, andAudioUrlSource.
Plugin source lives in src/plugins/<plugin>/index.js. Do not edit generated plugins/<plugin>/*Script.js files directly unless you are debugging generated output.
After source edits, rebuild the generated Grayjay scripts:
pnpm run build:allFor one plugin:
pnpm run build -- --plugin mp3quranCommit both the source changes under src/** and the generated plugins/**/*Script.js output. Signing should happen only after this build step; validation and signing both fail when a source file is newer than its generated script.
Create a new registry entry, config, source entry, generated script, and live test placeholder:
pnpm run scaffold -- --value midad --label "Midad" --stem Midad --platform-url https://midad.comRun pnpm run scaffold without flags for an interactive prompt.
The same task is available through mise:
mise run scaffold -- --value midad --label "Midad" --stem Midad --platform-url https://midad.comUse --dry-run to preview without writing files. Pass --icon-url when the default /favicon.ico is not suitable.
pnpm run validateValidation checks the source entry files, generated scripts, plugin configs, local icon paths, and source/script freshness.
pnpm run format
pnpm run format:check
pnpm run lint
pnpm run checkcheck runs formatting, linting, pnpm run build:all, validation, and unit tests.
Install Lefthook once after cloning:
lefthook installThe pre-commit hook runs pnpm run format, pnpm run lint:fix, and pnpm run validate, then stages fixed files.
Bump versions and sign scripts only after final source edits and pnpm run build:all, because any generated script change invalidates the signature.
Install local tooling once:
pnpm installpnpm run bump
pnpm run sign
pnpm run sign-bumpThe prompted tasks let you select plugins, enter changelog entries when bumping, and choose a signing key when signing. The signing key defaults to $GRAYJAY_SIGN_KEY. bump writes the next version and changelog, sign writes scriptSignature and scriptPublicKey, and sign-bump does both in one flow. Signing refuses stale generated scripts, so run the build first.
The same tasks can run non-interactively:
pnpm run bump -- --plugin mp3quran --message "Release notes" --yes --no-input
pnpm run sign -- --plugin all --key ~/.ssh/ysh --yes --no-input
pnpm run sign-bump -- --plugin all --message "Release notes" --key ~/.ssh/ysh --yes --no-inputUse --dry-run to preview without writing files. --plugin accepts any registry value from the active plugin table, or all, and can be repeated or comma-separated.
- MP3Quran website
- MP3Quran API docs
- MP3Quran ayah timing reads
- tvQuran website
- Quran text repository
- Quran text CDN pattern
- Quran.com
- Quran.com Content API docs
- Quran.com recitations API docs
- Quran.com chapter-reciter audio docs
- Internet Archive metadata API
- Internet Archive files, formats, and derivatives guide
- Internet Archive search API
- AlBadr website
- Grayjay plugin development docs
- Grayjay sample plugin
- Grayjay RadioBrowser plugin
This repository is licensed under the GNU Affero General Public License v3.0 or later. See LICENSE.