Add option to adjust minimum file duration

when splitting audiobooks into multiple files by chapter
This commit is contained in:
Michael Bucari-Tovo
2026-01-08 18:36:57 -07:00
parent dc58a101af
commit 068f37319f
10 changed files with 240 additions and 151 deletions

View File

@@ -40,58 +40,14 @@ namespace AaxDecrypter
}
}
/*
https://github.com/rmcrackan/Libation/pull/127#issuecomment-939088489
If the chapter truly is empty, that is, 0 audio frames in length, then yes it is ignored.
If the chapter is shorter than 3 seconds long but still has some audio frames, those frames are combined with the following chapter and not split into a new file.
I also implemented file naming by chapter title. When 2 or more consecutive chapters are combined, the first of the combined chapter's title is used in the file name. For example, given an audiobook with the following chapters:
00:00:00 - 00:00:02 | Part 1
00:00:02 - 00:35:00 | Chapter 1
00:35:02 - 01:02:00 | Chapter 2
01:02:00 - 01:02:02 | Part 2
01:02:02 - 01:41:00 | Chapter 3
01:41:00 - 02:05:00 | Chapter 4
The book will be split into the following files:
00:00:00 - 00:35:00 | Book - 01 - Part 1.m4b
00:35:00 - 01:02:00 | Book - 02 - Chapter 2.m4b
01:02:00 - 01:41:00 | Book - 03 - Part 2.m4b
01:41:00 - 02:05:00 | Book - 04 - Chapter 4.m4b
That naming may not be desirable for everyone, but it's an easy change to instead use the last of the combined chapter's title in the file name.
*/
protected async override Task<bool> Step_DownloadAndDecryptAudiobookAsync()
{
if (AaxFile is null) return false;
var chapters = DownloadOptions.ChapterInfo.Chapters;
// Ensure split files are at least minChapterLength in duration.
var splitChapters = new ChapterInfo(DownloadOptions.ChapterInfo.StartOffset);
var runningTotal = TimeSpan.Zero;
string title = "";
for (int i = 0; i < chapters.Count; i++)
{
if (runningTotal == TimeSpan.Zero)
title = chapters[i].Title;
runningTotal += chapters[i].Duration;
if (runningTotal >= minChapterLength)
{
splitChapters.AddChapter(title, runningTotal);
runningTotal = TimeSpan.Zero;
}
}
try
{
await (AaxConversion = decryptMultiAsync(AaxFile, splitChapters));
await (AaxConversion = decryptMultiAsync(AaxFile, DownloadOptions.ChapterInfo));
if (AaxConversion.IsCompletedSuccessfully)
await moveMoovToBeginning(AaxFile, workingFileStream?.Name);

View File

@@ -170,17 +170,6 @@ public partial class DownloadOptions
/// </summary>
public static DownloadOptions BuildDownloadOptions(LibraryBook libraryBook, Configuration config, LicenseInfo licInfo)
{
long chapterStartMs
= config.StripAudibleBrandAudio
? licInfo.ContentMetadata.ChapterInfo.BrandIntroDurationMs
: 0;
var dlOptions = new DownloadOptions(config, libraryBook, licInfo)
{
ChapterInfo = new AAXClean.ChapterInfo(TimeSpan.FromMilliseconds(chapterStartMs)),
RuntimeLength = TimeSpan.FromMilliseconds(licInfo.ContentMetadata.ChapterInfo.RuntimeLengthMs),
};
var titleConcat = config.CombineNestedChapterTitles ? ": " : null;
var chapters
= flattenChapters(licInfo.ContentMetadata.ChapterInfo.Chapters, titleConcat)
@@ -190,18 +179,22 @@ public partial class DownloadOptions
if (config.MergeOpeningAndEndCredits)
combineCredits(chapters);
if (config.StripAudibleBrandAudio)
stripBranding(chapters, licInfo.ContentMetadata.ChapterInfo.BrandIntroDurationMs, licInfo.ContentMetadata.ChapterInfo.BrandOutroDurationMs);
if (config.SplitFilesByChapter)
combineShortChapters(chapters, config.MinimumFileDuration * 1000);
var dlOptions = new DownloadOptions(config, libraryBook, licInfo)
{
ChapterInfo = new AAXClean.ChapterInfo(TimeSpan.FromMilliseconds(chapters[0].StartOffsetMs)),
RuntimeLength = TimeSpan.FromMilliseconds(licInfo.ContentMetadata.ChapterInfo.RuntimeLengthMs),
};
//Build AAXClean.ChapterInfo
for (int i = 0; i < chapters.Count; i++)
{
var chapter = chapters[i];
long chapLenMs = chapter.LengthMs;
if (i == 0)
chapLenMs -= chapterStartMs;
if (config.StripAudibleBrandAudio && i == chapters.Count - 1)
chapLenMs -= licInfo.ContentMetadata.ChapterInfo.BrandOutroDurationMs;
dlOptions.ChapterInfo.AddChapter(chapter.Title, TimeSpan.FromMilliseconds(chapLenMs));
dlOptions.ChapterInfo.AddChapter(chapters[i].Title, TimeSpan.FromMilliseconds(chapters[i].LengthMs));
}
return dlOptions;
@@ -349,6 +342,50 @@ public partial class DownloadOptions
return chaps;
}
/*
https://github.com/rmcrackan/Libation/pull/127#issuecomment-939088489
If the chapter truly is empty, that is, 0 audio frames in length, then yes it is ignored.
If the chapter is shorter than minChapterLength but still has some audio frames, those
frames are combined with the following chapter and not split into a new file.
When 2 or more consecutive chapters are combined, chapter titles are concatenated
with a apace in between. For example, given an audiobook with the following chapters:
00:00:00 - 00:00:02 | Part 1
00:00:02 - 00:35:00 | Chapter 1
00:35:02 - 01:02:00 | Chapter 2
01:02:00 - 01:02:02 | Part 2
01:02:02 - 01:41:00 | Chapter 3
01:41:00 - 02:05:00 | Chapter 4
The book will be split into the following files:
00:00:00 - 00:35:00 | Book - 01 - Part 1 Chapter 1.m4b
00:35:00 - 01:02:00 | Book - 02 - Chapter 2.m4b
01:02:00 - 01:41:00 | Book - 03 - Part 2.m4b
01:41:00 - 02:05:00 | Book - 04 - Chapter 4.m4b
*/
public static void combineShortChapters(List<Chapter> chapters, long minChapterLengthMs)
{
for (int i = 0; i < chapters.Count; i++)
{
while (chapters[i].LengthMs < minChapterLengthMs && chapters.Count > i + 1)
{
chapters[i].Title += " " + chapters[i + 1].Title;
chapters[i].LengthMs += chapters[i + 1].LengthMs;
chapters.RemoveAt(i + 1);
}
}
}
public static void stripBranding(List<Chapter> chapters, long introMs, long outroMs)
{
chapters[0].LengthMs -= introMs;
chapters[0].StartOffsetMs += introMs;
chapters[^1].LengthMs -= outroMs;
}
public static void combineCredits(IList<Chapter> chapters)
{
if (chapters.Count > 1 && chapters[0].Title == "Opening Credits")

View File

@@ -101,6 +101,29 @@
<Setter Property="BorderThickness" Value="2" />
</Style>
</Style>
<Style Selector="NumericUpDown.SmallNumericUpDown">
<Setter Property="Height" Value="{DynamicResource TextControlThemeMinHeight}" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Style Selector="^ /template/ ButtonSpinner#PART_Spinner">
<Style Selector="^ RepeatButton#PART_IncreaseButton">
<Setter Property="MinWidth" Value="24" />
<Style Selector="^ > PathIcon">
<Setter Property="Data">
M0,0 l8,-10 l8,10 Z
</Setter>
</Style>
</Style>
<Style Selector="^ RepeatButton#PART_DecreaseButton">
<Setter Property="MinWidth" Value="24" />
<Style Selector="^ > PathIcon">
<Setter Property="Data">
M0,0 l8,10 l8,-10 Z
</Setter>
</Style>
</Style>
</Style>
</Style>
</Application.Styles>
<NativeMenu.Menu>

View File

@@ -6,6 +6,7 @@
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
xmlns:vm="clr-namespace:LibationAvalonia.ViewModels.Settings"
x:DataType="vm:AudioSettingsVM"
x:CompileBindings="True"
x:Class="LibationAvalonia.Controls.Settings.Audio">
<Grid
@@ -38,43 +39,43 @@
<Grid ColumnDefinitions="*,Auto">
<TextBlock
VerticalAlignment="Center"
Text="{CompiledBinding FileDownloadQualityText}" />
Text="{Binding FileDownloadQualityText}" />
<controls:WheelComboBox
Margin="5,0,0,0"
Grid.Column="1"
ItemsSource="{CompiledBinding DownloadQualities}"
SelectedItem="{CompiledBinding FileDownloadQuality}"/>
ItemsSource="{Binding DownloadQualities}"
SelectedItem="{Binding FileDownloadQuality}"/>
</Grid>
<Grid ColumnDefinitions="*,Auto">
<CheckBox
ToolTip.Tip="{CompiledBinding UseWidevineTip}"
ToolTip.Tip="{Binding UseWidevineTip}"
IsCheckedChanged="UseWidevine_IsCheckedChanged"
IsChecked="{CompiledBinding UseWidevine, Mode=TwoWay}">
<TextBlock Text="{CompiledBinding UseWidevineText}" />
IsChecked="{Binding UseWidevine, Mode=TwoWay}">
<TextBlock Text="{Binding UseWidevineText}" />
</CheckBox>
<CheckBox
Grid.Column="1"
ToolTip.Tip="{CompiledBinding Request_xHE_AACTip}"
IsEnabled="{CompiledBinding UseWidevine}"
IsChecked="{CompiledBinding Request_xHE_AAC, Mode=TwoWay}">
<TextBlock Text="{CompiledBinding Request_xHE_AACText}" />
ToolTip.Tip="{Binding Request_xHE_AACTip}"
IsEnabled="{Binding UseWidevine}"
IsChecked="{Binding Request_xHE_AAC, Mode=TwoWay}">
<TextBlock Text="{Binding Request_xHE_AACText}" />
</CheckBox>
</Grid>
<Grid ColumnDefinitions="*,Auto">
<CheckBox
ToolTip.Tip="{CompiledBinding RequestSpatialTip}"
IsEnabled="{CompiledBinding UseWidevine}"
IsChecked="{CompiledBinding RequestSpatial, Mode=TwoWay}">
<TextBlock Text="{CompiledBinding RequestSpatialText}" />
ToolTip.Tip="{Binding RequestSpatialTip}"
IsEnabled="{Binding UseWidevine}"
IsChecked="{Binding RequestSpatial, Mode=TwoWay}">
<TextBlock Text="{Binding RequestSpatialText}" />
</CheckBox>
<Grid
Grid.Column="1"
ColumnDefinitions="Auto,Auto"
VerticalAlignment="Top"
ToolTip.Tip="{CompiledBinding SpatialAudioCodecTip}">
ToolTip.Tip="{Binding SpatialAudioCodecTip}">
<Grid.IsEnabled>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<MultiBinding.Bindings>
@@ -92,78 +93,100 @@
Margin="5,0,0,0"
Grid.Column="1"
VerticalAlignment="Center"
ItemsSource="{CompiledBinding SpatialAudioCodecs}"
SelectedItem="{CompiledBinding SpatialAudioCodec}"/>
ItemsSource="{Binding SpatialAudioCodecs}"
SelectedItem="{Binding SpatialAudioCodec}"/>
</Grid>
</Grid>
<CheckBox IsChecked="{CompiledBinding CreateCueSheet, Mode=TwoWay}">
<TextBlock Text="{CompiledBinding CreateCueSheetText}" />
<CheckBox IsChecked="{Binding CreateCueSheet, Mode=TwoWay}">
<TextBlock Text="{Binding CreateCueSheetText}" />
</CheckBox>
<CheckBox IsChecked="{CompiledBinding DownloadCoverArt, Mode=TwoWay}">
<TextBlock Text="{CompiledBinding DownloadCoverArtText}" />
<CheckBox IsChecked="{Binding DownloadCoverArt, Mode=TwoWay}">
<TextBlock Text="{Binding DownloadCoverArtText}" />
</CheckBox>
<Grid ColumnDefinitions="*,Auto">
<CheckBox IsChecked="{CompiledBinding DownloadClipsBookmarks, Mode=TwoWay}">
<CheckBox IsChecked="{Binding DownloadClipsBookmarks, Mode=TwoWay}">
<TextBlock Text="Download Clips, Notes and Bookmarks as" />
</CheckBox>
<controls:WheelComboBox
Margin="5,0,0,0"
Grid.Column="1"
IsEnabled="{CompiledBinding DownloadClipsBookmarks}"
ItemsSource="{CompiledBinding ClipBookmarkFormats}"
SelectedItem="{CompiledBinding ClipBookmarkFormat}"/>
IsEnabled="{Binding DownloadClipsBookmarks}"
ItemsSource="{Binding ClipBookmarkFormats}"
SelectedItem="{Binding ClipBookmarkFormat}"/>
</Grid>
<CheckBox
IsChecked="{CompiledBinding RetainAaxFile, Mode=TwoWay}"
ToolTip.Tip="{CompiledBinding RetainAaxFileTip}">
<TextBlock Text="{CompiledBinding RetainAaxFileText}" />
IsChecked="{Binding RetainAaxFile, Mode=TwoWay}"
ToolTip.Tip="{Binding RetainAaxFileTip}">
<TextBlock Text="{Binding RetainAaxFileText}" />
</CheckBox>
<CheckBox
IsChecked="{CompiledBinding MergeOpeningAndEndCredits, Mode=TwoWay}"
ToolTip.Tip="{CompiledBinding MergeOpeningAndEndCreditsTip}">
<TextBlock Text="{CompiledBinding MergeOpeningEndCreditsText}" />
IsChecked="{Binding MergeOpeningAndEndCredits, Mode=TwoWay}"
ToolTip.Tip="{Binding MergeOpeningAndEndCreditsTip}">
<TextBlock Text="{Binding MergeOpeningEndCreditsText}" />
</CheckBox>
<CheckBox
ToolTip.Tip="{CompiledBinding CombineNestedChapterTitlesTip}"
IsChecked="{CompiledBinding CombineNestedChapterTitles, Mode=TwoWay}">
<TextBlock Text="{CompiledBinding CombineNestedChapterTitlesText}" />
ToolTip.Tip="{Binding CombineNestedChapterTitlesTip}"
IsChecked="{Binding CombineNestedChapterTitles, Mode=TwoWay}">
<TextBlock Text="{Binding CombineNestedChapterTitlesText}" />
</CheckBox>
<CheckBox
ToolTip.Tip="{CompiledBinding AllowLibationFixupTip}"
IsChecked="{CompiledBinding AllowLibationFixup, Mode=TwoWay}">
<TextBlock Text="{CompiledBinding AllowLibationFixupText}" />
ToolTip.Tip="{Binding AllowLibationFixupTip}"
IsChecked="{Binding AllowLibationFixup, Mode=TwoWay}">
<TextBlock Text="{Binding AllowLibationFixupText}" />
</CheckBox>
<controls:GroupBox
Grid.Row="1"
Label="Audiobook Fix-ups"
IsEnabled="{CompiledBinding AllowLibationFixup}">
IsEnabled="{Binding AllowLibationFixup}">
<StackPanel Orientation="Vertical">
<CheckBox IsChecked="{CompiledBinding SplitFilesByChapter, Mode=TwoWay}">
<TextBlock Text="{CompiledBinding SplitFilesByChapterText}" />
<Grid
Margin="0,0,0,5"
Grid.ColumnDefinitions="Auto,Auto"
Grid.RowDefinitions="Auto,Auto">
<CheckBox Margin="0" Grid.ColumnSpan="2" IsChecked="{Binding SplitFilesByChapter, Mode=TwoWay}">
<TextBlock Grid.ColumnSpan="2" Text="{Binding SplitFilesByChapterText}" />
</CheckBox>
<TextBlock Grid.Row="1" Margin="15,0" VerticalAlignment="Center" ToolTip.Tip="{Binding MinimumFileDurationTip}" Text="{Binding MinimumFileDurationText}" />
<NumericUpDown
Classes="SmallNumericUpDown"
Grid.Column="1"
Grid.Row="1"
ToolTip.Tip="{Binding MinimumFileDurationTip}"
MinWidth="100"
Minimum="0"
Maximum="120"
Increment="1"
FormatString="N0"
ParsingNumberStyle="Integer"
IsEnabled="{Binding SplitFilesByChapter}"
Value="{Binding MinimumFileDuration, Mode=TwoWay}"/>
</Grid>
<CheckBox
IsChecked="{Binding StripAudibleBrandAudio, Mode=TwoWay}"
ToolTip.Tip="{Binding StripAudibleBrandAudioTip}">
<TextBlock Text="{Binding StripAudibleBrandingText}" />
</CheckBox>
<CheckBox
IsChecked="{CompiledBinding StripAudibleBrandAudio, Mode=TwoWay}"
ToolTip.Tip="{CompiledBinding StripAudibleBrandAudioTip}">
<TextBlock Text="{CompiledBinding StripAudibleBrandingText}" />
</CheckBox>
<CheckBox
IsChecked="{CompiledBinding StripUnabridged, Mode=TwoWay}"
ToolTip.Tip="{CompiledBinding StripUnabridgedTip}">
<TextBlock Text="{CompiledBinding StripUnabridgedText}" />
IsChecked="{Binding StripUnabridged, Mode=TwoWay}"
ToolTip.Tip="{Binding StripUnabridgedTip}">
<TextBlock Text="{Binding StripUnabridgedText}" />
</CheckBox>
</StackPanel>
</controls:GroupBox>
@@ -178,24 +201,24 @@
Margin="10,0,0,0">
<RadioButton
IsChecked="{CompiledBinding !DecryptToLossy, Mode=TwoWay}"
ToolTip.Tip="{CompiledBinding DecryptToLossyTip}">
IsChecked="{Binding !DecryptToLossy, Mode=TwoWay}"
ToolTip.Tip="{Binding DecryptToLossyTip}">
<StackPanel VerticalAlignment="Center">
<TextBlock
Text="Download my books in the original audio format (Lossless)" />
<CheckBox
IsEnabled="{CompiledBinding !DecryptToLossy}"
IsChecked="{CompiledBinding MoveMoovToBeginning, Mode=TwoWay}"
ToolTip.Tip="{CompiledBinding MoveMoovToBeginningTip}">
<TextBlock Text="{CompiledBinding MoveMoovToBeginningText}" />
IsEnabled="{Binding !DecryptToLossy}"
IsChecked="{Binding MoveMoovToBeginning, Mode=TwoWay}"
ToolTip.Tip="{Binding MoveMoovToBeginningTip}">
<TextBlock Text="{Binding MoveMoovToBeginningText}" />
</CheckBox>
</StackPanel>
</RadioButton>
<RadioButton
IsChecked="{CompiledBinding DecryptToLossy, Mode=TwoWay}"
ToolTip.Tip="{CompiledBinding DecryptToLossyTip}">
IsChecked="{Binding DecryptToLossy, Mode=TwoWay}"
ToolTip.Tip="{Binding DecryptToLossyTip}">
<TextBlock
TextWrapping="Wrap"
Text="Download my books as .MP3 files (transcode if necessary)" />
@@ -203,7 +226,7 @@
<controls:GroupBox
Grid.Column="1"
IsEnabled="{CompiledBinding DecryptToLossy}"
IsEnabled="{Binding DecryptToLossy}"
Label="Mp3 Encoding Options">
<Grid RowDefinitions="Auto,Auto,Auto,Auto,*">
@@ -220,21 +243,21 @@
<RadioButton
Margin="5"
Content="Bitrate"
IsChecked="{CompiledBinding LameTargetBitrate, Mode=TwoWay}"/>
IsChecked="{Binding LameTargetBitrate, Mode=TwoWay}"/>
<RadioButton
Grid.Column="1"
Margin="5"
Content="Quality"
IsChecked="{CompiledBinding !LameTargetBitrate, Mode=TwoWay}"/>
IsChecked="{Binding !LameTargetBitrate, Mode=TwoWay}"/>
</Grid>
</controls:GroupBox>
<CheckBox
HorizontalAlignment="Right"
Grid.Column="1"
IsChecked="{CompiledBinding LameDownsampleMono, Mode=TwoWay}"
ToolTip.Tip="{CompiledBinding LameDownsampleMonoTip}">
IsChecked="{Binding LameDownsampleMono, Mode=TwoWay}"
ToolTip.Tip="{Binding LameDownsampleMonoTip}">
<TextBlock
TextWrapping="Wrap"
@@ -249,8 +272,8 @@
<controls:WheelComboBox
Grid.Row="1"
HorizontalAlignment="Stretch"
ItemsSource="{CompiledBinding SampleRates}"
SelectedItem="{CompiledBinding SelectedSampleRate, Mode=TwoWay}"/>
ItemsSource="{Binding SampleRates}"
SelectedItem="{Binding SelectedSampleRate, Mode=TwoWay}"/>
<TextBlock Margin="0,0,0,5" Grid.Column="2" Text="Encoder Quality:" />
@@ -258,23 +281,23 @@
Grid.Column="2"
Grid.Row="1"
HorizontalAlignment="Stretch"
ItemsSource="{CompiledBinding EncoderQualities}"
SelectedItem="{CompiledBinding SelectedEncoderQuality, Mode=TwoWay}"/>
ItemsSource="{Binding EncoderQualities}"
SelectedItem="{Binding SelectedEncoderQuality, Mode=TwoWay}"/>
</Grid>
<controls:GroupBox
Grid.Row="2"
Margin="0,5"
Label="Bitrate"
IsEnabled="{CompiledBinding LameTargetBitrate}" >
IsEnabled="{Binding LameTargetBitrate}" >
<StackPanel>
<Grid ColumnDefinitions="*,25,Auto">
<Slider
Grid.Column="0"
IsEnabled="{CompiledBinding !LameMatchSource}"
Value="{CompiledBinding LameBitrate, Mode=TwoWay}"
IsEnabled="{Binding !LameMatchSource}"
Value="{Binding LameBitrate, Mode=TwoWay}"
Minimum="16"
Maximum="320"
IsSnapToTickEnabled="True" TickFrequency="16"
@@ -283,7 +306,7 @@
<Slider.Styles>
<Style Selector="Slider /template/ Thumb">
<Setter Property="ToolTip.Tip" Value="{CompiledBinding $parent[Slider].Value, Mode=OneWay, StringFormat='\{0:f0\} Kbps'}" />
<Setter Property="ToolTip.Tip" Value="{Binding $parent[Slider].Value, Mode=OneWay, StringFormat='\{0:f0\} Kbps'}" />
<Setter Property="ToolTip.Placement" Value="Top" />
<Setter Property="ToolTip.VerticalOffset" Value="-10" />
<Setter Property="ToolTip.HorizontalOffset" Value="-30" />
@@ -294,7 +317,7 @@
<TextBlock
Grid.Column="1"
HorizontalAlignment="Right"
Text="{CompiledBinding LameBitrate}" />
Text="{Binding LameBitrate}" />
<TextBlock
Grid.Column="2"
@@ -306,7 +329,7 @@
<CheckBox
Grid.Column="0"
IsChecked="{CompiledBinding LameConstantBitrate, Mode=TwoWay}">
IsChecked="{Binding LameConstantBitrate, Mode=TwoWay}">
<TextBlock
TextWrapping="Wrap"
@@ -317,7 +340,7 @@
<CheckBox
Grid.Column="1"
HorizontalAlignment="Right"
IsChecked="{CompiledBinding LameMatchSource, Mode=TwoWay}">
IsChecked="{Binding LameMatchSource, Mode=TwoWay}">
<TextBlock
TextWrapping="Wrap"
@@ -332,7 +355,7 @@
Grid.Row="3"
Margin="0,5"
Label="Quality"
IsEnabled="{CompiledBinding !LameTargetBitrate}">
IsEnabled="{Binding !LameTargetBitrate}">
<Grid
ColumnDefinitions="*,Auto,25"
@@ -341,7 +364,7 @@
<Slider
Grid.Column="0"
Grid.ColumnSpan="2"
Value="{CompiledBinding LameVBRQuality, Mode=TwoWay}"
Value="{Binding LameVBRQuality, Mode=TwoWay}"
Minimum="0"
Maximum="9"
IsSnapToTickEnabled="True" TickFrequency="1"
@@ -349,7 +372,7 @@
TickPlacement="Outside">
<Slider.Styles>
<Style Selector="Slider /template/ Thumb">
<Setter Property="ToolTip.Tip" Value="{CompiledBinding $parent[Slider].Value, Mode=OneWay, StringFormat='V\{0:f0\}'}" />
<Setter Property="ToolTip.Tip" Value="{Binding $parent[Slider].Value, Mode=OneWay, StringFormat='V\{0:f0\}'}" />
<Setter Property="ToolTip.Placement" Value="Top" />
<Setter Property="ToolTip.VerticalOffset" Value="-10" />
<Setter Property="ToolTip.HorizontalOffset" Value="-30" />
@@ -363,7 +386,7 @@
Orientation="Horizontal">
<TextBlock Text="V" />
<TextBlock Text="{CompiledBinding LameVBRQuality}" />
<TextBlock Text="{Binding LameVBRQuality}" />
</StackPanel>
@@ -397,8 +420,8 @@
Grid.Row="2"
Grid.ColumnSpan="2"
Margin="0,10,0,0"
IsEnabled="{CompiledBinding SplitFilesByChapter}"
Label="{CompiledBinding ChapterTitleTemplateText}">
IsEnabled="{Binding SplitFilesByChapter}"
Label="{Binding ChapterTitleTemplateText}">
<Grid ColumnDefinitions="*,Auto" Margin="0,8" >
@@ -406,7 +429,7 @@
Grid.Column="0"
FontSize="14"
IsReadOnly="True"
Text="{CompiledBinding ChapterTitleTemplate}" />
Text="{Binding ChapterTitleTemplate}" />
<Button
Grid.Column="1"

View File

@@ -39,6 +39,7 @@ namespace LibationAvalonia.ViewModels.Settings
DownloadClipsBookmarks = config.DownloadClipsBookmarks;
ClipBookmarkFormat = config.ClipsBookmarksFileFormat;
SplitFilesByChapter = config.SplitFilesByChapter;
MinimumFileDuration = config.MinimumFileDuration;
MergeOpeningAndEndCredits = config.MergeOpeningAndEndCredits;
StripAudibleBrandAudio = config.StripAudibleBrandAudio;
StripUnabridged = config.StripUnabridged;
@@ -71,6 +72,7 @@ namespace LibationAvalonia.ViewModels.Settings
config.DownloadClipsBookmarks = DownloadClipsBookmarks;
config.ClipsBookmarksFileFormat = ClipBookmarkFormat;
config.SplitFilesByChapter = SplitFilesByChapter;
config.MinimumFileDuration = MinimumFileDuration;
config.MergeOpeningAndEndCredits = MergeOpeningAndEndCredits;
config.StripAudibleBrandAudio = StripAudibleBrandAudio;
config.StripUnabridged = StripUnabridged;
@@ -118,6 +120,7 @@ namespace LibationAvalonia.ViewModels.Settings
public string DownloadCoverArtText { get; } = Configuration.GetDescription(nameof(Configuration.DownloadCoverArt));
public string RetainAaxFileText { get; } = Configuration.GetDescription(nameof(Configuration.RetainAaxFile));
public string SplitFilesByChapterText { get; } = Configuration.GetDescription(nameof(Configuration.SplitFilesByChapter));
public string MinimumFileDurationText { get; } = Configuration.GetDescription(nameof(Configuration.MinimumFileDuration));
public string MergeOpeningEndCreditsText { get; } = Configuration.GetDescription(nameof(Configuration.MergeOpeningAndEndCredits));
public string StripAudibleBrandingText { get; } = Configuration.GetDescription(nameof(Configuration.StripAudibleBrandAudio));
public string StripUnabridgedText { get; } = Configuration.GetDescription(nameof(Configuration.StripUnabridged));
@@ -146,6 +149,7 @@ namespace LibationAvalonia.ViewModels.Settings
public string StripUnabridgedTip => Configuration.GetHelpText(nameof(StripUnabridged));
public bool DecryptToLossy { get => field; set => this.RaiseAndSetIfChanged(ref field, value); }
public string DecryptToLossyTip => Configuration.GetHelpText(nameof(DecryptToLossy));
public string MinimumFileDurationTip => Configuration.GetHelpText(nameof(MinimumFileDuration));
public bool MoveMoovToBeginning { get; set; }
public bool LameDownsampleMono { get; set; } = Design.IsDesignMode;
@@ -153,6 +157,7 @@ namespace LibationAvalonia.ViewModels.Settings
public bool LameConstantBitrate { get; set; } = Design.IsDesignMode;
public bool SplitFilesByChapter { get => field; set { this.RaiseAndSetIfChanged(ref field, value); } }
public int MinimumFileDuration { get => field; set { this.RaiseAndSetIfChanged(ref field, value); } }
public bool LameTargetBitrate { get => field; set { this.RaiseAndSetIfChanged(ref field, value); } }
public bool LameMatchSource { get => field; set { this.RaiseAndSetIfChanged(ref field, value); } }
public int LameBitrate { get => field; set { this.RaiseAndSetIfChanged(ref field, value); } }

View File

@@ -58,7 +58,10 @@
</StackPanel.Styles>
<TextBlock Margin="0,0,6,0" FontSize="11" Text="DL&#xA;Limit" VerticalAlignment="Center" />
<NumericUpDown
Classes="SmallNumericUpDown"
MinWidth="100"
FontSize="12"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
TextConverter="{StaticResource myConverter}"
Height="{Binding #cancelAllBtn.DesiredSize.Height}"

View File

@@ -82,6 +82,13 @@ namespace LibationFileManager
from the decrypted audiobook. This does not require
re-encoding.
""" },
{nameof(MinimumFileDuration), """
The minimum duration (in minutes) for an chapter to
be split into its own file. Chapters shorter than
this duration will be merged with the following
chapter. Merged chapter titles will be joined with
a space between them.
""" },
{nameof(SpatialAudioCodec), """
The Dolby Digital Plus (E-AC-3) codec is more widely
supported than the AC-4 codec, but E-AC-3 files are

View File

@@ -169,6 +169,9 @@ namespace LibationFileManager
[Description("Split my books into multiple files by chapter")]
public bool SplitFilesByChapter { get => GetNonString(defaultValue: false); set => SetNonString(value); }
[Description("Minimum file duration (seconds)")]
public int MinimumFileDuration { get => Math.Max(0, GetNonString(defaultValue: 3)); set => SetNonString(value); }
[Description("Merge Opening/End Credits into the following/preceding chapters")]
public bool MergeOpeningAndEndCredits { get => GetNonString(defaultValue: false); set => SetNonString(value); }

View File

@@ -19,6 +19,7 @@ namespace LibationWinForms.Dialogs
this.retainAaxFileCbox.Text = desc(nameof(config.RetainAaxFile));
this.combineNestedChapterTitlesCbox.Text = desc(nameof(config.CombineNestedChapterTitles));
this.splitFilesByChapterCbox.Text = desc(nameof(config.SplitFilesByChapter));
this.minFileDurationLbl.Text = desc(nameof(config.MinimumFileDuration));
this.mergeOpeningEndCreditsCbox.Text = desc(nameof(config.MergeOpeningAndEndCredits));
this.stripAudibleBrandingCbox.Text = desc(nameof(config.StripAudibleBrandAudio));
this.stripUnabridgedCbox.Text = desc(nameof(config.StripUnabridged));
@@ -40,6 +41,8 @@ namespace LibationWinForms.Dialogs
toolTip.SetToolTip(requestSpatialCbox, Configuration.GetHelpText(nameof(config.RequestSpatial)));
toolTip.SetToolTip(request_xHE_AAC_Cbox, Configuration.GetHelpText(nameof(config.Request_xHE_AAC)));
toolTip.SetToolTip(spatialAudioCodecCb, Configuration.GetHelpText(nameof(config.SpatialAudioCodec)));
toolTip.SetToolTip(minFileDurationLbl, Configuration.GetHelpText(nameof(config.SpatialAudioCodec)));
toolTip.SetToolTip(minFileDurationNud, Configuration.GetHelpText(nameof(config.SpatialAudioCodec)));
fileDownloadQualityCb.Items.AddRange(
[
@@ -87,6 +90,7 @@ namespace LibationWinForms.Dialogs
retainAaxFileCbox.Checked = config.RetainAaxFile;
combineNestedChapterTitlesCbox.Checked = config.CombineNestedChapterTitles;
splitFilesByChapterCbox.Checked = config.SplitFilesByChapter;
minFileDurationNud.Value = config.MinimumFileDuration;
mergeOpeningEndCreditsCbox.Checked = config.MergeOpeningAndEndCredits;
stripUnabridgedCbox.Checked = config.StripUnabridged;
stripAudibleBrandingCbox.Checked = config.StripAudibleBrandAudio;
@@ -132,6 +136,7 @@ namespace LibationWinForms.Dialogs
config.RetainAaxFile = retainAaxFileCbox.Checked;
config.CombineNestedChapterTitles = combineNestedChapterTitlesCbox.Checked;
config.SplitFilesByChapter = splitFilesByChapterCbox.Checked;
config.MinimumFileDuration = (int)minFileDurationNud.Value;
config.MergeOpeningAndEndCredits = mergeOpeningEndCreditsCbox.Checked;
config.StripUnabridged = stripUnabridgedCbox.Checked;
config.StripAudibleBrandAudio = stripAudibleBrandingCbox.Checked;
@@ -167,7 +172,7 @@ namespace LibationWinForms.Dialogs
private void splitFilesByChapterCbox_CheckedChanged(object sender, EventArgs e)
{
chapterTitleTemplateGb.Enabled = splitFilesByChapterCbox.Checked;
chapterTitleTemplateGb.Enabled = minFileDurationNud.Enabled = minFileDurationLbl.Enabled = splitFilesByChapterCbox.Checked;
}
private void chapterTitleTemplateBtn_Click(object sender, EventArgs e)

View File

@@ -99,6 +99,8 @@
clipsBookmarksFormatCb = new System.Windows.Forms.ComboBox();
downloadClipsBookmarksCbox = new System.Windows.Forms.CheckBox();
audiobookFixupsGb = new System.Windows.Forms.GroupBox();
minFileDurationLbl = new System.Windows.Forms.Label();
minFileDurationNud = new System.Windows.Forms.NumericUpDown();
stripUnabridgedCbox = new System.Windows.Forms.CheckBox();
chapterTitleTemplateGb = new System.Windows.Forms.GroupBox();
chapterTitleTemplateBtn = new System.Windows.Forms.Button();
@@ -154,6 +156,7 @@
customFileNamingGb.SuspendLayout();
tab4AudioFileOptions.SuspendLayout();
audiobookFixupsGb.SuspendLayout();
((System.ComponentModel.ISupportInitialize)minFileDurationNud).BeginInit();
chapterTitleTemplateGb.SuspendLayout();
lameOptionsGb.SuspendLayout();
lameBitrateGb.SuspendLayout();
@@ -289,7 +292,7 @@
// stripAudibleBrandingCbox
//
stripAudibleBrandingCbox.AutoSize = true;
stripAudibleBrandingCbox.Location = new System.Drawing.Point(13, 70);
stripAudibleBrandingCbox.Location = new System.Drawing.Point(13, 88);
stripAudibleBrandingCbox.Name = "stripAudibleBrandingCbox";
stripAudibleBrandingCbox.Size = new System.Drawing.Size(143, 34);
stripAudibleBrandingCbox.TabIndex = 16;
@@ -960,20 +963,41 @@
//
// audiobookFixupsGb
//
audiobookFixupsGb.Controls.Add(minFileDurationLbl);
audiobookFixupsGb.Controls.Add(minFileDurationNud);
audiobookFixupsGb.Controls.Add(splitFilesByChapterCbox);
audiobookFixupsGb.Controls.Add(stripUnabridgedCbox);
audiobookFixupsGb.Controls.Add(stripAudibleBrandingCbox);
audiobookFixupsGb.Location = new System.Drawing.Point(6, 254);
audiobookFixupsGb.Name = "audiobookFixupsGb";
audiobookFixupsGb.Size = new System.Drawing.Size(416, 114);
audiobookFixupsGb.Size = new System.Drawing.Size(416, 128);
audiobookFixupsGb.TabIndex = 14;
audiobookFixupsGb.TabStop = false;
audiobookFixupsGb.Text = "Audiobook Fix-ups";
//
// minFileDurationLbl
//
minFileDurationLbl.AutoSize = true;
minFileDurationLbl.Location = new System.Drawing.Point(34, 44);
minFileDurationLbl.Margin = new System.Windows.Forms.Padding(0, 0, 2, 0);
minFileDurationLbl.Name = "minFileDurationLbl";
minFileDurationLbl.Size = new System.Drawing.Size(159, 15);
minFileDurationLbl.TabIndex = 29;
minFileDurationLbl.Text = "[MinimumFileDuration desc]";
//
// minFileDurationNud
//
minFileDurationNud.Location = new System.Drawing.Point(243, 41);
minFileDurationNud.Maximum = new decimal(new int[] { 120, 0, 0, 0 });
minFileDurationNud.Name = "minFileDurationNud";
minFileDurationNud.Size = new System.Drawing.Size(51, 23);
minFileDurationNud.TabIndex = 17;
minFileDurationNud.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// stripUnabridgedCbox
//
stripUnabridgedCbox.AutoSize = true;
stripUnabridgedCbox.Location = new System.Drawing.Point(13, 46);
stripUnabridgedCbox.Location = new System.Drawing.Point(13, 63);
stripUnabridgedCbox.Name = "stripUnabridgedCbox";
stripUnabridgedCbox.Size = new System.Drawing.Size(147, 19);
stripUnabridgedCbox.TabIndex = 15;
@@ -1498,6 +1522,7 @@
tab4AudioFileOptions.PerformLayout();
audiobookFixupsGb.ResumeLayout(false);
audiobookFixupsGb.PerformLayout();
((System.ComponentModel.ISupportInitialize)minFileDurationNud).EndInit();
chapterTitleTemplateGb.ResumeLayout(false);
chapterTitleTemplateGb.PerformLayout();
lameOptionsGb.ResumeLayout(false);
@@ -1627,5 +1652,7 @@
private System.Windows.Forms.ComboBox themeCb;
private System.Windows.Forms.Label themeLbl;
private System.Windows.Forms.CheckBox importPlusTitlesCb;
private System.Windows.Forms.Label minFileDurationLbl;
private System.Windows.Forms.NumericUpDown minFileDurationNud;
}
}