From a3a5e3722554415fefd9a49037d0891fc5c1c0e7 Mon Sep 17 00:00:00 2001 From: Samuel Webb Date: Sun, 7 Apr 2024 13:58:52 -0400 Subject: [PATCH 01/27] Add sqlite/update jetzig --- build.zig | 12 +++++++++++- build.zig.zon | 8 ++++++-- src/app/views/root/_content.zmpl | 4 +++- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/build.zig b/build.zig index d80454e..e488390 100644 --- a/build.zig +++ b/build.zig @@ -15,10 +15,20 @@ pub fn build(b: *std.Build) !void { // Example dependency: const iguanas_dep = b.dependency("iguanas", .{ .optimize = optimize, .target = target }); exe.root_module.addImport("iguanas", iguanas_dep.module("iguanas")); + + const sqlite = b.dependency("sqlite", .{ + .target = target, + .optimize = optimize, + }); + + exe.root_module.addImport("sqlite", sqlite.module("sqlite")); + + // links the bundled sqlite3, so leave this out if you link the system one + exe.linkLibrary(sqlite.artifact("sqlite")); // All dependencies **must** be added to imports above this line. - try jetzig.jetzigInit(b, exe, .{}); + try jetzig.jetzigInit(b, exe, .{.zmpl_version = .v2}); b.installArtifact(exe); diff --git a/build.zig.zon b/build.zig.zon index e14a6d7..bb50052 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -16,13 +16,17 @@ // internet connectivity. .dependencies = .{ .jetzig = .{ - .url = "https://github.com/jetzig-framework/jetzig/archive/31e8ae36e1d7f68166114eefb867c23df352d537.tar.gz", - .hash = "1220d368ce21549ccd721d5f303888986fff5714835b1f266a62e329f85eabec8f19", + .url = "https://github.com/jetzig-framework/jetzig/archive/68ca4d3bbad05880e6e38fc6d10e7a24e772081e.tar.gz", + .hash = "122047ebabab1dfe1bb7f96ffdac98410ffb4850b06dc6b1670ce74aaeaa49bdc08e", }, .iguanas = .{ .url = "https://github.com/jetzig-framework/iguanas/archive/89c2abf29de0bc31054a9a6feac5a6a83bab0459.tar.gz", .hash = "12202fd319a5ab4e124b00e8ddea474d07c19c4e005d77b6c29fc44860904ea01a5c", }, + .sqlite = .{ + .url = "https://github.com/vrischmann/zig-sqlite/archive/a3095ac.tar.gz", + .hash = "12203f7d14722debe8f1a2168f28a262aaa5ec2457a7a975fc7dcc1123ed11597c98", + } }, .paths = .{ // This makes *all* files, recursively, included in this package. It is generally diff --git a/src/app/views/root/_content.zmpl b/src/app/views/root/_content.zmpl index 6d3a665..39bf503 100644 --- a/src/app/views/root/_content.zmpl +++ b/src/app/views/root/_content.zmpl @@ -13,6 +13,8 @@
Visit jetzig.dev to get started.
Join our Discord server and introduce yourself:
- https://discord.gg/eufqssz7X6 + Search for an artist, album, or song: + +
From fee65c4b9d510a085be8ca153cfe989f19c4d7de Mon Sep 17 00:00:00 2001 From: Samuel Webb Date: Sun, 7 Apr 2024 15:41:57 -0400 Subject: [PATCH 02/27] Begin SQL testing --- build.zig.zon | 4 ++-- src/db.zig | 0 src/main.zig | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 src/db.zig diff --git a/build.zig.zon b/build.zig.zon index bb50052..578092d 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -24,8 +24,8 @@ .hash = "12202fd319a5ab4e124b00e8ddea474d07c19c4e005d77b6c29fc44860904ea01a5c", }, .sqlite = .{ - .url = "https://github.com/vrischmann/zig-sqlite/archive/a3095ac.tar.gz", - .hash = "12203f7d14722debe8f1a2168f28a262aaa5ec2457a7a975fc7dcc1123ed11597c98", + .url = "https://github.com/vrischmann/zig-sqlite/archive/6af21bb.tar.gz", + .hash = "12202799861d92c7880d1a74f95be099752d7f0420a0c9628dc923ca2b1a22b7bda8", } }, .paths = .{ diff --git a/src/db.zig b/src/db.zig new file mode 100644 index 0000000..e69de29 diff --git a/src/main.zig b/src/main.zig index c7438a3..922472f 100644 --- a/src/main.zig +++ b/src/main.zig @@ -4,6 +4,8 @@ pub const jetzig = @import("jetzig"); pub const routes = @import("routes"); +pub const sqlite = @import("sqlite"); + // Override default settings in `jetzig.config` here: pub const jetzig_options = struct { /// Middleware chain. Add any custom middleware here, or use middleware provided in @@ -85,6 +87,37 @@ pub const jetzig_options = struct { }; pub fn main() !void { + var db = try sqlite.Db.init(.{ + .mode = sqlite.Db.Mode{ .File = "/home/swebb/Source/zuletzt/src/app/database/data.db" }, + .open_flags = .{ + .write = true, + .create = true, + }, + .threading_mode = .MultiThread, + }); + + const create = + \\CREATE TABLE artists ('artist', 'plays') + ; + + const query = + \\INSERT INTO artists ('artist', 'plays') VALUES (?,?) + ; + + var build = try db.prepare(create); + defer build.deinit(); + + try build.exec(.{},.{}); + + var stmt = try db.prepare(query); + defer stmt.deinit(); + + try stmt.exec(.{}, .{ + .artist = "Wilco", + .plays = 2500, + }); + + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer std.debug.assert(gpa.deinit() == .ok); const allocator = gpa.allocator(); From 199dc0ca49cd9c30d5e2628e1a0b2e254ecc9e93 Mon Sep 17 00:00:00 2001 From: Samuel Webb Date: Sun, 7 Apr 2024 16:19:35 -0400 Subject: [PATCH 03/27] Partial upgrade to ZMPL v2 --- .gitignore | 1 + src/app/views/root/_content.zmpl | 2 +- src/app/views/root/index.zmpl | 10 ++++++---- src/main.zig | 12 ++++++------ 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index cbcae4c..1b3afe7 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ zig-cache/ *.core static/ .jetzig +src/app/database/ \ No newline at end of file diff --git a/src/app/views/root/_content.zmpl b/src/app/views/root/_content.zmpl index 39bf503..088ab6b 100644 --- a/src/app/views/root/_content.zmpl +++ b/src/app/views/root/_content.zmpl @@ -1,4 +1,4 @@ -// Renders the `message` response data value. +

{.message}

diff --git a/src/app/views/root/index.zmpl b/src/app/views/root/index.zmpl index cccb8d8..9473e7a 100644 --- a/src/app/views/root/index.zmpl +++ b/src/app/views/root/index.zmpl @@ -9,12 +9,14 @@
- // If present, renders the `message_param` response data value, add `?message=hello` to the - // URL to see the output: +

{.message_param}

- // Renders `src/app/views/root/_content.zmpl` with the same template data available: -
{^root/content}
+ +
+ @partial root/content +
diff --git a/src/main.zig b/src/main.zig index 922472f..9d5b43a 100644 --- a/src/main.zig +++ b/src/main.zig @@ -96,18 +96,18 @@ pub fn main() !void { .threading_mode = .MultiThread, }); - const create = - \\CREATE TABLE artists ('artist', 'plays') - ; + //const create = + // \\CREATE TABLE artists ('artist', 'plays') + //; const query = \\INSERT INTO artists ('artist', 'plays') VALUES (?,?) ; - var build = try db.prepare(create); - defer build.deinit(); + //var build = try db.prepare(create); + //defer build.deinit(); - try build.exec(.{},.{}); + //try build.exec(.{},.{}); var stmt = try db.prepare(query); defer stmt.deinit(); From 46f0b5038c76c10b547873104a5d92b3e9c75aca Mon Sep 17 00:00:00 2001 From: Samuel Webb Date: Sun, 7 Apr 2024 16:29:33 -0400 Subject: [PATCH 04/27] Update to ZMPL v2 --- src/app/views/root.zig | 7 +++---- src/app/views/root/_content.zmpl | 5 +++-- src/app/views/root/index.zmpl | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/app/views/root.zig b/src/app/views/root.zig index 0fa146c..d567fc5 100644 --- a/src/app/views/root.zig +++ b/src/app/views/root.zig @@ -1,3 +1,4 @@ +const std = @import("std"); const jetzig = @import("jetzig"); /// `src/app/views/root.zig` represents the root URL `/` @@ -18,7 +19,7 @@ pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { var root = try data.object(); // Add a string to the root object. - try root.put("message", data.string("Welcome to Jetzig!")); + try root.put("welcome_message", data.string("Welcome to Jetzig!")); // Request params have the same type as a `data.object()` so they can be inserted them // directly into the response data. Fetch `http://localhost:8080/?message=hello` to set the @@ -26,9 +27,7 @@ pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { // present. const params = try request.params(); - if (params.get("message")) |value| { - try root.put("message_param", value); - } + try root.put("message_param", params.get("message")); // Set arbitrary response headers as required. `content-type` is automatically assigned for // HTML, JSON responses. diff --git a/src/app/views/root/_content.zmpl b/src/app/views/root/_content.zmpl index 088ab6b..89ce5ae 100644 --- a/src/app/views/root/_content.zmpl +++ b/src/app/views/root/_content.zmpl @@ -1,5 +1,6 @@ -

{.message}

+@args message: *ZmplValue +

{{message}}

@@ -15,6 +16,6 @@
Search for an artist, album, or song: - +
diff --git a/src/app/views/root/index.zmpl b/src/app/views/root/index.zmpl index 9473e7a..38e3ef7 100644 --- a/src/app/views/root/index.zmpl +++ b/src/app/views/root/index.zmpl @@ -11,11 +11,11 @@
-

{.message_param}

+

{{.message_param}}

- @partial root/content + @partial root/content(message: .welcome_message)
From f1f1072a18a490486b785387113d387429978815 Mon Sep 17 00:00:00 2001 From: mitteneer Date: Mon, 8 Apr 2024 20:18:40 -0400 Subject: [PATCH 05/27] Begin building views --- public/styles.css | 19 +++++++++++++++++-- src/app/views/root/_content.zmpl | 18 ------------------ src/app/views/root/_graph.zmpl | 0 src/app/views/root/_header.zmpl | 6 ++++++ src/app/views/root/_history.zmpl | 0 src/app/views/root/_random.zmpl | 0 src/app/views/root/_recent.zmpl | 0 src/app/views/root/_top.zmpl | 31 +++++++++++++++++++++++++++++++ src/app/views/root/index.zmpl | 10 ++-------- src/app/views/search/index.zmpl | 3 +++ src/app/views/stats.zig | 7 +++++++ src/app/views/stats/index.zmpl | 3 +++ 12 files changed, 69 insertions(+), 28 deletions(-) delete mode 100644 src/app/views/root/_content.zmpl create mode 100644 src/app/views/root/_graph.zmpl create mode 100644 src/app/views/root/_header.zmpl create mode 100644 src/app/views/root/_history.zmpl create mode 100644 src/app/views/root/_random.zmpl create mode 100644 src/app/views/root/_recent.zmpl create mode 100644 src/app/views/root/_top.zmpl create mode 100644 src/app/views/search/index.zmpl create mode 100644 src/app/views/stats.zig create mode 100644 src/app/views/stats/index.zmpl diff --git a/public/styles.css b/public/styles.css index 1755d47..afdbce9 100644 --- a/public/styles.css +++ b/public/styles.css @@ -3,8 +3,23 @@ * * */ + @import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap'); -.message { - font-weight: bold; +.title { + font-family: 'Roboto'; font-size: 3rem; + font-weight: 500; + margin-left: 20px; + margin-right: 40px; + margin-top: 20px; +} + +.header-link{ + font-family: 'Roboto'; + font-size: 1.5rem; + margin-right: 20px; +} + +#replaceMe{ + font-family:'Courier New'; } diff --git a/src/app/views/root/_content.zmpl b/src/app/views/root/_content.zmpl deleted file mode 100644 index 6d3a665..0000000 --- a/src/app/views/root/_content.zmpl +++ /dev/null @@ -1,18 +0,0 @@ -// Renders the `message` response data value. -

{.message}

- -
- -
- - - - -
- -
Visit jetzig.dev to get started. -
Join our Discord server and introduce yourself:
- -
diff --git a/src/app/views/root/_graph.zmpl b/src/app/views/root/_graph.zmpl new file mode 100644 index 0000000..e69de29 diff --git a/src/app/views/root/_header.zmpl b/src/app/views/root/_header.zmpl new file mode 100644 index 0000000..3362c8f --- /dev/null +++ b/src/app/views/root/_header.zmpl @@ -0,0 +1,6 @@ +Zuletzt +Artists +Albums +Tracks +Stats +
\ No newline at end of file diff --git a/src/app/views/root/_history.zmpl b/src/app/views/root/_history.zmpl new file mode 100644 index 0000000..e69de29 diff --git a/src/app/views/root/_random.zmpl b/src/app/views/root/_random.zmpl new file mode 100644 index 0000000..e69de29 diff --git a/src/app/views/root/_recent.zmpl b/src/app/views/root/_recent.zmpl new file mode 100644 index 0000000..e69de29 diff --git a/src/app/views/root/_top.zmpl b/src/app/views/root/_top.zmpl new file mode 100644 index 0000000..1ce9914 --- /dev/null +++ b/src/app/views/root/_top.zmpl @@ -0,0 +1,31 @@ +@html { + +} +
+
+Top: +Artist +Album +Track +
+ +
+of: +Day +Week +Month +3 Months +6 Months +Current Year +365 days +All Time +
+ + + +hyello + + +
diff --git a/src/app/views/root/index.zmpl b/src/app/views/root/index.zmpl index cccb8d8..ea52da6 100644 --- a/src/app/views/root/index.zmpl +++ b/src/app/views/root/index.zmpl @@ -8,13 +8,7 @@ -
- // If present, renders the `message_param` response data value, add `?message=hello` to the - // URL to see the output: -

{.message_param}

- - // Renders `src/app/views/root/_content.zmpl` with the same template data available: -
{^root/content}
-
+ @partial root/header + @partial root/top diff --git a/src/app/views/search/index.zmpl b/src/app/views/search/index.zmpl new file mode 100644 index 0000000..76457d0 --- /dev/null +++ b/src/app/views/search/index.zmpl @@ -0,0 +1,3 @@ +
+ Content goes here +
diff --git a/src/app/views/stats.zig b/src/app/views/stats.zig new file mode 100644 index 0000000..4fac587 --- /dev/null +++ b/src/app/views/stats.zig @@ -0,0 +1,7 @@ +const std = @import("std"); +const jetzig = @import("jetzig"); + +pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + return request.render(.ok); +} diff --git a/src/app/views/stats/index.zmpl b/src/app/views/stats/index.zmpl new file mode 100644 index 0000000..76457d0 --- /dev/null +++ b/src/app/views/stats/index.zmpl @@ -0,0 +1,3 @@ +
+ Content goes here +
From dc959607f916a6db924fa603828ba3c2202674a8 Mon Sep 17 00:00:00 2001 From: Samuel Webb Date: Mon, 8 Apr 2024 20:19:24 -0400 Subject: [PATCH 06/27] Building db stuff --- .gitignore | 3 +-- src/app/database/data.db | Bin 0 -> 8192 bytes src/app/views/search.zig | 12 +++++++++ src/db.zig | 54 +++++++++++++++++++++++++++++++++++++++ src/main.zig | 34 ++++++++++++------------ 5 files changed, 84 insertions(+), 19 deletions(-) create mode 100644 src/app/database/data.db create mode 100644 src/app/views/search.zig diff --git a/.gitignore b/.gitignore index 1b3afe7..7932178 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,4 @@ zig-out/ zig-cache/ *.core static/ -.jetzig -src/app/database/ \ No newline at end of file +.jetzig \ No newline at end of file diff --git a/src/app/database/data.db b/src/app/database/data.db new file mode 100644 index 0000000000000000000000000000000000000000..6ba499c9458a58ca7ba31db1d16cf47b60c79684 GIT binary patch literal 8192 zcmeI%F-yZh6bJCTiwX+y3~DPB>02ZUqJ#YaZ9^stMHA>C4pB~Xk~SqdkbV|dXFq{% z4vzg8{Q!Oduh?#aWN;Av@9uGTk2gGi>qQqmZ4}+*#V|2+#CCqrtCDa+9f~ zFuFA5;t;`Q*oi}mJKbJLiy|HR<1Cq!{xSLACI85kZMG^~Ot$1yzR4H)wE0|ZRU8`a z5P$##AOHafKmY;|fB*y_0D*rZu;DqPzWzSY*=;Uo?COuk_G*qe;GgGubWcH{hh;u; zcO9|sQ2#NbK%0rTV>{SKoGL~Alp>|d+O{K3otZ7sAf5m0N2btYRoWT%ewy Date: Mon, 8 Apr 2024 22:58:30 -0400 Subject: [PATCH 07/27] Update all radios in _top.zmpl --- src/app/database/data.db | Bin 8192 -> 8192 bytes src/{ => app/lib}/db.zig | 2 +- src/app/views/root/_top.zmpl | 27 +++++++++++---------------- 3 files changed, 12 insertions(+), 17 deletions(-) rename src/{ => app/lib}/db.zig (94%) diff --git a/src/app/database/data.db b/src/app/database/data.db index 6ba499c9458a58ca7ba31db1d16cf47b60c79684..0601b68b0b1a1f2588d0d5b815e6847fe1eddea1 100644 GIT binary patch delta 262 zcmZp0XmFSyEvUr6z`z8=Fu*iX$CzJDglA-yq$&iL6lE5q z7V85=w80|UlOM{<`-+RQFv-d@EBKe^C^%=9RO$m6+8~C!2*?VC6F~K;3jP@i!Ko0< bsoMS-+8~agAPbYYBP34G!c;1XnJ66ZV;o|%)J z&;NoUdSYRCpr9BtlPcpE@65b(1?Qs7+~WK^UQuQyS!M -#myHeader { background-color: lightblue; color: black; padding: 40px; text-align: center;} - -}
Top: Artist -Album -Track +Album +Track
of: -Day -Week -Month -3 Months -6 Months -Current Year -365 days -All Time +Day +Week +Month +3 Months +6 Months +Current Year +365 days +All Time
-hyello +hyello
From 62ec4dc3ab86a59780341dff037f33f382427323 Mon Sep 17 00:00:00 2001 From: Samuel Webb Date: Tue, 9 Apr 2024 18:01:35 -0400 Subject: [PATCH 08/27] pain and misery --- src/app/database/data.db | Bin 8192 -> 8192 bytes src/app/lib/db.zig | 51 ++++++--------------------------------- src/app/views/search.zig | 42 +++++++++++++++++++++++++++++--- 3 files changed, 45 insertions(+), 48 deletions(-) diff --git a/src/app/database/data.db b/src/app/database/data.db index 0601b68b0b1a1f2588d0d5b815e6847fe1eddea1..9eabb42cf6881a7cf42bb7f3a78b6c44ae6ceb12 100644 GIT binary patch delta 77 zcmZp0XmFSy&8R$4#+gxhW5PmyVFm^UHhzC5{ Date: Tue, 9 Apr 2024 22:29:45 -0400 Subject: [PATCH 09/27] Make some allocator changes --- src/app/views/search.zig | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/app/views/search.zig b/src/app/views/search.zig index 47bfda8..df0a8eb 100644 --- a/src/app/views/search.zig +++ b/src/app/views/search.zig @@ -5,7 +5,10 @@ const sqlite = @import("sqlite"); pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { _ = data; - const allocator = std.heap.page_allocator; + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + const allocator = gpa.allocator(); + var arena = std.heap.ArenaAllocator.init(allocator); + defer arena.deinit(); var db = try sqlite.Db.init(.{ .mode = sqlite.Db.Mode{ .File = "/home/swebb/Source/zuletzt/src/app/database/data.db" }, .open_flags = .{ @@ -27,7 +30,7 @@ pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { artist: []u8, plays: usize, }, - allocator, + arena.allocator(), .{}, .{ .artist = query}, ); From 75a63a19fcd42c7cae1599a658db2ec63e424117 Mon Sep 17 00:00:00 2001 From: mitteneer Date: Wed, 10 Apr 2024 16:03:35 -0400 Subject: [PATCH 10/27] Begin HTML output --- build.zig | 4 ++-- src/app/views/search.zig | 25 +++++++++++++------------ src/app/views/search/index.zmpl | 18 +++++++++++++++--- 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/build.zig b/build.zig index e488390..88fed1c 100644 --- a/build.zig +++ b/build.zig @@ -15,7 +15,7 @@ pub fn build(b: *std.Build) !void { // Example dependency: const iguanas_dep = b.dependency("iguanas", .{ .optimize = optimize, .target = target }); exe.root_module.addImport("iguanas", iguanas_dep.module("iguanas")); - + const sqlite = b.dependency("sqlite", .{ .target = target, .optimize = optimize, @@ -28,7 +28,7 @@ pub fn build(b: *std.Build) !void { // All dependencies **must** be added to imports above this line. - try jetzig.jetzigInit(b, exe, .{.zmpl_version = .v2}); + try jetzig.jetzigInit(b, exe, .{ .zmpl_version = .v2 }); b.installArtifact(exe); diff --git a/src/app/views/search.zig b/src/app/views/search.zig index df0a8eb..f439a8a 100644 --- a/src/app/views/search.zig +++ b/src/app/views/search.zig @@ -4,7 +4,7 @@ const queries = @import("../lib/db.zig"); const sqlite = @import("sqlite"); pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { - _ = data; + var root = try data.array(); var gpa = std.heap.GeneralPurposeAllocator(.{}){}; const allocator = gpa.allocator(); var arena = std.heap.ArenaAllocator.init(allocator); @@ -16,30 +16,31 @@ pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { .create = true, }, .threading_mode = .MultiThread, - }); - //var root = try data.object(); + }); const params = try request.params(); - const query: ?[]const u8 = if (params.get("q")) |param| param.string.value else null; - if (query != null){ - // sql.search(query, db); + const queryOrNull: ?[]const u8 = if (params.get("q")) |param| param.string.value else null; + if (queryOrNull) |query| { + // sql.search(query, db); var artistSearch = try db.prepare(queries.getArtistSearch); defer artistSearch.deinit(); const artistResults = try artistSearch.all( - struct{ + struct { artist: []u8, - plays: usize, + url: usize, }, arena.allocator(), .{}, - .{ .artist = query}, + .{ .artist = query }, ); - for (artistResults) |r|{ - std.log.debug("artist: {s}, Plays: {}", .{r.artist, r.plays}); + for (artistResults) |r| { + std.log.debug("artist: {s}, url: {s}", .{ r.artist, r.url }); + root.append(data.string(r.artist)); + root.append(data.string(r.url)); //std.log.debug("{s}", .{r}); } - } else{ + } else { return request.render(.bad_request); } //const query = params.get("q"); diff --git a/src/app/views/search/index.zmpl b/src/app/views/search/index.zmpl index 76457d0..129b098 100644 --- a/src/app/views/search/index.zmpl +++ b/src/app/views/search/index.zmpl @@ -1,3 +1,15 @@ -
- Content goes here -
+ + + + + + + + @zig { + for (0..) + + + + } + +
ArtistAlbumTrack
\ No newline at end of file From 8f3481590465eaa1e8910cb28f9998f0a5006d95 Mon Sep 17 00:00:00 2001 From: Samuel Webb Date: Wed, 10 Apr 2024 23:48:22 -0400 Subject: [PATCH 11/27] Update Search view --- build.zig.zon | 24 ++++++++++-------------- src/app/lib/db.zig | 2 +- src/app/views/search.zig | 6 +++--- src/app/views/search/index.zmpl | 17 ++++++++++------- 4 files changed, 24 insertions(+), 25 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index 578092d..ff7ee12 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -14,20 +14,16 @@ // `zig build --fetch` can be used to fetch all dependencies of a package, recursively. // Once all dependencies are fetched, `zig build` no longer requires // internet connectivity. - .dependencies = .{ - .jetzig = .{ - .url = "https://github.com/jetzig-framework/jetzig/archive/68ca4d3bbad05880e6e38fc6d10e7a24e772081e.tar.gz", - .hash = "122047ebabab1dfe1bb7f96ffdac98410ffb4850b06dc6b1670ce74aaeaa49bdc08e", - }, - .iguanas = .{ - .url = "https://github.com/jetzig-framework/iguanas/archive/89c2abf29de0bc31054a9a6feac5a6a83bab0459.tar.gz", - .hash = "12202fd319a5ab4e124b00e8ddea474d07c19c4e005d77b6c29fc44860904ea01a5c", - }, - .sqlite = .{ - .url = "https://github.com/vrischmann/zig-sqlite/archive/6af21bb.tar.gz", - .hash = "12202799861d92c7880d1a74f95be099752d7f0420a0c9628dc923ca2b1a22b7bda8", - } - }, + .dependencies = .{ .jetzig = .{ + .url = "https://github.com/jetzig-framework/jetzig/archive/88abe2243d87b28ad110c5fe9fe90b716a6c6583.tar.gz", + .hash = "12200d15cef72d4c37a01c930db53c3ffafdc05142462afddeab818701a0291327d1", + }, .iguanas = .{ + .url = "https://github.com/jetzig-framework/iguanas/archive/89c2abf29de0bc31054a9a6feac5a6a83bab0459.tar.gz", + .hash = "12202fd319a5ab4e124b00e8ddea474d07c19c4e005d77b6c29fc44860904ea01a5c", + }, .sqlite = .{ + .url = "https://github.com/vrischmann/zig-sqlite/archive/6af21bb.tar.gz", + .hash = "12202799861d92c7880d1a74f95be099752d7f0420a0c9628dc923ca2b1a22b7bda8", + } }, .paths = .{ // This makes *all* files, recursively, included in this package. It is generally // better to explicitly list the files and directories instead, to insure that diff --git a/src/app/lib/db.zig b/src/app/lib/db.zig index bb75239..90f8e8a 100644 --- a/src/app/lib/db.zig +++ b/src/app/lib/db.zig @@ -13,5 +13,5 @@ pub const getTrack = \\SELECT artist, track, album, plays FROM tracks WHERE trac pub const getTrackSearch = \\SELECT url FROM artists WHERE artist == ? ; -pub const getArtistSearch = \\SELECT artist, plays FROM artists WHERE artist LIKE '%' || ? || '%' +pub const getArtistSearch = \\SELECT artist, url FROM artists WHERE artist LIKE '%' || ? || '%' ; \ No newline at end of file diff --git a/src/app/views/search.zig b/src/app/views/search.zig index f439a8a..5d9a5c6 100644 --- a/src/app/views/search.zig +++ b/src/app/views/search.zig @@ -27,7 +27,7 @@ pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { const artistResults = try artistSearch.all( struct { artist: []u8, - url: usize, + url: []u8, }, arena.allocator(), .{}, @@ -36,8 +36,8 @@ pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { for (artistResults) |r| { std.log.debug("artist: {s}, url: {s}", .{ r.artist, r.url }); - root.append(data.string(r.artist)); - root.append(data.string(r.url)); + try root.append(data.string(r.artist)); + try root.append(data.string(r.url)); //std.log.debug("{s}", .{r}); } } else { diff --git a/src/app/views/search/index.zmpl b/src/app/views/search/index.zmpl index 129b098..67ad7ea 100644 --- a/src/app/views/search/index.zmpl +++ b/src/app/views/search/index.zmpl @@ -4,12 +4,15 @@ Album Track - - @zig { - for (0..) - - - + @zig ZIG + for (zmpl.items(.array), 0..) |u,i| { + if(@mod(i,2)==0){ + + {{u}} + {{i}} + + + } } - + ZIG \ No newline at end of file From fa4c3a6eff270ff4431e35808208221a065a3068 Mon Sep 17 00:00:00 2001 From: Samuel Webb Date: Thu, 11 Apr 2024 17:22:24 -0400 Subject: [PATCH 12/27] Implement album and track searching --- .gitignore | 3 ++- src/app/database/data.db | Bin 8192 -> 16384 bytes src/app/lib/db.zig | 11 ++++++++--- src/app/views/search.zig | 6 ++++-- src/app/views/search/index.zmpl | 10 ++++------ 5 files changed, 18 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 7932178..9f6d7b4 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ zig-out/ zig-cache/ *.core static/ -.jetzig \ No newline at end of file +.jetzig +src/app/database/data.db-journal \ No newline at end of file diff --git a/src/app/database/data.db b/src/app/database/data.db index 9eabb42cf6881a7cf42bb7f3a78b6c44ae6ceb12..fa37ab7169d9ab40592d86dcc9c906a28a52db95 100644 GIT binary patch literal 16384 zcmeI%&rTCT7y$6uEkoN7I^tL>HVwl+E3CTPEs}6B2&5Vm1Zf35nFU5zU3Tm4R>d3d zVB!mSF!4g7kKh9sV|)fZn0W93{C3;Qp97eXc=3aD_xrwCW@dk!zG?ODoE=K>$n&?X zP?X6jqA29L5QLCX_;kRh{gZ@Xf3CC#j<}DKD~~^QGn*tbyCk#A-m;z}p2i^*Kmim$ z0Te(16hHwKKmim$0TgHnJXDgod|uhfhSsVhEkCq_FlgP>nWf59ts-hu({mNkDvFE6 zx?^nz#Y>{tEI~BT@STBlt#ZExan$<`q-XEcDmN=jBg=_ouD4g&Nj4X7RvTM^6<<3J zP9N7`_INOw{m(Kmd9=pCd13#PC-^TetNXVV`qtW$pm|Lk*jBR!+lqtZx7E!qlTOw} z*emGG%WRo-u`ldh=FQ*#p5UeMPf-8`Pyhu`00mG01yBG5Pyhu`;NJ^~vs4`}rj%c| zZFfV=`1V%dxq6gnt%TJ{Cn5c3D2M0DhEdy;QY>tWs+55qCECKK7A5DhR4w$S5@O-G zBWCPyTZcf40``Lp9rtRdd#IZ0O{G7s*v^`#$JbF#RlSr-e1m1g%4YPPeKqv$dUIhd zu4t`FE=PI3kQy_Ls_S`m>FcdKEX)rr-p!^-l3gI|0{h9nvJdPvd&(9MJ%lL=pa2S> z01BW03ZMWApa2S>01EsU0_PQ4;C)Ipw0uVffoT|eJ5~W8vxm@^q=S4=neiGMa4xXz zH|nM_9%ovdsY9K#$VZgwGxh1L>H?m_F~V%OKR| zy+$Ba(E)1=D^+`|?!YmC=WdurS#PFVl&a?vG|vl4bXHJt*G@$1MUa)(6Z-WsEpa2S>01BW03ZMWApa2S>01Eu)0!*Qt zXVXh^y#%KLa08F7mPvmn9p=S!)P6(9?uM4_mY~Itdhm8WLFqL^VgXx7eX}O}ZE#cv^9%ovVWfQcQ=hG`vzLb6m UTK{I0Rpat(;J`#BX#ShgZ+qh$&Hw-a delta 229 zcmZo@U~F)hAT6lOz`(!+#4x}#QOB5HnL#h>0xwXAk$(dN|Ax(q1`_-eCuni_F|vtE zOEZ>E-pS`KstHoS#_!L>|CYai{|x_Tet-VilNAJ%CL0LwY<3bj$v1I=i@LlR3zMok z;}`GDymSTUqRial{5*XSM;pq~6lP)4H=Mj)Ue!`WoP|+ZHYGSOKffTgNFPcwFo=t? yFv-d@EBKe^C^%=9RO$m6+8~C!2*_}T6O(P_C4~hAS(wBnIgf;A<|OCqgGm4uIXEr= diff --git a/src/app/lib/db.zig b/src/app/lib/db.zig index 90f8e8a..dd6f7bf 100644 --- a/src/app/lib/db.zig +++ b/src/app/lib/db.zig @@ -10,8 +10,13 @@ pub const getArtist = \\SELECT artist, plays FROM artists WHERE artist == ? pub const getTrack = \\SELECT artist, track, album, plays FROM tracks WHERE track == ? ; -pub const getTrackSearch = \\SELECT url FROM artists WHERE artist == ? +pub const getTrackSearch = \\SELECT track, url, form FROM tracks WHERE track LIKE '%' || ? || '%' ; -pub const getArtistSearch = \\SELECT artist, url FROM artists WHERE artist LIKE '%' || ? || '%' -; \ No newline at end of file +pub const getArtistSearch = \\SELECT artist, url, form FROM artists WHERE artist LIKE '%' || ? || '%' +; + +pub const getAlbumSearch = \\SELECT album, url, form FROM albums WHERE album LIKE '%' || ? || '%' +; + +pub const total_search = getArtistSearch ++ " UNION " ++ getAlbumSearch ++ " UNION " ++ getTrackSearch; \ No newline at end of file diff --git a/src/app/views/search.zig b/src/app/views/search.zig index 5d9a5c6..46d1592 100644 --- a/src/app/views/search.zig +++ b/src/app/views/search.zig @@ -21,23 +21,25 @@ pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { const queryOrNull: ?[]const u8 = if (params.get("q")) |param| param.string.value else null; if (queryOrNull) |query| { // sql.search(query, db); - var artistSearch = try db.prepare(queries.getArtistSearch); + var artistSearch = try db.prepare(queries.total_search); defer artistSearch.deinit(); const artistResults = try artistSearch.all( struct { artist: []u8, url: []u8, + form: []u8, }, arena.allocator(), .{}, - .{ .artist = query }, + .{ .artist = query, .album = query, .track = query }, ); for (artistResults) |r| { std.log.debug("artist: {s}, url: {s}", .{ r.artist, r.url }); try root.append(data.string(r.artist)); try root.append(data.string(r.url)); + try root.append(data.string(r.form)); //std.log.debug("{s}", .{r}); } } else { diff --git a/src/app/views/search/index.zmpl b/src/app/views/search/index.zmpl index 67ad7ea..4101944 100644 --- a/src/app/views/search/index.zmpl +++ b/src/app/views/search/index.zmpl @@ -4,15 +4,13 @@ Album Track - @zig ZIG + @zig { for (zmpl.items(.array), 0..) |u,i| { - if(@mod(i,2)==0){ + if(@mod(i,3)==0){ - {{u}} - {{i}} - + {{u}} } } - ZIG + } \ No newline at end of file From 8353920f6030bead141de7a3060be04bb059dcf7 Mon Sep 17 00:00:00 2001 From: mitteneer Date: Wed, 31 Jul 2024 13:55:41 -0400 Subject: [PATCH 13/27] Update build.zig.zon --- build.zig.zon | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index ff7ee12..54425ff 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -14,16 +14,16 @@ // `zig build --fetch` can be used to fetch all dependencies of a package, recursively. // Once all dependencies are fetched, `zig build` no longer requires // internet connectivity. - .dependencies = .{ .jetzig = .{ - .url = "https://github.com/jetzig-framework/jetzig/archive/88abe2243d87b28ad110c5fe9fe90b716a6c6583.tar.gz", - .hash = "12200d15cef72d4c37a01c930db53c3ffafdc05142462afddeab818701a0291327d1", - }, .iguanas = .{ - .url = "https://github.com/jetzig-framework/iguanas/archive/89c2abf29de0bc31054a9a6feac5a6a83bab0459.tar.gz", - .hash = "12202fd319a5ab4e124b00e8ddea474d07c19c4e005d77b6c29fc44860904ea01a5c", - }, .sqlite = .{ - .url = "https://github.com/vrischmann/zig-sqlite/archive/6af21bb.tar.gz", - .hash = "12202799861d92c7880d1a74f95be099752d7f0420a0c9628dc923ca2b1a22b7bda8", - } }, + .dependencies = .{ + .jetzig = .{ + .url = "https://github.com/jetzig-framework/jetzig/archive/3c47435fd6ec7ea705a778dab1f533ae74e9bd6a.tar.gz", + .hash = "12209edf5716323a2475a4fe0eb02f5281d8b1d5e6708bc03a104892f7cf72a6cab5", + }, + .sqlite = .{ + .url = "https://github.com/vrischmann/zig-sqlite/archive/706ec59.tar.gz", + .hash = "1220faeb7e1b89ce805a41be1395931fbec69530aac45fce9cb17b817532e4cb6899", + }, + }, .paths = .{ // This makes *all* files, recursively, included in this package. It is generally // better to explicitly list the files and directories instead, to insure that From b1a21d63df3423649af27b1df552880384ccfe71 Mon Sep 17 00:00:00 2001 From: Samuel Webb Date: Fri, 13 Sep 2024 13:44:38 -0400 Subject: [PATCH 14/27] Update Jetzig --- .gitignore | 4 ++-- build.zig.zon | 4 ++-- src/app/views/music.zig | 41 ++++++++++++++++++++++++++++++++++ src/app/views/music/index.zmpl | 2 ++ 4 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 src/app/views/music.zig create mode 100644 src/app/views/music/index.zmpl diff --git a/.gitignore b/.gitignore index 9f6d7b4..23cb6fa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ zig-out/ -zig-cache/ +.zig-cache/ *.core static/ .jetzig -src/app/database/data.db-journal \ No newline at end of file +src/app/database/data.db-journal diff --git a/build.zig.zon b/build.zig.zon index ff7ee12..849ffe0 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -15,8 +15,8 @@ // Once all dependencies are fetched, `zig build` no longer requires // internet connectivity. .dependencies = .{ .jetzig = .{ - .url = "https://github.com/jetzig-framework/jetzig/archive/88abe2243d87b28ad110c5fe9fe90b716a6c6583.tar.gz", - .hash = "12200d15cef72d4c37a01c930db53c3ffafdc05142462afddeab818701a0291327d1", + .url = "https://github.com/jetzig-framework/jetzig/archive/dda433bb73000614482af10a277d47dc9d89600c.tar.gz", + .hash = "12202ce84b803a8b300c91d98afbc7c326298b55a23bf05cf603182e934b621008ec", }, .iguanas = .{ .url = "https://github.com/jetzig-framework/iguanas/archive/89c2abf29de0bc31054a9a6feac5a6a83bab0459.tar.gz", .hash = "12202fd319a5ab4e124b00e8ddea474d07c19c4e005d77b6c29fc44860904ea01a5c", diff --git a/src/app/views/music.zig b/src/app/views/music.zig new file mode 100644 index 0000000..4dd0d07 --- /dev/null +++ b/src/app/views/music.zig @@ -0,0 +1,41 @@ +const std = @import("std"); +const jetzig = @import("jetzig"); + +pub const static_params = .{ + .index = .{ + //.{ .params = .{ .foo = "hi", .bar = "bye" } }, + //.{ .params = .{ .foo = "hello", .bar = "goodbye" } }, + }, + //.get = .{ + // .{ .id = "1", .params = .{ .foo = "hi", .bar = "bye" } }, + // .{ .id = "2", .params = .{ .foo = "hello", .bar = "goodbye" } }, + //}, +}; + +pub fn index(request: *jetzig.StaticRequest, data: *jetzig.Data) !jetzig.View { + var root = try data.object(); + + const params = try request.params(); + + if (params.get("foo")) |foo| try root.put("foo", foo); + if (params.get("foo") == null) try root.put("foo", data.string("no foo")); + if (params.get("bar")) |bar| try root.put("bar", bar); + if (params.get("bar") == null) try root.put("bar", data.string("no bar")); + + return request.render(.ok); +} + +//pub fn get(id: []const u8, request: *jetzig.StaticRequest, data: *jetzig.Data) !jetzig.View { +// var root = try data.object(); +// +// const params = try request.params(); +// +// if (std.mem.eql(u8, id, "1")) { +// try root.put("id", data.string("id is '1'")); +// } +// +// if (params.get("foo")) |foo| try root.put("foo", foo); +// if (params.get("bar")) |bar| try root.put("bar", bar); +// +// return request.render(.created); +//} \ No newline at end of file diff --git a/src/app/views/music/index.zmpl b/src/app/views/music/index.zmpl new file mode 100644 index 0000000..a14b357 --- /dev/null +++ b/src/app/views/music/index.zmpl @@ -0,0 +1,2 @@ +
{{.foo}}
+
{{.bar}}
\ No newline at end of file From 348545949ae47c9c3a7ef1a28a174e7f4db3f6bc Mon Sep 17 00:00:00 2001 From: mitteneer Date: Sat, 14 Sep 2024 11:04:27 -0400 Subject: [PATCH 15/27] Update to latest Jetzig --- build.zig | 18 ++---------- build.zig.zon | 8 ++--- src/app/views/search.zig | 52 --------------------------------- src/app/views/search/index.zmpl | 16 ---------- src/main.zig | 12 ++++---- 5 files changed, 11 insertions(+), 95 deletions(-) delete mode 100644 src/app/views/search.zig delete mode 100644 src/app/views/search/index.zmpl diff --git a/build.zig b/build.zig index 88fed1c..95a299d 100644 --- a/build.zig +++ b/build.zig @@ -7,24 +7,12 @@ pub fn build(b: *std.Build) !void { const exe = b.addExecutable(.{ .name = "zuletzt", - .root_source_file = .{ .path = "src/main.zig" }, + .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, }); // Example dependency: - const iguanas_dep = b.dependency("iguanas", .{ .optimize = optimize, .target = target }); - exe.root_module.addImport("iguanas", iguanas_dep.module("iguanas")); - - const sqlite = b.dependency("sqlite", .{ - .target = target, - .optimize = optimize, - }); - - exe.root_module.addImport("sqlite", sqlite.module("sqlite")); - - // links the bundled sqlite3, so leave this out if you link the system one - exe.linkLibrary(sqlite.artifact("sqlite")); // All dependencies **must** be added to imports above this line. @@ -41,7 +29,7 @@ pub fn build(b: *std.Build) !void { run_step.dependOn(&run_cmd.step); const lib_unit_tests = b.addTest(.{ - .root_source_file = .{ .path = "src/main.zig" }, + .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, }); @@ -49,7 +37,7 @@ pub fn build(b: *std.Build) !void { const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests); const exe_unit_tests = b.addTest(.{ - .root_source_file = .{ .path = "src/main.zig" }, + .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, }); diff --git a/build.zig.zon b/build.zig.zon index 54425ff..9b7f253 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -16,12 +16,8 @@ // internet connectivity. .dependencies = .{ .jetzig = .{ - .url = "https://github.com/jetzig-framework/jetzig/archive/3c47435fd6ec7ea705a778dab1f533ae74e9bd6a.tar.gz", - .hash = "12209edf5716323a2475a4fe0eb02f5281d8b1d5e6708bc03a104892f7cf72a6cab5", - }, - .sqlite = .{ - .url = "https://github.com/vrischmann/zig-sqlite/archive/706ec59.tar.gz", - .hash = "1220faeb7e1b89ce805a41be1395931fbec69530aac45fce9cb17b817532e4cb6899", + .url = "https://github.com/jetzig-framework/jetzig/archive/dda433bb73000614482af10a277d47dc9d89600c.tar.gz", + .hash = "12202ce84b803a8b300c91d98afbc7c326298b55a23bf05cf603182e934b621008ec", }, }, .paths = .{ diff --git a/src/app/views/search.zig b/src/app/views/search.zig deleted file mode 100644 index 46d1592..0000000 --- a/src/app/views/search.zig +++ /dev/null @@ -1,52 +0,0 @@ -const std = @import("std"); -const jetzig = @import("jetzig"); -const queries = @import("../lib/db.zig"); -const sqlite = @import("sqlite"); - -pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { - var root = try data.array(); - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - const allocator = gpa.allocator(); - var arena = std.heap.ArenaAllocator.init(allocator); - defer arena.deinit(); - var db = try sqlite.Db.init(.{ - .mode = sqlite.Db.Mode{ .File = "/home/swebb/Source/zuletzt/src/app/database/data.db" }, - .open_flags = .{ - .write = true, - .create = true, - }, - .threading_mode = .MultiThread, - }); - const params = try request.params(); - const queryOrNull: ?[]const u8 = if (params.get("q")) |param| param.string.value else null; - if (queryOrNull) |query| { - // sql.search(query, db); - var artistSearch = try db.prepare(queries.total_search); - defer artistSearch.deinit(); - - const artistResults = try artistSearch.all( - struct { - artist: []u8, - url: []u8, - form: []u8, - }, - arena.allocator(), - .{}, - .{ .artist = query, .album = query, .track = query }, - ); - - for (artistResults) |r| { - std.log.debug("artist: {s}, url: {s}", .{ r.artist, r.url }); - try root.append(data.string(r.artist)); - try root.append(data.string(r.url)); - try root.append(data.string(r.form)); - //std.log.debug("{s}", .{r}); - } - } else { - return request.render(.bad_request); - } - //const query = params.get("q"); - //try root.put("q",query); - //try root.put("q", data.string("Welcome")); - return request.render(.ok); -} diff --git a/src/app/views/search/index.zmpl b/src/app/views/search/index.zmpl deleted file mode 100644 index 4101944..0000000 --- a/src/app/views/search/index.zmpl +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - @zig { - for (zmpl.items(.array), 0..) |u,i| { - if(@mod(i,3)==0){ - - - - } - } - } -
ArtistAlbumTrack
{{u}}
\ No newline at end of file diff --git a/src/main.zig b/src/main.zig index fea50c2..ec577ff 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,10 +1,10 @@ const std = @import("std"); - -pub const jetzig = @import("jetzig"); - +const jetzig = @import("jetzig"); pub const routes = @import("routes"); +const zmd = @import("zmd"); +const builtin = @import("builtin"); +pub const static = @import("static"); -pub const sqlite = @import("sqlite"); // Override default settings in `jetzig.config` here: pub const jetzig_options = struct { @@ -72,13 +72,13 @@ pub const jetzig_options = struct { "", }; - pub fn block(allocator: std.mem.Allocator, node: jetzig.zmd.Node) ![]const u8 { + pub fn block(allocator: std.mem.Allocator, node: zmd.Node) ![]const u8 { return try std.fmt.allocPrint(allocator, \\
{s}
, .{ node.meta, node.content }); } - pub fn link(allocator: std.mem.Allocator, node: jetzig.zmd.Node) ![]const u8 { + pub fn link(allocator: std.mem.Allocator, node: zmd.Node) ![]const u8 { return try std.fmt.allocPrint(allocator, \\{1s} , .{ node.href.?, node.title.? }); From a15db5fb312c889313e87fb28c6cbea647ff4e5b Mon Sep 17 00:00:00 2001 From: mitteneer Date: Wed, 20 Nov 2024 18:17:14 -0500 Subject: [PATCH 16/27] Add views and begin database --- build.zig.zon | 4 +- config/database.zig | 28 ++++ src/app/database/Schema.zig | 130 ++++++++++++++++++ src/app/database/data.db | Bin 16384 -> 0 bytes .../2024-11-15_14-46-12_create_artists.zig | 26 ++++ .../2024-11-15_14-58-47_create_songs.zig | 27 ++++ ...024-11-15_17-52-04_create_artist_songs.zig | 20 +++ ...24-11-15_17-52-28_create_artist_albums.zig | 20 +++ ...2024-11-15_17-52-54_create_album_songs.zig | 20 +++ .../2024-11-20_00-19-54_create_scrobble.zig | 19 +++ .../2024-11-20_19-08-02_create_albums.zig | 28 ++++ src/app/lib/db.zig | 22 --- .../lastfmMiddleware.zig} | 0 .../spotifyMiddleware.zig} | 0 src/app/views/collection.zig | 36 +++++ .../index.zmpl => collection/delete.zmpl} | 0 src/app/views/collection/get.zmpl | 3 + src/app/views/collection/index.zmpl | 8 ++ src/app/views/collection/patch.zmpl | 3 + src/app/views/collection/post.zmpl | 3 + src/app/views/collection/put.zmpl | 3 + src/app/views/concerts.zig | 36 +++++ src/app/views/concerts/delete.zmpl | 3 + src/app/views/concerts/get.zmpl | 3 + src/app/views/concerts/index.zmpl | 8 ++ src/app/views/concerts/patch.zmpl | 3 + src/app/views/concerts/post.zmpl | 3 + src/app/views/concerts/put.zmpl | 3 + src/app/views/lists.zig | 36 +++++ src/app/views/lists/delete.zmpl | 3 + src/app/views/lists/get.zmpl | 3 + src/app/views/lists/index.zmpl | 8 ++ src/app/views/lists/patch.zmpl | 3 + src/app/views/lists/post.zmpl | 3 + src/app/views/lists/put.zmpl | 3 + src/app/views/music.zig | 41 ------ src/app/views/music/index.zmpl | 2 - src/app/views/partials/_header.zmpl | 7 + .../_random.zmpl => partials/_history.zmpl} | 0 .../_recent.zmpl => partials/_random.zmpl} | 0 src/app/views/partials/_recent.zmpl | 0 src/app/views/partials/_table.zmpl | 0 src/app/views/{root => partials}/_top.zmpl | 0 src/app/views/ratings.zig | 36 +++++ src/app/views/ratings/delete.zmpl | 3 + src/app/views/ratings/get.zmpl | 3 + src/app/views/ratings/index.zmpl | 8 ++ src/app/views/ratings/patch.zmpl | 3 + src/app/views/ratings/post.zmpl | 3 + src/app/views/ratings/put.zmpl | 3 + src/app/views/root/_header.zmpl | 6 - src/app/views/root/index.zmpl | 5 +- src/app/views/scrobbles.zig | 36 +++++ src/app/views/scrobbles/delete.zmpl | 3 + src/app/views/scrobbles/get.zmpl | 3 + src/app/views/scrobbles/index.zmpl | 8 ++ src/app/views/scrobbles/patch.zmpl | 3 + src/app/views/scrobbles/post.zmpl | 3 + src/app/views/scrobbles/put.zmpl | 3 + src/app/views/stats.zig | 7 - src/app/views/upload.zig | 48 +++++++ src/app/views/upload/delete.zmpl | 3 + src/app/views/upload/get.zmpl | 3 + src/app/views/upload/index.zmpl | 19 +++ src/app/views/upload/patch.zmpl | 3 + src/app/views/upload/post.zmpl | 3 + src/app/views/upload/put.zmpl | 3 + src/main.zig | 13 +- 68 files changed, 709 insertions(+), 90 deletions(-) create mode 100644 config/database.zig create mode 100644 src/app/database/Schema.zig delete mode 100644 src/app/database/data.db create mode 100644 src/app/database/migrations/2024-11-15_14-46-12_create_artists.zig create mode 100644 src/app/database/migrations/2024-11-15_14-58-47_create_songs.zig create mode 100644 src/app/database/migrations/2024-11-15_17-52-04_create_artist_songs.zig create mode 100644 src/app/database/migrations/2024-11-15_17-52-28_create_artist_albums.zig create mode 100644 src/app/database/migrations/2024-11-15_17-52-54_create_album_songs.zig create mode 100644 src/app/database/migrations/2024-11-20_00-19-54_create_scrobble.zig create mode 100644 src/app/database/migrations/2024-11-20_19-08-02_create_albums.zig delete mode 100644 src/app/lib/db.zig rename src/app/{views/root/_graph.zmpl => middleware/lastfmMiddleware.zig} (100%) rename src/app/{views/root/_history.zmpl => middleware/spotifyMiddleware.zig} (100%) create mode 100644 src/app/views/collection.zig rename src/app/views/{stats/index.zmpl => collection/delete.zmpl} (100%) create mode 100644 src/app/views/collection/get.zmpl create mode 100644 src/app/views/collection/index.zmpl create mode 100644 src/app/views/collection/patch.zmpl create mode 100644 src/app/views/collection/post.zmpl create mode 100644 src/app/views/collection/put.zmpl create mode 100644 src/app/views/concerts.zig create mode 100644 src/app/views/concerts/delete.zmpl create mode 100644 src/app/views/concerts/get.zmpl create mode 100644 src/app/views/concerts/index.zmpl create mode 100644 src/app/views/concerts/patch.zmpl create mode 100644 src/app/views/concerts/post.zmpl create mode 100644 src/app/views/concerts/put.zmpl create mode 100644 src/app/views/lists.zig create mode 100644 src/app/views/lists/delete.zmpl create mode 100644 src/app/views/lists/get.zmpl create mode 100644 src/app/views/lists/index.zmpl create mode 100644 src/app/views/lists/patch.zmpl create mode 100644 src/app/views/lists/post.zmpl create mode 100644 src/app/views/lists/put.zmpl delete mode 100644 src/app/views/music.zig delete mode 100644 src/app/views/music/index.zmpl create mode 100644 src/app/views/partials/_header.zmpl rename src/app/views/{root/_random.zmpl => partials/_history.zmpl} (100%) rename src/app/views/{root/_recent.zmpl => partials/_random.zmpl} (100%) create mode 100644 src/app/views/partials/_recent.zmpl create mode 100644 src/app/views/partials/_table.zmpl rename src/app/views/{root => partials}/_top.zmpl (100%) create mode 100644 src/app/views/ratings.zig create mode 100644 src/app/views/ratings/delete.zmpl create mode 100644 src/app/views/ratings/get.zmpl create mode 100644 src/app/views/ratings/index.zmpl create mode 100644 src/app/views/ratings/patch.zmpl create mode 100644 src/app/views/ratings/post.zmpl create mode 100644 src/app/views/ratings/put.zmpl delete mode 100644 src/app/views/root/_header.zmpl create mode 100644 src/app/views/scrobbles.zig create mode 100644 src/app/views/scrobbles/delete.zmpl create mode 100644 src/app/views/scrobbles/get.zmpl create mode 100644 src/app/views/scrobbles/index.zmpl create mode 100644 src/app/views/scrobbles/patch.zmpl create mode 100644 src/app/views/scrobbles/post.zmpl create mode 100644 src/app/views/scrobbles/put.zmpl delete mode 100644 src/app/views/stats.zig create mode 100644 src/app/views/upload.zig create mode 100644 src/app/views/upload/delete.zmpl create mode 100644 src/app/views/upload/get.zmpl create mode 100644 src/app/views/upload/index.zmpl create mode 100644 src/app/views/upload/patch.zmpl create mode 100644 src/app/views/upload/post.zmpl create mode 100644 src/app/views/upload/put.zmpl diff --git a/build.zig.zon b/build.zig.zon index 9b7f253..e541ab2 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -16,8 +16,8 @@ // internet connectivity. .dependencies = .{ .jetzig = .{ - .url = "https://github.com/jetzig-framework/jetzig/archive/dda433bb73000614482af10a277d47dc9d89600c.tar.gz", - .hash = "12202ce84b803a8b300c91d98afbc7c326298b55a23bf05cf603182e934b621008ec", + .url = "https://github.com/jetzig-framework/jetzig/archive/d887cd5bd88eef611fc145e8acb12eee05b9aeef.tar.gz", + .hash = "12204458ae2b5cb93339296d7ac0f90fc0a0292711fcd82156b6595131fc0a96c648", }, }, .paths = .{ diff --git a/config/database.zig b/config/database.zig new file mode 100644 index 0000000..56b00ec --- /dev/null +++ b/config/database.zig @@ -0,0 +1,28 @@ +pub const database = .{ + .testing = .{ + .adapter = .postgresql, + .hostname = "localhost", + .port = 5432, + .username = "postgres", + .password = "postgres", + .database = "zuletzt_testing", + }, + + .development = .{ + .adapter = .postgresql, + .hostname = "localhost", + .port = 5432, + .username = "postgres", + .password = "postgres", + .database = "zuletzt_dev", + }, + + .production = .{ + .adapter = .postgresql, + .hostname = "localhost", + .port = 5432, + .username = "postgres", + .password = "postgres", + .database = "zuletzt", + }, +}; diff --git a/src/app/database/Schema.zig b/src/app/database/Schema.zig new file mode 100644 index 0000000..7e30fee --- /dev/null +++ b/src/app/database/Schema.zig @@ -0,0 +1,130 @@ +const jetquery = @import("jetzig").jetquery; + +pub const AlbumSong = jetquery.Model( + @This(), + "album_songs", + struct { + id: i32, + album_id: i32, + song_id: i32, + created_at: jetquery.DateTime, + updated_at: jetquery.DateTime, + }, + .{}, +); + +pub const Album = jetquery.Model( + @This(), + "albums", + struct { + id: i32, + title: []const u8, + song_num: i32, + length: f64, + play_count: i32, + score: f64, + avg_song_score: f64, + url: []const u8, + holiday: bool, + compilation: bool, + collaboration: bool, + created_at: jetquery.DateTime, + updated_at: jetquery.DateTime, + }, + .{ + .relations = .{ + .scrobbles = jetquery.hasMany(.Scrobble, .{}), + }, + }, +); + +pub const ArtistAlbum = jetquery.Model( + @This(), + "artist_albums", + struct { + id: i32, + artist_id: i32, + album_id: i32, + created_at: jetquery.DateTime, + updated_at: jetquery.DateTime, + }, + .{}, +); + +pub const ArtistSong = jetquery.Model( + @This(), + "artist_songs", + struct { + id: i32, + artist_id: i32, + song_id: i32, + created_at: jetquery.DateTime, + updated_at: jetquery.DateTime, + }, + .{}, +); + +pub const Artist = jetquery.Model( + @This(), + "artists", + struct { + id: i32, + name: []const u8, + album_num: i32, + song_num: i32, + play_count: i32, + avg_album_score: f64, + avg_song_score: f64, + url: []const u8, + aliased: bool, + created_at: jetquery.DateTime, + updated_at: jetquery.DateTime, + }, + .{ + .relations = .{ + .scrobbles = jetquery.hasMany(.Scrobble, .{}), + }, + }, +); + +pub const Scrobble = jetquery.Model( + @This(), + "scrobbles", + struct { + id: i32, + date: jetquery.DateTime, + created_at: jetquery.DateTime, + updated_at: jetquery.DateTime, + }, + .{ + .relations = .{ + .song = jetquery.belongsTo(.Song, .{}), + .album = jetquery.belongsTo(.Album, .{}), + .artist = jetquery.belongsTo(.Artist, .{}), + }, + }, +); + +pub const Song = jetquery.Model( + @This(), + "songs", + struct { + id: i32, + name: []const u8, + play_count: i32, + length: f64, + score: f64, + url: []const u8, + aliased: bool, + track_num: i32, + hidden: bool, + holiday: bool, + created_at: jetquery.DateTime, + updated_at: jetquery.DateTime, + }, + .{ + .relations = .{ + .scrobbles = jetquery.hasMany(.Scrobble, .{}), + }, + }, +); diff --git a/src/app/database/data.db b/src/app/database/data.db deleted file mode 100644 index fa37ab7169d9ab40592d86dcc9c906a28a52db95..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 zcmeI%&rTCT7y$6uEkoN7I^tL>HVwl+E3CTPEs}6B2&5Vm1Zf35nFU5zU3Tm4R>d3d zVB!mSF!4g7kKh9sV|)fZn0W93{C3;Qp97eXc=3aD_xrwCW@dk!zG?ODoE=K>$n&?X zP?X6jqA29L5QLCX_;kRh{gZ@Xf3CC#j<}DKD~~^QGn*tbyCk#A-m;z}p2i^*Kmim$ z0Te(16hHwKKmim$0TgHnJXDgod|uhfhSsVhEkCq_FlgP>nWf59ts-hu({mNkDvFE6 zx?^nz#Y>{tEI~BT@STBlt#ZExan$<`q-XEcDmN=jBg=_ouD4g&Nj4X7RvTM^6<<3J zP9N7`_INOw{m(Kmd9=pCd13#PC-^TetNXVV`qtW$pm|Lk*jBR!+lqtZx7E!qlTOw} z*emGG%WRo-u`ldh=FQ*#p5UeMPf-8`Pyhu`00mG01yBG5Pyhu`;NJ^~vs4`}rj%c| zZFfV=`1V%dxq6gnt%TJ{Cn5c3D2M0DhEdy;QY>tWs+55qCECKK7A5DhR4w$S5@O-G zBWCPyTZcf40``Lp9rtRdd#IZ0O{G7s*v^`#$JbF#RlSr-e1m1g%4YPPeKqv$dUIhd zu4t`FE=PI3kQy_Ls_S`m>FcdKEX)rr-p!^-l3gI|0{h9nvJdPvd&(9MJ%lL=pa2S> z01BW03ZMWApa2S>01EsU0_PQ4;C)Ipw0uVffoT|eJ5~W8vxm@^q=S4=neiGMa4xXz zH|nM_9%ovdsY9K#$VZgwGxh1L>H?m_F~V%OKR| zy+$Ba(E)1=D^+`|?!YmC=WdurS#PFVl&a?vG|vl4bXHJt*G@$1MUa)(6Z-WsEpa2S>01BW03ZMWApa2S>01Eu)0!*Qt zXVXh^y#%KLa08F7mPvmn9p=S!)P6(9?uM4_mY~Itdhm8WLFqL^VgXx7eX}O}ZE#cv^9%ovVWfQcQ=hG`vzLb6m UTK{I0Rpat(;J`#BX#ShgZ+qh$&Hw-a diff --git a/src/app/database/migrations/2024-11-15_14-46-12_create_artists.zig b/src/app/database/migrations/2024-11-15_14-46-12_create_artists.zig new file mode 100644 index 0000000..224877b --- /dev/null +++ b/src/app/database/migrations/2024-11-15_14-46-12_create_artists.zig @@ -0,0 +1,26 @@ +const std = @import("std"); +const jetquery = @import("jetquery"); +const t = jetquery.schema.table; + +pub fn up(repo: anytype) !void { + try repo.createTable( + "artists", + &.{ + t.primaryKey("id", .{}), + t.column("name", .string, .{}), + t.column("album_num", .integer, .{}), + t.column("song_num", .integer, .{}), + t.column("play_count", .integer, .{}), + t.column("avg_album_score", .float, .{}), + t.column("avg_song_score", .float, .{}), + t.column("url", .string, .{}), + t.column("aliased", .boolean, .{}), + t.timestamps(.{}), + }, + .{}, + ); +} + +pub fn down(repo: anytype) !void { + try repo.dropTable("artists", .{}); +} diff --git a/src/app/database/migrations/2024-11-15_14-58-47_create_songs.zig b/src/app/database/migrations/2024-11-15_14-58-47_create_songs.zig new file mode 100644 index 0000000..4852b4d --- /dev/null +++ b/src/app/database/migrations/2024-11-15_14-58-47_create_songs.zig @@ -0,0 +1,27 @@ +const std = @import("std"); +const jetquery = @import("jetquery"); +const t = jetquery.schema.table; + +pub fn up(repo: anytype) !void { + try repo.createTable( + "songs", + &.{ + t.primaryKey("id", .{}), + t.column("name", .string, .{}), + t.column("play_count", .integer, .{}), + t.column("length", .float, .{}), + t.column("score", .float, .{}), + t.column("url", .string, .{}), + t.column("aliased", .boolean, .{}), + t.column("track_num", .integer, .{}), + t.column("hidden", .boolean, .{}), + t.column("holiday", .boolean, .{}), + t.timestamps(.{}), + }, + .{}, + ); +} + +pub fn down(repo: anytype) !void { + try repo.dropTable("songs", .{}); +} diff --git a/src/app/database/migrations/2024-11-15_17-52-04_create_artist_songs.zig b/src/app/database/migrations/2024-11-15_17-52-04_create_artist_songs.zig new file mode 100644 index 0000000..03f6ec4 --- /dev/null +++ b/src/app/database/migrations/2024-11-15_17-52-04_create_artist_songs.zig @@ -0,0 +1,20 @@ +const std = @import("std"); +const jetquery = @import("jetquery"); +const t = jetquery.schema.table; + +pub fn up(repo: anytype) !void { + try repo.createTable( + "artist_songs", + &.{ + t.primaryKey("id", .{}), + t.column("artist_id", .integer, .{}), + t.column("song_id", .integer, .{}), + t.timestamps(.{}), + }, + .{}, + ); +} + +pub fn down(repo: anytype) !void { + try repo.dropTable("artist_songs", .{}); +} diff --git a/src/app/database/migrations/2024-11-15_17-52-28_create_artist_albums.zig b/src/app/database/migrations/2024-11-15_17-52-28_create_artist_albums.zig new file mode 100644 index 0000000..3e186ad --- /dev/null +++ b/src/app/database/migrations/2024-11-15_17-52-28_create_artist_albums.zig @@ -0,0 +1,20 @@ +const std = @import("std"); +const jetquery = @import("jetquery"); +const t = jetquery.schema.table; + +pub fn up(repo: anytype) !void { + try repo.createTable( + "artist_albums", + &.{ + t.primaryKey("id", .{}), + t.column("artist_id", .integer, .{}), + t.column("album_id", .integer, .{}), + t.timestamps(.{}), + }, + .{}, + ); +} + +pub fn down(repo: anytype) !void { + try repo.dropTable("artist_albums", .{}); +} diff --git a/src/app/database/migrations/2024-11-15_17-52-54_create_album_songs.zig b/src/app/database/migrations/2024-11-15_17-52-54_create_album_songs.zig new file mode 100644 index 0000000..9462f82 --- /dev/null +++ b/src/app/database/migrations/2024-11-15_17-52-54_create_album_songs.zig @@ -0,0 +1,20 @@ +const std = @import("std"); +const jetquery = @import("jetquery"); +const t = jetquery.schema.table; + +pub fn up(repo: anytype) !void { + try repo.createTable( + "album_songs", + &.{ + t.primaryKey("id", .{}), + t.column("album_id", .integer, .{}), + t.column("song_id", .integer, .{}), + t.timestamps(.{}), + }, + .{}, + ); +} + +pub fn down(repo: anytype) !void { + try repo.dropTable("album_songs", .{}); +} diff --git a/src/app/database/migrations/2024-11-20_00-19-54_create_scrobble.zig b/src/app/database/migrations/2024-11-20_00-19-54_create_scrobble.zig new file mode 100644 index 0000000..c067423 --- /dev/null +++ b/src/app/database/migrations/2024-11-20_00-19-54_create_scrobble.zig @@ -0,0 +1,19 @@ +const std = @import("std"); +const jetquery = @import("jetquery"); +const t = jetquery.schema.table; + +pub fn up(repo: anytype) !void { + try repo.createTable( + "scrobbles", + &.{ + t.primaryKey("id", .{}), + t.column("date", .datetime, .{}), + t.timestamps(.{}), + }, + .{}, + ); +} + +pub fn down(repo: anytype) !void { + try repo.dropTable("scrobbles", .{}); +} diff --git a/src/app/database/migrations/2024-11-20_19-08-02_create_albums.zig b/src/app/database/migrations/2024-11-20_19-08-02_create_albums.zig new file mode 100644 index 0000000..292793e --- /dev/null +++ b/src/app/database/migrations/2024-11-20_19-08-02_create_albums.zig @@ -0,0 +1,28 @@ +const std = @import("std"); +const jetquery = @import("jetquery"); +const t = jetquery.schema.table; + +pub fn up(repo: anytype) !void { + try repo.createTable( + "albums", + &.{ + t.primaryKey("id", .{}), + t.column("title", .string, .{}), + t.column("song_num", .integer, .{}), + t.column("length", .float, .{}), + t.column("play_count", .integer, .{}), + t.column("score", .float, .{}), + t.column("avg_song_score", .float, .{}), + t.column("url", .string, .{}), + t.column("holiday", .boolean, .{}), + t.column("compilation", .boolean, .{}), + t.column("collaboration", .boolean, .{}), + t.timestamps(.{}), + }, + .{}, + ); +} + +pub fn down(repo: anytype) !void { + try repo.dropTable("albums", .{}); +} diff --git a/src/app/lib/db.zig b/src/app/lib/db.zig deleted file mode 100644 index dd6f7bf..0000000 --- a/src/app/lib/db.zig +++ /dev/null @@ -1,22 +0,0 @@ -pub const addArtist = \\INSERT INTO artists ('artist', 'plays', 'url') VALUES (?,?) -; - -pub const addTrack = \\INSERT INTO tracks ('artist', 'track', 'album', 'plays', 'url') VALUES (?,?,?,?) -; - -pub const getArtist = \\SELECT artist, plays FROM artists WHERE artist == ? -; - -pub const getTrack = \\SELECT artist, track, album, plays FROM tracks WHERE track == ? -; - -pub const getTrackSearch = \\SELECT track, url, form FROM tracks WHERE track LIKE '%' || ? || '%' -; - -pub const getArtistSearch = \\SELECT artist, url, form FROM artists WHERE artist LIKE '%' || ? || '%' -; - -pub const getAlbumSearch = \\SELECT album, url, form FROM albums WHERE album LIKE '%' || ? || '%' -; - -pub const total_search = getArtistSearch ++ " UNION " ++ getAlbumSearch ++ " UNION " ++ getTrackSearch; \ No newline at end of file diff --git a/src/app/views/root/_graph.zmpl b/src/app/middleware/lastfmMiddleware.zig similarity index 100% rename from src/app/views/root/_graph.zmpl rename to src/app/middleware/lastfmMiddleware.zig diff --git a/src/app/views/root/_history.zmpl b/src/app/middleware/spotifyMiddleware.zig similarity index 100% rename from src/app/views/root/_history.zmpl rename to src/app/middleware/spotifyMiddleware.zig diff --git a/src/app/views/collection.zig b/src/app/views/collection.zig new file mode 100644 index 0000000..8125efd --- /dev/null +++ b/src/app/views/collection.zig @@ -0,0 +1,36 @@ +const std = @import("std"); +const jetzig = @import("jetzig"); + +pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + return request.render(.ok); +} + +pub fn get(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + _ = id; + return request.render(.ok); +} + +pub fn post(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + return request.render(.created); +} + +pub fn put(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + _ = id; + return request.render(.ok); +} + +pub fn patch(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + _ = id; + return request.render(.ok); +} + +pub fn delete(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + _ = id; + return request.render(.ok); +} diff --git a/src/app/views/stats/index.zmpl b/src/app/views/collection/delete.zmpl similarity index 100% rename from src/app/views/stats/index.zmpl rename to src/app/views/collection/delete.zmpl diff --git a/src/app/views/collection/get.zmpl b/src/app/views/collection/get.zmpl new file mode 100644 index 0000000..76457d0 --- /dev/null +++ b/src/app/views/collection/get.zmpl @@ -0,0 +1,3 @@ +
+ Content goes here +
diff --git a/src/app/views/collection/index.zmpl b/src/app/views/collection/index.zmpl new file mode 100644 index 0000000..f802907 --- /dev/null +++ b/src/app/views/collection/index.zmpl @@ -0,0 +1,8 @@ + + +@partial partials/header +
+ Content goes here +
+ + \ No newline at end of file diff --git a/src/app/views/collection/patch.zmpl b/src/app/views/collection/patch.zmpl new file mode 100644 index 0000000..76457d0 --- /dev/null +++ b/src/app/views/collection/patch.zmpl @@ -0,0 +1,3 @@ +
+ Content goes here +
diff --git a/src/app/views/collection/post.zmpl b/src/app/views/collection/post.zmpl new file mode 100644 index 0000000..76457d0 --- /dev/null +++ b/src/app/views/collection/post.zmpl @@ -0,0 +1,3 @@ +
+ Content goes here +
diff --git a/src/app/views/collection/put.zmpl b/src/app/views/collection/put.zmpl new file mode 100644 index 0000000..76457d0 --- /dev/null +++ b/src/app/views/collection/put.zmpl @@ -0,0 +1,3 @@ +
+ Content goes here +
diff --git a/src/app/views/concerts.zig b/src/app/views/concerts.zig new file mode 100644 index 0000000..8125efd --- /dev/null +++ b/src/app/views/concerts.zig @@ -0,0 +1,36 @@ +const std = @import("std"); +const jetzig = @import("jetzig"); + +pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + return request.render(.ok); +} + +pub fn get(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + _ = id; + return request.render(.ok); +} + +pub fn post(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + return request.render(.created); +} + +pub fn put(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + _ = id; + return request.render(.ok); +} + +pub fn patch(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + _ = id; + return request.render(.ok); +} + +pub fn delete(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + _ = id; + return request.render(.ok); +} diff --git a/src/app/views/concerts/delete.zmpl b/src/app/views/concerts/delete.zmpl new file mode 100644 index 0000000..76457d0 --- /dev/null +++ b/src/app/views/concerts/delete.zmpl @@ -0,0 +1,3 @@ +
+ Content goes here +
diff --git a/src/app/views/concerts/get.zmpl b/src/app/views/concerts/get.zmpl new file mode 100644 index 0000000..76457d0 --- /dev/null +++ b/src/app/views/concerts/get.zmpl @@ -0,0 +1,3 @@ +
+ Content goes here +
diff --git a/src/app/views/concerts/index.zmpl b/src/app/views/concerts/index.zmpl new file mode 100644 index 0000000..f802907 --- /dev/null +++ b/src/app/views/concerts/index.zmpl @@ -0,0 +1,8 @@ + + +@partial partials/header +
+ Content goes here +
+ + \ No newline at end of file diff --git a/src/app/views/concerts/patch.zmpl b/src/app/views/concerts/patch.zmpl new file mode 100644 index 0000000..76457d0 --- /dev/null +++ b/src/app/views/concerts/patch.zmpl @@ -0,0 +1,3 @@ +
+ Content goes here +
diff --git a/src/app/views/concerts/post.zmpl b/src/app/views/concerts/post.zmpl new file mode 100644 index 0000000..76457d0 --- /dev/null +++ b/src/app/views/concerts/post.zmpl @@ -0,0 +1,3 @@ +
+ Content goes here +
diff --git a/src/app/views/concerts/put.zmpl b/src/app/views/concerts/put.zmpl new file mode 100644 index 0000000..76457d0 --- /dev/null +++ b/src/app/views/concerts/put.zmpl @@ -0,0 +1,3 @@ +
+ Content goes here +
diff --git a/src/app/views/lists.zig b/src/app/views/lists.zig new file mode 100644 index 0000000..8125efd --- /dev/null +++ b/src/app/views/lists.zig @@ -0,0 +1,36 @@ +const std = @import("std"); +const jetzig = @import("jetzig"); + +pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + return request.render(.ok); +} + +pub fn get(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + _ = id; + return request.render(.ok); +} + +pub fn post(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + return request.render(.created); +} + +pub fn put(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + _ = id; + return request.render(.ok); +} + +pub fn patch(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + _ = id; + return request.render(.ok); +} + +pub fn delete(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + _ = id; + return request.render(.ok); +} diff --git a/src/app/views/lists/delete.zmpl b/src/app/views/lists/delete.zmpl new file mode 100644 index 0000000..76457d0 --- /dev/null +++ b/src/app/views/lists/delete.zmpl @@ -0,0 +1,3 @@ +
+ Content goes here +
diff --git a/src/app/views/lists/get.zmpl b/src/app/views/lists/get.zmpl new file mode 100644 index 0000000..76457d0 --- /dev/null +++ b/src/app/views/lists/get.zmpl @@ -0,0 +1,3 @@ +
+ Content goes here +
diff --git a/src/app/views/lists/index.zmpl b/src/app/views/lists/index.zmpl new file mode 100644 index 0000000..f802907 --- /dev/null +++ b/src/app/views/lists/index.zmpl @@ -0,0 +1,8 @@ + + +@partial partials/header +
+ Content goes here +
+ + \ No newline at end of file diff --git a/src/app/views/lists/patch.zmpl b/src/app/views/lists/patch.zmpl new file mode 100644 index 0000000..76457d0 --- /dev/null +++ b/src/app/views/lists/patch.zmpl @@ -0,0 +1,3 @@ +
+ Content goes here +
diff --git a/src/app/views/lists/post.zmpl b/src/app/views/lists/post.zmpl new file mode 100644 index 0000000..76457d0 --- /dev/null +++ b/src/app/views/lists/post.zmpl @@ -0,0 +1,3 @@ +
+ Content goes here +
diff --git a/src/app/views/lists/put.zmpl b/src/app/views/lists/put.zmpl new file mode 100644 index 0000000..76457d0 --- /dev/null +++ b/src/app/views/lists/put.zmpl @@ -0,0 +1,3 @@ +
+ Content goes here +
diff --git a/src/app/views/music.zig b/src/app/views/music.zig deleted file mode 100644 index 4dd0d07..0000000 --- a/src/app/views/music.zig +++ /dev/null @@ -1,41 +0,0 @@ -const std = @import("std"); -const jetzig = @import("jetzig"); - -pub const static_params = .{ - .index = .{ - //.{ .params = .{ .foo = "hi", .bar = "bye" } }, - //.{ .params = .{ .foo = "hello", .bar = "goodbye" } }, - }, - //.get = .{ - // .{ .id = "1", .params = .{ .foo = "hi", .bar = "bye" } }, - // .{ .id = "2", .params = .{ .foo = "hello", .bar = "goodbye" } }, - //}, -}; - -pub fn index(request: *jetzig.StaticRequest, data: *jetzig.Data) !jetzig.View { - var root = try data.object(); - - const params = try request.params(); - - if (params.get("foo")) |foo| try root.put("foo", foo); - if (params.get("foo") == null) try root.put("foo", data.string("no foo")); - if (params.get("bar")) |bar| try root.put("bar", bar); - if (params.get("bar") == null) try root.put("bar", data.string("no bar")); - - return request.render(.ok); -} - -//pub fn get(id: []const u8, request: *jetzig.StaticRequest, data: *jetzig.Data) !jetzig.View { -// var root = try data.object(); -// -// const params = try request.params(); -// -// if (std.mem.eql(u8, id, "1")) { -// try root.put("id", data.string("id is '1'")); -// } -// -// if (params.get("foo")) |foo| try root.put("foo", foo); -// if (params.get("bar")) |bar| try root.put("bar", bar); -// -// return request.render(.created); -//} \ No newline at end of file diff --git a/src/app/views/music/index.zmpl b/src/app/views/music/index.zmpl deleted file mode 100644 index a14b357..0000000 --- a/src/app/views/music/index.zmpl +++ /dev/null @@ -1,2 +0,0 @@ -
{{.foo}}
-
{{.bar}}
\ No newline at end of file diff --git a/src/app/views/partials/_header.zmpl b/src/app/views/partials/_header.zmpl new file mode 100644 index 0000000..bf80e53 --- /dev/null +++ b/src/app/views/partials/_header.zmpl @@ -0,0 +1,7 @@ +Zuletzt +Scrobbles +Concerts +Collection +Ratings +Lists +
\ No newline at end of file diff --git a/src/app/views/root/_random.zmpl b/src/app/views/partials/_history.zmpl similarity index 100% rename from src/app/views/root/_random.zmpl rename to src/app/views/partials/_history.zmpl diff --git a/src/app/views/root/_recent.zmpl b/src/app/views/partials/_random.zmpl similarity index 100% rename from src/app/views/root/_recent.zmpl rename to src/app/views/partials/_random.zmpl diff --git a/src/app/views/partials/_recent.zmpl b/src/app/views/partials/_recent.zmpl new file mode 100644 index 0000000..e69de29 diff --git a/src/app/views/partials/_table.zmpl b/src/app/views/partials/_table.zmpl new file mode 100644 index 0000000..e69de29 diff --git a/src/app/views/root/_top.zmpl b/src/app/views/partials/_top.zmpl similarity index 100% rename from src/app/views/root/_top.zmpl rename to src/app/views/partials/_top.zmpl diff --git a/src/app/views/ratings.zig b/src/app/views/ratings.zig new file mode 100644 index 0000000..8125efd --- /dev/null +++ b/src/app/views/ratings.zig @@ -0,0 +1,36 @@ +const std = @import("std"); +const jetzig = @import("jetzig"); + +pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + return request.render(.ok); +} + +pub fn get(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + _ = id; + return request.render(.ok); +} + +pub fn post(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + return request.render(.created); +} + +pub fn put(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + _ = id; + return request.render(.ok); +} + +pub fn patch(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + _ = id; + return request.render(.ok); +} + +pub fn delete(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + _ = id; + return request.render(.ok); +} diff --git a/src/app/views/ratings/delete.zmpl b/src/app/views/ratings/delete.zmpl new file mode 100644 index 0000000..76457d0 --- /dev/null +++ b/src/app/views/ratings/delete.zmpl @@ -0,0 +1,3 @@ +
+ Content goes here +
diff --git a/src/app/views/ratings/get.zmpl b/src/app/views/ratings/get.zmpl new file mode 100644 index 0000000..76457d0 --- /dev/null +++ b/src/app/views/ratings/get.zmpl @@ -0,0 +1,3 @@ +
+ Content goes here +
diff --git a/src/app/views/ratings/index.zmpl b/src/app/views/ratings/index.zmpl new file mode 100644 index 0000000..f802907 --- /dev/null +++ b/src/app/views/ratings/index.zmpl @@ -0,0 +1,8 @@ + + +@partial partials/header +
+ Content goes here +
+ + \ No newline at end of file diff --git a/src/app/views/ratings/patch.zmpl b/src/app/views/ratings/patch.zmpl new file mode 100644 index 0000000..76457d0 --- /dev/null +++ b/src/app/views/ratings/patch.zmpl @@ -0,0 +1,3 @@ +
+ Content goes here +
diff --git a/src/app/views/ratings/post.zmpl b/src/app/views/ratings/post.zmpl new file mode 100644 index 0000000..76457d0 --- /dev/null +++ b/src/app/views/ratings/post.zmpl @@ -0,0 +1,3 @@ +
+ Content goes here +
diff --git a/src/app/views/ratings/put.zmpl b/src/app/views/ratings/put.zmpl new file mode 100644 index 0000000..76457d0 --- /dev/null +++ b/src/app/views/ratings/put.zmpl @@ -0,0 +1,3 @@ +
+ Content goes here +
diff --git a/src/app/views/root/_header.zmpl b/src/app/views/root/_header.zmpl deleted file mode 100644 index 3362c8f..0000000 --- a/src/app/views/root/_header.zmpl +++ /dev/null @@ -1,6 +0,0 @@ -Zuletzt -Artists -Albums -Tracks -Stats -
\ No newline at end of file diff --git a/src/app/views/root/index.zmpl b/src/app/views/root/index.zmpl index ea52da6..c7615bb 100644 --- a/src/app/views/root/index.zmpl +++ b/src/app/views/root/index.zmpl @@ -4,11 +4,10 @@ - - @partial root/header - @partial root/top + @partial partials/header + @partial partials/top diff --git a/src/app/views/scrobbles.zig b/src/app/views/scrobbles.zig new file mode 100644 index 0000000..8125efd --- /dev/null +++ b/src/app/views/scrobbles.zig @@ -0,0 +1,36 @@ +const std = @import("std"); +const jetzig = @import("jetzig"); + +pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + return request.render(.ok); +} + +pub fn get(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + _ = id; + return request.render(.ok); +} + +pub fn post(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + return request.render(.created); +} + +pub fn put(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + _ = id; + return request.render(.ok); +} + +pub fn patch(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + _ = id; + return request.render(.ok); +} + +pub fn delete(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + _ = id; + return request.render(.ok); +} diff --git a/src/app/views/scrobbles/delete.zmpl b/src/app/views/scrobbles/delete.zmpl new file mode 100644 index 0000000..76457d0 --- /dev/null +++ b/src/app/views/scrobbles/delete.zmpl @@ -0,0 +1,3 @@ +
+ Content goes here +
diff --git a/src/app/views/scrobbles/get.zmpl b/src/app/views/scrobbles/get.zmpl new file mode 100644 index 0000000..76457d0 --- /dev/null +++ b/src/app/views/scrobbles/get.zmpl @@ -0,0 +1,3 @@ +
+ Content goes here +
diff --git a/src/app/views/scrobbles/index.zmpl b/src/app/views/scrobbles/index.zmpl new file mode 100644 index 0000000..9bf7e31 --- /dev/null +++ b/src/app/views/scrobbles/index.zmpl @@ -0,0 +1,8 @@ + + +@partial partials/header +
+ Content goes here +
+ + diff --git a/src/app/views/scrobbles/patch.zmpl b/src/app/views/scrobbles/patch.zmpl new file mode 100644 index 0000000..76457d0 --- /dev/null +++ b/src/app/views/scrobbles/patch.zmpl @@ -0,0 +1,3 @@ +
+ Content goes here +
diff --git a/src/app/views/scrobbles/post.zmpl b/src/app/views/scrobbles/post.zmpl new file mode 100644 index 0000000..76457d0 --- /dev/null +++ b/src/app/views/scrobbles/post.zmpl @@ -0,0 +1,3 @@ +
+ Content goes here +
diff --git a/src/app/views/scrobbles/put.zmpl b/src/app/views/scrobbles/put.zmpl new file mode 100644 index 0000000..76457d0 --- /dev/null +++ b/src/app/views/scrobbles/put.zmpl @@ -0,0 +1,3 @@ +
+ Content goes here +
diff --git a/src/app/views/stats.zig b/src/app/views/stats.zig deleted file mode 100644 index 4fac587..0000000 --- a/src/app/views/stats.zig +++ /dev/null @@ -1,7 +0,0 @@ -const std = @import("std"); -const jetzig = @import("jetzig"); - -pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { - _ = data; - return request.render(.ok); -} diff --git a/src/app/views/upload.zig b/src/app/views/upload.zig new file mode 100644 index 0000000..491579c --- /dev/null +++ b/src/app/views/upload.zig @@ -0,0 +1,48 @@ +const std = @import("std"); +const jetzig = @import("jetzig"); +const jetquery = @import("jetzig").jetquery; + +pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + try request.repo.insert(.Artist, .{ + .id = 123, + .name = "wilco", + .album_num = 10, + .song_num = 200, + .play_count = 2700, + .avg_album_score = 10.0, + .avg_song_score = 10.0, + .url = "/wilco", + .aliased = false, + }); + return request.render(.ok); +} + +pub fn get(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + _ = id; + return request.render(.ok); +} + +pub fn post(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + return request.render(.created); +} + +pub fn put(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + _ = id; + return request.render(.ok); +} + +pub fn patch(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + _ = id; + return request.render(.ok); +} + +pub fn delete(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { + _ = data; + _ = id; + return request.render(.ok); +} diff --git a/src/app/views/upload/delete.zmpl b/src/app/views/upload/delete.zmpl new file mode 100644 index 0000000..76457d0 --- /dev/null +++ b/src/app/views/upload/delete.zmpl @@ -0,0 +1,3 @@ +
+ Content goes here +
diff --git a/src/app/views/upload/get.zmpl b/src/app/views/upload/get.zmpl new file mode 100644 index 0000000..76457d0 --- /dev/null +++ b/src/app/views/upload/get.zmpl @@ -0,0 +1,3 @@ +
+ Content goes here +
diff --git a/src/app/views/upload/index.zmpl b/src/app/views/upload/index.zmpl new file mode 100644 index 0000000..66a79f1 --- /dev/null +++ b/src/app/views/upload/index.zmpl @@ -0,0 +1,19 @@ + + +@partial partials/header +
+ Upload Last.fm or Spotify history file here (in json format). +
+
+ + + + + +
+ Last.fm + Spotify +
+
+ + \ No newline at end of file diff --git a/src/app/views/upload/patch.zmpl b/src/app/views/upload/patch.zmpl new file mode 100644 index 0000000..76457d0 --- /dev/null +++ b/src/app/views/upload/patch.zmpl @@ -0,0 +1,3 @@ +
+ Content goes here +
diff --git a/src/app/views/upload/post.zmpl b/src/app/views/upload/post.zmpl new file mode 100644 index 0000000..76457d0 --- /dev/null +++ b/src/app/views/upload/post.zmpl @@ -0,0 +1,3 @@ +
+ Content goes here +
diff --git a/src/app/views/upload/put.zmpl b/src/app/views/upload/put.zmpl new file mode 100644 index 0000000..76457d0 --- /dev/null +++ b/src/app/views/upload/put.zmpl @@ -0,0 +1,3 @@ +
+ Content goes here +
diff --git a/src/main.zig b/src/main.zig index ec577ff..3727351 100644 --- a/src/main.zig +++ b/src/main.zig @@ -5,9 +5,9 @@ const zmd = @import("zmd"); const builtin = @import("builtin"); pub const static = @import("static"); - // Override default settings in `jetzig.config` here: pub const jetzig_options = struct { + pub const Schema = @import("Schema"); /// Middleware chain. Add any custom middleware here, or use middleware provided in /// `jetzig.middleware` (e.g. `jetzig.middleware.HtmxMiddleware`). pub const middleware: []const type = &.{ @@ -95,20 +95,20 @@ pub fn main() !void { // }, // .threading_mode = .MultiThread, //}); - - //const create = + + //const create = // \\CREATE TABLE artists ('artist', 'plays') //; - //const query = + //const query = // \\INSERT INTO artists ('artist', 'plays') VALUES (?,?) //; //var build = try db.prepare(create); //defer build.deinit(); - + //try build.exec(.{},.{}); - + //var stmt = try db.prepare(query); //defer stmt.deinit(); @@ -117,7 +117,6 @@ pub fn main() !void { // .plays = 2500, //}); - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer std.debug.assert(gpa.deinit() == .ok); const allocator = gpa.allocator(); From 51df37aa21a1e63f9c3892918d961eb85490ebb3 Mon Sep 17 00:00:00 2001 From: mitteneer Date: Thu, 21 Nov 2024 18:45:13 -0500 Subject: [PATCH 17/27] Create migrations --- .../2024-11-21_21-51-03_create_ratings.zig | 21 +++++++++++++++++++ .../2024-11-21_21-51-38_create_aliases.zig | 20 ++++++++++++++++++ .../2024-11-21_21-52-55_create_table.zig | 20 ++++++++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 src/app/database/migrations/2024-11-21_21-51-03_create_ratings.zig create mode 100644 src/app/database/migrations/2024-11-21_21-51-38_create_aliases.zig create mode 100644 src/app/database/migrations/2024-11-21_21-52-55_create_table.zig diff --git a/src/app/database/migrations/2024-11-21_21-51-03_create_ratings.zig b/src/app/database/migrations/2024-11-21_21-51-03_create_ratings.zig new file mode 100644 index 0000000..d7ac939 --- /dev/null +++ b/src/app/database/migrations/2024-11-21_21-51-03_create_ratings.zig @@ -0,0 +1,21 @@ +const std = @import("std"); +const jetquery = @import("jetquery"); +const t = jetquery.schema.table; + +pub fn up(repo: anytype) !void { + try repo.createTable( + "ratings", + &.{ + t.primaryKey("id", .{}), + t.column("reference_id", .integer, .{}), + t.column("score", .float, .{}), + t.column("date", .datetime, .{}), + t.timestamps(.{}), + }, + .{}, + ); +} + +pub fn down(repo: anytype) !void { + try repo.dropTable("ratings", .{}); +} diff --git a/src/app/database/migrations/2024-11-21_21-51-38_create_aliases.zig b/src/app/database/migrations/2024-11-21_21-51-38_create_aliases.zig new file mode 100644 index 0000000..11cbb70 --- /dev/null +++ b/src/app/database/migrations/2024-11-21_21-51-38_create_aliases.zig @@ -0,0 +1,20 @@ +const std = @import("std"); +const jetquery = @import("jetquery"); +const t = jetquery.schema.table; + +pub fn up(repo: anytype) !void { + try repo.createTable( + "aliases", + &.{ + t.primaryKey("id", .{}), + t.column("reference_id", .integer, .{}), + t.column("alias", .string, .{}), + t.timestamps(.{}), + }, + .{}, + ); +} + +pub fn down(repo: anytype) !void { + try repo.dropTable("aliases", .{}); +} diff --git a/src/app/database/migrations/2024-11-21_21-52-55_create_table.zig b/src/app/database/migrations/2024-11-21_21-52-55_create_table.zig new file mode 100644 index 0000000..026899e --- /dev/null +++ b/src/app/database/migrations/2024-11-21_21-52-55_create_table.zig @@ -0,0 +1,20 @@ +const std = @import("std"); +const jetquery = @import("jetquery"); +const t = jetquery.schema.table; + +pub fn up(repo: anytype) !void { + try repo.createTable( + "concerts", + &.{ + t.primaryKey("id", .{}), + t.column("date", .datetime, .{}), + t.column("location", .string, .{}), + t.timestamps(.{}), + }, + .{}, + ); +} + +pub fn down(repo: anytype) !void { + try repo.dropTable("concerts", .{}); +} From e4e2115753b16ea203c5a30499434e002fad3982 Mon Sep 17 00:00:00 2001 From: Samuel Webb Date: Thu, 21 Nov 2024 18:53:12 -0500 Subject: [PATCH 18/27] Update concert migration --- build.zig.zon | 4 ++-- ...eate_table.zig => 2024-11-21_23-49-05_create_concerts.zig} | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) rename src/app/database/migrations/{2024-11-21_21-52-55_create_table.zig => 2024-11-21_23-49-05_create_concerts.zig} (91%) diff --git a/build.zig.zon b/build.zig.zon index e541ab2..c7e8caf 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -16,8 +16,8 @@ // internet connectivity. .dependencies = .{ .jetzig = .{ - .url = "https://github.com/jetzig-framework/jetzig/archive/d887cd5bd88eef611fc145e8acb12eee05b9aeef.tar.gz", - .hash = "12204458ae2b5cb93339296d7ac0f90fc0a0292711fcd82156b6595131fc0a96c648", + .url = "https://github.com/jetzig-framework/jetzig/archive/b1becfd6ec3bde7e0fe69ae03690dde6ef55e483.tar.gz", + .hash = "12201b18d05071a10de4b5cdb12712b6bae5f3d88b54e4d4950cb38f86869b6fc310", }, }, .paths = .{ diff --git a/src/app/database/migrations/2024-11-21_21-52-55_create_table.zig b/src/app/database/migrations/2024-11-21_23-49-05_create_concerts.zig similarity index 91% rename from src/app/database/migrations/2024-11-21_21-52-55_create_table.zig rename to src/app/database/migrations/2024-11-21_23-49-05_create_concerts.zig index 026899e..824b8d6 100644 --- a/src/app/database/migrations/2024-11-21_21-52-55_create_table.zig +++ b/src/app/database/migrations/2024-11-21_23-49-05_create_concerts.zig @@ -7,8 +7,9 @@ pub fn up(repo: anytype) !void { "concerts", &.{ t.primaryKey("id", .{}), - t.column("date", .datetime, .{}), + t.column("id", .integer, .{}), t.column("location", .string, .{}), + t.column("date", .datetime, .{}), t.timestamps(.{}), }, .{}, From 06fbe78dcaa3e16cede22e6b31a4007419db8aa9 Mon Sep 17 00:00:00 2001 From: mitteneer Date: Thu, 21 Nov 2024 21:09:12 -0500 Subject: [PATCH 19/27] Update Schema --- src/app/database/Schema.zig | 55 ++++++++++++++----- .../2024-11-15_14-46-12_create_artists.zig | 4 -- .../2024-11-15_14-58-47_create_songs.zig | 8 +-- .../2024-11-20_19-08-02_create_albums.zig | 6 +- .../2024-11-21_23-49-05_create_concerts.zig | 1 - 5 files changed, 45 insertions(+), 29 deletions(-) diff --git a/src/app/database/Schema.zig b/src/app/database/Schema.zig index 7e30fee..764a544 100644 --- a/src/app/database/Schema.zig +++ b/src/app/database/Schema.zig @@ -22,18 +22,20 @@ pub const Album = jetquery.Model( song_num: i32, length: f64, play_count: i32, - score: f64, - avg_song_score: f64, - url: []const u8, holiday: bool, compilation: bool, - collaboration: bool, + deluxe: bool, + live: bool, created_at: jetquery.DateTime, updated_at: jetquery.DateTime, }, .{ .relations = .{ .scrobbles = jetquery.hasMany(.Scrobble, .{}), + .ratings = jetquery.hasMany(.Ratings, .{}), + .aliases = jetquery.hasMany(.Aliases, .{}), + .songs = jetquery.hasMany(.AlbumSongs, .{}), + .artists = jetquery.hasMany(.ArtistAlbums, .{}), }, }, ); @@ -73,16 +75,16 @@ pub const Artist = jetquery.Model( album_num: i32, song_num: i32, play_count: i32, - avg_album_score: f64, - avg_song_score: f64, - url: []const u8, - aliased: bool, created_at: jetquery.DateTime, updated_at: jetquery.DateTime, }, .{ .relations = .{ .scrobbles = jetquery.hasMany(.Scrobble, .{}), + .aliases = jetquery.hasMany(.Aliases, .{}), + .concerts = jetquery.hasMany(.Concerts, .{}), + .songs = jetquery.hasMany(.ArtistSongs, .{}), + .albums = jetquery.hasMany(.ArtistAlbums, .{}), }, }, ); @@ -110,21 +112,46 @@ pub const Song = jetquery.Model( "songs", struct { id: i32, - name: []const u8, - play_count: i32, + title: []const u8, length: f64, - score: f64, - url: []const u8, - aliased: bool, - track_num: i32, hidden: bool, holiday: bool, + play_count: i32, created_at: jetquery.DateTime, updated_at: jetquery.DateTime, }, .{ .relations = .{ .scrobbles = jetquery.hasMany(.Scrobble, .{}), + .ratings = jetquery.hasMany(.Ratings, .{}), + .aliases = jetquery.hasMany(.Aliases, .{}), + .artists = jetquery.hasMany(.ArtistSongs, .{}), + .albums = jetquery.hasMany(.AlbumSongs, .{}), }, }, ); + +pub const Alias = jetquery.Model(@This(), "aliases", struct { + id: i32, + reference_id: i32, + alias: []const u8, + created_at: jetquery.DateTime, + updated_at: jetquery.DateTime, +}, .{}); + +pub const Concert = jetquery.Model(@This(), "concerts", struct { + id: i32, + location: []const u8, + date: jetquery.DateTime, + created_at: jetquery.DateTime, + updated_at: jetquery.DateTime, +}, .{}); + +pub const Rating = jetquery.Model(@This(), "ratings", struct { + id: i32, + reference_id: i32, + score: f64, + date: jetquery.DateTime, + created_at: jetquery.DateTime, + updated_at: jetquery.DateTime, +}, .{}); diff --git a/src/app/database/migrations/2024-11-15_14-46-12_create_artists.zig b/src/app/database/migrations/2024-11-15_14-46-12_create_artists.zig index 224877b..db0ceae 100644 --- a/src/app/database/migrations/2024-11-15_14-46-12_create_artists.zig +++ b/src/app/database/migrations/2024-11-15_14-46-12_create_artists.zig @@ -11,10 +11,6 @@ pub fn up(repo: anytype) !void { t.column("album_num", .integer, .{}), t.column("song_num", .integer, .{}), t.column("play_count", .integer, .{}), - t.column("avg_album_score", .float, .{}), - t.column("avg_song_score", .float, .{}), - t.column("url", .string, .{}), - t.column("aliased", .boolean, .{}), t.timestamps(.{}), }, .{}, diff --git a/src/app/database/migrations/2024-11-15_14-58-47_create_songs.zig b/src/app/database/migrations/2024-11-15_14-58-47_create_songs.zig index 4852b4d..d34bc30 100644 --- a/src/app/database/migrations/2024-11-15_14-58-47_create_songs.zig +++ b/src/app/database/migrations/2024-11-15_14-58-47_create_songs.zig @@ -7,15 +7,11 @@ pub fn up(repo: anytype) !void { "songs", &.{ t.primaryKey("id", .{}), - t.column("name", .string, .{}), - t.column("play_count", .integer, .{}), + t.column("title", .string, .{}), t.column("length", .float, .{}), - t.column("score", .float, .{}), - t.column("url", .string, .{}), - t.column("aliased", .boolean, .{}), - t.column("track_num", .integer, .{}), t.column("hidden", .boolean, .{}), t.column("holiday", .boolean, .{}), + t.column("play_count", .integer, .{}), t.timestamps(.{}), }, .{}, diff --git a/src/app/database/migrations/2024-11-20_19-08-02_create_albums.zig b/src/app/database/migrations/2024-11-20_19-08-02_create_albums.zig index 292793e..a18ca74 100644 --- a/src/app/database/migrations/2024-11-20_19-08-02_create_albums.zig +++ b/src/app/database/migrations/2024-11-20_19-08-02_create_albums.zig @@ -11,12 +11,10 @@ pub fn up(repo: anytype) !void { t.column("song_num", .integer, .{}), t.column("length", .float, .{}), t.column("play_count", .integer, .{}), - t.column("score", .float, .{}), - t.column("avg_song_score", .float, .{}), - t.column("url", .string, .{}), t.column("holiday", .boolean, .{}), t.column("compilation", .boolean, .{}), - t.column("collaboration", .boolean, .{}), + t.column("deluxe", .boolean, .{}), + t.column("live", .boolean, .{}), t.timestamps(.{}), }, .{}, diff --git a/src/app/database/migrations/2024-11-21_23-49-05_create_concerts.zig b/src/app/database/migrations/2024-11-21_23-49-05_create_concerts.zig index 824b8d6..8eb782e 100644 --- a/src/app/database/migrations/2024-11-21_23-49-05_create_concerts.zig +++ b/src/app/database/migrations/2024-11-21_23-49-05_create_concerts.zig @@ -7,7 +7,6 @@ pub fn up(repo: anytype) !void { "concerts", &.{ t.primaryKey("id", .{}), - t.column("id", .integer, .{}), t.column("location", .string, .{}), t.column("date", .datetime, .{}), t.timestamps(.{}), From 32c89bc12cf6b205ffd0a9c2a0c7720194f251a4 Mon Sep 17 00:00:00 2001 From: mitteneer Date: Mon, 25 Nov 2024 12:23:36 -0500 Subject: [PATCH 20/27] Update upload page --- build.zig.zon | 4 +- public/styles.css | 5 ++ src/app/database/Schema.zig | 61 +++++++++++++------ ...24-11-25_15-32-40_create_raw_scrobbles.zig | 22 +++++++ src/app/jobs/process_scrobbles.zig | 18 ++++++ src/app/views/partials/_table.zmpl | 18 ++++++ src/app/views/upload.zig | 52 ++++++++++++---- src/app/views/upload/index.zmpl | 5 +- src/app/views/upload/post.zmpl | 18 +++++- 9 files changed, 166 insertions(+), 37 deletions(-) create mode 100644 src/app/database/migrations/2024-11-25_15-32-40_create_raw_scrobbles.zig create mode 100644 src/app/jobs/process_scrobbles.zig diff --git a/build.zig.zon b/build.zig.zon index c7e8caf..7a4efa2 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -16,8 +16,8 @@ // internet connectivity. .dependencies = .{ .jetzig = .{ - .url = "https://github.com/jetzig-framework/jetzig/archive/b1becfd6ec3bde7e0fe69ae03690dde6ef55e483.tar.gz", - .hash = "12201b18d05071a10de4b5cdb12712b6bae5f3d88b54e4d4950cb38f86869b6fc310", + .url = "https://github.com/jetzig-framework/jetzig/archive/4e1a26a5f982a0af212fe15eaffc8d7d7ea2aab6.tar.gz", + .hash = "12206bec5c95058be1d0af92cc69c48e7dd9fc19c444736f9debd7e53d10e25c22c6", }, }, .paths = .{ diff --git a/public/styles.css b/public/styles.css index afdbce9..03416fd 100644 --- a/public/styles.css +++ b/public/styles.css @@ -4,6 +4,7 @@ * */ @import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap'); + @import url('https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,100..900;1,100..900&display=swap'); .title { font-family: 'Roboto'; @@ -20,6 +21,10 @@ margin-right: 20px; } +.cell { + font-family: 'Noto Sans' +} + #replaceMe{ font-family:'Courier New'; } diff --git a/src/app/database/Schema.zig b/src/app/database/Schema.zig index 764a544..1804ff0 100644 --- a/src/app/database/Schema.zig +++ b/src/app/database/Schema.zig @@ -131,27 +131,52 @@ pub const Song = jetquery.Model( }, ); -pub const Alias = jetquery.Model(@This(), "aliases", struct { - id: i32, - reference_id: i32, - alias: []const u8, - created_at: jetquery.DateTime, - updated_at: jetquery.DateTime, -}, .{}); +pub const Alias = jetquery.Model( + @This(), + "aliases", + struct { + id: i32, + reference_id: i32, + alias: []const u8, + created_at: jetquery.DateTime, + updated_at: jetquery.DateTime, + }, + .{}, +); -pub const Concert = jetquery.Model(@This(), "concerts", struct { - id: i32, - location: []const u8, - date: jetquery.DateTime, - created_at: jetquery.DateTime, - updated_at: jetquery.DateTime, -}, .{}); +pub const Concert = jetquery.Model( + @This(), + "concerts", + struct { + id: i32, + location: []const u8, + date: jetquery.DateTime, + created_at: jetquery.DateTime, + updated_at: jetquery.DateTime, + }, + .{}, +); -pub const Rating = jetquery.Model(@This(), "ratings", struct { +pub const Rating = jetquery.Model( + @This(), + "ratings", + struct { + id: i32, + reference_id: i32, + score: f64, + date: jetquery.DateTime, + created_at: jetquery.DateTime, + updated_at: jetquery.DateTime, + }, + .{}, +); + +pub const RawScrobble = jetquery.Model(@This(), "raw_scrobbles", struct { id: i32, - reference_id: i32, - score: f64, - date: jetquery.DateTime, + track: []const u8, + artist: []const u8, + album: []const u8, + date: i32, created_at: jetquery.DateTime, updated_at: jetquery.DateTime, }, .{}); diff --git a/src/app/database/migrations/2024-11-25_15-32-40_create_raw_scrobbles.zig b/src/app/database/migrations/2024-11-25_15-32-40_create_raw_scrobbles.zig new file mode 100644 index 0000000..12f307d --- /dev/null +++ b/src/app/database/migrations/2024-11-25_15-32-40_create_raw_scrobbles.zig @@ -0,0 +1,22 @@ +const std = @import("std"); +const jetquery = @import("jetquery"); +const t = jetquery.schema.table; + +pub fn up(repo: anytype) !void { + try repo.createTable( + "raw_scrobbles", + &.{ + t.primaryKey("id", .{}), + t.column("track", .string, .{}), + t.column("artist", .string, .{}), + t.column("album", .string, .{}), + t.column("date", .integer, .{}), + t.timestamps(.{}), + }, + .{}, + ); +} + +pub fn down(repo: anytype) !void { + try repo.dropTable("raw_scrobbles", .{}); +} diff --git a/src/app/jobs/process_scrobbles.zig b/src/app/jobs/process_scrobbles.zig new file mode 100644 index 0000000..3f0c191 --- /dev/null +++ b/src/app/jobs/process_scrobbles.zig @@ -0,0 +1,18 @@ +const std = @import("std"); +const jetzig = @import("jetzig"); + +// The `run` function for a job is invoked every time the job is processed by a queue worker +// (or by the Jetzig server if the job is processed in-line). +// +// Arguments: +// * allocator: Arena allocator for use during the job execution process. +// * params: Params assigned to a job (from a request, any values added to `data`). +// * env: Provides the following fields: +// - logger: Logger attached to the same stream as the Jetzig server. +// - environment: Enum of `{ production, development }`. +pub fn run(allocator: std.mem.Allocator, params: *jetzig.data.Value, env: jetzig.jobs.JobEnv) !void { + _ = allocator; + _ = params; + // Job execution code goes here. Add any code that you would like to run in the background. + try env.logger.INFO("Running a job.", .{}); +} diff --git a/src/app/views/partials/_table.zmpl b/src/app/views/partials/_table.zmpl index e69de29..8ea394d 100644 --- a/src/app/views/partials/_table.zmpl +++ b/src/app/views/partials/_table.zmpl @@ -0,0 +1,18 @@ +@args table_data: *ZmplValue, table_headers: *ZmplValue + + + +@for (table_headers) |text| { + +} + + + @for (table_data) |value| { + + + + + + + } +
{{text}}
{{value.track}}{{value.artist}}{{value.album}}{{value.date}}
\ No newline at end of file diff --git a/src/app/views/upload.zig b/src/app/views/upload.zig index 491579c..34df03d 100644 --- a/src/app/views/upload.zig +++ b/src/app/views/upload.zig @@ -4,17 +4,6 @@ const jetquery = @import("jetzig").jetquery; pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { _ = data; - try request.repo.insert(.Artist, .{ - .id = 123, - .name = "wilco", - .album_num = 10, - .song_num = 200, - .play_count = 2700, - .avg_album_score = 10.0, - .avg_song_score = 10.0, - .url = "/wilco", - .aliased = false, - }); return request.render(.ok); } @@ -24,8 +13,45 @@ pub fn get(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig return request.render(.ok); } -pub fn post(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { - _ = data; +pub fn post(request: *jetzig.Request) !jetzig.View { + const Scrobble = struct { + track: []u8, + artist: []u8, + album: []u8, + date: i64, + }; + + const lastfm = struct { + username: []u8, + scrobbles: []Scrobble, + }; + + var root = try request.data(.object); + + if (try request.file("upload")) |file| { + const parsed = try std.json.parseFromSlice(lastfm, request.allocator, file.content, .{}); + + const history = parsed.value; + + //std.debug.print("{s}", .{history.scrobbles[19].artist}); + + var scrobbles = try root.put("scrobbles", .array); + for (history.scrobbles) |scrobble| { + try scrobbles.append(scrobble); + + const database_update = jetzig.database.Query(.RawScrobble) + .insert(.{ .track = scrobble.track, .album = scrobble.album, .artist = scrobble.artist, .date = @divFloor(scrobble.date, 1000) }); + + try request.repo.execute(database_update); + } + } + + var upload_table = try root.put("upload_table", .array); + try upload_table.append("Track"); + try upload_table.append("Artist"); + try upload_table.append("Album"); + try upload_table.append("Date"); + return request.render(.created); } diff --git a/src/app/views/upload/index.zmpl b/src/app/views/upload/index.zmpl index 66a79f1..5048944 100644 --- a/src/app/views/upload/index.zmpl +++ b/src/app/views/upload/index.zmpl @@ -1,10 +1,13 @@ + + + @partial partials/header
Upload Last.fm or Spotify history file here (in json format).
-
+ diff --git a/src/app/views/upload/post.zmpl b/src/app/views/upload/post.zmpl index 76457d0..176f094 100644 --- a/src/app/views/upload/post.zmpl +++ b/src/app/views/upload/post.zmpl @@ -1,3 +1,15 @@ -
- Content goes here -
+ + + + + + +@partial partials/header +

File Uploaded Successfully

+ +

Scrobbles Added

+ +@partial partials/table(table_data: .scrobbles, table_headers: .upload_table, table_context: .context) + + + \ No newline at end of file From e32d8633542a3fdd9a8e60be322b0e81f099bf8e Mon Sep 17 00:00:00 2001 From: mitteneer Date: Sat, 30 Nov 2024 12:44:01 -0500 Subject: [PATCH 21/27] Start process_scrobbles job --- .gitignore | 1 + build.zig.zon | 4 +- config/database.zig | 3 + src/app/database/Schema.zig | 32 +++++---- ... 2024-11-30_15-40-39_create_scrobbles.zig} | 3 + src/app/jobs/process_scrobbles.zig | 65 ++++++++++++++++- src/app/middleware/lastfmMiddleware.zig | 71 +++++++++++++++++++ src/app/views/upload.zig | 12 +++- src/main.zig | 3 +- 9 files changed, 172 insertions(+), 22 deletions(-) rename src/app/database/migrations/{2024-11-20_00-19-54_create_scrobble.zig => 2024-11-30_15-40-39_create_scrobbles.zig} (71%) diff --git a/.gitignore b/.gitignore index 23cb6fa..a485595 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ zig-out/ static/ .jetzig src/app/database/data.db-journal +src/app/database/old_migrations/ diff --git a/build.zig.zon b/build.zig.zon index 7a4efa2..fb64b02 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -16,8 +16,8 @@ // internet connectivity. .dependencies = .{ .jetzig = .{ - .url = "https://github.com/jetzig-framework/jetzig/archive/4e1a26a5f982a0af212fe15eaffc8d7d7ea2aab6.tar.gz", - .hash = "12206bec5c95058be1d0af92cc69c48e7dd9fc19c444736f9debd7e53d10e25c22c6", + .url = "https://github.com/jetzig-framework/jetzig/archive/7b038505930e8075fc5507c1e396fc073f6a46bb.tar.gz", + .hash = "122029af480bfe9fdcbfb6be77ff0bef0c20dba803dceaf48c09de2a99ab375d11c8", }, }, .paths = .{ diff --git a/config/database.zig b/config/database.zig index 56b00ec..361129d 100644 --- a/config/database.zig +++ b/config/database.zig @@ -6,6 +6,7 @@ pub const database = .{ .username = "postgres", .password = "postgres", .database = "zuletzt_testing", + .pool_size = 16, }, .development = .{ @@ -15,6 +16,7 @@ pub const database = .{ .username = "postgres", .password = "postgres", .database = "zuletzt_dev", + .pool_size = 16, }, .production = .{ @@ -24,5 +26,6 @@ pub const database = .{ .username = "postgres", .password = "postgres", .database = "zuletzt", + .pool_size = 16, }, }; diff --git a/src/app/database/Schema.zig b/src/app/database/Schema.zig index 1804ff0..6249df6 100644 --- a/src/app/database/Schema.zig +++ b/src/app/database/Schema.zig @@ -20,7 +20,7 @@ pub const Album = jetquery.Model( id: i32, title: []const u8, song_num: i32, - length: f64, + length: f32, play_count: i32, holiday: bool, compilation: bool, @@ -95,6 +95,9 @@ pub const Scrobble = jetquery.Model( struct { id: i32, date: jetquery.DateTime, + song_id: i32, + album_id: ?i32, + artist_id: i32, created_at: jetquery.DateTime, updated_at: jetquery.DateTime, }, @@ -113,7 +116,7 @@ pub const Song = jetquery.Model( struct { id: i32, title: []const u8, - length: f64, + length: f32, hidden: bool, holiday: bool, play_count: i32, @@ -163,7 +166,7 @@ pub const Rating = jetquery.Model( struct { id: i32, reference_id: i32, - score: f64, + score: f32, date: jetquery.DateTime, created_at: jetquery.DateTime, updated_at: jetquery.DateTime, @@ -171,12 +174,17 @@ pub const Rating = jetquery.Model( .{}, ); -pub const RawScrobble = jetquery.Model(@This(), "raw_scrobbles", struct { - id: i32, - track: []const u8, - artist: []const u8, - album: []const u8, - date: i32, - created_at: jetquery.DateTime, - updated_at: jetquery.DateTime, -}, .{}); +pub const RawScrobble = jetquery.Model( + @This(), + "raw_scrobbles", + struct { + id: i32, + track: []const u8, + artist: []const u8, + album: []const u8, + date: i32, + created_at: jetquery.DateTime, + updated_at: jetquery.DateTime, + }, + .{}, +); diff --git a/src/app/database/migrations/2024-11-20_00-19-54_create_scrobble.zig b/src/app/database/migrations/2024-11-30_15-40-39_create_scrobbles.zig similarity index 71% rename from src/app/database/migrations/2024-11-20_00-19-54_create_scrobble.zig rename to src/app/database/migrations/2024-11-30_15-40-39_create_scrobbles.zig index c067423..ee4552f 100644 --- a/src/app/database/migrations/2024-11-20_00-19-54_create_scrobble.zig +++ b/src/app/database/migrations/2024-11-30_15-40-39_create_scrobbles.zig @@ -8,6 +8,9 @@ pub fn up(repo: anytype) !void { &.{ t.primaryKey("id", .{}), t.column("date", .datetime, .{}), + t.column("song_id", .integer, .{}), + t.column("album_id", .integer, .{ .optional = true }), + t.column("artist_id", .integer, .{}), t.timestamps(.{}), }, .{}, diff --git a/src/app/jobs/process_scrobbles.zig b/src/app/jobs/process_scrobbles.zig index 3f0c191..8cc1716 100644 --- a/src/app/jobs/process_scrobbles.zig +++ b/src/app/jobs/process_scrobbles.zig @@ -1,18 +1,77 @@ const std = @import("std"); const jetzig = @import("jetzig"); +const jetquery = @import("jetzig").jetquery; // The `run` function for a job is invoked every time the job is processed by a queue worker // (or by the Jetzig server if the job is processed in-line). // // Arguments: // * allocator: Arena allocator for use during the job execution process. -// * params: Params assigned to a job (from a request, any values added to `data`). +// * params: Params assigned to a job (from a request, values added to response data). // * env: Provides the following fields: // - logger: Logger attached to the same stream as the Jetzig server. // - environment: Enum of `{ production, development }`. pub fn run(allocator: std.mem.Allocator, params: *jetzig.data.Value, env: jetzig.jobs.JobEnv) !void { _ = allocator; _ = params; - // Job execution code goes here. Add any code that you would like to run in the background. - try env.logger.INFO("Running a job.", .{}); + + // Get all scrobbles from the RawScrobbles table + + const query = jetzig.database.Query(.RawScrobble).select(.{}); + const scrobbles = try env.repo.all(query); + defer env.repo.free(scrobbles); + + for (scrobbles) |scrobble| { + + // Make hashes + const album_hash = std.hash.Fnv1a_64.hash(scrobble.album); + const artist_hash = std.hash.Fnv1a_64.hash(scrobble.artist); + const song_hash = std.hash.Fnv1a_64.hash(scrobble.track); + + var album_id: u64 = 0; + const song_id = (song_hash ^ artist_hash ^ album_hash) % 99999989; + if (artist_hash == album_hash) { + album_id = album_hash % 99999989; + } else { + album_id = (artist_hash ^ album_hash) % 99999989; + } + const artist_id = artist_hash % 99999989; + + // ID start - I think we can use SERIAL for this + // We don't compare intermediate IDs to anything, + // so keeping it a SERIAL is probably fine + const artistalbum_offset = try jetzig.database.Query(.ArtistAlbum).select(.{}).count().execute(env.repo) orelse unreachable; + const albumsong_offset = try jetzig.database.Query(.AlbumSong).select(.{}).count().execute(env.repo) orelse unreachable; + const artistsong_offset = try jetzig.database.Query(.ArtistSong).select(.{}).count().execute(env.repo) orelse unreachable; + + // Inserts + const artistalbum_insert = jetzig.database.Query(.ArtistAlbum).insert(.{ .id = 1 + artistalbum_offset, .artist_id = artist_id, .album_id = album_id }); + const albumsong_insert = jetzig.database.Query(.AlbumSong).insert(.{ .id = 1 + albumsong_offset, .song_id = song_id, .album_id = album_id }); + const artistsong_insert = jetzig.database.Query(.ArtistSong).insert(.{ .id = 1 + artistsong_offset, .artist_id = artist_id, .song_id = song_id }); + const album_insert = jetzig.database.Query(.Album).insert(.{ .id = album_id, .title = scrobble.album, .song_num = 0, .length = 0.0, .play_count = 0, .holiday = false, .compilation = false, .deluxe = false, .live = false }); + const artist_insert = jetzig.database.Query(.Artist).insert(.{ .id = artist_id, .name = scrobble.artist, .album_num = 0, .song_num = 0, .play_count = 0 }); + const song_insert = jetzig.database.Query(.Song).insert(.{ .id = song_id, .title = scrobble.track, .length = 0.0, .hidden = false, .holiday = false, .play_count = 0 }); + + // Checks + const artistalbum_check = try jetzig.database.Query(.ArtistAlbum).where(.{ .{ .artist_id = artist_id }, .AND, .{ .album_id = album_id } }).count().execute(env.repo); + const albumsong_check = try jetzig.database.Query(.AlbumSong).where(.{ .{ .album_id = album_id }, .AND, .{ .song_id = song_id } }).count().execute(env.repo); + const artistsong_check = try jetzig.database.Query(.ArtistSong).where(.{ .{ .artist_id = artist_id }, .AND, .{ .song_id = song_id } }).count().execute(env.repo); + const album_check = try jetzig.database.Query(.Album).where(.{.{ .id = album_id }}).count().execute(env.repo); + const artist_check = try jetzig.database.Query(.Artist).where(.{.{ .id = artist_id }}).count().execute(env.repo); + const song_check = try jetzig.database.Query(.Song).where(.{.{ .id = song_id }}).count().execute(env.repo); + + // Insert into Intermediate Tables + if (artistalbum_check == 0) try env.repo.execute(artistalbum_insert); + if (albumsong_check == 0) try env.repo.execute(albumsong_insert); + if (artistsong_check == 0) try env.repo.execute(artistsong_insert); + + if (album_check == 0) try env.repo.execute(album_insert); + if (artist_check == 0) try env.repo.execute(artist_insert); + if (song_check == 0) try env.repo.execute(song_insert); + + const scrobble_offset = try jetzig.database.Query(.Scrobble).select(.{}).count().execute(env.repo) orelse unreachable; + try jetzig.database.Query(.Scrobble).insert(.{ .id = scrobble_offset + 1, .song_id = song_id, .album_id = album_id, .artist_id = artist_id }).execute(env.repo); + } + // Clear RawScrobbles when done processing + try jetzig.database.Query(.RawScrobble).deleteAll().execute(env.repo); } diff --git a/src/app/middleware/lastfmMiddleware.zig b/src/app/middleware/lastfmMiddleware.zig index e69de29..4fa60d8 100644 --- a/src/app/middleware/lastfmMiddleware.zig +++ b/src/app/middleware/lastfmMiddleware.zig @@ -0,0 +1,71 @@ +/// Demo middleware. Assign middleware by declaring `pub const middleware` in the +/// `jetzig_options` defined in your application's `src/main.zig`. +/// +/// Middleware is called before and after the request, providing full access to the active +/// request, allowing you to execute any custom code for logging, tracking, inserting response +/// headers, etc. +/// +/// This middleware is configured in the demo app's `src/main.zig`: +/// +/// ``` +/// pub const jetzig_options = struct { +/// pub const middleware: []const type = &.{@import("app/middleware/DemoMiddleware.zig")}; +/// }; +/// ``` +const std = @import("std"); +const jetzig = @import("jetzig"); +const jetquery = @import("jetzig").jetquery; + +/// Define any custom data fields you want to store here. Assigning to these fields in the `init` +/// function allows you to access them in various middleware callbacks defined below, where they +/// can also be modified. +my_custom_value: []const u8, + +const Self = @This(); + +/// Initialize middleware. +pub fn init(request: *jetzig.http.Request) !*Self { + var middleware = try request.allocator.create(Self); + middleware.my_custom_value = "initial value"; + return middleware; +} + +/// Invoked immediately after the request is received but before it has started processing. +/// Any calls to `request.render` or `request.redirect` will prevent further processing of the +/// request, including any other middleware in the chain. +//pub fn afterRequest(self: *Self, request: *jetzig.http.Request) !void { +// const Scrobble = struct { +// track: []u8, +// artist: []u8, +// album: []u8, +// date: i64 +// }; +// +// const lastfm = struct { +// username: u8, +// scrobbles: []Scrobble, +// }; +// +// var root = try request.data(.object); +// +// if (try request.file("upload")) |file| { +// const parsed = try std.json.parseFromSlice(lastfm, request.allocator, file.content, .{}); +// const history = parsed.value; +// var scrobbles = try root.put("scrobbles", .array); +// +// for (history.scrobbles) |scrobble| { +// try scrobbles.append(scrobble); +// } +// +// //var scrobble = try root.put("scrobbles", .array); +// } +//} + +/// Invoked immediately before the response renders to the client. +/// The response can be modified here if needed. +pub fn beforeResponse(self: *Self, request: *jetzig.http.Request, response: *jetzig.http.Response) !void { + try request.server.logger.DEBUG( + "[DemoMiddleware:beforeResponse] my_custom_value: {s}, response status: {s}", + .{ self.my_custom_value, @tagName(response.status_code) }, + ); +} diff --git a/src/app/views/upload.zig b/src/app/views/upload.zig index 34df03d..28405dd 100644 --- a/src/app/views/upload.zig +++ b/src/app/views/upload.zig @@ -27,25 +27,31 @@ pub fn post(request: *jetzig.Request) !jetzig.View { }; var root = try request.data(.object); + var job = try request.job("process_scrobbles"); + var counter: u16 = 0; if (try request.file("upload")) |file| { const parsed = try std.json.parseFromSlice(lastfm, request.allocator, file.content, .{}); const history = parsed.value; - //std.debug.print("{s}", .{history.scrobbles[19].artist}); - var scrobbles = try root.put("scrobbles", .array); for (history.scrobbles) |scrobble| { try scrobbles.append(scrobble); + //const song_hash: u64 = std.hash.Fnv1a_64.hash(scrobble.track) % 99999989; + //job.params.put(scrobble.song, song_hash); + //std.debug.print("{d}\n", .{song_hash}); const database_update = jetzig.database.Query(.RawScrobble) - .insert(.{ .track = scrobble.track, .album = scrobble.album, .artist = scrobble.artist, .date = @divFloor(scrobble.date, 1000) }); + .insert(.{ .id = counter, .track = scrobble.track, .album = scrobble.album, .artist = scrobble.artist, .date = @divFloor(scrobble.date, 1000) }); try request.repo.execute(database_update); + counter += 1; } } + try job.schedule(); + var upload_table = try root.put("upload_table", .array); try upload_table.append("Track"); try upload_table.append("Artist"); diff --git a/src/main.zig b/src/main.zig index 3727351..7437920 100644 --- a/src/main.zig +++ b/src/main.zig @@ -16,11 +16,10 @@ pub const jetzig_options = struct { jetzig.middleware.HtmxMiddleware, // Demo middleware included with new projects. Remove once you are familiar with Jetzig's // middleware system. - @import("app/middleware/DemoMiddleware.zig"), }; // Maximum bytes to allow in request body. - // pub const max_bytes_request_body: usize = std.math.pow(usize, 2, 16); + pub const max_bytes_request_body: usize = std.math.pow(usize, 2, 32); // Maximum filesize for `public/` content. // pub const max_bytes_public_content: usize = std.math.pow(usize, 2, 20); From 9695202cfe0bcc658e968dd78e0de4bb1694205d Mon Sep 17 00:00:00 2001 From: mitteneer Date: Sat, 30 Nov 2024 12:50:40 -0500 Subject: [PATCH 22/27] Remove middleware --- src/app/middleware/lastfmMiddleware.zig | 71 ------------------------ src/app/middleware/spotifyMiddleware.zig | 0 2 files changed, 71 deletions(-) delete mode 100644 src/app/middleware/lastfmMiddleware.zig delete mode 100644 src/app/middleware/spotifyMiddleware.zig diff --git a/src/app/middleware/lastfmMiddleware.zig b/src/app/middleware/lastfmMiddleware.zig deleted file mode 100644 index 4fa60d8..0000000 --- a/src/app/middleware/lastfmMiddleware.zig +++ /dev/null @@ -1,71 +0,0 @@ -/// Demo middleware. Assign middleware by declaring `pub const middleware` in the -/// `jetzig_options` defined in your application's `src/main.zig`. -/// -/// Middleware is called before and after the request, providing full access to the active -/// request, allowing you to execute any custom code for logging, tracking, inserting response -/// headers, etc. -/// -/// This middleware is configured in the demo app's `src/main.zig`: -/// -/// ``` -/// pub const jetzig_options = struct { -/// pub const middleware: []const type = &.{@import("app/middleware/DemoMiddleware.zig")}; -/// }; -/// ``` -const std = @import("std"); -const jetzig = @import("jetzig"); -const jetquery = @import("jetzig").jetquery; - -/// Define any custom data fields you want to store here. Assigning to these fields in the `init` -/// function allows you to access them in various middleware callbacks defined below, where they -/// can also be modified. -my_custom_value: []const u8, - -const Self = @This(); - -/// Initialize middleware. -pub fn init(request: *jetzig.http.Request) !*Self { - var middleware = try request.allocator.create(Self); - middleware.my_custom_value = "initial value"; - return middleware; -} - -/// Invoked immediately after the request is received but before it has started processing. -/// Any calls to `request.render` or `request.redirect` will prevent further processing of the -/// request, including any other middleware in the chain. -//pub fn afterRequest(self: *Self, request: *jetzig.http.Request) !void { -// const Scrobble = struct { -// track: []u8, -// artist: []u8, -// album: []u8, -// date: i64 -// }; -// -// const lastfm = struct { -// username: u8, -// scrobbles: []Scrobble, -// }; -// -// var root = try request.data(.object); -// -// if (try request.file("upload")) |file| { -// const parsed = try std.json.parseFromSlice(lastfm, request.allocator, file.content, .{}); -// const history = parsed.value; -// var scrobbles = try root.put("scrobbles", .array); -// -// for (history.scrobbles) |scrobble| { -// try scrobbles.append(scrobble); -// } -// -// //var scrobble = try root.put("scrobbles", .array); -// } -//} - -/// Invoked immediately before the response renders to the client. -/// The response can be modified here if needed. -pub fn beforeResponse(self: *Self, request: *jetzig.http.Request, response: *jetzig.http.Response) !void { - try request.server.logger.DEBUG( - "[DemoMiddleware:beforeResponse] my_custom_value: {s}, response status: {s}", - .{ self.my_custom_value, @tagName(response.status_code) }, - ); -} diff --git a/src/app/middleware/spotifyMiddleware.zig b/src/app/middleware/spotifyMiddleware.zig deleted file mode 100644 index e69de29..0000000 From 139c24949793ae0c9fe25fb69d52087f4bd73f12 Mon Sep 17 00:00:00 2001 From: mitteneer Date: Sat, 30 Nov 2024 23:22:17 -0500 Subject: [PATCH 23/27] Reduce max_bytes_request_body --- .gitignore | 1 + build.zig | 4 ++-- src/app/jobs/process_scrobbles.zig | 11 ++++++++++- src/main.zig | 2 +- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index a485595..abb3b4e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ static/ .jetzig src/app/database/data.db-journal src/app/database/old_migrations/ +src/lib/ diff --git a/build.zig b/build.zig index 95a299d..4ceba31 100644 --- a/build.zig +++ b/build.zig @@ -29,7 +29,7 @@ pub fn build(b: *std.Build) !void { run_step.dependOn(&run_cmd.step); const lib_unit_tests = b.addTest(.{ - .root_source_file = b.path("src/main.zig"), + .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, }); @@ -37,7 +37,7 @@ pub fn build(b: *std.Build) !void { const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests); const exe_unit_tests = b.addTest(.{ - .root_source_file = b.path("src/main.zig"), + .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, }); diff --git a/src/app/jobs/process_scrobbles.zig b/src/app/jobs/process_scrobbles.zig index 8cc1716..e0d276a 100644 --- a/src/app/jobs/process_scrobbles.zig +++ b/src/app/jobs/process_scrobbles.zig @@ -1,6 +1,9 @@ const std = @import("std"); const jetzig = @import("jetzig"); const jetquery = @import("jetzig").jetquery; +//const time = @cImport({ +// @cInclude("time.h"); +//}); // The `run` function for a job is invoked every time the job is processed by a queue worker // (or by the Jetzig server if the job is processed in-line). @@ -15,6 +18,8 @@ pub fn run(allocator: std.mem.Allocator, params: *jetzig.data.Value, env: jetzig _ = allocator; _ = params; + //const memory = try allocator.alloc(u8, 19); + // Get all scrobbles from the RawScrobbles table const query = jetzig.database.Query(.RawScrobble).select(.{}); @@ -22,6 +27,10 @@ pub fn run(allocator: std.mem.Allocator, params: *jetzig.data.Value, env: jetzig defer env.repo.free(scrobbles); for (scrobbles) |scrobble| { + //const date = [19]u8{}; + //time.strftime{ date, 19, "%Y-%m-%d %H:%M:%D", scrobbles.date }; + //time.strftime(memory, 19, "%Y-%m-%d %H:%M:%S", scrobbles.date); + //std.debug.print("{s}", .{memory}); // Make hashes const album_hash = std.hash.Fnv1a_64.hash(scrobble.album); @@ -70,7 +79,7 @@ pub fn run(allocator: std.mem.Allocator, params: *jetzig.data.Value, env: jetzig if (song_check == 0) try env.repo.execute(song_insert); const scrobble_offset = try jetzig.database.Query(.Scrobble).select(.{}).count().execute(env.repo) orelse unreachable; - try jetzig.database.Query(.Scrobble).insert(.{ .id = scrobble_offset + 1, .song_id = song_id, .album_id = album_id, .artist_id = artist_id }).execute(env.repo); + try jetzig.database.Query(.Scrobble).insert(.{ .id = scrobble_offset + 1, .song_id = song_id, .album_id = album_id, .artist_id = artist_id, .date = scrobble.date }).execute(env.repo); } // Clear RawScrobbles when done processing try jetzig.database.Query(.RawScrobble).deleteAll().execute(env.repo); diff --git a/src/main.zig b/src/main.zig index 7437920..73770eb 100644 --- a/src/main.zig +++ b/src/main.zig @@ -19,7 +19,7 @@ pub const jetzig_options = struct { }; // Maximum bytes to allow in request body. - pub const max_bytes_request_body: usize = std.math.pow(usize, 2, 32); + pub const max_bytes_request_body: usize = std.math.pow(usize, 2, 24); // Maximum filesize for `public/` content. // pub const max_bytes_public_content: usize = std.math.pow(usize, 2, 20); From 100984010bb1f80b6f4cec02083449eda6f5330a Mon Sep 17 00:00:00 2001 From: mitteneer Date: Sat, 30 Nov 2024 23:52:50 -0500 Subject: [PATCH 24/27] Add todo list --- README.md | 62 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 31b4ffa..4947ca5 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,49 @@ -# Zuletzt -**Zuletzt** gives you the statistics of your music listening habits. - -Inspired by [Last.fm](https://last.fm), [Maloja](https://github.com/krateng/maloja), and [Lastfmstats.com](https://www.lastfmstats.com). - - -**Z**uletzt is written with [**Z**ig](https://github.com/ziglang/zig) and [Jetzig](https://github.com/jetzig-framework/jetzig) as a means of learning the -language, reintroducing myself to programming, and combining -the functionality of the aforementioned inspirations. - -Zuletzt means "last" in German. - -Licensed under MIT. \ No newline at end of file +# Zuletzt +**Zuletzt** gives you the statistics of your music listening habits. + +Inspired by [Last.fm](https://last.fm), [Maloja](https://github.com/krateng/maloja), and [Lastfmstats.com](https://www.lastfmstats.com). + + +**Z**uletzt is written with [**Z**ig](https://github.com/ziglang/zig) and [Jetzig](https://github.com/jetzig-framework/jetzig) as a means of learning the +language, reintroducing myself to programming, and combining +the functionality of the aforementioned inspirations. + +Zuletzt means "last" in German. + +Licensed under MIT. + +## To-Do List: +- [ ] Last.fm statistics +- [ ] Lastfmstats.com statistics[^1] +- [ ] Collections + - [ ] Import from Discogs[^2] +- [ ] Import listening history + - [ ] From Lastfmstats.com (.json file)[^3] + - [ ] From Last.fm (authentication) + - [ ] From Spotify (.json file) + - [ ] From other streaming services[^4] +- [ ] Tags +- [ ] MusicBrainz integration +- [ ] Concerts + - [ ] Import from Setlist.fm[^5] +- [ ] Ratings + - [ ] RYM integration[^6] + - [ ] Rank songs +- [ ] Custom statistics[^7] +- [ ] "Playlists"[^8] + +[^1]: I do not intend to exactly replicate all the statistics Lastfmstats.com provides, but I would at least like to give the user the option to see those kinds of statistics, or generate them themselves (see 7). + +[^2]: I do not intend to provide the level of granularity that Discogs provides, but a simple toggle that means "I own some version of this release" is all that is necessary. + +[^3]: I have not investigated any other service for downloading your listening history from Last.fm, but providing the listening history as a JSOn rather than a CSV is highly preferred. I may eventually provide my own way of downloading Last.fm data as a JSON, but I would prefer to allow users to enter their username, or authenticate, and avoid needing to upload a file altogether. + +[^4]: I only intend to allow imports from Last.fm and Spotify at the moment because those are the only data sources I currently rely on. To that extent, I imagine I could import from other sources as well fairly easily, although I do not know what their data dumps look like. + +[^5]: I only intend to allow imports from Setlist.fm at the moment because that is the only data source I currently rely on. + +[^6]: RYM has the most data, and once it has an API, will be the only user-driven review site that *has* an API. In this context, "integration" simply means displaying the critic score and user score next to the album. You will be able to write reviews and ranks songs/albums(/artists?), but not for them to be published to RYM. + +[^7]: I envision something akin to the Custom Reports from [Actual Budget](https://github.com/actualbudget/actual) that will allow users to create their own ways of rating/ranking songs/albums, and view their listening habits. + +[^8]: Misleading title, but same functionality as "Lists" on AlbumOfTheYear, although I would like to allow albums and songs to appear on the same list. From c54189357209b56930d0e1fd6fe689fcdfb70974 Mon Sep 17 00:00:00 2001 From: mitteneer Date: Sat, 30 Nov 2024 23:59:59 -0500 Subject: [PATCH 25/27] Add to todo list --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4947ca5..68e41fd 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,11 @@ Licensed under MIT. - [ ] From Last.fm (authentication) - [ ] From Spotify (.json file) - [ ] From other streaming services[^4] + - [ ] Import rules + - [ ] Simple find/replace + - [ ] User-defined regex - [ ] Tags + - [ ] Genres - [ ] MusicBrainz integration - [ ] Concerts - [ ] Import from Setlist.fm[^5] @@ -36,7 +40,7 @@ Licensed under MIT. [^2]: I do not intend to provide the level of granularity that Discogs provides, but a simple toggle that means "I own some version of this release" is all that is necessary. -[^3]: I have not investigated any other service for downloading your listening history from Last.fm, but providing the listening history as a JSOn rather than a CSV is highly preferred. I may eventually provide my own way of downloading Last.fm data as a JSON, but I would prefer to allow users to enter their username, or authenticate, and avoid needing to upload a file altogether. +[^3]: I have not investigated any other service for downloading your listening history from Last.fm, but providing the listening history as a JSON rather than a CSV is highly preferred. I may eventually provide my own way of downloading Last.fm data as a JSON, but I would prefer to allow users to enter their username, or authenticate, and avoid needing to upload a file altogether. [^4]: I only intend to allow imports from Last.fm and Spotify at the moment because those are the only data sources I currently rely on. To that extent, I imagine I could import from other sources as well fairly easily, although I do not know what their data dumps look like. From d7f81e8cd63ad6e176687e52fb123a85fe7675e2 Mon Sep 17 00:00:00 2001 From: mitteneer Date: Fri, 13 Dec 2024 05:56:53 +0000 Subject: [PATCH 26/27] Update README.md Add section regarding Contributions. Funny to write that when I'm nearly certain no one will ever contribute, but it makes the project feel more real. --- README.md | 109 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 56 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index 68e41fd..8a2e029 100644 --- a/README.md +++ b/README.md @@ -1,53 +1,56 @@ -# Zuletzt -**Zuletzt** gives you the statistics of your music listening habits. - -Inspired by [Last.fm](https://last.fm), [Maloja](https://github.com/krateng/maloja), and [Lastfmstats.com](https://www.lastfmstats.com). - - -**Z**uletzt is written with [**Z**ig](https://github.com/ziglang/zig) and [Jetzig](https://github.com/jetzig-framework/jetzig) as a means of learning the -language, reintroducing myself to programming, and combining -the functionality of the aforementioned inspirations. - -Zuletzt means "last" in German. - -Licensed under MIT. - -## To-Do List: -- [ ] Last.fm statistics -- [ ] Lastfmstats.com statistics[^1] -- [ ] Collections - - [ ] Import from Discogs[^2] -- [ ] Import listening history - - [ ] From Lastfmstats.com (.json file)[^3] - - [ ] From Last.fm (authentication) - - [ ] From Spotify (.json file) - - [ ] From other streaming services[^4] - - [ ] Import rules - - [ ] Simple find/replace - - [ ] User-defined regex -- [ ] Tags - - [ ] Genres -- [ ] MusicBrainz integration -- [ ] Concerts - - [ ] Import from Setlist.fm[^5] -- [ ] Ratings - - [ ] RYM integration[^6] - - [ ] Rank songs -- [ ] Custom statistics[^7] -- [ ] "Playlists"[^8] - -[^1]: I do not intend to exactly replicate all the statistics Lastfmstats.com provides, but I would at least like to give the user the option to see those kinds of statistics, or generate them themselves (see 7). - -[^2]: I do not intend to provide the level of granularity that Discogs provides, but a simple toggle that means "I own some version of this release" is all that is necessary. - -[^3]: I have not investigated any other service for downloading your listening history from Last.fm, but providing the listening history as a JSON rather than a CSV is highly preferred. I may eventually provide my own way of downloading Last.fm data as a JSON, but I would prefer to allow users to enter their username, or authenticate, and avoid needing to upload a file altogether. - -[^4]: I only intend to allow imports from Last.fm and Spotify at the moment because those are the only data sources I currently rely on. To that extent, I imagine I could import from other sources as well fairly easily, although I do not know what their data dumps look like. - -[^5]: I only intend to allow imports from Setlist.fm at the moment because that is the only data source I currently rely on. - -[^6]: RYM has the most data, and once it has an API, will be the only user-driven review site that *has* an API. In this context, "integration" simply means displaying the critic score and user score next to the album. You will be able to write reviews and ranks songs/albums(/artists?), but not for them to be published to RYM. - -[^7]: I envision something akin to the Custom Reports from [Actual Budget](https://github.com/actualbudget/actual) that will allow users to create their own ways of rating/ranking songs/albums, and view their listening habits. - -[^8]: Misleading title, but same functionality as "Lists" on AlbumOfTheYear, although I would like to allow albums and songs to appear on the same list. +# Zuletzt +**Zuletzt** gives you the statistics of your music listening habits. + +Inspired by [Last.fm](https://last.fm), [Maloja](https://github.com/krateng/maloja), and [Lastfmstats.com](https://www.lastfmstats.com). + + +**Z**uletzt is written with [**Z**ig](https://github.com/ziglang/zig) and [Jetzig](https://github.com/jetzig-framework/jetzig) as a means of learning the +language, reintroducing myself to programming, and combining +the functionality of the aforementioned inspirations. + +Zuletzt means "last" in German. + +Licensed under MIT. + +## To-Do List: +- [ ] Last.fm statistics +- [ ] Lastfmstats.com statistics[^1] +- [ ] Collections + - [ ] Import from Discogs[^2] +- [ ] Import listening history + - [ ] From Lastfmstats.com (.json file)[^3] + - [ ] From Last.fm (authentication) + - [ ] From Spotify (.json file) + - [ ] From other streaming services[^4] + - [ ] Import rules + - [ ] Simple find/replace + - [ ] User-defined regex +- [ ] Tags + - [ ] Genres +- [ ] MusicBrainz integration +- [ ] Concerts + - [ ] Import from Setlist.fm[^5] +- [ ] Ratings + - [ ] RYM integration[^6] + - [ ] Rank songs +- [ ] Custom statistics[^7] +- [ ] "Playlists"[^8] + +[^1]: I do not intend to exactly replicate all the statistics Lastfmstats.com provides, but I would at least like to give the user the option to see those kinds of statistics, or generate them themselves (see 7). + +[^2]: I do not intend to provide the level of granularity that Discogs provides, but a simple toggle that means "I own some version of this release" is all that is necessary. + +[^3]: I have not investigated any other service for downloading your listening history from Last.fm, but providing the listening history as a JSON rather than a CSV is highly preferred. I may eventually provide my own way of downloading Last.fm data as a JSON, but I would prefer to allow users to enter their username, or authenticate, and avoid needing to upload a file altogether. + +[^4]: I only intend to allow imports from Last.fm and Spotify at the moment because those are the only data sources I currently rely on. To that extent, I imagine I could import from other sources as well fairly easily, although I do not know what their data dumps look like. + +[^5]: I only intend to allow imports from Setlist.fm at the moment because that is the only data source I currently rely on. + +[^6]: RYM has the most data, and once it has an API, will be the only user-driven review site that *has* an API. In this context, "integration" simply means displaying the critic score and user score next to the album. You will be able to write reviews and ranks songs/albums(/artists?), but not for them to be published to RYM. + +[^7]: I envision something akin to the Custom Reports from [Actual Budget](https://github.com/actualbudget/actual) that will allow users to create their own ways of rating/ranking songs/albums, and view their listening habits. + +[^8]: Misleading title, but same functionality as "Lists" on AlbumOfTheYear, although I would like to allow albums and songs to appear on the same list. + +## Contributing +I am a math student who is interested in programming. I will not be writing quality code. That said, Zuletzt is something that, at the moment, I am very excited about making, and using to relearn some things about programming. Unless contributions are given in the form of code review, or some kind of constructive criticism, it's not likely that I accept pull requests. The project is, however, licensed under the MIT License, so feel free to do what you like with it in your own way. From a3b3a6c0082a3cfbe5b925e32589ae3052298482 Mon Sep 17 00:00:00 2001 From: mitteneer Date: Mon, 16 Dec 2024 11:59:11 -0500 Subject: [PATCH 27/27] Fix date parsing For some reason, I had date in "raw_scrobbles" set as an i32. Pretty annoying considering I'm probably going to remove the raw_scrobbles step at some point anyways. --- build.zig.zon | 4 ++-- src/app/database/Schema.zig | 2 +- .../migrations/2024-11-25_15-32-40_create_raw_scrobbles.zig | 2 +- src/app/views/upload.zig | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index fb64b02..a85ac4d 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -16,8 +16,8 @@ // internet connectivity. .dependencies = .{ .jetzig = .{ - .url = "https://github.com/jetzig-framework/jetzig/archive/7b038505930e8075fc5507c1e396fc073f6a46bb.tar.gz", - .hash = "122029af480bfe9fdcbfb6be77ff0bef0c20dba803dceaf48c09de2a99ab375d11c8", + .url = "https://github.com/jetzig-framework/jetzig/archive/475ed269525624a67004594ddca44dc8ebea1919.tar.gz", + .hash = "1220bc060ba2320fa9fed8e554a8b692a93ef73fa3ab40617b9ed1d928d2029297fb", }, }, .paths = .{ diff --git a/src/app/database/Schema.zig b/src/app/database/Schema.zig index 6249df6..560ab3c 100644 --- a/src/app/database/Schema.zig +++ b/src/app/database/Schema.zig @@ -182,7 +182,7 @@ pub const RawScrobble = jetquery.Model( track: []const u8, artist: []const u8, album: []const u8, - date: i32, + date: jetquery.DateTime, created_at: jetquery.DateTime, updated_at: jetquery.DateTime, }, diff --git a/src/app/database/migrations/2024-11-25_15-32-40_create_raw_scrobbles.zig b/src/app/database/migrations/2024-11-25_15-32-40_create_raw_scrobbles.zig index 12f307d..e4264d3 100644 --- a/src/app/database/migrations/2024-11-25_15-32-40_create_raw_scrobbles.zig +++ b/src/app/database/migrations/2024-11-25_15-32-40_create_raw_scrobbles.zig @@ -10,7 +10,7 @@ pub fn up(repo: anytype) !void { t.column("track", .string, .{}), t.column("artist", .string, .{}), t.column("album", .string, .{}), - t.column("date", .integer, .{}), + t.column("date", .datetime, .{}), t.timestamps(.{}), }, .{}, diff --git a/src/app/views/upload.zig b/src/app/views/upload.zig index 28405dd..80b8a47 100644 --- a/src/app/views/upload.zig +++ b/src/app/views/upload.zig @@ -18,7 +18,7 @@ pub fn post(request: *jetzig.Request) !jetzig.View { track: []u8, artist: []u8, album: []u8, - date: i64, + date: u64, }; const lastfm = struct { @@ -43,7 +43,7 @@ pub fn post(request: *jetzig.Request) !jetzig.View { //std.debug.print("{d}\n", .{song_hash}); const database_update = jetzig.database.Query(.RawScrobble) - .insert(.{ .id = counter, .track = scrobble.track, .album = scrobble.album, .artist = scrobble.artist, .date = @divFloor(scrobble.date, 1000) }); + .insert(.{ .id = counter, .track = scrobble.track, .album = scrobble.album, .artist = scrobble.artist, .date = (scrobble.date * 1000) }); try request.repo.execute(database_update); counter += 1;