mirror of
https://github.com/patarapolw/cjdic2.git
synced 2026-11-09 17:39:35 +00:00
fix downloader, no bundle
This commit is contained in:
1 parent
5380742513
commit
047054f8f7
4 files changed
+129
-191
No files matched your search
@@ -192,7 +192,10 @@ impl AppService {
|
|||||||
{
|
{
|
||||||
let conn = Connection::open(self.db.dir.join(YOMITAN_DBFILE))?;
|
let conn = Connection::open(self.db.dir.join(YOMITAN_DBFILE))?;
|
||||||
for z in to_be_removed_dicts.clone().iter() {
|
for z in to_be_removed_dicts.clone().iter() {
|
||||||
conn.execute("DELETE FROM dictionaries WHERE bundle_name = ?1", [z])?;
|
conn.execute(
|
||||||
|
"DELETE FROM dictionaries WHERE bundle_name = ?1 AND lang = ?2",
|
||||||
|
[z, lang],
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,11 +3,14 @@ import { useEffect, useRef, useState } from "react";
|
|||||||
import { Dialog, Portal } from "@chakra-ui/react";
|
import { Dialog, Portal } from "@chakra-ui/react";
|
||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import { listen } from "@tauri-apps/api/event";
|
import { listen } from "@tauri-apps/api/event";
|
||||||
import { BaseDirectory, exists } from "@tauri-apps/plugin-fs";
|
|
||||||
import { fetch } from "@tauri-apps/plugin-http";
|
|
||||||
|
|
||||||
import { supabase } from "../lib/supabaseClient";
|
import { supabase } from "../lib/supabaseClient";
|
||||||
|
|
||||||
|
interface YomitanDictEntry {
|
||||||
|
url: string;
|
||||||
|
filepath: string;
|
||||||
|
}
|
||||||
|
|
||||||
interface LoadYomitanZipDirResult {
|
interface LoadYomitanZipDirResult {
|
||||||
new_dicts: string[];
|
new_dicts: string[];
|
||||||
to_be_removed_dicts: string[];
|
to_be_removed_dicts: string[];
|
||||||
@@ -44,99 +47,55 @@ function LoadingDialog() {
|
|||||||
|
|
||||||
downloadAssets();
|
downloadAssets();
|
||||||
async function downloadAssets(lang = "ja") {
|
async function downloadAssets(lang = "ja") {
|
||||||
const new_dicts = new Set<string>();
|
const dicts: YomitanDictEntry[] = [];
|
||||||
// const to_be_removed_dicts = new Set<string>();
|
|
||||||
|
|
||||||
if (lang === "ja") {
|
if (lang === "ja") {
|
||||||
await getPixiv().catch((e) => {
|
{
|
||||||
console.error(e);
|
|
||||||
});
|
|
||||||
async function getPixiv() {
|
|
||||||
const filename = "Pixiv.zip";
|
const filename = "Pixiv.zip";
|
||||||
new_dicts.add(filename);
|
|
||||||
|
|
||||||
const filepath = `yomitan/${lang}/${filename}`;
|
const filepath = `yomitan/${lang}/${filename}`;
|
||||||
if (
|
|
||||||
!(await exists(filepath, {
|
|
||||||
baseDir: BaseDirectory.AppData,
|
|
||||||
}))
|
|
||||||
) {
|
|
||||||
const publicUrl = await ghLatestReleaseURL(
|
|
||||||
"MarvNC/pixiv-yomitan",
|
|
||||||
/^Pixiv_.+\.zip$/,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (publicUrl) {
|
dicts.push({
|
||||||
await invoke("download_url", { url: publicUrl, filepath });
|
filepath,
|
||||||
}
|
url:
|
||||||
|
(await ghLatestReleaseURL(
|
||||||
|
"MarvNC/pixiv-yomitan",
|
||||||
|
/^Pixiv_.+\.zip$/,
|
||||||
|
).catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
return "";
|
||||||
|
})) || "",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const { data, error } = await supabase.storage
|
||||||
|
.from("yomitan")
|
||||||
|
.list(lang);
|
||||||
|
if (error) throw error;
|
||||||
|
|
||||||
|
for (const d of data || []) {
|
||||||
|
const filepath = `yomitan/${lang}/${d.name}`;
|
||||||
|
|
||||||
|
dicts.push({
|
||||||
|
filepath,
|
||||||
|
url: supabase.storage
|
||||||
|
.from("yomitan")
|
||||||
|
.getPublicUrl(`${lang}/${d.name}`).data.publicUrl,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let isSupabase = true;
|
await invoke("init_yomitan", { dicts, lang });
|
||||||
await getSupabase().catch((e) => {
|
|
||||||
console.error(e);
|
|
||||||
isSupabase = false;
|
|
||||||
});
|
|
||||||
async function getSupabase() {
|
|
||||||
const { data, error } = await supabase.storage
|
|
||||||
.from("yomitan")
|
|
||||||
.list(lang);
|
|
||||||
if (error) throw error;
|
|
||||||
|
|
||||||
for (const d of data || []) {
|
|
||||||
new_dicts.add(d.name);
|
|
||||||
const filepath = `yomitan/${lang}/${d.name}`;
|
|
||||||
if (
|
|
||||||
!(await exists(filepath, {
|
|
||||||
baseDir: BaseDirectory.AppData,
|
|
||||||
}))
|
|
||||||
) {
|
|
||||||
const {
|
|
||||||
data: { publicUrl },
|
|
||||||
} = supabase.storage
|
|
||||||
.from("yomitan")
|
|
||||||
.getPublicUrl(`${lang}/${d.name}`);
|
|
||||||
|
|
||||||
await invoke("download_url", { url: publicUrl, filepath });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isSupabase) {
|
|
||||||
// console.log([...new_dicts], [...to_be_removed_dicts]);
|
|
||||||
// for (const f of await readDir(`yomitan/${lang}`, {
|
|
||||||
// baseDir: BaseDirectory.AppData,
|
|
||||||
// })) {
|
|
||||||
// if (f.isFile && f.name.endsWith(".zip")) {
|
|
||||||
// if (new_dicts.has(f.name)) {
|
|
||||||
// new_dicts.delete(f.name);
|
|
||||||
// } else {
|
|
||||||
// to_be_removed_dicts.add(f.name);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// for (const bundleName of Array.from(new_dicts).sort()) {
|
|
||||||
// updateProgress({
|
|
||||||
// message: `Importing ${bundleName}`,
|
|
||||||
// });
|
|
||||||
// await invoke("import_yomitan_dict", {
|
|
||||||
// bundleName,
|
|
||||||
// lang,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// for (const bundleName of Array.from(to_be_removed_dicts).sort()) {
|
|
||||||
// updateProgress({
|
|
||||||
// message: `Removing old ${bundleName}`,
|
|
||||||
// });
|
|
||||||
// await invoke("remove_yomitan_dict", {
|
|
||||||
// bundleName,
|
|
||||||
// lang,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
setMessages([]);
|
setMessages([]);
|
||||||
|
|
||||||
|
// empty query warmup
|
||||||
|
await invoke("search_yomitan", {
|
||||||
|
qTerm: "",
|
||||||
|
qReading: "",
|
||||||
|
limit: 1,
|
||||||
|
offset: 0,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
listen<LoadYomitanZipDirResult>("load-yomitan-dir", ({ payload }) => {
|
listen<LoadYomitanZipDirResult>("load-yomitan-dir", ({ payload }) => {
|
||||||
@@ -144,7 +103,7 @@ function LoadingDialog() {
|
|||||||
if (payload.new_dicts.length > 0) {
|
if (payload.new_dicts.length > 0) {
|
||||||
messages = [
|
messages = [
|
||||||
...messages,
|
...messages,
|
||||||
{ message: "Importing Yomitan zip from the app bundle:" },
|
{ message: "Importing Yomitan zip:" },
|
||||||
...payload.new_dicts.map((d) => ({ message: `- ${d}` })),
|
...payload.new_dicts.map((d) => ({ message: `- ${d}` })),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,19 +45,6 @@ function SearchPage() {
|
|||||||
|
|
||||||
const lang = "ja-JP";
|
const lang = "ja-JP";
|
||||||
|
|
||||||
const isInit = useRef(false);
|
|
||||||
useEffect(() => {
|
|
||||||
if (isInit.current) return;
|
|
||||||
isInit.current = true;
|
|
||||||
|
|
||||||
invoke("search_yomitan", {
|
|
||||||
qTerm: "",
|
|
||||||
qReading: "",
|
|
||||||
limit: 1,
|
|
||||||
offset: 0,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
trySearch();
|
trySearch();
|
||||||
}, [q]);
|
}, [q]);
|
||||||
|
|||||||
+80
-91
@@ -1,95 +1,69 @@
|
|||||||
use std::{borrow::Cow, fs::File, io::Write, path::PathBuf};
|
use std::{fs::File, io::Write, path::PathBuf};
|
||||||
|
|
||||||
use cjdic2_core::{
|
use cjdic2_core::{AppService, CJDicError, SqlParam, YomitanRow, YomitanZipImportResult};
|
||||||
AppService, CJDicError, SqlParam, YomitanRow, YomitanZipImportResult, ZipSource,
|
|
||||||
};
|
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use serde::Serialize;
|
use serde::{Deserialize, Serialize};
|
||||||
use tauri::{AppHandle, Emitter, Manager, Runtime, path::BaseDirectory};
|
use tauri::{AppHandle, Emitter, Manager};
|
||||||
use tauri_plugin_fs::FsExt;
|
|
||||||
|
|
||||||
pub struct BundledZip<R: Runtime> {
|
#[derive(Deserialize)]
|
||||||
pub name: String,
|
pub struct YomitanDictEntry {
|
||||||
pub path: PathBuf, // resolved asset://localhost/... path on Android
|
url: String,
|
||||||
pub app: AppHandle<R>,
|
filepath: String,
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: Runtime> BundledZip<R> {
|
|
||||||
pub fn new(app: AppHandle<R>, relative_path: &str) -> Result<Self, tauri::Error> {
|
|
||||||
let name = relative_path
|
|
||||||
.split('/')
|
|
||||||
.last()
|
|
||||||
.unwrap_or(relative_path)
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
let path = app.path().resolve(relative_path, BaseDirectory::Resource)?;
|
|
||||||
|
|
||||||
Ok(Self { name, path, app })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: Runtime> ZipSource for BundledZip<R> {
|
|
||||||
fn file_name(&self) -> &str {
|
|
||||||
&self.name
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bytes(&self) -> std::io::Result<Cow<'_, [u8]>> {
|
|
||||||
self.app
|
|
||||||
.fs()
|
|
||||||
.read(&self.path) // PathBuf implements Into<FilePath>
|
|
||||||
.map(Cow::Owned)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn init_yomitan(
|
pub async fn init_yomitan(
|
||||||
app: AppHandle,
|
app: AppHandle,
|
||||||
|
dicts: Vec<YomitanDictEntry>,
|
||||||
|
lang: String,
|
||||||
state: tauri::State<'_, AppService>,
|
state: tauri::State<'_, AppService>,
|
||||||
) -> Result<(), CJDicError> {
|
) -> Result<(), CJDicError> {
|
||||||
let mut new_dicts: Vec<String> = vec![];
|
let mut new_dicts: Vec<String> = vec![];
|
||||||
let mut to_be_removed_dicts: Vec<String> = vec![];
|
let mut to_be_removed_dicts: Vec<String> = vec![];
|
||||||
|
|
||||||
for lang in vec!["ja"] {
|
let app_dir = app
|
||||||
let folder_name = format!("resources/yomitan/{lang}");
|
.path()
|
||||||
let folder_name = folder_name.as_str();
|
.app_data_dir()
|
||||||
|
.map_err(|e| CJDicError::Error(e.to_string()))?;
|
||||||
|
|
||||||
let zip_dir = app
|
for d in dicts.iter().clone() {
|
||||||
.path()
|
if !app_dir.join(&d.filepath).exists() {
|
||||||
.resolve(folder_name, tauri::path::BaseDirectory::Resource)
|
download_url_local(
|
||||||
|
d.url.clone(),
|
||||||
|
app_dir.clone(),
|
||||||
|
d.filepath.clone(),
|
||||||
|
|progress| {
|
||||||
|
app.emit("download-url-progress", progress).unwrap();
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
.map_err(|e| CJDicError::Error(e.to_string()))?;
|
.map_err(|e| CJDicError::Error(e.to_string()))?;
|
||||||
|
|
||||||
// Manifest is needed, as Android bundled fs can't read_dir
|
|
||||||
let files: Vec<String> =
|
|
||||||
serde_json::from_str(&app.fs().read_to_string(zip_dir.join("manifest.json"))?)?;
|
|
||||||
|
|
||||||
let zip_files = files
|
|
||||||
.iter()
|
|
||||||
.map(|f| BundledZip::new(app.clone(), &format!("{folder_name}/{f}")))
|
|
||||||
.collect::<Result<Vec<_>, _>>()
|
|
||||||
.map_err(|e| CJDicError::Error(e.to_string()))?;
|
|
||||||
|
|
||||||
if zip_files.is_empty() {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut r = state.load_yomitan_zip_dir(
|
|
||||||
zip_files,
|
|
||||||
lang,
|
|
||||||
|r| {
|
|
||||||
app.emit("load-yomitan-dir", r).unwrap();
|
|
||||||
},
|
|
||||||
|r| {
|
|
||||||
app.emit("yomitan-import-progress", r).unwrap();
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
|
|
||||||
new_dicts.append(&mut r.new_dicts);
|
|
||||||
to_be_removed_dicts.append(&mut r.to_be_removed_dicts);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let zip_files: Vec<PathBuf> = dicts
|
||||||
|
.into_iter()
|
||||||
|
.map(|d| app_dir.join(&d.filepath))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut r = state.load_yomitan_zip_dir(
|
||||||
|
zip_files,
|
||||||
|
lang.as_str(),
|
||||||
|
|r| {
|
||||||
|
app.emit("load-yomitan-dir", r).unwrap();
|
||||||
|
},
|
||||||
|
|r| {
|
||||||
|
app.emit("yomitan-import-progress", r).unwrap();
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
new_dicts.append(&mut r.new_dicts);
|
||||||
|
to_be_removed_dicts.append(&mut r.to_be_removed_dicts);
|
||||||
|
|
||||||
if new_dicts.len() + to_be_removed_dicts.len() > 0 {
|
if new_dicts.len() + to_be_removed_dicts.len() > 0 {
|
||||||
// Cleanup doesn't appear to help even after app restart
|
println!("Imported {:?}", new_dicts);
|
||||||
// state.cleanup_yomitan_writer()?;
|
println!("Removed {:?}", to_be_removed_dicts);
|
||||||
|
println!("Request app restart?",);
|
||||||
|
|
||||||
// Request app restart anyway
|
// Request app restart anyway
|
||||||
app.request_restart();
|
app.request_restart();
|
||||||
@@ -157,17 +131,16 @@ struct DownloadProgress {
|
|||||||
downloaded: u64,
|
downloaded: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
async fn download_url_local<Callback>(
|
||||||
pub async fn download_url(
|
|
||||||
app: AppHandle,
|
|
||||||
url: String,
|
url: String,
|
||||||
|
data_dir: PathBuf,
|
||||||
filepath: String,
|
filepath: String,
|
||||||
) -> Result<bool, CJDicError> {
|
callback: Callback,
|
||||||
|
) -> Result<bool, CJDicError>
|
||||||
|
where
|
||||||
|
Callback: Fn(DownloadProgress),
|
||||||
|
{
|
||||||
let client = Client::new();
|
let client = Client::new();
|
||||||
let data_dir = app
|
|
||||||
.path()
|
|
||||||
.app_data_dir()
|
|
||||||
.map_err(|e| CJDicError::Error(e.to_string()))?;
|
|
||||||
let file_pf = data_dir.join(filepath.as_str());
|
let file_pf = data_dir.join(filepath.as_str());
|
||||||
|
|
||||||
// Making the asynchronous GET request
|
// Making the asynchronous GET request
|
||||||
@@ -192,16 +165,12 @@ pub async fn download_url(
|
|||||||
file.write_all(&chunk)?;
|
file.write_all(&chunk)?;
|
||||||
downloaded += chunk.len() as u64;
|
downloaded += chunk.len() as u64;
|
||||||
|
|
||||||
app.emit(
|
callback(DownloadProgress {
|
||||||
"download-url-progress",
|
url: url.clone(),
|
||||||
DownloadProgress {
|
filepath: filepath.clone(),
|
||||||
url: url.clone(),
|
content_length,
|
||||||
filepath: filepath.clone(),
|
downloaded,
|
||||||
content_length,
|
});
|
||||||
downloaded,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
println!("File downloaded successfully to {:?}", &file_pf);
|
println!("File downloaded successfully to {:?}", &file_pf);
|
||||||
} else {
|
} else {
|
||||||
@@ -210,3 +179,23 @@ pub async fn download_url(
|
|||||||
|
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn download_url(
|
||||||
|
app: AppHandle,
|
||||||
|
url: String,
|
||||||
|
filepath: String,
|
||||||
|
) -> Result<bool, CJDicError> {
|
||||||
|
download_url_local(
|
||||||
|
url,
|
||||||
|
app.path()
|
||||||
|
.app_data_dir()
|
||||||
|
.map_err(|e| CJDicError::Error(e.to_string()))?,
|
||||||
|
filepath,
|
||||||
|
|progress| {
|
||||||
|
app.emit("download-url-progress", progress).unwrap();
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|e| CJDicError::Error(e.to_string()))
|
||||||
|
}
|
||||||
Reference in new issue
Block a user