11import abc
22import asyncio
3- from collections import deque
3+ from collections import defaultdict , deque
44from dataclasses import dataclass , field
55import hashlib
66import inspect
@@ -109,17 +109,19 @@ def __rtruediv__(self, __o: Any) -> 'FilePath':
109109
110110class iStorage (metaclass = abc .ABCMeta ):
111111 type : str = "_interface"
112- def __init__ (self , path : str , weight : int = 0 , list_concurrent : int = 32 ):
112+ def __init__ (
113+ self ,
114+ path : str ,
115+ weight : int = 0 ,
116+ list_concurrent : int = 32 ,
117+ name : Optional [str ] = None
118+ ):
113119 self .path = FilePath (path )
114120 self .weight = weight
115121 self .list_concurrent = list_concurrent
116122 self .current_weight = 0
123+ self ._name = name
117124
118- @staticmethod
119- @abc .abstractmethod
120- def from_config (config : dict [str , Any ]):
121- raise NotImplementedError ("from_config not implemented" )
122-
123125 @property
124126 @abc .abstractmethod
125127 def unique_id (self ):
@@ -165,16 +167,13 @@ def get_path(self, file: CollectionFile):
165167class LocalStorage (iStorage ):
166168 type = "local"
167169
168- def __init__ (self , path : str , weight : int = 0 , list_concurrent : int = 32 ):
169- super ().__init__ (path , weight , list_concurrent )
170+ def __init__ (self , path : str , weight : int = 0 , list_concurrent : int = 32 , name : Optional [ str ] = None ):
171+ super ().__init__ (path , weight , list_concurrent , name )
170172 self .async_executor = ThreadPoolExecutor (max_workers = list_concurrent )
171173
172174 @staticmethod
173175 def from_config (config : dict [str , Any ]):
174- path = config ["path" ]
175- weight = config .get ("weight" , 0 )
176- list_concurrent = config .get ("list_concurrent" , 32 )
177- return LocalStorage (path , weight , list_concurrent )
176+ return LocalStorage (config ["path" ], config .get ("weight" , 0 ), config .get ("list_concurrent" , 32 ), config .get ("name" ))
178177
179178 @property
180179 def unique_id (self ):
@@ -239,13 +238,22 @@ async def get_mtime(self, file: CollectionFile) -> float:
239238 return os .path .getmtime (Path (str (path )))
240239
241240class iNetworkStorage (iStorage ):
242-
243- def __init__ (self , path : str , username : str , password : str , endpoint : str , weight : int = 0 , list_concurrent : int = 32 ):
244- super ().__init__ (path , weight , list_concurrent )
241+ def __init__ (
242+ self ,
243+ path : str ,
244+ username : str ,
245+ password : str ,
246+ endpoint : str ,
247+ weight : int = 0 ,
248+ list_concurrent : int = 32 ,
249+ name : Optional [str ] = None ,
250+ cache_timeout : float = 60 ,
251+ ):
252+ super ().__init__ (path , weight , list_concurrent , name )
245253 self .username = username
246254 self .password = password
247255 self .endpoint = endpoint .rstrip ("/" )
248- self .cache = cache .TimeoutCache ()
256+ self .cache = cache .TimeoutCache (cache_timeout )
249257
250258 @property
251259 def unique_id (self ):
@@ -280,8 +288,20 @@ def __init__(self, result: AlistResult):
280288class AlistStorage (iNetworkStorage ):
281289 type = "alist"
282290
283- def __init__ (self , path : str , username : str , password : str , endpoint : str , weight : int = 0 , list_concurrent : int = 32 , retries : int = 3 , public_webdav_endpoint : str = "" ):
284- super ().__init__ (path , username , password , endpoint , weight , list_concurrent )
291+ def __init__ (
292+ self ,
293+ path : str ,
294+ username : str ,
295+ password : str ,
296+ endpoint : str ,
297+ weight : int = 0 ,
298+ list_concurrent : int = 32 ,
299+ name : Optional [str ] = None ,
300+ cache_timeout : float = 60 ,
301+ retries : int = 3 ,
302+ public_webdav_endpoint : str = ""
303+ ):
304+ super ().__init__ (path , username , password , endpoint , weight , list_concurrent , name , cache_timeout )
285305 self .retries = retries
286306 self .last_token : Optional [AlistToken ] = None
287307 self .session = aiohttp .ClientSession (
@@ -293,18 +313,6 @@ def __init__(self, path: str, username: str, password: str, endpoint: str, weigh
293313 urlobject = urlparse .urlparse (public_webdav_endpoint )
294314 self ._public_webdav_endpoint = f"{ urlobject .scheme } ://{ urlparse .quote (self .username )} :{ urlparse .quote (self .password )} @{ urlobject .netloc } { urlobject .path } "
295315
296- @staticmethod
297- def from_config (config : dict [str , Any ]):
298- path = config ["path" ]
299- username = config ["username" ]
300- password = config ["password" ]
301- endpoint = config ["endpoint" ]
302- weight = config .get ("weight" , 0 )
303- list_concurrent = config .get ("list_concurrent" , 32 )
304- retries = config .get ("retries" , 3 )
305- public_webdav_endpoint = config .get ("public_webdav_endpoint" , "" )
306- return AlistStorage (path , username , password , endpoint , weight , list_concurrent , retries , public_webdav_endpoint )
307-
308316 async def _get_token (self ):
309317 if self .last_token is None or self .last_token .expires < time .monotonic ():
310318 async with self .session .post (f"{ self .endpoint } /api/auth/login" , json = {"username" : self .username , "password" : self .password }) as resp :
@@ -516,8 +524,20 @@ def data_size(self) -> int:
516524
517525class WebDavStorage (iNetworkStorage ):
518526 type = "webdav"
519- def __init__ (self , path : str , username : str , password : str , endpoint : str , weight : int = 0 , list_concurrent : int = 32 , public_endpoint : str = "" , retries : int = 3 ):
520- super ().__init__ (path , username , password , endpoint , weight , list_concurrent )
527+ def __init__ (
528+ self ,
529+ path : str ,
530+ username : str ,
531+ password : str ,
532+ endpoint : str ,
533+ weight : int = 0 ,
534+ list_concurrent : int = 32 ,
535+ name : Optional [str ] = None ,
536+ cache_timeout : int = 60 ,
537+ public_endpoint : str = "" ,
538+ retries : int = 3
539+ ):
540+ super ().__init__ (path , username , password , endpoint , weight , list_concurrent , name , cache_timeout )
521541 self .client = webdav3_client .Client ({
522542 "webdav_hostname" : endpoint ,
523543 "webdav_login" : username ,
@@ -536,18 +556,6 @@ def __init__(self, path: str, username: str, password: str, endpoint: str, weigh
536556 self .base_url = f"{ urlobject .scheme } ://{ urlparse .quote (self .username )} :{ urlparse .quote (self .password )} @{ urlobject .hostname } :{ urlobject .port } { urlobject .path } "
537557 self .retries = retries
538558
539- @staticmethod
540- def from_config (config : dict [str , Any ]):
541- path = config ["path" ]
542- username = config ["username" ]
543- password = config ["password" ]
544- endpoint = config ["endpoint" ]
545- weight = config .get ("weight" , 0 )
546- list_concurrent = config .get ("list_concurrent" , 32 )
547- public_endpoint = config .get ("public_endpoint" , "" )
548- retries = config .get ("retries" , 3 )
549- return WebDavStorage (path , username , password , endpoint , weight , list_concurrent , public_endpoint , retries )
550-
551559 async def list_files (self , pbar : WrapperTQDM ) -> set [File ]:
552560 async def get_files (root_id : int ) -> list [WebDavFileInfo ]:
553561 root = self .path / DOWNLOAD_DIR / f"{ root_id :02x} "
@@ -670,16 +678,32 @@ async def write_file(self, file: MeasureFile | File, content: io.BytesIO):
670678 return True
671679
672680
681+ @dataclass
682+ class Parameter :
683+ name : str
684+ type : type
685+ default : Any = inspect ._empty
686+
673687def init_storage (config : Any ) -> Optional [iStorage ]:
674688 if not isinstance (config , dict ) or "type" not in config or config ["type" ] not in abstract_storages :
675689 return None
676690 try :
677- return abstract_storages [config ["type" ]].from_config (config )
691+ abstract_storage = abstract_storages [config ["type" ]]
692+ args = abstract_storage_args [abstract_storage ]
693+ params = {}
694+ for arg in args :
695+ if arg .name in config :
696+ params [arg .name ] = config [arg .name ]
697+ elif arg .default != inspect ._empty :
698+ params [arg .name ] = arg .default
699+
700+ return abstract_storage (** params )
678701 except :
679702 logger .traceback ()
680703 return None
681704
682705abstract_storages : dict [str , type [iStorage ]] = {}
706+ abstract_storage_args : defaultdict [type [iStorage ], list [Parameter ]] = defaultdict (list )
683707
684708T = TypeVar ("T" )
685709
@@ -695,6 +719,24 @@ async def init():
695719 if istorage .type == iStorage .type :
696720 continue
697721 abstract_storages [istorage .type ] = istorage
722+ arg = inspect .getfullargspec (istorage .__init__ )
723+ args = arg .args [1 :]
724+ # defaults 默认的长度和位置都是从后往前数的,
725+ # 填充一些空的在前面
726+ defaults = [
727+ inspect ._empty for _ in range (len (args ) - len (arg .defaults or []))
728+ ]
729+ defaults .extend (arg .defaults or [])
730+ for idx , arg_name in enumerate (args ):
731+ if arg_name == "self" :
732+ continue
733+ abstract_storage_args [istorage ].append (
734+ Parameter (
735+ name = arg_name ,
736+ type = arg .annotations .get (arg_name , Any ),
737+ default = defaults [idx ]
738+ )
739+ )
698740
699741 logger .debug ("Storage init complete" )
700742 logger .debug (f"Found { len (abstract_storages )} storage types: { ', ' .join (abstract_storages .keys ())} " )
0 commit comments