diff --git a/.gitignore b/.gitignore index 540d652..abb3b4e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ zig-out/ *.core static/ .jetzig +src/app/database/data.db-journal +src/app/database/old_migrations/ +src/lib/ diff --git a/README.md b/README.md index 31b4ffa..8a2e029 100644 --- a/README.md +++ b/README.md @@ -10,4 +10,47 @@ the functionality of the aforementioned inspirations. Zuletzt means "last" in German. -Licensed under MIT. \ No newline at end of file +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. diff --git a/build.zig b/build.zig index 36adc79..4ceba31 100644 --- a/build.zig +++ b/build.zig @@ -16,7 +16,7 @@ pub fn build(b: *std.Build) !void { // 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 c4ddd34..a85ac4d 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/dda433bb73000614482af10a277d47dc9d89600c.tar.gz", - .hash = "12202ce84b803a8b300c91d98afbc7c326298b55a23bf05cf603182e934b621008ec", - }, - .iguanas = .{ - .url = "https://github.com/jetzig-framework/iguanas/archive/89c2abf29de0bc31054a9a6feac5a6a83bab0459.tar.gz", - .hash = "12202fd319a5ab4e124b00e8ddea474d07c19c4e005d77b6c29fc44860904ea01a5c", + .url = "https://github.com/jetzig-framework/jetzig/archive/475ed269525624a67004594ddca44dc8ebea1919.tar.gz", + .hash = "1220bc060ba2320fa9fed8e554a8b692a93ef73fa3ab40617b9ed1d928d2029297fb", }, }, .paths = .{ diff --git a/config/database.zig b/config/database.zig new file mode 100644 index 0000000..361129d --- /dev/null +++ b/config/database.zig @@ -0,0 +1,31 @@ +pub const database = .{ + .testing = .{ + .adapter = .postgresql, + .hostname = "localhost", + .port = 5432, + .username = "postgres", + .password = "postgres", + .database = "zuletzt_testing", + .pool_size = 16, + }, + + .development = .{ + .adapter = .postgresql, + .hostname = "localhost", + .port = 5432, + .username = "postgres", + .password = "postgres", + .database = "zuletzt_dev", + .pool_size = 16, + }, + + .production = .{ + .adapter = .postgresql, + .hostname = "localhost", + .port = 5432, + .username = "postgres", + .password = "postgres", + .database = "zuletzt", + .pool_size = 16, + }, +}; diff --git a/public/styles.css b/public/styles.css index 1755d47..03416fd 100644 --- a/public/styles.css +++ b/public/styles.css @@ -3,8 +3,28 @@ * * */ + @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'); -.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; +} + +.cell { + font-family: 'Noto Sans' +} + +#replaceMe{ + font-family:'Courier New'; } diff --git a/src/app/database/Schema.zig b/src/app/database/Schema.zig new file mode 100644 index 0000000..560ab3c --- /dev/null +++ b/src/app/database/Schema.zig @@ -0,0 +1,190 @@ +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: f32, + play_count: i32, + holiday: bool, + compilation: 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, .{}), + }, + }, +); + +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, + 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, .{}), + }, + }, +); + +pub const Scrobble = jetquery.Model( + @This(), + "scrobbles", + struct { + id: i32, + date: jetquery.DateTime, + song_id: i32, + album_id: ?i32, + artist_id: i32, + 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, + title: []const u8, + length: f32, + 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: f32, + date: jetquery.DateTime, + 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: 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 new file mode 100644 index 0000000..db0ceae --- /dev/null +++ b/src/app/database/migrations/2024-11-15_14-46-12_create_artists.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( + "artists", + &.{ + t.primaryKey("id", .{}), + t.column("name", .string, .{}), + t.column("album_num", .integer, .{}), + t.column("song_num", .integer, .{}), + t.column("play_count", .integer, .{}), + 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..d34bc30 --- /dev/null +++ b/src/app/database/migrations/2024-11-15_14-58-47_create_songs.zig @@ -0,0 +1,23 @@ +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("title", .string, .{}), + t.column("length", .float, .{}), + t.column("hidden", .boolean, .{}), + t.column("holiday", .boolean, .{}), + t.column("play_count", .integer, .{}), + 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_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..a18ca74 --- /dev/null +++ b/src/app/database/migrations/2024-11-20_19-08-02_create_albums.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( + "albums", + &.{ + t.primaryKey("id", .{}), + t.column("title", .string, .{}), + t.column("song_num", .integer, .{}), + t.column("length", .float, .{}), + t.column("play_count", .integer, .{}), + t.column("holiday", .boolean, .{}), + t.column("compilation", .boolean, .{}), + t.column("deluxe", .boolean, .{}), + t.column("live", .boolean, .{}), + t.timestamps(.{}), + }, + .{}, + ); +} + +pub fn down(repo: anytype) !void { + try repo.dropTable("albums", .{}); +} 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_23-49-05_create_concerts.zig b/src/app/database/migrations/2024-11-21_23-49-05_create_concerts.zig new file mode 100644 index 0000000..8eb782e --- /dev/null +++ b/src/app/database/migrations/2024-11-21_23-49-05_create_concerts.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("location", .string, .{}), + t.column("date", .datetime, .{}), + t.timestamps(.{}), + }, + .{}, + ); +} + +pub fn down(repo: anytype) !void { + try repo.dropTable("concerts", .{}); +} 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..e4264d3 --- /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", .datetime, .{}), + t.timestamps(.{}), + }, + .{}, + ); +} + +pub fn down(repo: anytype) !void { + try repo.dropTable("raw_scrobbles", .{}); +} diff --git a/src/app/database/migrations/2024-11-30_15-40-39_create_scrobbles.zig b/src/app/database/migrations/2024-11-30_15-40-39_create_scrobbles.zig new file mode 100644 index 0000000..ee4552f --- /dev/null +++ b/src/app/database/migrations/2024-11-30_15-40-39_create_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( + "scrobbles", + &.{ + 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(.{}), + }, + .{}, + ); +} + +pub fn down(repo: anytype) !void { + try repo.dropTable("scrobbles", .{}); +} diff --git a/src/app/jobs/process_scrobbles.zig b/src/app/jobs/process_scrobbles.zig new file mode 100644 index 0000000..e0d276a --- /dev/null +++ b/src/app/jobs/process_scrobbles.zig @@ -0,0 +1,86 @@ +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). +// +// Arguments: +// * allocator: Arena allocator for use during the job execution process. +// * 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; + + //const memory = try allocator.alloc(u8, 19); + + // 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| { + //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); + 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, .date = scrobble.date }).execute(env.repo); + } + // Clear RawScrobbles when done processing + try jetzig.database.Query(.RawScrobble).deleteAll().execute(env.repo); +} 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/collection/delete.zmpl b/src/app/views/collection/delete.zmpl new file mode 100644 index 0000000..76457d0 --- /dev/null +++ b/src/app/views/collection/delete.zmpl @@ -0,0 +1,3 @@ +
+ Content goes here +
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/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/partials/_history.zmpl b/src/app/views/partials/_history.zmpl new file mode 100644 index 0000000..e69de29 diff --git a/src/app/views/partials/_random.zmpl b/src/app/views/partials/_random.zmpl new file mode 100644 index 0000000..e69de29 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..8ea394d --- /dev/null +++ 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/partials/_top.zmpl b/src/app/views/partials/_top.zmpl new file mode 100644 index 0000000..3440698 --- /dev/null +++ b/src/app/views/partials/_top.zmpl @@ -0,0 +1,26 @@ +
+
+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/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.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 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:
-
- https://discord.gg/eufqssz7X6 -
-
diff --git a/src/app/views/root/index.zmpl b/src/app/views/root/index.zmpl index cccb8d8..c7615bb 100644 --- a/src/app/views/root/index.zmpl +++ b/src/app/views/root/index.zmpl @@ -4,17 +4,10 @@ - -
- // 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 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/upload.zig b/src/app/views/upload.zig new file mode 100644 index 0000000..80b8a47 --- /dev/null +++ b/src/app/views/upload.zig @@ -0,0 +1,80 @@ +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; + 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) !jetzig.View { + const Scrobble = struct { + track: []u8, + artist: []u8, + album: []u8, + date: u64, + }; + + const lastfm = struct { + username: []u8, + scrobbles: []Scrobble, + }; + + 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; + + 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(.{ .id = counter, .track = scrobble.track, .album = scrobble.album, .artist = scrobble.artist, .date = (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"); + try upload_table.append("Album"); + try upload_table.append("Date"); + + 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..5048944 --- /dev/null +++ b/src/app/views/upload/index.zmpl @@ -0,0 +1,22 @@ + + + + + +@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..176f094 --- /dev/null +++ b/src/app/views/upload/post.zmpl @@ -0,0 +1,15 @@ + + + + + + +@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 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 c7438a3..73770eb 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,11 +1,13 @@ 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"); // 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 = &.{ @@ -14,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, 24); // Maximum filesize for `public/` content. // pub const max_bytes_public_content: usize = std.math.pow(usize, 2, 20); @@ -70,13 +71,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.? }); @@ -85,6 +86,36 @@ 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();