@@ -193,6 +193,16 @@ pub const Server = struct {
device: *SoundIo.Device,
base_timestamp: i128,
const Track = struct {
db_file_index: Db.File.Index,
player_file: *player.File,
fn cleanList(ally: Allocator, track_list: *std.MultiArrayList(@This())) void {
for (track_list.items(.player_file)) |file| file.close();
track_list.deinit(ally);
}
};
pub fn serve(s: *Server) noreturn {
while (true) {
const connection = s.net_server.accept() catch |err| {
@@ -242,7 +252,12 @@ pub const Server = struct {
const msg = try ws.readSmallMessage();
const cmd = try messageEnum(msg, protocol.Command);
switch (cmd) {
.enqueue_file => std.log.err("TODO implement enqueue_file", .{}),
.enqueue_file => {
const file_index = try messageIndex(msg[1..], s.db, Db.File.Index);
s.enqueueFile(file_index) catch |err| {
std.log.err("failed to enqueue file: {s}", .{@errorName(err)});
};
},
.enqueue_album => {
// TODO don't access db without a held lock
const album_index = try messageIndex(msg[1..], s.db, Db.Album.Index);
@@ -275,6 +290,7 @@ pub const Server = struct {
const I = @typeInfo(E).Enum.tag_type;
const len = switch (E) {
Db.Album.Index => db.albums.entries.len,
Db.File.Index => db.files.entries.len,
else => @compileError("missing type in switch"),
};
if (msg.len < @sizeOf(I)) return error.MessageTruncated;
@@ -357,20 +373,34 @@ pub const Server = struct {
try ws.writeMessagev(iovecs_buf[0..iovecs_n]);
}
fn enqueueFile(s: *Server, file_index: Db.File.Index) !void {
const gpa = s.gpa;
std.log.debug("command: enqueue album {d}", .{@intFromEnum(file_index)});
var track_list: std.MultiArrayList(Track) = .{};
defer Track.cleanList(gpa, &track_list);
const music_dir = s.config.music_directory.handle;
try track_list.ensureUnusedCapacity(gpa, 1);
var path_buf: [std.fs.max_path_bytes]u8 = undefined;
const file = s.db.files.entries.get(@intFromEnum(file_index));
const fs_path = s.db.musicDirPath(&file.key, &path_buf);
std.log.debug("opening '{s}'", .{fs_path});
track_list.appendAssumeCapacity(.{
.db_file_index = file_index,
.player_file = try player.File.open(music_dir, fs_path, fs_path),
});
return s.enqueueTrackList(&track_list);
}
fn enqueueAlbum(s: *Server, album_index: Db.Album.Index) !void {
const gpa = s.gpa;
std.log.debug("command: enqueue album {d}", .{@intFromEnum(album_index)});
const Track = struct {
db_file_index: Db.File.Index,
player_file: *player.File,
fn cleanList(ally: Allocator, track_list: *std.MultiArrayList(@This())) void {
for (track_list.items(.player_file)) |file| file.close();
track_list.deinit(ally);
}
};
var track_list: std.MultiArrayList(Track) = .{};
defer Track.cleanList(gpa, &track_list);
@@ -405,15 +435,12 @@ pub const Server = struct {
.tracks = track_list.items(.db_file_index),
});
try s.db.queue_items.ensureUnusedCapacity(gpa, track_list.len);
return s.enqueueTrackList(&track_list);
}
const playing = p: {
s.play_queue.mutex.lock();
defer s.play_queue.mutex.unlock();
fn enqueueTrackList(s: *Server, track_list: *std.MultiArrayList(Track)) !void {
const playing = try s.play_queue.appendSlice(s.gpa, track_list.items(.player_file));
try s.play_queue.files.appendSlice(gpa, track_list.items(.player_file));
break :p s.play_queue.playing.raw;
};
const seek_timestamp: ?i64 = if (playing) null else b: {
s.play_queue.start(s.device) catch |err| {
std.log.err("unable to start play queue: {s}", .{@errorName(err)});
@@ -423,6 +450,7 @@ pub const Server = struct {
const queue_index_start = s.db.queue_items.entries.len;
try s.db.queue_items.ensureUnusedCapacity(s.gpa, track_list.len);
for (track_list.items(.db_file_index)) |db_file_index| {
s.db.queue_items.putAssumeCapacityNoClobber(.{
.id = Db.QueueItem.Id.random(),