aioqzone 1.8.4.dev3__tar.gz → 1.8.6.dev1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/PKG-INFO +8 -8
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/pyproject.toml +8 -8
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/aioqzone/api/h5/model.py +39 -12
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/aioqzone/model/api/feed.py +12 -5
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/aioqzone/model/api/request.py +20 -1
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/aioqzone/model/api/response.py +2 -1
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/LICENSE +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/README.md +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/aioqzone/__init__.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/aioqzone/api/__init__.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/aioqzone/api/h5/__init__.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/aioqzone/api/login/__init__.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/aioqzone/api/login/_base.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/aioqzone/exception.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/aioqzone/message.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/aioqzone/model/__init__.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/aioqzone/model/api/__init__.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/aioqzone/model/api/profile.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/aioqzone/model/protocol/__init__.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/aioqzone/model/protocol/config.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/aioqzone/model/protocol/entity.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/aioqzone/utils/__init__.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/aioqzone/utils/entity.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/aioqzone/utils/regex.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/aioqzone/utils/retry.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/aioqzone/utils/time.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/qqqr/__init__.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/qqqr/base.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/qqqr/constant.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/qqqr/exception.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/qqqr/message.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/qqqr/py.typed +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/qqqr/qr/__init__.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/qqqr/qr/type.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/qqqr/type.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/qqqr/up/__init__.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/qqqr/up/_model.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/qqqr/up/captcha/__init__.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/qqqr/up/captcha/_model.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/qqqr/up/captcha/capsess.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/qqqr/up/captcha/pil_utils.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/qqqr/up/captcha/select/__init__.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/qqqr/up/captcha/select/_types.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/qqqr/up/captcha/slide/__init__.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/qqqr/up/captcha/slide/_types.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/qqqr/up/encrypt.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/qqqr/up/h5.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/qqqr/up/web.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/qqqr/utils/encrypt.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/qqqr/utils/iter.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/qqqr/utils/jsjson.py +0 -0
- {aioqzone-1.8.4.dev3 → aioqzone-1.8.6.dev1}/src/qqqr/utils/net.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: aioqzone
|
|
3
|
-
Version: 1.8.
|
|
3
|
+
Version: 1.8.6.dev1
|
|
4
4
|
Summary: A Python wrapper for Qzone login and H5 APIs.
|
|
5
5
|
Home-page: https://github.com/aioqzone/aioqzone
|
|
6
6
|
License: AGPL-3.0
|
|
@@ -20,16 +20,16 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
20
20
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
21
|
Classifier: Typing :: Typed
|
|
22
22
|
Provides-Extra: slide-captcha
|
|
23
|
-
Requires-Dist: aiohttp (>=3.
|
|
23
|
+
Requires-Dist: aiohttp (>=3.9.0,<4.0.0)
|
|
24
24
|
Requires-Dist: exceptiongroup (>=1.1.1,<2.0.0)
|
|
25
|
-
Requires-Dist: pillow (>=10.0
|
|
26
|
-
Requires-Dist: pychaosvm (>=0.
|
|
27
|
-
Requires-Dist: pydantic (>=2.0
|
|
28
|
-
Requires-Dist: pydantic-settings (>=2.0
|
|
25
|
+
Requires-Dist: pillow (>=10.1.0,<11.0.0)
|
|
26
|
+
Requires-Dist: pychaosvm (>=0.4.0,<0.5.0)
|
|
27
|
+
Requires-Dist: pydantic (>=2.5.0,<3.0.0)
|
|
28
|
+
Requires-Dist: pydantic-settings (>=2.2.0,<3.0.0)
|
|
29
29
|
Requires-Dist: rsa (>=4.8,<5.0)
|
|
30
30
|
Requires-Dist: slide-tc (>=0.1.1,<0.2.0) ; extra == "slide-captcha"
|
|
31
|
-
Requires-Dist: tenacity (>=8.2.3,<
|
|
32
|
-
Requires-Dist: tylisten (>=2.1.
|
|
31
|
+
Requires-Dist: tenacity (>=8.2.3,<10.0.0)
|
|
32
|
+
Requires-Dist: tylisten (>=2.1.4,<3.0.0)
|
|
33
33
|
Project-URL: Bug Tracker, https://github.com/aioqzone/aioqzone/issues
|
|
34
34
|
Project-URL: Documentation, https://aioqzone.github.io/aioqzone
|
|
35
35
|
Project-URL: Discussion, https://t.me/aioqzone_chatroom
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "aioqzone"
|
|
3
|
-
version = "1.8.
|
|
3
|
+
version = "1.8.6.dev1"
|
|
4
4
|
description = "A Python wrapper for Qzone login and H5 APIs."
|
|
5
5
|
authors = ["aioqzone <zzzzss990315@gmail.com>"]
|
|
6
6
|
license = "AGPL-3.0"
|
|
@@ -28,15 +28,15 @@ exclude = ["*.js"]
|
|
|
28
28
|
|
|
29
29
|
[tool.poetry.dependencies]
|
|
30
30
|
python = "^3.8"
|
|
31
|
-
aiohttp = "^3.
|
|
32
|
-
pydantic = "^2.0
|
|
33
|
-
pydantic-settings = "^2.0
|
|
31
|
+
aiohttp = "^3.9.0"
|
|
32
|
+
pydantic = "^2.5.0"
|
|
33
|
+
pydantic-settings = "^2.2.0"
|
|
34
34
|
rsa = "^4.8"
|
|
35
|
-
tenacity = "
|
|
35
|
+
tenacity = ">=8.2.3,<10.0.0"
|
|
36
36
|
exceptiongroup = "^1.1.1"
|
|
37
|
-
tylisten = "^2.1.
|
|
38
|
-
pillow = "^10.0
|
|
39
|
-
pychaosvm = { version = "
|
|
37
|
+
tylisten = "^2.1.4"
|
|
38
|
+
pillow = "^10.1.0"
|
|
39
|
+
pychaosvm = { version = "~0.4.0", source = "aioqzone-index" }
|
|
40
40
|
slide-tc = {version = "~0.1.1", optional = true, source = "aioqzone-index" }
|
|
41
41
|
|
|
42
42
|
[tool.poetry.extras]
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
from os import PathLike
|
|
2
3
|
|
|
3
4
|
from pydantic import ValidationError
|
|
4
5
|
from tenacity import AsyncRetrying, TryAgain, after_log, stop_after_attempt
|
|
@@ -177,7 +178,7 @@ class QzoneH5API:
|
|
|
177
178
|
async def publish_mood(
|
|
178
179
|
self,
|
|
179
180
|
content: str,
|
|
180
|
-
photos: t.Optional[t.
|
|
181
|
+
photos: t.Optional[t.Sequence[t.Union[PhotoData, PicInfo]]] = None,
|
|
181
182
|
sync_weibo=False,
|
|
182
183
|
ugc_right: UgcRight = UgcRight.all,
|
|
183
184
|
) -> PublishMoodResp:
|
|
@@ -189,21 +190,47 @@ class QzoneH5API:
|
|
|
189
190
|
:param ugc_right: access right, default to "Available to Everyone".
|
|
190
191
|
"""
|
|
191
192
|
photos = photos or []
|
|
192
|
-
return await self.call(
|
|
193
|
+
return await self.call(
|
|
194
|
+
PublishMoodApi(params=PublishMoodParams.model_validate(locals(), from_attributes=True))
|
|
195
|
+
)
|
|
193
196
|
|
|
194
197
|
async def upload_pic(
|
|
195
|
-
self,
|
|
198
|
+
self,
|
|
199
|
+
picture: t.Union[bytes, str, PathLike, t.IO[bytes]],
|
|
200
|
+
width: t.Optional[int] = None,
|
|
201
|
+
height: t.Optional[int] = None,
|
|
202
|
+
quality: t.Union[int, float] = 70,
|
|
196
203
|
) -> UploadPicResponse:
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
204
|
+
"""
|
|
205
|
+
.. versionchanged:: 1.8.5
|
|
206
|
+
|
|
207
|
+
In version <= 1.8.4, user is responsible for compressing a image and this api
|
|
208
|
+
encode the :obj:`picture` with Base64 and send it to Qzone _ASIS_.
|
|
209
|
+
|
|
210
|
+
Since version 1.8.5, we recognize a compressed image by :obj:`width` and :obj:`height`
|
|
211
|
+
parameters. If :obj:`width` and :obj:`height` is provided, this API will keep the former
|
|
212
|
+
behavior. If not provided, the image will be compressed with the given quality.
|
|
213
|
+
"""
|
|
214
|
+
if isinstance(quality, float):
|
|
215
|
+
if quality < 1:
|
|
216
|
+
quality *= 100
|
|
217
|
+
quality = int(quality)
|
|
218
|
+
|
|
219
|
+
assert 0 < quality <= 100
|
|
220
|
+
|
|
221
|
+
if isinstance(picture, (str, PathLike, t.IO)):
|
|
222
|
+
params = UploadPicParams.from_image(picture, quality)
|
|
223
|
+
elif (width is None) or (height is None):
|
|
224
|
+
params = UploadPicParams.from_bytes(picture, quality)
|
|
225
|
+
else:
|
|
226
|
+
params = UploadPicParams(
|
|
227
|
+
picture=picture,
|
|
228
|
+
hd_width=width,
|
|
229
|
+
hd_height=height,
|
|
230
|
+
hd_quality=quality,
|
|
205
231
|
)
|
|
206
|
-
|
|
232
|
+
|
|
233
|
+
return await self.call(UploadPicApi(params=params))
|
|
207
234
|
|
|
208
235
|
async def preupload_photos(
|
|
209
236
|
self, upload_pics: t.List[UploadPicResponse], cur_num=0, upload_hd=False
|
|
@@ -8,6 +8,7 @@ from pydantic import (
|
|
|
8
8
|
BaseModel,
|
|
9
9
|
Field,
|
|
10
10
|
HttpUrl,
|
|
11
|
+
RootModel,
|
|
11
12
|
field_validator,
|
|
12
13
|
model_validator,
|
|
13
14
|
)
|
|
@@ -97,21 +98,27 @@ class PhotoUrl(BaseModel):
|
|
|
97
98
|
and (o.height == self.height)
|
|
98
99
|
)
|
|
99
100
|
|
|
101
|
+
@property
|
|
102
|
+
def area(self):
|
|
103
|
+
return self.height * self.width
|
|
100
104
|
|
|
101
|
-
class PhotoUrls(BaseModel):
|
|
102
|
-
urls: t.Set[PhotoUrl]
|
|
103
105
|
|
|
106
|
+
class PhotoUrls(RootModel[t.Set[PhotoUrl]]):
|
|
104
107
|
@model_validator(mode="before")
|
|
105
108
|
def unpack_urls(cls, v: dict):
|
|
106
|
-
return
|
|
109
|
+
return list(v.values())
|
|
107
110
|
|
|
108
111
|
@property
|
|
109
112
|
def largest(self) -> PhotoUrl:
|
|
110
|
-
return max(self.
|
|
113
|
+
return max(self.root, key=lambda p: p.area)
|
|
111
114
|
|
|
112
115
|
@property
|
|
113
116
|
def smallest(self) -> PhotoUrl:
|
|
114
|
-
return min(self.
|
|
117
|
+
return min(self.root, key=lambda p: p.area)
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def urls(self):
|
|
121
|
+
return self.root
|
|
115
122
|
|
|
116
123
|
|
|
117
124
|
class FeedVideo(BaseModel):
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import typing as t
|
|
2
2
|
from base64 import b64encode
|
|
3
|
+
from io import BytesIO
|
|
3
4
|
from math import floor
|
|
5
|
+
from os import PathLike
|
|
4
6
|
from time import time
|
|
5
7
|
|
|
6
|
-
from pydantic import BaseModel, Field, field_serializer
|
|
8
|
+
from pydantic import BaseModel, Field, field_serializer, field_validator
|
|
9
|
+
from typing_extensions import Buffer
|
|
7
10
|
|
|
8
11
|
from aioqzone.utils.time import time_ms
|
|
9
12
|
|
|
@@ -183,6 +186,22 @@ class UploadPicParams(QzoneRequestParams):
|
|
|
183
186
|
def b64_picture(self, picture: t.ByteString) -> str:
|
|
184
187
|
return b64encode(picture).decode()
|
|
185
188
|
|
|
189
|
+
@classmethod
|
|
190
|
+
def from_image(cls, image_file: t.Union[str, PathLike, t.IO[bytes]], quality=70):
|
|
191
|
+
import PIL.Image as image
|
|
192
|
+
|
|
193
|
+
with image.open(image_file) as f:
|
|
194
|
+
buf = BytesIO()
|
|
195
|
+
f.save(buf, "JPEG", quality=quality)
|
|
196
|
+
return cls(
|
|
197
|
+
picture=buf.getvalue(), hd_height=f.height, hd_width=f.width, hd_quality=quality
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
@classmethod
|
|
201
|
+
def from_bytes(cls, image_bytes: Buffer, quality=70):
|
|
202
|
+
buf = BytesIO(image_bytes)
|
|
203
|
+
return cls.from_image(buf, quality=quality)
|
|
204
|
+
|
|
186
205
|
|
|
187
206
|
class PhotosPreuploadParams(QzoneRequestParams):
|
|
188
207
|
uin_fields = ("uin",)
|
|
@@ -33,6 +33,7 @@ __all__ = [
|
|
|
33
33
|
"UploadPicResponse",
|
|
34
34
|
"PhotosPreuploadResponse",
|
|
35
35
|
"FeedData",
|
|
36
|
+
"PicInfo",
|
|
36
37
|
"ProfileFeedData",
|
|
37
38
|
]
|
|
38
39
|
|
|
@@ -273,4 +274,4 @@ class PhotosPreuploadResponse(QzoneResponse):
|
|
|
273
274
|
|
|
274
275
|
picinfos = json_loads(m.group(1))
|
|
275
276
|
assert isinstance(picinfos, list)
|
|
276
|
-
return dict(photos=[PicInfo.from_response_object(info["picinfo"]) for info in picinfos])
|
|
277
|
+
return dict(photos=[PicInfo.from_response_object(info["picinfo"]) for info in picinfos]) # type: ignore
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|