Merge pull request #746 from Cucumberrbob/refactor/get-download-info

refactor: use `GetDownloadInfo` - a richer version of `GetDownloadLinks`
This commit is contained in:
Roger Far 2025-04-13 07:37:57 -06:00 committed by GitHub
commit 2e028ed17c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 159 additions and 140 deletions

View file

@ -1,4 +1,5 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using RdtClient.Data.Models.Data;
using Download = RdtClient.Data.Models.Data.Download; using Download = RdtClient.Data.Models.Data.Download;
namespace RdtClient.Data.Data; namespace RdtClient.Data.Data;
@ -29,13 +30,14 @@ public class DownloadData(DataContext dataContext)
.FirstOrDefaultAsync(m => m.TorrentId == torrentId && m.Path == path); .FirstOrDefaultAsync(m => m.TorrentId == torrentId && m.Path == path);
} }
public async Task<Download> Add(Guid torrentId, String path) public async Task<Download> Add(Guid torrentId, DownloadInfo downloadInfo)
{ {
var download = new Download var download = new Download
{ {
DownloadId = Guid.NewGuid(), DownloadId = Guid.NewGuid(),
TorrentId = torrentId, TorrentId = torrentId,
Path = path, FileName = downloadInfo.FileName,
Path = downloadInfo.RestrictedLink,
Added = DateTimeOffset.UtcNow, Added = DateTimeOffset.UtcNow,
DownloadQueued = DateTimeOffset.UtcNow, DownloadQueued = DateTimeOffset.UtcNow,
RetryCount = 0 RetryCount = 0

View file

@ -42,3 +42,19 @@ public class Download
[NotMapped] [NotMapped]
public Int64 Speed { get; set; } public Int64 Speed { get; set; }
} }
/// <summary>
/// Used to create <see cref="Download"/>s
/// </summary>
public class DownloadInfo
{
/// <summary>
/// The name of the file. Should not include directory.
/// If the filename is not known, set tn null and `GetFileName` will be called with the unrestricted link.
/// </summary>
public required String? FileName;
/// <summary>
/// The restricted link to download this download. If the debrid serice in question does not have restricted links, use either a fake or the unrestricted link
/// </summary>
public required String RestrictedLink;
}

View file

@ -405,7 +405,7 @@ public class AllDebridTorrentClientTest
} }
[Fact] [Fact]
public async Task GetDownloadLinks_WhenTorrentRdIdNull_ReturnsNull() public async Task GetDownloadInfos_WhenTorrentRdIdNull_ReturnsNull()
{ {
// Arrange // Arrange
var mocks = new Mocks(); var mocks = new Mocks();
@ -418,7 +418,7 @@ public class AllDebridTorrentClientTest
var allDebridTorrentClient = new AllDebridTorrentClient(mocks.LoggerMock.Object, mocks.AllDebridClientFactoryMock.Object, mocks.FileFilterMock.Object); var allDebridTorrentClient = new AllDebridTorrentClient(mocks.LoggerMock.Object, mocks.AllDebridClientFactoryMock.Object, mocks.FileFilterMock.Object);
// Act // Act
var result = await allDebridTorrentClient.GetDownloadLinks(torrent); var result = await allDebridTorrentClient.GetDownloadInfos(torrent);
// Assert // Assert
Assert.Null(result); Assert.Null(result);
@ -426,7 +426,7 @@ public class AllDebridTorrentClientTest
} }
[Fact] [Fact]
public async Task GetDownloadLinks_UsesFileFilter() public async Task GetDownloadInfos_UsesFileFilter()
{ {
// Arrange // Arrange
var mocks = new Mocks(); var mocks = new Mocks();
@ -467,16 +467,16 @@ public class AllDebridTorrentClientTest
var allDebridTorrentClient = new AllDebridTorrentClient(mocks.LoggerMock.Object, mocks.AllDebridClientFactoryMock.Object, mocks.FileFilterMock.Object); var allDebridTorrentClient = new AllDebridTorrentClient(mocks.LoggerMock.Object, mocks.AllDebridClientFactoryMock.Object, mocks.FileFilterMock.Object);
// Act // Act
var result = await allDebridTorrentClient.GetDownloadLinks(torrent); var result = await allDebridTorrentClient.GetDownloadInfos(torrent);
// Assert // Assert
Assert.NotNull(result); Assert.NotNull(result);
Assert.Single(result); Assert.Single(result);
Assert.Contains("https://fake.url/file1.txt", result); Assert.Single(result.Where(info => info.RestrictedLink == "https://fake.url/file1.txt"));
} }
[Fact] [Fact]
public async Task GetDownloadLinks_WhenAllFilesExcluded_ReturnsEmptyList() public async Task GetDownloadInfos_WhenAllFilesExcluded_ReturnsEmptyList()
{ {
// Arrange // Arrange
var mocks = new Mocks(); var mocks = new Mocks();
@ -508,7 +508,7 @@ public class AllDebridTorrentClientTest
var allDebridTorrentClient = new AllDebridTorrentClient(mocks.LoggerMock.Object, mocks.AllDebridClientFactoryMock.Object, mocks.FileFilterMock.Object); var allDebridTorrentClient = new AllDebridTorrentClient(mocks.LoggerMock.Object, mocks.AllDebridClientFactoryMock.Object, mocks.FileFilterMock.Object);
// Act // Act
var result = await allDebridTorrentClient.GetDownloadLinks(torrent); var result = await allDebridTorrentClient.GetDownloadInfos(torrent);
// Assert // Assert
Assert.NotNull(result); Assert.NotNull(result);

View file

@ -1,4 +1,5 @@
using RdtClient.Data.Data; using RdtClient.Data.Data;
using RdtClient.Data.Models.Data;
using Download = RdtClient.Data.Models.Data.Download; using Download = RdtClient.Data.Models.Data.Download;
namespace RdtClient.Service.Services; namespace RdtClient.Service.Services;
@ -20,9 +21,9 @@ public class Downloads(DownloadData downloadData) : IDownloads
return await downloadData.Get(torrentId, path); return await downloadData.Get(torrentId, path);
} }
public async Task<Download> Add(Guid torrentId, String path) public async Task<Download> Add(Guid torrentId, DownloadInfo downloadInfo)
{ {
return await downloadData.Add(torrentId, path); return await downloadData.Add(torrentId, downloadInfo);
} }
public async Task UpdateUnrestrictedLink(Guid downloadId, String unrestrictedLink) public async Task UpdateUnrestrictedLink(Guid downloadId, String unrestrictedLink)

View file

@ -7,7 +7,7 @@ public interface IDownloads
Task<List<Download>> GetForTorrent(Guid torrentId); Task<List<Download>> GetForTorrent(Guid torrentId);
Task<Download?> GetById(Guid downloadId); Task<Download?> GetById(Guid downloadId);
Task<Download?> Get(Guid torrentId, String path); Task<Download?> Get(Guid torrentId, String path);
Task<Download> Add(Guid torrentId, String path); Task<Download> Add(Guid torrentId, DownloadInfo downloadInfo);
Task UpdateUnrestrictedLink(Guid downloadId, String unrestrictedLink); Task UpdateUnrestrictedLink(Guid downloadId, String unrestrictedLink);
Task UpdateFileName(Guid downloadId, String fileName); Task UpdateFileName(Guid downloadId, String fileName);
Task UpdateDownloadStarted(Guid downloadId, DateTimeOffset? dateTime); Task UpdateDownloadStarted(Guid downloadId, DateTimeOffset? dateTime);

View file

@ -1,10 +1,10 @@
using AllDebridNET; using System.Diagnostics;
using AllDebridNET;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Newtonsoft.Json; using Newtonsoft.Json;
using RdtClient.Data.Enums; using RdtClient.Data.Enums;
using RdtClient.Data.Models.TorrentClient; using RdtClient.Data.Models.TorrentClient;
using RdtClient.Service.Helpers; using RdtClient.Service.Helpers;
using System.Web;
using RdtClient.Data.Models.Data; using RdtClient.Data.Models.Data;
using File = AllDebridNET.File; using File = AllDebridNET.File;
using Torrent = RdtClient.Data.Models.Data.Torrent; using Torrent = RdtClient.Data.Models.Data.Torrent;
@ -245,7 +245,7 @@ public class AllDebridTorrentClient(ILogger<AllDebridTorrentClient> logger, IAll
return torrent; return torrent;
} }
public async Task<IList<String>?> GetDownloadLinks(Torrent torrent) public async Task<IList<DownloadInfo>?> GetDownloadInfos(Torrent torrent)
{ {
if (torrent.RdId == null) if (torrent.RdId == null)
{ {
@ -258,21 +258,20 @@ public class AllDebridTorrentClient(ILogger<AllDebridTorrentClient> logger, IAll
var files = GetFiles(allFiles); var files = GetFiles(allFiles);
files = files.Where(f => fileFilter.IsDownloadable(torrent, f.Path, f.Bytes)).ToList(); return files.Where(f => fileFilter.IsDownloadable(torrent, f.Path, f.Bytes) && f.DownloadLink != null).Select(f => new DownloadInfo
{
return files.Where(m => m.DownloadLink != null).Select(m => m.DownloadLink!.ToString()).ToList(); FileName = Path.GetFileName(f.Path),
RestrictedLink = f.DownloadLink!
}).ToList();
} }
public Task<String> GetFileName(String downloadUrl) /// <inheritdoc />
public Task<String> GetFileName(Download download)
{ {
if (String.IsNullOrWhiteSpace(downloadUrl)) // FileName is set in GetDownlaadInfos
{ Debug.Assert(download.FileName != null);
return Task.FromResult("");
}
var uri = new Uri(downloadUrl); return Task.FromResult(download.FileName);
return Task.FromResult(HttpUtility.UrlDecode(uri.Segments.Last()));
} }
private async Task<TorrentClientTorrent> GetInfo(String torrentId) private async Task<TorrentClientTorrent> GetInfo(String torrentId)

View file

@ -1,12 +1,13 @@
using Microsoft.Extensions.Logging; using System.Diagnostics;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json; using Newtonsoft.Json;
using DebridLinkFrNET; using DebridLinkFrNET;
using RdtClient.Data.Enums; using RdtClient.Data.Enums;
using RdtClient.Data.Models.TorrentClient; using RdtClient.Data.Models.TorrentClient;
using RdtClient.Service.Helpers; using RdtClient.Service.Helpers;
using DebridLinkFrNET.Models; using RdtClient.Data.Models.Data;
using System.Web;
using Download = RdtClient.Data.Models.Data.Download; using Download = RdtClient.Data.Models.Data.Download;
using Torrent = DebridLinkFrNET.Models.Torrent;
namespace RdtClient.Service.Services.TorrentClients; namespace RdtClient.Service.Services.TorrentClients;
@ -234,7 +235,7 @@ public class DebridLinkClient(ILogger<DebridLinkClient> logger, IHttpClientFacto
return torrent; return torrent;
} }
public async Task<IList<String>?> GetDownloadLinks(Data.Models.Data.Torrent torrent) public async Task<IList<DownloadInfo>?> GetDownloadInfos(Data.Models.Data.Torrent torrent)
{ {
if (torrent.RdId == null) if (torrent.RdId == null)
{ {
@ -248,19 +249,14 @@ public class DebridLinkClient(ILogger<DebridLinkClient> logger, IHttpClientFacto
return null; return null;
} }
var downloadLinks = rdTorrent.Files? return rdTorrent.Files?
.Where(m => fileFilter.IsDownloadable(torrent, m.Path, m.Bytes) && m.DownloadLink != null) .Where(m => fileFilter.IsDownloadable(torrent, m.Path, m.Bytes) && m.DownloadLink != null)
.Select(m => m.DownloadLink!) .Select(m => new DownloadInfo
.ToList() ?? []; {
RestrictedLink = m.DownloadLink!,
Log($"Found {downloadLinks.Count} links", torrent); FileName = Path.GetFileName(m.Path)
})
foreach (var link in downloadLinks) .ToList();
{
Log($"{link}", torrent);
}
return downloadLinks;
} }
private async Task<TorrentClientTorrent> GetInfo(String torrentId) private async Task<TorrentClientTorrent> GetInfo(String torrentId)
@ -280,16 +276,13 @@ public class DebridLinkClient(ILogger<DebridLinkClient> logger, IHttpClientFacto
logger.LogDebug(message); logger.LogDebug(message);
} }
public Task<String> GetFileName(String downloadUrl) /// <inheritdoc />
public Task<String> GetFileName(Download download)
{ {
if (String.IsNullOrWhiteSpace(downloadUrl)) // FileName is set in GetDownlaadInfos
{ Debug.Assert(download.FileName != null);
return Task.FromResult("");
}
var uri = new Uri(downloadUrl); return Task.FromResult(download.FileName);
return Task.FromResult(HttpUtility.UrlDecode(uri.Segments.Last()));
} }
public static String? GetSymlinkPath(Data.Models.Data.Torrent torrent, Download download) public static String? GetSymlinkPath(Data.Models.Data.Torrent torrent, Download download)

View file

@ -14,6 +14,12 @@ public interface ITorrentClient
Task Delete(String torrentId); Task Delete(String torrentId);
Task<String> Unrestrict(String link); Task<String> Unrestrict(String link);
Task<Torrent> UpdateData(Torrent torrent, TorrentClientTorrent? torrentClientTorrent); Task<Torrent> UpdateData(Torrent torrent, TorrentClientTorrent? torrentClientTorrent);
Task<IList<String>?> GetDownloadLinks(Torrent torrent); Task<IList<DownloadInfo>?> GetDownloadInfos(Torrent torrent);
Task<String> GetFileName(String downloadUrl); /// <summary>
/// To be called only when <see cref="Data.Models.Data.Download" />.<see cref="Data.Models.Data.Download.FileName" /> is not set by
/// <see cref="GetDownloadInfos" />
/// </summary>
/// <param name="download">The download to get the filename of</param>
/// <returns>The filename of the download</returns>
Task<String> GetFileName(Download download);
} }

View file

@ -1,10 +1,11 @@
using Microsoft.Extensions.Logging; using System.Diagnostics;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json; using Newtonsoft.Json;
using PremiumizeNET; using PremiumizeNET;
using RdtClient.Data.Enums; using RdtClient.Data.Enums;
using RdtClient.Data.Models.TorrentClient; using RdtClient.Data.Models.TorrentClient;
using RdtClient.Service.Helpers; using RdtClient.Service.Helpers;
using System.Web; using RdtClient.Data.Models.Data;
using Torrent = RdtClient.Data.Models.Data.Torrent; using Torrent = RdtClient.Data.Models.Data.Torrent;
namespace RdtClient.Service.Services.TorrentClients; namespace RdtClient.Service.Services.TorrentClients;
@ -197,7 +198,7 @@ public class PremiumizeTorrentClient(ILogger<PremiumizeTorrentClient> logger, IH
return torrent; return torrent;
} }
public async Task<IList<String>?> GetDownloadLinks(Torrent torrent) public async Task<IList<DownloadInfo>?> GetDownloadInfos(Torrent torrent)
{ {
if (torrent.RdId == null) if (torrent.RdId == null)
{ {
@ -212,7 +213,7 @@ public class PremiumizeTorrentClient(ILogger<PremiumizeTorrentClient> logger, IH
Log($"Found transfer {transfer.Name} ({transfer.Id})", torrent); Log($"Found transfer {transfer.Name} ({transfer.Id})", torrent);
var downloadLinks = await GetAllDownloadLinks(torrent, transfer.FolderId); var downloadInfos = await GetAllDownloadInfos(torrent, transfer.FolderId);
if (!String.IsNullOrWhiteSpace(transfer.FileId)) if (!String.IsNullOrWhiteSpace(transfer.FileId))
{ {
@ -225,34 +226,31 @@ public class PremiumizeTorrentClient(ILogger<PremiumizeTorrentClient> logger, IH
Log($"File {file.Name} ({file.Id}) does not contain a link", torrent); Log($"File {file.Name} ({file.Id}) does not contain a link", torrent);
} }
downloadLinks.Add(file.Link); downloadInfos.Add(new () {RestrictedLink = file.Link, FileName = file.Name });
} }
if (downloadLinks.Count == 0) if (downloadInfos.Count == 0)
{ {
Log($"No download links found for transfer {transfer.Name} ({transfer.Id})", torrent); Log($"No download links found for transfer {transfer.Name} ({transfer.Id})", torrent);
return null; return null;
} }
foreach (var link in downloadLinks) foreach (var info in downloadInfos)
{ {
Log($"Found {link}", torrent); Log($"Found {info.RestrictedLink}", torrent);
} }
return downloadLinks; return downloadInfos;
} }
public Task<String> GetFileName(String downloadUrl) /// <inheritdoc />
public Task<String> GetFileName(Download download)
{ {
if (String.IsNullOrWhiteSpace(downloadUrl)) // FileName is set in GetDownlaadInfos
{ Debug.Assert(download.FileName != null);
return Task.FromResult("");
}
var uri = new Uri(downloadUrl); return Task.FromResult(download.FileName);
return Task.FromResult(HttpUtility.UrlDecode(uri.Segments.Last()));
} }
private async Task<TorrentClientTorrent> GetInfo(String id) private async Task<TorrentClientTorrent> GetInfo(String id)
@ -263,13 +261,12 @@ public class PremiumizeTorrentClient(ILogger<PremiumizeTorrentClient> logger, IH
return Map(result); return Map(result);
} }
private async Task<List<String>> GetAllDownloadLinks(Torrent torrent, String folderId) private async Task<List<DownloadInfo>> GetAllDownloadInfos(Torrent torrent, String folderId)
{ {
var downloadLinks = new List<String>();
if (String.IsNullOrWhiteSpace(folderId)) if (String.IsNullOrWhiteSpace(folderId))
{ {
return downloadLinks; return [];
} }
var folder = await GetClient().Folder.ListAsync(folderId); var folder = await GetClient().Folder.ListAsync(folderId);
@ -277,11 +274,13 @@ public class PremiumizeTorrentClient(ILogger<PremiumizeTorrentClient> logger, IH
if (folder.Content == null) if (folder.Content == null)
{ {
Log($"Found no items in folder {folder.Name} ({folderId})", torrent); Log($"Found no items in folder {folder.Name} ({folderId})", torrent);
return downloadLinks; return [];
} }
Log($"Found {folder.Content.Count} items in folder {folder.Name} ({folderId})", torrent); Log($"Found {folder.Content.Count} items in folder {folder.Name} ({folderId})", torrent);
var downloadInfos = new List<DownloadInfo>();
foreach (var item in folder.Content) foreach (var item in folder.Content)
{ {
if (item.Type == "file") if (item.Type == "file")
@ -300,7 +299,7 @@ public class PremiumizeTorrentClient(ILogger<PremiumizeTorrentClient> logger, IH
Log($"Found item {item.Name} in folder {folder.Name} ({folderId})", torrent); Log($"Found item {item.Name} in folder {folder.Name} ({folderId})", torrent);
downloadLinks.Add(item.Link); downloadInfos.Add(new () { RestrictedLink = item.Link, FileName = item.Name});
} }
else if (item.Type == "folder") else if (item.Type == "folder")
{ {
@ -311,8 +310,8 @@ public class PremiumizeTorrentClient(ILogger<PremiumizeTorrentClient> logger, IH
} }
Log($"Found subfolder {item.Name} in {folder.Name} ({folderId}), searching subfolder for files", torrent); Log($"Found subfolder {item.Name} in {folder.Name} ({folderId}), searching subfolder for files", torrent);
var subDownloadLinks = await GetAllDownloadLinks(torrent, item.Id); var subDownloadLinks = await GetAllDownloadInfos(torrent, item.Id);
downloadLinks.AddRange(subDownloadLinks); downloadInfos.AddRange(subDownloadLinks);
} }
else else
{ {
@ -320,7 +319,7 @@ public class PremiumizeTorrentClient(ILogger<PremiumizeTorrentClient> logger, IH
} }
} }
return downloadLinks; return downloadInfos;
} }
private void Log(String message, Torrent? torrent = null) private void Log(String message, Torrent? torrent = null)

View file

@ -3,8 +3,11 @@ using Microsoft.Extensions.Logging;
using Newtonsoft.Json; using Newtonsoft.Json;
using RDNET; using RDNET;
using RdtClient.Data.Enums; using RdtClient.Data.Enums;
using RdtClient.Data.Models.Data;
using RdtClient.Data.Models.TorrentClient; using RdtClient.Data.Models.TorrentClient;
using RdtClient.Service.Helpers; using RdtClient.Service.Helpers;
using Download = RdtClient.Data.Models.Data.Download;
using Torrent = RDNET.Torrent;
namespace RdtClient.Service.Services.TorrentClients; namespace RdtClient.Service.Services.TorrentClients;
@ -265,7 +268,7 @@ public class RealDebridTorrentClient(ILogger<RealDebridTorrentClient> logger, IH
return torrent; return torrent;
} }
public async Task<IList<String>?> GetDownloadLinks(Data.Models.Data.Torrent torrent) public async Task<IList<DownloadInfo>?> GetDownloadInfos(Data.Models.Data.Torrent torrent)
{ {
if (torrent.RdId == null) if (torrent.RdId == null)
{ {
@ -279,7 +282,14 @@ public class RealDebridTorrentClient(ILogger<RealDebridTorrentClient> logger, IH
return null; return null;
} }
var downloadLinks = rdTorrent.Links.Where(m => !String.IsNullOrWhiteSpace(m)).ToList(); var downloadLinks = rdTorrent.Links
.Where(m => !String.IsNullOrWhiteSpace(m))
.Select(l => new DownloadInfo
{
RestrictedLink = l,
FileName = null
})
.ToList();
Log($"Found {downloadLinks.Count} links", torrent); Log($"Found {downloadLinks.Count} links", torrent);
@ -326,14 +336,15 @@ public class RealDebridTorrentClient(ILogger<RealDebridTorrentClient> logger, IH
return null; return null;
} }
public Task<String> GetFileName(String downloadUrl) /// <inheritdoc />
public Task<String> GetFileName(Download download)
{ {
if (String.IsNullOrWhiteSpace(downloadUrl)) if (String.IsNullOrWhiteSpace(download.Link))
{ {
return Task.FromResult(""); return Task.FromResult("");
} }
var uri = new Uri(downloadUrl); var uri = new Uri(download.Link);
return Task.FromResult(HttpUtility.UrlDecode(uri.Segments.Last())); return Task.FromResult(HttpUtility.UrlDecode(uri.Segments.Last()));
} }

View file

@ -1,10 +1,10 @@
using System.Reflection; using System.Reflection;
using System.Diagnostics;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Newtonsoft.Json; using Newtonsoft.Json;
using TorBoxNET; using TorBoxNET;
using RdtClient.Data.Enums; using RdtClient.Data.Enums;
using RdtClient.Data.Models.TorrentClient; using RdtClient.Data.Models.TorrentClient;
using System.Web;
using RdtClient.Data.Models.Data; using RdtClient.Data.Models.Data;
using RdtClient.Service.Helpers; using RdtClient.Service.Helpers;
@ -285,7 +285,7 @@ public class TorBoxTorrentClient(ILogger<TorBoxTorrentClient> logger, IHttpClien
return torrent; return torrent;
} }
public async Task<IList<String>?> GetDownloadLinks(Torrent torrent) public async Task<IList<DownloadInfo>?> GetDownloadInfos(Torrent torrent)
{ {
var torrentId = await GetClient().Torrents.GetHashInfoAsync(torrent.Hash, skipCache: true); var torrentId = await GetClient().Torrents.GetHashInfoAsync(torrent.Hash, skipCache: true);
var downloadableFiles = torrent.Files.Where(file => fileFilter.IsDownloadable(torrent, file.Path, file.Bytes)).ToList(); var downloadableFiles = torrent.Files.Where(file => fileFilter.IsDownloadable(torrent, file.Path, file.Bytes)).ToList();
@ -293,51 +293,36 @@ public class TorBoxTorrentClient(ILogger<TorBoxTorrentClient> logger, IHttpClien
if (downloadableFiles.Count == torrent.Files.Count && torrent.DownloadClient != Data.Enums.DownloadClient.Symlink) if (downloadableFiles.Count == torrent.Files.Count && torrent.DownloadClient != Data.Enums.DownloadClient.Symlink)
{ {
logger.LogDebug("Downloading files from TorBox as a zip."); logger.LogDebug("Downloading files from TorBox as a zip.");
return [$"https://torbox.app/fakedl/{torrentId?.Id}/zip"];
return
[
new DownloadInfo
{
RestrictedLink = $"https://torbox.app/fakedl/{torrentId?.Id}/zip",
FileName = $"{torrent.RdName}.zip"
}
];
} }
else else
{ {
logger.LogDebug("Downloading files from TorBox individually."); logger.LogDebug("Downloading files from TorBox individually.");
return downloadableFiles.Select(file => $"https://torbox.app/fakedl/{torrentId?.Id}/{file.Id}").ToList();
return downloadableFiles.Select(file => new DownloadInfo
{
RestrictedLink = $"https://torbox.app/fakedl/{torrentId?.Id}/{file.Id}",
FileName = Path.GetFileName(file.Path)
})
.ToList();
} }
} }
public async Task<String> GetFileName(String downloadUrl) /// <inheritdoc />
public Task<String> GetFileName(Download download)
{ {
if (String.IsNullOrWhiteSpace(downloadUrl)) // FileName is set in GetDownlaadInfos
{ Debug.Assert(download.FileName != null);
return "";
}
var uri = new Uri(downloadUrl); return Task.FromResult(download.FileName);
using (HttpClient client = new())
{
client.Timeout = TimeSpan.FromSeconds(Settings.Get.Provider.Timeout);
var request = new HttpRequestMessage(HttpMethod.Head, uri);
var response = await client.SendAsync(request);
if (response.Content.Headers.ContentDisposition != null)
{
var fileName = response.Content.Headers.ContentDisposition.FileName;
if (!String.IsNullOrWhiteSpace(fileName))
{
return fileName.Trim('"');
}
}
else
{
if (response.Content.Headers.ContentType?.MediaType == "application/zip")
{
return $"download-{new Random().Next(1, 10001)}.zip";
}
else
{
logger.LogDebug($"Failed to get filename for URI {downloadUrl}");
}
}
}
return HttpUtility.UrlDecode(uri.Segments.Last());
} }
private DateTimeOffset? ChangeTimeZone(DateTimeOffset? dateTimeOffset) private DateTimeOffset? ChangeTimeZone(DateTimeOffset? dateTimeOffset)

View file

@ -362,8 +362,11 @@ public class TorrentRunner(ILogger<TorrentRunner> logger, Torrents torrents, Dow
var downloadLink = await torrents.UnrestrictLink(download.DownloadId); var downloadLink = await torrents.UnrestrictLink(download.DownloadId);
download.Link = downloadLink; download.Link = downloadLink;
var fileName = await torrents.RetrieveFileName(download.DownloadId); if (download.FileName == null)
download.FileName = fileName; {
var fileName = await torrents.RetrieveFileName(download.DownloadId);
download.FileName = fileName;
}
} }
} }
catch (Exception ex) catch (Exception ex)

View file

@ -235,14 +235,14 @@ public class Torrents(
return; return;
} }
var downloadLinks = await TorrentClient.GetDownloadLinks(torrent); var downloadInfos = await TorrentClient.GetDownloadInfos(torrent);
if (downloadLinks == null) if (downloadInfos == null)
{ {
return; return;
} }
if (downloadLinks.Count == 0) if (downloadInfos.Count == 0)
{ {
logger.LogInformation("All files excluded by filters (IncludeRegex: {includeRegex}, ExcludeRegex: {excludeRegex}, DownloadMinSize: {downloadMinSize} {torrentInfo}", logger.LogInformation("All files excluded by filters (IncludeRegex: {includeRegex}, ExcludeRegex: {excludeRegex}, DownloadMinSize: {downloadMinSize} {torrentInfo}",
torrent.IncludeRegex, torrent.IncludeRegex,
@ -256,14 +256,14 @@ public class Torrents(
return; return;
} }
foreach (var downloadLink in downloadLinks) foreach (var downloadInfo in downloadInfos)
{ {
// Make sure downloads don't get added multiple times // Make sure downloads don't get added multiple times
var downloadExists = await downloads.Get(torrent.TorrentId, downloadLink); var downloadExists = await downloads.Get(torrent.TorrentId, downloadInfo.RestrictedLink);
if (downloadExists == null && !String.IsNullOrWhiteSpace(downloadLink)) if (downloadExists == null && !String.IsNullOrWhiteSpace(downloadInfo.RestrictedLink))
{ {
await downloads.Add(torrent.TorrentId, downloadLink); await downloads.Add(torrent.TorrentId, downloadInfo);
} }
} }
} }
@ -390,13 +390,17 @@ public class Torrents(
return unrestrictedLink; return unrestrictedLink;
} }
/// <summary>
/// To be called only when <see cref="Data.Models.Data.Download" />.<see cref="Data.Models.Data.Download.FileName" /> is not set by
/// <see cref="ITorrentClient.GetDownloadInfos" />
/// </summary>
public async Task<String> RetrieveFileName(Guid downloadId) public async Task<String> RetrieveFileName(Guid downloadId)
{ {
var download = await downloads.GetById(downloadId) ?? throw new($"Download with ID {downloadId} not found"); var download = await downloads.GetById(downloadId) ?? throw new($"Download with ID {downloadId} not found");
Log($"Retrieving filename for", download, download.Torrent); Log($"Retrieving filename for", download, download.Torrent);
var fileName = await TorrentClient.GetFileName(download.Link!); var fileName = await TorrentClient.GetFileName(download!);
await downloads.UpdateFileName(downloadId, fileName); await downloads.UpdateFileName(downloadId, fileName);