Skip to content

Commit e5947e7

Browse files
author
Meyn
committed
Fix Slskd track search
1 parent 549c5c6 commit e5947e7

File tree

7 files changed

+303
-177
lines changed

7 files changed

+303
-177
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,3 +367,4 @@ FodyWeavers.xsd
367367
/_plugins/Tubifarry
368368
/enc_temp_folder/f7b6bdaa49df014838e55dd376c78df
369369
/_plugins/UnitTest
370+
/enc_temp_folder

Tubifarry/Download/Clients/Soulseek/SlskdClient.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public override async Task<string> Download(RemoteAlbum remoteAlbum, IIndexer in
5555
RemoveItemAsync(item).Wait();
5656
}
5757
catch { }
58-
return null!;
58+
throw;
5959
}
6060
return item.ID;
6161
}
@@ -221,7 +221,7 @@ private ReleaseInfo CreateReleaseInfoFromDownloadDirectory(string username, Slsk
221221
{
222222
SlskdFolderData folderData = dir.CreateFolderData(username);
223223

224-
SlskdSearchData searchData = new(null, null, false, 1);
224+
SlskdSearchData searchData = new(null, null, false, false, 1);
225225

226226
IGrouping<string, SlskdFileData> directory = dir.ToSlskdFileDataList().GroupBy(_ => dir.Directory).First();
227227

Tubifarry/Indexers/Soulseek/SlsdkRecords.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,19 @@ public record SlskdSearchData(
9595
[property: JsonPropertyName("artist")] string? Artist,
9696
[property: JsonPropertyName("album")] string? Album,
9797
[property: JsonPropertyName("interactive")] bool Interactive,
98+
[property: JsonPropertyName("expandDirectory")] bool ExpandDirectory,
9899
[property: JsonPropertyName("mimimumFiles")] int MinimumFiles)
99100
{
100101
public static SlskdSearchData FromJson(string jsonString) => JsonSerializer.Deserialize<SlskdSearchData>(jsonString, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true })!;
101102
}
103+
104+
public record SlskdDirectoryApiResponse(
105+
[property: JsonPropertyName("files")] List<SlskdDirectoryApiFile> Files
106+
);
107+
108+
public record SlskdDirectoryApiFile(
109+
[property: JsonPropertyName("filename")] string Filename,
110+
[property: JsonPropertyName("size")] long Size,
111+
[property: JsonPropertyName("code")] int Code
112+
);
102113
}

Tubifarry/Indexers/Soulseek/SlskdIndexerParser.cs

Lines changed: 41 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using NLog;
1+
using FuzzySharp;
2+
using NLog;
23
using NzbDrone.Common.Http;
34
using NzbDrone.Common.Instrumentation;
45
using NzbDrone.Core.Download;
@@ -8,6 +9,7 @@
89
using NzbDrone.Core.Parser.Model;
910
using System.Text.Json;
1011
using Tubifarry.Core.Model;
12+
using Tubifarry.Core.Utilities;
1113

1214
namespace Tubifarry.Indexers.Soulseek
1315
{
@@ -45,7 +47,6 @@ public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
4547
}
4648

4749
SlskdSearchData searchTextData = SlskdSearchData.FromJson(indexerResponse.HttpRequest.ContentSummary);
48-
4950
HashSet<string>? ignoredUsers = GetIgnoredUsers(Settings.IgnoreListPath);
5051

5152
foreach (SlskdFolderData response in searchResponse.Responses)
@@ -55,7 +56,7 @@ public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
5556

5657
IEnumerable<SlskdFileData> filteredFiles = SlskdFileData.GetFilteredFiles(response.Files, Settings.OnlyAudioFiles, Settings.IncludeFileExtensions);
5758

58-
foreach (IGrouping<string, SlskdFileData> directoryGroup in filteredFiles.GroupBy(f => GetDirectoryFromFilename(f.Filename)))
59+
foreach (IGrouping<string, SlskdFileData> directoryGroup in filteredFiles.GroupBy(f => SlskdTextProcessor.GetDirectoryFromFilename(f.Filename)))
5960
{
6061
if (string.IsNullOrEmpty(directoryGroup.Key))
6162
continue;
@@ -72,9 +73,11 @@ public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
7273
FileCount = response.FileCount
7374
};
7475

75-
AlbumData albumData = SlskdItemsParser.CreateAlbumData(searchResponse.Id, directoryGroup, searchTextData, folderData, Settings);
76+
if (searchTextData.ExpandDirectory && ShouldExpandDirectory(albumDatas, searchResponse, searchTextData, directoryGroup, folderData))
77+
continue;
7678

77-
albumDatas.Add(albumData);
79+
AlbumData originalAlbumData = SlskdItemsParser.CreateAlbumData(searchResponse.Id, directoryGroup, searchTextData, folderData, Settings);
80+
albumDatas.Add(originalAlbumData);
7881
}
7982
}
8083

@@ -88,12 +91,39 @@ public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
8891
return albumDatas.OrderByDescending(x => x.Priotity).Select(a => a.ToReleaseInfo()).ToList();
8992
}
9093

91-
private static string GetDirectoryFromFilename(string? filename)
94+
private bool ShouldExpandDirectory(List<AlbumData> albumDatas, SlskdSearchResponse searchResponse, SlskdSearchData searchTextData, IGrouping<string, SlskdFileData> directoryGroup, SlskdFolderData folderData)
9295
{
93-
if (string.IsNullOrEmpty(filename))
94-
return "";
95-
int lastBackslashIndex = filename.LastIndexOf('\\');
96-
return lastBackslashIndex >= 0 ? filename[..lastBackslashIndex] : "";
96+
if (string.IsNullOrEmpty(searchTextData.Artist) || string.IsNullOrEmpty(searchTextData.Album))
97+
return false;
98+
99+
bool artistMatch = Fuzz.PartialRatio(folderData.Artist, searchTextData.Artist) > 85;
100+
bool albumMatch = Fuzz.PartialRatio(folderData.Album, searchTextData.Album) > 85;
101+
102+
if (!artistMatch || !albumMatch)
103+
return false;
104+
105+
SlskdFileData? originalTrack = directoryGroup.FirstOrDefault(x => AudioFormatHelper.GetAudioCodecFromExtension(x.Extension?.ToLowerInvariant() ?? Path.GetExtension(x.Filename) ?? "") != AudioFormat.Unknown);
106+
107+
if (originalTrack == null)
108+
return false;
109+
110+
_logger.Trace($"Expanding directory for: {folderData.Username}:{directoryGroup.Key}");
111+
112+
SlskdRequestGenerator? requestGenerator = _indexer.GetExtendedRequestGenerator() as SlskdRequestGenerator;
113+
IGrouping<string, SlskdFileData>? expandedGroup = requestGenerator?.ExpandDirectory(folderData.Username, directoryGroup.Key, originalTrack).Result;
114+
115+
if (expandedGroup != null)
116+
{
117+
_logger.Debug($"Successfully expanded directory to {expandedGroup.Count()} files");
118+
AlbumData albumData = SlskdItemsParser.CreateAlbumData(searchResponse.Id, expandedGroup, searchTextData, folderData, Settings);
119+
albumDatas.Add(albumData);
120+
return true;
121+
}
122+
else
123+
{
124+
_logger.Warn($"Failed to expand directory for {folderData.Username}:{directoryGroup.Key}");
125+
}
126+
return false;
97127
}
98128

99129
public void RemoveSearch(string searchId, bool delay = false)
@@ -172,7 +202,7 @@ private async Task ExecuteRemovalAsync(SlskdSettings settings, string searchId)
172202
_logger.Trace($"Using cached ignore list from: {ignoreListPath} with {cached.IgnoredUsers.Count} users");
173203
return cached.IgnoredUsers;
174204
}
175-
HashSet<string> ignoredUsers = ParseIgnoreListContent(File.ReadAllText(ignoreListPath));
205+
HashSet<string> ignoredUsers = SlskdTextProcessor.ParseListContent(File.ReadAllText(ignoreListPath));
176206
_ignoreListCache[ignoreListPath] = (ignoredUsers, fileSize);
177207
_logger.Trace($"Loaded ignore list with {ignoredUsers.Count} users from: {ignoreListPath}");
178208
return ignoredUsers;
@@ -183,18 +213,5 @@ private async Task ExecuteRemovalAsync(SlskdSettings settings, string searchId)
183213
return null;
184214
}
185215
}
186-
187-
private static HashSet<string> ParseIgnoreListContent(string content)
188-
{
189-
if (string.IsNullOrWhiteSpace(content))
190-
return new HashSet<string>(StringComparer.OrdinalIgnoreCase);
191-
192-
return content
193-
.Split(new[] { '\t', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
194-
.Where(username => !string.IsNullOrWhiteSpace(username))
195-
.Select(username => username.Trim())
196-
.ToHashSet(StringComparer.OrdinalIgnoreCase);
197-
}
198-
199216
}
200217
}

0 commit comments

Comments
 (0)