mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-19 21:03:17 -07:00
commit
2e8736de46
122 changed files with 4120 additions and 564 deletions
38
Ombi.Api.Interfaces/IDiscordApi.cs
Normal file
38
Ombi.Api.Interfaces/IDiscordApi.cs
Normal file
|
@ -0,0 +1,38 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2017 Jamie Rees
|
||||
// File: IDiscordApi.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Api.Models.Notifications;
|
||||
|
||||
namespace Ombi.Api.Interfaces
|
||||
{
|
||||
public interface IDiscordApi
|
||||
{
|
||||
void SendMessage(string message, string webhookId, string webhookToken, string username = null);
|
||||
Task SendMessageAsync(string message, string webhookId, string webhookToken, string username = null);
|
||||
}
|
||||
}
|
|
@ -31,6 +31,6 @@ namespace Ombi.Api.Interfaces
|
|||
{
|
||||
public interface INetflixApi
|
||||
{
|
||||
NetflixMovieResult GetMovies(string movieName, string year = null);
|
||||
NetflixMovieResult CheckNetflix(string title, string year = null);
|
||||
}
|
||||
}
|
15
Ombi.Api.Interfaces/IRadarrApi.cs
Normal file
15
Ombi.Api.Interfaces/IRadarrApi.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Ombi.Api.Models.Radarr;
|
||||
using Ombi.Api.Models.Sonarr;
|
||||
|
||||
namespace Ombi.Api.Interfaces
|
||||
{
|
||||
public interface IRadarrApi
|
||||
{
|
||||
RadarrAddMovie AddMovie(int tmdbId, string title, int year, int qualityId, string rootPath, string apiKey, Uri baseUrl, bool searchNow = false);
|
||||
List<RadarrMovieResponse> GetMovies(string apiKey, Uri baseUrl);
|
||||
List<SonarrProfile> GetProfiles(string apiKey, Uri baseUrl);
|
||||
SystemStatus SystemStatus(string apiKey, Uri baseUrl);
|
||||
}
|
||||
}
|
|
@ -40,10 +40,11 @@ namespace Ombi.Api.Interfaces
|
|||
bool searchForMissingEpisodes = false);
|
||||
|
||||
SonarrAddSeries AddSeriesNew(int tvdbId, string title, int qualityId, bool seasonFolders, string rootPath,
|
||||
int[] seasons, string apiKey, Uri baseUrl, bool monitor = true,
|
||||
int[] seasons, string apiKey, Uri baseUrl, bool monitor = true,
|
||||
bool searchForMissingEpisodes = false);
|
||||
|
||||
SystemStatus SystemStatus(string apiKey, Uri baseUrl);
|
||||
List<SonarrRootFolder> GetRootFolders(string apiKey, Uri baseUrl);
|
||||
|
||||
List<Series> GetSeries(string apiKey, Uri baseUrl);
|
||||
Series GetSeries(string seriesId, string apiKey, Uri baseUrl);
|
||||
|
|
16
Ombi.Api.Interfaces/ITraktApi.cs
Normal file
16
Ombi.Api.Interfaces/ITraktApi.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using TraktApiSharp.Enums;
|
||||
using TraktApiSharp.Objects.Get.Shows;
|
||||
using TraktApiSharp.Objects.Get.Shows.Common;
|
||||
|
||||
namespace Ombi.Api.Interfaces
|
||||
{
|
||||
public interface ITraktApi
|
||||
{
|
||||
Task<IEnumerable<TraktMostAnticipatedShow>> GetAnticipatedShows(int? page = default(int?), int? limitPerPage = default(int?));
|
||||
Task<IEnumerable<TraktMostWatchedShow>> GetMostWatchesShows(TraktTimePeriod period = null, int? page = default(int?), int? limitPerPage = default(int?));
|
||||
Task<IEnumerable<TraktShow>> GetPopularShows(int? page = default(int?), int? limitPerPage = default(int?));
|
||||
Task<IEnumerable<TraktTrendingShow>> GetTrendingShows(int? page = default(int?), int? limitPerPage = default(int?));
|
||||
}
|
||||
}
|
|
@ -31,6 +31,10 @@
|
|||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="RestSharp, Version=105.2.3.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\RestSharp.105.2.3\lib\net45\RestSharp.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
|
@ -43,19 +47,26 @@
|
|||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="TraktApiSharp, Version=0.8.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\TraktApiSharp.0.8.0\lib\portable-net45+netcore45+wpa81\TraktApiSharp.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="IApiRequest.cs" />
|
||||
<Compile Include="ICouchPotatoApi.cs" />
|
||||
<Compile Include="IDiscordApi.cs" />
|
||||
<Compile Include="IHeadphonesApi.cs" />
|
||||
<Compile Include="IMusicBrainzApi.cs" />
|
||||
<Compile Include="INetflixApi.cs" />
|
||||
<Compile Include="IPlexApi.cs" />
|
||||
<Compile Include="IPushbulletApi.cs" />
|
||||
<Compile Include="IRadarrApi.cs" />
|
||||
<Compile Include="ISlackApi.cs" />
|
||||
<Compile Include="IPushoverApi.cs" />
|
||||
<Compile Include="ISickRageApi.cs" />
|
||||
<Compile Include="ISonarrApi.cs" />
|
||||
<Compile Include="ITraktApi.cs" />
|
||||
<Compile Include="IWatcherApi.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
|
||||
<package id="RestSharp" version="105.2.3" targetFramework="net45" />
|
||||
<package id="TraktApiSharp" version="0.8.0" targetFramework="net45" />
|
||||
</packages>
|
104
Ombi.Api.Models/Movie/TmdbMovieDetails.cs
Normal file
104
Ombi.Api.Models/Movie/TmdbMovieDetails.cs
Normal file
|
@ -0,0 +1,104 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2017 Jamie Rees
|
||||
// File: TmdbMovieDetails.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ombi.Api.Models.Movie
|
||||
{
|
||||
|
||||
public class Genre
|
||||
{
|
||||
public int id { get; set; }
|
||||
public string name { get; set; }
|
||||
}
|
||||
|
||||
public class ProductionCompany
|
||||
{
|
||||
public string name { get; set; }
|
||||
public int id { get; set; }
|
||||
}
|
||||
|
||||
public class ProductionCountry
|
||||
{
|
||||
public string iso_3166_1 { get; set; }
|
||||
public string name { get; set; }
|
||||
}
|
||||
|
||||
public class SpokenLanguage
|
||||
{
|
||||
public string iso_639_1 { get; set; }
|
||||
public string name { get; set; }
|
||||
}
|
||||
|
||||
public class Result
|
||||
{
|
||||
public string id { get; set; }
|
||||
public string iso_639_1 { get; set; }
|
||||
public string iso_3166_1 { get; set; }
|
||||
public string key { get; set; }
|
||||
public string name { get; set; }
|
||||
public string site { get; set; }
|
||||
public int size { get; set; }
|
||||
public string type { get; set; }
|
||||
}
|
||||
|
||||
public class Videos
|
||||
{
|
||||
public List<Result> results { get; set; }
|
||||
}
|
||||
|
||||
public class TmdbMovieDetails
|
||||
{
|
||||
public bool adult { get; set; }
|
||||
public string backdrop_path { get; set; }
|
||||
public object belongs_to_collection { get; set; }
|
||||
public int budget { get; set; }
|
||||
public List<Genre> genres { get; set; }
|
||||
public string homepage { get; set; }
|
||||
public int id { get; set; }
|
||||
public string imdb_id { get; set; }
|
||||
public string original_language { get; set; }
|
||||
public string original_title { get; set; }
|
||||
public string overview { get; set; }
|
||||
public double popularity { get; set; }
|
||||
public string poster_path { get; set; }
|
||||
public List<ProductionCompany> production_companies { get; set; }
|
||||
public List<ProductionCountry> production_countries { get; set; }
|
||||
public string release_date { get; set; }
|
||||
public int revenue { get; set; }
|
||||
public int runtime { get; set; }
|
||||
public List<SpokenLanguage> spoken_languages { get; set; }
|
||||
public string status { get; set; }
|
||||
public string tagline { get; set; }
|
||||
public string title { get; set; }
|
||||
public bool video { get; set; }
|
||||
public double vote_average { get; set; }
|
||||
public int vote_count { get; set; }
|
||||
public Videos videos { get; set; }
|
||||
}
|
||||
|
||||
}
|
|
@ -58,5 +58,12 @@ namespace Ombi.Api.Models.Netflix
|
|||
public string Mediatype { get; set; }
|
||||
[JsonProperty(PropertyName = "runtime")]
|
||||
public string Runtime { get; set; }
|
||||
|
||||
|
||||
// For errors
|
||||
[JsonProperty(PropertyName = "errorcode")]
|
||||
public int ErrorCode { get; set; }
|
||||
[JsonProperty(PropertyName = "message")]
|
||||
public string Message { get; set; }
|
||||
}
|
||||
}
|
34
Ombi.Api.Models/Notifications/DiscordWebhookRequest.cs
Normal file
34
Ombi.Api.Models/Notifications/DiscordWebhookRequest.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2017 Jamie Rees
|
||||
// File: DiscordWebhookRequest.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
namespace Ombi.Api.Models.Notifications
|
||||
{
|
||||
public class DiscordWebhookRequest
|
||||
{
|
||||
public string content { get; set; }
|
||||
public string username { get; set; }
|
||||
}
|
||||
}
|
47
Ombi.Api.Models/Notifications/DiscordWebhookResponse.cs
Normal file
47
Ombi.Api.Models/Notifications/DiscordWebhookResponse.cs
Normal file
|
@ -0,0 +1,47 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2017 Jamie Rees
|
||||
// File: DiscordWebhookResponse.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
|
||||
namespace Ombi.Api.Models.Notifications
|
||||
{
|
||||
public class DiscordWebhookResponse
|
||||
{
|
||||
public string name { get; set; }
|
||||
[JsonProperty(PropertyName = "channel_id")]
|
||||
public string channelid { get; set; }
|
||||
|
||||
public string token { get; set; }
|
||||
public string avatar { get; set; }
|
||||
[JsonProperty(PropertyName = "guild_id")]
|
||||
public string guildid { get; set; }
|
||||
|
||||
public string id { get; set; }
|
||||
|
||||
}
|
||||
}
|
|
@ -54,6 +54,7 @@
|
|||
<Compile Include="Movie\CouchPotatoProfiles.cs" />
|
||||
<Compile Include="Movie\CouchPotatoStatus.cs" />
|
||||
<Compile Include="Movie\CouchPotatoApiKey.cs" />
|
||||
<Compile Include="Movie\TmdbMovieDetails.cs" />
|
||||
<Compile Include="Music\HeadphonesAlbumSearchResult.cs" />
|
||||
<Compile Include="Music\HeadphonesArtistSearchResult.cs" />
|
||||
<Compile Include="Music\HeadphonesGetIndex.cs" />
|
||||
|
@ -62,6 +63,8 @@
|
|||
<Compile Include="Music\MusicBrainzReleaseInfo.cs" />
|
||||
<Compile Include="Music\MusicBrainzSearchResults.cs" />
|
||||
<Compile Include="Netflix\NetflixMovieResult.cs" />
|
||||
<Compile Include="Notifications\DiscordWebhookRequest.cs" />
|
||||
<Compile Include="Notifications\DiscordWebhookResponse.cs" />
|
||||
<Compile Include="Notifications\PushbulletPush.cs" />
|
||||
<Compile Include="Notifications\PushbulletResponse.cs" />
|
||||
<Compile Include="Notifications\PushoverResponse.cs" />
|
||||
|
@ -80,6 +83,10 @@
|
|||
<Compile Include="Plex\PlexUserRequest.cs" />
|
||||
<Compile Include="Plex\RecentlyAddedModelOld.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Radarr\RadarrAddMovie.cs" />
|
||||
<Compile Include="Radarr\RadarrAddOptions.cs" />
|
||||
<Compile Include="Radarr\RadarrError.cs" />
|
||||
<Compile Include="Radarr\RadarrMovieResponse.cs" />
|
||||
<Compile Include="SickRage\SickRageBase.cs" />
|
||||
<Compile Include="SickRage\SickrageShows.cs" />
|
||||
<Compile Include="SickRage\SickRagePing.cs" />
|
||||
|
@ -95,6 +102,7 @@
|
|||
<Compile Include="Sonarr\SonarrEpisodes.cs" />
|
||||
<Compile Include="Sonarr\SonarrError.cs" />
|
||||
<Compile Include="Sonarr\SonarrProfile.cs" />
|
||||
<Compile Include="Sonarr\SonarrRootFolder.cs" />
|
||||
<Compile Include="Sonarr\SonarrSearchCommand.cs" />
|
||||
<Compile Include="Sonarr\SonarrSeasonSearchResult.cs" />
|
||||
<Compile Include="Sonarr\SonarrSeriesSearchResult.cs" />
|
||||
|
|
56
Ombi.Api.Models/Radarr/RadarrAddMovie.cs
Normal file
56
Ombi.Api.Models/Radarr/RadarrAddMovie.cs
Normal file
|
@ -0,0 +1,56 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2017 Jamie Rees
|
||||
// File: RadarrAddMovie.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using Ombi.Api.Models.Sonarr;
|
||||
|
||||
namespace Ombi.Api.Models.Radarr
|
||||
{
|
||||
public class RadarrAddMovie
|
||||
{
|
||||
|
||||
public RadarrAddMovie()
|
||||
{
|
||||
images = new List<string>();
|
||||
}
|
||||
public RadarrError Error { get; set; }
|
||||
public RadarrAddOptions addOptions { get; set; }
|
||||
public string title { get; set; }
|
||||
public string rootFolderPath { get; set; }
|
||||
public int qualityProfileId { get; set; }
|
||||
public bool monitored { get; set; }
|
||||
public int tmdbId { get; set; }
|
||||
public List<string> images { get; set; }
|
||||
public string cleanTitle { get; set; }
|
||||
public string imdbId { get; set; }
|
||||
public string titleSlug { get; set; }
|
||||
public int id { get; set; }
|
||||
public int year { get; set; }
|
||||
|
||||
}
|
||||
}
|
35
Ombi.Api.Models/Radarr/RadarrAddOptions.cs
Normal file
35
Ombi.Api.Models/Radarr/RadarrAddOptions.cs
Normal file
|
@ -0,0 +1,35 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2017 Jamie Rees
|
||||
// File: RadarrAddOptions.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
namespace Ombi.Api.Models.Radarr
|
||||
{
|
||||
public class RadarrAddOptions
|
||||
{
|
||||
public bool ignoreEpisodesWithFiles { get; set; }
|
||||
public bool ignoreEpisodesWithoutFiles { get; set; }
|
||||
public bool searchForMovie { get; set; }
|
||||
}
|
||||
}
|
34
Ombi.Api.Models/Radarr/RadarrError.cs
Normal file
34
Ombi.Api.Models/Radarr/RadarrError.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2017 Jamie Rees
|
||||
// File: RadarrError.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
namespace Ombi.Api.Models.Radarr
|
||||
{
|
||||
public class RadarrError
|
||||
{
|
||||
public string message { get; set; }
|
||||
public string description { get; set; }
|
||||
}
|
||||
}
|
80
Ombi.Api.Models/Radarr/RadarrMovieResponse.cs
Normal file
80
Ombi.Api.Models/Radarr/RadarrMovieResponse.cs
Normal file
|
@ -0,0 +1,80 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2017 Jamie Rees
|
||||
// File: RadarrMovieResponse.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ombi.Api.Models.Radarr
|
||||
{
|
||||
|
||||
|
||||
public class Image
|
||||
{
|
||||
public string coverType { get; set; }
|
||||
public string url { get; set; }
|
||||
}
|
||||
|
||||
public class Ratings
|
||||
{
|
||||
public int votes { get; set; }
|
||||
public double value { get; set; }
|
||||
}
|
||||
|
||||
public class RadarrMovieResponse
|
||||
{
|
||||
public string title { get; set; }
|
||||
public string sortTitle { get; set; }
|
||||
public double sizeOnDisk { get; set; }
|
||||
public string status { get; set; }
|
||||
public string overview { get; set; }
|
||||
public string inCinemas { get; set; }
|
||||
public string physicalRelease { get; set; }
|
||||
public List<Image> images { get; set; }
|
||||
public string website { get; set; }
|
||||
public bool downloaded { get; set; }
|
||||
public int year { get; set; }
|
||||
public bool hasFile { get; set; }
|
||||
public string youTubeTrailerId { get; set; }
|
||||
public string studio { get; set; }
|
||||
public string path { get; set; }
|
||||
public int profileId { get; set; }
|
||||
public bool monitored { get; set; }
|
||||
public int runtime { get; set; }
|
||||
public string lastInfoSync { get; set; }
|
||||
public string cleanTitle { get; set; }
|
||||
public string imdbId { get; set; }
|
||||
public int tmdbId { get; set; }
|
||||
public string titleSlug { get; set; }
|
||||
public List<string> genres { get; set; }
|
||||
public List<object> tags { get; set; }
|
||||
public string added { get; set; }
|
||||
public Ratings ratings { get; set; }
|
||||
public List<string> alternativeTitles { get; set; }
|
||||
public int qualityProfileId { get; set; }
|
||||
public int id { get; set; }
|
||||
}
|
||||
|
||||
}
|
35
Ombi.Api.Models/Sonarr/SonarrRootFolder.cs
Normal file
35
Ombi.Api.Models/Sonarr/SonarrRootFolder.cs
Normal file
|
@ -0,0 +1,35 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2017 Jamie Rees
|
||||
// File: SonarrRootFolder.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
namespace Ombi.Api.Models.Sonarr
|
||||
{
|
||||
public class SonarrRootFolder
|
||||
{
|
||||
public int id { get; set; }
|
||||
public string path { get; set; }
|
||||
public long freespace { get; set; }
|
||||
}
|
||||
}
|
|
@ -70,7 +70,7 @@ namespace Ombi.Api
|
|||
|
||||
return response.Data;
|
||||
}
|
||||
|
||||
|
||||
public IRestResponse Execute(IRestRequest request, Uri baseUri)
|
||||
{
|
||||
var client = new RestClient { BaseUrl = baseUri };
|
||||
|
|
115
Ombi.Api/DiscordApi.cs
Normal file
115
Ombi.Api/DiscordApi.cs
Normal file
|
@ -0,0 +1,115 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2017 Jamie Rees
|
||||
// File: NetflixRouletteApi.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using Ombi.Api.Interfaces;
|
||||
using Ombi.Api.Models.Netflix;
|
||||
using Ombi.Api.Models.Notifications;
|
||||
using RestSharp;
|
||||
|
||||
namespace Ombi.Api
|
||||
{
|
||||
public class DiscordApi : IDiscordApi
|
||||
{
|
||||
public DiscordApi(IApiRequest req)
|
||||
{
|
||||
Api = req;
|
||||
}
|
||||
|
||||
private IApiRequest Api { get; }
|
||||
private Uri Endpoint => new Uri("https://discordapp.com/api/"); //webhooks/270828242636636161/lLysOMhJ96AFO1kvev0bSqP-WCZxKUh1UwfubhIcLkpS0DtM3cg4Pgeraw3waoTXbZii
|
||||
|
||||
|
||||
public void SendMessage(string message, string webhookId, string webhookToken, string username = null)
|
||||
{
|
||||
var request = new RestRequest
|
||||
{
|
||||
Resource = "webhooks/{webhookId}/{webhookToken}",
|
||||
Method = Method.POST
|
||||
};
|
||||
|
||||
request.AddUrlSegment("webhookId", webhookId);
|
||||
request.AddUrlSegment("webhookToken", webhookToken);
|
||||
|
||||
var body = new DiscordWebhookRequest
|
||||
{
|
||||
content = message,
|
||||
username = username
|
||||
};
|
||||
request.AddJsonBody(body);
|
||||
|
||||
request.AddHeader("Content-Type", "application/json");
|
||||
|
||||
Api.Execute(request, Endpoint);
|
||||
}
|
||||
|
||||
public async Task SendMessageAsync(string message, string webhookId, string webhookToken, string username = null)
|
||||
{
|
||||
var request = new RestRequest
|
||||
{
|
||||
Resource = "webhooks/{webhookId}/{webhookToken}",
|
||||
Method = Method.POST
|
||||
};
|
||||
|
||||
request.AddUrlSegment("webhookId", webhookId);
|
||||
request.AddUrlSegment("webhookToken", webhookToken);
|
||||
|
||||
var body = new DiscordWebhookRequest
|
||||
{
|
||||
content = message,
|
||||
username = username
|
||||
};
|
||||
request.AddJsonBody(body);
|
||||
|
||||
request.AddHeader("Content-Type", "application/json");
|
||||
|
||||
await Task.Run(
|
||||
() =>
|
||||
{
|
||||
Api.Execute(request, Endpoint);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
public NetflixMovieResult CheckNetflix(string title, string year = null)
|
||||
{
|
||||
var request = new RestRequest();
|
||||
request.AddQueryParameter("title", title);
|
||||
if (!string.IsNullOrEmpty(year))
|
||||
{
|
||||
request.AddQueryParameter("year", year);
|
||||
}
|
||||
var result = Api.Execute(request, Endpoint);
|
||||
|
||||
return JsonConvert.DeserializeObject<NetflixMovieResult>(result.Content);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -32,6 +32,8 @@ namespace Ombi.Api
|
|||
public abstract class MovieBase
|
||||
{
|
||||
private static readonly string Encrypted = "0T3QNSseexLO7n7UPiJvl70Y+KKnvbeTlsusl7Kwq0hPH0BHOuFNGwksNCjkwqWedyDdI/MJeUR4wtL4bIl0Z+//uHXEaYM/4H2pjeLbH5EWdUe5TTj1AhaIR5PQweamvcienRyFD/3YPCC/+qL5mHkKXBkPumMod3Zb/4yN0Ik=";
|
||||
protected string ApiKey = StringCipher.Decrypt(Encrypted, "ApiKey");
|
||||
private string _apiKey;
|
||||
|
||||
protected string ApiKey => _apiKey ?? (_apiKey = StringCipher.Decrypt(Encrypted, "ApiKey"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using Ombi.Api.Interfaces;
|
||||
using Ombi.Api.Models.Netflix;
|
||||
|
@ -43,10 +44,10 @@ namespace Ombi.Api
|
|||
private IApiRequest Api { get; }
|
||||
private Uri Endpoint => new Uri("http://netflixroulette.net/api/api.php");
|
||||
|
||||
public NetflixMovieResult GetMovies(string movieName, string year = null)
|
||||
public NetflixMovieResult CheckNetflix(string title, string year = null)
|
||||
{
|
||||
var request = new RestRequest();
|
||||
request.AddQueryParameter("title", movieName);
|
||||
request.AddQueryParameter("title", title);
|
||||
if (!string.IsNullOrEmpty(year))
|
||||
{
|
||||
request.AddQueryParameter("year", year);
|
||||
|
|
|
@ -66,10 +66,17 @@
|
|||
<HintPath>..\packages\TMDbLib.0.9.0.0-alpha\lib\net45\TMDbLib.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="TraktApiSharp, Version=0.8.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\TraktApiSharp.0.8.0\lib\portable-net45+netcore45+wpa81\TraktApiSharp.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="ApiRequest.cs" />
|
||||
<Compile Include="DiscordApi.cs" />
|
||||
<Compile Include="NetflixRouletteApi.cs" />
|
||||
<Compile Include="RadarrApi.cs" />
|
||||
<Compile Include="TraktApi.cs" />
|
||||
<Compile Include="WatcherApi.cs" />
|
||||
<Compile Include="MusicBrainzApi.cs" />
|
||||
<Compile Include="SlackApi.cs" />
|
||||
|
|
158
Ombi.Api/RadarrApi.cs
Normal file
158
Ombi.Api/RadarrApi.cs
Normal file
|
@ -0,0 +1,158 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: CouchPotatoApi.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using NLog;
|
||||
using Ombi.Api.Interfaces;
|
||||
using Ombi.Api.Models.Radarr;
|
||||
using Ombi.Api.Models.Sonarr;
|
||||
using Ombi.Helpers;
|
||||
using RestSharp;
|
||||
|
||||
namespace Ombi.Api
|
||||
{
|
||||
public class RadarrApi : IRadarrApi
|
||||
{
|
||||
public RadarrApi()
|
||||
{
|
||||
Api = new ApiRequest();
|
||||
}
|
||||
private ApiRequest Api { get; set; }
|
||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
public List<SonarrProfile> GetProfiles(string apiKey, Uri baseUrl)
|
||||
{
|
||||
var request = new RestRequest { Resource = "/api/profile", Method = Method.GET };
|
||||
|
||||
request.AddHeader("X-Api-Key", apiKey);
|
||||
var policy = RetryHandler.RetryAndWaitPolicy((exception, timespan) => Log.Error(exception, "Exception when calling GetProfiles for Sonarr, Retrying {0}", timespan), new TimeSpan[] {
|
||||
TimeSpan.FromSeconds (2),
|
||||
TimeSpan.FromSeconds(5),
|
||||
TimeSpan.FromSeconds(10)
|
||||
});
|
||||
|
||||
var obj = policy.Execute(() => Api.ExecuteJson<List<SonarrProfile>>(request, baseUrl));
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
public RadarrAddMovie AddMovie(int tmdbId, string title, int year, int qualityId, string rootPath, string apiKey, Uri baseUrl, bool searchNow = false)
|
||||
{
|
||||
var request = new RestRequest
|
||||
{
|
||||
Resource = "/api/movie",
|
||||
Method = Method.POST
|
||||
};
|
||||
|
||||
var options = new RadarrAddMovie
|
||||
{
|
||||
title = title,
|
||||
tmdbId = tmdbId,
|
||||
qualityProfileId = qualityId,
|
||||
rootFolderPath = rootPath,
|
||||
titleSlug = title,
|
||||
monitored = true,
|
||||
year = year
|
||||
};
|
||||
|
||||
if (searchNow)
|
||||
{
|
||||
options.addOptions = new RadarrAddOptions
|
||||
{
|
||||
searchForMovie = true
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
request.AddHeader("X-Api-Key", apiKey);
|
||||
request.AddJsonBody(options);
|
||||
|
||||
RadarrAddMovie result;
|
||||
try
|
||||
{
|
||||
var policy = RetryHandler.RetryAndWaitPolicy((exception, timespan) => Log.Error(exception, "Exception when calling AddSeries for Sonarr, Retrying {0}", timespan), new TimeSpan[] {
|
||||
TimeSpan.FromSeconds (2)
|
||||
});
|
||||
|
||||
var response = policy.Execute(() => Api.Execute(request, baseUrl));
|
||||
if (response.Content.Contains("\"message\":"))
|
||||
{
|
||||
var error = JsonConvert.DeserializeObject < RadarrError>(response.Content);
|
||||
return new RadarrAddMovie {Error = error};
|
||||
}
|
||||
if (response.Content.Contains("\"errorMessage\":"))
|
||||
{
|
||||
var error = JsonConvert.DeserializeObject<List<SonarrError>>(response.Content).FirstOrDefault();
|
||||
return new RadarrAddMovie {Error = new RadarrError {message = error?.errorMessage}};
|
||||
}
|
||||
return JsonConvert.DeserializeObject < RadarrAddMovie>(response.Content);
|
||||
}
|
||||
catch (JsonSerializationException jse)
|
||||
{
|
||||
Log.Error(jse);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public SystemStatus SystemStatus(string apiKey, Uri baseUrl)
|
||||
{
|
||||
var request = new RestRequest { Resource = "/api/system/status", Method = Method.GET };
|
||||
request.AddHeader("X-Api-Key", apiKey);
|
||||
|
||||
var policy = RetryHandler.RetryAndWaitPolicy((exception, timespan) => Log.Error(exception, "Exception when calling SystemStatus for Sonarr, Retrying {0}", timespan), new TimeSpan[] {
|
||||
TimeSpan.FromSeconds (2),
|
||||
TimeSpan.FromSeconds(5),
|
||||
TimeSpan.FromSeconds(10)
|
||||
});
|
||||
|
||||
var obj = policy.Execute(() => Api.ExecuteJson<SystemStatus>(request, baseUrl));
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
public List<RadarrMovieResponse> GetMovies(string apiKey, Uri baseUrl)
|
||||
{
|
||||
var request = new RestRequest { Resource = "/api/movie", Method = Method.GET };
|
||||
request.AddHeader("X-Api-Key", apiKey);
|
||||
|
||||
var policy = RetryHandler.RetryAndWaitPolicy((exception, timespan) => Log.Error(exception, "Exception when calling SystemStatus for Sonarr, Retrying {0}", timespan), new TimeSpan[] {
|
||||
TimeSpan.FromSeconds (2),
|
||||
TimeSpan.FromSeconds(5),
|
||||
TimeSpan.FromSeconds(10)
|
||||
});
|
||||
|
||||
var obj = policy.Execute(() => Api.Execute(request, baseUrl));
|
||||
|
||||
return JsonConvert.DeserializeObject<List<RadarrMovieResponse>>(obj.Content);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -62,6 +62,22 @@ namespace Ombi.Api
|
|||
return obj;
|
||||
}
|
||||
|
||||
public List<SonarrRootFolder> GetRootFolders(string apiKey, Uri baseUrl)
|
||||
{
|
||||
var request = new RestRequest { Resource = "/api/rootfolder", Method = Method.GET };
|
||||
|
||||
request.AddHeader("X-Api-Key", apiKey);
|
||||
var policy = RetryHandler.RetryAndWaitPolicy((exception, timespan) => Log.Error(exception, "Exception when calling GetRootFolders for Sonarr, Retrying {0}", timespan), new TimeSpan[] {
|
||||
TimeSpan.FromSeconds (2),
|
||||
TimeSpan.FromSeconds(5),
|
||||
TimeSpan.FromSeconds(10)
|
||||
});
|
||||
|
||||
var obj = policy.Execute(() => Api.ExecuteJson<List<SonarrRootFolder>>(request, baseUrl));
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
public SonarrAddSeries AddSeries(int tvdbId, string title, int qualityId, bool seasonFolders, string rootPath, int seasonCount, int[] seasons, string apiKey, Uri baseUrl, bool monitor = true, bool searchForMissingEpisodes = false)
|
||||
{
|
||||
Log.Debug("Adding series {0}", title);
|
||||
|
|
|
@ -25,12 +25,18 @@
|
|||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using NLog;
|
||||
using NLog.Fluent;
|
||||
using Ombi.Api.Models.Movie;
|
||||
using RestSharp;
|
||||
using TMDbLib.Client;
|
||||
using TMDbLib.Objects.General;
|
||||
using TMDbLib.Objects.Movies;
|
||||
using TMDbLib.Objects.Search;
|
||||
using Movie = TMDbLib.Objects.Movies.Movie;
|
||||
|
||||
namespace Ombi.Api
|
||||
{
|
||||
|
@ -39,9 +45,13 @@ namespace Ombi.Api
|
|||
public TheMovieDbApi()
|
||||
{
|
||||
Client = new TMDbClient(ApiKey);
|
||||
Api = new ApiRequest();
|
||||
}
|
||||
|
||||
private ApiRequest Api { get; }
|
||||
public TMDbClient Client { get; set; }
|
||||
private const string BaseUrl = "https://api.themoviedb.org/3/";
|
||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
public async Task<List<SearchMovie>> SearchMovie(string searchTerm)
|
||||
{
|
||||
var results = await Client.SearchMovie(searchTerm);
|
||||
|
@ -56,7 +66,27 @@ namespace Ombi.Api
|
|||
public async Task<List<MovieResult>> GetUpcomingMovies()
|
||||
{
|
||||
var movies = await Client.GetMovieList(MovieListType.Upcoming);
|
||||
return movies?.Results ?? new List<MovieResult>();
|
||||
return movies?.Results ?? new List<MovieResult>();
|
||||
}
|
||||
|
||||
public TmdbMovieDetails GetMovieInformationWithVideos(int tmdbId)
|
||||
{
|
||||
var request = new RestRequest { Resource = "movie/{movieId}", Method = Method.GET };
|
||||
request.AddUrlSegment("movieId", tmdbId.ToString());
|
||||
request.AddQueryParameter("api_key", ApiKey);
|
||||
request.AddQueryParameter("append_to_response", "videos"); // Get the videos
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
var obj = Api.ExecuteJson<TmdbMovieDetails>(request, new Uri(BaseUrl));
|
||||
return obj;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Movie> GetMovieInformation(int tmdbId)
|
||||
|
|
51
Ombi.Api/TraktApi.cs
Normal file
51
Ombi.Api/TraktApi.cs
Normal file
|
@ -0,0 +1,51 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Api.Interfaces;
|
||||
using Ombi.Helpers;
|
||||
using TraktApiSharp;
|
||||
using TraktApiSharp.Enums;
|
||||
using TraktApiSharp.Objects.Get.Shows;
|
||||
using TraktApiSharp.Objects.Get.Shows.Common;
|
||||
using TraktApiSharp.Requests.Params;
|
||||
|
||||
namespace Ombi.Api
|
||||
{
|
||||
public class TraktApi : ITraktApi
|
||||
{
|
||||
private TraktClient Client { get; }
|
||||
|
||||
private static readonly string Encrypted = "z/56wM/oEkkCWEvSIZCrzQyUvvqmafQ3njqf0UNK5xuKbNYh5Wz8ocoG2QDa5y1DBkozLaKsGxORmAB1XUvwbnom8DVNo9gE++9GTuwxmGlLDD318PXpRmYmpKqNwFSKRZgF6ewiY9qR4t3iG0pGQwPA08FK3+H7kpOKAGJNR9RMDP9wwB6Vl4DuOiZb9/DETjzZ+/zId0ZqimrbN+PLrg==";
|
||||
private readonly string _apiKey = StringCipher.Decrypt(Encrypted, "ApiKey");
|
||||
public TraktApi()
|
||||
{
|
||||
Client = new TraktClient(_apiKey);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TraktShow>> GetPopularShows(int? page = null, int? limitPerPage = null)
|
||||
{
|
||||
var popular = await Client.Shows.GetPopularShowsAsync(new TraktExtendedInfo { Full = true }, null, page ?? 1, limitPerPage ?? 10);
|
||||
return popular.Items;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TraktTrendingShow>> GetTrendingShows(int? page = null, int? limitPerPage = null)
|
||||
{
|
||||
var trendingShowsTop10 = await Client.Shows.GetTrendingShowsAsync(new TraktExtendedInfo { Full = true }, null, page ?? 1, limitPerPage ?? 10);
|
||||
return trendingShowsTop10.Items;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TraktMostAnticipatedShow>> GetAnticipatedShows(int? page = null, int? limitPerPage = null)
|
||||
{
|
||||
var anticipatedShows = await Client.Shows.GetMostAnticipatedShowsAsync(new TraktExtendedInfo { Full = true }, null, page ?? 1, limitPerPage ?? 10);
|
||||
return anticipatedShows.Items;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TraktMostWatchedShow>> GetMostWatchesShows(TraktTimePeriod period = null, int? page = null, int? limitPerPage = null)
|
||||
{
|
||||
var anticipatedShows = await Client.Shows.GetMostWatchedShowsAsync(period ?? TraktTimePeriod.Monthly, new TraktExtendedInfo { Full = true }, null, page ?? 1, limitPerPage ?? 10);
|
||||
return anticipatedShows.Items;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,4 +8,5 @@
|
|||
<package id="RestSharp" version="105.2.3" targetFramework="net45" />
|
||||
<package id="System.Net.Http" version="4.0.0" targetFramework="net45" />
|
||||
<package id="TMDbLib" version="0.9.0.0-alpha" targetFramework="net45" />
|
||||
<package id="TraktApiSharp" version="0.8.0" targetFramework="net45" />
|
||||
</packages>
|
64
Ombi.Core.Migration/Migrations/Version2200.cs
Normal file
64
Ombi.Core.Migration/Migrations/Version2200.cs
Normal file
|
@ -0,0 +1,64 @@
|
|||
#region Copyright
|
||||
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: Version1100.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
|
||||
#endregion
|
||||
|
||||
using System.Data;
|
||||
using NLog;
|
||||
using Ombi.Core.SettingModels;
|
||||
|
||||
namespace Ombi.Core.Migration.Migrations
|
||||
{
|
||||
[Migration(22000, "v2.20.0.0")]
|
||||
public class Version2200 : BaseMigration, IMigration
|
||||
{
|
||||
public Version2200(ISettingsService<CustomizationSettings> custom)
|
||||
{
|
||||
Customization = custom;
|
||||
}
|
||||
|
||||
public int Version => 22000;
|
||||
private ISettingsService<CustomizationSettings> Customization { get; set; }
|
||||
|
||||
|
||||
private static Logger Logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
public void Start(IDbConnection con)
|
||||
{
|
||||
//UpdateCustomSettings(); Turned off the migration for now until the search has been improved on.
|
||||
//UpdateSchema(con, Version);
|
||||
}
|
||||
|
||||
private void UpdateCustomSettings()
|
||||
{
|
||||
var settings = Customization.GetSettings();
|
||||
settings.NewSearch = true; // Use the new search
|
||||
|
||||
Customization.SaveSettings(settings);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -69,6 +69,7 @@
|
|||
<Compile Include="MigrationAttribute.cs" />
|
||||
<Compile Include="MigrationRunner.cs" />
|
||||
<Compile Include="Migrations\BaseMigration.cs" />
|
||||
<Compile Include="Migrations\Version2200.cs" />
|
||||
<Compile Include="Migrations\Version1100.cs" />
|
||||
<Compile Include="Migrations\Version195.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
|
|
|
@ -37,7 +37,9 @@ namespace Ombi.Core
|
|||
public const string PlexEpisodes = nameof(PlexEpisodes);
|
||||
public const string TvDbToken = nameof(TvDbToken);
|
||||
public const string SonarrQualityProfiles = nameof(SonarrQualityProfiles);
|
||||
public const string RadarrQualityProfiles = nameof(RadarrQualityProfiles);
|
||||
public const string SonarrQueued = nameof(SonarrQueued);
|
||||
public const string RadarrMovies = nameof(RadarrMovies);
|
||||
public const string SickRageQualityProfiles = nameof(SickRageQualityProfiles);
|
||||
public const string SickRageQueued = nameof(SickRageQueued);
|
||||
public const string CouchPotatoQualityProfiles = nameof(CouchPotatoQualityProfiles);
|
||||
|
@ -45,5 +47,6 @@ namespace Ombi.Core
|
|||
public const string WatcherQueued = nameof(WatcherQueued);
|
||||
public const string GetPlexRequestSettings = nameof(GetPlexRequestSettings);
|
||||
public const string LastestProductVersion = nameof(LastestProductVersion);
|
||||
public const string SonarrRootFolders = nameof(SonarrRootFolders);
|
||||
}
|
||||
}
|
|
@ -29,7 +29,10 @@ namespace Ombi.Core
|
|||
/// Gets the username this could be the alias! We should always use this method when getting the username
|
||||
/// </summary>
|
||||
/// <param name="username">The username.</param>
|
||||
/// <returns><c>null</c> if we cannot find a user</returns>
|
||||
/// <param name="session">The session.</param>
|
||||
/// <returns>
|
||||
/// <c>null</c> if we cannot find a user
|
||||
/// </returns>
|
||||
string GetUsername(string username, ISession session);
|
||||
}
|
||||
}
|
|
@ -37,16 +37,20 @@ namespace Ombi.Core
|
|||
public class MovieSender : IMovieSender
|
||||
{
|
||||
public MovieSender(ISettingsService<CouchPotatoSettings> cp, ISettingsService<WatcherSettings> watcher,
|
||||
ICouchPotatoApi cpApi, IWatcherApi watcherApi)
|
||||
ICouchPotatoApi cpApi, IWatcherApi watcherApi, IRadarrApi radarrApi, ISettingsService<RadarrSettings> radarrSettings)
|
||||
{
|
||||
CouchPotatoSettings = cp;
|
||||
WatcherSettings = watcher;
|
||||
CpApi = cpApi;
|
||||
WatcherApi = watcherApi;
|
||||
RadarrSettings = radarrSettings;
|
||||
RadarrApi = radarrApi;
|
||||
}
|
||||
|
||||
private ISettingsService<CouchPotatoSettings> CouchPotatoSettings { get; }
|
||||
private ISettingsService<WatcherSettings> WatcherSettings { get; }
|
||||
private ISettingsService<RadarrSettings> RadarrSettings { get; }
|
||||
private IRadarrApi RadarrApi { get; }
|
||||
private ICouchPotatoApi CpApi { get; }
|
||||
private IWatcherApi WatcherApi { get; }
|
||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
|
@ -55,6 +59,7 @@ namespace Ombi.Core
|
|||
{
|
||||
var cpSettings = await CouchPotatoSettings.GetSettingsAsync();
|
||||
var watcherSettings = await WatcherSettings.GetSettingsAsync();
|
||||
var radarrSettings = await RadarrSettings.GetSettingsAsync();
|
||||
|
||||
if (cpSettings.Enabled)
|
||||
{
|
||||
|
@ -66,6 +71,11 @@ namespace Ombi.Core
|
|||
return SendToWatcher(model, watcherSettings);
|
||||
}
|
||||
|
||||
if (radarrSettings.Enabled)
|
||||
{
|
||||
return SendToRadarr(model, radarrSettings);
|
||||
}
|
||||
|
||||
return new MovieSenderResult { Result = false, MovieSendingEnabled = false };
|
||||
}
|
||||
|
||||
|
@ -91,5 +101,23 @@ namespace Ombi.Core
|
|||
var result = CpApi.AddMovie(model.ImdbId, settings.ApiKey, model.Title, settings.FullUri, qualityId);
|
||||
return new MovieSenderResult { Result = result, MovieSendingEnabled = true };
|
||||
}
|
||||
|
||||
private MovieSenderResult SendToRadarr(RequestedModel model, RadarrSettings settings)
|
||||
{
|
||||
var qualityProfile = 0;
|
||||
int.TryParse(settings.QualityProfile, out qualityProfile);
|
||||
var result = RadarrApi.AddMovie(model.ProviderId, model.Title, model.ReleaseDate.Year, qualityProfile, settings.RootPath, settings.ApiKey, settings.FullUri, true);
|
||||
|
||||
if (!string.IsNullOrEmpty(result.Error?.message))
|
||||
{
|
||||
Log.Error(result.Error.message);
|
||||
return new MovieSenderResult { Result = false, Error = true};
|
||||
}
|
||||
if (!string.IsNullOrEmpty(result.title))
|
||||
{
|
||||
return new MovieSenderResult { Result = true, MovieSendingEnabled = true };
|
||||
}
|
||||
return new MovieSenderResult { Result = false, MovieSendingEnabled = true };
|
||||
}
|
||||
}
|
||||
}
|
|
@ -36,5 +36,7 @@ namespace Ombi.Core
|
|||
/// <c>true</c> if [movie sending enabled]; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool MovieSendingEnabled { get; set; }
|
||||
|
||||
public bool Error { get; set; }
|
||||
}
|
||||
}
|
|
@ -122,6 +122,8 @@
|
|||
<Compile Include="Queue\TransientFaultQueue.cs" />
|
||||
<Compile Include="SecurityExtensions.cs" />
|
||||
<Compile Include="SettingModels\AuthenticationSettings.cs" />
|
||||
<Compile Include="SettingModels\DiscordNotificationSettings.cs" />
|
||||
<Compile Include="SettingModels\RadarrSettings.cs" />
|
||||
<Compile Include="SettingModels\WatcherSettings.cs" />
|
||||
<Compile Include="SettingModels\ExternalSettings.cs" />
|
||||
<Compile Include="SettingModels\HeadphonesSettings.cs" />
|
||||
|
|
|
@ -97,7 +97,7 @@ namespace Ombi.Core.Queue
|
|||
Content = ByteConverterHelper.ReturnBytes(request),
|
||||
PrimaryIdentifier = id,
|
||||
FaultType = faultType,
|
||||
Message = description ?? string.Empty
|
||||
Description = description ?? string.Empty
|
||||
};
|
||||
await RequestQueue.InsertAsync(queue);
|
||||
}
|
||||
|
|
|
@ -94,40 +94,33 @@ namespace Ombi.Core
|
|||
/// Gets the username this could be the alias! We should always use this method when getting the username
|
||||
/// </summary>
|
||||
/// <param name="username">The username.</param>
|
||||
/// <returns><c>null</c> if we cannot find a user</returns>
|
||||
/// <param name="session"></param>
|
||||
/// <returns>
|
||||
/// <c>null</c> if we cannot find a user
|
||||
/// </returns>
|
||||
public string GetUsername(string username, ISession session)
|
||||
{
|
||||
var plexUser = PlexUsers.GetUserByUsername(username);
|
||||
if (plexUser != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(plexUser.UserAlias))
|
||||
{
|
||||
return plexUser.UserAlias;
|
||||
}
|
||||
else
|
||||
{
|
||||
return plexUser.Username;
|
||||
}
|
||||
return !string.IsNullOrEmpty(plexUser.UserAlias) ? plexUser.UserAlias : plexUser.Username;
|
||||
}
|
||||
|
||||
var dbUser = UserRepository.GetUserByUsername(username);
|
||||
if (dbUser != null)
|
||||
{
|
||||
var userProps = ByteConverterHelper.ReturnObject<UserProperties>(dbUser.UserProperties);
|
||||
if (!string.IsNullOrEmpty(userProps.UserAlias))
|
||||
{
|
||||
return userProps.UserAlias;
|
||||
}
|
||||
else
|
||||
{
|
||||
return dbUser.UserName;
|
||||
}
|
||||
return !string.IsNullOrEmpty(userProps.UserAlias) ? userProps.UserAlias : dbUser.UserName;
|
||||
}
|
||||
|
||||
// could be a local user
|
||||
var localName = session[SessionKeys.UsernameKey];
|
||||
var hasSessionKey = session[SessionKeys.UsernameKey] != null;
|
||||
if (hasSessionKey)
|
||||
{
|
||||
return (string)session[SessionKeys.UsernameKey];
|
||||
}
|
||||
|
||||
return localName as string;
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -53,5 +53,7 @@ namespace Ombi.Core.SettingModels
|
|||
|
||||
public int DefaultLang { get; set; }
|
||||
|
||||
public bool NewSearch { get; set; }
|
||||
|
||||
}
|
||||
}
|
31
Ombi.Core/SettingModels/DiscordNotificationSettings.cs
Normal file
31
Ombi.Core/SettingModels/DiscordNotificationSettings.cs
Normal file
|
@ -0,0 +1,31 @@
|
|||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Ombi.Core.SettingModels
|
||||
{
|
||||
public sealed class DiscordNotificationSettings : NotificationSettings
|
||||
{
|
||||
public string WebhookUrl { get; set; }
|
||||
public string Username { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public string WebookId => SplitWebUrl(4);
|
||||
|
||||
[JsonIgnore]
|
||||
public string Token => SplitWebUrl(5);
|
||||
|
||||
private string SplitWebUrl(int index)
|
||||
{
|
||||
if (!WebhookUrl.StartsWith("http", StringComparison.InvariantCulture))
|
||||
{
|
||||
WebhookUrl = "https://" + WebhookUrl;
|
||||
}
|
||||
var split = WebhookUrl.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
return split.Length < index
|
||||
? string.Empty
|
||||
: split[index];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -34,7 +34,6 @@ namespace Ombi.Core.SettingModels
|
|||
public string EmailSender { get; set; }
|
||||
public string EmailUsername { get; set; }
|
||||
public bool Authentication { get; set; }
|
||||
public bool EnableUserEmailNotifications { get; set; }
|
||||
public string RecipientEmail { get; set; }
|
||||
}
|
||||
}
|
37
Ombi.Core/SettingModels/RadarrSettings.cs
Normal file
37
Ombi.Core/SettingModels/RadarrSettings.cs
Normal file
|
@ -0,0 +1,37 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: SonarrSettings.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
namespace Ombi.Core.SettingModels
|
||||
{
|
||||
public sealed class RadarrSettings : ExternalSettings
|
||||
{
|
||||
public bool Enabled { get; set; }
|
||||
public string ApiKey { get; set; }
|
||||
public string QualityProfile { get; set; }
|
||||
public string RootPath { get; set; }
|
||||
|
||||
}
|
||||
}
|
|
@ -46,5 +46,6 @@ namespace Ombi.Core.SettingModels
|
|||
public int FaultQueueHandler { get; set; }
|
||||
public int PlexContentCacher { get; set; }
|
||||
public int PlexUserChecker { get; set; }
|
||||
public int RadarrCacher { get; set; }
|
||||
}
|
||||
}
|
|
@ -32,7 +32,13 @@ namespace Ombi.Core.SettingModels
|
|||
public string ApiKey { get; set; }
|
||||
public string QualityProfile { get; set; }
|
||||
public bool SeasonFolders { get; set; }
|
||||
/// <summary>
|
||||
/// This is the root path ID
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The root path.
|
||||
/// </value>
|
||||
public string RootPath { get; set; }
|
||||
|
||||
public string FullRootPath { get; set; }
|
||||
}
|
||||
}
|
|
@ -34,19 +34,22 @@ using Ombi.Api.Interfaces;
|
|||
using Ombi.Api.Models.SickRage;
|
||||
using Ombi.Api.Models.Sonarr;
|
||||
using Ombi.Core.SettingModels;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Store;
|
||||
|
||||
namespace Ombi.Core
|
||||
{
|
||||
public class TvSender
|
||||
{
|
||||
public TvSender(ISonarrApi sonarrApi, ISickRageApi srApi)
|
||||
public TvSender(ISonarrApi sonarrApi, ISickRageApi srApi, ICacheProvider cache)
|
||||
{
|
||||
SonarrApi = sonarrApi;
|
||||
SickrageApi = srApi;
|
||||
Cache = cache;
|
||||
}
|
||||
private ISonarrApi SonarrApi { get; }
|
||||
private ISickRageApi SickrageApi { get; }
|
||||
private ICacheProvider Cache { get; }
|
||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
public async Task<SonarrAddSeries> SendToSonarr(SonarrSettings sonarrSettings, RequestedModel model)
|
||||
|
@ -82,6 +85,8 @@ namespace Ombi.Core
|
|||
var latest = model.SeasonsRequested?.Equals("Latest", StringComparison.CurrentCultureIgnoreCase);
|
||||
var specificSeasonRequest = model.SeasonList?.Any();
|
||||
|
||||
var rootFolderPath = model.RootFolderSelected <= 0 ? sonarrSettings.FullRootPath : await GetRootPath(model.RootFolderSelected, sonarrSettings);
|
||||
|
||||
if (episodeRequest)
|
||||
{
|
||||
// Does series exist?
|
||||
|
@ -96,7 +101,7 @@ namespace Ombi.Core
|
|||
|
||||
// Series doesn't exist, need to add it as unmonitored.
|
||||
var addResult = await Task.Run(() => SonarrApi.AddSeries(model.ProviderId, model.Title, qualityProfile,
|
||||
sonarrSettings.SeasonFolders, sonarrSettings.RootPath, 0, new int[0], sonarrSettings.ApiKey,
|
||||
sonarrSettings.SeasonFolders, rootFolderPath, 0, new int[0], sonarrSettings.ApiKey,
|
||||
sonarrSettings.FullUri, false));
|
||||
|
||||
|
||||
|
@ -125,7 +130,7 @@ namespace Ombi.Core
|
|||
{
|
||||
// Set the series as monitored with a season count as 0 so it doesn't search for anything
|
||||
SonarrApi.AddSeriesNew(model.ProviderId, model.Title, qualityProfile,
|
||||
sonarrSettings.SeasonFolders, sonarrSettings.RootPath, new int[] {1,2,3,4,5,6,7,8,9,10,11,12,13}, sonarrSettings.ApiKey,
|
||||
sonarrSettings.SeasonFolders, rootFolderPath, new int[] {1,2,3,4,5,6,7,8,9,10,11,12,13}, sonarrSettings.ApiKey,
|
||||
sonarrSettings.FullUri);
|
||||
|
||||
await Task.Delay(TimeSpan.FromSeconds(1));
|
||||
|
@ -372,5 +377,20 @@ namespace Ombi.Core
|
|||
|
||||
return selectedSeries;
|
||||
}
|
||||
|
||||
private async Task<string> GetRootPath(int pathId, SonarrSettings sonarrSettings)
|
||||
{
|
||||
var rootFoldersResult = await Cache.GetOrSetAsync(CacheKeys.SonarrRootFolders, async () =>
|
||||
{
|
||||
return await Task.Run(() => SonarrApi.GetRootFolders(sonarrSettings.ApiKey, sonarrSettings.FullUri));
|
||||
});
|
||||
|
||||
foreach (var r in rootFoldersResult.Where(r => r.id == pathId))
|
||||
{
|
||||
return r.path;
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -34,19 +34,22 @@ using Ombi.Api.Interfaces;
|
|||
using Ombi.Api.Models.SickRage;
|
||||
using Ombi.Api.Models.Sonarr;
|
||||
using Ombi.Core.SettingModels;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Store;
|
||||
|
||||
namespace Ombi.Core
|
||||
{
|
||||
public class TvSenderOld
|
||||
{
|
||||
public TvSenderOld(ISonarrApi sonarrApi, ISickRageApi srApi)
|
||||
public TvSenderOld(ISonarrApi sonarrApi, ISickRageApi srApi, ICacheProvider cache)
|
||||
{
|
||||
SonarrApi = sonarrApi;
|
||||
SickrageApi = srApi;
|
||||
Cache = cache;
|
||||
}
|
||||
private ISonarrApi SonarrApi { get; }
|
||||
private ISickRageApi SickrageApi { get; }
|
||||
private ICacheProvider Cache { get; }
|
||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
public async Task<SonarrAddSeries> SendToSonarr(SonarrSettings sonarrSettings, RequestedModel model)
|
||||
|
@ -67,6 +70,8 @@ namespace Ombi.Core
|
|||
{
|
||||
int.TryParse(sonarrSettings.QualityProfile, out qualityProfile);
|
||||
}
|
||||
var rootFolderPath = model.RootFolderSelected <= 0 ? sonarrSettings.FullRootPath : await GetRootPath(model.RootFolderSelected, sonarrSettings);
|
||||
|
||||
|
||||
var series = await GetSonarrSeries(sonarrSettings, model.ProviderId);
|
||||
|
||||
|
@ -84,7 +89,7 @@ namespace Ombi.Core
|
|||
|
||||
// Series doesn't exist, need to add it as unmonitored.
|
||||
var addResult = await Task.Run(() => SonarrApi.AddSeries(model.ProviderId, model.Title, qualityProfile,
|
||||
sonarrSettings.SeasonFolders, sonarrSettings.RootPath, 0, new int[0], sonarrSettings.ApiKey,
|
||||
sonarrSettings.SeasonFolders, rootFolderPath, 0, new int[0], sonarrSettings.ApiKey,
|
||||
sonarrSettings.FullUri, false));
|
||||
|
||||
|
||||
|
@ -156,7 +161,7 @@ namespace Ombi.Core
|
|||
|
||||
|
||||
var result = SonarrApi.AddSeries(model.ProviderId, model.Title, qualityProfile,
|
||||
sonarrSettings.SeasonFolders, sonarrSettings.RootPath, model.SeasonCount, model.SeasonList, sonarrSettings.ApiKey,
|
||||
sonarrSettings.SeasonFolders, rootFolderPath, model.SeasonCount, model.SeasonList, sonarrSettings.ApiKey,
|
||||
sonarrSettings.FullUri, true, true);
|
||||
|
||||
return result;
|
||||
|
@ -298,5 +303,20 @@ namespace Ombi.Core
|
|||
|
||||
return selectedSeries;
|
||||
}
|
||||
|
||||
|
||||
private async Task<string> GetRootPath(int pathId, SonarrSettings sonarrSettings)
|
||||
{
|
||||
var rootFoldersResult = await Cache.GetOrSetAsync(CacheKeys.SonarrRootFolders, async () =>
|
||||
{
|
||||
return await Task.Run(() => SonarrApi.GetRootFolders(sonarrSettings.ApiKey, sonarrSettings.FullUri));
|
||||
});
|
||||
|
||||
foreach (var r in rootFoldersResult.Where(r => r.id == pathId))
|
||||
{
|
||||
return r.path;
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,6 +25,7 @@
|
|||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Helpers.Permissions;
|
||||
|
||||
|
@ -38,5 +39,8 @@ namespace Ombi.Core.Users
|
|||
public Features Features { get; set; }
|
||||
public string EmailAddress { get; set; }
|
||||
public UserType Type { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public string UsernameOrAlias => string.IsNullOrEmpty(UserAlias) ? Username : UserAlias;
|
||||
}
|
||||
}
|
|
@ -46,5 +46,12 @@ namespace Ombi.Helpers
|
|||
dtDateTime = dtDateTime.AddSeconds(unixTimeStamp).ToLocalTime();
|
||||
return dtDateTime;
|
||||
}
|
||||
|
||||
public static long ToJavascriptTimestamp(this DateTime input)
|
||||
{
|
||||
var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
var time = input.Subtract(new TimeSpan(epoch.Ticks));
|
||||
return (long)(time.Ticks / 10000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ namespace Ombi.Services.Interfaces
|
|||
{
|
||||
public interface IAvailabilityChecker
|
||||
{
|
||||
void Start();
|
||||
void CheckAndUpdateAll();
|
||||
IEnumerable<PlexContent> GetPlexMovies(IEnumerable<PlexContent> content);
|
||||
bool IsMovieAvailable(PlexContent[] plexMovies, string title, string year, string providerId = null);
|
||||
|
|
11
Ombi.Services/Interfaces/IRadarrCacher.cs
Normal file
11
Ombi.Services/Interfaces/IRadarrCacher.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
using System.Collections.Generic;
|
||||
using Ombi.Services.Models;
|
||||
|
||||
namespace Ombi.Services.Interfaces
|
||||
{
|
||||
public interface IRadarrCacher
|
||||
{
|
||||
void Queued();
|
||||
int[] QueuedIds();
|
||||
}
|
||||
}
|
|
@ -6,5 +6,6 @@ namespace Ombi.Services.Jobs
|
|||
{
|
||||
void Execute(IJobExecutionContext context);
|
||||
void Test();
|
||||
void Start();
|
||||
}
|
||||
}
|
10
Ombi.Services/Interfaces/IStoreBackup.cs
Normal file
10
Ombi.Services/Interfaces/IStoreBackup.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using Quartz;
|
||||
|
||||
namespace Ombi.Services.Jobs
|
||||
{
|
||||
public interface IStoreBackup
|
||||
{
|
||||
void Start();
|
||||
void Execute(IJobExecutionContext context);
|
||||
}
|
||||
}
|
10
Ombi.Services/Interfaces/IStoreCleanup.cs
Normal file
10
Ombi.Services/Interfaces/IStoreCleanup.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using Quartz;
|
||||
|
||||
namespace Ombi.Services.Jobs
|
||||
{
|
||||
public interface IStoreCleanup
|
||||
{
|
||||
void Execute(IJobExecutionContext context);
|
||||
void Start();
|
||||
}
|
||||
}
|
16
Ombi.Services/Interfaces/IUserRequestLimitResetter.cs
Normal file
16
Ombi.Services/Interfaces/IUserRequestLimitResetter.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using System.Collections.Generic;
|
||||
using Ombi.Core.SettingModels;
|
||||
using Ombi.Store.Models;
|
||||
using Quartz;
|
||||
|
||||
namespace Ombi.Services.Jobs
|
||||
{
|
||||
public interface IUserRequestLimitResetter
|
||||
{
|
||||
void AlbumLimit(PlexRequestSettings s, IEnumerable<RequestLimit> allUsers);
|
||||
void Execute(IJobExecutionContext context);
|
||||
void MovieLimit(PlexRequestSettings s, IEnumerable<RequestLimit> allUsers);
|
||||
void Start();
|
||||
void TvLimit(PlexRequestSettings s, IEnumerable<RequestLimit> allUsers);
|
||||
}
|
||||
}
|
|
@ -45,7 +45,7 @@ using Quartz;
|
|||
|
||||
namespace Ombi.Services.Jobs
|
||||
{
|
||||
public class FaultQueueHandler : IJob
|
||||
public class FaultQueueHandler : IJob, IFaultQueueHandler
|
||||
{
|
||||
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
|
@ -53,7 +53,7 @@ namespace Ombi.Services.Jobs
|
|||
ISickRageApi srApi, ISettingsService<SonarrSettings> sonarrSettings, ISettingsService<SickRageSettings> srSettings,
|
||||
ICouchPotatoApi cpApi, ISettingsService<CouchPotatoSettings> cpsettings, IRequestService requestService,
|
||||
ISettingsService<HeadphonesSettings> hpSettings, IHeadphonesApi headphonesApi, ISettingsService<PlexRequestSettings> prSettings,
|
||||
ISecurityExtensions security, IMovieSender movieSender)
|
||||
ISecurityExtensions security, IMovieSender movieSender, ICacheProvider cache)
|
||||
{
|
||||
Record = record;
|
||||
Repo = repo;
|
||||
|
@ -71,6 +71,8 @@ namespace Ombi.Services.Jobs
|
|||
Security = security;
|
||||
PrSettings = prSettings.GetSettings();
|
||||
MovieSender = movieSender;
|
||||
|
||||
Cache = cache;
|
||||
}
|
||||
|
||||
private IMovieSender MovieSender { get; }
|
||||
|
@ -78,6 +80,7 @@ namespace Ombi.Services.Jobs
|
|||
private IJobRecord Record { get; }
|
||||
private ISonarrApi SonarrApi { get; }
|
||||
private ISickRageApi SrApi { get; }
|
||||
private ICacheProvider Cache { get; }
|
||||
private ICouchPotatoApi CpApi { get; }
|
||||
private IHeadphonesApi HpApi { get; }
|
||||
private IRequestService RequestService { get; }
|
||||
|
@ -88,9 +91,8 @@ namespace Ombi.Services.Jobs
|
|||
private ISettingsService<HeadphonesSettings> HeadphoneSettings { get; }
|
||||
private ISecurityExtensions Security { get; }
|
||||
|
||||
public void Execute(IJobExecutionContext context)
|
||||
public void Start()
|
||||
{
|
||||
|
||||
Record.SetRunning(true, JobNames.CpCacher);
|
||||
try
|
||||
{
|
||||
|
@ -113,6 +115,11 @@ namespace Ombi.Services.Jobs
|
|||
Record.SetRunning(false, JobNames.CpCacher);
|
||||
}
|
||||
}
|
||||
public void Execute(IJobExecutionContext context)
|
||||
{
|
||||
|
||||
Start();
|
||||
}
|
||||
|
||||
|
||||
private void ProcessMissingInformation(List<RequestQueue> requests)
|
||||
|
@ -163,7 +170,7 @@ namespace Ombi.Services.Jobs
|
|||
try
|
||||
{
|
||||
|
||||
var sender = new TvSenderOld(SonarrApi, SrApi);
|
||||
var sender = new TvSenderOld(SonarrApi, SrApi, Cache);
|
||||
if (sonarr.Enabled)
|
||||
{
|
||||
var task = sender.SendToSonarr(sonarr, tvModel, sonarr.QualityProfile);
|
||||
|
|
14
Ombi.Services/Jobs/IFaultQueueHandler.cs
Normal file
14
Ombi.Services/Jobs/IFaultQueueHandler.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using System.Collections.Generic;
|
||||
using Ombi.Core.SettingModels;
|
||||
using Ombi.Store;
|
||||
using Quartz;
|
||||
|
||||
namespace Ombi.Services.Jobs
|
||||
{
|
||||
public interface IFaultQueueHandler
|
||||
{
|
||||
void Execute(IJobExecutionContext context);
|
||||
bool ShouldAutoApprove(RequestType requestType, PlexRequestSettings prSettings, List<string> username);
|
||||
void Start();
|
||||
}
|
||||
}
|
12
Ombi.Services/Jobs/IPlexEpisodeCacher.cs
Normal file
12
Ombi.Services/Jobs/IPlexEpisodeCacher.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
using Ombi.Core.SettingModels;
|
||||
using Quartz;
|
||||
|
||||
namespace Ombi.Services.Jobs
|
||||
{
|
||||
public interface IPlexEpisodeCacher
|
||||
{
|
||||
void CacheEpisodes(PlexSettings settings);
|
||||
void Execute(IJobExecutionContext context);
|
||||
void Start();
|
||||
}
|
||||
}
|
10
Ombi.Services/Jobs/IPlexUserChecker.cs
Normal file
10
Ombi.Services/Jobs/IPlexUserChecker.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using Quartz;
|
||||
|
||||
namespace Ombi.Services.Jobs
|
||||
{
|
||||
public interface IPlexUserChecker
|
||||
{
|
||||
void Execute(IJobExecutionContext context);
|
||||
void Start();
|
||||
}
|
||||
}
|
|
@ -32,6 +32,7 @@ namespace Ombi.Services.Jobs
|
|||
public const string CpCacher = "CouchPotato Cacher";
|
||||
public const string WatcherCacher = "Watcher Cacher";
|
||||
public const string SonarrCacher = "Sonarr Cacher";
|
||||
public const string RadarrCacher = "Radarr Cacher";
|
||||
public const string SrCacher = "SickRage Cacher";
|
||||
public const string PlexChecker = "Plex Availability Cacher";
|
||||
public const string PlexCacher = "Plex Cacher";
|
||||
|
|
|
@ -79,6 +79,7 @@ namespace Ombi.Services.Jobs
|
|||
|
||||
public void CheckAndUpdateAll()
|
||||
{
|
||||
|
||||
var plexSettings = Plex.GetSettings();
|
||||
|
||||
if (!ValidateSettings(plexSettings))
|
||||
|
@ -472,5 +473,23 @@ namespace Ombi.Services.Jobs
|
|||
Job.SetRunning(false, JobNames.PlexChecker);
|
||||
}
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
Job.SetRunning(true, JobNames.PlexChecker);
|
||||
try
|
||||
{
|
||||
CheckAndUpdateAll();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Job.Record(JobNames.PlexChecker);
|
||||
Job.SetRunning(false, JobNames.PlexChecker);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -43,7 +43,7 @@ using Quartz;
|
|||
|
||||
namespace Ombi.Services.Jobs
|
||||
{
|
||||
public class PlexEpisodeCacher : IJob
|
||||
public class PlexEpisodeCacher : IJob, IPlexEpisodeCacher
|
||||
{
|
||||
public PlexEpisodeCacher(ISettingsService<PlexSettings> plexSettings, IPlexApi plex, ICacheProvider cache,
|
||||
IJobRecord rec, IRepository<PlexEpisodes> repo, ISettingsService<ScheduledJobsSettings> jobs)
|
||||
|
@ -140,6 +140,38 @@ namespace Ombi.Services.Jobs
|
|||
}
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
try
|
||||
{
|
||||
var s = Plex.GetSettings();
|
||||
if (!s.EnableTvEpisodeSearching)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var jobs = Job.GetJobs();
|
||||
var job = jobs.FirstOrDefault(x => x.Name.Equals(JobNames.EpisodeCacher, StringComparison.CurrentCultureIgnoreCase));
|
||||
if (job != null)
|
||||
{
|
||||
if (job.LastRun > DateTime.Now.AddHours(-11)) // If it's been run in the last 11 hours
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
Job.SetRunning(true, JobNames.EpisodeCacher);
|
||||
CacheEpisodes(s);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Job.Record(JobNames.EpisodeCacher);
|
||||
Job.SetRunning(false, JobNames.EpisodeCacher);
|
||||
}
|
||||
}
|
||||
public void Execute(IJobExecutionContext context)
|
||||
{
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ using Quartz;
|
|||
|
||||
namespace Ombi.Services.Jobs
|
||||
{
|
||||
public class PlexUserChecker : IJob
|
||||
public class PlexUserChecker : IJob, IPlexUserChecker
|
||||
{
|
||||
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
|
@ -68,7 +68,7 @@ namespace Ombi.Services.Jobs
|
|||
private IRequestService RequestService { get; }
|
||||
private IUserRepository LocalUserRepository { get; }
|
||||
|
||||
public void Execute(IJobExecutionContext context)
|
||||
public void Start()
|
||||
{
|
||||
JobRecord.SetRunning(true, JobNames.PlexUserChecker);
|
||||
|
||||
|
@ -153,7 +153,7 @@ namespace Ombi.Services.Jobs
|
|||
}
|
||||
|
||||
// Looks like it's a new user!
|
||||
var m = new PlexUsers
|
||||
var m = new PlexUsers
|
||||
{
|
||||
PlexUserId = user.Id,
|
||||
Permissions = UserManagementHelper.GetPermissions(userManagementSettings),
|
||||
|
@ -170,7 +170,7 @@ namespace Ombi.Services.Jobs
|
|||
// Main Plex user
|
||||
var dbMainAcc = dbUsers.FirstOrDefault(x => x.Username.Equals(mainPlexAccount.Username, StringComparison.CurrentCulture));
|
||||
var localMainAcc = localUsers.FirstOrDefault(x => x.UserName.Equals(mainPlexAccount.Username, StringComparison.CurrentCulture));
|
||||
|
||||
|
||||
// TODO if admin acc does exist, check if we need to update it
|
||||
|
||||
|
||||
|
@ -188,7 +188,7 @@ namespace Ombi.Services.Jobs
|
|||
LoginId = Guid.NewGuid().ToString()
|
||||
};
|
||||
|
||||
a.Permissions += (int) Permissions.Administrator; // Make admin
|
||||
a.Permissions += (int)Permissions.Administrator; // Make admin
|
||||
|
||||
Repo.Insert(a);
|
||||
}
|
||||
|
@ -205,5 +205,9 @@ namespace Ombi.Services.Jobs
|
|||
JobRecord.Record(JobNames.PlexUserChecker);
|
||||
}
|
||||
}
|
||||
public void Execute(IJobExecutionContext context)
|
||||
{
|
||||
Start();
|
||||
}
|
||||
}
|
||||
}
|
110
Ombi.Services/Jobs/RadarrCacher.cs
Normal file
110
Ombi.Services/Jobs/RadarrCacher.cs
Normal file
|
@ -0,0 +1,110 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: PlexAvailabilityChecker.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using Ombi.Api.Interfaces;
|
||||
using Ombi.Api.Models.Radarr;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.SettingModels;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Services.Interfaces;
|
||||
using Quartz;
|
||||
|
||||
namespace Ombi.Services.Jobs
|
||||
{
|
||||
public class RadarrCacher : IJob, IRadarrCacher
|
||||
{
|
||||
public RadarrCacher(ISettingsService<RadarrSettings> radarrService, IRadarrApi radarrApi, ICacheProvider cache, IJobRecord rec)
|
||||
{
|
||||
RadarrSettings = radarrService;
|
||||
RadarrApi = radarrApi;
|
||||
Job = rec;
|
||||
Cache = cache;
|
||||
}
|
||||
|
||||
private ISettingsService<RadarrSettings> RadarrSettings { get; }
|
||||
private ICacheProvider Cache { get; }
|
||||
private IRadarrApi RadarrApi { get; }
|
||||
private IJobRecord Job { get; }
|
||||
|
||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
public void Queued()
|
||||
{
|
||||
var settings = RadarrSettings.GetSettings();
|
||||
if (settings.Enabled)
|
||||
{
|
||||
Job.SetRunning(true, JobNames.RadarrCacher);
|
||||
try
|
||||
{
|
||||
var movies = RadarrApi.GetMovies(settings.ApiKey, settings.FullUri);
|
||||
if (movies != null)
|
||||
{
|
||||
var movieIds = new List<int>();
|
||||
foreach (var m in movies)
|
||||
{
|
||||
if (m.tmdbId > 0)
|
||||
{
|
||||
movieIds.Add(m.tmdbId);
|
||||
}
|
||||
}
|
||||
//var movieIds = movies.Select(x => x.tmdbId).ToList();
|
||||
Cache.Set(CacheKeys.RadarrMovies, movieIds, CacheKeys.TimeFrameMinutes.SchedulerCaching);
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Failed caching queued items from Radarr");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Job.Record(JobNames.RadarrCacher);
|
||||
Job.SetRunning(false, JobNames.RadarrCacher);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we do not want to set here...
|
||||
public int[] QueuedIds()
|
||||
{
|
||||
var retVal = new List<int>();
|
||||
var movies = Cache.Get<List<int>>(CacheKeys.RadarrMovies);
|
||||
if (movies != null)
|
||||
{
|
||||
retVal.AddRange(movies);
|
||||
}
|
||||
return retVal.ToArray();
|
||||
}
|
||||
|
||||
public void Execute(IJobExecutionContext context)
|
||||
{
|
||||
Queued();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -78,7 +78,7 @@ namespace Ombi.Services.Jobs
|
|||
|
||||
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
public void Execute(IJobExecutionContext context)
|
||||
public void Start()
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -100,6 +100,10 @@ namespace Ombi.Services.Jobs
|
|||
JobRecord.SetRunning(false, JobNames.RecentlyAddedEmail);
|
||||
}
|
||||
}
|
||||
public void Execute(IJobExecutionContext context)
|
||||
{
|
||||
Start();
|
||||
}
|
||||
|
||||
public void Test()
|
||||
{
|
||||
|
@ -455,7 +459,7 @@ namespace Ombi.Services.Jobs
|
|||
|
||||
if (!testEmail)
|
||||
{
|
||||
var users = UserHelper.GetUsersWithFeature(Features.RequestAddedNotification);
|
||||
var users = UserHelper.GetUsersWithFeature(Features.Newsletter);
|
||||
if (users != null)
|
||||
{
|
||||
foreach (var user in users)
|
||||
|
|
|
@ -35,7 +35,7 @@ using Quartz;
|
|||
|
||||
namespace Ombi.Services.Jobs
|
||||
{
|
||||
public class StoreBackup : IJob
|
||||
public class StoreBackup : IJob, IStoreBackup
|
||||
{
|
||||
public StoreBackup(ISqliteConfiguration sql, IJobRecord rec)
|
||||
{
|
||||
|
@ -48,6 +48,13 @@ namespace Ombi.Services.Jobs
|
|||
|
||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
public void Start()
|
||||
{
|
||||
JobRecord.SetRunning(true, JobNames.CpCacher);
|
||||
TakeBackup();
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
public void Execute(IJobExecutionContext context)
|
||||
{
|
||||
JobRecord.SetRunning(true, JobNames.CpCacher);
|
||||
|
|
|
@ -36,7 +36,7 @@ using Quartz;
|
|||
|
||||
namespace Ombi.Services.Jobs
|
||||
{
|
||||
public class StoreCleanup : IJob
|
||||
public class StoreCleanup : IJob, IStoreCleanup
|
||||
{
|
||||
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
|
@ -81,6 +81,11 @@ namespace Ombi.Services.Jobs
|
|||
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
JobRecord.SetRunning(true, JobNames.CpCacher);
|
||||
Cleanup();
|
||||
}
|
||||
public void Execute(IJobExecutionContext context)
|
||||
{
|
||||
JobRecord.SetRunning(true, JobNames.CpCacher);
|
||||
|
|
|
@ -39,7 +39,7 @@ using Quartz;
|
|||
|
||||
namespace Ombi.Services.Jobs
|
||||
{
|
||||
public class UserRequestLimitResetter : IJob
|
||||
public class UserRequestLimitResetter : IJob, IUserRequestLimitResetter
|
||||
{
|
||||
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
|
@ -94,6 +94,31 @@ namespace Ombi.Services.Jobs
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public void Start()
|
||||
{
|
||||
Record.SetRunning(true, JobNames.CpCacher);
|
||||
try
|
||||
{
|
||||
var settings = Settings.GetSettings();
|
||||
var users = Repo.GetAll();
|
||||
var requestLimits = users as RequestLimit[] ?? users.ToArray();
|
||||
|
||||
MovieLimit(settings, requestLimits);
|
||||
TvLimit(settings, requestLimits);
|
||||
AlbumLimit(settings, requestLimits);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Record.Record(JobNames.RequestLimitReset);
|
||||
Record.SetRunning(false, JobNames.CpCacher);
|
||||
}
|
||||
}
|
||||
|
||||
public void Execute(IJobExecutionContext context)
|
||||
{
|
||||
Record.SetRunning(true, JobNames.CpCacher);
|
||||
|
|
165
Ombi.Services/Notification/DiscordNotification.cs
Normal file
165
Ombi.Services/Notification/DiscordNotification.cs
Normal file
|
@ -0,0 +1,165 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: SlackNotification.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using NLog;
|
||||
using Ombi.Api.Interfaces;
|
||||
using Ombi.Api.Models.Notifications;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.Models;
|
||||
using Ombi.Core.SettingModels;
|
||||
using Ombi.Services.Interfaces;
|
||||
|
||||
namespace Ombi.Services.Notification
|
||||
{
|
||||
public class DiscordNotification : INotification
|
||||
{
|
||||
public DiscordNotification(IDiscordApi api, ISettingsService<DiscordNotificationSettings> sn)
|
||||
{
|
||||
Api = api;
|
||||
Settings = sn;
|
||||
}
|
||||
|
||||
public string NotificationName => "DiscordNotification";
|
||||
|
||||
private IDiscordApi Api { get; }
|
||||
private ISettingsService<DiscordNotificationSettings> Settings { get; }
|
||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
|
||||
public async Task NotifyAsync(NotificationModel model)
|
||||
{
|
||||
var settings = Settings.GetSettings();
|
||||
|
||||
await NotifyAsync(model, settings);
|
||||
}
|
||||
|
||||
public async Task NotifyAsync(NotificationModel model, Settings settings)
|
||||
{
|
||||
if (settings == null) await NotifyAsync(model);
|
||||
|
||||
var pushSettings = (DiscordNotificationSettings)settings;
|
||||
if (!ValidateConfiguration(pushSettings))
|
||||
{
|
||||
Log.Error("Settings for Slack was not correct, we cannot push a notification");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (model.NotificationType)
|
||||
{
|
||||
case NotificationType.NewRequest:
|
||||
await PushNewRequestAsync(model, pushSettings);
|
||||
break;
|
||||
case NotificationType.Issue:
|
||||
await PushIssueAsync(model, pushSettings);
|
||||
break;
|
||||
case NotificationType.RequestAvailable:
|
||||
break;
|
||||
case NotificationType.RequestApproved:
|
||||
break;
|
||||
case NotificationType.AdminNote:
|
||||
break;
|
||||
case NotificationType.Test:
|
||||
await PushTest(pushSettings);
|
||||
break;
|
||||
case NotificationType.RequestDeclined:
|
||||
await PushRequestDeclinedAsync(model, pushSettings);
|
||||
break;
|
||||
case NotificationType.ItemAddedToFaultQueue:
|
||||
await PushFaultQueue(model, pushSettings);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task PushNewRequestAsync(NotificationModel model, DiscordNotificationSettings settings)
|
||||
{
|
||||
var message = $"{model.Title} has been requested by user: {model.User}";
|
||||
await Push(settings, message);
|
||||
}
|
||||
|
||||
private async Task PushRequestDeclinedAsync(NotificationModel model, DiscordNotificationSettings settings)
|
||||
{
|
||||
var message = $"Hello! Your request for {model.Title} has been declined, Sorry!";
|
||||
await Push(settings, message);
|
||||
}
|
||||
|
||||
private async Task PushIssueAsync(NotificationModel model, DiscordNotificationSettings settings)
|
||||
{
|
||||
var message = $"A new issue: {model.Body} has been reported by user: {model.User} for the title: {model.Title}";
|
||||
await Push(settings, message);
|
||||
}
|
||||
|
||||
private async Task PushTest(DiscordNotificationSettings settings)
|
||||
{
|
||||
var message = $"This is a test from Ombi, if you can see this then we have successfully pushed a notification!";
|
||||
await Push(settings, message);
|
||||
}
|
||||
|
||||
private async Task PushFaultQueue(NotificationModel model, DiscordNotificationSettings settings)
|
||||
{
|
||||
var message = $"Hello! The user '{model.User}' has requested {model.Title} but it could not be added. This has been added into the requests queue and will keep retrying";
|
||||
await Push(settings, message);
|
||||
}
|
||||
|
||||
private async Task Push(DiscordNotificationSettings config, string message)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Api.SendMessageAsync(message, config.WebookId, config.Token, config.Username);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
private bool ValidateConfiguration(DiscordNotificationSettings settings)
|
||||
{
|
||||
if (!settings.Enabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (string.IsNullOrEmpty(settings.WebhookUrl))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
var a = settings.Token;
|
||||
var b = settings.WebookId;
|
||||
}
|
||||
catch (IndexOutOfRangeException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -119,14 +119,6 @@ namespace Ombi.Services.Notification
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!settings.EnableUserEmailNotifications)
|
||||
{
|
||||
if (!settings.Enabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -237,16 +229,12 @@ namespace Ombi.Services.Notification
|
|||
|
||||
private async Task EmailAvailableRequest(NotificationModel model, EmailNotificationSettings settings)
|
||||
{
|
||||
if (!settings.EnableUserEmailNotifications)
|
||||
{
|
||||
await Task.FromResult(false);
|
||||
}
|
||||
var email = new EmailBasicTemplate();
|
||||
var html = email.LoadTemplate(
|
||||
$"Ombi: {model.Title} is now available!",
|
||||
$"Hello! You requested {model.Title} on PlexRequests! This is now available on Plex! :)",
|
||||
$"Hello! You requested {model.Title} on Ombi! This is now available on Plex! :)",
|
||||
model.ImgSrc);
|
||||
var body = new BodyBuilder { HtmlBody = html, TextBody = $"Hello! You requested {model.Title} on PlexRequests! This is now available on Plex! :)" };
|
||||
var body = new BodyBuilder { HtmlBody = html, TextBody = $"Hello! You requested {model.Title} on Ombi! This is now available on Plex! :)" };
|
||||
|
||||
var message = new MimeMessage
|
||||
{
|
||||
|
|
|
@ -98,10 +98,31 @@ namespace Ombi.Services.Notification
|
|||
return;
|
||||
}
|
||||
|
||||
var localUser =
|
||||
users.FirstOrDefault( x =>
|
||||
x.Username.Equals(user, StringComparison.CurrentCultureIgnoreCase) ||
|
||||
x.UserAlias.Equals(user, StringComparison.CurrentCultureIgnoreCase));
|
||||
UserHelperModel localUser = null;
|
||||
//users.FirstOrDefault( x =>
|
||||
// x.Username.Equals(user, StringComparison.CurrentCultureIgnoreCase) ||
|
||||
// x.UserAlias.Equals(user, StringComparison.CurrentCultureIgnoreCase));
|
||||
|
||||
foreach (var userHelperModel in users)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(userHelperModel.Username))
|
||||
{
|
||||
if (userHelperModel.Username.Equals(user, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
localUser = userHelperModel;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!string.IsNullOrEmpty(userHelperModel.UserAlias))
|
||||
{
|
||||
if (userHelperModel.UserAlias.Equals(user, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
localUser = userHelperModel;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// So if the request was from an alias, then we need to use the local user (since that contains the alias).
|
||||
// If we do not have a local user, then we should be using the Plex user if that user exists.
|
||||
|
@ -152,8 +173,36 @@ namespace Ombi.Services.Notification
|
|||
var users = UserHelper.GetUsersWithFeature(Features.RequestAddedNotification).ToList();
|
||||
Log.Debug("Notifying Users Count {0}", users.Count);
|
||||
|
||||
var selectedUsers = users.Select(x => x.Username).Intersect(model.RequestedUsers, StringComparer.CurrentCultureIgnoreCase);
|
||||
foreach (var user in selectedUsers)
|
||||
// Get the usernames or alias depending if they have an alias
|
||||
var userNamesWithFeature = users.Select(x => x.UsernameOrAlias).ToList();
|
||||
Log.Debug("Users with the feature count {0}", userNamesWithFeature.Count);
|
||||
Log.Debug("Usernames: ");
|
||||
foreach (var u in userNamesWithFeature)
|
||||
{
|
||||
Log.Debug(u);
|
||||
}
|
||||
|
||||
Log.Debug("Users in the requested model count: {0}", model.AllUsers.Count);
|
||||
Log.Debug("usernames from model: ");
|
||||
foreach (var modelAllUser in model.AllUsers)
|
||||
{
|
||||
Log.Debug(modelAllUser);
|
||||
}
|
||||
|
||||
if (model.AllUsers == null || !model.AllUsers.Any())
|
||||
{
|
||||
Log.Debug("There are no users in the model.AllUsers, no users to notify");
|
||||
return;
|
||||
}
|
||||
var usersToNotify = userNamesWithFeature.Intersect(model.AllUsers, StringComparer.CurrentCultureIgnoreCase).ToList();
|
||||
|
||||
if (!usersToNotify.Any())
|
||||
{
|
||||
Log.Debug("Could not find any users after the .Intersect()");
|
||||
}
|
||||
|
||||
Log.Debug("Users being notified for this request count {0}", users.Count);
|
||||
foreach (var user in usersToNotify)
|
||||
{
|
||||
Log.Info("Notifying user {0}", user);
|
||||
if (user.Equals(adminUsername, StringComparison.CurrentCultureIgnoreCase))
|
||||
|
|
|
@ -86,13 +86,21 @@
|
|||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Interfaces\IRadarrCacher.cs" />
|
||||
<Compile Include="Interfaces\IWatcherCacher.cs" />
|
||||
<Compile Include="Interfaces\IJobRecord.cs" />
|
||||
<Compile Include="Interfaces\INotificationEngine.cs" />
|
||||
<Compile Include="Interfaces\IStoreBackup.cs" />
|
||||
<Compile Include="Interfaces\IStoreCleanup.cs" />
|
||||
<Compile Include="Interfaces\IUserRequestLimitResetter.cs" />
|
||||
<Compile Include="Jobs\IFaultQueueHandler.cs" />
|
||||
<Compile Include="Jobs\IPlexEpisodeCacher.cs" />
|
||||
<Compile Include="Jobs\IPlexUserChecker.cs" />
|
||||
<Compile Include="Jobs\RadarrCacher.cs" />
|
||||
<Compile Include="Jobs\WatcherCacher.cs" />
|
||||
<Compile Include="Jobs\HtmlTemplateGenerator.cs" />
|
||||
<Compile Include="Jobs\IPlexContentCacher.cs" />
|
||||
<Compile Include="Jobs\IRecentlyAdded.cs" />
|
||||
<Compile Include="Interfaces\IPlexContentCacher.cs" />
|
||||
<Compile Include="Interfaces\IRecentlyAdded.cs" />
|
||||
<Compile Include="Jobs\JobRecord.cs" />
|
||||
<Compile Include="Jobs\JobNames.cs" />
|
||||
<Compile Include="Jobs\PlexContentCacher.cs" />
|
||||
|
@ -126,6 +134,7 @@
|
|||
<Compile Include="Notification\NotificationService.cs" />
|
||||
<Compile Include="Notification\PushoverNotification.cs" />
|
||||
<Compile Include="Notification\PushbulletNotification.cs" />
|
||||
<Compile Include="Notification\DiscordNotification.cs" />
|
||||
<Compile Include="Notification\SlackNotification.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#endregion
|
||||
|
||||
using Dapper.Contrib.Extensions;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Ombi.Store.Models
|
||||
{
|
||||
|
|
|
@ -41,7 +41,7 @@ namespace Ombi.Store.Models
|
|||
|
||||
public FaultType FaultType { get; set; }
|
||||
public DateTime? LastRetry { get; set; }
|
||||
public string Message { get; set; }
|
||||
public string Description { get; set; }
|
||||
}
|
||||
|
||||
public enum FaultType
|
||||
|
|
|
@ -27,9 +27,6 @@ namespace Ombi.Store
|
|||
public string Status { get; set; }
|
||||
public bool Approved { get; set; }
|
||||
|
||||
[Obsolete("Use RequestedUsers")] //TODO remove this obsolete property
|
||||
public string RequestedBy { get; set; }
|
||||
|
||||
public DateTime RequestedDate { get; set; }
|
||||
public bool Available { get; set; }
|
||||
public IssueState Issues { get; set; }
|
||||
|
@ -46,6 +43,13 @@ namespace Ombi.Store
|
|||
public List<EpisodesModel> Episodes { get; set; }
|
||||
public bool Denied { get; set; }
|
||||
public string DeniedReason { get; set; }
|
||||
/// <summary>
|
||||
/// For TV Shows with a custom root folder
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The root folder selected.
|
||||
/// </value>
|
||||
public int RootFolderSelected { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public List<string> AllUsers
|
||||
|
@ -53,14 +57,9 @@ namespace Ombi.Store
|
|||
get
|
||||
{
|
||||
var u = new List<string>();
|
||||
if (!string.IsNullOrEmpty(RequestedBy))
|
||||
if (RequestedUsers != null && RequestedUsers.Any())
|
||||
{
|
||||
u.Add(RequestedBy);
|
||||
}
|
||||
|
||||
if (RequestedUsers.Any())
|
||||
{
|
||||
u.AddRange(RequestedUsers.Where(requestedUser => requestedUser != RequestedBy));
|
||||
u.AddRange(RequestedUsers);
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ using Ombi.Api.Interfaces;
|
|||
using Ombi.Api.Models.Sonarr;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.SettingModels;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Store;
|
||||
using Ploeh.AutoFixture;
|
||||
|
||||
|
@ -49,6 +50,7 @@ namespace Ombi.UI.Tests
|
|||
|
||||
private TvSender Sender { get; set; }
|
||||
private Fixture F { get; set; }
|
||||
private Mock<ICacheProvider> Cache { get; set; }
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
|
@ -56,7 +58,8 @@ namespace Ombi.UI.Tests
|
|||
F = new Fixture();
|
||||
SonarrMock = new Mock<ISonarrApi>();
|
||||
SickrageMock = new Mock<ISickRageApi>();
|
||||
Sender = new TvSender(SonarrMock.Object, SickrageMock.Object);
|
||||
Cache = new Mock<ICacheProvider>();
|
||||
Sender = new TvSender(SonarrMock.Object, SickrageMock.Object, Cache.Object);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -66,7 +69,7 @@ namespace Ombi.UI.Tests
|
|||
var seriesResult = new SonarrAddSeries() { title = "ABC"};
|
||||
SonarrMock.Setup(x => x.GetSeries(It.IsAny<string>(), It.IsAny<Uri>())).Returns(F.Build<Series>().With(x => x.tvdbId, 1).With(x => x.title, "ABC").CreateMany().ToList());
|
||||
|
||||
Sender = new TvSender(SonarrMock.Object, SickrageMock.Object);
|
||||
Sender = new TvSender(SonarrMock.Object, SickrageMock.Object, Cache.Object);
|
||||
|
||||
var request = new RequestedModel {SeasonsRequested = "All", ProviderId = 1, Title = "ABC"};
|
||||
|
||||
|
@ -116,7 +119,7 @@ namespace Ombi.UI.Tests
|
|||
SonarrMock.Setup(x => x.GetEpisodes(It.IsAny<string>(), It.IsAny<string>(),
|
||||
It.IsAny<Uri>())).Returns(F.CreateMany<SonarrEpisodes>());
|
||||
|
||||
Sender = new TvSender(SonarrMock.Object, SickrageMock.Object);
|
||||
Sender = new TvSender(SonarrMock.Object, SickrageMock.Object, Cache.Object);
|
||||
var episodes = new List<EpisodesModel>
|
||||
{
|
||||
new EpisodesModel
|
||||
|
|
8
Ombi.UI/Content/base.css
vendored
8
Ombi.UI/Content/base.css
vendored
|
@ -509,3 +509,11 @@ label {
|
|||
background-position: center;
|
||||
position: absolute; }
|
||||
|
||||
.list-group-item-dropdown {
|
||||
position: relative;
|
||||
display: block;
|
||||
padding: 10px 15px;
|
||||
margin-bottom: -1px;
|
||||
background-color: #3e3e3e;
|
||||
border: 1px solid transparent; }
|
||||
|
||||
|
|
2
Ombi.UI/Content/base.min.css
vendored
2
Ombi.UI/Content/base.min.css
vendored
File diff suppressed because one or more lines are too long
|
@ -632,3 +632,13 @@ $border-radius: 10px;
|
|||
background-position:center;
|
||||
position:absolute;
|
||||
}
|
||||
|
||||
|
||||
.list-group-item-dropdown {
|
||||
position: relative;
|
||||
display: block;
|
||||
padding: 10px 15px;
|
||||
margin-bottom: -1px;
|
||||
background-color: #3e3e3e;
|
||||
border: 1px solid transparent;
|
||||
}
|
1
Ombi.UI/Content/bootstrap.css
vendored
1
Ombi.UI/Content/bootstrap.css
vendored
|
@ -5259,6 +5259,7 @@ a.thumbnail.active {
|
|||
background-color: #4e5d6c;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.list-group-item:first-child {
|
||||
border-top-right-radius: 0;
|
||||
border-top-left-radius: 0;
|
||||
|
|
53
Ombi.UI/Content/requests.js
vendored
53
Ombi.UI/Content/requests.js
vendored
|
@ -559,6 +559,25 @@ $(document).on("click", ".approve-with-quality", function (e) {
|
|||
|
||||
});
|
||||
|
||||
// Change root folder
|
||||
$(document).on("click", ".change-root-folder", function (e) {
|
||||
e.preventDefault();
|
||||
var $this = $(this);
|
||||
var $button = $this.parents('.btn-split').children('.change').first();
|
||||
var rootFolderId = e.target.id
|
||||
var $form = $this.parents('form').first();
|
||||
|
||||
if ($button.text() === " Loading...") {
|
||||
return;
|
||||
}
|
||||
|
||||
loadingButton($button.attr('id'), "success");
|
||||
|
||||
changeRootFolder($form, rootFolderId, function () {
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
// Change Availability
|
||||
$(document).on("click", ".change", function (e) {
|
||||
|
@ -638,6 +657,37 @@ function approveRequest($form, qualityId, successCallback) {
|
|||
});
|
||||
}
|
||||
|
||||
function changeRootFolder($form, rootFolderId, successCallback) {
|
||||
|
||||
var formData = $form.serialize();
|
||||
if (rootFolderId) formData += ("&rootFolderId=" + rootFolderId);
|
||||
|
||||
$.ajax({
|
||||
type: $form.prop('method'),
|
||||
url: $form.prop('action'),
|
||||
data: formData,
|
||||
dataType: "json",
|
||||
success: function (response) {
|
||||
|
||||
if (checkJsonResponse(response)) {
|
||||
if (response.message) {
|
||||
generateNotify(response.message, "success");
|
||||
} else {
|
||||
generateNotify("Success! Changed Root Path.", "success");
|
||||
}
|
||||
|
||||
if (successCallback) {
|
||||
successCallback();
|
||||
}
|
||||
}
|
||||
},
|
||||
error: function (e) {
|
||||
console.log(e);
|
||||
generateNotify("Something went wrong!", "danger");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function denyRequest($form, successCallback) {
|
||||
|
||||
var formData = $form.serialize();
|
||||
|
@ -808,6 +858,9 @@ function buildRequestContext(result, type) {
|
|||
musicBrainzId: result.musicBrainzId,
|
||||
denied: result.denied,
|
||||
deniedReason: result.deniedReason,
|
||||
hasRootFolders: result.hasRootFolders,
|
||||
rootFolders: result.rootFolders,
|
||||
currentRootPath : result.currentRootPath
|
||||
};
|
||||
|
||||
return context;
|
||||
|
|
87
Ombi.UI/Content/search.js
vendored
87
Ombi.UI/Content/search.js
vendored
|
@ -24,7 +24,8 @@ Function.prototype.bind = function (parent) {
|
|||
|
||||
$(function () {
|
||||
|
||||
var searchSource = $("#search-template").html();
|
||||
var useNewSearch = $('#useNewSearch').text() == 'True';
|
||||
var searchSource = useNewSearch ? $("#search-templateNew").html() : $("#search-template").html();
|
||||
var seasonsSource = $("#seasons-template").html();
|
||||
var musicSource = $("#music-template").html();
|
||||
var seasonsNumberSource = $("#seasonNumber-template").html();
|
||||
|
@ -72,6 +73,25 @@ $(function () {
|
|||
moviesInTheaters();
|
||||
});
|
||||
|
||||
// TV DropDown
|
||||
$('#popularShows').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
popularShows();
|
||||
});
|
||||
|
||||
$('#trendingShows').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
trendingTv();
|
||||
});
|
||||
$('#mostWatchedShows').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
mostwatchedTv();
|
||||
});
|
||||
$('#anticipatedShows').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
anticipatedTv();
|
||||
});
|
||||
|
||||
// Type in TV search
|
||||
$("#tvSearchContent").on("input", function () {
|
||||
if (searchTimer) {
|
||||
|
@ -293,6 +313,23 @@ $(function () {
|
|||
getMovies(url);
|
||||
}
|
||||
|
||||
function popularShows() {
|
||||
var url = createBaseUrl(base, '/search/tv/popular');
|
||||
getTvShows(url, true);
|
||||
}
|
||||
function anticipatedTv() {
|
||||
var url = createBaseUrl(base, '/search/tv/anticipated');
|
||||
getTvShows(url, true);
|
||||
}
|
||||
function trendingTv() {
|
||||
var url = createBaseUrl(base, '/search/tv/trending');
|
||||
getTvShows(url, true);
|
||||
}
|
||||
function mostwatchedTv() {
|
||||
var url = createBaseUrl(base, '/search/tv/mostwatched');
|
||||
getTvShows(url, true);
|
||||
}
|
||||
|
||||
function getMovies(url) {
|
||||
resetMovies();
|
||||
|
||||
|
@ -304,6 +341,8 @@ $(function () {
|
|||
|
||||
var html = searchTemplate(context);
|
||||
$("#movieList").append(html);
|
||||
|
||||
checkNetflix(context.title, context.id);
|
||||
});
|
||||
}
|
||||
else {
|
||||
|
@ -321,10 +360,10 @@ $(function () {
|
|||
var query = $("#tvSearchContent").val();
|
||||
|
||||
var url = createBaseUrl(base, '/search/tv/');
|
||||
query ? getTvShows(url + query) : resetTvShows();
|
||||
query ? getTvShows(url + query, false) : resetTvShows();
|
||||
}
|
||||
|
||||
function getTvShows(url) {
|
||||
function getTvShows(url, loadImage) {
|
||||
resetTvShows();
|
||||
|
||||
$('#tvSearchButton').attr("class", "fa fa-spinner fa-spin");
|
||||
|
@ -334,6 +373,11 @@ $(function () {
|
|||
var context = buildTvShowContext(result);
|
||||
var html = searchTemplate(context);
|
||||
$("#tvList").append(html);
|
||||
|
||||
checkNetflix(context.title, context.id);
|
||||
if (loadImage) {
|
||||
getTvPoster(result.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
|
@ -343,6 +387,19 @@ $(function () {
|
|||
});
|
||||
};
|
||||
|
||||
function checkNetflix(title, id) {
|
||||
var url = createBaseUrl(base, '/searchextension/netflix/' + title);
|
||||
$.ajax(url).success(function (results) {
|
||||
|
||||
if (results.result) {
|
||||
// It's on Netflix
|
||||
$('#' + id + 'netflixTab')
|
||||
.html("<a href='https://www.netflix.com/watch/"+results.netflixId+"' target='_blank'><span class='label label-success'>Avaialble on Netflix</span></a>");
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function resetTvShows() {
|
||||
$("#tvList").html("");
|
||||
}
|
||||
|
@ -388,6 +445,16 @@ $(function () {
|
|||
});
|
||||
};
|
||||
|
||||
function getTvPoster(theTvDbId) {
|
||||
|
||||
var url = createBaseUrl(base, '/search/tv/poster/');
|
||||
$.ajax(url + theTvDbId).success(function (result) {
|
||||
if (result) {
|
||||
$('#' + theTvDbId + "imgDiv").html(" <img class='img-responsive' src='" + result + "' width='150' alt='poster'>");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function buildMovieContext(result) {
|
||||
var date = new Date(result.releaseDate);
|
||||
var year = date.getFullYear();
|
||||
|
@ -404,7 +471,11 @@ $(function () {
|
|||
requested: result.requested,
|
||||
approved: result.approved,
|
||||
available: result.available,
|
||||
url: result.plexUrl
|
||||
url: result.plexUrl,
|
||||
trailer: result.trailer,
|
||||
homepage: result.homepage,
|
||||
releaseDate: Humanize(result.releaseDate),
|
||||
status: result.status
|
||||
};
|
||||
|
||||
return context;
|
||||
|
@ -414,6 +485,7 @@ $(function () {
|
|||
var date = new Date(result.firstAired);
|
||||
var year = date.getFullYear();
|
||||
var context = {
|
||||
status: result.status,
|
||||
posterPath: result.banner,
|
||||
id: result.id,
|
||||
title: result.seriesName,
|
||||
|
@ -430,8 +502,11 @@ $(function () {
|
|||
tvPartialAvailable: result.tvPartialAvailable,
|
||||
disableTvRequestsByEpisode: result.disableTvRequestsByEpisode,
|
||||
disableTvRequestsBySeason: result.disableTvRequestsBySeason,
|
||||
enableTvRequestsForOnlySeries: result.enableTvRequestsForOnlySeries
|
||||
};
|
||||
enableTvRequestsForOnlySeries: result.enableTvRequestsForOnlySeries,
|
||||
trailer: result.trailer,
|
||||
homepage: result.homepage,
|
||||
firstAired: Humanize(result.firstAired)
|
||||
};
|
||||
|
||||
return context;
|
||||
}
|
||||
|
|
15
Ombi.UI/Content/site.js
vendored
15
Ombi.UI/Content/site.js
vendored
|
@ -8,7 +8,7 @@
|
|||
return s;
|
||||
}
|
||||
|
||||
$(function() {
|
||||
$(function () {
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
});
|
||||
|
||||
|
@ -93,6 +93,19 @@ function createBaseUrl(base, url) {
|
|||
return url;
|
||||
}
|
||||
|
||||
|
||||
function createLocalUrl(url) {
|
||||
var base = $('#baseUrl').text();
|
||||
if (base) {
|
||||
if (url.charAt(0) === "/") {
|
||||
url = "/" + base + url;
|
||||
} else {
|
||||
url = "/" + base + "/" + url;
|
||||
}
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
var noResultsHtml = "<div class='no-search-results'>" +
|
||||
"<i class='fa fa-film no-search-results-icon'></i><div class='no-search-results-text'>Sorry, we didn't find any results!</div></div>";
|
||||
var noResultsMusic = "<div class='no-search-results'>" +
|
||||
|
|
41
Ombi.UI/Content/systemjs.config.js
vendored
Normal file
41
Ombi.UI/Content/systemjs.config.js
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
/**
|
||||
* System configuration for Angular 2 samples
|
||||
* Adjust as necessary for your application needs.
|
||||
*/
|
||||
(function (global) {
|
||||
System.config({
|
||||
paths: {
|
||||
// paths serve as alias
|
||||
'npm:': '../node_modules/',
|
||||
'app' : '../app/'
|
||||
},
|
||||
// map tells the System loader where to look for things
|
||||
map: {
|
||||
// our app is within the app folder
|
||||
app: 'app',
|
||||
|
||||
// angular bundles
|
||||
'@angular/core': 'npm:@angular/core/bundles/core.umd.js',
|
||||
'@angular/common': 'npm:@angular/common/bundles/common.umd.js',
|
||||
'@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
|
||||
'@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
|
||||
'@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
|
||||
'@angular/http': 'npm:@angular/http/bundles/http.umd.js',
|
||||
'@angular/router': 'npm:@angular/router/bundles/router.umd.js',
|
||||
'@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
|
||||
|
||||
// other libraries
|
||||
'rxjs': 'npm:rxjs'
|
||||
},
|
||||
// packages tells the System loader how to load when no filename and/or no extension
|
||||
packages: {
|
||||
app: {
|
||||
main: './main.js',
|
||||
defaultExtension: 'js'
|
||||
},
|
||||
rxjs: {
|
||||
defaultExtension: 'js'
|
||||
}
|
||||
}
|
||||
});
|
||||
})(this);
|
|
@ -199,7 +199,7 @@ namespace Ombi.UI.Helpers
|
|||
var assetLocation = GetBaseUrl();
|
||||
|
||||
var content = GetContentUrl(assetLocation);
|
||||
|
||||
|
||||
sb.AppendLine($"<script src=\"{content}/Content/wizard.js?v={Assembly}\" type=\"text/javascript\"></script>");
|
||||
|
||||
return helper.Raw(sb.ToString());
|
||||
|
@ -226,9 +226,9 @@ namespace Ombi.UI.Helpers
|
|||
sb.Append($"<script src=\"{content}/Content/app/userManagement/userManagementService.js?v={Assembly}\" type=\"text/javascript\"></script>");
|
||||
sb.Append($"<script src=\"{content}/Content/app/userManagement/Directives/userManagementDirective.js?v={Assembly}\" type=\"text/javascript\"></script>");
|
||||
sb.Append($"<script src=\"{content}/Content/moment.min.js\"></script>");
|
||||
sb.Append($"<script src=\"{content}/Content/spin.min.js\"></script>");
|
||||
sb.Append($"<script src=\"{content}/Content/Angular/angular-spinner.min.js\"></script>");
|
||||
sb.Append($"<script src=\"{content}/Content/Angular/angular-loading-spinner.js\"></script>");
|
||||
sb.Append($"<script src=\"{content}/Content/spin.min.js\"></script>");
|
||||
sb.Append($"<script src=\"{content}/Content/Angular/angular-spinner.min.js\"></script>");
|
||||
sb.Append($"<script src=\"{content}/Content/Angular/angular-loading-spinner.js\"></script>");
|
||||
|
||||
return helper.Raw(sb.ToString());
|
||||
}
|
||||
|
@ -290,16 +290,19 @@ namespace Ombi.UI.Helpers
|
|||
return helper.Raw(asset);
|
||||
}
|
||||
|
||||
public static IHtmlString GetSidebarUrl(this HtmlHelpers helper, NancyContext context, string url, string title)
|
||||
public static IHtmlString GetSidebarUrl(this HtmlHelpers helper, NancyContext context, string url, string title, string icon = null)
|
||||
{
|
||||
var content = GetLinkUrl(GetBaseUrl());
|
||||
if (!string.IsNullOrEmpty(content))
|
||||
{
|
||||
url = $"/{content}{url}";
|
||||
}
|
||||
var returnString = context.Request.Path == url
|
||||
? $"<a class=\"list-group-item active\" href=\"{url}\">{title}</a>"
|
||||
: $"<a class=\"list-group-item\" href=\"{url}\">{title}</a>";
|
||||
|
||||
|
||||
var iconHtml = string.IsNullOrEmpty(icon) ? "" : $"<span style=\"font-size:16px; \" class=\"pull-right hidden-xs showopacity {icon}\"></span>";
|
||||
var returnString = context.Request.Path == url
|
||||
? $"<a class=\"list-group-item active\" href=\"{url}\">{title} {iconHtml}</a>"
|
||||
: $"<a class=\"list-group-item\" href=\"{url}\">{title} {iconHtml}</a>";
|
||||
|
||||
return helper.Raw(returnString);
|
||||
}
|
||||
|
@ -311,8 +314,8 @@ namespace Ombi.UI.Helpers
|
|||
{
|
||||
url = $"/{content}{url}";
|
||||
}
|
||||
var returnString = context.Request.Path == url ?
|
||||
$"<li class=\"active\"><a href=\"{url}\"><i class=\"fa fa-{fontIcon}\"></i> {title}</a></li>"
|
||||
var returnString = context.Request.Path == url ?
|
||||
$"<li class=\"active\"><a href=\"{url}\"><i class=\"fa fa-{fontIcon}\"></i> {title}</a></li>"
|
||||
: $"<li><a href=\"{url}\"><i class=\"fa fa-{fontIcon}\"></i> {title}</a></li>";
|
||||
|
||||
return helper.Raw(returnString);
|
||||
|
@ -326,8 +329,8 @@ namespace Ombi.UI.Helpers
|
|||
url = $"/{content}{url}";
|
||||
}
|
||||
|
||||
var returnString = context.Request.Path == url
|
||||
? $"<li class=\"active\"><a href=\"{url}\"><i class=\"fa fa-{fontIcon}\"></i> {title} {extraHtml}</a></li>"
|
||||
var returnString = context.Request.Path == url
|
||||
? $"<li class=\"active\"><a href=\"{url}\"><i class=\"fa fa-{fontIcon}\"></i> {title} {extraHtml}</a></li>"
|
||||
: $"<li><a href=\"{url}\"><i class=\"fa fa-{fontIcon}\"></i> {title} {extraHtml}</a></li>";
|
||||
|
||||
return helper.Raw(returnString);
|
||||
|
|
|
@ -75,6 +75,7 @@ namespace Ombi.UI.Jobs
|
|||
JobBuilder.Create<UserRequestLimitResetter>().WithIdentity("UserRequestLimiter", "Request").Build(),
|
||||
JobBuilder.Create<RecentlyAdded>().WithIdentity("RecentlyAddedModel", "Email").Build(),
|
||||
JobBuilder.Create<FaultQueueHandler>().WithIdentity("FaultQueueHandler", "Fault").Build(),
|
||||
JobBuilder.Create<RadarrCacher>().WithIdentity("RadarrCacher", "Cache").Build(),
|
||||
};
|
||||
|
||||
jobs.AddRange(jobList);
|
||||
|
@ -170,6 +171,10 @@ namespace Ombi.UI.Jobs
|
|||
{
|
||||
s.PlexUserChecker = 24;
|
||||
}
|
||||
if (s.RadarrCacher == 0)
|
||||
{
|
||||
s.RadarrCacher = 60;
|
||||
}
|
||||
|
||||
var triggers = new List<ITrigger>();
|
||||
|
||||
|
@ -222,6 +227,14 @@ namespace Ombi.UI.Jobs
|
|||
.WithSimpleSchedule(x => x.WithIntervalInMinutes(s.WatcherCacher).RepeatForever())
|
||||
.Build();
|
||||
|
||||
var radarrCacher =
|
||||
TriggerBuilder.Create()
|
||||
.WithIdentity("RadarrCacher", "Cache")
|
||||
.StartNow()
|
||||
//.StartAt(DateBuilder.FutureDate(2, IntervalUnit.Minute))
|
||||
.WithSimpleSchedule(x => x.WithIntervalInMinutes(s.RadarrCacher).RepeatForever())
|
||||
.Build();
|
||||
|
||||
var storeBackup =
|
||||
TriggerBuilder.Create()
|
||||
.WithIdentity("StoreBackup", "Database")
|
||||
|
@ -280,6 +293,7 @@ namespace Ombi.UI.Jobs
|
|||
triggers.Add(fault);
|
||||
triggers.Add(plexCacher);
|
||||
triggers.Add(plexUserChecker);
|
||||
triggers.Add(radarrCacher);
|
||||
|
||||
return triggers;
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ namespace Ombi.UI.ModelDataProviders
|
|||
with.Property(x => x.QualityProfile).Description("Sonarr's quality profile").Required(true);
|
||||
|
||||
with.Property(x => x.SeasonFolders).Description("Sonarr's season folders").Required(false);
|
||||
|
||||
|
||||
with.Property(x => x.RootPath).Description("Sonarr's root path").Required(false);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -58,5 +58,8 @@ namespace Ombi.UI.Models
|
|||
public Store.EpisodesModel[] Episodes { get; set; }
|
||||
public bool Denied { get; set; }
|
||||
public string DeniedReason { get; set; }
|
||||
public RootFolderModel[] RootFolders { get; set; }
|
||||
public bool HasRootFolders { get; set; }
|
||||
public string CurrentRootPath { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
36
Ombi.UI/Models/RootFolderModel.cs
Normal file
36
Ombi.UI/Models/RootFolderModel.cs
Normal file
|
@ -0,0 +1,36 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2017 Jamie Rees
|
||||
// File: RootFolderModel.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
namespace Ombi.UI.Models
|
||||
{
|
||||
|
||||
public class RootFolderModel
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string Path { get; set; }
|
||||
public long FreeSpace { get; set; }
|
||||
}
|
||||
}
|
37
Ombi.UI/Models/SearchLoadViewModel.cs
Normal file
37
Ombi.UI/Models/SearchLoadViewModel.cs
Normal file
|
@ -0,0 +1,37 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2017 Jamie Rees
|
||||
// File: SearchLoadViewModel.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using Ombi.Core.SettingModels;
|
||||
|
||||
namespace Ombi.UI.Models
|
||||
{
|
||||
public class SearchLoadViewModel
|
||||
{
|
||||
public PlexRequestSettings Settings { get; set; }
|
||||
public CustomizationSettings CustomizationSettings { get; set; }
|
||||
}
|
||||
}
|
|
@ -47,5 +47,8 @@ namespace Ombi.UI.Models
|
|||
public double VoteAverage { get; set; }
|
||||
public int VoteCount { get; set; }
|
||||
public bool AlreadyInCp { get; set; }
|
||||
public string Trailer { get; set; }
|
||||
public string Homepage { get; set; }
|
||||
public string ImdbId { get; set; }
|
||||
}
|
||||
}
|
|
@ -58,5 +58,20 @@ namespace Ombi.UI.Models
|
|||
public bool DisableTvRequestsByEpisode { get; set; }
|
||||
public bool DisableTvRequestsBySeason { get; set; }
|
||||
public bool EnableTvRequestsForOnlySeries { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This is used from the Trakt API
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The trailer.
|
||||
/// </value>
|
||||
public string Trailer { get; set; }
|
||||
/// <summary>
|
||||
/// This is used from the Trakt API
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The trailer.
|
||||
/// </value>
|
||||
public string Homepage { get; set; }
|
||||
}
|
||||
}
|
|
@ -93,6 +93,10 @@ namespace Ombi.UI.Modules.Admin
|
|||
private IAnalytics Analytics { get; }
|
||||
private IRecentlyAdded RecentlyAdded { get; }
|
||||
private ISettingsService<NotificationSettingsV2> NotifySettings { get; }
|
||||
private ISettingsService<DiscordNotificationSettings> DiscordSettings { get; }
|
||||
private IDiscordApi DiscordApi { get; }
|
||||
private ISettingsService<RadarrSettings> RadarrSettings { get; }
|
||||
private IRadarrApi RadarrApi { get; }
|
||||
|
||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
public AdminModule(ISettingsService<PlexRequestSettings> prService,
|
||||
|
@ -118,7 +122,9 @@ namespace Ombi.UI.Modules.Admin
|
|||
ISlackApi slackApi, ISettingsService<LandingPageSettings> lp,
|
||||
ISettingsService<ScheduledJobsSettings> scheduler, IJobRecord rec, IAnalytics analytics,
|
||||
ISettingsService<NotificationSettingsV2> notifyService, IRecentlyAdded recentlyAdded,
|
||||
ISettingsService<WatcherSettings> watcherSettings
|
||||
ISettingsService<WatcherSettings> watcherSettings ,
|
||||
ISettingsService<DiscordNotificationSettings> discord,
|
||||
IDiscordApi discordapi, ISettingsService<RadarrSettings> settings, IRadarrApi radarrApi
|
||||
, ISecurityExtensions security) : base("admin", prService, security)
|
||||
{
|
||||
PrService = prService;
|
||||
|
@ -150,6 +156,10 @@ namespace Ombi.UI.Modules.Admin
|
|||
NotifySettings = notifyService;
|
||||
RecentlyAdded = recentlyAdded;
|
||||
WatcherSettings = watcherSettings;
|
||||
DiscordSettings = discord;
|
||||
DiscordApi = discordapi;
|
||||
RadarrSettings = settings;
|
||||
RadarrApi = radarrApi;
|
||||
|
||||
Before += (ctx) => Security.AdminLoginRedirect(Permissions.Administrator, ctx);
|
||||
|
||||
|
@ -165,18 +175,19 @@ namespace Ombi.UI.Modules.Admin
|
|||
Get["/getusers"] = _ => GetUsers();
|
||||
|
||||
Get["/couchpotato"] = _ => CouchPotato();
|
||||
Post["/couchpotato"] = _ => SaveCouchPotato();
|
||||
Post["/couchpotato", true] = async (x, ct) => await SaveCouchPotato();
|
||||
|
||||
Get["/plex"] = _ => Plex();
|
||||
Post["/plex", true] = async (x, ct) => await SavePlex();
|
||||
|
||||
Get["/sonarr"] = _ => Sonarr();
|
||||
Post["/sonarr"] = _ => SaveSonarr();
|
||||
Post["/sonarrprofiles"] = _ => GetSonarrQualityProfiles();
|
||||
|
||||
|
||||
Get["/sickrage"] = _ => Sickrage();
|
||||
Post["/sickrage"] = _ => SaveSickrage();
|
||||
|
||||
Post["/sonarrprofiles"] = _ => GetSonarrQualityProfiles();
|
||||
Post["/cpprofiles", true] = async (x, ct) => await GetCpProfiles();
|
||||
Post["/cpapikey"] = x => GetCpApiKey();
|
||||
|
||||
|
@ -200,18 +211,21 @@ namespace Ombi.UI.Modules.Admin
|
|||
Get["/headphones"] = _ => Headphones();
|
||||
Post["/headphones"] = _ => SaveHeadphones();
|
||||
|
||||
Get["/newsletter"] = _ => Newsletter();
|
||||
Post["/newsletter"] = _ => SaveNewsletter();
|
||||
Get["/newsletter", true] = async (x, ct) => await Newsletter();
|
||||
Post["/newsletter", true] = async (x, ct) => await SaveNewsletter();
|
||||
|
||||
Post["/createapikey"] = x => CreateApiKey();
|
||||
|
||||
|
||||
|
||||
Post["/testslacknotification", true] = async (x, ct) => await TestSlackNotification();
|
||||
|
||||
Get["/slacknotification"] = _ => SlackNotifications();
|
||||
Post["/slacknotification"] = _ => SaveSlackNotifications();
|
||||
|
||||
Post["/testdiscordnotification", true] = async (x, ct) => await TestDiscordNotification();
|
||||
Get["/discordnotification", true] = async (x, ct) => await DiscordNotification();
|
||||
Post["/discordnotification", true] = async (x, ct) => await SaveDiscordNotifications();
|
||||
|
||||
Get["/landingpage", true] = async (x, ct) => await LandingPage();
|
||||
Post["/landingpage", true] = async (x, ct) => await SaveLandingPage();
|
||||
|
||||
|
@ -368,7 +382,7 @@ namespace Ombi.UI.Modules.Admin
|
|||
return View["CouchPotato", settings];
|
||||
}
|
||||
|
||||
private Response SaveCouchPotato()
|
||||
private async Task<Response> SaveCouchPotato()
|
||||
{
|
||||
var couchPotatoSettings = this.Bind<CouchPotatoSettings>();
|
||||
var valid = this.Validate(couchPotatoSettings);
|
||||
|
@ -377,7 +391,7 @@ namespace Ombi.UI.Modules.Admin
|
|||
return Response.AsJson(valid.SendJsonError());
|
||||
}
|
||||
|
||||
var watcherSettings = WatcherSettings.GetSettings();
|
||||
var watcherSettings = await WatcherSettings.GetSettingsAsync();
|
||||
|
||||
if (watcherSettings.Enabled)
|
||||
{
|
||||
|
@ -389,8 +403,20 @@ namespace Ombi.UI.Modules.Admin
|
|||
});
|
||||
}
|
||||
|
||||
var radarrSettings = await RadarrSettings.GetSettingsAsync();
|
||||
|
||||
if (radarrSettings.Enabled)
|
||||
{
|
||||
return
|
||||
Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = false,
|
||||
Message = "Cannot have Radarr and CouchPotato both enabled."
|
||||
});
|
||||
}
|
||||
|
||||
couchPotatoSettings.ApiKey = couchPotatoSettings.ApiKey.Trim();
|
||||
var result = CpService.SaveSettings(couchPotatoSettings);
|
||||
var result = await CpService.SaveSettingsAsync(couchPotatoSettings);
|
||||
return Response.AsJson(result
|
||||
? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for CouchPotato!" }
|
||||
: new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." });
|
||||
|
@ -448,6 +474,7 @@ namespace Ombi.UI.Modules.Admin
|
|||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "SickRage is enabled, we cannot enable Sonarr and SickRage" });
|
||||
}
|
||||
|
||||
sonarrSettings.ApiKey = sonarrSettings.ApiKey.Trim();
|
||||
var result = SonarrService.SaveSettings(sonarrSettings);
|
||||
|
||||
|
@ -456,6 +483,10 @@ namespace Ombi.UI.Modules.Admin
|
|||
: new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." });
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private Negotiator Sickrage()
|
||||
{
|
||||
var settings = SickRageService.GetSettings();
|
||||
|
@ -814,13 +845,13 @@ namespace Ombi.UI.Modules.Admin
|
|||
: new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." });
|
||||
}
|
||||
|
||||
private Negotiator Newsletter()
|
||||
private async Task<Negotiator> Newsletter()
|
||||
{
|
||||
var settings = NewsLetterService.GetSettings();
|
||||
var settings = await NewsLetterService.GetSettingsAsync();
|
||||
return View["NewsletterSettings", settings];
|
||||
}
|
||||
|
||||
private Response SaveNewsletter()
|
||||
private async Task<Response> SaveNewsletter()
|
||||
{
|
||||
var settings = this.Bind<NewletterSettings>();
|
||||
|
||||
|
@ -828,9 +859,17 @@ namespace Ombi.UI.Modules.Admin
|
|||
if (!valid.IsValid)
|
||||
{
|
||||
var error = valid.SendJsonError();
|
||||
Log.Info("Error validating Headphones settings, message: {0}", error.Message);
|
||||
Log.Info("Error validating Newsletter settings, message: {0}", error.Message);
|
||||
return Response.AsJson(error);
|
||||
}
|
||||
|
||||
// Make sure emails are setup
|
||||
var emailSettings = await EmailService.GetSettingsAsync();
|
||||
if (!emailSettings.Enabled)
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Please enable your email notifications" });
|
||||
}
|
||||
|
||||
settings.SendRecentlyAddedEmail = settings.SendRecentlyAddedEmail;
|
||||
var result = NewsLetterService.SaveSettings(settings);
|
||||
|
||||
|
@ -918,6 +957,71 @@ namespace Ombi.UI.Modules.Admin
|
|||
: new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." });
|
||||
}
|
||||
|
||||
private async Task<Negotiator> DiscordNotification()
|
||||
{
|
||||
var settings = await DiscordSettings.GetSettingsAsync();
|
||||
return View["DiscordNotification", settings];
|
||||
}
|
||||
|
||||
private async Task<Response> TestDiscordNotification()
|
||||
{
|
||||
var settings = this.BindAndValidate<DiscordNotificationSettings>();
|
||||
if (!ModelValidationResult.IsValid)
|
||||
{
|
||||
return Response.AsJson(ModelValidationResult.SendJsonError());
|
||||
}
|
||||
var notificationModel = new NotificationModel
|
||||
{
|
||||
NotificationType = NotificationType.Test,
|
||||
DateTime = DateTime.Now
|
||||
};
|
||||
|
||||
var currentDicordSettings = await DiscordSettings.GetSettingsAsync();
|
||||
try
|
||||
{
|
||||
NotificationService.Subscribe(new DiscordNotification(DiscordApi, DiscordSettings));
|
||||
settings.Enabled = true;
|
||||
await NotificationService.Publish(notificationModel, settings);
|
||||
Log.Info("Sent Discord notification test");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e, "Failed to subscribe and publish test Discord Notification");
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!currentDicordSettings.Enabled)
|
||||
{
|
||||
NotificationService.UnSubscribe(new DiscordNotification(DiscordApi, DiscordSettings));
|
||||
}
|
||||
}
|
||||
return Response.AsJson(new JsonResponseModel { Result = true, Message = "Successfully sent a test Discord Notification! If you do not receive it please check the logs." });
|
||||
}
|
||||
|
||||
private async Task<Response> SaveDiscordNotifications()
|
||||
{
|
||||
var settings = this.BindAndValidate<DiscordNotificationSettings>();
|
||||
if (!ModelValidationResult.IsValid)
|
||||
{
|
||||
return Response.AsJson(ModelValidationResult.SendJsonError());
|
||||
}
|
||||
|
||||
var result = await DiscordSettings.SaveSettingsAsync(settings);
|
||||
if (settings.Enabled)
|
||||
{
|
||||
NotificationService.Subscribe(new DiscordNotification(DiscordApi, DiscordSettings));
|
||||
}
|
||||
else
|
||||
{
|
||||
NotificationService.UnSubscribe(new DiscordNotification(DiscordApi, DiscordSettings));
|
||||
}
|
||||
|
||||
Log.Info("Saved discord settings, result: {0}", result);
|
||||
return Response.AsJson(result
|
||||
? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for Discord Notifications!" }
|
||||
: new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." });
|
||||
}
|
||||
|
||||
private async Task<Negotiator> LandingPage()
|
||||
{
|
||||
var settings = await LandingSettings.GetSettingsAsync();
|
||||
|
|
|
@ -64,7 +64,7 @@ namespace Ombi.UI.Modules.Admin
|
|||
Id = r.Id,
|
||||
PrimaryIdentifier = r.PrimaryIdentifier,
|
||||
LastRetry = r.LastRetry,
|
||||
Message = r.Message
|
||||
Message = r.Description
|
||||
}).ToList();
|
||||
|
||||
return View["RequestFaultQueue", model];
|
||||
|
|
|
@ -36,6 +36,7 @@ using Nancy;
|
|||
using Nancy.ModelBinding;
|
||||
using Nancy.Responses.Negotiation;
|
||||
using Nancy.Validation;
|
||||
using Ombi.Api.Interfaces;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.SettingModels;
|
||||
using Ombi.Core.StatusChecker;
|
||||
|
@ -52,22 +53,40 @@ namespace Ombi.UI.Modules.Admin
|
|||
public class IntegrationModule : BaseModule
|
||||
{
|
||||
public IntegrationModule(ISettingsService<PlexRequestSettings> settingsService, ISettingsService<WatcherSettings> watcher,
|
||||
ISettingsService<CouchPotatoSettings> cp,ISecurityExtensions security, IAnalytics a) : base("admin", settingsService, security)
|
||||
ISettingsService<CouchPotatoSettings> cp,ISecurityExtensions security, IAnalytics a, ISettingsService<RadarrSettings> radarrSettings,
|
||||
ICacheProvider cache, IRadarrApi radarrApi, ISonarrApi sonarrApi) : base("admin", settingsService, security)
|
||||
{
|
||||
|
||||
WatcherSettings = watcher;
|
||||
Analytics = a;
|
||||
CpSettings = cp;
|
||||
Cache = cache;
|
||||
RadarrApi = radarrApi;
|
||||
RadarrSettings = radarrSettings;
|
||||
SonarrApi = sonarrApi;
|
||||
|
||||
Before += (ctx) => Security.AdminLoginRedirect(Permissions.Administrator, ctx);
|
||||
|
||||
|
||||
Post["/sonarrrootfolders"] = _ => GetSonarrRootFolders();
|
||||
|
||||
Get["/watcher", true] = async (x, ct) => await Watcher();
|
||||
Post["/watcher", true] = async (x, ct) => await SaveWatcher();
|
||||
|
||||
Get["/radarr", true] = async (x, ct) => await Radarr();
|
||||
Post["/radarr", true] = async (x, ct) => await SaveRadarr();
|
||||
|
||||
|
||||
Post["/radarrprofiles"] = _ => GetRadarrQualityProfiles();
|
||||
}
|
||||
|
||||
private ISettingsService<WatcherSettings> WatcherSettings { get; }
|
||||
private ISettingsService<CouchPotatoSettings> CpSettings { get; }
|
||||
private ISettingsService<RadarrSettings> RadarrSettings { get; }
|
||||
private IRadarrApi RadarrApi { get; }
|
||||
private ICacheProvider Cache { get; }
|
||||
private IAnalytics Analytics { get; }
|
||||
private ISonarrApi SonarrApi { get; }
|
||||
|
||||
private async Task<Negotiator> Watcher()
|
||||
{
|
||||
|
@ -97,6 +116,18 @@ namespace Ombi.UI.Modules.Admin
|
|||
});
|
||||
}
|
||||
|
||||
var radarrSettings = await RadarrSettings.GetSettingsAsync();
|
||||
|
||||
if (radarrSettings.Enabled)
|
||||
{
|
||||
return
|
||||
Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = false,
|
||||
Message = "Cannot have Radarr and CouchPotato both enabled."
|
||||
});
|
||||
}
|
||||
|
||||
settings.ApiKey = settings.ApiKey.Trim();
|
||||
var result = await WatcherSettings.SaveSettingsAsync(settings);
|
||||
return Response.AsJson(result
|
||||
|
@ -104,5 +135,72 @@ namespace Ombi.UI.Modules.Admin
|
|||
: new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." });
|
||||
}
|
||||
|
||||
private async Task<Negotiator> Radarr()
|
||||
{
|
||||
var settings = await RadarrSettings.GetSettingsAsync();
|
||||
|
||||
return View["Radarr", settings];
|
||||
}
|
||||
|
||||
private async Task<Response> SaveRadarr()
|
||||
{
|
||||
var radarrSettings = this.Bind<RadarrSettings>();
|
||||
|
||||
//Check Watcher and CP make sure they are not enabled
|
||||
var watcher = await WatcherSettings.GetSettingsAsync();
|
||||
if (watcher.Enabled)
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Watcher is enabled, we cannot enable Watcher and Radarr" });
|
||||
}
|
||||
|
||||
var cp = await CpSettings.GetSettingsAsync();
|
||||
if (cp.Enabled)
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "CouchPotato is enabled, we cannot enable Watcher and CouchPotato" });
|
||||
}
|
||||
|
||||
var valid = this.Validate(radarrSettings);
|
||||
if (!valid.IsValid)
|
||||
{
|
||||
return Response.AsJson(valid.SendJsonError());
|
||||
}
|
||||
|
||||
radarrSettings.ApiKey = radarrSettings.ApiKey.Trim();
|
||||
var result = await RadarrSettings.SaveSettingsAsync(radarrSettings);
|
||||
|
||||
return Response.AsJson(result
|
||||
? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for Radarr!" }
|
||||
: new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." });
|
||||
}
|
||||
|
||||
private Response GetRadarrQualityProfiles()
|
||||
{
|
||||
var settings = this.Bind<RadarrSettings>();
|
||||
var profiles = RadarrApi.GetProfiles(settings.ApiKey, settings.FullUri);
|
||||
|
||||
// set the cache
|
||||
if (profiles != null)
|
||||
{
|
||||
Cache.Set(CacheKeys.RadarrQualityProfiles, profiles);
|
||||
}
|
||||
|
||||
return Response.AsJson(profiles);
|
||||
}
|
||||
|
||||
private Response GetSonarrRootFolders()
|
||||
{
|
||||
var settings = this.Bind<SonarrSettings>();
|
||||
|
||||
var rootFolders = SonarrApi.GetRootFolders(settings.ApiKey, settings.FullUri);
|
||||
|
||||
// set the cache
|
||||
if (rootFolders != null)
|
||||
{
|
||||
Cache.Set(CacheKeys.SonarrRootFolders, rootFolders);
|
||||
}
|
||||
|
||||
return Response.AsJson(rootFolders);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
150
Ombi.UI/Modules/Admin/ScheduledJobsRunnerModule.cs
Normal file
150
Ombi.UI/Modules/Admin/ScheduledJobsRunnerModule.cs
Normal file
|
@ -0,0 +1,150 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: AboutModule.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Nancy;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.SettingModels;
|
||||
using Ombi.Helpers.Permissions;
|
||||
using Ombi.Services.Interfaces;
|
||||
using Ombi.Services.Jobs;
|
||||
using Ombi.UI.Models;
|
||||
using ISecurityExtensions = Ombi.Core.ISecurityExtensions;
|
||||
|
||||
namespace Ombi.UI.Modules.Admin
|
||||
{
|
||||
public class ScheduledJobsRunnerModule : BaseModule
|
||||
{
|
||||
public ScheduledJobsRunnerModule(ISettingsService<PlexRequestSettings> settingsService,
|
||||
ISecurityExtensions security, IPlexContentCacher contentCacher, ISonarrCacher sonarrCacher, IWatcherCacher watcherCacher,
|
||||
IRadarrCacher radarrCacher, ICouchPotatoCacher cpCacher, IStoreBackup store, ISickRageCacher srCacher, IAvailabilityChecker plexChceker,
|
||||
IStoreCleanup cleanup, IUserRequestLimitResetter requestLimit, IPlexEpisodeCacher episodeCacher, IRecentlyAdded recentlyAdded,
|
||||
IFaultQueueHandler faultQueueHandler, IPlexUserChecker plexUserChecker) : base("admin", settingsService, security)
|
||||
{
|
||||
Before += (ctx) => Security.AdminLoginRedirect(Permissions.Administrator, ctx);
|
||||
|
||||
PlexContentCacher = contentCacher;
|
||||
SonarrCacher = sonarrCacher;
|
||||
RadarrCacher = radarrCacher;
|
||||
WatcherCacher = watcherCacher;
|
||||
CpCacher = cpCacher;
|
||||
StoreBackup = store;
|
||||
SrCacher = srCacher;
|
||||
AvailabilityChecker = plexChceker;
|
||||
StoreCleanup = cleanup;
|
||||
RequestLimit = requestLimit;
|
||||
EpisodeCacher = episodeCacher;
|
||||
RecentlyAdded = recentlyAdded;
|
||||
FaultQueueHandler = faultQueueHandler;
|
||||
PlexUserChecker = plexUserChecker;
|
||||
|
||||
Post["/schedulerun", true] = async (x, ct) => await ScheduleRun((string)Request.Form.key);
|
||||
}
|
||||
|
||||
private IPlexContentCacher PlexContentCacher { get; }
|
||||
private IRadarrCacher RadarrCacher { get; }
|
||||
private ISonarrCacher SonarrCacher { get; }
|
||||
private IWatcherCacher WatcherCacher { get; }
|
||||
private ICouchPotatoCacher CpCacher { get; }
|
||||
private IStoreBackup StoreBackup { get; }
|
||||
private ISickRageCacher SrCacher { get; }
|
||||
private IAvailabilityChecker AvailabilityChecker { get; }
|
||||
private IStoreCleanup StoreCleanup { get; }
|
||||
private IUserRequestLimitResetter RequestLimit { get; }
|
||||
private IPlexEpisodeCacher EpisodeCacher { get; }
|
||||
private IRecentlyAdded RecentlyAdded { get; }
|
||||
private IFaultQueueHandler FaultQueueHandler { get; }
|
||||
private IPlexUserChecker PlexUserChecker { get; }
|
||||
|
||||
|
||||
private async Task<Response> ScheduleRun(string key)
|
||||
{
|
||||
if (key.Equals(JobNames.PlexCacher, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
PlexContentCacher.CacheContent();
|
||||
}
|
||||
|
||||
if (key.Equals(JobNames.WatcherCacher, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
WatcherCacher.Queued();
|
||||
}
|
||||
|
||||
if (key.Equals(JobNames.SonarrCacher, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
SonarrCacher.Queued();
|
||||
}
|
||||
if (key.Equals(JobNames.RadarrCacher, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
RadarrCacher.Queued();
|
||||
}
|
||||
if (key.Equals(JobNames.CpCacher, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
CpCacher.Queued();
|
||||
}
|
||||
if (key.Equals(JobNames.StoreBackup, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
StoreBackup.Start();
|
||||
}
|
||||
if (key.Equals(JobNames.SrCacher, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
SrCacher.Queued();
|
||||
}
|
||||
if (key.Equals(JobNames.PlexChecker, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
AvailabilityChecker.Start();
|
||||
}
|
||||
if (key.Equals(JobNames.StoreCleanup, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
StoreCleanup.Start();
|
||||
}
|
||||
if (key.Equals(JobNames.RequestLimitReset, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
RequestLimit.Start();
|
||||
}
|
||||
if (key.Equals(JobNames.EpisodeCacher, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
EpisodeCacher.Start();
|
||||
}
|
||||
if (key.Equals(JobNames.RecentlyAddedEmail, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
RecentlyAdded.Start();
|
||||
}
|
||||
if (key.Equals(JobNames.FaultQueueHandler, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
FaultQueueHandler.Start();
|
||||
}
|
||||
if (key.Equals(JobNames.PlexUserChecker, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
RequestLimit.Start();
|
||||
}
|
||||
|
||||
|
||||
return Response.AsJson(new JsonResponseModel { Result = true });
|
||||
}
|
||||
}
|
||||
}
|
|
@ -46,7 +46,7 @@ namespace Ombi.UI.Modules
|
|||
|
||||
public ApplicationTesterModule(ICouchPotatoApi cpApi, ISonarrApi sonarrApi, IPlexApi plexApi,
|
||||
ISickRageApi srApi, IHeadphonesApi hpApi, ISettingsService<PlexRequestSettings> pr, ISecurityExtensions security,
|
||||
IWatcherApi watcherApi) : base("test", pr, security)
|
||||
IWatcherApi watcherApi, IRadarrApi radarrApi) : base("test", pr, security)
|
||||
{
|
||||
this.RequiresAuthentication();
|
||||
|
||||
|
@ -56,9 +56,11 @@ namespace Ombi.UI.Modules
|
|||
SickRageApi = srApi;
|
||||
HeadphonesApi = hpApi;
|
||||
WatcherApi = watcherApi;
|
||||
RadarrApi = radarrApi;
|
||||
|
||||
Post["/cp"] = _ => CouchPotatoTest();
|
||||
Post["/sonarr"] = _ => SonarrTest();
|
||||
Post["/radarr"] = _ => RadarrTest();
|
||||
Post["/plex"] = _ => PlexTest();
|
||||
Post["/sickrage"] = _ => SickRageTest();
|
||||
Post["/headphones"] = _ => HeadphonesTest();
|
||||
|
@ -73,6 +75,7 @@ namespace Ombi.UI.Modules
|
|||
private ISickRageApi SickRageApi { get; }
|
||||
private IHeadphonesApi HeadphonesApi { get; }
|
||||
private IWatcherApi WatcherApi { get; }
|
||||
private IRadarrApi RadarrApi { get; }
|
||||
|
||||
private Response CouchPotatoTest()
|
||||
{
|
||||
|
@ -148,7 +151,7 @@ namespace Ombi.UI.Modules
|
|||
: Response.AsJson(new JsonResponseModel { Result = false, Message = "Could not connect to Sonarr, please check your settings." });
|
||||
|
||||
}
|
||||
catch (Exception e) // Exceptions are expected, if we cannot connect so we will just log and swallow them.
|
||||
catch (Exception e) // Exceptions are expected, if we cannot connect so we will just log and swallow them.
|
||||
{
|
||||
Log.Warn("Exception thrown when attempting to get Sonarr's status: ");
|
||||
Log.Warn(e);
|
||||
|
@ -161,6 +164,35 @@ namespace Ombi.UI.Modules
|
|||
}
|
||||
}
|
||||
|
||||
private Response RadarrTest()
|
||||
{
|
||||
var radarrSettings = this.Bind<RadarrSettings>();
|
||||
var valid = this.Validate(radarrSettings);
|
||||
if (!valid.IsValid)
|
||||
{
|
||||
return Response.AsJson(valid.SendJsonError());
|
||||
}
|
||||
try
|
||||
{
|
||||
var status = RadarrApi.SystemStatus(radarrSettings.ApiKey, radarrSettings.FullUri);
|
||||
return status?.version != null
|
||||
? Response.AsJson(new JsonResponseModel { Result = true, Message = "Connected to Radarr successfully!" })
|
||||
: Response.AsJson(new JsonResponseModel { Result = false, Message = "Could not connect to Radarr, please check your settings." });
|
||||
|
||||
}
|
||||
catch (Exception e) // Exceptions are expected, if we cannot connect so we will just log and swallow them.
|
||||
{
|
||||
Log.Warn("Exception thrown when attempting to get Radarr's status: ");
|
||||
Log.Warn(e);
|
||||
var message = $"Could not connect to Radarr, please check your settings. <strong>Exception Message:</strong> {e.Message}";
|
||||
if (e.InnerException != null)
|
||||
{
|
||||
message = $"Could not connect to Radarr, please check your settings. <strong>Exception Message:</strong> {e.InnerException.Message}";
|
||||
}
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = message });
|
||||
}
|
||||
}
|
||||
|
||||
private Response PlexTest()
|
||||
{
|
||||
var plexSettings = this.Bind<PlexSettings>();
|
||||
|
|
|
@ -50,7 +50,7 @@ namespace Ombi.UI.Modules
|
|||
public ApprovalModule(IRequestService service, ISonarrApi sonarrApi,
|
||||
ISettingsService<SonarrSettings> sonarrSettings, ISickRageApi srApi, ISettingsService<SickRageSettings> srSettings,
|
||||
ISettingsService<HeadphonesSettings> hpSettings, IHeadphonesApi hpApi, ISettingsService<PlexRequestSettings> pr, ITransientFaultQueue faultQueue
|
||||
, ISecurityExtensions security, IMovieSender movieSender) : base("approval", pr, security)
|
||||
, ISecurityExtensions security, IMovieSender movieSender, ICacheProvider cache) : base("approval", pr, security)
|
||||
{
|
||||
|
||||
Before += (ctx) => Security.AdminLoginRedirect(ctx, Permissions.Administrator,Permissions.ManageRequests);
|
||||
|
@ -64,6 +64,7 @@ namespace Ombi.UI.Modules
|
|||
HeadphoneApi = hpApi;
|
||||
FaultQueue = faultQueue;
|
||||
MovieSender = movieSender;
|
||||
Cache = cache;
|
||||
|
||||
Post["/approve", true] = async (x, ct) => await Approve((int)Request.Form.requestid, (string)Request.Form.qualityId);
|
||||
Post["/deny", true] = async (x, ct) => await DenyRequest((int)Request.Form.requestid, (string)Request.Form.reason);
|
||||
|
@ -86,6 +87,7 @@ namespace Ombi.UI.Modules
|
|||
private ISickRageApi SickRageApi { get; }
|
||||
private IHeadphonesApi HeadphoneApi { get; }
|
||||
private ITransientFaultQueue FaultQueue { get; }
|
||||
private ICacheProvider Cache { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Approves the specified request identifier.
|
||||
|
@ -120,7 +122,7 @@ namespace Ombi.UI.Modules
|
|||
|
||||
private async Task<Response> RequestTvAndUpdateStatus(RequestedModel request, string qualityId)
|
||||
{
|
||||
var sender = new TvSenderOld(SonarrApi, SickRageApi); // TODO put back
|
||||
var sender = new TvSenderOld(SonarrApi, SickRageApi, Cache); // TODO put back
|
||||
|
||||
var sonarrSettings = await SonarrSettings.GetSettingsAsync();
|
||||
if (sonarrSettings.Enabled)
|
||||
|
@ -435,7 +437,7 @@ namespace Ombi.UI.Modules
|
|||
}
|
||||
if (r.Type == RequestType.TvShow)
|
||||
{
|
||||
var sender = new TvSenderOld(SonarrApi, SickRageApi); // TODO put back
|
||||
var sender = new TvSenderOld(SonarrApi, SickRageApi, Cache); // TODO put back
|
||||
var sr = await SickRageSettings.GetSettingsAsync();
|
||||
var sonarr = await SonarrSettings.GetSettingsAsync();
|
||||
if (sr.Enabled)
|
||||
|
|
|
@ -31,6 +31,7 @@ using System.Linq;
|
|||
using System.Threading;
|
||||
using Nancy;
|
||||
using Nancy.Security;
|
||||
using NLog;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.SettingModels;
|
||||
using Ombi.Helpers;
|
||||
|
@ -100,6 +101,7 @@ namespace Ombi.UI.Modules
|
|||
}
|
||||
|
||||
|
||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
private string _username;
|
||||
/// <summary>
|
||||
/// Returns the Username or UserAlias
|
||||
|
@ -112,15 +114,16 @@ namespace Ombi.UI.Modules
|
|||
{
|
||||
try
|
||||
{
|
||||
var username = Security.GetUsername(User.UserName, Session);
|
||||
var username = Security.GetUsername(User?.UserName, Session);
|
||||
if (string.IsNullOrEmpty(username))
|
||||
{
|
||||
return Session[SessionKeys.UsernameKey].ToString();
|
||||
return string.Empty;
|
||||
}
|
||||
_username = username;
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Info(e);
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
@ -149,9 +152,10 @@ namespace Ombi.UI.Modules
|
|||
|
||||
protected bool LoggedIn => Context?.CurrentUser != null;
|
||||
|
||||
protected string Culture { get; set; }
|
||||
private string Culture { get; set; }
|
||||
protected const string CultureCookieName = "_culture";
|
||||
protected Response SetCookie()
|
||||
|
||||
private Response SetCookie()
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
|
@ -96,6 +96,8 @@ namespace Ombi.UI.Modules
|
|||
|
||||
Post["/changeavailability", true] = async (x, ct) => await ChangeRequestAvailability((int)Request.Form.Id, (bool)Request.Form.Available);
|
||||
|
||||
Post["/changeRootFolder", true] = async (x, ct) => await ChangeRootFolder((int) Request.Form.requestId, (int) Request.Form.rootFolderId);
|
||||
|
||||
Get["/UpdateFilters", true] = async (x, ct) => await GetFilterAndSortSettings();
|
||||
}
|
||||
|
||||
|
@ -160,7 +162,7 @@ namespace Ombi.UI.Modules
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
var canManageRequest = Security.HasAnyPermissions(User, Permissions.Administrator, Permissions.ManageRequests);
|
||||
var viewModel = dbMovies.Select(movie => new RequestViewModel
|
||||
{
|
||||
|
@ -185,7 +187,7 @@ namespace Ombi.UI.Modules
|
|||
IssueId = movie.IssueId,
|
||||
Denied = movie.Denied,
|
||||
DeniedReason = movie.DeniedReason,
|
||||
Qualities = qualities.ToArray()
|
||||
Qualities = qualities.ToArray(),
|
||||
}).ToList();
|
||||
|
||||
return Response.AsJson(viewModel);
|
||||
|
@ -193,32 +195,39 @@ namespace Ombi.UI.Modules
|
|||
|
||||
private async Task<Response> GetTvShows()
|
||||
{
|
||||
var settingsTask = PrSettings.GetSettingsAsync();
|
||||
|
||||
var requests = await Service.GetAllAsync();
|
||||
requests = requests.Where(x => x.Type == RequestType.TvShow);
|
||||
|
||||
var dbTv = requests;
|
||||
var settings = await settingsTask;
|
||||
if (Security.HasPermissions(User, Permissions.UsersCanViewOnlyOwnRequests) && !IsAdmin)
|
||||
{
|
||||
dbTv = dbTv.Where(x => x.UserHasRequested(Username)).ToList();
|
||||
}
|
||||
|
||||
IEnumerable<QualityModel> qualities = new List<QualityModel>();
|
||||
IEnumerable<RootFolderModel> rootFolders = new List<RootFolderModel>();
|
||||
|
||||
var sonarrSettings = await SonarrSettings.GetSettingsAsync();
|
||||
if (IsAdmin)
|
||||
{
|
||||
try
|
||||
{
|
||||
var sonarrSettings = await SonarrSettings.GetSettingsAsync();
|
||||
if (sonarrSettings.Enabled)
|
||||
{
|
||||
var result = Cache.GetOrSetAsync(CacheKeys.SonarrQualityProfiles, async () =>
|
||||
var result = await Cache.GetOrSetAsync(CacheKeys.SonarrQualityProfiles, async () =>
|
||||
{
|
||||
return await Task.Run(() => SonarrApi.GetProfiles(sonarrSettings.ApiKey, sonarrSettings.FullUri));
|
||||
});
|
||||
qualities = result.Result.Select(x => new QualityModel { Id = x.id.ToString(), Name = x.name }).ToList();
|
||||
}
|
||||
qualities = result.Select(x => new QualityModel { Id = x.id.ToString(), Name = x.name }).ToList();
|
||||
|
||||
|
||||
var rootFoldersResult =await Cache.GetOrSetAsync(CacheKeys.SonarrRootFolders, async () =>
|
||||
{
|
||||
return await Task.Run(() => SonarrApi.GetRootFolders(sonarrSettings.ApiKey, sonarrSettings.FullUri));
|
||||
});
|
||||
|
||||
rootFolders = rootFoldersResult.Select(x => new RootFolderModel { Id = x.id.ToString(), Path = x.path, FreeSpace = x.freespace}).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
var sickRageSettings = await SickRageSettings.GetSettingsAsync();
|
||||
|
@ -235,6 +244,8 @@ namespace Ombi.UI.Modules
|
|||
|
||||
}
|
||||
|
||||
|
||||
|
||||
var canManageRequest = Security.HasAnyPermissions(User, Permissions.Administrator, Permissions.ManageRequests);
|
||||
var viewModel = dbTv.Select(tv => new RequestViewModel
|
||||
{
|
||||
|
@ -243,7 +254,7 @@ namespace Ombi.UI.Modules
|
|||
Status = tv.Status,
|
||||
ImdbId = tv.ImdbId,
|
||||
Id = tv.Id,
|
||||
PosterPath = tv.PosterPath,
|
||||
PosterPath = tv.PosterPath.Contains("http:") ? tv.PosterPath.Replace("http:", "https:") : tv.PosterPath, // We make the poster path https on request, but this is just incase
|
||||
ReleaseDate = tv.ReleaseDate,
|
||||
ReleaseDateTicks = tv.ReleaseDate.Ticks,
|
||||
RequestedDate = tv.RequestedDate,
|
||||
|
@ -262,11 +273,30 @@ namespace Ombi.UI.Modules
|
|||
TvSeriesRequestType = tv.SeasonsRequested,
|
||||
Qualities = qualities.ToArray(),
|
||||
Episodes = tv.Episodes.ToArray(),
|
||||
RootFolders = rootFolders.ToArray(),
|
||||
HasRootFolders = rootFolders.Any(),
|
||||
CurrentRootPath = sonarrSettings.Enabled ? GetRootPath(tv.RootFolderSelected, sonarrSettings).Result : null
|
||||
}).ToList();
|
||||
|
||||
return Response.AsJson(viewModel);
|
||||
}
|
||||
|
||||
private async Task<string> GetRootPath(int pathId, SonarrSettings sonarrSettings)
|
||||
{
|
||||
var rootFoldersResult = await Cache.GetOrSetAsync(CacheKeys.SonarrRootFolders, async () =>
|
||||
{
|
||||
return await Task.Run(() => SonarrApi.GetRootFolders(sonarrSettings.ApiKey, sonarrSettings.FullUri));
|
||||
});
|
||||
|
||||
foreach (var r in rootFoldersResult.Where(r => r.id == pathId))
|
||||
{
|
||||
return r.path;
|
||||
}
|
||||
|
||||
// Return default path
|
||||
return rootFoldersResult.FirstOrDefault(x => x.id.Equals(int.Parse(sonarrSettings.RootPath)))?.path ?? string.Empty;
|
||||
}
|
||||
|
||||
private async Task<Response> GetAlbumRequests()
|
||||
{
|
||||
var settings = PrSettings.GetSettings();
|
||||
|
@ -430,5 +460,34 @@ namespace Ombi.UI.Modules
|
|||
|
||||
return Response.AsJson(vm);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<Response> ChangeRootFolder(int id, int rootFolderId)
|
||||
{
|
||||
// Get all root folders
|
||||
var settings = await SonarrSettings.GetSettingsAsync();
|
||||
var rootFolders = SonarrApi.GetRootFolders(settings.ApiKey, settings.FullUri);
|
||||
|
||||
// Get Request
|
||||
var allRequests = await Service.GetAllAsync();
|
||||
var request = allRequests.FirstOrDefault(x => x.Id == id);
|
||||
|
||||
if (request == null)
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel {Result = false});
|
||||
}
|
||||
|
||||
foreach (var folder in rootFolders)
|
||||
{
|
||||
if (folder.id.Equals(rootFolderId))
|
||||
{
|
||||
request.RootFolderSelected = folder.id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
await Service.UpdateRequestAsync(request);
|
||||
|
||||
return Response.AsJson(new JsonResponseModel {Result = true});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
62
Ombi.UI/Modules/SearchExtensionModule.cs
Normal file
62
Ombi.UI/Modules/SearchExtensionModule.cs
Normal file
|
@ -0,0 +1,62 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2017 Jamie Rees
|
||||
// File: SearchExtensionModule.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Nancy;
|
||||
using Ombi.Api.Interfaces;
|
||||
using Ombi.Core;
|
||||
using Ombi.Core.SettingModels;
|
||||
|
||||
namespace Ombi.UI.Modules
|
||||
{
|
||||
public class SearchExtensionModule : BaseAuthModule
|
||||
{
|
||||
public SearchExtensionModule(ISettingsService<PlexRequestSettings> pr, ISecurityExtensions security, INetflixApi netflix) : base("searchextension",pr, security)
|
||||
{
|
||||
NetflixApi = netflix;
|
||||
|
||||
Get["/netflix/{searchTerm}", true] = async (x, ctx) => await Netflix(x.searchTerm);
|
||||
}
|
||||
|
||||
private INetflixApi NetflixApi { get; }
|
||||
|
||||
|
||||
public async Task<Response> Netflix(string title)
|
||||
{
|
||||
var result = NetflixApi.CheckNetflix(title);
|
||||
|
||||
if (!string.IsNullOrEmpty(result.Message))
|
||||
{
|
||||
return Response.AsJson(new { Result = false });
|
||||
}
|
||||
|
||||
return Response.AsJson(new { Result = true, NetflixId = result.ShowId });
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -58,6 +58,7 @@ using Ombi.Store.Repository;
|
|||
using Ombi.UI.Helpers;
|
||||
using Ombi.UI.Models;
|
||||
using TMDbLib.Objects.General;
|
||||
using TraktApiSharp.Objects.Get.Shows;
|
||||
using Action = Ombi.Helpers.Analytics.Action;
|
||||
using EpisodesModel = Ombi.Store.EpisodesModel;
|
||||
using ISecurityExtensions = Ombi.Core.ISecurityExtensions;
|
||||
|
@ -76,7 +77,7 @@ namespace Ombi.UI.Modules
|
|||
ISettingsService<PlexSettings> plexService, ISettingsService<AuthenticationSettings> auth,
|
||||
IRepository<UsersToNotify> u, ISettingsService<EmailNotificationSettings> email,
|
||||
IIssueService issue, IAnalytics a, IRepository<RequestLimit> rl, ITransientFaultQueue tfQueue, IRepository<PlexContent> content,
|
||||
ISecurityExtensions security, IMovieSender movieSender)
|
||||
ISecurityExtensions security, IMovieSender movieSender, IRadarrCacher radarrCacher, ITraktApi traktApi, ISettingsService<CustomizationSettings> cus)
|
||||
: base("search", prSettings, security)
|
||||
{
|
||||
Auth = auth;
|
||||
|
@ -108,6 +109,9 @@ namespace Ombi.UI.Modules
|
|||
PlexContentRepository = content;
|
||||
MovieSender = movieSender;
|
||||
WatcherCacher = watcherCacher;
|
||||
RadarrCacher = radarrCacher;
|
||||
TraktApi = traktApi;
|
||||
CustomizationSettings = cus;
|
||||
|
||||
Get["SearchIndex", "/", true] = async (x, ct) => await RequestLoad();
|
||||
|
||||
|
@ -119,6 +123,13 @@ namespace Ombi.UI.Modules
|
|||
Get["movie/upcoming", true] = async (x, ct) => await UpcomingMovies();
|
||||
Get["movie/playing", true] = async (x, ct) => await CurrentlyPlayingMovies();
|
||||
|
||||
Get["tv/popular", true] = async (x, ct) => await ProcessShows(ShowSearchType.Popular);
|
||||
Get["tv/trending", true] = async (x, ct) => await ProcessShows(ShowSearchType.Trending);
|
||||
Get["tv/mostwatched", true] = async (x, ct) => await ProcessShows(ShowSearchType.MostWatched);
|
||||
Get["tv/anticipated", true] = async (x, ct) => await ProcessShows(ShowSearchType.Anticipated);
|
||||
|
||||
Get["tv/poster/{id}"] = p => GetTvPoster((int)p.id);
|
||||
|
||||
Post["request/movie", true] = async (x, ct) => await RequestMovie((int)Request.Form.movieId);
|
||||
Post["request/tv", true] =
|
||||
async (x, ct) => await RequestTvShow((int)Request.Form.tvId, (string)Request.Form.seasons);
|
||||
|
@ -128,6 +139,7 @@ namespace Ombi.UI.Modules
|
|||
Get["/seasons"] = x => GetSeasons();
|
||||
Get["/episodes", true] = async (x, ct) => await GetEpisodes();
|
||||
}
|
||||
private ITraktApi TraktApi { get; }
|
||||
private IWatcherCacher WatcherCacher { get; }
|
||||
private IMovieSender MovieSender { get; }
|
||||
private IRepository<PlexContent> PlexContentRepository { get; }
|
||||
|
@ -157,14 +169,23 @@ namespace Ombi.UI.Modules
|
|||
private IAnalytics Analytics { get; }
|
||||
private ITransientFaultQueue FaultQueue { get; }
|
||||
private IRepository<RequestLimit> RequestLimitRepo { get; }
|
||||
private IRadarrCacher RadarrCacher { get; }
|
||||
private ISettingsService<CustomizationSettings> CustomizationSettings { get; }
|
||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
private async Task<Negotiator> RequestLoad()
|
||||
{
|
||||
|
||||
var settings = await PrService.GetSettingsAsync();
|
||||
var custom = await CustomizationSettings.GetSettingsAsync();
|
||||
var searchViewModel = new SearchLoadViewModel
|
||||
{
|
||||
Settings = settings,
|
||||
CustomizationSettings = custom
|
||||
};
|
||||
|
||||
return View["Search/Index", settings];
|
||||
|
||||
return View["Search/Index", searchViewModel];
|
||||
}
|
||||
|
||||
private async Task<Response> UpcomingMovies()
|
||||
|
@ -188,6 +209,17 @@ namespace Ombi.UI.Modules
|
|||
return await ProcessMovies(MovieSearchType.Search, searchTerm);
|
||||
}
|
||||
|
||||
private Response GetTvPoster(int theTvDbId)
|
||||
{
|
||||
var result = TvApi.ShowLookupByTheTvDbId(theTvDbId);
|
||||
|
||||
var banner = result.image?.medium;
|
||||
if (!string.IsNullOrEmpty(banner))
|
||||
{
|
||||
banner = banner.Replace("http", "https"); // Always use the Https banners
|
||||
}
|
||||
return banner;
|
||||
}
|
||||
private async Task<Response> ProcessMovies(MovieSearchType searchType, string searchTerm)
|
||||
{
|
||||
List<MovieResult> apiMovies;
|
||||
|
@ -236,22 +268,13 @@ namespace Ombi.UI.Modules
|
|||
|
||||
var cpCached = CpCacher.QueuedIds();
|
||||
var watcherCached = WatcherCacher.QueuedIds();
|
||||
var radarrCached = RadarrCacher.QueuedIds();
|
||||
var content = PlexContentRepository.GetAll();
|
||||
var plexMovies = Checker.GetPlexMovies(content);
|
||||
var viewMovies = new List<SearchMovieViewModel>();
|
||||
var counter = 0;
|
||||
foreach (var movie in apiMovies)
|
||||
{
|
||||
var imdbId = string.Empty;
|
||||
if (counter <= 5) // Let's only do it for the first 5 items
|
||||
{
|
||||
var movieInfoTask = await MovieApi.GetMovieInformation(movie.Id).ConfigureAwait(false);
|
||||
// TODO needs to be careful about this, it's adding extra time to search...
|
||||
// https://www.themoviedb.org/talk/5807f4cdc3a36812160041f2
|
||||
imdbId = movieInfoTask?.ImdbId;
|
||||
counter++;
|
||||
}
|
||||
|
||||
var viewMovie = new SearchMovieViewModel
|
||||
{
|
||||
Adult = movie.Adult,
|
||||
|
@ -269,6 +292,28 @@ namespace Ombi.UI.Modules
|
|||
VoteAverage = movie.VoteAverage,
|
||||
VoteCount = movie.VoteCount
|
||||
};
|
||||
|
||||
var imdbId = string.Empty;
|
||||
if (counter <= 5) // Let's only do it for the first 5 items
|
||||
{
|
||||
var movieInfo = MovieApi.GetMovieInformationWithVideos(movie.Id);
|
||||
|
||||
// TODO needs to be careful about this, it's adding extra time to search...
|
||||
// https://www.themoviedb.org/talk/5807f4cdc3a36812160041f2
|
||||
viewMovie.ImdbId = movieInfo?.imdb_id;
|
||||
viewMovie.Homepage = movieInfo?.homepage;
|
||||
var videoId = movieInfo?.video ?? false
|
||||
? movieInfo?.videos?.results?.FirstOrDefault()?.key
|
||||
: string.Empty;
|
||||
|
||||
viewMovie.Trailer = string.IsNullOrEmpty(videoId)
|
||||
? string.Empty
|
||||
: $"https://www.youtube.com/watch?v={videoId}";
|
||||
|
||||
counter++;
|
||||
}
|
||||
|
||||
|
||||
var canSee = CanUserSeeThisRequest(viewMovie.Id, Security.HasPermissions(User, Permissions.UsersCanViewOnlyOwnRequests), dbMovies);
|
||||
var plexMovie = Checker.GetMovie(plexMovies.ToArray(), movie.Title, movie.ReleaseDate?.Year.ToString(),
|
||||
imdbId);
|
||||
|
@ -287,13 +332,19 @@ namespace Ombi.UI.Modules
|
|||
}
|
||||
else if (cpCached.Contains(movie.Id) && canSee) // compare to the couchpotato db
|
||||
{
|
||||
viewMovie.Approved = true;
|
||||
viewMovie.Requested = true;
|
||||
}
|
||||
else if(watcherCached.Contains(imdbId) && canSee) // compare to the watcher db
|
||||
{
|
||||
viewMovie.Approved = true;
|
||||
viewMovie.Requested = true;
|
||||
}
|
||||
else if (radarrCached.Contains(movie.Id) && canSee)
|
||||
{
|
||||
viewMovie.Approved = true;
|
||||
viewMovie.Requested = true;
|
||||
}
|
||||
|
||||
viewMovies.Add(viewMovie);
|
||||
}
|
||||
|
||||
|
@ -312,6 +363,186 @@ namespace Ombi.UI.Modules
|
|||
return true;
|
||||
}
|
||||
|
||||
private async Task<Response> ProcessShows(ShowSearchType type)
|
||||
{
|
||||
var shows = new List<SearchTvShowViewModel>();
|
||||
var prSettings = await PrService.GetSettingsAsync();
|
||||
switch (type)
|
||||
{
|
||||
case ShowSearchType.Popular:
|
||||
Analytics.TrackEventAsync(Category.Search, Action.TvShow, "Popular", Username, CookieHelper.GetAnalyticClientId(Cookies));
|
||||
var popularShows = await TraktApi.GetPopularShows();
|
||||
|
||||
foreach (var popularShow in popularShows)
|
||||
{
|
||||
var theTvDbId = int.Parse(popularShow.Ids.Tvdb.ToString());
|
||||
|
||||
var model = new SearchTvShowViewModel
|
||||
{
|
||||
FirstAired = popularShow.FirstAired?.ToString("yyyy-MM-ddTHH:mm:ss"),
|
||||
Id = theTvDbId,
|
||||
ImdbId = popularShow.Ids.Imdb,
|
||||
Network = popularShow.Network,
|
||||
Overview = popularShow.Overview.RemoveHtml(),
|
||||
Rating = popularShow.Rating.ToString(),
|
||||
Runtime = popularShow.Runtime.ToString(),
|
||||
SeriesName = popularShow.Title,
|
||||
Status = popularShow.Status.DisplayName,
|
||||
DisableTvRequestsByEpisode = prSettings.DisableTvRequestsByEpisode,
|
||||
DisableTvRequestsBySeason = prSettings.DisableTvRequestsBySeason,
|
||||
EnableTvRequestsForOnlySeries = (prSettings.DisableTvRequestsByEpisode && prSettings.DisableTvRequestsBySeason),
|
||||
Trailer = popularShow.Trailer,
|
||||
Homepage = popularShow.Homepage
|
||||
};
|
||||
shows.Add(model);
|
||||
}
|
||||
shows = await MapToTvModel(shows, prSettings);
|
||||
break;
|
||||
case ShowSearchType.Anticipated:
|
||||
Analytics.TrackEventAsync(Category.Search, Action.TvShow, "Anticipated", Username, CookieHelper.GetAnalyticClientId(Cookies));
|
||||
var anticipated = await TraktApi.GetAnticipatedShows();
|
||||
foreach (var anticipatedShow in anticipated)
|
||||
{
|
||||
var show = anticipatedShow.Show;
|
||||
var theTvDbId = int.Parse(show.Ids.Tvdb.ToString());
|
||||
|
||||
var model = new SearchTvShowViewModel
|
||||
{
|
||||
FirstAired = show.FirstAired?.ToString("yyyy-MM-ddTHH:mm:ss"),
|
||||
Id = theTvDbId,
|
||||
ImdbId = show.Ids.Imdb,
|
||||
Network = show.Network ?? string.Empty,
|
||||
Overview = show.Overview?.RemoveHtml() ?? string.Empty,
|
||||
Rating = show.Rating.ToString(),
|
||||
Runtime = show.Runtime.ToString(),
|
||||
SeriesName = show.Title,
|
||||
Status = show.Status?.DisplayName ?? string.Empty,
|
||||
DisableTvRequestsByEpisode = prSettings.DisableTvRequestsByEpisode,
|
||||
DisableTvRequestsBySeason = prSettings.DisableTvRequestsBySeason,
|
||||
EnableTvRequestsForOnlySeries = (prSettings.DisableTvRequestsByEpisode && prSettings.DisableTvRequestsBySeason),
|
||||
Trailer = show.Trailer,
|
||||
Homepage = show.Homepage
|
||||
};
|
||||
shows.Add(model);
|
||||
}
|
||||
shows = await MapToTvModel(shows, prSettings);
|
||||
break;
|
||||
case ShowSearchType.MostWatched:
|
||||
Analytics.TrackEventAsync(Category.Search, Action.TvShow, "MostWatched", Username, CookieHelper.GetAnalyticClientId(Cookies));
|
||||
var mostWatched = await TraktApi.GetMostWatchesShows();
|
||||
foreach (var watched in mostWatched)
|
||||
{
|
||||
var show = watched.Show;
|
||||
var theTvDbId = int.Parse(show.Ids.Tvdb.ToString());
|
||||
var model = new SearchTvShowViewModel
|
||||
{
|
||||
FirstAired = show.FirstAired?.ToString("yyyy-MM-ddTHH:mm:ss"),
|
||||
Id = theTvDbId,
|
||||
ImdbId = show.Ids.Imdb,
|
||||
Network = show.Network,
|
||||
Overview = show.Overview.RemoveHtml(),
|
||||
Rating = show.Rating.ToString(),
|
||||
Runtime = show.Runtime.ToString(),
|
||||
SeriesName = show.Title,
|
||||
Status = show.Status.DisplayName,
|
||||
DisableTvRequestsByEpisode = prSettings.DisableTvRequestsByEpisode,
|
||||
DisableTvRequestsBySeason = prSettings.DisableTvRequestsBySeason,
|
||||
EnableTvRequestsForOnlySeries = (prSettings.DisableTvRequestsByEpisode && prSettings.DisableTvRequestsBySeason),
|
||||
Trailer = show.Trailer,
|
||||
Homepage = show.Homepage
|
||||
};
|
||||
shows.Add(model);
|
||||
}
|
||||
shows = await MapToTvModel(shows, prSettings);
|
||||
break;
|
||||
case ShowSearchType.Trending:
|
||||
Analytics.TrackEventAsync(Category.Search, Action.TvShow, "Trending", Username, CookieHelper.GetAnalyticClientId(Cookies));
|
||||
var trending = await TraktApi.GetTrendingShows();
|
||||
foreach (var watched in trending)
|
||||
{
|
||||
var show = watched.Show;
|
||||
var theTvDbId = int.Parse(show.Ids.Tvdb.ToString());
|
||||
var model = new SearchTvShowViewModel
|
||||
{
|
||||
FirstAired = show.FirstAired?.ToString("yyyy-MM-ddTHH:mm:ss"),
|
||||
Id = theTvDbId,
|
||||
ImdbId = show.Ids.Imdb,
|
||||
Network = show.Network,
|
||||
Overview = show.Overview.RemoveHtml(),
|
||||
Rating = show.Rating.ToString(),
|
||||
Runtime = show.Runtime.ToString(),
|
||||
SeriesName = show.Title,
|
||||
Status = show.Status.DisplayName,
|
||||
DisableTvRequestsByEpisode = prSettings.DisableTvRequestsByEpisode,
|
||||
DisableTvRequestsBySeason = prSettings.DisableTvRequestsBySeason,
|
||||
EnableTvRequestsForOnlySeries = (prSettings.DisableTvRequestsByEpisode && prSettings.DisableTvRequestsBySeason),
|
||||
Trailer = show.Trailer,
|
||||
Homepage = show.Homepage
|
||||
};
|
||||
shows.Add(model);
|
||||
}
|
||||
shows = await MapToTvModel(shows, prSettings);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(type), type, null);
|
||||
}
|
||||
|
||||
|
||||
return Response.AsJson(shows);
|
||||
}
|
||||
|
||||
private async Task<List<SearchTvShowViewModel>> MapToTvModel(List<SearchTvShowViewModel> shows, PlexRequestSettings prSettings)
|
||||
{
|
||||
|
||||
var plexSettings = await PlexService.GetSettingsAsync();
|
||||
|
||||
var providerId = string.Empty;
|
||||
// Get the requests
|
||||
var allResults = await RequestService.GetAllAsync();
|
||||
allResults = allResults.Where(x => x.Type == RequestType.TvShow);
|
||||
var distinctResults = allResults.DistinctBy(x => x.ProviderId);
|
||||
var dbTv = distinctResults.ToDictionary(x => x.ProviderId);
|
||||
|
||||
// Check the external applications
|
||||
var sonarrCached = SonarrCacher.QueuedIds().ToList();
|
||||
var sickRageCache = SickRageCacher.QueuedIds(); // consider just merging sonarr/sickrage arrays
|
||||
var content = PlexContentRepository.GetAll();
|
||||
var plexTvShows = Checker.GetPlexTvShows(content).ToList();
|
||||
|
||||
foreach (var show in shows)
|
||||
{
|
||||
if (plexSettings.AdvancedSearch)
|
||||
{
|
||||
providerId = show.Id.ToString();
|
||||
}
|
||||
|
||||
var plexShow = Checker.GetTvShow(plexTvShows.ToArray(), show.SeriesName, show.FirstAired?.Substring(0, 4),
|
||||
providerId);
|
||||
if (plexShow != null)
|
||||
{
|
||||
show.Available = true;
|
||||
show.PlexUrl = plexShow.Url;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dbTv.ContainsKey(show.Id))
|
||||
{
|
||||
var dbt = dbTv[show.Id];
|
||||
|
||||
show.Requested = true;
|
||||
show.Episodes = dbt.Episodes.ToList();
|
||||
show.Approved = dbt.Approved;
|
||||
}
|
||||
if (sonarrCached.Select(x => x.TvdbId).Contains(show.Id) || sickRageCache.Contains(show.Id))
|
||||
// compare to the sonarr/sickrage db
|
||||
{
|
||||
show.Requested = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return shows;
|
||||
}
|
||||
|
||||
private async Task<Response> SearchTvShow(string searchTerm)
|
||||
{
|
||||
|
||||
|
@ -345,6 +576,10 @@ namespace Ombi.UI.Modules
|
|||
var viewTv = new List<SearchTvShowViewModel>();
|
||||
foreach (var t in apiTv)
|
||||
{
|
||||
if (!(t.show.externals?.thetvdb.HasValue) ?? false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var banner = t.show.image?.medium;
|
||||
if (!string.IsNullOrEmpty(banner))
|
||||
{
|
||||
|
@ -574,7 +809,17 @@ namespace Ombi.UI.Modules
|
|||
if (result.Result)
|
||||
{
|
||||
return await AddRequest(model, settings,
|
||||
$"{fullMovieName} {Resources.UI.Search_SuccessfullyAdded}");
|
||||
$"{fullMovieName} {Resources.UI.Search_SuccessfullyAdded}");
|
||||
}
|
||||
if (result.Error)
|
||||
|
||||
{
|
||||
return
|
||||
Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Message = "Could not add movie, please contract your administrator",
|
||||
Result = false
|
||||
});
|
||||
}
|
||||
if (!result.MovieSendingEnabled)
|
||||
{
|
||||
|
@ -679,11 +924,13 @@ namespace Ombi.UI.Modules
|
|||
DateTime.TryParse(showInfo.premiered, out firstAir);
|
||||
string fullShowName = $"{showInfo.name} ({firstAir.Year})";
|
||||
|
||||
// For some reason the poster path is always http
|
||||
var posterPath = showInfo.image?.medium.Replace("http:", "https:");
|
||||
var model = new RequestedModel
|
||||
{
|
||||
Type = RequestType.TvShow,
|
||||
Overview = showInfo.summary.RemoveHtml(),
|
||||
PosterPath = showInfo.image?.medium,
|
||||
PosterPath = posterPath,
|
||||
Title = showInfo.name,
|
||||
ReleaseDate = firstAir,
|
||||
Status = showInfo.status,
|
||||
|
@ -762,6 +1009,11 @@ namespace Ombi.UI.Modules
|
|||
existingRequest.Episodes.AddRange(newEpisodes ?? Enumerable.Empty<EpisodesModel>());
|
||||
|
||||
// It's technically a new request now, so set the status to not approved.
|
||||
var autoApprove = ShouldAutoApprove(RequestType.TvShow);
|
||||
if (autoApprove)
|
||||
{
|
||||
return await SendTv(model, sonarrSettings, existingRequest, fullShowName, settings);
|
||||
}
|
||||
existingRequest.Approved = false;
|
||||
|
||||
return await AddUserToRequest(existingRequest, settings, fullShowName, true);
|
||||
|
@ -888,54 +1140,7 @@ namespace Ombi.UI.Modules
|
|||
{
|
||||
if (ShouldAutoApprove(RequestType.TvShow))
|
||||
{
|
||||
model.Approved = true;
|
||||
var s = await sonarrSettings;
|
||||
var sender = new TvSenderOld(SonarrApi, SickrageApi); // TODO put back
|
||||
if (s.Enabled)
|
||||
{
|
||||
var result = await sender.SendToSonarr(s, model);
|
||||
if (!string.IsNullOrEmpty(result?.title))
|
||||
{
|
||||
if (existingRequest != null)
|
||||
{
|
||||
return await UpdateRequest(model, settings,
|
||||
$"{fullShowName} {Resources.UI.Search_SuccessfullyAdded}");
|
||||
}
|
||||
return
|
||||
await
|
||||
AddRequest(model, settings,
|
||||
$"{fullShowName} {Resources.UI.Search_SuccessfullyAdded}");
|
||||
}
|
||||
Log.Debug("Error with sending to sonarr.");
|
||||
return
|
||||
Response.AsJson(ValidationHelper.SendSonarrError(result?.ErrorMessages ?? new List<string>()));
|
||||
}
|
||||
|
||||
var srSettings = SickRageService.GetSettings();
|
||||
if (srSettings.Enabled)
|
||||
{
|
||||
var result = sender.SendToSickRage(srSettings, model);
|
||||
if (result?.result == "success")
|
||||
{
|
||||
return await AddRequest(model, settings,
|
||||
$"{fullShowName} {Resources.UI.Search_SuccessfullyAdded}");
|
||||
}
|
||||
return
|
||||
Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = false,
|
||||
Message = result?.message ?? Resources.UI.Search_SickrageError
|
||||
});
|
||||
}
|
||||
|
||||
if (!srSettings.Enabled && !s.Enabled)
|
||||
{
|
||||
return await AddRequest(model, settings, $"{fullShowName} {Resources.UI.Search_SuccessfullyAdded}");
|
||||
}
|
||||
|
||||
return
|
||||
Response.AsJson(new JsonResponseModel { Result = false, Message = Resources.UI.Search_TvNotSetUp });
|
||||
|
||||
return await SendTv(model, sonarrSettings, existingRequest, fullShowName, settings);
|
||||
}
|
||||
return await AddRequest(model, settings, $"{fullShowName} {Resources.UI.Search_SuccessfullyAdded}");
|
||||
}
|
||||
|
@ -1385,5 +1590,64 @@ namespace Ombi.UI.Modules
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private enum ShowSearchType
|
||||
{
|
||||
Popular,
|
||||
Anticipated,
|
||||
MostWatched,
|
||||
Trending
|
||||
}
|
||||
|
||||
private async Task<Response> SendTv(RequestedModel model, Task<SonarrSettings> sonarrSettings, RequestedModel existingRequest, string fullShowName, PlexRequestSettings settings)
|
||||
{
|
||||
model.Approved = true;
|
||||
var s = await sonarrSettings;
|
||||
var sender = new TvSenderOld(SonarrApi, SickrageApi, Cache); // TODO put back
|
||||
if (s.Enabled)
|
||||
{
|
||||
var result = await sender.SendToSonarr(s, model);
|
||||
if (!string.IsNullOrEmpty(result?.title))
|
||||
{
|
||||
if (existingRequest != null)
|
||||
{
|
||||
return await UpdateRequest(model, settings,
|
||||
$"{fullShowName} {Resources.UI.Search_SuccessfullyAdded}");
|
||||
}
|
||||
return
|
||||
await
|
||||
AddRequest(model, settings,
|
||||
$"{fullShowName} {Resources.UI.Search_SuccessfullyAdded}");
|
||||
}
|
||||
Log.Debug("Error with sending to sonarr.");
|
||||
return
|
||||
Response.AsJson(ValidationHelper.SendSonarrError(result?.ErrorMessages ?? new List<string>()));
|
||||
}
|
||||
|
||||
var srSettings = SickRageService.GetSettings();
|
||||
if (srSettings.Enabled)
|
||||
{
|
||||
var result = sender.SendToSickRage(srSettings, model);
|
||||
if (result?.result == "success")
|
||||
{
|
||||
return await AddRequest(model, settings,
|
||||
$"{fullShowName} {Resources.UI.Search_SuccessfullyAdded}");
|
||||
}
|
||||
return
|
||||
Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = false,
|
||||
Message = result?.message ?? Resources.UI.Search_SickrageError
|
||||
});
|
||||
}
|
||||
|
||||
if (!srSettings.Enabled && !s.Enabled)
|
||||
{
|
||||
return await AddRequest(model, settings, $"{fullShowName} {Resources.UI.Search_SuccessfullyAdded}");
|
||||
}
|
||||
|
||||
return
|
||||
Response.AsJson(new JsonResponseModel { Result = false, Message = Resources.UI.Search_TvNotSetUp });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue