parent
fa25324463
commit
404da4ae22
@ -1,37 +0,0 @@
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.ParserTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class MusicParserFixture : CoreTest
|
||||
{
|
||||
//[TestCase("___▲▲▲___")]
|
||||
//[TestCase("Add N to (X)")]
|
||||
//[TestCase("Animal Collective")]
|
||||
//[TestCase("D12")]
|
||||
//[TestCase("David Sylvian[Discography]")]
|
||||
//[TestCase("Eagle-Eye Cherry")]
|
||||
//[TestCase("Erlend Øye")]
|
||||
//[TestCase("Adult.")] // Not sure if valid, not openable in Windows OS
|
||||
//[TestCase("Maroon 5")]
|
||||
//[TestCase("Moimir Papalescu & The Nihilists")]
|
||||
//[TestCase("N.W.A")]
|
||||
//[TestCase("oOoOO")]
|
||||
//[TestCase("Panic! at the Disco")]
|
||||
//[TestCase("The 5 6 7 8's")]
|
||||
//[TestCase("tUnE-yArDs")]
|
||||
//[TestCase("U2")]
|
||||
//[TestCase("Белые Братья")]
|
||||
//[TestCase("Zog Bogbean - From The Marcy Playground")]
|
||||
|
||||
// TODO: Rewrite this test to something that makes sense.
|
||||
public void should_parse_author_names(string title)
|
||||
{
|
||||
Parser.Parser.ParseTitle(title).AuthorTitle.Should().Be(title);
|
||||
ExceptionVerification.IgnoreWarns();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.Azw
|
||||
{
|
||||
public class AzwFile
|
||||
{
|
||||
public byte[] RawData { get; }
|
||||
public ushort SectionCount { get; private set; }
|
||||
public SectionInfo[] Info { get; private set; }
|
||||
public string Ident { get; private set; }
|
||||
|
||||
protected AzwFile(string path)
|
||||
{
|
||||
RawData = File.ReadAllBytes(path);
|
||||
GetSectionInfo();
|
||||
}
|
||||
|
||||
protected void GetSectionInfo()
|
||||
{
|
||||
Ident = Encoding.ASCII.GetString(RawData, 0x3c, 8);
|
||||
SectionCount = Math.Min(Util.GetUInt16(RawData, 76), (ushort)1);
|
||||
|
||||
if (Ident != "BOOKMOBI" || SectionCount == 0)
|
||||
{
|
||||
throw new AzwTagException("Invalid mobi header");
|
||||
}
|
||||
|
||||
Info = new SectionInfo[SectionCount];
|
||||
Info[0].Start_addr = Util.GetUInt32(RawData, 78);
|
||||
Info[0].End_addr = Util.GetUInt32(RawData, 78 + 8);
|
||||
}
|
||||
|
||||
protected byte[] GetSectionData(uint i)
|
||||
{
|
||||
return Util.SubArray(RawData, Info[i].Start_addr, Info[i].Length);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
using System;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.Azw
|
||||
{
|
||||
[Serializable]
|
||||
public class AzwTagException : Exception
|
||||
{
|
||||
public AzwTagException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
protected AzwTagException(System.Runtime.Serialization.SerializationInfo info,
|
||||
System.Runtime.Serialization.StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Instrumentation;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.Azw
|
||||
{
|
||||
public class ExtMeta
|
||||
{
|
||||
private static readonly Logger Logger = NzbDroneLogger.GetLogger(typeof(ExtMeta));
|
||||
|
||||
public Dictionary<uint, ulong> IdValue { get; } = new Dictionary<uint, ulong>();
|
||||
public Dictionary<uint, List<string>> IdString { get; } = new Dictionary<uint, List<string>>();
|
||||
public Dictionary<uint, string> IdHex { get; } = new Dictionary<uint, string>();
|
||||
|
||||
public ExtMeta(byte[] ext, Encoding encoding)
|
||||
{
|
||||
var num_items = Util.GetUInt32(ext, 8);
|
||||
uint pos = 12;
|
||||
for (var i = 0; i < num_items; i++)
|
||||
{
|
||||
var id = Util.GetUInt32(ext, pos);
|
||||
var size = Util.GetUInt32(ext, pos + 4);
|
||||
if (IdMapping.Id_map_strings.ContainsKey(id))
|
||||
{
|
||||
var a = encoding.GetString(Util.SubArray(ext, pos + 8, size - 8));
|
||||
|
||||
if (IdString.ContainsKey(id))
|
||||
{
|
||||
IdString[id].Add(a);
|
||||
}
|
||||
else
|
||||
{
|
||||
IdString.Add(id, new List<string> { a });
|
||||
}
|
||||
}
|
||||
else if (IdMapping.Id_map_values.ContainsKey(id))
|
||||
{
|
||||
ulong a = 0;
|
||||
switch (size)
|
||||
{
|
||||
case 9:
|
||||
a = Util.GetUInt8(ext, pos + 8);
|
||||
break;
|
||||
case 10:
|
||||
a = Util.GetUInt16(ext, pos + 8);
|
||||
break;
|
||||
case 12:
|
||||
a = Util.GetUInt32(ext, pos + 8);
|
||||
break;
|
||||
case 16:
|
||||
a = Util.GetUInt64(ext, pos + 8);
|
||||
break;
|
||||
default:
|
||||
Logger.Warn("unexpected size:" + size);
|
||||
break;
|
||||
}
|
||||
|
||||
if (IdValue.ContainsKey(id))
|
||||
{
|
||||
Logger.Debug("Meta id duplicate:{0}\nPervious:{1} \nLatter:{2}", IdMapping.Id_map_values[id], IdValue[id], a);
|
||||
}
|
||||
else
|
||||
{
|
||||
IdValue.Add(id, a);
|
||||
}
|
||||
}
|
||||
else if (IdMapping.Id_map_hex.ContainsKey(id))
|
||||
{
|
||||
var a = Util.ToHexString(ext, pos + 8, size - 8);
|
||||
|
||||
if (IdHex.ContainsKey(id))
|
||||
{
|
||||
Logger.Debug("Meta id duplicate:{0}\nPervious:{1} \nLatter:{2}", IdMapping.Id_map_hex[id], IdHex[id], a);
|
||||
}
|
||||
else
|
||||
{
|
||||
IdHex.Add(id, a);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unknown id
|
||||
}
|
||||
|
||||
pos += size;
|
||||
}
|
||||
}
|
||||
|
||||
public string StringOrNull(uint key)
|
||||
{
|
||||
return IdString.TryGetValue(key, out var value) ? value.FirstOrDefault() : null;
|
||||
}
|
||||
|
||||
public List<string> StringList(uint key)
|
||||
{
|
||||
return IdString.TryGetValue(key, out var value) ? value : new List<string>();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,135 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.Azw
|
||||
{
|
||||
public class ExtMeta
|
||||
{
|
||||
public Dictionary<uint, ulong> IdValue;
|
||||
public Dictionary<uint, string> IdString;
|
||||
public Dictionary<uint, string> IdHex;
|
||||
|
||||
public ExtMeta(byte[] ext, Encoding encoding)
|
||||
{
|
||||
IdValue = new Dictionary<uint, ulong>();
|
||||
IdString = new Dictionary<uint, string>();
|
||||
IdHex = new Dictionary<uint, string>();
|
||||
|
||||
var num_items = Util.GetUInt32(ext, 8);
|
||||
uint pos = 12;
|
||||
for (var i = 0; i < num_items; i++)
|
||||
{
|
||||
var id = Util.GetUInt32(ext, pos);
|
||||
var size = Util.GetUInt32(ext, pos + 4);
|
||||
if (IdMapping.Id_map_strings.ContainsKey(id))
|
||||
{
|
||||
var a = encoding.GetString(Util.SubArray(ext, pos + 8, size - 8));
|
||||
|
||||
if (IdString.ContainsKey(id))
|
||||
{
|
||||
if (id == 100 || id == 517)
|
||||
{
|
||||
IdString[id] += "&" + a;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine(string.Format("Meta id duplicate:{0}\nPervious:{1} \nLatter:{2}", IdMapping.Id_map_strings[id], IdString[id], a));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
IdString.Add(id, a);
|
||||
}
|
||||
}
|
||||
else if (IdMapping.Id_map_values.ContainsKey(id))
|
||||
{
|
||||
ulong a = 0;
|
||||
switch (size)
|
||||
{
|
||||
case 9: a = Util.GetUInt8(ext, pos + 8); break;
|
||||
case 10: a = Util.GetUInt16(ext, pos + 8); break;
|
||||
case 12: a = Util.GetUInt32(ext, pos + 8); break;
|
||||
case 16: a = Util.GetUInt64(ext, pos + 8); break;
|
||||
default: Console.WriteLine("unexpected size:" + size); break;
|
||||
}
|
||||
|
||||
if (IdValue.ContainsKey(id))
|
||||
{
|
||||
Console.WriteLine(string.Format("Meta id duplicate:{0}\nPervious:{1} \nLatter:{2}", IdMapping.Id_map_values[id], IdValue[id], a));
|
||||
}
|
||||
else
|
||||
{
|
||||
IdValue.Add(id, a);
|
||||
}
|
||||
}
|
||||
else if (IdMapping.Id_map_hex.ContainsKey(id))
|
||||
{
|
||||
var a = Util.ToHexString(ext, pos + 8, size - 8);
|
||||
|
||||
if (IdHex.ContainsKey(id))
|
||||
{
|
||||
Console.WriteLine(string.Format("Meta id duplicate:{0}\nPervious:{1} \nLatter:{2}", IdMapping.Id_map_hex[id], IdHex[id], a));
|
||||
}
|
||||
else
|
||||
{
|
||||
IdHex.Add(id, a);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unknown id
|
||||
}
|
||||
|
||||
pos += size;
|
||||
}
|
||||
}
|
||||
|
||||
public string StringOrNull(uint key)
|
||||
{
|
||||
return IdString.TryGetValue(key, out var value) ? value : null;
|
||||
}
|
||||
}
|
||||
|
||||
public class MobiHeader : Section
|
||||
{
|
||||
private readonly uint _length;
|
||||
private readonly uint _codepage;
|
||||
private readonly uint _exth_flag;
|
||||
|
||||
public MobiHeader(byte[] header)
|
||||
: base("Mobi Header", header)
|
||||
{
|
||||
var mobi = Encoding.ASCII.GetString(header, 16, 4);
|
||||
if (mobi != "MOBI")
|
||||
{
|
||||
throw new AzwTagException("Invalid mobi header");
|
||||
}
|
||||
|
||||
Version = Util.GetUInt32(header, 36);
|
||||
MobiType = Util.GetUInt32(header, 24);
|
||||
|
||||
_codepage = Util.GetUInt32(header, 28);
|
||||
|
||||
var encoding = _codepage == 65001 ? Encoding.UTF8 : CodePagesEncodingProvider.Instance.GetEncoding((int)_codepage);
|
||||
Title = encoding.GetString(header, (int)Util.GetUInt32(header, 0x54), (int)Util.GetUInt32(header, 0x58));
|
||||
|
||||
_exth_flag = Util.GetUInt32(header, 0x80);
|
||||
_length = Util.GetUInt32(header, 20);
|
||||
if ((_exth_flag & 0x40) > 0)
|
||||
{
|
||||
var exth = Util.SubArray(header, _length + 16, Util.GetUInt32(header, _length + 20));
|
||||
ExtMeta = new ExtMeta(exth, encoding);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new AzwTagException("No EXTH header. Readarr cannot process this file.");
|
||||
}
|
||||
}
|
||||
|
||||
public string Title { get; private set; }
|
||||
public uint Version { get; private set; }
|
||||
public uint MobiType { get; private set; }
|
||||
public ExtMeta ExtMeta { get; private set; }
|
||||
}
|
||||
}
|
@ -1,58 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.Azw
|
||||
{
|
||||
public struct SectionInfo
|
||||
{
|
||||
public ulong Start_addr;
|
||||
public ulong End_addr;
|
||||
|
||||
public ulong Length => End_addr - Start_addr;
|
||||
}
|
||||
|
||||
public class AzwFile
|
||||
{
|
||||
public byte[] Raw_data;
|
||||
public ushort Section_count;
|
||||
public SectionInfo[] Section_info;
|
||||
public string Ident;
|
||||
|
||||
protected AzwFile(string path)
|
||||
{
|
||||
Raw_data = File.ReadAllBytes(path);
|
||||
GetSectionInfo();
|
||||
|
||||
if (Ident != "BOOKMOBI" || Section_count == 0)
|
||||
{
|
||||
throw new AzwTagException("Invalid mobi header");
|
||||
}
|
||||
}
|
||||
|
||||
protected void GetSectionInfo()
|
||||
{
|
||||
Ident = Encoding.ASCII.GetString(Raw_data, 0x3c, 8);
|
||||
Section_count = Util.GetUInt16(Raw_data, 76);
|
||||
Section_info = new SectionInfo[Section_count];
|
||||
|
||||
Section_info[0].Start_addr = Util.GetUInt32(Raw_data, 78);
|
||||
for (uint i = 1; i < Section_count; i++)
|
||||
{
|
||||
Section_info[i].Start_addr = Util.GetUInt32(Raw_data, 78 + (i * 8));
|
||||
Section_info[i - 1].End_addr = Section_info[i].Start_addr;
|
||||
}
|
||||
|
||||
Section_info[Section_count - 1].End_addr = (ulong)Raw_data.Length;
|
||||
}
|
||||
|
||||
protected byte[] GetSectionData(uint i)
|
||||
{
|
||||
return Util.SubArray(Raw_data, Section_info[i].Start_addr, Section_info[i].Length);
|
||||
}
|
||||
}
|
||||
|
||||
public class IdMapping
|
||||
public static class IdMapping
|
||||
{
|
||||
public static Dictionary<uint, string> Id_map_strings = new Dictionary<uint, string>
|
||||
{
|
@ -0,0 +1,41 @@
|
||||
using System.Text;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.Azw
|
||||
{
|
||||
public class MobiHeader
|
||||
{
|
||||
public MobiHeader(byte[] header)
|
||||
{
|
||||
var mobi = Encoding.ASCII.GetString(header, 16, 4);
|
||||
if (mobi != "MOBI")
|
||||
{
|
||||
throw new AzwTagException("Invalid mobi header");
|
||||
}
|
||||
|
||||
Version = Util.GetUInt32(header, 36);
|
||||
MobiType = Util.GetUInt32(header, 24);
|
||||
|
||||
var codepage = Util.GetUInt32(header, 28);
|
||||
|
||||
var encoding = codepage == 65001 ? Encoding.UTF8 : CodePagesEncodingProvider.Instance.GetEncoding((int)codepage);
|
||||
Title = encoding.GetString(header, (int)Util.GetUInt32(header, 0x54), (int)Util.GetUInt32(header, 0x58));
|
||||
|
||||
var exthFlag = Util.GetUInt32(header, 0x80);
|
||||
var length = Util.GetUInt32(header, 20);
|
||||
if ((exthFlag & 0x40) > 0)
|
||||
{
|
||||
var exth = Util.SubArray(header, length + 16, Util.GetUInt32(header, length + 20));
|
||||
ExtMeta = new ExtMeta(exth, encoding);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new AzwTagException("No EXTH header. Readarr cannot process this file.");
|
||||
}
|
||||
}
|
||||
|
||||
public string Title { get; }
|
||||
public uint Version { get; }
|
||||
public uint MobiType { get; }
|
||||
public ExtMeta ExtMeta { get; }
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
using System.Text;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.Azw
|
||||
{
|
||||
public class Section
|
||||
{
|
||||
public string Type;
|
||||
public byte[] Raw;
|
||||
public string Comment = "";
|
||||
|
||||
public Section(byte[] raw)
|
||||
{
|
||||
Raw = raw;
|
||||
if (raw.Length < 4)
|
||||
{
|
||||
Type = "Empty Section";
|
||||
return;
|
||||
}
|
||||
|
||||
Type = Encoding.ASCII.GetString(raw, 0, 4);
|
||||
|
||||
switch (Type)
|
||||
{
|
||||
case "??\r\n": Type = "End Of File"; break;
|
||||
case "?6?\t": Type = "Place Holder"; break;
|
||||
case "\0\0\0\0": Type = "Empty Section0"; break;
|
||||
}
|
||||
}
|
||||
|
||||
public Section(Section s)
|
||||
{
|
||||
Type = s.Type;
|
||||
Raw = s.Raw;
|
||||
}
|
||||
|
||||
public Section(string type, byte[] raw)
|
||||
{
|
||||
Type = type;
|
||||
Raw = raw;
|
||||
}
|
||||
|
||||
public virtual int GetSize()
|
||||
{
|
||||
return Raw.Length;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
namespace NzbDrone.Core.MediaFiles.Azw
|
||||
{
|
||||
public struct SectionInfo
|
||||
{
|
||||
public ulong Start_addr;
|
||||
public ulong End_addr;
|
||||
|
||||
public ulong Length => End_addr - Start_addr;
|
||||
}
|
||||
}
|
Loading…
Reference in new issue