From 7256bccc363de06ced11ce043849db6c5a65cfff Mon Sep 17 00:00:00 2001 From: mitteneer Date: Fri, 28 Mar 2025 20:38:50 -0400 Subject: [PATCH] Work on albums view Lots of unanswered questions, but significantly faster. Will probably need to take another look at the database and see if there's something more/different I can do, but I'm liking where this is going. Really need to figure out how the scrobbles view is going to work, and albums view is currently not entirely correct. --- src/app/database/Schema.zig | 36 +++--------- .../2025-02-17_22-38-46_create_scrobbles.zig | 6 +- ...-02-21_14-24-31_create_scrobbleartists.zig | 20 ------- src/app/jobs/process_scrobbles.zig | 42 ++++++++------ src/app/views/albums.zig | 43 +++++++------- src/app/views/artists.zig | 26 +++++---- src/app/views/scrobbles.zig | 57 ++++++++++--------- src/app/views/songs.zig | 34 ++++++----- 8 files changed, 117 insertions(+), 147 deletions(-) delete mode 100644 src/app/database/migrations/2025-02-21_14-24-31_create_scrobbleartists.zig diff --git a/src/app/database/Schema.zig b/src/app/database/Schema.zig index 9ce8acd..520ca59 100644 --- a/src/app/database/Schema.zig +++ b/src/app/database/Schema.zig @@ -113,20 +113,18 @@ pub const Scrobble = jetquery.Model( "scrobbles", struct { id: i32, - albumartists_id: i32, - songsartists_id: i32, - albumsongs_id: i32, + albumartist_id: i32, + songartist_id: i32, + albumsong_id: i32, date: jetquery.DateTime, created_at: jetquery.DateTime, updated_at: jetquery.DateTime, }, - .{ - .relations = .{ - .song = jetquery.belongsTo(.Song, .{}), - .album = jetquery.belongsTo(.Album, .{}), - //.scrobbleartists = jetquery.hasMany(.Scrobbleartist, .{}), - }, - }, + .{ .relations = .{ + .albumartist = jetquery.belongsTo(.Albumartist, .{}), + .albumsong = jetquery.belongsTo(.Albumsong, .{}), + .songartist = jetquery.belongsTo(.Songartist, .{}), + } }, ); pub const Song = jetquery.Model( @@ -207,21 +205,3 @@ pub const Albumsong = jetquery.Model( }, }, ); - -pub const Scrobbleartist = jetquery.Model( - @This(), - "Scrobbleartists", - struct { - id: i32, - scrobble_id: i32, - artist_id: i32, - created_at: jetquery.DateTime, - updated_at: jetquery.DateTime, - }, - .{ - .relations = .{ - .scrobble = jetquery.belongsTo(.Scrobble, .{}), - .artist = jetquery.belongsTo(.Artist, .{}), - }, - }, -); diff --git a/src/app/database/migrations/2025-02-17_22-38-46_create_scrobbles.zig b/src/app/database/migrations/2025-02-17_22-38-46_create_scrobbles.zig index 5ee8a08..088984e 100644 --- a/src/app/database/migrations/2025-02-17_22-38-46_create_scrobbles.zig +++ b/src/app/database/migrations/2025-02-17_22-38-46_create_scrobbles.zig @@ -7,9 +7,9 @@ pub fn up(repo: anytype) !void { "scrobbles", &.{ t.primaryKey("id", .{}), - t.column("albumartists_id", .integer, .{}), - t.column("songsartists_id", .integer, .{}), - t.column("albumsongs_id", .integer, .{}), + t.column("albumartist_id", .integer, .{}), + t.column("songartist_id", .integer, .{}), + t.column("albumsong_id", .integer, .{}), t.column("date", .datetime, .{}), t.timestamps(.{}), }, diff --git a/src/app/database/migrations/2025-02-21_14-24-31_create_scrobbleartists.zig b/src/app/database/migrations/2025-02-21_14-24-31_create_scrobbleartists.zig deleted file mode 100644 index 2125a87..0000000 --- a/src/app/database/migrations/2025-02-21_14-24-31_create_scrobbleartists.zig +++ /dev/null @@ -1,20 +0,0 @@ -const std = @import("std"); -const jetquery = @import("jetquery"); -const t = jetquery.schema.table; - -pub fn up(repo: anytype) !void { - try repo.createTable( - "Scrobbleartists", - &.{ - t.primaryKey("id", .{}), - t.column("scrobble_id", .integer, .{}), - t.column("artist_id", .integer, .{}), - t.timestamps(.{}), - }, - .{}, - ); -} - -pub fn down(repo: anytype) !void { - try repo.dropTable("Scrobbleartists", .{}); -} diff --git a/src/app/jobs/process_scrobbles.zig b/src/app/jobs/process_scrobbles.zig index 6305696..2c7cd77 100644 --- a/src/app/jobs/process_scrobbles.zig +++ b/src/app/jobs/process_scrobbles.zig @@ -77,37 +77,45 @@ pub fn run(allocator: std.mem.Allocator, params: *jetzig.data.Value, env: jetzig // There are very few situations where artist_check is null // but song_check/album is not. Also yes, the order of these // checks is weird, I didn't put a lot of thought into it - var associative_table_flags: [3]bool = [3]bool{ true, true, true }; - var associative_table_ids: [3][]const u8 = [3][]const u8{ null, null, null }; + //var associative_table_flags = [3]bool{ true, true, true }; + var associative_table_ids = [3]?i32{ null, null, null }; if (album_check == null) { try env.repo.execute(album_insert); - associative_table_ids[0] = try jetzig.database.Query(.Albumartist).insert(.{ .album_id = album_id, .artist_id = artist_id }).returning(.{.id}).execute(env.repo); - associative_table_flags[0] = false; - associative_table_ids[1] = try jetzig.database.Query(.Albumsong).insert(.{ .album_id = album_id, .song_id = song_id }).returning(.{.id}).execute(env.repo); - associative_table_flags[1] = false; + const albart = try jetzig.database.Query(.Albumartist).insert(.{ .album_id = album_id, .artist_id = artist_id }).returning(.{.id}).execute(env.repo); + const albson = try jetzig.database.Query(.Albumsong).insert(.{ .album_id = album_id, .song_id = song_id }).returning(.{.id}).execute(env.repo); + if (associative_table_ids[0] == null) associative_table_ids[0] = albart.?.id; + if (associative_table_ids[1] == null) associative_table_ids[1] = albson.?.id; } if (artist_check == null) { try env.repo.execute(artist_insert); - if (associative_table_flags[0]) associative_table_ids[0] = try jetzig.database.Query(.Albumartist).insert(.{ .album_id = album_id, .artist_id = artist_id }).returning(.{.id}).execute(env.repo); - associative_table_ids[2] = try jetzig.database.Query(.Songartist).insert(.{ .song_id = song_id, .artist_id = artist_id }).returning(.{.id}).execute(env.repo); - associative_table_flags[2] = false; + const albart = try jetzig.database.Query(.Albumartist).insert(.{ .album_id = album_id, .artist_id = artist_id }).returning(.{.id}).execute(env.repo); + const sonart = try jetzig.database.Query(.Songartist).insert(.{ .song_id = song_id, .artist_id = artist_id }).returning(.{.id}).execute(env.repo); + if (associative_table_ids[0] == null) associative_table_ids[0] = albart.?.id; + if (associative_table_ids[2] == null) associative_table_ids[2] = sonart.?.id; } if (song_check == null) { try env.repo.execute(song_insert); - if (associative_table_flags[1]) associative_table_ids[1] = try jetzig.database.Query(.Albumsong).insert(.{ .album_id = album_id, .song_id = song_id }).returning(.{.id}).execute(env.repo); - if (associative_table_flags[2]) associative_table_ids[2] = try jetzig.database.Query(.Songartist).insert(.{ .song_id = song_id, .artist_id = artist_id }).returning(.{.id}).execute(env.repo); + const albson = try jetzig.database.Query(.Albumsong).insert(.{ .album_id = album_id, .song_id = song_id }).returning(.{.id}).execute(env.repo); + const sonart = try jetzig.database.Query(.Songartist).insert(.{ .song_id = song_id, .artist_id = artist_id }).returning(.{.id}).execute(env.repo); + if (associative_table_ids[1] == null) associative_table_ids[1] = albson.?.id; + if (associative_table_ids[2] == null) associative_table_ids[2] = sonart.?.id; } + //if (associative_table_flags[1]) associative_table_ids[1] = (try jetzig.database.Query(.Albumsong).insert(.{ .album_id = album_id, .song_id = song_id }).returning(.{.id}).execute(env.repo)).?.id; + //if (associative_table_flags[2]) associative_table_ids[2] = (try jetzig.database.Query(.Songartist).insert(.{ .song_id = song_id, .artist_id = artist_id }).returning(.{.id}).execute(env.repo)).?.id; + if (associative_table_ids[0] == null) associative_table_ids[0] = (try jetzig.database.Query(.Albumartist).findBy(.{ .album_id = album_id, .artist_id = artist_id }).execute(env.repo)).?.id; + if (associative_table_ids[1] == null) associative_table_ids[1] = (try jetzig.database.Query(.Albumsong).findBy(.{ .album_id = album_id, .song_id = song_id }).execute(env.repo)).?.id; + if (associative_table_ids[2] == null) associative_table_ids[2] = (try jetzig.database.Query(.Songartist).findBy(.{ .song_id = song_id, .artist_id = artist_id }).execute(env.repo)).?.id; - defer { - for (0..3) |i| { - if (associative_table_ids[i]) env.repo.free(associative_table_ids[i]); - } - } + //defer { + // for (0..3) |i| { + // if (associative_table_ids[i]) |id| env.repo.free(id); + // } + //} - try jetzig.database.Query(.Scrobble).insert(.{ .albumartists_id = associative_table_ids[0], .albumsong_id = associative_table_ids[1], .songartists_id = associative_table_ids[2], .date = scrobble.date }).execute(env.repo); + try jetzig.database.Query(.Scrobble).insert(.{ .albumartist_id = associative_table_ids[0].?, .albumsong_id = associative_table_ids[1].?, .songartist_id = associative_table_ids[2].?, .date = scrobble.date }).execute(env.repo); //defer env.repo.free(scr_id); //try jetzig.database.Query(.Scrobbleartist).insert(.{ .scrobble_id = scr_id.?.id, .artist_id = artist_id }).execute(env.repo); } diff --git a/src/app/views/albums.zig b/src/app/views/albums.zig index 634f414..8a2f15c 100644 --- a/src/app/views/albums.zig +++ b/src/app/views/albums.zig @@ -5,30 +5,28 @@ const jetquery = @import("jetzig").jetquery; pub fn index(request: *jetzig.Request) !jetzig.View { var root = try request.data(.object); var albums_view = try root.put("albums", .array); - const albums = try jetzig.database.Query(.Album) - .select(.{ .id, .name }) - .include(.albumartists, .{ .select = .{.artist_id} }) + const albums = try jetzig.database.Query(.Albumartist) + .select(.{.id}) + .include(.album, .{ .select = .{ .id, .name } }) + .include(.artist, .{ .select = .{ .id, .name } }) .include(.scrobbles, .{ .select = .{.id} }) - .orderBy(.{ .name = .asc }) + //.groupBy(.{ .album = .{.id} }) + .orderBy(.{ .album = .{ .name = .asc } }) .all(request.repo); - //const albums = try request.repo.all(query); for (albums) |album| { + //var buf: [11]u8 = undefined; + //const id_string: []const u8 = try std.fmt.bufPrint(&buf, "{}", .{album.album.id}); var album_view = try albums_view.append(.object); - var artist_infos = try album_view.put("artist_info", .array); - for (album.albumartists) |artist| { - var artist_info = try artist_infos.append(.object); - const artist_data = try jetzig.database.Query(.Artist) - .find(artist.artist_id) - .select(.{ .id, .name }) - .execute(request.repo); - try artist_info.put("name", artist_data.?.name); - try artist_info.put("id", artist_data.?.id); - } + // TODO: Come back to this when multiple artists are supported + var artist_infos = album_view.put("artist_info", .array) catch continue; + var artist_info = try artist_infos.append(.object); + try artist_info.put("name", album.artist.name); + try artist_info.put("id", album.artist.id); - try album_view.put("name", album.name); - try album_view.put("url", album.id); + try album_view.put("name", album.album.name); + try album_view.put("url", album.album.id); try album_view.put("scrobbles", (album.scrobbles).len); } return request.render(.ok); @@ -45,19 +43,20 @@ pub fn get(id: []const u8, request: *jetzig.Request) !jetzig.View { const query = jetzig.database.Query(.Albumsong) .select(.{.id}) .include(.song, .{ .select = .{ .name, .id } }) + .include(.scrobbles, .{ .select = .{.id} }) .join(.inner, .album) .where(.{ .album = .{ .id = id } }); const songs = try request.repo.all(query); for (songs) |song| { - const scrobbles = try jetzig.database.Query(.Scrobble) - .where(.{ .song_id = song.song.id }) - .count() - .execute(request.repo); + //const scrobbles = try jetzig.database.Query(.Scrobble) + // .where(.{ .song_id = song.song.id }) + // .count() + // .execute(request.repo); var song_view = try songs_view.append(.object); try song_view.put("name", song.song.name); try song_view.put("url", song.song.id); - try song_view.put("scrobbles", scrobbles); + try song_view.put("scrobbles", (song.scrobbles).len); } return request.render(.ok); } diff --git a/src/app/views/artists.zig b/src/app/views/artists.zig index 78058a7..487a510 100644 --- a/src/app/views/artists.zig +++ b/src/app/views/artists.zig @@ -5,16 +5,17 @@ const jetquery = @import("jetzig").jetquery; pub fn index(request: *jetzig.Request) !jetzig.View { var root = try request.data(.object); var artists_view = try root.put("artists", .array); - const artists = try jetzig.database.Query(.Artist) - .select(.{ .id, .name }) - .include(.scrobbleartists, .{ .select = .{.id} }) - .orderBy(.{ .name = .asc }) + const artists = try jetzig.database.Query(.Songartist) + //.include(.song, .{ .select = .{ .id, .name } }) + .include(.artist, .{ .select = .{ .id, .name } }) + .include(.scrobbles, .{ .select = .{.id} }) + .orderBy(.{ .artist = .{ .name = .asc } }) .all(request.repo); for (artists) |artist| { var artist_view = try artists_view.append(.object); - try artist_view.put("name", artist.name); - try artist_view.put("url", artist.id); - try artist_view.put("scrobbles", (artist.scrobbleartists).len); + try artist_view.put("name", artist.artist.name); + try artist_view.put("url", artist.artist.id); + try artist_view.put("scrobbles", (artist.scrobbles).len); } return request.render(.ok); @@ -31,19 +32,20 @@ pub fn get(id: []const u8, request: *jetzig.Request) !jetzig.View { const query = jetzig.database.Query(.Albumartist) .select(.{.id}) .include(.album, .{ .select = .{ .name, .id } }) + .include(.scrobbles, .{ .select = .{.id} }) .join(.inner, .artist) .where(.{ .artist = .{ .id = id } }); const albums = try request.repo.all(query); for (albums) |album| { - const scrobbles = try jetzig.database.Query(.Scrobble) - .where(.{ .album_id = album.album.id }) - .count() - .execute(request.repo); + //const scrobbles = try jetzig.database.Query(.Scrobble) + // .where(.{ .album_id = album.album.id }) + // .count() + // .execute(request.repo); var album_view = try albums_view.append(.object); try album_view.put("name", album.album.name); try album_view.put("url", album.album.id); - try album_view.put("scrobbles", scrobbles); + try album_view.put("scrobbles", (album.scrobbles).len); } return request.render(.ok); } diff --git a/src/app/views/scrobbles.zig b/src/app/views/scrobbles.zig index ebda828..0a3ff07 100644 --- a/src/app/views/scrobbles.zig +++ b/src/app/views/scrobbles.zig @@ -2,35 +2,38 @@ const std = @import("std"); const jetzig = @import("jetzig"); pub fn index(request: *jetzig.Request) !jetzig.View { - var root = try request.data(.object); - var scrobbles_view = try root.put("scrobbles", .array); - const query = jetzig.database.Query(.Scrobble) - .select(.{ .id, .date }) - .include(.song, .{ .select = .{ .id, .name } }) - .include(.album, .{ .select = .{ .id, .name } }) - .include(.scrobbleartists, .{ .select = .{.artist_id} }) - .orderBy(.{ .date = .desc }); - const scrobbles = try request.repo.all(query); - for (scrobbles) |scrobble| { - var scrobble_view = try scrobbles_view.append(.object); + //var root = try request.data(.object); + //var scrobbles_view = try root.put("scrobbles", .array); + //const scrobbles = try jetzig.database.Query(.Songartist) + // .select(.{.id}) + // .include(.song, .{ .select = .{ .id, .name } }) + // .include(.artist, .{ .select = .{ .id, .name } }) + // .include(.scrobbles, .{ .select = .{ .id, .date } }) + // .groupBy(.{ .song_id, .artist_id }) + // .orderBy(.{ .scrobbles = .{ .date = .desc } }) + // .all(request.repo); + //for (scrobbles) |scrobble| { + // var buf: [11]u8 = undefined; + // const id_string:[]const u8 = try std.fmt.bufPrint(&buf, "{}", .{scrobble.}) + // var scrobble_view = try scrobbles_view.append(.object); - var artist_infos = try scrobble_view.put("artist_info", .array); - for (scrobble.scrobbleartists) |artist| { - var artist_info = try artist_infos.append(.object); - const artist_data = try jetzig.database.Query(.Artist) - .find(artist.artist_id) - .select(.{ .id, .name }) - .execute(request.repo); - try artist_info.put("name", artist_data.?.name); - try artist_info.put("id", artist_data.?.id); - } + // var artist_infos = try scrobble_view.put("artist_info", .array); + // for (scrobble.scrobbleartists) |artist| { + // var artist_info = try artist_infos.append(.object); + // const artist_data = try jetzig.database.Query(.Artist) + // .find(artist.artist_id) + // .select(.{ .id, .name }) + // .execute(request.repo); + // try artist_info.put("name", artist_data.?.name); + // try artist_info.put("id", artist_data.?.id); + // } - try scrobble_view.put("song_name", scrobble.song.name); - try scrobble_view.put("song_id", scrobble.song.id); - try scrobble_view.put("album_name", scrobble.album.name); - try scrobble_view.put("album_id", scrobble.album.id); - try scrobble_view.put("date", scrobble.date); - } + // try scrobble_view.put("song_name", scrobble.song.name); + // try scrobble_view.put("song_id", scrobble.song.id); + // try scrobble_view.put("album_name", scrobble.album.name); + // try scrobble_view.put("album_id", scrobble.album.id); + // try scrobble_view.put("date", scrobble.date); + //} return request.render(.ok); } diff --git a/src/app/views/songs.zig b/src/app/views/songs.zig index 6c134b1..5f2a997 100644 --- a/src/app/views/songs.zig +++ b/src/app/views/songs.zig @@ -3,29 +3,27 @@ const jetzig = @import("jetzig"); pub fn index(request: *jetzig.Request) !jetzig.View { var root = try request.data(.object); - var songs_view = try root.put("songs", .array); - const songs = try jetzig.database.Query(.Song) - .select(.{ .id, .name }) - .include(.songartists, .{ .select = .{.artist_id} }) + //var songs_view = try root.put("songs", .array); + const songs = try jetzig.database.Query(.Songartist) + .select(.{.id}) + .include(.song, .{ .select = .{ .id, .name } }) + .include(.artist, .{ .select = .{ .id, .name } }) .include(.scrobbles, .{ .select = .{.id} }) - .orderBy(.{ .name = .asc }) + .orderBy(.{ .song = .{ .name = .asc } }) .all(request.repo); for (songs) |song| { - var song_view = try songs_view.append(.object); + var buf: [11]u8 = undefined; + const id_string: []const u8 = try std.fmt.bufPrint(&buf, "{}", .{song.song.id}); + var song_view = try root.put(id_string, .object); - var artist_infos = try song_view.put("artist_info", .array); - for (song.songartists) |artist| { - var artist_info = try artist_infos.append(.object); - const artist_data = try jetzig.database.Query(.Artist) - .find(artist.artist_id) - .select(.{ .id, .name }) - .execute(request.repo); - try artist_info.put("name", artist_data.?.name); - try artist_info.put("id", artist_data.?.id); - } - try song_view.put("name", song.name); - try song_view.put("url", song.id); + var artist_infos = song_view.put("artist_info", .array) catch continue; + var artist_info = try artist_infos.append(.object); + try artist_info.put("name", song.artist.name); + try artist_info.put("id", song.artist.id); + + try song_view.put("name", song.song.name); + try song_view.put("url", song.song.id); try song_view.put("scrobbles", (song.scrobbles).len); } return request.render(.ok);