diff --git a/api.py b/api.py index c7cf453..2e4a6d2 100644 --- a/api.py +++ b/api.py @@ -1,18 +1,19 @@ from flask import Flask, request, jsonify -from qushuiyin import qushuiyin +from qushuiyin import clean_mask app = Flask(__name__) -@app.route("/get_dy_urls", methods=["POST"]) -async def get_dy_urls(): +@app.route("/get_urls", methods=["POST"]) +def get_urls(): data = request.get_json() # 从请求中获取JSON数据 if not data: return jsonify({"error": "No data provided"}), 400 # 处理数据 - dy_urls = await qushuiyin(data["share_url"]) - return jsonify(dy_urls), 200 + url = clean_mask(data["share_url"]) + print(url) + return jsonify(url), 200 if __name__ == "__main__": diff --git a/qushuiyin.py b/qushuiyin.py index d2f4967..f0eb43f 100644 --- a/qushuiyin.py +++ b/qushuiyin.py @@ -1,39 +1,131 @@ -import asyncio -from f2.apps.douyin.utils import AwemeIdFetcher -from f2.utils.utils import extract_valid_urls -from f2.apps.douyin.handler import DouyinHandler - -kwargs = { - "headers": { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0", - "Referer": "https://www.douyin.com/", - }, - "cookie": "FORCE_LOGIN=%7B%22videoConsumedRemainSeconds%22%3A180%2C%22isForcePopClose%22%3A1%7D; UIFID_TEMP=96cd3b166f3029d7c1cc3f64582454ab8a83ff1f9e6d6689076dd47ef1dca5f83186b571ff21ff4bb6319242fd3a000958f84401be4ff7112aab5e1d0d28d225826c5cab4c0c98ad8e053a867536155b; passport_csrf_token=8cd8f40fd238dfc66726a9313bab7b43; passport_csrf_token_default=8cd8f40fd238dfc66726a9313bab7b43; strategyABtestKey=%221720775917.246%22; volume_info=%7B%22isUserMute%22%3Afalse%2C%22isMute%22%3Atrue%2C%22volume%22%3A0.5%7D; IsDouyinActive=false; UIFID=96cd3b166f3029d7c1cc3f64582454ab8a83ff1f9e6d6689076dd47ef1dca5f83186b571ff21ff4bb6319242fd3a000935cd9a29c945708e8dff5eac4d7162af456df1a101a167e1a053dd6804a055da1e54bcbc4da003151237a7ea7ff17ab16ecd680ca9dbb269d30e2dcf8b83e2dd3a615757a5b6b1b2a9f4cab94b0f01e64e80472791023d28c6086dd081454b6722c267408bb9fb23d9f9dd1afef82d04; __security_server_data_status=1; _bd_ticket_crypt_cookie=a2d6bc10b99e0ebe24f3b101dcdf8959; _bd_ticket_crypt_doamin=2; bd_ticket_guard_client_data=eyJiZC10aWNrZXQtZ3VhcmQtdmVyc2lvbiI6MiwiYmQtdGlja2V0LWd1YXJkLWl0ZXJhdGlvbi12ZXJzaW9uIjoxLCJiZC10aWNrZXQtZ3VhcmQtcmVlLXB1YmxpYy1rZXkiOiJCTkdmSW1QelhyYzdZdjNoczBsNVEyc3J5K0hWbFVURit6c2hXWUI3M0F0ckpURGN6eTlEa3hFa2hYck83MW1uMnVObUpmV3FOQllwWWQ4ZVpxWEdSNFk9IiwiYmQtdGlja2V0LWd1YXJkLXdlYi12ZXJzaW9uIjoxfQ%3D%3D; bd_ticket_guard_client_web_domain=2; biz_trace_id=b81688a4; home_can_add_dy_2_desktop=%221%22; n_mh=Hv1EJfXw0zYqU4nePlN0-RVMnIEWX6hsRuiqz9swB1g; odin_tt=517b4f71a98f5723cfb8702ea16e58a603fd74c3b0def5b3a0e9b7ad7f9107665aaa596b71780779ef7b186a9007a199; passport_assist_user=CjwaLaY1yUBd1BWBMaw9dw3dRtIhzeqTb55PAhaEKIp71exKOkVqHaXMD3h3jvzY8QUHeyefpHjoS36Wz5oaSgo8v2Hef5bG0COZ34nU_EzlMVDkAWO9QR155auuQIYTm4GbH7UkdJ1Mjp0jLIuQJFbb82AjYyyvyVCLwiJKELy51g0Yia_WVCABIgED-THeTQ%3D%3D; passport_auth_status=ae5e2fdc63086401ca880f6f7b5755a7%2C; passport_auth_status_ss=ae5e2fdc63086401ca880f6f7b5755a7%2C; publish_badge_show_info=%220%2C0%2C0%2C1720775946680%22; sessionid=f000d470c442a50b3053c21d630613e4; sessionid_ss=f000d470c442a50b3053c21d630613e4; sid_guard=f000d470c442a50b3053c21d630613e4%7C1720775948%7C5183997%7CTue%2C+10-Sep-2024+09%3A19%3A05+GMT; sid_tt=f000d470c442a50b3053c21d630613e4; sid_ucp_sso_v1=1.0.0-KDVmMGM5ODI2ODVmODJjZjFjZjE2NWM5YWU4ZmJkM2QyOTY1NjFhZWUKHwjant_X3AIQhurDtAYY7zEgDDDo17vUBTgGQPQHSAYaAmxmIiA4ZDU5MzVjNjU4MmE1NDNkODFkNzE1ZWQ4OGEyYmU5Yw; sid_ucp_v1=1.0.0-KGQzMzdiNTI0YTIxNTllZmU5YmQ0MjQ0ZGIxZmRmYTMwNDA2MGI4MjYKGQjant_X3AIQjOrDtAYY7zEgDDgGQPQHSAQaAmhsIiBmMDAwZDQ3MGM0NDJhNTBiMzA1M2MyMWQ2MzA2MTNlNA; ssid_ucp_sso_v1=1.0.0-KDVmMGM5ODI2ODVmODJjZjFjZjE2NWM5YWU4ZmJkM2QyOTY1NjFhZWUKHwjant_X3AIQhurDtAYY7zEgDDDo17vUBTgGQPQHSAYaAmxmIiA4ZDU5MzVjNjU4MmE1NDNkODFkNzE1ZWQ4OGEyYmU5Yw; ssid_ucp_v1=1.0.0-KGQzMzdiNTI0YTIxNTllZmU5YmQ0MjQ0ZGIxZmRmYTMwNDA2MGI4MjYKGQjant_X3AIQjOrDtAYY7zEgDDgGQPQHSAQaAmhsIiBmMDAwZDQ3MGM0NDJhNTBiMzA1M2MyMWQ2MzA2MTNlNA; sso_uid_tt=bb63fd08ff1688397ce405b1c5fb0501; sso_uid_tt_ss=bb63fd08ff1688397ce405b1c5fb0501; store-region=cn-sc; store-region-src=uid; stream_player_status_params=%22%7B%5C%22is_auto_play%5C%22%3A0%2C%5C%22is_full_screen%5C%22%3A0%2C%5C%22is_full_webscreen%5C%22%3A0%2C%5C%22is_mute%5C%22%3A1%2C%5C%22is_speed%5C%22%3A1%2C%5C%22is_visible%5C%22%3A0%7D%22; stream_recommend_feed_params=%22%7B%5C%22cookie_enabled%5C%22%3Atrue%2C%5C%22screen_width%5C%22%3A1440%2C%5C%22screen_height%5C%22%3A900%2C%5C%22browser_online%5C%22%3Atrue%2C%5C%22cpu_core_num%5C%22%3A8%2C%5C%22device_memory%5C%22%3A8%2C%5C%22downlink%5C%22%3A1.45%2C%5C%22effective_type%5C%22%3A%5C%223g%5C%22%2C%5C%22round_trip_time%5C%22%3A300%7D%22; toutiao_sso_user=8d5935c6582a543d81d715ed88a2be9c; toutiao_sso_user_ss=8d5935c6582a543d81d715ed88a2be9c; ttwid=1%7CburBMWkF_7QUTgeJnTOmvWfuMru9HzMGR94GSk2PbOM%7C1720775913%7C88e448a4fb798a93ac31cbb4f11533a9d8edd1314f03fe7e7a83991a9ede1ebc; uid_tt=4c93500137dae9741192d37d2e9f7600; uid_tt_ss=4c93500137dae9741192d37d2e9f7600; __ac_nonce=06690f4e900408e95cafa; __ac_signature=_02B4Z6wo00f01w0O85QAAIDAX.HTjoG5QxsNLvcAAKXdfd; device_web_cpu_core=8; device_web_memory_size=8; fpk1=U2FsdGVkX19QlXys0TN0qt/rloGcQnCb+pMhwVraOEscCHNEiGn9DqCC4NEHxt0qo36hgXj1vV9vV1xZZR3/eA==; fpk2=10f9287deaf609ee36fb37783f2b89c0; s_v_web_id=verify_lyihjt5b_W9CsZ62G_SzZ4_4kS2_Apei_7Cez2wak0XW2; =douyin.com; csrf_session_id=75739f2b4ce4064529cf3ad2c566e80e; dy_sheight=900; dy_swidth=1440; xg_device_score=6.719622045696811; passport_fe_beating_status=false", - "proxies": {"http://": None, "https://": None}, -} +import re +import json +import requests -async def get_video_id(share_url): - raw_urls = [ - share_url, - ] +class Video: + def pipixia(self, url): + loc = requests.head(url).headers.get("Location") + id_match = re.search(r"item/(.*)\?", loc) + if id_match: + item_id = id_match.group(1) + response = requests.get( + f"https://is.snssdk.com/bds/cell/detail/?cell_type=1&aid=1319&app_name=super&cell_id={item_id}" + ) + arr = response.json() + video_url = arr["data"]["data"]["item"]["origin_video_download"][ + "url_list" + ][0]["url"] + if video_url: + return { + "code": 200, + "data": { + "author": arr["data"]["data"]["item"]["author"]["name"], + "avatar": arr["data"]["data"]["item"]["author"]["avatar"][ + "download_list" + ][0]["url"], + "time": arr["data"]["data"]["display_time"], + "title": arr["data"]["data"]["item"]["content"], + "cover": arr["data"]["data"]["item"]["cover"]["url_list"][0][ + "url" + ], + "url": video_url, + }, + } + return None - # 提取有效URL - urls = extract_valid_urls(raw_urls) + def douyin(self, url): + # Extract ID from URL + id_match = self.extract_id(url) + if not id_match: + return {"code": 400, "msg": "Unable to parse video ID"} - # 对于URL列表 - return await AwemeIdFetcher.get_all_aweme_id(urls) + video_id = id_match + + # Construct request headers + headers = { + "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1 Edg/122.0.0.0" + } + + # Send request to get video info + response = requests.get( + f"https://www.iesdouyin.com/share/video/{video_id}", headers=headers + ) + pattern = r"window\._ROUTER_DATA\s*=\s*(.*?)" + matches = re.search(pattern, response.text, re.DOTALL) + + if not matches: + return {"code": 201, "msg": "Parsing failed"} + + video_info = json.loads(matches.group(1).strip()) + if "loaderData" not in video_info: + return {"code": 201, "msg": "Parsing failed"} + + video_res_url = video_info["loaderData"]["video_(id)/page"]["videoInfoRes"][ + "item_list" + ][0]["video"]["play_addr"]["url_list"][0] + video_res_url = video_res_url.replace("playwm", "play") + + return { + "code": 200, + "msg": "Parsing successful", + "data": { + "author": video_info["loaderData"]["video_(id)/page"]["videoInfoRes"][ + "item_list" + ][0]["author"]["nickname"], + "uid": video_info["loaderData"]["video_(id)/page"]["videoInfoRes"][ + "item_list" + ][0]["author"]["unique_id"], + "avatar": video_info["loaderData"]["video_(id)/page"]["videoInfoRes"][ + "item_list" + ][0]["author"]["avatar_medium"]["url_list"][0], + "like": video_info["loaderData"]["video_(id)/page"]["videoInfoRes"][ + "item_list" + ][0]["statistics"]["digg_count"], + "time": video_info["loaderData"]["video_(id)/page"]["videoInfoRes"][ + "item_list" + ][0]["create_time"], + "title": video_info["loaderData"]["video_(id)/page"]["videoInfoRes"][ + "item_list" + ][0]["desc"], + "cover": video_info["loaderData"]["video_(id)/page"]["videoInfoRes"][ + "item_list" + ][0]["video"]["cover"]["url_list"][0], + "url": video_res_url, + "music": { + "author": video_info["loaderData"]["video_(id)/page"][ + "videoInfoRes" + ]["item_list"][0]["music"]["author"], + "avatar": video_info["loaderData"]["video_(id)/page"][ + "videoInfoRes" + ]["item_list"][0]["music"]["cover_large"]["url_list"][0], + }, + }, + } + + def extract_id(self, url): + try: + response = requests.head(url, allow_redirects=True) + final_url = response.url + id_match = re.search(r"/(\d+)", final_url) + return id_match.group(1) if id_match else None + except Exception as e: + return None -async def qushuiyin(share_url): - aweme_id = await get_video_id(share_url) - video = await DouyinHandler(kwargs).fetch_one_video(aweme_id=aweme_id[0]) - video_info = video._to_dict() - cover_url = video_info["cover"] - music_url = video_info["music_play_url"] - video_urls = video_info["video_play_addr"] - return {"cover_url": cover_url, "music_url": music_url, "video_urls": video_urls} +def get_raw_url(share_url): + regex = r"http[s]?://[\w.]+[\w\/]*[\w.]*\??[\w=&:\-\+\%]*[/]*" + match = re.search(regex, share_url) + if match: + url = match.group(0) + return url + else: + return None -if __name__ == "__main__": - print(asyncio.run(qushuiyin())) +def clean_mask(share_url): + raw_url = get_raw_url(share_url) + video = Video() + result = video.douyin(raw_url) + print(result) + return result diff --git a/requirements.txt b/requirements.txt index 66117f7..7464955 100644 Binary files a/requirements.txt and b/requirements.txt differ