mirror of
https://github.com/nimbold/Firelink.git
synced 2026-07-26 12:08:27 +00:00
feat(downloads): add media quality and queue reordering
This commit is contained in:
@@ -121,6 +121,8 @@ pub struct DownloadItem {
|
||||
#[ts(optional)]
|
||||
pub media_format_selector: Option<String>,
|
||||
#[ts(optional)]
|
||||
pub media_quality: Option<String>,
|
||||
#[ts(optional)]
|
||||
pub queue_id: Option<String>,
|
||||
#[ts(optional)]
|
||||
pub queue_position: Option<i32>,
|
||||
|
||||
+11
-4
@@ -5650,11 +5650,18 @@ async fn move_many_in_queue(
|
||||
ids: Vec<String>,
|
||||
queue_id: String,
|
||||
direction: crate::ipc::QueueDirection,
|
||||
target_index: Option<usize>,
|
||||
) -> Result<Vec<String>, AppError> {
|
||||
Ok(state
|
||||
.queue_manager
|
||||
.move_many_in_queue(&ids, &queue_id, direction)
|
||||
.await)
|
||||
Ok(match target_index {
|
||||
Some(target_index) => state
|
||||
.queue_manager
|
||||
.move_many_in_queue_to(&ids, &queue_id, target_index)
|
||||
.await,
|
||||
None => state
|
||||
.queue_manager
|
||||
.move_many_in_queue(&ids, &queue_id, direction)
|
||||
.await,
|
||||
})
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
|
||||
+64
-4
@@ -22,6 +22,34 @@ pub fn clamp_download_connections(connections: i32) -> i32 {
|
||||
connections.clamp(DOWNLOAD_CONNECTIONS_MIN, DOWNLOAD_CONNECTIONS_MAX)
|
||||
}
|
||||
|
||||
fn reorder_selected_queue_tasks(
|
||||
queue_tasks: &[QueuedTask],
|
||||
ids: &[String],
|
||||
target_index: usize,
|
||||
) -> Option<Vec<QueuedTask>> {
|
||||
let selected_ids = ids.iter().collect::<HashSet<_>>();
|
||||
let selected_tasks = queue_tasks
|
||||
.iter()
|
||||
.filter(|task| selected_ids.contains(&task.id))
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
if selected_tasks.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let unselected_tasks = queue_tasks
|
||||
.iter()
|
||||
.filter(|task| !selected_ids.contains(&task.id))
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
let insert_index = target_index.min(unselected_tasks.len());
|
||||
let mut reordered = Vec::with_capacity(queue_tasks.len());
|
||||
reordered.extend_from_slice(&unselected_tasks[..insert_index]);
|
||||
reordered.extend(selected_tasks);
|
||||
reordered.extend_from_slice(&unselected_tasks[insert_index..]);
|
||||
Some(reordered)
|
||||
}
|
||||
|
||||
type Aria2ControlLocks = Arc<StdMutex<HashMap<String, Arc<Mutex<()>>>>>;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
@@ -1591,10 +1619,8 @@ impl<R: tauri::Runtime> QueueManager<R> {
|
||||
.saturating_add(1)
|
||||
.min(unselected_tasks.len()),
|
||||
};
|
||||
let mut reordered = Vec::with_capacity(queue_tasks.len());
|
||||
reordered.extend_from_slice(&unselected_tasks[..insert_index]);
|
||||
reordered.extend(selected_tasks);
|
||||
reordered.extend_from_slice(&unselected_tasks[insert_index..]);
|
||||
let reordered = reorder_selected_queue_tasks(&queue_tasks, ids, insert_index)
|
||||
.expect("selected queue tasks were present");
|
||||
|
||||
for (queue_index, pending_index) in queue_positions.iter().enumerate() {
|
||||
pending[*pending_index] = reordered[queue_index].clone();
|
||||
@@ -1607,6 +1633,40 @@ impl<R: tauri::Runtime> QueueManager<R> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Atomically place a selected block at an insertion index among the
|
||||
/// unselected tasks in one queue. This is the drag/drop counterpart to
|
||||
/// move_many_in_queue; the index is clamped at the backend boundary so a
|
||||
/// stale pointer position cannot create an invalid queue state.
|
||||
pub async fn move_many_in_queue_to(
|
||||
&self,
|
||||
ids: &[String],
|
||||
queue_id: &str,
|
||||
target_index: usize,
|
||||
) -> Vec<String> {
|
||||
let mut pending = self.pending.lock().await;
|
||||
let queue_positions = pending
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(index, task)| (task.queue_id == queue_id).then_some(index))
|
||||
.collect::<Vec<_>>();
|
||||
let queue_tasks = queue_positions
|
||||
.iter()
|
||||
.map(|index| pending[*index].clone())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if let Some(reordered) = reorder_selected_queue_tasks(&queue_tasks, ids, target_index) {
|
||||
for (queue_index, pending_index) in queue_positions.iter().enumerate() {
|
||||
pending[*pending_index] = reordered[queue_index].clone();
|
||||
}
|
||||
}
|
||||
|
||||
pending
|
||||
.iter()
|
||||
.filter(|task| task.queue_id == queue_id)
|
||||
.map(|task| task.id.clone())
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Remove a task from pending if present (used by remove_download).
|
||||
/// Does NOT release a permit (the caller handles active permits via
|
||||
/// release_permit if the task was already dispatched).
|
||||
|
||||
@@ -1247,6 +1247,33 @@ async fn multi_move_reorders_selected_items_as_one_atomic_block() {
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn target_move_reorders_a_selected_block_and_clamps_the_target() {
|
||||
use firelink_lib::ipc::QueueDirection;
|
||||
|
||||
let (mgr, _spawner) = make_manager(3);
|
||||
for id in ["a", "b", "c", "d", "e"] {
|
||||
mgr.push(sample_task(id)).await.unwrap();
|
||||
}
|
||||
|
||||
let selected = vec!["b".to_string(), "d".to_string()];
|
||||
assert_eq!(
|
||||
mgr.move_many_in_queue_to(&selected, "main", 1).await,
|
||||
vec!["a", "b", "d", "c", "e"]
|
||||
);
|
||||
assert_eq!(
|
||||
mgr.move_many_in_queue_to(&selected, "main", usize::MAX).await,
|
||||
vec!["a", "c", "e", "b", "d"]
|
||||
);
|
||||
|
||||
// The original direction API remains unchanged for keyboard/button moves.
|
||||
assert_eq!(
|
||||
mgr.move_many_in_queue(&selected, "main", QueueDirection::Up)
|
||||
.await,
|
||||
vec!["a", "c", "b", "d", "e"]
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn moving_one_queue_does_not_reorder_another_queue() {
|
||||
use firelink_lib::ipc::QueueDirection;
|
||||
|
||||
Reference in New Issue
Block a user