mirror of
https://github.com/rmcrackan/Libation.git
synced 2025-12-31 09:58:43 -05:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff20d777a6 | ||
|
|
270e2531e2 | ||
|
|
959a1aebe9 | ||
|
|
2217fe6948 | ||
|
|
96abf56a87 | ||
|
|
5731a8f693 | ||
|
|
ff722b6a52 | ||
|
|
9271114408 | ||
|
|
ebfdd44142 | ||
|
|
6ed4eb34bd |
40
AaxDecrypter/AAXChapters.cs
Normal file
40
AaxDecrypter/AAXChapters.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Dinah.Core.Diagnostics;
|
||||
|
||||
namespace AaxDecrypter
|
||||
{
|
||||
public class AAXChapters : Chapters
|
||||
{
|
||||
public AAXChapters(string file)
|
||||
{
|
||||
var info = new ProcessStartInfo
|
||||
{
|
||||
FileName = DecryptSupportLibraries.ffprobePath,
|
||||
Arguments = "-loglevel panic -show_chapters -print_format xml \"" + file + "\""
|
||||
};
|
||||
var xml = info.RunHidden().Output;
|
||||
|
||||
var xmlDocument = new System.Xml.XmlDocument();
|
||||
xmlDocument.LoadXml(xml);
|
||||
var chaptersXml = xmlDocument.SelectNodes("/ffprobe/chapters/chapter")
|
||||
.Cast<System.Xml.XmlNode>()
|
||||
.Where(n => n.Name == "chapter");
|
||||
|
||||
foreach (var cnode in chaptersXml)
|
||||
{
|
||||
double startTime = double.Parse(cnode.Attributes["start_time"].Value.Replace(",", "."), CultureInfo.InvariantCulture);
|
||||
double endTime = double.Parse(cnode.Attributes["end_time"].Value.Replace(",", "."), CultureInfo.InvariantCulture);
|
||||
|
||||
string chapterTitle = cnode.ChildNodes
|
||||
.Cast<System.Xml.XmlNode>()
|
||||
.Where(childnode => childnode.Attributes["key"].Value == "title")
|
||||
.Select(childnode => childnode.Attributes["value"].Value)
|
||||
.FirstOrDefault();
|
||||
|
||||
AddChapter(new Chapter(startTime, endTime, chapterTitle));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,57 +10,11 @@
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="..\..\..\..\..\..\Dinah%2527s folder\coding\_NET\Visual Studio 2019\Libation\AaxDecrypter\UNTESTED\BytesCrackerLib\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Dinah.Core\Dinah.Core\Dinah.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="BytesCrackerLib\alglib1.dll">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="BytesCrackerLib\audible_byte#4-4_0_10000x789935_0.rtc">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="BytesCrackerLib\audible_byte#4-4_1_10000x791425_0.rtc">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="BytesCrackerLib\audible_byte#4-4_2_10000x790991_0.rtc">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="BytesCrackerLib\audible_byte#4-4_3_10000x792120_0.rtc">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="BytesCrackerLib\audible_byte#4-4_4_10000x790743_0.rtc">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="BytesCrackerLib\audible_byte#4-4_5_10000x790568_0.rtc">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="BytesCrackerLib\audible_byte#4-4_6_10000x791458_0.rtc">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="BytesCrackerLib\audible_byte#4-4_7_10000x791707_0.rtc">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="BytesCrackerLib\audible_byte#4-4_8_10000x790202_0.rtc">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="BytesCrackerLib\audible_byte#4-4_9_10000x791022_0.rtc">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="BytesCrackerLib\ffmpeg.exe">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="BytesCrackerLib\ffprobe.exe">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="BytesCrackerLib\rcrack.exe">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="DecryptLib\AtomicParsley.exe">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
|
||||
@@ -62,15 +62,18 @@ namespace AaxDecrypter
|
||||
public Tags tags { get; private set; }
|
||||
public EncodingInfo encodingInfo { get; private set; }
|
||||
|
||||
public static async Task<AaxToM4bConverter> CreateAsync(string inputFile, string decryptKey)
|
||||
private Func<Task<string>> getKeyFuncAsync { get; }
|
||||
|
||||
public static async Task<AaxToM4bConverter> CreateAsync(string inputFile, string decryptKey, Func<Task<string>> getKeyFunc, Chapters chapters = null)
|
||||
{
|
||||
var converter = new AaxToM4bConverter(inputFile, decryptKey);
|
||||
var converter = new AaxToM4bConverter(inputFile, decryptKey, getKeyFunc);
|
||||
converter.chapters = chapters ?? new AAXChapters(inputFile);
|
||||
await converter.prelimProcessing();
|
||||
converter.printPrelim();
|
||||
|
||||
return converter;
|
||||
}
|
||||
private AaxToM4bConverter(string inputFile, string decryptKey)
|
||||
private AaxToM4bConverter(string inputFile, string decryptKey, Func<Task<string>> getKeyFunc)
|
||||
{
|
||||
ArgumentValidator.EnsureNotNullOrWhiteSpace(inputFile, nameof(inputFile));
|
||||
if (!File.Exists(inputFile))
|
||||
@@ -92,13 +95,13 @@ namespace AaxDecrypter
|
||||
|
||||
inputFileName = inputFile;
|
||||
this.decryptKey = decryptKey;
|
||||
this.getKeyFuncAsync = getKeyFunc;
|
||||
}
|
||||
|
||||
private async Task prelimProcessing()
|
||||
{
|
||||
tags = new Tags(inputFileName);
|
||||
encodingInfo = new EncodingInfo(inputFileName);
|
||||
chapters = new Chapters(inputFileName, tags.duration.TotalSeconds);
|
||||
|
||||
var defaultFilename = Path.Combine(
|
||||
Path.GetDirectoryName(inputFileName),
|
||||
@@ -209,22 +212,12 @@ namespace AaxDecrypter
|
||||
|
||||
private int getKey_decrypt(string tempRipFile)
|
||||
{
|
||||
getKey();
|
||||
decryptKey = getKey();
|
||||
return decrypt(tempRipFile);
|
||||
}
|
||||
private void getKey()
|
||||
{
|
||||
Console.WriteLine("Discovering decrypt key");
|
||||
|
||||
Console.WriteLine("Getting file hash");
|
||||
var checksum = BytesCracker.GetChecksum(inputFileName);
|
||||
Console.WriteLine("File hash calculated: " + checksum);
|
||||
|
||||
Console.WriteLine("Cracking activation bytes");
|
||||
var activation_bytes = BytesCracker.GetActivationBytes(checksum);
|
||||
decryptKey = activation_bytes;
|
||||
Console.WriteLine("Activation bytes cracked. Decrypt key: " + activation_bytes);
|
||||
}
|
||||
// I am NOT happy about doing async this way. Async needs to be added to Step framework
|
||||
string getKey() => getKeyFuncAsync().GetAwaiter().GetResult();
|
||||
|
||||
private int decrypt(string tempRipFile)
|
||||
{
|
||||
@@ -278,9 +271,9 @@ namespace AaxDecrypter
|
||||
public bool Step3_Chapterize()
|
||||
{
|
||||
var str1 = "";
|
||||
if (chapters.FirstChapterStart != 0.0)
|
||||
if (chapters.FirstChapter.StartTime != 0.0)
|
||||
{
|
||||
str1 = " -ss " + chapters.FirstChapterStart.ToString("0.000", CultureInfo.InvariantCulture) + " -t " + (chapters.LastChapterStart - 1.0).ToString("0.000", CultureInfo.InvariantCulture) + " ";
|
||||
str1 = " -ss " + chapters.FirstChapter.StartTime.ToString("0.000", CultureInfo.InvariantCulture) + " -t " + chapters.LastChapter.EndTime.ToString("0.000", CultureInfo.InvariantCulture) + " ";
|
||||
}
|
||||
|
||||
var ffmpegTags = tags.GenerateFfmpegTags();
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
27
AaxDecrypter/Chapter.cs
Normal file
27
AaxDecrypter/Chapter.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AaxDecrypter
|
||||
{
|
||||
public class Chapter
|
||||
{
|
||||
public Chapter(double startTime, double endTime, string title)
|
||||
{
|
||||
StartTime = startTime;
|
||||
EndTime = endTime;
|
||||
Title = title;
|
||||
}
|
||||
/// <summary>
|
||||
/// Chapter start time, in seconds.
|
||||
/// </summary>
|
||||
public double StartTime { get; private set; }
|
||||
/// <summary>
|
||||
/// Chapter end time, in seconds.
|
||||
/// </summary>
|
||||
public double EndTime { get; private set; }
|
||||
public string Title { get; private set; }
|
||||
}
|
||||
}
|
||||
40
AaxDecrypter/Chapters.cs
Normal file
40
AaxDecrypter/Chapters.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace AaxDecrypter
|
||||
{
|
||||
public abstract class Chapters
|
||||
{
|
||||
private List<Chapter> _chapterList = new();
|
||||
public int Count => _chapterList.Count;
|
||||
public Chapter FirstChapter => _chapterList[0];
|
||||
public Chapter LastChapter => _chapterList[Count - 1];
|
||||
public IEnumerable<Chapter> ChapterList => _chapterList.AsEnumerable();
|
||||
public IEnumerable<TimeSpan> GetBeginningTimes() => ChapterList.Select(c => TimeSpan.FromSeconds(c.StartTime));
|
||||
protected void AddChapter(Chapter chapter)
|
||||
{
|
||||
_chapterList.Add(chapter);
|
||||
}
|
||||
protected void AddChapters(IEnumerable<Chapter> chapters)
|
||||
{
|
||||
_chapterList.AddRange(chapters);
|
||||
}
|
||||
public string GenerateFfmpegChapters()
|
||||
{
|
||||
var stringBuilder = new StringBuilder();
|
||||
|
||||
foreach (Chapter c in ChapterList)
|
||||
{
|
||||
stringBuilder.Append("[CHAPTER]\n");
|
||||
stringBuilder.Append("TIMEBASE=1/1000\n");
|
||||
stringBuilder.Append("START=" + c.StartTime * 1000 + "\n");
|
||||
stringBuilder.Append("END=" + c.EndTime * 1000 + "\n");
|
||||
stringBuilder.Append("title=" + c.Title + "\n");
|
||||
}
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,20 +14,15 @@ namespace AaxDecrypter
|
||||
|
||||
stringBuilder.AppendLine(GetFileLine(filePath, "MP3"));
|
||||
|
||||
var beginningTimes = chapters.GetBeginningTimes().ToList();
|
||||
for (var i = 0; i < beginningTimes.Count; i++)
|
||||
var trackCount = 0;
|
||||
foreach (Chapter c in chapters.ChapterList)
|
||||
{
|
||||
var chapter = i + 1;
|
||||
trackCount++;
|
||||
var startTime = TimeSpan.FromSeconds(c.StartTime);
|
||||
|
||||
var timeSpan = beginningTimes[i];
|
||||
var minutes = Math.Floor(timeSpan.TotalMinutes).ToString();
|
||||
var seconds = timeSpan.Seconds.ToString("D2");
|
||||
var milliseconds = (timeSpan.Milliseconds / 10).ToString("D2");
|
||||
var time = minutes + ":" + seconds + ":" + milliseconds;
|
||||
|
||||
stringBuilder.AppendLine($"TRACK {chapter} AUDIO");
|
||||
stringBuilder.AppendLine($" TITLE \"Chapter {chapter:D2}\"");
|
||||
stringBuilder.AppendLine($" INDEX 01 {time}");
|
||||
stringBuilder.AppendLine($"TRACK {trackCount} AUDIO");
|
||||
stringBuilder.AppendLine($" TITLE \"{c.Title}\"");
|
||||
stringBuilder.AppendLine($" INDEX 01 {(int)startTime.TotalMinutes}:{startTime:ss\\:ff}");
|
||||
}
|
||||
|
||||
return stringBuilder.ToString();
|
||||
@@ -1,52 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Dinah.Core;
|
||||
using Dinah.Core.Diagnostics;
|
||||
|
||||
namespace AaxDecrypter
|
||||
{
|
||||
public static class BytesCracker
|
||||
{
|
||||
public static string GetChecksum(string aaxPath)
|
||||
{
|
||||
var info = new ProcessStartInfo
|
||||
{
|
||||
FileName = BytesCrackerSupportLibraries.ffprobePath,
|
||||
Arguments = aaxPath.SurroundWithQuotes(),
|
||||
WorkingDirectory = Directory.GetCurrentDirectory()
|
||||
};
|
||||
|
||||
// checksum is in the debug info. ffprobe's debug info is written to stderr, not stdout
|
||||
var ffprobeStderr = info.RunHidden().Error;
|
||||
|
||||
// example checksum line:
|
||||
// ... [aax] file checksum == 0c527840c4f18517157eb0b4f9d6f9317ce60cd1
|
||||
var checksum = ffprobeStderr.ExtractString("file checksum == ", 40);
|
||||
|
||||
return checksum;
|
||||
}
|
||||
|
||||
/// <summary>use checksum to get activation bytes. activation bytes are unique per audible customer. only have to do this 1x/customer</summary>
|
||||
public static string GetActivationBytes(string checksum)
|
||||
{
|
||||
var info = new ProcessStartInfo
|
||||
{
|
||||
FileName = BytesCrackerSupportLibraries.rcrackPath,
|
||||
Arguments = @". -h " + checksum,
|
||||
WorkingDirectory = Directory.GetCurrentDirectory()
|
||||
};
|
||||
|
||||
var rcrackStdout = info.RunHidden().Output;
|
||||
|
||||
// example result
|
||||
// 0c527840c4f18517157eb0b4f9d6f9317ce60cd1 \xbd\x89X\x09 hex:bd895809
|
||||
var activation_bytes = rcrackStdout.ExtractString("hex:", 8);
|
||||
|
||||
return activation_bytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
using System.IO;
|
||||
|
||||
namespace AaxDecrypter
|
||||
{
|
||||
public static class BytesCrackerSupportLibraries
|
||||
{
|
||||
// GetActivationBytes dependencies
|
||||
// rcrack.exe
|
||||
// alglib1.dll
|
||||
// RainbowCrack files to recover your own Audible activation data (activation_bytes) in an offline manner
|
||||
// audible_byte#4-4_0_10000x789935_0.rtc
|
||||
// audible_byte#4-4_1_10000x791425_0.rtc
|
||||
// audible_byte#4-4_2_10000x790991_0.rtc
|
||||
// audible_byte#4-4_3_10000x792120_0.rtc
|
||||
// audible_byte#4-4_4_10000x790743_0.rtc
|
||||
// audible_byte#4-4_5_10000x790568_0.rtc
|
||||
// audible_byte#4-4_6_10000x791458_0.rtc
|
||||
// audible_byte#4-4_7_10000x791707_0.rtc
|
||||
// audible_byte#4-4_8_10000x790202_0.rtc
|
||||
// audible_byte#4-4_9_10000x791022_0.rtc
|
||||
|
||||
private static string appPath_ { get; } = Path.GetDirectoryName(Dinah.Core.Exe.FileLocationOnDisk);
|
||||
private static string bytesCrackerLib_ { get; } = Path.Combine(appPath_, "BytesCrackerLib");
|
||||
|
||||
public static string ffprobePath { get; } = Path.Combine(bytesCrackerLib_, "ffprobe.exe");
|
||||
public static string rcrackPath { get; } = Path.Combine(bytesCrackerLib_, "rcrack.exe");
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Dinah.Core.Diagnostics;
|
||||
|
||||
namespace AaxDecrypter
|
||||
{
|
||||
public class Chapters
|
||||
{
|
||||
private List<double> markers { get; }
|
||||
|
||||
public double FirstChapterStart => markers[0];
|
||||
public double LastChapterStart => markers[markers.Count - 1];
|
||||
|
||||
public Chapters(string file, double totalTime)
|
||||
{
|
||||
markers = getAAXChapters(file);
|
||||
|
||||
// add end time
|
||||
markers.Add(totalTime);
|
||||
}
|
||||
|
||||
private static List<double> getAAXChapters(string file)
|
||||
{
|
||||
var info = new ProcessStartInfo
|
||||
{
|
||||
FileName = DecryptSupportLibraries.ffprobePath,
|
||||
Arguments = "-loglevel panic -show_chapters -print_format xml \"" + file + "\""
|
||||
};
|
||||
var xml = info.RunHidden().Output;
|
||||
|
||||
var xmlDocument = new System.Xml.XmlDocument();
|
||||
xmlDocument.LoadXml(xml);
|
||||
var chapters = xmlDocument.SelectNodes("/ffprobe/chapters/chapter")
|
||||
.Cast<System.Xml.XmlNode>()
|
||||
.Select(xmlNode => double.Parse(xmlNode.Attributes["start_time"].Value.Replace(",", "."), CultureInfo.InvariantCulture))
|
||||
.ToList();
|
||||
return chapters;
|
||||
}
|
||||
|
||||
// subtract 1 b/c end time marker is a real entry but isn't a real chapter. ie: fencepost
|
||||
public int Count => markers.Count - 1;
|
||||
|
||||
public IEnumerable<TimeSpan> GetBeginningTimes()
|
||||
{
|
||||
for (var i = 0; i < Count; i++)
|
||||
yield return TimeSpan.FromSeconds(markers[i]);
|
||||
}
|
||||
|
||||
public string GenerateFfmpegChapters()
|
||||
{
|
||||
var stringBuilder = new StringBuilder();
|
||||
|
||||
for (var i = 0; i < Count; i++)
|
||||
{
|
||||
var chapter = i + 1;
|
||||
|
||||
var start = markers[i] * 1000.0;
|
||||
var end = markers[i + 1] * 1000.0;
|
||||
var chapterName = chapter.ToString("D3");
|
||||
|
||||
stringBuilder.Append("[CHAPTER]\n");
|
||||
stringBuilder.Append("TIMEBASE=1/1000\n");
|
||||
stringBuilder.Append("START=" + start + "\n");
|
||||
stringBuilder.Append("END=" + end + "\n");
|
||||
stringBuilder.Append("title=" + chapterName + "\n");
|
||||
}
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CsvHelper" Version="16.0.0" />
|
||||
<PackageReference Include="NPOI" Version="2.5.1" />
|
||||
<PackageReference Include="CsvHelper" Version="27.0.2" />
|
||||
<PackageReference Include="NPOI" Version="2.5.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -12,13 +12,12 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.0">
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.5">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.0">
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.5" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.5">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -56,9 +56,10 @@ namespace DataLayer
|
||||
modelBuilder.ApplyConfiguration(new SeriesBookConfig());
|
||||
modelBuilder.ApplyConfiguration(new CategoryConfig());
|
||||
|
||||
// seeds go here. examples in scratch pad
|
||||
modelBuilder
|
||||
.Entity<Category>()
|
||||
// seeds go here. examples in Dinah.EntityFrameworkCore.Tests\DbContextFactoryExample.cs
|
||||
|
||||
modelBuilder
|
||||
.Entity<Category>()
|
||||
.HasData(Category.GetEmpty());
|
||||
modelBuilder
|
||||
.Entity<Contributor>()
|
||||
@@ -6,9 +6,6 @@ namespace DataLayer
|
||||
public class LibationContextFactory : DesignTimeDbContextFactoryBase<LibationContext>
|
||||
{
|
||||
protected override LibationContext CreateNewInstance(DbContextOptions<LibationContext> options) => new LibationContext(options);
|
||||
protected override void UseDatabaseEngine(DbContextOptionsBuilder optionsBuilder, string connectionString) => optionsBuilder
|
||||
//.UseSqlServer
|
||||
.UseSqlite
|
||||
(connectionString);
|
||||
protected override void UseDatabaseEngine(DbContextOptionsBuilder optionsBuilder, string connectionString) => optionsBuilder.UseSqlite(connectionString);
|
||||
}
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
using System;
|
||||
using Dinah.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace _scratch_pad
|
||||
{
|
||||
////// to use this as a console, open properties and change from class library => console
|
||||
//// DON'T FORGET TO REVERT IT
|
||||
//public class Program
|
||||
//{
|
||||
// public static void Main(string[] args)
|
||||
// {
|
||||
// var user = new Student() { Name = "Dinah Cheshire" };
|
||||
// var udi = new UserDef { UserDefId = 1, TagsRaw = "my,tags" };
|
||||
|
||||
// using var context = new MyTestContextDesignTimeDbContextFactory().Create();
|
||||
// context.Add(user);
|
||||
// //context.Add(udi);
|
||||
// context.Update(udi);
|
||||
// context.SaveChanges();
|
||||
|
||||
// Console.WriteLine($"Student was saved in the database with id: {user.Id}");
|
||||
// }
|
||||
//}
|
||||
|
||||
public class MyTestContextDesignTimeDbContextFactory : DesignTimeDbContextFactoryBase<MyTestContext>
|
||||
{
|
||||
protected override MyTestContext CreateNewInstance(DbContextOptions<MyTestContext> options) => new MyTestContext(options);
|
||||
protected override void UseDatabaseEngine(DbContextOptionsBuilder optionsBuilder, string connectionString) => optionsBuilder.UseSqlite(connectionString);
|
||||
}
|
||||
|
||||
public class MyTestContext : DbContext
|
||||
{
|
||||
// see DesignTimeDbContextFactoryBase for info about ctors and connection strings/OnConfiguring()
|
||||
public MyTestContext(DbContextOptions<MyTestContext> options) : base(options) { }
|
||||
|
||||
#region classes for OnModelCreating() seed example
|
||||
class Blog
|
||||
{
|
||||
public int BlogId { get; set; }
|
||||
public string Url { get; set; }
|
||||
public System.Collections.Generic.ICollection<Post> Posts { get; set; }
|
||||
}
|
||||
class Post
|
||||
{
|
||||
public int PostId { get; set; }
|
||||
public string Content { get; set; }
|
||||
public string Title { get; set; }
|
||||
public int BlogId { get; set; }
|
||||
public Blog Blog { get; set; }
|
||||
public Name AuthorName { get; set; }
|
||||
}
|
||||
class Name
|
||||
{
|
||||
public string First { get; set; }
|
||||
public string Last { get; set; }
|
||||
}
|
||||
#endregion
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
// config
|
||||
modelBuilder.Entity<Blog>(entity => entity.Property(e => e.Url).IsRequired());
|
||||
modelBuilder.Entity<Order>().OwnsOne(p => p.OrderDetails, cb =>
|
||||
{
|
||||
cb.OwnsOne(c => c.BillingAddress);
|
||||
cb.OwnsOne(c => c.ShippingAddress);
|
||||
});
|
||||
modelBuilder.Entity<Post>(entity =>
|
||||
entity
|
||||
.HasOne(d => d.Blog)
|
||||
.WithMany(p => p.Posts)
|
||||
.HasForeignKey("BlogId"));
|
||||
|
||||
// BlogSeed
|
||||
modelBuilder.Entity<Blog>().HasData(new Blog { BlogId = 1, Url = "http://sample.com" });
|
||||
|
||||
// PostSeed
|
||||
modelBuilder.Entity<Post>().HasData(new Post() { BlogId = 1, PostId = 1, Title = "First post", Content = "Test 1" });
|
||||
|
||||
// AnonymousPostSeed
|
||||
modelBuilder.Entity<Post>().HasData(new { BlogId = 1, PostId = 2, Title = "Second post", Content = "Test 2" });
|
||||
|
||||
// OwnedTypeSeed
|
||||
modelBuilder.Entity<Post>().OwnsOne(p => p.AuthorName).HasData(
|
||||
new { PostId = 1, First = "Andriy", Last = "Svyryd" },
|
||||
new { PostId = 2, First = "Diego", Last = "Vega" });
|
||||
}
|
||||
|
||||
public DbSet<Student> Students { get; set; }
|
||||
public DbSet<UserDef> UserDefs { get; set; }
|
||||
public DbSet<Order> Orders { get; set; }
|
||||
}
|
||||
|
||||
public class Student
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
public class UserDef
|
||||
{
|
||||
public int UserDefId { get; set; }
|
||||
public string TagsRaw { get; set; }
|
||||
}
|
||||
|
||||
public class Order
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public OrderDetails OrderDetails { get; set; }
|
||||
}
|
||||
public class OrderDetails
|
||||
{
|
||||
public StreetAddress BillingAddress { get; set; }
|
||||
public StreetAddress ShippingAddress { get; set; }
|
||||
}
|
||||
public class StreetAddress
|
||||
{
|
||||
public string Street { get; set; }
|
||||
public string City { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,6 @@
|
||||
{
|
||||
"ConnectionStrings": {
|
||||
"LibationContext_sqlserver": "Server=(LocalDb)\\MSSQLLocalDB;Database=DataLayer.LibationContext;Integrated Security=true;",
|
||||
|
||||
"// this connection string is ONLY used for DataLayer's Migrations. this appsettings.json file is NOT used at all by application; it is overwritten": "",
|
||||
"LibationContext": "Data Source=LibationContext.db;Foreign Keys=False;",
|
||||
|
||||
"// sqlite notes": "",
|
||||
"// absolute path example": "Data Source=C:/foo/bar/sample.db",
|
||||
"// relative path example": "Data Source=sample.db",
|
||||
"// on windows: sqlite paths accept windows and/or unix slashes": "",
|
||||
"MyTestContext": "Data Source=%DESKTOP%/sample.db"
|
||||
"LibationContext": "Data Source=LibationContext.db;Foreign Keys=False;"
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using AaxDecrypter;
|
||||
using AudibleApi;
|
||||
using DataLayer;
|
||||
using Dinah.Core;
|
||||
using Dinah.Core.ErrorHandling;
|
||||
@@ -56,7 +57,11 @@ namespace FileLiberator
|
||||
if (AudibleFileStorage.Audio.Exists(libraryBook.Book.AudibleProductId))
|
||||
return new StatusHandler { "Cannot find decrypt. Final audio file already exists" };
|
||||
|
||||
var outputAudioFilename = await aaxToM4bConverterDecrypt(aaxFilename, libraryBook);
|
||||
var api = await AudibleApiActions.GetApiAsync(libraryBook.Account, libraryBook.Book.Locale);
|
||||
|
||||
var chapters = await downloadChapterNames(libraryBook, api);
|
||||
|
||||
var outputAudioFilename = await aaxToM4bConverterDecrypt(aaxFilename, libraryBook, chapters, api);
|
||||
|
||||
// decrypt failed
|
||||
if (outputAudioFilename == null)
|
||||
@@ -90,7 +95,23 @@ namespace FileLiberator
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string> aaxToM4bConverterDecrypt(string aaxFilename, LibraryBook libraryBook)
|
||||
private static async Task<Chapters> downloadChapterNames(LibraryBook libraryBook, Api api)
|
||||
{
|
||||
try
|
||||
{
|
||||
var contentMetadata = await api.GetLibraryBookMetadataAsync(libraryBook.Book.AudibleProductId);
|
||||
if (contentMetadata?.ChapterInfo is null)
|
||||
return null;
|
||||
|
||||
return new DownloadedChapters(contentMetadata.ChapterInfo);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string> aaxToM4bConverterDecrypt(string aaxFilename, LibraryBook libraryBook, Chapters chapters, Api api)
|
||||
{
|
||||
DecryptBegin?.Invoke(this, $"Begin decrypting {aaxFilename}");
|
||||
|
||||
@@ -102,7 +123,7 @@ namespace FileLiberator
|
||||
.AccountsSettings
|
||||
.GetAccount(libraryBook.Account, libraryBook.Book.Locale);
|
||||
|
||||
var converter = await AaxToM4bConverter.CreateAsync(aaxFilename, account.DecryptKey);
|
||||
var converter = await AaxToM4bConverter.CreateAsync(aaxFilename, account.DecryptKey, api.GetActivationBytesAsync, chapters);
|
||||
converter.AppName = "Libation";
|
||||
|
||||
TitleDiscovered?.Invoke(this, converter.tags.title);
|
||||
22
FileLiberator/DownloadedChapters.cs
Normal file
22
FileLiberator/DownloadedChapters.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using AaxDecrypter;
|
||||
using AudibleApiDTOs;
|
||||
using Dinah.Core.Diagnostics;
|
||||
|
||||
|
||||
namespace FileLiberator
|
||||
{
|
||||
public class DownloadedChapters : Chapters
|
||||
{
|
||||
public DownloadedChapters(ChapterInfo chapterInfo)
|
||||
{
|
||||
AddChapters(chapterInfo.Chapters
|
||||
.Select(c => new AaxDecrypter.Chapter(c.StartOffsetMs / 1000d, (c.StartOffsetMs + c.LengthMs) / 1000d, c.Title)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,9 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Polly" Version="7.2.1" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.33" />
|
||||
<PackageReference Include="Octokit" Version="0.50.0" />
|
||||
<PackageReference Include="Polly" Version="7.2.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -86,7 +86,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "0 Libation Tests", "0 Libat
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InternalUtilities.Tests", "_Tests\InternalUtilities.Tests\InternalUtilities.Tests.csproj", "{8447C956-B03E-4F59-9DD4-877793B849D9}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibationSearchEngine.Tests", "_Tests\LibationSearchEngine.Tests\LibationSearchEngine.Tests.csproj", "{C5B21768-C7C9-4FCB-AC1E-187B223D5A98}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibationSearchEngine.Tests", "_Tests\LibationSearchEngine.Tests\LibationSearchEngine.Tests.csproj", "{C5B21768-C7C9-4FCB-AC1E-187B223D5A98}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dinah.EntityFrameworkCore.Tests", "..\Dinah.Core\_Tests\Dinah.EntityFrameworkCore.Tests\Dinah.EntityFrameworkCore.Tests.csproj", "{6F5131A0-09AE-4707-B82B-5E53CB74688E}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@@ -214,6 +216,10 @@ Global
|
||||
{C5B21768-C7C9-4FCB-AC1E-187B223D5A98}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C5B21768-C7C9-4FCB-AC1E-187B223D5A98}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C5B21768-C7C9-4FCB-AC1E-187B223D5A98}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6F5131A0-09AE-4707-B82B-5E53CB74688E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6F5131A0-09AE-4707-B82B-5E53CB74688E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6F5131A0-09AE-4707-B82B-5E53CB74688E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6F5131A0-09AE-4707-B82B-5E53CB74688E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -249,6 +255,7 @@ Global
|
||||
{F3B04A3A-20C8-4582-A54A-715AF6A5D859} = {8679CAC8-9164-4007-BDD2-F004810EDA14}
|
||||
{8447C956-B03E-4F59-9DD4-877793B849D9} = {67E66E82-5532-4440-AFB3-9FB1DF9DEF53}
|
||||
{C5B21768-C7C9-4FCB-AC1E-187B223D5A98} = {67E66E82-5532-4440-AFB3-9FB1DF9DEF53}
|
||||
{6F5131A0-09AE-4707-B82B-5E53CB74688E} = {38E6C6D9-963A-4C5B-89F4-F2F14885ADFD}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {615E00ED-BAEF-4E8E-A92A-9B82D87942A9}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<!-- <PublishSingleFile>true</PublishSingleFile> -->
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
|
||||
<Version>4.2.4.1</Version>
|
||||
<Version>4.4.0.5</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -21,7 +21,7 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Octokit" Version="0.48.0" />
|
||||
<PackageReference Include="Octokit" Version="0.50.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user