Mornye logo

A focused core.
Your choice of extensions.

Mornye resolves track, album and playlist metadata. It ships with no bundled provider and no default extension source — every audio backend is a module you find, review, and plug in yourself.

Python module Python 3.11+ MIT licensed
$ python -m pip install .
core.socket()awaiting connection
CORE metadata only tidal-web — empty — — empty — — empty — your-ext.py
How it works

Three sockets, in order

Nothing about the audio backend is decided for you. The core resolves what a URL means; getting the actual audio is a separate, opt-in step.

01

Resolve the URL

Paste a track, album, playlist or artist link. The core resolves title, artist, album, and other metadata. No audio yet.

02

Point at a registry

Set MORNYE_REGISTRIES, or add one from Settings if you're using the GUI/web interface, to a source of extensions you've chosen. Nothing is fetched until you do.

03

Retrieve audio

An installed extension (JavaScript or Python) handles the actual provider. The core never talks to a provider directly.

Features

What the core actually does

 

API

Sync & async Python

A blocking Mornye() call for scripts, or AsyncMornye for bots and web backends.

+

JS or Python extensions

Bring your own registry. Nothing bundled, nothing installed automatically, ever.

◧

CLI, GUI, web, terminal

--gui for a native window, --web for the same interface in a browser, --tui for a guided terminal UI.

♫

MusicBrainz enrichment

Genre, label, BPM, UPC and ISRC tags added automatically when a match is found.

Aa

Synced lyrics embedding

Optional, multi-provider, embedded directly into the file's tags.

⟳

Local retagger

The GUI rescans and fixes tags on files you already have, with automatic backup before writing.

∞

Watch mode

--watch re-syncs a playlist, album, or artist on an interval, indefinitely — only what's new gets fetched.

≈

Duplicate detection

Finds the same recording across your library by acoustic fingerprint, independent of tags or ISRC.

✓

Discovery & signed registries

Browse community registries before adding one, and verify Ed25519-signed extension entries on top of the usual checksum.

Quick start

Pick an interface

All talk to the same core. None can retrieve audio until you've installed at least one extension — see below.

# clone and install
git clone https://github.com/mornyeapp/Mornye-Python.git
cd Mornye-Python
python -m pip install .

# point at a registry you've chosen, then install an extension
export MORNYE_REGISTRIES="https://example.com/my-registry.json"

# resolve + download using an installed extension
mornye https://open.spotify.com/track/TRACK_ID ./downloads --service ext:tidal-web
# native window
mornye --gui
# same interface, served locally — open http://127.0.0.1:8000
mornye --web
from mornye import Mornye

Mornye(
    url="https://open.spotify.com/track/TRACK_ID",
    output_dir="./downloads",
    services=["ext:tidal-web"],  # requires the extension to be installed
)
+

About that ext:tidal-web

It only resolves to something if you've installed an extension by that name, from a registry you configured. The maintainer doesn't ship, curate, or default to one — see Extensions in the README for the full mechanism.

FAQ

Common questions

 

Why doesn't anything download right after I install it?
By design. The core resolves metadata only — it has no built-in provider and no default extension source. You need to point MORNYE_REGISTRIES at a registry of your choosing and install at least one extension before it can retrieve audio.
Can I write my own extension?
Yes — JavaScript and Python are both supported. mornye --ext-scaffold NAME generates a working starting point for either, and mornye --ext-dry-run PATH validates it (manifest, entry point, exposes the right shape) without installing it or contacting any registry. For Python there's a full walkthrough — the BaseProvider contract, the core toolbox, a complete example provider, packaging and publishing: see Writing a Python Extension.
GUI or web mode — which should I use?
--gui opens a native window. --web serves the identical interface as a local web server instead, useful on headless machines or inside Docker. Web mode binds to 127.0.0.1 by default — changing that exposes it to anything that can reach the port, unless you also set --web-token (a shared secret) or --web-multiuser (per-account login).
Does the maintainer review third-party extensions or registries?
No. Extensions are found, chosen, and installed entirely by you. The maintainer doesn't review, endorse, or take responsibility for what a third-party extension or registry does — treat installing one with the same care you'd apply to any other third-party code.
Is this legal to use?
Mornye's core doesn't host, store, or distribute copyrighted content — it resolves metadata. What happens when you install and use a third-party extension is between you and the terms of service of whatever platform that extension talks to. See the full disclaimer for specifics; this isn't legal advice.

Disclaimer

This project is an independent tool and is not affiliated with, endorsed by, or connected to Spotify, Tidal, Qobuz, Amazon Music, Deezer, or any other streaming service.

  • No copyrighted content is hosted, stored, mirrored, or distributed by this project. The core does not bundle, ship, or default to any third-party extension, registry, or provider.
  • You are responsible for ensuring your use of this software, and any extension or registry you choose to install, complies with your local laws and the terms of service of any platform you access.
  • The maintainer does not review, endorse, or take responsibility for the content or behavior of third-party extensions or registries a user may choose to install.
Full disclaimer, license and details → github.com/mornyeapp/Mornye-Python