feat: priority system
This commit is contained in:
parent
65e6fbaf90
commit
2178cb181a
5 changed files with 84 additions and 60 deletions
|
@ -19,7 +19,7 @@ a:visited {
|
|||
background-color: #181825;
|
||||
}
|
||||
|
||||
#enqueue_form *:not([type="checkbox"]),
|
||||
#enqueue_form *:not([type="checkbox"]):not([type="number"]),
|
||||
#pause_btn {
|
||||
display: block;
|
||||
width: 100%;
|
||||
|
@ -86,3 +86,15 @@ ul {
|
|||
#head_div * {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.prio_low_txt {
|
||||
color: #94e2d5;
|
||||
}
|
||||
|
||||
.prio_medium_txt {
|
||||
color: #a6e3a1;
|
||||
}
|
||||
|
||||
.prio_high_txt {
|
||||
color: #eba0ac;
|
||||
}
|
||||
|
|
|
@ -4,14 +4,14 @@ import js.html.XMLHttpRequest;
|
|||
class EnqueueTask {
|
||||
var url:String;
|
||||
var outname:Null<String>;
|
||||
var prepend:Bool;
|
||||
var priority:Int;
|
||||
var description:Bool;
|
||||
var audio_only:Bool;
|
||||
|
||||
public function new(url:String, outname:Null<String>, prepend:Bool, description:Bool, audio_only:Bool) {
|
||||
public function new(url:String, outname:Null<String>, priority:Int, description:Bool, audio_only:Bool) {
|
||||
this.url = url;
|
||||
this.outname = outname;
|
||||
this.prepend = prepend;
|
||||
this.priority = priority;
|
||||
this.description = description;
|
||||
this.audio_only = audio_only;
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ function onEnqSubmit(e:Dynamic):Void {
|
|||
new EnqueueTask(
|
||||
url_inp.value,
|
||||
outname_inp.value.length == 0 ? null : outname_inp.value,
|
||||
cast(Browser.document.getElementById("prepend_switch"), InputElement).checked,
|
||||
Std.parseInt(cast(Browser.document.getElementById("priority_num"), InputElement).value),
|
||||
cast(Browser.document.getElementById("description_switch"), InputElement).checked,
|
||||
cast(Browser.document.getElementById("audio_only_switch"), InputElement).checked
|
||||
).send();
|
||||
|
|
|
@ -8,6 +8,9 @@ pub const Task = struct {
|
|||
/// URL of the video
|
||||
url: []const u8,
|
||||
|
||||
/// Priority to order queue by
|
||||
priority: i32,
|
||||
|
||||
/// Optional output file name or yt-dlp format string
|
||||
outname: ?[]const u8,
|
||||
|
||||
|
@ -17,21 +20,32 @@ pub const Task = struct {
|
|||
/// Write description
|
||||
description: bool,
|
||||
|
||||
pub fn copy(self: *const Task, alloc: std.mem.Allocator) !Task {
|
||||
pub fn copy(self: Task, alloc: std.mem.Allocator) !Task {
|
||||
const url_c = try alloc.dupe(u8, self.url);
|
||||
errdefer alloc.free(url_c);
|
||||
|
||||
const outname_c = if (self.outname) |n| try alloc.dupe(u8, n) else null;
|
||||
errdefer if (outname_c) |n| alloc.free(n);
|
||||
|
||||
return .{
|
||||
.url = try alloc.dupe(u8, self.url),
|
||||
.outname = blk: {
|
||||
break :blk try alloc.dupe(u8, self.outname orelse break :blk null);
|
||||
},
|
||||
.url = url_c,
|
||||
.priority = self.priority,
|
||||
.outname = outname_c,
|
||||
.description = self.description,
|
||||
.audio_only = self.audio_only,
|
||||
};
|
||||
}
|
||||
|
||||
fn compare(ctx: void, a: Task, b: Task) std.math.Order {
|
||||
_ = ctx;
|
||||
return std.math.order(b.priority, a.priority);
|
||||
}
|
||||
};
|
||||
|
||||
const TaskQueue = std.PriorityQueue(Task, void, Task.compare);
|
||||
|
||||
state: *State,
|
||||
alloc: std.mem.Allocator,
|
||||
tasks: std.TailQueue(Task),
|
||||
tasks: TaskQueue,
|
||||
next_cond: std.Thread.Condition,
|
||||
m: std.Thread.Mutex,
|
||||
active_task: ?Task,
|
||||
|
@ -43,8 +57,7 @@ pub fn spawn(alloc: std.mem.Allocator, state: *State) !*DownloadQueue {
|
|||
const self = try alloc.create(DownloadQueue);
|
||||
self.* = .{
|
||||
.state = state,
|
||||
.alloc = alloc,
|
||||
.tasks = .{},
|
||||
.tasks = TaskQueue.init(alloc, {}),
|
||||
.next_cond = .{},
|
||||
.m = .{},
|
||||
.active_task = null,
|
||||
|
@ -59,18 +72,17 @@ pub fn spawn(alloc: std.mem.Allocator, state: *State) !*DownloadQueue {
|
|||
}
|
||||
|
||||
/// Adds a task to the queue. The task given is copied.
|
||||
pub fn pushTask(self: *DownloadQueue, task: Task, prepend: bool) !void {
|
||||
const node = try self.alloc.create(std.TailQueue(Task).Node);
|
||||
node.* = .{ .data = try task.copy(self.alloc) };
|
||||
pub fn pushTask(self: *DownloadQueue, task: Task) !void {
|
||||
const task_copy = try task.copy(self.tasks.allocator);
|
||||
errdefer {
|
||||
self.tasks.allocator.free(task_copy.url);
|
||||
if (task_copy.outname) |n| self.tasks.allocator.free(n);
|
||||
}
|
||||
|
||||
self.m.lock();
|
||||
defer self.m.unlock();
|
||||
|
||||
if (prepend) {
|
||||
self.tasks.append(node);
|
||||
} else {
|
||||
self.tasks.prepend(node);
|
||||
}
|
||||
try self.tasks.add(task_copy);
|
||||
self.next_cond.signal();
|
||||
}
|
||||
|
||||
|
@ -92,36 +104,33 @@ fn run(self: *DownloadQueue) noreturn {
|
|||
}
|
||||
|
||||
fn next(self: *DownloadQueue) !void {
|
||||
var arena = std.heap.ArenaAllocator.init(self.alloc);
|
||||
var arena = std.heap.ArenaAllocator.init(self.tasks.allocator);
|
||||
defer arena.deinit();
|
||||
|
||||
const task = blk: {
|
||||
self.m.lock();
|
||||
defer self.m.unlock();
|
||||
|
||||
if (self.state.conf.autopause and self.tasks.len <= 0 and !self.paused) {
|
||||
if (self.state.conf.autopause and self.tasks.count() <= 0 and !self.paused) {
|
||||
std.log.info("queue done; autopausing", .{});
|
||||
self.paused = true;
|
||||
}
|
||||
|
||||
while (self.tasks.len <= 0 or self.paused) {
|
||||
while (self.tasks.count() <= 0 or self.paused) {
|
||||
self.next_cond.wait(&self.m);
|
||||
}
|
||||
|
||||
const node = self.tasks.pop().?;
|
||||
defer self.alloc.destroy(node);
|
||||
|
||||
self.active_task = node.data;
|
||||
|
||||
break :blk node.data;
|
||||
const task = self.tasks.remove();
|
||||
self.active_task = task;
|
||||
break :blk task;
|
||||
};
|
||||
defer {
|
||||
self.m.lock();
|
||||
defer self.m.unlock();
|
||||
|
||||
self.alloc.free(task.url);
|
||||
self.tasks.allocator.free(task.url);
|
||||
if (task.outname) |outname| {
|
||||
self.alloc.free(outname);
|
||||
self.tasks.allocator.free(outname);
|
||||
}
|
||||
self.active_task = null;
|
||||
}
|
||||
|
|
|
@ -82,20 +82,28 @@ pub fn indexRoute(
|
|||
\\<table id="tasks_tbl"><tbody>
|
||||
);
|
||||
|
||||
var task = state.downloads.tasks.last;
|
||||
while (task) |t| {
|
||||
var iter = state.downloads.tasks.iterator();
|
||||
while (iter.next()) |t| {
|
||||
try w.print(
|
||||
\\<tr><td>{s}{s}{s}</td><td>{s}</td></tr>
|
||||
\\<tr>
|
||||
\\ <td>{s}{s}{s}</td>
|
||||
\\ <td>{s}</td>
|
||||
\\ <td><span class="prio_{s}_txt">{}</span></td>
|
||||
\\</tr>
|
||||
,
|
||||
.{
|
||||
if (t.data.description) description_prefix_html else "",
|
||||
if (t.data.audio_only) audio_only_prefix_html else "",
|
||||
t.data.url,
|
||||
t.data.outname orelse inferred_html,
|
||||
if (t.description) description_prefix_html else "",
|
||||
if (t.audio_only) audio_only_prefix_html else "",
|
||||
t.url,
|
||||
t.outname orelse inferred_html,
|
||||
switch (std.math.order(t.priority, 0)) {
|
||||
.gt => "high",
|
||||
.eq => "medium",
|
||||
.lt => "low",
|
||||
},
|
||||
t.priority,
|
||||
},
|
||||
);
|
||||
|
||||
task = t.prev;
|
||||
}
|
||||
|
||||
try w.writeAll(
|
||||
|
@ -114,7 +122,7 @@ pub fn indexRoute(
|
|||
\\<form id="enqueue_form">
|
||||
\\ <input type="text" placeholder="URL" id="url_inp"><br>
|
||||
\\ <input type="text" placeholder="output name" id="outname_inp"><br>
|
||||
\\ <input type="checkbox" id="prepend_switch">Prepend<br>
|
||||
\\ <input type="number" id="priority_num" min="-1000" max="1000" value="0"> Priority<br>
|
||||
\\ <input type="checkbox" id="description_switch">Description<br>
|
||||
\\ <input type="checkbox" id="audio_only_switch">Audio Only<br>
|
||||
\\ <input type="submit" id="enqueue_btn" value="Enqueue">
|
||||
|
@ -204,14 +212,12 @@ pub fn indexJsonRoute(
|
|||
defer arena.deinit();
|
||||
const arena_alloc = arena.allocator();
|
||||
|
||||
// TODO: optimize
|
||||
const tasks = try arena_alloc.alloc(DownloadQueue.Task, state.downloads.tasks.len);
|
||||
var cur_task = state.downloads.tasks.last;
|
||||
var task_iter = state.downloads.tasks.iterator();
|
||||
var i: usize = 0;
|
||||
while (cur_task) |t| {
|
||||
tasks[i] = t.data;
|
||||
i += 1;
|
||||
cur_task = t.prev;
|
||||
}
|
||||
while (task_iter.next()) |task| : (i += 1)
|
||||
tasks[i] = task;
|
||||
|
||||
var vids_dir = try std.fs.cwd().openIterableDir(state.vids_dir, .{});
|
||||
defer vids_dir.close();
|
||||
|
@ -256,8 +262,7 @@ pub const ApiTask = struct {
|
|||
outname: ?[]const u8,
|
||||
audio_only: bool = false,
|
||||
description: bool = false,
|
||||
|
||||
prepend: bool = false,
|
||||
priority: i32 = 0,
|
||||
};
|
||||
|
||||
pub fn vidsRoute(
|
||||
|
@ -332,15 +337,13 @@ pub fn vidsRoute(
|
|||
}
|
||||
|
||||
std.log.info("adding {s} to queue", .{task.url});
|
||||
try state.downloads.pushTask(
|
||||
.{
|
||||
.url = task.url,
|
||||
.outname = task.outname,
|
||||
.audio_only = task.audio_only,
|
||||
.description = task.description,
|
||||
},
|
||||
task.prepend,
|
||||
);
|
||||
try state.downloads.pushTask(.{
|
||||
.url = task.url,
|
||||
.outname = task.outname,
|
||||
.audio_only = task.audio_only,
|
||||
.description = task.description,
|
||||
.priority = task.priority,
|
||||
});
|
||||
|
||||
try status_response.sendJsonResponseText(res, .accepted);
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue