diff --git a/frontend/PauseButton.hx b/frontend/PauseButton.hx index 237dffb..026e35b 100644 --- a/frontend/PauseButton.hx +++ b/frontend/PauseButton.hx @@ -5,7 +5,7 @@ function onClick() { Util.paused = !Util.paused; var xhr = new XMLHttpRequest(); - xhr.open("POST", Util.resolveUrl("set_paused")); + xhr.open("POST", Util.resolveUrl("api/set_paused")); xhr.send(Std.string(Util.paused)); updateDisplay(); diff --git a/src/main.zig b/src/main.zig index 96eb971..cc0a14f 100644 --- a/src/main.zig +++ b/src/main.zig @@ -140,10 +140,10 @@ fn tryHandleRequest(state: *State, res_: std.http.Server.Response) !void { if (std.mem.eql(u8, path, "/") or std.mem.eql(u8, path, "/index.html")) { try routes.indexRoute(state, &res); - } else if (std.mem.eql(u8, path, "/index.json")) { - try routes.indexJsonRoute(state, &res); - } else if (std.mem.eql(u8, path, "/set_paused")) { - try routes.setPausedRoute(state, &res); + } else if (std.mem.eql(u8, path, "/api/index.json")) { + try routes.api.indexJsonRoute(state, &res); + } else if (std.mem.eql(u8, path, "/api/set_paused")) { + try routes.api.setPausedRoute(state, &res); } else if (std.mem.eql(u8, path, "/static/index.js")) { try routes.static(&res, assets.@"index.js", "application/javascript"); } else if (std.mem.eql(u8, path, "/static/index.css")) { diff --git a/src/routes.zig b/src/routes.zig index fb640d2..d9e8e18 100644 --- a/src/routes.zig +++ b/src/routes.zig @@ -31,6 +31,106 @@ pub fn static(res: *std.http.Server.Response, data: []const u8, mime: ?[]const u try res.finish(); } +pub const api = struct { + pub fn indexJsonRoute( + state: *State, + res: *std.http.Server.Response, + ) !void { + const alloc = res.allocator; + + const Response = struct { + active_task: ?DownloadQueue.Task, + tasks: []DownloadQueue.Task, + vids: [][]u8, + paused: bool, + }; + + var arena = std.heap.ArenaAllocator.init(alloc); + defer arena.deinit(); + const arena_alloc = arena.allocator(); + + // TODO: optimize + const tasks = try alloc.alloc(DownloadQueue.Task, state.downloads.tasks.len()); + defer alloc.free(tasks); + + var cur_task = state.downloads.tasks.first; + var i: usize = 0; + while (cur_task) |t| : (i += 1) { + tasks[i] = t.data; + cur_task = t.next; + } + + const vids_dir_path = try std.fs.path.join(alloc, &.{ state.conf.data_dir, "vids" }); + defer alloc.free(vids_dir_path); + + var vids_dir = try std.fs.cwd().openIterableDir(vids_dir_path, .{}); + defer vids_dir.close(); + + var iter = vids_dir.iterate(); + + var vids = std.ArrayList([]u8).init(alloc); + defer vids.deinit(); + while (try iter.next()) |ent| { + if (ent.kind != .file) + continue; + + const quote = try std.Uri.escapeString(arena_alloc, ent.name); + + try vids.append(try std.fmt.allocPrint( + alloc, + "{s}/vids/{s}", + .{ state.conf.base_url, quote }, + )); + } + + try res.headers.append("Content-Type", "application/json"); + // Streaming serialization to HTTP! + res.transfer_encoding = .chunked; + + const data = Response{ + .active_task = state.downloads.active_task, + .tasks = tasks, + .vids = vids.items, + .paused = state.downloads.paused, + }; + + try res.do(); + var buf_writer = std.io.bufferedWriter(res.writer()); + try std.json.stringify(data, .{}, buf_writer.writer()); + try buf_writer.flush(); + try res.finish(); + } + + pub fn setPausedRoute( + state: *State, + res: *std.http.Server.Response, + ) !void { + const alloc = res.allocator; + + if (res.request.method != .POST) { + try status_response.sendJsonResponseText(res, .method_not_allowed); + return; + } + + const data = try res.reader().readAllAlloc(alloc, 1024); + defer alloc.free(data); + + const paused = if (std.mem.eql(u8, data, "true")) + true + else if (std.mem.eql(u8, data, "false")) + false + else { + try status_response.sendJsonResponseText(res, .bad_request); + return; + }; + + std.log.info("setting paused state to {}", .{paused}); + state.downloads.setPaused(paused); + + try status_response.sendJsonResponseText(res, .ok); + } +}; + pub fn indexRoute( state: *State, res: *std.http.Server.Response, @@ -180,75 +280,6 @@ pub fn indexRoute( try res.finish(); } -pub fn indexJsonRoute( - state: *State, - res: *std.http.Server.Response, -) !void { - const alloc = res.allocator; - - const Response = struct { - active_task: ?DownloadQueue.Task, - tasks: []DownloadQueue.Task, - vids: [][]u8, - paused: bool, - }; - - var arena = std.heap.ArenaAllocator.init(alloc); - defer arena.deinit(); - const arena_alloc = arena.allocator(); - - // TODO: optimize - const tasks = try alloc.alloc(DownloadQueue.Task, state.downloads.tasks.len()); - defer alloc.free(tasks); - - var cur_task = state.downloads.tasks.first; - var i: usize = 0; - while (cur_task) |t| : (i += 1) { - tasks[i] = t.data; - cur_task = t.next; - } - - const vids_dir_path = try std.fs.path.join(alloc, &.{ state.conf.data_dir, "vids" }); - defer alloc.free(vids_dir_path); - - var vids_dir = try std.fs.cwd().openIterableDir(vids_dir_path, .{}); - defer vids_dir.close(); - - var iter = vids_dir.iterate(); - - var vids = std.ArrayList([]u8).init(alloc); - defer vids.deinit(); - while (try iter.next()) |ent| { - if (ent.kind != .file) - continue; - - const quote = try std.Uri.escapeString(arena_alloc, ent.name); - - try vids.append(try std.fmt.allocPrint( - alloc, - "{s}/vids/{s}", - .{ state.conf.base_url, quote }, - )); - } - - try res.headers.append("Content-Type", "application/json"); - // Streaming serialization to HTTP! - res.transfer_encoding = .chunked; - - const data = Response{ - .active_task = state.downloads.active_task, - .tasks = tasks, - .vids = vids.items, - .paused = state.downloads.paused, - }; - - try res.do(); - var buf_writer = std.io.bufferedWriter(res.writer()); - try std.json.stringify(data, .{}, buf_writer.writer()); - try buf_writer.flush(); - try res.finish(); -} - pub const ApiTask = struct { url: []const u8, outname: ?[]const u8, @@ -366,32 +397,3 @@ pub fn vidsRoute( }, } } - -pub fn setPausedRoute( - state: *State, - res: *std.http.Server.Response, -) !void { - const alloc = res.allocator; - - if (res.request.method != .POST) { - try status_response.sendJsonResponseText(res, .method_not_allowed); - return; - } - - const data = try res.reader().readAllAlloc(alloc, 1024); - defer alloc.free(data); - - const paused = if (std.mem.eql(u8, data, "true")) - true - else if (std.mem.eql(u8, data, "false")) - false - else { - try status_response.sendJsonResponseText(res, .bad_request); - return; - }; - - std.log.info("setting paused state to {}", .{paused}); - state.downloads.setPaused(paused); - - try status_response.sendJsonResponseText(res, .ok); -}