Compare commits
10 commits
3dd20e41ea
...
7453e571fc
Author | SHA1 | Date | |
---|---|---|---|
7453e571fc | |||
b53fc11b37 | |||
![]() |
f71ef49f26 | ||
![]() |
0925edeb17 | ||
![]() |
416aa04c38 | ||
![]() |
4c4de516a1 | ||
![]() |
1add7b3b52 | ||
![]() |
ce41af1468 | ||
![]() |
bb8f1cee06 | ||
![]() |
ed1694999c |
3 changed files with 55 additions and 16 deletions
2
Makefile
2
Makefile
|
@ -19,7 +19,7 @@ all:
|
||||||
|
|
||||||
install:
|
install:
|
||||||
$(MKDIR) "$(DESTDIR)$(PREFIX)/share/danmaku2ass"
|
$(MKDIR) "$(DESTDIR)$(PREFIX)/share/danmaku2ass"
|
||||||
$(INSTALL) -Dm0755 danmaku2ass.py "$(DESTDIR)$(PREFIX)/share/danmaku2ass/danmaku2ass.py"
|
$(INSTALL) -m0755 danmaku2ass.py "$(DESTDIR)$(PREFIX)/share/danmaku2ass/danmaku2ass.py"
|
||||||
for locale in en ja zh_CN zh_TW; do \
|
for locale in en ja zh_CN zh_TW; do \
|
||||||
$(MKDIR) "$(DESTDIR)$(PREFIX)/share/locale/$$locale/LC_MESSAGES" ; \
|
$(MKDIR) "$(DESTDIR)$(PREFIX)/share/locale/$$locale/LC_MESSAGES" ; \
|
||||||
$(CP) "po/$$locale.mo" "$(DESTDIR)$(PREFIX)/share/locale/$$locale/LC_MESSAGES/danmaku2ass.mo" ; \
|
$(CP) "po/$$locale.mo" "$(DESTDIR)$(PREFIX)/share/locale/$$locale/LC_MESSAGES/danmaku2ass.mo" ; \
|
||||||
|
|
|
@ -97,6 +97,8 @@ optional arguments:
|
||||||
Duration of still comment display [default: 5]
|
Duration of still comment display [default: 5]
|
||||||
-fl FILTER, --filter FILTER
|
-fl FILTER, --filter FILTER
|
||||||
Regular expression to filter comments
|
Regular expression to filter comments
|
||||||
|
-flf FILTER_FILE, --filter-file FILTER_FILE
|
||||||
|
Regular expressions from file (one line one regex) to filter comments
|
||||||
-p HEIGHT, --protect HEIGHT
|
-p HEIGHT, --protect HEIGHT
|
||||||
Reserve blank on the bottom of the stage
|
Reserve blank on the bottom of the stage
|
||||||
-r, --reduce Reduce the amount of comments if stage is full
|
-r, --reduce Reduce the amount of comments if stage is full
|
||||||
|
|
|
@ -74,6 +74,8 @@ def ProbeCommentFormat(f):
|
||||||
return 'Niconico'
|
return 'Niconico'
|
||||||
elif tmp == 'xml version="1.0" encoding="UTF-8"?><i':
|
elif tmp == 'xml version="1.0" encoding="UTF-8"?><i':
|
||||||
return 'Bilibili'
|
return 'Bilibili'
|
||||||
|
elif tmp == 'xml version="2.0" encoding="UTF-8"?><i':
|
||||||
|
return 'Bilibili2'
|
||||||
elif tmp == 'xml version="1.0" encoding="utf-8"?><i':
|
elif tmp == 'xml version="1.0" encoding="utf-8"?><i':
|
||||||
return 'Bilibili' # tucao.cc, with the same file format as Bilibili
|
return 'Bilibili' # tucao.cc, with the same file format as Bilibili
|
||||||
elif tmp == 'xml version="1.0" encoding="Utf-8"?>\n<':
|
elif tmp == 'xml version="1.0" encoding="Utf-8"?>\n<':
|
||||||
|
@ -142,7 +144,7 @@ def ReadCommentsNiconico(f, fontsize):
|
||||||
size = fontsize * 0.64
|
size = fontsize * 0.64
|
||||||
elif mailstyle in NiconicoColorMap:
|
elif mailstyle in NiconicoColorMap:
|
||||||
color = NiconicoColorMap[mailstyle]
|
color = NiconicoColorMap[mailstyle]
|
||||||
yield (max(int(comment.getAttribute('vpos')), 0) * 0.01, int(comment.getAttribute('date')), int(comment.getAttribute('no')), c, pos, color, size, (c.count('\n') + 1) * size, CalculateLength(c) * size)
|
yield (max(int(comment.getAttribute('vpos')), 0) * 0.01, int(comment.getAttribute('date')), 0, c, pos, color, size, (c.count('\n') + 1) * size, CalculateLength(c) * size)
|
||||||
except (AssertionError, AttributeError, IndexError, TypeError, ValueError):
|
except (AssertionError, AttributeError, IndexError, TypeError, ValueError):
|
||||||
logging.warning(_('Invalid comment: %s') % comment.toxml())
|
logging.warning(_('Invalid comment: %s') % comment.toxml())
|
||||||
continue
|
continue
|
||||||
|
@ -194,6 +196,30 @@ def ReadCommentsBilibili(f, fontsize):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
||||||
|
def ReadCommentsBilibili2(f, fontsize):
|
||||||
|
dom = xml.dom.minidom.parse(f)
|
||||||
|
comment_element = dom.getElementsByTagName('d')
|
||||||
|
for i, comment in enumerate(comment_element):
|
||||||
|
try:
|
||||||
|
p = str(comment.getAttribute('p')).split(',')
|
||||||
|
assert len(p) >= 7
|
||||||
|
assert p[3] in ('1', '4', '5', '6', '7', '8')
|
||||||
|
if comment.childNodes.length > 0:
|
||||||
|
time = float(p[2]) / 1000.0
|
||||||
|
if p[3] in ('1', '4', '5', '6'):
|
||||||
|
c = str(comment.childNodes[0].wholeText).replace('/n', '\n')
|
||||||
|
size = int(p[4]) * fontsize / 25.0
|
||||||
|
yield (time, int(p[6]), i, c, {'1': 0, '4': 2, '5': 1, '6': 3}[p[3]], int(p[5]), size, (c.count('\n') + 1) * size, CalculateLength(c) * size)
|
||||||
|
elif p[3] == '7': # positioned comment
|
||||||
|
c = str(comment.childNodes[0].wholeText)
|
||||||
|
yield (time, int(p[6]), i, c, 'bilipos', int(p[5]), int(p[4]), 0, 0)
|
||||||
|
elif p[3] == '8':
|
||||||
|
pass # ignore scripted comment
|
||||||
|
except (AssertionError, AttributeError, IndexError, TypeError, ValueError):
|
||||||
|
logging.warning(_('Invalid comment: %s') % comment.toxml())
|
||||||
|
continue
|
||||||
|
|
||||||
|
|
||||||
def ReadCommentsTudou(f, fontsize):
|
def ReadCommentsTudou(f, fontsize):
|
||||||
comment_element = json.load(f)
|
comment_element = json.load(f)
|
||||||
for i, comment in enumerate(comment_element['comment_list']):
|
for i, comment in enumerate(comment_element['comment_list']):
|
||||||
|
@ -244,7 +270,7 @@ def ReadCommentsMioMio(f, fontsize):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
||||||
CommentFormatMap = {'Niconico': ReadCommentsNiconico, 'Acfun': ReadCommentsAcfun, 'Bilibili': ReadCommentsBilibili, 'Tudou': ReadCommentsTudou, 'Tudou2': ReadCommentsTudou2, 'MioMio': ReadCommentsMioMio}
|
CommentFormatMap = {'Niconico': ReadCommentsNiconico, 'Acfun': ReadCommentsAcfun, 'Bilibili': ReadCommentsBilibili, 'Bilibili2': ReadCommentsBilibili2, 'Tudou': ReadCommentsTudou, 'Tudou2': ReadCommentsTudou2, 'MioMio': ReadCommentsMioMio}
|
||||||
|
|
||||||
|
|
||||||
def WriteCommentBilibiliPositioned(f, c, width, height, styleid):
|
def WriteCommentBilibiliPositioned(f, c, width, height, styleid):
|
||||||
|
@ -371,7 +397,7 @@ def WriteCommentAcfunPositioned(f, c, width, height, styleid):
|
||||||
try:
|
try:
|
||||||
comment_args = c[3]
|
comment_args = c[3]
|
||||||
text = ASSEscape(str(comment_args['n']).replace('\r', '\n'))
|
text = ASSEscape(str(comment_args['n']).replace('\r', '\n'))
|
||||||
common_styles = ['\org(%d, %d)' % (width / 2, height / 2)]
|
common_styles = ['\\org(%d, %d)' % (width / 2, height / 2)]
|
||||||
anchor = {0: 7, 1: 8, 2: 9, 3: 4, 4: 5, 5: 6, 6: 1, 7: 2, 8: 3}.get(comment_args.get('c', 0), 7)
|
anchor = {0: 7, 1: 8, 2: 9, 3: 4, 4: 5, 5: 6, 6: 1, 7: 2, 8: 3}.get(comment_args.get('c', 0), 7)
|
||||||
if anchor != 7:
|
if anchor != 7:
|
||||||
common_styles.append('\\an%s' % anchor)
|
common_styles.append('\\an%s' % anchor)
|
||||||
|
@ -512,7 +538,7 @@ def ConvertFlashRotation(rotY, rotZ, X, Y, width, height):
|
||||||
return (trX, trY, WrapAngle(outX), WrapAngle(outY), WrapAngle(outZ), scaleXY * 100, scaleXY * 100)
|
return (trX, trY, WrapAngle(outX), WrapAngle(outY), WrapAngle(outZ), scaleXY * 100, scaleXY * 100)
|
||||||
|
|
||||||
|
|
||||||
def ProcessComments(comments, f, width, height, bottomReserved, fontface, fontsize, alpha, duration_marquee, duration_still, filter_regex, reduced, progress_callback):
|
def ProcessComments(comments, f, width, height, bottomReserved, fontface, fontsize, alpha, duration_marquee, duration_still, filters_regex, reduced, progress_callback):
|
||||||
styleid = 'Danmaku2ASS_%04x' % random.randint(0, 0xffff)
|
styleid = 'Danmaku2ASS_%04x' % random.randint(0, 0xffff)
|
||||||
WriteASSHead(f, width, height, fontface, fontsize, alpha, styleid)
|
WriteASSHead(f, width, height, fontface, fontsize, alpha, styleid)
|
||||||
rows = [[None] * (height - bottomReserved + 1) for i in range(4)]
|
rows = [[None] * (height - bottomReserved + 1) for i in range(4)]
|
||||||
|
@ -520,7 +546,12 @@ def ProcessComments(comments, f, width, height, bottomReserved, fontface, fontsi
|
||||||
if progress_callback and idx % 1000 == 0:
|
if progress_callback and idx % 1000 == 0:
|
||||||
progress_callback(idx, len(comments))
|
progress_callback(idx, len(comments))
|
||||||
if isinstance(i[4], int):
|
if isinstance(i[4], int):
|
||||||
|
skip = False
|
||||||
|
for filter_regex in filters_regex:
|
||||||
if filter_regex and filter_regex.search(i[3]):
|
if filter_regex and filter_regex.search(i[3]):
|
||||||
|
skip = True
|
||||||
|
break
|
||||||
|
if skip:
|
||||||
continue
|
continue
|
||||||
row = 0
|
row = 0
|
||||||
rowmax = height - bottomReserved - i[7]
|
rowmax = height - bottomReserved - i[7]
|
||||||
|
@ -726,12 +757,17 @@ def export(func):
|
||||||
|
|
||||||
|
|
||||||
@export
|
@export
|
||||||
def Danmaku2ASS(input_files, input_format, output_file, stage_width, stage_height, reserve_blank=0, font_face=_('(FONT) sans-serif')[7:], font_size=25.0, text_opacity=1.0, duration_marquee=5.0, duration_still=5.0, comment_filter=None, is_reduce_comments=False, progress_callback=None):
|
def Danmaku2ASS(input_files, input_format, output_file, stage_width, stage_height, reserve_blank=0, font_face=_('(FONT) sans-serif')[7:], font_size=25.0, text_opacity=1.0, duration_marquee=5.0, duration_still=5.0, comment_filter=None, comment_filters_file=None, is_reduce_comments=False, progress_callback=None):
|
||||||
|
comment_filters = [comment_filter]
|
||||||
|
if comment_filters_file:
|
||||||
|
with open(comment_filters_file, 'r') as f:
|
||||||
|
d = f.readlines()
|
||||||
|
comment_filters.extend([i.strip() for i in d])
|
||||||
|
filters_regex = []
|
||||||
|
for comment_filter in comment_filters:
|
||||||
try:
|
try:
|
||||||
if comment_filter:
|
if comment_filter:
|
||||||
filter_regex = re.compile(comment_filter)
|
filters_regex.append(re.compile(comment_filter))
|
||||||
else:
|
|
||||||
filter_regex = None
|
|
||||||
except:
|
except:
|
||||||
raise ValueError(_('Invalid regular expression: %s') % comment_filter)
|
raise ValueError(_('Invalid regular expression: %s') % comment_filter)
|
||||||
fo = None
|
fo = None
|
||||||
|
@ -741,7 +777,7 @@ def Danmaku2ASS(input_files, input_format, output_file, stage_width, stage_heigh
|
||||||
fo = ConvertToFile(output_file, 'w', encoding='utf-8-sig', errors='replace', newline='\r\n')
|
fo = ConvertToFile(output_file, 'w', encoding='utf-8-sig', errors='replace', newline='\r\n')
|
||||||
else:
|
else:
|
||||||
fo = sys.stdout
|
fo = sys.stdout
|
||||||
ProcessComments(comments, fo, stage_width, stage_height, reserve_blank, font_face, font_size, text_opacity, duration_marquee, duration_still, filter_regex, is_reduce_comments, progress_callback)
|
ProcessComments(comments, fo, stage_width, stage_height, reserve_blank, font_face, font_size, text_opacity, duration_marquee, duration_still, filters_regex, is_reduce_comments, progress_callback)
|
||||||
finally:
|
finally:
|
||||||
if output_file and fo != output_file:
|
if output_file and fo != output_file:
|
||||||
fo.close()
|
fo.close()
|
||||||
|
@ -800,6 +836,7 @@ def main():
|
||||||
parser.add_argument('-dm', '--duration-marquee', metavar=_('SECONDS'), help=_('Duration of scrolling comment display [default: %s]') % 5, type=float, default=5.0)
|
parser.add_argument('-dm', '--duration-marquee', metavar=_('SECONDS'), help=_('Duration of scrolling comment display [default: %s]') % 5, type=float, default=5.0)
|
||||||
parser.add_argument('-ds', '--duration-still', metavar=_('SECONDS'), help=_('Duration of still comment display [default: %s]') % 5, type=float, default=5.0)
|
parser.add_argument('-ds', '--duration-still', metavar=_('SECONDS'), help=_('Duration of still comment display [default: %s]') % 5, type=float, default=5.0)
|
||||||
parser.add_argument('-fl', '--filter', help=_('Regular expression to filter comments'))
|
parser.add_argument('-fl', '--filter', help=_('Regular expression to filter comments'))
|
||||||
|
parser.add_argument('-flf', '--filter-file', help=_('Regular expressions from file (one line one regex) to filter comments'))
|
||||||
parser.add_argument('-p', '--protect', metavar=_('HEIGHT'), help=_('Reserve blank on the bottom of the stage'), type=int, default=0)
|
parser.add_argument('-p', '--protect', metavar=_('HEIGHT'), help=_('Reserve blank on the bottom of the stage'), type=int, default=0)
|
||||||
parser.add_argument('-r', '--reduce', action='store_true', help=_('Reduce the amount of comments if stage is full'))
|
parser.add_argument('-r', '--reduce', action='store_true', help=_('Reduce the amount of comments if stage is full'))
|
||||||
parser.add_argument('file', metavar=_('FILE'), nargs='+', help=_('Comment file to be processed'))
|
parser.add_argument('file', metavar=_('FILE'), nargs='+', help=_('Comment file to be processed'))
|
||||||
|
@ -810,7 +847,7 @@ def main():
|
||||||
height = int(height)
|
height = int(height)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise ValueError(_('Invalid stage size: %r') % args.size)
|
raise ValueError(_('Invalid stage size: %r') % args.size)
|
||||||
Danmaku2ASS(args.file, args.format, args.output, width, height, args.protect, args.font, args.fontsize, args.alpha, args.duration_marquee, args.duration_still, args.filter, args.reduce)
|
Danmaku2ASS(args.file, args.format, args.output, width, height, args.protect, args.font, args.fontsize, args.alpha, args.duration_marquee, args.duration_still, args.filter, args.filter_file, args.reduce)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue