webapp: drop rig:playlist pipeline, source track count from mpc stats

The four overlays that read /data/playlist.js (loading, goodbye,
music-box, music/nc) only ever consumed `tracks.length` for cosmetic
"LIBRARY :: N TRACKS" / "indexed N tracks" flavor text — all live
playback signal flows through bridges/mpd-state.py over OBS WS. The
142 KB JSON, ffprobe walk, and ETag-cached route were all producing
one integer that MPD itself already knows.

RigData::playlistCount() now shells `mpc stats` and parses "Songs: N",
returning 0 on any failure (treated by callers as empty library).
Removes RigPlaylistCommand, DataController::playlist(), the
/data/playlist.js route, the per-overlay <script src> + window.__PLAYLIST
shims, and the stale storage/data/playlist.json artifact.

AudioController still consumes config('rig.music_dirs') for the HTTP
audio stream — that's orthogonal and stays.
This commit is contained in:
Jakub Zych
2026-05-22 21:04:24 +02:00
parent dd3493921b
commit fd1c78b333
12 changed files with 39 additions and 213 deletions

View File

@@ -3,53 +3,10 @@
namespace App\Http\Controllers;
use App\Support\RigData;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;
class DataController extends Controller
{
/**
* Serve the playlist as a script-tag-friendly JS global. Cached via ETag on file mtime
* so CEF can revalidate cheaply (the playlist file changes rarely but the body is ~142 KB).
*/
public function playlist(Request $request, RigData $rig)
{
$path = $rig->playlistPath();
$tracks = $rig->playlist();
$mtime = is_file($path) ? filemtime($path) : 0;
$etag = '"' . md5('playlist:' . $mtime) . '"';
if ($request->headers->get('If-None-Match') === $etag) {
return response('', 304)->header('ETag', $etag);
}
// Rewrite `file://` URIs into `/audio?p=...` HTTP URLs so the
// music-daemon page (served over HTTP) can load tracks. Chromium
// blocks HTTP-origin pages from loading file:// media.
if (isset($tracks['tracks']) && is_array($tracks['tracks'])) {
foreach ($tracks['tracks'] as $i => $track) {
if (!isset($track['file'])) continue;
if (str_starts_with($track['file'], 'file://')) {
$absolute = rawurldecode(substr($track['file'], 7));
$tracks['tracks'][$i]['file'] = \App\Http\Controllers\AudioController::urlFor($absolute);
}
}
}
$body = 'window.__PLAYLIST = ' . json_encode(
$tracks,
JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE
) . ';';
return response($body, 200, [
'Content-Type' => 'application/javascript; charset=utf-8',
'Cache-Control' => 'public, must-revalidate',
'ETag' => $etag,
]);
}
/**
* Serve bridges/cover.jpg. `no-store` so CEF never holds a stale image
* even if a caller forgets the ?v=<hash> cache-bust. 404 returns a 1×1