From 77170a1e285549bd000a63a5c78e50dfdb489aad Mon Sep 17 00:00:00 2001 From: mitteneer Date: Mon, 21 Apr 2025 16:41:40 -0400 Subject: [PATCH] Move Scrobble rule application to upload.zig They couldn't see the changes made by rules after uploading Scrobbles which made it seem like the rules weren't being applied. Also makes Album rules easier to apply I believe. --- src/app/jobs/process_scrobbles.zig | 10 ++-------- src/app/views/rules/index.zmpl | 8 ++++++-- src/app/views/upload.zig | 17 ++++++++++++++--- src/apply_rule.zig | 2 +- 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/app/jobs/process_scrobbles.zig b/src/app/jobs/process_scrobbles.zig index 06d7365..4262fe5 100644 --- a/src/app/jobs/process_scrobbles.zig +++ b/src/app/jobs/process_scrobbles.zig @@ -18,17 +18,11 @@ const rules = @import("../../apply_rule.zig"); // - environment: Enum of `{ production, development }`. pub fn run(allocator: std.mem.Allocator, params: *jetzig.data.Value, env: jetzig.jobs.JobEnv) !void { //_ = env; - const file = try std.fs.cwd().openFile("rules.json", .{ .mode = .read_only }); - const file_content = try file.readToEndAlloc(allocator, 16_000_000); - - const rule_list = try std.json.parseFromSliceLeaky(Data.Rules, allocator, file_content, .{}); - + _ = allocator; if (params.getT(.array, "scrobbles")) |scrobbles| { for (scrobbles.items()) |item| { //const fixed_date: u32 = @as(u32, item.getT(.integer, "date").?); - const pre_scrobble: Scrobble = .{ .track = item.getT(.string, "track").?, .artist = item.getT(.string, "artist").?, .album = item.getT(.string, "album") orelse "", .date = @as(u64, @bitCast(@as(i64, @truncate(item.getT(.integer, "date").? * 1000)))) }; - - const scrobble = rules.applyScrobbleRule(pre_scrobble, rule_list); + const scrobble: Scrobble = .{ .track = item.getT(.string, "track").?, .artist = item.getT(.string, "artist").?, .album = item.getT(.string, "album") orelse "", .date = @as(u64, @bitCast(@as(i64, @truncate(item.getT(.integer, "date").? * 1000)))) }; // Make hashes //const album_hash = @as(i32, @bitCast(std.hash.Fnv1a_32.hash(scrobble.album))); diff --git a/src/app/views/rules/index.zmpl b/src/app/views/rules/index.zmpl index b9a1b17..ab55c96 100644 --- a/src/app/views/rules/index.zmpl +++ b/src/app/views/rules/index.zmpl @@ -16,7 +16,7 @@ If + + + + @@ -87,7 +91,7 @@ then with diff --git a/src/app/views/upload.zig b/src/app/views/upload.zig index 53f3b27..032da9d 100644 --- a/src/app/views/upload.zig +++ b/src/app/views/upload.zig @@ -3,6 +3,8 @@ const jetzig = @import("jetzig"); const jetquery = @import("jetzig").jetquery; const ScrobbleTypes = @import("../../types.zig"); const zeit = @import("zeit"); +const rules = @import("../../apply_rule.zig"); +const Data = @import("../../types.zig"); pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { _ = data; @@ -31,6 +33,11 @@ pub fn post(request: *jetzig.Request) !jetzig.View { var skipped_tracks: u64 = 0; var limited_tracks: u64 = 0; + const rule_file = try std.fs.cwd().openFile("rules.json", .{ .mode = .read_only }); + defer rule_file.close(); + const file_content = try rule_file.readToEndAlloc(request.allocator, 16_000_000); + const rule_list = try std.json.parseFromSliceLeaky(Data.Rules, request.allocator, file_content, .{}); + // The only difference between a LastFM scrobble and a Spotify scrobble is the format. // I've made a branches for each, because doing it all in one made the readability terrible, // and formatting the date in particular was challenging. I could probably pull out the @@ -46,12 +53,14 @@ pub fn post(request: *jetzig.Request) !jetzig.View { if ((before_limiter or after_limiter) and (scrobble.date > before_limiting_date or scrobble.date < after_limiting_date)) continue :appends; var value = try scrobbles_data.append(.object); + const formatted_scrobble = rules.applyScrobbleRule(scrobble, rule_list); + // This is so unnecessary, probably useful once I start doing Spotify integration though inline for (std.meta.fields(ScrobbleTypes.LastFMScrobble)) |f| { - try value.put(f.name, @as(f.type, @field(scrobble, f.name))); + try value.put(f.name, @as(f.type, @field(formatted_scrobble, f.name))); } // Note sure why this works for ZMPL, but not for jobs. - try scrobbles_view.append(scrobble); + try scrobbles_view.append(formatted_scrobble); } }, 1 => { @@ -88,7 +97,9 @@ pub fn post(request: *jetzig.Request) !jetzig.View { } // Turn SpotifyScrobble into a LastFM scrobble - const formatted_scrobble: ScrobbleTypes.LastFMScrobble = .{ .track = scrobble.master_metadata_track_name.?, .album = scrobble.master_metadata_album_album_name.?, .artist = scrobble.master_metadata_album_artist_name.?, .date = (try zeit.instant(.{ .source = .{ .iso8601 = scrobble.ts } })).unixTimestamp() * 1000 }; + const pre_formatted_scrobble: ScrobbleTypes.LastFMScrobble = .{ .track = scrobble.master_metadata_track_name.?, .album = scrobble.master_metadata_album_album_name.?, .artist = scrobble.master_metadata_album_artist_name.?, .date = (try zeit.instant(.{ .source = .{ .iso8601 = scrobble.ts } })).unixTimestamp() * 1000 }; + + const formatted_scrobble = rules.applyScrobbleRule(pre_formatted_scrobble, rule_list); var value = try scrobbles_data.append(.object); diff --git a/src/apply_rule.zig b/src/apply_rule.zig index 769d062..92d7598 100644 --- a/src/apply_rule.zig +++ b/src/apply_rule.zig @@ -12,7 +12,7 @@ pub fn applyScrobbleRule(scrobble: Scrobble, rules: Rules) Scrobble { inline else => |on| match_found = match_found and std.mem.eql(u8, @field(scrobble, @tagName(on)), cond.match_txt), }, .contains => switch (cond.match_on) { - inline else => |on| match_found = match_found and (std.mem.count(u8, @field(scrobble, @tagName(on)), cond.match_txt) > 0), + inline else => |on| match_found = match_found and std.mem.containsAtLeast(u8, @field(scrobble, @tagName(on)), 1, cond.match_txt), }, } }