style(tiktok): 整理代码格式

This commit is contained in:
imgyh 2023-04-21 16:53:37 +08:00
parent 48fcf902be
commit 064ae7dc63
14 changed files with 1849 additions and 1816 deletions

318
.gitignore vendored
View File

@ -1,160 +1,160 @@
# Byte-compiled / optimized / DLL files # Byte-compiled / optimized / DLL files
__pycache__/ __pycache__/
*.py[cod] *.py[cod]
*$py.class *$py.class
# C extensions # C extensions
*.so *.so
# Distribution / packaging # Distribution / packaging
.Python .Python
build/ build/
develop-eggs/ develop-eggs/
dist/ dist/
downloads/ downloads/
eggs/ eggs/
.eggs/ .eggs/
lib/ lib/
lib64/ lib64/
parts/ parts/
sdist/ sdist/
var/ var/
wheels/ wheels/
share/python-wheels/ share/python-wheels/
*.egg-info/ *.egg-info/
.installed.cfg .installed.cfg
*.egg *.egg
MANIFEST MANIFEST
# PyInstaller # PyInstaller
# Usually these files are written by a python script from a template # Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it. # before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest *.manifest
*.spec *.spec
# Installer logs # Installer logs
pip-log.txt pip-log.txt
pip-delete-this-directory.txt pip-delete-this-directory.txt
# Unit test / coverage reports # Unit test / coverage reports
htmlcov/ htmlcov/
.tox/ .tox/
.nox/ .nox/
.coverage .coverage
.coverage.* .coverage.*
.cache .cache
nosetests.xml nosetests.xml
coverage.xml coverage.xml
*.cover *.cover
*.py,cover *.py,cover
.hypothesis/ .hypothesis/
.pytest_cache/ .pytest_cache/
cover/ cover/
# Translations # Translations
*.mo *.mo
*.pot *.pot
# Django stuff: # Django stuff:
*.log *.log
local_settings.py local_settings.py
db.sqlite3 db.sqlite3
db.sqlite3-journal db.sqlite3-journal
# Flask stuff: # Flask stuff:
instance/ instance/
.webassets-cache .webassets-cache
# Scrapy stuff: # Scrapy stuff:
.scrapy .scrapy
# Sphinx documentation # Sphinx documentation
docs/_build/ docs/_build/
# PyBuilder # PyBuilder
.pybuilder/ .pybuilder/
target/ target/
# Jupyter Notebook # Jupyter Notebook
.ipynb_checkpoints .ipynb_checkpoints
# IPython # IPython
profile_default/ profile_default/
ipython_config.py ipython_config.py
# pyenv # pyenv
# For a library or package, you might want to ignore these files since the code is # For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in: # intended to run in multiple environments; otherwise, check them in:
# .python-version # .python-version
# pipenv # pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies # However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not # having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies. # install all needed dependencies.
#Pipfile.lock #Pipfile.lock
# poetry # poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more # This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries. # commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock #poetry.lock
# pdm # pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock #pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control. # in version control.
# https://pdm.fming.dev/#use-with-ide # https://pdm.fming.dev/#use-with-ide
.pdm.toml .pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/ __pypackages__/
# Celery stuff # Celery stuff
celerybeat-schedule celerybeat-schedule
celerybeat.pid celerybeat.pid
# SageMath parsed files # SageMath parsed files
*.sage.py *.sage.py
# Environments # Environments
.env .env
.venv .venv
env/ env/
venv/ venv/
ENV/ ENV/
env.bak/ env.bak/
venv.bak/ venv.bak/
# Spyder project settings # Spyder project settings
.spyderproject .spyderproject
.spyproject .spyproject
# Rope project settings # Rope project settings
.ropeproject .ropeproject
# mkdocs documentation # mkdocs documentation
/site /site
# mypy # mypy
.mypy_cache/ .mypy_cache/
.dmypy.json .dmypy.json
dmypy.json dmypy.json
# Pyre type checker # Pyre type checker
.pyre/ .pyre/
# pytype static type analyzer # pytype static type analyzer
.pytype/ .pytype/
# Cython debug symbols # Cython debug symbols
cython_debug/ cython_debug/
# PyCharm # PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear # and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder. # option (not recommended) you can uncomment the following to ignore the entire idea folder.
.idea/ .idea/

View File

@ -1,19 +1,19 @@
# This Dockerfile is used to build an Python environment # This Dockerfile is used to build an Python environment
FROM node:18-bullseye-slim FROM node:18-bullseye-slim
LABEL maintainer="imgyh<admin@imgyh.com>" LABEL maintainer="imgyh<admin@imgyh.com>"
WORKDIR /app WORKDIR /app
ADD . $WORKDIR ADD . $WORKDIR
RUN sed -i s/deb.debian.org/mirrors.aliyun.com/g /etc/apt/sources.list RUN sed -i s/deb.debian.org/mirrors.aliyun.com/g /etc/apt/sources.list
RUN apt-get update && apt-get install -y python3.9 python3-pip RUN apt-get update && apt-get install -y python3.9 python3-pip
RUN pip3 install -r requirements.txt RUN pip3 install -r requirements.txt
ENV TZ=Asia/Shanghai ENV TZ=Asia/Shanghai
CMD ["python3", "TikTokWeb.py"] CMD ["python3", "TikTokWeb.py"]

View File

@ -83,6 +83,7 @@ class TikTok(object):
userVideoUrls.append(videoRealUrl) userVideoUrls.append(videoRealUrl)
return userVideoUrls return userVideoUrls
tk = TikTok() tk = TikTok()
# tk.oneVideoInfo() # tk.oneVideoInfo()
tk.userVideoInfo() tk.userVideoInfo()

View File

@ -48,10 +48,10 @@ class TikTok(object):
self.utils = Utils() self.utils = Utils()
self.result = Result() self.result = Result()
self.headers = { self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36',
'referer': 'https://www.douyin.com/', 'referer': 'https://www.douyin.com/',
'accept-encoding': None, 'accept-encoding': None,
'Cookie': f"msToken={self.utils.generate_random_str(107)}; ttwid={self.utils.getttwid()}; odin_tt=324fb4ea4a89c0c05827e18a1ed9cf9bf8a17f7705fcc793fec935b637867e2a5a9b8168c885554d029919117a18ba69; passport_csrf_token=f61602fc63757ae0e4fd9d6bdcee4810;" 'Cookie': f"msToken={self.utils.generate_random_str(107)}; ttwid={self.utils.getttwid()}; odin_tt=324fb4ea4a89c0c05827e18a1ed9cf9bf8a17f7705fcc793fec935b637867e2a5a9b8168c885554d029919117a18ba69; passport_csrf_token=f61602fc63757ae0e4fd9d6bdcee4810;"
} }
# 用于设置重复请求某个接口的最大时间 # 用于设置重复请求某个接口的最大时间
self.timeout = 10 self.timeout = 10
@ -71,7 +71,6 @@ class TikTok(object):
# self.done_event = Event() # self.done_event = Event()
# signal.signal(signal.SIGINT, self.handle_sigint) # signal.signal(signal.SIGINT, self.handle_sigint)
# 从分享链接中提取网址 # 从分享链接中提取网址
def getShareLink(self, string): def getShareLink(self, string):
# findall() 查找匹配正则表达式的字符串 # findall() 查找匹配正则表达式的字符串
@ -145,7 +144,6 @@ class TikTok(object):
return key_type, key return key_type, key
def getAwemeInfoApi(self, aweme_id): def getAwemeInfoApi(self, aweme_id):
if aweme_id is None: if aweme_id is None:
return None return None
@ -205,7 +203,7 @@ class TikTok(object):
if end - start > self.timeout: if end - start > self.timeout:
# raise RuntimeError("重复请求该接口" + str(self.timeout) + "s, 仍然未获取到数据") # raise RuntimeError("重复请求该接口" + str(self.timeout) + "s, 仍然未获取到数据")
print("[ 提示 ]:重复请求该接口" + str(self.timeout) + "s, 仍然未获取到数据") print("[ 提示 ]:重复请求该接口" + str(self.timeout) + "s, 仍然未获取到数据")
return {},{} return {}, {}
# print("[ 警告 ]:接口未返回数据, 正在重新请求!\r") # print("[ 警告 ]:接口未返回数据, 正在重新请求!\r")
# 清空self.awemeDict # 清空self.awemeDict
@ -225,7 +223,6 @@ class TikTok(object):
return self.result.awemeDict, datadict return self.result.awemeDict, datadict
def getUserInfoApi(self, sec_uid, mode="post", count=35, max_cursor=0): def getUserInfoApi(self, sec_uid, mode="post", count=35, max_cursor=0):
if sec_uid is None: if sec_uid is None:
return None return None
@ -346,10 +343,10 @@ class TikTok(object):
awemeList.append(copy.deepcopy(self.result.awemeDict)) awemeList.append(copy.deepcopy(self.result.awemeDict))
if numflag: if numflag:
number-=1 number -= 1
if number==0: if number == 0:
break break
if numflag and number==0: if numflag and number == 0:
print("\r\n[ 提示 ]: [主页] 下指定数量作品数据获取完成...\r\n") print("\r\n[ 提示 ]: [主页] 下指定数量作品数据获取完成...\r\n")
break break
@ -399,7 +396,8 @@ class TikTok(object):
self.result.liveDict["cover"] = live_json['data']['data'][0]['cover']['url_list'][0] self.result.liveDict["cover"] = live_json['data']['data'][0]['cover']['url_list'][0]
# 头像 # 头像
self.result.liveDict["avatar"] = live_json['data']['data'][0]['owner']['avatar_thumb']['url_list'][0].replace("100x100", "1080x1080") self.result.liveDict["avatar"] = live_json['data']['data'][0]['owner']['avatar_thumb']['url_list'][0].replace(
"100x100", "1080x1080")
# 观看人数 # 观看人数
self.result.liveDict["user_count"] = live_json['data']['data'][0]['user_count_str'] self.result.liveDict["user_count"] = live_json['data']['data'][0]['user_count_str']
@ -419,19 +417,20 @@ class TikTok(object):
try: try:
# 分区 # 分区
self.result.liveDict["partition"] = live_json['data']['partition_road_map']['partition']['title'] self.result.liveDict["partition"] = live_json['data']['partition_road_map']['partition']['title']
self.result.liveDict["sub_partition"] = live_json['data']['partition_road_map']['sub_partition']['partition'][ self.result.liveDict["sub_partition"] = \
live_json['data']['partition_road_map']['sub_partition']['partition'][
'title'] 'title']
except Exception as e: except Exception as e:
self.result.liveDict["partition"] = '' self.result.liveDict["partition"] = ''
self.result.liveDict["sub_partition"] = '' self.result.liveDict["sub_partition"] = ''
flv = [] flv = []
for i, f in enumerate(self.result.liveDict["flv_pull_url"].keys()): for i, f in enumerate(self.result.liveDict["flv_pull_url"].keys()):
flv.append(f) flv.append(f)
self.result.liveDict["flv_pull_url0"] = self.result.liveDict["flv_pull_url"][flv[0]].replace("http://", "https://") self.result.liveDict["flv_pull_url0"] = self.result.liveDict["flv_pull_url"][flv[0]].replace("http://",
"https://")
return self.result.liveDict, live_json return self.result.liveDict, live_json
@ -477,7 +476,8 @@ class TikTok(object):
self.result.liveDict["cover"] = live_json['data']['data'][0]['cover']['url_list'][0] self.result.liveDict["cover"] = live_json['data']['data'][0]['cover']['url_list'][0]
# 头像 # 头像
self.result.liveDict["avatar"] = live_json['data']['data'][0]['owner']['avatar_thumb']['url_list'][0].replace("100x100", "1080x1080") self.result.liveDict["avatar"] = live_json['data']['data'][0]['owner']['avatar_thumb']['url_list'][0].replace(
"100x100", "1080x1080")
# 观看人数 # 观看人数
self.result.liveDict["user_count"] = live_json['data']['data'][0]['user_count_str'] self.result.liveDict["user_count"] = live_json['data']['data'][0]['user_count_str']
@ -497,7 +497,8 @@ class TikTok(object):
try: try:
# 分区 # 分区
self.result.liveDict["partition"] = live_json['data']['partition_road_map']['partition']['title'] self.result.liveDict["partition"] = live_json['data']['partition_road_map']['partition']['title']
self.result.liveDict["sub_partition"] = live_json['data']['partition_road_map']['sub_partition']['partition'][ self.result.liveDict["sub_partition"] = \
live_json['data']['partition_road_map']['sub_partition']['partition'][
'title'] 'title']
except Exception as e: except Exception as e:
self.result.liveDict["partition"] = '' self.result.liveDict["partition"] = ''
@ -516,7 +517,8 @@ class TikTok(object):
rate = int(input('[ 🎬 ]输入数字选择推流清晰度:')) rate = int(input('[ 🎬 ]输入数字选择推流清晰度:'))
self.result.liveDict["flv_pull_url0"] = self.result.liveDict["flv_pull_url"][flv[rate]].replace("http://", "https://") self.result.liveDict["flv_pull_url0"] = self.result.liveDict["flv_pull_url"][flv[rate]].replace("http://",
"https://")
# 显示清晰度列表 # 显示清晰度列表
print('[ %s ]:%s' % (flv[rate], self.result.liveDict["flv_pull_url"][flv[rate]])) print('[ %s ]:%s' % (flv[rate], self.result.liveDict["flv_pull_url"][flv[rate]]))
@ -544,7 +546,6 @@ class TikTok(object):
if end - start > self.timeout: if end - start > self.timeout:
return None return None
for aweme in datadict["aweme_list"]: for aweme in datadict["aweme_list"]:
# 清空self.awemeDict # 清空self.awemeDict
@ -672,13 +673,12 @@ class TikTok(object):
return None return None
for mix in datadict["mix_infos"]: for mix in datadict["mix_infos"]:
mixIdNameDict={} mixIdNameDict = {}
mixIdNameDict["https://www.douyin.com/collection/" + mix["mix_id"]] = mix["mix_name"] mixIdNameDict["https://www.douyin.com/collection/" + mix["mix_id"]] = mix["mix_name"]
mixIdlist.append(mixIdNameDict) mixIdlist.append(mixIdNameDict)
return mixIdlist, datadict, datadict["cursor"], datadict["has_more"] return mixIdlist, datadict, datadict["cursor"], datadict["has_more"]
def getUserAllMixInfo(self, sec_uid, count=35, number=0): def getUserAllMixInfo(self, sec_uid, count=35, number=0):
print('[ 提示 ]:正在请求的用户 id = %s\r\n' % sec_uid) print('[ 提示 ]:正在请求的用户 id = %s\r\n' % sec_uid)
if sec_uid is None: if sec_uid is None:
@ -762,7 +762,6 @@ class TikTok(object):
if end - start > self.timeout: if end - start > self.timeout:
return None return None
for aweme in datadict["aweme_list"]: for aweme in datadict["aweme_list"]:
# 清空self.awemeDict # 清空self.awemeDict
self.result.clearDict(self.result.awemeDict) self.result.clearDict(self.result.awemeDict)
@ -924,7 +923,7 @@ class TikTok(object):
try: try:
# 使用作品 创建时间+描述 当文件夹 # 使用作品 创建时间+描述 当文件夹
file_name = awemeDict["create_time"] + "_" + self.utils.replaceStr(awemeDict["desc"]) file_name = awemeDict["create_time"] + "_" + self.utils.replaceStr(awemeDict["desc"])
aweme_path = os.path.join(savePath, file_name) aweme_path = os.path.join(savePath, file_name)
if not os.path.exists(aweme_path): if not os.path.exists(aweme_path):
os.mkdir(aweme_path) os.mkdir(aweme_path)
@ -937,7 +936,7 @@ class TikTok(object):
f.write(json.dumps(awemeDict, ensure_ascii=False, indent=2)) f.write(json.dumps(awemeDict, ensure_ascii=False, indent=2))
f.close() f.close()
except Exception as e: except Exception as e:
print("[ 错误 ]:保存 result.json 失败... 作品名: " + file_name +"\r\n") print("[ 错误 ]:保存 result.json 失败... 作品名: " + file_name + "\r\n")
desc = file_name[:30] desc = file_name[:30]
# 下载 视频 # 下载 视频
@ -958,7 +957,7 @@ class TikTok(object):
self.alltask.append( self.alltask.append(
self.pool.submit(self.progressBarDownload, url, video_path, "[ 视频 ]:" + desc)) self.pool.submit(self.progressBarDownload, url, video_path, "[ 视频 ]:" + desc))
except Exception as e: except Exception as e:
print("[ 警告 ]:视频下载失败,请重试... 作品名: " + file_name +"\r\n") print("[ 警告 ]:视频下载失败,请重试... 作品名: " + file_name + "\r\n")
# 下载 图集 # 下载 图集
if awemeDict["awemeType"] == 1: if awemeDict["awemeType"] == 1:
@ -978,7 +977,7 @@ class TikTok(object):
self.alltask.append( self.alltask.append(
self.pool.submit(self.progressBarDownload, url, image_path, "[ 图集 ]:" + desc)) self.pool.submit(self.progressBarDownload, url, image_path, "[ 图集 ]:" + desc))
except Exception as e: except Exception as e:
print("[ 警告 ]:图片下载失败,请重试... 作品名: " + file_name +"\r\n") print("[ 警告 ]:图片下载失败,请重试... 作品名: " + file_name + "\r\n")
# 下载 音乐 # 下载 音乐
if music: if music:
@ -999,7 +998,7 @@ class TikTok(object):
self.alltask.append( self.alltask.append(
self.pool.submit(self.progressBarDownload, url, music_path, "[ 原声 ]:" + desc)) self.pool.submit(self.progressBarDownload, url, music_path, "[ 原声 ]:" + desc))
except Exception as e: except Exception as e:
print("[ 警告 ]:音乐(原声)下载失败,请重试... 作品名: " + file_name +"\r\n") print("[ 警告 ]:音乐(原声)下载失败,请重试... 作品名: " + file_name + "\r\n")
# 下载 cover # 下载 cover
if cover and awemeDict["awemeType"] == 0: if cover and awemeDict["awemeType"] == 0:
@ -1019,7 +1018,7 @@ class TikTok(object):
self.alltask.append( self.alltask.append(
self.pool.submit(self.progressBarDownload, url, cover_path, "[ 封面 ]:" + desc)) self.pool.submit(self.progressBarDownload, url, cover_path, "[ 封面 ]:" + desc))
except Exception as e: except Exception as e:
print("[ 警告 ]:cover下载失败,请重试... 作品名: " + file_name +"\r\n") print("[ 警告 ]:cover下载失败,请重试... 作品名: " + file_name + "\r\n")
# 下载 avatar # 下载 avatar
if avatar: if avatar:
@ -1039,7 +1038,7 @@ class TikTok(object):
self.alltask.append( self.alltask.append(
self.pool.submit(self.progressBarDownload, url, avatar_path, "[ 头像 ]:" + desc)) self.pool.submit(self.progressBarDownload, url, avatar_path, "[ 头像 ]:" + desc))
except Exception as e: except Exception as e:
print("[ 警告 ]:avatar下载失败,请重试... 作品名: " + file_name +"\r\n") print("[ 警告 ]:avatar下载失败,请重试... 作品名: " + file_name + "\r\n")
except Exception as e: except Exception as e:
print("[ 错误 ]:下载作品时出错\r\n") print("[ 错误 ]:下载作品时出错\r\n")
@ -1093,8 +1092,8 @@ class TikTok(object):
# end = time.time() # 结束时间 # end = time.time() # 结束时间
# print('\n' + '[下载完成]:耗时: %d分钟%d秒\n' % (int((end - start) / 60), ((end - start) % 60))) # 输出下载用时时间 # print('\n' + '[下载完成]:耗时: %d分钟%d秒\n' % (int((end - start) / 60), ((end - start) % 60))) # 输出下载用时时间
def userDownload(self, awemeList: list, music=True, cover=True, avatar=True, resjson=True, savePath=os.getcwd(),
def userDownload(self, awemeList: list, music=True, cover=True, avatar=True, resjson=True, savePath=os.getcwd(), thread=5): thread=5):
if awemeList is None: if awemeList is None:
return return
if not os.path.exists(savePath): if not os.path.exists(savePath):
@ -1106,7 +1105,8 @@ class TikTok(object):
start = time.time() # 开始时间 start = time.time() # 开始时间
for aweme in awemeList: for aweme in awemeList:
self.awemeDownload(awemeDict=aweme, music=music, cover=cover, avatar=avatar, resjson=resjson, savePath=savePath) self.awemeDownload(awemeDict=aweme, music=music, cover=cover, avatar=avatar, resjson=resjson,
savePath=savePath)
# time.sleep(0.5) # time.sleep(0.5)
wait(self.alltask, return_when=ALL_COMPLETED) wait(self.alltask, return_when=ALL_COMPLETED)
@ -1116,7 +1116,8 @@ class TikTok(object):
self.isdwownload = True self.isdwownload = True
# 下载上一步失败的 # 下载上一步失败的
for aweme in awemeList: for aweme in awemeList:
self.awemeDownload(awemeDict=aweme, music=music, cover=cover, avatar=avatar, resjson=resjson, savePath=savePath) self.awemeDownload(awemeDict=aweme, music=music, cover=cover, avatar=avatar, resjson=resjson,
savePath=savePath)
# time.sleep(0.5) # time.sleep(0.5)
wait(self.alltask, return_when=ALL_COMPLETED) wait(self.alltask, return_when=ALL_COMPLETED)
@ -1126,5 +1127,6 @@ class TikTok(object):
end = time.time() # 结束时间 end = time.time() # 结束时间
print('\n' + '[下载完成]:耗时: %d分钟%d\n' % (int((end - start) / 60), ((end - start) % 60))) # 输出下载用时时间 print('\n' + '[下载完成]:耗时: %d分钟%d\n' % (int((end - start) / 60), ((end - start) % 60))) # 输出下载用时时间
if __name__ == "__main__": if __name__ == "__main__":
pass pass

View File

@ -42,6 +42,7 @@ configModel = {
} }
def argument(): def argument():
parser = argparse.ArgumentParser(description='抖音批量下载工具 使用帮助') parser = argparse.ArgumentParser(description='抖音批量下载工具 使用帮助')
parser.add_argument("--cmd", "-C", help="使用命令行(True)或者配置文件(False), 默认为False", parser.add_argument("--cmd", "-C", help="使用命令行(True)或者配置文件(False), 默认为False",
@ -50,7 +51,7 @@ def argument():
help="作品(视频或图集)、直播、合集、音乐集合、个人主页的分享链接或者电脑浏览器网址, 可以设置多个链接(删除文案, 保证只有URL, https://v.douyin.com/kcvMpuN/ 或者 https://www.douyin.com/开头的)", help="作品(视频或图集)、直播、合集、音乐集合、个人主页的分享链接或者电脑浏览器网址, 可以设置多个链接(删除文案, 保证只有URL, https://v.douyin.com/kcvMpuN/ 或者 https://www.douyin.com/开头的)",
type=str, required=False, default=[], action="append") type=str, required=False, default=[], action="append")
parser.add_argument("--path", "-p", help="下载保存位置, 默认当前文件位置", parser.add_argument("--path", "-p", help="下载保存位置, 默认当前文件位置",
type=str, required=False,default=os.getcwd()) type=str, required=False, default=os.getcwd())
parser.add_argument("--music", "-m", help="是否下载视频中的音乐(True/False), 默认为True", parser.add_argument("--music", "-m", help="是否下载视频中的音乐(True/False), 默认为True",
type=Utils().str2bool, required=False, default=True) type=Utils().str2bool, required=False, default=True)
parser.add_argument("--cover", "-c", help="是否下载视频的封面(True/False), 默认为True, 当下载视频时有效", parser.add_argument("--cover", "-c", help="是否下载视频的封面(True/False), 默认为True, 当下载视频时有效",
@ -82,12 +83,13 @@ def argument():
return args return args
def yamlConfig(): def yamlConfig():
curPath = os.path.dirname(os.path.realpath(sys.argv[0])) curPath = os.path.dirname(os.path.realpath(sys.argv[0]))
yamlPath = os.path.join(curPath, "config.yml") yamlPath = os.path.join(curPath, "config.yml")
f = open(yamlPath, 'r', encoding='utf-8') f = open(yamlPath, 'r', encoding='utf-8')
cfg = f.read() cfg = f.read()
configDict = yaml.load(stream=cfg,Loader=yaml.FullLoader) configDict = yaml.load(stream=cfg, Loader=yaml.FullLoader)
try: try:
if configDict["link"] != None: if configDict["link"] != None:
@ -159,7 +161,7 @@ def yamlConfig():
cookiekey = configDict["cookies"].keys() cookiekey = configDict["cookies"].keys()
cookieStr = "" cookieStr = ""
for i in cookiekey: for i in cookiekey:
cookieStr = cookieStr + i + "=" + configDict["cookies"][i] + "; " cookieStr = cookieStr + i + "=" + configDict["cookies"][i] + "; "
configModel["cookie"] = cookieStr configModel["cookie"] = cookieStr
except Exception as e: except Exception as e:
pass pass
@ -184,7 +186,7 @@ def main():
configModel["avatar"] = args.avatar configModel["avatar"] = args.avatar
configModel["json"] = args.json configModel["json"] = args.json
if args.mode == None or args.mode == []: if args.mode == None or args.mode == []:
args.mode=[] args.mode = []
args.mode.append("post") args.mode.append("post")
configModel["mode"] = list(set(args.mode)) configModel["mode"] = list(set(args.mode))
configModel["number"]["post"] = args.postnumber configModel["number"]["post"] = args.postnumber
@ -215,7 +217,7 @@ def main():
key_type, key = tk.getKey(url) key_type, key = tk.getKey(url)
if key_type == "user": if key_type == "user":
print("[ 提示 ]:正在请求用户主页下作品\r\n") print("[ 提示 ]:正在请求用户主页下作品\r\n")
userPath = os.path.join(configModel["path"], "user_"+key) userPath = os.path.join(configModel["path"], "user_" + key)
if not os.path.exists(userPath): if not os.path.exists(userPath):
os.mkdir(userPath) os.mkdir(userPath)
@ -242,13 +244,15 @@ def main():
modePath = os.path.join(userPath, mode) modePath = os.path.join(userPath, mode)
if not os.path.exists(modePath): if not os.path.exists(modePath):
os.mkdir(modePath) os.mkdir(modePath)
tk.userDownload(awemeList=datalist, music=configModel["music"], cover=configModel["cover"], tk.userDownload(awemeList=datalist, music=configModel["music"],
cover=configModel["cover"],
avatar=configModel["avatar"], resjson=configModel["json"], avatar=configModel["avatar"], resjson=configModel["json"],
savePath=os.path.join(modePath, mix_file_name), thread=configModel["thread"]) savePath=os.path.join(modePath, mix_file_name),
thread=configModel["thread"])
print(f'[ 提示 ]:合集 [{mixIdNameDict[mix_id]}] 中的作品下载完成\r\n') print(f'[ 提示 ]:合集 [{mixIdNameDict[mix_id]}] 中的作品下载完成\r\n')
elif key_type == "mix": elif key_type == "mix":
print("[ 提示 ]:正在请求单个合集下作品\r\n") print("[ 提示 ]:正在请求单个合集下作品\r\n")
datalist = tk.getMixInfo(key,35, configModel["number"]["mix"]) datalist = tk.getMixInfo(key, 35, configModel["number"]["mix"])
if datalist is not None and datalist != []: if datalist is not None and datalist != []:
mixPath = os.path.join(configModel["path"], "mix_" + key) mixPath = os.path.join(configModel["path"], "mix_" + key)
if not os.path.exists(mixPath): if not os.path.exists(mixPath):
@ -258,7 +262,7 @@ def main():
savePath=mixPath, thread=configModel["thread"]) savePath=mixPath, thread=configModel["thread"])
elif key_type == "music": elif key_type == "music":
print("[ 提示 ]:正在请求音乐(原声)下作品\r\n") print("[ 提示 ]:正在请求音乐(原声)下作品\r\n")
datalist = tk.getMusicInfo(key,35, configModel["number"]["music"]) datalist = tk.getMusicInfo(key, 35, configModel["number"]["music"])
if datalist is not None and datalist != []: if datalist is not None and datalist != []:
musicPath = os.path.join(configModel["path"], "music_" + key) musicPath = os.path.join(configModel["path"], "music_" + key)
if not os.path.exists(musicPath): if not os.path.exists(musicPath):
@ -281,19 +285,20 @@ def main():
elif key_type == "live": elif key_type == "live":
print("[ 提示 ]:正在进行直播解析\r\n") print("[ 提示 ]:正在进行直播解析\r\n")
live_json = tk.getLiveInfo(key) live_json = tk.getLiveInfo(key)
if configModel["json"]: if configModel["json"]:
livePath = os.path.join(configModel["path"], "live") livePath = os.path.join(configModel["path"], "live")
if not os.path.exists(livePath): if not os.path.exists(livePath):
os.mkdir(livePath) os.mkdir(livePath)
live_file_name = utils.replaceStr(key + live_json["nickname"]) live_file_name = utils.replaceStr(key + live_json["nickname"])
# 保存获取到json # 保存获取到json
print("[ 提示 ]:正在保存获取到的信息到result.json\r\n") print("[ 提示 ]:正在保存获取到的信息到result.json\r\n")
with open(os.path.join(livePath, live_file_name + ".json"), "w", encoding='utf-8') as f: with open(os.path.join(livePath, live_file_name + ".json"), "w", encoding='utf-8') as f:
f.write(json.dumps(live_json, ensure_ascii=False, indent=2)) f.write(json.dumps(live_json, ensure_ascii=False, indent=2))
f.close() f.close()
end = time.time() # 结束时间 end = time.time() # 结束时间
print('\n' + '[下载完成]:总耗时: %d分钟%d\n' % (int((end - start) / 60), ((end - start) % 60))) # 输出下载用时时间 print('\n' + '[下载完成]:总耗时: %d分钟%d\n' % (int((end - start) / 60), ((end - start) % 60))) # 输出下载用时时间
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@ -1,283 +1,284 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
''' '''
@Description:TikTok.py @Description:TikTok.py
@Date :2023/02/11 13:06:23 @Date :2023/02/11 13:06:23
@Author :imgyh @Author :imgyh
@version :1.0 @version :1.0
@Github :https://github.com/imgyh @Github :https://github.com/imgyh
@Mail :admin@imgyh.com @Mail :admin@imgyh.com
------------------------------------------------- -------------------------------------------------
Change Log : Change Log :
------------------------------------------------- -------------------------------------------------
''' '''
import time import time
import copy import copy
class Result(object):
def __init__(self): class Result(object):
# 作者信息 def __init__(self):
self.authorDict = { # 作者信息
"avatar_thumb": { self.authorDict = {
"height": "", "avatar_thumb": {
"uri": "", "height": "",
"url_list": [], "uri": "",
"width": "" "url_list": [],
}, "width": ""
"avatar": { },
"height": "", "avatar": {
"uri": "", "height": "",
"url_list": [], "uri": "",
"width": "" "url_list": [],
}, "width": ""
"cover_url": { },
"height": "", "cover_url": {
"uri": "", "height": "",
"url_list": [], "uri": "",
"width": "" "url_list": [],
}, "width": ""
# 喜欢的作品数 },
"favoriting_count": "", # 喜欢的作品数
# 粉丝数 "favoriting_count": "",
"follower_count": "", # 粉丝数
# 关注数 "follower_count": "",
"following_count": "", # 关注数
# 昵称 "following_count": "",
"nickname": "", # 昵称
# 是否允许下载 "nickname": "",
"prevent_download": "", # 是否允许下载
# 用户 url id "prevent_download": "",
"sec_uid": "", # 用户 url id
# 是否私密账号 "sec_uid": "",
"secret": "", # 是否私密账号
# 短id "secret": "",
"short_id": "", # 短id
# 签名 "short_id": "",
"signature": "", # 签名
# 总获赞数 "signature": "",
"total_favorited": "", # 总获赞数
# 用户id "total_favorited": "",
"uid": "", # 用户id
# 用户自定义唯一id 抖音号 "uid": "",
"unique_id": "", # 用户自定义唯一id 抖音号
# 年龄 "unique_id": "",
"user_age": "", # 年龄
"user_age": "",
}
# 图片信息 }
self.picDict = { # 图片信息
"height": "", self.picDict = {
"mask_url_list": "", "height": "",
"uri": "", "mask_url_list": "",
"url_list": [], "uri": "",
"width": "" "url_list": [],
} "width": ""
# 音乐信息 }
self.musicDict = { # 音乐信息
"cover_hd": { self.musicDict = {
"height": "", "cover_hd": {
"uri": "", "height": "",
"url_list": [], "uri": "",
"width": "" "url_list": [],
}, "width": ""
"cover_large": { },
"height": "", "cover_large": {
"uri": "", "height": "",
"url_list": [], "uri": "",
"width": "" "url_list": [],
}, "width": ""
"cover_medium": { },
"height": "", "cover_medium": {
"uri": "", "height": "",
"url_list": [], "uri": "",
"width": "" "url_list": [],
}, "width": ""
"cover_thumb": { },
"height": "", "cover_thumb": {
"uri": "", "height": "",
"url_list": [], "uri": "",
"width": "" "url_list": [],
}, "width": ""
# 音乐作者抖音号 },
"owner_handle": "", # 音乐作者抖音号
# 音乐作者id "owner_handle": "",
"owner_id": "", # 音乐作者id
# 音乐作者昵称 "owner_id": "",
"owner_nickname": "", # 音乐作者昵称
"play_url": { "owner_nickname": "",
"height": "", "play_url": {
"uri": "", "height": "",
"url_key": "", "uri": "",
"url_list": [], "url_key": "",
"width": "" "url_list": [],
}, "width": ""
# 音乐名字 },
"title": "", # 音乐名字
} "title": "",
# 视频信息 }
self.videoDict = { # 视频信息
"play_addr": { self.videoDict = {
"uri": "", "play_addr": {
"url_list": "", "uri": "",
}, "url_list": "",
"cover_original_scale": { },
"height": "", "cover_original_scale": {
"uri": "", "height": "",
"url_list": [], "uri": "",
"width": "" "url_list": [],
}, "width": ""
"dynamic_cover": { },
"height": "", "dynamic_cover": {
"uri": "", "height": "",
"url_list": [], "uri": "",
"width": "" "url_list": [],
}, "width": ""
"origin_cover": { },
"height": "", "origin_cover": {
"uri": "", "height": "",
"url_list": [], "uri": "",
"width": "" "url_list": [],
}, "width": ""
"cover": { },
"height": "", "cover": {
"uri": "", "height": "",
"url_list": [], "uri": "",
"width": "" "url_list": [],
} "width": ""
} }
# 作品信息 }
self.awemeDict = { # 作品信息
# 作品创建时间 self.awemeDict = {
"create_time": "", # 作品创建时间
# awemeType=0 视频, awemeType=1 图集, awemeType=2 直播 "create_time": "",
"awemeType": "", # awemeType=0 视频, awemeType=1 图集, awemeType=2 直播
# 作品 id "awemeType": "",
"aweme_id": "", # 作品 id
# 作者信息 "aweme_id": "",
"author": self.authorDict, # 作者信息
# 作品描述 "author": self.authorDict,
"desc": "", # 作品描述
# 图片 "desc": "",
"images": [], # 图片
# 音乐 "images": [],
"music": self.musicDict, # 音乐
# 视频 "music": self.musicDict,
"video": self.videoDict, # 视频
# 作品信息统计 "video": self.videoDict,
"statistics": { # 作品信息统计
"admire_count": "", "statistics": {
"collect_count": "", "admire_count": "",
"comment_count": "", "collect_count": "",
"digg_count": "", "comment_count": "",
"play_count": "", "digg_count": "",
"share_count": "" "play_count": "",
} "share_count": ""
} }
# 用户作品信息 }
self.awemeList = [] # 用户作品信息
# 直播信息 self.awemeList = []
self.liveDict = { # 直播信息
# awemeType=0 视频, awemeType=1 图集, awemeType=2 直播 self.liveDict = {
"awemeType": "", # awemeType=0 视频, awemeType=1 图集, awemeType=2 直播
# 是否在播 "awemeType": "",
"status": "", # 是否在播
# 直播标题 "status": "",
"title": "", # 直播标题
# 直播cover "title": "",
"cover": "", # 直播cover
# 头像 "cover": "",
"avatar": "", # 头像
# 观看人数 "avatar": "",
"user_count": "", # 观看人数
# 昵称 "user_count": "",
"nickname": "", # 昵称
# sec_uid "nickname": "",
"sec_uid": "", # sec_uid
# 直播间观看状态 "sec_uid": "",
"display_long": "", # 直播间观看状态
# 推流 "display_long": "",
"flv_pull_url": "", # 推流
# 分区 "flv_pull_url": "",
"partition": "", # 分区
"sub_partition": "", "partition": "",
# 最清晰的地址 "sub_partition": "",
"flv_pull_url0": "", # 最清晰的地址
} "flv_pull_url0": "",
}
# 将得到的json数据dataRaw精简成自己定义的数据dataNew
# 转换得到的数据 # 将得到的json数据dataRaw精简成自己定义的数据dataNew
def dataConvert(self, awemeType, dataNew, dataRaw): # 转换得到的数据
for item in dataNew: def dataConvert(self, awemeType, dataNew, dataRaw):
try: for item in dataNew:
# 作品创建时间 try:
if item == "create_time": # 作品创建时间
dataNew['create_time'] = time.strftime( if item == "create_time":
"%Y-%m-%d %H.%M.%S", time.localtime(dataRaw['create_time'])) dataNew['create_time'] = time.strftime(
continue "%Y-%m-%d %H.%M.%S", time.localtime(dataRaw['create_time']))
# 设置 awemeType continue
if item == "awemeType": # 设置 awemeType
dataNew["awemeType"] = awemeType if item == "awemeType":
continue dataNew["awemeType"] = awemeType
# 当 解析的链接 是图片时 continue
if item == "images": # 当 解析的链接 是图片时
if awemeType == 1: if item == "images":
for image in dataRaw[item]: if awemeType == 1:
for i in image: for image in dataRaw[item]:
self.picDict[i] = image[i] for i in image:
# 字典要深拷贝 self.picDict[i] = image[i]
self.awemeDict["images"].append(copy.deepcopy(self.picDict)) # 字典要深拷贝
continue self.awemeDict["images"].append(copy.deepcopy(self.picDict))
# 当 解析的链接 是视频时 continue
if item == "video": # 当 解析的链接 是视频时
if awemeType == 0: if item == "video":
self.dataConvert(awemeType, dataNew[item], dataRaw[item]) if awemeType == 0:
continue self.dataConvert(awemeType, dataNew[item], dataRaw[item])
# 将小头像放大 continue
if item == "avatar": # 将小头像放大
for i in dataNew[item]: if item == "avatar":
if i == "url_list": for i in dataNew[item]:
for j in self.awemeDict["author"]["avatar_thumb"]["url_list"]: if i == "url_list":
dataNew[item][i].append(j.replace("100x100", "1080x1080")) for j in self.awemeDict["author"]["avatar_thumb"]["url_list"]:
elif i == "uri": dataNew[item][i].append(j.replace("100x100", "1080x1080"))
dataNew[item][i] = self.awemeDict["author"]["avatar_thumb"][i].replace("100x100", elif i == "uri":
"1080x1080") dataNew[item][i] = self.awemeDict["author"]["avatar_thumb"][i].replace("100x100",
else: "1080x1080")
dataNew[item][i] = self.awemeDict["author"]["avatar_thumb"][i] else:
continue dataNew[item][i] = self.awemeDict["author"]["avatar_thumb"][i]
continue
# 原来的json是[{}] 而我们的是 {}
if item == "cover_url": # 原来的json是[{}] 而我们的是 {}
self.dataConvert(awemeType, dataNew[item], dataRaw[item][0]) if item == "cover_url":
continue self.dataConvert(awemeType, dataNew[item], dataRaw[item][0])
continue
# 根据 uri 获取 1080p 视频
if item == "play_addr": # 根据 uri 获取 1080p 视频
dataNew[item]["uri"] = dataRaw["bit_rate"][0]["play_addr"]["uri"] if item == "play_addr":
# 使用 这个api 可以获得1080p dataNew[item]["uri"] = dataRaw["bit_rate"][0]["play_addr"]["uri"]
# dataNew[item]["url_list"] = "https://aweme.snssdk.com/aweme/v1/play/?video_id=%s&ratio=1080p&line=0" \ # 使用 这个api 可以获得1080p
# % dataNew[item]["uri"] # dataNew[item]["url_list"] = "https://aweme.snssdk.com/aweme/v1/play/?video_id=%s&ratio=1080p&line=0" \
dataNew[item]["url_list"] = dataRaw["bit_rate"][0]["play_addr"]["url_list"][0] # % dataNew[item]["uri"]
continue dataNew[item]["url_list"] = dataRaw["bit_rate"][0]["play_addr"]["url_list"][0]
continue
# 常规 递归遍历 字典
if isinstance(dataNew[item], dict): # 常规 递归遍历 字典
self.dataConvert(awemeType, dataNew[item], dataRaw[item]) if isinstance(dataNew[item], dict):
else: self.dataConvert(awemeType, dataNew[item], dataRaw[item])
# 赋值 else:
dataNew[item] = dataRaw[item] # 赋值
except Exception as e: dataNew[item] = dataRaw[item]
# 删除这个警告, 总是让人误会出错了 except Exception as e:
# print("[ 警告 ]:转换数据时在接口中未找到 %s\r" % (item)) # 删除这个警告, 总是让人误会出错了
pass # print("[ 警告 ]:转换数据时在接口中未找到 %s\r" % (item))
pass
def clearDict(self, data):
for item in data: def clearDict(self, data):
# 常规 递归遍历 字典 for item in data:
if isinstance(data[item], dict): # 常规 递归遍历 字典
self.clearDict(data[item]) if isinstance(data[item], dict):
elif isinstance(data[item], list): self.clearDict(data[item])
data[item] = [] elif isinstance(data[item], list):
else: data[item] = []
data[item] = "" else:
data[item] = ""

View File

@ -1,101 +1,109 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
''' '''
@Description:TikTok.py @Description:TikTok.py
@Date :2023/02/11 13:06:23 @Date :2023/02/11 13:06:23
@Author :imgyh @Author :imgyh
@version :1.0 @version :1.0
@Github :https://github.com/imgyh @Github :https://github.com/imgyh
@Mail :admin@imgyh.com @Mail :admin@imgyh.com
------------------------------------------------- -------------------------------------------------
Change Log : Change Log :
------------------------------------------------- -------------------------------------------------
''' '''
import TikTokUtils import TikTokUtils
from TikTok import TikTok from TikTok import TikTok
def getAwemeInfo():
share_link_video = "3.56 uSy:/ 复制打开抖音,看看【小透明的作品】没有女朋友就用我的吧哈哈哈哈 # 表情包锁屏 https://v.douyin.com/BugmVVD/" def getAwemeInfo():
share_link_pic = "8.20 MJI:/ 复制打开抖音,看看【舍溪的图文作品】我又来放图集啦~还有你们要的小可爱大图也放啦~# ... https://v.douyin.com/BugrFTN/" share_link_video = "3.56 uSy:/ 复制打开抖音,看看【小透明的作品】没有女朋友就用我的吧哈哈哈哈 # 表情包锁屏 https://v.douyin.com/BugmVVD/"
tk = TikTok() share_link_pic = "8.20 MJI:/ 复制打开抖音,看看【舍溪的图文作品】我又来放图集啦~还有你们要的小可爱大图也放啦~# ... https://v.douyin.com/BugrFTN/"
tk = TikTok()
url = tk.getShareLink(share_link_pic)
key_type, key = tk.getKey(url) url = tk.getShareLink(share_link_pic)
datanew, dataraw = tk.getAwemeInfo(key) key_type, key = tk.getKey(url)
print(datanew) datanew, dataraw = tk.getAwemeInfo(key)
print(datanew)
def getUserInfo():
share_link_post = "1- 长按复制此条消息打开抖音搜索查看TA的更多作品。 https://v.douyin.com/BupCppt/"
share_link_like = "2- 长按复制此条消息打开抖音搜索查看TA的更多作品。 https://v.douyin.com/BusJrfr/" def getUserInfo():
tk = TikTok() share_link_post = "1- 长按复制此条消息打开抖音搜索查看TA的更多作品。 https://v.douyin.com/BupCppt/"
share_link_like = "2- 长按复制此条消息打开抖音搜索查看TA的更多作品。 https://v.douyin.com/BusJrfr/"
url = tk.getShareLink(share_link_like) tk = TikTok()
key_type, key = tk.getKey(url)
awemeList = tk.getUserInfo(key, mode="like", count=35) url = tk.getShareLink(share_link_like)
print(awemeList) key_type, key = tk.getKey(url)
awemeList = tk.getUserInfo(key, mode="like", count=35)
def getLiveInfo(): print(awemeList)
live_link = "https://live.douyin.com/40768897856"
tk = TikTok()
def getLiveInfo():
url = tk.getShareLink(live_link) live_link = "https://live.douyin.com/40768897856"
key_type, key = tk.getKey(url) tk = TikTok()
live_json = tk.getLiveInfo(key)
print(live_json) url = tk.getShareLink(live_link)
key_type, key = tk.getKey(url)
def getMixInfo(): live_json = tk.getLiveInfo(key)
mix_link = 'https://v.douyin.com/B3J63Le/' print(live_json)
tk = TikTok()
url = tk.getShareLink(mix_link) def getMixInfo():
key_type, key = tk.getKey(url) mix_link = 'https://v.douyin.com/B3J63Le/'
awemeList = tk.getMixInfo(key, count=35) tk = TikTok()
print(len(awemeList))
url = tk.getShareLink(mix_link)
def getUserAllMixInfo(): key_type, key = tk.getKey(url)
user_all_mix_link = 'https://v.douyin.com/B38oovu/' awemeList = tk.getMixInfo(key, count=35)
tk = TikTok() print(len(awemeList))
url = tk.getShareLink(user_all_mix_link)
key_type, key = tk.getKey(url) def getUserAllMixInfo():
mixIdNameDict = tk.getUserAllMixInfo(key, count=35) user_all_mix_link = 'https://v.douyin.com/B38oovu/'
print(mixIdNameDict) tk = TikTok()
def getMusicInfo(): url = tk.getShareLink(user_all_mix_link)
music_link = 'https://v.douyin.com/S6YMNXs/' key_type, key = tk.getKey(url)
tk = TikTok() mixIdNameDict = tk.getUserAllMixInfo(key, count=35)
print(mixIdNameDict)
url = tk.getShareLink(music_link)
key_type, key = tk.getKey(url)
awemeList = tk.getMusicInfo(key,count=35) def getMusicInfo():
print(len(awemeList)) music_link = 'https://v.douyin.com/S6YMNXs/'
tk = TikTok()
def test():
utils=TikTokUtils.Utils() url = tk.getShareLink(music_link)
user_all_mix_link = 'https://www.douyin.com/aweme/v1/web/aweme/favorite/?'+\ key_type, key = tk.getKey(url)
utils.getXbogus(url='device_platform=webapp&aid=6383&sec_user_id=MS4wLjABAAAAjQn6ONfaGgUpk0Q1ep8dPiD3W4T_lxTJmemfy3MTJ64&max_cursor=1676441180000&count=10') awemeList = tk.getMusicInfo(key, count=35)
headers = { print(len(awemeList))
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36',
'referer': 'https://www.douyin.com/',
'accept-encoding': None, def test():
'Cookie': 'ttwid=1|oLudm-Hi5ikxQQhmAnv4Km4LjwwvLa4Qk_JGrKffuYU|1681878460|b0f581d97797bb67d2260bf92d65e8808b90713afc08bf5d8100f571fd70a275; passport_csrf_token=570d671dcd8d8598fcde4c3f7c99664d; passport_csrf_token_default=570d671dcd8d8598fcde4c3f7c99664d; s_v_web_id=verify_lgn70gzw_sAVtjJdD_Clyk_43UL_8M9L_6JiTtPC2TyU0; n_mh=vrLGYVtwqutbPLOGNTDUGahwaD9AyYjn4iVvAO2Xt0s; sso_uid_tt=92e014dfb6653bdc319ecc6a6ceea870; sso_uid_tt_ss=92e014dfb6653bdc319ecc6a6ceea870; toutiao_sso_user=d0bd8d5cc0f75420799903572941ce83; toutiao_sso_user_ss=d0bd8d5cc0f75420799903572941ce83; passport_auth_status=5c576b088f4a19448eb4efd7aaeb7c5e,; passport_auth_status_ss=5c576b088f4a19448eb4efd7aaeb7c5e,; uid_tt=564215aecc985785d033c9e4c9d00fc4; uid_tt_ss=564215aecc985785d033c9e4c9d00fc4; sid_tt=f14390d924c81856fde84b1cd534bf09; sessionid=f14390d924c81856fde84b1cd534bf09; sessionid_ss=f14390d924c81856fde84b1cd534bf09; odin_tt=0aa1c100d17bf3541dce9e8dd62c6353302b8abaaa0a5998d5c437313feb245d8f7b6391a69772e4afa7d3c718878837; passport_assist_user=CjwC3fi9JyApG4DN-HiFI6n5FcGAdzNDT29nR-nSDJYAVfqh2BYI77qdTN2GRfgagkYLRi1wxNp1akHBwGcaSAo8XDkNT-UWnkFmc_0eXMYCb8OIh4G_YnH8pylwwdfS-7PCTekX3trj0JyENUVWLWFxnKuG5HhSS4FB2CtxEJ7yrg0Yia_WVCIBA9bY7IU=; sid_ucp_sso_v1=1.0.0-KDc0NmY3NzA5ZWFhNzI2YzMyOWMxMDMwNjAwMDg3YzRjYzg3ZjlhODcKHQjG-7zT9QEQ5dv9oQYY7zEgDDCL_7nMBTgGQPQHGgJscSIgZDBiZDhkNWNjMGY3NTQyMDc5OTkwMzU3Mjk0MWNlODM; ssid_ucp_sso_v1=1.0.0-KDc0NmY3NzA5ZWFhNzI2YzMyOWMxMDMwNjAwMDg3YzRjYzg3ZjlhODcKHQjG-7zT9QEQ5dv9oQYY7zEgDDCL_7nMBTgGQPQHGgJscSIgZDBiZDhkNWNjMGY3NTQyMDc5OTkwMzU3Mjk0MWNlODM; publish_badge_show_info="0,0,0,1681878505305"; LOGIN_STATUS=1; store-region=cn-sc; store-region-src=uid; sid_guard=f14390d924c81856fde84b1cd534bf09|1681878504|5183998|Sun,+18-Jun-2023+04:28:22+GMT; sid_ucp_v1=1.0.0-KGY5OGQ3OTQ4YmVkYzczODgzYzM0MmJmOWYxMzhkMDliMTc5NGI3NjMKGQjG-7zT9QEQ6Nv9oQYY7zEgDDgGQPQHSAQaAmxmIiBmMTQzOTBkOTI0YzgxODU2ZmRlODRiMWNkNTM0YmYwOQ; ssid_ucp_v1=1.0.0-KGY5OGQ3OTQ4YmVkYzczODgzYzM0MmJmOWYxMzhkMDliMTc5NGI3NjMKGQjG-7zT9QEQ6Nv9oQYY7zEgDDgGQPQHSAQaAmxmIiBmMTQzOTBkOTI0YzgxODU2ZmRlODRiMWNkNTM0YmYwOQ; download_guide="3/20230419"; pwa2="3|0"; FOLLOW_NUMBER_YELLOW_POINT_INFO="MS4wLjABAAAA-jD2lukp--I21BF8VQsmYUqJDbj3FmU-kGQTHl2y1Cw/1681920000000/0/0/1681900423333"; __ac_nonce=0644234d60042cacebbb6; __ac_signature=_02B4Z6wo00f01eZN0dAAAIDAhUcRuQz7dNHmbdVAAB3TRUC7i8VqXQejR8jv-D8UggBx1MBLH564PE.cCZf00m7Cw640CNUvcc5jJfhgn8u5FhvVndykvwbQb.HEpSfGN-8eqql7GpuGZJ8f1d; strategyABtestKey="1682060514.261"; passport_fe_beating_status=true; csrf_session_id=405b0ec4f6338a5d893a5f14f6fd1a64; bd_ticket_guard_client_data=eyJiZC10aWNrZXQtZ3VhcmQtdmVyc2lvbiI6MiwiYmQtdGlja2V0LWd1YXJkLWl0ZXJhdGlvbi12ZXJzaW9uIjoxLCJiZC10aWNrZXQtZ3VhcmQtY2xpZW50LWNlcnQiOiItLS0tLUJFR0lOIENFUlRJRklDQVRFLS0tLS1cbk1JSUNGRENDQWJxZ0F3SUJBZ0lVR1ljQ3FuQUh0UUJBZm5WWkYxQW84cUtjY2Zrd0NnWUlLb1pJemowRUF3SXdcbk1URUxNQWtHQTFVRUJoTUNRMDR4SWpBZ0JnTlZCQU1NR1hScFkydGxkRjluZFdGeVpGOWpZVjlsWTJSellWOHlcbk5UWXdIaGNOTWpNd016STNNRE15T0RBeldoY05Nek13TXpJM01URXlPREF6V2pBbk1Rc3dDUVlEVlFRR0V3SkRcblRqRVlNQllHQTFVRUF3d1BZbVJmZEdsamEyVjBYMmQxWVhKa01Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERcbkFRY0RRZ0FFNmZ1Z3d0MEJnZUh5akVub1FvNWtXUS9qc2daTTV1YXBiNTQ4KytTV0dRSjMwb2lSTHNtYlVFSUZcblFIYzh3UEthZzZmdXNPTm91WncxOEdNYm5vTStwNk9CdVRDQnRqQU9CZ05WSFE4QkFmOEVCQU1DQmFBd01RWURcblZSMGxCQ293S0FZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQ0JnZ3JCZ0VGQlFjREF3WUlLd1lCQlFVSEF3UXdcbktRWURWUjBPQkNJRUlPV3Y3d01ZUGhoeUNPL2ZwenJGNDJNeEQ4ZGIzN0YyTDgxaW8zVTVlVFpaTUNzR0ExVWRcbkl3UWtNQ0tBSURLbForcU9aRWdTamN4T1RVQjdjeFNiUjIxVGVxVFJnTmQ1bEpkN0lrZURNQmtHQTFVZEVRUVNcbk1CQ0NEbmQzZHk1a2IzVjVhVzR1WTI5dE1Bb0dDQ3FHU000OUJBTUNBMGdBTUVVQ0lCbm9xRDBRbVdUSlNLOVNcbkFZRjJ6YkljYzBsZjFRMDVTUGxXMURDQ0FMVUpBaUVBazJFRWpKdkdFRnl0YzBWbXRoRTA5bFpGeFFkUmlGN21cbk9pdE5IZzBOZUJrPVxuLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLVxuIn0=; msToken=qSfWwAdwksuBS5wmQAvpzUsf2ovkFFKefOLSvZDKA1Z_FX7ith-wCknpVQB08kft4ISWp00GHeQBaPwV9tcWJq6xBC-mPnQKNjBVINeOQGvFtSdsfacWMtWpa8x1RJE=; FOLLOW_LIVE_POINT_INFO="MS4wLjABAAAA-jD2lukp--I21BF8VQsmYUqJDbj3FmU-kGQTHl2y1Cw/1682092800000/0/0/1682061497980"; msToken=jDwC5gjTRXV6hrFvGMG-AkOBXHGrt_Mp5NaltB1upOUm0aQnZ7sy7qlSEn2tQbGHShp2X7ayNMDQQlPekSTV9MkxBv56LR9zepTlYNOoqbH_RdjDvbl-MZDrmui3OEE=; tt_scid=hERl4ibL-B89BGXb9wUfsmVkQh-G2dvL2wI0QvEDYLooFsvwoz.q6J4ZA0WErn0Tb9fb; home_can_add_dy_2_desktop="1"' } utils = TikTokUtils.Utils()
user_all_mix_link = 'https://www.douyin.com/aweme/v1/web/aweme/favorite/?' + \
import requests utils.getXbogus(
url='device_platform=webapp&aid=6383&sec_user_id=MS4wLjABAAAAjQn6ONfaGgUpk0Q1ep8dPiD3W4T_lxTJmemfy3MTJ64&max_cursor=1676441180000&count=10')
res = requests.get(user_all_mix_link,headers=headers).text headers = {
import json 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36',
datadict = json.loads(res) 'referer': 'https://www.douyin.com/',
print(datadict["aweme_list"][0]["video"]["bit_rate"]) 'accept-encoding': None,
print(len(datadict["aweme_list"][0]["video"]["bit_rate"])) 'Cookie': 'ttwid=1|oLudm-Hi5ikxQQhmAnv4Km4LjwwvLa4Qk_JGrKffuYU|1681878460|b0f581d97797bb67d2260bf92d65e8808b90713afc08bf5d8100f571fd70a275; passport_csrf_token=570d671dcd8d8598fcde4c3f7c99664d; passport_csrf_token_default=570d671dcd8d8598fcde4c3f7c99664d; s_v_web_id=verify_lgn70gzw_sAVtjJdD_Clyk_43UL_8M9L_6JiTtPC2TyU0; n_mh=vrLGYVtwqutbPLOGNTDUGahwaD9AyYjn4iVvAO2Xt0s; sso_uid_tt=92e014dfb6653bdc319ecc6a6ceea870; sso_uid_tt_ss=92e014dfb6653bdc319ecc6a6ceea870; toutiao_sso_user=d0bd8d5cc0f75420799903572941ce83; toutiao_sso_user_ss=d0bd8d5cc0f75420799903572941ce83; passport_auth_status=5c576b088f4a19448eb4efd7aaeb7c5e,; passport_auth_status_ss=5c576b088f4a19448eb4efd7aaeb7c5e,; uid_tt=564215aecc985785d033c9e4c9d00fc4; uid_tt_ss=564215aecc985785d033c9e4c9d00fc4; sid_tt=f14390d924c81856fde84b1cd534bf09; sessionid=f14390d924c81856fde84b1cd534bf09; sessionid_ss=f14390d924c81856fde84b1cd534bf09; odin_tt=0aa1c100d17bf3541dce9e8dd62c6353302b8abaaa0a5998d5c437313feb245d8f7b6391a69772e4afa7d3c718878837; passport_assist_user=CjwC3fi9JyApG4DN-HiFI6n5FcGAdzNDT29nR-nSDJYAVfqh2BYI77qdTN2GRfgagkYLRi1wxNp1akHBwGcaSAo8XDkNT-UWnkFmc_0eXMYCb8OIh4G_YnH8pylwwdfS-7PCTekX3trj0JyENUVWLWFxnKuG5HhSS4FB2CtxEJ7yrg0Yia_WVCIBA9bY7IU=; sid_ucp_sso_v1=1.0.0-KDc0NmY3NzA5ZWFhNzI2YzMyOWMxMDMwNjAwMDg3YzRjYzg3ZjlhODcKHQjG-7zT9QEQ5dv9oQYY7zEgDDCL_7nMBTgGQPQHGgJscSIgZDBiZDhkNWNjMGY3NTQyMDc5OTkwMzU3Mjk0MWNlODM; ssid_ucp_sso_v1=1.0.0-KDc0NmY3NzA5ZWFhNzI2YzMyOWMxMDMwNjAwMDg3YzRjYzg3ZjlhODcKHQjG-7zT9QEQ5dv9oQYY7zEgDDCL_7nMBTgGQPQHGgJscSIgZDBiZDhkNWNjMGY3NTQyMDc5OTkwMzU3Mjk0MWNlODM; publish_badge_show_info="0,0,0,1681878505305"; LOGIN_STATUS=1; store-region=cn-sc; store-region-src=uid; sid_guard=f14390d924c81856fde84b1cd534bf09|1681878504|5183998|Sun,+18-Jun-2023+04:28:22+GMT; sid_ucp_v1=1.0.0-KGY5OGQ3OTQ4YmVkYzczODgzYzM0MmJmOWYxMzhkMDliMTc5NGI3NjMKGQjG-7zT9QEQ6Nv9oQYY7zEgDDgGQPQHSAQaAmxmIiBmMTQzOTBkOTI0YzgxODU2ZmRlODRiMWNkNTM0YmYwOQ; ssid_ucp_v1=1.0.0-KGY5OGQ3OTQ4YmVkYzczODgzYzM0MmJmOWYxMzhkMDliMTc5NGI3NjMKGQjG-7zT9QEQ6Nv9oQYY7zEgDDgGQPQHSAQaAmxmIiBmMTQzOTBkOTI0YzgxODU2ZmRlODRiMWNkNTM0YmYwOQ; download_guide="3/20230419"; pwa2="3|0"; FOLLOW_NUMBER_YELLOW_POINT_INFO="MS4wLjABAAAA-jD2lukp--I21BF8VQsmYUqJDbj3FmU-kGQTHl2y1Cw/1681920000000/0/0/1681900423333"; __ac_nonce=0644234d60042cacebbb6; __ac_signature=_02B4Z6wo00f01eZN0dAAAIDAhUcRuQz7dNHmbdVAAB3TRUC7i8VqXQejR8jv-D8UggBx1MBLH564PE.cCZf00m7Cw640CNUvcc5jJfhgn8u5FhvVndykvwbQb.HEpSfGN-8eqql7GpuGZJ8f1d; strategyABtestKey="1682060514.261"; passport_fe_beating_status=true; csrf_session_id=405b0ec4f6338a5d893a5f14f6fd1a64; bd_ticket_guard_client_data=eyJiZC10aWNrZXQtZ3VhcmQtdmVyc2lvbiI6MiwiYmQtdGlja2V0LWd1YXJkLWl0ZXJhdGlvbi12ZXJzaW9uIjoxLCJiZC10aWNrZXQtZ3VhcmQtY2xpZW50LWNlcnQiOiItLS0tLUJFR0lOIENFUlRJRklDQVRFLS0tLS1cbk1JSUNGRENDQWJxZ0F3SUJBZ0lVR1ljQ3FuQUh0UUJBZm5WWkYxQW84cUtjY2Zrd0NnWUlLb1pJemowRUF3SXdcbk1URUxNQWtHQTFVRUJoTUNRMDR4SWpBZ0JnTlZCQU1NR1hScFkydGxkRjluZFdGeVpGOWpZVjlsWTJSellWOHlcbk5UWXdIaGNOTWpNd016STNNRE15T0RBeldoY05Nek13TXpJM01URXlPREF6V2pBbk1Rc3dDUVlEVlFRR0V3SkRcblRqRVlNQllHQTFVRUF3d1BZbVJmZEdsamEyVjBYMmQxWVhKa01Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERcbkFRY0RRZ0FFNmZ1Z3d0MEJnZUh5akVub1FvNWtXUS9qc2daTTV1YXBiNTQ4KytTV0dRSjMwb2lSTHNtYlVFSUZcblFIYzh3UEthZzZmdXNPTm91WncxOEdNYm5vTStwNk9CdVRDQnRqQU9CZ05WSFE4QkFmOEVCQU1DQmFBd01RWURcblZSMGxCQ293S0FZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQ0JnZ3JCZ0VGQlFjREF3WUlLd1lCQlFVSEF3UXdcbktRWURWUjBPQkNJRUlPV3Y3d01ZUGhoeUNPL2ZwenJGNDJNeEQ4ZGIzN0YyTDgxaW8zVTVlVFpaTUNzR0ExVWRcbkl3UWtNQ0tBSURLbForcU9aRWdTamN4T1RVQjdjeFNiUjIxVGVxVFJnTmQ1bEpkN0lrZURNQmtHQTFVZEVRUVNcbk1CQ0NEbmQzZHk1a2IzVjVhVzR1WTI5dE1Bb0dDQ3FHU000OUJBTUNBMGdBTUVVQ0lCbm9xRDBRbVdUSlNLOVNcbkFZRjJ6YkljYzBsZjFRMDVTUGxXMURDQ0FMVUpBaUVBazJFRWpKdkdFRnl0YzBWbXRoRTA5bFpGeFFkUmlGN21cbk9pdE5IZzBOZUJrPVxuLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLVxuIn0=; msToken=qSfWwAdwksuBS5wmQAvpzUsf2ovkFFKefOLSvZDKA1Z_FX7ith-wCknpVQB08kft4ISWp00GHeQBaPwV9tcWJq6xBC-mPnQKNjBVINeOQGvFtSdsfacWMtWpa8x1RJE=; FOLLOW_LIVE_POINT_INFO="MS4wLjABAAAA-jD2lukp--I21BF8VQsmYUqJDbj3FmU-kGQTHl2y1Cw/1682092800000/0/0/1682061497980"; msToken=jDwC5gjTRXV6hrFvGMG-AkOBXHGrt_Mp5NaltB1upOUm0aQnZ7sy7qlSEn2tQbGHShp2X7ayNMDQQlPekSTV9MkxBv56LR9zepTlYNOoqbH_RdjDvbl-MZDrmui3OEE=; tt_scid=hERl4ibL-B89BGXb9wUfsmVkQh-G2dvL2wI0QvEDYLooFsvwoz.q6J4ZA0WErn0Tb9fb; home_can_add_dy_2_desktop="1"'}
if __name__ == "__main__": import requests
# test()
# getMusicInfo() res = requests.get(user_all_mix_link, headers=headers).text
# getUserAllMixInfo() import json
# getMixInfo() datadict = json.loads(res)
# getAwemeInfo() print(datadict["aweme_list"][0]["video"]["bit_rate"])
# getUserInfo() print(len(datadict["aweme_list"][0]["video"]["bit_rate"]))
# getLiveInfo()
pass
if __name__ == "__main__":
# test()
# getMusicInfo()
# getUserAllMixInfo()
# getMixInfo()
# getAwemeInfo()
# getUserInfo()
# getLiveInfo()
pass

View File

@ -1,89 +1,90 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
''' '''
@Description:TikTok.py @Description:TikTok.py
@Date :2023/02/11 13:06:23 @Date :2023/02/11 13:06:23
@Author :imgyh @Author :imgyh
@version :1.0 @version :1.0
@Github :https://github.com/imgyh @Github :https://github.com/imgyh
@Mail :admin@imgyh.com @Mail :admin@imgyh.com
------------------------------------------------- -------------------------------------------------
Change Log : Change Log :
------------------------------------------------- -------------------------------------------------
''' '''
class Urls(object): class Urls(object):
def __init__(self): def __init__(self):
# https://langyue.cc/APIdocV1.0.html # https://langyue.cc/APIdocV1.0.html
######################################### WEB ######################################### ######################################### WEB #########################################
# 首页推荐 # 首页推荐
self.TAB_FEED = 'https://www.douyin.com/aweme/v1/web/tab/feed/?' self.TAB_FEED = 'https://www.douyin.com/aweme/v1/web/tab/feed/?'
# 用户短信息给多少个用户secid就返回多少的用户信息 # 用户短信息给多少个用户secid就返回多少的用户信息
self.USER_SHORT_INFO = 'https://www.douyin.com/aweme/v1/web/im/user/info/?' self.USER_SHORT_INFO = 'https://www.douyin.com/aweme/v1/web/im/user/info/?'
# 用户详细信息 # 用户详细信息
self.USER_DETAIL = 'https://www.douyin.com/aweme/v1/web/user/profile/other/?' self.USER_DETAIL = 'https://www.douyin.com/aweme/v1/web/user/profile/other/?'
# 用户作品 # 用户作品
# cookies 暂时只需要 __ac_signature, s_v_web_id两个参数, 好像会过期 # cookies 暂时只需要 __ac_signature, s_v_web_id两个参数, 好像会过期
# url 暂时不需要携带 msToken, X-Bogus, _signature # url 暂时不需要携带 msToken, X-Bogus, _signature
# 每次返回数据很少 # 每次返回数据很少
# self.USER_POST = 'https://m.douyin.com/web/api/v2/aweme/post/?' # self.USER_POST = 'https://m.douyin.com/web/api/v2/aweme/post/?'
# 2023/02/19 失效 # 2023/02/19 失效
self.USER_POST = 'https://www.douyin.com/aweme/v1/web/aweme/post/?' self.USER_POST = 'https://www.douyin.com/aweme/v1/web/aweme/post/?'
# 作品信息 # 作品信息
self.POST_DETAIL = 'https://www.douyin.com/aweme/v1/web/aweme/detail/?' self.POST_DETAIL = 'https://www.douyin.com/aweme/v1/web/aweme/detail/?'
# 用户喜欢A # 用户喜欢A
# 需要 odin_tt # 需要 odin_tt
self.USER_FAVORITE_A = 'https://www.douyin.com/aweme/v1/web/aweme/favorite/?' self.USER_FAVORITE_A = 'https://www.douyin.com/aweme/v1/web/aweme/favorite/?'
# 用户喜欢B # 用户喜欢B
self.USER_FAVORITE_B = 'https://www.iesdouyin.com/web/api/v2/aweme/like/?' self.USER_FAVORITE_B = 'https://www.iesdouyin.com/web/api/v2/aweme/like/?'
# 用户历史 # 用户历史
self.USER_HISTORY = 'https://www.douyin.com/aweme/v1/web/history/read/?' self.USER_HISTORY = 'https://www.douyin.com/aweme/v1/web/history/read/?'
# 用户收藏 # 用户收藏
self.USER_COLLECTION = 'https://www.douyin.com/aweme/v1/web/aweme/listcollection/?' self.USER_COLLECTION = 'https://www.douyin.com/aweme/v1/web/aweme/listcollection/?'
# 用户评论 # 用户评论
self.COMMENT = 'https://www.douyin.com/aweme/v1/web/comment/list/?' self.COMMENT = 'https://www.douyin.com/aweme/v1/web/comment/list/?'
# 首页朋友作品 # 首页朋友作品
self.FRIEND_FEED = 'https://www.douyin.com/aweme/v1/web/familiar/feed/?' self.FRIEND_FEED = 'https://www.douyin.com/aweme/v1/web/familiar/feed/?'
# 关注用户作品 # 关注用户作品
self.FOLLOW_FEED = 'https://www.douyin.com/aweme/v1/web/follow/feed/?' self.FOLLOW_FEED = 'https://www.douyin.com/aweme/v1/web/follow/feed/?'
# 合集下所有作品 # 合集下所有作品
# 只需要X-Bogus # 只需要X-Bogus
self.USER_MIX = 'https://www.douyin.com/aweme/v1/web/mix/aweme/?' self.USER_MIX = 'https://www.douyin.com/aweme/v1/web/mix/aweme/?'
# 用户所有合集列表 # 用户所有合集列表
# 需要 ttwid # 需要 ttwid
self.USER_MIX_LIST = 'https://www.douyin.com/aweme/v1/web/mix/list/?' self.USER_MIX_LIST = 'https://www.douyin.com/aweme/v1/web/mix/list/?'
# 直播 # 直播
self.LIVE = 'https://live.douyin.com/webcast/room/web/enter/?' self.LIVE = 'https://live.douyin.com/webcast/room/web/enter/?'
self.LIVE2 = 'https://webcast.amemv.com/webcast/room/reflow/info/?' self.LIVE2 = 'https://webcast.amemv.com/webcast/room/reflow/info/?'
# 音乐 # 音乐
self.MUSIC = 'https://www.douyin.com/aweme/v1/web/music/aweme/?' self.MUSIC = 'https://www.douyin.com/aweme/v1/web/music/aweme/?'
# X-Bogus Path # X-Bogus Path
# 60 秒内,请求同一URI累计超过 600 次,封锁IP 300 秒 # 60 秒内,请求同一URI累计超过 600 次,封锁IP 300 秒
# 两个都可以用 # 两个都可以用
# 服务器在国外 # 服务器在国外
# self.GET_XB_PATH = 'https://tiktok.199933.xyz/xb' # self.GET_XB_PATH = 'https://tiktok.199933.xyz/xb'
# 服务器在国内 # 服务器在国内
self.GET_XB_PATH = 'http://47.115.208.101:9090/xb' self.GET_XB_PATH = 'http://47.115.208.101:9090/xb'
####################################################################################### #######################################################################################
if __name__ == '__main__':
Urls() if __name__ == '__main__':
Urls()

View File

@ -22,6 +22,7 @@ import sys
import json import json
from TikTokUrls import Urls from TikTokUrls import Urls
class Utils(object): class Utils(object):
def __init__(self): def __init__(self):
pass pass
@ -52,7 +53,7 @@ class Utils(object):
# 去除前后空格 # 去除前后空格
return result return result
def resource_path(self,relative_path): def resource_path(self, relative_path):
if getattr(sys, 'frozen', False): # 是否Bundle Resource if getattr(sys, 'frozen', False): # 是否Bundle Resource
base_path = sys._MEIPASS base_path = sys._MEIPASS
else: else:
@ -62,9 +63,10 @@ class Utils(object):
def getXbogus(self, url, headers=None): def getXbogus(self, url, headers=None):
# getXbogus算法开源地址https://github.com/B1gM8c/tiktok # getXbogus算法开源地址https://github.com/B1gM8c/tiktok
user_agent = headers.get( user_agent = headers.get(
'User-Agent') if headers else "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36" 'User-Agent') if headers else "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36"
try: try:
xbogus = execjs.compile(open(self.resource_path(os.path.join("X-Bogus.js"))).read()).call('sign', url, user_agent) xbogus = execjs.compile(open(self.resource_path(os.path.join("X-Bogus.js"))).read()).call('sign', url,
user_agent)
params = url + "&X-Bogus=" + xbogus params = url + "&X-Bogus=" + xbogus
except Exception as e: except Exception as e:
# print('[ 错误 ]:X-Bogus算法异常或者本地没有JS环境') # print('[ 错误 ]:X-Bogus算法异常或者本地没有JS环境')
@ -79,8 +81,6 @@ class Utils(object):
return return
return params return params
def str2bool(self, v): def str2bool(self, v):
if isinstance(v, bool): if isinstance(v, bool):
return v return v
@ -97,7 +97,7 @@ class Utils(object):
data = '{"region":"cn","aid":1768,"needFid":false,"service":"www.ixigua.com","migrate_info":{"ticket":"","source":"node"},"cbUrlProtocol":"https","union":true}' data = '{"region":"cn","aid":1768,"needFid":false,"service":"www.ixigua.com","migrate_info":{"ticket":"","source":"node"},"cbUrlProtocol":"https","union":true}'
res = requests.post(url=url, data=data) res = requests.post(url=url, data=data)
for i,j in res.cookies.items(): for i, j in res.cookies.items():
return j return j

View File

@ -33,7 +33,8 @@ def work(share_link, max_cursor, mode, cookie):
has_more = None has_more = None
if key_type == "user": if key_type == "user":
if mode == 'post' or mode == 'like': if mode == 'post' or mode == 'like':
datalist, rawdatalist, cursor, has_more = tk.getUserInfoApi(sec_uid=key, mode=mode, count=35, max_cursor=max_cursor) datalist, rawdatalist, cursor, has_more = tk.getUserInfoApi(sec_uid=key, mode=mode, count=35,
max_cursor=max_cursor)
elif mode == 'mix': elif mode == 'mix':
datalist, rawdatalist, cursor, has_more = tk.getUserAllMixInfoApi(sec_uid=key, count=35, cursor=max_cursor) datalist, rawdatalist, cursor, has_more = tk.getUserAllMixInfoApi(sec_uid=key, count=35, cursor=max_cursor)
elif key_type == "mix": elif key_type == "mix":
@ -45,7 +46,7 @@ def work(share_link, max_cursor, mode, cookie):
elif key_type == "live": elif key_type == "live":
datalist, rawdatalist = tk.getLiveInfoApi(web_rid=key) datalist, rawdatalist = tk.getLiveInfoApi(web_rid=key)
datadict={} datadict = {}
if datalist is not None and datalist != []: if datalist is not None and datalist != []:
datadict["data"] = datalist datadict["data"] = datalist
@ -57,6 +58,7 @@ def work(share_link, max_cursor, mode, cookie):
datadict["status_code"] = 500 datadict["status_code"] = 500
return datadict return datadict
def deal(mode=None): def deal(mode=None):
usefuldict = {} usefuldict = {}
if request.headers.get("content_type") == "application/json": if request.headers.get("content_type") == "application/json":
@ -83,10 +85,12 @@ def deal(mode=None):
usefuldict["status_code"] = 500 usefuldict["status_code"] = 500
return jsonify(usefuldict) return jsonify(usefuldict)
app = Flask(__name__) app = Flask(__name__)
# 设置编码 # 设置编码
app.config['JSON_AS_ASCII'] = False app.config['JSON_AS_ASCII'] = False
def argument(): def argument():
parser = argparse.ArgumentParser(description='抖音去水印工具 使用帮助') parser = argparse.ArgumentParser(description='抖音去水印工具 使用帮助')
parser.add_argument("--port", "-p", help="Web端口", parser.add_argument("--port", "-p", help="Web端口",
@ -95,34 +99,42 @@ def argument():
return args return args
@app.route("/douyin/music", methods=["POST"]) @app.route("/douyin/music", methods=["POST"])
def douyinMusic(): def douyinMusic():
return deal() return deal()
@app.route("/douyin/mix", methods=["POST"]) @app.route("/douyin/mix", methods=["POST"])
def douyinMix(): def douyinMix():
return deal() return deal()
@app.route("/douyin/user/mix", methods=["POST"]) @app.route("/douyin/user/mix", methods=["POST"])
def douyinUserMix(): def douyinUserMix():
return deal(mode="mix") return deal(mode="mix")
@app.route("/douyin/user/like", methods=["POST"]) @app.route("/douyin/user/like", methods=["POST"])
def douyinUserLike(): def douyinUserLike():
return deal(mode="like") return deal(mode="like")
@app.route("/douyin/user/post", methods=["POST"]) @app.route("/douyin/user/post", methods=["POST"])
def douyinUserPost(): def douyinUserPost():
return deal(mode="post") return deal(mode="post")
@app.route("/douyin/aweme", methods=["POST"]) @app.route("/douyin/aweme", methods=["POST"])
def douyinAweme(): def douyinAweme():
return deal() return deal()
@app.route("/douyin/live", methods=["POST"]) @app.route("/douyin/live", methods=["POST"])
def douyinLive(): def douyinLive():
return deal() return deal()
@app.route("/douyin", methods=["POST"]) @app.route("/douyin", methods=["POST"])
def douyin(): def douyin():
return deal() return deal()

View File

@ -1,81 +1,81 @@
####################################### #######################################
# 说明: # 说明:
# 1. 井号(#)为注释 # 1. 井号(#)为注释
# 2. 缩进严格对齐,使用空格缩进, 注意有些冒号后面有一个空格, 有些没有空格 # 2. 缩进严格对齐,使用空格缩进, 注意有些冒号后面有一个空格, 有些没有空格
# 3. 请使用英文字符 # 3. 请使用英文字符
# 4. 更多yaml语法请上网查看 # 4. 更多yaml语法请上网查看
####################################### #######################################
# 作品(视频或图集)、直播、合集、音乐集合、个人主页的分享链接或者电脑浏览器网址 # 作品(视频或图集)、直播、合集、音乐集合、个人主页的分享链接或者电脑浏览器网址
# (删除文案, 保证只有URL, https://v.douyin.com/kcvMpuN/ 或者 https://www.douyin.com/开头的) # (删除文案, 保证只有URL, https://v.douyin.com/kcvMpuN/ 或者 https://www.douyin.com/开头的)
# 可以设置多个链接, 确保至少一个链接 # 可以设置多个链接, 确保至少一个链接
# 必选 # 必选
link: link:
- https://live.douyin.com/759547612580 - https://live.douyin.com/759547612580
- https://v.douyin.com/BugmVVD/ - https://v.douyin.com/BugmVVD/
- https://v.douyin.com/BugrFTN/ - https://v.douyin.com/BugrFTN/
- https://v.douyin.com/B72pdU5/ - https://v.douyin.com/B72pdU5/
- https://v.douyin.com/B72QgDw/ - https://v.douyin.com/B72QgDw/
- https://v.douyin.com/AJp8D3f/ - https://v.douyin.com/AJp8D3f/
- https://v.douyin.com/B38oovu/ - https://v.douyin.com/B38oovu/
- https://v.douyin.com/S6YMNXs/ - https://v.douyin.com/S6YMNXs/
# 下载保存位置, 默认当前文件位置 # 下载保存位置, 默认当前文件位置
# 必选 # 必选
path: /mnt/c/project/test333 path: /mnt/c/project/test333
# 是否下载视频中的音乐(True/False), 默认为True # 是否下载视频中的音乐(True/False), 默认为True
# 可选 # 可选
music: True music: True
# 是否下载视频的封面(True/False), 默认为True, 当下载视频时有效 # 是否下载视频的封面(True/False), 默认为True, 当下载视频时有效
# 可选 # 可选
cover: True cover: True
# 是否下载作者的头像(True/False), 默认为True # 是否下载作者的头像(True/False), 默认为True
# 可选 # 可选
avatar: True avatar: True
# 是否保存获取到的数据(True/False), 默认为True # 是否保存获取到的数据(True/False), 默认为True
# 可选 # 可选
json: True json: True
# link是个人主页时, 设置下载发布的作品(post)或喜欢的作品(like)或者用户所有合集(mix), 默认为post, 可以设置多种模式 # link是个人主页时, 设置下载发布的作品(post)或喜欢的作品(like)或者用户所有合集(mix), 默认为post, 可以设置多种模式
# 可选 # 可选
mode: mode:
- post - post
- like - like
- mix - mix
# 下载作品个数设置 # 下载作品个数设置
# 可选 # 可选
number: number:
post: 5 # 主页下作品下载个数设置, 默认为0 全部下载 post: 5 # 主页下作品下载个数设置, 默认为0 全部下载
like: 5 # 主页下喜欢下载个数设置, 默认为0 全部下载 like: 5 # 主页下喜欢下载个数设置, 默认为0 全部下载
allmix: 1 # 主页下合集下载个数设置, 默认为0 全部下载 allmix: 1 # 主页下合集下载个数设置, 默认为0 全部下载
mix: 5 # 单个合集下作品下载个数设置, 默认为0 全部下载 mix: 5 # 单个合集下作品下载个数设置, 默认为0 全部下载
music: 5 # 音乐(原声)下作品下载个数设置, 默认为0 全部下载 music: 5 # 音乐(原声)下作品下载个数设置, 默认为0 全部下载
# 设置线程数, 默认5个线程 # 设置线程数, 默认5个线程
# 可选 # 可选
thread: 5 thread: 5
# cookie 请登录网页抖音后F12查看 # cookie 请登录网页抖音后F12查看
# cookies 和 cookie 二选一, 要使用这种形式, 请注释下面的cookie # cookies 和 cookie 二选一, 要使用这种形式, 请注释下面的cookie
# 目前只需要msToken、ttwid、odin_tt、passport_csrf_token、sid_guard # 目前只需要msToken、ttwid、odin_tt、passport_csrf_token、sid_guard
# 可以动态添加, 程序会根据填的键查找,并没有写死, 如果抖音需要更多的cookie自己加上就行了 # 可以动态添加, 程序会根据填的键查找,并没有写死, 如果抖音需要更多的cookie自己加上就行了
cookies: cookies:
msToken: xxx msToken: xxx
ttwid: xxx ttwid: xxx
odin_tt: xxx odin_tt: xxx
passport_csrf_token: xxx passport_csrf_token: xxx
sid_guard: xxx sid_guard: xxx
# cookie 请登录网页抖音后F12查看 # cookie 请登录网页抖音后F12查看
# cookies 和 cookie 二选一, 要使用这种形式, 请注释上面的cookies及包含的所有键值对 # cookies 和 cookie 二选一, 要使用这种形式, 请注释上面的cookies及包含的所有键值对
# 设置了这个后上面的cookies选项自动失效, 这个优先级更高 # 设置了这个后上面的cookies选项自动失效, 这个优先级更高
# 格式: "name1=value1; name2=value2;" 注意要加冒号 # 格式: "name1=value1; name2=value2;" 注意要加冒号
# 冒号中的内容包括不限于以下键值对, 如果抖音需要更多的cookie自己加上就行了 # 冒号中的内容包括不限于以下键值对, 如果抖音需要更多的cookie自己加上就行了
#cookie: "msToken=xxx; ttwid=xxx; odin_tt=xxx; passport_csrf_token=xxx; sid_guard=xxx;" #cookie: "msToken=xxx; ttwid=xxx; odin_tt=xxx; passport_csrf_token=xxx; sid_guard=xxx;"

File diff suppressed because it is too large Load Diff

View File

@ -1,229 +1,229 @@
// 发 post 请求 // 发 post 请求
function SendAjax() { function SendAjax() {
var data = {}; var data = {};
data = $('#form1').serialize(); data = $('#form1').serialize();
$.ajax({ $.ajax({
type: 'POST', type: 'POST',
url: "/douyin", url: "/douyin",
data: data, data: data,
dataType: 'json', dataType: 'json',
beforeSend: function () { beforeSend: function () {
$("#loading").attr("style", "display:block;");//在请求后台数据之前显示loading图标 $("#loading").attr("style", "display:block;");//在请求后台数据之前显示loading图标
$("#download").attr("style", "display:none;");//隐藏 download $("#download").attr("style", "display:none;");//隐藏 download
}, },
success: function (result) { success: function (result) {
// console.log(result);//打印服务端返回的数据(调试用) // console.log(result);//打印服务端返回的数据(调试用)
if (result.status_code === 200) { if (result.status_code === 200) {
result = result.data result = result.data
if (result.awemeType === 0) { if (result.awemeType === 0) {
$("#awemeType").html("预览视频"); $("#awemeType").html("预览视频");
$("#AwemeOrLive").html("下载视频"); $("#AwemeOrLive").html("下载视频");
$("#video").attr("href", result.video.play_addr.url_list); $("#video").attr("href", result.video.play_addr.url_list);
$("#pre_video").attr("src", result.video.play_addr.url_list); $("#pre_video").attr("src", result.video.play_addr.url_list);
$("#video").attr("style", "display:inline;");//显示 video $("#video").attr("style", "display:inline;");//显示 video
} }
if (result.awemeType === 1) { if (result.awemeType === 1) {
$("#awemeType").html("预览图集"); $("#awemeType").html("预览图集");
var images = result.images; var images = result.images;
var licontent = ""; // 拼接输入的 li 标签的字符串 var licontent = ""; // 拼接输入的 li 标签的字符串
for (var i = 0; i < images.length; i++) { for (var i = 0; i < images.length; i++) {
licontent += "<li><img src= " + images[i].url_list[0] + "></li>" licontent += "<li><img src= " + images[i].url_list[0] + "></li>"
} }
document.getElementById("images").innerHTML = licontent; document.getElementById("images").innerHTML = licontent;
$("#video").attr("style", "display:none;");//隐藏 video $("#video").attr("style", "display:none;");//隐藏 video
} }
if (result.awemeType === 0 || result.awemeType === 1) { if (result.awemeType === 0 || result.awemeType === 1) {
$("#cover").attr("href", result.video.cover_original_scale.url_list[0]); $("#cover").attr("href", result.video.cover_original_scale.url_list[0]);
$("#pre_video").attr("poster", result.video.dynamic_cover.url_list[0]); $("#pre_video").attr("poster", result.video.dynamic_cover.url_list[0]);
$("#music").attr("href", result.music.play_url.url_list[0]); $("#music").attr("href", result.music.play_url.url_list[0]);
$("#avatar").attr("src", result.author.avatar.url_list[0]); $("#avatar").attr("src", result.author.avatar.url_list[0]);
$("#avatar").attr("alt", result.author.nickname); $("#avatar").attr("alt", result.author.nickname);
$("#nickname").html(result.author.nickname); $("#nickname").html(result.author.nickname);
$("#desc").html(result.desc); $("#desc").html(result.desc);
var count = result.statistics.digg_count; var count = result.statistics.digg_count;
var digg_count; var digg_count;
if (count < 1000) { if (count < 1000) {
digg_count = count digg_count = count
} else if (count >= 1000 && count < 10000) { } else if (count >= 1000 && count < 10000) {
digg_count = (count / 1000).toFixed(1) + "K" digg_count = (count / 1000).toFixed(1) + "K"
} else { } else {
digg_count = (count / 10000).toFixed(1) + "W" digg_count = (count / 10000).toFixed(1) + "W"
} }
$("#aweme_digg_count").html(digg_count); $("#aweme_digg_count").html(digg_count);
count = result.statistics.comment_count; count = result.statistics.comment_count;
var comment_count; var comment_count;
if (count < 1000) { if (count < 1000) {
comment_count = count comment_count = count
} else if (count >= 1000 && count < 10000) { } else if (count >= 1000 && count < 10000) {
comment_count = (count / 1000).toFixed(1) + "K" comment_count = (count / 1000).toFixed(1) + "K"
} else { } else {
comment_count = (count / 10000).toFixed(1) + "W" comment_count = (count / 10000).toFixed(1) + "W"
} }
$("#aweme_comment_count").html(comment_count); $("#aweme_comment_count").html(comment_count);
count = result.statistics.collect_count; count = result.statistics.collect_count;
var collect_count; var collect_count;
if (count < 1000) { if (count < 1000) {
collect_count = count collect_count = count
} else if (count >= 1000 && count < 10000) { } else if (count >= 1000 && count < 10000) {
collect_count = (count / 1000).toFixed(1) + "K" collect_count = (count / 1000).toFixed(1) + "K"
} else { } else {
collect_count = (count / 10000).toFixed(1) + "W" collect_count = (count / 10000).toFixed(1) + "W"
} }
$("#aweme_collect_count").html(collect_count); $("#aweme_collect_count").html(collect_count);
count = result.statistics.share_count; count = result.statistics.share_count;
var share_count; var share_count;
if (count < 1000) { if (count < 1000) {
share_count = count share_count = count
} else if (count >= 1000 && count < 10000) { } else if (count >= 1000 && count < 10000) {
share_count = (count / 1000).toFixed(1) + "K" share_count = (count / 1000).toFixed(1) + "K"
} else { } else {
share_count = (count / 10000).toFixed(1) + "W" share_count = (count / 10000).toFixed(1) + "W"
} }
$("#aweme_share_count").html(share_count); $("#aweme_share_count").html(share_count);
$("#icons").attr("style", "display:flex;");//显示 icons $("#icons").attr("style", "display:flex;");//显示 icons
$("#icon").attr("style", "display:table-row;");//显示 icon $("#icon").attr("style", "display:table-row;");//显示 icon
$("#music").attr("style", "display:inline;");//显示 music $("#music").attr("style", "display:inline;");//显示 music
$("#loading").attr("style", "display:none;");//隐藏 loading $("#loading").attr("style", "display:none;");//隐藏 loading
$("#download").attr("style", "display:block;");//显示 download $("#download").attr("style", "display:block;");//显示 download
// alert("SUCCESS"); // alert("SUCCESS");
// 执行弹框 // 执行弹框
narnSuccess(); narnSuccess();
} }
if (result.awemeType === 2) { if (result.awemeType === 2) {
if (result.status === 4) { if (result.status === 4) {
$("#loading").attr("style", "display:none;");//隐藏 loading $("#loading").attr("style", "display:none;");//隐藏 loading
$("#download").attr("style", "display:none;");//隐藏 download $("#download").attr("style", "display:none;");//隐藏 download
// 执行弹框 // 执行弹框
narnWarn() narnWarn()
} else { } else {
$("#AwemeOrLive").html("下载直播"); $("#AwemeOrLive").html("下载直播");
$("#awemeType").html("预览直播"); $("#awemeType").html("预览直播");
$("#video").attr("href", result.flv_pull_url0); $("#video").attr("href", result.flv_pull_url0);
$("#pre_video").attr("src", result.flv_pull_url0); $("#pre_video").attr("src", result.flv_pull_url0);
$("#cover").attr("href", result.cover); $("#cover").attr("href", result.cover);
$("#pre_video").attr("poster", result.cover); $("#pre_video").attr("poster", result.cover);
$("#avatar").attr("src", result.avatar); $("#avatar").attr("src", result.avatar);
$("#avatar").attr("alt", result.nickname); $("#avatar").attr("alt", result.nickname);
$("#nickname").html(result.nickname); $("#nickname").html(result.nickname);
$("#desc").html(result.title); $("#desc").html(result.title);
$("#video").attr("style", "display:inline;");//显示 video $("#video").attr("style", "display:inline;");//显示 video
$("#icons").attr("style", "display:none;");//隐藏 icons $("#icons").attr("style", "display:none;");//隐藏 icons
$("#icon").attr("style", "display:none;");//隐藏 icon $("#icon").attr("style", "display:none;");//隐藏 icon
$("#music").attr("style", "display:none;");//隐藏 music $("#music").attr("style", "display:none;");//隐藏 music
$("#loading").attr("style", "display:none;");//隐藏 loading $("#loading").attr("style", "display:none;");//隐藏 loading
$("#download").attr("style", "display:block;");//显示 download $("#download").attr("style", "display:block;");//显示 download
// alert("SUCCESS"); // alert("SUCCESS");
// 执行弹框 // 执行弹框
narnSuccess(); narnSuccess();
} }
} }
} else { } else {
$("#loading").attr("style", "display:none;");//隐藏 loading $("#loading").attr("style", "display:none;");//隐藏 loading
$("#download").attr("style", "display:none;");//隐藏 download $("#download").attr("style", "display:none;");//隐藏 download
// 执行弹框 // 执行弹框
narnFail(); narnFail();
} }
; ;
}, },
error: function (xhr, type) { error: function (xhr, type) {
$("#loading").attr("style", "display:none;");//隐藏 loading $("#loading").attr("style", "display:none;");//隐藏 loading
$("#download").attr("style", "display:none;");//隐藏 download $("#download").attr("style", "display:none;");//隐藏 download
// alert("异常!"); // alert("异常!");
// 执行弹框 // 执行弹框
narnFail(); narnFail();
} }
}); });
} }
// 右上角弹框 // 右上角弹框
function narnSuccess() { function narnSuccess() {
naranja().success({ naranja().success({
title: '解析成功', title: '解析成功',
text: '请及时下载音视频', text: '请及时下载音视频',
icon: true, icon: true,
timeout: 5000, timeout: 5000,
buttons: [] buttons: []
}) })
} }
function narnFail() { function narnFail() {
naranja().error({ naranja().error({
title: '解析失败', title: '解析失败',
text: '直播/视频/图集不存在或接口失效', text: '直播/视频/图集不存在或接口失效',
icon: true, icon: true,
timeout: 5000, timeout: 5000,
buttons: [] buttons: []
}) })
} }
function narnWarn() { function narnWarn() {
naranja().warn({ naranja().warn({
title: '提示', title: '提示',
text: '直播未开始', text: '直播未开始',
icon: true, icon: true,
timeout: 5000, timeout: 5000,
buttons: [] buttons: []
}) })
} }
window.addEventListener('DOMContentLoaded', function () { window.addEventListener('DOMContentLoaded', function () {
document.getElementById('view_aweme').addEventListener('click', function () { document.getElementById('view_aweme').addEventListener('click', function () {
var awemeType = document.getElementById("awemeType").innerText; var awemeType = document.getElementById("awemeType").innerText;
if (awemeType === "预览视频") { if (awemeType === "预览视频") {
// 调小音量 // 调小音量
var videoElement = document.getElementById("pre_video"); var videoElement = document.getElementById("pre_video");
videoElement.volume = 0.6 videoElement.volume = 0.6
/*弹出视频播放层*/ /*弹出视频播放层*/
$("#show-video").show(); $("#show-video").show();
} }
// 图片查看器 // 图片查看器
if (awemeType === "预览图集") { if (awemeType === "预览图集") {
var viewer = new Viewer(document.getElementById('images'), { var viewer = new Viewer(document.getElementById('images'), {
hidden: function () { hidden: function () {
viewer.destroy(); viewer.destroy();
}, },
}); });
// image.click(); // image.click();
viewer.show(); viewer.show();
} }
// 预览直播 // 预览直播
if (awemeType === "预览直播") { if (awemeType === "预览直播") {
if (flvjs.isSupported()) {//检查flvjs能否正常使用 if (flvjs.isSupported()) {//检查flvjs能否正常使用
var videoElement = document.getElementById('pre_video');//使用id选择器找到第二步设置的dom元素 var videoElement = document.getElementById('pre_video');//使用id选择器找到第二步设置的dom元素
var flvPlayer = flvjs.createPlayer({//创建一个新的flv播放器对象 var flvPlayer = flvjs.createPlayer({//创建一个新的flv播放器对象
type: 'flv',//类型flv type: 'flv',//类型flv
url: $("#video").attr("href")//flv文件地址 url: $("#video").attr("href")//flv文件地址
}); });
flvPlayer.attachMediaElement(videoElement);//将flv视频装载进video元素内 flvPlayer.attachMediaElement(videoElement);//将flv视频装载进video元素内
flvPlayer.load();//载入视频 flvPlayer.load();//载入视频
flvPlayer.play();//播放视频,如果不想要自动播放,去掉本行 flvPlayer.play();//播放视频,如果不想要自动播放,去掉本行
/*弹出视频播放层*/ /*弹出视频播放层*/
$("#show-video").show(); $("#show-video").show();
} }
} }
}); });
/*关闭视频播放层*/ /*关闭视频播放层*/
$(".video-close").click(function () { $(".video-close").click(function () {
var videoElement = document.getElementById("pre_video"); var videoElement = document.getElementById("pre_video");
videoElement.pause() videoElement.pause()
$("#show-video").hide(); $("#show-video").hide();
}) })
}); });

View File

@ -1,172 +1,175 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="zhxx"> <html lang="zhxx">
<head> <head>
<title>抖音去水印工具</title> <title>抖音去水印工具</title>
<!-- Meta tag Keywords --> <!-- Meta tag Keywords -->
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="UTF-8"/> <meta charset="UTF-8"/>
<meta name="keywords" content="抖音去水印工具"/> <meta name="keywords" content="抖音去水印工具"/>
<meta name="referrer" content="never"> <meta name="referrer" content="never">
<script> <script>
addEventListener("load", function () { addEventListener("load", function () {
setTimeout(hideURLbar, 0); setTimeout(hideURLbar, 0);
}, false); }, false);
function hideURLbar() { function hideURLbar() {
window.scrollTo(0, 1); window.scrollTo(0, 1);
} }
</script> </script>
<!-- //Meta tag Keywords --> <!-- //Meta tag Keywords -->
<!-- /Favicons --> <!-- /Favicons -->
<link href="https://cdn.jsdelivr.net/gh/imgyh/tiktok/static/img/favicon.ico" rel="shortcut icon" type="image/x-icon"> <link href="https://cdn.jsdelivr.net/gh/imgyh/tiktok/static/img/favicon.ico" rel="shortcut icon"
<!-- //Favicons --> type="image/x-icon">
<!--/Style-CSS --> <!-- //Favicons -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/imgyh/tiktok/static/css/style.css" type="text/css" media="all"/> <!--/Style-CSS -->
<!--//Style-CSS --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/imgyh/tiktok/static/css/style.css" type="text/css"
<!-- font-awesome-icons --> media="all"/>
<link href="https://cdn.jsdelivr.net/gh/imgyh/tiktok/static/css/font-awesome.css" rel="stylesheet"> <!--//Style-CSS -->
<!-- //font-awesome-icons --> <!-- font-awesome-icons -->
<!-- naranja 右下角弹框提示 https://github.com/e1016/naranja--> <link href="https://cdn.jsdelivr.net/gh/imgyh/tiktok/static/css/font-awesome.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/imgyh/tiktok/static/css/naranja.min.css" type="text/css"/> <!-- //font-awesome-icons -->
<!-- //naranja --> <!-- naranja 右下角弹框提示 https://github.com/e1016/naranja-->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/imgyh/tiktok/static/css/naranja.min.css" type="text/css"/>
<!-- viewerjs 图片查看器 https://github.com/fengyuanchen/viewerjs--> <!-- //naranja -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/imgyh/tiktok/static/css/viewer.min.css" type="text/css"/>
<!-- //viewerjs --> <!-- viewerjs 图片查看器 https://github.com/fengyuanchen/viewerjs-->
</head> <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/imgyh/tiktok/static/css/viewer.min.css" type="text/css"/>
<!-- //viewerjs -->
<body> </head>
<div class="error-61-mian"> <body>
<div class="wrapper">
<div class="errors-16-top"> <div class="error-61-mian">
<div class="wrapper">
<p style="color:#00c4b6;font-size:32px;">抖音去水印工具</p> <div class="errors-16-top">
<br>
<p>支持视频/图集/直播解析,粘贴视频/图集/直播分享链接时无需删除文案,但如果链接正确但解析失败请删掉文案后重试 https://v.douyin.com/kcvMpuN/</p> <p style="color:#00c4b6;font-size:32px;">抖音去水印工具</p>
{# <p>2.支持直播解析,需要网页版直播链接 https://live.douyin.com/343806013144</p>#} <br>
<p>关于抖音批量下载与去水印工具的更多实现细节请点击: <a href="https://www.imgyh.com/archives/41.html" target="_blank" style="color:#00c4b6;">抖音批量下载与去水印工具</a></p> <p>支持视频/图集/直播解析,粘贴视频/图集/直播分享链接时无需删除文案,但如果链接正确但解析失败请删掉文案后重试 https://v.douyin.com/kcvMpuN/</p>
<form id="form1" onsubmit="return false" action="#" method="post" class="d-flex error-page-form"> {# <p>2.支持直播解析,需要网页版直播链接 https://live.douyin.com/343806013144</p>#}
{# 以前需要手动选择 图片 或者 视频 现在加了自动判断#} <p>关于抖音批量下载与去水印工具的更多实现细节请点击: <a href="https://www.imgyh.com/archives/41.html" target="_blank"
{# <div class="select">#} style="color:#00c4b6;">抖音批量下载与去水印工具</a></p>
{# <select name="awemeType" required="required">#} <form id="form1" onsubmit="return false" action="#" method="post" class="d-flex error-page-form">
{# <option value="0" selected="selected">视频</option>#} {# 以前需要手动选择 图片 或者 视频 现在加了自动判断#}
{# <option value="1">图集</option>#} {# <div class="select">#}
{# </select>#} {# <select name="awemeType" required="required">#}
{# </div>#} {# <option value="0" selected="selected">视频</option>#}
{# <option value="1">图集</option>#}
<input type="text" placeholder="粘贴视频/图集/直播分享地址" name="share_link" required="required"> {# </select>#}
<button type="reset" onclick="SendAjax()">解析</button> {# </div>#}
</form>
{# <div class="social-coming-icons">#} <input type="text" placeholder="粘贴视频/图集/直播分享地址" name="share_link" required="required">
{# <a href="#" title="Facebook" class="footer-fb"><span class="fa fa-facebook"#} <button type="reset" onclick="SendAjax()">解析</button>
{# aria-hidden="true"></span></a>#} </form>
{# <a href="#" title="Twitter" class="footer-tw"><span class="fa fa-twitter"#} {# <div class="social-coming-icons">#}
{# aria-hidden="true"></span></a>#} {# <a href="#" title="Facebook" class="footer-fb"><span class="fa fa-facebook"#}
{# <a href="#" title="Google Plus" class="footer-gg"><span class="fa fa-google-plus"#} {# aria-hidden="true"></span></a>#}
{# aria-hidden="true"></span></a>#} {# <a href="#" title="Twitter" class="footer-tw"><span class="fa fa-twitter"#}
{# <a href="#" title="Linkedin" class="footer-lin"><span class="fa fa-linkedin"#} {# aria-hidden="true"></span></a>#}
{# aria-hidden="true"></span></a>#} {# <a href="#" title="Google Plus" class="footer-gg"><span class="fa fa-google-plus"#}
{# </div>#} {# aria-hidden="true"></span></a>#}
{# <a href="#" title="Linkedin" class="footer-lin"><span class="fa fa-linkedin"#}
</div> {# aria-hidden="true"></span></a>#}
<div class="errors-16-mid"> {# </div>#}
<div class="loading" id="loading">
<div class="shape shape-1"></div> </div>
<div class="shape shape-2"></div> <div class="errors-16-mid">
<div class="shape shape-3"></div> <div class="loading" id="loading">
<div class="shape shape-4"></div> <div class="shape shape-1"></div>
</div> <div class="shape shape-2"></div>
<div class="shape shape-3"></div>
<div id="download" style="display: none;"> <div class="shape shape-4"></div>
<div class="photo"> </div>
<img id="avatar" src="#" class="avatar" alt="avatar">
<br> <div id="download" style="display: none;">
<p id="nickname" class="nickname"></p> <div class="photo">
</div> <img id="avatar" src="#" class="avatar" alt="avatar">
<div class="info"> <br>
<div id="icons" class="icons"> <p id="nickname" class="nickname"></p>
<div id="icon" class="icon"> </div>
<i class="fa fa-heart" style="color:#fd325c;" aria-hidden="true"></i> <div class="info">
<p id="aweme_digg_count"></p> <div id="icons" class="icons">
</div> <div id="icon" class="icon">
<div id="icon" class="icon"> <i class="fa fa-heart" style="color:#fd325c;" aria-hidden="true"></i>
<i class="fa fa-comment" style="color:#efeeec;" aria-hidden="true"></i> <p id="aweme_digg_count"></p>
<p id="aweme_comment_count"></p> </div>
</div> <div id="icon" class="icon">
<div id="icon" class="icon"> <i class="fa fa-comment" style="color:#efeeec;" aria-hidden="true"></i>
<i class="fa fa-star" style="color:#fcb505;" aria-hidden="true"></i> <p id="aweme_comment_count"></p>
<p id="aweme_collect_count"></p> </div>
</div> <div id="icon" class="icon">
<div id="icon" class="icon"> <i class="fa fa-star" style="color:#fcb505;" aria-hidden="true"></i>
<i class="fa fa-share" style="color:#e7e8e6;" aria-hidden="true"></i> <p id="aweme_collect_count"></p>
<p id="aweme_share_count"></p> </div>
</div> <div id="icon" class="icon">
</div> <i class="fa fa-share" style="color:#e7e8e6;" aria-hidden="true"></i>
<br> <p id="aweme_share_count"></p>
<p id="desc"></p> </div>
<br> </div>
<a id="cover" href="#" target="_blank"> <br>
<button type="button" class="btn btn1 "> <p id="desc"></p>
<span>下载封面</span> <br>
</button> <a id="cover" href="#" target="_blank">
</a> <button type="button" class="btn btn1 ">
<a id="video" href="#" target="_blank"> <span>下载封面</span>
<button type="button" class="btn btn1"> </button>
<span id="AwemeOrLive"></span> </a>
</button> <a id="video" href="#" target="_blank">
</a> <button type="button" class="btn btn1">
<a id="music" href="#" target="_blank"> <span id="AwemeOrLive"></span>
<button type="button" class="btn btn1"> </button>
<span>下载音乐</span> </a>
</button> <a id="music" href="#" target="_blank">
</a> <button type="button" class="btn btn1">
<button id="view_aweme" type="button" class="btn btn1"> <span>下载音乐</span>
<span id="awemeType"></span> </button>
</button> </a>
</div> <button id="view_aweme" type="button" class="btn btn1">
</div> <span id="awemeType"></span>
</button>
</div> </div>
</div>
</div>
<div class="copy-right"> </div>
<p>Copyright &copy; 2021-2023.我的博客 <a href="https://www.imgyh.com/" target="_blank">GYH's Blog</a> && 项目地址 <a
href="https://github.com/imgyh/douyin" target="_blank">Github</a> </div>
All rights reserved.</p> <div class="copy-right">
</div> <p>Copyright &copy; 2021-2023.我的博客 <a href="https://www.imgyh.com/" target="_blank">GYH's Blog</a> && 项目地址 <a
</div> href="https://github.com/imgyh/douyin" target="_blank">Github</a>
All rights reserved.</p>
{# 视频预览效果 https://blog.csdn.net/qq_45140694/article/details/115266928 #} </div>
<div id="show-video"> </div>
<a class="video-close">
<span> {# 视频预览效果 https://blog.csdn.net/qq_45140694/article/details/115266928 #}
<svg t="1614676844098" class="icon" viewBox="0 0 1024 1024" <div id="show-video">
xmlns="http://www.w3.org/2000/svg" p-id="2082" <a class="video-close">
width="30" height="30"> <span>
<path d="M591.506286 511.853714l417.133714-416.914285a54.601143 54.601143 0 0 0 0-76.8l-2.267429-2.267429a54.601143 54.601143 0 0 0-76.8 0L512.438857 433.481143 95.305143 15.798857a54.601143 54.601143 0 0 0-76.8 0L16.237714 18.066286a53.577143 53.577143 0 0 0 0 76.8l417.097143 416.987428L16.201143 929.097143a54.601143 54.601143 0 0 0 0 76.8l2.267428 2.267428a54.601143 54.601143 0 0 0 76.8 0l417.170286-417.060571 417.097143 417.097143a54.601143 54.601143 0 0 0 76.8 0l2.267429-2.267429a54.601143 54.601143 0 0 0 0-76.8z" <svg t="1614676844098" class="icon" viewBox="0 0 1024 1024"
p-id="2083" fill="#e6e6e6"></path> xmlns="http://www.w3.org/2000/svg" p-id="2082"
</svg> width="30" height="30">
</span> <path d="M591.506286 511.853714l417.133714-416.914285a54.601143 54.601143 0 0 0 0-76.8l-2.267429-2.267429a54.601143 54.601143 0 0 0-76.8 0L512.438857 433.481143 95.305143 15.798857a54.601143 54.601143 0 0 0-76.8 0L16.237714 18.066286a53.577143 53.577143 0 0 0 0 76.8l417.097143 416.987428L16.201143 929.097143a54.601143 54.601143 0 0 0 0 76.8l2.267428 2.267428a54.601143 54.601143 0 0 0 76.8 0l417.170286-417.060571 417.097143 417.097143a54.601143 54.601143 0 0 0 76.8 0l2.267429-2.267429a54.601143 54.601143 0 0 0 0-76.8z"
</a> p-id="2083" fill="#e6e6e6"></path>
{# https://blog.csdn.net/seeeeeeeeeee/article/details/119981594 #} </svg>
<video src="" id="pre_video" controls="controls" poster=""></video> </span>
</div> </a>
{# https://blog.csdn.net/seeeeeeeeeee/article/details/119981594 #}
<div style="display: none"> <video src="" id="pre_video" controls="controls" poster=""></video>
<ul id="images"> </div>
{# <li><img src="picture-1.jpg" alt="Picture 1"></li>#}
{# <li><img src="picture-2.jpg" alt="Picture 2"></li>#} <div style="display: none">
{# <li><img src="picture-3.jpg" alt="Picture 3"></li>#} <ul id="images">
</ul> {# <li><img src="picture-1.jpg" alt="Picture 1"></li>#}
</div> {# <li><img src="picture-2.jpg" alt="Picture 2"></li>#}
{# <li><img src="picture-3.jpg" alt="Picture 3"></li>#}
<script src="https://cdn.jsdelivr.net/gh/imgyh/tiktok/static/js/jquery-1.8.2.min.js" type="text/javascript"></script> </ul>
<script src="https://cdn.jsdelivr.net/gh/imgyh/tiktok/static/js/naranja.min.js" type="text/javascript"></script> </div>
<script src="https://cdn.jsdelivr.net/gh/imgyh/tiktok/static/js/viewer.min.js" type="text/javascript"></script>
<script src="https://cdn.jsdelivr.net/gh/imgyh/tiktok/static/js/custom.js" type="text/javascript"></script> <script src="https://cdn.jsdelivr.net/gh/imgyh/tiktok/static/js/jquery-1.8.2.min.js" type="text/javascript"></script>
<script src="https://cdn.jsdelivr.net/gh/imgyh/tiktok/static/js/flv.min.js" type="text/javascript"></script> <script src="https://cdn.jsdelivr.net/gh/imgyh/tiktok/static/js/naranja.min.js" type="text/javascript"></script>
</body> <script src="https://cdn.jsdelivr.net/gh/imgyh/tiktok/static/js/viewer.min.js" type="text/javascript"></script>
<script src="https://cdn.jsdelivr.net/gh/imgyh/tiktok/static/js/custom.js" type="text/javascript"></script>
<script src="https://cdn.jsdelivr.net/gh/imgyh/tiktok/static/js/flv.min.js" type="text/javascript"></script>
</body>
</html> </html>