Compare commits

...

4 commits

Author SHA1 Message Date
d638fa66c5 Create GET function for a song view 2025-05-29 15:33:10 -04:00
3ff973e193 Use queries.zig in scrobbles view 2025-05-26 11:15:51 -04:00
f59eec79a8 Removed inline else from upload.zig
If I can figure out a way to get an array of a union instead of a union of arrays, we're in business to make this even better, but this is fine right now. The inline else was just a dumb way to keep the for on the outside
2025-05-26 11:15:19 -04:00
aab61631a3 Directly append complete_scrobble in upload.zig
Thanks bob :)
2025-05-25 16:16:18 -04:00
6 changed files with 188 additions and 147 deletions

View file

@ -17,8 +17,8 @@
// internet connectivity. // internet connectivity.
.dependencies = .{ .dependencies = .{
.jetzig = .{ .jetzig = .{
.url = "https://github.com/jetzig-framework/jetzig/archive/7be1d137fcab5c422e05f12092f6e04a02900d6f.tar.gz", .url = "https://github.com/jetzig-framework/jetzig/archive/695664bc10e6e73389ee2fe7fbcaedc27fa8adc9.tar.gz",
.hash = "jetzig-0.0.0-IpAgLURbDwB1NlywUH7lnQ3zptNvSQWVosaA1k7l1cNz", .hash = "jetzig-0.0.0-IpAgLSFeDwBEhfEaDBo0JqhRbvQA3ScbWjssBqhJHFmb",
}, },
.zeit = .{ .zeit = .{
.url = "https://github.com/rockorager/zeit/archive/refs/tags/v0.6.0.tar.gz", .url = "https://github.com/rockorager/zeit/archive/refs/tags/v0.6.0.tar.gz",

View file

@ -1,56 +1,12 @@
const std = @import("std"); const std = @import("std");
const jetzig = @import("jetzig"); const jetzig = @import("jetzig");
const zeit = @import("zeit"); const queries = @import("../../queries.zig");
const TableRow = @import("../../types.zig").TableRow;
const HyperlinkData = @import("../../types.zig").HyperlinkData;
const Utils = @import("../../date_fmt.zig");
pub fn index(request: *jetzig.Request) !jetzig.View { pub fn index(request: *jetzig.Request) !jetzig.View {
var root = try request.data(.object); var root = try request.data(.object);
var scrobbles_view = try root.put("scrobbles", .array);
const query = const scrobbles = try queries.entityQueryResult(request, queries.generateQuery(.scrobble, .entities), .{}, .array);
\\SELECT songs.name, songs.id, albums.name, albums.id, artists.name, artists.id, scrobbles.id, scrobbles.datetime try root.put("scrobbles", scrobbles);
\\FROM albumsongs
\\INNER JOIN songs ON songs.id = albumsongs.song_id
\\INNER JOIN albums ON albums.id = albumsongs.album_id
\\INNER JOIN scrobbles ON scrobbles.albumsong = albumsongs.id
\\INNER JOIN albumsongsartists ON albumsongsartists.albumsong_id = albumsongs.id
\\INNER JOIN artists ON artists.id = albumsongsartists.artist_id
\\ORDER BY scrobbles.datetime ASC
;
var scrobbles_jq_result = try request.repo.executeSql(query, .{});
defer scrobbles_jq_result.deinit();
const Scrobble = struct { song_name: []const u8, song_id: i32, album_name: []const u8, album_id: i32, artist_name: []const u8, artist_id: i32, s_id: i32, date: i64 };
var prev_s_id: ?i32 = null;
var row: ?TableRow = null;
var artistlist = std.ArrayList(HyperlinkData).init(request.allocator);
blk: while (try scrobbles_jq_result.postgresql.result.next()) |scrobble_row| {
const scrobble = try scrobble_row.to(Scrobble, .{ .dupe = true, .allocator = request.allocator });
if (scrobble.s_id == prev_s_id) {
try artistlist.append(.{ .name = scrobble.artist_name, .id = scrobble.artist_id });
continue :blk;
} else {
const date = try Utils.dateFmt(request.allocator, scrobble.date);
try artistlist.append(.{ .name = scrobble.artist_name, .id = scrobble.artist_id });
row = TableRow{
.song = .{ .name = scrobble.song_name, .id = scrobble.song_id },
.album = .{ .name = scrobble.album_name, .id = scrobble.album_id },
.artistlist = try artistlist.toOwnedSlice(),
.date = date,
};
try scrobbles_view.append(row);
}
prev_s_id = scrobble.s_id;
}
return request.render(.ok); return request.render(.ok);
} }

View file

@ -12,6 +12,21 @@ pub fn index(request: *jetzig.Request) !jetzig.View {
} }
pub fn get(id: []const u8, request: *jetzig.Request) !jetzig.View { pub fn get(id: []const u8, request: *jetzig.Request) !jetzig.View {
_ = id; var root = try request.data(.object);
const song = try queries.entityQueryResult(request, queries.generateQuery(.song, .entity_info), .{id}, .object);
try root.put("song", song.get("entity_info"));
const scrobbles = try queries.entityQueryResult(request, queries.generateQuery(.song, .entity_items), .{id}, .array);
try root.put("scrobbles", scrobbles);
const appears = try queries.entityQueryResult(request, queries.generateQuery(.song, .appears), .{id}, .array);
try root.put("appears", appears);
const firstlast = try queries.entityQueryResult(request, queries.generateQuery(.song, .firstlast), .{id}, .array);
try root.put("firstlast", firstlast);
const timescale = try queries.entityQueryResult(request, queries.generateQuery(.song, .timescale), .{id}, .array);
try root.put("yearly", timescale);
return request.render(.ok); return request.render(.ok);
} }

View file

@ -1,3 +1,19 @@
<div> @zig {
<span>Content goes here</span> const ColumnChoices = []const enum{song, album, artist, artistlist, scrobbles, date};
</div> const columns: ColumnChoices = &.{.song, .artistlist, .album, .date};
}
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
@partial partials/header
<h1>{{.song}}</h1>
@partial partials/firstlast_listens(scrobbles: .song.scrobbles, rank: .song.rank, firstlast: .firstlast)
<h3>Yearly Performance</h3>
@partial partials/timescale(range: .yearly)
<h2>Scrobbles</h2>
@partial partials/newtable(T: ColumnChoices, table_data: .scrobbles, columns: columns)
</body>
</html>

View file

@ -81,23 +81,46 @@ pub fn post(request: *jetzig.Request) !jetzig.View {
// Not sure if I should be proud or feel sick // Not sure if I should be proud or feel sick
switch (imported_scrobbles) { switch (imported_scrobbles) {
inline else => |scrobbles| { .LastFMStats => |scrobbles| {
appends: for (scrobbles) |scrobble| { appends: for (scrobbles) |scrobble| {
const filtered_scrobble: Data.Scrobble = blk: switch (@TypeOf(scrobble)) {
Data.IgnorantScrobble => {
if (scrobble.date > latest_timestamp * 1_000 or scrobble.date < earliest_timestamp * 1_000) { if (scrobble.date > latest_timestamp * 1_000 or scrobble.date < earliest_timestamp * 1_000) {
limited_tracks += 1; limited_tracks += 1;
continue :appends; continue :appends;
} }
break :blk Data.Scrobble{ const filtered_scrobble = Data.Scrobble{
.album = scrobble.album, .album = scrobble.album,
.artists_album = &[_][]const u8{scrobble.artist}, .artists_album = &[_][]const u8{scrobble.artist},
.track = scrobble.track, .track = scrobble.track,
.artists_track = &[_][]const u8{scrobble.artist}, .artists_track = &[_][]const u8{scrobble.artist},
.date = scrobble.date * 1_000, .date = scrobble.date * 1_000,
}; };
const complete_scrobble = if (rule_list) |rl| try rules.applyScrobbleRule(request.allocator, filtered_scrobble, rl) else filtered_scrobble;
const row = try Utils.scrobbleToRow(request.allocator, complete_scrobble);
try view_params.append(row);
try job_params.append(complete_scrobble);
}
}, },
Data.SpotifyScrobble => { .LastFMWeb => |scrobbles| {
for (scrobbles) |scrobble| {
const filtered_scrobble = Data.Scrobble{
.album = if (scrobble.album) |album| album.@"#text" else "Not Provided",
.artists_album = &[_][]const u8{scrobble.artist.@"#text"},
.track = scrobble.name,
.artists_track = &[_][]const u8{scrobble.artist.@"#text"},
.date = try std.fmt.parseInt(i64, scrobble.date.?.uts, 10) * 1_000_000,
};
const complete_scrobble = if (rule_list) |rl| try rules.applyScrobbleRule(request.allocator, filtered_scrobble, rl) else filtered_scrobble;
const row = try Utils.scrobbleToRow(request.allocator, complete_scrobble);
try view_params.append(row);
try job_params.append(complete_scrobble);
}
},
.Spotify => |scrobbles| {
appends: for (scrobbles) |scrobble| {
if (scrobble.ms_played < 30_000 and (scrobble.reason_end == null or !std.mem.eql(u8, scrobble.reason_end.?, "trackdone"))) { if (scrobble.ms_played < 30_000 and (scrobble.reason_end == null or !std.mem.eql(u8, scrobble.reason_end.?, "trackdone"))) {
skipped_tracks += 1; skipped_tracks += 1;
continue :appends; continue :appends;
@ -113,45 +136,19 @@ pub fn post(request: *jetzig.Request) !jetzig.View {
continue :appends; continue :appends;
} }
break :blk Data.Scrobble{ const filtered_scrobble = Data.Scrobble{
.album = scrobble.master_metadata_album_album_name.?, .album = scrobble.master_metadata_album_album_name.?,
.artists_album = &[_][]const u8{scrobble.master_metadata_album_artist_name.?}, .artists_album = &[_][]const u8{scrobble.master_metadata_album_artist_name.?},
.track = scrobble.master_metadata_track_name.?, .track = scrobble.master_metadata_track_name.?,
.artists_track = &[_][]const u8{scrobble.master_metadata_album_artist_name.?}, .artists_track = &[_][]const u8{scrobble.master_metadata_album_artist_name.?},
.date = (try zeit.instant(.{ .source = .{ .iso8601 = scrobble.ts } })).unixTimestamp() * 1_000_000, .date = (try zeit.instant(.{ .source = .{ .iso8601 = scrobble.ts } })).unixTimestamp() * 1_000_000,
}; };
},
Data.LastFMWebScrobble => {
break :blk Data.Scrobble{
.album = if (scrobble.album) |album| album.@"#text" else "Not Provided",
.artists_album = &[_][]const u8{scrobble.artist.@"#text"},
.track = scrobble.name,
.artists_track = &[_][]const u8{scrobble.artist.@"#text"},
.date = try std.fmt.parseInt(i64, scrobble.date.?.uts, 10) * 1_000_000,
};
},
else => unreachable,
};
const complete_scrobble = if (rule_list) |rl| try rules.applyScrobbleRule(request.allocator, filtered_scrobble, rl) else filtered_scrobble; const complete_scrobble = if (rule_list) |rl| try rules.applyScrobbleRule(request.allocator, filtered_scrobble, rl) else filtered_scrobble;
const row = try Utils.scrobbleToRow(request.allocator, complete_scrobble); const row = try Utils.scrobbleToRow(request.allocator, complete_scrobble);
try view_params.append(row); try view_params.append(row);
var job_param = try job_params.append(.object); try job_params.append(complete_scrobble);
try job_param.put("album", complete_scrobble.album);
try job_param.put("track", complete_scrobble.track);
try job_param.put("date", complete_scrobble.date);
var track_artists_array = try job_param.put("artists_track", .array);
for (complete_scrobble.artists_track) |a| {
try track_artists_array.append(a);
}
var album_artists_array = try job_param.put("artists_album", .array);
for (complete_scrobble.artists_album) |a| {
try album_artists_array.append(a);
}
} }
}, },
} }

View file

@ -20,30 +20,27 @@ pub fn entityQueryResult(request: *jetzig.Request, query: GeneratedQuery, args:
blk: while (try result.postgresql.result.next()) |entity_row| { blk: while (try result.postgresql.result.next()) |entity_row| {
switch (query.ResultType) { switch (query.ResultType) {
FirstlastResult, TimescaleResult, EntitiesScrobbleResult, EntitiesSongResult, EntitiesAlbumResult, EntitiesArtistResult, EntityItemsResult, AppearsResult, EntityInfoResult => |T| { FirstlastResult, TimescaleResult, EntitiesScrobbleResult, EntitiesSongResult, EntitiesAlbumResult, EntitiesArtistResult, EntityItemsResult, AppearsResult, EntityInfoResult, EntityItemsSongResult => |T| {
const entity = try entity_row.to(T, .{ .dupe = true, .allocator = request.allocator }); const entity = try entity_row.to(T, .{ .dupe = true, .allocator = request.allocator });
const item: ?TableRow = switch (query.ResultType) { const item: ?TableRow = switch (query.ResultType) {
EntitiesScrobbleResult, EntitiesSongResult, EntitiesAlbumResult, EntitiesArtistResult => switch (query.entity) { EntitiesScrobbleResult, EntitiesSongResult, EntitiesAlbumResult, EntitiesArtistResult => switch (query.entity) {
.artist => TableRow{ .artist = .{ .name = entity.name, .id = entity.id }, .scrobbles = entity.scrobbles }, .artist => TableRow{ .artist = .{ .name = entity.name, .id = entity.id }, .scrobbles = entity.scrobbles },
.album => album_entities: { .scrobble, .song, .album => album_entities: {
const last_artist = artist_list.getLastOrNull(); const last_artist = artist_list.getLastOrNull();
try artist_list.append(.{ .name = entity.artist_name, .id = entity.artist_id }); try artist_list.append(.{ .name = entity.artist_name, .id = entity.artist_id });
if (last_artist) |la| { if (last_artist) |la| {
if (la.id == entity.artist_id) continue :blk; if (la.id == entity.artist_id) continue :blk;
} }
break :album_entities TableRow{ .album = .{ .name = entity.name, .id = entity.id }, .artistlist = try artist_list.toOwnedSlice(), .scrobbles = entity.scrobbles }; break :album_entities switch (query.entity) {
}, .scrobble => TableRow{ .song = .{ .name = entity.song_name, .id = entity.song_id }, .album = .{ .name = entity.album_name, .id = entity.album_id }, .artistlist = try artist_list.toOwnedSlice(), .date = entity.date },
.song => song_entities: { .song => TableRow{ .song = .{ .name = entity.name, .id = entity.id }, .artistlist = try artist_list.toOwnedSlice(), .scrobbles = entity.scrobbles },
const last_artist = artist_list.getLastOrNull(); .album => TableRow{ .album = .{ .name = entity.name, .id = entity.id }, .artistlist = try artist_list.toOwnedSlice(), .scrobbles = entity.scrobbles },
try artist_list.append(.{ .name = entity.artist_name, .id = entity.artist_id });
if (last_artist) |la| {
if (la.id == entity.artist_id) continue :blk;
}
break :song_entities TableRow{ .song = .{ .name = entity.name, .id = entity.id }, .artistlist = try artist_list.toOwnedSlice(), .scrobbles = entity.scrobbles };
},
else => unreachable, else => unreachable,
};
}, },
EntityItemsResult => switch (query.entity) { },
EntityItemsResult, EntityItemsSongResult => switch (query.entity) {
.song => TableRow{ .song = .{ .name = entity.song_name, .id = entity.song_id }, .album = .{ .name = entity.album_name, .id = entity.album_id }, .date = entity.date },
.artist => TableRow{ .album = .{ .name = entity.name, .id = entity.id }, .scrobbles = entity.scrobbles }, .artist => TableRow{ .album = .{ .name = entity.name, .id = entity.id }, .scrobbles = entity.scrobbles },
.album => TableRow{ .song = .{ .name = entity.name, .id = entity.id }, .scrobbles = entity.scrobbles }, .album => TableRow{ .song = .{ .name = entity.name, .id = entity.id }, .scrobbles = entity.scrobbles },
else => unreachable, else => unreachable,
@ -92,10 +89,21 @@ const TimescaleResult = struct { year: []const u8, scrobbles: i64 };
const EntitiesArtistResult = struct { name: []const u8, id: i32, scrobbles: i64 }; const EntitiesArtistResult = struct { name: []const u8, id: i32, scrobbles: i64 };
const EntitiesAlbumResult = struct { name: []const u8, id: i32, artist_name: []const u8, artist_id: i32, scrobbles: i64 }; const EntitiesAlbumResult = struct { name: []const u8, id: i32, artist_name: []const u8, artist_id: i32, scrobbles: i64 };
const EntitiesSongResult = struct { name: []const u8, id: i32, artist_name: []const u8, artist_id: i32, scrobbles: i64 }; const EntitiesSongResult = struct { name: []const u8, id: i32, artist_name: []const u8, artist_id: i32, scrobbles: i64 };
const EntitiesScrobbleResult = struct { song_name: []const u8, song_id: i32, album_name: []const u8, album_id: i32, artist_name: []const u8, artist_id: i32, s_id: i32, date: i64 }; const EntitiesScrobbleResult = struct { song_name: []const u8, song_id: i32, album_name: []const u8, album_id: i32, artist_name: []const u8, artist_id: i32, date: []const u8 };
const EntityItemsResult = struct { name: []const u8, id: i32, scrobbles: i64 }; const EntityItemsResult = struct { name: []const u8, id: i32, scrobbles: i64 };
const AppearsResult = struct { name: []const u8, id: i32, scrobbles: i64 }; const AppearsResult = struct { name: []const u8, id: i32, scrobbles: i64 };
const EntityInfoResult = struct { name: []const u8, id: i32, scrobbles: i64, rank: []const u8 }; const EntityInfoResult = struct { name: []const u8, id: i32, scrobbles: i64, rank: []const u8 };
const EntityItemsSongResult = struct { song_name: []const u8, song_id: i32, album_name: []const u8, album_id: i32, date: []const u8 };
const UnifiedResult = struct {
album_name: ?[]const u8 = null,
album_id: ?i32 = null,
song_name: ?[]const u8 = null,
song_id: ?i32 = null,
artist_names: ?[]const []const u8 = null,
artist_ids: ?[]i32 = null,
scrobbles: ?i64 = null,
date: ?[]const u8 = null,
};
pub fn generateQuery(entity: EntityType, query_type: QueryTypeEnum) GeneratedQuery { pub fn generateQuery(entity: EntityType, query_type: QueryTypeEnum) GeneratedQuery {
return GeneratedQuery{ return GeneratedQuery{
@ -110,7 +118,11 @@ pub fn generateQuery(entity: EntityType, query_type: QueryTypeEnum) GeneratedQue
.album => EntitiesAlbumResult, .album => EntitiesAlbumResult,
.artist => EntitiesArtistResult, .artist => EntitiesArtistResult,
}, },
.entity_items => EntityItemsResult, .entity_items => switch (entity) {
.scrobble => unreachable,
.song => EntityItemsSongResult,
else => EntityItemsResult,
},
.appears => AppearsResult, .appears => AppearsResult,
.entity_info => EntityInfoResult, .entity_info => EntityInfoResult,
}, },
@ -120,29 +132,27 @@ pub fn generateQuery(entity: EntityType, query_type: QueryTypeEnum) GeneratedQue
switch (entity) { switch (entity) {
.scrobble => unreachable, .scrobble => unreachable,
.song => .song =>
\\(SELECT songs.name AS name, songs.id AS id, scrobbles.datetime AS date \\(SELECT songs.name AS name, songs.id AS id, TO_CHAR(scrobbles.datetime,'YYYY-MM-DD HH24:MI:SS') AS date
\\FROM albumsongs \\FROM albumsongs
\\INNER JOIN songs ON songs.id = albumsongs.song_id \\INNER JOIN songs ON songs.id = albumsongs.song_id
\\INNER JOIN scrobbles ON albumsongs.id = scrobbles.albumsong \\INNER JOIN scrobbles ON albumsongs.id = scrobbles.albumsong
\\INNER JOIN albumsongsartists ON albumsongsartists.albumsong_id = albumsongs.id \\WHERE albumsongs.song_id = $1
\\WHERE albumsongsartists.artist_id = $1
\\ORDER BY scrobbles.datetime ASC \\ORDER BY scrobbles.datetime ASC
\\LIMIT 1) \\LIMIT 1)
\\ \\
\\UNION ALL \\UNION ALL
\\ \\
\\(SELECT songs.name AS name, songs.id AS id, scrobbles.datetime AS date \\(SELECT songs.name AS name, songs.id AS id, TO_CHAR(scrobbles.datetime, 'YYYY-MM-DD HH24:MI:SS') AS date
\\FROM albumsongs \\FROM albumsongs
\\INNER JOIN songs ON songs.id = albumsongs.song_id \\INNER JOIN songs ON songs.id = albumsongs.song_id
\\INNER JOIN scrobbles ON albumsongs.id = scrobbles.albumsong \\INNER JOIN scrobbles ON albumsongs.id = scrobbles.albumsong
\\INNER JOIN albumsongsartists ON albumsongsartists.albumsong_id = albumsongs.id \\WHERE albumsongs.song_id = $1
\\WHERE albumsongsartists.artist_id = $1
\\ORDER BY scrobbles.datetime DESC \\ORDER BY scrobbles.datetime DESC
\\LIMIT 1) \\LIMIT 1)
, ,
.album => .album =>
\\(SELECT songs.name AS name, songs.id AS id, TO_CHAR(scrobbles.datetime, 'YYYY-MM-DD HH24:MM:SS') AS date \\(SELECT songs.name AS name, songs.id AS id, TO_CHAR(scrobbles.datetime, 'YYYY-MM-DD HH24:MI:SS') AS date
\\FROM albumsongs \\FROM albumsongs
\\INNER JOIN songs ON songs.id = albumsongs.song_id \\INNER JOIN songs ON songs.id = albumsongs.song_id
\\INNER JOIN scrobbles ON albumsongs.id = scrobbles.albumsong \\INNER JOIN scrobbles ON albumsongs.id = scrobbles.albumsong
@ -153,7 +163,7 @@ pub fn generateQuery(entity: EntityType, query_type: QueryTypeEnum) GeneratedQue
\\ \\
\\UNION ALL \\UNION ALL
\\ \\
\\(SELECT songs.name AS name, songs.id AS id, TO_CHAR(scrobbles.datetime, 'YYYY-MM-DD HH24:MM:SS') AS date \\(SELECT songs.name AS name, songs.id AS id, TO_CHAR(scrobbles.datetime, 'YYYY-MM-DD HH24:MI:SS') AS date
\\FROM albumsongs \\FROM albumsongs
\\INNER JOIN songs ON songs.id = albumsongs.song_id \\INNER JOIN songs ON songs.id = albumsongs.song_id
\\INNER JOIN scrobbles ON albumsongs.id = scrobbles.albumsong \\INNER JOIN scrobbles ON albumsongs.id = scrobbles.albumsong
@ -164,7 +174,7 @@ pub fn generateQuery(entity: EntityType, query_type: QueryTypeEnum) GeneratedQue
, ,
.artist => .artist =>
\\(SELECT songs.name AS name, songs.id AS id, TO_CHAR(scrobbles.datetime, 'YYYY-MM-DD HH24:MM:SS') AS date \\(SELECT songs.name AS name, songs.id AS id, TO_CHAR(scrobbles.datetime, 'YYYY-MM-DD HH24:MI:SS') AS date
\\FROM albumsongs \\FROM albumsongs
\\INNER JOIN songs ON songs.id = albumsongs.song_id \\INNER JOIN songs ON songs.id = albumsongs.song_id
\\INNER JOIN scrobbles ON albumsongs.id = scrobbles.albumsong \\INNER JOIN scrobbles ON albumsongs.id = scrobbles.albumsong
@ -175,7 +185,7 @@ pub fn generateQuery(entity: EntityType, query_type: QueryTypeEnum) GeneratedQue
\\ \\
\\UNION ALL \\UNION ALL
\\ \\
\\(SELECT songs.name AS name, songs.id AS id, TO_CHAR(scrobbles.datetime, 'YYYY-MM-DD HH24:MM:SS') AS date \\(SELECT songs.name AS name, songs.id AS id, TO_CHAR(scrobbles.datetime, 'YYYY-MM-DD HH24:MI:SS') AS date
\\FROM albumsongs \\FROM albumsongs
\\INNER JOIN songs ON songs.id = albumsongs.song_id \\INNER JOIN songs ON songs.id = albumsongs.song_id
\\INNER JOIN scrobbles ON albumsongs.id = scrobbles.albumsong \\INNER JOIN scrobbles ON albumsongs.id = scrobbles.albumsong
@ -194,9 +204,8 @@ pub fn generateQuery(entity: EntityType, query_type: QueryTypeEnum) GeneratedQue
\\SELECT TO_CHAR(date_trunc('year', datetime), 'YYYY') AS year, COUNT(*) as scrobbles \\SELECT TO_CHAR(date_trunc('year', datetime), 'YYYY') AS year, COUNT(*) as scrobbles
\\FROM scrobbles \\FROM scrobbles
\\INNER JOIN albumsongs ON albumsongs.id = scrobbles.albumsong \\INNER JOIN albumsongs ON albumsongs.id = scrobbles.albumsong
\\INNER JOIN albumsongsartists ON albumsongsartists.albumsong_id = albumsongs.id \\INNER JOIN songs ON songs.id = albumsongs.song_id
\\INNER JOIN artists ON artists.id = albumsongsartists.artist_id \\WHERE songs.id = $1
\\WHERE artists.id = $1
\\GROUP BY year \\GROUP BY year
\\ORDER BY year ASC; \\ORDER BY year ASC;
, ,
@ -227,7 +236,14 @@ pub fn generateQuery(entity: EntityType, query_type: QueryTypeEnum) GeneratedQue
//.ResultType = EntitiesResult, //.ResultType = EntitiesResult,
switch (entity) { switch (entity) {
.scrobble => .scrobble =>
\\none \\SELECT songs.name AS song_name, songs.id AS song_id, albums.name AS album_name, albums.id AS album_id, artists.name AS artist_name, artists.id AS artist_id, TO_CHAR(scrobbles.datetime, 'YYYY-MM-DD HH24:MI:SS') AS date
\\FROM albumsongs
\\INNER JOIN songs ON songs.id = albumsongs.song_id
\\INNER JOIN albums ON albums.id = albumsongs.album_id
\\INNER JOIN scrobbles ON scrobbles.albumsong = albumsongs.id
\\INNER JOIN albumsongsartists ON albumsongsartists.albumsong_id = albumsongs.id
\\INNER JOIN artists ON artists.id = albumsongsartists.artist_id
\\ORDER BY scrobbles.datetime ASC
, ,
.song => .song =>
\\SELECT songs.name, songs.id, artists.name, artists.id, COUNT(scrobbles) AS scrobbles \\SELECT songs.name, songs.id, artists.name, artists.id, COUNT(scrobbles) AS scrobbles
@ -265,13 +281,21 @@ pub fn generateQuery(entity: EntityType, query_type: QueryTypeEnum) GeneratedQue
switch (entity) { switch (entity) {
.scrobble => unreachable, .scrobble => unreachable,
.song => .song =>
\\ NOTHING YET \\SELECT albums.name AS album_name, albums.id AS album_id, COUNT(scrobbles) AS scrobbles
\\FROM artistalbums
\\INNER JOIN albums ON albums.id = artistalbums.album_id
\\INNER JOIN albumsongs ON albumsongs.album_id = albums.id
\\INNER JOIN scrobbles ON scrobbles.albumsong = albumsongs.id
\\INNER JOIN albumsongsartists ON albumsongsartists.albumsong_id = albumsongs.id
\\WHERE albumsongs.song_id = $1
\\GROUP BY albums.id
\\ORDER BY scrobbles DESC;
, ,
.album => .album =>
\\nope \\nope
, ,
.artist => .artist =>
\\SELECT albums.name AS name, albums.id AS id, COUNT(scrobbles) AS scrobbles \\SELECT albums.name AS album_name, albums.id AS album_id, COUNT(scrobbles) AS scrobbles
\\FROM artistalbums \\FROM artistalbums
\\INNER JOIN albums ON albums.id = artistalbums.album_id \\INNER JOIN albums ON albums.id = artistalbums.album_id
\\INNER JOIN albumsongs ON albumsongs.album_id = albums.id \\INNER JOIN albumsongs ON albumsongs.album_id = albums.id
@ -288,7 +312,13 @@ pub fn generateQuery(entity: EntityType, query_type: QueryTypeEnum) GeneratedQue
switch (entity) { switch (entity) {
.scrobble => unreachable, .scrobble => unreachable,
.song => .song =>
\\get all scrobbles \\SELECT songs.name, songs.id, albums.name, albums.id, TO_CHAR(scrobbles.datetime, 'YYYY-MM-DD HH24:MI:SS') AS date
\\FROM albumsongs
\\INNER JOIN albums ON albums.id = albumsongs.album_id
\\INNER JOIN songs ON songs.id = albumsongs.song_id
\\INNER JOIN scrobbles ON scrobbles.albumsong = albumsongs.id
\\WHERE songs.id = $1
\\ORDER BY date ASC
, ,
.album => .album =>
\\SELECT songs.name, songs.id, COUNT(scrobbles) AS scrobbles \\SELECT songs.name, songs.id, COUNT(scrobbles) AS scrobbles
@ -314,12 +344,19 @@ pub fn generateQuery(entity: EntityType, query_type: QueryTypeEnum) GeneratedQue
.entity_info => switch (entity) { .entity_info => switch (entity) {
.scrobble => unreachable, .scrobble => unreachable,
.song => .song =>
\\lol idk \\SELECT * FROM
\\(SELECT *, TO_CHAR(ROW_NUMBER() OVER (ORDER BY scrobbles DESC),'FM99999th') AS rank FROM
\\(SELECT songs.name AS name, songs.id AS id, COUNT(scrobbles) AS scrobbles
\\FROM albumsongs
\\INNER JOIN songs ON albumsongs.song_id = songs.id
\\INNER JOIN scrobbles ON scrobbles.albumsong = albumsongs.id
\\GROUP BY songs.id))
\\WHERE id = $1
, ,
.album => .album =>
\\SELECT * FROM \\SELECT * FROM
\\(SELECT *, TO_CHAR(ROW_NUMBER() OVER (ORDER BY scrobbles DESC),'FM9999th') AS rank FROM \\(SELECT *, TO_CHAR(ROW_NUMBER() OVER (ORDER BY scrobbles DESC),'FM9999th') AS rank FROM
\\(SELECT albums.name, albums.id, COUNT(scrobbles) AS scrobbles \\(SELECT albums.name AS name, albums.id AS id, COUNT(scrobbles) AS scrobbles
\\FROM albumsongs \\FROM albumsongs
\\INNER JOIN albums ON albumsongs.album_id = albums.id \\INNER JOIN albums ON albumsongs.album_id = albums.id
\\INNER JOIN scrobbles ON scrobbles.albumsong = albumsongs.id \\INNER JOIN scrobbles ON scrobbles.albumsong = albumsongs.id
@ -338,6 +375,26 @@ pub fn generateQuery(entity: EntityType, query_type: QueryTypeEnum) GeneratedQue
\\WHERE id = $1 \\WHERE id = $1
, ,
}, },
.datestreak => switch (entity) {
.song =>
\\SELECT songs.name, albums.name, streak.maxseq, streak.ds, streak.de FROM (SELECT albumsong, MAX(numdays) AS maxseq, ds, de
\\FROM (SELECT albumsong, grp, MIN(datetime::date) AS ds, MAX(datetime::date) AS de,
\\COUNT(DISTINCT datetime::date) AS numdays
\\FROM (SELECT scrobbles.*,
\\((datetime::date - '1970-01-01'::date) - DENSE_RANK() OVER (PARTITION BY albumsong ORDER BY datetime::date)) AS grp
\\FROM scrobbles
\\) scrobbles
\\GROUP BY albumsong, grp
\\) scrobbles
\\GROUP BY albumsong, ds, de) AS streak
\\INNER JOIN albumsongs ON albumsongs.id = streak.albumsong
\\INNER JOIN albums ON albums.id = albumsongs.album_id
\\INNER JOIN songs ON songs.id = albumsongs.song_id
\\ORDER BY streak.maxseq DESC;
,
else => unreachable,
},
}, },
}; };
} }