Merge pull request #300 from Mbucari/master
Option to combine Opening/End Credits chapters and other changes
This commit is contained in:
commit
dceb3121b1
2
.gitignore
vendored
2
.gitignore
vendored
@ -184,7 +184,7 @@ publish/
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
#*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
|
||||
12
Source/.config/dotnet-tools.json
Normal file
12
Source/.config/dotnet-tools.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"version": 1,
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"frogvall.dotnetbumpversion": {
|
||||
"version": "3.0.1",
|
||||
"commands": [
|
||||
"bump-version"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -8,7 +8,16 @@
|
||||
<PackageReference Include="AAXClean.Codecs" Version="0.2.10" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\FileManager\FileManager.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@ -1,4 +0,0 @@
|
||||
{
|
||||
"//": "https://github.com/BalassaMarton/MSBump",
|
||||
BumpRevision: true
|
||||
}
|
||||
@ -1,22 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
<Version>8.1.4.1</Version>
|
||||
<Version>8.1.4.31</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MSBump" Version="2.3.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Octokit" Version="0.51.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ApplicationServices\ApplicationServices.csproj" />
|
||||
<ProjectReference Include="..\AudibleUtilities\AudibleUtilities.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
|
||||
<Exec Command="if $(Configuration) == Debug (
dotnet bump-version revision AppScaffolding.csproj
)" />
|
||||
</Target>
|
||||
</Project>
|
||||
@ -25,7 +25,7 @@ namespace AppScaffolding
|
||||
: value;
|
||||
|
||||
#region appsettings.json
|
||||
private static string APPSETTINGS_JSON { get; } = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location), "appsettings.json");
|
||||
private static string APPSETTINGS_JSON { get; } = Path.Combine(Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName), "appsettings.json");
|
||||
|
||||
public static bool APPSETTINGS_Json_Exists => File.Exists(APPSETTINGS_JSON);
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
@ -14,4 +14,12 @@
|
||||
<ProjectReference Include="..\LibationSearchEngine\LibationSearchEngine.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@ -162,10 +162,19 @@ namespace AudibleUtilities
|
||||
if (exceptions is not null && exceptions.Any())
|
||||
throw new AggregateException(exceptions);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private static List<IValidator> getValidators()
|
||||
{
|
||||
var type = typeof(IValidator);
|
||||
var types = AppDomain.CurrentDomain.GetAssemblies()
|
||||
.SelectMany(s => s.GetTypes())
|
||||
.Where(p => type.IsAssignableFrom(p) && !p.IsInterface);
|
||||
|
||||
return types.Select(t => Activator.CreateInstance(t) as IValidator).ToList();
|
||||
}
|
||||
|
||||
#region episodes and podcasts
|
||||
|
||||
private async Task<List<Item>> getChildEpisodesAsync(SemaphoreSlim concurrencySemaphore, Item parent)
|
||||
@ -197,7 +206,8 @@ namespace AudibleUtilities
|
||||
if (numSeriesParents != 1)
|
||||
{
|
||||
//There should only ever be 1 top-level parent per episode. If not, log
|
||||
//and throw so we can figure out what to do about those special cases.
|
||||
//so we can figure out what to do about those special cases, and don't
|
||||
//import the episode.
|
||||
JsonSerializerSettings Settings = new()
|
||||
{
|
||||
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
|
||||
@ -207,9 +217,8 @@ namespace AudibleUtilities
|
||||
new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
|
||||
},
|
||||
};
|
||||
var ex = new ApplicationException($"Found {numSeriesParents} parents for {parent.Asin}");
|
||||
Serilog.Log.Logger.Error(ex, $"Episode Product:\r\n{JsonConvert.SerializeObject(parent, Formatting.None, Settings)}");
|
||||
throw ex;
|
||||
Serilog.Log.Logger.Error($"Found {numSeriesParents} parents for {parent.Asin}\r\nEpisode Product:\r\n{JsonConvert.SerializeObject(parent, Formatting.None, Settings)}");
|
||||
return new List<Item>();
|
||||
}
|
||||
|
||||
var realParent = seriesParents.Single(p => p.IsSeriesParent);
|
||||
@ -329,15 +338,5 @@ namespace AudibleUtilities
|
||||
return results;
|
||||
}
|
||||
#endregion
|
||||
|
||||
private static List<IValidator> getValidators()
|
||||
{
|
||||
var type = typeof(IValidator);
|
||||
var types = AppDomain.CurrentDomain.GetAssemblies()
|
||||
.SelectMany(s => s.GetTypes())
|
||||
.Where(p => type.IsAssignableFrom(p) && !p.IsInterface);
|
||||
|
||||
return types.Select(t => Activator.CreateInstance(t) as IValidator).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,5 +11,13 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\LibationFileManager\LibationFileManager.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@ -12,11 +12,13 @@ namespace DataLayer.Configurations
|
||||
|
||||
entity.OwnsOne(b => b.Rating);
|
||||
|
||||
entity.Property(nameof(Book._audioFormat));
|
||||
//
|
||||
// CRUCIAL: ignore unmapped collections, even get-only
|
||||
//
|
||||
entity.Ignore(nameof(Book.Authors));
|
||||
entity.Ignore(nameof(Book.Narrators));
|
||||
entity.Ignore(nameof(Book.AudioFormat));
|
||||
//// these don't seem to matter
|
||||
//entity.Ignore(nameof(Book.AuthorNames));
|
||||
//entity.Ignore(nameof(Book.NarratorNames));
|
||||
|
||||
@ -6,9 +6,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
|
||||
<ApplicationIcon />
|
||||
<OutputType>Library</OutputType>
|
||||
<StartupObject />
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -23,8 +21,16 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\LibationFileManager\LibationFileManager.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
62
Source/DataLayer/EfClasses/AudioFormat.cs
Normal file
62
Source/DataLayer/EfClasses/AudioFormat.cs
Normal file
@ -0,0 +1,62 @@
|
||||
using System;
|
||||
|
||||
namespace DataLayer
|
||||
{
|
||||
internal enum AudioFormatEnum : long
|
||||
{
|
||||
//Defining the enum this way ensures that when comparing:
|
||||
//LC_128_44100_stereo > LC_64_44100_stereo > LC_64_22050_stereo > LC_64_22050_stereo
|
||||
//This matches how audible interprets these codecs when specifying quality using AudibleApi.DownloadQuality
|
||||
//I've never seen mono formats.
|
||||
Unknown = 0,
|
||||
LC_32_22050_stereo = (32L << 18) | (22050 << 2) | 2,
|
||||
LC_64_22050_stereo = (64L << 18) | (22050 << 2) | 2,
|
||||
LC_64_44100_stereo = (64L << 18) | (44100 << 2) | 2,
|
||||
LC_128_44100_stereo = (128L << 18) | (44100 << 2) | 2,
|
||||
}
|
||||
|
||||
public class AudioFormat : IComparable<AudioFormat>, IComparable
|
||||
{
|
||||
|
||||
internal int AudioFormatID { get; private set; }
|
||||
public int Bitrate { get; private init; }
|
||||
public int SampleRate { get; private init; }
|
||||
public int Channels { get; private init; }
|
||||
public bool IsValid => Bitrate != 0 && SampleRate != 0 && Channels != 0;
|
||||
|
||||
public static AudioFormat FromString(string formatStr)
|
||||
{
|
||||
if (Enum.TryParse(formatStr, ignoreCase: true, out AudioFormatEnum enumVal))
|
||||
return FromEnum(enumVal);
|
||||
return FromEnum(AudioFormatEnum.Unknown);
|
||||
}
|
||||
|
||||
internal static AudioFormat FromEnum(AudioFormatEnum enumVal)
|
||||
{
|
||||
var val = (long)enumVal;
|
||||
|
||||
return new()
|
||||
{
|
||||
Bitrate = (int)(val >> 18),
|
||||
SampleRate = (int)(val >> 2) & ushort.MaxValue,
|
||||
Channels = (int)(val & 3)
|
||||
};
|
||||
}
|
||||
internal AudioFormatEnum ToEnum()
|
||||
{
|
||||
var val = (AudioFormatEnum)(((long)Bitrate << 18) | ((long)SampleRate << 2) | (long)Channels);
|
||||
|
||||
return Enum.IsDefined(val) ?
|
||||
val : AudioFormatEnum.Unknown;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
=> IsValid ?
|
||||
$"{Bitrate} Kbps, {SampleRate / 1000d:F1} kHz, {(Channels == 2 ? "Stereo" : Channels)}" :
|
||||
"Unknown";
|
||||
|
||||
public int CompareTo(AudioFormat other) => ToEnum().CompareTo(other.ToEnum());
|
||||
|
||||
public int CompareTo(object obj) => CompareTo(obj as AudioFormat);
|
||||
}
|
||||
}
|
||||
@ -25,6 +25,7 @@ namespace DataLayer
|
||||
Parent = 4,
|
||||
}
|
||||
|
||||
|
||||
public class Book
|
||||
{
|
||||
// implementation detail. set by db only. only used by data layer
|
||||
@ -38,6 +39,10 @@ namespace DataLayer
|
||||
public ContentType ContentType { get; private set; }
|
||||
public string Locale { get; private set; }
|
||||
|
||||
internal AudioFormatEnum _audioFormat;
|
||||
|
||||
public AudioFormat AudioFormat { get => AudioFormat.FromEnum(_audioFormat); set => _audioFormat = value.ToEnum(); }
|
||||
|
||||
// mutable
|
||||
public string PictureId { get; set; }
|
||||
public string PictureLarge { get; set; }
|
||||
|
||||
397
Source/DataLayer/Migrations/20220624214932_AddAudioFormat.Designer.cs
generated
Normal file
397
Source/DataLayer/Migrations/20220624214932_AddAudioFormat.Designer.cs
generated
Normal file
@ -0,0 +1,397 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using DataLayer;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace DataLayer.Migrations
|
||||
{
|
||||
[DbContext(typeof(LibationContext))]
|
||||
[Migration("20220624214932_AddAudioFormat")]
|
||||
partial class AddAudioFormat
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "6.0.6");
|
||||
|
||||
modelBuilder.Entity("DataLayer.Book", b =>
|
||||
{
|
||||
b.Property<int>("BookId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("AudibleProductId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("CategoryId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ContentType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime?>("DatePublished")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsAbridged")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("LengthInMinutes")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Locale")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PictureId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PictureLarge")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long>("_audioFormat")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("BookId");
|
||||
|
||||
b.HasIndex("AudibleProductId");
|
||||
|
||||
b.HasIndex("CategoryId");
|
||||
|
||||
b.ToTable("Books");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.BookContributor", b =>
|
||||
{
|
||||
b.Property<int>("BookId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ContributorId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Role")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<byte>("Order")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("BookId", "ContributorId", "Role");
|
||||
|
||||
b.HasIndex("BookId");
|
||||
|
||||
b.HasIndex("ContributorId");
|
||||
|
||||
b.ToTable("BookContributor");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Category", b =>
|
||||
{
|
||||
b.Property<int>("CategoryId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("AudibleCategoryId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("ParentCategoryCategoryId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("CategoryId");
|
||||
|
||||
b.HasIndex("AudibleCategoryId");
|
||||
|
||||
b.HasIndex("ParentCategoryCategoryId");
|
||||
|
||||
b.ToTable("Categories");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
CategoryId = -1,
|
||||
AudibleCategoryId = "",
|
||||
Name = ""
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Contributor", b =>
|
||||
{
|
||||
b.Property<int>("ContributorId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("AudibleContributorId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("ContributorId");
|
||||
|
||||
b.HasIndex("Name");
|
||||
|
||||
b.ToTable("Contributors");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
ContributorId = -1,
|
||||
Name = ""
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.LibraryBook", b =>
|
||||
{
|
||||
b.Property<int>("BookId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Account")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("DateAdded")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("BookId");
|
||||
|
||||
b.ToTable("LibraryBooks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Series", b =>
|
||||
{
|
||||
b.Property<int>("SeriesId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("AudibleSeriesId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("SeriesId");
|
||||
|
||||
b.HasIndex("AudibleSeriesId");
|
||||
|
||||
b.ToTable("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.SeriesBook", b =>
|
||||
{
|
||||
b.Property<int>("SeriesId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("BookId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Order")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("SeriesId", "BookId");
|
||||
|
||||
b.HasIndex("BookId");
|
||||
|
||||
b.HasIndex("SeriesId");
|
||||
|
||||
b.ToTable("SeriesBook");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Book", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Category", "Category")
|
||||
.WithMany()
|
||||
.HasForeignKey("CategoryId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.OwnsOne("DataLayer.Rating", "Rating", b1 =>
|
||||
{
|
||||
b1.Property<int>("BookId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b1.Property<float>("OverallRating")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b1.Property<float>("PerformanceRating")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b1.Property<float>("StoryRating")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b1.HasKey("BookId");
|
||||
|
||||
b1.ToTable("Books");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("BookId");
|
||||
});
|
||||
|
||||
b.OwnsMany("DataLayer.Supplement", "Supplements", b1 =>
|
||||
{
|
||||
b1.Property<int>("SupplementId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b1.Property<int>("BookId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b1.Property<string>("Url")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b1.HasKey("SupplementId");
|
||||
|
||||
b1.HasIndex("BookId");
|
||||
|
||||
b1.ToTable("Supplement");
|
||||
|
||||
b1.WithOwner("Book")
|
||||
.HasForeignKey("BookId");
|
||||
|
||||
b1.Navigation("Book");
|
||||
});
|
||||
|
||||
b.OwnsOne("DataLayer.UserDefinedItem", "UserDefinedItem", b1 =>
|
||||
{
|
||||
b1.Property<int>("BookId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b1.Property<int>("BookStatus")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b1.Property<int?>("PdfStatus")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b1.Property<string>("Tags")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b1.HasKey("BookId");
|
||||
|
||||
b1.ToTable("UserDefinedItem", (string)null);
|
||||
|
||||
b1.WithOwner("Book")
|
||||
.HasForeignKey("BookId");
|
||||
|
||||
b1.OwnsOne("DataLayer.Rating", "Rating", b2 =>
|
||||
{
|
||||
b2.Property<int>("UserDefinedItemBookId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b2.Property<float>("OverallRating")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b2.Property<float>("PerformanceRating")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b2.Property<float>("StoryRating")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b2.HasKey("UserDefinedItemBookId");
|
||||
|
||||
b2.ToTable("UserDefinedItem");
|
||||
|
||||
b2.WithOwner()
|
||||
.HasForeignKey("UserDefinedItemBookId");
|
||||
});
|
||||
|
||||
b1.Navigation("Book");
|
||||
|
||||
b1.Navigation("Rating");
|
||||
});
|
||||
|
||||
b.Navigation("Category");
|
||||
|
||||
b.Navigation("Rating");
|
||||
|
||||
b.Navigation("Supplements");
|
||||
|
||||
b.Navigation("UserDefinedItem");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.BookContributor", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Book", "Book")
|
||||
.WithMany("ContributorsLink")
|
||||
.HasForeignKey("BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("DataLayer.Contributor", "Contributor")
|
||||
.WithMany("BooksLink")
|
||||
.HasForeignKey("ContributorId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Book");
|
||||
|
||||
b.Navigation("Contributor");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Category", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Category", "ParentCategory")
|
||||
.WithMany()
|
||||
.HasForeignKey("ParentCategoryCategoryId");
|
||||
|
||||
b.Navigation("ParentCategory");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.LibraryBook", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Book", "Book")
|
||||
.WithOne()
|
||||
.HasForeignKey("DataLayer.LibraryBook", "BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Book");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.SeriesBook", b =>
|
||||
{
|
||||
b.HasOne("DataLayer.Book", "Book")
|
||||
.WithMany("SeriesLink")
|
||||
.HasForeignKey("BookId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("DataLayer.Series", "Series")
|
||||
.WithMany("BooksLink")
|
||||
.HasForeignKey("SeriesId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Book");
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Book", b =>
|
||||
{
|
||||
b.Navigation("ContributorsLink");
|
||||
|
||||
b.Navigation("SeriesLink");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Contributor", b =>
|
||||
{
|
||||
b.Navigation("BooksLink");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DataLayer.Series", b =>
|
||||
{
|
||||
b.Navigation("BooksLink");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
26
Source/DataLayer/Migrations/20220624214932_AddAudioFormat.cs
Normal file
26
Source/DataLayer/Migrations/20220624214932_AddAudioFormat.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace DataLayer.Migrations
|
||||
{
|
||||
public partial class AddAudioFormat : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<long>(
|
||||
name: "_audioFormat",
|
||||
table: "Books",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0L);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "_audioFormat",
|
||||
table: "Books");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -15,7 +15,7 @@ namespace DataLayer.Migrations
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "6.0.4");
|
||||
modelBuilder.HasAnnotation("ProductVersion", "6.0.6");
|
||||
|
||||
modelBuilder.Entity("DataLayer.Book", b =>
|
||||
{
|
||||
@ -56,6 +56,9 @@ namespace DataLayer.Migrations
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long>("_audioFormat")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("BookId");
|
||||
|
||||
b.HasIndex("AudibleProductId");
|
||||
|
||||
@ -162,6 +162,9 @@ namespace DtoImporterService
|
||||
{
|
||||
var item = importItem.DtoItem;
|
||||
|
||||
var codec = item.AvailableCodecs?.Max(f => AudioFormat.FromString(f.EnhancedCodec)) ?? new AudioFormat();
|
||||
book.AudioFormat = codec;
|
||||
|
||||
// set/update book-specific info which may have changed
|
||||
if (item.PictureId is not null)
|
||||
book.PictureId = item.PictureId;
|
||||
|
||||
@ -91,7 +91,7 @@ namespace DtoImporterService
|
||||
return hash.Count;
|
||||
}
|
||||
|
||||
private Contributor addContributor(string name, string id = null)
|
||||
private Contributor addContributor(string name, string id = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -108,6 +108,6 @@ namespace DtoImporterService
|
||||
Serilog.Log.Logger.Error(ex, "Error adding contributor. {@DebugInfo}", new { name, id });
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,14 @@
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\AudibleUtilities\AudibleUtilities.csproj" />
|
||||
<ProjectReference Include="..\DataLayer\DataLayer.csproj" />
|
||||
|
||||
@ -55,7 +55,7 @@ namespace DtoImporterService
|
||||
protected ItemsImporterBase(LibationContext context) : base(context) { }
|
||||
|
||||
protected abstract IValidator Validator { get; }
|
||||
public sealed override IEnumerable<Exception> Validate(IEnumerable<ImportItem> importItems)
|
||||
public sealed override IEnumerable<Exception> Validate(IEnumerable<ImportItem> importItems)
|
||||
=> Validator.Validate(importItems.Select(i => i.DtoItem));
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@ namespace DtoImporterService
|
||||
// just use the first
|
||||
var hash = newItems.ToDictionarySafe(dto => dto.DtoItem.ProductId);
|
||||
foreach (var kvp in hash)
|
||||
{
|
||||
{
|
||||
var newItem = kvp.Value;
|
||||
|
||||
var libraryBook = new LibraryBook(
|
||||
|
||||
@ -166,6 +166,9 @@ namespace FileLiberator
|
||||
};
|
||||
|
||||
var chapters = flattenChapters(contentLic.ContentMetadata.ChapterInfo.Chapters).OrderBy(c => c.StartOffsetMs).ToList();
|
||||
|
||||
if (config.MergeOpeningAndEndCredits)
|
||||
combineCredits(chapters);
|
||||
|
||||
if (config.AllowLibationFixup || outputFormat == OutputFormat.Mp3)
|
||||
{
|
||||
@ -192,7 +195,7 @@ namespace FileLiberator
|
||||
return dlOptions;
|
||||
}
|
||||
|
||||
public static List<AudibleApi.Common.Chapter> flattenChapters(IEnumerable<AudibleApi.Common.Chapter> chapters, string titleConcat = ": ")
|
||||
public static List<AudibleApi.Common.Chapter> flattenChapters(IList<AudibleApi.Common.Chapter> chapters, string titleConcat = ": ")
|
||||
{
|
||||
List<AudibleApi.Common.Chapter> chaps = new();
|
||||
|
||||
@ -217,6 +220,22 @@ namespace FileLiberator
|
||||
return chaps;
|
||||
}
|
||||
|
||||
public static void combineCredits(IList<AudibleApi.Common.Chapter> chapters)
|
||||
{
|
||||
if (chapters.Count > 1 && chapters[0].Title == "Opening Credits")
|
||||
{
|
||||
chapters[1].StartOffsetMs = chapters[0].StartOffsetMs;
|
||||
chapters[1].StartOffsetSec = chapters[0].StartOffsetSec;
|
||||
chapters[1].LengthMs += chapters[0].LengthMs;
|
||||
chapters.RemoveAt(0);
|
||||
}
|
||||
if (chapters.Count > 1 && chapters[^1].Title == "End Credits")
|
||||
{
|
||||
chapters[^2].LengthMs += chapters[^1].LengthMs;
|
||||
chapters.Remove(chapters[^1]);
|
||||
}
|
||||
}
|
||||
|
||||
private static void downloadValidation(LibraryBook libraryBook)
|
||||
{
|
||||
string errorString(string field)
|
||||
|
||||
@ -11,4 +11,13 @@
|
||||
<ProjectReference Include="..\AudibleUtilities\AudibleUtilities.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
</Project>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
@ -9,4 +9,12 @@
|
||||
<PackageReference Include="Polly" Version="7.2.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@ -35,18 +35,24 @@ namespace FileManager
|
||||
}
|
||||
else
|
||||
{
|
||||
file = replaceFileName(file, paramReplacements);
|
||||
fileName = Path.GetDirectoryName(fileName);
|
||||
pathParts.Add(file);
|
||||
fileName = Path.GetDirectoryName(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
pathParts.Reverse();
|
||||
var fileNamePart = pathParts[^1];
|
||||
pathParts.Remove(fileNamePart);
|
||||
|
||||
return FileUtility.GetValidFilename(Path.Join(pathParts.ToArray()), replacements, returnFirstExisting);
|
||||
LongPath directory = Path.Join(pathParts.Select(p => replaceFileName(p, paramReplacements, LongPath.MaxFilenameLength)).ToArray());
|
||||
|
||||
//If file already exists, GetValidFilename will append " (n)" to the filename.
|
||||
//This could cause the filename length to exceed MaxFilenameLength, so reduce
|
||||
//allowable filename length by 5 chars, allowing for up to 99 duplicates.
|
||||
return FileUtility.GetValidFilename(Path.Join(directory, replaceFileName(fileNamePart, paramReplacements, LongPath.MaxFilenameLength - 5)), replacements, returnFirstExisting);
|
||||
}
|
||||
|
||||
private string replaceFileName(string filename, Dictionary<string,string> paramReplacements)
|
||||
private string replaceFileName(string filename, Dictionary<string,string> paramReplacements, int maxFilenameLength)
|
||||
{
|
||||
List<StringBuilder> filenameParts = new();
|
||||
//Build the filename in parts, replacing replacement parameters with
|
||||
@ -82,7 +88,7 @@ namespace FileManager
|
||||
|
||||
//Remove 1 character from the end of the longest filename part until
|
||||
//the total filename is less than max filename length
|
||||
while (filenameParts.Sum(p => p.Length) > LongPath.MaxFilenameLength)
|
||||
while (filenameParts.Sum(p => p.Length) > maxFilenameLength)
|
||||
{
|
||||
int maxLength = filenameParts.Max(p => p.Length);
|
||||
var maxEntry = filenameParts.First(p => p.Length == maxLength);
|
||||
|
||||
@ -136,19 +136,21 @@ namespace FileManager
|
||||
{
|
||||
if (toReplace == Replacement.QUOTE_MARK)
|
||||
{
|
||||
if (preceding == default ||
|
||||
(preceding != default
|
||||
&& !char.IsLetter(preceding)
|
||||
&& !char.IsNumber(preceding)
|
||||
&& (char.IsLetter(succeding) || char.IsNumber(succeding))
|
||||
if (
|
||||
preceding == default ||
|
||||
(
|
||||
!char.IsLetter(preceding) &&
|
||||
!char.IsNumber(preceding) &&
|
||||
(char.IsLetter(succeding) || char.IsNumber(succeding))
|
||||
)
|
||||
)
|
||||
return OpenQuote;
|
||||
else if (succeding == default ||
|
||||
(succeding != default
|
||||
&& !char.IsLetter(succeding)
|
||||
&& !char.IsNumber(succeding)
|
||||
&& (char.IsLetter(preceding) || char.IsNumber(preceding))
|
||||
else if (
|
||||
succeding == default ||
|
||||
(
|
||||
!char.IsLetter(succeding) &&
|
||||
!char.IsNumber(succeding) &&
|
||||
(char.IsLetter(preceding) || char.IsNumber(preceding))
|
||||
)
|
||||
)
|
||||
return CloseQuote;
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<ApplicationIcon>hangover.ico</ApplicationIcon>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
|
||||
<PublishReadyToRun>true</PublishReadyToRun>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
@ -24,11 +23,13 @@
|
||||
edit debug and release output paths
|
||||
-->
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<OutputPath>..\LibationWinForms\bin\Debug</OutputPath>
|
||||
<OutputPath>..\bin\Debug</OutputPath>
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<OutputPath>..\LibationWinForms\bin\Release</OutputPath>
|
||||
<OutputPath>..\bin\Release</OutputPath>
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -37,7 +38,7 @@
|
||||
<ProjectReference Include="..\FileLiberator\FileLiberator.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="Form1.*.cs">
|
||||
<DependentUpon>Form1.cs</DependentUpon>
|
||||
</Compile>
|
||||
|
||||
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
-->
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Any CPU</Platform>
|
||||
<PublishDir>..\bin\publish\</PublishDir>
|
||||
<PublishProtocol>FileSystem</PublishProtocol>
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<SelfContained>false</SelfContained>
|
||||
<PublishSingleFile>false</PublishSingleFile>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@ -4,12 +4,11 @@
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
|
||||
<PublishTrimmed>true</PublishTrimmed>
|
||||
<PublishReadyToRun>true</PublishReadyToRun>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<IsPublishable>True</IsPublishable>
|
||||
</PropertyGroup>
|
||||
|
||||
<!--
|
||||
@ -23,11 +22,13 @@
|
||||
edit debug and release output paths
|
||||
-->
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<OutputPath>..\LibationWinForms\bin\Debug</OutputPath>
|
||||
<OutputPath>..\bin\Debug</OutputPath>
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<OutputPath>..\LibationWinForms\bin\Release</OutputPath>
|
||||
<OutputPath>..\bin\Release</OutputPath>
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
-->
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Any CPU</Platform>
|
||||
<PublishDir>..\bin\publish\</PublishDir>
|
||||
<PublishProtocol>FileSystem</PublishProtocol>
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<SelfContained>false</SelfContained>
|
||||
<PublishSingleFile>false</PublishSingleFile>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@ -117,6 +117,13 @@ namespace LibationFileManager
|
||||
set => persistentDictionary.SetNonString(nameof(SplitFilesByChapter), value);
|
||||
}
|
||||
|
||||
[Description("Merge Opening/End Credits into the following/preceding chapters")]
|
||||
public bool MergeOpeningAndEndCredits
|
||||
{
|
||||
get => persistentDictionary.GetNonString<bool>(nameof(MergeOpeningAndEndCredits));
|
||||
set => persistentDictionary.SetNonString(nameof(MergeOpeningAndEndCredits), value);
|
||||
}
|
||||
|
||||
[Description("Strip \"(Unabridged)\" from audiobook metadata tags")]
|
||||
public bool StripUnabridged
|
||||
{
|
||||
@ -437,7 +444,7 @@ namespace LibationFileManager
|
||||
#endregion
|
||||
|
||||
#region LibationFiles
|
||||
private static string APPSETTINGS_JSON { get; } = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location), "appsettings.json");
|
||||
private static string APPSETTINGS_JSON { get; } = Path.Combine(Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName), "appsettings.json");
|
||||
private const string LIBATION_FILES_KEY = "LibationFiles";
|
||||
|
||||
[Description("Location for storage of program-created files")]
|
||||
|
||||
@ -14,4 +14,12 @@
|
||||
<ProjectReference Include="..\FileManager\FileManager.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@ -15,5 +15,14 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\DataLayer\DataLayer.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
</Project>
|
||||
|
||||
@ -49,6 +49,7 @@ Title: {Book.Title}
|
||||
Author(s): {Book.AuthorNames()}
|
||||
Narrator(s): {Book.NarratorNames()}
|
||||
Length: {(Book.LengthInMinutes == 0 ? "" : $"{Book.LengthInMinutes / 60} hr {Book.LengthInMinutes % 60} min")}
|
||||
Audio Bitrate: {Book.AudioFormat}
|
||||
Category: {string.Join(" > ", Book.CategoriesNames())}
|
||||
Purchase Date: {_libraryBook.DateAdded.ToString("d")}
|
||||
".Trim();
|
||||
|
||||
@ -13,6 +13,7 @@ namespace LibationWinForms.Dialogs
|
||||
this.downloadCoverArtCbox.Text = desc(nameof(config.DownloadCoverArt));
|
||||
this.retainAaxFileCbox.Text = desc(nameof(config.RetainAaxFile));
|
||||
this.splitFilesByChapterCbox.Text = desc(nameof(config.SplitFilesByChapter));
|
||||
this.mergeOpeningEndCreditsCbox.Text = desc(nameof(config.MergeOpeningAndEndCredits));
|
||||
this.stripAudibleBrandingCbox.Text = desc(nameof(config.StripAudibleBrandAudio));
|
||||
this.stripUnabridgedCbox.Text = desc(nameof(config.StripUnabridged));
|
||||
|
||||
@ -21,6 +22,7 @@ namespace LibationWinForms.Dialogs
|
||||
downloadCoverArtCbox.Checked = config.DownloadCoverArt;
|
||||
retainAaxFileCbox.Checked = config.RetainAaxFile;
|
||||
splitFilesByChapterCbox.Checked = config.SplitFilesByChapter;
|
||||
mergeOpeningEndCreditsCbox.Checked = config.MergeOpeningAndEndCredits;
|
||||
stripUnabridgedCbox.Checked = config.StripUnabridged;
|
||||
stripAudibleBrandingCbox.Checked = config.StripAudibleBrandAudio;
|
||||
convertLosslessRb.Checked = !config.DecryptToLossy;
|
||||
@ -50,6 +52,7 @@ namespace LibationWinForms.Dialogs
|
||||
config.DownloadCoverArt = downloadCoverArtCbox.Checked;
|
||||
config.RetainAaxFile = retainAaxFileCbox.Checked;
|
||||
config.SplitFilesByChapter = splitFilesByChapterCbox.Checked;
|
||||
config.MergeOpeningAndEndCredits = mergeOpeningEndCreditsCbox.Checked;
|
||||
config.StripUnabridged = stripUnabridgedCbox.Checked;
|
||||
config.StripAudibleBrandAudio = stripAudibleBrandingCbox.Checked;
|
||||
config.DecryptToLossy = convertLossyRb.Checked;
|
||||
|
||||
@ -108,6 +108,7 @@
|
||||
this.retainAaxFileCbox = new System.Windows.Forms.CheckBox();
|
||||
this.downloadCoverArtCbox = new System.Windows.Forms.CheckBox();
|
||||
this.createCueSheetCbox = new System.Windows.Forms.CheckBox();
|
||||
this.mergeOpeningEndCreditsCbox = new System.Windows.Forms.CheckBox();
|
||||
this.badBookGb.SuspendLayout();
|
||||
this.tabControl.SuspendLayout();
|
||||
this.tab1ImportantSettings.SuspendLayout();
|
||||
@ -251,7 +252,7 @@
|
||||
// stripAudibleBrandingCbox
|
||||
//
|
||||
this.stripAudibleBrandingCbox.AutoSize = true;
|
||||
this.stripAudibleBrandingCbox.Location = new System.Drawing.Point(19, 168);
|
||||
this.stripAudibleBrandingCbox.Location = new System.Drawing.Point(19, 193);
|
||||
this.stripAudibleBrandingCbox.Name = "stripAudibleBrandingCbox";
|
||||
this.stripAudibleBrandingCbox.Size = new System.Drawing.Size(143, 34);
|
||||
this.stripAudibleBrandingCbox.TabIndex = 13;
|
||||
@ -285,7 +286,7 @@
|
||||
// convertLossyRb
|
||||
//
|
||||
this.convertLossyRb.AutoSize = true;
|
||||
this.convertLossyRb.Location = new System.Drawing.Point(19, 232);
|
||||
this.convertLossyRb.Location = new System.Drawing.Point(19, 257);
|
||||
this.convertLossyRb.Name = "convertLossyRb";
|
||||
this.convertLossyRb.Size = new System.Drawing.Size(329, 19);
|
||||
this.convertLossyRb.TabIndex = 12;
|
||||
@ -297,7 +298,7 @@
|
||||
//
|
||||
this.convertLosslessRb.AutoSize = true;
|
||||
this.convertLosslessRb.Checked = true;
|
||||
this.convertLosslessRb.Location = new System.Drawing.Point(19, 207);
|
||||
this.convertLosslessRb.Location = new System.Drawing.Point(19, 232);
|
||||
this.convertLosslessRb.Name = "convertLosslessRb";
|
||||
this.convertLosslessRb.Size = new System.Drawing.Size(335, 19);
|
||||
this.convertLosslessRb.TabIndex = 11;
|
||||
@ -608,6 +609,7 @@
|
||||
this.tab4AudioFileOptions.Controls.Add(this.stripAudibleBrandingCbox);
|
||||
this.tab4AudioFileOptions.Controls.Add(this.convertLosslessRb);
|
||||
this.tab4AudioFileOptions.Controls.Add(this.stripUnabridgedCbox);
|
||||
this.tab4AudioFileOptions.Controls.Add(this.mergeOpeningEndCreditsCbox);
|
||||
this.tab4AudioFileOptions.Controls.Add(this.splitFilesByChapterCbox);
|
||||
this.tab4AudioFileOptions.Controls.Add(this.retainAaxFileCbox);
|
||||
this.tab4AudioFileOptions.Controls.Add(this.downloadCoverArtCbox);
|
||||
@ -980,7 +982,7 @@
|
||||
// stripUnabridgedCbox
|
||||
//
|
||||
this.stripUnabridgedCbox.AutoSize = true;
|
||||
this.stripUnabridgedCbox.Location = new System.Drawing.Point(19, 143);
|
||||
this.stripUnabridgedCbox.Location = new System.Drawing.Point(19, 168);
|
||||
this.stripUnabridgedCbox.Name = "stripUnabridgedCbox";
|
||||
this.stripUnabridgedCbox.Size = new System.Drawing.Size(147, 19);
|
||||
this.stripUnabridgedCbox.TabIndex = 13;
|
||||
@ -1024,6 +1026,17 @@
|
||||
this.createCueSheetCbox.UseVisualStyleBackColor = true;
|
||||
this.createCueSheetCbox.CheckedChanged += new System.EventHandler(this.allowLibationFixupCbox_CheckedChanged);
|
||||
//
|
||||
// mergeBeginningEndCreditsCbox
|
||||
//
|
||||
this.mergeOpeningEndCreditsCbox.AutoSize = true;
|
||||
this.mergeOpeningEndCreditsCbox.Location = new System.Drawing.Point(19, 143);
|
||||
this.mergeOpeningEndCreditsCbox.Name = "mergeOpeningEndCreditsCbox";
|
||||
this.mergeOpeningEndCreditsCbox.Size = new System.Drawing.Size(206, 19);
|
||||
this.mergeOpeningEndCreditsCbox.TabIndex = 13;
|
||||
this.mergeOpeningEndCreditsCbox.Text = "[MergeOpeningEndCredits desc]";
|
||||
this.mergeOpeningEndCreditsCbox.UseVisualStyleBackColor = true;
|
||||
this.mergeOpeningEndCreditsCbox.CheckedChanged += new System.EventHandler(this.splitFilesByChapterCbox_CheckedChanged);
|
||||
//
|
||||
// SettingsDialog
|
||||
//
|
||||
this.AcceptButton = this.saveBtn;
|
||||
@ -1155,5 +1168,6 @@
|
||||
private System.Windows.Forms.Button chapterTitleTemplateBtn;
|
||||
private System.Windows.Forms.TextBox chapterTitleTemplateTb;
|
||||
private System.Windows.Forms.Button editCharreplacementBtn;
|
||||
private System.Windows.Forms.CheckBox mergeOpeningEndCreditsCbox;
|
||||
}
|
||||
}
|
||||
@ -7,7 +7,7 @@
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<ApplicationIcon>libation.ico</ApplicationIcon>
|
||||
<AssemblyName>Libation</AssemblyName>
|
||||
|
||||
|
||||
<PublishReadyToRun>true</PublishReadyToRun>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
@ -27,6 +27,16 @@
|
||||
<SatelliteResourceLanguages>en;es</SatelliteResourceLanguages>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<OutputPath>..\bin\Debug</OutputPath>
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<OutputPath>..\bin\Release</OutputPath>
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Autoupdater.NET.Official" Version="1.7.3" />
|
||||
<PackageReference Include="Dinah.Core.WindowsDesktop" Version="4.2.3.1" />
|
||||
|
||||
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
-->
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
<PublishDir>..\bin\publish\</PublishDir>
|
||||
<PublishProtocol>FileSystem</PublishProtocol>
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<SelfContained>false</SelfContained>
|
||||
<PublishSingleFile>false</PublishSingleFile>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@ -2,7 +2,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
@ -14,8 +14,142 @@ namespace FileLiberator.Tests
|
||||
[TestClass]
|
||||
public class DownloadDecryptBookTests
|
||||
{
|
||||
private static Chapter[] HierarchicalChapters => new Chapter[]
|
||||
{
|
||||
new ()
|
||||
{
|
||||
Title = "Opening Credits",
|
||||
StartOffsetMs = 0,
|
||||
StartOffsetSec = 0,
|
||||
LengthMs = 10000,
|
||||
},
|
||||
new ()
|
||||
{
|
||||
Title = "Book 1",
|
||||
StartOffsetMs = 10000,
|
||||
StartOffsetSec = 10,
|
||||
LengthMs = 2000,
|
||||
Chapters = new Chapter[]
|
||||
{
|
||||
new ()
|
||||
{
|
||||
Title = "Part 1",
|
||||
StartOffsetMs = 12000,
|
||||
StartOffsetSec = 12,
|
||||
LengthMs = 2000,
|
||||
Chapters = new Chapter[]
|
||||
{
|
||||
new ()
|
||||
{
|
||||
Title = "Chapter 1",
|
||||
StartOffsetMs = 14000,
|
||||
StartOffsetSec = 14,
|
||||
LengthMs = 86000,
|
||||
},
|
||||
new()
|
||||
{
|
||||
Title = "Chapter 2",
|
||||
StartOffsetMs = 100000,
|
||||
StartOffsetSec = 100,
|
||||
LengthMs = 100000,
|
||||
},
|
||||
}
|
||||
},
|
||||
new()
|
||||
{
|
||||
Title = "Part 2",
|
||||
StartOffsetMs = 200000,
|
||||
StartOffsetSec = 200,
|
||||
LengthMs = 2000,
|
||||
Chapters = new Chapter[]
|
||||
{
|
||||
new()
|
||||
{
|
||||
Title = "Chapter 3",
|
||||
StartOffsetMs = 202000,
|
||||
StartOffsetSec = 202,
|
||||
LengthMs = 98000,
|
||||
},
|
||||
new()
|
||||
{
|
||||
Title = "Chapter 4",
|
||||
StartOffsetMs = 300000,
|
||||
StartOffsetSec = 300,
|
||||
LengthMs = 100000,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
new()
|
||||
{
|
||||
Title = "Book 2",
|
||||
StartOffsetMs = 400000,
|
||||
StartOffsetSec = 400,
|
||||
LengthMs = 2000,
|
||||
Chapters = new Chapter[]
|
||||
{
|
||||
new()
|
||||
{
|
||||
Title = "Part 3",
|
||||
StartOffsetMs = 402000,
|
||||
StartOffsetSec = 402,
|
||||
LengthMs = 2000,
|
||||
Chapters = new Chapter[]
|
||||
{
|
||||
new()
|
||||
{
|
||||
Title = "Chapter 5",
|
||||
StartOffsetMs = 404000,
|
||||
StartOffsetSec = 404,
|
||||
LengthMs = 96000,
|
||||
},
|
||||
new()
|
||||
{
|
||||
Title = "Chapter 6",
|
||||
StartOffsetMs = 500000,
|
||||
StartOffsetSec = 500,
|
||||
LengthMs = 100000,
|
||||
},
|
||||
}
|
||||
},
|
||||
new()
|
||||
{
|
||||
Title = "Part 4",
|
||||
StartOffsetMs = 600000,
|
||||
StartOffsetSec = 600,
|
||||
LengthMs = 2000,
|
||||
Chapters = new Chapter[]
|
||||
{
|
||||
new()
|
||||
{
|
||||
Title = "Chapter 7",
|
||||
StartOffsetMs = 602000,
|
||||
StartOffsetSec = 602,
|
||||
LengthMs = 98000,
|
||||
},
|
||||
new()
|
||||
{
|
||||
Title = "Chapter 8",
|
||||
StartOffsetMs = 700000,
|
||||
StartOffsetSec = 700,
|
||||
LengthMs = 100000,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
new()
|
||||
{
|
||||
Title = "End Credits",
|
||||
StartOffsetMs = 800000,
|
||||
StartOffsetSec = 800,
|
||||
LengthMs = 10000,
|
||||
},
|
||||
};
|
||||
|
||||
[TestMethod]
|
||||
public void HierarchicalChapters_Flatten()
|
||||
public void Chapters_CombineCredits()
|
||||
{
|
||||
var expected = new Chapter[]
|
||||
{
|
||||
@ -73,129 +207,109 @@ namespace FileLiberator.Tests
|
||||
Title = "Book 2: Part 4: Chapter 8",
|
||||
StartOffsetMs = 700000,
|
||||
StartOffsetSec = 700,
|
||||
LengthMs = 100000,
|
||||
LengthMs = 110000,
|
||||
}
|
||||
};
|
||||
|
||||
var hierarchicalChapters = new Chapter[]
|
||||
{
|
||||
new()
|
||||
{
|
||||
Title = "Book 1",
|
||||
StartOffsetMs = 0,
|
||||
StartOffsetSec = 0,
|
||||
LengthMs = 2000,
|
||||
Chapters = new Chapter[]
|
||||
{
|
||||
new()
|
||||
{ Title = "Part 1",
|
||||
StartOffsetMs = 2000,
|
||||
StartOffsetSec = 2,
|
||||
LengthMs = 2000,
|
||||
Chapters = new Chapter[]
|
||||
{
|
||||
new()
|
||||
{ Title = "Chapter 1",
|
||||
StartOffsetMs = 4000,
|
||||
StartOffsetSec = 4,
|
||||
LengthMs = 96000,
|
||||
},
|
||||
new()
|
||||
{ Title = "Chapter 2",
|
||||
StartOffsetMs = 100000,
|
||||
StartOffsetSec = 100,
|
||||
LengthMs = 100000,
|
||||
},
|
||||
}
|
||||
},
|
||||
new()
|
||||
{ Title = "Part 2",
|
||||
StartOffsetMs = 200000,
|
||||
StartOffsetSec = 200,
|
||||
LengthMs = 2000,
|
||||
Chapters = new Chapter[]
|
||||
{
|
||||
new()
|
||||
{ Title = "Chapter 3",
|
||||
StartOffsetMs = 202000,
|
||||
StartOffsetSec = 202,
|
||||
LengthMs = 98000,
|
||||
},
|
||||
new()
|
||||
{ Title = "Chapter 4",
|
||||
StartOffsetMs = 300000,
|
||||
StartOffsetSec = 300,
|
||||
LengthMs = 100000,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
new()
|
||||
{
|
||||
Title = "Book 2",
|
||||
StartOffsetMs = 400000,
|
||||
StartOffsetSec = 400,
|
||||
LengthMs = 2000,
|
||||
Chapters = new Chapter[]
|
||||
{
|
||||
new()
|
||||
{ Title = "Part 3",
|
||||
StartOffsetMs = 402000,
|
||||
StartOffsetSec = 402,
|
||||
LengthMs = 2000,
|
||||
Chapters = new Chapter[]
|
||||
{
|
||||
new()
|
||||
{ Title = "Chapter 5",
|
||||
StartOffsetMs = 404000,
|
||||
StartOffsetSec = 404,
|
||||
LengthMs = 96000,
|
||||
},
|
||||
new()
|
||||
{ Title = "Chapter 6",
|
||||
StartOffsetMs = 500000,
|
||||
StartOffsetSec = 500,
|
||||
LengthMs = 100000,
|
||||
},
|
||||
}
|
||||
},
|
||||
new()
|
||||
{ Title = "Part 4",
|
||||
StartOffsetMs = 600000,
|
||||
StartOffsetSec = 600,
|
||||
LengthMs = 2000,
|
||||
Chapters = new Chapter[]
|
||||
{
|
||||
new()
|
||||
{ Title = "Chapter 7",
|
||||
StartOffsetMs = 602000,
|
||||
StartOffsetSec = 602,
|
||||
LengthMs = 98000,
|
||||
},
|
||||
new()
|
||||
{ Title = "Chapter 8",
|
||||
StartOffsetMs = 700000,
|
||||
StartOffsetSec = 700,
|
||||
LengthMs = 100000,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
var flatChapters = DownloadDecryptBook.flattenChapters(HierarchicalChapters);
|
||||
DownloadDecryptBook.combineCredits(flatChapters);
|
||||
checkChapters(flatChapters, expected);
|
||||
}
|
||||
|
||||
var flatChapters = DownloadDecryptBook.flattenChapters(hierarchicalChapters);
|
||||
|
||||
flatChapters.Count.Should().Be(expected.Length);
|
||||
|
||||
for (int i = 0; i < flatChapters.Count; i++)
|
||||
[TestMethod]
|
||||
public void HierarchicalChapters_Flatten()
|
||||
{
|
||||
var expected = new Chapter[]
|
||||
{
|
||||
flatChapters[i].Title.Should().Be(expected[i].Title);
|
||||
flatChapters[i].StartOffsetMs.Should().Be(expected[i].StartOffsetMs);
|
||||
flatChapters[i].StartOffsetSec.Should().Be(expected[i].StartOffsetSec);
|
||||
flatChapters[i].LengthMs.Should().Be(expected[i].LengthMs);
|
||||
flatChapters[i].Chapters.Should().BeNull();
|
||||
new()
|
||||
{
|
||||
Title = "Opening Credits",
|
||||
StartOffsetMs = 0,
|
||||
StartOffsetSec = 0,
|
||||
LengthMs = 10000,
|
||||
},
|
||||
new()
|
||||
{
|
||||
Title = "Book 1: Part 1: Chapter 1",
|
||||
StartOffsetMs = 10000,
|
||||
StartOffsetSec = 10,
|
||||
LengthMs = 90000,
|
||||
},
|
||||
new()
|
||||
{
|
||||
Title = "Book 1: Part 1: Chapter 2",
|
||||
StartOffsetMs = 100000,
|
||||
StartOffsetSec = 100,
|
||||
LengthMs = 100000,
|
||||
},
|
||||
new()
|
||||
{
|
||||
Title = "Book 1: Part 2: Chapter 3",
|
||||
StartOffsetMs = 200000,
|
||||
StartOffsetSec = 200,
|
||||
LengthMs = 100000,
|
||||
},
|
||||
new()
|
||||
{
|
||||
Title = "Book 1: Part 2: Chapter 4",
|
||||
StartOffsetMs = 300000,
|
||||
StartOffsetSec = 300,
|
||||
LengthMs = 100000,
|
||||
},
|
||||
new()
|
||||
{
|
||||
Title = "Book 2: Part 3: Chapter 5",
|
||||
StartOffsetMs = 400000,
|
||||
StartOffsetSec = 400,
|
||||
LengthMs = 100000,
|
||||
},
|
||||
new()
|
||||
{
|
||||
Title = "Book 2: Part 3: Chapter 6",
|
||||
StartOffsetMs = 500000,
|
||||
StartOffsetSec = 500,
|
||||
LengthMs = 100000,
|
||||
},
|
||||
new()
|
||||
{
|
||||
Title = "Book 2: Part 4: Chapter 7",
|
||||
StartOffsetMs = 600000,
|
||||
StartOffsetSec = 600,
|
||||
LengthMs = 100000,
|
||||
},
|
||||
new()
|
||||
{
|
||||
Title = "Book 2: Part 4: Chapter 8",
|
||||
StartOffsetMs = 700000,
|
||||
StartOffsetSec = 700,
|
||||
LengthMs = 100000,
|
||||
},
|
||||
new()
|
||||
{
|
||||
Title = "End Credits",
|
||||
StartOffsetMs = 800000,
|
||||
StartOffsetSec = 800,
|
||||
LengthMs = 10000,
|
||||
}
|
||||
};
|
||||
|
||||
var flatChapters = DownloadDecryptBook.flattenChapters(HierarchicalChapters);
|
||||
|
||||
checkChapters(flatChapters, expected);
|
||||
}
|
||||
|
||||
private static void checkChapters(IList<Chapter> value, IList<Chapter> expected)
|
||||
{
|
||||
value.Count.Should().Be(expected.Count);
|
||||
|
||||
for (int i = 0; i < value.Count; i++)
|
||||
{
|
||||
value[i].Title.Should().Be(expected[i].Title);
|
||||
value[i].StartOffsetMs.Should().Be(expected[i].StartOffsetMs);
|
||||
value[i].StartOffsetSec.Should().Be(expected[i].StartOffsetSec);
|
||||
value[i].LengthMs.Should().Be(expected[i].LengthMs);
|
||||
value[i].Chapters.Should().BeNull();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
16
Source/publish.ps1
Normal file
16
Source/publish.ps1
Normal file
@ -0,0 +1,16 @@
|
||||
<# You must enable running powershell scripts.
|
||||
|
||||
Set-ExecutionPolicy -Scope CurrentUser Unrestricted
|
||||
#>
|
||||
|
||||
$pubDir = "bin\Publish"
|
||||
Remove-Item $pubDir -Recurse -Force
|
||||
|
||||
dotnet publish -c Release LibationWinForms\LibationWinForms.csproj -p:PublishProfile=LibationWinForms\Properties\PublishProfiles\FolderProfile.pubxml
|
||||
dotnet publish -c Release LibationCli\LibationCli.csproj -p:PublishProfile=LibationCli\Properties\PublishProfiles\FolderProfile.pubxml
|
||||
dotnet publish -c Release Hangover\Hangover.csproj -p:PublishProfile=Hangover\Properties\PublishProfiles\FolderProfile.pubxml
|
||||
|
||||
$verMatch = Select-String -Path 'AppScaffolding\AppScaffolding.csproj' -Pattern '<Version>(\d{0,3}\.\d{0,3}\.\d{0,3})\.\d{0,3}</Version>'
|
||||
$archiveName = "bin\Libation."+$verMatch.Matches.Groups[1].Value+".zip"
|
||||
Get-ChildItem -Path $pubDir -Recurse |
|
||||
Compress-Archive -DestinationPath $archiveName -Force
|
||||
Loading…
x
Reference in New Issue
Block a user