commit 709e79732448b73df60384f3cc765cb8d3d65a64
parent d58489b29506923144e34b634541427f8dbcc3c2
Author: mtmn <miro@haravara.org>
Date: Mon, 13 Apr 2026 18:55:21 +0200
chore: rename to corpus
Diffstat:
15 files changed, 51 insertions(+), 51 deletions(-)
diff --git a/.env.example b/.env.example
@@ -2,14 +2,14 @@ LISTENBRAINZ_USER=mtmn
LASTFM_USER=
DISCOGS_TOKEN=
LASTFM_API_KEY=
-S3_BUCKET=scorpus-covers
+S3_BUCKET=corpus-covers
S3_REGION=us-east-1
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_S3_ADDRESSING_STYLE=path
AWS_ENDPOINT_URL=http://localhost:8333
INITIAL_SYNC=true
-DATABASE_FILE=/tmp/scorpus.db
+DATABASE_FILE=/tmp/corpus.db
COVER_CACHE_ENABLED=true
BACKUP_ENABLED=true
BACKUP_INTERVAL_HOURS=1
diff --git a/Containerfile b/Containerfile
@@ -40,7 +40,7 @@ RUN mkdir -p /app/data && chown -R node:node /app/data
USER node
ENV PORT=8321
-ENV DATABASE_FILE=/app/data/scorpus.db
+ENV DATABASE_FILE=/app/data/corpus.db
ENV NODE_ENV=production
CMD ["node", "--no-deprecation", "server.js"]
diff --git a/README.md b/README.md
@@ -1,8 +1,8 @@
-# Scorpus
+# corpus
| | |
| :--- | ---: |
-| **Scorpus** is an alternative [ListenBrainz](https://listenbrainz.org) and [Last.fm](https://last.fm) frontend that stores metadata and cover images.<br><br>Includes scrobbles fetching, metadata enrichment, and an interactive [PureScript](https://purescript.org) frontend for exploration of your listening habits.<br><br>[Live instance running here.](https://scrobbler.mtmn.name) | <img src="docs/korpus.webp" width="400" alt="Korpus"> |
+| **corpus** is an alternative [ListenBrainz](https://listenbrainz.org) and [Last.fm](https://last.fm) frontend that stores metadata and cover images.<br><br>Includes scrobbles fetching, metadata enrichment, and an interactive [PureScript](https://purescript.org) frontend for exploration of your listening habits.<br><br>[Live instance running here.](https://scrobbler.mtmn.name) | <img src="docs/korpus.webp" width="400" alt="Korpus"> |
## Documentation
@@ -50,7 +50,7 @@ npx spago bundle --module Client --outfile client.js --platform browser
| `LISTENBRAINZ_USER` | — | ListenBrainz username — enables scrobble sync when set |
| `LASTFM_USER` | — | Last.fm username — enables scrobble sync when set |
| `LASTFM_API_KEY` | — | Last.fm API key (required when `LASTFM_USER` is set; also used for genre and cover art fallback) |
-| `DATABASE_FILE` | `scorpus.db` | Path to the DuckDB database file |
+| `DATABASE_FILE` | `corpus.db` | Path to the DuckDB database file |
| `PORT` | `8000` | HTTP port to listen on |
| `COVER_CACHE_ENABLED` | `true` | Set to `false` to disable S3 cover art caching |
| `S3_BUCKET` | — | S3 bucket name for cover art caching |
@@ -63,4 +63,4 @@ npx spago bundle --module Client --outfile client.js --platform browser
| `BACKUP_ENABLED` | — | Set to `true` to enable periodic local database backups |
| `BACKUP_INTERVAL_HOURS` | `1` | How often to back up the database (in hours) |
-Backups are written to a `backup/` directory alongside `DATABASE_FILE`, named `scorpus-<timestamp>.db`.
+Backups are written to a `backup/` directory alongside `DATABASE_FILE`, named `corpus-<timestamp>.db`.
diff --git a/devenv.nix b/devenv.nix
@@ -1,3 +1,3 @@
{
- cachix.push = "scorpus";
+ cachix.push = "corpus";
}
diff --git a/docs/architecture.md b/docs/architecture.md
@@ -1,6 +1,6 @@
-# Scorpus Architecture
+# Corpus Architecture
-Scorpus is a personal music listening history dashboard and analytics service. It synchronizes scrobbles from ListenBrainz and Last.fm and provides a performant web interface for data exploration and statistics.
+Corpus is a personal music listening history dashboard and analytics service. It synchronizes scrobbles from ListenBrainz and Last.fm and provides a performant web interface for data exploration and statistics.
## System Components
@@ -19,7 +19,7 @@ A Single Page Application (SPA) built with PureScript and the [Halogen](https://
- **Responsive UI**: Designed for both desktop and mobile viewing with a "retro-modern" aesthetic.
### Database
-Scorpus uses **DuckDB** for its primary data storage.
+Corpus uses **DuckDB** for its primary data storage.
- **Schema**:
- `scrobbles`: Stores the core listening history (timestamp, track, artist, album, MBIDs). The `listened_at` Unix timestamp is the primary key — scrobbles from ListenBrainz and Last.fm deduplicate naturally.
- `release_metadata`: Stores enriched metadata indexed by MusicBrainz Release ID (MBID).
@@ -73,7 +73,7 @@ When a cover is requested:
## Foreign Function Interface (FFI)
-Scorpus relies on FFI to interact with the Node.js and browser ecosystems where native PureScript wrappers are unavailable or where direct JS access is required. Key FFI integrations include:
+Corpus relies on FFI to interact with the Node.js and browser ecosystems where native PureScript wrappers are unavailable or where direct JS access is required. Key FFI integrations include:
- **Database (`Db.js`)**: Provides a high-performance interface to the native `duckdb` library. It includes custom logic to handle BigInt conversions, ensuring database results are compatible with standard JSON serialization.
- **Cloud Storage (`S3.js`)**: Leverages the official AWS SDK (`@aws-sdk/client-s3`) to manage cover art caching in S3-compatible storage.
@@ -91,7 +91,7 @@ graph TD
CAA[Cover Art Archive]
end
- subgraph Scorpus Server
+ subgraph Corpus Server
LBSync[ListenBrainz Sync]
LFSync[Last.fm Sync]
Enrich[Enrichment Task]
diff --git a/docs/duckdb.md b/docs/duckdb.md
@@ -1,6 +1,6 @@
-# DuckDB in Scorpus
+# DuckDB in Corpus
-Scorpus uses [DuckDB](https://duckdb.org/) as its primary analytical database. DuckDB's columnar storage and efficient query engine allow Scorpus to provide fast filtering, pagination, and statistics over large sets of listening history data.
+Corpus uses [DuckDB](https://duckdb.org/) as its primary analytical database. DuckDB's columnar storage and efficient query engine allow Corpus to provide fast filtering, pagination, and statistics over large sets of listening history data.
## Database Schema
@@ -37,7 +37,7 @@ The application interacts with DuckDB via a PureScript FFI layer (`src/Db.js` an
## Common Analytical Queries
-You can run these queries directly against your `scorpus.db` file using the DuckDB CLI or any compatible tool.
+You can run these queries directly against your `corpus.db` file using the DuckDB CLI or any compatible tool.
### Top 10 Artists of All Time
```sql
diff --git a/flake.nix b/flake.nix
@@ -5,8 +5,8 @@
};
nixConfig = {
- extra-substituters = ["https://attic.saatana.cat/scorpus"];
- extra-trusted-public-keys = ["scorpus:mtDW9MGPAwrpye91ZDh3dMPBBzyz597OHNs4KempuLY="];
+ extra-substituters = ["https://attic.saatana.cat/corpus"];
+ extra-trusted-public-keys = ["corpus:mtDW9MGPAwrpye91ZDh3dMPBBzyz597OHNs4KempuLY="];
};
outputs = {
@@ -39,7 +39,7 @@
};
spagoDeps = pkgs.stdenv.mkDerivation {
- name = "scorpus-spago-deps";
+ name = "corpus-spago-deps";
src = pkgs.lib.cleanSourceWith {
src = self;
@@ -93,8 +93,8 @@
);
};
- scorpus = pkgs.buildNpmPackage {
- pname = "scorpus";
+ corpus = pkgs.buildNpmPackage {
+ pname = "corpus";
version = "1.0.0";
inherit src;
@@ -130,30 +130,30 @@
'';
installPhase = ''
- mkdir -p $out/lib/scorpus
- cp -r server.js client.js package.json node_modules assets $out/lib/scorpus/
+ mkdir -p $out/lib/corpus
+ cp -r server.js client.js package.json node_modules assets $out/lib/corpus/
if [ -f .env ]; then
- cp .env $out/lib/scorpus/
+ cp .env $out/lib/corpus/
else
- cp .env.example $out/lib/scorpus/.env
+ cp .env.example $out/lib/corpus/.env
fi
mkdir -p $out/bin
- makeWrapper ${pkgs.nodejs}/bin/node $out/bin/scorpus-server \
+ makeWrapper ${pkgs.nodejs}/bin/node $out/bin/corpus-server \
--add-flags "--no-deprecation" \
- --add-flags "$out/lib/scorpus/server.js" \
- --chdir "$out/lib/scorpus"
+ --add-flags "$out/lib/corpus/server.js" \
+ --chdir "$out/lib/corpus"
'';
};
in {
- packages.default = scorpus;
+ packages.default = corpus;
packages.container = pkgs.dockerTools.buildLayeredImage {
- name = "scorpus";
+ name = "corpus";
tag = "latest";
contents = [pkgs.nodejs pkgs.cacert];
config = {
- Cmd = ["${scorpus}/bin/scorpus-server"];
+ Cmd = ["${corpus}/bin/corpus-server"];
WorkingDir = "/app";
ExposedPorts = {
"8321/tcp" = {};
diff --git a/justfile b/justfile
@@ -31,7 +31,7 @@ nix command:
@if [ "{{ command }}" = "build" ]; then \
nix build .; \
elif [ "{{ command }}" = "run" ]; then \
- ./result/bin/scorpus-server; \
+ ./result/bin/corpus-server; \
else \
echo "Unknown nix command: {{ command }}"; \
exit 1; \
@@ -52,7 +52,7 @@ container command:
elif [ "{{ command }}" = "push" ]; then \
skopeo copy \
--dest-precompute-digests \
- docker-archive:result docker://ghcr.io/mtmn/scorpus:latest; \
+ docker-archive:result docker://ghcr.io/mtmn/corpus:latest; \
else \
echo "Unknown container command: {{ command }}"; \
exit 1; \
diff --git a/package-lock.json b/package-lock.json
@@ -1,11 +1,11 @@
{
- "name": "scorpus",
+ "name": "corpus",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
- "name": "scorpus",
+ "name": "corpus",
"version": "1.0.0",
"dependencies": {
"@aws-sdk/client-s3": "^3.1029.0",
diff --git a/package.json b/package.json
@@ -1,5 +1,5 @@
{
- "name": "scorpus",
+ "name": "corpus",
"version": "1.0.0",
"description": "ListenBrainz frontend in PureScript",
"main": "index.js",
diff --git a/spago.lock b/spago.lock
@@ -1,7 +1,7 @@
{
"workspace": {
"packages": {
- "scorpus": {
+ "corpus": {
"path": "./",
"core": {
"dependencies": [
diff --git a/spago.yaml b/spago.yaml
@@ -1,5 +1,5 @@
package:
- name: scorpus
+ name: corpus
dependencies:
- aff
- affjax
diff --git a/src/Db.purs b/src/Db.purs
@@ -73,7 +73,7 @@ performBackup conn dbFile = do
Left _ -> "unknown"
let dir = dirName dbFile <> "backup/"
void $ try $ FSA.mkdir' dir { recursive: true, mode: Perms.mkPerms Perms.all Perms.all Perms.read }
- let dest = dir <> "scorpus-" <> ts <> ".db"
+ let dest = dir <> "corpus-" <> ts <> ".db"
FSA.copyFile dbFile dest
Log.info $ "Backup saved locally: " <> dest
diff --git a/src/Main.purs b/src/Main.purs
@@ -1036,7 +1036,7 @@ type MbData = { genre :: Maybe String, label :: Maybe String, year :: Maybe Int
fetchMusicBrainzRelease :: String -> Aff (Maybe MbData)
fetchMusicBrainzRelease mbid = do
let url = "https://musicbrainz.org/ws/2/release/" <> mbid <> "?inc=genres+labels+release-groups&fmt=json"
- result <- try $ fetch url { method: GET, headers: { "User-Agent": "Scorpus/1.0 +https://codeberg.org/mtmn/scorpus" } }
+ result <- try $ fetch url { method: GET, headers: { "User-Agent": "corpus/1.0 +https://codeberg.org/mtmn/corpus" } }
case result of
Left err -> do
Log.error $ "MusicBrainz fetch error for " <> mbid <> ": " <> Exception.message err
@@ -1257,7 +1257,7 @@ main = do
env <- getEnv
let
port = getEnvInt env "PORT" 8000
- dbFile = getEnvStr env "DATABASE_FILE" "scorpus.db"
+ dbFile = getEnvStr env "DATABASE_FILE" "corpus.db"
username = getEnvStr env "LISTENBRAINZ_USER" ""
backupEnabled = getEnvBool env "BACKUP_ENABLED" false
backupIntervalHours = getEnvInt env "BACKUP_INTERVAL_HOURS" 1
diff --git a/test/Main.purs b/test/Main.purs
@@ -28,7 +28,7 @@ main = do
Process.setEnv "S3_BUCKET" "my-bucket"
runSpecAndExitProcess [consoleReporter] do
- describe "Scorpus Main Utils" do
+ describe "Corpus Main Utils" do
it "should build ListenBrainz URLs correctly" do
listenBrainzUrl "user1" `shouldEqual` "https://api.listenbrainz.org/1/user/user1/listens"
@@ -37,7 +37,7 @@ main = do
sanitizeKey "T.est-123" `shouldEqual` "T.est-123"
sanitizeKey "multiple spaces" `shouldEqual` "multiple_spaces"
- describe "Scorpus Types" do
+ describe "Corpus Types" do
describe "MbidMapping Codecs" do
it "should roundtrip MbidMapping" do
let mbid = MbidMapping { releaseMbid: Just "release-123", caaReleaseMbid: Just "caa-456" }
@@ -111,7 +111,7 @@ main = do
Left err ->
fail $ "Decoding failed: " <> show err
- describe "Scorpus Database" do
+ describe "Corpus Database" do
it "should handle scrobble and metadata operations" do
conn <- connect ":memory:"
initDb conn
@@ -157,18 +157,18 @@ main = do
listensEmpty <- getScrobbles conn 10 0 (Just { field: "genre", value: "Jazz" })
length listensEmpty `shouldEqual` 0
- describe "Scorpus Backup" do
+ describe "Corpus Backup" do
describe "dirName" do
it "extracts directory from an absolute path" do
- dirName "/app/data/scorpus.db" `shouldEqual` "/app/data/"
+ dirName "/app/data/corpus.db" `shouldEqual` "/app/data/"
it "extracts directory from a nested path" do
- dirName "/tmp/test/scorpus.db" `shouldEqual` "/tmp/test/"
+ dirName "/tmp/test/corpus.db" `shouldEqual` "/tmp/test/"
it "returns ./ for a bare filename" do
- dirName "scorpus.db" `shouldEqual` "./"
+ dirName "corpus.db" `shouldEqual` "./"
it "local backup creates a file in backup/ alongside the db" do
- let testDir = "/tmp/scorpus-backup-test"
- let dbPath = testDir <> "/scorpus.db"
+ let testDir = "/tmp/corpus-backup-test"
+ let dbPath = testDir <> "/corpus.db"
let backupDir = testDir <> "/backup"
-- clean up any previous run
void $ try $ FSA.rm' testDir { force: true, recursive: true, maxRetries: 0, retryDelay: 100 }
@@ -288,7 +288,7 @@ main = do
listens <- getScrobbles conn 10 0 Nothing
length listens `shouldEqual` 1
- describe "Scorpus S3" do
+ describe "Corpus S3" do
it "should generate virtual-host style S3 URLs" do
liftEffect $ Process.setEnv "AWS_S3_ADDRESSING_STYLE" "virtual"
let url = getS3Url "covers/test.jpg"