using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Text.RegularExpressions; using NLog; using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; using NzbDrone.Core.Indexers.Definitions.Gazelle; using NzbDrone.Core.Indexers.Exceptions; using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Parser; using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.Indexers.Definitions; public class SecretCinema : GazelleBase { public override string Name => "Secret Cinema"; public override string[] IndexerUrls => new[] { "https://secret-cinema.pw/" }; public override string Description => "A tracker for rare movies."; public override IndexerPrivacy Privacy => IndexerPrivacy.Private; public override IndexerCapabilities Capabilities => SetCapabilities(); public SecretCinema(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } public override IParseIndexerResponse GetParser() { return new SecretCinemaParser(Settings, Capabilities); } protected override IndexerCapabilities SetCapabilities() { var caps = new IndexerCapabilities { MovieSearchParams = new List { MovieSearchParam.Q, MovieSearchParam.ImdbId }, MusicSearchParams = new List { MusicSearchParam.Q, MusicSearchParam.Artist, MusicSearchParam.Album, MusicSearchParam.Label, MusicSearchParam.Year } }; caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.Movies, "Movies"); caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.Audio, "Music"); return caps; } } public class SecretCinemaParser : IParseIndexerResponse { private readonly GazelleSettings _settings; private readonly IndexerCapabilities _capabilities; public SecretCinemaParser(GazelleSettings settings, IndexerCapabilities capabilities) { _settings = settings; _capabilities = capabilities; } public IList ParseResponse(IndexerResponse indexerResponse) { var releaseInfos = new List(); if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK) { // Remove cookie cache CookiesUpdater(null, null); throw new IndexerException(indexerResponse, $"Unexpected response status {indexerResponse.HttpResponse.StatusCode} code from indexer request"); } if (!indexerResponse.HttpResponse.Headers.ContentType.Contains(HttpAccept.Json.Value)) { // Remove cookie cache CookiesUpdater(null, null); throw new IndexerException(indexerResponse, $"Unexpected response header {indexerResponse.HttpResponse.Headers.ContentType} from indexer request, expected {HttpAccept.Json.Value}"); } var jsonResponse = new HttpResponse(indexerResponse.HttpResponse); if (jsonResponse.Resource.Status != "success" || jsonResponse.Resource.Status.IsNullOrWhiteSpace() || jsonResponse.Resource.Response == null) { return releaseInfos.ToArray(); } foreach (var result in jsonResponse.Resource.Response.Results) { if (result.Torrents != null) { foreach (var torrent in result.Torrents) { var id = torrent.TorrentId; var infoUrl = GetInfoUrl(result.GroupId, id); // in SC movies, artist=director and GroupName=title var artist = WebUtility.HtmlDecode(result.Artist); var title = WebUtility.HtmlDecode(result.GroupName); var time = DateTime.SpecifyKind(torrent.Time, DateTimeKind.Unspecified); var release = new TorrentInfo { Guid = infoUrl, InfoUrl = infoUrl, DownloadUrl = GetDownloadUrl(id), Title = title, Container = torrent.Encoding, Files = torrent.FileCount, Grabs = torrent.Snatches, Codec = torrent.Format, Size = long.Parse(torrent.Size), Seeders = int.Parse(torrent.Seeders), Peers = int.Parse(torrent.Leechers) + int.Parse(torrent.Seeders), PublishDate = new DateTimeOffset(time, TimeSpan.FromHours(2)).UtcDateTime, Scene = torrent.Scene, DownloadVolumeFactor = torrent.IsFreeLeech || torrent.IsNeutralLeech || torrent.IsPersonalFreeLeech ? 0 : 1, UploadVolumeFactor = torrent.IsNeutralLeech ? 0 : 1 }; var category = torrent.Category; if (category == null || category.Contains("Select Category")) { release.Categories = _capabilities.Categories.MapTrackerCatToNewznab("1"); } else { release.Categories = _capabilities.Categories.MapTrackerCatDescToNewznab(category); } if (IsAnyMovieCategory(release.Categories)) { // Remove director from title // SC API returns no more useful information than this release.Title = $"{title} ({result.GroupYear}) {torrent.Media}".Trim(); if (torrent.RemasterTitle.IsNotNullOrWhiteSpace()) { release.Title += $" [{torrent.RemasterTitle.Trim()}]"; } // Replace media formats with standards release.Title = Regex.Replace(release.Title, @"\bBDMV\b", "COMPLETE BLURAY", RegexOptions.IgnoreCase); release.Title = Regex.Replace(release.Title, @"\bSD\b", "DVDRip", RegexOptions.IgnoreCase); } else { // SC API currently doesn't return anything but title. release.Title = $"{artist} - {title} ({result.GroupYear}) [{torrent.Format} {torrent.Encoding}] [{torrent.Media}]"; release.Artist = artist; release.Album = title; } if (torrent.HasCue) { release.Title += " [Cue]"; } releaseInfos.Add(release); } } else { var id = result.TorrentId; var infoUrl = GetInfoUrl(result.GroupId, id); var groupName = WebUtility.HtmlDecode(result.GroupName); var release = new TorrentInfo { Guid = infoUrl, InfoUrl = infoUrl, DownloadUrl = GetDownloadUrl(id), Title = groupName, Size = long.Parse(result.Size), Seeders = int.Parse(result.Seeders), Peers = int.Parse(result.Leechers) + int.Parse(result.Seeders), Files = result.FileCount, Grabs = result.Snatches, PublishDate = DateTimeOffset.FromUnixTimeSeconds(ParseUtil.CoerceLong(result.GroupTime)).UtcDateTime, DownloadVolumeFactor = result.IsFreeLeech || result.IsNeutralLeech || result.IsPersonalFreeLeech ? 0 : 1, UploadVolumeFactor = result.IsNeutralLeech ? 0 : 1 }; var category = result.Category; if (category == null || category.Contains("Select Category")) { release.Categories = _capabilities.Categories.MapTrackerCatToNewznab("1"); } else { release.Categories = _capabilities.Categories.MapTrackerCatDescToNewznab(category); } releaseInfos.Add(release); } } // order by date return releaseInfos .OrderByDescending(o => o.PublishDate) .ToArray(); } private bool IsAnyMovieCategory(ICollection category) { return category.Contains(NewznabStandardCategory.Movies) || NewznabStandardCategory.Movies.SubCategories.Any(category.Contains); } private string GetDownloadUrl(int torrentId) { var url = new HttpUri(_settings.BaseUrl) .CombinePath("/torrents.php") .AddQueryParam("action", "download") .AddQueryParam("id", torrentId); if (_settings.UseFreeleechToken) { url = url.AddQueryParam("useToken", "1"); } return url.FullUri; } private string GetInfoUrl(string groupId, int torrentId) { var url = new HttpUri(_settings.BaseUrl) .CombinePath("/torrents.php") .AddQueryParam("id", groupId) .AddQueryParam("torrentid", torrentId); return url.FullUri; } public Action, DateTime?> CookiesUpdater { get; set; } }