Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added src/Jackett/Content/logos/danishbits.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/Jackett/Controllers/AdminController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ private void ConfigureCacheResults(List<TrackerCacheResult> results)
foreach (var result in results)
{
var link = result.Link;
result.Link = serverService.ConvertToProxyLink(link, serverUrl, result.TrackerId);
result.Link = serverService.ConvertToProxyLink(link, serverUrl, result.TrackerId, "dl", result.Title + ".torrent");
if (result.Link != null && result.Link.Scheme != "magnet" && !string.IsNullOrWhiteSpace(Engine.Server.Config.BlackholeDir))
result.BlackholeLink = serverService.ConvertToProxyLink(link, serverUrl, result.TrackerId, "bh", string.Empty);

Expand Down
11 changes: 10 additions & 1 deletion src/Jackett/Controllers/DownloadController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
using MonoTorrent.BEncoding;

namespace Jackett.Controllers
{
Expand All @@ -29,7 +30,7 @@ public DownloadController(IIndexerManagerService i, Logger l, IServerService s)
}

[HttpGet]
public async Task<HttpResponseMessage> Download(string indexerID, string path, string apikey)
public async Task<HttpResponseMessage> Download(string indexerID, string path, string apikey, string file)
{
try
{
Expand All @@ -51,9 +52,17 @@ public async Task<HttpResponseMessage> Download(string indexerID, string path, s

var downloadBytes = await indexer.Download(target);

// This will fix torrents where the keys are not sorted, and thereby not supported by Sonarr.
var torrentDictionary = BEncodedDictionary.DecodeTorrent(downloadBytes);
downloadBytes = torrentDictionary.Encode();

var result = new HttpResponseMessage(HttpStatusCode.OK);
result.Content = new ByteArrayContent(downloadBytes);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-bittorrent");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = file
};
return result;
}
catch (Exception e)
Expand Down
2 changes: 1 addition & 1 deletion src/Jackett/Controllers/PotatoController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ public async Task<HttpResponseMessage> Call(string indexerID, [FromUri]TorrentPo
foreach (var r in releases)
{
var release = Mapper.Map<ReleaseInfo>(r);
release.Link = serverService.ConvertToProxyLink(release.Link, serverUrl, indexerID);
release.Link = serverService.ConvertToProxyLink(release.Link, serverUrl, indexerID, "dl", release.Title + ".torrent");

// Only accept torrent links, magnet is not supported
if (release.Link != null)
Expand Down
2 changes: 1 addition & 1 deletion src/Jackett/Controllers/TorznabController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ public async Task<HttpResponseMessage> Call(string indexerID)
foreach(var result in releases)
{
var clone = Mapper.Map<ReleaseInfo>(result);
clone.Link = serverService.ConvertToProxyLink(clone.Link, serverUrl, indexerID);
clone.Link = serverService.ConvertToProxyLink(clone.Link, serverUrl, indexerID, "dl", result.Title + ".torrent");
resultPage.Releases.Add(clone);
}

Expand Down
291 changes: 291 additions & 0 deletions src/Jackett/Indexers/DanishBits.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,291 @@
using CsQuery;
using Jackett.Indexers;
using Jackett.Models;
using Jackett.Services;
using Jackett.Utils;
using Jackett.Utils.Clients;
using Newtonsoft.Json.Linq;
using NLog;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web;
using System.Web.UI.WebControls;
using CsQuery.ExtensionMethods;
using Jackett.Models.IndexerConfig;

namespace Jackett.Indexers
{
public class DanishBits : BaseIndexer, IIndexer
{
private string LoginUrl { get { return SiteLink + "login.php"; } }
private string SearchUrl { get { return SiteLink + "torrents.php"; } }

new NxtGnConfigurationData configData
{
get { return (NxtGnConfigurationData)base.configData; }
set { base.configData = value; }
}

public DanishBits(IIndexerManagerService i, Logger l, IWebClient c, IProtectionService ps)
: base(name: "DanishBits",
description: "A danish closed torrent tracker",
link: "https://danishbits.org/",
caps: TorznabUtil.CreateDefaultTorznabTVCaps(),
manager: i,
client: c,
logger: l,
p: ps,
configData: new NxtGnConfigurationData())
{
// Movies Mapping
// DanishBits HD
AddCategoryMapping(2, TorznabCatType.MoviesHD);
AddCategoryMapping(2, TorznabCatType.MoviesWEBDL);

// Danske film
AddCategoryMapping(3, TorznabCatType.MoviesHD);
AddCategoryMapping(3, TorznabCatType.MoviesWEBDL);
AddCategoryMapping(3, TorznabCatType.MoviesDVD);
AddCategoryMapping(3, TorznabCatType.MoviesForeign);
AddCategoryMapping(3, TorznabCatType.MoviesSD);

// DVDR Nordic
AddCategoryMapping(10, TorznabCatType.MoviesDVD);
AddCategoryMapping(10, TorznabCatType.MoviesForeign);

// Custom
AddCategoryMapping(28, TorznabCatType.MoviesHD);
AddCategoryMapping(28, TorznabCatType.MoviesDVD);

// Custom HD
AddCategoryMapping(29, TorznabCatType.MoviesHD);
AddCategoryMapping(29, TorznabCatType.MoviesWEBDL);

// Custom Tablet
AddCategoryMapping(31, TorznabCatType.MoviesSD);

if (!configData.OnlyDanishCategories.Value)
{
// Bluray
AddCategoryMapping(8, TorznabCatType.MoviesBluRay);

// Boxset
AddCategoryMapping(9, TorznabCatType.MoviesHD);
AddCategoryMapping(9, TorznabCatType.MoviesForeign);
AddCategoryMapping(9, TorznabCatType.MoviesDVD);

// DVDR
AddCategoryMapping(11, TorznabCatType.MoviesDVD);

// HDx264
AddCategoryMapping(22, TorznabCatType.MoviesHD);

// XviD/MP4/SDx264
AddCategoryMapping(24, TorznabCatType.MoviesSD);
}

// TV Mapping
// DanishBits TV
AddCategoryMapping(1, TorznabCatType.TVHD);
AddCategoryMapping(1, TorznabCatType.TVWEBDL);

// Dansk TV
AddCategoryMapping(4, TorznabCatType.TVHD);
AddCategoryMapping(4, TorznabCatType.TVWEBDL);
AddCategoryMapping(4, TorznabCatType.TVFOREIGN);
AddCategoryMapping(4, TorznabCatType.TVSD);

// Custom TV
AddCategoryMapping(30, TorznabCatType.TVHD);
AddCategoryMapping(30, TorznabCatType.TVWEBDL);

if (!configData.OnlyDanishCategories.Value)
{
// TV
AddCategoryMapping(20, TorznabCatType.TVHD);
AddCategoryMapping(20, TorznabCatType.TVSD);
AddCategoryMapping(20, TorznabCatType.TVWEBDL);

// TV Boxset
AddCategoryMapping(21, TorznabCatType.TVHD);
AddCategoryMapping(21, TorznabCatType.TVSD);
AddCategoryMapping(21, TorznabCatType.TVWEBDL);
}

// E-book
AddCategoryMapping(12, TorznabCatType.BooksEbook);
// Audiobooks
AddCategoryMapping(6, TorznabCatType.AudioAudiobook);
}

public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
{
configData.LoadValuesFromJson(configJson);
var pairs = new Dictionary<string, string> {
{ "username", configData.Username.Value },
{ "password", configData.Password.Value },
{ "langlang", null },
{ "login", "login" }
};
// Get inital cookies
CookieHeader = string.Empty;
var response = await RequestLoginAndFollowRedirect(LoginUrl, pairs, CookieHeader, true, null, LoginUrl);

await ConfigureIfOK(response.Cookies, response.Content != null && response.Content.Contains("logout.php"), () =>
{
CQ dom = response.Content;
var messageEl = dom["#loginform .warning"];
var errorMessage = messageEl.Text().Trim();
throw new ExceptionWithConfigData(errorMessage, configData);
});
return IndexerConfigurationStatus.RequiresTesting;
}

public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
{
var releasesPerPage = 100;
var releases = new List<ReleaseInfo>();

var page = (query.Offset/releasesPerPage) + 1;

string episodeSearchUrl;
if (string.IsNullOrEmpty(query.GetQueryString()))
{
episodeSearchUrl = SearchUrl + "?page=" + page;
}
else
{
var cats = MapTorznabCapsToTrackers(query);
var catsUrlPart = string.Join("&", cats.Select(c => $"filter_{c}=on"));
episodeSearchUrl = $"{SearchUrl}?page={page}&group=0&{catsUrlPart}&search={HttpUtility.UrlEncode(query.GetQueryString())}&pre_type=torrents&type=";
}
var results = await RequestStringWithCookiesAndRetry(episodeSearchUrl);
if (string.IsNullOrEmpty(results.Content))
{
CookieHeader = string.Empty;
var pairs = new Dictionary<string, string>
{
{"username", configData.Username.Value},
{"password", configData.Password.Value},
{"langlang", null},
{"login", "login"}
};
var response = await RequestLoginAndFollowRedirect(LoginUrl, pairs, CookieHeader, true, null, LoginUrl);

await ConfigureIfOK(response.Cookies, response.Content != null && response.Content.Contains("logout.php"), () =>
{
CQ dom = response.Content;
var messageEl = dom["#loginform .warning"];
var errorMessage = messageEl.Text().Trim();
throw new ExceptionWithConfigData(errorMessage, configData);
});
results = await RequestStringWithCookiesAndRetry(episodeSearchUrl);
}
try
{
CQ dom = results.Content;
var rows = dom["#torrent_table tr.torrent"];
foreach (var row in rows)
{
var qRow = row.Cq();
var release = new ReleaseInfo
{
MinimumRatio = 1,
MinimumSeedTime = 172800
};

var catAnchor = row.FirstChild.FirstChild;
var catUrl = catAnchor.GetAttribute("href");
var catStr = Regex.Match(catUrl, "filter_(?<catNo>[0-9]+)=on").Groups["catNo"].Value;
var catNo = int.Parse(catStr);
var moviesCatsDanish = new[] { 2,3,10,28,29,31 };
var moviesCatsIntl = new[] { 8,9,11,22,24 };
var moviesCats = configData.OnlyDanishCategories.Value
? moviesCatsDanish
: moviesCatsDanish.Concat(moviesCatsIntl);
var seriesCatsDanish = new[] { 1,4,30 };
var seriesCatsIntl = new[] { 20,21 };
var seriesCats = configData.OnlyDanishCategories.Value
? seriesCatsDanish
: seriesCatsDanish.Concat(seriesCatsIntl);
if (moviesCats.Contains(catNo))
release.Category = TorznabCatType.Movies.ID;
else if (seriesCats.Contains(catNo))
release.Category = TorznabCatType.TV.ID;
else if (catNo == 12)
release.Category = TorznabCatType.BooksEbook.ID;
else if (catNo == 6)
release.Category = TorznabCatType.AudioAudiobook.ID;
else
continue;

var titleAnchor = qRow.Find("div.croptorrenttext a").FirstElement();
var title = titleAnchor.GetAttribute("title");
release.Title = title;

var dlUrlAnchor = qRow.Find("span.right a").FirstElement();
var dlUrl = dlUrlAnchor.GetAttribute("href");
var authkey = Regex.Match(dlUrl, "authkey=(?<authkey>[0-9a-zA-Z]+)").Groups["authkey"].Value;
var torrentPass = Regex.Match(dlUrl, "torrent_pass=(?<torrent_pass>[0-9a-zA-Z]+)").Groups["torrent_pass"].Value;
var torrentId = Regex.Match(dlUrl, "id=(?<id>[0-9]+)").Groups["id"].Value;
release.Link = new Uri($"{SearchUrl}/{title}.torrent/?action=download&authkey={authkey}&torrent_pass={torrentPass}&id={torrentId}");

var torrentLink = titleAnchor.GetAttribute("href");
release.Guid = new Uri(SiteLink + torrentLink);
release.Comments = new Uri(SearchUrl + torrentLink);

var addedElement = qRow.Find("span.time").FirstElement();
var addedStr = addedElement.GetAttribute("title");
release.PublishDate = DateTime.ParseExact(addedStr, "MMM dd yyyy, HH:mm",
CultureInfo.InvariantCulture);

var columns = qRow.Children();
var seedersElement = columns.Reverse().Skip(1).First();
release.Seeders = int.Parse(seedersElement.InnerText);

var leechersElement = columns.Last().FirstElement();
release.Peers = release.Seeders + int.Parse(leechersElement.InnerText);

var sizeElement = columns.Skip(2).First();
var sizeStr = sizeElement.InnerText;
release.Size = ReleaseInfo.GetBytes(sizeStr);

var imdbAnchor = qRow.Find(".torrentnotes a")
.FirstOrDefault(a => a.GetAttribute("href").Contains("imdb.com"));
if (imdbAnchor != null)
{
var referrerUrl = imdbAnchor.GetAttribute("href");
release.Imdb = long.Parse(Regex.Match(referrerUrl, "tt(?<imdbId>[0-9]+)").Groups["imdbId"].Value);
}
releases.Add(release);
}
}
catch (Exception ex)
{
OnParseError(results.Content, ex);
}
return releases;
}

public class NxtGnConfigurationData : ConfigurationData
{
public NxtGnConfigurationData()
{
Username = new StringItem { Name = "Username" };
Password = new StringItem { Name = "Password" };
OnlyDanishCategories = new BoolItem { Name = "Only Danish Categories" };
}
public StringItem Username { get; private set; }
public StringItem Password { get; private set; }
public BoolItem OnlyDanishCategories { get; private set; }
}
}
}
Loading