From adbb255303648b86ba9f37c8ed5f011922ff8321 Mon Sep 17 00:00:00 2001 From: Priopus <134188283+Priopus@users.noreply.github.com> Date: Wed, 4 Dec 2024 00:06:46 +0800 Subject: [PATCH 1/3] Update tiktok.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增接口 --- nonebot-plugin-resolver/constants/tiktok.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/nonebot-plugin-resolver/constants/tiktok.py b/nonebot-plugin-resolver/constants/tiktok.py index e80e1cf..c25463f 100644 --- a/nonebot-plugin-resolver/constants/tiktok.py +++ b/nonebot-plugin-resolver/constants/tiktok.py @@ -18,6 +18,11 @@ """ DOUYIN_VIDEO = "https://www.douyin.com/aweme/v1/web/aweme/detail/?device_platform=webapp&aid=6383&channel=channel_pc_web&aweme_id={}&pc_client_type=1&version_code=190500&version_name=19.5.0&cookie_enabled=true&screen_width=1344&screen_height=756&browser_language=zh-CN&browser_platform=Win32&browser_name=Firefox&browser_version=118.0&browser_online=true&engine_name=Gecko&engine_version=109.0&os_name=Windows&os_version=10&cpu_core_num=16&device_memory=&platform=PC" +""" +dy临时接口 +""" +DOUYIN_TEMP_API = "https://api.xingzhige.com/API/douyin/?url={}" + """ 今日头条 DY API """ @@ -26,4 +31,4 @@ """ tiktok视频信息 """ -TIKTOK_VIDEO = "https://api22-normal-c-alisg.tiktokv.com/aweme/v1/feed/" \ No newline at end of file +TIKTOK_VIDEO = "https://api22-normal-c-alisg.tiktokv.com/aweme/v1/feed/" From 76df2128c41966f467a1d4b839cc1220484c3b6e Mon Sep 17 00:00:00 2001 From: Priopus <134188283+Priopus@users.noreply.github.com> Date: Wed, 4 Dec 2024 00:11:06 +0800 Subject: [PATCH 2/3] Update __init__.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 切换抖音图文处理模式 --- nonebot-plugin-resolver/__init__.py | 113 +++++++++++++--------------- 1 file changed, 52 insertions(+), 61 deletions(-) diff --git a/nonebot-plugin-resolver/__init__.py b/nonebot-plugin-resolver/__init__.py index 936afd6..8119ccb 100644 --- a/nonebot-plugin-resolver/__init__.py +++ b/nonebot-plugin-resolver/__init__.py @@ -18,7 +18,7 @@ from .config import Config # noinspection PyUnresolvedReferences -from .constants import COMMON_HEADER, URL_TYPE_CODE_DICT, DOUYIN_VIDEO, GENERAL_REQ_LINK, XHS_REQ_LINK, DY_TOUTIAO_INFO, \ +from .constants import COMMON_HEADER, URL_TYPE_CODE_DICT, DOUYIN_VIDEO, DOUYIN_TEMP_API, GENERAL_REQ_LINK, XHS_REQ_LINK, DY_TOUTIAO_INFO, \ BILIBILI_HEADER, NETEASE_API_CN, NETEASE_TEMP_API, VIDEO_MAX_MB, \ WEIBO_SINGLE_INFO, KUGOU_TEMP_API from .core.acfun import parse_url, download_m3u8_videos, parse_m3u8, merge_ac_file_to_mp4 @@ -362,68 +362,59 @@ async def dy(bot: Bot, event: Event) -> None: :return: """ # 消息 - msg: str = str(event.message).strip() - logger.info(msg) - # 正则匹配 - reg = r"(http:|https:)\/\/v.douyin.com\/[A-Za-z\d._?%&+\-=#]*" - dou_url = re.search(reg, msg, re.I)[0] - dou_url_2 = httpx.get(dou_url).headers.get('location') - # logger.error(dou_url_2) - reg2 = r".*(video|note)\/(\d+)\/(.*?)" - # 获取到ID - dou_id = re.search(reg2, dou_url_2, re.I)[2] - # logger.info(dou_id) - # 如果没有设置dy的ck就结束,因为获取不到 + msg = str(event.message).strip() + match = re.search(r"(http:|https:)\/\/v.douyin.com\/[A-Za-z\d._?%&+\-=#]*", msg, re.I) + dou_url = match.group(0) douyin_ck = getattr(global_config, "douyin_ck", "") - if douyin_ck == "": - logger.error(global_config) - await douyin.send(Message(f"{GLOBAL_NICKNAME}识别:抖音,无法获取到管理员设置的抖音ck!")) + if not douyin_ck: + await douyin.finish(Message(f"{GLOBAL_NICKNAME}\n来源:【抖音】\n无法获取管理员设置的抖音ck!")) return - # API、一些后续要用到的参数 - headers = { - 'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2', - 'referer': f'https://www.douyin.com/video/{dou_id}', - 'cookie': douyin_ck - } | COMMON_HEADER - api_url = DOUYIN_VIDEO.replace("{}", dou_id) - api_url = generate_x_bogus_url(api_url, headers) # 如果请求失败直接返回 - async with aiohttp.ClientSession() as session: - async with session.get(api_url, headers=headers, timeout=10) as response: - detail = await response.json() - if detail is None: - await douyin.send(Message(f"{GLOBAL_NICKNAME}识别:抖音,解析失败!")) - return - # 获取信息 - detail = detail['aweme_detail'] - # 判断是图片还是视频 - url_type_code = detail['aweme_type'] - url_type = URL_TYPE_CODE_DICT.get(url_type_code, 'video') - await douyin.send(Message(f"{GLOBAL_NICKNAME}识别:抖音,{detail.get('desc')}")) - # 根据类型进行发送 - if url_type == 'video': - # 识别播放地址 - player_uri = detail.get("video").get("play_addr")['uri'] - player_real_addr = DY_TOUTIAO_INFO.replace("{}", player_uri) - # 发送视频 - # logger.info(player_addr) - # await douyin.send(Message(MessageSegment.video(player_addr))) - await auto_video_send(event, player_real_addr) - elif url_type == 'image': - # 无水印图片列表/No watermark image list - no_watermark_image_list = [] - # 有水印图片列表/With watermark image list - watermark_image_list = [] - # 遍历图片列表/Traverse image list - for i in detail['images']: - # 无水印图片列表 - # no_watermark_image_list.append(i['url_list'][0]) - no_watermark_image_list.append(MessageSegment.image(i['url_list'][0])) - # 有水印图片列表 - # watermark_image_list.append(i['download_url_list'][0]) - # 异步发送 - # logger.info(no_watermark_image_list) - # imgList = await asyncio.gather([]) - await send_forward_both(bot, event, make_node_segment(bot.self_id, no_watermark_image_list)) + try: # 尝试获取视频信息 + dou_url_2 = httpx.get(dou_url, follow_redirects=True).url + dou_id = re.search(r".*(video|note)\/(\d+)\/(.*?)", str(dou_url_2), re.I).group(2) + headers = { + 'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2', + 'referer': f'https://www.douyin.com/video/{dou_id}', + 'cookie': douyin_ck + } | COMMON_HEADER + api_url = DOUYIN_VIDEO.format(dou_id) + api_url = generate_x_bogus_url(api_url, headers) + async with aiohttp.ClientSession() as session: + async with session.get(api_url, headers=headers, timeout=10) as response: + detail = await response.json() + if not detail: + raise ValueError("视频信息返回空数据") + detail = detail['aweme_detail'] + url_type = URL_TYPE_CODE_DICT.get(detail['aweme_type'], 'video') # 获取类型 + await douyin.send(Message(f"{GLOBAL_NICKNAME}\n来源:【抖音】\n标题:{detail.get('desc', '')}")) + + if url_type == 'video': # 视频类型 + player_uri = detail['video']['play_addr']['uri'] + player_real_addr = DY_TOUTIAO_INFO.format(player_uri) + await auto_video_send(event, player_real_addr, IS_LAGRANGE) + return # 发送后返回 + elif url_type == 'image': # 图片类型,这里改用备用API + raise ValueError("检测到图片类型,将使用备用 API") + except (AttributeError, aiohttp.ClientError, ValueError, KeyError) as e: + logger.warning(f"视频解析失败或检测到图片类型,尝试使用备用 API: {e}") + # 使用备用 API 尝试处理 + try: + douyin_temp_data = httpx.get(DOUYIN_TEMP_API.format(dou_url), headers=COMMON_HEADER).json() + data = douyin_temp_data.get("data", {}) + images = data.get("item", {}).get("images", []) + + if images: + author = data.get("author", {}).get("name", "") + title = data.get("item", {}).get("title", "") + await douyin.send(Message(f"{GLOBAL_NICKNAME}\n来源:【抖音】\n作者:{author}\n标题:{title}")) + await send_forward_both(bot, event, make_node_segment(bot.self_id, [MessageSegment.image(url) for url in images])) + return + + await douyin.finish(Message(f"{GLOBAL_NICKNAME}\n来源:【抖音】\n备用解析失败!")) + + except (httpx.HTTPError, ValueError, KeyError) as e: + logger.error(f"备用解析失败: {e}") + await douyin.finish(Message(f"{GLOBAL_NICKNAME}\n来源:【抖音】\n解析失败!")) @tik.handle() From 216e19132fa75a207603974b60f775bc5c85fc46 Mon Sep 17 00:00:00 2001 From: Priopus <134188283+Priopus@users.noreply.github.com> Date: Wed, 4 Dec 2024 02:18:42 +0800 Subject: [PATCH 3/3] Update __init__.py --- nonebot-plugin-resolver/__init__.py | 120 ++++++++++++++++++---------- 1 file changed, 80 insertions(+), 40 deletions(-) diff --git a/nonebot-plugin-resolver/__init__.py b/nonebot-plugin-resolver/__init__.py index 8119ccb..5c62649 100644 --- a/nonebot-plugin-resolver/__init__.py +++ b/nonebot-plugin-resolver/__init__.py @@ -369,52 +369,92 @@ async def dy(bot: Bot, event: Event) -> None: if not douyin_ck: await douyin.finish(Message(f"{GLOBAL_NICKNAME}\n来源:【抖音】\n无法获取管理员设置的抖音ck!")) return - try: # 尝试获取视频信息 - dou_url_2 = httpx.get(dou_url, follow_redirects=True).url - dou_id = re.search(r".*(video|note)\/(\d+)\/(.*?)", str(dou_url_2), re.I).group(2) + try: # 首先尝试使用备用 API + douyin_temp_data = httpx.get(DOUYIN_TEMP_API.format(dou_url), headers=COMMON_HEADER, timeout=10).json() + data = douyin_temp_data.get("data", {}) + item_id = data.get("jx", {}).get("item_id") + item_type = data.get("jx", {}).get("type") + + if not item_id or not item_type: + raise ValueError("备用 API 未返回 item_id 或 type") + + if item_type == "图集": # 备用API成功解析图集,直接处理 + images = data.get("item", {}).get("images", []) + if images: # 只有在有图片的情况下才发送 + author = data.get("author", {}).get("name", "") + title = data.get("item", {}).get("title", "") + await douyin.send(Message(f"{GLOBAL_NICKNAME}\n来源:【抖音】\n作者:{author}\n标题:{title}")) + await send_forward_both(bot, event, + make_node_segment(bot.self_id, [MessageSegment.image(url) for url in images])) + return # 处理完成 + else: # 如果没有图片,则尝试使用原始 API + logger.warning("备用 API 返回图集类型但未返回图片数据,尝试使用原始 API") # 记录警告信息 + # 注意:这里不需要 raise ValueError,直接进入外部的 except 块即可 + + elif item_type != "图集": # 如果类型不是图集,也使用原始API + logger.warning(f"备用 API 返回类型为 {item_type},尝试使用原始 API") # 记录警告信息 + + + except (httpx.HTTPError, ValueError, KeyError) as e: # 备用 API 失败或未返回图集数据,尝试使用原始 API + logger.warning(f"备用 API 解析失败或未返回图集数据,尝试使用原始API: {e}") + + try: # 使用旧版本处理 + dou_url_2 = httpx.get(dou_url).headers.get('location') + # logger.error(dou_url_2) + reg2 = r".*(video|note)\/(\d+)\/(.*?)" + # 获取到ID + dou_id = re.search(reg2, dou_url_2, re.I)[2] + # logger.info(dou_id) headers = { - 'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2', - 'referer': f'https://www.douyin.com/video/{dou_id}', - 'cookie': douyin_ck - } | COMMON_HEADER - api_url = DOUYIN_VIDEO.format(dou_id) - api_url = generate_x_bogus_url(api_url, headers) + 'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2', + 'referer': f'https://www.douyin.com/video/{dou_id}', + 'cookie': douyin_ck + } | COMMON_HEADER + api_url = DOUYIN_VIDEO.replace("{}", dou_id) + api_url = generate_x_bogus_url(api_url, headers) # 如果请求失败直接返回 async with aiohttp.ClientSession() as session: async with session.get(api_url, headers=headers, timeout=10) as response: detail = await response.json() - if not detail: - raise ValueError("视频信息返回空数据") + if detail is None: + await douyin.send(Message(f"{GLOBAL_NICKNAME}识别:抖音,解析失败!")) + return + # 获取信息 detail = detail['aweme_detail'] - url_type = URL_TYPE_CODE_DICT.get(detail['aweme_type'], 'video') # 获取类型 - await douyin.send(Message(f"{GLOBAL_NICKNAME}\n来源:【抖音】\n标题:{detail.get('desc', '')}")) - - if url_type == 'video': # 视频类型 - player_uri = detail['video']['play_addr']['uri'] - player_real_addr = DY_TOUTIAO_INFO.format(player_uri) - await auto_video_send(event, player_real_addr, IS_LAGRANGE) - return # 发送后返回 - elif url_type == 'image': # 图片类型,这里改用备用API - raise ValueError("检测到图片类型,将使用备用 API") - except (AttributeError, aiohttp.ClientError, ValueError, KeyError) as e: - logger.warning(f"视频解析失败或检测到图片类型,尝试使用备用 API: {e}") - # 使用备用 API 尝试处理 - try: - douyin_temp_data = httpx.get(DOUYIN_TEMP_API.format(dou_url), headers=COMMON_HEADER).json() - data = douyin_temp_data.get("data", {}) - images = data.get("item", {}).get("images", []) - - if images: - author = data.get("author", {}).get("name", "") - title = data.get("item", {}).get("title", "") - await douyin.send(Message(f"{GLOBAL_NICKNAME}\n来源:【抖音】\n作者:{author}\n标题:{title}")) - await send_forward_both(bot, event, make_node_segment(bot.self_id, [MessageSegment.image(url) for url in images])) - return - - await douyin.finish(Message(f"{GLOBAL_NICKNAME}\n来源:【抖音】\n备用解析失败!")) - - except (httpx.HTTPError, ValueError, KeyError) as e: - logger.error(f"备用解析失败: {e}") + # 判断是图片还是视频 + url_type_code = detail['aweme_type'] + url_type = URL_TYPE_CODE_DICT.get(url_type_code, 'video') + await douyin.send(Message(f"{GLOBAL_NICKNAME}\n来源:【抖音】\n标题:{detail.get('desc')}")) + # 根据类型进行发送 + if url_type == 'video': + # 识别播放地址 + player_uri = detail.get("video").get("play_addr")['uri'] + player_real_addr = DY_TOUTIAO_INFO.replace("{}", player_uri) + # 发送视频 + # logger.info(player_addr) + # await douyin.send(Message(MessageSegment.video(player_addr))) + await auto_video_send(event, player_real_addr) + elif url_type == 'image': + # 无水印图片列表/No watermark image list + no_watermark_image_list = [] + # 有水印图片列表/With watermark image list + watermark_image_list = [] + # 遍历图片列表/Traverse image list + for i in detail['images']: + # 无水印图片列表 + # no_watermark_image_list.append(i['url_list'][0]) + no_watermark_image_list.append(MessageSegment.image(i['url_list'][0])) + # 有水印图片列表 + # watermark_image_list.append(i['download_url_list'][0]) + # 异步发送 + # logger.info(no_watermark_image_list) + # imgList = await asyncio.gather([]) + await send_forward_both(bot, event, make_node_segment(bot.self_id, no_watermark_image_list)) + + + except (aiohttp.ClientError, ValueError, KeyError, AttributeError) as e: + logger.error(f"解析失败: {e}") await douyin.finish(Message(f"{GLOBAL_NICKNAME}\n来源:【抖音】\n解析失败!")) + return @tik.handle()