1
8
Fork
You've already forked nup
0
App Engine app for streaming a music collection
  • Go 66%
  • TypeScript 16%
  • C 9.2%
  • HTML 4.2%
  • JavaScript 3.2%
  • Other 1.4%
Daniel Erat d7695c7ea8
build: Update google-cloud-cli and Typescript in Dockerfile. [skip ci]
Set CLOUDSDK_SKIP_PY_COMPILATION=1 to try to shrink
google-cloud-cli's size, and upgrade to NPM 24.18.0 and
Typescript 7.0.2.
I was hoping that this would have a bigger effect, but it
just drops the Artifact Registry image size from 1.2 GB to
1.1 GB (Chrome's always getting bigger, so that's not
nothing).
2026年07月10日 09:22:41 +09:00
build build: Update google-cloud-cli and Typescript in Dockerfile. [skip ci] 2026年07月10日 09:22:41 +09:00
cmd cmd/nup/metadata: Accept MusicBrainz URLs via -set-album-id. 2026年06月21日 09:41:58 -06:00
common cmd/nup/update, common/strutil: Improve path manipulation. 2026年03月17日 07:39:44 -06:00
example example: Support serving existing music dir. [skip ci] 2025年12月16日 11:03:35 -07:00
server build, cmd/nup-headless, server: Get version from debug.BuildInfo. 2026年04月05日 18:19:19 -06:00
test test/web: Avoid focus races on faster systems. 2026年06月18日 14:52:14 -06:00
web web: Use overflow-y on stats-dialog's yearly stats. 2026年06月07日 06:35:32 -06:00
.dprint.json Use dprint for HTML too. 2025年05月20日 20:31:56 -04:00
.gcloudignore web: Switch to a better spinner icon. 2025年12月31日 18:44:56 -07:00
.gitignore server: Make /cover and /song return 404 for missing files. 2022年04月21日 08:03:15 -04:00
app.yaml build, cmd/nup-headless, server: Get version from debug.BuildInfo. 2026年04月05日 18:19:19 -06:00
cron.yaml server: Add /stats endpoint. 2022年02月04日 12:35:57 -04:00
embed.go server, test/web: Make static file changes for nup-headless. 2025年12月09日 10:23:47 -07:00
go.mod Use Go 1.25.12 and update deps for govulncheck. 2026年07月09日 20:54:37 +09:00
go.sum Use Go 1.25.12 and update deps for govulncheck. 2026年07月09日 20:54:37 +09:00
index.yaml index.yaml: Add index on RatingAtLeast1 and LastStartTime. 2023年07月22日 07:16:59 -04:00
LICENSE Add LICENSE file. 2021年11月19日 07:31:11 -04:00
README.md Update README.md to list third-party code. 2026年03月28日 12:45:29 -06:00
tsconfig.json Add noUnusedParameters to tsconfig.json. 2024年05月01日 08:37:55 -04:00

nup

Build Status

App Engine app for streaming a music collection.

Overview

light mode screenshot dark mode screenshot

This repository contains a server for serving a personal music collection, along with a web client, a headless local client that exposes a web interface, and a command-line program for managing the data.

The basic idea is that you mirror your music collection (and the corresponding album artwork) to Google Cloud Storage and then run the nup update command against the local copy to save metadata to a Datastore database. User-generated information like ratings, tags, and playback history is also saved in Datastore. The App Engine app performs queries against Datastore and serves songs and album art from Cloud Storage.

An Android client is also available.

This project is probably only of potential interest to people who both buy all of their music and are comfortable setting up a Google Cloud project and compiling and running command-line programs, which seems like a very small set. If it sounds appealing to you and you'd like to see more detailed instructions, though, please let me know!

History

In 2001 or 2002, I wrote dmc, a silly C application that used the FMOD library to play my MP3 collection. It used OpenGL to render a UI and some simple visualizations. I ran it on a small (Mini-ITX? I don't remember) computer plugged into my TV.

Sometime around 2005 or 2006, I decided that I wanted to be able to rate and tag the songs in my music collection and track my playback history so I could listen to stuff that I liked but hadn't heard recently, or play non-distracting instrumental music while reading or programming. I was using MPD to play locally-stored MP3 files at the time, so I wrote some Ruby scripts to search for and enqueue songs and display information about the current song onscreen. I also wrote a Ruby audioscrobbler library for sending playback reports to the service that later became Last.fm.

In 2010, I decided that it was silly to need to have my desktop computer turned on whenever I wanted to listen to music, so I wrote a daemon in Ruby to serve music and album art and support searching/tagging/rating/etc. over HTTP. Song information was stored in a SQLite database. I added a web interface and wrote an Android client that supported offline playback, and ran the server on a little always-on SoC Linux device. This was before the Raspberry Pi was released, and all I remember about the device was that upgrades were terrifying because it didn't put out enough power to be able to reliably boot off its external HDD.

In 2014, I decided that it'd be nice to be less dependent on my home network connection, so I rewrote the server in Go as a Google App Engine app that'd serve music and covers from Google Cloud Storage. That's what this repository contains.

It's 2021 now and I haven't felt the urge to rewrite all this code again.

The name "nup" doesn't mean anything; it just didn't seem to be used by any major projects. (I tried to think of a backronym for it but didn't come up with anything obvious other than the 'p' standing for "player".)

Dependencies

In addition to linking against the dependencies listed in go.mod, this repository contains code, fonts, and icons from several other projects:

Please see the LICENSE files in these directories for additional information.

Configuration

At the very least, you'll need to do the following:

  • Create a Google Cloud project.
  • Enable the Cloud Storage and App Engine APIs.
  • Create Cloud Storage buckets for your songs and album art.
  • Use the gsutil tool to sync songs and album art to Cloud Storage.
  • Compile the nup tool using go install ./cmd/nup.
  • Deploy the App Engine app.
  • Write config files for the nup tool and the app.
  • Use nup update to send song metadata to the App Engine app so it can be saved to Datastore.

As mentioned above, please let me know if you're feeling adventurous and would like to see detailed instructions for these steps.

nup tool

Create a YAML file at $HOME/.nup/config.yaml corresponding to the Config struct in cmd/nup/client/config.go:

serverUrl:https://my-project-id.appspot.comusername:toolspassword:my-tools-passwordcoverDir:/home/me/music/.coversmusicDir:/home/me/musiccomputeGain:true

Server

Create a YAML file corresponding to the Config struct in server/config/config.go:

users:- email:example@gmail.com- email:example.2@gmail.com- username:androidpassword:my-android-password- username:toolspassword:my-tools-passwordadmin:truesongBucket:my-songscoverBucket:my-covers

Run nup config -set /path/to/server_config.yaml to save the server configuration to Datastore.

Deploying

To deploy the App Engine app (as defined in app.yaml) and delete old, non-serving versions, run the build/deploy.sh script.

Note that App Engine often continues serving stale versions of static files for 10 minutes or more after deploying. I think that this has been broken for a long time. This Stack Overflow question has more discussion.

After changes to index.yaml, run build/deploy.sh -i to create new Datastore indexes and delete old ones.

You should also run build/deploy.sh cron.yaml once to deploy the daily stats-updating cron job described in cron.yaml.

Development and testing

The example/ directory contains code for starting a local App Engine server with example data for development.

All tests can be executed by running go test ./... from the root of the repository.

  • Unit tests live alongside the code that they exercise.
  • End-to-end tests that exercise the App Engine server and the nup executable are in the test/e2e/ directory.
  • Selenium tests that exercise both the web interface (in Chrome) and the server are in the test/web/ directory. By default, Chrome runs headlessly.