Begin Spotify uploading

This commit is contained in:
mitteneer 2025-02-21 22:53:27 -05:00
parent 7957345057
commit e2ff66ea50
4 changed files with 36 additions and 21 deletions

View file

@ -1,7 +1,7 @@
const std = @import("std"); const std = @import("std");
const jetzig = @import("jetzig"); const jetzig = @import("jetzig");
const jetquery = @import("jetzig").jetquery; const jetquery = @import("jetzig").jetquery;
const Scrobble = @import("../../types.zig").SafeLastFMScrobble; const Scrobble = @import("../../types.zig").LastFMScrobble;
const lastfm = @import("../../types.zig").LastFM; const lastfm = @import("../../types.zig").LastFM;
// The `run` function for a job is invoked every time the job is processed by a queue worker // The `run` function for a job is invoked every time the job is processed by a queue worker

View file

@ -1,8 +1,10 @@
const std = @import("std"); const std = @import("std");
const jetzig = @import("jetzig"); const jetzig = @import("jetzig");
const jetquery = @import("jetzig").jetquery; const jetquery = @import("jetzig").jetquery;
const Scrobble = @import("../../types.zig").LastFMScrobble; //const Scrobble = @import("../../types.zig").LastFMScrobble;
const lastfm = @import("../../types.zig").LastFM; //const lastfm = @import("../../types.zig").LastFM;
//const UploadData = @import("../../types").UploadData;
const ScrobbleTypes = @import("../../types.zig");
const zeit = @import("zeit"); const zeit = @import("zeit");
pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
@ -20,26 +22,36 @@ pub fn post(request: *jetzig.Request) !jetzig.View {
var root = try request.data(.object); var root = try request.data(.object);
if (try request.file("upload")) |file| { if (try request.file("upload")) |file| {
//std.debug.print("{s}", .{file.content}); const params = try request.params();
const content = try std.json.parseFromSliceLeaky(lastfm, request.allocator, file.content, .{ .ignore_unknown_fields = true });
var content: ScrobbleTypes.UploadData = undefined;
const source = std.fmt.parseInt(u8, params.get("t").string.value, 10).?;
//if (params.get("t")) |param| {
// const source = std.fmt.parseInt(u8, param.string.value, 10);
switch (source) {
0 => content = try std.json.parseFromSliceLeaky(ScrobbleTypes.LastFM, request.allocator, file.content, .{}),
1 => content = try std.json.parseFromSliceLeaky(ScrobbleTypes.Spotify, request.allocator, file.content, .{}),
else => unreachable,
}
//const content = try std.json.parseFromSliceLeaky(lastfm, request.allocator, file.content, .{ .ignore_unknown_fields = true });
var scrobbles_view = try root.put("scrobbles", .array); var scrobbles_view = try root.put("scrobbles", .array);
var job = try request.job("process_scrobbles"); var job = try request.job("process_scrobbles");
var scrobbles_data = try job.params.put("scrobbles", .array); var scrobbles_data = try job.params.put("scrobbles", .array);
const params = try request.params();
const limiting_date_string: ?[]const u8 = if (params.get("l")) |param| param.string.value else null;
const limiting_date_instant: ?zeit.Instant = if (limiting_date_string) |str| try zeit.instant(.{ .source = .{ .iso8601 = str } }) else null;
// This is seconds from Unix epoch // This is seconds from Unix epoch
const limiting_date_epoch = if (limiting_date_instant) |time| time.unixTimestamp() else 9_223_372_036_854_775_807; const limiting_date = if (params.get("l")) |param| (try zeit.instant(.{ .source = .{ .iso8601 = param.string.value } })).unixTimestamp() else 9_223_372_036_854_775_807;
appends: for (content.scrobbles) |scrobble| { appends: for (content.scrobbles) |scrobble| {
if (std.mem.eql(u8, @typeName(scrobble), "SpotifyScrobble")) scrobble = scrobble.scrobblize();
// Scrobble.date is in milliseconds from Unix epoch // Scrobble.date is in milliseconds from Unix epoch
if (scrobble.date < limiting_date_epoch * 1000) continue :appends; if (scrobble.date < limiting_date * 1000) continue :appends;
var value = try scrobbles_data.append(.object); var value = try scrobbles_data.append(.object);
// 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(Scrobble)) |f| { inline for (std.meta.fields(ScrobbleTypes.Scrobble)) |f| {
try value.put(f.name, @as(f.type, @field(scrobble, f.name))); try value.put(f.name, @as(f.type, @field(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.

View file

@ -14,8 +14,8 @@
<input type="file" name="upload" /> <input type="file" name="upload" />
<input type="submit" value="Submit" /> <input type="submit" value="Submit" />
<fieldset> <fieldset>
<input type="radio" name="t" label="Last.fm">Last.fm</input> <input type="radio" name="t" label="Last.fm" value="0">Last.fm</input>
<input type="radio" name="t" label="Spotify">Spotify</input> <input type="radio" name="t" label="Spotify" value="1">Spotify</input>
Upload Scrobbles after: <input type="datetime-local" name="l" label="date"></input> Upload Scrobbles after: <input type="datetime-local" name="l" label="date"></input>
</fieldset> </fieldset>
</form> </form>

View file

@ -1,17 +1,12 @@
const zeit = @import("zeit");
pub const LastFMScrobble = struct { pub const LastFMScrobble = struct {
track: []const u8, track: []const u8,
artist: []const u8, artist: []const u8,
album: ?[]const u8 = "", album: []const u8 = "",
date: i128, date: i128,
}; };
pub const SafeLastFMScrobble = struct {
track: []const u8,
artist: []const u8,
album: []const u8,
date: u64,
};
// From lastfmstats.com // From lastfmstats.com
pub const LastFM = struct { username: []const u8, scrobbles: []LastFMScrobble }; pub const LastFM = struct { username: []const u8, scrobbles: []LastFMScrobble };
@ -35,6 +30,14 @@ pub const SpotifyScrobble = struct {
offline: bool, offline: bool,
offline_timestamp: u64, offline_timestamp: u64,
incognito_mode: bool, incognito_mode: bool,
pub fn scrobblize(self: *SpotifyScrobble) LastFMScrobble {
return LastFMScrobble{ .track = self.master_metadata_track_name, .artist = self.master_metadata_artist_name, .album = self.master_metadata_album_name, .date = try zeit.instant(.{ .source = .{ .iso8601 = self.ts } }).unixTimestamp() };
}
}; };
pub const Spotify = struct { scrobbles: []SpotifyScrobble }; pub const Spotify = struct { scrobbles: []SpotifyScrobble };
const UploadDataTag = enum { spotify, lastfm };
pub const UploadData = union(UploadDataTag) { spotify: Spotify, lastfm: LastFM };