完善说明,爱奇艺支持series命令,增加弹幕区间设定

This commit is contained in:
xhlove
2020-02-07 19:05:58 +08:00
parent 27de5ce4a3
commit 1c31057b9d
11 changed files with 115 additions and 36 deletions

View File

@@ -3,7 +3,7 @@
''' '''
# 作者: weimo # 作者: weimo
# 创建日期: 2020-01-04 19:14:39 # 创建日期: 2020-01-04 19:14:39
# 上次编辑时间 : 2020-01-28 18:39:00 # 上次编辑时间 : 2020-02-07 18:23:18
# 一个人的命运啊,当然要靠自我奋斗,但是... # 一个人的命运啊,当然要靠自我奋斗,但是...
''' '''
@@ -18,6 +18,8 @@ from sites.sohu import main as sohu
from sites.mgtv import main as mgtv from sites.mgtv import main as mgtv
from pfunc.cfunc import check_url_site from pfunc.cfunc import check_url_site
from basic.vars import ALLOW_SITES
# ------------------------------------------- # -------------------------------------------
# 基本流程 # 基本流程
# 1. 根据传入参数确定网站,否则请求输入有关参数或链接。并初始化字幕的基本信息。 # 1. 根据传入参数确定网站,否则请求输入有关参数或链接。并初始化字幕的基本信息。
@@ -32,7 +34,8 @@ def main():
parser = ArgumentParser(description="视频网站弹幕转换/下载工具项目地址https://github.com/xhlove/GetDanMu任何问题请联系vvtoolbox.dev@gmail.com") parser = ArgumentParser(description="视频网站弹幕转换/下载工具项目地址https://github.com/xhlove/GetDanMu任何问题请联系vvtoolbox.dev@gmail.com")
parser.add_argument("-f", "--font", default="微软雅黑", help="指定输出字幕字体") parser.add_argument("-f", "--font", default="微软雅黑", help="指定输出字幕字体")
parser.add_argument("-fs", "--font-size", default=28, help="指定输出字幕字体大小") parser.add_argument("-fs", "--font-size", default=28, help="指定输出字幕字体大小")
parser.add_argument("-s", "--site", default="", help="指定网站") parser.add_argument("-s", "--site", default="", help=f"使用非url方式下载需指定网站 支持的网站 -> {ALLOW_SITES}")
parser.add_argument("-r", "--range", default="0,720", help="指定弹幕的纵向范围 默认0到720 请用逗号隔开")
parser.add_argument("-cid", "--cid", default="", help="下载cid对应视频的弹幕腾讯 芒果视频合集)") parser.add_argument("-cid", "--cid", default="", help="下载cid对应视频的弹幕腾讯 芒果视频合集)")
parser.add_argument("-vid", "--vid", default="", help="下载vid对应视频的弹幕支持同时多个vid需要用逗号隔开") parser.add_argument("-vid", "--vid", default="", help="下载vid对应视频的弹幕支持同时多个vid需要用逗号隔开")
parser.add_argument("-aid", "--aid", default="", help="下载aid对应视频的弹幕爱奇艺合集") parser.add_argument("-aid", "--aid", default="", help="下载aid对应视频的弹幕爱奇艺合集")

View File

@@ -5,24 +5,43 @@
项目主页https://github.com/xhlove/GetDanMu 项目主页https://github.com/xhlove/GetDanMu
## 网站支持 ## 网站支持
| Site | URL | 单集? | 合集? | 综艺合集? | | Site | URL | 单集? | 合集? | 综艺合集? | 支持series? |
| :--: | :-- | :-----: | :-----: | :-----: | | :--: | :-- | :-----: | :-----: | :-----: | :-----: |
| **腾讯视频** | <https://v.qq.com/> |✓|✓| | | **腾讯视频** | <https://v.qq.com/> |✓|✓| |
| **爱奇艺** | <https://www.iqiyi.com/> |✓|✓|✓| | **爱奇艺** | <https://www.iqiyi.com/> |✓|✓|✓|✓|
| **优酷** | <https://v.youku.com/> |✓|✓|✓| | **优酷** | <https://v.youku.com/> |✓|✓|✓|✓|
| **搜狐视频** | <https://tv.sohu.com/> |✓|✓|| | **搜狐视频** | <https://tv.sohu.com/> |✓|✓|||
| **芒果TV** | <https://www.mgtv.com/> |✓|✓|✓| | **芒果TV** | <https://www.mgtv.com/> |✓|✓|✓|✓|
# 使用示例 # 使用示例
- 命令(建议) - 命令(建议)
> GetDanMu.exe -u https://www.mgtv.com/b/334727/7452407.html > GetDanMu.exe -s mgtv -r 20,960 -series -u https://www.mgtv.com/b/334727/7452407.html
- 双击运行 - 双击运行
> 提示逻辑有待完善 > 提示逻辑有待完善
- 选项说明
> -f或--font 指定输出字幕字体,默认微软雅黑)
> -fs或--font-size 指定输出字幕字体大小默认28)
> -s或--site 使用非url方式下载需指定网站 支持的网站 -> ["qq", "iqiyi", "youku", "sohu", "mgtv"]
> -r或--range 指定弹幕的纵向范围 默认0到720请用逗号隔开
> -cid或--cid 下载cid对应视频的弹幕腾讯 芒果视频合集)
> -vid或--vid 下载vid对应视频的弹幕支持同时多个vid需要用逗号隔开
> -aid或--aid 下载aid对应视频的弹幕爱奇艺合集
> -tvid或--tvid 下载tvid对应视频的弹幕支持同时多个tvid需要用逗号隔开
> -series或--series 尝试通过单集得到合集的全部弹幕 默认不使用
> -u或--url 下载视频链接所指向视频的弹幕
> -y或--y 覆盖原有弹幕而不提示 默认不使用
# 效果示意(字幕与视频不相关) # 效果示意(字幕与视频不相关)
[potplayer截屏](http://puui.qpic.cn/vshpic/0/5TLOX3WbgjudEj61IxYZ4tAuf2lFwl-ynf4S5T4sXkdjS9cd_0/0) ![potplayer截屏](http://puui.qpic.cn/vshpic/0/5TLOX3WbgjudEj61IxYZ4tAuf2lFwl-ynf4S5T4sXkdjS9cd_0/0)
[查看使用演示视频点我][2]
注意有背景音乐
演示是直接使用的python命令使用exe的话把python GetDanMu.py换成GetDanMu.exe即可
## 可能存在的问题 ## 可能存在的问题
- 下载进度接近100%时暂时没有反应 - 下载进度接近100%时暂时没有反应
@@ -34,6 +53,13 @@
# 更新日志 # 更新日志
## 2020/2/7
- 完善说明
- 爱奇艺支持series选项并完善地区判断
- 增加字体配置文件,建立字体名称与实际字体文件的映射关系,用于预先设定,方便更准确计算弹幕的分布
- 增加自定义弹幕区间选项,即-r或--range命令
- README完善
## 2020/1/28 ## 2020/1/28
- 增加芒果TV的支持支持综艺合集、支持series命令 - 增加芒果TV的支持支持综艺合集、支持series命令
- 爱奇艺bug修复 - 爱奇艺bug修复
@@ -54,4 +80,8 @@
- 增加了通过链接下载爱奇艺视频弹幕的方法,支持综艺合集。 - 增加了通过链接下载爱奇艺视频弹幕的方法,支持综艺合集。
- 增加通过链接判断网站 - 增加通过链接判断网站
[赞助点此][3]
[1]: https://blog.weimo.info/archives/431/ [1]: https://blog.weimo.info/archives/431/
[2]: https://alime-customer-upload-cn-hangzhou.oss-cn-hangzhou.aliyuncs.com/customer-upload/1581073011183_8t14dpgg2bdc.mp4
[3]: https://afdian.net/@vvtoolbox_dev

View File

@@ -3,15 +3,14 @@
''' '''
# 作者: weimo # 作者: weimo
# 创建日期: 2020-01-04 19:14:46 # 创建日期: 2020-01-04 19:14:46
# 上次编辑时间 : 2020-01-11 17:20:21 # 上次编辑时间 : 2020-02-07 18:33:14
# 一个人的命运啊,当然要靠自我奋斗,但是... # 一个人的命运啊,当然要靠自我奋斗,但是...
''' '''
import os import os
import json
from basic.vars import fonts from basic.vars import fonts
ass_script = """[Script Info] ass_script = """[Script Info]
; Script generated by N ; Script generated by N
ScriptType: v4.00+ ScriptType: v4.00+
@@ -30,15 +29,29 @@ ass_events_head = """[Events]\nFormat: Layer, Start, End, Style, Name, MarginL,
# 基于当前时间范围在0~1000ms之间停留在(676.571,506.629)处在1000~3000ms内从位置1300,600移动到360,600原点在左上 # 基于当前时间范围在0~1000ms之间停留在(676.571,506.629)处在1000~3000ms内从位置1300,600移动到360,600原点在左上
# ass_baseline = """Dialogue: 0,0:20:08.00,0:20:28.00,Default,,0,0,0,,{\t(1000,3000,\move(1300,600,360,600))\pos(676.571,506.629)}这是字幕内容示意""" # ass_baseline = """Dialogue: 0,0:20:08.00,0:20:28.00,Default,,0,0,0,,{\t(1000,3000,\move(1300,600,360,600))\pos(676.571,506.629)}这是字幕内容示意"""
def get_fonts_info():
fonts_path = r"C:\Windows\Fonts"
if os.path.exists("config.json"):
with open("config.json", "r", encoding="utf-8") as f:
fr = f.read()
try:
config = json.loads(fr)
except Exception as e:
print("get_fonts_info error info ->", e)
else:
fonts_path = config["fonts_base_folder"]
fonts = config["fonts"]
return fonts_path, fonts
def get_ass_head(font_style_name, font_size): def get_ass_head(font_style_name, font_size):
ass_head = ass_script + "\n\n" + ass_style_head + "\n" + ass_style_base.format(font=font_style_name, font_size=font_size) + "\n\n" + ass_events_head ass_head = ass_script + "\n\n" + ass_style_head + "\n" + ass_style_base.format(font=font_style_name, font_size=font_size) + "\n\n" + ass_events_head
return ass_head return ass_head
def check_font(font): def check_font(font):
win_font_path = r"C:\Windows\Fonts" fonts_path, fonts = get_fonts_info()
maybe_font_path = os.path.join(win_font_path, font) maybe_font_path = os.path.join(fonts_path, font)
font_style_name = "微软雅黑" font_style_name = "微软雅黑"
font_path = os.path.join(win_font_path, fonts[font_style_name]) # 默认 font_path = os.path.join(fonts_path, fonts[font_style_name]) # 默认
if os.path.exists(font): if os.path.exists(font):
# 字体就在当前文件夹 或 完整路径 # 字体就在当前文件夹 或 完整路径
if os.path.isfile(font): if os.path.isfile(font):
@@ -58,7 +71,7 @@ def check_font(font):
pass pass
elif fonts.get(font): elif fonts.get(font):
# 别名映射 # 别名映射
font_path = os.path.join(win_font_path, fonts.get(font)) font_path = os.path.join(fonts_path, fonts.get(font))
font_style_name = font font_style_name = font
else: else:
pass pass

View File

@@ -3,7 +3,7 @@
''' '''
# 作者: weimo # 作者: weimo
# 创建日期: 2020-01-04 19:14:35 # 创建日期: 2020-01-04 19:14:35
# 上次编辑时间 : 2020-01-28 17:43:44 # 上次编辑时间 : 2020-02-07 17:57:05
# 一个人的命运啊,当然要靠自我奋斗,但是... # 一个人的命运啊,当然要靠自我奋斗,但是...
''' '''

8
config.json Normal file
View File

@@ -0,0 +1,8 @@
{
"fonts_base_folder": "C:/Windows/Fonts",
"fonts": {
"微软雅黑":"msyh.ttc",
"微软雅黑粗体":"msyhbd.ttc",
"微软雅黑细体":"msyhl.ttc"
}
}

View File

@@ -3,7 +3,7 @@
''' '''
# 作者: weimo # 作者: weimo
# 创建日期: 2020-01-04 19:14:47 # 创建日期: 2020-01-04 19:14:47
# 上次编辑时间: 2020-01-05 14:46:51 # 上次编辑时间 : 2020-02-07 18:40:42
# 一个人的命运啊,当然要靠自我奋斗,但是... # 一个人的命运啊,当然要靠自我奋斗,但是...
''' '''
@@ -14,10 +14,10 @@ class SameHeight(object):
''' '''
# 等高弹幕 --> 矩形分割问题? # 等高弹幕 --> 矩形分割问题?
''' '''
def __init__(self, text, font_path="msyh.ttc", font_size=14): def __init__(self, text, ass_range: str, font_path="msyh.ttc", font_size=14):
self.font = truetype(font=font_path, size=font_size) self.font = truetype(font=font_path, size=font_size)
self.width, self.height = self.get_danmu_size(text) self.width, self.height = self.get_danmu_size(text)
self.height_range = [0, 720] self.height_range = [int(n.strip()) for n in ass_range.split(",")]
self.width_range = [0, 1920] self.width_range = [0, 1920]
self.lines_start_y = list(range(*(self.height_range + [self.height]))) self.lines_start_y = list(range(*(self.height_range + [self.height])))
self.lines_width_used = [[y, 0] for y in self.lines_start_y] self.lines_width_used = [[y, 0] for y in self.lines_start_y]
@@ -49,7 +49,7 @@ class SameHeight(object):
def main(): def main():
text = "测试" text = "测试"
show_time = 13 show_time = 13
sh = SameHeight(text) sh = SameHeight(text, "0,720")
sh.get_xy(text, show_time) sh.get_xy(text, show_time)

View File

@@ -3,7 +3,7 @@
''' '''
# 作者: weimo # 作者: weimo
# 创建日期: 2020-01-04 19:17:44 # 创建日期: 2020-01-04 19:17:44
# 上次编辑时间 : 2020-01-16 20:06:23 # 上次编辑时间 : 2020-02-07 18:17:48
# 一个人的命运啊,当然要靠自我奋斗,但是... # 一个人的命运啊,当然要靠自我奋斗,但是...
''' '''
import os import os
@@ -17,7 +17,7 @@ def write_one_video_subtitles(file_path, comments, args):
# 对于合集则每次都都得检查一次 也可以放在上一级 放在这里 考虑后面可能特殊指定字体的情况 # 对于合集则每次都都得检查一次 也可以放在上一级 放在这里 考虑后面可能特殊指定字体的情况
font_path, font_style_name = check_font(args.font) font_path, font_style_name = check_font(args.font)
ass_head = get_ass_head(font_style_name, args.font_size) ass_head = get_ass_head(font_style_name, args.font_size)
get_xy_obj = SameHeight("那就写这一句作为初始化测试吧!", font_path=font_path, font_size=int(args.font_size)) get_xy_obj = SameHeight("那就写这一句作为初始化测试吧!", args.range, font_path=font_path, font_size=int(args.font_size))
subtitle = ASS(file_path, get_xy_obj, font=font_style_name) subtitle = ASS(file_path, get_xy_obj, font=font_style_name)
comments = remove_same_danmu(comments) comments = remove_same_danmu(comments)
for comment in comments: for comment in comments:

View File

@@ -3,7 +3,7 @@
''' '''
# 作者: weimo # 作者: weimo
# 创建日期: 2020-01-04 19:14:43 # 创建日期: 2020-01-04 19:14:43
# 上次编辑时间 : 2020-01-28 18:38:32 # 上次编辑时间 : 2020-02-07 17:36:24
# 一个人的命运啊,当然要靠自我奋斗,但是... # 一个人的命运啊,当然要靠自我奋斗,但是...
''' '''
import re import re
@@ -134,7 +134,7 @@ def get_year_range(aid, locale="zh_cn"):
year_end = int(data["latestVideo"]["period"][:4]) year_end = int(data["latestVideo"]["period"][:4])
return list(range(year_start, year_end + 1)) return list(range(year_start, year_end + 1))
def get_vinfo_by_tvid(tvid, locale="zh_cn"): def get_vinfo_by_tvid(tvid, locale="zh_cn", isall=False):
api_url = "https://pcw-api.iqiyi.com/video/video/baseinfo/{}".format(tvid) api_url = "https://pcw-api.iqiyi.com/video/video/baseinfo/{}".format(tvid)
if locale != "zh_cn": if locale != "zh_cn":
api_url += "?locale=" + locale api_url += "?locale=" + locale
@@ -146,10 +146,33 @@ def get_vinfo_by_tvid(tvid, locale="zh_cn"):
data = json.loads(r)["data"] data = json.loads(r)["data"]
if data.__class__ != dict: if data.__class__ != dict:
return None return None
if isall:
aid = data.get("albumId")
if aid is None:
print("通过单集tvid获取合集aid失败将只下载单集的弹幕")
locale = check_video_area_by_tvid(tvid)
if locale is None:
locale = "zh_cn"
return get_vinfos(aid, locale=locale)
name = data["name"] name = data["name"]
duration = data["durationSec"] duration = data["durationSec"]
return [[name + "_" + str(duration), duration, tvid]] return [[name + "_" + str(duration), duration, tvid]]
def check_video_area_by_tvid(tvid):
api_url = "https://pcw-api.iqiyi.com/video/video/playervideoinfo?tvid={}".format(tvid)
try:
r = requests.get(api_url, headers=chrome, timeout=5).content.decode("utf-8")
except Exception as e:
print("check_video_area_by_tvid error info -->", e)
return None
data = json.loads(r)["data"]
intl_flag = data["operation_base"]["is_international"]
langs = [item["language"].lower() for item in data["operation_language_base"]]
locale = "zh_cn"
if intl_flag is False and "zh_tw" in langs:
locale = "zh_tw"
return locale
def get_vinfos_by_year(aid, years: list, cid=6, locale="zh_cn"): def get_vinfos_by_year(aid, years: list, cid=6, locale="zh_cn"):
api_url = "https://pcw-api.iqiyi.com/album/source/svlistinfo?cid={}&sourceid={}&timelist={}".format(cid, aid, ",".join([str(_) for _ in years.copy()])) api_url = "https://pcw-api.iqiyi.com/album/source/svlistinfo?cid={}&sourceid={}&timelist={}".format(cid, aid, ",".join([str(_) for _ in years.copy()]))
if locale != "zh_cn": if locale != "zh_cn":
@@ -171,7 +194,7 @@ def get_vinfos_by_year(aid, years: list, cid=6, locale="zh_cn"):
vinfos.append([ep["shortTitle"] + "_" + str(sec), sec, ep["tvId"]]) vinfos.append([ep["shortTitle"] + "_" + str(sec), sec, ep["tvId"]])
return vinfos return vinfos
def get_vinfos_by_url(url): def get_vinfos_by_url(url, isall=False):
locale = check_url_locale(url) locale = check_url_locale(url)
patterns = [".+?/w_(\w+?).html", ".+?/v_(\w+?).html", ".+?/a_(\w+?).html", ".+?/lib/m_(\w+?).html"] patterns = [".+?/w_(\w+?).html", ".+?/v_(\w+?).html", ".+?/a_(\w+?).html", ".+?/lib/m_(\w+?).html"]
isw, isep, isas, isms = [re.match(pattern, url) for pattern in patterns] isw, isep, isas, isms = [re.match(pattern, url) for pattern in patterns]
@@ -204,7 +227,7 @@ def get_vinfos_by_url(url):
if isep or isw: if isep or isw:
if tvid is None: if tvid is None:
return return
return get_vinfo_by_tvid(tvid, locale=locale) return get_vinfo_by_tvid(tvid, locale=locale, isall=isall)
if isas or isms: if isas or isms:
if aid is None: if aid is None:

View File

@@ -3,7 +3,7 @@
''' '''
# 作者: weimo # 作者: weimo
# 创建日期: 2020-01-04 19:14:41 # 创建日期: 2020-01-04 19:14:41
# 上次编辑时间 : 2020-01-16 19:58:51 # 上次编辑时间 : 2020-02-07 17:32:10
# 一个人的命运啊,当然要靠自我奋斗,但是... # 一个人的命运啊,当然要靠自我奋斗,但是...
''' '''
@@ -66,8 +66,11 @@ def get_danmu_by_tvid(name, duration, tvid):
def main(args): def main(args):
vinfos = [] vinfos = []
isall = False
if args.series:
isall = True
if args.tvid: if args.tvid:
vi = get_vinfo_by_tvid(args.tvid) vi = get_vinfo_by_tvid(args.tvid, isall=isall)
if vi: if vi:
vinfos.append(vi) vinfos.append(vi)
if args.aid: if args.aid:
@@ -77,7 +80,7 @@ def main(args):
if args.tvid == "" and args.aid == "" and args.url == "": if args.tvid == "" and args.aid == "" and args.url == "":
args.url = input("请输入iqiyi链接\n") args.url = input("请输入iqiyi链接\n")
if args.url: if args.url:
vi = get_vinfos_by_url(args.url) vi = get_vinfos_by_url(args.url, isall=isall)
if vi: if vi:
vinfos += vi vinfos += vi
subtitles = {} subtitles = {}

View File

@@ -3,7 +3,7 @@
''' '''
# 作者: weimo # 作者: weimo
# 创建日期: 2020-01-28 15:55:22 # 创建日期: 2020-01-28 15:55:22
# 上次编辑时间 : 2020-01-28 19:57:57 # 上次编辑时间 : 2020-02-07 18:32:05
# 一个人的命运啊,当然要靠自我奋斗,但是... # 一个人的命运啊,当然要靠自我奋斗,但是...
''' '''
import re import re
@@ -155,7 +155,6 @@ def get_vinfos_by_url(url: str, isall: bool):
if vinfo is None: if vinfo is None:
return return
vinfos.append(vinfo) vinfos.append(vinfo)
print("ccc", cid_v1)
if cid_v1 or cid_v2: if cid_v1 or cid_v2:
if cid_v2 is None: if cid_v2 is None:
cid = cid_v1.group(1) cid = cid_v1.group(1)

View File

@@ -3,7 +3,7 @@
''' '''
# 作者: weimo # 作者: weimo
# 创建日期: 2020-01-16 17:45:35 # 创建日期: 2020-01-16 17:45:35
# 上次编辑时间 : 2020-01-16 20:09:22 # 上次编辑时间 : 2020-02-07 18:43:55
# 一个人的命运啊,当然要靠自我奋斗,但是... # 一个人的命运啊,当然要靠自我奋斗,但是...
''' '''
import json import json
@@ -99,7 +99,7 @@ def get_vinfos(aid: str):
try: try:
r = requests.get(api_url, params=params, headers=chrome, timeout=3).content.decode("gbk") r = requests.get(api_url, params=params, headers=chrome, timeout=3).content.decode("gbk")
except Exception as e: except Exception as e:
print("get sohu (vid -> {}) videolist failed.".format(vid)) print("get sohu (aid -> {}) videolist failed.".format(aid))
return None return None
data = json.loads(r) data = json.loads(r)
if data.get("videos"): if data.get("videos"):