清理硬盘发现以前写过一个进行一半的代码,这次补全并从硬盘删掉。

格式说明来自 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.Linq;
using System.Text;
using System.IO;
using System.IO.Compression;
using ICSharpCode.SharpZipLib.Zip.Compression;
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
using System.Diagnostics; namespace KRC.KRCLib
{
public static class KRCFile
{
/// <summary>
/// 异或加密 密钥
/// </summary>
public static readonly char[] KRCFileXorKey = { '@', 'G', 'a', 'w', '^', '', 't', 'G', 'Q', '', '', '-', 'Î', 'Ò', 'n', 'i' }; /// <summary>
/// KRC 文件头
/// </summary>
public static readonly char[] KRCFileHead = { 'k', 'r', 'c', '' }; /// <summary>
/// KRC 文件头的字节
/// </summary>
public static readonly byte[] KRCFileHeadBytes = { 0x6B, 0x72, 0x63, 0x31 }; /// <summary>
/// 解码
/// </summary>
public static string DecodeFileToString(string krcFilePath)
{
//krc1
var headBytes = new byte[];
byte[] encodedBytes;
byte[] zipedBytes; using (var krcfs = new FileStream(krcFilePath, FileMode.Open))
{
encodedBytes = new byte[krcfs.Length - headBytes.Length];
zipedBytes = new byte[krcfs.Length - headBytes.Length]; //读文件头标记
krcfs.Read(headBytes, , headBytes.Length); //读XOR加密的内容
krcfs.Read(encodedBytes, , encodedBytes.Length); //关闭文件
krcfs.Close();
} for (var i = ; i < encodedBytes.Length; i++)
{
zipedBytes[i] = (byte)(encodedBytes[i] ^ KRCFileXorKey[i % ]);
} //前面3字节是 UTF-8 的 BOM
var unzipedBytes = Decompress(zipedBytes); //编码器带有BOM输出时多了3字节,所以跳过开头的3字节bom
var text = RemoveBom(Encoding.UTF8.GetString(unzipedBytes)); return text;
} /// <summary>
/// 编码到字节数组
/// </summary>
/// <param name="inText"></param>
/// <returns></returns>
public static byte[] EncodeStringToBytes(string inText)
{
//用默认的,编码时带有UTF-8的BOM
byte[] inbytes = Encoding.UTF8.GetPreamble().Concat(Encoding.UTF8.GetBytes(inText)).ToArray(); byte[] zipedBytes = Compress(inbytes); int encodedBytesLength = zipedBytes.Length; var encodedBytes = new byte[zipedBytes.Length]; for (int i = ; i < encodedBytesLength; i++)
{
int l = i % ; encodedBytes[i] = (byte)(zipedBytes[i] ^ KRCFileXorKey[l]);
} byte[] byets = null; using (var ms = new MemoryStream())
{
ms.Write(KRCFileHeadBytes, , KRCFileHeadBytes.Length);
ms.Write(encodedBytes, , encodedBytes.Length);
ms.Flush();
byets = ms.ToArray();
} return byets;
} /// <summary>
/// 移除UTF-8 BOM
/// </summary>
/// <param name="p"></param>
/// <returns></returns>
private static string RemoveBom(string p)
{
string bomMarkUtf8 = Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble());
if (p.StartsWith(bomMarkUtf8))
p = p.Remove(, bomMarkUtf8.Length);
return p.Replace("\0", "");
} #region 压缩 解压缩
private static byte[] Compress(byte[] pBytes)
{
byte[] outdata = null;
using (var mMemory = new MemoryStream(pBytes))
using (var mStream = new DeflaterOutputStream(mMemory, new Deflater(Deflater.DEFAULT_COMPRESSION), ))
{
mStream.Write(pBytes, , pBytes.Length);
mStream.Flush();
mMemory.Flush();
outdata = mMemory.ToArray();
}
return outdata;
} /// <summary>
/// 解压缩
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
private static byte[] Decompress(byte[] data)
{
byte[] outdata = null;
using (var ms = new MemoryStream())
using (var inputStream = new InflaterInputStream(new MemoryStream(data), new Inflater(false)))
{
inputStream.CopyTo(ms);
ms.Flush(); outdata = ms.ToArray();
ms.Close();
}
return outdata;
}
#endregion }
}

KRCFile

 using System;
using System.CodeDom;
using System.Diagnostics;
using System.Text.RegularExpressions; namespace KRC.KRCLib
{
/// <summary>
/// KRC文件行字符
/// </summary>
[DebuggerDisplay("{DebuggerDisplay}")]
public class KRCLyricsChar
{
/// <summary>
/// 字符
/// </summary>
public char Char { get; set; } /// <summary>
/// 字符KRC字符串
/// </summary>
public string KRCCharString
{
get
{
return string.Format(@"<{0},{1},{2}>{3}", this.CharStart.TotalMilliseconds, this.CharDuring.TotalMilliseconds, , this.Char);
}
} /// <summary>
/// 字符起始时间(计算时加上字符所属行的起始时间)
/// </summary>
public TimeSpan CharStart { get; set; } /// <summary>
/// 字符时长
/// </summary>
public TimeSpan CharDuring { get; set; } public KRCLyricsChar()
{
this.CharStart = TimeSpan.Zero;
this.CharDuring = TimeSpan.Zero;
} public KRCLyricsChar(string krcCharString)
: this()
{
var chars = Regex.Match(krcCharString, @"<(\d+),(\d+),(\d+)>(.?)"); if (chars.Success)
{
if (chars.Groups.Count >= )
{
var charstart = chars.Groups[].Value;
var charduring = chars.Groups[].Value;
var unknowAlwaysZero = chars.Groups[].Value; this.CharStart = TimeSpan.FromMilliseconds(double.Parse(charstart));
this.CharDuring = TimeSpan.FromMilliseconds(double.Parse(charduring)); if (chars.Groups.Count >= )
{
var charchar = chars.Groups[].Value;
this.Char = char.Parse(charchar);
}
else
{
this.Char = char.Parse(" ");
}
}
}
} public string DebuggerDisplay
{
get
{
return string.Format(@"{0:hh\:mm\:ss\.fff} {1:hh\:mm\:ss\.fff} {2}", this.CharStart, this.CharDuring, this.Char);
}
}
}
}

KRCLyricsChar

 using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text.RegularExpressions; namespace KRC.KRCLib
{
/// <summary>
/// KRC文件行
/// </summary>
[DebuggerDisplay("{DebuggerDisplay}")]
public class KRCLyricsLine
{
private readonly List<KRCLyricsChar> _chars = new List<KRCLyricsChar>(); /// <summary>
/// 行字符串
/// </summary>
public string KRCLineString
{
get
{
return string.Format(@"[{0},{1}]{2}", this.LineStart.TotalMilliseconds, this.LineDuring.TotalMilliseconds,
string.Join("", this.Chars.Select(x => x.KRCCharString)));
}
} /// <summary>
/// 行开始事件
/// </summary>
public TimeSpan LineStart { get; set; } /// <summary>
/// 行总时间
/// </summary>
public TimeSpan LineDuring
{
get
{
//计算行时间
var sum = this.Chars.Select(x => x.CharDuring.TotalMilliseconds).Sum();
return TimeSpan.FromMilliseconds(sum);
}
} /// <summary>
/// 行内字符
/// </summary> public List<KRCLyricsChar> Chars
{
get { return _chars; }
} public KRCLyricsLine()
{
this.LineStart = TimeSpan.Zero;
} public KRCLyricsLine(string krclinestring):this()
{
var regLineTime = new Regex(@"^\[(.*),(.*)\](.*)"); var m1 = regLineTime.Match(krclinestring);
if (m1.Success && m1.Groups.Count == )
{
var linestart = m1.Groups[].Value;
var linelength = m1.Groups[].Value; this.LineStart = TimeSpan.FromMilliseconds(double.Parse(linestart));
//this.LineDuring = TimeSpan.FromMilliseconds(double.Parse(linelength)); var linecontent = m1.Groups[].Value; var chars = Regex.Matches(linecontent, @"<(\d+),(\d+),(\d+)>(.?)"); foreach (Match m in chars)
{
this.Chars.Add(new KRCLyricsChar(m.Value));
}
}
} public string DebuggerDisplay
{
get
{
return string.Format(@"{0:hh\:mm\:ss\.fff} {1:hh\:mm\:ss\.fff} {2}", this.LineStart, this.LineDuring,
string.Join(",", this.Chars.Select(x => x.Char.ToString())));
}
}
}
}

KRCLyricsLine

 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 == )
{
prop.Item2(mm.Groups[].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();
}
}
}

酷狗 KRC 文件的解析的更多相关文章

  1. 酷狗.kgtemp文件加密算法逆向

    该帖转载于孤心浪子--http://www.cnblogs.com/KMBlog/p/6877752.html 酷狗音乐上的一些歌曲是不能免费下载的,然而用户仍然可以离线试听,这说明有缓存文件,并且极 ...

  2. 笨笨-歌词伴侣V1.2(酷狗KRC转LRC,LRC歌词批量下载)

      最近由于某些热心博友在我CSDN博客上使用了我的软件,提出了一些建议,看到自己的成果有人使用并且提出了一些建议,焉有不高兴之理!刚好碰上最近研究UI界面,有了一个初步的框架,就顺手将歌词相关功能集 ...

  3. Python脚本之Lrc歌词去时间轴转Txt文件,附带酷狗音乐APP关联已有krc歌词

    一.Lrc歌词去时间轴转Txt文件 环境:Python2.7.x, Mac(Windows需装cygwin环境,当然你也可以自己改代码,Python新手,勿喷) # -*- coding: UTF-8 ...

  4. 仿酷狗音乐播放器开发日志二十七 用ole为窗体增加文件拖动功能(附源码)

    转载请说明原出处,谢谢~~ 中秋到了,出去玩了几天.今天把仿酷狗程序做了收尾,已经开发完成了,下一篇博客把完结的情况说一下.在这篇博客里说一下使用OLE为窗体增加文件拖拽的功能.使用播放器,我更喜欢直 ...

  5. Python爬取酷狗飙升榜前十首(100)首,写入CSV文件

    酷狗飙升榜,写入CSV文件 爬取酷狗音乐飙升榜的前十首歌名.歌手.时间,是一个很好的爬取网页内容的例子,对爬虫不熟悉的读者可以根据这个例子熟悉爬虫是如何爬取网页内容的. 需要用到的库:requests ...

  6. 在线音乐播放器-----酷狗音乐api接口抓取

    首先身为一个在线音乐播放器,需要前端和数据库的搭配使用. 在数据库方面,我们没有办法制作,首先是版权问题,再加上数据量.所以我们需要借用其他网络播放器的数据库. 但是这些在线播放器,如百度,酷狗,酷我 ...

  7. 开源小工具 酷狗、网易音乐缓存文件转mp3工具

    发布一个开源小工具,支持将酷狗和网易云音乐的缓存文件转码为MP3文件. 以前写过kgtemp文件转mp3工具,正好当前又有网易云音乐缓存文件需求,因此就在原来小工具的基础上做了一点修改,增加了对网易云 ...

  8. Python实例---爬去酷狗音乐

    项目一:获取酷狗TOP 100 http://www.kugou.com/yy/rank/home/1-8888.html 排名 文件&&歌手 时长 效果: 附源码: import t ...

  9. Java爬虫系列之实战:爬取酷狗音乐网 TOP500 的歌曲(附源码)

    在前面分享的两篇随笔中分别介绍了HttpClient和Jsoup以及简单的代码案例: Java爬虫系列二:使用HttpClient抓取页面HTML Java爬虫系列三:使用Jsoup解析HTML 今天 ...

随机推荐

  1. Shell基础-ech0,cat,history,alias,unalias,bash快捷键,wc,执行结果写入文件

    1 系统所支持的shell存放于 /etc/shells 文件中,shell脚本的开头 #!/bing/bash 是指定使用的脚本类型 不能省略,省略之后有些文件可以执行,但容易出错 这行不是注释 2 ...

  2. 用keras的cnn做人脸分类

    keras介绍 Keras是一个简约,高度模块化的神经网络库.采用Python / Theano开发. 使用Keras如果你需要一个深度学习库: 可以很容易和快速实现原型(通过总模块化,极简主义,和可 ...

  3. ExtJs 获取Dom对象

    对象指页面上的某一部分,如:Input等.我觉得在EXT JS中会有三类基本对象,htmlelement , EXT.Element和CompositeElement .分别解释一下: htmlele ...

  4. 以太坊只能合约摸索——第一关,ubuntu开发环境部署

    1. 安装“eth”命令行工具 sudo add-apt-repository ppa:ethereum/ethereum-qt sudo add-apt-repository ppa:ethereu ...

  5. WPF Image控件的Source属性是一个ImageSource对象

    1.固定的图片路径是可以的,如下: <Image Source="../test.png" /> 2.绑定的Path是一个String类型的图片路径,而实际情况它需要的 ...

  6. arch linux 安装 之 ---GPT--引导篇

    新入一二手X240 ,直接格式化,装archLinux. 一.引导程序安装--------------- 1.首先搞清楚什么是UEFI Under UEFI下的引导顺序 System switched ...

  7. ios 的touch事件分析

    IOS之触摸事件和手势   13.1 事件概述 13.2 触摸事件 13.3 手势 13.1 事件概述 事件是当用户手指触击屏幕及在屏幕上移动时,系统不断发送给应用程序的对象. 系统将事件按照特定的路 ...

  8. 【Java】JDBC连接数据库

    JDBC介绍 JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言 ...

  9. mvc+mysql+EF

    网上有很多关于EF在联机情况下利用nuget管理器安装的案例,我就讲一下脱机状况吧! 一.建立一个文件夹,例如D:/Packages 放入安装EF和mysql需要的包:EntityFramework. ...

  10. php中的抽象类(abstract class)和接口(interface)

    一. 抽象类abstract class 1 .抽象类是指在 class 前加了 abstract 关键字且存在抽象方法(在类方法 function 关键字前加了 abstract 关键字)的类. 2 ...