refactor: switch to own quirks_path

This commit is contained in:
master 2024-03-05 09:59:00 +08:00
parent b996be0702
commit 8c460dfdc0
12 changed files with 66 additions and 177 deletions

8
Cargo.lock generated
View File

@ -3584,6 +3584,7 @@ dependencies = [
"maplit", "maplit",
"opendal", "opendal",
"qbit-rs", "qbit-rs",
"quirks_path",
"regex", "regex",
"reqwest", "reqwest",
"rss", "rss",
@ -3600,7 +3601,6 @@ dependencies = [
"tokio", "tokio",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
"uni-path",
"url", "url",
"uuid", "uuid",
"validator", "validator",
@ -5592,12 +5592,6 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
[[package]]
name = "uni-path"
version = "1.51.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25e328d505b1f855c20e7358711b7ec6398524181664f016dd15cfb36c3a6275"
[[package]] [[package]]
name = "unic-char-property" name = "unic-char-property"
version = "0.9.0" version = "0.9.0"

View File

@ -10,6 +10,7 @@ use std::{
collections::TryReserveError, collections::TryReserveError,
error::Error, error::Error,
fmt, fmt,
fmt::Formatter,
hash::{Hash, Hasher}, hash::{Hash, Hasher},
io, io,
iter::FusedIterator, iter::FusedIterator,
@ -20,7 +21,7 @@ use std::{
}; };
use ::url::Url; use ::url::Url;
pub use url::PathToUrlError; pub use url::{path_equals_as_file_url, PathToUrlError};
use windows::is_windows_sep; use windows::is_windows_sep;
use crate::{ use crate::{
@ -707,7 +708,7 @@ impl PathBuf {
self self
} }
fn push<P: AsRef<Path>>(&mut self, path: P) { pub fn push<P: AsRef<Path>>(&mut self, path: P) {
self._push(path.as_ref()) self._push(path.as_ref())
} }
@ -991,6 +992,12 @@ impl fmt::Debug for PathBuf {
} }
} }
impl fmt::Display for PathBuf {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&**self, formatter)
}
}
impl Deref for PathBuf { impl Deref for PathBuf {
type Target = Path; type Target = Path;
#[inline] #[inline]
@ -1364,6 +1371,12 @@ impl fmt::Debug for Path {
} }
} }
impl fmt::Display for Path {
fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.inner, formatter)
}
}
impl PartialEq for Path { impl PartialEq for Path {
#[inline] #[inline]
fn eq(&self, other: &Path) -> bool { fn eq(&self, other: &Path) -> bool {

View File

@ -95,3 +95,13 @@ pub(crate) fn path_to_file_url_segments(
Ok((host_end, host_internal)) Ok((host_end, host_internal))
} }
pub fn path_equals_as_file_url<A: AsRef<Path>, B: AsRef<Path>>(
a: A,
b: B,
) -> Result<bool, PathToUrlError> {
let u1 = a.as_ref().to_file_url()?;
let u2 = b.as_ref().to_file_url()?;
Ok(u1.as_str() == u2.as_str())
}

View File

@ -53,12 +53,12 @@ fancy-regex = "0.13.0"
regex = "1.10.3" regex = "1.10.3"
lazy_static = "1.4.0" lazy_static = "1.4.0"
maplit = "1.0.2" maplit = "1.0.2"
uni-path = "1.51.1"
tl = { version = "0.7.8", features = ["simd"] } tl = { version = "0.7.8", features = ["simd"] }
lightningcss = "1.0.0-alpha.54" lightningcss = "1.0.0-alpha.54"
html-escape = "0.2.13" html-escape = "0.2.13"
opendal = "0.45.0" opendal = "0.45.0"
librqbit-core = "3.5.0" librqbit-core = "3.5.0"
quirks_path = { path = "../quirks_path" }
[dev-dependencies] [dev-dependencies]
serial_test = "2.0.0" serial_test = "2.0.0"

View File

@ -1,7 +1,6 @@
use std::path::Path; use std::path::Path;
use async_trait::async_trait; use async_trait::async_trait;
use axum::Router;
use loco_rs::{ use loco_rs::{
app::{AppContext, Hooks}, app::{AppContext, Hooks},
boot::{create_app, BootResult, StartMode}, boot::{create_app, BootResult, StartMode},

View File

@ -1,13 +1,11 @@
use bytes::Bytes; use bytes::Bytes;
use opendal::{layers::LoggingLayer, services, Operator}; use opendal::{layers::LoggingLayer, services, Operator};
use quirks_path::{Path, PathBuf};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use url::Url; use url::Url;
use uuid::Uuid; use uuid::Uuid;
use crate::{ use crate::config::AppDalConf;
config::AppDalConf,
path::{VFSSubPath, VFSSubPathBuf},
};
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
@ -50,8 +48,8 @@ impl AppDalContext {
let basename = format!("{}{}", Uuid::new_v4(), extname); let basename = format!("{}{}", Uuid::new_v4(), extname);
let mut dirname = [subscriber_pid, content_category.as_ref()] let mut dirname = [subscriber_pid, content_category.as_ref()]
.into_iter() .into_iter()
.map(VFSSubPath::new) .map(Path::new)
.collect::<VFSSubPathBuf>(); .collect::<PathBuf>();
let mut fs_builder = services::Fs::default(); let mut fs_builder = services::Fs::default();
fs_builder.root(self.config.fs_root.as_str()); fs_builder.root(self.config.fs_root.as_str());

View File

@ -13,6 +13,7 @@ use qbit_rs::{
model::{AddTorrentArg, Credential, GetTorrentListArg, NonEmptyStr, SyncData}, model::{AddTorrentArg, Credential, GetTorrentListArg, NonEmptyStr, SyncData},
Qbit, Qbit,
}; };
use quirks_path::{path_equals_as_file_url, Path, PathBuf};
use tokio::time::sleep; use tokio::time::sleep;
use url::Url; use url::Url;
@ -24,7 +25,6 @@ use super::{
use crate::{ use crate::{
downloaders::defs::{QbitTorrent, QbitTorrentContent, TorrentContent}, downloaders::defs::{QbitTorrent, QbitTorrentContent, TorrentContent},
models::{entities::downloaders, prelude::DownloaderCategory}, models::{entities::downloaders, prelude::DownloaderCategory},
path::{path_str_equals, VFSPathBuf, VFSSubPath},
}; };
pub struct SyncDataCache { pub struct SyncDataCache {
@ -35,7 +35,7 @@ pub struct QBittorrentDownloader {
pub subscriber_id: i32, pub subscriber_id: i32,
pub endpoint_url: Url, pub endpoint_url: Url,
pub client: Arc<Qbit>, pub client: Arc<Qbit>,
pub save_path: String, pub save_path: PathBuf,
pub wait_sync_timeout: Duration, pub wait_sync_timeout: Duration,
} }
@ -65,7 +65,7 @@ impl QBittorrentDownloader {
client: Arc::new(client), client: Arc::new(client),
endpoint_url, endpoint_url,
subscriber_id: model.subscriber_id, subscriber_id: model.subscriber_id,
save_path: model.save_path, save_path: model.save_path.into(),
wait_sync_timeout: Duration::from_millis(10000), wait_sync_timeout: Duration::from_millis(10000),
}) })
} }
@ -268,12 +268,15 @@ impl TorrentDownloader for QBittorrentDownloader {
new_path: &str, new_path: &str,
) -> eyre::Result<()> { ) -> eyre::Result<()> {
self.client.rename_file(hash, old_path, new_path).await?; self.client.rename_file(hash, old_path, new_path).await?;
let new_path = self.save_path.join(new_path);
let save_path = self.save_path.as_path();
self.wait_torrent_contents_until( self.wait_torrent_contents_until(
hash, hash,
|contents| -> bool { |contents| -> bool {
contents contents.iter().any(|c| {
.iter() path_equals_as_file_url(save_path.join(c.get_name()), &new_path)
.any(|c| path_str_equals(c.get_name(), new_path).unwrap_or(false)) .unwrap_or(false)
})
}, },
None, None,
) )
@ -291,9 +294,9 @@ impl TorrentDownloader for QBittorrentDownloader {
.build(), .build(),
|torrents| -> bool { |torrents| -> bool {
torrents.iter().all(|t| { torrents.iter().all(|t| {
t.save_path t.save_path.as_ref().map_or(false, |p| {
.as_ref() path_equals_as_file_url(p, new_path).unwrap_or(false)
.map_or(false, |p| path_str_equals(p, new_path).unwrap_or(false)) })
}) })
}, },
None, None,
@ -396,8 +399,8 @@ impl TorrentDownloader for QBittorrentDownloader {
Ok(()) Ok(())
} }
fn get_save_path(&self, sub_path: &VFSSubPath) -> VFSPathBuf { fn get_save_path(&self, sub_path: &Path) -> PathBuf {
VFSPathBuf::new(self.save_path.clone(), sub_path.to_path_buf()) self.save_path.join(sub_path)
} }
} }
@ -463,7 +466,7 @@ pub mod tests {
} }
async fn test_qbittorrent_downloader_impl() { async fn test_qbittorrent_downloader_impl() {
let base_save_path = VFSSubPath::new(get_tmp_qbit_test_folder()); let base_save_path = Path::new(get_tmp_qbit_test_folder());
let downloader = QBittorrentDownloader::from_downloader_model(downloaders::Model { let downloader = QBittorrentDownloader::from_downloader_model(downloaders::Model {
created_at: Default::default(), created_at: Default::default(),

View File

@ -1,4 +1,5 @@
use downloaders::DownloaderCategory; use downloaders::DownloaderCategory;
use quirks_path::{Path, PathBuf};
use sea_orm::{ActiveModelTrait, ActiveValue, DatabaseConnection, IntoActiveModel}; use sea_orm::{ActiveModelTrait, ActiveValue, DatabaseConnection, IntoActiveModel};
use url::Url; use url::Url;
@ -8,7 +9,7 @@ use super::{
}; };
use crate::{ use crate::{
models::{bangumi, downloaders, downloads}, models::{bangumi, downloaders, downloads},
path::{torrent_path::gen_bangumi_sub_path, VFSPathBuf, VFSSubPath}, path::torrent_path::gen_bangumi_sub_path,
}; };
#[async_trait::async_trait] #[async_trait::async_trait]
@ -48,7 +49,7 @@ pub trait TorrentDownloader {
async fn add_category(&self, category: &str) -> eyre::Result<()>; async fn add_category(&self, category: &str) -> eyre::Result<()>;
fn get_save_path(&self, sub_path: &VFSSubPath) -> VFSPathBuf; fn get_save_path(&self, sub_path: &Path) -> PathBuf;
async fn add_downloads_for_bangumi<'a, 'b>( async fn add_downloads_for_bangumi<'a, 'b>(
&self, &self,

View File

@ -1,10 +1,11 @@
use quirks_path::Path;
use super::defs::{ use super::defs::{
BRACKETS_REG, DIGIT_1PLUS_REG, SEASON_REGEX, SUBTITLE_LANG, TORRENT_PRASE_RULE_REGS, BRACKETS_REG, DIGIT_1PLUS_REG, SEASON_REGEX, SUBTITLE_LANG, TORRENT_PRASE_RULE_REGS,
}; };
use crate::path::VFSPath;
pub fn get_path_basename<'a>(path: &'a VFSPath) -> &'a str { pub fn get_path_basename(path: &Path) -> &str {
path.basename() path.parent().map_or("", |s| s.as_str())
} }
pub fn get_fansub(group_and_title: &str) -> (Option<&str>, &str) { pub fn get_fansub(group_and_title: &str) -> (Option<&str>, &str) {
@ -53,8 +54,8 @@ pub fn get_subtitle_lang(subtitle_name: &str) -> Option<&'static str> {
None None
} }
pub fn parse_torrent<'a>( pub fn parse_torrent(
torrent_path: &'a VFSPath<'a>, torrent_path: &Path,
torrent_name: Option<&str>, torrent_name: Option<&str>,
season: Option<i32>, season: Option<i32>,
file_type: Option<&str>, file_type: Option<&str>,

View File

@ -1,7 +1 @@
pub mod torrent_path; pub mod torrent_path;
pub mod vfs_path;
pub use vfs_path::{
path_str_equals, path_str_to_file_url, VFSComponent, VFSComponents, VFSPath, VFSPathBuf,
VFSSubPath, VFSSubPathBuf,
};

View File

@ -1,17 +1,18 @@
use std::collections::HashSet; use std::collections::HashSet;
use quirks_path::{Path, PathBuf};
use crate::{ use crate::{
downloaders::defs::Torrent, downloaders::defs::Torrent,
models::{bangumi, subscribers}, models::{bangumi, subscribers},
parsers::defs::SEASON_REGEX, parsers::defs::SEASON_REGEX,
path::{VFSPath, VFSSubPathBuf},
}; };
pub fn check_files(info: &Torrent) -> (Vec<VFSSubPathBuf>, Vec<VFSSubPathBuf>) { pub fn check_files(info: &Torrent) -> (Vec<PathBuf>, Vec<PathBuf>) {
let mut media_list = vec![]; let mut media_list = vec![];
let mut subtitle_list = vec![]; let mut subtitle_list = vec![];
for f in info.iter_files() { for f in info.iter_files() {
let file_name = VFSSubPathBuf::from(f.get_name()); let file_name = PathBuf::from(f.get_name());
let extension = file_name.extension().unwrap_or_default().to_lowercase(); let extension = file_name.extension().unwrap_or_default().to_lowercase();
match extension.as_str() { match extension.as_str() {
@ -27,8 +28,8 @@ pub fn check_files(info: &Torrent) -> (Vec<VFSSubPathBuf>, Vec<VFSSubPathBuf>) {
} }
pub fn path_to_bangumi<'a>( pub fn path_to_bangumi<'a>(
save_path: VFSPath<'a>, save_path: &'a Path,
downloader_path: VFSPath<'a>, downloader_path: &'a Path,
) -> Option<(&'a str, i32)> { ) -> Option<(&'a str, i32)> {
let downloader_parts = downloader_path let downloader_parts = downloader_path
.components() .components()
@ -57,16 +58,16 @@ pub fn path_to_bangumi<'a>(
} }
} }
pub fn file_depth(path: &VFSPath<'_>) -> usize { pub fn file_depth(path: &Path) -> usize {
path.components().count() path.components().count()
} }
pub fn is_ep(path: &VFSPath<'_>) -> bool { pub fn is_ep(path: &Path) -> bool {
file_depth(path) <= 2 file_depth(path) <= 2
} }
pub fn gen_bangumi_sub_path(data: &bangumi::Model) -> VFSSubPathBuf { pub fn gen_bangumi_sub_path(data: &bangumi::Model) -> PathBuf {
VFSSubPathBuf::from(data.official_title.to_string()).join(format!("Season {}", data.season)) PathBuf::from(data.official_title.to_string()).join(format!("Season {}", data.season))
} }
pub fn rule_name(bgm: &bangumi::Model, conf: &subscribers::SubscriberBangumiConfig) -> String { pub fn rule_name(bgm: &bangumi::Model, conf: &subscribers::SubscriberBangumiConfig) -> String {

View File

@ -1,125 +0,0 @@
use std::{borrow::Cow, collections::VecDeque, path::PathBuf};
use lazy_static::lazy_static;
pub use uni_path::{Path as VFSSubPath, PathBuf as VFSSubPathBuf};
use url::Url;
use crate::parsers::errors::ParseError;
pub fn path_str_to_file_url(path: &str) -> eyre::Result<Url> {
Url::parse(&format!("file:///{path}")).map_err(|e| e.into())
}
pub fn path_str_equals(p1: &str, p2: &str) -> eyre::Result<bool> {
let p1 = path_str_to_file_url(p1)?;
let p2 = path_str_to_file_url(p2)?;
Ok(p1.as_str() == p2.as_str())
}
const VFS_EMPTY_STR: &str = "";
lazy_static! {
pub static ref VFS_SUB_ROOT_BUF: VFSSubPathBuf = VFSSubPathBuf::from("/");
pub static ref VFS_SUB_ROOT: &'static VFSSubPath = &VFS_SUB_ROOT_BUF.as_path();
}
pub type VFSComponents<'a> = uni_path::Components<'a>;
pub type VFSComponent<'a> = uni_path::Component<'a>;
#[derive(Debug, Clone)]
pub struct VFSPath<'a> {
pub root: &'a str,
pub sub: &'a VFSSubPath,
}
impl<'a> VFSPath<'a> {
pub fn new(root: &'a str, sub: &'a VFSSubPath) -> VFSPath<'a> {
Self { root, sub }
}
pub fn file_name(&self) -> Option<&str> {
self.sub.file_name()
}
pub fn parent(&self) -> Option<VFSPath> {
self.sub.parent().map(|p| Self::new(self.root, p))
}
pub fn dirname(&'a self) -> VFSPath<'a> {
self.parent()
.unwrap_or_else(|| Self::new(self.root, &VFS_SUB_ROOT))
}
pub fn basename(&self) -> &str {
self.file_name().unwrap_or(VFS_EMPTY_STR)
}
pub fn components(&self) -> VFSComponents<'a> {
self.sub.components()
}
pub fn join<P: AsRef<VFSSubPath>>(&self, path: P) -> VFSPathBuf {
VFSPathBuf::new(self.root, self.sub.join(path))
}
pub fn extension(&self) -> Option<&str> {
self.sub.extension()
}
pub fn extname(&self) -> &str {
self.extension().unwrap_or_default()
}
pub fn to_std_path_buf(&self) -> PathBuf {
PathBuf::from(self.root).join(self.sub.as_str())
}
}
#[derive(Clone, Debug)]
pub struct VFSPathBuf {
root: String,
sub: VFSSubPathBuf,
}
impl VFSPathBuf {
pub fn new<R: Into<String>, S: Into<VFSSubPathBuf>>(root: R, sub: S) -> Self {
Self {
root: root.into(),
sub: sub.into(),
}
}
pub fn from_root(root: &str) -> Result<Self, ParseError> {
Ok(Self {
root: root.to_string(),
sub: VFS_SUB_ROOT_BUF.clone(),
})
}
pub fn as_path(&self) -> VFSPath {
VFSPath::new(&self.root as &str, self.sub.as_path())
}
pub fn push<P: AsRef<VFSSubPath>>(&mut self, path: P) {
self.sub.push(path);
}
pub fn pop(&mut self) -> bool {
self.sub.pop()
}
pub fn set_extension<S: AsRef<str>>(&mut self, ext: S) {
self.sub.set_extension(ext);
}
pub fn set_file_name<S: AsRef<str>>(&mut self, file_name: S) {
self.sub.set_file_name(file_name);
}
}
impl Into<PathBuf> for VFSPathBuf {
fn into(self) -> PathBuf {
let root = self.root;
PathBuf::from(root).join(self.sub.as_str())
}
}