Skip to content

Commit 6d3a821

Browse files
author
Meyn
committed
Implement QueneCleaner
1 parent 0230ea8 commit 6d3a821

File tree

7 files changed

+417
-7
lines changed

7 files changed

+417
-7
lines changed

README.md

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,18 @@ Additionally, Tubifarry supports fetching soundtracks from **Sonarr** (series) a
77

88
---
99

10+
## Table of Contents 📑
11+
12+
1. [Tubifarry for Lidarr 🎶](#tubifarry-for-lidarr-)
13+
2. [Installation 🚀](#installation-)
14+
3. [Soulseek (Slskd) Setup 🎧](#soulseek-slskd-setup-)
15+
4. [YouTube Downloader Setup 🎥](#youtube-downloader-setup-)
16+
5. [Fetching Soundtracks 🎬🎵](#fetching-soundtracks-from-sonarr-and-radarr-)
17+
6. [Queue Cleaner 🧹](#queue-cleaner-)
18+
7. [Troubleshooting 🛠️](#troubleshooting-)
19+
20+
----
21+
1022
## Installation 🚀
1123
To use Tubifarry, ensure your Lidarr setup is on the `plugins` branch. Follow the steps below to get started.
1224

@@ -90,7 +102,24 @@ To enable this feature:
90102

91103
---
92104

93-
### Troubleshooting 🛠️
105+
## Queue Cleaner 🧹
106+
107+
The **Queue Cleaner** automatically processes items in your Lidarr queue that have **failed to import**. It ensures your library stays organized by handling failed imports based on your preferences.
108+
109+
1. **Key Options**:
110+
- *Blocklist*: Choose to remove, blocklist, or both for failed imports.
111+
- *Rename*: Automatically rename album folders and tracks using available metadata.
112+
- *Clean Imports*: Decide when to clean—when tracks are missing, metadata is incomplete, or always.
113+
- *Retry Finding Release*: Automatically retry searching for a release if the import fails.
114+
115+
2. **How to Enable**:
116+
- Navigate to `Settings -> Connect` in Lidarr.
117+
- Add a new connection and select the **Queue Cleaner**.
118+
- Configure the settings to match your needs.
119+
120+
---
121+
122+
## Troubleshooting 🛠️
94123
- **Slskd Download Path Permissions**: Ensure Lidarr has read/write access to the Slskd download path. Verify folder permissions and ensure the user running Lidarr has the necessary access. For Docker setups, confirm the volume is correctly mounted and permissions are set.
95124
- **Optional: FFmpeg Issues**: If you choose to use FFmpeg and songs fail to process, verify that FFmpeg is correctly installed and accessible in your system's PATH. If not, try reinstalling or downloading it manually.
96125
- **Metadata Issues**: If metadata is not being added to downloaded files, confirm that the files are in a supported format. If using FFmpeg, ensure it is extracting audio to formats like AAC embedded in MP4 containers (check debug logs).

Tubifarry/Core/AlbumData.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public class AlbumData
2727

2828
// Properties from YoutubeSearchResults
2929
public int Bitrate { get; set; }
30+
public int BitDepth { get; set; }
3031
public long Duration { get; set; }
3132

3233
// Soulseek
@@ -91,6 +92,8 @@ private string ConstructTitle()
9192

9293
if (AudioFormatHelper.IsLossyFormat(Codec) && calculatedBitrate != 0)
9394
title += $" [{Codec} {calculatedBitrate}kbps] [WEB]";
95+
if (!AudioFormatHelper.IsLossyFormat(Codec) && BitDepth != 0)
96+
title += $" [{Codec} {BitDepth}bit] [WEB]";
9497
else
9598
title += $" [{Codec}] [WEB]";
9699

Tubifarry/Download/Clients/Soulseek/SlskdClient.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using NzbDrone.Common.Extensions;
55
using NzbDrone.Common.Http;
66
using NzbDrone.Core.Configuration;
7-
using NzbDrone.Core.History;
87
using NzbDrone.Core.Indexers;
98
using NzbDrone.Core.Parser.Model;
109
using NzbDrone.Core.RemotePathMappings;
@@ -23,7 +22,7 @@ public class SlskdClient : DownloadClientBase<SlskdProviderSettings>
2322
public override string Name => "Slskd";
2423
public override string Protocol => nameof(SoulseekDownloadProtocol);
2524

26-
public SlskdClient(IHttpClient httpClient, IConfigService configService, IDiskProvider diskProvider, IHistoryService history, IRemotePathMappingService remotePathMappingService, Logger logger)
25+
public SlskdClient(IHttpClient httpClient, IConfigService configService, IDiskProvider diskProvider, IRemotePathMappingService remotePathMappingService, Logger logger)
2726
: base(configService, diskProvider, remotePathMappingService, logger) => _httpClient = httpClient;
2827

2928

Tubifarry/Indexers/Soulseek/SlskdParser.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,13 @@ private AlbumData CreateAlbumData(string searchId, IGrouping<string, SlskdFileDa
8585
long totalSize = directory.Sum(f => f.Size);
8686
int totalDuration = directory.Sum(f => f.Length ?? 0);
8787

88-
int? bitRate = directory.First().BitRate;
89-
if (!bitRate.HasValue && totalDuration > 0)
90-
bitRate = (int)((totalSize * 8) / (totalDuration * 1000));
88+
int? mostCommonBitRate = directory.GroupBy(f => f.BitRate).OrderByDescending(g => g.Count()).FirstOrDefault()?.Key;
89+
90+
int? mostCommonBitDepth = directory.GroupBy(f => f.BitDepth).OrderByDescending(g => g.Count()).FirstOrDefault()?.Key;
91+
92+
93+
if (!mostCommonBitRate.HasValue && totalDuration > 0)
94+
mostCommonBitRate = (int)((totalSize * 8) / (totalDuration * 1000));
9195

9296
List<SlskdFileData>? filesToDownload = directory.GroupBy(f => f.Filename?[..f.Filename.LastIndexOf('\\')]).FirstOrDefault(g => g.Key == directory.Key)?.ToList();
9397
AudioFormat codec = AudioFormatHelper.GetAudioCodecFromExtension(mostCommonExtension ?? string.Empty);
@@ -99,7 +103,8 @@ private AlbumData CreateAlbumData(string searchId, IGrouping<string, SlskdFileDa
99103
ReleaseDate = folderData.Year,
100104
ReleaseDateTime = (string.IsNullOrEmpty(folderData.Year) || !int.TryParse(folderData.Year, out int yearInt) ? DateTime.MinValue : new DateTime(yearInt, 1, 1)),
101105
Codec = codec,
102-
Bitrate = (codec == AudioFormat.MP3 ? AudioFormatHelper.RoundToStandardBitrate(bitRate ?? 0) : bitRate) ?? 0,
106+
BitDepth = mostCommonBitDepth ?? 0,
107+
Bitrate = (codec == AudioFormat.MP3 ? AudioFormatHelper.RoundToStandardBitrate(mostCommonBitRate ?? 0) : mostCommonBitRate) ?? 0,
103108
Size = totalSize,
104109
Priotity = folderData.CalculatePriority(),
105110
CustomString = JsonConvert.SerializeObject(filesToDownload),
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
using NLog;
2+
using NzbDrone.Common.Extensions;
3+
using NzbDrone.Core.MediaFiles.Events;
4+
using NzbDrone.Core.Messaging.Events;
5+
using NzbDrone.Core.Music;
6+
using NzbDrone.Core.ThingiProvider;
7+
8+
namespace NzbDrone.Core.Notifications
9+
{
10+
public class ImportFailureNotificationService : IHandle<AlbumImportIncompleteEvent>
11+
{
12+
13+
private readonly INotificationFactory _notificationFactory;
14+
private readonly INotificationStatusService _notificationStatusService;
15+
private readonly Logger _logger;
16+
17+
public ImportFailureNotificationService(INotificationFactory notificationFactory, INotificationStatusService notificationStatusService, Logger logger)
18+
{
19+
_notificationFactory = notificationFactory;
20+
_notificationStatusService = notificationStatusService;
21+
_logger = logger;
22+
}
23+
24+
25+
private bool ShouldHandleArtist(ProviderDefinition definition, Artist artist)
26+
{
27+
if (definition.Tags.Empty())
28+
{
29+
_logger.Debug("No tags set for this notification.");
30+
return true;
31+
}
32+
33+
if (definition.Tags.Intersect(artist.Tags).Any())
34+
{
35+
_logger.Debug("Notification and artist have one or more intersecting tags.");
36+
return true;
37+
}
38+
_logger.Debug("{0} does not have any intersecting tags with {1}. Notification will not be sent.", definition.Name, artist.Name);
39+
return false;
40+
}
41+
42+
43+
public void Handle(AlbumImportIncompleteEvent message)
44+
{
45+
foreach (INotification? notification in _notificationFactory.OnImportFailureEnabled())
46+
{
47+
if (notification is not QueueCleaner.QueueCleaner queue)
48+
continue;
49+
try
50+
{
51+
if (ShouldHandleArtist(notification.Definition, message.TrackedDownload.RemoteAlbum.Artist))
52+
{
53+
queue.CleanImport(message);
54+
_notificationStatusService.RecordSuccess(notification.Definition.Id);
55+
}
56+
}
57+
catch (Exception ex)
58+
{
59+
_notificationStatusService.RecordFailure(notification.Definition.Id);
60+
_logger.Warn(ex, "Unable to send CleanImport: " + notification.Definition.Name);
61+
}
62+
}
63+
}
64+
}
65+
}

0 commit comments

Comments
 (0)