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.
This commit is contained in:
mitteneer 2025-04-21 16:41:40 -04:00
parent 87a2fe2d34
commit 77170a1e28
4 changed files with 23 additions and 14 deletions

View file

@ -18,17 +18,11 @@ const rules = @import("../../apply_rule.zig");
// - environment: Enum of `{ production, development }`. // - environment: Enum of `{ production, development }`.
pub fn run(allocator: std.mem.Allocator, params: *jetzig.data.Value, env: jetzig.jobs.JobEnv) !void { pub fn run(allocator: std.mem.Allocator, params: *jetzig.data.Value, env: jetzig.jobs.JobEnv) !void {
//_ = env; //_ = env;
const file = try std.fs.cwd().openFile("rules.json", .{ .mode = .read_only }); _ = allocator;
const file_content = try file.readToEndAlloc(allocator, 16_000_000);
const rule_list = try std.json.parseFromSliceLeaky(Data.Rules, allocator, file_content, .{});
if (params.getT(.array, "scrobbles")) |scrobbles| { if (params.getT(.array, "scrobbles")) |scrobbles| {
for (scrobbles.items()) |item| { for (scrobbles.items()) |item| {
//const fixed_date: u32 = @as(u32, item.getT(.integer, "date").?); //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: 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);
// Make hashes // Make hashes
//const album_hash = @as(i32, @bitCast(std.hash.Fnv1a_32.hash(scrobble.album))); //const album_hash = @as(i32, @bitCast(std.hash.Fnv1a_32.hash(scrobble.album)));

View file

@ -16,7 +16,7 @@ If
<select name="match-on" id="match-on"> <select name="match-on" id="match-on">
<option value="artist">artist</option> <option value="artist">artist</option>
<option value="album">album</option> <option value="album">album</option>
<option value="song">song</option> <option value="track">song</option>
</select> </select>
<select name="match-cond" id="match-cond"> <select name="match-cond" id="match-cond">
<option value="is">is</option> <option value="is">is</option>
@ -24,6 +24,10 @@ If
<option value="matches">matches regex</option> <option value="matches">matches regex</option>
</select> </select>
<input type="text" name="match-txt" id="match-txt"> <input type="text" name="match-txt" id="match-txt">
<label for="case-sens">Toggle case sensitivity</label>
<input type="checkbox" name="case-sens" id="case-sens">
<label for="accent-sens">Toggle diacritic sensitivity</label>
<input type="checkbox" name="accent-sens" id="accent-sens">
<button type="button" onclick="condAdd()"> <button type="button" onclick="condAdd()">
Add Conditional Add Conditional
</button> </button>
@ -87,7 +91,7 @@ then
<select name="action-on" id="action-on"> <select name="action-on" id="action-on">
<option value="artist">artist</option> <option value="artist">artist</option>
<option value="album">album</option> <option value="album">album</option>
<option value="song">song</option> <option value="track">song</option>
</select> </select>
with with
<input type="text" name="action-txt" id="action-txt"> <input type="text" name="action-txt" id="action-txt">

View file

@ -3,6 +3,8 @@ const jetzig = @import("jetzig");
const jetquery = @import("jetzig").jetquery; const jetquery = @import("jetzig").jetquery;
const ScrobbleTypes = @import("../../types.zig"); const ScrobbleTypes = @import("../../types.zig");
const zeit = @import("zeit"); 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 { pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
_ = data; _ = data;
@ -31,6 +33,11 @@ pub fn post(request: *jetzig.Request) !jetzig.View {
var skipped_tracks: u64 = 0; var skipped_tracks: u64 = 0;
var limited_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. // 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, // 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 // 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; 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); 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 // This is so unnecessary, probably useful once I start doing Spotify integration though
inline for (std.meta.fields(ScrobbleTypes.LastFMScrobble)) |f| { 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. // Note sure why this works for ZMPL, but not for jobs.
try scrobbles_view.append(scrobble); try scrobbles_view.append(formatted_scrobble);
} }
}, },
1 => { 1 => {
@ -88,7 +97,9 @@ pub fn post(request: *jetzig.Request) !jetzig.View {
} }
// Turn SpotifyScrobble into a LastFM scrobble // 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); var value = try scrobbles_data.append(.object);

View file

@ -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), inline else => |on| match_found = match_found and std.mem.eql(u8, @field(scrobble, @tagName(on)), cond.match_txt),
}, },
.contains => switch (cond.match_on) { .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),
}, },
} }
} }