From 15e72ea3262de56b2e15823a354a979b3d649637 Mon Sep 17 00:00:00 2001 From: mitteneer Date: Thu, 3 Jul 2025 12:23:57 -0400 Subject: [PATCH] Revert entity name in url redirect in all cases and create disambiguation pages for all entities Useless for artists right now --- src/app/views/albums.zig | 53 ++++++++++++++++------------------ src/app/views/albums/get.zmpl | 11 ++++++- src/app/views/artists.zig | 53 ++++++++++++++++------------------ src/app/views/artists/get.zmpl | 7 +++++ src/app/views/songs.zig | 6 ++-- src/queries.zig | 20 +++++++++++++ 6 files changed, 90 insertions(+), 60 deletions(-) diff --git a/src/app/views/albums.zig b/src/app/views/albums.zig index 55db929..5f3e0e4 100644 --- a/src/app/views/albums.zig +++ b/src/app/views/albums.zig @@ -16,46 +16,43 @@ pub fn index(request: *jetzig.Request) !jetzig.View { } pub fn get(id: []const u8, request: *jetzig.Request) !jetzig.View { - const parse_err = blk: { - const rdr_id = std.fmt.parseInt(i64, id, 10) catch |err| break :blk err; - const album = try jetzig.database.Query(.Album).find(rdr_id).execute(request.repo); - if (album == null) break :blk error.InvalidCharacter; - var name = std.ArrayList(u8).init(request.allocator); - try name.appendSlice("http://127.0.0.1:8080/albums/"); - try name.appendSlice(album.?.name); - return request.redirect(try name.toOwnedSlice(), .found); - }; - - const id_int = switch (parse_err) { - error.Overflow => return request.fail(.not_found), - error.InvalidCharacter => blk: { - const rn = try decode(request.allocator, id); - std.log.debug("{s}", .{rn}); - const songs = try jetzig.database.Query(.Album).where(.{ .name = rn }).all(request.repo); - - if (songs.len == 0) return request.fail(.not_found); - if (songs.len > 1) return request.redirect("http://127.0.0.1:8080", .found); - break :blk songs[0].id; - }, - }; var root = try request.data(.object); - const album = try queries.entityQueryResult(request, queries.loadQuery(.album, .entity_info), .{id_int}); + const id_int = blk: { + const rn = try decode(request.allocator, id); + // Try to find the song by name + const queried_albums = try jetzig.database.Query(.Album).select(.{.id}).where(.{ .name = rn }).all(request.repo); + if (queried_albums.len == 0) { + // Either we've been given an id in the db, or the song doesn't exist + break :blk std.fmt.parseInt(i64, id, 10) catch return request.fail(.not_found); + } else if (queried_albums.len == 1) { + // It can only be one song + break :blk queried_albums[0].id; + } else { + // It could be a variety of songs + const albums = try queries.entityQueryResult(request, comptime queries.loadQuery(.album, .entities_by_name), .{rn}); + try root.put("name", rn); + try root.put("albums", albums); + try root.put("disambiguation", true); + return request.render(.ok); + } + }; + const album = try queries.entityQueryResult(request, comptime queries.loadQuery(.album, .entity_info), .{id_int}); try root.put("album", album); - const scrobbles = try queries.entityQueryResult(request, queries.loadQuery(.song, .get_scrobbles), .{id_int}); + const scrobbles = try queries.entityQueryResult(request, comptime queries.loadQuery(.song, .get_scrobbles), .{id_int}); try root.put("scrobbles", scrobbles); - const songs = try queries.entityQueryResult(request, queries.loadQuery(.album, .get_songs), .{id_int}); + const songs = try queries.entityQueryResult(request, comptime queries.loadQuery(.album, .get_songs), .{id_int}); try root.put("songs", songs); - const firstlast = try queries.entityQueryResult(request, queries.loadQuery(.album, .firstlast), .{id_int}); + const firstlast = try queries.entityQueryResult(request, comptime queries.loadQuery(.album, .firstlast), .{id_int}); try root.put("firstlast", firstlast); - const timescale = try queries.entityQueryResult(request, queries.loadQuery(.album, .timescale), .{id_int}); + const timescale = try queries.entityQueryResult(request, comptime queries.loadQuery(.album, .timescale), .{id_int}); try root.put("yearly", timescale); - const ratings = try queries.entityQueryResult(request, queries.loadQuery(.song, .get_ratings), .{id_int}); + const ratings = try queries.entityQueryResult(request, comptime queries.loadQuery(.song, .get_ratings), .{id_int}); try root.put("reviews", ratings); const peak = try queries.entityQueryResult(request, comptime queries.loadQuery(.album, .peak), .{id_int}); diff --git a/src/app/views/albums/get.zmpl b/src/app/views/albums/get.zmpl index e4e2196..d62af1c 100644 --- a/src/app/views/albums/get.zmpl +++ b/src/app/views/albums/get.zmpl @@ -1,7 +1,7 @@ @zig { const ColumnChoices = []const enum{song, album, artist, artistlist, scrobbles, date}; const columns: ColumnChoices = &.{.song, .scrobbles}; - const reviews = try zmpl.coerceArray(".reviews"); + const dis_columns: ColumnChoices = &.{.album, .artistlist, .scrobbles}; } @@ -11,6 +11,14 @@ @partial partials/header +@if ($.disambiguation) +

{{.name}} (disambiguation)

+@partial partials/newtable(T: ColumnChoices, table_data: .albums, columns: dis_columns) +@else + +@zig { + const reviews = try zmpl.coerceArray(".reviews"); +}

{{.album.album_name}}

{{.album.artist_name}}

@@ -49,5 +57,6 @@ }
+@end \ No newline at end of file diff --git a/src/app/views/artists.zig b/src/app/views/artists.zig index e5f13d5..540ccd3 100644 --- a/src/app/views/artists.zig +++ b/src/app/views/artists.zig @@ -17,44 +17,41 @@ pub fn index(request: *jetzig.Request) !jetzig.View { } pub fn get(id: []const u8, request: *jetzig.Request) !jetzig.View { - const parse_err = blk: { - const rdr_id = std.fmt.parseInt(i64, id, 10) catch |err| break :blk err; - const artist = try jetzig.database.Query(.Artist).find(rdr_id).execute(request.repo); - if (artist == null) break :blk error.InvalidCharacter; - var name = std.ArrayList(u8).init(request.allocator); - try name.appendSlice("http://127.0.0.1:8080/artists/"); - try name.appendSlice(artist.?.name); - return request.redirect(try name.toOwnedSlice(), .found); - }; - - const id_int = switch (parse_err) { - error.Overflow => return request.fail(.not_found), - error.InvalidCharacter => blk: { - const rn = try decode(request.allocator, id); - std.log.debug("{s}", .{rn}); - const artists = try jetzig.database.Query(.Artist).where(.{ .name = rn }).all(request.repo); - - if (artists.len == 0) return request.fail(.not_found); - if (artists.len > 1) return request.redirect("http://127.0.0.1:8080", .found); - break :blk artists[0].id; - }, - }; - var root = try request.data(.object); - const artist = try queries.entityQueryResult(request, queries.loadQuery(.artist, .entity_info), .{id_int}); + const id_int = blk: { + const rn = try decode(request.allocator, id); + // Try to find the song by name + const queried_artists = try jetzig.database.Query(.Artist).select(.{.id}).where(.{ .name = rn }).all(request.repo); + if (queried_artists.len == 0) { + // Either we've been given an id in the db, or the song doesn't exist + break :blk std.fmt.parseInt(i64, id, 10) catch return request.fail(.not_found); + } else if (queried_artists.len == 1) { + // It can only be one song + break :blk queried_artists[0].id; + } else { + // It could be a variety of songs + const artists = try queries.entityQueryResult(request, comptime queries.loadQuery(.artist, .entities_by_name), .{rn}); + try root.put("name", rn); + try root.put("artists", artists); + try root.put("disambiguation", true); + return request.render(.ok); + } + }; + + const artist = try queries.entityQueryResult(request, comptime queries.loadQuery(.artist, .entity_info), .{id_int}); try root.put("artist", artist); - const albums = try queries.entityQueryResult(request, queries.loadQuery(.artist, .get_albums), .{id_int}); + const albums = try queries.entityQueryResult(request, comptime queries.loadQuery(.artist, .get_albums), .{id_int}); try root.put("albums", albums); - const appears = try queries.entityQueryResult(request, queries.loadQuery(.artist, .appears), .{id_int}); + const appears = try queries.entityQueryResult(request, comptime queries.loadQuery(.artist, .appears), .{id_int}); try root.put("appears", appears); - const firstlast = try queries.entityQueryResult(request, queries.loadQuery(.artist, .firstlast), .{id_int}); + const firstlast = try queries.entityQueryResult(request, comptime queries.loadQuery(.artist, .firstlast), .{id_int}); try root.put("firstlast", firstlast); - const timescale = try queries.entityQueryResult(request, queries.loadQuery(.artist, .timescale), .{id_int}); + const timescale = try queries.entityQueryResult(request, comptime queries.loadQuery(.artist, .timescale), .{id_int}); try root.put("yearly", timescale); const peak = try queries.entityQueryResult(request, comptime queries.loadQuery(.artist, .peak), .{id_int}); diff --git a/src/app/views/artists/get.zmpl b/src/app/views/artists/get.zmpl index 9ae4ad3..37a5379 100644 --- a/src/app/views/artists/get.zmpl +++ b/src/app/views/artists/get.zmpl @@ -1,6 +1,7 @@ @zig { const ColumnChoices = []const enum{song, album, artist, artistlist, scrobbles, date}; const columns: ColumnChoices = &.{.album, .scrobbles}; + const dis_columns: ColumnChoices = &.{.artist, .scrobbles}; } @@ -9,6 +10,11 @@ @partial partials/header +@if ($.disambiguation) +

{{.name}} (disambiguation)

+@partial partials/newtable(T: ColumnChoices, table_data: .artists, columns: dis_columns) +@else +

{{.artist.artist_name}}

@if ($.artist.is_tie) @@ -29,5 +35,6 @@

Albums Featured On

@partial partials/newtable(T: ColumnChoices, table_data: .appears, columns: columns) +@end \ No newline at end of file diff --git a/src/app/views/songs.zig b/src/app/views/songs.zig index da69feb..f3d4bf5 100644 --- a/src/app/views/songs.zig +++ b/src/app/views/songs.zig @@ -11,9 +11,9 @@ pub fn index(request: *jetzig.Request) !jetzig.View { try root.put("htmx", htmx_query != null); const songs = if (htmx_query) |name| - try queries.entityQueryResult(request, queries.loadQuery(.song, .entities_by_name), .{name}) + try queries.entityQueryResult(request, comptime queries.loadQuery(.song, .entities_by_name), .{name}) else - try queries.entityQueryResult(request, queries.loadQuery(.song, .entities), .{}); + try queries.entityQueryResult(request, comptime queries.loadQuery(.song, .entities), .{}); try root.put("songs", songs); @@ -34,7 +34,7 @@ pub fn get(id: []const u8, request: *jetzig.Request) !jetzig.View { break :blk queried_songs[0].id; } else { // It could be a variety of songs - const songs = try queries.entityQueryResult(request, queries.loadQuery(.song, .entities_by_name), .{rn}); + const songs = try queries.entityQueryResult(request, comptime queries.loadQuery(.song, .entities_by_name), .{rn}); try root.put("name", rn); try root.put("songs", songs); try root.put("disambiguation", true); diff --git a/src/queries.zig b/src/queries.zig index 778e64b..371c15e 100644 --- a/src/queries.zig +++ b/src/queries.zig @@ -487,6 +487,26 @@ pub fn loadQuery(comptime entity: EntityType, comptime query_type: QueryTypeEnum \\GROUP BY songs.id, albums.id, artists.id \\ORDER BY songs.name ASC, scrobbles DESC; , + .album => + \\SELECT albums.name AS album_name, albums.id AS album_id, artists.name AS artist_name, artists.id AS artist_id, COUNT(scrobbles) AS scrobbles + \\FROM albumsongs + \\INNER JOIN albums ON albumsongs.album_id = albums.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 + \\WHERE LOWER(albums.name) LIKE LOWER($1) + \\GROUP BY albums.id, artists.id + \\ORDER BY albums.name ASC, scrobbles DESC; + , + .artist => + \\SELECT artists.name AS artist_name, artists.id AS artist_id, COUNT(scrobbles) AS scrobbles + \\FROM scrobbles + \\INNER JOIN albumsongsartists ON albumsongsartists.albumsong_id = scrobbles.albumsong + \\INNER JOIN artists ON artists.id = albumsongsartists.artist_id + \\WHERE LOWER(artists.name) LIKE LOWER($1) + \\GROUP BY artists.id + \\ORDER BY artists.name ASC, scrobbles DESC; + , else => unreachable, }, .get_ratings => switch (entity) {