Skip to content

Commit d05b54e

Browse files
committed
Update low-level API.
1 parent 6c7f806 commit d05b54e

File tree

12 files changed

+329
-158
lines changed

12 files changed

+329
-158
lines changed

py115/compat/__init__.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
__author__ = 'deadblue'
2+
3+
try:
4+
from enum import StrEnum
5+
except ImportError:
6+
# Before Python 3.11
7+
from enum import Enum
8+
class StrEnum(str, Enum): pass
9+
10+
11+
__all__ = [
12+
'StrEnum'
13+
]

py115/lowlevel/__init__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
__author__ = 'deadblue'
22

3-
from .protocol import Client
4-
from .types import ApiSpec, CommonParams
3+
from .protocol import ApiSpec, Client, CommonParams
54

65
__all__ = [
76
'Client',

py115/lowlevel/api/__init__.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
11
__author__ = 'deadblue'
22

3-
from .app import AppVersionApi
3+
from .app import (
4+
AppName,
5+
AppVersionApi
6+
)
47
from .file import (
5-
FileObject,
6-
FileListResult,
8+
FileObject,
9+
FileListResult,
710
FileListApi,
11+
FileSearchApi,
12+
FileGetResult,
13+
FileGetApi,
814
FileDeleteApi,
9-
FileMoveApi,
15+
FileMoveApi,
1016
FileRenameApi,
11-
1217
DirAddApi,
1318
DirOrder,
1419
DirSortApi,
15-
1620
SpaceInfoResult,
1721
SpaceInfoApi,
1822
)
@@ -29,6 +33,7 @@
2933
VideoPlayDesktopApi
3034
)
3135
from .offline import (
36+
TaskObject,
3237
OfflineListResult,
3338
OfflineListApi,
3439
OfflineDeleteApi,
@@ -64,6 +69,9 @@
6469
from .exceptions import ApiException
6570

6671
__all__ = [
72+
'ApiException',
73+
74+
'AppName',
6775
'AppVersionApi',
6876

6977
'DirAddApi',
@@ -76,13 +84,18 @@
7684
'FileObject',
7785
'FileListResult',
7886
'FileListApi',
87+
'FileGetResult',
88+
'FileGetApi',
89+
'FileSearchApi',
7990
'FileDeleteApi',
8091
'FileMoveApi',
8192
'FileRenameApi',
8293

8394
'ImageLinkResult',
8495
'ImageLinkApi',
8596

97+
'TaskObject',
98+
8699
'OfflineListResult',
87100
'OfflineListApi',
88101
'OfflineDeleteApi',
@@ -119,6 +132,4 @@
119132

120133
'UserInfoResult',
121134
'UserInfoApi',
122-
123-
'ApiException'
124135
]

py115/lowlevel/api/_base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from urllib.parse import urlencode
77

88
from .._crypto import m115
9-
from ..types import ApiSpec, R
9+
from ..protocol import ApiSpec, R
1010
from .exceptions import ApiException
1111

1212

py115/lowlevel/api/app.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,25 @@
22

33
from typing import Any, Dict
44

5+
from py115._internal.api import app
6+
from py115.compat import StrEnum
57
from ._base import JsonApiSpec
68

79

10+
class AppName(StrEnum):
11+
12+
WINDOWS = 'window_115'
13+
MAC = 'mac_115'
14+
LINUX = 'linux_115'
15+
16+
817
class AppVersionApi(JsonApiSpec[str]):
918

10-
def __init__(self) -> None:
19+
_app_name: str
20+
21+
def __init__(self, app_name: AppName = AppName.LINUX) -> None:
1122
super().__init__('https://appversion.115.com/1/web/1.0/api/chrome')
23+
self._app_name = app_name.value
1224

1325
def _parse_json_result(self, obj: Dict[str, Any]) -> str:
14-
return obj['data']['linux_115']['version_code']
26+
return obj['data'][self._app_name]['version_code']

py115/lowlevel/api/file.py

Lines changed: 126 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,62 @@
11
__author__ = 'deadblue'
22

33
from dataclasses import dataclass
4+
from enum import IntEnum
45
from typing import Dict, List, Optional, Sequence
56

6-
from ..protocol import RetryException
7+
from py115.compat import StrEnum
8+
from py115.lowlevel.protocol import RetryException
79
from ._base import JsonApiSpec, JsonResult, VoidApiSpec
810
from ._util import to_timestamp
911

1012

11-
try:
12-
from enum import StrEnum
13-
14-
class DirOrder(StrEnum):
15-
FILE_NAME = 'file_name'
16-
FILE_SIZE = 'file_size'
17-
FILE_TYPE = 'file_type'
18-
CREATE_TIME = 'user_ptime'
19-
UPDATE_TIME = 'user_utime'
20-
OPEN_TIME = 'user_otime'
21-
22-
except:
23-
from enum import Enum
24-
25-
class DirOrder(str, Enum):
26-
FILE_NAME = 'file_name'
27-
FILE_SIZE = 'file_size'
28-
FILE_TYPE = 'file_type'
29-
UPDATE_TIME = 'user_utime'
30-
CREATE_TIME = 'user_ptime'
31-
OPEN_TIME = 'user_otime'
32-
33-
3413
@dataclass(init=False)
3514
class FileObject:
15+
"""
16+
FileObject represents a file/directory item in cloud storage.
17+
"""
18+
3619
file_id: str
20+
"""Unique ID of the file/directory."""
21+
3722
parent_id: str
23+
"""File ID of parent directory."""
24+
3825
name: str
26+
"""Base name."""
27+
3928
pickcode: str
29+
"""Pick code for download/play."""
30+
4031
is_dir: bool
32+
"""Indicate whether file is a directory."""
33+
4134
is_hidden: bool
35+
"""Indicate whether file is hidden."""
36+
4237
size: int = 0
38+
"""File size in bytes."""
39+
4340
sha1: Optional[str] = None
41+
"""File SHA-1 hash in HEX-format."""
42+
4443
update_time: int
44+
"""Timestamp when file is updated."""
45+
4546
create_time: Optional[int] = None
47+
"""Timestamp when file is created."""
48+
4649
open_time: Optional[int] = None
50+
"""Timestamp when file is opened."""
51+
4752
is_video: bool = False
53+
"""Indicate whether file is video."""
54+
4855
media_duration: Optional[int] = None
56+
"""Media duration in seconds for audio/video file."""
57+
4958
video_definition: Optional[int] = None
59+
"""Video definition for video file."""
5060

5161
def __new__(cls, json_obj: JsonResult):
5262
ret = object.__new__(cls)
@@ -78,6 +88,7 @@ def __new__(cls, json_obj: JsonResult):
7888

7989
@dataclass
8090
class FileListResult:
91+
8192
files: List[FileObject]
8293
order_mode: str
8394
order_asc: int
@@ -88,12 +99,8 @@ class FileListResult:
8899

89100
class FileListApi(JsonApiSpec[FileListResult]):
90101

91-
_offset: int
92-
_limit: int
93-
94102
def __init__(self, dir_id: str, offset: int = 0, limit: int = 115) -> None:
95103
super().__init__('')
96-
self._offset, self._limit = offset, limit
97104
self.query.update({
98105
'aid': '1',
99106
'cid': dir_id,
@@ -137,9 +144,72 @@ def _parse_json_result(self, obj: JsonResult) -> FileListResult:
137144
ret.files.append(FileObject(file_obj))
138145
return ret
139146

140-
def page_down(self):
141-
self._offset += self._limit
142-
self.query['offset'] = str(self._offset)
147+
def set_offset(self, value: int):
148+
self.query['offset'] = str(value)
149+
150+
151+
152+
class FileSearchApi(JsonApiSpec[FileListResult]):
153+
154+
def __init__(
155+
self,
156+
keyword: str,
157+
dir_id: str = '0',
158+
offset: int = 0,
159+
limit: int = 115
160+
) -> None:
161+
super().__init__('https://webapi.115.com/files/search')
162+
self.query.update({
163+
'aid': '1',
164+
'cid': dir_id,
165+
'search_value': keyword,
166+
'offset': str(offset),
167+
'limit': str(limit),
168+
'format': 'json'
169+
})
170+
171+
def _parse_json_result(self, json_obj: JsonResult) -> FileListResult:
172+
ret = FileListResult(
173+
files=[],
174+
order_mode=json_obj.get('order'),
175+
order_asc=json_obj.get('is_ac'),
176+
offset=json_obj.get('offset'),
177+
limit=json_obj.get('page_size'),
178+
count=json_obj.get('count')
179+
)
180+
for file_obj in json_obj['data']:
181+
ret.files.append(FileObject(file_obj))
182+
return ret
183+
184+
def set_offset(self, value: int):
185+
self.query['offset'] = str(value)
186+
187+
188+
@dataclass
189+
class PathNode:
190+
191+
file_id: str
192+
name: str
193+
194+
195+
@dataclass
196+
class FileGetResult:
197+
198+
name: str
199+
pickcode: str
200+
is_dir: bool
201+
path: List[PathNode]
202+
203+
204+
class FileGetApi(JsonApiSpec[FileGetResult]):
205+
206+
def __init__(self, file_id: str) -> None:
207+
super().__init__('https://webapi.115.com/category/get')
208+
self.query['cid'] = file_id
209+
210+
def _parse_json_result(self, json_obj: JsonResult) -> FileGetResult:
211+
return super()._parse_json_result(json_obj)
212+
143213

144214

145215
class FileDeleteApi(VoidApiSpec):
@@ -187,6 +257,30 @@ def _parse_json_result(self, json_obj: JsonResult) -> str:
187257
return json_obj.get('file_id')
188258

189259

260+
class DirOrder(StrEnum):
261+
"""
262+
Mode to sort files in directory.
263+
"""
264+
265+
FILE_NAME = 'file_name'
266+
"""Sort files by name."""
267+
268+
FILE_SIZE = 'file_size'
269+
"""Sort files by size."""
270+
271+
FILE_TYPE = 'file_type'
272+
"""Sort files by type."""
273+
274+
CREATE_TIME = 'user_ptime'
275+
"""Sort files by created time."""
276+
277+
UPDATE_TIME = 'user_utime'
278+
"""Sort files by last updated name."""
279+
280+
OPEN_TIME = 'user_otime'
281+
"""Sort files by last opened time."""
282+
283+
190284
class DirSortApi(VoidApiSpec):
191285

192286
def __init__(self, dir_id: str, order: DirOrder, is_asc: bool) -> None:
@@ -203,6 +297,7 @@ def __init__(self, dir_id: str, order: DirOrder, is_asc: bool) -> None:
203297

204298
@dataclass
205299
class SpaceInfoResult:
300+
206301
total_size: float
207302
remain_size: float
208303
used_size: float

py115/lowlevel/api/media.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44
from dataclasses import dataclass
55
from typing import List
66

7-
from ..types import CommonParams
7+
from ..protocol import CommonParams
88
from ._base import JsonApiSpec, JsonResult, M115ApiSpec
99
from ._util import to_int, to_https_url
1010

1111

1212
@dataclass
1313
class ImageLinkResult:
14+
1415
file_name: str
1516
origin_url: str
1617

@@ -35,13 +36,15 @@ def _parse_json_result(self, json_obj: JsonResult) -> ImageLinkResult:
3536

3637
@dataclass
3738
class VideoObject:
39+
3840
width: int
3941
height: int
4042
play_url: str
4143

4244

4345
@dataclass
4446
class VideoPlayResult:
47+
4548
file_name: str
4649
duration: int
4750
items: List[VideoObject]

0 commit comments

Comments
 (0)