Files
radiantwave/src/audio/audio_downloader.h
T
NeilClack 5caf1d6ef8
build-dev / nix-build (push) Failing after 13s
Move to checksums.md5
Server is scheduled to generate this file nightly
2026-08-23 12:39:05 -05:00

113 lines
4.2 KiB
C++

#pragma once
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <mutex>
#include <string>
#include <thread>
#include <vector>
namespace radiantwave::db {
class DatabaseManager;
}
namespace radiantwave::audio {
// One entry from the audio checksums manifest.
//
// Supported line formats (in priority order):
// <filename> '<display name>' <hz> <md5> // current
// <md5> <filename> // legacy fallback
//
// hz is the actual frequency value of the track (e.g. 396), written by hand
// or by a script that has no access to the frequency-id table; 0 means
// "none / unset". The manifest author may also write the frequency id (or any
// unrecognized number): only values that resolve to a known frequency are
// honored, anything else falls back to the Hz parsed from the filename, so
// the file's own name is always the safety net.
struct AudioManifestEntry {
std::string filename;
std::string display_name;
int solfeggio_frequency_hz = 0;
std::string md5;
};
// Downloads audio assets from files.radiantwavetech.com in the background
// and keeps the database's audio_files table in sync with what is actually
// on disk.
//
// Fetches checksums.md5, compares each entry against the local file's MD5,
// and re-downloads anything that is missing or whose checksum differs.
// Files present on disk but absent from the manifest are deleted, and the
// audio_files table is reconciled on every pass (and after each individual
// download) so the settings audio list reflects live progress: a file that
// just finished downloading appears in the list immediately, and files not
// yet downloaded are not listed. The worker runs a persistent loop: if
// there is no internet at startup the status shows "Waiting for internet..."
// and a download starts automatically once connectivity returns. When all
// files verify it keeps re-checking the manifest periodically so changed
// checksums are picked up while running.
class AudioDownloader {
public:
AudioDownloader() = default;
~AudioDownloader();
AudioDownloader(const AudioDownloader &) = delete;
AudioDownloader &operator=(const AudioDownloader &) = delete;
// Spawns the background thread. Safe to call repeatedly; only one worker
// runs at a time. The manifest is reconciled against `db` on every pass.
void start(db::DatabaseManager &db);
// Signals the worker to stop and joins the thread.
void shutdown();
// Returns the current human-readable status for display (e.g. footer).
std::string statusString() const;
static constexpr const char *AUDIO_BASE_URL =
"https://files.radiantwavetech.com/audio/";
static constexpr const char *MANIFEST_FILENAME = "checksums.md5";
private:
void run();
bool fetchManifest(std::vector<AudioManifestEntry> &out);
bool downloadAndVerify(const std::string &filename,
const std::string &expectedMd5,
const std::string &displayName);
void setStatus(const std::string &msg);
void wait(std::chrono::seconds seconds);
mutable std::mutex mutex_;
std::condition_variable cv_;
std::thread worker_;
std::atomic<bool> stop_{false};
std::string status_;
db::DatabaseManager *db_ = nullptr;
};
// Parses checksums.md5 manifest text into entries. Blank lines and lines
// starting with '#' are ignored. Lines that don't match a known format are
// skipped. Returns false if no valid entries were found.
bool parseManifest(const std::string &text,
std::vector<AudioManifestEntry> &out);
// Maps a frequency value in Hz to its internal solfeggio frequency id using
// the same table the color palettes are seeded from (174/285->1, 396->2,
// 417->3, 432->4, 528->5, 639->6, 741->7, 852->8, 963->9). Returns 0 for
// frequencies with no id (binaurals, AlphaWaves, OriginalAudio, ...).
int frequencyIdForHz(int hz);
// Parses the leading frequency value out of an audio filename like
// "396Hz.wav" -> 396. Requires the digits to be followed by "Hz"
// (case-insensitive); returns 0 when the name has no frequency (e.g.
// "AlphaWaves.wav", "OriginalAudio.wav").
int hzFromFilename(const std::string &filename);
// Computes the lowercase MD5 hex of a file, or an empty string on failure.
std::string computeFileMd5(const std::string &path);
} // namespace radiantwave::audio