mirror of
https://github.com/modrinth/code.git
synced 2026-08-26 17:44:50 +00:00
feat: instances v2 (#6431)
* feat: base of instances v2 * feat: use old profiles with compat layer * prototype: instances v2 * fix: install_from using profile * fix: skins migration fix * fix: frontend still using profile path * fix: add update proj multiselect guard * fix: cargo fmt * fix: content missing fields * feat: break up app-lib/api/instance.rs * fix: check_content_updates mismatch * fix: updater modal cleanup w/new structure * feat: better update all handling * fix: remove preview_update_all * fix: feedback on bulk update + lint * fix: rem transitions * fix: change to jsonb * feat: app db backup after update * fix: lint * fix: sqlx prepare + use sqlx macros * fix: lint * fix: bugs * feat: defuck the installing process up * fix: bug of hell * fix: shear * fix: fmt * fix: install progress spacing + change mc/content/overrides to bytes * fix: lint * fix: prepr * fix: navtabs anim not working in app * fix: worlds.vue improvements + browse page fixes * feat: optimise queries + adapter fns * fix: lint * fix: lint * feat: shared modrinth-content-management crate (#6469) * feat: disable warnings setting * feat: add instances shortcuts (#6329) * Add modrinth://launch deep link to start a profile Support external profile launching via modrinth://launch/{profile_path} for integrations such as Stream Deck. * Change route to /launch/profile/{id} for future extensibility * fix: ensure profile path is url decoded * fix: URL-decode profile path from deep link * fix: use urlencoding crate for URL decoding * feat: implement app instance shortcuts * feat: change windows shortcut creation to use windows api instead * feat: implement creating a shortcut launching world/server * format * fmt * fix multiline inline tables * pnpm prepr * feat: move create shortcut to last item * refactor: split up shortcuts.rs for individual platforms * refactor: turn profile launch url into url type * use string literal and add safety comment * pt2 * refactor: rename anything that's profile into instance * update mac shortcut --------- Co-authored-by: DJCheesusReal <134006619+DJCheesusReal@users.noreply.github.com> --------- Co-authored-by: Truman Gao <106889354+tdgao@users.noreply.github.com> Co-authored-by: DJCheesusReal <134006619+DJCheesusReal@users.noreply.github.com>
This commit is contained in:
co-authored by
DJCheesusReal
Truman Gao
parent
ef4044534f
commit
734720e11e
@@ -12,7 +12,9 @@ use serde::de::DeserializeOwned;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::ffi::OsStr;
|
||||
use std::future::Future;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::pin::Pin;
|
||||
use std::sync::LazyLock;
|
||||
use std::time::{self};
|
||||
use tokio::sync::Semaphore;
|
||||
@@ -207,6 +209,13 @@ pub static REQWEST_CLIENT: LazyLock<reqwest::Client> = LazyLock::new(|| {
|
||||
|
||||
const FETCH_ATTEMPTS: usize = 2;
|
||||
|
||||
pub type FetchProgressFn<'a> = dyn FnMut(
|
||||
u64,
|
||||
u64,
|
||||
) -> Pin<Box<dyn Future<Output = crate::Result<()>> + Send + 'a>>
|
||||
+ Send
|
||||
+ 'a;
|
||||
|
||||
#[tracing::instrument(skip(semaphore))]
|
||||
pub async fn fetch(
|
||||
url: &str,
|
||||
@@ -311,6 +320,38 @@ pub async fn fetch_advanced(
|
||||
.await
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(json_body, semaphore, progress))]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn fetch_advanced_with_progress(
|
||||
method: Method,
|
||||
url: &str,
|
||||
sha1: Option<&str>,
|
||||
json_body: Option<serde_json::Value>,
|
||||
header: Option<(&str, &str)>,
|
||||
download_meta: Option<&DownloadMeta>,
|
||||
loading_bar: Option<(&LoadingBarId, f64)>,
|
||||
uri_path: Option<&'static str>,
|
||||
semaphore: &FetchSemaphore,
|
||||
exec: impl sqlx::Executor<'_, Database = sqlx::Sqlite>,
|
||||
progress: Option<&mut FetchProgressFn<'_>>,
|
||||
) -> crate::Result<Bytes> {
|
||||
fetch_advanced_with_client_and_progress(
|
||||
method,
|
||||
url,
|
||||
sha1,
|
||||
json_body,
|
||||
header,
|
||||
download_meta,
|
||||
loading_bar,
|
||||
uri_path,
|
||||
semaphore,
|
||||
exec,
|
||||
&INSECURE_REQWEST_CLIENT,
|
||||
progress,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Downloads a file with retry and checksum functionality
|
||||
#[tracing::instrument(skip(json_body, semaphore))]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
@@ -326,6 +367,39 @@ pub async fn fetch_advanced_with_client(
|
||||
semaphore: &FetchSemaphore,
|
||||
exec: impl sqlx::Executor<'_, Database = sqlx::Sqlite>,
|
||||
client: &reqwest::Client,
|
||||
) -> crate::Result<Bytes> {
|
||||
fetch_advanced_with_client_and_progress(
|
||||
method,
|
||||
url,
|
||||
sha1,
|
||||
json_body,
|
||||
header,
|
||||
download_meta,
|
||||
loading_bar,
|
||||
uri_path,
|
||||
semaphore,
|
||||
exec,
|
||||
client,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(json_body, semaphore, client, progress))]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn fetch_advanced_with_client_and_progress(
|
||||
method: Method,
|
||||
url: &str,
|
||||
sha1: Option<&str>,
|
||||
json_body: Option<serde_json::Value>,
|
||||
header: Option<(&str, &str)>,
|
||||
download_meta: Option<&DownloadMeta>,
|
||||
loading_bar: Option<(&LoadingBarId, f64)>,
|
||||
uri_path: Option<&'static str>,
|
||||
semaphore: &FetchSemaphore,
|
||||
exec: impl sqlx::Executor<'_, Database = sqlx::Sqlite>,
|
||||
client: &reqwest::Client,
|
||||
mut progress: Option<&mut FetchProgressFn<'_>>,
|
||||
) -> crate::Result<Bytes> {
|
||||
let _permit = semaphore.0.acquire().await?;
|
||||
|
||||
@@ -398,23 +472,30 @@ pub async fn fetch_advanced_with_client(
|
||||
return Err(backup_error.into());
|
||||
}
|
||||
|
||||
let bytes = if let Some((bar, total)) = &loading_bar {
|
||||
let bytes = if loading_bar.is_some() || progress.is_some() {
|
||||
let length = resp.content_length();
|
||||
if let Some(total_size) = length {
|
||||
use futures::StreamExt;
|
||||
let mut stream = resp.bytes_stream();
|
||||
let mut bytes = Vec::new();
|
||||
let mut downloaded = 0_u64;
|
||||
while let Some(item) = stream.next().await {
|
||||
let chunk = item.or(Err(ErrorKind::NoValueFor(
|
||||
"fetch bytes".to_string(),
|
||||
)))?;
|
||||
downloaded += chunk.len() as u64;
|
||||
bytes.append(&mut chunk.to_vec());
|
||||
emit_loading(
|
||||
bar,
|
||||
(chunk.len() as f64 / total_size as f64)
|
||||
* total,
|
||||
None,
|
||||
)?;
|
||||
if let Some((bar, total)) = &loading_bar {
|
||||
emit_loading(
|
||||
bar,
|
||||
(chunk.len() as f64 / total_size as f64)
|
||||
* total,
|
||||
None,
|
||||
)?;
|
||||
}
|
||||
if let Some(progress) = progress.as_mut() {
|
||||
progress(downloaded, total_size).await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(bytes::Bytes::from(bytes))
|
||||
|
||||
@@ -314,18 +314,6 @@ pub async fn remove_file(
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn open_file(
|
||||
path: impl AsRef<std::path::Path>,
|
||||
) -> Result<tokio::fs::File, IOError> {
|
||||
let path = path.as_ref();
|
||||
tokio::fs::File::open(path)
|
||||
.await
|
||||
.map_err(|e| IOError::IOPathError {
|
||||
source: e,
|
||||
path: path.to_string_lossy().to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn remove_dir(
|
||||
path: impl AsRef<std::path::Path>,
|
||||
) -> Result<(), IOError> {
|
||||
|
||||
Reference in New Issue
Block a user