From 97a0588deb344756990bd5350314ccfe3484cc7a Mon Sep 17 00:00:00 2001 From: wxy1343 <1343890272@qq.com> Date: Sun, 3 Oct 2021 16:50:57 +0800 Subject: [PATCH] =?UTF-8?q?[+]=E5=88=86=E4=BA=AB=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E6=9F=A5=E7=9C=8B=20#46=20[+]=E5=88=86=E4=BA=AB=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E8=BD=AC=E5=AD=98=20#52?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 +++++ aliyunpan/about.py | 2 +- aliyunpan/api/core.py | 80 ++++++++++++++++++++++++++++++++++------- aliyunpan/api/models.py | 16 +++++---- aliyunpan/api/type.py | 13 ++++++- aliyunpan/cli/cli.py | 6 ++-- aliyunpan/cli/tui.py | 11 ++++-- main.py | 8 +++-- 8 files changed, 115 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 91701d7..d6adaf0 100644 --- a/README.md +++ b/README.md @@ -164,6 +164,14 @@ python main.py -h -a, --album 是否访问相册 + + -s, --share-id + 指定分享id + + + -sp, --share-pwd + 指定分享密码 + diff --git a/aliyunpan/about.py b/aliyunpan/about.py index c0072c6..f2df444 100644 --- a/aliyunpan/about.py +++ b/aliyunpan/about.py @@ -1 +1 @@ -__version__ = '2.7.15' +__version__ = '2.8.0' diff --git a/aliyunpan/api/core.py b/aliyunpan/api/core.py index c51ae06..bc152d6 100644 --- a/aliyunpan/api/core.py +++ b/aliyunpan/api/core.py @@ -4,13 +4,14 @@ from datetime import datetime from pathlib import Path from threading import RLock +from typing import List import requests import simplejson # from aliyunpan.api import ua from aliyunpan.api.req import * -from aliyunpan.api.type import UserInfo, AlibumInfo +from aliyunpan.api.type import UserInfo, AlibumInfo, Share from aliyunpan.api.utils import * from aliyunpan.common import * from aliyunpan.exceptions import InvalidRefreshToken, AliyunpanException, AliyunpanCode, LoginFailed, \ @@ -22,11 +23,12 @@ class AliyunPan(object): - def __init__(self, refresh_token: str = None, album: bool = False): + def __init__(self, refresh_token: str = None, album: bool = False, share: Share = Share()): self._req = Req(self) self._user_info = None self._alibum_info = None self._album = album + self._share = share self._access_token = None self._drive_id = None self._username = None @@ -46,6 +48,7 @@ def __init__(self, refresh_token: str = None, album: bool = False): drive_id = property(lambda self: (self._lock.acquire(), next(self._drive_id_gen_), self._lock.release())[1], lambda self, value: setattr(self, '_drive_id', value)) album = property(lambda self: self._album, lambda self, value: setattr(self, '_album', value)) + share = property(lambda self: self._share) def login(self, username: str = None, password: str = None, ua: str = None): """ @@ -104,10 +107,21 @@ def get_file_list(self, parent_file_id: str = 'root', next_marker: str = None, r :param next_marker: :return: """ - url = 'https://api.aliyundrive.com/v2/file/list' - json = {"drive_id": self.drive_id, "parent_file_id": parent_file_id, 'fields': '*', 'marker': next_marker} + json = {"parent_file_id": parent_file_id} + if next_marker: + json['marker'] = next_marker + headers = {} + kwargs = {} + if self._share.share_id: + url = 'https://api.aliyundrive.com/adrive/v3/file/list' + json.update({'share_id': self._share.share_id, 'share_pwd': self._share.share_pwd}) + headers = {'x-share-token': self.get_share_token()} + kwargs = {'access_token': None} + else: + url = 'https://api.aliyundrive.com/v2/file/list' + json.update({"drive_id": self.drive_id, 'fields': '*'}) logger.info(f'Get the list of parent_file_id {parent_file_id}.') - r = self._req.post(url, json=json) + r = self._req.post(url, json=json, headers=headers, **kwargs) try: logger.debug(r.json()) except simplejson.errors.JSONDecodeError: @@ -140,21 +154,43 @@ def delete_file(self, file_id: str): return r.json()['responses'][0]['id'] return False - def move_file(self, file_id: str, parent_file_id: str): + def batch(self, file_id_list: list, parent_file_id: str, force: bool = False) -> requests.models.Response: + """ + 移动文件 + """ + file_json_list = [] + auto_rename = False if force else True + headers = {} + for file_id in file_id_list: + body = {'file_id': file_id, 'to_parent_file_id': parent_file_id, 'auto_rename': auto_rename} + file_json = {'body': body, + 'headers': {'Content-Type': 'application/json'}, 'id': 0, 'method': 'POST', + 'url': '/file/move'} + file_json_list.append(file_json) + if self._share.share_id: + url = 'https://api.aliyundrive.com/adrive/v2/batch' + for i, file_json in enumerate(file_json_list): + file_json_list[i]['body']['to_drive_id'] = self.drive_id + file_json_list[i]['body']['share_id'] = self._share.share_id + file_json_list[i]['url'] = '/file/copy' + headers['x-share-token'] = self.get_share_token() + else: + url = 'https://api.aliyundrive.com/v2/batch' + for i, file_json in enumerate(file_json_list): + file_json_list[i]['id'] = file_json['body']['file_id'] + file_json_list[i]['body']['drive_id'] = self.drive_id + json = {'requests': file_json_list, 'resource': "file"} + return self._req.post(url, json=json, headers=headers) + + def move_file(self, file_id: List[str], parent_file_id: str): """ 移动文件 :param file_id: :param parent_file_id: :return: """ - url = 'https://api.aliyundrive.com/v2/batch' - json = {"requests": [{"body": {"drive_id": self.drive_id, "file_id": file_id, - "to_parent_file_id": parent_file_id}, - "headers": {"Content-Type": "application/json"}, - "id": file_id, "method": "POST", "url": "/file/move"}], - "resource": "file"} logger.info(f'Move files {file_id} to {parent_file_id}') - r = self._req.post(url, json=json) + r = self.batch([file_id], parent_file_id) if r.status_code == 200: if 'message' in r.json()['responses'][0]['body']: print(r.json()['responses'][0]['body']['message']) @@ -724,6 +760,24 @@ def share_link(self, file_id_list: list, expiration=''): return r.json()['share_url'] def get_share_by_anonymous(self, share_id: str): + """ + 获取分享文件列表 + """ url = f'https://api.aliyundrive.com/adrive/v3/share_link/get_share_by_anonymous' r = self._req.post(url, json={'share_id': share_id}) return r.json()['file_infos'] + + def get_share_token(self, share: Share = None): + """ + 获取share_token + """ + if not share: + share = self._share + if share.share_token: + return share.share_token + url = 'https://api.aliyundrive.com/v2/share_link/get_share_token' + json = {'share_id': share.share_id, 'share_pwd': share.share_pwd} + r = self._req.post(url, json=json) + share_token = r.json().get('share_token', '') + share.share_token = share_token + return share_token diff --git a/aliyunpan/api/models.py b/aliyunpan/api/models.py index f1c3da8..f093e48 100644 --- a/aliyunpan/api/models.py +++ b/aliyunpan/api/models.py @@ -101,12 +101,13 @@ def get_file_info(info): for info in info_list: if info['type'] == 'file': file_info = FileInfo(name=info['name'], id=info['file_id'], pid=info['parent_file_id'], type=True, - ctime=time.strptime(info['created_at'], '%Y-%m-%dT%H:%M:%S.%fZ'), + ctime=time.strptime(info['created_at'], + '%Y-%m-%dT%H:%M:%S.%fZ') if 'created_at' in info else time.localtime(), update_time=time.strptime(info['updated_at'], '%Y-%m-%dT%H:%M:%S.%fZ'), - hidden=info['hidden'], category=info['category'], - content_type=info['content_type'], - size=info['size'], content_hash_name=info['content_hash_name'], - content_hash=info['content_hash'], + hidden=info.get('hidden'), category=info['category'], + content_type=info.get('content_type'), + size=info['size'], content_hash_name=info.get('content_hash_name'), + content_hash=info.get('content_hash'), download_url=info['download_url'] if 'download_url' in info else '', video_media_metadata=info[ 'video_media_metadata'] if 'video_media_metadata' in info else None, @@ -114,9 +115,10 @@ def get_file_info(info): 'video_preview_metadata'] if 'video_preview_metadata' in info else None) else: file_info = FileInfo(name=info['name'], id=info['file_id'], pid=info['parent_file_id'], type=False, - ctime=time.strptime(info['created_at'], '%Y-%m-%dT%H:%M:%S.%fZ'), + ctime=time.strptime(info['created_at'], + '%Y-%m-%dT%H:%M:%S.%fZ') if 'created_at' in info else time.time(), update_time=time.strptime(info['updated_at'], '%Y-%m-%dT%H:%M:%S.%fZ'), - hidden=info['hidden']) + hidden=info.get('hidden')) file_info_list.append(file_info) return file_info_list diff --git a/aliyunpan/api/type.py b/aliyunpan/api/type.py index 4ff54e9..f9a70bb 100644 --- a/aliyunpan/api/type.py +++ b/aliyunpan/api/type.py @@ -1,6 +1,6 @@ from collections import namedtuple -__all__ = ['FileInfo', 'UserInfo', 'ShareInfo', 'AlibumInfo'] +__all__ = ['FileInfo', 'UserInfo', 'ShareInfo', 'AlibumInfo', 'Share'] _file_info = ( 'name', 'id', 'pid', 'type', 'ctime', 'update_time', 'hidden', 'category', 'content_type', 'size', @@ -15,3 +15,14 @@ ShareInfo.__new__.__defaults__ = ('',) * 6 AlibumInfo = namedtuple('AlibumInfo', ['drive_name', 'drive_id']) AlibumInfo.__new__.__defaults__ = ('',) * 2 + + +class Share: + share_id = '' + share_pwd = '' + share_token = '' + + def __init__(self, share_id='', share_pwd='', share_token=''): + self.share_id = share_id + self.share_pwd = share_pwd + self.share_token = share_token diff --git a/aliyunpan/cli/cli.py b/aliyunpan/cli/cli.py index 0cc0f3c..adf24cc 100644 --- a/aliyunpan/cli/cli.py +++ b/aliyunpan/cli/cli.py @@ -9,6 +9,7 @@ from aliyunpan.api.core import AliyunPan from aliyunpan.api.models import * from aliyunpan.api.req import * +from aliyunpan.api.type import Share from aliyunpan.api.utils import * from aliyunpan.cli.config import Config from aliyunpan.cli.tui import AliyunpanTUI @@ -23,7 +24,7 @@ class Commander: def __init__(self, init=True, *args, **kwargs): self._disk = AliyunPan() self._path_list = PathList(self._disk) - self._req = Req() + self._req = Req(self._disk) self._config = Config() self._task_config = Config(ROOT_DIR / Path('tasks.yaml')) self._share_link = 'aliyunpan://' @@ -46,11 +47,12 @@ def __del__(self): pass def init(self, config_file=None, refresh_token=None, username=None, password=None, depth=3, timeout=None, - drive_id=None, album=False): + drive_id=None, album=False, share_id='', share_pwd=''): self._path_list.depth = depth self._req.timeout = timeout self._disk.drive_id = drive_id self._disk.album = album + self._disk._share = Share(share_id, share_pwd) config_file_list = list( filter(lambda x: get_real_path(x).is_file(), map(lambda x: get_real_path(x), self._config_set))) if config_file: diff --git a/aliyunpan/cli/tui.py b/aliyunpan/cli/tui.py index 5f8a4e9..8883fe8 100644 --- a/aliyunpan/cli/tui.py +++ b/aliyunpan/cli/tui.py @@ -427,7 +427,11 @@ def update_file_list(self, name=None, file_id=None): # 保存当前目录信息 self._parent_file_info = None if file_id != 'root': - self._parent_file_info = self.parent.parentApp._cli._path_list._tree.get_node(file_id).data + file_node = self.parent.parentApp._cli._path_list._tree.get_node(file_id) + if file_node: + self._parent_file_info = file_node.data + else: + self._file_list = self.parent.parentApp._cli._path_list.get_fid_list('root', update=False) else: self._file_list = self.parent.parentApp._cli._path_list.get_fid_list(self._parent_file_info.id, update=False) @@ -447,7 +451,10 @@ def update_path(self): pid = self._parent_file_info.pid if self._parent_file_info else None while True: if pid: - file_info = self.parent.parentApp._cli._path_list._tree.get_node(pid).data + file_node = self.parent.parentApp._cli._path_list._tree.get_node(pid) + if not file_node: + file_node = self.parent.parentApp._cli._path_list._tree.get_node('root') + file_info = file_node.data if file_info: path = file_info.name / path pid = file_info.pid diff --git a/main.py b/main.py index cced74a..c255e63 100755 --- a/main.py +++ b/main.py @@ -20,14 +20,16 @@ @click.option('-D', '--debug', is_flag=True, help='Debug mode.') @click.option('-T', '--timeout', type=click.FLOAT, help='Api request timeout.') @click.option('-id', '--drive-id', type=click.STRING, help='Specify DRIVE_ID.') -@click.option('-a', '--album', is_flag=True, help='Specify album') -def cli(config_file, refresh_token, username, password, depth, debug, timeout, drive_id, album): +@click.option('-a', '--album', is_flag=True, help='Specify album.') +@click.option('-s', '--share-id', type=click.STRING, help='Specify share_id.') +@click.option('-sp', '--share-pwd', type=click.STRING, help='Specify share_pwd.') +def cli(config_file, refresh_token, username, password, depth, debug, timeout, drive_id, album, share_id, share_pwd): logger.info(f'Version:{__version__}') if debug: logger.setLevel('DEBUG') commander.init(config_file=None if refresh_token or username else config_file, refresh_token=None if username else refresh_token, username=username, password=password, depth=depth, - timeout=timeout, drive_id=drive_id, album=album) + timeout=timeout, drive_id=drive_id, album=album, share_id=share_id, share_pwd=share_pwd) @cli.command(aliases=['l', 'list', 'dir'], help='List files.')