Fix AzuraCast integration against verified API behavior
Verified against AzuraCast source (Files\BatchAction, FlowUploadAction,
Files\ListAction, Paginator, StationPlaylistMediaRepository):
- Rotation read a nonexistent 'mtime' key; the files API returns
'timestamp' (unix epoch). Age buckets now work, so tracks actually
migrate Currents -> Recurrents -> Gold as intended.
- Rotation now filters on type == 'media' (dirs are 'directory', not
'dir') and skips rows without a path/timestamp.
- Follow ?page= pagination on /files: the default (no params) response
is an unpaginated JSON array, but any paginated response is now
traversed instead of only page 1 being visible.
- POST /files/upload returns only {success, message}; it never returns
unique_id/path, and the file lands at the uploaded filename. Removed
the dead unique_id search; playlist+queue now use the known path.
- Added success/error checks for batch ops (playlist/queue) instead of
discarding responses, and queue no longer runs when playlist assign
or Currents lookup failed.
- Replaced curl subprocess shelling with aiohttp (already a matrix-nio
dependency): single helper for API calls, real multipart upload,
timeouts everywhere, no temp JSON/pickle files in DATA_DIR.
Functional test (mock AzuraCast mirroring verified API shapes):
15/15 checks pass - upload contract, pagination, batch ops, and age
bucketing (fresh/6d/10d/30d -> Currents/Currents/Recurrents/Gold,
directories and non-media excluded).