Skip to content

Commit

Permalink
Implement Torrent endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
PoLaKoSz committed May 13, 2020
1 parent c70dcbb commit f00b2f0
Show file tree
Hide file tree
Showing 10 changed files with 855 additions and 4 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ namespace ConsoleApp
IEnumerable<DownloadedTorrent> hitAndRuns = nCore.HitAndRuns.List()
.GetAwaiter().GetResult();

Torrent firstTorrent = nCore.Torrent.Get(2)
.GetAwaiter().GetResult();

Console.Read();
}
}
Expand Down
16 changes: 12 additions & 4 deletions samples/ConsoleApp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,28 @@ public static void Main(string[] args)
{
UserConfig userConfig = LoadCredentialsFromFile();

NcoreClient client = GetAuthenticatedClientFor(userConfig)
NcoreClient nCore = GetAuthenticatedClientFor(userConfig)
.ConfigureAwait(false).GetAwaiter().GetResult();

Thread.Sleep(TimeSpan.FromSeconds(2));

ISearchResultContainer resultContainer = client.Search.List()
ISearchResultContainer resultContainer = nCore.Search.List()
.GetAwaiter().GetResult();

Thread.Sleep(TimeSpan.FromSeconds(2));

ISearchResultContainer innaResults = client.Search.For("Inna")
ISearchResultContainer innaResults = nCore.Search.For("Inna")
.ConfigureAwait(false).GetAwaiter().GetResult();

IEnumerable<HitAndRunTorrent> hitAndRunTorrents = client.HitAndRuns.List()
IEnumerable<HitAndRunTorrent> hitAndRunTorrents = nCore.HitAndRuns.List()
.ConfigureAwait(false).GetAwaiter().GetResult();

Torrent torrent = nCore.Torrent.Get(1683491)
.ConfigureAwait(false).GetAwaiter().GetResult();

FileStream destination = new FileStream($"{torrent.UploadName}.torrent", FileMode.CreateNew);

torrent.DownloadAsFileTo(destination)
.ConfigureAwait(false).GetAwaiter().GetResult();

Console.Read();
Expand Down
32 changes: 32 additions & 0 deletions src/PoLaKoSz.Ncore/EndPoints/ITorrentEndPoint.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System.Threading.Tasks;
using PoLaKoSz.Ncore.Models;

namespace PoLaKoSz.Ncore.EndPoints
{
/// <summary>
/// Provides access to a specific torrent.
/// </summary>
public interface ITorrentEndPoint
{
/// <summary>
/// Gets the details of a torrent.
/// </summary>
/// <param name="id">The requested torrent unique ID.</param>
/// <returns>Async <see cref="Task" />.</returns>
Task<Torrent> Get(int id);

/// <summary>
/// Gets the details of a torrent.
/// </summary>
/// <param name="torrent">The requested torrent.</param>
/// <returns>Async <see cref="Task" />.</returns>
Task<Torrent> Get(ISearchResultTorrent torrent);

/// <summary>
/// Gets the details of a torrent.
/// </summary>
/// <param name="torrent">The requested torrent.</param>
/// <returns>Async <see cref="Task" />.</returns>
Task<Torrent> Get(HitAndRunTorrent torrent);
}
}
39 changes: 39 additions & 0 deletions src/PoLaKoSz.Ncore/EndPoints/TorrentEndPoint.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System.Net.Http;
using System.Threading.Tasks;
using PoLaKoSz.Ncore.Models;
using PoLaKoSz.Ncore.Parsers;

namespace PoLaKoSz.Ncore.EndPoints
{
/// <inheritdoc cref="ITorrentEndPoint" />
internal class TorrentEndPoint : ProtectedEndPoint, ITorrentEndPoint
{
private readonly TorrentParser _parser;

internal TorrentEndPoint(HttpClient client, LoginParser authChecker)
: base(client, authChecker)
{
_parser = new TorrentParser(client);
}

/// <inheritdoc />
public async Task<Torrent> Get(int id)
{
string html = await GetStringAsync($"/torrents.php?action=details&id={id}").ConfigureAwait(false);

return await _parser.ExtractResultFrom(html).ConfigureAwait(false);
}

/// <inheritdoc />
public Task<Torrent> Get(ISearchResultTorrent torrent)
{
return Get(torrent.ID);
}

/// <inheritdoc />
public Task<Torrent> Get(HitAndRunTorrent torrent)
{
return Get(torrent.ID);
}
}
}
69 changes: 69 additions & 0 deletions src/PoLaKoSz.Ncore/Models/Torrent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using System;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;

namespace PoLaKoSz.Ncore.Models
{
/// <summary>
/// Class to hold detailed infromation about an nCore torrent.
/// </summary>
public class Torrent
{
private readonly HttpClient _client;

/// <summary>
/// Initialize a new instance.
/// </summary>
/// <param name="id">The unique ID given by nCore.</param>
/// <param name="uploadName">The name given by the uploader.</param>
/// <param name="uploadDate">The local time when this torrent was uploaded.</param>
/// <param name="downloadURL">The absolute path to the .torrent file.</param>
/// <param name="client">The <see cref="HttpClient" /> to download the .torrent file.</param>
public Torrent(
int id,
string uploadName,
DateTime uploadDate,
Uri downloadURL,
HttpClient client)
{
UploadDate = uploadDate;
ID = id;
UploadName = uploadName;
DownloadURL = downloadURL;
_client = client;
}

/// <summary>
/// Gets the torrent unique ID given by nCore.
/// </summary>
public int ID { get; }

/// <summary>
/// Gets the uploadname given by the uploader.
/// </summary>
public string UploadName { get; }

/// <summary>
/// Gets when this torrent was uploaded to nCore (local time).
/// </summary>
public DateTime UploadDate { get; }

/// <summary>
/// Gets the absolute path for the .torrent file.
/// </summary>
public Uri DownloadURL { get; }

/// <summary>
/// Downloads this torrent .torrent file.
/// </summary>
/// <param name="destination">The path where the file should be placed.</param>
/// <returns>Async <see cref="Task" />.</returns>
public async Task DownloadAsFileTo(FileStream destination)
{
var response = await _client.GetAsync(DownloadURL).ConfigureAwait(false);

await response.Content.CopyToAsync(destination).ConfigureAwait(false);
}
}
}
6 changes: 6 additions & 0 deletions src/PoLaKoSz.Ncore/NcoreClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public NcoreClient(HttpClientHandler messageHandler)
Login = new LoginEndPoint(client, cookies, authChecker, userConfig);
Search = new SearchEndPoint(client, authChecker, userConfig);
HitAndRuns = new HitAndRunEndPoint(client, authChecker);
Torrent = new TorrentEndPoint(client, authChecker);
}

/// <summary>
Expand All @@ -57,5 +58,10 @@ public NcoreClient(HttpClientHandler messageHandler)
/// Gets an access point to Hit'n'Run page.
/// </summary>
public IHitAndRunEndPoint HitAndRuns { get; }

/// <summary>
/// Gets an access point to a <see cref="Torrent" /> resource.
/// </summary>
public ITorrentEndPoint Torrent { get; }
}
}
99 changes: 99 additions & 0 deletions src/PoLaKoSz.Ncore/Parsers/TorrentParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;
using AngleSharp;
using AngleSharp.Dom;
using PoLaKoSz.Ncore.Exceptions;
using PoLaKoSz.Ncore.Models;

namespace PoLaKoSz.Ncore.Parsers
{
internal class TorrentParser
{
private static string torrentUrlShema = "torrents.php?action=addnews&id=";
private readonly HttpClient _client;

public TorrentParser(HttpClient client)
{
_client = client;
}

/// <exception cref="DeprecatedWrapperException"></exception>
internal async Task<Torrent> ExtractResultFrom(string html)
{
var context = BrowsingContext.New(Configuration.Default);
IDocument document = await context.OpenAsync(req => req.Content(html)).ConfigureAwait(false);

IElement uploadNameNode = document.QuerySelector("div#details1 div.fobox_tartalom div.torrent_reszletek_cim");
if (uploadNameNode == null)
{
throw new DeprecatedWrapperException("Couldn't find upload name!", document.DocumentElement);
}

IElement uploadDateTitleNode = document.QuerySelector("#details1 > div.fobox_tartalom > div.torrent_reszletek > div.torrent_col1 > div:nth-child(3)");
if (uploadDateTitleNode == null)
{
throw new DeprecatedWrapperException("Couldn't find upload date title!", document.DocumentElement);
}

IElement uploadDateNode = uploadDateTitleNode.NextElementSibling;
if (uploadDateNode == null)
{
throw new DeprecatedWrapperException("Couldn't find the upload date!", uploadDateTitleNode.ParentElement);
}

if (!DateTime.TryParse(uploadDateNode.TextContent, out DateTime uploadDate))
{
throw new DeprecatedWrapperException("Invalid upload date!", uploadDateNode);
}

return new Torrent(
ParseID(document),
uploadNameNode.TextContent,
uploadDate,
ParseTorrentFileUrl(document),
_client);
}

private int ParseID(IDocument document)
{
IElement idNode = document.QuerySelector("div#details1 div.fobox_tartalom div.torrent_reszletek_konyvjelzo a:nth-child(2)");
if (idNode == null)
{
throw new DeprecatedWrapperException("Couldn't find node to extract torrent ID!", document.DocumentElement);
}

if (!idNode.HasAttribute("href"))
{
throw new DeprecatedWrapperException("Node which should contain the ID is invalid!", idNode);
}

string href = idNode.GetAttribute("href");
if (href.Length <= torrentUrlShema.Length)
{
throw new DeprecatedWrapperException("Node which should contain the ID is invalid!", idNode);
}

string id = href.Substring(torrentUrlShema.Length);
id = id.Substring(0, id.IndexOf("&"));

return int.Parse(id);
}

private Uri ParseTorrentFileUrl(IDocument document)
{
IElement anchorNode = document.QuerySelector("div#details1 div.fobox_tartalom div.download a");
if (anchorNode == null)
{
throw new DeprecatedWrapperException("Couldn't find node to extract torrent file URL!", document.DocumentElement);
}

if (!anchorNode.HasAttribute("href"))
{
throw new DeprecatedWrapperException("Invalidd torrent file URL node!", anchorNode);
}

return new Uri($"{_client.BaseAddress}{anchorNode.GetAttribute("href")}");
}
}
}
65 changes: 65 additions & 0 deletions tests/Integration/EndPoints/TorrentEndPointTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using System;
using System.Threading.Tasks;
using NUnit.Framework;
using PoLaKoSz.Ncore.EndPoints;
using PoLaKoSz.Ncore.Models;

namespace PoLaKoSz.Ncore.Tests.Integration.EndPoints
{
[TestFixture]
internal class TorrentEndPointTests : IntegrationTestFixture
{
private ITorrentEndPoint endPoint;

public TorrentEndPointTests()
: base("TorrentEndPoint")
{
}

[SetUp]
public void SetUp()
{
endPoint = base.GetAuthenticatedClient().Torrent;
}

[Test]
public async Task GetParseIdCorrectly()
{
SetServerResponse("movie");

Torrent torrent = await endPoint.Get(-1).ConfigureAwait(false);

Assert.That(torrent.ID, Is.EqualTo(1683491));
}

[Test]
public async Task GetParseUploadNameCorrectly()
{
SetServerResponse("movie");

Torrent torrent = await endPoint.Get(-1).ConfigureAwait(false);

Assert.That(torrent.UploadName, Is.EqualTo("The.Purge.Anarchy.2014.BDRip.XviD.Hungarian-nCORE"));
}

[Test]
public async Task GetParseDownloadUrlCorrectly()
{
SetServerResponse("movie");

Torrent torrent = await endPoint.Get(-1).ConfigureAwait(false);

Assert.That(torrent.DownloadURL, Is.EqualTo(new Uri("https://ncore.cc/torrents.php?action=download&id=1683491&key=b6d64d585e5615c0721c1b008a7473bc")));
}

[Test]
public async Task GetParseUploadDateCorrectly()
{
SetServerResponse("movie");

Torrent torrent = await endPoint.Get(-1).ConfigureAwait(false);

Assert.That(torrent.UploadDate, Is.EqualTo(new DateTime(2014, 11, 09, 19, 11, 29, DateTimeKind.Local)));
}
}
}
3 changes: 3 additions & 0 deletions tests/Integration/Integration.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@
<None Update="StaticResources\HitAndRunEndPoint\multiple-torrents.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="StaticResources\TorrentEndPoint\movie.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
Loading

0 comments on commit f00b2f0

Please sign in to comment.