From e2310ea70b5e4a066d1b0d74cff146651de21929 Mon Sep 17 00:00:00 2001 From: Diego Heras Date: Sun, 9 Feb 2020 03:43:32 +0100 Subject: [PATCH] anidex: rewrite in C# to bypass DDOS. resolves #7036 resolves #6834 (#7142) * Replace Cardigann Anidex indexer for C# impelementation Add bypass for DDOS Guard * Improve error messages from type conversions * Add missing cookie check * Fix index out of range exception * Change error handling to only warn about DDoS bypass exceptions This is so that searches will still be attempted if there are issues with the DDoS protection (e.g. if it is removed). * Improve error handling and clean up code * pending changes --- src/Jackett.Common/Definitions/anidex.yml | 108 -------- src/Jackett.Common/Indexers/Anidex.cs | 319 ++++++++++++++++++++++ src/Jackett.Updater/Program.cs | 215 +++++++-------- 3 files changed, 427 insertions(+), 215 deletions(-) delete mode 100644 src/Jackett.Common/Definitions/anidex.yml create mode 100644 src/Jackett.Common/Indexers/Anidex.cs diff --git a/src/Jackett.Common/Definitions/anidex.yml b/src/Jackett.Common/Definitions/anidex.yml deleted file mode 100644 index c6d29c9f8..000000000 --- a/src/Jackett.Common/Definitions/anidex.yml +++ /dev/null @@ -1,108 +0,0 @@ ---- - site: anidex - name: Anidex - description: "Anidex is a Public torrent tracker and indexer, primarily for English fansub groups of anime" - language: en-us - encoding: UTF-8 - type: public - links: - - https://anidex.info/ - - caps: - categorymappings: - - {id: 1, cat: TV/Anime, desc: "Anime - Sub"} - - {id: 2, cat: TV/Anime, desc: "Anime - Raw"} - - {id: 3, cat: TV/Anime, desc: "Anime - Dub"} - - {id: 4, cat: TV/Anime, desc: "LA - Sub"} - - {id: 5, cat: TV/Anime, desc: "LA - Raw"} - - {id: 6, cat: TV/Anime, desc: "Light Novel"} - - {id: 7, cat: TV/Anime, desc: "Manga - TLed"} - - {id: 8, cat: TV/Anime, desc: "Manga - Raw"} - - {id: 9, cat: TV/Anime, desc: "♫ - Lossy"} - - {id: 10, cat: TV/Anime, desc: "♫ - Lossless"} - - {id: 11, cat: TV/Anime, desc: "♫ - Video"} - - {id: 12, cat: TV/Anime, desc: "Games"} - - {id: 13, cat: TV/Anime, desc: "Applications"} - - {id: 14, cat: TV/Anime, desc: "Pictures"} - - {id: 15, cat: TV/Anime, desc: "Adult Video"} - - {id: 16, cat: TV/Anime, desc: "Other"} - - modes: - search: [q] - tv-search: [q, season, ep] - - settings: - - name: lang-id - type: text - label: Language ID - - name: info - type: info - label: Language ID Note - default: "You can filter your searches using any of the following language ID (comma delimited):
19 :Arabic
22 :Bengali
14 :Bulgarian
21 :Chinese (Simplified)
24 :Czech
20 :Danish
5 :Dutch
1 :English
11 :Finnish
10 :French
8 :German
13 :Greek
9 :Hungarian
27 :Indonesian
6 :Italian
2 :Japanese
28 :Korean
31 :Malaysian
25 :Mongolian
30 :Persian
3 :Polish
16 :Portuguese (Brazil)
17 :Portuguese (Portugal)
23 :Romanian
7 :Russian
4 :Serbo-Croatian
29 :Spanish (LATAM)
15 :Spanish (Spain)
18 :Swedish
26 :Turkish
12 :Vietnamese" - - name: sort - type: select - label: Sort requested from site - default: "upload_timestamp" - options: - "upload_timestamp": "created" - "seeders": "seeders" - "size": "size" - "filename": "title" - - name: type - type: select - label: Order requested from site - default: "desc" - options: - "desc": "desc" - "asc": "asc" - - search: - paths: - # https://anidex.info/?page=search&id=1,2,3&lang_id=5,1,10&group_id=0&q=rinshi - - path: "?page=search&id={{ if .Categories }}{{ range .Categories }},{{.}}{{end}}{{else}}0{{end}}{{ if .Config.lang-id }}&lang_id={{ .Config.lang-id }}{{else}}{{end}}&group_id=0&q={{ if .Keywords }}{{ .Keywords }}{{else}}{{end}}&s={{ .Config.sort }}&o={{ .Config.type }}" - - rows: - selector: div.table-responsive > table > tbody > tr - - fields: - category: - selector: a[href^="/?id="] - attribute: href - filters: - - name: querystring - args: id - title: - selector: td:nth-child(3) > a.torrent > span.span-1440 - filters: - - name: re_replace # remove anidb id from return string - args: ["(\\[[A-Z0-9]*\\])\\.", "."] - details: - selector: td:nth-child(3) > a.torrent - attribute: href - download: - selector: td:nth-child(5) > a - attribute: href - magnet: - selector: a[href^="magnet:?"] - attribute: href - size: - selector: td:nth-child(7) - date: - selector: td:nth-child(8) - attribute: title - filters: - - name: replace - args: ["UTC", "+00:00"] - - name: dateparse - args: "2006-01-02 15:04:05 -07:00" - seeders: - selector: td:nth-child(9) - leechers: - selector: td:nth-child(10) - grabs: - selector: td:nth-child(11) - downloadvolumefactor: - text: 0 - uploadvolumefactor: - text: 1 -# engine n/a diff --git a/src/Jackett.Common/Indexers/Anidex.cs b/src/Jackett.Common/Indexers/Anidex.cs new file mode 100644 index 000000000..2f26da13f --- /dev/null +++ b/src/Jackett.Common/Indexers/Anidex.cs @@ -0,0 +1,319 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Globalization; +using System.IO; +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; +using Newtonsoft.Json.Linq; +using NLog; +using static Jackett.Common.Models.IndexerConfig.ConfigurationData; + +namespace Jackett.Common.Indexers +{ + public class Anidex : BaseWebIndexer + { + public Anidex(IIndexerConfigurationService configService, Utils.Clients.WebClient wc, Logger l, IProtectionService ps) + : base(name: "Anidex", + description: "Anidex is a Public torrent tracker and indexer, primarily for English fansub groups of anime", + link: "https://anidex.info/", + caps: new TorznabCapabilities(), + configService: configService, + client: wc, + logger: l, + p: ps, + configData: new ConfigurationData()) + { + Encoding = Encoding.UTF8; + Language = "en-us"; + Type = "public"; + + // Configure the category mappings + AddCategoryMapping(1, TorznabCatType.TVAnime, "Anime - Sub"); + AddCategoryMapping(2, TorznabCatType.TVAnime, "Anime - Raw"); + AddCategoryMapping(3, TorznabCatType.TVAnime, "Anime - Dub"); + AddCategoryMapping(4, TorznabCatType.TVAnime, "LA - Sub"); + AddCategoryMapping(5, TorznabCatType.TVAnime, "LA - Raw"); + AddCategoryMapping(6, TorznabCatType.TVAnime, "Light Novel"); + AddCategoryMapping(7, TorznabCatType.TVAnime, "Manga - TLed"); + AddCategoryMapping(8, TorznabCatType.TVAnime, "Manga - Raw"); + AddCategoryMapping(9, TorznabCatType.TVAnime, "♫ - Lossy"); + AddCategoryMapping(10, TorznabCatType.TVAnime, "♫ - Lossless"); + AddCategoryMapping(11, TorznabCatType.TVAnime, "♫ - Video"); + AddCategoryMapping(12, TorznabCatType.TVAnime, "Games"); + AddCategoryMapping(13, TorznabCatType.TVAnime, "Applications"); + AddCategoryMapping(14, TorznabCatType.TVAnime, "Pictures"); + AddCategoryMapping(15, TorznabCatType.TVAnime, "Adult Video"); + AddCategoryMapping(16, TorznabCatType.TVAnime, "Other"); + + // Configure the language select option + var languageSelect = new SelectItem(new Dictionary() + { + {"1", "English"}, + {"2", "Japanese"}, + {"3", "Polish"}, + {"4", "Serbo-Croatian" }, + {"5", "Dutch"}, + {"6", "Italian"}, + {"7", "Russian"}, + {"8", "German"}, + {"9", "Hungarian"}, + {"10", "French"}, + {"11", "Finnish"}, + {"12", "Vietnamese"}, + {"13", "Greek"}, + {"14", "Bulgarian"}, + {"15", "Spanish (Spain)" }, + {"16", "Portuguese (Brazil)" }, + {"17", "Portuguese (Portugal)" }, + {"18", "Swedish"}, + {"19", "Arabic"}, + {"20", "Danish"}, + {"21", "Chinese (Simplified)" }, + {"22", "Bengali"}, + {"23", "Romanian"}, + {"24", "Czech"}, + {"25", "Mongolian"}, + {"26", "Turkish"}, + {"27", "Indonesian"}, + {"28", "Korean"}, + {"29", "Spanish (LATAM)" }, + {"30", "Persian"}, + {"31", "Malaysian"} + }) { Name = "Language", Value = "1" }; + configData.AddDynamic("languageid", languageSelect); + + // Configure the sort selects + var sortBySelect = new SelectItem(new Dictionary() + { + {"upload_timestamp", "created"}, + {"seeders", "seeders"}, + {"size", "size"}, + {"filename", "title"} + }) { Name = "Sort by", Value = "upload_timestamp" }; + configData.AddDynamic("sortrequestedfromsite", sortBySelect); + + var orderSelect = new SelectItem(new Dictionary() + { + {"desc", "Descending"}, + {"asc", "Ascending"} + }) + { Name = "Order", Value = "desc" }; + configData.AddDynamic("orderrequestedfromsite", orderSelect); + } + + private string GetSortBy => ((SelectItem)configData.GetDynamic("sortrequestedfromsite")).Value; + + private string GetOrder => ((SelectItem)configData.GetDynamic("orderrequestedfromsite")).Value; + + private Uri GetAbsoluteUrl(string relativeUrl) => new Uri(SiteLink + relativeUrl.TrimStart('/')); + + 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 releases from this URL")); + + return IndexerConfigurationStatus.Completed; + } + + protected override async Task> PerformQuery(TorznabQuery query) + { + try + { + await ConfigureDDoSGuardCookie(); + } + catch (Exception ex) + { + logger.Log(LogLevel.Warn, ex, $"Exception while configuring DDoS Guard cookie. Attempting search without. (Exception: {ex})"); + } + + // Get specified categories. If none were specified, use all available. + var searchCategories = MapTorznabCapsToTrackers(query); + if (searchCategories.Count == 0) + searchCategories = GetAllTrackerCategories(); + + // Prepare the search query + var queryParameters = new NameValueCollection + { + { "page", "search" }, + { "id", string.Join(",", searchCategories) }, + { "group", "0" }, // No group + { "q", query.SearchTerm ?? string.Empty }, + { "s", GetSortBy }, + { "o", GetOrder } + }; + + // Make search request + var searchUri = GetAbsoluteUrl("?" + queryParameters.GetQueryString()); + var response = await RequestStringWithCookiesAndRetry(searchUri.AbsoluteUri); + + // Check for DDOS Guard or other error + if (response.Status == System.Net.HttpStatusCode.Forbidden) + throw new IOException("Anidex search was forbidden. This was likely caused by DDOS protection."); + + if (response.Status != System.Net.HttpStatusCode.OK) + throw new IOException($"Anidex search returned unexpected result. Expected 200 OK but got {response.Status.ToString()}."); + + // Search seems to have been a success so parse it + return ParseResult(response.Content); + } + + private IEnumerable ParseResult(string response) + { + const string rowSelector = "div#content table > tbody > tr"; + + try + { + var resultParser = new HtmlParser(); + var resultDocument = resultParser.ParseDocument(response); + IEnumerable rows = resultDocument.QuerySelectorAll(rowSelector); + + var releases = new List(); + foreach (var r in rows) + try + { + var release = new ReleaseInfo(); + + release.Category = ParseValueFromRow(r, nameof(release.Category), "td:nth-child(1) a", (e) => MapTrackerCatToNewznab(e.Attributes["href"].Value.Substring(5))); + release.Title = ParseStringValueFromRow(r, nameof(release.Title), "td:nth-child(3) span"); + release.Link = ParseValueFromRow(r, nameof(release.Link), "a[href^=\"/dl/\"]", (e) => GetAbsoluteUrl(e.Attributes["href"].Value)); + release.MagnetUri = ParseValueFromRow(r, nameof(release.MagnetUri), "a[href^=\"magnet:?\"]", (e) => new Uri(e.Attributes["href"].Value)); + release.Size = ParseValueFromRow(r, nameof(release.Size), "td:nth-child(7)", (e) => ReleaseInfo.GetBytes(e.Text())); + release.PublishDate = ParseValueFromRow(r, nameof(release.PublishDate), "td:nth-child(8)", (e) => DateTime.ParseExact(e.Attributes["title"].Value, "yyyy-MM-dd HH:mm:ss UTC", CultureInfo.InvariantCulture)); + release.Seeders = ParseIntValueFromRow(r, nameof(release.Seeders), "td:nth-child(9)"); + release.Peers = ParseIntValueFromRow(r, nameof(release.Peers), "td:nth-child(10)") + release.Seeders; + release.Grabs = ParseIntValueFromRow(r, nameof(release.Grabs), "td:nth-child(11)"); + release.Comments = ParseValueFromRow(r, nameof(release.Comments), "td:nth-child(3) a", (e) => GetAbsoluteUrl(e.Attributes["href"].Value)); + release.Guid = release.Comments; + release.MinimumRatio = 1; + release.MinimumSeedTime = 172800; // 48 hours + release.DownloadVolumeFactor = 0; + release.UploadVolumeFactor = 1; + + releases.Add(release); + } + catch (Exception ex) + { + logger.Error($"Anidex: Error parsing search result row '{r.ToHtmlPretty()}':\n\n{ex}"); + } + + return releases; + } + catch (Exception ex) + { + throw new IOException($"Error parsing search result page: {ex}"); + } + } + + private async Task ConfigureDDoSGuardCookie() + { + const string pathAndQueryBase64Encoded = "Lw=="; // "/" + const string baseUriBase64Encoded = "aHR0cHM6Ly9hbmlkZXguaW5mbw=="; // "http://anidex.info" + const string ddosPostUrl = "https://ddgu.ddos-guard.net/ddgu/"; + + try + { + // Check if the cookie already exists, if so exit without doing anything + if (IsCookiePresent("__ddgu") && IsCookiePresent("__ddg1")) + { + logger.Debug("DDOS Guard cookies are already present. Skipping bypass."); + return; + } + + // Make a request to DDoS Guard to get the redirect URL + var ddosPostData = new List> + { + { "u", pathAndQueryBase64Encoded }, + { "h", baseUriBase64Encoded }, + { "p", string.Empty } + }; + + var result = await PostDataWithCookiesAndRetry(ddosPostUrl, ddosPostData); + + if (!result.IsRedirect) + // Success returns a redirect. For anything else, assume a failure. + throw new IOException($"Unexpected result from DDOS Guard while attempting to bypass: {result.Content}"); + + // Call the redirect URL to retrieve the cookie + result = await RequestStringWithCookiesAndRetry(result.RedirectingTo); + if (!result.IsRedirect) + // Success is another redirect. For anything else, assume a failure. + throw new IOException($"Unexpected result when returning from DDOS Guard bypass: {result.Content}"); + + // If we got to this point, the bypass should have succeeded and we have stored the necessary cookies to access the site normally. + } + catch (Exception ex) + { + throw new IOException($"Error while configuring DDoS Guard cookie: {ex}"); + } + } + + private bool IsCookiePresent(string name) + { + var rawCookies = CookieHeader.Split(';'); + IDictionary cookies = rawCookies + .Where(e => e.Contains('=')) + .ToDictionary((e) => e.Split('=')[0].Trim(), (e) => e.Split('=')[1].Trim()); + + return cookies.ContainsKey(name); + } + + private static TResult ParseValueFromRow(IElement row, string propertyName, string selector, + Func parseFunction) + { + try + { + var selectedElement = row.QuerySelector(selector); + if (selectedElement == null) + throw new IOException($"Unable to find '{selector}'."); + + return parseFunction(selectedElement); + } + catch (Exception ex) + { + throw new IOException($"Error parsing for property '{propertyName}': {ex.Message}"); + } + } + + private static string ParseStringValueFromRow(IElement row, string propertyName, string selector) + { + try + { + var selectedElement = row.QuerySelector(selector); + if (selectedElement == null) + throw new IOException($"Unable to find '{selector}'."); + + return selectedElement.Text(); + } + catch (Exception ex) + { + throw new IOException($"Error parsing for property '{propertyName}': {ex.Message}"); + } + } + + private static int ParseIntValueFromRow(IElement row, string propertyName, string selector) + { + try + { + var text = ParseStringValueFromRow(row, propertyName, selector); + if (!int.TryParse(text, out var value)) + throw new IOException($"Could not convert '{text}' to int."); + return value; + } + catch (Exception ex) + { + throw new IOException($"Error parsing for property '{propertyName}': {ex.Message}"); + } + } + } +} diff --git a/src/Jackett.Updater/Program.cs b/src/Jackett.Updater/Program.cs index 4ef4f4160..42ceb71f8 100644 --- a/src/Jackett.Updater/Program.cs +++ b/src/Jackett.Updater/Program.cs @@ -267,49 +267,118 @@ namespace Jackett.Updater // delete old files string[] oldFiles = new string[] { + "appsettings.Development.json", + "Autofac.Integration.WebApi.dll", + "Content/congruent_outline.png", + "Content/crissXcross.png", "Content/css/jquery.dataTables.css", "Content/css/jquery.dataTables_themeroller.css", - "Definitions/tspate.yml", - "Definitions/freakstrackingsystem.yml", - "Definitions/rarbg.yml", - "Definitions/t411.yml", - "Definitions/hdbc.yml", // renamed to hdbitscom - "Definitions/maniatorrent.yml", - "Definitions/nyaa.yml", - "Definitions/nachtwerk.yml", - "Definitions/leparadisdunet.yml", - "Definitions/qctorrent.yml", - "Definitions/dragonworld.yml", - "Definitions/hdclub.yml", - "Definitions/polishtracker.yml", - "Definitions/zetorrents.yml", - "Definitions/rapidetracker.yml", - "Definitions/isohunt.yml", - "Definitions/t411v2.yml", - "Definitions/bithq.yml", - "Definitions/blubits.yml", - "Definitions/torrentproject.yml", - "Definitions/torrentvault.yml", - "Definitions/apollo.yml", // migrated to C# gazelle base tracker - "Definitions/secretcinema.yml", // migrated to C# gazelle base tracker - "Definitions/utorrents.yml", // same as SzeneFZ now - "Definitions/ultrahdclub.yml", - "Definitions/infinityt.yml", - "Definitions/hachede-c.yml", - "Definitions/skytorrents.yml", - "Definitions/gormogon.yml", - "Definitions/czteam.yml", - "Definitions/rockhardlossless.yml", - "Definitions/tehconnection.yml", - "Definitions/torrentwtf.yml", - "Definitions/eotforum.yml", - "Definitions/nexttorrent.yml", - "Definitions/torrentsmd.yml", - "Definitions/scenehd.yml", // migrated to C# (use JSON API) - "appsettings.Development.json", "CurlSharp.dll", "CurlSharp.pdb", - "Autofac.Integration.WebApi.dll", + "Definitions/420files.yml", + "Definitions/aox.yml", + "Definitions/anidex.yml", // migrated to C# + "Definitions/apollo.yml", // migrated to C# gazelle base tracker + "Definitions/archetorrent.yml", + "Definitions/asiandvdclub.yml", + "Definitions/avg.yml", + "Definitions/b2s-share.yml", + "Definitions/bithq.yml", + "Definitions/bitme.yml", + "Definitions/blubits.yml", + "Definitions/bt-scene.yml", + "Definitions/btbit.yml", + "Definitions/btkitty.yml", + "Definitions/btstornet.yml", + "Definitions/btxpress.yml", + "Definitions/cinefilhd.yml", + "Definitions/czteam.yml", + "Definitions/dark-shadow.yml", + "Definitions/digbt.yml", + "Definitions/dragonworld.yml", + "Definitions/dreamteam.yml", + "Definitions/elitehd.yml", + "Definitions/elittracker.yml", + "Definitions/eotforum.yml", + "Definitions/evolutionpalace.yml", + "Definitions/extratorrent-ag.yml", + "Definitions/extratorrentclone.yml", + "Definitions/freakstrackingsystem.yml", + "Definitions/freedomhd.yml", + "Definitions/gdf76.yml", + "Definitions/gfxnews.yml", + "Definitions/gods.yml", + "Definitions/gormogon.yml", + "Definitions/hachede-c.yml", + "Definitions/hd4free.yml", + "Definitions/hdbc.yml", // renamed to hdbitscom + "Definitions/hdclub.yml", + "Definitions/hdplus.yml", + "Definitions/hon3yhd-net.yml", + "Definitions/horriblesubs.yml", + "Definitions/hyperay.yml", + "Definitions/idopeclone.yml", + "Definitions/iloveclassics.yml", + "Definitions/infinityt.yml", + "Definitions/isohunt.yml", + "Definitions/katcrs.yml", + "Definitions/kikibt.yml", + "Definitions/lapausetorrents.yml", + "Definitions/lechaudron.yml", + "Definitions/lemencili.yml", + "Definitions/leparadisdunet.yml", + "Definitions/maniatorrent.yml", + "Definitions/manicomioshare.yml", + "Definitions/megabliz.yml", + "Definitions/mkvcage.yml", + "Definitions/music-master.yml", + "Definitions/nachtwerk.yml", + "Definitions/nexttorrent.yml", + "Definitions/nyaa.yml", + "Definitions/nyoo.yml", + "Definitions/passionetorrent.yml", + "Definitions/polishtracker.yml", + "Definitions/qctorrent.yml", + "Definitions/qxr.yml", + "Definitions/rapidetracker.yml", + "Definitions/rarbg.yml", + "Definitions/redtopia.yml", + "Definitions/rgu.yml", + "Definitions/rockethd.yml", + "Definitions/rockhardlossless.yml", + "Definitions/scenehd.yml", // migrated to C# (use JSON API) + "Definitions/scenereactor.yml", + "Definitions/secretcinema.yml", // migrated to C# gazelle base tracker + "Definitions/sharingue.yml", + "Definitions/skytorrents.yml", + "Definitions/solidtorrents.yml", // migrated to C# + "Definitions/speed-share.yml", + "Definitions/t411.yml", + "Definitions/t411v2.yml", + "Definitions/tazmaniaden.yml", + "Definitions/tbplus.yml", + "Definitions/tehconnection.yml", + "Definitions/themoviecave.yml", + "Definitions/thetorrents.yml", + "Definitions/tigers-dl.yml", + "Definitions/tntvillage.yml", + "Definitions/torrentcouch.yml", + "Definitions/torrentkim.yml", + "Definitions/torrentproject.yml", + "Definitions/torrentsmd.yml", + "Definitions/torrentvault.yml", + "Definitions/torrentwtf.yml", + "Definitions/torrof.yml", + "Definitions/torviet.yml", + "Definitions/tspate.yml", + "Definitions/ultimategamerclub.yml", + "Definitions/ultrahdclub.yml", + "Definitions/utorrents.yml", // same as SzeneFZ now + "Definitions/waffles.yml", + "Definitions/worldofp2p.yml", + "Definitions/worldwidetorrents.yml", + "Definitions/xktorrent.yml", + "Definitions/zetorrents.yml", "Microsoft.Owin.dll", "Microsoft.Owin.FileSystems.dll", "Microsoft.Owin.Host.HttpListener.dll", @@ -319,74 +388,6 @@ namespace Jackett.Updater "System.Web.Http.dll", "System.Web.Http.Owin.dll", "System.Web.Http.Tracing.dll", - "Definitions/torrentkim.yml", - "Definitions/horriblesubs.yml", - "Definitions/bt-scene.yml", - "Definitions/extratorrentclone.yml", - "Definitions/torrentcouch.yml", - "Definitions/idopeclone.yml", - "Definitions/torrof.yml", - "Definitions/archetorrent.yml", - "Definitions/420files.yml", - "Definitions/redtopia.yml", - "Definitions/btxpress.yml", - "Definitions/btstornet.yml", - "Definitions/hdplus.yml", - "Definitions/gods.yml", - "Definitions/freedomhd.yml", - "Definitions/sharingue.yml", - "Definitions/cinefilhd.yml", - "Definitions/tbplus.yml", - "Definitions/manicomioshare.yml", - "Definitions/speed-share.yml", - "Definitions/b2s-share.yml", - "Definitions/nyoo.yml", - "Definitions/ultimategamerclub.yml", - "Definitions/evolutionpalace.yml", - "Definitions/qxr.yml", - "Definitions/gfxnews.yml", - "Definitions/megabliz.yml", - "Definitions/tigers-dl.yml", - "Definitions/worldwidetorrents.yml", - "Definitions/tntvillage.yml", - "Definitions/xktorrent.yml", - "Definitions/btkitty.yml", - "Definitions/kikibt.yml", - "Definitions/rockethd.yml", - "Definitions/worldofp2p.yml", - "Definitions/avg.yml", - "Definitions/aox.yml", - "Definitions/dreamteam.yml", - "Definitions/elitehd.yml", - "Definitions/gdf76.yml", - "Definitions/hyperay.yml", - "Definitions/scenereactor.yml", - "Definitions/lapausetorrents.yml", - "Definitions/lechaudron.yml", - "Definitions/katcrs.yml", - "Definitions/iloveclassics.yml", - "Definitions/hd4free.yml", - "Definitions/lemencili.yml", - "Definitions/btbit.yml", - "Definitions/digbt.yml", - "Definitions/mkvcage.yml", - "Content/congruent_outline.png", - "Content/crissXcross.png", - "Definitions/dark-shadow.yml", - "Definitions/bitme.yml", - "Definitions/asiandvdclub.yml", - "Definitions/music-master.yml", - "Definitions/torviet.yml", - "Definitions/waffles.yml", - "Definitions/rgu.yml", - "Definitions/elittracker.yml", - "Definitions/hon3yhd-net.yml", - "Definitions/solidtorrents.yml", - "Definitions/extratorrent-ag.yml", - "Definitions/passionetorrent.yml", - "Definitions/thetorrents.yml", - "Definitions/themoviecave.yml", - "Definitions/tazmaniaden.yml", }; foreach (var oldFile in oldFiles)