✨ Significantly improve the download experience.
This commit is contained in:
1120
src-tauri/Cargo.lock
generated
1120
src-tauri/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -18,7 +18,6 @@ tauri-build = { version = "1.4.0", features = [] }
|
|||||||
serde_json = "1.0.96"
|
serde_json = "1.0.96"
|
||||||
serde = { version = "1.0.163", features = ["derive"] }
|
serde = { version = "1.0.163", features = ["derive"] }
|
||||||
tauri = { version = "1.4.1", features = ["api-all", "system-tray"] }
|
tauri = { version = "1.4.1", features = ["api-all", "system-tray"] }
|
||||||
download_rs = { version = "0.2.0", features = ["sync_download"] }
|
|
||||||
tauri-plugin-window-state = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "dev" }
|
tauri-plugin-window-state = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "dev" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
use crate::util::{check_file_or_append, get_download_message, show_toast};
|
use crate::util::{check_file_or_append, get_download_message, MessageType, show_toast};
|
||||||
use download_rs::sync_download::Download;
|
|
||||||
use tauri::{api, command, AppHandle, Manager, Window};
|
use tauri::{api, command, AppHandle, Manager, Window};
|
||||||
|
use tauri::api::http::{ClientBuilder, HttpRequestBuilder, ResponseType};
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize)]
|
||||||
pub struct DownloadFileParams {
|
pub struct DownloadFileParams {
|
||||||
@@ -11,16 +13,29 @@ pub struct DownloadFileParams {
|
|||||||
#[command]
|
#[command]
|
||||||
pub async fn download_file(app: AppHandle, params: DownloadFileParams) -> Result<(), String> {
|
pub async fn download_file(app: AppHandle, params: DownloadFileParams) -> Result<(), String> {
|
||||||
let window: Window = app.get_window("pake").unwrap();
|
let window: Window = app.get_window("pake").unwrap();
|
||||||
|
show_toast(&window, &get_download_message(MessageType::Start));
|
||||||
|
|
||||||
let output_path = api::path::download_dir().unwrap().join(params.filename);
|
let output_path = api::path::download_dir().unwrap().join(params.filename);
|
||||||
let file_path = check_file_or_append(output_path.to_str().unwrap());
|
let file_path = check_file_or_append(output_path.to_str().unwrap());
|
||||||
let download = Download::new(¶ms.url, Some(&file_path), None);
|
let client = ClientBuilder::new().build().unwrap();
|
||||||
match download.download() {
|
|
||||||
Ok(_) => {
|
let response = client.send(
|
||||||
show_toast(&window, &get_download_message());
|
HttpRequestBuilder::new("GET", ¶ms.url)
|
||||||
|
.unwrap()
|
||||||
|
.response_type(ResponseType::Binary)
|
||||||
|
).await;
|
||||||
|
|
||||||
|
match response {
|
||||||
|
Ok(res) => {
|
||||||
|
let bytes = res.bytes().await.unwrap().data;
|
||||||
|
|
||||||
|
let mut file = File::create(file_path).unwrap();
|
||||||
|
file.write_all(&bytes).unwrap();
|
||||||
|
show_toast(&window, &get_download_message(MessageType::Success));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
show_toast(&window, &e.to_string());
|
show_toast(&window, &get_download_message(MessageType::Failure));
|
||||||
Err(e.to_string())
|
Err(e.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ pub fn get_menu() -> Menu {
|
|||||||
pub fn menu_event_handle(event: WindowMenuEvent) {
|
pub fn menu_event_handle(event: WindowMenuEvent) {
|
||||||
if event.menu_item_id() == "close" {
|
if event.menu_item_id() == "close" {
|
||||||
event.window().minimize().expect("can't minimize window");
|
event.window().minimize().expect("can't minimize window");
|
||||||
// event.window().eval("toggleVideoPlayback(true);").unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if event.menu_item_id() == "goto_url" {
|
if event.menu_item_id() == "goto_url" {
|
||||||
|
|||||||
130
src-tauri/src/inject/event.js
vendored
130
src-tauri/src/inject/event.js
vendored
@@ -1,17 +1,13 @@
|
|||||||
const shortcuts = {
|
const shortcuts = {
|
||||||
ArrowUp: () => scrollTo(0, 0),
|
'ArrowUp': () => scrollTo(0, 0),
|
||||||
ArrowDown: () => scrollTo(0, document.body.scrollHeight),
|
'ArrowDown': () => scrollTo(0, document.body.scrollHeight),
|
||||||
// Don't use command + ArrowLeft or command + ArrowRight
|
|
||||||
// When editing text in page, it causes unintended page navigation.
|
|
||||||
// ArrowLeft: () => window.history.back(),
|
|
||||||
// ArrowRight: () => window.history.forward(),
|
|
||||||
'[': () => window.history.back(),
|
'[': () => window.history.back(),
|
||||||
']': () => window.history.forward(),
|
']': () => window.history.forward(),
|
||||||
r: () => window.location.reload(),
|
'r': () => window.location.reload(),
|
||||||
'-': () => zoomOut(),
|
'-': () => zoomOut(),
|
||||||
'=': () => zoomIn(),
|
'=': () => zoomIn(),
|
||||||
'+': () => zoomIn(),
|
'+': () => zoomIn(),
|
||||||
0: () => setZoom('100%'),
|
'0': () => setZoom('100%'),
|
||||||
};
|
};
|
||||||
|
|
||||||
function setZoom(zoom) {
|
function setZoom(zoom) {
|
||||||
@@ -40,46 +36,6 @@ function handleShortcut(event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//这里参考 ChatGPT 的代码
|
|
||||||
const uid = () => window.crypto.getRandomValues(new Uint32Array(1))[0];
|
|
||||||
|
|
||||||
function transformCallback(callback = () => {}, once = false) {
|
|
||||||
const identifier = uid();
|
|
||||||
const prop = `_${identifier}`;
|
|
||||||
Object.defineProperty(window, prop, {
|
|
||||||
value: (result) => {
|
|
||||||
if (once) {
|
|
||||||
Reflect.deleteProperty(window, prop);
|
|
||||||
}
|
|
||||||
return callback(result);
|
|
||||||
},
|
|
||||||
writable: false,
|
|
||||||
configurable: true,
|
|
||||||
});
|
|
||||||
return identifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function invoke(cmd, args) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
if (!window.__TAURI_POST_MESSAGE__)
|
|
||||||
reject('__TAURI_POST_MESSAGE__ does not exist~');
|
|
||||||
const callback = transformCallback((e) => {
|
|
||||||
resolve(e);
|
|
||||||
Reflect.deleteProperty(window, `_${error}`);
|
|
||||||
}, true);
|
|
||||||
const error = transformCallback((e) => {
|
|
||||||
reject(e);
|
|
||||||
Reflect.deleteProperty(window, `_${callback}`);
|
|
||||||
}, true);
|
|
||||||
window.__TAURI_POST_MESSAGE__({
|
|
||||||
cmd,
|
|
||||||
callback,
|
|
||||||
error,
|
|
||||||
...args,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Judgment of file download.
|
// Judgment of file download.
|
||||||
function isDownloadLink(url) {
|
function isDownloadLink(url) {
|
||||||
const fileExtensions = [
|
const fileExtensions = [
|
||||||
@@ -105,6 +61,7 @@ function externalTargetLink() {
|
|||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
const tauri = window.__TAURI__;
|
const tauri = window.__TAURI__;
|
||||||
const appWindow = tauri.window.appWindow;
|
const appWindow = tauri.window.appWindow;
|
||||||
|
const invoke = tauri.tauri.invoke;
|
||||||
|
|
||||||
const topDom = document.createElement('div');
|
const topDom = document.createElement('div');
|
||||||
topDom.id = 'pack-top-dom';
|
topDom.id = 'pack-top-dom';
|
||||||
@@ -217,26 +174,7 @@ function setDefaultZoom() {
|
|||||||
|
|
||||||
function getFilenameFromUrl(url) {
|
function getFilenameFromUrl(url) {
|
||||||
const urlPath = new URL(url).pathname;
|
const urlPath = new URL(url).pathname;
|
||||||
const filename = urlPath.substring(urlPath.lastIndexOf('/') + 1);
|
return urlPath.substring(urlPath.lastIndexOf('/') + 1);
|
||||||
return filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeUrlParameters(url) {
|
|
||||||
const parsedUrl = new URL(url);
|
|
||||||
parsedUrl.search = '';
|
|
||||||
return parsedUrl.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Toggle video playback when the window is hidden.
|
|
||||||
function toggleVideoPlayback(pause) {
|
|
||||||
const videos = document.getElementsByTagName('video');
|
|
||||||
for (const video of videos) {
|
|
||||||
if (pause) {
|
|
||||||
video.pause();
|
|
||||||
} else {
|
|
||||||
video.play();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect blob urls to blob by overriding window.URL.createObjectURL
|
// Collect blob urls to blob by overriding window.URL.createObjectURL
|
||||||
@@ -262,33 +200,49 @@ function convertBlobUrlToBinary(blobUrl) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function downloadFromBlobUrl(blobUrl, filename) {
|
async function downloadFromBlobUrl(blobUrl, filename) {
|
||||||
const tauri = window.__TAURI__;
|
try {
|
||||||
convertBlobUrlToBinary(blobUrl).then((binary) => {
|
const tauri = window.__TAURI__;
|
||||||
console.log('binary', binary);
|
const binary = await convertBlobUrlToBinary(blobUrl);
|
||||||
tauri.fs.writeBinaryFile(filename, binary, {
|
|
||||||
|
await tauri.fs.writeBinaryFile(filename, binary, {
|
||||||
dir: tauri.fs.BaseDirectory.Download,
|
dir: tauri.fs.BaseDirectory.Download,
|
||||||
}).then(() => {
|
|
||||||
window.pakeToast('Download successful, saved to download directory~');
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
const lang = getSystemLanguage();
|
||||||
|
window.pakeToast(lang === 'en' ? 'Download successful, saved to download directory~' : '下载成功,已保存到下载目录~');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error downloading from Blob URL:', error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// detect blob download by createElement("a")
|
// detect blob download by createElement("a")
|
||||||
function detectDownloadByCreateAnchor() {
|
function detectDownloadByCreateAnchor() {
|
||||||
const createEle = document.createElement;
|
const originalCreateElement = document.createElement;
|
||||||
document.createElement = (el) => {
|
|
||||||
if (el !== 'a') return createEle.call(document, el);
|
|
||||||
const anchorEle = createEle.call(document, el);
|
|
||||||
|
|
||||||
// use addEventListener to avoid overriding the original click event.
|
document.createElement = function(el, ...args) {
|
||||||
anchorEle.addEventListener('click', () => {
|
const element = originalCreateElement.call(this, el, ...args);
|
||||||
const url = anchorEle.href;
|
|
||||||
if (window.blobToUrlCaches.has(url)) {
|
|
||||||
downloadFromBlobUrl(url, anchorEle.download || getFilenameFromUrl(url));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return anchorEle;
|
if (el === 'a') {
|
||||||
|
element.addEventListener('click', (event) => {
|
||||||
|
const url = element.href;
|
||||||
|
if (window.blobToUrlCaches.has(url)) {
|
||||||
|
// Prevent default 'click' event if a blob URL is detected
|
||||||
|
event.preventDefault();
|
||||||
|
const filename = element.download || getFilenameFromUrl(url);
|
||||||
|
downloadFromBlobUrl(url, filename);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return element;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Determine the language of the current system.
|
||||||
|
function getSystemLanguage() {
|
||||||
|
const lang = navigator.language.substr(0, 2);
|
||||||
|
return lang === 'ch' ? 'ch' : 'en';
|
||||||
|
}
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ pub fn run_app() {
|
|||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
{
|
{
|
||||||
event.window().minimize().unwrap();
|
event.window().minimize().unwrap();
|
||||||
// event.window().eval("toggleVideoPlayback(true);").unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
|||||||
@@ -33,19 +33,43 @@ pub fn show_toast(window: &Window, message: &str) {
|
|||||||
window.eval(&script).unwrap();
|
window.eval(&script).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_download_message() -> String {
|
pub enum MessageType {
|
||||||
let default_message = "Download successful, saved to download directory~";
|
Start,
|
||||||
let chinese_message = "下载成功,已保存到下载目录~";
|
Success,
|
||||||
|
Failure,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_download_message(message_type: MessageType) -> String {
|
||||||
|
let default_start_message = "Start downloading~";
|
||||||
|
let chinese_start_message = "开始下载中~";
|
||||||
|
|
||||||
|
let default_success_message = "Download successful, saved to download directory~";
|
||||||
|
let chinese_success_message = "下载成功,已保存到下载目录~";
|
||||||
|
|
||||||
|
let default_failure_message = "Download failed, please check your network connection~";
|
||||||
|
let chinese_failure_message = "下载失败,请检查你的网络连接~";
|
||||||
|
|
||||||
env::var("LANG")
|
env::var("LANG")
|
||||||
.map(|lang| {
|
.map(|lang| {
|
||||||
if lang.starts_with("zh") {
|
if lang.starts_with("zh") {
|
||||||
chinese_message
|
match message_type {
|
||||||
|
MessageType::Start => chinese_start_message,
|
||||||
|
MessageType::Success => chinese_success_message,
|
||||||
|
MessageType::Failure => chinese_failure_message,
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
default_message
|
match message_type {
|
||||||
|
MessageType::Start => default_start_message,
|
||||||
|
MessageType::Success => default_success_message,
|
||||||
|
MessageType::Failure => default_failure_message,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.unwrap_or(default_message)
|
.unwrap_or_else(|_| match message_type {
|
||||||
|
MessageType::Start => default_start_message,
|
||||||
|
MessageType::Success => default_success_message,
|
||||||
|
MessageType::Failure => default_failure_message,
|
||||||
|
})
|
||||||
.to_string()
|
.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user