refactor: switch to own quirks_path
This commit is contained in:
parent
b996be0702
commit
8c460dfdc0
8
Cargo.lock
generated
8
Cargo.lock
generated
@ -3584,6 +3584,7 @@ dependencies = [
|
||||
"maplit",
|
||||
"opendal",
|
||||
"qbit-rs",
|
||||
"quirks_path",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"rss",
|
||||
@ -3600,7 +3601,6 @@ dependencies = [
|
||||
"tokio",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"uni-path",
|
||||
"url",
|
||||
"uuid",
|
||||
"validator",
|
||||
@ -5592,12 +5592,6 @@ version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
|
||||
|
||||
[[package]]
|
||||
name = "uni-path"
|
||||
version = "1.51.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25e328d505b1f855c20e7358711b7ec6398524181664f016dd15cfb36c3a6275"
|
||||
|
||||
[[package]]
|
||||
name = "unic-char-property"
|
||||
version = "0.9.0"
|
||||
|
@ -10,6 +10,7 @@ use std::{
|
||||
collections::TryReserveError,
|
||||
error::Error,
|
||||
fmt,
|
||||
fmt::Formatter,
|
||||
hash::{Hash, Hasher},
|
||||
io,
|
||||
iter::FusedIterator,
|
||||
@ -20,7 +21,7 @@ use std::{
|
||||
};
|
||||
|
||||
use ::url::Url;
|
||||
pub use url::PathToUrlError;
|
||||
pub use url::{path_equals_as_file_url, PathToUrlError};
|
||||
use windows::is_windows_sep;
|
||||
|
||||
use crate::{
|
||||
@ -707,7 +708,7 @@ impl PathBuf {
|
||||
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())
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
type Target = Path;
|
||||
#[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 {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Path) -> bool {
|
||||
|
@ -95,3 +95,13 @@ pub(crate) fn path_to_file_url_segments(
|
||||
|
||||
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())
|
||||
}
|
||||
|
@ -53,12 +53,12 @@ fancy-regex = "0.13.0"
|
||||
regex = "1.10.3"
|
||||
lazy_static = "1.4.0"
|
||||
maplit = "1.0.2"
|
||||
uni-path = "1.51.1"
|
||||
tl = { version = "0.7.8", features = ["simd"] }
|
||||
lightningcss = "1.0.0-alpha.54"
|
||||
html-escape = "0.2.13"
|
||||
opendal = "0.45.0"
|
||||
librqbit-core = "3.5.0"
|
||||
quirks_path = { path = "../quirks_path" }
|
||||
|
||||
[dev-dependencies]
|
||||
serial_test = "2.0.0"
|
||||
@ -66,4 +66,4 @@ rstest = "0.18.2"
|
||||
loco-rs = { version = "0.3.1", features = ["testing"] }
|
||||
insta = { version = "1.34.0", features = ["redactions", "yaml", "filters"] }
|
||||
testcontainers = { version = "0.15.0" }
|
||||
testcontainers-modules = { version = "0.3.5" }
|
||||
testcontainers-modules = { version = "0.3.5" }
|
||||
|
@ -1,7 +1,6 @@
|
||||
use std::path::Path;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use axum::Router;
|
||||
use loco_rs::{
|
||||
app::{AppContext, Hooks},
|
||||
boot::{create_app, BootResult, StartMode},
|
||||
|
@ -1,13 +1,11 @@
|
||||
use bytes::Bytes;
|
||||
use opendal::{layers::LoggingLayer, services, Operator};
|
||||
use quirks_path::{Path, PathBuf};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
config::AppDalConf,
|
||||
path::{VFSSubPath, VFSSubPathBuf},
|
||||
};
|
||||
use crate::config::AppDalConf;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
@ -50,8 +48,8 @@ impl AppDalContext {
|
||||
let basename = format!("{}{}", Uuid::new_v4(), extname);
|
||||
let mut dirname = [subscriber_pid, content_category.as_ref()]
|
||||
.into_iter()
|
||||
.map(VFSSubPath::new)
|
||||
.collect::<VFSSubPathBuf>();
|
||||
.map(Path::new)
|
||||
.collect::<PathBuf>();
|
||||
|
||||
let mut fs_builder = services::Fs::default();
|
||||
fs_builder.root(self.config.fs_root.as_str());
|
||||
|
@ -13,6 +13,7 @@ use qbit_rs::{
|
||||
model::{AddTorrentArg, Credential, GetTorrentListArg, NonEmptyStr, SyncData},
|
||||
Qbit,
|
||||
};
|
||||
use quirks_path::{path_equals_as_file_url, Path, PathBuf};
|
||||
use tokio::time::sleep;
|
||||
use url::Url;
|
||||
|
||||
@ -24,7 +25,6 @@ use super::{
|
||||
use crate::{
|
||||
downloaders::defs::{QbitTorrent, QbitTorrentContent, TorrentContent},
|
||||
models::{entities::downloaders, prelude::DownloaderCategory},
|
||||
path::{path_str_equals, VFSPathBuf, VFSSubPath},
|
||||
};
|
||||
|
||||
pub struct SyncDataCache {
|
||||
@ -35,7 +35,7 @@ pub struct QBittorrentDownloader {
|
||||
pub subscriber_id: i32,
|
||||
pub endpoint_url: Url,
|
||||
pub client: Arc<Qbit>,
|
||||
pub save_path: String,
|
||||
pub save_path: PathBuf,
|
||||
pub wait_sync_timeout: Duration,
|
||||
}
|
||||
|
||||
@ -65,7 +65,7 @@ impl QBittorrentDownloader {
|
||||
client: Arc::new(client),
|
||||
endpoint_url,
|
||||
subscriber_id: model.subscriber_id,
|
||||
save_path: model.save_path,
|
||||
save_path: model.save_path.into(),
|
||||
wait_sync_timeout: Duration::from_millis(10000),
|
||||
})
|
||||
}
|
||||
@ -268,12 +268,15 @@ impl TorrentDownloader for QBittorrentDownloader {
|
||||
new_path: &str,
|
||||
) -> eyre::Result<()> {
|
||||
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(
|
||||
hash,
|
||||
|contents| -> bool {
|
||||
contents
|
||||
.iter()
|
||||
.any(|c| path_str_equals(c.get_name(), new_path).unwrap_or(false))
|
||||
contents.iter().any(|c| {
|
||||
path_equals_as_file_url(save_path.join(c.get_name()), &new_path)
|
||||
.unwrap_or(false)
|
||||
})
|
||||
},
|
||||
None,
|
||||
)
|
||||
@ -291,9 +294,9 @@ impl TorrentDownloader for QBittorrentDownloader {
|
||||
.build(),
|
||||
|torrents| -> bool {
|
||||
torrents.iter().all(|t| {
|
||||
t.save_path
|
||||
.as_ref()
|
||||
.map_or(false, |p| path_str_equals(p, new_path).unwrap_or(false))
|
||||
t.save_path.as_ref().map_or(false, |p| {
|
||||
path_equals_as_file_url(p, new_path).unwrap_or(false)
|
||||
})
|
||||
})
|
||||
},
|
||||
None,
|
||||
@ -396,8 +399,8 @@ impl TorrentDownloader for QBittorrentDownloader {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_save_path(&self, sub_path: &VFSSubPath) -> VFSPathBuf {
|
||||
VFSPathBuf::new(self.save_path.clone(), sub_path.to_path_buf())
|
||||
fn get_save_path(&self, sub_path: &Path) -> PathBuf {
|
||||
self.save_path.join(sub_path)
|
||||
}
|
||||
}
|
||||
|
||||
@ -463,7 +466,7 @@ pub mod tests {
|
||||
}
|
||||
|
||||
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 {
|
||||
created_at: Default::default(),
|
||||
|
@ -1,4 +1,5 @@
|
||||
use downloaders::DownloaderCategory;
|
||||
use quirks_path::{Path, PathBuf};
|
||||
use sea_orm::{ActiveModelTrait, ActiveValue, DatabaseConnection, IntoActiveModel};
|
||||
use url::Url;
|
||||
|
||||
@ -8,7 +9,7 @@ use super::{
|
||||
};
|
||||
use crate::{
|
||||
models::{bangumi, downloaders, downloads},
|
||||
path::{torrent_path::gen_bangumi_sub_path, VFSPathBuf, VFSSubPath},
|
||||
path::torrent_path::gen_bangumi_sub_path,
|
||||
};
|
||||
|
||||
#[async_trait::async_trait]
|
||||
@ -48,7 +49,7 @@ pub trait TorrentDownloader {
|
||||
|
||||
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>(
|
||||
&self,
|
||||
|
@ -1,10 +1,11 @@
|
||||
use quirks_path::Path;
|
||||
|
||||
use super::defs::{
|
||||
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 {
|
||||
path.basename()
|
||||
pub fn get_path_basename(path: &Path) -> &str {
|
||||
path.parent().map_or("", |s| s.as_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
|
||||
}
|
||||
|
||||
pub fn parse_torrent<'a>(
|
||||
torrent_path: &'a VFSPath<'a>,
|
||||
pub fn parse_torrent(
|
||||
torrent_path: &Path,
|
||||
torrent_name: Option<&str>,
|
||||
season: Option<i32>,
|
||||
file_type: Option<&str>,
|
||||
|
@ -1,7 +1 @@
|
||||
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,
|
||||
};
|
||||
|
@ -1,17 +1,18 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use quirks_path::{Path, PathBuf};
|
||||
|
||||
use crate::{
|
||||
downloaders::defs::Torrent,
|
||||
models::{bangumi, subscribers},
|
||||
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 subtitle_list = vec![];
|
||||
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();
|
||||
|
||||
match extension.as_str() {
|
||||
@ -27,8 +28,8 @@ pub fn check_files(info: &Torrent) -> (Vec<VFSSubPathBuf>, Vec<VFSSubPathBuf>) {
|
||||
}
|
||||
|
||||
pub fn path_to_bangumi<'a>(
|
||||
save_path: VFSPath<'a>,
|
||||
downloader_path: VFSPath<'a>,
|
||||
save_path: &'a Path,
|
||||
downloader_path: &'a Path,
|
||||
) -> Option<(&'a str, i32)> {
|
||||
let downloader_parts = downloader_path
|
||||
.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()
|
||||
}
|
||||
|
||||
pub fn is_ep(path: &VFSPath<'_>) -> bool {
|
||||
pub fn is_ep(path: &Path) -> bool {
|
||||
file_depth(path) <= 2
|
||||
}
|
||||
|
||||
pub fn gen_bangumi_sub_path(data: &bangumi::Model) -> VFSSubPathBuf {
|
||||
VFSSubPathBuf::from(data.official_title.to_string()).join(format!("Season {}", data.season))
|
||||
pub fn gen_bangumi_sub_path(data: &bangumi::Model) -> PathBuf {
|
||||
PathBuf::from(data.official_title.to_string()).join(format!("Season {}", data.season))
|
||||
}
|
||||
|
||||
pub fn rule_name(bgm: &bangumi::Model, conf: &subscribers::SubscriberBangumiConfig) -> String {
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user