Skip to content

Commit f00b2f0

Browse files
committed
Implement Torrent endpoint
1 parent c70dcbb commit f00b2f0

File tree

10 files changed

+855
-4
lines changed

10 files changed

+855
-4
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ namespace ConsoleApp
4545
IEnumerable<DownloadedTorrent> hitAndRuns = nCore.HitAndRuns.List()
4646
.GetAwaiter().GetResult();
4747

48+
Torrent firstTorrent = nCore.Torrent.Get(2)
49+
.GetAwaiter().GetResult();
50+
4851
Console.Read();
4952
}
5053
}

samples/ConsoleApp/Program.cs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,20 +27,28 @@ public static void Main(string[] args)
2727
{
2828
UserConfig userConfig = LoadCredentialsFromFile();
2929

30-
NcoreClient client = GetAuthenticatedClientFor(userConfig)
30+
NcoreClient nCore = GetAuthenticatedClientFor(userConfig)
3131
.ConfigureAwait(false).GetAwaiter().GetResult();
3232

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

35-
ISearchResultContainer resultContainer = client.Search.List()
35+
ISearchResultContainer resultContainer = nCore.Search.List()
3636
.GetAwaiter().GetResult();
3737

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

40-
ISearchResultContainer innaResults = client.Search.For("Inna")
40+
ISearchResultContainer innaResults = nCore.Search.For("Inna")
4141
.ConfigureAwait(false).GetAwaiter().GetResult();
4242

43-
IEnumerable<HitAndRunTorrent> hitAndRunTorrents = client.HitAndRuns.List()
43+
IEnumerable<HitAndRunTorrent> hitAndRunTorrents = nCore.HitAndRuns.List()
44+
.ConfigureAwait(false).GetAwaiter().GetResult();
45+
46+
Torrent torrent = nCore.Torrent.Get(1683491)
47+
.ConfigureAwait(false).GetAwaiter().GetResult();
48+
49+
FileStream destination = new FileStream($"{torrent.UploadName}.torrent", FileMode.CreateNew);
50+
51+
torrent.DownloadAsFileTo(destination)
4452
.ConfigureAwait(false).GetAwaiter().GetResult();
4553

4654
Console.Read();
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using System.Threading.Tasks;
2+
using PoLaKoSz.Ncore.Models;
3+
4+
namespace PoLaKoSz.Ncore.EndPoints
5+
{
6+
/// <summary>
7+
/// Provides access to a specific torrent.
8+
/// </summary>
9+
public interface ITorrentEndPoint
10+
{
11+
/// <summary>
12+
/// Gets the details of a torrent.
13+
/// </summary>
14+
/// <param name="id">The requested torrent unique ID.</param>
15+
/// <returns>Async <see cref="Task" />.</returns>
16+
Task<Torrent> Get(int id);
17+
18+
/// <summary>
19+
/// Gets the details of a torrent.
20+
/// </summary>
21+
/// <param name="torrent">The requested torrent.</param>
22+
/// <returns>Async <see cref="Task" />.</returns>
23+
Task<Torrent> Get(ISearchResultTorrent torrent);
24+
25+
/// <summary>
26+
/// Gets the details of a torrent.
27+
/// </summary>
28+
/// <param name="torrent">The requested torrent.</param>
29+
/// <returns>Async <see cref="Task" />.</returns>
30+
Task<Torrent> Get(HitAndRunTorrent torrent);
31+
}
32+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using System.Net.Http;
2+
using System.Threading.Tasks;
3+
using PoLaKoSz.Ncore.Models;
4+
using PoLaKoSz.Ncore.Parsers;
5+
6+
namespace PoLaKoSz.Ncore.EndPoints
7+
{
8+
/// <inheritdoc cref="ITorrentEndPoint" />
9+
internal class TorrentEndPoint : ProtectedEndPoint, ITorrentEndPoint
10+
{
11+
private readonly TorrentParser _parser;
12+
13+
internal TorrentEndPoint(HttpClient client, LoginParser authChecker)
14+
: base(client, authChecker)
15+
{
16+
_parser = new TorrentParser(client);
17+
}
18+
19+
/// <inheritdoc />
20+
public async Task<Torrent> Get(int id)
21+
{
22+
string html = await GetStringAsync($"/torrents.php?action=details&id={id}").ConfigureAwait(false);
23+
24+
return await _parser.ExtractResultFrom(html).ConfigureAwait(false);
25+
}
26+
27+
/// <inheritdoc />
28+
public Task<Torrent> Get(ISearchResultTorrent torrent)
29+
{
30+
return Get(torrent.ID);
31+
}
32+
33+
/// <inheritdoc />
34+
public Task<Torrent> Get(HitAndRunTorrent torrent)
35+
{
36+
return Get(torrent.ID);
37+
}
38+
}
39+
}

src/PoLaKoSz.Ncore/Models/Torrent.cs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
using System;
2+
using System.IO;
3+
using System.Net.Http;
4+
using System.Threading.Tasks;
5+
6+
namespace PoLaKoSz.Ncore.Models
7+
{
8+
/// <summary>
9+
/// Class to hold detailed infromation about an nCore torrent.
10+
/// </summary>
11+
public class Torrent
12+
{
13+
private readonly HttpClient _client;
14+
15+
/// <summary>
16+
/// Initialize a new instance.
17+
/// </summary>
18+
/// <param name="id">The unique ID given by nCore.</param>
19+
/// <param name="uploadName">The name given by the uploader.</param>
20+
/// <param name="uploadDate">The local time when this torrent was uploaded.</param>
21+
/// <param name="downloadURL">The absolute path to the .torrent file.</param>
22+
/// <param name="client">The <see cref="HttpClient" /> to download the .torrent file.</param>
23+
public Torrent(
24+
int id,
25+
string uploadName,
26+
DateTime uploadDate,
27+
Uri downloadURL,
28+
HttpClient client)
29+
{
30+
UploadDate = uploadDate;
31+
ID = id;
32+
UploadName = uploadName;
33+
DownloadURL = downloadURL;
34+
_client = client;
35+
}
36+
37+
/// <summary>
38+
/// Gets the torrent unique ID given by nCore.
39+
/// </summary>
40+
public int ID { get; }
41+
42+
/// <summary>
43+
/// Gets the uploadname given by the uploader.
44+
/// </summary>
45+
public string UploadName { get; }
46+
47+
/// <summary>
48+
/// Gets when this torrent was uploaded to nCore (local time).
49+
/// </summary>
50+
public DateTime UploadDate { get; }
51+
52+
/// <summary>
53+
/// Gets the absolute path for the .torrent file.
54+
/// </summary>
55+
public Uri DownloadURL { get; }
56+
57+
/// <summary>
58+
/// Downloads this torrent .torrent file.
59+
/// </summary>
60+
/// <param name="destination">The path where the file should be placed.</param>
61+
/// <returns>Async <see cref="Task" />.</returns>
62+
public async Task DownloadAsFileTo(FileStream destination)
63+
{
64+
var response = await _client.GetAsync(DownloadURL).ConfigureAwait(false);
65+
66+
await response.Content.CopyToAsync(destination).ConfigureAwait(false);
67+
}
68+
}
69+
}

src/PoLaKoSz.Ncore/NcoreClient.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public NcoreClient(HttpClientHandler messageHandler)
4141
Login = new LoginEndPoint(client, cookies, authChecker, userConfig);
4242
Search = new SearchEndPoint(client, authChecker, userConfig);
4343
HitAndRuns = new HitAndRunEndPoint(client, authChecker);
44+
Torrent = new TorrentEndPoint(client, authChecker);
4445
}
4546

4647
/// <summary>
@@ -57,5 +58,10 @@ public NcoreClient(HttpClientHandler messageHandler)
5758
/// Gets an access point to Hit'n'Run page.
5859
/// </summary>
5960
public IHitAndRunEndPoint HitAndRuns { get; }
61+
62+
/// <summary>
63+
/// Gets an access point to a <see cref="Torrent" /> resource.
64+
/// </summary>
65+
public ITorrentEndPoint Torrent { get; }
6066
}
6167
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
using System;
2+
using System.Net.Http;
3+
using System.Threading.Tasks;
4+
using AngleSharp;
5+
using AngleSharp.Dom;
6+
using PoLaKoSz.Ncore.Exceptions;
7+
using PoLaKoSz.Ncore.Models;
8+
9+
namespace PoLaKoSz.Ncore.Parsers
10+
{
11+
internal class TorrentParser
12+
{
13+
private static string torrentUrlShema = "torrents.php?action=addnews&id=";
14+
private readonly HttpClient _client;
15+
16+
public TorrentParser(HttpClient client)
17+
{
18+
_client = client;
19+
}
20+
21+
/// <exception cref="DeprecatedWrapperException"></exception>
22+
internal async Task<Torrent> ExtractResultFrom(string html)
23+
{
24+
var context = BrowsingContext.New(Configuration.Default);
25+
IDocument document = await context.OpenAsync(req => req.Content(html)).ConfigureAwait(false);
26+
27+
IElement uploadNameNode = document.QuerySelector("div#details1 div.fobox_tartalom div.torrent_reszletek_cim");
28+
if (uploadNameNode == null)
29+
{
30+
throw new DeprecatedWrapperException("Couldn't find upload name!", document.DocumentElement);
31+
}
32+
33+
IElement uploadDateTitleNode = document.QuerySelector("#details1 > div.fobox_tartalom > div.torrent_reszletek > div.torrent_col1 > div:nth-child(3)");
34+
if (uploadDateTitleNode == null)
35+
{
36+
throw new DeprecatedWrapperException("Couldn't find upload date title!", document.DocumentElement);
37+
}
38+
39+
IElement uploadDateNode = uploadDateTitleNode.NextElementSibling;
40+
if (uploadDateNode == null)
41+
{
42+
throw new DeprecatedWrapperException("Couldn't find the upload date!", uploadDateTitleNode.ParentElement);
43+
}
44+
45+
if (!DateTime.TryParse(uploadDateNode.TextContent, out DateTime uploadDate))
46+
{
47+
throw new DeprecatedWrapperException("Invalid upload date!", uploadDateNode);
48+
}
49+
50+
return new Torrent(
51+
ParseID(document),
52+
uploadNameNode.TextContent,
53+
uploadDate,
54+
ParseTorrentFileUrl(document),
55+
_client);
56+
}
57+
58+
private int ParseID(IDocument document)
59+
{
60+
IElement idNode = document.QuerySelector("div#details1 div.fobox_tartalom div.torrent_reszletek_konyvjelzo a:nth-child(2)");
61+
if (idNode == null)
62+
{
63+
throw new DeprecatedWrapperException("Couldn't find node to extract torrent ID!", document.DocumentElement);
64+
}
65+
66+
if (!idNode.HasAttribute("href"))
67+
{
68+
throw new DeprecatedWrapperException("Node which should contain the ID is invalid!", idNode);
69+
}
70+
71+
string href = idNode.GetAttribute("href");
72+
if (href.Length <= torrentUrlShema.Length)
73+
{
74+
throw new DeprecatedWrapperException("Node which should contain the ID is invalid!", idNode);
75+
}
76+
77+
string id = href.Substring(torrentUrlShema.Length);
78+
id = id.Substring(0, id.IndexOf("&"));
79+
80+
return int.Parse(id);
81+
}
82+
83+
private Uri ParseTorrentFileUrl(IDocument document)
84+
{
85+
IElement anchorNode = document.QuerySelector("div#details1 div.fobox_tartalom div.download a");
86+
if (anchorNode == null)
87+
{
88+
throw new DeprecatedWrapperException("Couldn't find node to extract torrent file URL!", document.DocumentElement);
89+
}
90+
91+
if (!anchorNode.HasAttribute("href"))
92+
{
93+
throw new DeprecatedWrapperException("Invalidd torrent file URL node!", anchorNode);
94+
}
95+
96+
return new Uri($"{_client.BaseAddress}{anchorNode.GetAttribute("href")}");
97+
}
98+
}
99+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
using NUnit.Framework;
4+
using PoLaKoSz.Ncore.EndPoints;
5+
using PoLaKoSz.Ncore.Models;
6+
7+
namespace PoLaKoSz.Ncore.Tests.Integration.EndPoints
8+
{
9+
[TestFixture]
10+
internal class TorrentEndPointTests : IntegrationTestFixture
11+
{
12+
private ITorrentEndPoint endPoint;
13+
14+
public TorrentEndPointTests()
15+
: base("TorrentEndPoint")
16+
{
17+
}
18+
19+
[SetUp]
20+
public void SetUp()
21+
{
22+
endPoint = base.GetAuthenticatedClient().Torrent;
23+
}
24+
25+
[Test]
26+
public async Task GetParseIdCorrectly()
27+
{
28+
SetServerResponse("movie");
29+
30+
Torrent torrent = await endPoint.Get(-1).ConfigureAwait(false);
31+
32+
Assert.That(torrent.ID, Is.EqualTo(1683491));
33+
}
34+
35+
[Test]
36+
public async Task GetParseUploadNameCorrectly()
37+
{
38+
SetServerResponse("movie");
39+
40+
Torrent torrent = await endPoint.Get(-1).ConfigureAwait(false);
41+
42+
Assert.That(torrent.UploadName, Is.EqualTo("The.Purge.Anarchy.2014.BDRip.XviD.Hungarian-nCORE"));
43+
}
44+
45+
[Test]
46+
public async Task GetParseDownloadUrlCorrectly()
47+
{
48+
SetServerResponse("movie");
49+
50+
Torrent torrent = await endPoint.Get(-1).ConfigureAwait(false);
51+
52+
Assert.That(torrent.DownloadURL, Is.EqualTo(new Uri("https://ncore.cc/torrents.php?action=download&id=1683491&key=b6d64d585e5615c0721c1b008a7473bc")));
53+
}
54+
55+
[Test]
56+
public async Task GetParseUploadDateCorrectly()
57+
{
58+
SetServerResponse("movie");
59+
60+
Torrent torrent = await endPoint.Get(-1).ConfigureAwait(false);
61+
62+
Assert.That(torrent.UploadDate, Is.EqualTo(new DateTime(2014, 11, 09, 19, 11, 29, DateTimeKind.Local)));
63+
}
64+
}
65+
}

tests/Integration/Integration.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@
4141
<None Update="StaticResources\HitAndRunEndPoint\multiple-torrents.html">
4242
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
4343
</None>
44+
<None Update="StaticResources\TorrentEndPoint\movie.html">
45+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
46+
</None>
4447
</ItemGroup>
4548

4649
</Project>

0 commit comments

Comments
 (0)