/// <summary> /// 将Bilibili弹幕转换为ASS /// 作者: Kengwang /// </summary> /// <see cref="https://github.com/ikde/danmu2ass/blob/master/Danmu2Ass/PythonFile/Niconvert.py"/> /// <param name="xmlNodeList"></param> /// <param name="x"></param> /// <param name="y"></param> /// <returns></returns> public static string Convert(XmlNodeList xmlNodeList, int x, int y) { string returnstr = @"[Script Info] ScriptType: v4.00+ Collisions: Normal PlayResX: " + x.ToString() + @" PlayResY: " + y.ToString() + @" [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: BiliDuangDanmaku, Microsoft YaHei, 64, &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 "; List <DanmakuSingle> danmakulist = new List <DanmakuSingle>(); try { foreach (XmlNode xmlNode in xmlNodeList) { string text = xmlNode.InnerText; string param = xmlNode.Attributes.GetNamedItem("p").InnerText; string[] paramarr = param.Split(','); DanmakuSingle danmaku = new DanmakuSingle { timeoriginal = paramarr[0], type = (DanmakuType)int.Parse(paramarr[1]), fontsize = int.Parse(paramarr[2]), fontcolordec = int.Parse(paramarr[3]), timestamp = long.Parse(paramarr[4]), pool = int.Parse(paramarr[5]), userid = paramarr[6], rowid = long.Parse(paramarr[7]), content = text }; int timeint = int.Parse(paramarr[0].Substring(0, paramarr[0].IndexOf('.'))); danmaku.timesecond = timeint; string hour = (timeint / 3600).ToString("D2"); string minute = ((timeint - (int.Parse(hour) * 3600)) / 60).ToString("D2"); string second = ((timeint - (int.Parse(hour) * 3600) - (int.Parse(minute) * 60))).ToString("D2"); danmaku.time = hour + ":" + minute + ":" + second + "." + double.Parse(paramarr[0].Substring(paramarr[0].IndexOf(".") - 1)).ToString("F2").Substring(2); danmaku.timeelapse = hour + ":" + minute + ":" + ((timeint - (int.Parse(hour) * 3600) - (int.Parse(minute) * 60)) + 10).ToString("D2") + "." + double.Parse(paramarr[0].Substring(paramarr[0].IndexOf(".") - 1)).ToString("F2").Substring(2); danmaku.fontcolorhex = int.Parse(paramarr[3]).ToString("X6"); danmakulist.Add(danmaku); } danmakulist = danmakulist.OrderBy(d => d.timesecond).ToList(); Dictionary <int, int> screenrow = new Dictionary <int, int>(); //row endtime foreach (DanmakuSingle danmaku in danmakulist) { //整理为ASS int offset = (System.Text.Encoding.Default.GetByteCount(danmaku.content) / 2) * 5; //一个字节5像素来计算 danmaku.startx = x + offset; danmaku.endx = 0 - offset; int nowrow = 1; bool mid = false; while (true) { if (screenrow.ContainsKey(nowrow) && screenrow[nowrow] + 1 > danmaku.timesecond) { //这行有字幕不能占有 nowrow++; continue; } else { //可以占用此行 danmaku.y = nowrow * (y / 12); if (danmaku.y > y) { danmaku.y = (danmaku.y % y) + (mid ? (y / 24) : 0); mid = !mid; } screenrow[nowrow] = danmaku.timesecond; nowrow = 1; break; } } //当前是滚动来的 string asssingle = "Dialogue: 3," + danmaku.time + "," + danmaku.timeelapse + ",BiliDuangDanmaku,,0000,0000,0000,," + "{\\move(" + danmaku.startx + ", " + danmaku.y + ", " + danmaku.endx + ", " + danmaku.y + ")\\c&" + danmaku.fontcolorhex + "}" + danmaku.content + "\r\n"; returnstr += asssingle; } } catch (Exception) { } return(returnstr); }
/// <summary> /// 将Bilibili弹幕转换为ASS /// 作者: Kengwang /// </summary> /// <see cref="https://github.com/ikde/danmu2ass/blob/master/Danmu2Ass/PythonFile/Niconvert.py"/> /// <param name="xmlNodeList"></param> /// <param name="x"></param> /// <param name="y"></param> /// <returns></returns> public static string Convert(XmlNodeList xmlNodeList, int x, int y) { string returnstr = @"[Script Info] ; Script Generated by BiliDuang ScriptType: v4.00+ Collisions: Normal PlayResX: " + x.ToString() + @" PlayResY: " + y.ToString() + @" [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: BiliDuangDanmaku,Microsoft YaHei,64,&H00FFFFFF,&H00FFFFFF,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,1,0,2,20,20,20,0 [Events] Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text "; List <DanmakuSingle> danmakulist = new List <DanmakuSingle>(); try { foreach (XmlNode xmlNode in xmlNodeList) { string text = xmlNode.InnerText; string param = xmlNode.Attributes.GetNamedItem("p").InnerText; string[] paramarr = param.Split(','); DanmakuSingle danmaku = new DanmakuSingle { timeoriginal = paramarr[0], type = (DanmakuType)int.Parse(paramarr[1]), fontsize = int.Parse(paramarr[2]), fontcolordec = int.Parse(paramarr[3]), timestamp = long.Parse(paramarr[4]), pool = int.Parse(paramarr[5]), userid = paramarr[6], rowid = long.Parse(paramarr[7]), content = text }; //采用原生的时间转换,更加稳定 DateTime timein = new DateTime(long.Parse((double.Parse(danmaku.timeoriginal) * 10000000).ToString())); danmaku.realTime = timein; danmaku.timesecond = (int)(timein.TimeOfDay.TotalSeconds); danmaku.time = timein.ToString("H:mm:ss.ff");//神奇吗,要用24小时进制 if (danmaku.type == DanmakuType.Top || danmaku.type == DanmakuType.Bottom) { danmaku.timeelapse = timein.AddSeconds(5).ToString("H:mm:ss.ff"); } else { danmaku.timeelapse = timein.AddSeconds(10).ToString("H:mm:ss.ff"); } Color color = ColorTranslator.FromWin32(int.Parse(paramarr[3])); danmaku.fontcolorhex = string.Format("{0:X2}{1:X2}{2:X2}", color.R, color.G, color.B); danmakulist.Add(danmaku); } danmakulist = danmakulist.OrderBy(d => d.timesecond).ToList(); Dictionary <int, int> screenrollingrow = new Dictionary <int, int>(); //rolling danmaku row endtime Dictionary <int, int> screenstaticrow = new Dictionary <int, int>(); //static danmaku row endtime Font msyh = new Font("Microsoft Yahei", 16f); foreach (DanmakuSingle danmaku in danmakulist) {//整理为ASS string asssingle = ""; if (danmaku.type == DanmakuType.Bottom) { int nowrow = 12; //底部弹幕 while (true) { if (screenstaticrow.ContainsKey(nowrow) && screenstaticrow[nowrow] + 5 > danmaku.timesecond) { //这行有字幕不能占有 nowrow--; continue; } else { //可以占用此行 danmaku.y = nowrow * (y / 12); screenstaticrow[nowrow] = danmaku.timesecond; nowrow = 1; break; } } asssingle = "Dialogue: 4," + danmaku.time + "," + danmaku.timeelapse + ",BiliDuangDanmaku,,0,0,0,," + "{\\pos(" + x / 2 + ", " + danmaku.y + ")\\c&" + danmaku.fontcolorhex + "}" + danmaku.content + "\r\n"; } else if (danmaku.type == DanmakuType.Top) { int nowrow = 1; //底部弹幕 while (true) { if (screenstaticrow.ContainsKey(nowrow) && screenstaticrow[nowrow] + 5 > danmaku.timesecond) { //这行有字幕不能占有 nowrow++; continue; } else { //可以占用此行 danmaku.y = nowrow * (y / 12); screenstaticrow[nowrow] = danmaku.timesecond; nowrow = 1; break; } } asssingle = "Dialogue: 4," + danmaku.time + "," + danmaku.timeelapse + ",BiliDuangDanmaku,,0,0,0,," + "{\\pos(" + x / 2 + ", " + danmaku.y + ")\\c&" + danmaku.fontcolorhex + "}" + danmaku.content + "\r\n"; } else {//滚动弹幕 int offset = (int)(new Control().CreateGraphics().MeasureString(danmaku.content, msyh).Width); danmaku.startx = x + offset; danmaku.endx = 0 - offset; int nowrow = 1; bool mid = false; while (true) { if (screenrollingrow.ContainsKey(nowrow) && screenrollingrow[nowrow] + (System.Text.Encoding.Default.GetByteCount(danmaku.content) / 2) > danmaku.timesecond) { //这行有字幕不能占有 nowrow++; continue; } else { //可以占用此行 danmaku.y = nowrow * (y / 12); if (danmaku.y > y) { danmaku.y = (danmaku.y % y) + (mid ? (y / 24) : 0); mid = !mid; } screenrollingrow[nowrow] = danmaku.timesecond; nowrow = 1; break; } } //当前是滚动来的 asssingle = "Dialogue: 3," + danmaku.time + "," + danmaku.timeelapse + ",BiliDuangDanmaku,,0,0,0,," + "{\\move(" + danmaku.startx + ", " + danmaku.y + ", " + danmaku.endx + ", " + danmaku.y + ")\\c&" + danmaku.fontcolorhex + "}" + danmaku.content + "\r\n"; } returnstr += asssingle; } } catch (Exception) { } return(returnstr); }