Skip to content

Commit 026a837

Browse files
author
Meyn
committed
Implement automatic ImportList for saved Spotify playlists
Improved Logging
1 parent 933ff72 commit 026a837

12 files changed

+366
-42
lines changed

Tubifarry/Core/AudioMetadataHandler.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using NLog;
2+
using NzbDrone.Common.Instrumentation;
23
using NzbDrone.Core.Parser.Model;
34
using Xabe.FFmpeg;
45
using Xabe.FFmpeg.Downloader;
@@ -16,10 +17,10 @@ internal class AudioMetadataHandler
1617
public bool UseID3v2_3 { get; set; }
1718

1819

19-
public AudioMetadataHandler(string originalPath, Logger? logger)
20+
public AudioMetadataHandler(string originalPath)
2021
{
2122
TrackPath = originalPath;
22-
_logger = logger;
23+
_logger = NzbDroneLogger.GetLogger(this); ;
2324
}
2425

2526

Tubifarry/Core/FileCache.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ public FileCache(string cacheDirectory)
2525

2626
if (cachedData == null || DateTime.UtcNow - cachedData.CreatedAt > cachedData.ExpirationDuration)
2727
{
28-
// Cache is expired or invalid, delete the file
2928
File.Delete(cacheFilePath);
3029
return default;
3130
}
@@ -44,7 +43,9 @@ public async Task SetAsync<T>(string cacheKey, T data, TimeSpan expirationDurati
4443
ExpirationDuration = expirationDuration
4544
};
4645

47-
string json = JsonSerializer.Serialize(cachedData);
46+
JsonSerializerOptions options = new() { WriteIndented = true };
47+
48+
string json = JsonSerializer.Serialize(cachedData, options);
4849
await File.WriteAllTextAsync(cacheFilePath, json);
4950
}
5051

Tubifarry/Core/ReleaseFormatter.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,8 @@ public string BuildAlbumFilename(string? pattern, Album album)
3434

3535
public string BuildArtistFolderName(string? pattern)
3636
{
37-
// Fall back to the ArtistFolderFormat if no pattern is provided
3837
pattern ??= _namingConfig?.ArtistFolderFormat ?? "{Artist Name}";
39-
Dictionary<string, Func<string>> tokenHandlers = GetTokenHandlers(null, null); // No track or album tokens for artist folder names
38+
Dictionary<string, Func<string>> tokenHandlers = GetTokenHandlers(null, null);
4039
string formattedString = ReplaceTokens(pattern, tokenHandlers);
4140
return CleanFileName(formattedString);
4241
}
@@ -72,8 +71,8 @@ private Dictionary<string, Func<string>> GetTokenHandlers(Track? track, Album? a
7271
{ "{Track ArtistName}", () => _artist?.Name ?? string.Empty },
7372
{ "{Track ArtistNameThe}", () => TitleThe(_artist?.Name) },
7473
{ "{Track ArtistMbId}", () => _artist?.ForeignArtistId ?? string.Empty },
75-
{ "{track:0}", () => FormatTrackNumber(track?.TrackNumber, "0") }, // Zero-padded single digit
76-
{ "{track:00}", () => FormatTrackNumber(track?.TrackNumber, "00") }, // Zero-padded two digits
74+
{ "{track:0}", () => FormatTrackNumber(track?.TrackNumber, "0") },
75+
{ "{track:00}", () => FormatTrackNumber(track?.TrackNumber, "00") },
7776

7877
// Medium Tokens (only added if track is provided)
7978
{ "{Medium Name}", () => track?.AlbumRelease?.Value?.Media?.FirstOrDefault(m => m.Number == track.MediumNumber)?.Name ?? string.Empty },
@@ -91,7 +90,7 @@ private static string ReplaceTokens(string pattern, Dictionary<string, Func<stri
9190
if (tokenHandlers.TryGetValue($"{{{token}}}", out Func<string>? handler))
9291
return handler();
9392

94-
return string.Empty; // Remove unknown tokens
93+
return string.Empty;
9594
});
9695

9796
private string CleanFileName(string fileName)
@@ -101,7 +100,6 @@ private string CleanFileName(string fileName)
101100
char[] invalidChars = invalidFileNameChars.Union(invalidPathChars).ToArray();
102101
fileName = invalidChars.Aggregate(fileName, (current, invalidChar) => current.Replace(invalidChar.ToString(), string.Empty));
103102

104-
// Handle colon replacement based on the naming config
105103
switch (_namingConfig?.ColonReplacementFormat)
106104
{
107105
case ColonReplacementFormat.Delete:

Tubifarry/Download/Clients/YouTube/YoutubeDownloadManager.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public YoutubeDownloadManager(Logger logger)
4242
_logger = logger;
4343
_queue = new();
4444
_ytClient = new YouTubeMusicClient();
45-
_logger.Debug("Initialized");
45+
_logger.Trace("Initialized");
4646
}
4747

4848
public void SetCookies(string path)
@@ -71,11 +71,10 @@ public Task<string> Download(RemoteAlbum remoteAlbum, IIndexer indexer, NamingCo
7171
LRCLIBInstance = provider.Settings.LRCLIBInstance,
7272
UseID3v2_3 = provider.Settings.UseID3v2_3,
7373
ReEncodeOptions = (ReEncodeOptions)provider.Settings.ReEncode,
74-
ClientInfo = DownloadClientItemClientInfo.FromDownloadClient(provider, false),
75-
Logger = _logger,
74+
ClientInfo = DownloadClientItemClientInfo.FromDownloadClient(provider, false)
7675
});
7776
_queue.Add(request);
78-
_logger.Debug($"Download request added to queue. Request ID: {request.ID}");
77+
_logger.Trace($"Download request added to queue. Request ID: {request.ID}");
7978
return Task.FromResult(request.ID);
8079
}
8180

@@ -88,7 +87,7 @@ public void RemoveItem(DownloadClientItem item)
8887
return;
8988
req.Dispose();
9089
_queue.Remove(req);
91-
_logger.Debug($"Item removed from queue. Download ID: {item.DownloadId}");
90+
_logger.Trace($"Item removed from queue. Download ID: {item.DownloadId}");
9291
}
9392
}
9493
}

Tubifarry/Download/Clients/YouTubeAlbumOptions.cs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using NLog;
2-
using NzbDrone.Core.Download;
1+
using NzbDrone.Core.Download;
32
using NzbDrone.Core.Download.Clients.YouTube;
43
using NzbDrone.Core.Organizer;
54
using Requests.Options;
@@ -11,8 +10,6 @@ public record YouTubeAlbumOptions : RequestOptions<string, string>
1110
{
1211
public YouTubeMusicClient? YouTubeMusicClient { get; set; }
1312

14-
public Logger? Logger { get; set; }
15-
1613
public DownloadClientItemClientInfo? ClientInfo { get; set; }
1714

1815
public bool TryIncludeLrc { get; set; }
@@ -36,7 +33,6 @@ public YouTubeAlbumOptions() { }
3633
protected YouTubeAlbumOptions(YouTubeAlbumOptions options) : base(options)
3734
{
3835
YouTubeMusicClient = options.YouTubeMusicClient;
39-
Logger = options.Logger;
4036
ClientInfo = options.ClientInfo;
4137
TryIncludeSycLrc = options.TryIncludeSycLrc;
4238
TryIncludeLrc = options.TryIncludeLrc;

Tubifarry/Download/Clients/YouTubeAlbumRequest.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using DownloadAssistant.Requests;
44
using NLog;
55
using NzbDrone.Common.Disk;
6+
using NzbDrone.Common.Instrumentation;
67
using NzbDrone.Core.Download;
78
using NzbDrone.Core.Download.Clients.YouTube;
89
using NzbDrone.Core.Music;
@@ -28,13 +29,13 @@ internal class YouTubeAlbumRequest : Request<YouTubeAlbumOptions, string, string
2829
private readonly Album _albumData;
2930
private readonly DownloadClientItem _clientItem;
3031
private readonly ReleaseFormatter _releaseFormatter;
32+
private readonly Logger _logger;
3133

3234
private DateTime _lastUpdateTime = DateTime.MinValue;
3335
private long _lastRemainingSize = 0;
3436
private byte[]? _albumCover;
3537

3638
private ReleaseInfo ReleaseInfo => _remoteAlbum.Release;
37-
private Logger? Logger => Options.Logger;
3839

3940
public override Task Task => _requestContainer.Task;
4041
public override RequestState State => _requestContainer.State;
@@ -57,6 +58,7 @@ public DownloadClientItem ClientItem
5758
public YouTubeAlbumRequest(RemoteAlbum remoteAlbum, YouTubeAlbumOptions? options) : base(options)
5859
{
5960
Options.YouTubeMusicClient ??= new YouTubeMusicClient();
61+
_logger = NzbDroneLogger.GetLogger(this);
6062
_remoteAlbum = remoteAlbum;
6163
_albumData = remoteAlbum.Albums.FirstOrDefault() ?? new();
6264
_releaseFormatter = new(ReleaseInfo, remoteAlbum.Artist, Options.NameingConfig);
@@ -86,7 +88,7 @@ private async Task ProcessAlbumAsync(CancellationToken token)
8688
{
8789
string albumBrowseID = await Options.YouTubeMusicClient!.GetAlbumBrowseIdAsync(ReleaseInfo.DownloadUrl, token).ConfigureAwait(false);
8890
AlbumInfo albumInfo = await Options.YouTubeMusicClient.GetAlbumInfoAsync(albumBrowseID, token).ConfigureAwait(false);
89-
Logger.Info(Options.NameingConfig.StandardTrackFormat);
91+
_logger.Info(Options.NameingConfig.StandardTrackFormat);
9092
if (albumInfo?.Songs == null || !albumInfo.Songs.Any())
9193
{
9294
LogAndAppendMessage($"No tracks to download found in the album: {ReleaseInfo.Album}", LogLevel.Debug);
@@ -117,15 +119,15 @@ private async Task ProcessAlbumAsync(CancellationToken token)
117119
catch (Exception ex)
118120
{
119121
LogAndAppendMessage($"Failed to process track '{trackInfo.Name}' in album '{ReleaseInfo.Album}'", LogLevel.Error);
120-
Logger?.Error(ex, $"Failed to process track '{trackInfo.Name}' in album '{ReleaseInfo.Album}'.");
122+
_logger?.Error(ex, $"Failed to process track '{trackInfo.Name}' in album '{ReleaseInfo.Album}'.");
121123
}
122124
}
123125
}
124126

125127
private void LogAndAppendMessage(string message, LogLevel logLevel)
126128
{
127129
_message.AppendLine(message);
128-
Logger?.Log(logLevel, message);
130+
_logger?.Log(logLevel, message);
129131
}
130132

131133
public async Task<byte[]?> TryDownloadCoverAsync(AlbumInfo albumInfo, CancellationToken token)
@@ -201,7 +203,7 @@ private async Task<bool> SongDownloadCompletedAsync(AlbumInfo albumInfo, AlbumSo
201203
if (!File.Exists(trackPath))
202204
return false;
203205

204-
AudioMetadataHandler audioData = new(trackPath, Options.Logger) { AlbumCover = _albumCover, UseID3v2_3 = Options.UseID3v2_3 };
206+
AudioMetadataHandler audioData = new(trackPath) { AlbumCover = _albumCover, UseID3v2_3 = Options.UseID3v2_3 };
205207

206208
if (Options.TryIncludeLrc)
207209
audioData.Lyric = await Lyric.FetchLyricsFromLRCLIBAsync(Options.LRCLIBInstance, ReleaseInfo, trackInfo, token);

Tubifarry/ImportLists/ArrStack/ArrSoundtrackImport.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ public class ArrSoundtrackImport : HttpImportListBase<ArrSoundtrackImportSetting
3030

3131
public ArrSoundtrackImport(IHttpClient httpClient, IImportListStatusService importListStatusService, IConfigService configService, IParsingService parsingService, Logger logger) : base(httpClient, importListStatusService, configService, parsingService, logger) { }
3232

33-
public override IImportListRequestGenerator GetRequestGenerator() => _generator ??= new ArrSoundtrackRequestGenerator(Settings, _logger);
33+
public override IImportListRequestGenerator GetRequestGenerator() => _generator ??= new ArrSoundtrackRequestGenerator(Settings);
3434

35-
public override IParseImportListResponse GetParser() => _parser ??= new ArrSoundtrackImportParser(Settings, _logger, _httpClient);
35+
public override IParseImportListResponse GetParser() => _parser ??= new ArrSoundtrackImportParser(Settings, _httpClient);
3636

3737
protected override void Test(List<ValidationFailure> failures)
3838
{

Tubifarry/ImportLists/ArrStack/ArrSoundtrackImportParser.cs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using NLog;
22
using NzbDrone.Common.Http;
3+
using NzbDrone.Common.Instrumentation;
34
using NzbDrone.Core.Parser.Model;
45
using System.Security.Cryptography;
56
using System.Text;
@@ -15,22 +16,23 @@ namespace NzbDrone.Core.ImportLists.ArrStack
1516
internal class ArrSoundtrackImportParser : IParseImportListResponse
1617
{
1718
private static readonly string[] SoundtrackTerms = { "soundtrack", "ost", "score", "original soundtrack", "film score" };
18-
19-
public ArrSoundtrackImportSettings Settings { get; set; }
20-
public Logger Logger { get; set; }
19+
public readonly Logger _logger;
2120
private readonly IHttpClient _httpClient;
2221
private readonly FileCache _fileCache;
2322

23+
public ArrSoundtrackImportSettings Settings { get; set; }
24+
25+
2426
private static readonly JsonSerializerOptions _jsonOptions = new()
2527
{
2628
PropertyNameCaseInsensitive = true,
2729
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
2830
};
2931

30-
public ArrSoundtrackImportParser(ArrSoundtrackImportSettings settings, Logger logger, IHttpClient httpClient)
32+
public ArrSoundtrackImportParser(ArrSoundtrackImportSettings settings, IHttpClient httpClient)
3133
{
3234
Settings = settings;
33-
Logger = logger;
35+
_logger = NzbDroneLogger.GetLogger(this);
3436
_httpClient = httpClient;
3537
_fileCache = new FileCache(Settings.CacheDirectory);
3638
}
@@ -91,7 +93,7 @@ public async Task<List<ImportListItemInfo>> ParseResponseAsync(ImportListRespons
9193
itemInfos.Add(importItem);
9294
}
9395
else
94-
Logger.Debug($"Not similar enough: {albumDetails?.Title ?? "Empty"} | {media.Title}");
96+
_logger.Debug($"Not similar enough: {albumDetails?.Title ?? "Empty"} | {media.Title}");
9597
}
9698

9799
CachedData cachedDataToSave = new()
@@ -111,10 +113,10 @@ public async Task<List<ImportListItemInfo>> ParseResponseAsync(ImportListRespons
111113
{
112114
if (ex.Message.Contains("503:ServiceUnavailable"))
113115
{
114-
Logger.Warn("Rate limit exceeded. Stopping further processing.");
116+
_logger.Warn("Rate limit exceeded. Stopping further processing.");
115117
break;
116118
}
117-
Logger.Error(ex, "Failed fetching search");
119+
_logger.Error(ex, "Failed fetching search");
118120
}
119121
}
120122
return itemInfos;
@@ -179,7 +181,7 @@ private static string EscapeLuceneQuery(string query)
179181

180182
if (releaseGroup == null)
181183
{
182-
Logger.Warn($"No release-group found for album ID: {albumId}");
184+
_logger.Warn($"No release-group found for album ID: {albumId}");
183185
return null;
184186
}
185187
return MusicBrainzAlbumItem.FromXml(releaseGroup, ns);

Tubifarry/ImportLists/ArrStack/ArrSoundtrackImportSettings.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public class ArrSoundtrackImportSettings : IImportListSettings
6666
[FieldDefinition(4, Label = "Use Strong Search", Type = FieldType.Checkbox, HelpText = "Enable to use a strong-typed search query on MusicBrainz. Disable to allow more lenient searches, which may include audio tracks from movies.", Advanced = true)]
6767
public bool UseStrongMusicBrainzSearch { get; set; } = true;
6868

69-
[FieldDefinition(5, Label = "Cache Directory", Type = FieldType.Path, HelpText = "The directory where cached data will be stored. If left empty, the default cache directory will be used.", Placeholder = "/config/soundtrack-cache")]
69+
[FieldDefinition(5, Label = "Cache Directory", Type = FieldType.Path, HelpText = "The directory where cached data will be stored.", Placeholder = "/config/soundtrack-cache")]
7070
public string CacheDirectory { get; set; } = string.Empty;
7171

7272
[FieldDefinition(6, Label = "Cache Retention Time", Type = FieldType.Number, HelpText = "The number of days to retain cached data.", Advanced = true, Placeholder = "7")]

Tubifarry/ImportLists/ArrStack/ArrSoundtrackRequestGenerator.cs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
1-
using NLog;
2-
using NzbDrone.Common.Http;
1+
using NzbDrone.Common.Http;
32

43
namespace NzbDrone.Core.ImportLists.ArrStack
54
{
65
internal class ArrSoundtrackRequestGenerator : IImportListRequestGenerator
76
{
87
public ArrSoundtrackImportSettings Settings { get; set; }
9-
public Logger Logger { get; set; }
108

11-
public ArrSoundtrackRequestGenerator(ArrSoundtrackImportSettings settings, Logger logger)
9+
public ArrSoundtrackRequestGenerator(ArrSoundtrackImportSettings settings)
1210
{
1311
Settings = settings;
14-
Logger = logger;
1512
}
1613

1714
public ImportListPageableRequestChain GetListItems()

0 commit comments

Comments
 (0)