mirror of
https://github.com/Radarr/Radarr.git
synced 2024-08-16 15:29:40 +02:00
Merge remote-tracking branch 'origin/develop' into develop
This commit is contained in:
commit
b1e75ffc57
@ -24,7 +24,7 @@ The project was inspired by other Usenet/BitTorrent movie downloaders such as Co
|
||||
[![AppVeyor Builds](https://img.shields.io/badge/downloads-continuous-green.svg?maxAge=60&style=flat-square)](https://ci.appveyor.com/project/galli-leo/radarr-usby1/build/artifacts)
|
||||
|
||||
[![Docker release](https://img.shields.io/badge/docker-release-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://store.docker.com/community/images/linuxserver/radarr)
|
||||
[![Docker nightly](https://img.shields.io/badge/docker-nightly-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://store.docker.com/community/images/hotio/radarr)
|
||||
[![Docker nightly](https://img.shields.io/badge/docker-nightly-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://store.docker.com/community/images/hotio/suitarr)
|
||||
[![Docker armhf](https://img.shields.io/badge/docker-armhf-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://store.docker.com/community/images/lsioarmhf/radarr)
|
||||
[![Docker aarch64](https://img.shields.io/badge/docker-aarch64-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://store.docker.com/community/images/lsioarmhf/radarr-aarch64)
|
||||
|
||||
|
16
appveyor.yml
16
appveyor.yml
@ -16,14 +16,14 @@ install:
|
||||
build_script:
|
||||
- ps: ./build-appveyor.ps1
|
||||
|
||||
# test: off
|
||||
test:
|
||||
assemblies:
|
||||
- '_tests\*Test.dll'
|
||||
categories:
|
||||
except:
|
||||
- IntegrationTest
|
||||
- AutomationTest
|
||||
test: off
|
||||
#test:
|
||||
# assemblies:
|
||||
# - '_tests\*Test.dll'
|
||||
# categories:
|
||||
# except:
|
||||
# - IntegrationTest
|
||||
# - AutomationTest
|
||||
|
||||
artifacts:
|
||||
- path: '_artifacts\*.zip'
|
||||
|
4190
package-lock.json
generated
Normal file
4190
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "Sonarr",
|
||||
"name": "Radarr",
|
||||
"version": "2.0.0",
|
||||
"description": "Sonarr",
|
||||
"description": "Radarr",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
"build": "gulp build",
|
||||
@ -9,7 +9,7 @@
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/Sonarr/Sonarr.git"
|
||||
"url": "git://github.com/Radarr/Radarr.git"
|
||||
},
|
||||
"author": "",
|
||||
"license": "GPL-3.0",
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Marr.Data.Mapping;
|
||||
using Marr.Data.QGen.Dialects;
|
||||
|
||||
@ -129,7 +130,16 @@ public void BuildWhereClause(StringBuilder sql)
|
||||
public void BuildOrderClause(StringBuilder sql)
|
||||
{
|
||||
sql.Append(OrderBy.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public void BuildGroupBy(StringBuilder sql)
|
||||
{
|
||||
var baseTable = this.Tables.First();
|
||||
var primaryKeyColumn = baseTable.Columns.Single(c => c.ColumnInfo.IsPrimaryKey);
|
||||
|
||||
string token = this.Dialect.CreateToken(string.Concat(baseTable.Alias, ".", primaryKeyColumn.ColumnInfo.Name));
|
||||
sql.AppendFormat(" GROUP BY {0}", token);
|
||||
}
|
||||
|
||||
private string TranslateJoin(JoinType join)
|
||||
{
|
||||
|
@ -14,8 +14,21 @@ public SqliteRowCountQueryDecorator(SelectQuery innerQuery)
|
||||
public string Generate()
|
||||
{
|
||||
StringBuilder sql = new StringBuilder();
|
||||
|
||||
|
||||
BuildSelectCountClause(sql);
|
||||
|
||||
if (_innerQuery.IsJoin)
|
||||
{
|
||||
sql.Append(" FROM (");
|
||||
_innerQuery.BuildSelectClause(sql);
|
||||
_innerQuery.BuildFromClause(sql);
|
||||
_innerQuery.BuildJoinClauses(sql);
|
||||
_innerQuery.BuildGroupBy(sql);
|
||||
sql.Append(") ");
|
||||
|
||||
return sql.ToString();
|
||||
}
|
||||
|
||||
_innerQuery.BuildFromClause(sql);
|
||||
_innerQuery.BuildJoinClauses(sql);
|
||||
_innerQuery.BuildWhereClause(sql);
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using Nancy;
|
||||
@ -17,7 +17,7 @@ public class IndexHtmlMapper : StaticResourceMapperBase
|
||||
private readonly IAnalyticsService _analyticsService;
|
||||
private readonly Func<ICacheBreakerProvider> _cacheBreakProviderFactory;
|
||||
private readonly string _indexPath;
|
||||
private static readonly Regex ReplaceRegex = new Regex(@"(?:(?<attribute>href|src)=\"")(?<path>.*?(?<extension>css|js|png|ico|ics))(?:\"")(?:\s(?<nohash>data-no-hash))?", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static readonly Regex ReplaceRegex = new Regex(@"(?:(?<attribute>href|src|content)=\"")(?<path>.*?(?<extension>css|js|png|ico|ics|svg|json|xml))(?:\"")(?:\s(?<nohash>data-no-hash))?", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
private static string API_KEY;
|
||||
private static string URL_BASE;
|
||||
|
@ -46,7 +46,7 @@ public ReleaseModule(IFetchAndParseRss rssFetcherAndParser,
|
||||
GetResourceAll = GetReleases;
|
||||
Post["/"] = x => DownloadRelease(this.Bind<ReleaseResource>());
|
||||
|
||||
PostValidator.RuleFor(s => s.DownloadAllowed).Equal(true);
|
||||
//PostValidator.RuleFor(s => s.DownloadAllowed).Equal(true);
|
||||
PostValidator.RuleFor(s => s.Guid).NotEmpty();
|
||||
|
||||
_remoteEpisodeCache = cacheManager.GetCache<RemoteEpisode>(GetType(), "remoteEpisodes");
|
||||
@ -70,7 +70,7 @@ private Response DownloadRelease(ReleaseResource release)
|
||||
|
||||
try
|
||||
{
|
||||
_downloadService.DownloadReport(remoteMovie);
|
||||
_downloadService.DownloadReport(remoteMovie, false);
|
||||
}
|
||||
catch (ReleaseDownloadException ex)
|
||||
{
|
||||
|
@ -8,6 +8,7 @@
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using System.Linq;
|
||||
using NzbDrone.Core.Datastore.Migration;
|
||||
|
||||
namespace NzbDrone.Api.Indexers
|
||||
{
|
||||
@ -29,8 +30,8 @@ public class ReleaseResource : RestResource
|
||||
public bool FullSeason { get; set; }
|
||||
public int SeasonNumber { get; set; }
|
||||
public Language Language { get; set; }
|
||||
public string AirDate { get; set; }
|
||||
public string SeriesTitle { get; set; }
|
||||
public int Year { get; set; }
|
||||
public string MovieTitle { get; set; }
|
||||
public int[] EpisodeNumbers { get; set; }
|
||||
public int[] AbsoluteEpisodeNumbers { get; set; }
|
||||
public bool Approved { get; set; }
|
||||
@ -43,8 +44,9 @@ public class ReleaseResource : RestResource
|
||||
public string CommentUrl { get; set; }
|
||||
public string DownloadUrl { get; set; }
|
||||
public string InfoUrl { get; set; }
|
||||
public bool DownloadAllowed { get; set; }
|
||||
public MappingResultType MappingResult { get; set; }
|
||||
public int ReleaseWeight { get; set; }
|
||||
public int SuspectedMovieId { get; set; }
|
||||
|
||||
public IEnumerable<string> IndexerFlags { get; set; }
|
||||
|
||||
@ -88,11 +90,12 @@ public static ReleaseResource ToResource(this DownloadDecision model)
|
||||
var parsedEpisodeInfo = model.RemoteEpisode.ParsedEpisodeInfo;
|
||||
var remoteEpisode = model.RemoteEpisode;
|
||||
var torrentInfo = (model.RemoteEpisode.Release as TorrentInfo) ?? new TorrentInfo();
|
||||
var downloadAllowed = model.RemoteEpisode.DownloadAllowed;
|
||||
var mappingResult = MappingResultType.Success;
|
||||
if (model.IsForMovie)
|
||||
{
|
||||
downloadAllowed = model.RemoteMovie.DownloadAllowed;
|
||||
mappingResult = model.RemoteMovie.MappingResult;
|
||||
var parsedMovieInfo = model.RemoteMovie.ParsedMovieInfo;
|
||||
var movieId = model.RemoteMovie.Movie?.Id ?? 0;
|
||||
|
||||
return new ReleaseResource
|
||||
{
|
||||
@ -111,8 +114,8 @@ public static ReleaseResource ToResource(this DownloadDecision model)
|
||||
//FullSeason = parsedMovieInfo.FullSeason,
|
||||
//SeasonNumber = parsedMovieInfo.SeasonNumber,
|
||||
Language = parsedMovieInfo.Language,
|
||||
AirDate = "",
|
||||
SeriesTitle = parsedMovieInfo.MovieTitle,
|
||||
Year = parsedMovieInfo.Year,
|
||||
MovieTitle = parsedMovieInfo.MovieTitle,
|
||||
EpisodeNumbers = new int[0],
|
||||
AbsoluteEpisodeNumbers = new int[0],
|
||||
Approved = model.Approved,
|
||||
@ -125,8 +128,10 @@ public static ReleaseResource ToResource(this DownloadDecision model)
|
||||
CommentUrl = releaseInfo.CommentUrl,
|
||||
DownloadUrl = releaseInfo.DownloadUrl,
|
||||
InfoUrl = releaseInfo.InfoUrl,
|
||||
DownloadAllowed = downloadAllowed,
|
||||
MappingResult = mappingResult,
|
||||
//ReleaseWeight
|
||||
|
||||
SuspectedMovieId = movieId,
|
||||
|
||||
MagnetUrl = torrentInfo.MagnetUrl,
|
||||
InfoHash = torrentInfo.InfoHash,
|
||||
@ -161,8 +166,8 @@ public static ReleaseResource ToResource(this DownloadDecision model)
|
||||
FullSeason = parsedEpisodeInfo.FullSeason,
|
||||
SeasonNumber = parsedEpisodeInfo.SeasonNumber,
|
||||
Language = parsedEpisodeInfo.Language,
|
||||
AirDate = parsedEpisodeInfo.AirDate,
|
||||
SeriesTitle = parsedEpisodeInfo.SeriesTitle,
|
||||
//AirDate = parsedEpisodeInfo.AirDate,
|
||||
//SeriesTitle = parsedEpisodeInfo.SeriesTitle,
|
||||
EpisodeNumbers = parsedEpisodeInfo.EpisodeNumbers,
|
||||
AbsoluteEpisodeNumbers = parsedEpisodeInfo.AbsoluteEpisodeNumbers,
|
||||
Approved = model.Approved,
|
||||
@ -175,7 +180,7 @@ public static ReleaseResource ToResource(this DownloadDecision model)
|
||||
CommentUrl = releaseInfo.CommentUrl,
|
||||
DownloadUrl = releaseInfo.DownloadUrl,
|
||||
InfoUrl = releaseInfo.InfoUrl,
|
||||
DownloadAllowed = downloadAllowed,
|
||||
//DownloadAllowed = downloadAllowed,
|
||||
//ReleaseWeight
|
||||
|
||||
MagnetUrl = torrentInfo.MagnetUrl,
|
||||
|
57
src/NzbDrone.Api/Movies/AlternativeTitleModule.cs
Normal file
57
src/NzbDrone.Api/Movies/AlternativeTitleModule.cs
Normal file
@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Marr.Data;
|
||||
using Nancy;
|
||||
using NzbDrone.Api;
|
||||
using NzbDrone.Api.Movie;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.MediaCover;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.MetadataSource;
|
||||
using NzbDrone.Core.MetadataSource.RadarrAPI;
|
||||
using NzbDrone.Core.Movies.AlternativeTitles;
|
||||
using NzbDrone.Core.RootFolders;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Tv.Events;
|
||||
|
||||
namespace NzbDrone.Api.Movie
|
||||
{
|
||||
public class AlternativeTitleModule : NzbDroneRestModule<AlternativeTitleResource>
|
||||
{
|
||||
private readonly IAlternativeTitleService _altTitleService;
|
||||
private readonly IMovieService _movieService;
|
||||
private readonly IRadarrAPIClient _radarrApi;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
|
||||
public AlternativeTitleModule(IAlternativeTitleService altTitleService, IMovieService movieService, IRadarrAPIClient radarrApi, IEventAggregator eventAggregator)
|
||||
: base("/alttitle")
|
||||
{
|
||||
_altTitleService = altTitleService;
|
||||
_movieService = movieService;
|
||||
_radarrApi = radarrApi;
|
||||
CreateResource = AddTitle;
|
||||
GetResourceById = GetTitle;
|
||||
_eventAggregator = eventAggregator;
|
||||
}
|
||||
|
||||
private int AddTitle(AlternativeTitleResource altTitle)
|
||||
{
|
||||
var title = altTitle.ToModel();
|
||||
var movie = _movieService.GetMovie(altTitle.MovieId);
|
||||
var newTitle = _radarrApi.AddNewAlternativeTitle(title, movie.TmdbId);
|
||||
|
||||
var addedTitle = _altTitleService.AddAltTitle(newTitle, movie);
|
||||
_eventAggregator.PublishEvent(new MovieUpdatedEvent(movie));
|
||||
return addedTitle.Id;
|
||||
}
|
||||
|
||||
private AlternativeTitleResource GetTitle(int id)
|
||||
{
|
||||
return _altTitleService.GetById(id).ToResource();
|
||||
}
|
||||
}
|
||||
}
|
63
src/NzbDrone.Api/Movies/AlternativeYearModule.cs
Normal file
63
src/NzbDrone.Api/Movies/AlternativeYearModule.cs
Normal file
@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Marr.Data;
|
||||
using Nancy;
|
||||
using NzbDrone.Api;
|
||||
using NzbDrone.Api.Movie;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Messaging;
|
||||
using NzbDrone.Core.MediaCover;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.MetadataSource;
|
||||
using NzbDrone.Core.MetadataSource.RadarrAPI;
|
||||
using NzbDrone.Core.Movies.AlternativeTitles;
|
||||
using NzbDrone.Core.RootFolders;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Tv.Events;
|
||||
|
||||
namespace NzbDrone.Api.Movie
|
||||
{
|
||||
public class AlternativeYearModule : NzbDroneRestModule<AlternativeYearResource>
|
||||
{
|
||||
private readonly IMovieService _movieService;
|
||||
private readonly IRadarrAPIClient _radarrApi;
|
||||
private readonly ICached<int> _yearCache;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
|
||||
public AlternativeYearModule(IMovieService movieService, IRadarrAPIClient radarrApi, ICacheManager cacheManager, IEventAggregator eventAggregator)
|
||||
: base("/altyear")
|
||||
{
|
||||
_movieService = movieService;
|
||||
_radarrApi = radarrApi;
|
||||
CreateResource = AddYear;
|
||||
GetResourceById = GetYear;
|
||||
_yearCache = cacheManager.GetCache<int>(GetType(), "altYears");
|
||||
_eventAggregator = eventAggregator;
|
||||
}
|
||||
|
||||
private int AddYear(AlternativeYearResource altYear)
|
||||
{
|
||||
var id = new Random().Next();
|
||||
_yearCache.Set(id.ToString(), altYear.Year, TimeSpan.FromMinutes(1));
|
||||
var movie = _movieService.GetMovie(altYear.MovieId);
|
||||
var newYear = _radarrApi.AddNewAlternativeYear(altYear.Year, movie.TmdbId);
|
||||
movie.SecondaryYear = newYear.Year;
|
||||
movie.SecondaryYearSourceId = newYear.SourceId;
|
||||
_movieService.UpdateMovie(movie);
|
||||
_eventAggregator.PublishEvent(new MovieUpdatedEvent(movie));
|
||||
return id;
|
||||
}
|
||||
|
||||
private AlternativeYearResource GetYear(int id)
|
||||
{
|
||||
return new AlternativeYearResource
|
||||
{
|
||||
Year = _yearCache.Find(id.ToString())
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
75
src/NzbDrone.Api/Movies/AlternativeYearResource.cs
Normal file
75
src/NzbDrone.Api/Movies/AlternativeYearResource.cs
Normal file
@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Api.REST;
|
||||
using NzbDrone.Core.MediaCover;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Api.Series;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Movies.AlternativeTitles;
|
||||
using NzbDrone.Core.Parser;
|
||||
|
||||
namespace NzbDrone.Api.Movie
|
||||
{
|
||||
public class AlternativeYearResource : RestResource
|
||||
{
|
||||
public AlternativeYearResource()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//Todo: Sorters should be done completely on the client
|
||||
//Todo: Is there an easy way to keep IgnoreArticlesWhenSorting in sync between, Series, History, Missing?
|
||||
//Todo: We should get the entire Profile instead of ID and Name separately
|
||||
|
||||
public int MovieId { get; set; }
|
||||
public int Year { get; set; }
|
||||
|
||||
//TODO: Add series statistics as a property of the series (instead of individual properties)
|
||||
}
|
||||
|
||||
/*public static class AlternativeYearResourceMapper
|
||||
{
|
||||
/*public static AlternativeYearResource ToResource(this AlternativeTitle model)
|
||||
{
|
||||
if (model == null) return null;
|
||||
|
||||
AlternativeTitleResource resource = null;
|
||||
|
||||
return new AlternativeTitleResource
|
||||
{
|
||||
Id = model.Id,
|
||||
SourceType = model.SourceType,
|
||||
MovieId = model.MovieId,
|
||||
Title = model.Title,
|
||||
SourceId = model.SourceId,
|
||||
Votes = model.Votes,
|
||||
VoteCount = model.VoteCount,
|
||||
Language = model.Language
|
||||
};
|
||||
}
|
||||
|
||||
public static AlternativeTitle ToModel(this AlternativeTitleResource resource)
|
||||
{
|
||||
if (resource == null) return null;
|
||||
|
||||
return new AlternativeTitle
|
||||
{
|
||||
Id = resource.Id,
|
||||
SourceType = resource.SourceType,
|
||||
MovieId = resource.MovieId,
|
||||
Title = resource.Title,
|
||||
SourceId = resource.SourceId,
|
||||
Votes = resource.Votes,
|
||||
VoteCount = resource.VoteCount,
|
||||
Language = resource.Language
|
||||
};
|
||||
}
|
||||
|
||||
public static List<AlternativeTitleResource> ToResource(this IEnumerable<AlternativeTitle> movies)
|
||||
{
|
||||
return movies.Select(ToResource).ToList();
|
||||
}
|
||||
}*/
|
||||
}
|
@ -119,6 +119,9 @@
|
||||
<Compile Include="Frontend\Mappers\RobotsTxtMapper.cs" />
|
||||
<Compile Include="Indexers\ReleaseModuleBase.cs" />
|
||||
<Compile Include="Indexers\ReleasePushModule.cs" />
|
||||
<Compile Include="Movies\AlternativeTitleModule.cs" />
|
||||
<Compile Include="Movies\AlternativeYearResource.cs" />
|
||||
<Compile Include="Movies\AlternativeYearModule.cs" />
|
||||
<Compile Include="Movies\MovieModuleWithSignalR.cs" />
|
||||
<Compile Include="Movies\MovieBulkImportModule.cs" />
|
||||
<Compile Include="Movies\MovieFileModule.cs" />
|
||||
@ -240,7 +243,7 @@
|
||||
<Compile Include="RootFolders\RootFolderModule.cs" />
|
||||
<Compile Include="RootFolders\RootFolderResource.cs" />
|
||||
<Compile Include="SeasonPass\SeasonPassResource.cs" />
|
||||
<Compile Include="Series\AlternateTitleResource.cs" />
|
||||
<Compile Include="Series\AlternativeTitleResource.cs" />
|
||||
<Compile Include="Series\MovieFileResource.cs" />
|
||||
<Compile Include="Series\FetchMovieListModule.cs" />
|
||||
<Compile Include="Series\SeasonResource.cs" />
|
||||
|
@ -105,7 +105,7 @@ private JsonResponse<QueueResource> Grab()
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
_downloadService.DownloadReport(pendingRelease.RemoteMovie);
|
||||
_downloadService.DownloadReport(pendingRelease.RemoteMovie, false);
|
||||
|
||||
return resource.AsResponse();
|
||||
}
|
||||
|
@ -1,9 +0,0 @@
|
||||
namespace NzbDrone.Api.Series
|
||||
{
|
||||
public class AlternateTitleResource
|
||||
{
|
||||
public string Title { get; set; }
|
||||
public int? SeasonNumber { get; set; }
|
||||
public int? SceneSeasonNumber { get; set; }
|
||||
}
|
||||
}
|
81
src/NzbDrone.Api/Series/AlternativeTitleResource.cs
Normal file
81
src/NzbDrone.Api/Series/AlternativeTitleResource.cs
Normal file
@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Api.REST;
|
||||
using NzbDrone.Core.MediaCover;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Api.Series;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Movies.AlternativeTitles;
|
||||
using NzbDrone.Core.Parser;
|
||||
|
||||
namespace NzbDrone.Api.Movie
|
||||
{
|
||||
public class AlternativeTitleResource : RestResource
|
||||
{
|
||||
public AlternativeTitleResource()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//Todo: Sorters should be done completely on the client
|
||||
//Todo: Is there an easy way to keep IgnoreArticlesWhenSorting in sync between, Series, History, Missing?
|
||||
//Todo: We should get the entire Profile instead of ID and Name separately
|
||||
|
||||
public SourceType SourceType { get; set; }
|
||||
public int MovieId { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string CleanTitle { get; set; }
|
||||
public int SourceId { get; set; }
|
||||
public int Votes { get; set; }
|
||||
public int VoteCount { get; set; }
|
||||
public Language Language { get; set; }
|
||||
|
||||
//TODO: Add series statistics as a property of the series (instead of individual properties)
|
||||
}
|
||||
|
||||
public static class AlternativeTitleResourceMapper
|
||||
{
|
||||
public static AlternativeTitleResource ToResource(this AlternativeTitle model)
|
||||
{
|
||||
if (model == null) return null;
|
||||
|
||||
AlternativeTitleResource resource = null;
|
||||
|
||||
return new AlternativeTitleResource
|
||||
{
|
||||
Id = model.Id,
|
||||
SourceType = model.SourceType,
|
||||
MovieId = model.MovieId,
|
||||
Title = model.Title,
|
||||
SourceId = model.SourceId,
|
||||
Votes = model.Votes,
|
||||
VoteCount = model.VoteCount,
|
||||
Language = model.Language
|
||||
};
|
||||
}
|
||||
|
||||
public static AlternativeTitle ToModel(this AlternativeTitleResource resource)
|
||||
{
|
||||
if (resource == null) return null;
|
||||
|
||||
return new AlternativeTitle
|
||||
{
|
||||
Id = resource.Id,
|
||||
SourceType = resource.SourceType,
|
||||
MovieId = resource.MovieId,
|
||||
Title = resource.Title,
|
||||
SourceId = resource.SourceId,
|
||||
Votes = resource.Votes,
|
||||
VoteCount = resource.VoteCount,
|
||||
Language = resource.Language
|
||||
};
|
||||
}
|
||||
|
||||
public static List<AlternativeTitleResource> ToResource(this IEnumerable<AlternativeTitle> movies)
|
||||
{
|
||||
return movies.Select(ToResource).ToList();
|
||||
}
|
||||
}
|
||||
}
|
@ -21,7 +21,9 @@ public MovieResource()
|
||||
|
||||
//View Only
|
||||
public string Title { get; set; }
|
||||
public List<AlternateTitleResource> AlternateTitles { get; set; }
|
||||
public List<AlternativeTitleResource> AlternativeTitles { get; set; }
|
||||
public int? SecondaryYear { get; set; }
|
||||
public int SecondaryYearSourceId { get; set; }
|
||||
public string SortTitle { get; set; }
|
||||
public long? SizeOnDisk { get; set; }
|
||||
public MovieStatusType Status { get; set; }
|
||||
@ -62,7 +64,7 @@ public MovieResource()
|
||||
public DateTime Added { get; set; }
|
||||
public AddMovieOptions AddOptions { get; set; }
|
||||
public Ratings Ratings { get; set; }
|
||||
public List<string> AlternativeTitles { get; set; }
|
||||
//public List<string> AlternativeTitles { get; set; }
|
||||
public MovieFileResource MovieFile { get; set; }
|
||||
|
||||
//TODO: Add series statistics as a property of the series (instead of individual properties)
|
||||
@ -107,6 +109,8 @@ public static MovieResource ToResource(this Core.Tv.Movie model)
|
||||
downloaded = true;
|
||||
movieFile = model.MovieFile.Value.ToResource();
|
||||
}
|
||||
|
||||
//model.AlternativeTitles.LazyLoad();
|
||||
|
||||
return new MovieResource
|
||||
{
|
||||
@ -131,6 +135,8 @@ public static MovieResource ToResource(this Core.Tv.Movie model)
|
||||
Images = model.Images,
|
||||
|
||||
Year = model.Year,
|
||||
SecondaryYear = model.SecondaryYear,
|
||||
SecondaryYearSourceId = model.SecondaryYearSourceId,
|
||||
|
||||
Path = model.Path,
|
||||
ProfileId = model.ProfileId,
|
||||
@ -156,7 +162,7 @@ public static MovieResource ToResource(this Core.Tv.Movie model)
|
||||
Tags = model.Tags,
|
||||
Added = model.Added,
|
||||
AddOptions = model.AddOptions,
|
||||
AlternativeTitles = model.AlternativeTitles,
|
||||
AlternativeTitles = model.AlternativeTitles.ToResource(),
|
||||
Ratings = model.Ratings,
|
||||
MovieFile = movieFile,
|
||||
YouTubeTrailerId = model.YouTubeTrailerId,
|
||||
@ -189,6 +195,8 @@ public static Core.Tv.Movie ToModel(this MovieResource resource)
|
||||
Images = resource.Images,
|
||||
|
||||
Year = resource.Year,
|
||||
SecondaryYear = resource.SecondaryYear,
|
||||
SecondaryYearSourceId = resource.SecondaryYearSourceId,
|
||||
|
||||
Path = resource.Path,
|
||||
ProfileId = resource.ProfileId,
|
||||
@ -209,7 +217,7 @@ public static Core.Tv.Movie ToModel(this MovieResource resource)
|
||||
Tags = resource.Tags,
|
||||
Added = resource.Added,
|
||||
AddOptions = resource.AddOptions,
|
||||
AlternativeTitles = resource.AlternativeTitles,
|
||||
//AlternativeTitles = resource.AlternativeTitles,
|
||||
Ratings = resource.Ratings,
|
||||
YouTubeTrailerId = resource.YouTubeTrailerId,
|
||||
Studio = resource.Studio
|
||||
|
@ -199,7 +199,7 @@ private void PopulateAlternateTitles(SeriesResource resource)
|
||||
|
||||
if (mappings == null) return;
|
||||
|
||||
resource.AlternateTitles = mappings.Select(v => new AlternateTitleResource { Title = v.Title, SeasonNumber = v.SeasonNumber, SceneSeasonNumber = v.SceneSeasonNumber }).ToList();
|
||||
//resource.AlternateTitles = mappings.Select(v => new AlternateTitleResource { Title = v.Title, SeasonNumber = v.SeasonNumber, SceneSeasonNumber = v.SceneSeasonNumber }).ToList();
|
||||
}
|
||||
|
||||
public void Handle(EpisodeImportedEvent message)
|
||||
|
@ -20,7 +20,7 @@ public SeriesResource()
|
||||
|
||||
//View Only
|
||||
public string Title { get; set; }
|
||||
public List<AlternateTitleResource> AlternateTitles { get; set; }
|
||||
//public List<AlternativeTitleResource> AlternateTitles { get; set; }
|
||||
public string SortTitle { get; set; }
|
||||
|
||||
public int SeasonCount
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using NzbDrone.Core.Datastore.Events;
|
||||
@ -12,7 +12,7 @@ public class TaskModule : NzbDroneRestModuleWithSignalR<TaskResource, ScheduledT
|
||||
{
|
||||
private readonly ITaskManager _taskManager;
|
||||
|
||||
private static readonly Regex NameRegex = new Regex("(?<!^)[A-Z]", RegexOptions.Compiled);
|
||||
private static readonly Regex NameRegex = new Regex("(?<!^)[A-Z][a-z]", RegexOptions.Compiled);
|
||||
|
||||
public TaskModule(ITaskManager taskManager, IBroadcastSignalRMessage broadcastSignalRMessage)
|
||||
: base(broadcastSignalRMessage, "system/task")
|
||||
|
@ -263,7 +263,7 @@ public void should_not_allow_download_if_series_is_unknown()
|
||||
|
||||
result.Should().HaveCount(1);
|
||||
|
||||
result.First().RemoteMovie.DownloadAllowed.Should().BeFalse();
|
||||
//result.First().RemoteMovie.DownloadAllowed.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -278,7 +278,7 @@ public void should_not_allow_download_if_no_episodes_found()
|
||||
|
||||
result.Should().HaveCount(1);
|
||||
|
||||
result.First().RemoteMovie.DownloadAllowed.Should().BeFalse();
|
||||
//result.First().RemoteMovie.DownloadAllowed.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -76,7 +76,7 @@ public void should_download_report_if_movie_was_not_already_downloaded()
|
||||
decisions.Add(new DownloadDecision(remoteMovie));
|
||||
|
||||
Subject.ProcessDecisions(decisions);
|
||||
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.IsAny<RemoteMovie>()), Times.Once());
|
||||
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.IsAny<RemoteMovie>(), false), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -89,7 +89,7 @@ public void should_only_download_movie_once()
|
||||
decisions.Add(new DownloadDecision(remoteMovie));
|
||||
|
||||
Subject.ProcessDecisions(decisions);
|
||||
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.IsAny<RemoteMovie>()), Times.Once());
|
||||
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.IsAny<RemoteMovie>(), false), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -157,7 +157,7 @@ public void should_not_add_to_downloaded_list_when_download_fails()
|
||||
var decisions = new List<DownloadDecision>();
|
||||
decisions.Add(new DownloadDecision(remoteMovie));
|
||||
|
||||
Mocker.GetMock<IDownloadService>().Setup(s => s.DownloadReport(It.IsAny<RemoteMovie>())).Throws(new Exception());
|
||||
Mocker.GetMock<IDownloadService>().Setup(s => s.DownloadReport(It.IsAny<RemoteMovie>(), false)).Throws(new Exception());
|
||||
Subject.ProcessDecisions(decisions).Grabbed.Should().BeEmpty();
|
||||
ExceptionVerification.ExpectedWarns(1);
|
||||
}
|
||||
@ -183,7 +183,7 @@ public void should_not_grab_if_pending()
|
||||
decisions.Add(new DownloadDecision(remoteMovie));
|
||||
|
||||
Subject.ProcessDecisions(decisions);
|
||||
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.IsAny<RemoteMovie>()), Times.Never());
|
||||
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.IsAny<RemoteMovie>(), false), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -8,7 +8,7 @@ namespace NzbDrone.Core.Test.HealthCheck
|
||||
[TestFixture]
|
||||
public class HealthCheckFixture : CoreTest
|
||||
{
|
||||
private const string WikiRoot = "https://github.com/Sonarr/Sonarr/wiki/";
|
||||
private const string WikiRoot = "https://github.com/Radarr/Radarr/wiki/";
|
||||
|
||||
[TestCase("I blew up because of some weird user mistake", null, WikiRoot + "Health-checks#i-blew-up-because-of-some-weird-user-mistake")]
|
||||
[TestCase("I blew up because of some weird user mistake", "#my-health-check", WikiRoot + "Health-checks#my-health-check")]
|
||||
|
@ -54,6 +54,7 @@ public class LanguageParserFixture : CoreTest
|
||||
[TestCase("Der.Soldat.James.German.Bluray.FuckYou.Pso.Why.cant.you.follow.scene.rules.1998", Language.German)]
|
||||
[TestCase("Passengers.German.DL.AC3.Dubbed..BluRay.x264-PsO", Language.German)]
|
||||
[TestCase("Valana la Legende FRENCH BluRay 720p 2016 kjhlj", Language.French)]
|
||||
[TestCase("Smurfs.The.Lost.Village.2017.1080p.BluRay.HebDub.x264-iSrael",Language.Hebrew)]
|
||||
public void should_parse_language(string postTitle, Language language)
|
||||
{
|
||||
var result = Parser.Parser.ParseMovieTitle(postTitle, true);
|
||||
|
@ -6,7 +6,9 @@
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.DataAugmentation.Scene;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Movies.AlternativeTitles;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Tv;
|
||||
@ -43,7 +45,7 @@ public void Setup()
|
||||
.With(m => m.Title = "Fack Ju Göthe 2")
|
||||
.With(m => m.CleanTitle = "fackjugoethe2")
|
||||
.With(m => m.Year = 2015)
|
||||
.With(m => m.AlternativeTitles = new List<string> { "Fack Ju Göthe 2: Same same" })
|
||||
.With(m => m.AlternativeTitles = new LazyList<AlternativeTitle>( new List<AlternativeTitle> {new AlternativeTitle("Fack Ju Göthe 2: Same same")}))
|
||||
.Build();
|
||||
|
||||
_episodes = Builder<Episode>.CreateListOfSize(1)
|
||||
@ -80,7 +82,7 @@ public void Setup()
|
||||
|
||||
_alternativeTitleInfo = new ParsedMovieInfo
|
||||
{
|
||||
MovieTitle = _movie.AlternativeTitles.First(),
|
||||
MovieTitle = _movie.AlternativeTitles.First().Title,
|
||||
Year = _movie.Year,
|
||||
};
|
||||
|
||||
|
@ -307,6 +307,7 @@ public void should_parse_quality_from_extension(string title)
|
||||
|
||||
[TestCase("Movie.Title.2016.1080p.KORSUB.WEBRip.x264.AAC2.0-RADARR", "korsub")]
|
||||
[TestCase("Movie.Title.2016.1080p.KORSUBS.WEBRip.x264.AAC2.0-RADARR", "korsubs")]
|
||||
[TestCase("Wonder Woman 2017 HC 720p HDRiP DD5 1 x264-LEGi0N", "Generic Hardcoded Subs")]
|
||||
public void should_parse_hardcoded_subs(string postTitle, string sub)
|
||||
{
|
||||
QualityParser.ParseQuality(postTitle).HardcodedSubs.Should().Be(sub);
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@ -39,15 +39,15 @@ public void Init_should_skip_if_any_profiles_already_exist()
|
||||
|
||||
|
||||
[Test]
|
||||
public void should_not_be_able_to_delete_profile_if_assigned_to_series()
|
||||
public void should_not_be_able_to_delete_profile_if_assigned_to_movie()
|
||||
{
|
||||
var seriesList = Builder<Series>.CreateListOfSize(3)
|
||||
var movieList = Builder<Movie>.CreateListOfSize(3)
|
||||
.Random(1)
|
||||
.With(c => c.ProfileId = 2)
|
||||
.Build().ToList();
|
||||
|
||||
|
||||
Mocker.GetMock<ISeriesService>().Setup(c => c.GetAllSeries()).Returns(seriesList);
|
||||
Mocker.GetMock<IMovieService>().Setup(c => c.GetAllMovies()).Returns(movieList);
|
||||
|
||||
Assert.Throws<ProfileInUseException>(() => Subject.Delete(2));
|
||||
|
||||
@ -57,15 +57,15 @@ public void should_not_be_able_to_delete_profile_if_assigned_to_series()
|
||||
|
||||
|
||||
[Test]
|
||||
public void should_delete_profile_if_not_assigned_to_series()
|
||||
public void should_delete_profile_if_not_assigned_to_movie()
|
||||
{
|
||||
var seriesList = Builder<Series>.CreateListOfSize(3)
|
||||
var movieList = Builder<Movie>.CreateListOfSize(3)
|
||||
.All()
|
||||
.With(c => c.ProfileId = 2)
|
||||
.Build().ToList();
|
||||
|
||||
|
||||
Mocker.GetMock<ISeriesService>().Setup(c => c.GetAllSeries()).Returns(seriesList);
|
||||
Mocker.GetMock<IMovieService>().Setup(c => c.GetAllMovies()).Returns(movieList);
|
||||
|
||||
Subject.Delete(1);
|
||||
|
||||
|
@ -3,8 +3,10 @@
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Web.Hosting;
|
||||
using Marr.Data;
|
||||
using Marr.Data.QGen;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Datastore.Events;
|
||||
using NzbDrone.Core.Datastore.Extensions;
|
||||
@ -48,7 +50,7 @@ public BasicRepository(IDatabase database, IEventAggregator eventAggregator)
|
||||
_eventAggregator = eventAggregator;
|
||||
}
|
||||
|
||||
protected QueryBuilder<TModel> Query => DataMapper.Query<TModel>();
|
||||
protected QueryBuilder<TModel> Query => AddJoinQueries(DataMapper.Query<TModel>());
|
||||
|
||||
protected void Delete(Expression<Func<TModel, bool>> filter)
|
||||
{
|
||||
@ -246,18 +248,23 @@ public void SetFields(TModel model, params Expression<Func<TModel, object>>[] pr
|
||||
|
||||
public virtual PagingSpec<TModel> GetPaged(PagingSpec<TModel> pagingSpec)
|
||||
{
|
||||
pagingSpec.Records = GetPagedQuery(Query, pagingSpec).ToList();
|
||||
pagingSpec.Records = GetPagedQuery(Query, pagingSpec).Skip(pagingSpec.PagingOffset())
|
||||
.Take(pagingSpec.PageSize).ToList();
|
||||
pagingSpec.TotalRecords = GetPagedQuery(Query, pagingSpec).GetRowCount();
|
||||
|
||||
var queryStr = GetPagedQuery(Query, pagingSpec).BuildQuery();
|
||||
var beforeQuery = Query.BuildQuery();
|
||||
|
||||
pagingSpec.SortKey = beforeQuery;
|
||||
pagingSpec.SortKey = queryStr;
|
||||
|
||||
return pagingSpec;
|
||||
}
|
||||
|
||||
protected virtual SortBuilder<TModel> GetPagedQuery(QueryBuilder<TModel> query, PagingSpec<TModel> pagingSpec)
|
||||
{
|
||||
return query.Where(pagingSpec.FilterExpression)
|
||||
.OrderBy(pagingSpec.OrderByClause(), pagingSpec.ToSortDirection())
|
||||
.Skip(pagingSpec.PagingOffset())
|
||||
.Take(pagingSpec.PageSize);
|
||||
.OrderBy(pagingSpec.OrderByClause(), pagingSpec.ToSortDirection());
|
||||
}
|
||||
|
||||
protected void ModelCreated(TModel model)
|
||||
@ -283,6 +290,11 @@ private void PublishModelEvent(TModel model, ModelAction action)
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual QueryBuilder<TModel> AddJoinQueries(QueryBuilder<TModel> baseQuery)
|
||||
{
|
||||
return baseQuery;
|
||||
}
|
||||
|
||||
protected virtual bool PublishModelEvents => false;
|
||||
}
|
||||
}
|
||||
|
@ -110,10 +110,10 @@ public IDatabase Create(MigrationContext migrationContext)
|
||||
{
|
||||
if (OsInfo.IsOsx)
|
||||
{
|
||||
throw new CorruptDatabaseException("Database file: {0} is corrupt, restore from backup if available. See: https://github.com/Sonarr/Sonarr/wiki/FAQ#i-use-sonarr-on-a-mac-and-it-suddenly-stopped-working-what-happened", ex, fileName);
|
||||
throw new CorruptDatabaseException("Database file: {0} is corrupt, restore from backup if available. See: https://github.com/Radarr/Radarr/wiki/FAQ#i-use-radarr-on-a-mac-and-it-suddenly-stopped-working-what-happened", ex, fileName);
|
||||
}
|
||||
|
||||
throw new CorruptDatabaseException("Database file: {0} is corrupt, restore from backup if available. See: https://github.com/Sonarr/Sonarr/wiki/FAQ#i-am-getting-an-error-database-disk-image-is-malformed", ex, fileName);
|
||||
throw new CorruptDatabaseException("Database file: {0} is corrupt, restore from backup if available. See: https://github.com/Radarr/Radarr/wiki/FAQ#i-am-getting-an-error-database-disk-image-is-malformed", ex, fileName);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,12 +28,12 @@ public static RelationshipBuilder<TParent> Relationship<TParent>(this ColumnMapB
|
||||
return mapBuilder.Relationships.AutoMapComplexTypeProperties<ILazyLoaded>();
|
||||
}
|
||||
|
||||
public static RelationshipBuilder<TParent> HasMany<TParent, TChild>(this RelationshipBuilder<TParent> relationshipBuilder, Expression<Func<TParent, LazyList<TChild>>> portalExpression, Func<TParent, int> childIdSelector)
|
||||
public static RelationshipBuilder<TParent> HasMany<TParent, TChild>(this RelationshipBuilder<TParent> relationshipBuilder, Expression<Func<TParent, LazyList<TChild>>> portalExpression, Func<TChild, int> parentIdSelector)
|
||||
where TParent : ModelBase
|
||||
where TChild : ModelBase
|
||||
{
|
||||
return relationshipBuilder.For(portalExpression.GetMemberName())
|
||||
.LazyLoad((db, parent) => db.Query<TChild>().Where(c => c.Id == childIdSelector(parent)).ToList());
|
||||
.LazyLoad((db, parent) => db.Query<TChild>().Where(c => parentIdSelector(c) == parent.Id).ToList());
|
||||
}
|
||||
|
||||
private static string GetMemberName<T, TMember>(this Expression<Func<T, TMember>> member)
|
||||
|
@ -0,0 +1,39 @@
|
||||
using System.Data;
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Globalization;
|
||||
using Marr.Data.QGen;
|
||||
using NzbDrone.Common.Extensions;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(140)]
|
||||
public class add_alternative_titles_table : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
if (!this.Schema.Schema("dbo").Table("alternative_titles").Exists())
|
||||
{
|
||||
Create.TableForModel("AlternativeTitles")
|
||||
.WithColumn("MovieId").AsInt64().NotNullable()
|
||||
.WithColumn("Title").AsString().NotNullable()
|
||||
.WithColumn("CleanTitle").AsString().NotNullable()
|
||||
.WithColumn("SourceType").AsInt64().WithDefault(0)
|
||||
.WithColumn("SourceId").AsInt64().WithDefault(0)
|
||||
.WithColumn("Votes").AsInt64().WithDefault(0)
|
||||
.WithColumn("VoteCount").AsInt64().WithDefault(0)
|
||||
.WithColumn("Language").AsInt64().WithDefault(0);
|
||||
|
||||
Delete.Column("AlternativeTitles").FromTable("Movies");
|
||||
}
|
||||
|
||||
Alter.Table("Movies").AddColumn("SecondaryYear").AsInt32().Nullable();
|
||||
Alter.Table("Movies").AddColumn("SecondaryYearSourceId").AsInt64().Nullable().WithDefault(0);
|
||||
}
|
||||
}
|
||||
}
|
@ -36,6 +36,45 @@
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
using NzbDrone.Core.NetImport;
|
||||
using NzbDrone.Core.NetImport.ImportExclusions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Marr.Data;
|
||||
using Marr.Data.Mapping;
|
||||
using NzbDrone.Common.Reflection;
|
||||
using NzbDrone.Core.Blacklisting;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.DataAugmentation.Scene;
|
||||
using NzbDrone.Core.Datastore.Converters;
|
||||
using NzbDrone.Core.Datastore.Extensions;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Download.Pending;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Instrumentation;
|
||||
using NzbDrone.Core.Jobs;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Profiles.Delay;
|
||||
using NzbDrone.Core.RemotePathMappings;
|
||||
using NzbDrone.Core.Notifications;
|
||||
using NzbDrone.Core.Organizer;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Restrictions;
|
||||
using NzbDrone.Core.RootFolders;
|
||||
using NzbDrone.Core.SeriesStats;
|
||||
using NzbDrone.Core.Tags;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Core.Authentication;
|
||||
using NzbDrone.Core.Extras.Metadata;
|
||||
using NzbDrone.Core.Extras.Metadata.Files;
|
||||
using NzbDrone.Core.Extras.Others;
|
||||
using NzbDrone.Core.Extras.Subtitles;
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
using NzbDrone.Core.Movies.AlternativeTitles;
|
||||
using NzbDrone.Core.NetImport;
|
||||
using NzbDrone.Core.NetImport.ImportExclusions;
|
||||
|
||||
namespace NzbDrone.Core.Datastore
|
||||
{
|
||||
@ -101,12 +140,19 @@ public static void Map()
|
||||
query: (db, parent) => db.Query<Movie>().Where(c => c.MovieFileId == parent.Id).ToList())
|
||||
.HasOne(file => file.Movie, file => file.MovieId);
|
||||
|
||||
Mapper.Entity<Movie>().RegisterModel("Movies")
|
||||
Mapper.Entity<Movie>().RegisterModel("Movies")
|
||||
.Ignore(s => s.RootFolderPath)
|
||||
.Relationship()
|
||||
.HasOne(s => s.Profile, s => s.ProfileId)
|
||||
.HasOne(m => m.MovieFile, m => m.MovieFileId);
|
||||
|
||||
Mapper.Entity<AlternativeTitle>().RegisterModel("AlternativeTitles")
|
||||
.For(t => t.Id)
|
||||
.SetAltName("AltTitle_Id")
|
||||
.Relationship()
|
||||
.HasOne(t => t.Movie, t => t.MovieId);
|
||||
|
||||
|
||||
Mapper.Entity<ImportExclusion>().RegisterModel("ImportExclusions");
|
||||
|
||||
|
||||
|
@ -113,11 +113,11 @@ private IEnumerable<DownloadDecision> GetMovieDecisions(List<ReleaseInfo> report
|
||||
var remoteMovie = result.RemoteMovie;
|
||||
|
||||
remoteMovie.Release = report;
|
||||
remoteMovie.MappingResult = result.MappingResultType;
|
||||
|
||||
if (result.MappingResultType != MappingResultType.Success && result.MappingResultType != MappingResultType.SuccessLenientMapping)
|
||||
{
|
||||
var rejection = result.ToRejection();
|
||||
remoteMovie.Movie = null; // HACK: For now!
|
||||
decision = new DownloadDecision(remoteMovie, rejection);
|
||||
|
||||
}
|
||||
@ -125,7 +125,7 @@ private IEnumerable<DownloadDecision> GetMovieDecisions(List<ReleaseInfo> report
|
||||
{
|
||||
if (parsedMovieInfo.Quality.HardcodedSubs.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
remoteMovie.DownloadAllowed = true;
|
||||
//remoteMovie.DownloadAllowed = true;
|
||||
if (_configService.AllowHardcodedSubs)
|
||||
{
|
||||
decision = GetDecisionForReport(remoteMovie, searchCriteria);
|
||||
@ -146,7 +146,7 @@ private IEnumerable<DownloadDecision> GetMovieDecisions(List<ReleaseInfo> report
|
||||
}
|
||||
else
|
||||
{
|
||||
remoteMovie.DownloadAllowed = true;
|
||||
//remoteMovie.DownloadAllowed = true;
|
||||
decision = GetDecisionForReport(remoteMovie, searchCriteria);
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.Profiles.Delay;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Parser;
|
||||
|
||||
namespace NzbDrone.Core.DecisionEngine
|
||||
{
|
||||
@ -36,13 +37,13 @@ public List<DownloadDecision> PrioritizeDecisions(List<DownloadDecision> decisio
|
||||
|
||||
public List<DownloadDecision> PrioritizeDecisionsForMovies(List<DownloadDecision> decisions)
|
||||
{
|
||||
return decisions.Where(c => c.RemoteMovie.Movie != null)
|
||||
return decisions.Where(c => c.RemoteMovie.MappingResult == MappingResultType.Success || c.RemoteMovie.MappingResult == MappingResultType.SuccessLenientMapping)
|
||||
.GroupBy(c => c.RemoteMovie.Movie.Id, (movieId, downloadDecisions) =>
|
||||
{
|
||||
return downloadDecisions.OrderByDescending(decision => decision, new DownloadDecisionComparer(_delayProfileService, _configService));
|
||||
})
|
||||
.SelectMany(c => c)
|
||||
.Union(decisions.Where(c => c.RemoteMovie.Movie == null))
|
||||
.Union(decisions.Where(c => c.RemoteMovie.MappingResult != MappingResultType.Success || c.RemoteMovie.MappingResult != MappingResultType.SuccessLenientMapping))
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Common.Disk;
|
||||
@ -111,7 +111,7 @@ public override IEnumerable<DownloadClientItem> GetItems()
|
||||
{
|
||||
case "error": // some error occurred, applies to paused torrents
|
||||
item.Status = DownloadItemStatus.Failed;
|
||||
item.Message = "QBittorrent is reporting an error";
|
||||
item.Message = "qBittorrent is reporting an error";
|
||||
break;
|
||||
|
||||
case "pausedDL": // torrent is paused and has NOT finished downloading
|
||||
@ -212,7 +212,7 @@ private ValidationFailure TestConnection()
|
||||
var config = _proxy.GetConfig(Settings);
|
||||
if (config.MaxRatioEnabled && config.RemoveOnMaxRatio)
|
||||
{
|
||||
return new NzbDroneValidationFailure(String.Empty, "QBittorrent is configured to remove torrents when they reach their Share Ratio Limit")
|
||||
return new NzbDroneValidationFailure(String.Empty, "qBittorrent is configured to remove torrents when they reach their Share Ratio Limit")
|
||||
{
|
||||
DetailedDescription = "Radarr will be unable to perform Completed Download Handling as configured. You can fix this in qBittorrent ('Tools -> Options...' in the menu) by changing 'Options -> BitTorrent -> Share Ratio Limiting' from 'Remove them' to 'Pause them'."
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using NLog;
|
||||
@ -72,7 +72,13 @@ public void AddTorrentFromUrl(string torrentUrl, QBittorrentSettings settings)
|
||||
.Post()
|
||||
.AddFormParameter("urls", torrentUrl);
|
||||
|
||||
ProcessRequest<object>(request, settings);
|
||||
var result = ProcessRequest(request, settings);
|
||||
|
||||
// Note: Older qbit versions returned nothing, so we can't do != "Ok." here.
|
||||
if (result == "Fails.")
|
||||
{
|
||||
throw new DownloadClientException("Download client failed to add torrent by url");
|
||||
}
|
||||
}
|
||||
|
||||
public void AddTorrentFromFile(string fileName, Byte[] fileContent, QBittorrentSettings settings)
|
||||
@ -81,7 +87,13 @@ public void AddTorrentFromFile(string fileName, Byte[] fileContent, QBittorrentS
|
||||
.Post()
|
||||
.AddFormUpload("torrents", fileName, fileContent);
|
||||
|
||||
ProcessRequest<object>(request, settings);
|
||||
var result = ProcessRequest(request, settings);
|
||||
|
||||
// Note: Current qbit versions return nothing, so we can't do != "Ok." here.
|
||||
if (result == "Fails.")
|
||||
{
|
||||
throw new DownloadClientException("Download client failed to add torrent");
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveTorrent(string hash, Boolean removeData, QBittorrentSettings settings)
|
||||
@ -90,7 +102,7 @@ public void RemoveTorrent(string hash, Boolean removeData, QBittorrentSettings s
|
||||
.Post()
|
||||
.AddFormParameter("hashes", hash);
|
||||
|
||||
ProcessRequest<object>(request, settings);
|
||||
ProcessRequest(request, settings);
|
||||
}
|
||||
|
||||
public void SetTorrentLabel(string hash, string label, QBittorrentSettings settings)
|
||||
@ -101,7 +113,7 @@ public void SetTorrentLabel(string hash, string label, QBittorrentSettings setti
|
||||
.AddFormParameter("category", label);
|
||||
try
|
||||
{
|
||||
ProcessRequest<object>(setCategoryRequest, settings);
|
||||
ProcessRequest(setCategoryRequest, settings);
|
||||
}
|
||||
catch(DownloadClientException ex)
|
||||
{
|
||||
@ -112,7 +124,8 @@ public void SetTorrentLabel(string hash, string label, QBittorrentSettings setti
|
||||
.Post()
|
||||
.AddFormParameter("hashes", hash)
|
||||
.AddFormParameter("label", label);
|
||||
ProcessRequest<object>(setLabelRequest, settings);
|
||||
|
||||
ProcessRequest(setLabelRequest, settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -125,7 +138,7 @@ public void MoveTorrentToTopInQueue(string hash, QBittorrentSettings settings)
|
||||
|
||||
try
|
||||
{
|
||||
var response = ProcessRequest<object>(request, settings);
|
||||
ProcessRequest(request, settings);
|
||||
}
|
||||
catch (DownloadClientException ex)
|
||||
{
|
||||
@ -152,10 +165,18 @@ private HttpRequestBuilder BuildRequest(QBittorrentSettings settings)
|
||||
|
||||
private TResult ProcessRequest<TResult>(HttpRequestBuilder requestBuilder, QBittorrentSettings settings)
|
||||
where TResult : new()
|
||||
{
|
||||
var responseContent = ProcessRequest(requestBuilder, settings);
|
||||
|
||||
return Json.Deserialize<TResult>(responseContent);
|
||||
}
|
||||
|
||||
private string ProcessRequest(HttpRequestBuilder requestBuilder, QBittorrentSettings settings)
|
||||
{
|
||||
AuthenticateClient(requestBuilder, settings);
|
||||
|
||||
var request = requestBuilder.Build();
|
||||
request.LogResponseContent = true;
|
||||
|
||||
HttpResponse response;
|
||||
try
|
||||
@ -176,15 +197,15 @@ private TResult ProcessRequest<TResult>(HttpRequestBuilder requestBuilder, QBitt
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new DownloadClientException("Failed to connect to qBitTorrent, check your settings.", ex);
|
||||
throw new DownloadClientException("Failed to connect to qBittorrent, check your settings.", ex);
|
||||
}
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
throw new DownloadClientException("Failed to connect to qBitTorrent, please check your settings.", ex);
|
||||
throw new DownloadClientException("Failed to connect to qBittorrent, please check your settings.", ex);
|
||||
}
|
||||
|
||||
return Json.Deserialize<TResult>(response.Content);
|
||||
return response.Content;
|
||||
}
|
||||
|
||||
private void AuthenticateClient(HttpRequestBuilder requestBuilder, QBittorrentSettings settings, bool reauthenticate = false)
|
||||
@ -218,23 +239,23 @@ private void AuthenticateClient(HttpRequestBuilder requestBuilder, QBittorrentSe
|
||||
_logger.Debug("qbitTorrent authentication failed.");
|
||||
if (ex.Response.StatusCode == HttpStatusCode.Forbidden)
|
||||
{
|
||||
throw new DownloadClientAuthenticationException("Failed to authenticate with qbitTorrent.", ex);
|
||||
throw new DownloadClientAuthenticationException("Failed to authenticate with qBittorrent.", ex);
|
||||
}
|
||||
|
||||
throw new DownloadClientException("Failed to connect to qBitTorrent, please check your settings.", ex);
|
||||
throw new DownloadClientException("Failed to connect to qBittorrent, please check your settings.", ex);
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
throw new DownloadClientException("Failed to connect to qBitTorrent, please check your settings.", ex);
|
||||
throw new DownloadClientException("Failed to connect to qBittorrent, please check your settings.", ex);
|
||||
}
|
||||
|
||||
if (response.Content != "Ok.") // returns "Fails." on bad login
|
||||
{
|
||||
_logger.Debug("qbitTorrent authentication failed.");
|
||||
throw new DownloadClientAuthenticationException("Failed to authenticate with qbitTorrent.");
|
||||
throw new DownloadClientAuthenticationException("Failed to authenticate with qBittorrent.");
|
||||
}
|
||||
|
||||
_logger.Debug("qbitTorrent authentication succeeded.");
|
||||
_logger.Debug("qBittorrent authentication succeeded.");
|
||||
|
||||
cookies = response.GetCookies();
|
||||
|
||||
|
@ -14,7 +14,7 @@ namespace NzbDrone.Core.Download
|
||||
public interface IDownloadService
|
||||
{
|
||||
void DownloadReport(RemoteEpisode remoteEpisode);
|
||||
void DownloadReport(RemoteMovie remoteMovie);
|
||||
void DownloadReport(RemoteMovie remoteMovie, bool forceDownload);
|
||||
}
|
||||
|
||||
|
||||
@ -92,7 +92,7 @@ public void DownloadReport(RemoteEpisode remoteEpisode)
|
||||
_eventAggregator.PublishEvent(episodeGrabbedEvent);
|
||||
}
|
||||
|
||||
public void DownloadReport(RemoteMovie remoteMovie)
|
||||
public void DownloadReport(RemoteMovie remoteMovie, bool foceDownload = false)
|
||||
{
|
||||
//Ensure.That(remoteEpisode.Series, () => remoteEpisode.Series).IsNotNull();
|
||||
//Ensure.That(remoteEpisode.Episodes, () => remoteEpisode.Episodes).HasItems(); TODO update this shit
|
||||
|
@ -88,7 +88,7 @@ public ProcessedDecisions ProcessDecisions(List<DownloadDecision> decisions)
|
||||
|
||||
try
|
||||
{
|
||||
_downloadService.DownloadReport(remoteMovie);
|
||||
_downloadService.DownloadReport(remoteMovie, false);
|
||||
grabbed.Add(report);
|
||||
}
|
||||
catch (Exception e)
|
||||
|
@ -39,7 +39,7 @@ private static string MakeWikiFragment(string message)
|
||||
|
||||
private static HttpUri MakeWikiUrl(string fragment)
|
||||
{
|
||||
return new HttpUri("https://github.com/Sonarr/Sonarr/wiki/Health-checks") + new HttpUri(fragment);
|
||||
return new HttpUri("https://github.com/Radarr/Radarr/wiki/Health-checks") + new HttpUri(fragment);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,7 +59,7 @@ public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchC
|
||||
else
|
||||
{
|
||||
var searchTitle = System.Web.HttpUtility.UrlPathEncode(Parser.Parser.ReplaceGermanUmlauts(Parser.Parser.NormalizeTitle(searchCriteria.Movie.Title)));
|
||||
var altTitles = searchCriteria.Movie.AlternativeTitles.DistinctBy(t => Parser.Parser.CleanSeriesTitle(t)).Take(5).ToList();
|
||||
var altTitles = searchCriteria.Movie.AlternativeTitles.Take(5).Select(t => t.Title).ToList();
|
||||
|
||||
var realMaxPages = (int)MaxPages / (altTitles.Count() + 1);
|
||||
|
||||
|
@ -3,7 +3,10 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Core.MetadataSource.SkyHook.Resource;
|
||||
using NzbDrone.Core.Movies.AlternativeTitles;
|
||||
using NzbDrone.Core.Parser;
|
||||
|
||||
namespace NzbDrone.Core.MetadataSource.RadarrAPI
|
||||
{
|
||||
@ -11,6 +14,10 @@ public interface IRadarrAPIClient
|
||||
{
|
||||
IHttpRequestBuilderFactory RadarrAPI { get; }
|
||||
List<MovieResult> DiscoverMovies(string action, Func<HttpRequest, HttpRequest> enhanceRequest);
|
||||
List<AlternativeTitle> AlternativeTitlesForMovie(int TmdbId);
|
||||
Tuple<List<AlternativeTitle>, AlternativeYear> AlternativeTitlesAndYearForMovie(int tmdbId);
|
||||
AlternativeTitle AddNewAlternativeTitle(AlternativeTitle title, int TmdbId);
|
||||
AlternativeYear AddNewAlternativeYear(int year, int tmdbId);
|
||||
string APIURL { get; }
|
||||
}
|
||||
|
||||
@ -65,7 +72,7 @@ private T Execute<T>(HttpRequest request)
|
||||
{
|
||||
var error = JsonConvert.DeserializeObject<RadarrError>(response.Content);
|
||||
|
||||
if (error != null && error.Errors.Count != 0)
|
||||
if (error != null && error.Errors != null && error.Errors.Count != 0)
|
||||
{
|
||||
throw new RadarrAPIException(error);
|
||||
}
|
||||
@ -96,6 +103,83 @@ public List<MovieResult> DiscoverMovies(string action, Func<HttpRequest, HttpReq
|
||||
return Execute<List<MovieResult>>(request);
|
||||
}
|
||||
|
||||
|
||||
public List<AlternativeTitle> AlternativeTitlesForMovie(int TmdbId)
|
||||
{
|
||||
var request = RadarrAPI.Create().SetSegment("route", "mappings").SetSegment("action", "find").AddQueryParam("tmdbid", TmdbId).Build();
|
||||
|
||||
var mappings = Execute<Mapping>(request);
|
||||
|
||||
var titles = new List<NzbDrone.Core.Movies.AlternativeTitles.AlternativeTitle>();
|
||||
|
||||
foreach (var altTitle in mappings.Mappings.Titles)
|
||||
{
|
||||
titles.Add(new NzbDrone.Core.Movies.AlternativeTitles.AlternativeTitle(altTitle.Info.AkaTitle, SourceType.Mappings, altTitle.Id));
|
||||
}
|
||||
|
||||
return titles;
|
||||
}
|
||||
|
||||
public Tuple<List<AlternativeTitle>, AlternativeYear> AlternativeTitlesAndYearForMovie(int tmdbId)
|
||||
{
|
||||
var request = RadarrAPI.Create().SetSegment("route", "mappings").SetSegment("action", "find").AddQueryParam("tmdbid", tmdbId).Build();
|
||||
|
||||
var mappings = Execute<Mapping>(request);
|
||||
|
||||
var titles = new List<NzbDrone.Core.Movies.AlternativeTitles.AlternativeTitle>();
|
||||
|
||||
foreach (var altTitle in mappings.Mappings.Titles)
|
||||
{
|
||||
titles.Add(new NzbDrone.Core.Movies.AlternativeTitles.AlternativeTitle(altTitle.Info.AkaTitle, SourceType.Mappings, altTitle.Id));
|
||||
}
|
||||
|
||||
var year = mappings.Mappings.Years.Where(y => y.Votes >= 3).OrderBy(y => y.Votes).FirstOrDefault();
|
||||
|
||||
AlternativeYear newYear = null;
|
||||
|
||||
if (year != null)
|
||||
{
|
||||
newYear = new AlternativeYear
|
||||
{
|
||||
Year = year.Info.AkaYear,
|
||||
SourceId = year.Id
|
||||
};
|
||||
}
|
||||
|
||||
return new Tuple<List<AlternativeTitle>, AlternativeYear>(titles, newYear);
|
||||
}
|
||||
|
||||
public AlternativeTitle AddNewAlternativeTitle(AlternativeTitle title, int TmdbId)
|
||||
{
|
||||
var request = RadarrAPI.Create().SetSegment("route", "mappings").SetSegment("action", "add")
|
||||
.AddQueryParam("tmdbid", TmdbId).AddQueryParam("type", "title")
|
||||
.AddQueryParam("language", IsoLanguages.Get(title.Language).TwoLetterCode)
|
||||
.AddQueryParam("aka_title", title.Title).Build();
|
||||
|
||||
var newMapping = Execute<AddTitleMapping>(request);
|
||||
|
||||
var newTitle = new AlternativeTitle(newMapping.Info.AkaTitle, SourceType.Mappings, newMapping.Id, title.Language);
|
||||
newTitle.VoteCount = newMapping.VoteCount;
|
||||
newTitle.Votes = newMapping.Votes;
|
||||
|
||||
return newTitle;
|
||||
}
|
||||
|
||||
public AlternativeYear AddNewAlternativeYear(int year, int tmdbId)
|
||||
{
|
||||
var request = RadarrAPI.Create().SetSegment("route", "mappings").SetSegment("action", "add")
|
||||
.AddQueryParam("tmdbid", tmdbId).AddQueryParam("type", "year")
|
||||
.AddQueryParam("aka_year", year).Build();
|
||||
|
||||
var newYear = Execute<AddYearMapping>(request);
|
||||
|
||||
return new AlternativeYear
|
||||
{
|
||||
Year = newYear.Info.AkaYear,
|
||||
SourceId = newYear.Id
|
||||
};
|
||||
}
|
||||
|
||||
public IHttpRequestBuilderFactory RadarrAPI { get; private set; }
|
||||
}
|
||||
}
|
||||
|
@ -27,21 +27,183 @@ public class RadarrError
|
||||
|
||||
public class RadarrAPIException : Exception
|
||||
{
|
||||
RadarrError APIErrors;
|
||||
public RadarrError APIErrors;
|
||||
|
||||
public RadarrAPIException(RadarrError apiError) : base(HumanReadable(apiError))
|
||||
{
|
||||
|
||||
APIErrors = apiError;
|
||||
}
|
||||
|
||||
private static string HumanReadable(RadarrError APIErrors)
|
||||
private static string HumanReadable(RadarrError apiErrors)
|
||||
{
|
||||
var firstError = APIErrors.Errors.First();
|
||||
var details = string.Join("\n", APIErrors.Errors.Select(error =>
|
||||
var firstError = apiErrors.Errors.First();
|
||||
var details = string.Join("\n", apiErrors.Errors.Select(error =>
|
||||
{
|
||||
return $"{error.Title} ({error.Status}, RayId: {error.RayId}), Details: {error.Detail}";
|
||||
}));
|
||||
return $"Error while calling api: {firstError.Title}\nFull error(s): {details}";
|
||||
}
|
||||
}
|
||||
|
||||
public class TitleInfo
|
||||
{
|
||||
|
||||
[JsonProperty("id")]
|
||||
public int Id { get; set; }
|
||||
|
||||
[JsonProperty("aka_title")]
|
||||
public string AkaTitle { get; set; }
|
||||
|
||||
[JsonProperty("aka_clean_title")]
|
||||
public string AkaCleanTitle { get; set; }
|
||||
}
|
||||
|
||||
public class YearInfo
|
||||
{
|
||||
|
||||
[JsonProperty("id")]
|
||||
public int Id { get; set; }
|
||||
|
||||
[JsonProperty("aka_year")]
|
||||
public int AkaYear { get; set; }
|
||||
}
|
||||
|
||||
public class Title
|
||||
{
|
||||
|
||||
[JsonProperty("id")]
|
||||
public int Id { get; set; }
|
||||
|
||||
[JsonProperty("tmdbid")]
|
||||
public int Tmdbid { get; set; }
|
||||
|
||||
[JsonProperty("votes")]
|
||||
public int Votes { get; set; }
|
||||
|
||||
[JsonProperty("vote_count")]
|
||||
public int VoteCount { get; set; }
|
||||
|
||||
[JsonProperty("locked")]
|
||||
public bool Locked { get; set; }
|
||||
|
||||
[JsonProperty("info_type")]
|
||||
public string InfoType { get; set; }
|
||||
|
||||
[JsonProperty("info_id")]
|
||||
public int InfoId { get; set; }
|
||||
|
||||
[JsonProperty("info")]
|
||||
public TitleInfo Info { get; set; }
|
||||
}
|
||||
|
||||
public class Year
|
||||
{
|
||||
|
||||
[JsonProperty("id")]
|
||||
public int Id { get; set; }
|
||||
|
||||
[JsonProperty("tmdbid")]
|
||||
public int Tmdbid { get; set; }
|
||||
|
||||
[JsonProperty("votes")]
|
||||
public int Votes { get; set; }
|
||||
|
||||
[JsonProperty("vote_count")]
|
||||
public int VoteCount { get; set; }
|
||||
|
||||
[JsonProperty("locked")]
|
||||
public bool Locked { get; set; }
|
||||
|
||||
[JsonProperty("info_type")]
|
||||
public string InfoType { get; set; }
|
||||
|
||||
[JsonProperty("info_id")]
|
||||
public int InfoId { get; set; }
|
||||
|
||||
[JsonProperty("info")]
|
||||
public YearInfo Info { get; set; }
|
||||
}
|
||||
|
||||
public class Mappings
|
||||
{
|
||||
|
||||
[JsonProperty("titles")]
|
||||
public IList<Title> Titles { get; set; }
|
||||
|
||||
[JsonProperty("years")]
|
||||
public IList<Year> Years { get; set; }
|
||||
}
|
||||
|
||||
public class Mapping
|
||||
{
|
||||
|
||||
[JsonProperty("id")]
|
||||
public int Id { get; set; }
|
||||
|
||||
[JsonProperty("title")]
|
||||
public string Title { get; set; }
|
||||
|
||||
[JsonProperty("imdb_id")]
|
||||
public string ImdbId { get; set; }
|
||||
|
||||
[JsonProperty("mappings")]
|
||||
public Mappings Mappings { get; set; }
|
||||
}
|
||||
|
||||
public class AddTitleMapping
|
||||
{
|
||||
|
||||
[JsonProperty("tmdbid")]
|
||||
public string Tmdbid { get; set; }
|
||||
|
||||
[JsonProperty("info_type")]
|
||||
public string InfoType { get; set; }
|
||||
|
||||
[JsonProperty("info_id")]
|
||||
public int InfoId { get; set; }
|
||||
|
||||
[JsonProperty("id")]
|
||||
public int Id { get; set; }
|
||||
|
||||
[JsonProperty("info")]
|
||||
public TitleInfo Info { get; set; }
|
||||
|
||||
[JsonProperty("votes")]
|
||||
public int Votes { get; set; }
|
||||
|
||||
[JsonProperty("vote_count")]
|
||||
public int VoteCount { get; set; }
|
||||
|
||||
[JsonProperty("locked")]
|
||||
public bool Locked { get; set; }
|
||||
}
|
||||
|
||||
public class AddYearMapping
|
||||
{
|
||||
|
||||
[JsonProperty("tmdbid")]
|
||||
public string Tmdbid { get; set; }
|
||||
|
||||
[JsonProperty("info_type")]
|
||||
public string InfoType { get; set; }
|
||||
|
||||
[JsonProperty("info_id")]
|
||||
public int InfoId { get; set; }
|
||||
|
||||
[JsonProperty("id")]
|
||||
public int Id { get; set; }
|
||||
|
||||
[JsonProperty("info")]
|
||||
public YearInfo Info { get; set; }
|
||||
|
||||
[JsonProperty("votes")]
|
||||
public int Votes { get; set; }
|
||||
|
||||
[JsonProperty("vote_count")]
|
||||
public int VoteCount { get; set; }
|
||||
|
||||
[JsonProperty("locked")]
|
||||
public bool Locked { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,7 +2,8 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using NLog;
|
||||
using System.ServiceModel;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cloud;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
@ -19,6 +20,7 @@
|
||||
using NzbDrone.Core.NetImport.ImportExclusions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.MetadataSource.RadarrAPI;
|
||||
using NzbDrone.Core.Movies.AlternativeTitles;
|
||||
|
||||
namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||
{
|
||||
@ -33,12 +35,13 @@ public class SkyHookProxy : IProvideSeriesInfo, ISearchForNewSeries, IProvideMov
|
||||
private readonly IMovieService _movieService;
|
||||
private readonly IPreDBService _predbService;
|
||||
private readonly IImportExclusionsService _exclusionService;
|
||||
private readonly IAlternativeTitleService _altTitleService;
|
||||
private readonly IRadarrAPIClient _radarrAPI;
|
||||
|
||||
private readonly IHttpRequestBuilderFactory _apiBuilder;
|
||||
|
||||
public SkyHookProxy(IHttpClient httpClient, ISonarrCloudRequestBuilder requestBuilder, ITmdbConfigService configService, IMovieService movieService,
|
||||
IPreDBService predbService, IImportExclusionsService exclusionService, IRadarrAPIClient radarrAPI, Logger logger)
|
||||
IPreDBService predbService, IImportExclusionsService exclusionService, IAlternativeTitleService altTitleService, IRadarrAPIClient radarrAPI, Logger logger)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
_requestBuilder = requestBuilder.SkyHookTvdb;
|
||||
@ -47,6 +50,7 @@ public SkyHookProxy(IHttpClient httpClient, ISonarrCloudRequestBuilder requestBu
|
||||
_movieService = movieService;
|
||||
_predbService = predbService;
|
||||
_exclusionService = exclusionService;
|
||||
_altTitleService = altTitleService;
|
||||
_radarrAPI = radarrAPI;
|
||||
|
||||
_logger = logger;
|
||||
@ -133,21 +137,28 @@ public Movie GetMovieInfo(int TmdbId, Profile profile = null, bool hasPreDBEntry
|
||||
}
|
||||
|
||||
var movie = new Movie();
|
||||
var altTitles = new List<AlternativeTitle>();
|
||||
|
||||
if (langCode != "us")
|
||||
if (langCode != "en")
|
||||
{
|
||||
movie.AlternativeTitles.Add(resource.original_title);
|
||||
var iso = IsoLanguages.Find(resource.original_language);
|
||||
if (iso != null)
|
||||
{
|
||||
altTitles.Add(new AlternativeTitle(resource.original_title, SourceType.TMDB, TmdbId, iso.Language));
|
||||
}
|
||||
|
||||
//movie.AlternativeTitles.Add(resource.original_title);
|
||||
}
|
||||
|
||||
foreach (var alternativeTitle in resource.alternative_titles.titles)
|
||||
{
|
||||
if (alternativeTitle.iso_3166_1.ToLower() == langCode)
|
||||
{
|
||||
movie.AlternativeTitles.Add(alternativeTitle.title);
|
||||
altTitles.Add(new AlternativeTitle(alternativeTitle.title, SourceType.TMDB, TmdbId, IsoLanguages.Find(alternativeTitle.iso_3166_1.ToLower()).Language));
|
||||
}
|
||||
else if (alternativeTitle.iso_3166_1.ToLower() == "us")
|
||||
{
|
||||
movie.AlternativeTitles.Add(alternativeTitle.title);
|
||||
altTitles.Add(new AlternativeTitle(alternativeTitle.title, SourceType.TMDB, TmdbId, Language.English));
|
||||
}
|
||||
}
|
||||
|
||||
@ -321,6 +332,8 @@ public Movie GetMovieInfo(int TmdbId, Profile profile = null, bool hasPreDBEntry
|
||||
}
|
||||
}
|
||||
|
||||
movie.AlternativeTitles.AddRange(altTitles);
|
||||
|
||||
return movie;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,77 @@
|
||||
using System;
|
||||
using Marr.Data;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.Movies.AlternativeTitles
|
||||
{
|
||||
public class AlternativeTitle : ModelBase
|
||||
{
|
||||
public SourceType SourceType { get; set; }
|
||||
public int MovieId { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string CleanTitle { get; set; }
|
||||
public int SourceId { get; set; }
|
||||
public int Votes { get; set; }
|
||||
public int VoteCount { get; set; }
|
||||
public Language Language { get; set; }
|
||||
public LazyLoaded<Movie> Movie { get; set; }
|
||||
|
||||
public AlternativeTitle()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public AlternativeTitle(string title, SourceType sourceType = SourceType.TMDB, int sourceId = 0, Language language = Language.English)
|
||||
{
|
||||
Title = title;
|
||||
CleanTitle = title.CleanSeriesTitle();
|
||||
SourceType = sourceType;
|
||||
SourceId = sourceId;
|
||||
Language = language;
|
||||
}
|
||||
|
||||
public bool IsTrusted(int minVotes = 3)
|
||||
{
|
||||
switch (SourceType)
|
||||
{
|
||||
case SourceType.TMDB:
|
||||
return Votes >= minVotes;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
var item = obj as AlternativeTitle;
|
||||
|
||||
if (item == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return item.CleanTitle == this.CleanTitle;
|
||||
}
|
||||
|
||||
public override String ToString()
|
||||
{
|
||||
return Title;
|
||||
}
|
||||
}
|
||||
|
||||
public enum SourceType
|
||||
{
|
||||
TMDB = 0,
|
||||
Mappings = 1,
|
||||
User = 2,
|
||||
Indexer = 3
|
||||
}
|
||||
|
||||
public class AlternativeYear
|
||||
{
|
||||
public int Year { get; set; }
|
||||
public int SourceId { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
|
||||
namespace NzbDrone.Core.Movies.AlternativeTitles
|
||||
{
|
||||
public interface IAlternativeTitleRepository : IBasicRepository<AlternativeTitle>
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class AlternativeTitleRepository : BasicRepository<AlternativeTitle>, IAlternativeTitleRepository
|
||||
{
|
||||
protected IMainDatabase _database;
|
||||
|
||||
public AlternativeTitleRepository(IMainDatabase database, IEventAggregator eventAggregator)
|
||||
: base(database, eventAggregator)
|
||||
{
|
||||
_database = database;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.EnsureThat;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.DataAugmentation.Scene;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Organizer;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Tv.Events;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.Events;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Configuration;
|
||||
|
||||
namespace NzbDrone.Core.Movies.AlternativeTitles
|
||||
{
|
||||
public interface IAlternativeTitleService
|
||||
{
|
||||
List<AlternativeTitle> GetAllTitlesForMovie(Movie movie);
|
||||
AlternativeTitle AddAltTitle(AlternativeTitle title, Movie movie);
|
||||
List<AlternativeTitle> AddAltTitles(List<AlternativeTitle> titles, Movie movie);
|
||||
AlternativeTitle GetById(int id);
|
||||
}
|
||||
|
||||
public class AlternativeTitleService : IAlternativeTitleService
|
||||
{
|
||||
private readonly IAlternativeTitleRepository _titleRepo;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly Logger _logger;
|
||||
|
||||
|
||||
public AlternativeTitleService(IAlternativeTitleRepository titleRepo,
|
||||
IEventAggregator eventAggregator,
|
||||
IConfigService configService,
|
||||
Logger logger)
|
||||
{
|
||||
_titleRepo = titleRepo;
|
||||
_eventAggregator = eventAggregator;
|
||||
_configService = configService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public List<AlternativeTitle> GetAllTitlesForMovie(Movie movie)
|
||||
{
|
||||
return _titleRepo.All().ToList();
|
||||
}
|
||||
|
||||
public AlternativeTitle AddAltTitle(AlternativeTitle title, Movie movie)
|
||||
{
|
||||
title.MovieId = movie.Id;
|
||||
return _titleRepo.Insert(title);
|
||||
}
|
||||
|
||||
public List<AlternativeTitle> AddAltTitles(List<AlternativeTitle> titles, Movie movie)
|
||||
{
|
||||
titles.ForEach(t => t.MovieId = movie.Id);
|
||||
_titleRepo.InsertMany(titles);
|
||||
return titles;
|
||||
}
|
||||
|
||||
public AlternativeTitle GetById(int id)
|
||||
{
|
||||
return _titleRepo.Get(id);
|
||||
}
|
||||
}
|
||||
}
|
@ -12,6 +12,9 @@ public class SlackPayload
|
||||
[JsonProperty("icon_emoji")]
|
||||
public string IconEmoji { get; set; }
|
||||
|
||||
[JsonProperty("icon_url")]
|
||||
public string IconUrl { get; set; }
|
||||
|
||||
public List<Attachment> Attachments { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,8 @@
|
||||
using FluentValidation.Results;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Notifications.Slack.Payloads;
|
||||
using NzbDrone.Core.Rest;
|
||||
using NzbDrone.Core.Tv;
|
||||
@ -14,10 +16,12 @@ namespace NzbDrone.Core.Notifications.Slack
|
||||
{
|
||||
public class Slack : NotificationBase<SlackSettings>
|
||||
{
|
||||
private readonly ISlackProxy _proxy;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public Slack(Logger logger)
|
||||
|
||||
public Slack(ISlackProxy proxy, Logger logger)
|
||||
{
|
||||
_proxy = proxy;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@ -27,86 +31,68 @@ public Slack(Logger logger)
|
||||
|
||||
public override void OnGrab(GrabMessage message)
|
||||
{
|
||||
var payload = new SlackPayload
|
||||
{
|
||||
IconEmoji = Settings.Icon,
|
||||
Username = Settings.Username,
|
||||
Text = $"Grabbed: {message.Message}",
|
||||
Attachments = new List<Attachment>
|
||||
{
|
||||
new Attachment
|
||||
{
|
||||
Fallback = message.Message,
|
||||
Title = message.Movie.Title,
|
||||
Text = message.Message,
|
||||
Color = "warning"
|
||||
}
|
||||
}
|
||||
};
|
||||
var attachments = new List<Attachment>
|
||||
{
|
||||
new Attachment
|
||||
{
|
||||
Fallback = message.Message,
|
||||
Title = message.Movie.Title,
|
||||
Text = message.Message,
|
||||
Color = "warning"
|
||||
}
|
||||
};
|
||||
var payload = CreatePayload($"Grabbed: {message.Message}", attachments);
|
||||
|
||||
NotifySlack(payload);
|
||||
_proxy.SendPayload(payload, Settings);
|
||||
}
|
||||
|
||||
public override void OnDownload(DownloadMessage message)
|
||||
{
|
||||
var payload = new SlackPayload
|
||||
{
|
||||
IconEmoji = Settings.Icon,
|
||||
Username = Settings.Username,
|
||||
Text = $"Imported: {message.Message}",
|
||||
Attachments = new List<Attachment>
|
||||
{
|
||||
new Attachment
|
||||
{
|
||||
Fallback = message.Message,
|
||||
Title = message.Movie.Title,
|
||||
Text = message.Message,
|
||||
Color = "good"
|
||||
}
|
||||
}
|
||||
};
|
||||
var attachments = new List<Attachment>
|
||||
{
|
||||
new Attachment
|
||||
{
|
||||
Fallback = message.Message,
|
||||
Title = message.Movie.Title,
|
||||
Text = message.Message,
|
||||
Color = "good"
|
||||
}
|
||||
};
|
||||
var payload = CreatePayload($"Imported: {message.Message}", attachments);
|
||||
|
||||
NotifySlack(payload);
|
||||
_proxy.SendPayload(payload, Settings);
|
||||
}
|
||||
|
||||
public override void OnMovieRename(Movie movie)
|
||||
{
|
||||
var payload = new SlackPayload
|
||||
{
|
||||
IconEmoji = Settings.Icon,
|
||||
Username = Settings.Username,
|
||||
Text = "Renamed",
|
||||
Attachments = new List<Attachment>
|
||||
{
|
||||
new Attachment
|
||||
{
|
||||
Title = movie.Title,
|
||||
}
|
||||
}
|
||||
};
|
||||
var attachments = new List<Attachment>
|
||||
{
|
||||
new Attachment
|
||||
{
|
||||
Title = movie.Title,
|
||||
}
|
||||
};
|
||||
|
||||
var payload = CreatePayload("Renamed", attachments);
|
||||
|
||||
NotifySlack(payload);
|
||||
_proxy.SendPayload(payload, Settings);
|
||||
}
|
||||
|
||||
public override void OnRename(Series series)
|
||||
{
|
||||
var payload = new SlackPayload
|
||||
{
|
||||
IconEmoji = Settings.Icon,
|
||||
Username = Settings.Username,
|
||||
Text = "Renamed",
|
||||
Attachments = new List<Attachment>
|
||||
{
|
||||
new Attachment
|
||||
{
|
||||
Title = series.Title,
|
||||
}
|
||||
}
|
||||
};
|
||||
var attachments = new List<Attachment>
|
||||
{
|
||||
new Attachment
|
||||
{
|
||||
Title = series.Title,
|
||||
}
|
||||
};
|
||||
|
||||
NotifySlack(payload);
|
||||
var payload = CreatePayload("Renamed", attachments);
|
||||
|
||||
_proxy.SendPayload(payload, Settings);
|
||||
}
|
||||
|
||||
|
||||
public override ValidationResult Test()
|
||||
{
|
||||
var failures = new List<ValidationFailure>();
|
||||
@ -121,14 +107,10 @@ public ValidationFailure TestMessage()
|
||||
try
|
||||
{
|
||||
var message = $"Test message from Radarr posted at {DateTime.Now}";
|
||||
var payload = new SlackPayload
|
||||
{
|
||||
IconEmoji = Settings.Icon,
|
||||
Username = Settings.Username,
|
||||
Text = message
|
||||
};
|
||||
|
||||
NotifySlack(payload);
|
||||
var payload = CreatePayload(message);
|
||||
|
||||
_proxy.SendPayload(payload, Settings);
|
||||
|
||||
}
|
||||
catch (SlackExeption ex)
|
||||
@ -139,24 +121,31 @@ public ValidationFailure TestMessage()
|
||||
return null;
|
||||
}
|
||||
|
||||
private void NotifySlack(SlackPayload payload)
|
||||
private SlackPayload CreatePayload(string message, List<Attachment> attachments = null)
|
||||
{
|
||||
try
|
||||
var icon = Settings.Icon;
|
||||
|
||||
var payload = new SlackPayload
|
||||
{
|
||||
var client = RestClientFactory.BuildClient(Settings.WebHookUrl);
|
||||
var request = new RestRequest(Method.POST)
|
||||
Username = Settings.Username,
|
||||
Text = message,
|
||||
Attachments = attachments
|
||||
};
|
||||
|
||||
if (icon.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
// Set the correct icon based on the value
|
||||
if (icon.StartsWith(":") && icon.EndsWith(":"))
|
||||
{
|
||||
RequestFormat = DataFormat.Json,
|
||||
JsonSerializer = new JsonNetSerializer()
|
||||
};
|
||||
request.AddBody(payload);
|
||||
client.ExecuteAndValidate(request);
|
||||
}
|
||||
catch (RestException ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to post payload {0}", payload);
|
||||
throw new SlackExeption("Unable to post payload", ex);
|
||||
payload.IconEmoji = icon;
|
||||
}
|
||||
else
|
||||
{
|
||||
payload.IconUrl = icon;
|
||||
}
|
||||
}
|
||||
|
||||
return payload;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
46
src/NzbDrone.Core/Notifications/Slack/SlackProxy.cs
Normal file
46
src/NzbDrone.Core/Notifications/Slack/SlackProxy.cs
Normal file
@ -0,0 +1,46 @@
|
||||
using NLog;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Notifications.Slack.Payloads;
|
||||
using NzbDrone.Core.Rest;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Slack
|
||||
{
|
||||
public interface ISlackProxy
|
||||
{
|
||||
void SendPayload(SlackPayload payload, SlackSettings settings);
|
||||
}
|
||||
|
||||
public class SlackProxy : ISlackProxy
|
||||
{
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public SlackProxy(IHttpClient httpClient, Logger logger)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void SendPayload(SlackPayload payload, SlackSettings settings)
|
||||
{
|
||||
try
|
||||
{
|
||||
var request = new HttpRequestBuilder(settings.WebHookUrl)
|
||||
.Accept(HttpAccept.Json)
|
||||
.Build();
|
||||
|
||||
request.Method = HttpMethod.POST;
|
||||
request.Headers.ContentType = "application/json";
|
||||
request.SetContent(payload.ToJson());
|
||||
|
||||
_httpClient.Execute(request);
|
||||
}
|
||||
catch (RestException ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to post payload {0}", payload);
|
||||
throw new SlackExeption("Unable to post payload", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -24,7 +24,7 @@ public class SlackSettings : IProviderConfig
|
||||
[FieldDefinition(1, Label = "Username", HelpText = "Choose the username that this integration will post as", Type = FieldType.Textbox)]
|
||||
public string Username { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Icon", HelpText = "Change the icon that is used for messages from this integration", Type = FieldType.Textbox, HelpLink = "http://www.emoji-cheat-sheet.com/")]
|
||||
[FieldDefinition(2, Label = "Icon", HelpText = "Change the icon that is used for messages from this integration (Emoji or URL)", Type = FieldType.Textbox, HelpLink = "http://www.emoji-cheat-sheet.com/")]
|
||||
public string Icon { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
@ -37,10 +37,10 @@ public TwitterSettings()
|
||||
AuthorizeNotification = "step1";
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "Consumer Key", HelpText = "Consumer key from a Twitter application", HelpLink = "https://github.com/Sonarr/Sonarr/wiki/Twitter-Notifications")]
|
||||
[FieldDefinition(0, Label = "Consumer Key", HelpText = "Consumer key from a Twitter application", HelpLink = "https://github.com/Radarr/Radarr/wiki/Twitter-Notifications")]
|
||||
public string ConsumerKey { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "Consumer Secret", HelpText = "Consumer secret from a Twitter application", HelpLink = "https://github.com/Sonarr/Sonarr/wiki/Twitter-Notifications")]
|
||||
[FieldDefinition(1, Label = "Consumer Secret", HelpText = "Consumer secret from a Twitter application", HelpLink = "https://github.com/Radarr/Radarr/wiki/Twitter-Notifications")]
|
||||
public string ConsumerSecret { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Access Token", Advanced = true)]
|
||||
|
@ -15,7 +15,7 @@ public Webhook(IWebhookService service)
|
||||
_service = service;
|
||||
}
|
||||
|
||||
public override string Link => "https://github.com/Sonarr/Sonarr/wiki/Webhook";
|
||||
public override string Link => "https://github.com/Radarr/Radarr/wiki/Webhook";
|
||||
|
||||
public override void OnGrab(GrabMessage message)
|
||||
{
|
||||
|
@ -125,6 +125,7 @@
|
||||
<Compile Include="Authentication\UserRepository.cs" />
|
||||
<Compile Include="Authentication\UserService.cs" />
|
||||
<Compile Include="Datastore\Migration\123_create_netimport_table.cs" />
|
||||
<Compile Include="Datastore\Migration\140_add_alternative_titles_table.cs" />
|
||||
<Compile Include="MediaFiles\Events\MovieFileUpdatedEvent.cs" />
|
||||
<Compile Include="Datastore\Migration\134_add_remux_qualities_for_the_wankers.cs" />
|
||||
<Compile Include="Datastore\Migration\129_add_parsed_movie_info_to_pending_release.cs" />
|
||||
@ -134,6 +135,9 @@
|
||||
<Compile Include="Datastore\Migration\133_add_minimumavailability.cs" />
|
||||
<Compile Include="IndexerSearch\CutoffUnmetMoviesSearchCommand.cs" />
|
||||
<Compile Include="Indexers\HDBits\HDBitsInfo.cs" />
|
||||
<Compile Include="Movies\AlternativeTitles\AlternativeTitle.cs" />
|
||||
<Compile Include="Movies\AlternativeTitles\AlternativeTitleRepository.cs" />
|
||||
<Compile Include="Movies\AlternativeTitles\AlternativeTitleService.cs" />
|
||||
<Compile Include="NetImport\NetImportListLevels.cs" />
|
||||
<Compile Include="NetImport\TMDb\TMDbLanguageCodes.cs" />
|
||||
<Compile Include="NetImport\TMDb\TMDbSettings.cs" />
|
||||
@ -966,6 +970,7 @@
|
||||
<Compile Include="Notifications\Slack\Payloads\SlackPayload.cs" />
|
||||
<Compile Include="Notifications\Slack\Slack.cs" />
|
||||
<Compile Include="Notifications\Slack\SlackExeption.cs" />
|
||||
<Compile Include="Notifications\Slack\SlackProxy.cs" />
|
||||
<Compile Include="Notifications\Slack\SlackSettings.cs" />
|
||||
<Compile Include="Notifications\Synology\SynologyException.cs" />
|
||||
<Compile Include="Notifications\Synology\SynologyIndexer.cs" />
|
||||
|
@ -28,7 +28,8 @@ public static class IsoLanguages
|
||||
// new IsoLanguage("nl", "nld", Language.Flemish),
|
||||
new IsoLanguage("el", "ell", Language.Greek),
|
||||
new IsoLanguage("ko", "kor", Language.Korean),
|
||||
new IsoLanguage("hu", "hun", Language.Hungarian)
|
||||
new IsoLanguage("hu", "hun", Language.Hungarian)//,
|
||||
//new IsoLanguage("he", "heb", Language.Hebrew)
|
||||
};
|
||||
|
||||
public static IsoLanguage Find(string isoCode)
|
||||
|
@ -24,6 +24,7 @@ public enum Language
|
||||
Flemish = 19,
|
||||
Greek = 20,
|
||||
Korean = 21,
|
||||
Hungarian = 22
|
||||
Hungarian = 22,
|
||||
Hebrew = 23
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ public static class LanguageParser
|
||||
{
|
||||
private static readonly Logger Logger = NzbDroneLogger.GetLogger(typeof(LanguageParser));
|
||||
|
||||
private static readonly Regex LanguageRegex = new Regex(@"(?:\W|_)(?<italian>\b(?:ita|italian)\b)|(?<german>german\b|videomann)|(?<flemish>flemish)|(?<greek>greek)|(?<french>(?:\W|_)(?:FR|VOSTFR|VO|VFF|VFQ|TRUEFRENCH)(?:\W|_))|(?<russian>\brus\b)|(?<dutch>nl\W?subs?)|(?<hungarian>\b(?:HUNDUB|HUN)\b)",
|
||||
private static readonly Regex LanguageRegex = new Regex(@"(?:\W|_)(?<italian>\b(?:ita|italian)\b)|(?<german>german\b|videomann)|(?<flemish>flemish)|(?<greek>greek)|(?<french>(?:\W|_)(?:FR|VOSTFR|VO|VFF|VFQ|TRUEFRENCH)(?:\W|_))|(?<russian>\brus\b)|(?<dutch>nl\W?subs?)|(?<hungarian>\b(?:HUNDUB|HUN)\b)|(?<hebrew>\bHebDub\b)",
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
private static readonly Regex SubtitleLanguageRegex = new Regex(".+?[-_. ](?<iso_code>[a-z]{2,3})$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
@ -77,6 +77,9 @@ public static Language ParseLanguage(string title)
|
||||
if (lowerTitle.Contains("hungarian"))
|
||||
return Language.Hungarian;
|
||||
|
||||
if (lowerTitle.Contains("hebrew"))
|
||||
return Language.Hebrew;
|
||||
|
||||
var match = LanguageRegex.Match(title);
|
||||
|
||||
if (match.Groups["italian"].Captures.Cast<Capture>().Any())
|
||||
@ -103,6 +106,9 @@ public static Language ParseLanguage(string title)
|
||||
if (match.Groups["hungarian"].Success)
|
||||
return Language.Hungarian;
|
||||
|
||||
if (match.Groups["hebrew"].Success)
|
||||
return Language.Hebrew;
|
||||
|
||||
return Language.English;
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@ public class RemoteMovie
|
||||
public ParsedEpisodeInfo ParsedEpisodeInfo { get; set; } //TODO: Change to ParsedMovieInfo, for now though ParsedEpisodeInfo will do.
|
||||
public ParsedMovieInfo ParsedMovieInfo { get; set; }
|
||||
public Movie Movie { get; set; }
|
||||
public bool DownloadAllowed { get; set; }
|
||||
public MappingResultType MappingResult { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
|
@ -9,6 +9,7 @@
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Movies.AlternativeTitles;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Parser.RomanNumerals;
|
||||
using NzbDrone.Core.Tv;
|
||||
@ -381,7 +382,7 @@ private bool TryGetMovieByImDbId(ParsedMovieInfo parsedMovieInfo, string imdbId,
|
||||
{
|
||||
var movie = _movieService.FindByImdbId(imdbId);
|
||||
//Should fix practically all problems, where indexer is shite at adding correct imdbids to movies.
|
||||
if (movie != null && parsedMovieInfo.Year > 1800 && parsedMovieInfo.Year != movie.Year)
|
||||
if (movie != null && parsedMovieInfo.Year > 1800 && (parsedMovieInfo.Year != movie.Year && movie.SecondaryYear != parsedMovieInfo.Year))
|
||||
{
|
||||
result = new MappingResult { Movie = movie, MappingResultType = MappingResultType.WrongYear};
|
||||
return false;
|
||||
@ -458,9 +459,9 @@ private bool TryGetMovieBySearchCriteria(ParsedMovieInfo parsedMovieInfo, Search
|
||||
|
||||
possibleTitles.Add(searchCriteria.Movie.CleanTitle);
|
||||
|
||||
foreach (string altTitle in searchCriteria.Movie.AlternativeTitles)
|
||||
foreach (AlternativeTitle altTitle in searchCriteria.Movie.AlternativeTitles)
|
||||
{
|
||||
possibleTitles.Add(altTitle.CleanSeriesTitle());
|
||||
possibleTitles.Add(altTitle.CleanTitle);
|
||||
}
|
||||
|
||||
string cleanTitle = parsedMovieInfo.MovieTitle.CleanSeriesTitle();
|
||||
@ -494,7 +495,7 @@ private bool TryGetMovieBySearchCriteria(ParsedMovieInfo parsedMovieInfo, Search
|
||||
|
||||
if (possibleMovie != null)
|
||||
{
|
||||
if (parsedMovieInfo.Year < 1800 || possibleMovie.Year == parsedMovieInfo.Year)
|
||||
if (parsedMovieInfo.Year < 1800 || possibleMovie.Year == parsedMovieInfo.Year || possibleMovie.SecondaryYear == parsedMovieInfo.Year)
|
||||
{
|
||||
result = new MappingResult { Movie = possibleMovie, MappingResultType = MappingResultType.Success };
|
||||
return true;
|
||||
@ -509,7 +510,7 @@ private bool TryGetMovieBySearchCriteria(ParsedMovieInfo parsedMovieInfo, Search
|
||||
cleanTitle.Contains(searchCriteria.Movie.CleanTitle))
|
||||
{
|
||||
possibleMovie = searchCriteria.Movie;
|
||||
if (parsedMovieInfo.Year > 1800 && parsedMovieInfo.Year == possibleMovie.Year)
|
||||
if (parsedMovieInfo.Year > 1800 && parsedMovieInfo.Year == possibleMovie.Year || possibleMovie.SecondaryYear == parsedMovieInfo.Year)
|
||||
{
|
||||
result = new MappingResult {Movie = possibleMovie, MappingResultType = MappingResultType.SuccessLenientMapping};
|
||||
return true;
|
||||
|
@ -22,13 +22,13 @@ public interface IProfileService
|
||||
public class ProfileService : IProfileService, IHandle<ApplicationStartedEvent>
|
||||
{
|
||||
private readonly IProfileRepository _profileRepository;
|
||||
private readonly ISeriesService _seriesService;
|
||||
private readonly IMovieService _movieService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public ProfileService(IProfileRepository profileRepository, ISeriesService seriesService, Logger logger)
|
||||
public ProfileService(IProfileRepository profileRepository, IMovieService movieService, Logger logger)
|
||||
{
|
||||
_profileRepository = profileRepository;
|
||||
_seriesService = seriesService;
|
||||
_movieService = movieService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@ public void Update(Profile profile)
|
||||
|
||||
public void Delete(int id)
|
||||
{
|
||||
if (_seriesService.GetAllSeries().Any(c => c.ProfileId == id))
|
||||
if (_movieService.GetAllMovies().Any(c => c.ProfileId == id))
|
||||
{
|
||||
throw new ProfileInUseException(id);
|
||||
}
|
||||
|
@ -6,6 +6,8 @@
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using System.IO;
|
||||
using NzbDrone.Core.Movies;
|
||||
using NzbDrone.Core.Movies.AlternativeTitles;
|
||||
|
||||
namespace NzbDrone.Core.Tv
|
||||
{
|
||||
@ -17,7 +19,7 @@ public Movie()
|
||||
Genres = new List<string>();
|
||||
Actors = new List<Actor>();
|
||||
Tags = new HashSet<int>();
|
||||
AlternativeTitles = new List<string>();
|
||||
AlternativeTitles = new List<AlternativeTitle>();
|
||||
}
|
||||
public int TmdbId { get; set; }
|
||||
public string ImdbId { get; set; }
|
||||
@ -52,7 +54,10 @@ public Movie()
|
||||
public LazyLoaded<MovieFile> MovieFile { get; set; }
|
||||
public bool HasPreDBEntry { get; set; }
|
||||
public int MovieFileId { get; set; }
|
||||
public List<string> AlternativeTitles { get; set; }
|
||||
//Get Loaded via a Join Query
|
||||
public List<AlternativeTitle> AlternativeTitles { get; set; }
|
||||
public int? SecondaryYear { get; set; }
|
||||
public int SecondaryYearSourceId { get; set; }
|
||||
public string YouTubeTrailerId{ get; set; }
|
||||
public string Studio { get; set; }
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
using NzbDrone.Core.Datastore.Extensions;
|
||||
using Marr.Data.QGen;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Movies.AlternativeTitles;
|
||||
using NzbDrone.Core.Parser.RomanNumerals;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using CoreParser = NzbDrone.Core.Parser.Parser;
|
||||
@ -103,7 +104,7 @@ public PagingSpec<Movie> MoviesWithoutFiles(PagingSpec<Movie> pagingSpec)
|
||||
|
||||
public override PagingSpec<Movie> GetPaged(PagingSpec<Movie> pagingSpec)
|
||||
{
|
||||
if (pagingSpec.SortKey == "downloadedQuality")
|
||||
/*if (pagingSpec.SortKey == "downloadedQuality")
|
||||
{
|
||||
var mapper = _database.GetDataMapper();
|
||||
var offset = pagingSpec.PagingOffset();
|
||||
@ -113,7 +114,7 @@ public override PagingSpec<Movie> GetPaged(PagingSpec<Movie> pagingSpec)
|
||||
{
|
||||
direction = "DESC";
|
||||
}
|
||||
var q = mapper.Query<Movie>($"SELECT * from \"Movies\" , \"MovieFiles\", \"QualityDefinitions\" WHERE Movies.MovieFileId=MovieFiles.Id AND instr(MovieFiles.Quality, ('quality\": ' || QualityDefinitions.Quality || \",\")) > 0 ORDER BY QualityDefinitions.Title {direction} LIMIT {offset},{limit};");
|
||||
var q = Query.Select($"SELECT * from \"Movies\" , \"MovieFiles\", \"QualityDefinitions\" WHERE Movies.MovieFileId=MovieFiles.Id AND instr(MovieFiles.Quality, ('quality\": ' || QualityDefinitions.Quality || \",\")) > 0 ORDER BY QualityDefinitions.Title {direction} LIMIT {offset},{limit};");
|
||||
var q2 = mapper.Query<Movie>("SELECT * from \"Movies\" , \"MovieFiles\", \"QualityDefinitions\" WHERE Movies.MovieFileId=MovieFiles.Id AND instr(MovieFiles.Quality, ('quality\": ' || QualityDefinitions.Quality || \",\")) > 0 ORDER BY QualityDefinitions.Title ASC;");
|
||||
|
||||
//var ok = q.BuildQuery();
|
||||
@ -122,9 +123,11 @@ public override PagingSpec<Movie> GetPaged(PagingSpec<Movie> pagingSpec)
|
||||
pagingSpec.TotalRecords = q2.Count();
|
||||
|
||||
}
|
||||
else
|
||||
else*/
|
||||
{
|
||||
pagingSpec = base.GetPaged(pagingSpec);
|
||||
//pagingSpec.Records = GetPagedQuery(Query, pagingSpec).ToList();
|
||||
//pagingSpec.TotalRecords = GetPagedQuery(Query, pagingSpec).GetRowCount();
|
||||
}
|
||||
|
||||
if (pagingSpec.Records.Count == 0 && pagingSpec.Page != 1)
|
||||
@ -136,6 +139,22 @@ public override PagingSpec<Movie> GetPaged(PagingSpec<Movie> pagingSpec)
|
||||
|
||||
return pagingSpec;
|
||||
}
|
||||
|
||||
/*protected override SortBuilder<Movie> GetPagedQuery(QueryBuilder<Movie> query, PagingSpec<Movie> pagingSpec)
|
||||
{
|
||||
return DataMapper.Query<Movie>().Join<Movie, AlternativeTitle>(JoinType.Left, m => m.AlternativeTitles,
|
||||
(m, t) => m.Id == t.MovieId).Where(pagingSpec.FilterExpression)
|
||||
.OrderBy(pagingSpec.OrderByClause(), pagingSpec.ToSortDirection())
|
||||
.Skip(pagingSpec.PagingOffset())
|
||||
.Take(pagingSpec.PageSize);
|
||||
}*/
|
||||
|
||||
/*protected override SortBuilder<Movie> GetPagedQuery(QueryBuilder<Movie> query, PagingSpec<Movie> pagingSpec)
|
||||
{
|
||||
var newQuery = base.GetPagedQuery(query.Join<Movie, AlternativeTitle>(JoinType.Left, m => m.JoinAlternativeTitles, (movie, title) => title.MovieId == movie.Id), pagingSpec);
|
||||
System.Console.WriteLine(newQuery.ToString());
|
||||
return newQuery;
|
||||
}*/
|
||||
|
||||
public SortBuilder<Movie> GetMoviesWithoutFilesQuery(PagingSpec<Movie> pagingSpec)
|
||||
{
|
||||
@ -247,22 +266,39 @@ private Movie FindByTitle(string cleanTitle, int? year)
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
IEnumerable<Movie> movies = All();
|
||||
/*IEnumerable<Movie> movies = All();
|
||||
Func<string, string> titleCleaner = title => CoreParser.CleanSeriesTitle(title.ToLower());
|
||||
Func<IEnumerable<string>, string, bool> altTitleComparer =
|
||||
Func<IEnumerable<AlternativeTitle>, string, bool> altTitleComparer =
|
||||
(alternativeTitles, atitle) =>
|
||||
alternativeTitles.Any(altTitle => titleCleaner(altTitle) == atitle);
|
||||
alternativeTitles.Any(altTitle => altTitle.CleanTitle == atitle);*/
|
||||
|
||||
result = movies.Where(m => altTitleComparer(m.AlternativeTitles, cleanTitle) ||
|
||||
/*result = movies.Where(m => altTitleComparer(m.AlternativeTitles, cleanTitle) ||
|
||||
altTitleComparer(m.AlternativeTitles, cleanTitleWithRomanNumbers) ||
|
||||
altTitleComparer(m.AlternativeTitles, cleanTitleWithArabicNumbers)).FirstWithYear(year);
|
||||
altTitleComparer(m.AlternativeTitles, cleanTitleWithArabicNumbers)).FirstWithYear(year);*/
|
||||
|
||||
//result = Query.Join<Movie, AlternativeTitle>(JoinType.Inner, m => m._newAltTitles,
|
||||
//(m, t) => m.Id == t.MovieId && (t.CleanTitle == cleanTitle)).FirstWithYear(year);
|
||||
result = Query.Where<AlternativeTitle>(t =>
|
||||
t.CleanTitle == cleanTitle || t.CleanTitle == cleanTitleWithArabicNumbers
|
||||
|| t.CleanTitle == cleanTitleWithRomanNumbers).FirstWithYear(year);
|
||||
|
||||
}
|
||||
}
|
||||
return result;
|
||||
/*return year.HasValue
|
||||
? results?.FirstOrDefault(movie => movie.Year == year.Value)
|
||||
: results?.FirstOrDefault();*/
|
||||
|
||||
|
||||
: results?.FirstOrDefault();*/
|
||||
}
|
||||
|
||||
protected override QueryBuilder<Movie> AddJoinQueries(QueryBuilder<Movie> baseQuery)
|
||||
{
|
||||
baseQuery = base.AddJoinQueries(baseQuery);
|
||||
baseQuery = baseQuery.Join<Movie, AlternativeTitle>(JoinType.Left, m => m.AlternativeTitles,
|
||||
(m, t) => m.Id == t.MovieId);
|
||||
|
||||
return baseQuery;
|
||||
}
|
||||
|
||||
public Movie FindByTmdbId(int tmdbid)
|
||||
|
@ -16,7 +16,7 @@ public static class QueryExtensions
|
||||
{
|
||||
public static Movie FirstWithYear(this SortBuilder<Movie> query, int? year)
|
||||
{
|
||||
return year.HasValue ? query.FirstOrDefault(movie => movie.Year == year) : query.FirstOrDefault();
|
||||
return year.HasValue ? query.FirstOrDefault(movie => movie.Year == year || movie.SecondaryYear == year) : query.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@ public static class EnumerableExtensions
|
||||
{
|
||||
public static Movie FirstWithYear(this IEnumerable<Movie> query, int? year)
|
||||
{
|
||||
return year.HasValue ? query.FirstOrDefault(movie => movie.Year == year) : query.FirstOrDefault();
|
||||
return year.HasValue ? query.FirstOrDefault(movie => movie.Year == year || movie.SecondaryYear == year) : query.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,8 @@
|
||||
using NzbDrone.Core.Tv.Commands;
|
||||
using NzbDrone.Core.Tv.Events;
|
||||
using NzbDrone.Core.MediaFiles.Commands;
|
||||
using NzbDrone.Core.MetadataSource.RadarrAPI;
|
||||
using NzbDrone.Core.Movies.AlternativeTitles;
|
||||
|
||||
namespace NzbDrone.Core.Tv
|
||||
{
|
||||
@ -21,27 +23,34 @@ public class RefreshMovieService : IExecute<RefreshMovieCommand>
|
||||
{
|
||||
private readonly IProvideMovieInfo _movieInfo;
|
||||
private readonly IMovieService _movieService;
|
||||
private readonly IAlternativeTitleService _titleService;
|
||||
private readonly IRefreshEpisodeService _refreshEpisodeService;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly IManageCommandQueue _commandQueueManager;
|
||||
private readonly IDiskScanService _diskScanService;
|
||||
private readonly ICheckIfMovieShouldBeRefreshed _checkIfMovieShouldBeRefreshed;
|
||||
private readonly IRadarrAPIClient _apiClient;
|
||||
|
||||
private readonly Logger _logger;
|
||||
|
||||
public RefreshMovieService(IProvideMovieInfo movieInfo,
|
||||
IMovieService movieService,
|
||||
IAlternativeTitleService titleService,
|
||||
IRefreshEpisodeService refreshEpisodeService,
|
||||
IEventAggregator eventAggregator,
|
||||
IDiskScanService diskScanService,
|
||||
IRadarrAPIClient apiClient,
|
||||
ICheckIfMovieShouldBeRefreshed checkIfMovieShouldBeRefreshed,
|
||||
IManageCommandQueue commandQueue,
|
||||
Logger logger)
|
||||
{
|
||||
_movieInfo = movieInfo;
|
||||
_movieService = movieService;
|
||||
_titleService = titleService;
|
||||
_refreshEpisodeService = refreshEpisodeService;
|
||||
_eventAggregator = eventAggregator;
|
||||
_commandQueueManager = commandQueue;
|
||||
_apiClient = apiClient;
|
||||
_commandQueueManager = commandQueue;
|
||||
_diskScanService = diskScanService;
|
||||
_checkIfMovieShouldBeRefreshed = checkIfMovieShouldBeRefreshed;
|
||||
_logger = logger;
|
||||
@ -85,7 +94,7 @@ private void RefreshMovieInfo(Movie movie)
|
||||
movie.Certification = movieInfo.Certification;
|
||||
movie.InCinemas = movieInfo.InCinemas;
|
||||
movie.Website = movieInfo.Website;
|
||||
movie.AlternativeTitles = movieInfo.AlternativeTitles;
|
||||
//movie.AlternativeTitles = movieInfo.AlternativeTitles;
|
||||
movie.Year = movieInfo.Year;
|
||||
movie.PhysicalRelease = movieInfo.PhysicalRelease;
|
||||
movie.YouTubeTrailerId = movieInfo.YouTubeTrailerId;
|
||||
@ -102,8 +111,47 @@ private void RefreshMovieInfo(Movie movie)
|
||||
_logger.Warn(e, "Couldn't update movie path for " + movie.Path);
|
||||
}
|
||||
|
||||
movieInfo.AlternativeTitles = movieInfo.AlternativeTitles.Where(t => t.CleanTitle != movie.CleanTitle)
|
||||
.DistinctBy(t => t.CleanTitle)
|
||||
.ExceptBy(t => t.CleanTitle, movie.AlternativeTitles, t => t.CleanTitle, EqualityComparer<string>.Default).ToList();
|
||||
|
||||
try
|
||||
{
|
||||
var mappings = _apiClient.AlternativeTitlesAndYearForMovie(movieInfo.TmdbId);
|
||||
var mappingsTitles = mappings.Item1;
|
||||
|
||||
movie.AlternativeTitles.AddRange(_titleService.AddAltTitles(movieInfo.AlternativeTitles, movie));
|
||||
|
||||
mappingsTitles = mappingsTitles.ExceptBy(t => t.CleanTitle, movie.AlternativeTitles,
|
||||
t => t.CleanTitle, EqualityComparer<string>.Default).ToList();
|
||||
|
||||
movie.AlternativeTitles.AddRange(_titleService.AddAltTitles(mappingsTitles, movie));
|
||||
|
||||
if (mappings.Item2 != null)
|
||||
{
|
||||
movie.SecondaryYear = mappings.Item2.Year;
|
||||
movie.SecondaryYearSourceId = mappings.Item2.SourceId;
|
||||
}
|
||||
}
|
||||
catch (RadarrAPIException ex)
|
||||
{
|
||||
//Not that wild, could just be a 404.
|
||||
}
|
||||
|
||||
|
||||
_movieService.UpdateMovie(movie);
|
||||
|
||||
try
|
||||
{
|
||||
var newTitles = movieInfo.AlternativeTitles.Except(movie.AlternativeTitles);
|
||||
//_titleService.AddAltTitles(newTitles.ToList(), movie);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Debug(e, "Failed adding alternative titles.");
|
||||
throw;
|
||||
}
|
||||
|
||||
_logger.Debug("Finished movie refresh for {0}", movie.Title);
|
||||
_eventAggregator.PublishEvent(new MovieUpdatedEvent(movie));
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ public ShouldRefreshMovie(IEpisodeService episodeService, Logger logger)
|
||||
|
||||
public bool ShouldRefresh(Movie movie)
|
||||
{
|
||||
//return false;
|
||||
if (movie.LastInfoSync < DateTime.UtcNow.AddDays(-30))
|
||||
{
|
||||
_logger.Trace("Movie {0} last updated more than 30 days ago, should refresh.", movie.Title);
|
||||
|
@ -47,7 +47,7 @@ private bool BeValidRelease(ReleaseResource releaseResource)
|
||||
releaseResource.Age.Should().BeGreaterOrEqualTo(-1);
|
||||
releaseResource.Title.Should().NotBeNullOrWhiteSpace();
|
||||
releaseResource.DownloadUrl.Should().NotBeNullOrWhiteSpace();
|
||||
releaseResource.SeriesTitle.Should().NotBeNullOrWhiteSpace();
|
||||
releaseResource.MovieTitle.Should().NotBeNullOrWhiteSpace();
|
||||
//TODO: uncomment these after moving to restsharp for rss
|
||||
//releaseResource.NzbInfoUrl.Should().NotBeNullOrWhiteSpace();
|
||||
//releaseResource.Size.Should().BeGreaterThan(0);
|
||||
|
@ -14,6 +14,7 @@
|
||||
"define": true,
|
||||
"window": true,
|
||||
"document": true,
|
||||
"console": true
|
||||
"console": true,
|
||||
"_": true
|
||||
}
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ module.exports = Marionette.Layout.extend({
|
||||
|
||||
if (options.action === "search") {
|
||||
this.search({term: options.query});
|
||||
} else if (options.action == "discover") {
|
||||
} else if (options.action === "discover") {
|
||||
this.isDiscover = true;
|
||||
}
|
||||
|
||||
@ -254,7 +254,7 @@ module.exports = Marionette.Layout.extend({
|
||||
|
||||
_discover : function(action) {
|
||||
if (this.collection.action === action) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.collection.specialProperty === "special") {
|
||||
|
@ -39,14 +39,14 @@ module.exports = Backgrid.Cell.extend({
|
||||
break;
|
||||
case "PTP_Approved":
|
||||
addon = "✔";
|
||||
title = "Approved by PTP"
|
||||
title = "Approved by PTP";
|
||||
break;
|
||||
case "HDB_Internal":
|
||||
addon = "⭐️";
|
||||
title = "HDBits Internal";
|
||||
break;
|
||||
}
|
||||
if (addon != "") {
|
||||
if (addon !== "") {
|
||||
html += "<span title='{0}'>{1}</span> ".format(title, addon);
|
||||
}
|
||||
});
|
||||
|
Binary file not shown.
@ -7,6 +7,15 @@
|
||||
border-radius: .1em;
|
||||
}
|
||||
|
||||
.@{fa-css-prefix}-pull-left { float: left; }
|
||||
.@{fa-css-prefix}-pull-right { float: right; }
|
||||
|
||||
.@{fa-css-prefix} {
|
||||
&.@{fa-css-prefix}-pull-left { margin-right: .3em; }
|
||||
&.@{fa-css-prefix}-pull-right { margin-left: .3em; }
|
||||
}
|
||||
|
||||
/* Deprecated as of 4.4.0 */
|
||||
.pull-right { float: right; }
|
||||
.pull-left { float: left; }
|
||||
|
||||
|
@ -3,11 +3,10 @@
|
||||
|
||||
.@{fa-css-prefix} {
|
||||
display: inline-block;
|
||||
font: normal normal normal @fa-font-size-base/1 FontAwesome; // shortening font declaration
|
||||
font: normal normal normal @fa-font-size-base/@fa-line-height-base FontAwesome; // shortening font declaration
|
||||
font-size: inherit; // can't have font-size inherit on line above, so need to override
|
||||
text-rendering: auto; // optimizelegibility throws things off #1094
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
transform: translate(0, 0); // ensures no half-pixel rendering in firefox
|
||||
|
||||
}
|
||||
|
3
src/UI/Content/FontAwesome/font-awesome.less
vendored
3
src/UI/Content/FontAwesome/font-awesome.less
vendored
@ -1,5 +1,5 @@
|
||||
/*!
|
||||
* Font Awesome 4.3.0 by @davegandy - http://fontawesome.io - @fontawesome
|
||||
* Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome
|
||||
* License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
|
||||
*/
|
||||
|
||||
@ -15,3 +15,4 @@
|
||||
@import "rotated-flipped.less";
|
||||
@import "stacked.less";
|
||||
@import "icons.less";
|
||||
@import "screen-reader.less";
|
||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Before Width: | Height: | Size: 306 KiB After Width: | Height: | Size: 434 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -163,6 +163,7 @@
|
||||
.@{fa-css-prefix}-github:before { content: @fa-var-github; }
|
||||
.@{fa-css-prefix}-unlock:before { content: @fa-var-unlock; }
|
||||
.@{fa-css-prefix}-credit-card:before { content: @fa-var-credit-card; }
|
||||
.@{fa-css-prefix}-feed:before,
|
||||
.@{fa-css-prefix}-rss:before { content: @fa-var-rss; }
|
||||
.@{fa-css-prefix}-hdd-o:before { content: @fa-var-hdd-o; }
|
||||
.@{fa-css-prefix}-bullhorn:before { content: @fa-var-bullhorn; }
|
||||
@ -437,7 +438,7 @@
|
||||
.@{fa-css-prefix}-stumbleupon:before { content: @fa-var-stumbleupon; }
|
||||
.@{fa-css-prefix}-delicious:before { content: @fa-var-delicious; }
|
||||
.@{fa-css-prefix}-digg:before { content: @fa-var-digg; }
|
||||
.@{fa-css-prefix}-pied-piper:before { content: @fa-var-pied-piper; }
|
||||
.@{fa-css-prefix}-pied-piper-pp:before { content: @fa-var-pied-piper-pp; }
|
||||
.@{fa-css-prefix}-pied-piper-alt:before { content: @fa-var-pied-piper-alt; }
|
||||
.@{fa-css-prefix}-drupal:before { content: @fa-var-drupal; }
|
||||
.@{fa-css-prefix}-joomla:before { content: @fa-var-joomla; }
|
||||
@ -487,11 +488,14 @@
|
||||
.@{fa-css-prefix}-life-ring:before { content: @fa-var-life-ring; }
|
||||
.@{fa-css-prefix}-circle-o-notch:before { content: @fa-var-circle-o-notch; }
|
||||
.@{fa-css-prefix}-ra:before,
|
||||
.@{fa-css-prefix}-resistance:before,
|
||||
.@{fa-css-prefix}-rebel:before { content: @fa-var-rebel; }
|
||||
.@{fa-css-prefix}-ge:before,
|
||||
.@{fa-css-prefix}-empire:before { content: @fa-var-empire; }
|
||||
.@{fa-css-prefix}-git-square:before { content: @fa-var-git-square; }
|
||||
.@{fa-css-prefix}-git:before { content: @fa-var-git; }
|
||||
.@{fa-css-prefix}-y-combinator-square:before,
|
||||
.@{fa-css-prefix}-yc-square:before,
|
||||
.@{fa-css-prefix}-hacker-news:before { content: @fa-var-hacker-news; }
|
||||
.@{fa-css-prefix}-tencent-weibo:before { content: @fa-var-tencent-weibo; }
|
||||
.@{fa-css-prefix}-qq:before { content: @fa-var-qq; }
|
||||
@ -502,7 +506,6 @@
|
||||
.@{fa-css-prefix}-send-o:before,
|
||||
.@{fa-css-prefix}-paper-plane-o:before { content: @fa-var-paper-plane-o; }
|
||||
.@{fa-css-prefix}-history:before { content: @fa-var-history; }
|
||||
.@{fa-css-prefix}-genderless:before,
|
||||
.@{fa-css-prefix}-circle-thin:before { content: @fa-var-circle-thin; }
|
||||
.@{fa-css-prefix}-header:before { content: @fa-var-header; }
|
||||
.@{fa-css-prefix}-paragraph:before { content: @fa-var-paragraph; }
|
||||
@ -573,6 +576,7 @@
|
||||
.@{fa-css-prefix}-venus:before { content: @fa-var-venus; }
|
||||
.@{fa-css-prefix}-mars:before { content: @fa-var-mars; }
|
||||
.@{fa-css-prefix}-mercury:before { content: @fa-var-mercury; }
|
||||
.@{fa-css-prefix}-intersex:before,
|
||||
.@{fa-css-prefix}-transgender:before { content: @fa-var-transgender; }
|
||||
.@{fa-css-prefix}-transgender-alt:before { content: @fa-var-transgender-alt; }
|
||||
.@{fa-css-prefix}-venus-double:before { content: @fa-var-venus-double; }
|
||||
@ -582,6 +586,7 @@
|
||||
.@{fa-css-prefix}-mars-stroke-v:before { content: @fa-var-mars-stroke-v; }
|
||||
.@{fa-css-prefix}-mars-stroke-h:before { content: @fa-var-mars-stroke-h; }
|
||||
.@{fa-css-prefix}-neuter:before { content: @fa-var-neuter; }
|
||||
.@{fa-css-prefix}-genderless:before { content: @fa-var-genderless; }
|
||||
.@{fa-css-prefix}-facebook-official:before { content: @fa-var-facebook-official; }
|
||||
.@{fa-css-prefix}-pinterest-p:before { content: @fa-var-pinterest-p; }
|
||||
.@{fa-css-prefix}-whatsapp:before { content: @fa-var-whatsapp; }
|
||||
@ -594,3 +599,191 @@
|
||||
.@{fa-css-prefix}-train:before { content: @fa-var-train; }
|
||||
.@{fa-css-prefix}-subway:before { content: @fa-var-subway; }
|
||||
.@{fa-css-prefix}-medium:before { content: @fa-var-medium; }
|
||||
.@{fa-css-prefix}-yc:before,
|
||||
.@{fa-css-prefix}-y-combinator:before { content: @fa-var-y-combinator; }
|
||||
.@{fa-css-prefix}-optin-monster:before { content: @fa-var-optin-monster; }
|
||||
.@{fa-css-prefix}-opencart:before { content: @fa-var-opencart; }
|
||||
.@{fa-css-prefix}-expeditedssl:before { content: @fa-var-expeditedssl; }
|
||||
.@{fa-css-prefix}-battery-4:before,
|
||||
.@{fa-css-prefix}-battery:before,
|
||||
.@{fa-css-prefix}-battery-full:before { content: @fa-var-battery-full; }
|
||||
.@{fa-css-prefix}-battery-3:before,
|
||||
.@{fa-css-prefix}-battery-three-quarters:before { content: @fa-var-battery-three-quarters; }
|
||||
.@{fa-css-prefix}-battery-2:before,
|
||||
.@{fa-css-prefix}-battery-half:before { content: @fa-var-battery-half; }
|
||||
.@{fa-css-prefix}-battery-1:before,
|
||||
.@{fa-css-prefix}-battery-quarter:before { content: @fa-var-battery-quarter; }
|
||||
.@{fa-css-prefix}-battery-0:before,
|
||||
.@{fa-css-prefix}-battery-empty:before { content: @fa-var-battery-empty; }
|
||||
.@{fa-css-prefix}-mouse-pointer:before { content: @fa-var-mouse-pointer; }
|
||||
.@{fa-css-prefix}-i-cursor:before { content: @fa-var-i-cursor; }
|
||||
.@{fa-css-prefix}-object-group:before { content: @fa-var-object-group; }
|
||||
.@{fa-css-prefix}-object-ungroup:before { content: @fa-var-object-ungroup; }
|
||||
.@{fa-css-prefix}-sticky-note:before { content: @fa-var-sticky-note; }
|
||||
.@{fa-css-prefix}-sticky-note-o:before { content: @fa-var-sticky-note-o; }
|
||||
.@{fa-css-prefix}-cc-jcb:before { content: @fa-var-cc-jcb; }
|
||||
.@{fa-css-prefix}-cc-diners-club:before { content: @fa-var-cc-diners-club; }
|
||||
.@{fa-css-prefix}-clone:before { content: @fa-var-clone; }
|
||||
.@{fa-css-prefix}-balance-scale:before { content: @fa-var-balance-scale; }
|
||||
.@{fa-css-prefix}-hourglass-o:before { content: @fa-var-hourglass-o; }
|
||||
.@{fa-css-prefix}-hourglass-1:before,
|
||||
.@{fa-css-prefix}-hourglass-start:before { content: @fa-var-hourglass-start; }
|
||||
.@{fa-css-prefix}-hourglass-2:before,
|
||||
.@{fa-css-prefix}-hourglass-half:before { content: @fa-var-hourglass-half; }
|
||||
.@{fa-css-prefix}-hourglass-3:before,
|
||||
.@{fa-css-prefix}-hourglass-end:before { content: @fa-var-hourglass-end; }
|
||||
.@{fa-css-prefix}-hourglass:before { content: @fa-var-hourglass; }
|
||||
.@{fa-css-prefix}-hand-grab-o:before,
|
||||
.@{fa-css-prefix}-hand-rock-o:before { content: @fa-var-hand-rock-o; }
|
||||
.@{fa-css-prefix}-hand-stop-o:before,
|
||||
.@{fa-css-prefix}-hand-paper-o:before { content: @fa-var-hand-paper-o; }
|
||||
.@{fa-css-prefix}-hand-scissors-o:before { content: @fa-var-hand-scissors-o; }
|
||||
.@{fa-css-prefix}-hand-lizard-o:before { content: @fa-var-hand-lizard-o; }
|
||||
.@{fa-css-prefix}-hand-spock-o:before { content: @fa-var-hand-spock-o; }
|
||||
.@{fa-css-prefix}-hand-pointer-o:before { content: @fa-var-hand-pointer-o; }
|
||||
.@{fa-css-prefix}-hand-peace-o:before { content: @fa-var-hand-peace-o; }
|
||||
.@{fa-css-prefix}-trademark:before { content: @fa-var-trademark; }
|
||||
.@{fa-css-prefix}-registered:before { content: @fa-var-registered; }
|
||||
.@{fa-css-prefix}-creative-commons:before { content: @fa-var-creative-commons; }
|
||||
.@{fa-css-prefix}-gg:before { content: @fa-var-gg; }
|
||||
.@{fa-css-prefix}-gg-circle:before { content: @fa-var-gg-circle; }
|
||||
.@{fa-css-prefix}-tripadvisor:before { content: @fa-var-tripadvisor; }
|
||||
.@{fa-css-prefix}-odnoklassniki:before { content: @fa-var-odnoklassniki; }
|
||||
.@{fa-css-prefix}-odnoklassniki-square:before { content: @fa-var-odnoklassniki-square; }
|
||||
.@{fa-css-prefix}-get-pocket:before { content: @fa-var-get-pocket; }
|
||||
.@{fa-css-prefix}-wikipedia-w:before { content: @fa-var-wikipedia-w; }
|
||||
.@{fa-css-prefix}-safari:before { content: @fa-var-safari; }
|
||||
.@{fa-css-prefix}-chrome:before { content: @fa-var-chrome; }
|
||||
.@{fa-css-prefix}-firefox:before { content: @fa-var-firefox; }
|
||||
.@{fa-css-prefix}-opera:before { content: @fa-var-opera; }
|
||||
.@{fa-css-prefix}-internet-explorer:before { content: @fa-var-internet-explorer; }
|
||||
.@{fa-css-prefix}-tv:before,
|
||||
.@{fa-css-prefix}-television:before { content: @fa-var-television; }
|
||||
.@{fa-css-prefix}-contao:before { content: @fa-var-contao; }
|
||||
.@{fa-css-prefix}-500px:before { content: @fa-var-500px; }
|
||||
.@{fa-css-prefix}-amazon:before { content: @fa-var-amazon; }
|
||||
.@{fa-css-prefix}-calendar-plus-o:before { content: @fa-var-calendar-plus-o; }
|
||||
.@{fa-css-prefix}-calendar-minus-o:before { content: @fa-var-calendar-minus-o; }
|
||||
.@{fa-css-prefix}-calendar-times-o:before { content: @fa-var-calendar-times-o; }
|
||||
.@{fa-css-prefix}-calendar-check-o:before { content: @fa-var-calendar-check-o; }
|
||||
.@{fa-css-prefix}-industry:before { content: @fa-var-industry; }
|
||||
.@{fa-css-prefix}-map-pin:before { content: @fa-var-map-pin; }
|
||||
.@{fa-css-prefix}-map-signs:before { content: @fa-var-map-signs; }
|
||||
.@{fa-css-prefix}-map-o:before { content: @fa-var-map-o; }
|
||||
.@{fa-css-prefix}-map:before { content: @fa-var-map; }
|
||||
.@{fa-css-prefix}-commenting:before { content: @fa-var-commenting; }
|
||||
.@{fa-css-prefix}-commenting-o:before { content: @fa-var-commenting-o; }
|
||||
.@{fa-css-prefix}-houzz:before { content: @fa-var-houzz; }
|
||||
.@{fa-css-prefix}-vimeo:before { content: @fa-var-vimeo; }
|
||||
.@{fa-css-prefix}-black-tie:before { content: @fa-var-black-tie; }
|
||||
.@{fa-css-prefix}-fonticons:before { content: @fa-var-fonticons; }
|
||||
.@{fa-css-prefix}-reddit-alien:before { content: @fa-var-reddit-alien; }
|
||||
.@{fa-css-prefix}-edge:before { content: @fa-var-edge; }
|
||||
.@{fa-css-prefix}-credit-card-alt:before { content: @fa-var-credit-card-alt; }
|
||||
.@{fa-css-prefix}-codiepie:before { content: @fa-var-codiepie; }
|
||||
.@{fa-css-prefix}-modx:before { content: @fa-var-modx; }
|
||||
.@{fa-css-prefix}-fort-awesome:before { content: @fa-var-fort-awesome; }
|
||||
.@{fa-css-prefix}-usb:before { content: @fa-var-usb; }
|
||||
.@{fa-css-prefix}-product-hunt:before { content: @fa-var-product-hunt; }
|
||||
.@{fa-css-prefix}-mixcloud:before { content: @fa-var-mixcloud; }
|
||||
.@{fa-css-prefix}-scribd:before { content: @fa-var-scribd; }
|
||||
.@{fa-css-prefix}-pause-circle:before { content: @fa-var-pause-circle; }
|
||||
.@{fa-css-prefix}-pause-circle-o:before { content: @fa-var-pause-circle-o; }
|
||||
.@{fa-css-prefix}-stop-circle:before { content: @fa-var-stop-circle; }
|
||||
.@{fa-css-prefix}-stop-circle-o:before { content: @fa-var-stop-circle-o; }
|
||||
.@{fa-css-prefix}-shopping-bag:before { content: @fa-var-shopping-bag; }
|
||||
.@{fa-css-prefix}-shopping-basket:before { content: @fa-var-shopping-basket; }
|
||||
.@{fa-css-prefix}-hashtag:before { content: @fa-var-hashtag; }
|
||||
.@{fa-css-prefix}-bluetooth:before { content: @fa-var-bluetooth; }
|
||||
.@{fa-css-prefix}-bluetooth-b:before { content: @fa-var-bluetooth-b; }
|
||||
.@{fa-css-prefix}-percent:before { content: @fa-var-percent; }
|
||||
.@{fa-css-prefix}-gitlab:before { content: @fa-var-gitlab; }
|
||||
.@{fa-css-prefix}-wpbeginner:before { content: @fa-var-wpbeginner; }
|
||||
.@{fa-css-prefix}-wpforms:before { content: @fa-var-wpforms; }
|
||||
.@{fa-css-prefix}-envira:before { content: @fa-var-envira; }
|
||||
.@{fa-css-prefix}-universal-access:before { content: @fa-var-universal-access; }
|
||||
.@{fa-css-prefix}-wheelchair-alt:before { content: @fa-var-wheelchair-alt; }
|
||||
.@{fa-css-prefix}-question-circle-o:before { content: @fa-var-question-circle-o; }
|
||||
.@{fa-css-prefix}-blind:before { content: @fa-var-blind; }
|
||||
.@{fa-css-prefix}-audio-description:before { content: @fa-var-audio-description; }
|
||||
.@{fa-css-prefix}-volume-control-phone:before { content: @fa-var-volume-control-phone; }
|
||||
.@{fa-css-prefix}-braille:before { content: @fa-var-braille; }
|
||||
.@{fa-css-prefix}-assistive-listening-systems:before { content: @fa-var-assistive-listening-systems; }
|
||||
.@{fa-css-prefix}-asl-interpreting:before,
|
||||
.@{fa-css-prefix}-american-sign-language-interpreting:before { content: @fa-var-american-sign-language-interpreting; }
|
||||
.@{fa-css-prefix}-deafness:before,
|
||||
.@{fa-css-prefix}-hard-of-hearing:before,
|
||||
.@{fa-css-prefix}-deaf:before { content: @fa-var-deaf; }
|
||||
.@{fa-css-prefix}-glide:before { content: @fa-var-glide; }
|
||||
.@{fa-css-prefix}-glide-g:before { content: @fa-var-glide-g; }
|
||||
.@{fa-css-prefix}-signing:before,
|
||||
.@{fa-css-prefix}-sign-language:before { content: @fa-var-sign-language; }
|
||||
.@{fa-css-prefix}-low-vision:before { content: @fa-var-low-vision; }
|
||||
.@{fa-css-prefix}-viadeo:before { content: @fa-var-viadeo; }
|
||||
.@{fa-css-prefix}-viadeo-square:before { content: @fa-var-viadeo-square; }
|
||||
.@{fa-css-prefix}-snapchat:before { content: @fa-var-snapchat; }
|
||||
.@{fa-css-prefix}-snapchat-ghost:before { content: @fa-var-snapchat-ghost; }
|
||||
.@{fa-css-prefix}-snapchat-square:before { content: @fa-var-snapchat-square; }
|
||||
.@{fa-css-prefix}-pied-piper:before { content: @fa-var-pied-piper; }
|
||||
.@{fa-css-prefix}-first-order:before { content: @fa-var-first-order; }
|
||||
.@{fa-css-prefix}-yoast:before { content: @fa-var-yoast; }
|
||||
.@{fa-css-prefix}-themeisle:before { content: @fa-var-themeisle; }
|
||||
.@{fa-css-prefix}-google-plus-circle:before,
|
||||
.@{fa-css-prefix}-google-plus-official:before { content: @fa-var-google-plus-official; }
|
||||
.@{fa-css-prefix}-fa:before,
|
||||
.@{fa-css-prefix}-font-awesome:before { content: @fa-var-font-awesome; }
|
||||
.@{fa-css-prefix}-handshake-o:before { content: @fa-var-handshake-o; }
|
||||
.@{fa-css-prefix}-envelope-open:before { content: @fa-var-envelope-open; }
|
||||
.@{fa-css-prefix}-envelope-open-o:before { content: @fa-var-envelope-open-o; }
|
||||
.@{fa-css-prefix}-linode:before { content: @fa-var-linode; }
|
||||
.@{fa-css-prefix}-address-book:before { content: @fa-var-address-book; }
|
||||
.@{fa-css-prefix}-address-book-o:before { content: @fa-var-address-book-o; }
|
||||
.@{fa-css-prefix}-vcard:before,
|
||||
.@{fa-css-prefix}-address-card:before { content: @fa-var-address-card; }
|
||||
.@{fa-css-prefix}-vcard-o:before,
|
||||
.@{fa-css-prefix}-address-card-o:before { content: @fa-var-address-card-o; }
|
||||
.@{fa-css-prefix}-user-circle:before { content: @fa-var-user-circle; }
|
||||
.@{fa-css-prefix}-user-circle-o:before { content: @fa-var-user-circle-o; }
|
||||
.@{fa-css-prefix}-user-o:before { content: @fa-var-user-o; }
|
||||
.@{fa-css-prefix}-id-badge:before { content: @fa-var-id-badge; }
|
||||
.@{fa-css-prefix}-drivers-license:before,
|
||||
.@{fa-css-prefix}-id-card:before { content: @fa-var-id-card; }
|
||||
.@{fa-css-prefix}-drivers-license-o:before,
|
||||
.@{fa-css-prefix}-id-card-o:before { content: @fa-var-id-card-o; }
|
||||
.@{fa-css-prefix}-quora:before { content: @fa-var-quora; }
|
||||
.@{fa-css-prefix}-free-code-camp:before { content: @fa-var-free-code-camp; }
|
||||
.@{fa-css-prefix}-telegram:before { content: @fa-var-telegram; }
|
||||
.@{fa-css-prefix}-thermometer-4:before,
|
||||
.@{fa-css-prefix}-thermometer:before,
|
||||
.@{fa-css-prefix}-thermometer-full:before { content: @fa-var-thermometer-full; }
|
||||
.@{fa-css-prefix}-thermometer-3:before,
|
||||
.@{fa-css-prefix}-thermometer-three-quarters:before { content: @fa-var-thermometer-three-quarters; }
|
||||
.@{fa-css-prefix}-thermometer-2:before,
|
||||
.@{fa-css-prefix}-thermometer-half:before { content: @fa-var-thermometer-half; }
|
||||
.@{fa-css-prefix}-thermometer-1:before,
|
||||
.@{fa-css-prefix}-thermometer-quarter:before { content: @fa-var-thermometer-quarter; }
|
||||
.@{fa-css-prefix}-thermometer-0:before,
|
||||
.@{fa-css-prefix}-thermometer-empty:before { content: @fa-var-thermometer-empty; }
|
||||
.@{fa-css-prefix}-shower:before { content: @fa-var-shower; }
|
||||
.@{fa-css-prefix}-bathtub:before,
|
||||
.@{fa-css-prefix}-s15:before,
|
||||
.@{fa-css-prefix}-bath:before { content: @fa-var-bath; }
|
||||
.@{fa-css-prefix}-podcast:before { content: @fa-var-podcast; }
|
||||
.@{fa-css-prefix}-window-maximize:before { content: @fa-var-window-maximize; }
|
||||
.@{fa-css-prefix}-window-minimize:before { content: @fa-var-window-minimize; }
|
||||
.@{fa-css-prefix}-window-restore:before { content: @fa-var-window-restore; }
|
||||
.@{fa-css-prefix}-times-rectangle:before,
|
||||
.@{fa-css-prefix}-window-close:before { content: @fa-var-window-close; }
|
||||
.@{fa-css-prefix}-times-rectangle-o:before,
|
||||
.@{fa-css-prefix}-window-close-o:before { content: @fa-var-window-close-o; }
|
||||
.@{fa-css-prefix}-bandcamp:before { content: @fa-var-bandcamp; }
|
||||
.@{fa-css-prefix}-grav:before { content: @fa-var-grav; }
|
||||
.@{fa-css-prefix}-etsy:before { content: @fa-var-etsy; }
|
||||
.@{fa-css-prefix}-imdb:before { content: @fa-var-imdb; }
|
||||
.@{fa-css-prefix}-ravelry:before { content: @fa-var-ravelry; }
|
||||
.@{fa-css-prefix}-eercast:before { content: @fa-var-eercast; }
|
||||
.@{fa-css-prefix}-microchip:before { content: @fa-var-microchip; }
|
||||
.@{fa-css-prefix}-snowflake-o:before { content: @fa-var-snowflake-o; }
|
||||
.@{fa-css-prefix}-superpowers:before { content: @fa-var-superpowers; }
|
||||
.@{fa-css-prefix}-wpexplorer:before { content: @fa-var-wpexplorer; }
|
||||
.@{fa-css-prefix}-meetup:before { content: @fa-var-meetup; }
|
||||
|
@ -3,25 +3,58 @@
|
||||
|
||||
.fa-icon() {
|
||||
display: inline-block;
|
||||
font: normal normal normal @fa-font-size-base/1 FontAwesome; // shortening font declaration
|
||||
font: normal normal normal @fa-font-size-base/@fa-line-height-base FontAwesome; // shortening font declaration
|
||||
font-size: inherit; // can't have font-size inherit on line above, so need to override
|
||||
text-rendering: auto; // optimizelegibility throws things off #1094
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
transform: translate(0, 0); // ensures no half-pixel rendering in firefox
|
||||
|
||||
}
|
||||
|
||||
.fa-icon-rotate(@degrees, @rotation) {
|
||||
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=@rotation);
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=@{rotation})";
|
||||
-webkit-transform: rotate(@degrees);
|
||||
-ms-transform: rotate(@degrees);
|
||||
transform: rotate(@degrees);
|
||||
}
|
||||
|
||||
.fa-icon-flip(@horiz, @vert, @rotation) {
|
||||
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=@rotation, mirror=1);
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=@{rotation}, mirror=1)";
|
||||
-webkit-transform: scale(@horiz, @vert);
|
||||
-ms-transform: scale(@horiz, @vert);
|
||||
transform: scale(@horiz, @vert);
|
||||
}
|
||||
|
||||
|
||||
// Only display content to screen readers. A la Bootstrap 4.
|
||||
//
|
||||
// See: http://a11yproject.com/posts/how-to-hide-content/
|
||||
|
||||
.sr-only() {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0,0,0,0);
|
||||
border: 0;
|
||||
}
|
||||
|
||||
// Use in conjunction with .sr-only to only display content when it's focused.
|
||||
//
|
||||
// Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1
|
||||
//
|
||||
// Credit: HTML5 Boilerplate
|
||||
|
||||
.sr-only-focusable() {
|
||||
&:active,
|
||||
&:focus {
|
||||
position: static;
|
||||
width: auto;
|
||||
height: auto;
|
||||
margin: 0;
|
||||
overflow: visible;
|
||||
clip: auto;
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
url('@{fa-font-path}/fontawesome-webfont.woff?v=@{fa-version}') format('woff'),
|
||||
url('@{fa-font-path}/fontawesome-webfont.ttf?v=@{fa-version}') format('truetype'),
|
||||
url('@{fa-font-path}/fontawesome-webfont.svg?v=@{fa-version}#fontawesomeregular') format('svg');
|
||||
// src: url('@{fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts
|
||||
// src: url('@{fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
5
src/UI/Content/FontAwesome/screen-reader.less
Normal file
5
src/UI/Content/FontAwesome/screen-reader.less
Normal file
@ -0,0 +1,5 @@
|
||||
// Screen Readers
|
||||
// -------------------------
|
||||
|
||||
.sr-only { .sr-only(); }
|
||||
.sr-only-focusable { .sr-only-focusable(); }
|
@ -3,20 +3,28 @@
|
||||
|
||||
@fa-font-path: "../Content/FontAwesome";
|
||||
@fa-font-size-base: 14px;
|
||||
//@fa-font-path: "//netdna.bootstrapcdn.com/font-awesome/4.3.0/fonts"; // for referencing Bootstrap CDN font files directly
|
||||
@fa-line-height-base: 1;
|
||||
//@fa-font-path: "//netdna.bootstrapcdn.com/font-awesome/4.7.0/fonts"; // for referencing Bootstrap CDN font files directly
|
||||
@fa-css-prefix: fa;
|
||||
@fa-version: "4.3.0";
|
||||
@fa-version: "4.7.0";
|
||||
@fa-border-color: #eee;
|
||||
@fa-inverse: #fff;
|
||||
@fa-li-width: (30em / 14);
|
||||
|
||||
@fa-var-500px: "\f26e";
|
||||
@fa-var-address-book: "\f2b9";
|
||||
@fa-var-address-book-o: "\f2ba";
|
||||
@fa-var-address-card: "\f2bb";
|
||||
@fa-var-address-card-o: "\f2bc";
|
||||
@fa-var-adjust: "\f042";
|
||||
@fa-var-adn: "\f170";
|
||||
@fa-var-align-center: "\f037";
|
||||
@fa-var-align-justify: "\f039";
|
||||
@fa-var-align-left: "\f036";
|
||||
@fa-var-align-right: "\f038";
|
||||
@fa-var-amazon: "\f270";
|
||||
@fa-var-ambulance: "\f0f9";
|
||||
@fa-var-american-sign-language-interpreting: "\f2a3";
|
||||
@fa-var-anchor: "\f13d";
|
||||
@fa-var-android: "\f17b";
|
||||
@fa-var-angellist: "\f209";
|
||||
@ -47,16 +55,34 @@
|
||||
@fa-var-arrows-alt: "\f0b2";
|
||||
@fa-var-arrows-h: "\f07e";
|
||||
@fa-var-arrows-v: "\f07d";
|
||||
@fa-var-asl-interpreting: "\f2a3";
|
||||
@fa-var-assistive-listening-systems: "\f2a2";
|
||||
@fa-var-asterisk: "\f069";
|
||||
@fa-var-at: "\f1fa";
|
||||
@fa-var-audio-description: "\f29e";
|
||||
@fa-var-automobile: "\f1b9";
|
||||
@fa-var-backward: "\f04a";
|
||||
@fa-var-balance-scale: "\f24e";
|
||||
@fa-var-ban: "\f05e";
|
||||
@fa-var-bandcamp: "\f2d5";
|
||||
@fa-var-bank: "\f19c";
|
||||
@fa-var-bar-chart: "\f080";
|
||||
@fa-var-bar-chart-o: "\f080";
|
||||
@fa-var-barcode: "\f02a";
|
||||
@fa-var-bars: "\f0c9";
|
||||
@fa-var-bath: "\f2cd";
|
||||
@fa-var-bathtub: "\f2cd";
|
||||
@fa-var-battery: "\f240";
|
||||
@fa-var-battery-0: "\f244";
|
||||
@fa-var-battery-1: "\f243";
|
||||
@fa-var-battery-2: "\f242";
|
||||
@fa-var-battery-3: "\f241";
|
||||
@fa-var-battery-4: "\f240";
|
||||
@fa-var-battery-empty: "\f244";
|
||||
@fa-var-battery-full: "\f240";
|
||||
@fa-var-battery-half: "\f242";
|
||||
@fa-var-battery-quarter: "\f243";
|
||||
@fa-var-battery-three-quarters: "\f241";
|
||||
@fa-var-bed: "\f236";
|
||||
@fa-var-beer: "\f0fc";
|
||||
@fa-var-behance: "\f1b4";
|
||||
@ -71,12 +97,17 @@
|
||||
@fa-var-bitbucket: "\f171";
|
||||
@fa-var-bitbucket-square: "\f172";
|
||||
@fa-var-bitcoin: "\f15a";
|
||||
@fa-var-black-tie: "\f27e";
|
||||
@fa-var-blind: "\f29d";
|
||||
@fa-var-bluetooth: "\f293";
|
||||
@fa-var-bluetooth-b: "\f294";
|
||||
@fa-var-bold: "\f032";
|
||||
@fa-var-bolt: "\f0e7";
|
||||
@fa-var-bomb: "\f1e2";
|
||||
@fa-var-book: "\f02d";
|
||||
@fa-var-bookmark: "\f02e";
|
||||
@fa-var-bookmark-o: "\f097";
|
||||
@fa-var-braille: "\f2a1";
|
||||
@fa-var-briefcase: "\f0b1";
|
||||
@fa-var-btc: "\f15a";
|
||||
@fa-var-bug: "\f188";
|
||||
@ -89,7 +120,11 @@
|
||||
@fa-var-cab: "\f1ba";
|
||||
@fa-var-calculator: "\f1ec";
|
||||
@fa-var-calendar: "\f073";
|
||||
@fa-var-calendar-check-o: "\f274";
|
||||
@fa-var-calendar-minus-o: "\f272";
|
||||
@fa-var-calendar-o: "\f133";
|
||||
@fa-var-calendar-plus-o: "\f271";
|
||||
@fa-var-calendar-times-o: "\f273";
|
||||
@fa-var-camera: "\f030";
|
||||
@fa-var-camera-retro: "\f083";
|
||||
@fa-var-car: "\f1b9";
|
||||
@ -105,7 +140,9 @@
|
||||
@fa-var-cart-plus: "\f217";
|
||||
@fa-var-cc: "\f20a";
|
||||
@fa-var-cc-amex: "\f1f3";
|
||||
@fa-var-cc-diners-club: "\f24c";
|
||||
@fa-var-cc-discover: "\f1f2";
|
||||
@fa-var-cc-jcb: "\f24b";
|
||||
@fa-var-cc-mastercard: "\f1f1";
|
||||
@fa-var-cc-paypal: "\f1f4";
|
||||
@fa-var-cc-stripe: "\f1f5";
|
||||
@ -127,12 +164,14 @@
|
||||
@fa-var-chevron-right: "\f054";
|
||||
@fa-var-chevron-up: "\f077";
|
||||
@fa-var-child: "\f1ae";
|
||||
@fa-var-chrome: "\f268";
|
||||
@fa-var-circle: "\f111";
|
||||
@fa-var-circle-o: "\f10c";
|
||||
@fa-var-circle-o-notch: "\f1ce";
|
||||
@fa-var-circle-thin: "\f1db";
|
||||
@fa-var-clipboard: "\f0ea";
|
||||
@fa-var-clock-o: "\f017";
|
||||
@fa-var-clone: "\f24d";
|
||||
@fa-var-close: "\f00d";
|
||||
@fa-var-cloud: "\f0c2";
|
||||
@fa-var-cloud-download: "\f0ed";
|
||||
@ -141,20 +180,26 @@
|
||||
@fa-var-code: "\f121";
|
||||
@fa-var-code-fork: "\f126";
|
||||
@fa-var-codepen: "\f1cb";
|
||||
@fa-var-codiepie: "\f284";
|
||||
@fa-var-coffee: "\f0f4";
|
||||
@fa-var-cog: "\f013";
|
||||
@fa-var-cogs: "\f085";
|
||||
@fa-var-columns: "\f0db";
|
||||
@fa-var-comment: "\f075";
|
||||
@fa-var-comment-o: "\f0e5";
|
||||
@fa-var-commenting: "\f27a";
|
||||
@fa-var-commenting-o: "\f27b";
|
||||
@fa-var-comments: "\f086";
|
||||
@fa-var-comments-o: "\f0e6";
|
||||
@fa-var-compass: "\f14e";
|
||||
@fa-var-compress: "\f066";
|
||||
@fa-var-connectdevelop: "\f20e";
|
||||
@fa-var-contao: "\f26d";
|
||||
@fa-var-copy: "\f0c5";
|
||||
@fa-var-copyright: "\f1f9";
|
||||
@fa-var-creative-commons: "\f25e";
|
||||
@fa-var-credit-card: "\f09d";
|
||||
@fa-var-credit-card-alt: "\f283";
|
||||
@fa-var-crop: "\f125";
|
||||
@fa-var-crosshairs: "\f05b";
|
||||
@fa-var-css3: "\f13c";
|
||||
@ -165,6 +210,8 @@
|
||||
@fa-var-dashboard: "\f0e4";
|
||||
@fa-var-dashcube: "\f210";
|
||||
@fa-var-database: "\f1c0";
|
||||
@fa-var-deaf: "\f2a4";
|
||||
@fa-var-deafness: "\f2a4";
|
||||
@fa-var-dedent: "\f03b";
|
||||
@fa-var-delicious: "\f1a5";
|
||||
@fa-var-desktop: "\f108";
|
||||
@ -175,17 +222,25 @@
|
||||
@fa-var-dot-circle-o: "\f192";
|
||||
@fa-var-download: "\f019";
|
||||
@fa-var-dribbble: "\f17d";
|
||||
@fa-var-drivers-license: "\f2c2";
|
||||
@fa-var-drivers-license-o: "\f2c3";
|
||||
@fa-var-dropbox: "\f16b";
|
||||
@fa-var-drupal: "\f1a9";
|
||||
@fa-var-edge: "\f282";
|
||||
@fa-var-edit: "\f044";
|
||||
@fa-var-eercast: "\f2da";
|
||||
@fa-var-eject: "\f052";
|
||||
@fa-var-ellipsis-h: "\f141";
|
||||
@fa-var-ellipsis-v: "\f142";
|
||||
@fa-var-empire: "\f1d1";
|
||||
@fa-var-envelope: "\f0e0";
|
||||
@fa-var-envelope-o: "\f003";
|
||||
@fa-var-envelope-open: "\f2b6";
|
||||
@fa-var-envelope-open-o: "\f2b7";
|
||||
@fa-var-envelope-square: "\f199";
|
||||
@fa-var-envira: "\f299";
|
||||
@fa-var-eraser: "\f12d";
|
||||
@fa-var-etsy: "\f2d7";
|
||||
@fa-var-eur: "\f153";
|
||||
@fa-var-euro: "\f153";
|
||||
@fa-var-exchange: "\f0ec";
|
||||
@ -193,11 +248,13 @@
|
||||
@fa-var-exclamation-circle: "\f06a";
|
||||
@fa-var-exclamation-triangle: "\f071";
|
||||
@fa-var-expand: "\f065";
|
||||
@fa-var-expeditedssl: "\f23e";
|
||||
@fa-var-external-link: "\f08e";
|
||||
@fa-var-external-link-square: "\f14c";
|
||||
@fa-var-eye: "\f06e";
|
||||
@fa-var-eye-slash: "\f070";
|
||||
@fa-var-eyedropper: "\f1fb";
|
||||
@fa-var-fa: "\f2b4";
|
||||
@fa-var-facebook: "\f09a";
|
||||
@fa-var-facebook-f: "\f09a";
|
||||
@fa-var-facebook-official: "\f230";
|
||||
@ -205,6 +262,7 @@
|
||||
@fa-var-fast-backward: "\f049";
|
||||
@fa-var-fast-forward: "\f050";
|
||||
@fa-var-fax: "\f1ac";
|
||||
@fa-var-feed: "\f09e";
|
||||
@fa-var-female: "\f182";
|
||||
@fa-var-fighter-jet: "\f0fb";
|
||||
@fa-var-file: "\f15b";
|
||||
@ -230,6 +288,8 @@
|
||||
@fa-var-filter: "\f0b0";
|
||||
@fa-var-fire: "\f06d";
|
||||
@fa-var-fire-extinguisher: "\f134";
|
||||
@fa-var-firefox: "\f269";
|
||||
@fa-var-first-order: "\f2b0";
|
||||
@fa-var-flag: "\f024";
|
||||
@fa-var-flag-checkered: "\f11e";
|
||||
@fa-var-flag-o: "\f11d";
|
||||
@ -242,9 +302,13 @@
|
||||
@fa-var-folder-open: "\f07c";
|
||||
@fa-var-folder-open-o: "\f115";
|
||||
@fa-var-font: "\f031";
|
||||
@fa-var-font-awesome: "\f2b4";
|
||||
@fa-var-fonticons: "\f280";
|
||||
@fa-var-fort-awesome: "\f286";
|
||||
@fa-var-forumbee: "\f211";
|
||||
@fa-var-forward: "\f04e";
|
||||
@fa-var-foursquare: "\f180";
|
||||
@fa-var-free-code-camp: "\f2c5";
|
||||
@fa-var-frown-o: "\f119";
|
||||
@fa-var-futbol-o: "\f1e3";
|
||||
@fa-var-gamepad: "\f11b";
|
||||
@ -253,29 +317,50 @@
|
||||
@fa-var-ge: "\f1d1";
|
||||
@fa-var-gear: "\f013";
|
||||
@fa-var-gears: "\f085";
|
||||
@fa-var-genderless: "\f1db";
|
||||
@fa-var-genderless: "\f22d";
|
||||
@fa-var-get-pocket: "\f265";
|
||||
@fa-var-gg: "\f260";
|
||||
@fa-var-gg-circle: "\f261";
|
||||
@fa-var-gift: "\f06b";
|
||||
@fa-var-git: "\f1d3";
|
||||
@fa-var-git-square: "\f1d2";
|
||||
@fa-var-github: "\f09b";
|
||||
@fa-var-github-alt: "\f113";
|
||||
@fa-var-github-square: "\f092";
|
||||
@fa-var-gitlab: "\f296";
|
||||
@fa-var-gittip: "\f184";
|
||||
@fa-var-glass: "\f000";
|
||||
@fa-var-glide: "\f2a5";
|
||||
@fa-var-glide-g: "\f2a6";
|
||||
@fa-var-globe: "\f0ac";
|
||||
@fa-var-google: "\f1a0";
|
||||
@fa-var-google-plus: "\f0d5";
|
||||
@fa-var-google-plus-circle: "\f2b3";
|
||||
@fa-var-google-plus-official: "\f2b3";
|
||||
@fa-var-google-plus-square: "\f0d4";
|
||||
@fa-var-google-wallet: "\f1ee";
|
||||
@fa-var-graduation-cap: "\f19d";
|
||||
@fa-var-gratipay: "\f184";
|
||||
@fa-var-grav: "\f2d6";
|
||||
@fa-var-group: "\f0c0";
|
||||
@fa-var-h-square: "\f0fd";
|
||||
@fa-var-hacker-news: "\f1d4";
|
||||
@fa-var-hand-grab-o: "\f255";
|
||||
@fa-var-hand-lizard-o: "\f258";
|
||||
@fa-var-hand-o-down: "\f0a7";
|
||||
@fa-var-hand-o-left: "\f0a5";
|
||||
@fa-var-hand-o-right: "\f0a4";
|
||||
@fa-var-hand-o-up: "\f0a6";
|
||||
@fa-var-hand-paper-o: "\f256";
|
||||
@fa-var-hand-peace-o: "\f25b";
|
||||
@fa-var-hand-pointer-o: "\f25a";
|
||||
@fa-var-hand-rock-o: "\f255";
|
||||
@fa-var-hand-scissors-o: "\f257";
|
||||
@fa-var-hand-spock-o: "\f259";
|
||||
@fa-var-hand-stop-o: "\f256";
|
||||
@fa-var-handshake-o: "\f2b5";
|
||||
@fa-var-hard-of-hearing: "\f2a4";
|
||||
@fa-var-hashtag: "\f292";
|
||||
@fa-var-hdd-o: "\f0a0";
|
||||
@fa-var-header: "\f1dc";
|
||||
@fa-var-headphones: "\f025";
|
||||
@ -286,16 +371,33 @@
|
||||
@fa-var-home: "\f015";
|
||||
@fa-var-hospital-o: "\f0f8";
|
||||
@fa-var-hotel: "\f236";
|
||||
@fa-var-hourglass: "\f254";
|
||||
@fa-var-hourglass-1: "\f251";
|
||||
@fa-var-hourglass-2: "\f252";
|
||||
@fa-var-hourglass-3: "\f253";
|
||||
@fa-var-hourglass-end: "\f253";
|
||||
@fa-var-hourglass-half: "\f252";
|
||||
@fa-var-hourglass-o: "\f250";
|
||||
@fa-var-hourglass-start: "\f251";
|
||||
@fa-var-houzz: "\f27c";
|
||||
@fa-var-html5: "\f13b";
|
||||
@fa-var-i-cursor: "\f246";
|
||||
@fa-var-id-badge: "\f2c1";
|
||||
@fa-var-id-card: "\f2c2";
|
||||
@fa-var-id-card-o: "\f2c3";
|
||||
@fa-var-ils: "\f20b";
|
||||
@fa-var-image: "\f03e";
|
||||
@fa-var-imdb: "\f2d8";
|
||||
@fa-var-inbox: "\f01c";
|
||||
@fa-var-indent: "\f03c";
|
||||
@fa-var-industry: "\f275";
|
||||
@fa-var-info: "\f129";
|
||||
@fa-var-info-circle: "\f05a";
|
||||
@fa-var-inr: "\f156";
|
||||
@fa-var-instagram: "\f16d";
|
||||
@fa-var-institution: "\f19c";
|
||||
@fa-var-internet-explorer: "\f26b";
|
||||
@fa-var-intersex: "\f224";
|
||||
@fa-var-ioxhost: "\f208";
|
||||
@fa-var-italic: "\f033";
|
||||
@fa-var-joomla: "\f1aa";
|
||||
@ -323,6 +425,7 @@
|
||||
@fa-var-link: "\f0c1";
|
||||
@fa-var-linkedin: "\f0e1";
|
||||
@fa-var-linkedin-square: "\f08c";
|
||||
@fa-var-linode: "\f2b8";
|
||||
@fa-var-linux: "\f17c";
|
||||
@fa-var-list: "\f03a";
|
||||
@fa-var-list-alt: "\f022";
|
||||
@ -334,13 +437,18 @@
|
||||
@fa-var-long-arrow-left: "\f177";
|
||||
@fa-var-long-arrow-right: "\f178";
|
||||
@fa-var-long-arrow-up: "\f176";
|
||||
@fa-var-low-vision: "\f2a8";
|
||||
@fa-var-magic: "\f0d0";
|
||||
@fa-var-magnet: "\f076";
|
||||
@fa-var-mail-forward: "\f064";
|
||||
@fa-var-mail-reply: "\f112";
|
||||
@fa-var-mail-reply-all: "\f122";
|
||||
@fa-var-male: "\f183";
|
||||
@fa-var-map: "\f279";
|
||||
@fa-var-map-marker: "\f041";
|
||||
@fa-var-map-o: "\f278";
|
||||
@fa-var-map-pin: "\f276";
|
||||
@fa-var-map-signs: "\f277";
|
||||
@fa-var-mars: "\f222";
|
||||
@fa-var-mars-double: "\f227";
|
||||
@fa-var-mars-stroke: "\f229";
|
||||
@ -350,25 +458,37 @@
|
||||
@fa-var-meanpath: "\f20c";
|
||||
@fa-var-medium: "\f23a";
|
||||
@fa-var-medkit: "\f0fa";
|
||||
@fa-var-meetup: "\f2e0";
|
||||
@fa-var-meh-o: "\f11a";
|
||||
@fa-var-mercury: "\f223";
|
||||
@fa-var-microchip: "\f2db";
|
||||
@fa-var-microphone: "\f130";
|
||||
@fa-var-microphone-slash: "\f131";
|
||||
@fa-var-minus: "\f068";
|
||||
@fa-var-minus-circle: "\f056";
|
||||
@fa-var-minus-square: "\f146";
|
||||
@fa-var-minus-square-o: "\f147";
|
||||
@fa-var-mixcloud: "\f289";
|
||||
@fa-var-mobile: "\f10b";
|
||||
@fa-var-mobile-phone: "\f10b";
|
||||
@fa-var-modx: "\f285";
|
||||
@fa-var-money: "\f0d6";
|
||||
@fa-var-moon-o: "\f186";
|
||||
@fa-var-mortar-board: "\f19d";
|
||||
@fa-var-motorcycle: "\f21c";
|
||||
@fa-var-mouse-pointer: "\f245";
|
||||
@fa-var-music: "\f001";
|
||||
@fa-var-navicon: "\f0c9";
|
||||
@fa-var-neuter: "\f22c";
|
||||
@fa-var-newspaper-o: "\f1ea";
|
||||
@fa-var-object-group: "\f247";
|
||||
@fa-var-object-ungroup: "\f248";
|
||||
@fa-var-odnoklassniki: "\f263";
|
||||
@fa-var-odnoklassniki-square: "\f264";
|
||||
@fa-var-opencart: "\f23d";
|
||||
@fa-var-openid: "\f19b";
|
||||
@fa-var-opera: "\f26a";
|
||||
@fa-var-optin-monster: "\f23c";
|
||||
@fa-var-outdent: "\f03b";
|
||||
@fa-var-pagelines: "\f18c";
|
||||
@fa-var-paint-brush: "\f1fc";
|
||||
@ -378,18 +498,22 @@
|
||||
@fa-var-paragraph: "\f1dd";
|
||||
@fa-var-paste: "\f0ea";
|
||||
@fa-var-pause: "\f04c";
|
||||
@fa-var-pause-circle: "\f28b";
|
||||
@fa-var-pause-circle-o: "\f28c";
|
||||
@fa-var-paw: "\f1b0";
|
||||
@fa-var-paypal: "\f1ed";
|
||||
@fa-var-pencil: "\f040";
|
||||
@fa-var-pencil-square: "\f14b";
|
||||
@fa-var-pencil-square-o: "\f044";
|
||||
@fa-var-percent: "\f295";
|
||||
@fa-var-phone: "\f095";
|
||||
@fa-var-phone-square: "\f098";
|
||||
@fa-var-photo: "\f03e";
|
||||
@fa-var-picture-o: "\f03e";
|
||||
@fa-var-pie-chart: "\f200";
|
||||
@fa-var-pied-piper: "\f1a7";
|
||||
@fa-var-pied-piper: "\f2ae";
|
||||
@fa-var-pied-piper-alt: "\f1a8";
|
||||
@fa-var-pied-piper-pp: "\f1a7";
|
||||
@fa-var-pinterest: "\f0d2";
|
||||
@fa-var-pinterest-p: "\f231";
|
||||
@fa-var-pinterest-square: "\f0d3";
|
||||
@ -402,28 +526,36 @@
|
||||
@fa-var-plus-circle: "\f055";
|
||||
@fa-var-plus-square: "\f0fe";
|
||||
@fa-var-plus-square-o: "\f196";
|
||||
@fa-var-podcast: "\f2ce";
|
||||
@fa-var-power-off: "\f011";
|
||||
@fa-var-print: "\f02f";
|
||||
@fa-var-product-hunt: "\f288";
|
||||
@fa-var-puzzle-piece: "\f12e";
|
||||
@fa-var-qq: "\f1d6";
|
||||
@fa-var-qrcode: "\f029";
|
||||
@fa-var-question: "\f128";
|
||||
@fa-var-question-circle: "\f059";
|
||||
@fa-var-question-circle-o: "\f29c";
|
||||
@fa-var-quora: "\f2c4";
|
||||
@fa-var-quote-left: "\f10d";
|
||||
@fa-var-quote-right: "\f10e";
|
||||
@fa-var-ra: "\f1d0";
|
||||
@fa-var-random: "\f074";
|
||||
@fa-var-ravelry: "\f2d9";
|
||||
@fa-var-rebel: "\f1d0";
|
||||
@fa-var-recycle: "\f1b8";
|
||||
@fa-var-reddit: "\f1a1";
|
||||
@fa-var-reddit-alien: "\f281";
|
||||
@fa-var-reddit-square: "\f1a2";
|
||||
@fa-var-refresh: "\f021";
|
||||
@fa-var-registered: "\f25d";
|
||||
@fa-var-remove: "\f00d";
|
||||
@fa-var-renren: "\f18b";
|
||||
@fa-var-reorder: "\f0c9";
|
||||
@fa-var-repeat: "\f01e";
|
||||
@fa-var-reply: "\f112";
|
||||
@fa-var-reply-all: "\f122";
|
||||
@fa-var-resistance: "\f1d0";
|
||||
@fa-var-retweet: "\f079";
|
||||
@fa-var-rmb: "\f157";
|
||||
@fa-var-road: "\f018";
|
||||
@ -436,8 +568,11 @@
|
||||
@fa-var-rub: "\f158";
|
||||
@fa-var-ruble: "\f158";
|
||||
@fa-var-rupee: "\f156";
|
||||
@fa-var-s15: "\f2cd";
|
||||
@fa-var-safari: "\f267";
|
||||
@fa-var-save: "\f0c7";
|
||||
@fa-var-scissors: "\f0c4";
|
||||
@fa-var-scribd: "\f28a";
|
||||
@fa-var-search: "\f002";
|
||||
@fa-var-search-minus: "\f010";
|
||||
@fa-var-search-plus: "\f00e";
|
||||
@ -455,10 +590,15 @@
|
||||
@fa-var-shield: "\f132";
|
||||
@fa-var-ship: "\f21a";
|
||||
@fa-var-shirtsinbulk: "\f214";
|
||||
@fa-var-shopping-bag: "\f290";
|
||||
@fa-var-shopping-basket: "\f291";
|
||||
@fa-var-shopping-cart: "\f07a";
|
||||
@fa-var-shower: "\f2cc";
|
||||
@fa-var-sign-in: "\f090";
|
||||
@fa-var-sign-language: "\f2a7";
|
||||
@fa-var-sign-out: "\f08b";
|
||||
@fa-var-signal: "\f012";
|
||||
@fa-var-signing: "\f2a7";
|
||||
@fa-var-simplybuilt: "\f215";
|
||||
@fa-var-sitemap: "\f0e8";
|
||||
@fa-var-skyatlas: "\f216";
|
||||
@ -467,6 +607,10 @@
|
||||
@fa-var-sliders: "\f1de";
|
||||
@fa-var-slideshare: "\f1e7";
|
||||
@fa-var-smile-o: "\f118";
|
||||
@fa-var-snapchat: "\f2ab";
|
||||
@fa-var-snapchat-ghost: "\f2ac";
|
||||
@fa-var-snapchat-square: "\f2ad";
|
||||
@fa-var-snowflake-o: "\f2dc";
|
||||
@fa-var-soccer-ball-o: "\f1e3";
|
||||
@fa-var-sort: "\f0dc";
|
||||
@fa-var-sort-alpha-asc: "\f15d";
|
||||
@ -499,7 +643,11 @@
|
||||
@fa-var-step-backward: "\f048";
|
||||
@fa-var-step-forward: "\f051";
|
||||
@fa-var-stethoscope: "\f0f1";
|
||||
@fa-var-sticky-note: "\f249";
|
||||
@fa-var-sticky-note-o: "\f24a";
|
||||
@fa-var-stop: "\f04d";
|
||||
@fa-var-stop-circle: "\f28d";
|
||||
@fa-var-stop-circle-o: "\f28e";
|
||||
@fa-var-street-view: "\f21d";
|
||||
@fa-var-strikethrough: "\f0cc";
|
||||
@fa-var-stumbleupon: "\f1a4";
|
||||
@ -508,6 +656,7 @@
|
||||
@fa-var-subway: "\f239";
|
||||
@fa-var-suitcase: "\f0f2";
|
||||
@fa-var-sun-o: "\f185";
|
||||
@fa-var-superpowers: "\f2dd";
|
||||
@fa-var-superscript: "\f12b";
|
||||
@fa-var-support: "\f1cd";
|
||||
@fa-var-table: "\f0ce";
|
||||
@ -517,6 +666,8 @@
|
||||
@fa-var-tags: "\f02c";
|
||||
@fa-var-tasks: "\f0ae";
|
||||
@fa-var-taxi: "\f1ba";
|
||||
@fa-var-telegram: "\f2c6";
|
||||
@fa-var-television: "\f26c";
|
||||
@fa-var-tencent-weibo: "\f1d5";
|
||||
@fa-var-terminal: "\f120";
|
||||
@fa-var-text-height: "\f034";
|
||||
@ -524,6 +675,18 @@
|
||||
@fa-var-th: "\f00a";
|
||||
@fa-var-th-large: "\f009";
|
||||
@fa-var-th-list: "\f00b";
|
||||
@fa-var-themeisle: "\f2b2";
|
||||
@fa-var-thermometer: "\f2c7";
|
||||
@fa-var-thermometer-0: "\f2cb";
|
||||
@fa-var-thermometer-1: "\f2ca";
|
||||
@fa-var-thermometer-2: "\f2c9";
|
||||
@fa-var-thermometer-3: "\f2c8";
|
||||
@fa-var-thermometer-4: "\f2c7";
|
||||
@fa-var-thermometer-empty: "\f2cb";
|
||||
@fa-var-thermometer-full: "\f2c7";
|
||||
@fa-var-thermometer-half: "\f2c9";
|
||||
@fa-var-thermometer-quarter: "\f2ca";
|
||||
@fa-var-thermometer-three-quarters: "\f2c8";
|
||||
@fa-var-thumb-tack: "\f08d";
|
||||
@fa-var-thumbs-down: "\f165";
|
||||
@fa-var-thumbs-o-down: "\f088";
|
||||
@ -533,6 +696,8 @@
|
||||
@fa-var-times: "\f00d";
|
||||
@fa-var-times-circle: "\f057";
|
||||
@fa-var-times-circle-o: "\f05c";
|
||||
@fa-var-times-rectangle: "\f2d3";
|
||||
@fa-var-times-rectangle-o: "\f2d4";
|
||||
@fa-var-tint: "\f043";
|
||||
@fa-var-toggle-down: "\f150";
|
||||
@fa-var-toggle-left: "\f191";
|
||||
@ -540,6 +705,7 @@
|
||||
@fa-var-toggle-on: "\f205";
|
||||
@fa-var-toggle-right: "\f152";
|
||||
@fa-var-toggle-up: "\f151";
|
||||
@fa-var-trademark: "\f25c";
|
||||
@fa-var-train: "\f238";
|
||||
@fa-var-transgender: "\f224";
|
||||
@fa-var-transgender-alt: "\f225";
|
||||
@ -547,6 +713,7 @@
|
||||
@fa-var-trash-o: "\f014";
|
||||
@fa-var-tree: "\f1bb";
|
||||
@fa-var-trello: "\f181";
|
||||
@fa-var-tripadvisor: "\f262";
|
||||
@fa-var-trophy: "\f091";
|
||||
@fa-var-truck: "\f0d1";
|
||||
@fa-var-try: "\f195";
|
||||
@ -554,33 +721,45 @@
|
||||
@fa-var-tumblr: "\f173";
|
||||
@fa-var-tumblr-square: "\f174";
|
||||
@fa-var-turkish-lira: "\f195";
|
||||
@fa-var-tv: "\f26c";
|
||||
@fa-var-twitch: "\f1e8";
|
||||
@fa-var-twitter: "\f099";
|
||||
@fa-var-twitter-square: "\f081";
|
||||
@fa-var-umbrella: "\f0e9";
|
||||
@fa-var-underline: "\f0cd";
|
||||
@fa-var-undo: "\f0e2";
|
||||
@fa-var-universal-access: "\f29a";
|
||||
@fa-var-university: "\f19c";
|
||||
@fa-var-unlink: "\f127";
|
||||
@fa-var-unlock: "\f09c";
|
||||
@fa-var-unlock-alt: "\f13e";
|
||||
@fa-var-unsorted: "\f0dc";
|
||||
@fa-var-upload: "\f093";
|
||||
@fa-var-usb: "\f287";
|
||||
@fa-var-usd: "\f155";
|
||||
@fa-var-user: "\f007";
|
||||
@fa-var-user-circle: "\f2bd";
|
||||
@fa-var-user-circle-o: "\f2be";
|
||||
@fa-var-user-md: "\f0f0";
|
||||
@fa-var-user-o: "\f2c0";
|
||||
@fa-var-user-plus: "\f234";
|
||||
@fa-var-user-secret: "\f21b";
|
||||
@fa-var-user-times: "\f235";
|
||||
@fa-var-users: "\f0c0";
|
||||
@fa-var-vcard: "\f2bb";
|
||||
@fa-var-vcard-o: "\f2bc";
|
||||
@fa-var-venus: "\f221";
|
||||
@fa-var-venus-double: "\f226";
|
||||
@fa-var-venus-mars: "\f228";
|
||||
@fa-var-viacoin: "\f237";
|
||||
@fa-var-viadeo: "\f2a9";
|
||||
@fa-var-viadeo-square: "\f2aa";
|
||||
@fa-var-video-camera: "\f03d";
|
||||
@fa-var-vimeo: "\f27d";
|
||||
@fa-var-vimeo-square: "\f194";
|
||||
@fa-var-vine: "\f1ca";
|
||||
@fa-var-vk: "\f189";
|
||||
@fa-var-volume-control-phone: "\f2a0";
|
||||
@fa-var-volume-down: "\f027";
|
||||
@fa-var-volume-off: "\f026";
|
||||
@fa-var-volume-up: "\f028";
|
||||
@ -590,16 +769,31 @@
|
||||
@fa-var-weixin: "\f1d7";
|
||||
@fa-var-whatsapp: "\f232";
|
||||
@fa-var-wheelchair: "\f193";
|
||||
@fa-var-wheelchair-alt: "\f29b";
|
||||
@fa-var-wifi: "\f1eb";
|
||||
@fa-var-wikipedia-w: "\f266";
|
||||
@fa-var-window-close: "\f2d3";
|
||||
@fa-var-window-close-o: "\f2d4";
|
||||
@fa-var-window-maximize: "\f2d0";
|
||||
@fa-var-window-minimize: "\f2d1";
|
||||
@fa-var-window-restore: "\f2d2";
|
||||
@fa-var-windows: "\f17a";
|
||||
@fa-var-won: "\f159";
|
||||
@fa-var-wordpress: "\f19a";
|
||||
@fa-var-wpbeginner: "\f297";
|
||||
@fa-var-wpexplorer: "\f2de";
|
||||
@fa-var-wpforms: "\f298";
|
||||
@fa-var-wrench: "\f0ad";
|
||||
@fa-var-xing: "\f168";
|
||||
@fa-var-xing-square: "\f169";
|
||||
@fa-var-y-combinator: "\f23b";
|
||||
@fa-var-y-combinator-square: "\f1d4";
|
||||
@fa-var-yahoo: "\f19e";
|
||||
@fa-var-yc: "\f23b";
|
||||
@fa-var-yc-square: "\f1d4";
|
||||
@fa-var-yelp: "\f1e9";
|
||||
@fa-var-yen: "\f157";
|
||||
@fa-var-yoast: "\f2b1";
|
||||
@fa-var-youtube: "\f167";
|
||||
@fa-var-youtube-play: "\f16a";
|
||||
@fa-var-youtube-square: "\f166";
|
||||
|
@ -268,6 +268,11 @@
|
||||
.fa-icon-color(@brand-warning);
|
||||
}
|
||||
|
||||
.icon-radarr-download-warning {
|
||||
.fa-icon-content(@fa-var-download);
|
||||
.fa-icon-color(@brand-warning);
|
||||
}
|
||||
|
||||
.icon-sonarr-shutdown {
|
||||
.fa-icon-content(@fa-var-power-off);
|
||||
.fa-icon-color(@brand-danger);
|
||||
|
@ -64,6 +64,11 @@ Handlebars.registerHelper('alternativeTitlesString', function() {
|
||||
if (titles.length === 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
titles = _.map(titles, function(item){
|
||||
return item.title;
|
||||
});
|
||||
|
||||
if (titles.length === 1) {
|
||||
return titles[0];
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ var EpisodeFileEditorLayout = require('../../EpisodeFile/Editor/EpisodeFileEdito
|
||||
var HistoryLayout = require('../History/MovieHistoryLayout');
|
||||
var SearchLayout = require('../Search/MovieSearchLayout');
|
||||
var FilesLayout = require("../Files/FilesLayout");
|
||||
var TitlesLayout = require("../Titles/TitlesLayout");
|
||||
require('backstrech');
|
||||
require('../../Mixins/backbone.signalr.mixin');
|
||||
|
||||
@ -24,7 +25,8 @@ module.exports = Marionette.Layout.extend({
|
||||
info : '#info',
|
||||
search : '#movie-search',
|
||||
history : '#movie-history',
|
||||
files : "#movie-files"
|
||||
files : "#movie-files",
|
||||
titles: "#movie-titles",
|
||||
},
|
||||
|
||||
|
||||
@ -39,7 +41,8 @@ module.exports = Marionette.Layout.extend({
|
||||
manualSearch : '.x-manual-search',
|
||||
history : '.x-movie-history',
|
||||
search : '.x-movie-search',
|
||||
files : ".x-movie-files"
|
||||
files : ".x-movie-files",
|
||||
titles: ".x-movie-titles",
|
||||
},
|
||||
|
||||
events : {
|
||||
@ -53,6 +56,7 @@ module.exports = Marionette.Layout.extend({
|
||||
'click .x-movie-history' : '_showHistory',
|
||||
'click .x-movie-search' : '_showSearch',
|
||||
"click .x-movie-files" : "_showFiles",
|
||||
"click .x-movie-titles" : "_showTitles",
|
||||
},
|
||||
|
||||
initialize : function() {
|
||||
@ -83,6 +87,7 @@ module.exports = Marionette.Layout.extend({
|
||||
this.searchLayout.startManualSearch = true;
|
||||
|
||||
this.filesLayout = new FilesLayout({ model : this.model });
|
||||
this.titlesLayout = new TitlesLayout({ model : this.model });
|
||||
|
||||
this._showBackdrop();
|
||||
this._showSeasons();
|
||||
@ -170,6 +175,15 @@ module.exports = Marionette.Layout.extend({
|
||||
this.files.show(this.filesLayout);
|
||||
},
|
||||
|
||||
_showTitles : function(e) {
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
this.ui.titles.tab("show");
|
||||
this.titles.show(this.titlesLayout);
|
||||
},
|
||||
|
||||
_toggleMonitored : function() {
|
||||
var savePromise = this.model.save('monitored', !this.model.get('monitored'), { wait : true });
|
||||
|
||||
|
@ -6,7 +6,8 @@
|
||||
<div>
|
||||
<h1 class="header-text">
|
||||
<i class="x-monitored" title="Toggle monitored state for movie"/>
|
||||
{{title}}
|
||||
{{title}} <span class="year">({{year}}{{#if secondaryYear}} / <a href="https://mappings.radarr.video/mapping/{{secondaryYearSourceId}}" target="_blank"><span title="Secondary year pulled from Radarr Mappings.
|
||||
Click to head on over there and tell us whether this is correct or not.">{{secondaryYear}}</span></a>{{/if}})</span>
|
||||
<div class="movie-actions pull-right">
|
||||
<div class="x-episode-file-editor">
|
||||
<i class="icon-sonarr-episode-file" title="Modify movie files"/>
|
||||
@ -43,11 +44,13 @@
|
||||
<li><a href="#movie-history" class="x-movie-history">History</a></li>
|
||||
<li><a href="#movie-search" class="x-movie-search">Search</a></li>
|
||||
<li><a href="#movie-files" class="x-movie-files">Files</a></li>
|
||||
<li><a href="#movie-titles" class="x-movie-titles">Titles</a></li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane" id="movie-history"/>
|
||||
<div class="tab-pane" id="movie-search"/>
|
||||
<div class="tab-pane" id="movie-files"/>
|
||||
<div class="tab-pane" id="movie-titles"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -23,7 +23,7 @@ var view = Marionette.ItemView.extend({
|
||||
initialize : function() {
|
||||
this.model.set('profiles', Profiles);
|
||||
var pathState = this.model.get("pathState");
|
||||
if (pathState == "static") {
|
||||
if (pathState === "static") {
|
||||
this.model.set("pathState", true);
|
||||
} else {
|
||||
this.model.set("pathState", false);
|
||||
|
22
src/UI/Movies/Titles/LanguageCell.js
Normal file
22
src/UI/Movies/Titles/LanguageCell.js
Normal file
@ -0,0 +1,22 @@
|
||||
var NzbDroneCell = require('../../Cells/NzbDroneCell');
|
||||
|
||||
module.exports = NzbDroneCell.extend({
|
||||
className : 'language-cell',
|
||||
|
||||
render : function() {
|
||||
this.$el.empty();
|
||||
|
||||
var language = this.model.get("language");
|
||||
|
||||
this.$el.html(this.toTitleCase(language));
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
toTitleCase : function(str)
|
||||
{
|
||||
return str.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
|
||||
}
|
||||
|
||||
|
||||
});
|
5
src/UI/Movies/Titles/NoTitlesView.js
Normal file
5
src/UI/Movies/Titles/NoTitlesView.js
Normal file
@ -0,0 +1,5 @@
|
||||
var Marionette = require('marionette');
|
||||
|
||||
module.exports = Marionette.ItemView.extend({
|
||||
template : 'Movies/Titles/NoTitlesViewTemplate'
|
||||
});
|
3
src/UI/Movies/Titles/NoTitlesViewTemplate.hbs
Normal file
3
src/UI/Movies/Titles/NoTitlesViewTemplate.hbs
Normal file
@ -0,0 +1,3 @@
|
||||
<p class="text-warning">
|
||||
No alternative titles for this movie.
|
||||
</p>
|
42
src/UI/Movies/Titles/SourceCell.js
Normal file
42
src/UI/Movies/Titles/SourceCell.js
Normal file
@ -0,0 +1,42 @@
|
||||
var NzbDroneCell = require('../../Cells/NzbDroneCell');
|
||||
|
||||
module.exports = NzbDroneCell.extend({
|
||||
className : 'title-source-cell',
|
||||
|
||||
render : function() {
|
||||
this.$el.empty();
|
||||
|
||||
var link;
|
||||
var sourceTitle = this.model.get("sourceType");
|
||||
var sourceId = this.model.get("sourceId");
|
||||
|
||||
switch (sourceTitle) {
|
||||
case "tmdb":
|
||||
sourceTitle = "TMDB";
|
||||
link = "https://themoviedb.org/movie/" + sourceId;
|
||||
break;
|
||||
case "mappings":
|
||||
sourceTitle = "Radarr Mappings";
|
||||
link = "https://mappings.radarr.video/mapping/" + sourceId;
|
||||
break;
|
||||
case "user":
|
||||
sourceTitle = "Force Download";
|
||||
break;
|
||||
case "indexer":
|
||||
sourceTitle = "Indexer";
|
||||
break;
|
||||
}
|
||||
|
||||
var a = "{0}";
|
||||
|
||||
if (link) {
|
||||
a = "<a href='"+link+"' target='_blank'>{0}</a>";
|
||||
}
|
||||
|
||||
this.$el.html(a.format(sourceTitle));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
});
|
6
src/UI/Movies/Titles/TitleCell.js
Normal file
6
src/UI/Movies/Titles/TitleCell.js
Normal file
@ -0,0 +1,6 @@
|
||||
var TemplatedCell = require('../../Cells/TemplatedCell');
|
||||
|
||||
module.exports = TemplatedCell.extend({
|
||||
className : 'series-title-cell',
|
||||
template : 'Movies/Titles/TitleTemplate'
|
||||
});
|
3
src/UI/Movies/Titles/TitleModel.js
Normal file
3
src/UI/Movies/Titles/TitleModel.js
Normal file
@ -0,0 +1,3 @@
|
||||
var Backbone = require('backbone');
|
||||
|
||||
module.exports = Backbone.Model.extend({});
|
1
src/UI/Movies/Titles/TitleTemplate.hbs
Normal file
1
src/UI/Movies/Titles/TitleTemplate.hbs
Normal file
@ -0,0 +1 @@
|
||||
{{this}}
|
30
src/UI/Movies/Titles/TitlesCollection.js
Normal file
30
src/UI/Movies/Titles/TitlesCollection.js
Normal file
@ -0,0 +1,30 @@
|
||||
var PagableCollection = require('backbone.pageable');
|
||||
var TitleModel = require('./TitleModel');
|
||||
var AsSortedCollection = require('../../Mixins/AsSortedCollection');
|
||||
|
||||
var Collection = PagableCollection.extend({
|
||||
url : window.NzbDrone.ApiRoot + "/aka",
|
||||
model : TitleModel,
|
||||
|
||||
state : {
|
||||
pageSize : 2000,
|
||||
sortKey : 'title',
|
||||
order : -1
|
||||
},
|
||||
|
||||
mode : 'client',
|
||||
|
||||
sortMappings : {
|
||||
"source" : {
|
||||
sortKey : "sourceType"
|
||||
},
|
||||
"language" : {
|
||||
sortKey : "language"
|
||||
}
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
Collection = AsSortedCollection.call(Collection);
|
||||
|
||||
module.exports = Collection;
|
117
src/UI/Movies/Titles/TitlesLayout.js
Normal file
117
src/UI/Movies/Titles/TitlesLayout.js
Normal file
@ -0,0 +1,117 @@
|
||||
var vent = require('vent');
|
||||
var Marionette = require('marionette');
|
||||
var Backgrid = require('backgrid');
|
||||
//var ButtonsView = require('./ButtonsView');
|
||||
//var ManualSearchLayout = require('./ManualLayout');
|
||||
var TitlesCollection = require('./TitlesCollection');
|
||||
var CommandController = require('../../Commands/CommandController');
|
||||
var LoadingView = require('../../Shared/LoadingView');
|
||||
var NoResultsView = require('./NoTitlesView');
|
||||
var TitleModel = require("./TitleModel");
|
||||
var TitleCell = require("./TitleCell");
|
||||
var SourceCell = require("./SourceCell");
|
||||
var LanguageCell = require("./LanguageCell");
|
||||
|
||||
module.exports = Marionette.Layout.extend({
|
||||
template : 'Movies/Titles/TitlesLayoutTemplate',
|
||||
|
||||
regions : {
|
||||
main : '#movie-titles-region',
|
||||
grid : "#movie-titles-grid"
|
||||
},
|
||||
|
||||
events : {
|
||||
'click .x-search-auto' : '_searchAuto',
|
||||
'click .x-search-manual' : '_searchManual',
|
||||
'click .x-search-back' : '_showButtons'
|
||||
},
|
||||
|
||||
columns : [
|
||||
{
|
||||
name : 'title',
|
||||
label : 'Title',
|
||||
cell : Backgrid.StringCell
|
||||
},
|
||||
{
|
||||
name : "this",
|
||||
label : "Source",
|
||||
cell : SourceCell,
|
||||
sortKey : "sourceType",
|
||||
},
|
||||
{
|
||||
name : "this",
|
||||
label : "Language",
|
||||
cell : LanguageCell
|
||||
}
|
||||
],
|
||||
|
||||
|
||||
initialize : function(movie) {
|
||||
this.titlesCollection = new TitlesCollection();
|
||||
var titles = movie.model.get("alternativeTitles");
|
||||
this.movie = movie;
|
||||
this.titlesCollection.add(titles);
|
||||
//this.listenTo(this.releaseCollection, 'sync', this._showSearchResults);
|
||||
this.listenTo(this.model, 'change', function(model, options) {
|
||||
if (options && options.changeSource === 'signalr') {
|
||||
this._refresh(model);
|
||||
}
|
||||
});
|
||||
|
||||
//vent.on(vent.Commands.MovieFileEdited, this._showGrid, this);
|
||||
},
|
||||
|
||||
_refresh : function(model) {
|
||||
this.titlesCollection = new TitlesCollection();
|
||||
var file = model.get("alternativeTitles");
|
||||
this.titlesCollection.add(file);
|
||||
|
||||
|
||||
this.onShow();
|
||||
},
|
||||
|
||||
_refreshClose : function(options) {
|
||||
this.titlesCollection = new TitlesCollection();
|
||||
var file = this.movie.model.get("alternativeTitles");
|
||||
this.titlesCollection.add(file);
|
||||
this._showGrid();
|
||||
},
|
||||
|
||||
onShow : function() {
|
||||
this.grid.show(new Backgrid.Grid({
|
||||
row : Backgrid.Row,
|
||||
columns : this.columns,
|
||||
collection : this.titlesCollection,
|
||||
className : 'table table-hover'
|
||||
}));
|
||||
},
|
||||
|
||||
_showGrid : function() {
|
||||
this.regionManager.get('grid').show(new Backgrid.Grid({
|
||||
row : Backgrid.Row,
|
||||
columns : this.columns,
|
||||
collection : this.titlesCollection,
|
||||
className : 'table table-hover'
|
||||
}));
|
||||
},
|
||||
|
||||
_showMainView : function() {
|
||||
this.main.show(this.mainView);
|
||||
},
|
||||
|
||||
_showButtons : function() {
|
||||
this._showMainView();
|
||||
},
|
||||
|
||||
_showSearchResults : function() {
|
||||
if (this.releaseCollection.length === 0) {
|
||||
this.mainView = new NoResultsView();
|
||||
}
|
||||
|
||||
else {
|
||||
//this.mainView = new ManualSearchLayout({ collection : this.releaseCollection });
|
||||
}
|
||||
|
||||
this._showMainView();
|
||||
}
|
||||
});
|
3
src/UI/Movies/Titles/TitlesLayoutTemplate.hbs
Normal file
3
src/UI/Movies/Titles/TitlesLayoutTemplate.hbs
Normal file
@ -0,0 +1,3 @@
|
||||
<div id="movie-titles-region">
|
||||
<div id="movie-titles-grid" class="table-responsive"></div>
|
||||
</div>
|
@ -534,3 +534,9 @@
|
||||
list-style-type : none;
|
||||
}
|
||||
}
|
||||
|
||||
.header-text {
|
||||
.year {
|
||||
color : gray;
|
||||
}
|
||||
}
|
6
src/UI/Release/AlternativeTitleModel.js
Normal file
6
src/UI/Release/AlternativeTitleModel.js
Normal file
@ -0,0 +1,6 @@
|
||||
var Backbone = require('backbone');
|
||||
var _ = require('underscore');
|
||||
|
||||
module.exports = Backbone.Model.extend({
|
||||
urlRoot : window.NzbDrone.ApiRoot + '/alttitle',
|
||||
});
|
6
src/UI/Release/AlternativeYearModel.js
Normal file
6
src/UI/Release/AlternativeYearModel.js
Normal file
@ -0,0 +1,6 @@
|
||||
var Backbone = require('backbone');
|
||||
var _ = require('underscore');
|
||||
|
||||
module.exports = Backbone.Model.extend({
|
||||
urlRoot : window.NzbDrone.ApiRoot + '/altyear',
|
||||
});
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user