@@ -85,13 +85,7 @@ pub fn main() anyerror!noreturn {
try thread_pool.init(.{ .allocator = gpa });
defer thread_pool.deinit();
var db: Db = .{
.files = .{},
.directories = .{},
.string_bytes = .{},
.string_table = .{},
.albums = .{},
};
var db = Db.empty;
defer db.deinit(gpa);
// Reserve string index 0 for an empty string.
@@ -194,7 +188,11 @@ pub const Server = struct {
};
defer send_thread.join();
while (true) {}
while (true) {
const msg = try ws.readSmallMessage();
_ = msg;
@panic("TODO handle message from client");
}
}
fn websocketSendLoop(s: *Server, ws: *WebSocket) !void {
@@ -223,7 +221,7 @@ pub const Server = struct {
fn scanDir(db: *Db, gpa: Allocator, db_dir: Db.Path.Index, it: *std.fs.Dir.Iterator) anyerror!void {
while (try it.next()) |entry| switch (entry.kind) {
.directory => {
.directory, .sym_link => {
const basename = try db.getOrPutString(gpa, entry.name);
var sub_dir = try it.dir.openDir(entry.name, .{ .iterate = true });
defer sub_dir.close();
@@ -335,17 +333,31 @@ fn fileMetaData(gpa: Allocator, db: *Db, file: *player.File) !FileMetaData {
const parsed_track = parseTrackTuple(trim(tag.value));
result.track_number = parsed_track.numerator;
result.track_count = parsed_track.denominator;
} else if (asciizEql(tag.key, "tracktotal") or
asciizEql(tag.key, "totaltracks"))
{
if (std.fmt.parseInt(i16, trim(tag.value), 10)) |x| {
result.track_count = x;
} else |_| {}
} else if (asciizEql(tag.key, "disc") or
asciizEql(tag.key, "TPA") or
asciizEql(tag.key, "TPOS"))
asciizEql(tag.key, "tpa") or
asciizEql(tag.key, "tpos"))
{
const parsed_disc = parseTrackTuple(trim(tag.value));
result.disc_number = parsed_disc.numerator;
result.disc_count = parsed_disc.denominator;
} else if (asciizEql(tag.key, "disctotal") or
asciizEql(tag.key, "totaldiscs"))
{
if (std.fmt.parseInt(i16, trim(tag.value), 10)) |x| {
result.disc_count = x;
} else |_| {}
} else if (asciizEql(tag.key, "date")) {
result.year = std.fmt.parseInt(i16, trim(tag.value), 10) catch -1;
} else if (asciizEql(tag.key, "title")) {
std.log.debug("pre='{s}' trimmed='{s}'", .{ tag.value, trim(tag.value) });
result.title = (try db.getOrPutString(gpa, trim(tag.value))).toOptional();
std.log.debug("saved title '{s}'", .{db.optStringToSlice(result.title).?});
} else if (asciizEql(tag.key, "artist")) {
result.artist = (try db.getOrPutString(gpa, trim(tag.value))).toOptional();
} else if (asciizEql(tag.key, "album_artist")) {
@@ -357,15 +369,14 @@ fn fileMetaData(gpa: Allocator, db: *Db, file: *player.File) !FileMetaData {
} else if (asciizEql(tag.key, "genre")) {
result.genre = (try db.getOrPutString(gpa, trim(tag.value))).toOptional();
} else if (asciizEql(tag.key, "composer") or
asciizEql(tag.key, "TCM"))
asciizEql(tag.key, "tcm"))
{
result.composer = (try db.getOrPutString(gpa, trim(tag.value))).toOptional();
} else if (asciizEql(tag.key, "TCP") or
asciizEql(tag.key, "TCMP") or
asciizEql(tag.key, "COMPILATION") or
asciizEql(tag.key, "Compilation") or
} else if (asciizEql(tag.key, "tcp") or
asciizEql(tag.key, "tcmp") or
asciizEql(tag.key, "compilation") or
asciizEql(tag.key, "cpil") or
asciizEql(tag.key, "WM/IsCompilation"))
asciizEql(tag.key, "wm/iscompilation"))
{
result.compilation = true;
} else {
@@ -375,12 +386,16 @@ fn fileMetaData(gpa: Allocator, db: *Db, file: *player.File) !FileMetaData {
return result;
}
/// Asserts `s` is lower case already.
fn asciizEql(tag: [*:0]const u8, s: [*:0]const u8) bool {
var p_a = tag;
var p_b = s;
while (true) {
if (p_a[0] != p_b[0]) return false;
if (p_a[0] == 0) return true;
const a_lower = std.ascii.toLower(p_a[0]);
const b_lower = std.ascii.toLower(p_b[0]);
assert(p_b[0] == b_lower);
if (a_lower != b_lower) return false;
if (a_lower == 0) return true;
p_a += 1;
p_b += 1;
}
@@ -393,12 +408,18 @@ fn trim(s: [*:0]const u8) []const u8 {
else => break,
};
var len: usize = 0;
while (true) switch (start[len]) {
0, ' ', '\r', '\n', '\t' => return start[0..len],
else => len += 1,
var last_non_space: usize = 0;
while (true) : (len += 1) switch (start[len]) {
0 => return start[0 .. last_non_space + 1],
' ', '\r', '\n', '\t' => {},
else => last_non_space = len,
};
}
test trim {
try std.testing.expectEqualStrings("Turn Off", trim("Turn Off "));
}
const TrackTuple = struct {
numerator: i16,
denominator: i16,
@@ -429,15 +450,15 @@ fn parseTrackTuple(s: []const u8) TrackTuple {
test parseTrackTuple {
const expectEqual = std.testing.expectEqual;
try expectEqual(@as(?i16, null), parseTrackTuple("").numerator);
try expectEqual(@as(?i16, null), parseTrackTuple("").denominator);
try expectEqual(@as(i16, -1), parseTrackTuple("").numerator);
try expectEqual(@as(i16, -1), parseTrackTuple("").denominator);
try expectEqual(@as(?i16, 1), parseTrackTuple("1/100").numerator);
try expectEqual(@as(?i16, 100), parseTrackTuple("1/100").denominator);
try expectEqual(@as(i16, 1), parseTrackTuple("1/100").numerator);
try expectEqual(@as(i16, 100), parseTrackTuple("1/100").denominator);
try expectEqual(@as(?i16, null), parseTrackTuple("/-50").numerator);
try expectEqual(@as(?i16, -50), parseTrackTuple("/-50").denominator);
try expectEqual(@as(i16, -1), parseTrackTuple("/-50").numerator);
try expectEqual(@as(i16, -50), parseTrackTuple("/-50").denominator);
try expectEqual(@as(?i16, 10), parseTrackTuple("10").numerator);
try expectEqual(@as(?i16, null), parseTrackTuple("10").denominator);
try expectEqual(@as(i16, 10), parseTrackTuple("10").numerator);
try expectEqual(@as(i16, -1), parseTrackTuple("10").denominator);
}