ytbchat2ass.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. # -*- coding:utf-8 -*-
  2. from chat_downloader import ChatDownloader
  3. import sys
  4. import math
  5. import urllib.request
  6. import re
  7. # 目前仅支持开启了chat的回放,看不懂chat-downloader的源代码,甚至不想把直播安排进todo
  8. def sec2hms(sec):# 时间转换
  9. hms = str(int(sec//3600)).zfill(2)+':' + \
  10. str(int((sec % 3600)//60)).zfill(2)+':'+str(round(sec % 60, 2))
  11. return hms
  12. url = 'youtu.be/'+sys.argv[1]
  13. html = urllib.request.urlopen("https://www.youtube.com/watch?v="+sys.argv[1]).read().decode('utf-8')
  14. name = [] #预留加人用
  15. title = re.findall("<title>(.+?)</title>",html)[0].replace(' - YouTube','')
  16. name += re.findall('itemprop="name" content="(.+?)">',html)
  17. chat = ChatDownloader().get_chat(url,message_groups=['messages','superchat']) #默认普通评论和sc
  18. count = 0
  19. limitLineAmount = 12 # 屏上弹幕行数限制
  20. danmakuPassageway = [] # 塞弹幕用,记录每行上一条弹幕的消失时间
  21. for i in range(limitLineAmount):
  22. danmakuPassageway.append(0)
  23. fontName = 'Source Han Sans JP' # 字体自己换
  24. videoWidth = 1280 # 视频宽度,按720P处理了后面的内容,不建议改
  25. videoHeight = 720 # 视频高度
  26. OfficeBgHeight = 72
  27. OfficeSize = 36
  28. fontSize = 58
  29. head = '[Script Info]\n\
  30. ; Script generated by Aegisub 3.2.2\n\
  31. ; http://www.aegisub.org/\n\
  32. ScriptType: v4.00+\n\
  33. PlayResX: 1280\n\
  34. PlayResY: 720\n\
  35. \n\
  36. [V4+ Styles]\n\
  37. Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, marginL, marginR, marginV, Encoding\n\
  38. Style: Default,微软雅黑,54,&H00FFFFFF,&H00FFFFFF,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,2,0,2,0,0,0,0\n\
  39. Style: Alternate,微软雅黑,36,&H00FFFFFF,&H00FFFFFF,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,2,0,2,0,0,0,0\n\
  40. Style: Office,'+fontName+','+str(OfficeSize)+',&H00FFFFFF,&H00FFFFFF,&H00000000,&H00000000,-1,0,0,0,100,100,2,0,1,1.5,0,2,0,0,10,0\n\
  41. Style: Danmaku,'+fontName+','+str(fontSize)+',&H00FFFFFF,&H00FFFFFF,&H00000000,&H00000000,-1,0,0,0,100,100,2,0,1,1.5,0,2,0,0,10,0\n\n\
  42. [Events]\n\
  43. Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n'
  44. f = open(sys.argv[1]+'.ass','w',encoding='utf-8-sig')
  45. f.write(head)
  46. for message in chat:
  47. if message['time_in_seconds'] > 0:
  48. vpos = message['time_in_seconds']
  49. vpos_end = vpos+8 # 普通弹幕的时长,默认8秒
  50. else:
  51. continue
  52. if 'name' not in message['author'].keys():
  53. continue
  54. if 'money' in message.keys():
  55. text = '('+str(message['money']['amount'])+message['money']['currency']+')' # 打钱的标上数额
  56. if 'message' in message.keys():
  57. if message['message']:
  58. text += message['message'] # 打钱有留言的加上
  59. vpos_end += 2 # 打钱的多给2秒
  60. else:
  61. text=message['author']['name']+': '+message['message'] if message['author']['name'] in name else message['message'] # 没打钱的直接记录弹幕,设置了的号加上账号名字
  62. if 'emotes' in message.keys():
  63. for i in message['emotes']:
  64. if i['is_custom_emoji']:
  65. text = text.replace(i['name'],'')
  66. else:
  67. text = text.replace(i['name'],i['id'])
  68. if text:
  69. if len(text) == 0:
  70. continue
  71. else:
  72. continue
  73. if message['author']['name'] in name: # 特定账号的弹幕放上面并加上背景
  74. f.write('Dialogue: 4,'+sec2hms(vpos)+','+sec2hms(vpos_end)+',Office,,0,0,0,,{\\an5\\p1\\pos('+str(videoWidth/2)+','+str(math.floor(OfficeBgHeight/2))+')\\bord0\\1c&H000000&\\1a&H78&}'+'m 0 0 l '+str(videoWidth)+' 0 l '+str(videoWidth) + ' '+str(OfficeBgHeight)+' l 0 '+str(OfficeBgHeight)+'\n')
  75. f.write('Dialogue: 5,'+sec2hms(vpos)+','+sec2hms(vpos_end)+',Office,,0,0,0,,{\\an5\\pos('+str(videoWidth/2)+','+str(math.floor(OfficeBgHeight/2))+')\\bord0\\fsp0}'+text+'\n')
  76. count += 1
  77. else: # 其他人的弹幕放滚动
  78. vpos_next_min = float('inf')
  79. vpos_next = vpos+1280/(len(text)*60+1280) * 8
  80. for i in range(limitLineAmount):
  81. if vpos_next >= danmakuPassageway[i]:
  82. passageway_index = i
  83. danmakuPassageway[i] = vpos+8
  84. break
  85. elif danmakuPassageway[i] < vpos_next_min:
  86. vpos_next_min = danmakuPassageway[i]
  87. Passageway_min = i
  88. if i == limitLineAmount-1 and vpos_next < vpos_next_min:
  89. passageway_index = Passageway_min
  90. danmakuPassageway[Passageway_min] = vpos+8
  91. # 计算弹幕位置
  92. sx = videoWidth
  93. sy = fontSize*(passageway_index)
  94. ex = 0
  95. for i in text:
  96. if re.search("[A-Za-z 0-9',.]",i):
  97. ex = ex-30
  98. else:
  99. ex = ex-60
  100. ey = fontSize*(passageway_index)
  101. f.write('Dialogue: 0,'+sec2hms(vpos)+','+ sec2hms(vpos_end) + ',Danmaku,'+message['author']['name'].replace(',','')+',0,0,0,,{\\an7\\move('+str(sx)+','+str(sy)+','+str(ex)+','+str(ey)+')}'+text+'\n')
  102. count += 1
  103. f.close()
  104. print(title+'的弹幕已经存为'+sys.argv[1]+'.ass,共'+str(count)+'条')