diff --git a/build.zig.zon b/build.zig.zon
index d7fb785..5db05e3 100644
--- a/build.zig.zon
+++ b/build.zig.zon
@@ -17,8 +17,8 @@
// internet connectivity.
.dependencies = .{
.jetzig = .{
- .url = "https://github.com/jetzig-framework/jetzig/archive/7be1d137fcab5c422e05f12092f6e04a02900d6f.tar.gz",
- .hash = "jetzig-0.0.0-IpAgLURbDwB1NlywUH7lnQ3zptNvSQWVosaA1k7l1cNz",
+ .url = "https://github.com/jetzig-framework/jetzig/archive/695664bc10e6e73389ee2fe7fbcaedc27fa8adc9.tar.gz",
+ .hash = "jetzig-0.0.0-IpAgLSFeDwBEhfEaDBo0JqhRbvQA3ScbWjssBqhJHFmb",
},
.zeit = .{
.url = "https://github.com/rockorager/zeit/archive/refs/tags/v0.6.0.tar.gz",
diff --git a/src/app/views/scrobbles.zig b/src/app/views/scrobbles.zig
index 3fe946a..e61df4d 100644
--- a/src/app/views/scrobbles.zig
+++ b/src/app/views/scrobbles.zig
@@ -1,56 +1,12 @@
const std = @import("std");
const jetzig = @import("jetzig");
-const zeit = @import("zeit");
-const TableRow = @import("../../types.zig").TableRow;
-const HyperlinkData = @import("../../types.zig").HyperlinkData;
-const Utils = @import("../../date_fmt.zig");
+const queries = @import("../../queries.zig");
pub fn index(request: *jetzig.Request) !jetzig.View {
var root = try request.data(.object);
- var scrobbles_view = try root.put("scrobbles", .array);
- const query =
- \\SELECT songs.name, songs.id, albums.name, albums.id, artists.name, artists.id, scrobbles.id, scrobbles.datetime
- \\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;
- }
+ const scrobbles = try queries.entityQueryResult(request, queries.generateQuery(.scrobble, .entities), .{}, .array);
+ try root.put("scrobbles", scrobbles);
return request.render(.ok);
}
diff --git a/src/app/views/songs.zig b/src/app/views/songs.zig
index d5a7944..3143f01 100644
--- a/src/app/views/songs.zig
+++ b/src/app/views/songs.zig
@@ -12,6 +12,21 @@ pub fn index(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);
}
diff --git a/src/app/views/songs/get.zmpl b/src/app/views/songs/get.zmpl
index 76457d0..0a051a4 100644
--- a/src/app/views/songs/get.zmpl
+++ b/src/app/views/songs/get.zmpl
@@ -1,3 +1,19 @@
-
- Content goes here
-
+@zig {
+ const ColumnChoices = []const enum{song, album, artist, artistlist, scrobbles, date};
+ const columns: ColumnChoices = &.{.song, .artistlist, .album, .date};
+}
+
+
+
+
+
+
+@partial partials/header
+{{.song}}
+@partial partials/firstlast_listens(scrobbles: .song.scrobbles, rank: .song.rank, firstlast: .firstlast)
+Yearly Performance
+@partial partials/timescale(range: .yearly)
+Scrobbles
+@partial partials/newtable(T: ColumnChoices, table_data: .scrobbles, columns: columns)
+
+
\ No newline at end of file
diff --git a/src/app/views/upload.zig b/src/app/views/upload.zig
index af5d41d..bdb5cef 100644
--- a/src/app/views/upload.zig
+++ b/src/app/views/upload.zig
@@ -81,77 +81,74 @@ pub fn post(request: *jetzig.Request) !jetzig.View {
// Not sure if I should be proud or feel sick
switch (imported_scrobbles) {
- inline else => |scrobbles| {
+ .LastFMStats => |scrobbles| {
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) {
- limited_tracks += 1;
- continue :appends;
- }
- break :blk Data.Scrobble{
- .album = scrobble.album,
- .artists_album = &[_][]const u8{scrobble.artist},
- .track = scrobble.track,
- .artists_track = &[_][]const u8{scrobble.artist},
- .date = scrobble.date * 1_000,
- };
- },
- Data.SpotifyScrobble => {
- if (scrobble.ms_played < 30_000 and (scrobble.reason_end == null or !std.mem.eql(u8, scrobble.reason_end.?, "trackdone"))) {
- skipped_tracks += 1;
- continue :appends;
- }
- if (scrobble.master_metadata_album_artist_name == null or scrobble.master_metadata_track_name == null) {
- skipped_tracks += 1;
- continue :appends;
- }
-
- const iso_ts = try zeit.Time.fromISO8601(scrobble.ts);
- if ((iso_ts.after(latest_date) or iso_ts.before(earliest_date))) {
- limited_tracks += 1;
- continue :appends;
- }
-
- break :blk Data.Scrobble{
- .album = scrobble.master_metadata_album_album_name.?,
- .artists_album = &[_][]const u8{scrobble.master_metadata_album_artist_name.?},
- .track = scrobble.master_metadata_track_name.?,
- .artists_track = &[_][]const u8{scrobble.master_metadata_album_artist_name.?},
- .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,
+ if (scrobble.date > latest_timestamp * 1_000 or scrobble.date < earliest_timestamp * 1_000) {
+ limited_tracks += 1;
+ continue :appends;
+ }
+ const filtered_scrobble = Data.Scrobble{
+ .album = scrobble.album,
+ .artists_album = &[_][]const u8{scrobble.artist},
+ .track = scrobble.track,
+ .artists_track = &[_][]const u8{scrobble.artist},
+ .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);
- var job_param = try job_params.append(.object);
- try job_param.put("album", complete_scrobble.album);
- try job_param.put("track", complete_scrobble.track);
- try job_param.put("date", complete_scrobble.date);
+ try job_params.append(complete_scrobble);
+ }
+ },
+ .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;
- var track_artists_array = try job_param.put("artists_track", .array);
- for (complete_scrobble.artists_track) |a| {
- try track_artists_array.append(a);
+ 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"))) {
+ skipped_tracks += 1;
+ continue :appends;
+ }
+ if (scrobble.master_metadata_album_artist_name == null or scrobble.master_metadata_track_name == null) {
+ skipped_tracks += 1;
+ continue :appends;
}
- var album_artists_array = try job_param.put("artists_album", .array);
- for (complete_scrobble.artists_album) |a| {
- try album_artists_array.append(a);
+ const iso_ts = try zeit.Time.fromISO8601(scrobble.ts);
+ if ((iso_ts.after(latest_date) or iso_ts.before(earliest_date))) {
+ limited_tracks += 1;
+ continue :appends;
}
+
+ const filtered_scrobble = Data.Scrobble{
+ .album = scrobble.master_metadata_album_album_name.?,
+ .artists_album = &[_][]const u8{scrobble.master_metadata_album_artist_name.?},
+ .track = scrobble.master_metadata_track_name.?,
+ .artists_track = &[_][]const u8{scrobble.master_metadata_album_artist_name.?},
+ .date = (try zeit.instant(.{ .source = .{ .iso8601 = scrobble.ts } })).unixTimestamp() * 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);
}
},
}
diff --git a/src/queries.zig b/src/queries.zig
index cc5137b..061b5b3 100644
--- a/src/queries.zig
+++ b/src/queries.zig
@@ -20,30 +20,27 @@ pub fn entityQueryResult(request: *jetzig.Request, query: GeneratedQuery, args:
blk: while (try result.postgresql.result.next()) |entity_row| {
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 item: ?TableRow = switch (query.ResultType) {
EntitiesScrobbleResult, EntitiesSongResult, EntitiesAlbumResult, EntitiesArtistResult => switch (query.entity) {
.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();
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 :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 => TableRow{ .song = .{ .name = entity.name, .id = entity.id }, .artistlist = try artist_list.toOwnedSlice(), .scrobbles = entity.scrobbles },
+ .album => TableRow{ .album = .{ .name = entity.name, .id = entity.id }, .artistlist = try artist_list.toOwnedSlice(), .scrobbles = entity.scrobbles },
+ else => unreachable,
+ };
},
- .song => song_entities: {
- const last_artist = artist_list.getLastOrNull();
- 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,
},
- 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 },
.album => TableRow{ .song = .{ .name = entity.name, .id = entity.id }, .scrobbles = entity.scrobbles },
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 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 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 AppearsResult = struct { name: []const u8, id: i32, scrobbles: i64 };
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 {
return GeneratedQuery{
@@ -110,7 +118,11 @@ pub fn generateQuery(entity: EntityType, query_type: QueryTypeEnum) GeneratedQue
.album => EntitiesAlbumResult,
.artist => EntitiesArtistResult,
},
- .entity_items => EntityItemsResult,
+ .entity_items => switch (entity) {
+ .scrobble => unreachable,
+ .song => EntityItemsSongResult,
+ else => EntityItemsResult,
+ },
.appears => AppearsResult,
.entity_info => EntityInfoResult,
},
@@ -120,29 +132,27 @@ pub fn generateQuery(entity: EntityType, query_type: QueryTypeEnum) GeneratedQue
switch (entity) {
.scrobble => unreachable,
.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
\\INNER JOIN songs ON songs.id = albumsongs.song_id
\\INNER JOIN scrobbles ON albumsongs.id = scrobbles.albumsong
- \\INNER JOIN albumsongsartists ON albumsongsartists.albumsong_id = albumsongs.id
- \\WHERE albumsongsartists.artist_id = $1
+ \\WHERE albumsongs.song_id = $1
\\ORDER BY scrobbles.datetime ASC
\\LIMIT 1)
\\
\\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
\\INNER JOIN songs ON songs.id = albumsongs.song_id
\\INNER JOIN scrobbles ON albumsongs.id = scrobbles.albumsong
- \\INNER JOIN albumsongsartists ON albumsongsartists.albumsong_id = albumsongs.id
- \\WHERE albumsongsartists.artist_id = $1
+ \\WHERE albumsongs.song_id = $1
\\ORDER BY scrobbles.datetime DESC
\\LIMIT 1)
,
.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
\\INNER JOIN songs ON songs.id = albumsongs.song_id
\\INNER JOIN scrobbles ON albumsongs.id = scrobbles.albumsong
@@ -153,7 +163,7 @@ pub fn generateQuery(entity: EntityType, query_type: QueryTypeEnum) GeneratedQue
\\
\\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
\\INNER JOIN songs ON songs.id = albumsongs.song_id
\\INNER JOIN scrobbles ON albumsongs.id = scrobbles.albumsong
@@ -164,7 +174,7 @@ pub fn generateQuery(entity: EntityType, query_type: QueryTypeEnum) GeneratedQue
,
.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
\\INNER JOIN songs ON songs.id = albumsongs.song_id
\\INNER JOIN scrobbles ON albumsongs.id = scrobbles.albumsong
@@ -175,7 +185,7 @@ pub fn generateQuery(entity: EntityType, query_type: QueryTypeEnum) GeneratedQue
\\
\\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
\\INNER JOIN songs ON songs.id = albumsongs.song_id
\\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
\\FROM scrobbles
\\INNER JOIN albumsongs ON albumsongs.id = scrobbles.albumsong
- \\INNER JOIN albumsongsartists ON albumsongsartists.albumsong_id = albumsongs.id
- \\INNER JOIN artists ON artists.id = albumsongsartists.artist_id
- \\WHERE artists.id = $1
+ \\INNER JOIN songs ON songs.id = albumsongs.song_id
+ \\WHERE songs.id = $1
\\GROUP BY year
\\ORDER BY year ASC;
,
@@ -227,7 +236,14 @@ pub fn generateQuery(entity: EntityType, query_type: QueryTypeEnum) GeneratedQue
//.ResultType = EntitiesResult,
switch (entity) {
.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 =>
\\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) {
.scrobble => unreachable,
.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 =>
\\nope
,
.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
\\INNER JOIN albums ON albums.id = artistalbums.album_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) {
.scrobble => unreachable,
.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 =>
\\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) {
.scrobble => unreachable,
.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 =>
\\SELECT * 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
\\INNER JOIN albums ON albumsongs.album_id = albums.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
,
},
+
+ .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,
+ },
},
};
}