Skip to content
Β 
Β 

Latest commit

Β 

History

14 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Deezer Rank to Navidrome Ratings (dztnr)

This script repurposes the star ratings in Navidrome by aligning them with Deezer's track rank. It fetches the Deezer popularity rank (0–1,000,000+) for each track via the public search API and maps it to a 1–5 star rating. Higher rank = more popular on Deezer. Optionally, ratings can be graded relative to the most popular track of the artist or album (--curve), which is more meaningful for niche libraries. No API key is required β€” the Deezer API is completely open.

This is a fork of krestaino/sptnr by Kevin Restaino, which originally used Spotify's popularity score. This fork replaces Spotify with Deezer's public API, removing the need for OAuth, client secrets, or any authentication.

Screenshot of script and phone

Table of Contents

  1. Features
  2. Requirements
  3. Quick Start (GHCR pre-built image)
  4. Building from this fork
  5. Using Docker Compose
  6. Running Natively (Python)
  7. Usage
  8. Examples
  9. Resuming Interrupted Sessions
  10. Managing Docker Containers
  11. Mapping Deezer Rank to Navidrome Ratings
  12. Relative Popularity Grading (--curve)
  13. Estimated Processing Times
  14. Importance of Accurate Metadata for Track Lookup
  15. Logs
  16. CI/CD (GitHub Actions)
  17. Credits

Features

  • Deezer Integration: Uses the public Deezer Search API (api.deezer.com/search) β€” no API key or authentication needed.
  • Navidrome Integration: Updates track ratings in Navidrome based on Deezer's popularity rank.
  • Relative Popularity Grading: --curve album|artist grades each track against the most popular track of its album or artist on Deezer, instead of the absolute global rank β€” ideal for niche libraries.
  • Flexible Processing: Process specific artists, albums, or a range of artists or albums.
  • Skip Existing: Optionally skip tracks that are already starred (favorited) or already rated in Navidrome, preserving your manual curation.
  • Preview Mode: Run the script in preview mode to see changes without making any actual updates.
  • Logging: Detailed logging of the process, both in the console and to a file.
  • Docker Support: Pre-built image available on GitHub Container Registry (GHCR) with multi-arch support (amd64 + arm64).

Requirements

  • Docker or Python 3.x
  • Access to a Navidrome server
  • No API keys or external accounts required (Deezer API is public)

Compatibility Note: While this script was built with Navidrome in mind, it should theoretically work on any Subsonic server.

Quick Start (GHCR pre-built image)

The easiest way to run dztnr is using the pre-built Docker image published to GitHub Container Registry on every push to main. It supports both linux/amd64 and linux/arm64.

docker run -t --rm \
  -e NAV_BASE_URL=https://your_navidrome.example.com \
  -e NAV_USER=your_navidrome_username \
  -e NAV_PASS=your_navidrome_password \
  ghcr.io/scatolo/dztnr:latest

To pin a specific version (check packages for available tags):

docker run -t --rm \
  -e NAV_BASE_URL=https://your_navidrome.example.com \
  -e NAV_USER=your_navidrome_username \
  -e NAV_PASS=your_navidrome_password \
  ghcr.io/scatolo/dztnr:1.3.0

Tip: Use --rm to auto-remove the container when it finishes. If you want persistent logs, add -v ./logs:/usr/src/app/logs.

First run? Start in preview mode with a single artist to test connectivity:

docker run -t --rm \
  -e NAV_BASE_URL=https://your_navidrome.example.com \
  -e NAV_USER=your_admin_user \
  -e NAV_PASS=your_admin_password \
  ghcr.io/scatolo/dztnr:latest -p -l 1

(-p = preview, no writes; -l 1 = only the first artist)

Building from this fork

This is a fork of krestaino/sptnr. The main changes are:

  • Deezer instead of Spotify β€” no OAuth, no client ID/secret, no API key
  • Rating based on Deezer rank instead of Spotify popularity
  • Optional relative popularity grading (--curve album|artist)
  • Rate limiting at 0.2s per API call
  • Pre-built images published to GHCR via GitHub Actions

Build locally

git clone https://github.com/scatolo/dztnr.git
cd dztnr
docker build -t dztnr-local .
docker run -t --rm \
  -e NAV_BASE_URL=https://your_navidrome.example.com \
  -e NAV_USER=your_navidrome_username \
  -e NAV_PASS=your_navidrome_password \
  dztnr-local

Run natively with Python

git clone https://github.com/scatolo/dztnr.git
cd dztnr
cp .env.example .env
# Edit .env with your Navidrome URL and credentials
pip install -r requirements.txt
python dztnr.py -p -l 1   # preview mode, first artist only

Using Docker Compose

  1. Create docker-compose.yml: copy docker-compose.yml.example and fill in your Navidrome details.

    Using the pre-built GHCR image (recommended):

    version: "3.8"
    
    services:
      dztnr:
        container_name: dztnr
        image: ghcr.io/scatolo/dztnr:latest
        environment:
          - NAV_BASE_URL=https://your_navidrome.example.com
          - NAV_USER=your_navidrome_username
          - NAV_PASS=your_navidrome_password
        volumes:
          - ./logs:/usr/src/app/logs

    Or build locally:

    version: "3.8"
    
    services:
      dztnr:
        container_name: dztnr
        build: .
        environment:
          - NAV_BASE_URL=https://your_navidrome.example.com
          - NAV_USER=your_navidrome_username
          - NAV_PASS=your_navidrome_password
        volumes:
          - ./logs:/usr/src/app/logs
  2. Run the script:

    docker-compose run --rm dztnr

    With options:

    docker-compose run --rm dztnr -p -l 1

Running Natively (Python)

  1. Clone the repository or download the necessary files (dztnr.py, requirements.txt, .env.example).

  2. Install dependencies:

    pip install -r requirements.txt
  3. Configure environment variables. Rename .env.example to .env and fill in your Navidrome credentials:

    cp .env.example .env
    # Edit .env β€” only NAV_BASE_URL, NAV_USER, NAV_PASS are needed
  4. Run the script:

    python dztnr.py [options]

Usage

Options

Flag Long form Description
-p --preview Preview mode β€” no changes written to Navidrome
-a --artist ID Process a specific artist by Navidrome ID (repeatable)
-b --album ID Process a specific album by Navidrome ID (repeatable)
-s --start N Start processing from artist at index N (0-based)
-l --limit N Process at most N artists from the start index
--recent N Process the N most recently added albums
-d --debug Show additional debugging information (Deezer URLs, ranks, etc.)
--curve album Grade tracks relative to the album's most popular track on Deezer
--curve artist Grade tracks relative to the artist's most popular track on Deezer
--skip-starred Skip tracks already starred/favorited in Navidrome
--skip-rated Skip tracks that already have a rating (>0) in Navidrome
-v --version Print version and exit

Command Formats

Method Command
GHCR image docker run -t --rm -e NAV_BASE_URL=... -e NAV_USER=... -e NAV_PASS=... ghcr.io/scatolo/dztnr:latest [options]
Local Docker docker run -t --rm -e NAV_BASE_URL=... -e NAV_USER=... -e NAV_PASS=... dztnr-local [options]
Docker Compose (GHCR) docker-compose run --rm dztnr [options]
Python python dztnr.py [options]

Examples

Preview Mode

See what would be updated without making changes:

python dztnr.py -p
docker-compose run --rm dztnr -p

Process a Single Artist

python dztnr.py -a <navidrome_artist_id>
docker run -t --rm -e NAV_BASE_URL=... -e NAV_USER=... -e NAV_PASS=... ghcr.io/scatolo/dztnr:latest -a <navidrome_artist_id>

Process a Range of Artists

Start from artist #10 and process the next 5:

python dztnr.py -s 10 -l 5

Process Specific Albums

python dztnr.py -b <album_id_1> -b <album_id_2>

Process the N Most Recently Added Albums

Process only the albums most recently added to Navidrome:

python dztnr.py --recent 10

Debugging

Show additional debugging information (Deezer lookup URLs, ranks, etc.):

python dztnr.py -d

The debug flag can be combined with any other option:

python dztnr.py --recent 10 -d

Skip Already Starred or Rated Tracks

Use these flags to preserve your manual ratings and favorites. Both flags can be combined.

Skip tracks you've already favorited (starred):

python dztnr.py --skip-starred

Skip tracks that already have a rating:

python dztnr.py --skip-rated

Combine both to only process unrated, unstarred tracks:

python dztnr.py --skip-starred --skip-rated

With preview and artist range:

python dztnr.py -p --skip-starred --skip-rated -s 0 -l 20

Resuming Interrupted Sessions

If the session gets interrupted (network error, machine sleep, etc.), you can resume from where you left off.

Check the log file for the last processed artist. The log entry contains the index in brackets: Artist: ARTIST_NAME (ARTIST_ID)[INDEX].

Restart with -s INDEX:

python dztnr.py -s 42
docker run -t --rm -e ... ghcr.io/scatolo/dztnr:latest -s 42

Managing Docker Containers

docker run and docker-compose run create a new container each time. Use --rm to auto-remove them on exit, or clean up manually:

docker container prune

Warning: This removes all stopped containers on your system. Check with docker ps -a first.

Mapping Deezer Rank to Navidrome Ratings

The script translates Deezer's popularity rank into Navidrome's 5-star rating system:

Deezer Rank Rating Description
0 – 9,999 0 Unknown / Niche
10,000 – 99,999 1 Low popularity
100,000 – 299,999 2 Moderately popular
300,000 – 599,999 3 Popular
600,000 – 849,999 4 Very popular
850,000+ 5 Globally known

With --curve, this absolute mapping is replaced by the relative grading described below.

Relative Popularity Grading (--curve)

Absolute ranks are global: in niche or low-popularity libraries, most tracks end up with 1–2 stars. With --curve, each track is graded against the most popular track of the same scope on Deezer instead of the absolute global rank, so the reference is always the artist's or album's own biggest hit:

  • --curve artist β€” reference is the artist's most popular track on Deezer. Resolved once per artist, applied to all of their albums.
  • --curve album β€” reference is the most popular track of the album on Deezer. Resolved once per album.

Rating formula

ratio = track rank / reference rank, mapped to stars as follows:

Ratio Rating
β‰₯ 0.80 5
0.60 – 0.79 4
0.40 – 0.59 3
0.20 – 0.39 2
> 0 and < 0.20 1
0 0

The most popular track of the artist/album always gets 5 stars. Any matched track keeps at least 1 star (0 is reserved for unmatched tracks).

How the reference is resolved

  • Artist: https://api.deezer.com/search/artist?q=<artist> β†’ first match β†’ https://api.deezer.com/artist/<id>/top?limit=100; the highest rank among the top tracks is the reference.
  • Album: https://api.deezer.com/search/album?q=<artist> <album> β†’ first match β†’ https://api.deezer.com/album/<id>/tracks; the highest rank among the tracks is the reference.

These lookups add 2 extra API calls per artist (or per album) and are subject to the same 0.2s rate limiting. If the reference cannot be resolved (no match on Deezer, missing rank data), the script falls back to absolute grading for that artist/album and logs a warning.

Examples

python dztnr.py --curve artist
python dztnr.py --curve artist -a <navidrome_artist_id>
python dztnr.py --curve album -b <album_id_1> -b <album_id_2>
python dztnr.py -p --curve artist -s 0 -l 20

Estimated Processing Times

With a time.sleep(0.2) delay between Deezer API calls:

Library Size (Tracks) Estimated Time
1,000 ~5 minutes
5,000 ~25 minutes
10,000 ~50 minutes
50,000 ~4 hours
100,000 ~8 hours

These estimates assume a stable network connection. Actual times may vary.

Importance of Accurate Metadata for Track Lookup

The script searches Deezer using the query artist + track. Deezer's search is fairly forgiving, but accurate artist and track titles significantly improve the match rate. Tag your music library with MusicBrainz for best results.

Logs

Logs are stored in the logs/ directory. Each execution creates a new log file named deezer-rank_<timestamp>.log. Delete old logs manually if needed.

Log Format

r:450000 β†’ β˜…:3 | Song Title
  • r:<number> β€” Deezer rank (0–1,000,000+), or ?? if not found
  • β†’ β˜…:<rating> β€” Navidrome star rating assigned (0–5)

When --skip-starred or --skip-rated is used:

skipping (starred): Song Title
skipping (rated β˜…:4): Song Title
skipping (starred, rated β˜…:3): Song Title

Terminal Output Colors

  • Green β€” Track matched and processed
  • Yellow β€” Track skipped (starred or already rated)
  • Red β€” Track not found on Deezer (??)

Colors are terminal-only and stripped from log files.

CI/CD (GitHub Actions)

On every push to main, a GitHub Actions workflow (.github/workflows/docker-ghcr.yml) builds a multi-arch Docker image (linux/amd64 + linux/arm64) tagged with the version from the VERSION file and latest, then pushes it to GitHub Container Registry at ghcr.io/scatolo/dztnr.

Pull requests also trigger a build (amd64 only, no push) to validate the Dockerfile.

Available tags: github.com/scatolo/dztnr/pkgs/container/dztnr

Credits

This project is a fork of krestaino/sptnr by Kevin Restaino, who originally created the script to sync Spotify popularity with Navidrome ratings. This fork adapts the codebase to use Deezer's public API instead, removing the need for any authentication or API keys while preserving all the original Navidrome integration logic, CLI flags, and Docker support.

About

🎡 Sync Deezer's track popularity to Navidrome ratings.

Topics

Resources

Stars

17 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages