调整代码,添加mkdir和delete方法
This commit is contained in:
parent
847cab47f0
commit
bfde0982bd
2
main.py
2
main.py
@ -159,7 +159,7 @@ class InputLoggerApp(App):
|
|||||||
|
|
||||||
def on_mount(self) -> None:
|
def on_mount(self) -> None:
|
||||||
self.setup_logger()
|
self.setup_logger()
|
||||||
self.fs = pikpakFs.VirtFs("", "", "", loginCachePath = "token.json")
|
self.fs = pikpakFs.VirtFs(loginCachePath = "token.json", proxy = "http://127.0.0.1:7897")
|
||||||
|
|
||||||
async def handle_command(self, command) -> None:
|
async def handle_command(self, command) -> None:
|
||||||
try:
|
try:
|
||||||
|
295
pikpakFs.py
295
pikpakFs.py
@ -9,18 +9,22 @@ import os
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
class PathWalker():
|
class PathWalker():
|
||||||
def __init__(self, pathStr : str, subDir : str = None, sep : str = "/"):
|
def __init__(self, pathStr : str, sep : str = "/"):
|
||||||
self.pathSpots : list[str] = []
|
self.__pathSpots : list[str] = []
|
||||||
pathStr = pathStr.strip()
|
pathStr = pathStr.strip()
|
||||||
if not pathStr.startswith(sep):
|
if not pathStr.startswith(sep):
|
||||||
self.pathSpots.append(".")
|
self.__pathSpots.append(".")
|
||||||
pathSpots = [spot.strip() for spot in pathStr.split(sep) if spot.strip() != ""]
|
pathSpots = [spot.strip() for spot in pathStr.split(sep) if spot.strip() != ""]
|
||||||
self.pathSpots.extend(pathSpots)
|
self.__pathSpots.extend(pathSpots)
|
||||||
if subDir != None:
|
|
||||||
self.pathSpots.append(subDir)
|
|
||||||
|
|
||||||
def IsAbsolute(self) -> bool:
|
def IsAbsolute(self) -> bool:
|
||||||
return len(self.pathSpots) == 0 or self.pathSpots[0] != "."
|
return len(self.__pathSpots) == 0 or self.__pathSpots[0] != "."
|
||||||
|
|
||||||
|
def AppendSpot(self, spot):
|
||||||
|
self.__pathSpots.append(spot)
|
||||||
|
|
||||||
|
def Walk(self) -> list[str]:
|
||||||
|
return self.__pathSpots
|
||||||
|
|
||||||
class VirtFsNode:
|
class VirtFsNode:
|
||||||
def __init__(self, id : str, name : str, fatherId : str):
|
def __init__(self, id : str, name : str, fatherId : str):
|
||||||
@ -29,12 +33,11 @@ class VirtFsNode:
|
|||||||
self.fatherId = fatherId
|
self.fatherId = fatherId
|
||||||
|
|
||||||
class DirNode(VirtFsNode):
|
class DirNode(VirtFsNode):
|
||||||
def __init__(self, id : str, name : str, fatherId : str, childrenId : list[str]):
|
def __init__(self, id : str, name : str, fatherId : str):
|
||||||
super().__init__(id, name, fatherId)
|
super().__init__(id, name, fatherId)
|
||||||
self.childrenId = childrenId
|
self.childrenId : list[str] = []
|
||||||
self.lastUpdate : datetime = None
|
self.lastUpdate : datetime = None
|
||||||
|
|
||||||
|
|
||||||
class FileNode(VirtFsNode):
|
class FileNode(VirtFsNode):
|
||||||
def __init__(self, id : str, name : str, fatherId : str):
|
def __init__(self, id : str, name : str, fatherId : str):
|
||||||
super().__init__(id, name, fatherId)
|
super().__init__(id, name, fatherId)
|
||||||
@ -60,11 +63,38 @@ class VirtFs:
|
|||||||
def __CalcMd5(self, text : str):
|
def __CalcMd5(self, text : str):
|
||||||
return md5(text.encode()).hexdigest()
|
return md5(text.encode()).hexdigest()
|
||||||
|
|
||||||
def __init__(self, username : str, password : str, proxy : str = None, loginCachePath : str = None):
|
def __ToDir(self, node : VirtFsNode) -> DirNode:
|
||||||
|
if isinstance(node, DirNode):
|
||||||
|
return node
|
||||||
|
return None
|
||||||
|
|
||||||
|
def __ToFile(self, node : VirtFsNode) -> FileNode:
|
||||||
|
if isinstance(node, FileNode):
|
||||||
|
return node
|
||||||
|
return None
|
||||||
|
|
||||||
|
def __init__(self, loginCachePath : str = None, proxy : str = None):
|
||||||
|
self.nodes : Dict[str, VirtFsNode] = {}
|
||||||
|
self.root = DirNode(None, "", None)
|
||||||
|
self.currentLocation = self.root
|
||||||
|
|
||||||
|
self.loginCachePath = loginCachePath
|
||||||
|
self.proxyConfig = proxy
|
||||||
|
self.client : PikPakApi = None
|
||||||
|
self.__TryLoginFromCache()
|
||||||
|
|
||||||
|
def __InitClientByToken(self, token : PikpakToken):
|
||||||
|
self.__InitClientByUsernamePassword(token.username, token.password)
|
||||||
|
self.client.access_token = token.access_token
|
||||||
|
self.client.refresh_token = token.refresh_token
|
||||||
|
self.client.user_id = token.user_id
|
||||||
|
self.client.encode_token()
|
||||||
|
|
||||||
|
def __InitClientByUsernamePassword(self, username : str, password : str):
|
||||||
httpx_client_args = None
|
httpx_client_args = None
|
||||||
if proxy != None:
|
if self.proxyConfig != None:
|
||||||
httpx_client_args = {
|
httpx_client_args = {
|
||||||
"proxy": proxy,
|
"proxy": self.proxyConfig,
|
||||||
"transport": httpx.AsyncHTTPTransport(retries=1),
|
"transport": httpx.AsyncHTTPTransport(retries=1),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,13 +103,7 @@ class VirtFs:
|
|||||||
password = password,
|
password = password,
|
||||||
httpx_client_args=httpx_client_args)
|
httpx_client_args=httpx_client_args)
|
||||||
|
|
||||||
self.nodes : Dict[str, VirtFsNode] = {}
|
def __TryLoginFromCache(self):
|
||||||
self.loginCachePath = loginCachePath
|
|
||||||
self.root = DirNode(None, "", None, [])
|
|
||||||
self.currentLocation = self.root
|
|
||||||
self.__LoginFromCache()
|
|
||||||
|
|
||||||
def __LoginFromCache(self):
|
|
||||||
if self.loginCachePath == None:
|
if self.loginCachePath == None:
|
||||||
return
|
return
|
||||||
if not os.path.exists(self.loginCachePath):
|
if not os.path.exists(self.loginCachePath):
|
||||||
@ -87,13 +111,7 @@ class VirtFs:
|
|||||||
with open(self.loginCachePath, 'r', encoding='utf-8') as file:
|
with open(self.loginCachePath, 'r', encoding='utf-8') as file:
|
||||||
content = file.read()
|
content = file.read()
|
||||||
token = PikpakToken.from_json(content)
|
token = PikpakToken.from_json(content)
|
||||||
if self.client.username != token.username or self.client.password != token.password:
|
self.__InitClientByToken(token)
|
||||||
logging.error("failed to load login info from cache, not match")
|
|
||||||
return
|
|
||||||
self.client.access_token = token.access_token
|
|
||||||
self.client.refresh_token = token.refresh_token
|
|
||||||
self.client.user_id = token.user_id
|
|
||||||
self.client.encode_token()
|
|
||||||
logging.info("successfully load login info from cache")
|
logging.info("successfully load login info from cache")
|
||||||
|
|
||||||
def __DumpLoginInfo(self):
|
def __DumpLoginInfo(self):
|
||||||
@ -104,9 +122,16 @@ class VirtFs:
|
|||||||
file.write(token.to_json())
|
file.write(token.to_json())
|
||||||
logging.info("successfully dump login info to cache")
|
logging.info("successfully dump login info to cache")
|
||||||
|
|
||||||
async def __RefreshAccessToken(self):
|
def __IsAncestorsOf(self, nodeA : VirtFsNode, nodeB : VirtFsNode) -> bool:
|
||||||
result = await self.client.refresh_access_token()
|
if nodeB is nodeA:
|
||||||
return json.dumps(result, indent=4)
|
return False
|
||||||
|
if nodeA is self.root:
|
||||||
|
return True
|
||||||
|
while nodeB.fatherId != None:
|
||||||
|
nodeB = self.nodes[nodeB.fatherId]
|
||||||
|
if nodeB is nodeA:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
async def __RefreshDirectory(self, dirNode : DirNode):
|
async def __RefreshDirectory(self, dirNode : DirNode):
|
||||||
dirInfo = await self.client.file_list(parent_id = dirNode.id)
|
dirInfo = await self.client.file_list(parent_id = dirNode.id)
|
||||||
@ -117,57 +142,76 @@ class VirtFs:
|
|||||||
child : VirtFsNode = None
|
child : VirtFsNode = None
|
||||||
id = node["id"]
|
id = node["id"]
|
||||||
name = node["name"]
|
name = node["name"]
|
||||||
|
fatherId = dirNode.id
|
||||||
if id in self.nodes:
|
if id in self.nodes:
|
||||||
child = self.nodes[id]
|
child = self.nodes[id]
|
||||||
else:
|
else:
|
||||||
if node["kind"].endswith("folder"):
|
child = DirNode(id, name, fatherId) if node["kind"].endswith("folder") else FileNode(id, name, fatherId)
|
||||||
child = DirNode(id, name, dirNode.id, [])
|
|
||||||
else:
|
|
||||||
child = FileNode(id, name, dirNode.id)
|
|
||||||
self.nodes[id] = child
|
self.nodes[id] = child
|
||||||
|
|
||||||
child.name = name
|
child.name = name
|
||||||
|
child.fatherId = fatherId
|
||||||
dirNode.childrenId.append(id)
|
dirNode.childrenId.append(id)
|
||||||
|
|
||||||
dirNode.lastUpdate = datetime.now()
|
dirNode.lastUpdate = datetime.now()
|
||||||
|
|
||||||
async def __PathToNode(self, pathStr : str, subDir : str = None) -> VirtFsNode:
|
def __FindChildInDirByName(self, dir : DirNode, name : str):
|
||||||
pathWalker = PathWalker(pathStr, subDir)
|
if dir is self.root and name == "":
|
||||||
current : VirtFsNode = None
|
return self.root
|
||||||
if pathWalker.IsAbsolute():
|
for childId in dir.childrenId:
|
||||||
current = self.root
|
node = self.nodes[childId]
|
||||||
else:
|
if name == node.name:
|
||||||
current = self.currentLocation
|
return node
|
||||||
|
return None
|
||||||
|
|
||||||
for spot in pathWalker.pathSpots:
|
async def __PathToNode(self, pathStr : str) -> VirtFsNode:
|
||||||
|
father, sonName = await self.__PathToFatherNodeAndNodeName(pathStr)
|
||||||
|
fatherDir = self.__ToDir(father)
|
||||||
|
if fatherDir == None:
|
||||||
|
return None
|
||||||
|
return self.__FindChildInDirByName(father, sonName)
|
||||||
|
|
||||||
|
async def __PathToFatherNodeAndNodeName(self, pathStr : str) -> tuple[VirtFsNode, str]:
|
||||||
|
pathWalker = PathWalker(pathStr)
|
||||||
|
father : VirtFsNode = None
|
||||||
|
sonName : str = None
|
||||||
|
current = self.root if pathWalker.IsAbsolute() else self.currentLocation
|
||||||
|
|
||||||
|
for spot in pathWalker.Walk():
|
||||||
if current == None:
|
if current == None:
|
||||||
|
father = None
|
||||||
break
|
break
|
||||||
if spot == "..":
|
if spot == "..":
|
||||||
if current.fatherId == None:
|
current = self.root if current.fatherId == None else self.nodes[current.fatherId]
|
||||||
current = self.root
|
|
||||||
else:
|
|
||||||
current = self.nodes[current.fatherId]
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if not isinstance(current, DirNode):
|
father = current
|
||||||
return None
|
|
||||||
|
currentDir = self.__ToDir(current)
|
||||||
|
if currentDir == None:
|
||||||
|
current = None
|
||||||
|
continue
|
||||||
|
|
||||||
currentDir : DirNode = current
|
|
||||||
if currentDir.lastUpdate == None:
|
if currentDir.lastUpdate == None:
|
||||||
await self.__RefreshDirectory(currentDir)
|
await self.__RefreshDirectory(currentDir)
|
||||||
|
|
||||||
if spot == ".":
|
if spot == ".":
|
||||||
continue
|
continue
|
||||||
else:
|
|
||||||
current = None
|
|
||||||
for childId in currentDir.childrenId:
|
|
||||||
node = self.nodes[childId]
|
|
||||||
if spot == node.name:
|
|
||||||
current = node
|
|
||||||
break
|
|
||||||
|
|
||||||
return current
|
sonName = spot
|
||||||
|
current = self.__FindChildInDirByName(currentDir, spot)
|
||||||
|
|
||||||
|
if current != None:
|
||||||
|
currentDir = self.__ToDir(current)
|
||||||
|
if currentDir != None:
|
||||||
|
await self.__RefreshDirectory(currentDir)
|
||||||
|
father = self.root if current.fatherId == None else self.nodes[current.fatherId]
|
||||||
|
sonName = current.name
|
||||||
|
|
||||||
|
return father, sonName
|
||||||
|
|
||||||
|
async def __MakeDir(self, node : DirNode, name : str) -> DirNode:
|
||||||
|
await self.client.create_folder(name, node.id)
|
||||||
|
await self.__RefreshDirectory(node)
|
||||||
|
return self.__ToDir(self.__FindChildInDirByName(node, name))
|
||||||
|
|
||||||
async def __NodeToPath(self, node : VirtFsNode) -> str:
|
async def __NodeToPath(self, node : VirtFsNode) -> str:
|
||||||
spots : list[str] = [""]
|
spots : list[str] = [""]
|
||||||
@ -180,87 +224,110 @@ class VirtFs:
|
|||||||
spots.append("")
|
spots.append("")
|
||||||
return "/".join(reversed(spots))
|
return "/".join(reversed(spots))
|
||||||
|
|
||||||
async def login(self):
|
async def login(self, username : str = None, password : str = None) -> str:
|
||||||
result = await self.client.login()
|
if self.client != None and username == None and password == None:
|
||||||
|
username = self.client.username
|
||||||
|
password = self.client.password
|
||||||
|
|
||||||
|
if self.client != None and self.client.username == username and self.client.password == password:
|
||||||
|
logging.info("already login, try refresh token")
|
||||||
|
try:
|
||||||
|
await self.client.refresh_access_token()
|
||||||
|
self.__DumpLoginInfo()
|
||||||
|
return "success"
|
||||||
|
except Exception:
|
||||||
|
logging.info("Refresh access token failed! Try relogin")
|
||||||
|
|
||||||
|
self.__InitClientByUsernamePassword(username, password)
|
||||||
|
await self.client.login()
|
||||||
self.__DumpLoginInfo()
|
self.__DumpLoginInfo()
|
||||||
logging.debug(json.dumps(result, indent=4))
|
return "success"
|
||||||
return "Login Success"
|
|
||||||
|
|
||||||
async def ls(self, pathStr : str = "") -> str:
|
async def ls(self, pathStr : str = "") -> str:
|
||||||
node = await self.__PathToNode(pathStr)
|
dirNode = self.__ToDir(await self.__PathToNode(pathStr))
|
||||||
if node == None:
|
if dirNode == None:
|
||||||
return f"path not found: {pathStr}"
|
return f"path not found or is file: {pathStr}"
|
||||||
if not isinstance(node, DirNode):
|
result = []
|
||||||
return f"path is not directory"
|
|
||||||
dirNode : DirNode = node
|
|
||||||
result = ["==== ls ===="]
|
|
||||||
for childId in dirNode.childrenId:
|
for childId in dirNode.childrenId:
|
||||||
node = self.nodes[childId]
|
node = self.nodes[childId]
|
||||||
result.append(node.name)
|
result.append(node.name)
|
||||||
return "\n".join(result)
|
return "\n".join(result)
|
||||||
|
|
||||||
async def cd(self, pathStr : str = "") -> str:
|
async def cd(self, pathStr : str = "") -> str:
|
||||||
node = await self.__PathToNode(pathStr)
|
dirNode = self.__ToDir(await self.__PathToNode(pathStr))
|
||||||
if node == None:
|
if dirNode == None:
|
||||||
return f"path not found: {pathStr}"
|
return f"path not found or is file: {pathStr}"
|
||||||
if not isinstance(node, DirNode):
|
|
||||||
return f"path is not directory"
|
|
||||||
dirNode : DirNode = node
|
|
||||||
self.currentLocation = dirNode
|
self.currentLocation = dirNode
|
||||||
return ""
|
return "success"
|
||||||
|
|
||||||
async def cwd(self) -> str:
|
async def cwd(self) -> str:
|
||||||
path = await self.__NodeToPath(self.currentLocation)
|
path = await self.__NodeToPath(self.currentLocation)
|
||||||
if path == None:
|
return path if path != None else "cwd failed"
|
||||||
return f"cwd failed"
|
|
||||||
return path
|
|
||||||
|
|
||||||
async def geturl(self, pathStr : str) -> str:
|
async def geturl(self, pathStr : str) -> str:
|
||||||
node = await self.__PathToNode(pathStr)
|
fileNode = self.__ToFile(await self.__PathToNode(pathStr))
|
||||||
if node == None:
|
if fileNode == None:
|
||||||
return f"path not found: {pathStr}"
|
return f"path not found or is not file: {pathStr}"
|
||||||
if not isinstance(node, FileNode):
|
|
||||||
return f"path is not file"
|
result = await self.client.get_download_url(fileNode.id)
|
||||||
result = await self.client.get_download_url(node.id)
|
|
||||||
logging.debug(json.dumps(result, indent=4))
|
|
||||||
return result["web_content_link"]
|
return result["web_content_link"]
|
||||||
|
|
||||||
async def offdown(self, url : str, pathStr : str = "") -> str :
|
async def mkdir(self, pathStr : str) -> str:
|
||||||
node = await self.__PathToNode(pathStr)
|
father, target = await self.__PathToFatherNodeAndNodeName(pathStr)
|
||||||
if node == None:
|
fatherDir = self.__ToDir(father)
|
||||||
return f"path not found: {pathStr}"
|
if fatherDir == None:
|
||||||
elif not isinstance(node, DirNode):
|
return "Failed to locate"
|
||||||
return f"path is not directory"
|
if self.__FindChildInDirByName(fatherDir, target) != None:
|
||||||
|
return f"Path {pathStr} already existed"
|
||||||
|
await self.__MakeDir(fatherDir, target)
|
||||||
|
return "success"
|
||||||
|
|
||||||
|
async def download(self, url : str, pathStr : str = "") -> str :
|
||||||
|
# todo: 完善离线下载task相关
|
||||||
|
dirNode = self.__ToDir(await self.__PathToNode(pathStr))
|
||||||
|
if dirNode == None:
|
||||||
|
return f"path not found or is file: {pathStr}"
|
||||||
|
|
||||||
subFolderName = self.__CalcMd5(url)
|
subFolderName = self.__CalcMd5(url)
|
||||||
subNode = await self.__PathToNode(pathStr, subFolderName)
|
newDirNode = await self.__MakeDir(dirNode, subFolderName)
|
||||||
if subNode == None:
|
if newDirNode == None:
|
||||||
result = await self.client.create_folder(subFolderName, node.id)
|
return f"falied to create sub folder {subFolderName}"
|
||||||
logging.debug(json.dumps(result, indent=4))
|
|
||||||
await self.__RefreshDirectory(node)
|
|
||||||
subNode = await self.__PathToNode(pathStr, subFolderName)
|
|
||||||
elif not isinstance(subNode, DirNode):
|
|
||||||
return f"path is not directory"
|
|
||||||
|
|
||||||
if subNode == None:
|
|
||||||
return f"path not found: {pathStr}"
|
|
||||||
elif not isinstance(subNode, DirNode):
|
|
||||||
return f"path is not directory"
|
|
||||||
|
|
||||||
result = await self.client.offline_download(url, subNode.id)
|
|
||||||
logging.debug(json.dumps(result, indent=4))
|
|
||||||
|
|
||||||
|
await self.client.offline_download(url, newDirNode.id)
|
||||||
return subFolderName
|
return subFolderName
|
||||||
|
|
||||||
|
async def update(self, pathStr : str = ""):
|
||||||
|
dirNode = self.__ToDir(await self.__PathToNode(pathStr))
|
||||||
|
if dirNode == None:
|
||||||
|
return f"path not found or is file: {pathStr}"
|
||||||
|
await self.__RefreshDirectory(dirNode)
|
||||||
|
return "success"
|
||||||
|
|
||||||
|
async def delete(self, pathStr : str):
|
||||||
|
father, name = await self.__PathToFatherNodeAndNodeName(pathStr)
|
||||||
|
fatherDir = self.__ToDir(father)
|
||||||
|
if fatherDir == None:
|
||||||
|
return "Failed to locate"
|
||||||
|
node = self.__FindChildInDirByName(fatherDir, name)
|
||||||
|
if node == None:
|
||||||
|
return f"path {pathStr} not existed"
|
||||||
|
|
||||||
|
if self.currentLocation is node or self.__IsAncestorsOf(node, self.currentLocation):
|
||||||
|
return f"delete self or ancestor is not allowed"
|
||||||
|
await self.client.delete_to_trash([node.id])
|
||||||
|
await self.__RefreshDirectory(fatherDir)
|
||||||
|
return "success"
|
||||||
|
|
||||||
async def HandlerCommand(self, command):
|
async def HandlerCommand(self, command):
|
||||||
result = re.findall(r'"(.*?)"|(\S+)', command)
|
result = re.findall(r'"(.*?)"|(\S+)', command)
|
||||||
filtered_result = [item for sublist in result for item in sublist if item]
|
filtered_result = [item for sublist in result for item in sublist if item]
|
||||||
|
|
||||||
command = filtered_result[0]
|
cmd = filtered_result[0]
|
||||||
args = filtered_result[1:]
|
args = filtered_result[1:]
|
||||||
|
|
||||||
method = getattr(self, command)
|
method = getattr(self, cmd)
|
||||||
if method == None:
|
if method == None:
|
||||||
return f"Unknown command: {command}"
|
return f"Unknown command: {cmd}"
|
||||||
return await method(*args)
|
output = await method(*args)
|
||||||
|
logging.info(f"{command} : {repr(output)}")
|
||||||
|
return output
|
Loading…
x
Reference in New Issue
Block a user