您的位置:首页 > 其它

酷狗 KRC 文件的解析

2014-11-18 16:40 471 查看
清理硬盘发现以前写过一个进行一半的代码,这次补全并从硬盘删掉。

格式说明来自 https://shansing.com/read/392/
krc解码并解压缩后得到一个字符串,例子:


[id:$00000000]
[ar:信乐团]
[ti:北京一夜]
[by:韩佯Τé]
[hash:766fe295bf2722a9ede2abdd61d580c1]
[total:278438]
[sign:大家去北京玩一夜吧!!!!]
[53883,3092]<0,632,0>One <632,784,0>Night <1416,372,0>in <1788,548,0>北<2336,755,0>京
[56675,3539]<0,560,0>我<560,416,0>留<976,392,0>下<1368,412,0>许<1780,392,0>多<2172,1366,0>情
[59914,2577]<0,549,0>不<549,276,0>管<825,252,0>你<1077,214,0>爱<1291,182,0>与<1473,212,0>不 <1685,887,0>爱
[62191,3344]<0,560,0>都<560,210,0>是<770,210,0>历<980,204,0>史<1184,202,0>的<1386,564,0>尘<1950,1387,0>埃


开头的几行就不用解释了,lrc也有。

其中快速匹配歌词的可能方式是靠计算歌曲文件的hash,以及匹配歌词与歌曲的total

歌词开始的行格式:


[此行开始时刻距0时刻的毫秒数,此行持续的毫秒数]<0,此字持续的毫秒数,0>歌<此字开始的时刻距此行开始时刻的毫秒数,此字持续的毫秒数,0>词<此字开始的时刻距此行开始时刻的毫秒数,此字持续的毫秒数,0>正<此字开始的时刻距此行开始时刻的毫秒数,此字持续的毫秒数,0>文



具体代码如下:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace KRC.KRCLib
{
/// <summary>
/// KRC歌词文件
/// </summary>
public class KRCLyrics
{
public List<KRCLyricsLine> Lines
{
get { return _lines; }
}

/// <summary>
/// 歌词文本
/// </summary>
public string KRCString { get; set; }

/// <summary>
/// ID (总是$00000000,意义未知)
/// </summary>
public string ID { get; set; }

/// <summary>
/// 艺术家
/// </summary>
public string Ar { get; set; }

/// <summary>
///
/// </summary>
public string Al { get; set; }

/// <summary>
/// 标题
/// </summary>
public string Title { get; set; }

/// <summary>
/// 歌词文件作者
/// </summary>
public string By { get; set; }

/// <summary>
/// 歌曲文件Hash
/// </summary>
public string Hash { get; set; }

/// <summary>
/// 总时长
/// </summary>
public TimeSpan Total
{
get
{
//计算总时间=所有行时间
var sum = this.Lines.Select(x => x.LineDuring.TotalMilliseconds).Sum();
return TimeSpan.FromMilliseconds(sum);
}
}

/// <summary>
/// 偏移
/// </summary>
public TimeSpan Offset { get; set; }

private readonly List<KRCLyricsLine> _lines = new List<KRCLyricsLine>();
private readonly List<Tuple<Regex, Action<string>>> _properties;
private readonly Regex _regGetValueFromKeyValuePair = new Regex(@"\[(.*):(.*)\]");

/// <summary>
/// 默认构造
/// </summary>
public KRCLyrics()
{
//this.Total = TimeSpan.Zero;
this.Offset = TimeSpan.Zero;

this._properties = new List<Tuple<Regex, Action<string>>>()
{
new Tuple<Regex, Action<string>>(new Regex("\\[id:[^\\]]+\\]"), (s) => { this.ID = s; }),
new Tuple<Regex, Action<string>>(new Regex("\\[al:[^\\n]+\\n"), (s) => { this.Al = s; }),
new Tuple<Regex, Action<string>>(new Regex("\\[ar:[^\\]]+\\]"), (s) => { this.Ar = s; }),
new Tuple<Regex, Action<string>>(new Regex("\\[ti:[^\\]]+\\]"), (s) => { this.Title = s; }),
new Tuple<Regex, Action<string>>(new Regex("\\[hash:[^\\n]+\\n"), (s) => { this.Hash = s; }),
new Tuple<Regex, Action<string>>(new Regex("\\[by:[^\\n]+\\n"), (s) => { this.By = s; }),
new Tuple<Regex, Action<string>>(new Regex("\\[total:[^\\n]+\\n"), (s) =>
{
//this.Total = TimeSpan.FromMilliseconds(double.Parse(s));
}),
new Tuple<Regex, Action<string>>(new Regex("\\[offset:[^\\n]+\\n"), (s) =>
{
this.Offset = TimeSpan.FromMilliseconds(double.Parse(s));
}),
};
}

/// <summary>
/// 构造
/// </summary>
/// <param name="krcstring">KRC字符文本</param>
private KRCLyrics(string krcstring):this()
{
this.KRCString = krcstring;
this.LoadProperties();
this.LoadLines();
}

/// <summary>
/// 加载KRC属性
/// </summary>
private void LoadProperties()
{
foreach (var prop in _properties)
{
var m = prop.Item1.Match(this.KRCString);
if (m.Success)
{
var mm = _regGetValueFromKeyValuePair.Match(m.Value);

if (mm.Success && mm.Groups.Count == 3)
{
prop.Item2(mm.Groups[2].Value);
}
}
}
}

/// <summary>
/// 加载KRC所有行数据
/// </summary>
private void LoadLines()
{
var linesMachCollection = Regex.Matches(this.KRCString, @"\[\d{1,}[^\n]+\n");
foreach (Match m in linesMachCollection)
{
this.Lines.Add(new KRCLyricsLine(m.Value));
}
}

/// <summary>
/// 保存到文件
/// </summary>
/// <param name="outputFilePath"></param>
public void SaveToFile(string outputFilePath)
{
var sb = new StringBuilder();
sb.AppendLine(string.Format("[id:{0}]", this.ID));

if (!string.IsNullOrEmpty(this.Al))
{
sb.AppendLine(string.Format("[al:{0}]", this.Al));
}

if (!string.IsNullOrEmpty(this.Ar))
{
sb.AppendLine(string.Format("[ar:{0}]", this.Ar));
}

if (!string.IsNullOrEmpty(this.Title))
{
sb.AppendLine(string.Format("[ti:{0}]", this.Title));
}

if (!string.IsNullOrEmpty(this.Hash))
{
sb.AppendLine(string.Format("[hash:{0}]", this.Hash));
}

if (!string.IsNullOrEmpty(this.By))
{
sb.AppendLine(string.Format("[by:{0}]", this.By));
}

if (this.Total!= TimeSpan.Zero)
{
sb.AppendLine(string.Format("[total:{0}]", this.Total.TotalMilliseconds));
}

if (this.Offset != TimeSpan.Zero)
{
sb.AppendLine(string.Format("[offset:{0}]", this.Offset.TotalMilliseconds));
}

foreach (var line in this.Lines)
{
sb.AppendLine(line.KRCLineString);
}

var bytes = KRCFile.EncodeStringToBytes(sb.ToString());

File.WriteAllBytes(outputFilePath, bytes);

}

/// <summary>
/// 从文件加载
/// </summary>
/// <param name="inputFilePath"></param>
/// <returns></returns>
public static KRCLyrics LoadFromFile(string inputFilePath)
{
var str = KRCFile.DecodeFileToString(inputFilePath);

return LoadFromString(str);
}

/// <summary>
/// 从文本加载
/// </summary>
/// <param name="krcstring"></param>
/// <returns></returns>
public static KRCLyrics LoadFromString(string krcstring)
{
var aa = new KRCLyrics(krcstring);
return aa;
}
}
}


KRCLyrics

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using KRC.KRCLib;

namespace KRC.Test
{
class Program
{
static void Main(string[] args)
{
string inputFile = @"杨钰莹.桃花运-b0c4014bd991a6a637445defa56822f9.krc";
string outputFile = @"123.krc";
KRCLyrics krc = KRCLyrics.LoadFromFile(inputFile);
Console.WriteLine("解码 [{0}] 完毕。", inputFile);
krc.SaveToFile(outputFile);
Console.WriteLine("另存为 [{0}] 完毕。", outputFile);
Console.ReadLine();
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: