Added TagLib through package manager and improved tag retreival.

This commit is contained in:
Michael Bucari-Tovo 2021-06-30 00:54:52 -06:00
parent 78f278121b
commit e958944466
5 changed files with 175 additions and 39 deletions

View File

@ -4,6 +4,10 @@
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net5.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<PackageReference Include="TagLibSharp" Version="2.2.0" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<Reference Include="taglib-sharp"> <Reference Include="taglib-sharp">
<HintPath>lib\taglib-sharp.dll</HintPath> <HintPath>lib\taglib-sharp.dll</HintPath>
@ -42,9 +46,6 @@
<None Update="DecryptLib\swscale-5.dll"> <None Update="DecryptLib\swscale-5.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
<None Update="DecryptLib\taglib-sharp.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -41,12 +41,12 @@ namespace AaxDecrypter
public string outDir { get; private set; } public string outDir { get; private set; }
public string outputFileName { get; private set; } public string outputFileName { get; private set; }
public ChapterInfo chapters { get; private set; } public ChapterInfo chapters { get; private set; }
public string Title => aaxcTagLib.Tag.Title?.Replace(" (Unabridged)", "") ?? "[unknown]"; public string Title => aaxcTagLib.TitleSansUnabridged ?? "[unknown]";
public string Author => aaxcTagLib.Tag.FirstPerformer ?? "[unknown]"; public string Author => aaxcTagLib.FirstAuthor ?? "[unknown]";
public string Narrator => aaxcTagLib.GetTag(TagLib.TagTypes.Apple).Narrator ?? "[unknown]"; public string Narrator => aaxcTagLib.Narrator ?? "[unknown]";
public byte[] CoverArt => aaxcTagLib.Tag.Pictures.Length > 0 ? aaxcTagLib.Tag.Pictures[0].Data.Data : default; public byte[] CoverArt => aaxcTagLib.Tag.Pictures.Length > 0 ? aaxcTagLib.Tag.Pictures[0].Data.Data : default;
private TagLib.Mpeg4.File aaxcTagLib { get; set; } private AaxcTagLibFile aaxcTagLib { get; set; }
private StepSequence steps { get; } private StepSequence steps { get; }
private DownloadLicense downloadLicense { get; set; } private DownloadLicense downloadLicense { get; set; }
private FFMpegAaxcProcesser aaxcProcesser; private FFMpegAaxcProcesser aaxcProcesser;
@ -90,7 +90,7 @@ namespace AaxDecrypter
var networkFile = await NetworkFileAbstraction.CreateAsync(client, new Uri(downloadLicense.DownloadUrl)); var networkFile = await NetworkFileAbstraction.CreateAsync(client, new Uri(downloadLicense.DownloadUrl));
aaxcTagLib = await Task.Run(() => TagLib.File.Create(networkFile, "audio/mp4", TagLib.ReadStyle.Average) as TagLib.Mpeg4.File); aaxcTagLib = await Task.Run(() => new AaxcTagLibFile(networkFile));
var defaultFilename = Path.Combine( var defaultFilename = Path.Combine(
outDir, outDir,
@ -239,21 +239,8 @@ namespace AaxDecrypter
/// </summary> /// </summary>
public bool Step3_RestoreMetadata() public bool Step3_RestoreMetadata()
{ {
var outFile = new TagLib.Mpeg4.File(outputFileName, TagLib.ReadStyle.Average); var outFile = new AaxcTagLibFile(outputFileName);
outFile.CopyTagsFrom(aaxcTagLib);
var destTags = outFile.GetTag(TagLib.TagTypes.Apple) as TagLib.Mpeg4.AppleTag;
destTags.Clear();
var sourceTag = aaxcTagLib.GetTag(TagLib.TagTypes.Apple) as TagLib.Mpeg4.AppleTag;
//copy all metadata fields in the source file, even those that TagLib doesn't
//recognize, to the output file.
//NOTE: Chapters aren't stored in MPEG-4 metadata. They are encoded as a Timed
//Text Stream (MPEG-4 Part 17), so taglib doesn't read or write them.
foreach (var stag in sourceTag)
{
destTags.SetData(stag.BoxType, stag.Children.Cast<TagLib.Mpeg4.AppleDataBox>().ToArray());
}
outFile.Save(); outFile.Save();
return true; return true;

View File

@ -3,32 +3,31 @@ namespace AaxDecrypter
{ {
public static class NFO public static class NFO
{ {
public static string CreateContents(string ripper, TagLib.File aaxcTagLib, ChapterInfo chapters) public static string CreateContents(string ripper, AaxcTagLibFile aaxcTagLib, ChapterInfo chapters)
{ {
var tag = aaxcTagLib.GetTag(TagLib.TagTypes.Apple); var tag = aaxcTagLib.AppleTags;
string narator = string.IsNullOrWhiteSpace(aaxcTagLib.Tag.FirstComposer) ? tag.Narrator : aaxcTagLib.Tag.FirstComposer;
var _hours = (int)aaxcTagLib.Properties.Duration.TotalHours; var _hours = (int)aaxcTagLib.Properties.Duration.TotalHours;
var myDuration var myDuration
= (_hours > 0 ? _hours + " hours, " : "") = (_hours > 0 ? _hours + " hours, " : string.Empty)
+ aaxcTagLib.Properties.Duration.Minutes + " minutes, " + aaxcTagLib.Properties.Duration.Minutes + " minutes, "
+ aaxcTagLib.Properties.Duration.Seconds + " seconds"; + aaxcTagLib.Properties.Duration.Seconds + " seconds";
var header var header
= "General Information\r\n" = "General Information\r\n"
+ "===================\r\n" + "===================\r\n"
+ $" Title: {aaxcTagLib.Tag.Title.Replace(" (Unabridged)", "")}\r\n" + $" Title: {aaxcTagLib.TitleSansUnabridged ?? "[unknown]"}\r\n"
+ $" Author: {aaxcTagLib.Tag.FirstPerformer ?? "[unknown]"}\r\n" + $" Author: {aaxcTagLib.FirstAuthor ?? "[unknown]"}\r\n"
+ $" Read By: {aaxcTagLib.GetTag(TagLib.TagTypes.Apple).Narrator??"[unknown]"}\r\n" + $" Read By: {aaxcTagLib.Narrator ?? "[unknown]"}\r\n"
+ $" Copyright: {aaxcTagLib.Tag.Year}\r\n" + $" Release Date: {aaxcTagLib.ReleaseDate ?? "[unknown]"}\r\n"
+ $" Audiobook Copyright: {aaxcTagLib.Tag.Year}\r\n"; + $" Book Copyright: {aaxcTagLib.BookCopyright ?? "[unknown]"}\r\n"
if (!string.IsNullOrEmpty(aaxcTagLib.Tag.FirstGenre)) + $" Recording Copyright: {aaxcTagLib.RecordingCopyright ?? "[unknown]"}\r\n";
header += $" Genre: {aaxcTagLib.Tag.FirstGenre}\r\n"; if (!string.IsNullOrEmpty(tag.FirstGenre))
header += $" Genre: {aaxcTagLib.AppleTags.FirstGenre}\r\n";
var s var s
= header = header
+ $" Publisher: {tag.Publisher ?? ""}\r\n" + $" Publisher: {aaxcTagLib.Publisher ?? string.Empty}\r\n"
+ $" Duration: {myDuration}\r\n" + $" Duration: {myDuration}\r\n"
+ $" Chapters: {chapters.Count}\r\n" + $" Chapters: {chapters.Count}\r\n"
+ "\r\n" + "\r\n"
@ -38,20 +37,20 @@ namespace AaxDecrypter
+ " Source Format: Audible AAX\r\n" + " Source Format: Audible AAX\r\n"
+ $" Source Sample Rate: {aaxcTagLib.Properties.AudioSampleRate} Hz\r\n" + $" Source Sample Rate: {aaxcTagLib.Properties.AudioSampleRate} Hz\r\n"
+ $" Source Channels: {aaxcTagLib.Properties.AudioChannels}\r\n" + $" Source Channels: {aaxcTagLib.Properties.AudioChannels}\r\n"
+ $" Source Bitrate: {aaxcTagLib.Properties.AudioBitrate} kbits\r\n" + $" Source Bitrate: {aaxcTagLib.Properties.AudioBitrate} Kbps\r\n"
+ "\r\n" + "\r\n"
+ " Lossless Encode: Yes\r\n" + " Lossless Encode: Yes\r\n"
+ " Encoded Codec: AAC / M4B\r\n" + " Encoded Codec: AAC / M4B\r\n"
+ $" Encoded Sample Rate: {aaxcTagLib.Properties.AudioSampleRate} Hz\r\n" + $" Encoded Sample Rate: {aaxcTagLib.Properties.AudioSampleRate} Hz\r\n"
+ $" Encoded Channels: {aaxcTagLib.Properties.AudioChannels}\r\n" + $" Encoded Channels: {aaxcTagLib.Properties.AudioChannels}\r\n"
+ $" Encoded Bitrate: {aaxcTagLib.Properties.AudioBitrate} kbits\r\n" + $" Encoded Bitrate: {aaxcTagLib.Properties.AudioBitrate} Kbps\r\n"
+ "\r\n" + "\r\n"
+ $" Ripper: {ripper}\r\n" + $" Ripper: {ripper}\r\n"
+ "\r\n" + "\r\n"
+ "\r\n" + "\r\n"
+ "Book Description\r\n" + "Book Description\r\n"
+ "================\r\n" + "================\r\n"
+ (!string.IsNullOrWhiteSpace(tag.LongDescription) ? tag.LongDescription : tag.Description); + (!string.IsNullOrWhiteSpace(aaxcTagLib.LongDescription) ? aaxcTagLib.LongDescription : aaxcTagLib.Tag.Comment);
return s; return s;
} }

View File

@ -0,0 +1,149 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using TagLib;
using TagLib.Mpeg4;
namespace AaxDecrypter
{
public class AaxcTagLibFile : TagLib.Mpeg4.File
{
public AppleTag AppleTags => GetTag(TagTypes.Apple) as AppleTag;
private static ReadOnlyByteVector naratorType = new ReadOnlyByteVector(0xa9, (byte)'n', (byte)'r', (byte)'t');
private static ReadOnlyByteVector descriptionType = new ReadOnlyByteVector(0xa9, (byte)'d', (byte)'e', (byte)'s');
private static ReadOnlyByteVector publisherType = new ReadOnlyByteVector(0xa9, (byte)'p', (byte)'u', (byte)'b');
public string Narrator { get; }
public string LongDescription { get; }
public string ReleaseDate { get; }
public string Publisher { get; }
//TagLib uses @ART, which is the Artist tag
public string[] Authors => AppleTags.Performers;
public string FirstAuthor { get; }
public string TitleSansUnabridged { get; }
public string BookCopyright { get; }
public string RecordingCopyright { get; }
private string[] _copyright;
public AaxcTagLibFile(IFileAbstraction abstraction)
: base(abstraction, ReadStyle.Average)
{
_copyright = AppleTags.Copyright?.Replace("&#169;", string.Empty).Replace("(P)", string.Empty)?.Split(';');
BookCopyright = _copyright is not null && _copyright.Length > 0 ? _copyright[0] : default;
RecordingCopyright = _copyright is not null && _copyright.Length > 1 ? _copyright[1] : default;
TitleSansUnabridged = AppleTags.Title?.Replace(" (Unabridged)", "");
FirstAuthor = Authors?.Length > 0 ? unicodeToAscii(Authors[0]) : default;
string[] text = AppleTags.GetText(publisherType);
Publisher = text.Length == 0 ? null : text[0];
text = AppleTags.GetText("rldt");
ReleaseDate = text.Length == 0 ? null : text[0];
text = AppleTags.GetText(descriptionType);
LongDescription = text.Length == 0 ? null : unicodeToAscii(text[0]);
text = AppleTags.GetText(naratorType);
Narrator = text.Length == 0 ? null : unicodeToAscii(text[0]);
}
public AaxcTagLibFile(string path) : this(new LocalFileAbstraction(path))
{
}
public void CopyTagsFrom(AaxcTagLibFile sourceFile)
{
AppleTags.Clear();
//copy all metadata fields in the source file, even those that TagLib doesn't
//recognize, to the output file.
//NOTE: Chapters aren't stored in MPEG-4 metadata. They are encoded as a Timed
//Text Stream (MPEG-4 Part 17), so taglib doesn't read or write them.
foreach (var stag in sourceFile.AppleTags)
{
AppleTags.SetData(stag.BoxType, stag.Children.Cast<AppleDataBox>().ToArray());
}
}
/// <summary>
/// Attempts to convert unicode characters to an approximately equal ASCII character.
/// </summary>
private string unicodeToAscii(string unicodeStr)
{
//Accents
unicodeStr = Regex.Replace(unicodeStr, "[éèëêð]", "e");
unicodeStr = Regex.Replace(unicodeStr, "[ÉÈËÊ]", "E");
unicodeStr = Regex.Replace(unicodeStr, "[àâä]", "a");
unicodeStr = Regex.Replace(unicodeStr, "[ÀÁÂÃÄÅ]", "A");
unicodeStr = Regex.Replace(unicodeStr, "[àáâãäå]", "a");
unicodeStr = Regex.Replace(unicodeStr, "[ÙÚÛÜ]", "U");
unicodeStr = Regex.Replace(unicodeStr, "[ùúûüµ]", "u");
unicodeStr = Regex.Replace(unicodeStr, "[òóôõöø]", "o");
unicodeStr = Regex.Replace(unicodeStr, "[ÒÓÔÕÖØ]", "O");
unicodeStr = Regex.Replace(unicodeStr, "[ìíîï]", "i");
unicodeStr = Regex.Replace(unicodeStr, "[ÌÍÎÏ]", "I");
unicodeStr = Regex.Replace(unicodeStr, "[š]", "s");
unicodeStr = Regex.Replace(unicodeStr, "[Š]", "S");
unicodeStr = Regex.Replace(unicodeStr, "[ñ]", "n");
unicodeStr = Regex.Replace(unicodeStr, "[Ñ]", "N");
unicodeStr = Regex.Replace(unicodeStr, "[ç]", "c");
unicodeStr = Regex.Replace(unicodeStr, "[Ç]", "C");
unicodeStr = Regex.Replace(unicodeStr, "[ÿ]", "y");
unicodeStr = Regex.Replace(unicodeStr, "[Ÿ]", "Y");
unicodeStr = Regex.Replace(unicodeStr, "[ž]", "z");
unicodeStr = Regex.Replace(unicodeStr, "[Ž]", "Z");
unicodeStr = Regex.Replace(unicodeStr, "[Ð]", "D");
//Ligatures
unicodeStr = Regex.Replace(unicodeStr, "[œ]", "oe");
unicodeStr = Regex.Replace(unicodeStr, "[Œ]", "Oe");
unicodeStr = Regex.Replace(unicodeStr, "[ꜳ]", "aa");
unicodeStr = Regex.Replace(unicodeStr, "[Ꜳ]", "AA");
unicodeStr = Regex.Replace(unicodeStr, "[æ]", "ae");
unicodeStr = Regex.Replace(unicodeStr, "[Æ]", "AE");
unicodeStr = Regex.Replace(unicodeStr, "[ꜵ]", "ao");
unicodeStr = Regex.Replace(unicodeStr, "[Ꜵ]", "AO");
unicodeStr = Regex.Replace(unicodeStr, "[ꜷ]", "au");
unicodeStr = Regex.Replace(unicodeStr, "[Ꜷ]", "AU");
unicodeStr = Regex.Replace(unicodeStr, "[«»ꜹꜻ]", "av");
unicodeStr = Regex.Replace(unicodeStr, "[«»ꜸꜺ]", "AV");
unicodeStr = Regex.Replace(unicodeStr, "[🙰]", "et");
unicodeStr = Regex.Replace(unicodeStr, "[ff]", "ff");
unicodeStr = Regex.Replace(unicodeStr, "[ffi]", "ffi");
unicodeStr = Regex.Replace(unicodeStr, "[ffl]", "ffl");
unicodeStr = Regex.Replace(unicodeStr, "[fi]", "fi");
unicodeStr = Regex.Replace(unicodeStr, "[fl]", "fl");
unicodeStr = Regex.Replace(unicodeStr, "[ƕ]", "hv");
unicodeStr = Regex.Replace(unicodeStr, "[Ƕ]", "Hv");
unicodeStr = Regex.Replace(unicodeStr, "[℔]", "lb");
unicodeStr = Regex.Replace(unicodeStr, "[ꝏ]", "oo");
unicodeStr = Regex.Replace(unicodeStr, "[Ꝏ]", "OO");
unicodeStr = Regex.Replace(unicodeStr, "[st]", "st");
unicodeStr = Regex.Replace(unicodeStr, "[ꜩ]", "tz");
unicodeStr = Regex.Replace(unicodeStr, "[Ꜩ]", "TZ");
unicodeStr = Regex.Replace(unicodeStr, "[ᵫ]", "ue");
unicodeStr = Regex.Replace(unicodeStr, "[ꭣ]", "uo");
//Punctuation
unicodeStr = Regex.Replace(unicodeStr, "[«»\u2018\u2019\u201A\u201B\u2032\u2035]", "\'");
unicodeStr = Regex.Replace(unicodeStr, "[«»\u201C\u201D\u201E\u201F\u2033\u2036]", "\"");
unicodeStr = Regex.Replace(unicodeStr, "[\u2026]", "...");
unicodeStr = Regex.Replace(unicodeStr, "[\u1680]", "-");
//Spaces
unicodeStr = Regex.Replace(unicodeStr, "[«»\u00A0\u2000\u2002\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u200F\u205F\u3000]", " ");
unicodeStr = Regex.Replace(unicodeStr, "[«»\u2001\u2003]", " ");
unicodeStr = Regex.Replace(unicodeStr, "[«»\u180E\u200B\uFEFF]", "");
return unicodeStr;
}
}
}