MeUtils 2025.6.6.18.15.9__py3-none-any.whl → 2025.6.9.9.17.14__py3-none-any.whl
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.
- {MeUtils-2025.6.6.18.15.9.dist-info → MeUtils-2025.6.9.9.17.14.dist-info}/METADATA +262 -262
- {MeUtils-2025.6.6.18.15.9.dist-info → MeUtils-2025.6.9.9.17.14.dist-info}/RECORD +25 -16
- meutils/apis/fal/images.py +11 -4
- meutils/apis/hailuoai/videos.py +3 -2
- meutils/apis/jimeng/common.py +1 -1
- meutils/apis/jimeng/images.py +0 -1
- meutils/apis/jimeng_global/__init__.py +10 -0
- meutils/apis/jimeng_global/audio.py +192 -0
- meutils/apis/jimeng_global/common.py +218 -0
- meutils/apis/jimeng_global/doubao_images.py +70 -0
- meutils/apis/jimeng_global/doubao_utils.py +175 -0
- meutils/apis/jimeng_global/files.py +368 -0
- meutils/apis/jimeng_global/images.py +744 -0
- meutils/apis/jimeng_global/videos.py +187 -0
- meutils/apis/jimeng_global/videos_videos.py +334 -0
- meutils/data/VERSION +1 -1
- meutils/schemas/image_types.py +6 -1
- meutils/schemas/jimeng_types.py +1 -0
- meutils/schemas/oneapi/common.py +4 -2
- meutils/str_utils/__init__.py +43 -6
- meutils/str_utils/regular_expression.py +1 -0
- {MeUtils-2025.6.6.18.15.9.dist-info → MeUtils-2025.6.9.9.17.14.dist-info}/LICENSE +0 -0
- {MeUtils-2025.6.6.18.15.9.dist-info → MeUtils-2025.6.9.9.17.14.dist-info}/WHEEL +0 -0
- {MeUtils-2025.6.6.18.15.9.dist-info → MeUtils-2025.6.9.9.17.14.dist-info}/entry_points.txt +0 -0
- {MeUtils-2025.6.6.18.15.9.dist-info → MeUtils-2025.6.9.9.17.14.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,70 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
# @Project : AI. @by PyCharm
|
4
|
+
# @File : images
|
5
|
+
# @Time : 2024/12/16 17:46
|
6
|
+
# @Author : betterme
|
7
|
+
# @WeChat : meutils
|
8
|
+
# @Software : PyCharm
|
9
|
+
# @Description :
|
10
|
+
|
11
|
+
from openai import AsyncClient
|
12
|
+
from meutils.pipe import *
|
13
|
+
from meutils.apis.jimeng.doubao_utils import generate_cookie, generate_params
|
14
|
+
|
15
|
+
from meutils.schemas.image_types import ImageRequest
|
16
|
+
|
17
|
+
from meutils.schemas.jimeng_types import BASE_URL, MODELS_MAP, FEISHU_URL
|
18
|
+
from meutils.config_utils.lark_utils import get_next_token_for_polling
|
19
|
+
|
20
|
+
|
21
|
+
async def create(token: Optional[str] = None):
|
22
|
+
token = token or "712a47e7eec7c03b4cc7229775e06841"
|
23
|
+
cookie = generate_cookie(token)
|
24
|
+
params = generate_params()
|
25
|
+
|
26
|
+
headers = {
|
27
|
+
'Cookie': cookie,
|
28
|
+
'agw-js-conv': 'str',
|
29
|
+
'User-Agent': 'Apifox/1.0.0 (https://apifox.com)',
|
30
|
+
'content-type': 'application/json'
|
31
|
+
}
|
32
|
+
payload = {
|
33
|
+
"messages": [
|
34
|
+
{
|
35
|
+
"content": "{\"text\":\"一只猫\"}",
|
36
|
+
"content_type": 2009,
|
37
|
+
"attachments": [
|
38
|
+
|
39
|
+
]
|
40
|
+
}
|
41
|
+
],
|
42
|
+
"completion_option": {
|
43
|
+
"is_regen": False,
|
44
|
+
"with_suggest": False,
|
45
|
+
"need_create_conversation": False,
|
46
|
+
"launch_stage": 1,
|
47
|
+
"is_replace": False,
|
48
|
+
"is_delete": False,
|
49
|
+
"message_from": 0,
|
50
|
+
"event_id": "0"
|
51
|
+
},
|
52
|
+
"section_id": "6287920686327298",
|
53
|
+
"conversation_id": "6287920686327042",
|
54
|
+
"local_message_id": "936eee40-354d-11f0-83df-6b1810ffef8a"
|
55
|
+
|
56
|
+
# "local_message_id": str(uuid.uuid4())
|
57
|
+
|
58
|
+
}
|
59
|
+
|
60
|
+
client = AsyncClient(base_url="https://www.doubao.com/samantha", default_headers=headers, api_key='xx',
|
61
|
+
)
|
62
|
+
response = await client.post("/chat/completion", body=payload, cast_to=object, stream=True,
|
63
|
+
options=dict(params=params))
|
64
|
+
async for i in response:
|
65
|
+
print(i)
|
66
|
+
# return response
|
67
|
+
|
68
|
+
|
69
|
+
if __name__ == '__main__':
|
70
|
+
arun(create())
|
@@ -0,0 +1,175 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
# @Project : AI. @by PyCharm
|
4
|
+
# @File : utils
|
5
|
+
# @Time : 2024/12/18 11:03
|
6
|
+
# @Author : betterme
|
7
|
+
# @WeChat : meutils
|
8
|
+
# @Software : PyCharm
|
9
|
+
# @Description :
|
10
|
+
|
11
|
+
from meutils.pipe import *
|
12
|
+
import random
|
13
|
+
import base64
|
14
|
+
import time
|
15
|
+
import uuid
|
16
|
+
import secrets
|
17
|
+
import string
|
18
|
+
|
19
|
+
# 常量定义
|
20
|
+
MODEL_NAME = "doubao"
|
21
|
+
DEFAULT_ASSISTANT_ID = "497858"
|
22
|
+
VERSION_CODE = "20800"
|
23
|
+
DEVICE_ID = random.random() * 999999999999999999 + 7000000000000000000
|
24
|
+
WEB_ID = random.random() * 999999999999999999 + 7000000000000000000
|
25
|
+
USER_ID = str(uuid.uuid4()).replace('-', '')
|
26
|
+
MAX_RETRY_COUNT = 3
|
27
|
+
RETRY_DELAY = 5000
|
28
|
+
FILE_MAX_SIZE = 100 * 1024 * 1024
|
29
|
+
|
30
|
+
# 伪装headers
|
31
|
+
FAKE_HEADERS = {
|
32
|
+
"Accept": "*/*",
|
33
|
+
"Accept-Encoding": "gzip, deflate, br, zstd",
|
34
|
+
"Accept-language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7",
|
35
|
+
"Cache-control": "no-cache",
|
36
|
+
"Last-event-id": "undefined",
|
37
|
+
"Origin": "https://www.doubao.com",
|
38
|
+
"Pragma": "no-cache",
|
39
|
+
"Priority": "u=1, i",
|
40
|
+
"Referer": "https://www.doubao.com",
|
41
|
+
"Sec-Ch-Ua": '"Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
|
42
|
+
"Sec-Ch-Ua-Mobile": "?0",
|
43
|
+
"Sec-Ch-Ua-Platform": '"Windows"',
|
44
|
+
"Sec-Fetch-Dest": "empty",
|
45
|
+
"Sec-Fetch-Mode": "cors",
|
46
|
+
"Sec-Fetch-Site": "same-origin",
|
47
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
|
48
|
+
}
|
49
|
+
|
50
|
+
|
51
|
+
async def acquire_token(refresh_token: str) -> str:
|
52
|
+
"""
|
53
|
+
获取缓存中的access_token
|
54
|
+
目前doubao的access_token是固定的,暂无刷新功能
|
55
|
+
|
56
|
+
Args:
|
57
|
+
refresh_token: 用于刷新access_token的refresh_token
|
58
|
+
|
59
|
+
Returns:
|
60
|
+
str: access_token
|
61
|
+
"""
|
62
|
+
return refresh_token
|
63
|
+
|
64
|
+
|
65
|
+
def generate_fake_ms_token() -> str:
|
66
|
+
"""
|
67
|
+
生成伪msToken
|
68
|
+
"""
|
69
|
+
# 生成96字节的随机数据
|
70
|
+
random_bytes = secrets.token_bytes(96)
|
71
|
+
# 转换为base64,并替换特殊字符
|
72
|
+
token = base64.b64encode(random_bytes).decode('utf-8')
|
73
|
+
return token.replace('+', '-').replace('/', '_').rstrip('=')
|
74
|
+
|
75
|
+
|
76
|
+
def generate_random_string(length: int) -> str:
|
77
|
+
"""
|
78
|
+
生成指定长度的随机字符串
|
79
|
+
"""
|
80
|
+
chars = string.ascii_letters + string.digits
|
81
|
+
return ''.join(random.choice(chars) for _ in range(length))
|
82
|
+
|
83
|
+
|
84
|
+
def generate_fake_a_bogus() -> str:
|
85
|
+
"""
|
86
|
+
生成伪a_bogus
|
87
|
+
"""
|
88
|
+
return f"mf-{generate_random_string(34)}-{generate_random_string(6)}"
|
89
|
+
|
90
|
+
|
91
|
+
def generate_params():
|
92
|
+
params = {
|
93
|
+
"aid": "497858",
|
94
|
+
"device_id": int(DEVICE_ID),
|
95
|
+
"device_platform": "web",
|
96
|
+
"language": "zh",
|
97
|
+
"pkg_type": "release_version",
|
98
|
+
"real_aid": "497858",
|
99
|
+
"region": "CN",
|
100
|
+
"samantha_web": "1",
|
101
|
+
"sys_region": "CN",
|
102
|
+
"tea_uuid": int(WEB_ID),
|
103
|
+
"use-olympus-account": "1",
|
104
|
+
"version_code": "20800",
|
105
|
+
"web_id": int(WEB_ID),
|
106
|
+
"msToken": generate_fake_ms_token(),
|
107
|
+
"a_bogus": generate_fake_a_bogus()
|
108
|
+
}
|
109
|
+
|
110
|
+
return params
|
111
|
+
|
112
|
+
|
113
|
+
def generate_cookie(refresh_token: str, ms_token: Optional[str] = None) -> str:
|
114
|
+
"""
|
115
|
+
生成cookie
|
116
|
+
"""
|
117
|
+
ms_token = ms_token or generate_fake_ms_token()
|
118
|
+
|
119
|
+
current_timestamp = int(time.time())
|
120
|
+
cookie_parts = [
|
121
|
+
f"is_staff_user=false",
|
122
|
+
f"store-region=cn-gd",
|
123
|
+
f"store-region-src=uid",
|
124
|
+
f"sid_guard={refresh_token}%7C{current_timestamp}%7C5184000%7CSun%2C+02-Feb-2025+04%3A17%3A20+GMT",
|
125
|
+
f"uid_tt={USER_ID}",
|
126
|
+
f"uid_tt_ss={USER_ID}",
|
127
|
+
f"sid_tt={refresh_token}",
|
128
|
+
f"sessionid={refresh_token}",
|
129
|
+
f"sessionid_ss={refresh_token}",
|
130
|
+
f"msToken={ms_token}",
|
131
|
+
]
|
132
|
+
return "; ".join(cookie_parts)
|
133
|
+
|
134
|
+
|
135
|
+
async def get_upload_token(refresh_token): # 3600过期
|
136
|
+
"""
|
137
|
+
|
138
|
+
{'code': 0,
|
139
|
+
'data': {'auth': {'access_key_id': 'AKTPYzNkMjJlNTNjMWE1NDJiN2E5MWFkOTYxMWViYzQxYTM',
|
140
|
+
'current_time': '2024-12-18T11:17:22+08:00',
|
141
|
+
'expired_time': '2024-12-18T12:17:22+08:00',
|
142
|
+
'secret_access_key': 'HFFTkEFKf+0DVpUrYy2yMzvgnkxLMU6+qydnGUSaDmd0vSRedpIi0qmeWSVElyOU',
|
143
|
+
'session_token': 'STS2eyJMVEFjY2Vzc0tleUlkIjoiQUtMVFlUZGhPR0ptWVRNNFl6ZG1OR1JoWVRoaE0yWTJPVFl5TW1SbU0yRmhNREEiLCJBY2Nlc3NLZXlJZCI6IkFLVFBZek5rTWpKbE5UTmpNV0UxTkRKaU4yRTVNV0ZrT1RZeE1XVmlZelF4WVRNIiwiU2lnbmVkU2VjcmV0QWNjZXNzS2V5IjoiTC9WZTVSMmt4N3dsY1kvS0E5alp1WVlpSlVFM0ZjdHMzQ2Q5QjJZMVE3NlRnUDVONWViMmpKQkRQMUdyUEtqeXNYNXRKVkJPdExvVjVNOGFyY24wQ2ZtdUZRRWMxMG8xMSs3UHdKdGY0LzQ9IiwiRXhwaXJlZFRpbWUiOjE3MzQ0OTU0NDIsIlBvbGljeVN0cmluZyI6IntcIlN0YXRlbWVudFwiOlt7XCJFZmZlY3RcIjpcIkFsbG93XCIsXCJBY3Rpb25cIjpbXCJJbWFnZVg6QXBwbHlJbWFnZVVwbG9hZFwiLFwiSW1hZ2VYOkNvbW1pdEltYWdlVXBsb2FkXCJdLFwiUmVzb3VyY2VcIjpbXCJ0cm46SW1hZ2VYOio6KjpTZXJ2aWNlSWQvYTlybnMycmw5OFwiXX0se1wiRWZmZWN0XCI6XCJBbGxvd1wiLFwiQWN0aW9uXCI6W1wiUFNNXCJdLFwiUmVzb3VyY2VcIjpbXCJmbG93LmFsaWNlLnJlc291cmNlX2FwaVwiXX1dfSIsIlNpZ25hdHVyZSI6ImI2MGUxNDZkZTU0Njg2NTdlYzVlZmFjZjJlOWNlOWE5YTdhY2UwNTFlZTdkYTJjZTRmNjdiYmRiM2U4MDQ3N2IifQ=='},
|
144
|
+
'service_id': 'a9rns2rl98',
|
145
|
+
'upload_host': 'imagex.bytedanceapi.com',
|
146
|
+
'upload_path_prefix': 'bot-chat-image'},
|
147
|
+
'msg': ''}
|
148
|
+
|
149
|
+
:return:
|
150
|
+
"""
|
151
|
+
cookie = generate_cookie(refresh_token)
|
152
|
+
url = "https://www.doubao.com/alice/upload/auth_token"
|
153
|
+
|
154
|
+
headers = {
|
155
|
+
'Cookie': cookie,
|
156
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
|
157
|
+
}
|
158
|
+
payload = {
|
159
|
+
"scene": "bot_chat",
|
160
|
+
"data_type": "image"
|
161
|
+
}
|
162
|
+
|
163
|
+
async with httpx.AsyncClient(timeout=60) as client:
|
164
|
+
response = await client.post(url, headers=headers, json=payload)
|
165
|
+
response.raise_for_status()
|
166
|
+
|
167
|
+
return response.json()
|
168
|
+
|
169
|
+
|
170
|
+
if __name__ == '__main__':
|
171
|
+
# generate_cookie("")
|
172
|
+
|
173
|
+
arun(get_upload_token("712a47e7eec7c03b4cc7229775e06841"))
|
174
|
+
# print(generate_params())
|
175
|
+
# print(get_signing_key('xW9YbDhTlWsXdaN7O2g1lfcyePxf5kJyg/r2mwSZG/iuSmbvVgToO6LVCLmUjVJ3'))
|
@@ -0,0 +1,368 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
# @Project : AI. @by PyCharm
|
4
|
+
# @File : kaiyuan
|
5
|
+
# @Time : 2024/12/18 16:03
|
6
|
+
# @Author : betterme
|
7
|
+
# @WeChat : meutils
|
8
|
+
# @Software : PyCharm
|
9
|
+
# @Description :
|
10
|
+
|
11
|
+
import hmac
|
12
|
+
import zlib
|
13
|
+
import hashlib
|
14
|
+
|
15
|
+
from meutils.pipe import *
|
16
|
+
from meutils.caches.redis_cache import cache
|
17
|
+
from meutils.io.files_utils import to_bytes
|
18
|
+
from meutils.apis.jimeng.common import get_upload_token, get_headers, BASE_URL, FEISHU_URL, check_token
|
19
|
+
from meutils.notice.feishu import send_message_for_images
|
20
|
+
from meutils.config_utils.lark_utils import get_next_token_for_polling
|
21
|
+
|
22
|
+
|
23
|
+
def random_str(n):
|
24
|
+
return ''.join(random.sample('zyxwvutsrqponmlkjihgfedcba0123456789', n))
|
25
|
+
|
26
|
+
|
27
|
+
def hash256(msg):
|
28
|
+
return hashlib.sha256(msg.encode('utf-8')).hexdigest()
|
29
|
+
|
30
|
+
|
31
|
+
def hmac_hash256(key, msg):
|
32
|
+
if type(key) == str:
|
33
|
+
return hmac.new(key.encode('utf-8'), msg.encode('utf-8'), hashlib.sha256)
|
34
|
+
elif type(key) == hmac.HMAC:
|
35
|
+
return hmac.new(key.digest(), msg.encode('utf-8'), hashlib.sha256)
|
36
|
+
|
37
|
+
|
38
|
+
def fileCRC32(file_buffer):
|
39
|
+
return hex(zlib.crc32(file_buffer) & 0xFFFFFFFF)[2:]
|
40
|
+
|
41
|
+
|
42
|
+
def u(params):
|
43
|
+
new_params = sorted(params.items(), key=lambda x: x[0])
|
44
|
+
new_params = [f"{k}={v}" for k, v in new_params]
|
45
|
+
return "&".join(new_params)
|
46
|
+
|
47
|
+
|
48
|
+
class JJRequest:
|
49
|
+
def __init__(self, e, t, api, method="GET", params=None, data=None, serviceName="imagex"):
|
50
|
+
self.t = t
|
51
|
+
self.e = e
|
52
|
+
self.api = api
|
53
|
+
self.method = method
|
54
|
+
self.params = params
|
55
|
+
self.data = data
|
56
|
+
|
57
|
+
self.serviceName = serviceName
|
58
|
+
|
59
|
+
def getAuthorization(self):
|
60
|
+
return f"AWS4-HMAC-SHA256 Credential={self.e['access_key_id']}/{self.t[:8]}/cn-north-1/{self.serviceName}/aws4_request, SignedHeaders=x-amz-date;x-amz-security-token, Signature={self.signature()}"
|
61
|
+
|
62
|
+
def signature(self):
|
63
|
+
r = self.getSigningKey()
|
64
|
+
return hmac_hash256(r, self.stringToSign()).hexdigest()
|
65
|
+
|
66
|
+
def getSigningKey(self, r="cn-north-1"):
|
67
|
+
n = self.serviceName
|
68
|
+
|
69
|
+
o = hmac_hash256("AWS4" + self.e['secret_access_key'], str(self.t[0:8]))
|
70
|
+
i = hmac_hash256(o, str(r))
|
71
|
+
s = hmac_hash256(i, str(n))
|
72
|
+
return hmac_hash256(s, "aws4_request")
|
73
|
+
|
74
|
+
def stringToSign(self):
|
75
|
+
t = []
|
76
|
+
t.append("AWS4-HMAC-SHA256")
|
77
|
+
t.append(self.t)
|
78
|
+
t.append(self.credentialString())
|
79
|
+
t.append(hash256(self.canonicalString()))
|
80
|
+
return "\n".join(t)
|
81
|
+
|
82
|
+
def credentialString(self, region="cn-north-1"):
|
83
|
+
return "/".join([self.t[0:8], region, self.serviceName, "aws4_request"])
|
84
|
+
|
85
|
+
def canonicalString(self):
|
86
|
+
e = []
|
87
|
+
e.append(self.method)
|
88
|
+
e.append("/")
|
89
|
+
e.append(u(self.params))
|
90
|
+
e.append(self.canonicalHeaders())
|
91
|
+
e.append(self.signedHeaders())
|
92
|
+
e.append(self.hexEncodedBodyHash())
|
93
|
+
return "\n".join(e)
|
94
|
+
|
95
|
+
def canonicalHeaders(self):
|
96
|
+
return f"x-amz-date:{self.t}\nx-amz-security-token:{self.e['session_token']}\n"
|
97
|
+
|
98
|
+
def signedHeaders(self):
|
99
|
+
return "x-amz-date;x-amz-security-token"
|
100
|
+
|
101
|
+
def hexEncodedBodyHash(self):
|
102
|
+
return hash256("")
|
103
|
+
|
104
|
+
|
105
|
+
# @cache(ttl=15 * 60)
|
106
|
+
async def upload(image: bytes, upload_token: dict): # oss 跨账号不知道是否可以使用
|
107
|
+
# e = auth = upload_token['data']['auth'] # 豆包
|
108
|
+
data = upload_token['data']
|
109
|
+
# service_id = data.get('service_id', '3jr8j4ixpe') # 即梦 3jr8j4ixpe 豆包 a9rns2rl98
|
110
|
+
|
111
|
+
service_id = data.get('space_name') or "3jr8j4ixpe" # 即梦视频 tb4s082cfz
|
112
|
+
|
113
|
+
if 'auth' in data:
|
114
|
+
data = data['auth']
|
115
|
+
|
116
|
+
session_token = data['session_token']
|
117
|
+
|
118
|
+
t = time.strftime("%Y%m%dT%H%M%SZ", time.gmtime())
|
119
|
+
|
120
|
+
# "ServiceId": "tb4s082cfz",
|
121
|
+
params = {
|
122
|
+
"Action": "ApplyImageUpload",
|
123
|
+
"Version": "2018-08-01",
|
124
|
+
"ServiceId": service_id,
|
125
|
+
"s": random_str(10),
|
126
|
+
|
127
|
+
# "FileExtension": ".png", #####
|
128
|
+
"FileSize": len(image),
|
129
|
+
}
|
130
|
+
|
131
|
+
r = JJRequest(data, t, "https://imagex.bytedanceapi.com/", method="GET", params=params)
|
132
|
+
headers = {
|
133
|
+
'authorization': r.getAuthorization(),
|
134
|
+
'x-amz-date': t,
|
135
|
+
'x-amz-security-token': session_token,
|
136
|
+
}
|
137
|
+
# logger.debug(headers)
|
138
|
+
response = requests.get(r.api, params=params, headers=headers)
|
139
|
+
response.raise_for_status()
|
140
|
+
logger.debug(response.status_code)
|
141
|
+
response = response.json()
|
142
|
+
|
143
|
+
logger.debug(bjson(response))
|
144
|
+
if "Result" not in response:
|
145
|
+
return
|
146
|
+
|
147
|
+
store_info = response['Result']['UploadAddress']['StoreInfos'][0]
|
148
|
+
logger.debug(bjson(store_info))
|
149
|
+
|
150
|
+
oss_uri = store_info['StoreUri']
|
151
|
+
oss_token = store_info['Auth']
|
152
|
+
|
153
|
+
headers = {
|
154
|
+
"authorization": oss_token,
|
155
|
+
"content-length": str(len(image)),
|
156
|
+
"content-Type": "image/jpeg",
|
157
|
+
"content-crc32": fileCRC32(image),
|
158
|
+
}
|
159
|
+
|
160
|
+
# oss_url = f"https://{resp['Result']['UploadAddress']['UploadHosts'][0]}/{oss_uri}"
|
161
|
+
|
162
|
+
# image_resp = requests.put( # post
|
163
|
+
# oss_url,
|
164
|
+
# headers=headers,
|
165
|
+
# data=image,
|
166
|
+
# )
|
167
|
+
# logger.debug(image_resp.json())
|
168
|
+
|
169
|
+
# return get_url(StoreUri)
|
170
|
+
|
171
|
+
# upload_url = f"https://tos-hl-x.snssdk.com/upload/v1/{oss_uri}"
|
172
|
+
upload_url = f"https://{response['Result']['UploadAddress']['UploadHosts'][0]}/upload/v1/{oss_uri}"
|
173
|
+
|
174
|
+
# response = requests.post(upload_url, headers=headers, data=image)
|
175
|
+
# response.raise_for_status()
|
176
|
+
# response = response.json()
|
177
|
+
# logger.debug(response)
|
178
|
+
|
179
|
+
async with httpx.AsyncClient(headers=headers, timeout=60) as client:
|
180
|
+
response = await client.post(upload_url, content=image)
|
181
|
+
response.raise_for_status()
|
182
|
+
data = response.json()
|
183
|
+
logger.debug(bjson(data))
|
184
|
+
|
185
|
+
return oss_uri
|
186
|
+
|
187
|
+
|
188
|
+
async def upload_for_vod(image: bytes, upload_token: dict): # oss 跨账号不知道是否可以使用
|
189
|
+
"""
|
190
|
+
"UploadHost": "tos-lf-x.snssdk.com",
|
191
|
+
"UploadHost": "tos-hl-x.snssdk.com",
|
192
|
+
|
193
|
+
:param image:
|
194
|
+
:param upload_token:
|
195
|
+
:return:
|
196
|
+
"""
|
197
|
+
# e = auth = upload_token['data']['auth'] # 豆包
|
198
|
+
data = upload_token['data']
|
199
|
+
service_id = data.get('space_name', 'tb4s082cfz') # 即梦 3jr8j4ixpe 豆包 a9rns2rl98
|
200
|
+
|
201
|
+
if 'auth' in data:
|
202
|
+
data = data['auth']
|
203
|
+
|
204
|
+
session_token = data['session_token']
|
205
|
+
|
206
|
+
t = time.strftime("%Y%m%dT%H%M%SZ", time.gmtime())
|
207
|
+
|
208
|
+
params = {
|
209
|
+
"Action": "ApplyUploadInner",
|
210
|
+
"Version": "2020-11-19",
|
211
|
+
|
212
|
+
"ServiceId": service_id,
|
213
|
+
# "SpaceName": "artist_op",
|
214
|
+
# "SpaceName": "dreamina",
|
215
|
+
|
216
|
+
"IsInner": "1",
|
217
|
+
|
218
|
+
"FileType": "video",
|
219
|
+
"FileSize": len(image),
|
220
|
+
|
221
|
+
"s": random_str(10),
|
222
|
+
|
223
|
+
}
|
224
|
+
|
225
|
+
r = JJRequest(data, t, "https://vod.bytedanceapi.com/", method="GET", params=params, serviceName="vod")
|
226
|
+
headers = {
|
227
|
+
'authorization': r.getAuthorization(),
|
228
|
+
'x-amz-date': t,
|
229
|
+
'x-amz-security-token': session_token,
|
230
|
+
}
|
231
|
+
# logger.debug(headers)
|
232
|
+
# response = requests.get(r.api, params=params, headers=headers)
|
233
|
+
# response.raise_for_status()
|
234
|
+
# logger.debug(response.status_code)
|
235
|
+
# response = response.json()
|
236
|
+
# logger.debug(bjson(response))
|
237
|
+
|
238
|
+
async with httpx.AsyncClient(headers=headers, params=params, timeout=120) as client:
|
239
|
+
response = await client.get(r.api)
|
240
|
+
response.raise_for_status()
|
241
|
+
response = response.json()
|
242
|
+
logger.debug(bjson(response))
|
243
|
+
|
244
|
+
if "Result" not in response:
|
245
|
+
return
|
246
|
+
|
247
|
+
upload_node = response['Result']['InnerUploadAddress']['UploadNodes'][0]
|
248
|
+
store_info = upload_node['StoreInfos'][0]
|
249
|
+
logger.debug(bjson(store_info))
|
250
|
+
|
251
|
+
vid = upload_node['Vid']
|
252
|
+
upload_host = upload_node['UploadHost']
|
253
|
+
|
254
|
+
oss_uri = store_info['StoreUri']
|
255
|
+
oss_token = store_info['Auth']
|
256
|
+
|
257
|
+
headers = {
|
258
|
+
"authorization": oss_token,
|
259
|
+
"content-length": str(len(image)),
|
260
|
+
"content-Type": "application/octet-stream",
|
261
|
+
"content-crc32": fileCRC32(image),
|
262
|
+
}
|
263
|
+
|
264
|
+
# upload_url = f"https://tos-hl-x.snssdk.com/upload/v1/{oss_uri}"
|
265
|
+
upload_url = f"https://{upload_host}/upload/v1/{oss_uri}"
|
266
|
+
|
267
|
+
# response = requests.post(upload_url, headers=headers, data=image)
|
268
|
+
# response.raise_for_status()
|
269
|
+
# response = response.json()
|
270
|
+
# logger.debug(response)
|
271
|
+
|
272
|
+
async with httpx.AsyncClient(headers=headers, timeout=120) as client:
|
273
|
+
response = await client.post(upload_url, content=image)
|
274
|
+
response.raise_for_status()
|
275
|
+
data = response.json()
|
276
|
+
logger.debug(bjson(data))
|
277
|
+
|
278
|
+
return vid, oss_uri
|
279
|
+
|
280
|
+
|
281
|
+
async def upload_for_image(image, token, biz: Optional[str] = None): # todo: 跨账号token
|
282
|
+
"""image url base64 bytes"""
|
283
|
+
if not image: return
|
284
|
+
|
285
|
+
upload_token = await get_upload_token(token, biz)
|
286
|
+
image_uri = await upload(await to_bytes(image), upload_token)
|
287
|
+
return image_uri
|
288
|
+
|
289
|
+
|
290
|
+
async def upload_for_video(video, token): # 跨账号token
|
291
|
+
"""video url base64 bytes
|
292
|
+
"""
|
293
|
+
if not video: return
|
294
|
+
|
295
|
+
upload_token = await get_upload_token(token)
|
296
|
+
vid, uri = await upload_for_vod(await to_bytes(video), upload_token)
|
297
|
+
return vid, uri
|
298
|
+
|
299
|
+
|
300
|
+
async def face_recognize(image, token: Optional[str] = None):
|
301
|
+
"""
|
302
|
+
图片识别
|
303
|
+
"""
|
304
|
+
# token = "1c21a9fe6a4230609d7ff13e5cec41ec" # 跨账号测试
|
305
|
+
|
306
|
+
token = token or await get_next_token_for_polling(FEISHU_URL, check_token)
|
307
|
+
|
308
|
+
url = "/mweb/v1/face_recognize"
|
309
|
+
headers = get_headers(url, token)
|
310
|
+
|
311
|
+
image_uri = await upload_for_image(image, token)
|
312
|
+
payload = {
|
313
|
+
"image_uri_list": [
|
314
|
+
image_uri
|
315
|
+
]
|
316
|
+
}
|
317
|
+
|
318
|
+
async with httpx.AsyncClient(base_url=BASE_URL, headers=headers, timeout=120) as client:
|
319
|
+
response = await client.post(url, json=payload)
|
320
|
+
response.raise_for_status()
|
321
|
+
data = response.json()
|
322
|
+
|
323
|
+
try:
|
324
|
+
data['data']['image_uri'] = image_uri
|
325
|
+
data['data']['face_recognize_list'][0][0]['type'] = ""
|
326
|
+
data['data']['face_recognize_list'][0][0]['id'] = str(uuid.uuid4())
|
327
|
+
data['data']['face_recognize_list'][0][0]['is_selected'] = True
|
328
|
+
except Exception as e:
|
329
|
+
logger.error(e)
|
330
|
+
send_message_for_images(f"图片识别失败 {bjson(data)}", __name__)
|
331
|
+
|
332
|
+
return data
|
333
|
+
|
334
|
+
|
335
|
+
if __name__ == "__main__":
|
336
|
+
# 豆包
|
337
|
+
token = "de2215a7bb8e442774cf388f03fac84c"
|
338
|
+
|
339
|
+
# jimeng
|
340
|
+
token = "ffeee346fbd19eceebb79a7bfbca4bfe"
|
341
|
+
token = "34438eb03d165737122180caf62a8058"
|
342
|
+
#
|
343
|
+
# upload_token = arun(get_upload_token(token))
|
344
|
+
|
345
|
+
#
|
346
|
+
# with open("test.jpg", "rb") as f:
|
347
|
+
# file = image = f.read()
|
348
|
+
#
|
349
|
+
# print(upload(image, upload_token))
|
350
|
+
# print(upload_for_vod(image, upload_token))
|
351
|
+
#
|
352
|
+
# with timer():
|
353
|
+
# arun(upload_for_video("https://fal.media/files/koala/8teUPbRRMtAUTORDvqy0l.mp4", token))
|
354
|
+
|
355
|
+
# with timer():
|
356
|
+
# arun(upload_for_image("https://oss.ffire.cc/files/kling_watermark.png", token))
|
357
|
+
|
358
|
+
# with timer():
|
359
|
+
# url = "https://oss.ffire.cc/files/lipsync.mp3"
|
360
|
+
# # arun(upload_for_video("https://oss.ffire.cc/files/lipsync.mp3", token))
|
361
|
+
# arun(upload_for_video(url, token))
|
362
|
+
image_url = "https://oss.ffire.cc/files/kling_watermark.png"
|
363
|
+
# arun(face_recognize(image_url, token))
|
364
|
+
# arun(face_recognize(image_url))
|
365
|
+
|
366
|
+
token = "ed16bb360a4744696f88a7b52b7c10a3"
|
367
|
+
|
368
|
+
arun(upload_for_image(image_url, token, "video"))
|