using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text; using System.Threading.Tasks; using AngleSharp.Dom; using AngleSharp.Html.Parser; using Jackett.Common.Models; using Jackett.Common.Models.IndexerConfig; using Jackett.Common.Services.Interfaces; using Jackett.Common.Utils.Clients; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NLog; namespace Jackett.Common.Indexers { [ExcludeFromCodeCoverage] public class TorrentMafya : BaseWebIndexer { private class TorrentMafyaRowResult { public string col1 { get; set; } public string col2 { get; set; } public string col3 { get; set; } } private class TorrentMafyaArchiveResponse { public IEnumerable aaData { get; set; } } public TorrentMafya(IIndexerConfigurationService configService, WebClient wc, Logger l, IProtectionService ps, ICacheService cs) : base(id: "torrentmafya", name: "TorrentMafya", description: "TorrentMafya is a Turkish general torrent tracker ", link: "https://www.torrentmafya.org/", caps: new TorznabCapabilities { TvSearchParams = new List { TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep }, MovieSearchParams = new List { MovieSearchParam.Q } }, configService: configService, client: wc, logger: l, p: ps, cacheService: cs, configData: new ConfigurationData()) { Encoding = Encoding.UTF8; Language = "tr-tr"; Type = "public"; AddCategoryMapping("games", TorznabCatType.PCGames, "Oyun"); AddCategoryMapping("programs", TorznabCatType.PC, "Program"); AddCategoryMapping("movies", TorznabCatType.Movies, "Film"); AddCategoryMapping("tv", TorznabCatType.TV, "Dizi"); AddCategoryMapping("apk", TorznabCatType.PCMobileAndroid, "APK"); } private static DateTime ParseReleasePublishDate(string date) { try { var splitDate = date.Split(' '); var firstPart = int.Parse(splitDate[0]) * -1; var secondPart = splitDate[1].ToLowerInvariant(); switch (secondPart) { case "saat": return DateTime.Now.AddHours(firstPart); case "gün": return DateTime.Now.AddDays(firstPart); case "ay": return DateTime.Now.AddMonths(firstPart); case "yıl": return DateTime.Now.AddYears(firstPart); default: return DateTime.MinValue; } } catch { return DateTime.MinValue; } } private static List ParseReleaseCategory(ITokenList classes) { var result = new List(); if (classes.Contains("fa-gamepad")) { result.Add(TorznabCatType.PCGames.ID); } else if (classes.Contains("fa-film")) { result.Add(TorznabCatType.Movies.ID); } else if (classes.Contains("fa-tv")) { result.Add(TorznabCatType.TV.ID); } else if (classes.Contains("fa-microchip")) { result.Add(TorznabCatType.PC.ID); } else if (classes.Contains("fa-android")) { result.Add(TorznabCatType.PCMobileAndroid.ID); } return result; } public override async Task ApplyConfiguration(JToken configJson) { LoadValuesFromJson(configJson); var releases = await PerformQuery(new TorznabQuery()); await ConfigureIfOK(string.Empty, releases.Any(), () => throw new Exception("Could not find release from this URL.")); return IndexerConfigurationStatus.Completed; } private static ReleaseInfo ParseReleaseInfo(IHtmlParser parser, TorrentMafyaRowResult row) { var firstColumn = parser.ParseDocument(row.col1); var mainColumn = parser.ParseDocument(row.col3); var sizeContent = mainColumn.QuerySelector("span.boyut")?.TextContent; var magnetLink = mainColumn.QuerySelector("a[href^=\"magnet:?xt=\"]")?.GetAttribute("href"); var fileLink = mainColumn.QuerySelector("a[title^=\"İndir\"]")?.GetAttribute("href"); var detailsLink = new Uri(firstColumn.QuerySelector("a").GetAttribute("href")); var category = ParseReleaseCategory(firstColumn.QuerySelector("i")?.ClassList); var seederContent = mainColumn.QuerySelector("span.sayiGonderen")?.TextContent; var leecherContent = mainColumn.QuerySelector("span.sayiIndiren")?.TextContent; int.TryParse(seederContent, out var seeders); int.TryParse(leecherContent, out var leechers); return new ReleaseInfo { Title = firstColumn.QuerySelector("a")?.TextContent, Details = detailsLink, Guid = detailsLink, Link = string.IsNullOrWhiteSpace(fileLink) ? null : new Uri(fileLink), MagnetUri = string.IsNullOrWhiteSpace(magnetLink) ? null : new Uri(magnetLink), PublishDate = ParseReleasePublishDate(row.col2), Category = category, Seeders = seeders, Peers = seeders + leechers, DownloadVolumeFactor = 0, UploadVolumeFactor = 1, Size = !string.IsNullOrEmpty(sizeContent) ? ReleaseInfo.GetBytes(sizeContent) : 0 }; } private static Dictionary PrepareSearchQueryCollection(string searchString) => new Dictionary { {"draw", "8"}, {"columns[0][data]", "col1"}, {"columns[0][name]", ""}, {"columns[0][searchable]", "true"}, {"columns[0][orderable]", "false"}, {"columns[0][search][value]", ""}, {"columns[0][search][regex]", "false"}, {"columns[1][data]", "col2"}, {"columns[1][name]", ""}, {"columns[1][searchable]", "true"}, {"columns[1][orderable]", "false"}, {"columns[1][search][value]", ""}, {"columns[1][search][regex]", "false"}, {"columns[2][data]", "col3"}, {"columns[2][name]", ""}, {"columns[2][searchable]", "true"}, {"columns[2][orderable]", "false"}, {"columns[2][search][value]", ""}, {"columns[2][search][regex]", "false"}, {"start", "0"}, {"length", "20"}, {"search[value]", searchString}, {"search[regex]", "false"}, {"tur", "1"} }; protected override async Task> PerformQuery(TorznabQuery query) { var result = new List(); var searchString = query.GetQueryString().Replace(" ", "%"); var searchUrl = $"{SiteLink}table.php"; var referrer = $"{SiteLink}arsiv/"; var body = PrepareSearchQueryCollection(searchString); var response = await RequestWithCookiesAsync(searchUrl, null, RequestType.POST, referrer, body); var content = response.ContentString; try { var json = JsonConvert.DeserializeObject(response.ContentString); var parser = new HtmlParser(); result.AddRange(json.aaData.Select(row => ParseReleaseInfo(parser, row))); } catch (Exception ex) { OnParseError(content, ex); } return result; } } }