This commit is contained in:
mitteneer 2025-03-23 13:50:13 -04:00
parent 742c3887c9
commit 319c5aeae8
2 changed files with 93 additions and 110 deletions

View file

@ -20,43 +20,44 @@ pub const Result = struct {
if (count == 0) return null;
const sorted_recordings = self.recordings.?;
std.mem.sort(Recording, sorted_recordings, {}, Recording.lessThan);
rc_loop: for (sorted_recordings) |rc| {
for (sorted_recordings) |rc| {
switch (pbo.parse) {
.song => {
var output = searchIDs{ .id = "" };
switch (pbo.specifyBy) {
.song => {
if (std.mem.eql(u8, rc.title, smd.tn)) {
output.id = rc.id;
break :rc_loop;
}
},
.album => {
if (rc.releases) |rls| {
for (rls) |rl| {
if (std.mem.eql(u8, rl.title, smd.rgn)) {
output.id = rc.id;
break :rc_loop;
}
const output = searchIDs{
.id = switch (pbo.specifyBy) {
.song => song_song: {
if (std.mem.eql(u8, rc.title, smd.tn)) {
break :song_song rc.id;
//break :rc_loop;
}
} else {
std.log.err("Could not find any releases", .{});
}
},
.artist => {
if (rc.@"artist-credit") |acs| {
for (acs) |ac| {
if (std.mem.eql(u8, ac.name, smd.an)) {
output.id = rc.id;
break :rc_loop;
},
.album => song_album: {
if (rc.releases) |rls| {
for (rls) |rl| {
if (std.mem.eql(u8, rl.title, smd.rgn)) {
break :song_album rc.id;
//break :rc_loop;
}
}
} else {
std.log.err("Could not find any releases", .{});
}
} else {
std.log.err("Could not find any artists", .{});
}
},
.artist => song_artist: {
if (rc.@"artist-credit") |acs| {
for (acs) |ac| {
if (std.mem.eql(u8, ac.name, smd.an)) {
break :song_artist rc.id;
//break :rc_loop;
}
}
} else {
std.log.err("Could not find any artists", .{});
}
},
.all => unreachable, // I'll do this later
},
.all => unreachable, // I'll do this later
}
};
return output;
},
.album => {
@ -133,59 +134,45 @@ pub const parseOption = union(enum) {
pub const parseByOptions = struct { parse: parseOption = .song, specifyBy: parseOption = .song };
const eunuchMetadata = struct {
tn: struct { dec: []const u8, enc: []const u8 = undefined },
rn: struct { dec: []const u8, enc: []const u8 = undefined },
an: struct { dec: []const u8, enc: []const u8 = undefined },
pub const searchMetadata = struct {
tn: []const u8,
rn: []const u8,
an: []const u8,
};
// Maybe just use a HashMap...
pub const searchMetadata = struct {
alloc: std.mem.Allocator = undefined,
tn: struct { dec: []const u8, enc: []const u8 = undefined },
rn: struct { dec: []const u8, enc: []const u8 = undefined },
an: struct { dec: []const u8, enc: []const u8 = undefined },
pub const searchMetadataEncoded = struct {
tn: []const u8,
rn: []const u8,
an: []const u8,
pub fn deinit(self: *searchMetadata) void {
self.alloc.free(self.tn.enc);
self.alloc.free(self.rn.enc);
self.alloc.free(self.an.enc);
}
pub fn percentEncode(self: *searchMetadata) !void {
inline for (std.meta.fields(eunuchMetadata)) |k| {
var encoded = std.ArrayList(u8).init(self.alloc);
errdefer encoded.deinit();
for (@field(self, k.name).dec) |v| {
if ((v >= 'A' and v <= 'Z') or (v >= 'a' and v <= 'z') or (v >= '0' and v <= '9') or v == '-' or v == '_' or v == '.' or v == '~') {
try encoded.append(v);
} else if (v == ' ') {
try encoded.append('+');
} else {
const hex = try std.fmt.allocPrint(self.alloc, "%{x}", .{v});
defer self.alloc.free(hex);
try encoded.appendSlice(hex);
}
}
@field(self, k.name).enc = try encoded.toOwnedSlice();
}
}
//pub fn deinit(self: *searchMetadata) void {
// self.alloc.free(self.tn.enc);
// self.alloc.free(self.rn.enc);
// self.alloc.free(self.an.enc);
//}
};
pub fn percentEncode(alloc: std.mem.Allocator, smd: searchMetadata) !searchMetadataEncoded {
var output: searchMetadataEncoded = undefined;
inline for (std.meta.fields(searchMetadata)) |k| {
var encoded = std.ArrayList(u8).init(alloc);
errdefer encoded.deinit();
for (@field(smd, k.name)) |v| {
if ((v >= 'A' and v <= 'Z') or (v >= 'a' and v <= 'z') or (v >= '0' and v <= '9') or v == '-' or v == '_' or v == '.' or v == '~') {
try encoded.append(v);
} else if (v == ' ') {
try encoded.append('+');
} else {
const hex = try std.fmt.allocPrint(alloc, "%{x}", .{v});
defer alloc.free(hex);
try encoded.appendSlice(hex);
}
}
@field(output, k.name) = try encoded.toOwnedSlice();
}
// I don't think I need a second type, but I'd like to hold on to the original, unencoded metadata as well, so maybe this is fine
// Maybe make fields for encoded data?
//pub const searchMetadataEncoded = struct {
// alloc: std.mem.Allocator,
// tn: []const u8,
// rgn: []const u8,
// an: []const u8,
//
// pub fn deinit(self: *const searchMetadataEncoded) void {
// self.alloc.free(self.an);
// self.alloc.free(self.rgn);
// self.alloc.free(self.tn);
// }
//};
return output;
}
pub const searchIDs = union {
id: []const u8,

View file

@ -8,57 +8,53 @@ const Client = std.http.Client;
pub const user_agent: []const u8 = "ZuletztMBClient/0.0.1 (swebbguy@gmail.com)";
pub fn mbSearch(allocator: std.mem.Allocator, ar: *std.ArrayList(u8), smd: MBQ.searchMetadata) !?[]const u8 {
const encoded = try MBQ.percentEncode(allocator, smd);
defer encoded.deinit();
var client = Client{ .allocator = allocator };
pub fn mbSearch(alloc: std.mem.Allocator, smd: MBQ.searchMetadata) !?MBQ.Result {
const encoded = try MBQ.percentEncode(alloc, smd);
// Might consider making just one of these and passing it around
var client = Client{ .allocator = alloc };
defer client.deinit();
const query: []const u8 = try std.fmt.allocPrint(allocator, "https://musicbrainz.org/ws/2/recording/?query=\"{s}\"%20AND%20artist:\"{s}\"%20AND%20release:\"{s}\"&fmt=json", .{ encoded.tn, encoded.an, encoded.rgn });
defer allocator.free(query);
const query: []const u8 = try std.fmt.allocPrint(alloc, "https://musicbrainz.org/ws/2/recording/?query=\"{s}\"%20AND%20artist:\"{s}\"%20AND%20release:\"{s}\"&fmt=json", .{ encoded.tn, encoded.an, encoded.rn });
defer alloc.free(query);
const response: Client.FetchResult = try client.fetch(.{ .response_storage = .{ .dynamic = ar }, .location = .{ .url = query }, .method = .GET, .headers = .{ .user_agent = .{ .override = user_agent } } });
var ar = std.ArrayList(u8).init(alloc);
errdefer ar.deinit();
const response: Client.FetchResult = try client.fetch(.{ .response_storage = .{ .dynamic = &ar }, .location = .{ .url = query }, .method = .GET, .headers = .{ .user_agent = .{ .override = user_agent } } });
switch (@intFromEnum(response.status)) {
0...299 => std.log.debug("Success\n", .{}),
0...299 => std.log.debug("Success", .{}),
300...399 => {
std.log.err("Redirected\n", .{});
std.log.err("Redirected", .{});
return null;
},
400...511 => {
std.log.err("Get rekt\n{s}", .{ar.items});
std.log.err("Get rekt {s}", .{ar.items});
return Client.ConnectError.ConnectionRefused;
},
512 => {
std.log.err("Need to login\n", .{});
std.log.err("Need to login", .{});
return null;
},
else => unreachable,
}
return ar.items;
const json = try std.json.parseFromSlice(MBQ.Result, alloc, ar.items, .{ .ignore_unknown_fields = true });
errdefer json.deinit();
return json.value;
}
// This test is very volatile, but I think
// these params are specific enough that
// it shouldn't need changing too often
test "arid_via_recording" {
const test_alloc = std.testing.allocator;
const metadata = MBQ.searchMetadata{ .tn = "Veni veni Emmanuel", .an = "iamthemorning", .rgn = "Counting the Ghosts" };
const metadata = MBQ.searchMetadata{ .tn = "Veni veni Emmanuel", .an = "iamthemorning", .rn = "Counting the Ghosts" };
const iatm_id: []const u8 = "5854a6de-af8f-4b99-8710-cb47d6436a19";
var mb_result = std.ArrayList(u8).init(test_alloc);
defer mb_result.deinit();
const search_result = try mbSearch(test_alloc, &mb_result, metadata);
if (search_result) |sr| {
const json = try std.json.parseFromSlice(MBQ.Result, test_alloc, sr, .{ .ignore_unknown_fields = true });
defer json.deinit();
const result: MBQ.Result = json.value;
const id = result.getIDs(metadata, .{ .parse = .artist, .specifyBy = .song });
const search_result = try mbSearch(test_alloc, metadata);
if (search_result) |rs| {
const id = rs.getIDs(metadata, .{ .parse = .artist, .specifyBy = .song });
std.Thread.sleep(1000000000);
@ -71,7 +67,7 @@ test "arid_via_recording" {
test "arid_via_recording_multiple_artists_1" {
const test_alloc = std.testing.allocator;
const metadata = MBQ.searchMetadata{ .tn = "Roll Me Up And Smoke Me When I Die", .an = "Lyle Lovett", .rgn = "Willie Nelson American Outlaw" };
const metadata = MBQ.searchMetadata{ .alloc = test_alloc, .tn = .{ .dec = "Roll Me Up And Smoke Me When I Die" }, .an = .{ .dec = "Lyle Lovett" }, .rn = .{ .dec = "Willie Nelson American Outlaw" } };
const ll_id: []const u8 = "7241e3ed-5ad4-4849-94df-6858ea833472";
var mb_result = std.ArrayList(u8).init(test_alloc);
@ -97,7 +93,7 @@ test "arid_via_recording_multiple_artists_1" {
test "rgid_via_recording" {
const test_alloc = std.testing.allocator;
const metadata = MBQ.searchMetadata{ .tn = "I Of The Storm", .rgn = "Beneath the Skin", .an = "Of Monsters and Men" };
const metadata = MBQ.searchMetadata{ .alloc = test_alloc, .tn = .{ .dec = "I Of The Storm" }, .rn = .{ .dec = "Beneath the Skin" }, .an = .{ .dec = "Of Monsters and Men" } };
const bts_id: []const u8 = "4f4f5b98-45ac-4b47-addb-66b501473bd8";
var mb_result = std.ArrayList(u8).init(test_alloc);
@ -123,7 +119,7 @@ test "rgid_via_recording" {
test "rgid_via_recording_multiple_artists_2" {
const test_alloc = std.testing.allocator;
const metadata = MBQ.searchMetadata{ .tn = "Hesitating Beauty", .rgn = "Mermaid Avenue", .an = "Wilco" };
const metadata = MBQ.searchMetadata{ .alloc = test_alloc, .tn = .{ .dec = "Hesitating Beauty" }, .rn = .{ .dec = "Mermaid Avenue" }, .an = .{ .dec = "Wilco" } };
const wilco_id: []const u8 = "9ba73bf8-6c15-4bd2-8da1-e2292538f617";
var mb_result = std.ArrayList(u8).init(test_alloc);
@ -150,7 +146,7 @@ test "rgid_via_recording_multiple_artists_2" {
test "rid_aqualung" {
const test_alloc = std.testing.allocator;
const metadata = MBQ.searchMetadata{ .tn = "Aqualung", .rgn = "Aqualung", .an = "Jethro Tull" };
const metadata = MBQ.searchMetadata{ .alloc = test_alloc, .tn = .{ .dec = "Aqualung" }, .rn = .{ .dec = "Aqualung" }, .an = .{ .dec = "Jethro Tull" } };
const aqualung_id: []const u8 = "2621d113-3a9f-41f9-a0f2-b9459f86e8e9";
var mb_result = std.ArrayList(u8).init(test_alloc);
@ -174,7 +170,7 @@ test "rid_aqualung" {
test "rid_battery" {
const test_alloc = std.testing.allocator;
const metadata = MBQ.searchMetadata{ .tn = "Battery", .rgn = "Master of Puppets", .an = "Metallica" };
const metadata = MBQ.searchMetadata{ .alloc = test_alloc, .tn = .{ .dec = "Battery" }, .rn = .{ .dec = "Master of Puppets" }, .an = .{ .dec = "Metallica" } };
const battery_id: []const u8 = "3bfda26a-49fa-4bc4-a4d6-8bbfa0767ab7";
var mb_result = std.ArrayList(u8).init(test_alloc);