Update source
This commit is contained in:
		
							parent
							
								
									54dec22616
								
							
						
					
					
						commit
						1bb8360c07
					
				
					 1 changed files with 114 additions and 5 deletions
				
			
		
							
								
								
									
										119
									
								
								convert.py
									
										
									
									
									
								
							
							
						
						
									
										119
									
								
								convert.py
									
										
									
									
									
								
							|  | @ -1,16 +1,85 @@ | ||||||
| #!/usr/bin/env python3 | #!/usr/bin/env python3 | ||||||
| 
 | 
 | ||||||
| import argparse | import argparse | ||||||
|  | import colorsys | ||||||
| import logging | import logging | ||||||
|  | import math | ||||||
| import sys | import sys | ||||||
| import xml.dom.minidom | import xml.dom.minidom | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def ProcessComments(comments, f, width, height, bottomReserved, fontface, fontsize, reduced): | def ProcessComments(comments, f, width, height, bottomReserved, fontface, fontsize, lifetime, reduced): | ||||||
|     rows = [None]*(height-bottomReserved) |     WriteASSHead(f, width, height, fontface, fontsize) | ||||||
|  |     rows = [[None]*(height-bottomReserved), [None]*(height-bottomReserved), [None]*(height-bottomReserved)] | ||||||
|  |     for i in comments: | ||||||
|  |         row = 0 | ||||||
|  |         rowmax = height-bottomReserved-i[7] | ||||||
|  |         while row < rowmax: | ||||||
|  |             freerows = TestFreeRows(rows, i, row, width, height, bottomReserved, lifetime) | ||||||
|  |             if freerows >= i[7]: | ||||||
|  |                 MarkCommentRow(rows, i, row) | ||||||
|  |                 WriteComment(f, i, row, width, height, bottomReserved, fontsize, lifetime) | ||||||
|  |                 break | ||||||
|  |             else: | ||||||
|  |                 row += freerows or 1 | ||||||
|  |         else: | ||||||
|  |             if not reduced: | ||||||
|  |                 row = FindAlternativeRow(rows, i, height, bottomReserved) | ||||||
|  |                 MarkCommentRow(rows, i, row) | ||||||
|  |                 WriteComment(f, i, row, width, height, bottomReserved, fontsize, lifetime) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def TestFreeRows(rows, c, row, width, height, bottomReserved, lifetime): | ||||||
|  |     res = 0 | ||||||
|  |     rowmax = height-bottomReserved-c[7] | ||||||
|  |     while row < rowmax and res < c[7]: | ||||||
|  |         if c[4] in (1, 2): | ||||||
|  |             if rows[c[4]][row] and rows[c[4]][row][0]+lifetime > c[0]: | ||||||
|  |                 break | ||||||
|  |         else: | ||||||
|  |             if rows[c[4]][row] and rows[c[4]][row][0]+lifetime*c[8]/width > c[0]: | ||||||
|  |                 break | ||||||
|  |         row += 1 | ||||||
|  |         res += 1 | ||||||
|  |     return res | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def FindAlternativeRow(rows, c, height, bottomReserved): | ||||||
|  |     res = 0 | ||||||
|  |     for row in range(height-bottomReserved-math.ceil(c[7])): | ||||||
|  |         if not rows[c[4]][row]: | ||||||
|  |             return row | ||||||
|  |         elif rows[c[4]][row][1] < rows[c[4]][res][1]: | ||||||
|  |             res = row | ||||||
|  |     return res | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def MarkCommentRow(rows, c, row): | ||||||
|  |     for i in range(row, row+math.ceil(c[7])): | ||||||
|  |         rows[c[4]][i] = c | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def WriteComment(f, c, row, width, height, bottomReserved, fontsize, lifetime): | ||||||
|  |     text = c[3].replace('\\', '\\\\').replace('\n', '\\n') | ||||||
|  |     if c[4] == 1: | ||||||
|  |         styles = '{\\a2}{\\pos(%(halfwidth)s, %(row)s)}' % {'halfwidth': round(width/2), 'row': row} | ||||||
|  |     elif c[4] == 2: | ||||||
|  |         styles = '{\\a6}{\\pos(%(halfwidth)s, %(row)s)}' % {'halfwidth': round(width/2), 'row': ConvertType2(row, height, bottomReserved)} | ||||||
|  |     else: | ||||||
|  |         styles = '{\\a1}{\\move(%(width)s, %(row)s, %(neglen)s, %(row)s)}' % {'width': width, 'row': row, 'neglen': -math.ceil(c[8])} | ||||||
|  |     if -1 < c[6]-fontsize < 1: | ||||||
|  |         styles += '{\\fs%s}' % round(c[6]) | ||||||
|  |     if c[5] != 0xffffff: | ||||||
|  |         styles += '{\\c&H%02X%02X%02x&}' % (c[5]&0xff, (c[5]>>8)&0xff, (c[5]>>16)&0xff) | ||||||
|  |         if c[5] == 0x000000: | ||||||
|  |             styles += '{\\3c&HFFFFFF&}' | ||||||
|  |         else: | ||||||
|  |             styles += '{\\bord0}' | ||||||
|  |     f.write('Dialogue: 3,%(start)s,%(end)s,Default,,0000,0000,0000,,%(styles)s%(text)s\n' % {'start': ConvertTimestamp(c[0]), 'end': ConvertTimestamp(c[0]+lifetime), 'styles': styles, 'text': text}) | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| def ReadCommentsBilibili(f, fontsize): | def ReadCommentsBilibili(f, fontsize): | ||||||
|     'Output format: [(timeline, timestamp, no, comment, type, color, size, height)]' |     'Output format: [(timeline, timestamp, no, comment, type, color, size, height, width)]' | ||||||
|     dom = xml.dom.minidom.parse(f) |     dom = xml.dom.minidom.parse(f) | ||||||
|     comment_element = dom.getElementsByTagName('d') |     comment_element = dom.getElementsByTagName('d') | ||||||
|     i = 0 |     i = 0 | ||||||
|  | @ -20,19 +89,59 @@ def ReadCommentsBilibili(f, fontsize): | ||||||
|             assert len(p) >= 8 |             assert len(p) >= 8 | ||||||
|             assert p[1] in ('1', '4', '5') |             assert p[1] in ('1', '4', '5') | ||||||
|             c = str(comment.childNodes[0].wholeText).replace('/n', '\\n') |             c = str(comment.childNodes[0].wholeText).replace('/n', '\\n') | ||||||
|             yield (float(p[0]), int(p[4]), i, c, {'1': 0, '4': 2, '5': 1}[p[1]], int(p[3]), int(p[2])/25.0, (c.count('\\n')+1)*int(p[2])/25.0) |             size = int(p[2])*fontsize/25.0 | ||||||
|  |             yield (float(p[0]), int(p[4]), i, c, {'1': 0, '4': 2, '5': 1}[p[1]], int(p[3]), size, (c.count('\\n')+1)*size, CalculateLength(c)*size) | ||||||
|             i += 1 |             i += 1 | ||||||
|         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 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | def ConvertTimestamp(timestamp): | ||||||
|  |     hour, minute = divmod(timestamp, 3600) | ||||||
|  |     minute, second = divmod(minute, 60) | ||||||
|  |     centsecond = int(second*100.0) | ||||||
|  |     return '%d:%02d:%02d.%02d' % (int(hour), int(minute), int(second), centsecond) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def WriteASSHead(f, width, height, fontface, fontsize): | ||||||
|  |     f.write( | ||||||
|  | '''[Script Info] | ||||||
|  | ScriptType: v4.00+ | ||||||
|  | Collisions: Normal | ||||||
|  | PlayResX: %(width)s | ||||||
|  | PlayResY: %(height)s | ||||||
|  | 
 | ||||||
|  | [V4+ Styles] | ||||||
|  | Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding | ||||||
|  | Style: Default, %(fontface)s, %(fontsize)s, &H00FFFFFF, &H00FFFFFF, &H00000000, &H00000000, 0, 0, 0, 0, 100, 100, 0.00, 0.00, 1, 1, 0, 2, 20, 20, 20, 0 | ||||||
|  | 
 | ||||||
|  | [Events] | ||||||
|  | Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text | ||||||
|  | ''' % {'width': width, 'height': height, 'fontface': fontface, 'fontsize': fontsize} | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def NeedWhiteBorder(rgb): | ||||||
|  |     h, l, s = colorsys.rgb_to_hls(((rgb>>16)&0xff)/255, ((rgb>>8)&0xff)/255, (rgb&0xff)/255) | ||||||
|  |     return (1/12 < h < 7/12 and l < 1/3) or l < 5/12 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def CalculateLength(s): | ||||||
|  |     return max(map(len, s.split('\\n'))) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def ConvertType2(row, height, bottomReserved): | ||||||
|  |     return height-bottomReserved-row | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| if __name__ == '__main__': | if __name__ == '__main__': | ||||||
|     parser = argparse.ArgumentParser() |     parser = argparse.ArgumentParser() | ||||||
|     parser.add_argument('-o', '--output', metavar='OUTPUT', help='Output file') |     parser.add_argument('-o', '--output', metavar='OUTPUT', help='Output file') | ||||||
|     parser.add_argument('-s', '--size', metavar='WIDTHxHEIGHT', required=True, help='Stage size in pixels') |     parser.add_argument('-s', '--size', metavar='WIDTHxHEIGHT', required=True, help='Stage size in pixels') | ||||||
|     parser.add_argument('-fn', '--font', metavar='FONT', help='Specify font face', default='黑体') |     parser.add_argument('-fn', '--font', metavar='FONT', help='Specify font face', default='黑体') | ||||||
|     parser.add_argument('-fs', '--fontsize', metavar='SIZE', help='Default font size', type=float, default=25.0) |     parser.add_argument('-fs', '--fontsize', metavar='SIZE', help='Default font size', type=float, default=25.0) | ||||||
|  |     parser.add_argument('-l', '--lifetime', metavar='SECONDS', help='Duration of comment display', type=float, default=5.0) | ||||||
|     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 danmakus if stage is full') |     parser.add_argument('-r', '--reduce', action='store_true', help='Reduce the amount of danmakus 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') | ||||||
|  | @ -53,6 +162,6 @@ if __name__ == '__main__': | ||||||
|     else: |     else: | ||||||
|         fo = sys.stdout |         fo = sys.stdout | ||||||
|     comments.sort() |     comments.sort() | ||||||
|     ProcessComments(comments, fo, width, height, args.protect, args.font, args.fontsize, args.reduce) |     ProcessComments(comments, fo, width, height, args.protect, args.font, args.fontsize, args.lifetime, args.reduce) | ||||||
|     if args.output: |     if args.output: | ||||||
|         fo.close() |         fo.close() | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Star Brilliant
						Star Brilliant