Skip to content

Commit d0e93c8

Browse files
author
Meyn
committed
Add ALAC format
1 parent 67f4381 commit d0e93c8

File tree

6 files changed

+43
-24
lines changed

6 files changed

+43
-24
lines changed

Tubifarry/Core/Model/AudioMetadataHandler.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ public AudioMetadataHandler(string originalPath)
3333
{ AudioFormat.Opus, new[] { "-codec:a libopus", "-vbr on", "-compression_level 10", "-application audio" } },
3434
{ AudioFormat.Vorbis, new[] { "-codec:a libvorbis", "-q:a 7" } },
3535
{ AudioFormat.FLAC, new[] { "-codec:a flac" } },
36+
{ AudioFormat.ALAC, new[] { "-codec:a alac" } },
3637
{ AudioFormat.WAV, new[] { "-codec:a pcm_s16le" } },
3738
{ AudioFormat.MP4, new[] { "-codec:a aac", "-q:a 0", "-movflags +faststart" } },
3839
{ AudioFormat.AIFF, new[] { "-codec:a pcm_s16le" } },
@@ -107,7 +108,7 @@ public async Task<bool> IsVideoContainerAsync()
107108
return true;
108109

109110
byte[] header = new byte[8];
110-
using (FileStream stream = new(TrackPath, FileMode.Open, FileAccess.Read))
111+
await using (FileStream stream = new(TrackPath, FileMode.Open, FileAccess.Read))
111112
{
112113
await stream.ReadAsync(header);
113114
}
@@ -297,8 +298,7 @@ public static bool CheckFFmpegInstalled()
297298

298299
if (!isInstalled)
299300
{
300-
string[] paths = Environment.GetEnvironmentVariable("PATH")?.Split(Path.PathSeparator) ?? Array.Empty<string>();
301-
foreach (string path in paths)
301+
foreach (string path in Environment.GetEnvironmentVariable("PATH")?.Split(Path.PathSeparator) ?? Array.Empty<string>())
302302
{
303303
if (Directory.Exists(path))
304304
{

Tubifarry/Core/Utilities/AudioFormat.cs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ public enum AudioFormat
1616
OGG,
1717
MIDI,
1818
AMR,
19-
WMA
19+
WMA,
20+
ALAC
2021
}
2122

2223
internal static class AudioFormatHelper
@@ -31,7 +32,6 @@ internal static class AudioFormatHelper
3132
AudioFormat.WMA
3233
};
3334

34-
3535
private static readonly int[] _standardBitrates = { 0, 96, 128, 160, 192, 256, 320 };
3636

3737
/// <summary>
@@ -66,6 +66,7 @@ internal static class AudioFormatHelper
6666
"mid" or "midi" => AudioFormat.MIDI,
6767
"amr" => AudioFormat.AMR,
6868
"wma" => AudioFormat.WMA,
69+
"alac" => AudioFormat.ALAC,
6970
_ => AudioFormat.Unknown // Default for unknown formats
7071
};
7172

@@ -86,6 +87,7 @@ internal static class AudioFormatHelper
8687
AudioFormat.WMA => ".wma",
8788
AudioFormat.MP4 => ".mp4",
8889
AudioFormat.OGG => ".ogg",
90+
AudioFormat.ALAC => ".m4a",
8991
_ => ".aac" // Default to AAC if the format is unknown
9092
};
9193

@@ -116,16 +118,19 @@ internal static class AudioFormatHelper
116118
"mp3" => AudioFormat.MP3,
117119
"opus" => AudioFormat.Opus,
118120
"ogg" or "vorbis" => AudioFormat.Vorbis,
119-
"flac" or "alac" => AudioFormat.FLAC,
121+
"flac" => AudioFormat.FLAC,
120122
"wav" => AudioFormat.WAV,
121123
"aiff" or "aif" or "aifc" => AudioFormat.AIFF,
122124
"mid" or "midi" => AudioFormat.MIDI,
123125
"amr" => AudioFormat.AMR,
124126
"wma" => AudioFormat.WMA,
125-
_ => AudioFormat.Unknown // Default for unknown extensions
127+
"alac" => AudioFormat.ALAC,
128+
_ => AudioFormat.Unknown
126129
};
127130

128-
131+
/// <summary>
132+
/// Rounds a bitrate to the nearest standard value.
133+
/// </summary>
129134
public static int RoundToStandardBitrate(int bitrateKbps) => _standardBitrates.OrderBy(b => Math.Abs(b - bitrateKbps)).First();
130135
}
131136
}

Tubifarry/Indexers/Youtube/YoutubeParser.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ public static AlbumSearchResult GetAlbums(JToken jsonToken)
134134
artists: artists,
135135
releaseYear: jsonToken.SelectObject<int>($"flexColumns[1].musicResponsiveListItemFlexColumnRenderer.text.runs[{yearIndex}].text"),
136136
isSingle: jsonToken.SelectObject<string>("flexColumns[1].musicResponsiveListItemFlexColumnRenderer.text.runs[0].text") == "Single",
137+
isEp: jsonToken.SelectObject<string>("flexColumns[1].musicResponsiveListItemFlexColumnRenderer.text.runs[0].text") == "EP",
137138
radio: jsonToken.SelectRadio("menu.menuRenderer.items[1].menuNavigationItemRenderer.navigationEndpoint.watchPlaylistEndpoint.playlistId", null),
138139
thumbnails: jsonToken.SelectThumbnails()
139140
);

Tubifarry/Metadata/Converter/AudioConverter.cs

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ private async Task ConvertTrack(TrackFile trackFile)
4545
}
4646

4747
AudioFormat targetFormat = GetTargetFormatForTrack(trackFormat);
48+
if (targetFormat == AudioFormat.Unknown)
49+
return;
4850

4951
_logger.Debug("Converting track from {0} to {1}: {2}", trackFormat, targetFormat, trackFile.Path);
5052

@@ -61,12 +63,23 @@ private async Task ConvertTrack(TrackFile trackFile)
6163
private AudioFormat GetTargetFormatForTrack(AudioFormat trackFormat)
6264
{
6365
foreach (KeyValuePair<string, string> rule in Settings.CustomConversion)
64-
if (rule.Key.Equals(trackFormat.ToString(), StringComparison.OrdinalIgnoreCase))
65-
if (Enum.TryParse(rule.Value, true, out AudioFormat customTargetFormat))
66-
{
67-
_logger.Trace("Using custom target format {0} for track format {1}", customTargetFormat, trackFormat);
68-
return customTargetFormat;
69-
}
66+
{
67+
if (rule.Key.Equals(trackFormat.ToString(), StringComparison.OrdinalIgnoreCase) && Enum.TryParse(rule.Value, true, out AudioFormat customTargetFormat))
68+
{
69+
_logger.Trace("Using custom target format {0} for track format {1}", customTargetFormat, trackFormat);
70+
return customTargetFormat;
71+
}
72+
}
73+
if (Settings.CustomConversion.FirstOrDefault(x => x.Key.Equals("all", StringComparison.OrdinalIgnoreCase)) is KeyValuePair<string, string> all && Enum.TryParse(all.Value, true, out AudioFormat customAllTargetsFormat))
74+
{
75+
_logger.Trace("Using custom target format {0} for track format {1}", customAllTargetsFormat, trackFormat);
76+
if (AudioFormatHelper.IsLossyFormat(trackFormat) && !AudioFormatHelper.IsLossyFormat(customAllTargetsFormat))
77+
{
78+
_logger.Warn("Blocked lossy ➔ lossless conversion via 'all' rule for: {0}", trackFormat);
79+
return AudioFormat.Unknown;
80+
}
81+
return customAllTargetsFormat;
82+
}
7083
return (AudioFormat)Settings.TargetFormat;
7184
}
7285

@@ -79,10 +92,10 @@ private bool ShouldConvertTrack(TrackFile trackFile)
7992
return false;
8093
}
8194

82-
bool shouldConvertCustom = Settings.CustomConversion.Any() && Settings.CustomConversion.Any(rule => rule.Key.Equals(trackFormat.ToString(), StringComparison.OrdinalIgnoreCase));
83-
bool shouldConvertDefault = IsFormatEnabledForConversion(trackFormat);
84-
bool shouldConvert = shouldConvertCustom || shouldConvertDefault;
85-
return shouldConvert;
95+
bool hasDirectRule = Settings.CustomConversion.Any(r => r.Key.Equals(trackFormat.ToString(), StringComparison.OrdinalIgnoreCase));
96+
bool hasGlobalRule = Settings.CustomConversion.Any(r => r.Key.Equals("all", StringComparison.OrdinalIgnoreCase));
97+
bool defaultConversion = IsFormatEnabledForConversion(trackFormat);
98+
return hasDirectRule || hasGlobalRule || defaultConversion;
8699
}
87100

88101
private bool IsFormatEnabledForConversion(AudioFormat format) => format switch

Tubifarry/Metadata/Converter/AudioConverterSettings.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ public AudioConverterSettingsValidator()
2222

2323
// Validate custom conversion rules
2424
RuleFor(x => x.CustomConversion)
25-
.Must(customConversions => customConversions == null || customConversions.All(IsValidConversionRule))
25+
.Must(customConversions => customConversions?.All(IsValidConversionRule) != false)
2626
.WithMessage("Custom conversion rules must be in the format 'source -> target' (e.g., mp3 to flac).");
2727

2828
RuleFor(x => x.CustomConversion)
29-
.Must(customConversions => customConversions == null || customConversions.All(IsValidLossyConversion))
29+
.Must(customConversions => customConversions?.All(IsValidLossyConversion) != false)
3030
.WithMessage("Lossy formats cannot be converted to non-lossy formats.");
3131

3232
RuleFor(x => x)
@@ -39,15 +39,15 @@ private bool IsValidConversionRule(KeyValuePair<string, string> rule)
3939
if (string.IsNullOrWhiteSpace(rule.Key) || string.IsNullOrWhiteSpace(rule.Value))
4040
return false;
4141

42-
bool isValidSource = Enum.TryParse(rule.Key, true, out AudioFormat sourceFormat) && sourceFormat != AudioFormat.Unknown;
42+
bool isValidSource = (string.Equals(rule.Key, "all", StringComparison.OrdinalIgnoreCase)) || (Enum.TryParse(rule.Key, true, out AudioFormat sourceFormat) && sourceFormat != AudioFormat.Unknown);
4343
bool isValidTarget = Enum.TryParse(rule.Value, true, out AudioFormat targetFormat) && targetFormat != AudioFormat.Unknown;
4444

4545
return isValidSource && isValidTarget;
4646
}
4747

4848
private bool IsValidLossyConversion(KeyValuePair<string, string> rule)
4949
{
50-
if (string.IsNullOrWhiteSpace(rule.Key) || string.IsNullOrWhiteSpace(rule.Value))
50+
if (rule.Key.Equals("all", StringComparison.OrdinalIgnoreCase))
5151
return true;
5252

5353
if (!Enum.TryParse(rule.Key, true, out AudioFormat sourceFormat) || !Enum.TryParse(rule.Value, true, out AudioFormat targetFormat))

Tubifarry/Tubifarry.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@
1515
<PrivateAssets>all</PrivateAssets>
1616
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1717
</PackageReference>
18-
<PackageReference Include="Shard.DownloadAssistant" Version="1.0.9" />
18+
<PackageReference Include="Shard.DownloadAssistant" Version="1.1.0" />
1919
<PackageReference Include="Xabe.FFmpeg" Version="5.2.6" />
2020
<PackageReference Include="Xabe.FFmpeg.Downloader" Version="5.2.6" />
21-
<PackageReference Include="YouTubeMusicAPI" Version="2.1.1" />
21+
<PackageReference Include="YouTubeMusicAPI" Version="2.2.0" />
2222
</ItemGroup>
2323

2424
<ItemGroup>

0 commit comments

Comments
 (0)