diff --git a/CHANGELOG.md b/CHANGELOG.md
index fb91d9d96..bdb3b8539 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,6 @@
# Changelog
-## (unreleased)
+## v3.0.3477 (2018-07-18)
### **New Features**
diff --git a/build.cake b/build.cake
index 36368268e..d706e7b6b 100644
--- a/build.cake
+++ b/build.cake
@@ -1,10 +1,10 @@
#tool "nuget:?package=GitVersion.CommandLine"
#addin "Cake.Gulp"
-#addin "nuget:?package=Cake.Npm&version=0.13.0"
#addin "SharpZipLib"
#addin nuget:?package=Cake.Compression&version=0.1.4
#addin "Cake.Incubator"
+#addin "Cake.Yarn"
//////////////////////////////////////////////////////////////////////
// ARGUMENTS
@@ -122,36 +122,19 @@ Task("SetVersionInfo")
Task("NPM")
.Does(() => {
- var settings = new NpmInstallSettings {
- LogLevel = NpmLogLevel.Silent,
- WorkingDirectory = webProjDir,
- Production = true
- };
-
- NpmInstall(settings);
+ Yarn.FromPath(webProjDir).Install();
});
Task("Gulp Publish")
.IsDependentOn("NPM")
- .Does(() => {
-
- var runScriptSettings = new NpmRunScriptSettings {
- ScriptName="publish",
- WorkingDirectory = webProjDir,
- };
-
- NpmRunScript(runScriptSettings);
+ .Does(() => {
+ Yarn.FromPath(webProjDir).RunScript("publish");
});
Task("TSLint")
.Does(() =>
{
- var settings = new NpmRunScriptSettings {
- WorkingDirectory = webProjDir,
- ScriptName = "lint"
- };
-
- NpmRunScript(settings);
+ Yarn.FromPath(webProjDir).RunScript("lint");
});
Task("PrePublish")
diff --git a/src/Ombi.Api.Notifications/Ombi.Api.Notifications.csproj b/src/Ombi.Api.Notifications/Ombi.Api.Notifications.csproj
index a3651df3c..7b890e2dd 100644
--- a/src/Ombi.Api.Notifications/Ombi.Api.Notifications.csproj
+++ b/src/Ombi.Api.Notifications/Ombi.Api.Notifications.csproj
@@ -4,6 +4,10 @@
netstandard2.0
+
+
+
+
diff --git a/src/Ombi.Api.Sonarr/Models/Episode.cs b/src/Ombi.Api.Sonarr/Models/Episode.cs
index c17f5486c..b01e6fd8c 100644
--- a/src/Ombi.Api.Sonarr/Models/Episode.cs
+++ b/src/Ombi.Api.Sonarr/Models/Episode.cs
@@ -6,6 +6,30 @@ namespace Ombi.Api.Sonarr.Models
{
public class Episode
{
+ public Episode()
+ {
+
+ }
+
+ public Episode(Episode ep)
+ {
+ seriesId = ep.seriesId;
+ episodeFileId = ep.episodeFileId;
+ seasonNumber = ep.seasonNumber;
+ episodeNumber = ep.episodeNumber;
+ title = ep.title;
+ airDate = ep.airDate;
+ airDateUtc = ep.airDateUtc;
+ overview = ep.overview;
+ hasFile = ep.hasFile;
+ monitored = ep.monitored;
+ unverifiedSceneNumbering = ep.unverifiedSceneNumbering;
+ id = ep.id;
+ absoluteEpisodeNumber = ep.absoluteEpisodeNumber;
+ sceneAbsoluteEpisodeNumber = ep.sceneAbsoluteEpisodeNumber;
+ sceneEpisodeNumber = ep.sceneEpisodeNumber;
+ sceneSeasonNumber = ep.sceneSeasonNumber;
+ }
public int seriesId { get; set; }
public int episodeFileId { get; set; }
public int seasonNumber { get; set; }
@@ -27,6 +51,24 @@ namespace Ombi.Api.Sonarr.Models
public class Episodefile
{
+ public Episodefile()
+ {
+
+ }
+
+ public Episodefile(Episodefile e)
+ {
+ seriesId = e.seriesId;
+ seasonNumber = e.seasonNumber;
+ relativePath = e.relativePath;
+ path = e.path;
+ size = e.size;
+ dateAdded = e.dateAdded;
+ sceneName = e.sceneName;
+ quality = new EpisodeQuality(e.quality);
+ qualityCutoffNotMet = e.qualityCutoffNotMet;
+ id = e.id;
+ }
public int seriesId { get; set; }
public int seasonNumber { get; set; }
public string relativePath { get; set; }
@@ -41,12 +83,32 @@ namespace Ombi.Api.Sonarr.Models
public class EpisodeQuality
{
+ public EpisodeQuality()
+ {
+
+ }
+
+ public EpisodeQuality(EpisodeQuality e)
+ {
+ quality = new Quality(e.quality);
+ revision = new Revision(e.revision);
+ }
public Quality quality { get; set; }
public Revision revision { get; set; }
}
public class Revision
{
+ public Revision()
+ {
+
+ }
+
+ public Revision(Revision r)
+ {
+ version = r.version;
+ real = r.real;
+ }
public int version { get; set; }
public int real { get; set; }
}
diff --git a/src/Ombi.Api.Sonarr/Models/Quality.cs b/src/Ombi.Api.Sonarr/Models/Quality.cs
index 76a1c92d8..9989a9c3e 100644
--- a/src/Ombi.Api.Sonarr/Models/Quality.cs
+++ b/src/Ombi.Api.Sonarr/Models/Quality.cs
@@ -2,6 +2,16 @@ namespace Ombi.Api.Sonarr.Models
{
public class Quality
{
+ public Quality()
+ {
+
+ }
+
+ public Quality(Quality q)
+ {
+ id = q.id;
+ name = q.name;
+ }
public int id { get; set; }
public string name { get; set; }
}
diff --git a/src/Ombi.Core.Tests/Rule/Search/EmbyAvailabilityRuleTests.cs b/src/Ombi.Core.Tests/Rule/Search/EmbyAvailabilityRuleTests.cs
index 0633d641e..99ff5b6bd 100644
--- a/src/Ombi.Core.Tests/Rule/Search/EmbyAvailabilityRuleTests.cs
+++ b/src/Ombi.Core.Tests/Rule/Search/EmbyAvailabilityRuleTests.cs
@@ -29,7 +29,10 @@ namespace Ombi.Core.Tests.Rule.Search
{
ProviderId = "123"
});
- var search = new SearchMovieViewModel();
+ var search = new SearchMovieViewModel()
+ {
+ TheMovieDbId = "123",
+ };
var result = await Rule.Execute(search);
Assert.True(result.Success);
diff --git a/src/Ombi.Core/Engine/Interfaces/ITvRequestEngine.cs b/src/Ombi.Core/Engine/Interfaces/ITvRequestEngine.cs
index 348dc91e7..36ae7da61 100644
--- a/src/Ombi.Core/Engine/Interfaces/ITvRequestEngine.cs
+++ b/src/Ombi.Core/Engine/Interfaces/ITvRequestEngine.cs
@@ -15,13 +15,13 @@ namespace Ombi.Core.Engine.Interfaces
Task DenyChildRequest(int requestId);
Task> GetRequestsLite(int count, int position, OrderFilterModel type);
Task> SearchTvRequest(string search);
- Task>>> SearchTvRequestTree(string search);
Task UpdateTvRequest(TvRequests request);
- Task>>> GetRequestsTreeNode(int count, int position);
Task> GetAllChldren(int tvId);
Task UpdateChildRequest(ChildRequests request);
Task RemoveTvChild(int requestId);
Task ApproveChildRequest(int id);
Task> GetRequestsLite();
+ Task UpdateQualityProfile(int requestId, int profileId);
+ Task UpdateRootPath(int requestId, int rootPath);
}
}
\ No newline at end of file
diff --git a/src/Ombi.Core/Engine/Interfaces/ITvSearchEngine.cs b/src/Ombi.Core/Engine/Interfaces/ITvSearchEngine.cs
index 53721f792..0926a7f9a 100644
--- a/src/Ombi.Core/Engine/Interfaces/ITvSearchEngine.cs
+++ b/src/Ombi.Core/Engine/Interfaces/ITvSearchEngine.cs
@@ -7,16 +7,10 @@ namespace Ombi.Core.Engine.Interfaces
public interface ITvSearchEngine
{
Task> Search(string searchTerm);
- Task>> SearchTreeNode(string searchTerm);
- Task> GetShowInformationTreeNode(int tvdbid);
Task GetShowInformation(int tvdbid);
- Task>> PopularTree();
Task> Popular();
- Task>> AnticipatedTree();
Task> Anticipated();
- Task>> MostWatchesTree();
Task> MostWatches();
- Task>> TrendingTree();
Task> Trending();
}
}
\ No newline at end of file
diff --git a/src/Ombi.Core/Engine/MovieRequestEngine.cs b/src/Ombi.Core/Engine/MovieRequestEngine.cs
index 15383ed12..f73c6fda1 100644
--- a/src/Ombi.Core/Engine/MovieRequestEngine.cs
+++ b/src/Ombi.Core/Engine/MovieRequestEngine.cs
@@ -452,6 +452,7 @@ namespace Ombi.Core.Engine
}
request.Available = true;
+ request.MarkedAsAvailable = DateTime.Now;
NotificationHelper.Notify(request, NotificationType.RequestAvailable);
await MovieRepository.Update(request);
diff --git a/src/Ombi.Core/Engine/TreeNode.cs b/src/Ombi.Core/Engine/TreeNode.cs
deleted file mode 100644
index 14f2f4cb0..000000000
--- a/src/Ombi.Core/Engine/TreeNode.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using System.Collections.Generic;
-
-namespace Ombi.Core.Engine
-{
-
- public class TreeNode
- {
- public string Label { get; set; }
- public T Data { get; set; }
- public List> Children { get; set; }
- public bool Leaf { get; set; }
- public bool Expanded { get; set; }
- }
-
- public class TreeNode
- {
- public string Label { get; set; }
- public T Data { get; set; }
- public List> Children { get; set; }
- public bool Leaf { get; set; }
- public bool Expanded { get; set; }
- }
-}
\ No newline at end of file
diff --git a/src/Ombi.Core/Engine/TvRequestEngine.cs b/src/Ombi.Core/Engine/TvRequestEngine.cs
index e8fc65a26..abb854021 100644
--- a/src/Ombi.Core/Engine/TvRequestEngine.cs
+++ b/src/Ombi.Core/Engine/TvRequestEngine.cs
@@ -171,24 +171,30 @@ namespace Ombi.Core.Engine
public async Task> GetRequestsLite(int count, int position, OrderFilterModel type)
{
var shouldHide = await HideFromOtherUsers();
- List allRequests;
+ List allRequests = null;
if (shouldHide.Hide)
{
- allRequests = await TvRepository.GetLite(shouldHide.UserId)
- .OrderByDescending(x => x.ChildRequests.Max(y => y.RequestedDate))
- .Skip(position).Take(count).ToListAsync();
+ var tv = TvRepository.GetLite(shouldHide.UserId);
+ if (tv.Any() && tv.Select(x => x.ChildRequests).Any())
+ {
+ allRequests = await tv.OrderByDescending(x => x.ChildRequests.Max(y => y.RequestedDate)).Skip(position).Take(count).ToListAsync();
+ }
// Filter out children
-
FilterChildren(allRequests, shouldHide);
}
else
{
- allRequests = await TvRepository.GetLite()
- .OrderByDescending(x => x.ChildRequests.Max(y => y.RequestedDate))
- .Skip(position).Take(count).ToListAsync();
+ var tv = TvRepository.GetLite();
+ if (tv.Any() && tv.Select(x => x.ChildRequests).Any())
+ {
+ allRequests = await tv.OrderByDescending(x => x.ChildRequests.Max(y => y.RequestedDate)).Skip(position).Take(count).ToListAsync();
+ }
+ }
+ if (allRequests == null)
+ {
+ return new RequestsViewModel();
}
-
allRequests.ForEach(async r => { await CheckForSubscription(shouldHide, r); });
return new RequestsViewModel
@@ -196,38 +202,6 @@ namespace Ombi.Core.Engine
Collection = allRequests
};
}
-
- public async Task>>> GetRequestsTreeNode(int count, int position)
- {
- var shouldHide = await HideFromOtherUsers();
- List allRequests;
- if (shouldHide.Hide)
- {
- allRequests = await TvRepository.Get(shouldHide.UserId)
- .Include(x => x.ChildRequests)
- .ThenInclude(x => x.SeasonRequests)
- .ThenInclude(x => x.Episodes)
- .Where(x => x.ChildRequests.Any())
- .OrderByDescending(x => x.ChildRequests.Max(y => y.RequestedDate))
- .Skip(position).Take(count).ToListAsync();
-
- FilterChildren(allRequests, shouldHide);
- }
- else
- {
- allRequests = await TvRepository.Get()
- .Include(x => x.ChildRequests)
- .ThenInclude(x => x.SeasonRequests)
- .ThenInclude(x => x.Episodes)
- .Where(x => x.ChildRequests.Any())
- .OrderByDescending(x => x.ChildRequests.Max(y => y.RequestedDate))
- .Skip(position).Take(count).ToListAsync();
- }
-
- allRequests.ForEach(async r => { await CheckForSubscription(shouldHide, r); });
- return ParseIntoTreeNode(allRequests);
- }
-
public async Task> GetRequests()
{
var shouldHide = await HideFromOtherUsers();
@@ -288,6 +262,10 @@ namespace Ombi.Core.Engine
private static void FilterChildren(IEnumerable allRequests, HideResult shouldHide)
{
+ if (allRequests == null)
+ {
+ return;
+ }
// Filter out children
foreach (var t in allRequests)
{
@@ -350,21 +328,22 @@ namespace Ombi.Core.Engine
return results;
}
- public async Task>>> SearchTvRequestTree(string search)
+ public async Task UpdateRootPath(int requestId, int rootPath)
{
- var shouldHide = await HideFromOtherUsers();
- IQueryable allRequests;
- if (shouldHide.Hide)
- {
- allRequests = TvRepository.Get(shouldHide.UserId);
- }
- else
- {
- allRequests = TvRepository.Get();
- }
- var results = await allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)).ToListAsync();
- results.ForEach(async r => { await CheckForSubscription(shouldHide, r); });
- return ParseIntoTreeNode(results);
+ var allRequests = TvRepository.Get();
+ var results = await allRequests.FirstOrDefaultAsync(x => x.Id == requestId);
+ results.RootFolder = rootPath;
+
+ await TvRepository.Update(results);
+ }
+
+ public async Task UpdateQualityProfile(int requestId, int profileId)
+ {
+ var allRequests = TvRepository.Get();
+ var results = await allRequests.FirstOrDefaultAsync(x => x.Id == requestId);
+ results.QualityOverride = profileId;
+
+ await TvRepository.Update(results);
}
public async Task UpdateTvRequest(TvRequests request)
@@ -516,6 +495,7 @@ namespace Ombi.Core.Engine
};
}
request.Available = true;
+ request.MarkedAsAvailable = DateTime.Now;
foreach (var season in request.SeasonRequests)
{
foreach (var e in season.Episodes)
@@ -585,29 +565,7 @@ namespace Ombi.Core.Engine
return await AfterRequest(model.ChildRequests.FirstOrDefault());
}
- private static List>> ParseIntoTreeNode(IEnumerable result)
- {
- var node = new List>>();
-
- foreach (var value in result)
- {
- node.Add(new TreeNode>
- {
- Data = value,
- Children = new List>>
- {
- new TreeNode>
- {
- Data = SortEpisodes(value.ChildRequests),
- Leaf = true
- }
- }
- });
- }
- return node;
- }
-
- private static List SortEpisodes(List items)
+ private static List SortEpisodes(List items)
{
foreach (var value in items)
{
diff --git a/src/Ombi.Core/Engine/TvSearchEngine.cs b/src/Ombi.Core/Engine/TvSearchEngine.cs
index bc5a2e984..253363ec1 100644
--- a/src/Ombi.Core/Engine/TvSearchEngine.cs
+++ b/src/Ombi.Core/Engine/TvSearchEngine.cs
@@ -59,11 +59,6 @@ namespace Ombi.Core.Engine
return null;
}
- public async Task>> SearchTreeNode(string searchTerm)
- {
- var result = await Search(searchTerm);
- return result.Select(ParseIntoTreeNode).ToList();
- }
public async Task GetShowInformation(int tvdbid)
{
var show = await TvMazeApi.ShowLookupByTheTvDbId(tvdbid);
@@ -116,19 +111,6 @@ namespace Ombi.Core.Engine
return await ProcessResult(mapped);
}
- public async Task> GetShowInformationTreeNode(int tvdbid)
- {
- var result = await GetShowInformation(tvdbid);
- return ParseIntoTreeNode(result);
- }
-
- public async Task>> PopularTree()
- {
- var result = await Cache.GetOrAdd(CacheKeys.PopularTv, async () => await TraktApi.GetPopularShows(), DateTime.Now.AddHours(12));
- var processed = await ProcessResults(result);
- return processed.Select(ParseIntoTreeNode).ToList();
- }
-
public async Task> Popular()
{
var result = await Cache.GetOrAdd(CacheKeys.PopularTv, async () => await TraktApi.GetPopularShows(), DateTime.Now.AddHours(12));
@@ -136,12 +118,6 @@ namespace Ombi.Core.Engine
return processed;
}
- public async Task>> AnticipatedTree()
- {
- var result = await Cache.GetOrAdd(CacheKeys.AnticipatedTv, async () => await TraktApi.GetAnticipatedShows(), DateTime.Now.AddHours(12));
- var processed = await ProcessResults(result);
- return processed.Select(ParseIntoTreeNode).ToList();
- }
public async Task> Anticipated()
{
@@ -150,12 +126,6 @@ namespace Ombi.Core.Engine
return processed;
}
- public async Task>> MostWatchesTree()
- {
- var result = await Cache.GetOrAdd(CacheKeys.MostWatchesTv, async () => await TraktApi.GetMostWatchesShows(), DateTime.Now.AddHours(12));
- var processed = await ProcessResults(result);
- return processed.Select(ParseIntoTreeNode).ToList();
- }
public async Task> MostWatches()
{
var result = await Cache.GetOrAdd(CacheKeys.MostWatchesTv, async () => await TraktApi.GetMostWatchesShows(), DateTime.Now.AddHours(12));
@@ -163,13 +133,6 @@ namespace Ombi.Core.Engine
return processed;
}
- public async Task>> TrendingTree()
- {
- var result = await Cache.GetOrAdd(CacheKeys.TrendingTv, async () => await TraktApi.GetTrendingShows(), DateTime.Now.AddHours(12));
- var processed = await ProcessResults(result);
- return processed.Select(ParseIntoTreeNode).ToList();
- }
-
public async Task> Trending()
{
var result = await Cache.GetOrAdd(CacheKeys.TrendingTv, async () => await TraktApi.GetTrendingShows(), DateTime.Now.AddHours(12));
@@ -177,22 +140,6 @@ namespace Ombi.Core.Engine
return processed;
}
- private static TreeNode ParseIntoTreeNode(SearchTvShowViewModel result)
- {
- return new TreeNode
- {
- Data = result,
- Children = new List>
- {
- new TreeNode
- {
- Data = result, Leaf = true
- }
- },
- Leaf = false
- };
- }
-
private async Task> ProcessResults(IEnumerable items)
{
var retVal = new List();
diff --git a/src/Ombi.Core/Engine/UserStatsEngine.cs b/src/Ombi.Core/Engine/UserStatsEngine.cs
new file mode 100644
index 000000000..f416d56e3
--- /dev/null
+++ b/src/Ombi.Core/Engine/UserStatsEngine.cs
@@ -0,0 +1,98 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.EntityFrameworkCore;
+using Ombi.Core.Authentication;
+using Ombi.Store.Entities;
+using Ombi.Store.Repository.Requests;
+
+namespace Ombi.Core.Engine
+{
+ public class UserStatsEngine
+ {
+ public UserStatsEngine(OmbiUserManager um, IMovieRequestRepository movieRequest, ITvRequestRepository tvRequest)
+ {
+ _userManager = um;
+ _movieRequest = movieRequest;
+ _tvRequest = tvRequest;
+ }
+
+ private readonly OmbiUserManager _userManager;
+ private readonly IMovieRequestRepository _movieRequest;
+ private readonly ITvRequestRepository _tvRequest;
+
+ public async Task GetSummary(SummaryRequest request)
+ {
+ /* What do we want?
+
+ This is Per week/month/all time (filter by date)
+
+ 1. Total Requests
+ 2. Total Movie Requests
+ 3. Total Tv Requests
+ 4. Total Issues (If enabled)
+ 5. Total Requests fufilled (now available)
+
+ Then
+
+ 2. Most requested user Movie
+ 3. Most requested user tv
+
+ Then
+
+ 1.
+
+ */
+
+ // get all movie requests
+ var movies = _movieRequest.GetWithUser();
+ var filteredMovies = movies.Where(x => x.RequestedDate >= request.From && x.RequestedDate <= request.To);
+ var tv = _tvRequest.GetLite();
+ var children = tv.SelectMany(x =>
+ x.ChildRequests.Where(c => c.RequestedDate >= request.From && c.RequestedDate <= request.To));
+
+ var moviesCount = filteredMovies.CountAsync();
+ var childrenCount = children.CountAsync();
+ var availableMovies =
+ movies.Select(x => x.MarkedAsAvailable >= request.From && x.MarkedAsAvailable <= request.To).CountAsync();
+ var availableChildren = tv.SelectMany(x =>
+ x.ChildRequests.Where(c => c.MarkedAsAvailable >= request.From && c.MarkedAsAvailable <= request.To)).CountAsync();
+
+ var userMovie = filteredMovies.GroupBy(x => x.RequestedUserId).OrderBy(x => x.Key).FirstOrDefaultAsync();
+ var userTv = children.GroupBy(x => x.RequestedUserId).OrderBy(x => x.Key).FirstOrDefaultAsync();
+
+
+ return new UserStatsSummary
+ {
+ TotalMovieRequests = await moviesCount,
+ TotalTvRequests = await childrenCount,
+ CompletedRequestsTv = await availableChildren,
+ CompletedRequestsMovies = await availableMovies,
+ MostRequestedUserMovie = (await userMovie).FirstOrDefault().RequestedUser,
+ MostRequestedUserTv = (await userTv).FirstOrDefault().RequestedUser,
+ };
+ }
+ }
+
+ public class SummaryRequest
+ {
+ public DateTime From { get; set; }
+ public DateTime To { get; set; }
+ }
+
+ public class UserStatsSummary
+ {
+ public int TotalRequests => TotalTvRequests + TotalTvRequests;
+ public int TotalMovieRequests { get; set; }
+ public int TotalTvRequests { get; set; }
+ public int TotalIssues { get; set; }
+ public int CompletedRequestsMovies { get; set; }
+ public int CompletedRequestsTv { get; set; }
+ public int CompletedRequests => CompletedRequestsMovies + CompletedRequestsTv;
+ public OmbiUser MostRequestedUserMovie { get; set; }
+ public OmbiUser MostRequestedUserTv { get; set; }
+
+ }
+}
diff --git a/src/Ombi.Core/Senders/TvSender.cs b/src/Ombi.Core/Senders/TvSender.cs
index 2cccd6778..3f5192ab8 100644
--- a/src/Ombi.Core/Senders/TvSender.cs
+++ b/src/Ombi.Core/Senders/TvSender.cs
@@ -165,25 +165,15 @@ namespace Ombi.Core.Senders
titleSlug = model.ParentRequest.Title,
addOptions = new AddOptions
{
- ignoreEpisodesWithFiles = true, // There shouldn't be any episodes with files, this is a new season
- ignoreEpisodesWithoutFiles = true, // We want all missing
+ ignoreEpisodesWithFiles = false, // There shouldn't be any episodes with files, this is a new season
+ ignoreEpisodesWithoutFiles = false, // We want all missing
searchForMissingEpisodes = false // we want dont want to search yet. We want to make sure everything is unmonitored/monitored correctly.
}
};
// Montitor the correct seasons,
// If we have that season in the model then it's monitored!
- var seasonsToAdd = new List();
- for (var i = 0; i < model.ParentRequest.TotalSeasons + 1; i++)
- {
- var index = i;
- var season = new Season
- {
- seasonNumber = i,
- monitored = model.SeasonRequests.Any(x => x.SeasonNumber == index && x.SeasonNumber != 0)
- };
- seasonsToAdd.Add(season);
- }
+ var seasonsToAdd = GetSeasonsToCreate(model);
newSeries.seasons = seasonsToAdd;
var result = await SonarrApi.AddSeries(newSeries, s.ApiKey, s.FullUri);
existingSeries = await SonarrApi.GetSeriesById(result.id, s.ApiKey, s.FullUri);
@@ -237,7 +227,7 @@ namespace Ombi.Core.Senders
{
var sonarrEp = sonarrEpList.FirstOrDefault(x =>
x.episodeNumber == ep.EpisodeNumber && x.seasonNumber == req.SeasonNumber);
- if (sonarrEp != null)
+ if (sonarrEp != null && !sonarrEp.monitored)
{
sonarrEp.monitored = true;
episodesToUpdate.Add(sonarrEp);
@@ -245,27 +235,64 @@ namespace Ombi.Core.Senders
}
}
var seriesChanges = false;
+
foreach (var season in model.SeasonRequests)
{
var sonarrSeason = sonarrEpList.Where(x => x.seasonNumber == season.SeasonNumber);
var sonarrEpCount = sonarrSeason.Count();
var ourRequestCount = season.Episodes.Count;
+ var existingSeason =
+ result.seasons.FirstOrDefault(x => x.seasonNumber == season.SeasonNumber);
+ if (existingSeason == null)
+ {
+ Logger.LogError("There was no season numer {0} in Sonarr for title {1}", season.SeasonNumber, model.ParentRequest.Title);
+ continue;
+ }
+
+
if (sonarrEpCount == ourRequestCount)
{
// We have the same amount of requests as all of the episodes in the season.
- var existingSeason =
- result.seasons.FirstOrDefault(x => x.seasonNumber == season.SeasonNumber);
- if (existingSeason == null)
+
+ if (!existingSeason.monitored)
{
- Logger.LogError("The sonarr ep count was the same as out request count, but could not match the season number {0}", season.SeasonNumber);
- continue;
+ existingSeason.monitored = true;
+ seriesChanges = true;
}
- existingSeason.monitored = true;
- seriesChanges = true;
}
else
{
+ // Make sure this season is set to monitored
+ if (!existingSeason.monitored)
+ {
+ // We need to monitor it, problem being is all episodes will now be monitored
+ // So we need to monior the series but unmonitor every episode
+ // Except the episodes that are already monitored before we update the series (we do not want to unmonitor episodes that are monitored beforehand)
+ existingSeason.monitored = true;
+ var sea = result.seasons.FirstOrDefault(x => x.seasonNumber == existingSeason.seasonNumber);
+ sea.monitored = true;
+ //var previouslyMonitoredEpisodes = sonarrEpList.Where(x =>
+ // x.seasonNumber == existingSeason.seasonNumber && x.monitored).Select(x => x.episodeNumber).ToList(); // We probably don't actually care about this
+ result = await SonarrApi.UpdateSeries(result, s.ApiKey, s.FullUri);
+ var epToUnmonitor = new List();
+ var newEpList = sonarrEpList.ConvertAll(ep => new Episode(ep)); // Clone it so we don't modify the orignal member
+ foreach (var ep in newEpList.Where(x => x.seasonNumber == existingSeason.seasonNumber).ToList())
+ {
+ //if (previouslyMonitoredEpisodes.Contains(ep.episodeNumber))
+ //{
+ // // This was previously monitored.
+ // continue;
+ //}
+ ep.monitored = false;
+ epToUnmonitor.Add(ep);
+ }
+
+ foreach (var epToUpdate in epToUnmonitor)
+ {
+ await SonarrApi.UpdateEpisode(epToUpdate, s.ApiKey, s.FullUri);
+ }
+ }
// Now update the episodes that need updating
foreach (var epToUpdate in episodesToUpdate.Where(x => x.seasonNumber == season.SeasonNumber))
{
@@ -285,6 +312,24 @@ namespace Ombi.Core.Senders
}
}
+ private static List GetSeasonsToCreate(ChildRequests model)
+ {
+ // Let's get a list of seasons just incase we need to change it
+ var seasonsToUpdate = new List();
+ for (var i = 0; i < model.ParentRequest.TotalSeasons + 1; i++)
+ {
+ var index = i;
+ var sea = new Season
+ {
+ seasonNumber = i,
+ monitored = model.SeasonRequests.Any(x => x.SeasonNumber == index && x.SeasonNumber != 0)
+ };
+ seasonsToUpdate.Add(sea);
+ }
+
+ return seasonsToUpdate;
+ }
+
private async Task SendToSickRage(ChildRequests model, SickRageSettings settings, string qualityId = null)
{
var tvdbid = model.ParentRequest.TvDbId;
diff --git a/src/Ombi.Notifications/NotificationMessageCurlys.cs b/src/Ombi.Notifications/NotificationMessageCurlys.cs
index f42126156..497c49c86 100644
--- a/src/Ombi.Notifications/NotificationMessageCurlys.cs
+++ b/src/Ombi.Notifications/NotificationMessageCurlys.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using Humanizer;
using Ombi.Helpers;
using Ombi.Notifications.Models;
using Ombi.Settings.Settings.Models;
@@ -39,7 +40,7 @@ namespace Ombi.Notifications
RequestedDate = req?.RequestedDate.ToString("D");
if (Type.IsNullOrEmpty())
{
- Type = req?.RequestType.ToString();
+ Type = req?.RequestType.Humanize();
}
Overview = req?.Overview;
Year = req?.ReleaseDate.Year.ToString();
@@ -91,7 +92,7 @@ namespace Ombi.Notifications
RequestedDate = req?.RequestedDate.ToString("D");
if (Type.IsNullOrEmpty())
{
- Type = req?.RequestType.ToString();
+ Type = req?.RequestType.Humanize();
}
Overview = req?.ParentRequest.Overview;
@@ -161,7 +162,7 @@ namespace Ombi.Notifications
IssueSubject = opts.Substitutes.TryGetValue("IssueSubject", out val) ? val : string.Empty;
NewIssueComment = opts.Substitutes.TryGetValue("NewIssueComment", out val) ? val : string.Empty;
UserName = opts.Substitutes.TryGetValue("IssueUser", out val) ? val : string.Empty;
- Type = opts.Substitutes.TryGetValue("RequestType", out val) ? val : string.Empty;
+ Type = opts.Substitutes.TryGetValue("RequestType", out val) ? val.Humanize() : string.Empty;
}
// User Defined
diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyAvaliabilityChecker.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyAvaliabilityChecker.cs
index ec675ebd8..7007b3743 100644
--- a/src/Ombi.Schedule/Jobs/Emby/EmbyAvaliabilityChecker.cs
+++ b/src/Ombi.Schedule/Jobs/Emby/EmbyAvaliabilityChecker.cs
@@ -89,6 +89,7 @@ namespace Ombi.Schedule.Jobs.Emby
_log.LogInformation("We have found the request {0} on Emby, sending the notification", movie?.Title ?? string.Empty);
movie.Available = true;
+ movie.MarkedAsAvailable = DateTime.Now;
if (movie.Available)
{
var recipient = movie.RequestedUser.Email.HasValue() ? movie.RequestedUser.Email : string.Empty;
@@ -185,6 +186,7 @@ namespace Ombi.Schedule.Jobs.Emby
{
// We have fulfulled this request!
child.Available = true;
+ child.MarkedAsAvailable = DateTime.Now;
BackgroundJob.Enqueue(() => _notificationService.Publish(new NotificationOptions
{
DateTime = DateTime.Now,
diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs b/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs
index 9978b7e2b..e0278f854 100644
--- a/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs
+++ b/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs
@@ -123,6 +123,7 @@ namespace Ombi.Schedule.Jobs.Plex
{
// We have fulfulled this request!
child.Available = true;
+ child.MarkedAsAvailable = DateTime.Now;
_backgroundJobClient.Enqueue(() => _notificationService.Publish(new NotificationOptions
{
DateTime = DateTime.Now,
@@ -163,6 +164,7 @@ namespace Ombi.Schedule.Jobs.Plex
}
movie.Available = true;
+ movie.MarkedAsAvailable = DateTime.Now;
if (movie.Available)
{
_backgroundJobClient.Enqueue(() => _notificationService.Publish(new NotificationOptions
diff --git a/src/Ombi.Store/Entities/Requests/BaseRequest.cs b/src/Ombi.Store/Entities/Requests/BaseRequest.cs
index 95395d0bf..f224032f1 100644
--- a/src/Ombi.Store/Entities/Requests/BaseRequest.cs
+++ b/src/Ombi.Store/Entities/Requests/BaseRequest.cs
@@ -8,10 +8,13 @@ namespace Ombi.Store.Entities.Requests
{
public string Title { get; set; }
public bool Approved { get; set; }
+ public DateTime MarkedAsApproved { get; set; }
public DateTime RequestedDate { get; set; }
public bool Available { get; set; }
+ public DateTime? MarkedAsAvailable { get; set; }
public string RequestedUserId { get; set; }
public bool? Denied { get; set; }
+ public DateTime MarkedAsDenied { get; set; }
public string DeniedReason { get; set; }
public RequestType RequestType { get; set; }
diff --git a/src/Ombi.Store/Migrations/20180730085903_UserStats.Designer.cs b/src/Ombi.Store/Migrations/20180730085903_UserStats.Designer.cs
new file mode 100644
index 000000000..1f34d280f
--- /dev/null
+++ b/src/Ombi.Store/Migrations/20180730085903_UserStats.Designer.cs
@@ -0,0 +1,988 @@
+//
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Ombi.Store.Context;
+
+namespace Ombi.Store.Migrations
+{
+ [DbContext(typeof(OmbiContext))]
+ [Migration("20180730085903_UserStats")]
+ partial class UserStats
+ {
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "2.1.1-rtm-30846");
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("ConcurrencyStamp")
+ .IsConcurrencyToken();
+
+ b.Property("Name")
+ .HasMaxLength(256);
+
+ b.Property("NormalizedName")
+ .HasMaxLength(256);
+
+ b.HasKey("Id");
+
+ b.HasIndex("NormalizedName")
+ .IsUnique()
+ .HasName("RoleNameIndex");
+
+ b.ToTable("AspNetRoles");
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("ClaimType");
+
+ b.Property("ClaimValue");
+
+ b.Property("RoleId")
+ .IsRequired();
+
+ b.HasKey("Id");
+
+ b.HasIndex("RoleId");
+
+ b.ToTable("AspNetRoleClaims");
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("ClaimType");
+
+ b.Property("ClaimValue");
+
+ b.Property("UserId")
+ .IsRequired();
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("AspNetUserClaims");
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b =>
+ {
+ b.Property("LoginProvider");
+
+ b.Property("ProviderKey");
+
+ b.Property("ProviderDisplayName");
+
+ b.Property("UserId")
+ .IsRequired();
+
+ b.HasKey("LoginProvider", "ProviderKey");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("AspNetUserLogins");
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b =>
+ {
+ b.Property("UserId");
+
+ b.Property("RoleId");
+
+ b.HasKey("UserId", "RoleId");
+
+ b.HasIndex("RoleId");
+
+ b.ToTable("AspNetUserRoles");
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b =>
+ {
+ b.Property("UserId");
+
+ b.Property("LoginProvider");
+
+ b.Property("Name");
+
+ b.Property("Value");
+
+ b.HasKey("UserId", "LoginProvider", "Name");
+
+ b.ToTable("AspNetUserTokens");
+ });
+
+ modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Type");
+
+ b.Property("Value");
+
+ b.HasKey("Id");
+
+ b.ToTable("ApplicationConfiguration");
+ });
+
+ modelBuilder.Entity("Ombi.Store.Entities.Audit", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("AuditArea");
+
+ b.Property("AuditType");
+
+ b.Property("DateTime");
+
+ b.Property("Description");
+
+ b.Property("User");
+
+ b.HasKey("Id");
+
+ b.ToTable("Audit");
+ });
+
+ modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("TheMovieDbId");
+
+ b.HasKey("Id");
+
+ b.ToTable("CouchPotatoCache");
+ });
+
+ modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("AddedAt");
+
+ b.Property("EmbyId")
+ .IsRequired();
+
+ b.Property("ImdbId");
+
+ b.Property("ProviderId");
+
+ b.Property("TheMovieDbId");
+
+ b.Property("Title");
+
+ b.Property("TvDbId");
+
+ b.Property("Type");
+
+ b.Property("Url");
+
+ b.HasKey("Id");
+
+ b.ToTable("EmbyContent");
+ });
+
+ modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("AddedAt");
+
+ b.Property("EmbyId");
+
+ b.Property("EpisodeNumber");
+
+ b.Property("ImdbId");
+
+ b.Property("ParentId");
+
+ b.Property("ProviderId");
+
+ b.Property("SeasonNumber");
+
+ b.Property("TheMovieDbId");
+
+ b.Property("Title");
+
+ b.Property("TvDbId");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ParentId");
+
+ b.ToTable("EmbyEpisode");
+ });
+
+ modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Content");
+
+ b.Property("SettingsName");
+
+ b.HasKey("Id");
+
+ b.ToTable("GlobalSettings");
+ });
+
+ modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Agent");
+
+ b.Property("Enabled");
+
+ b.Property("Message");
+
+ b.Property("NotificationType");
+
+ b.Property("Subject");
+
+ b.HasKey("Id");
+
+ b.ToTable("NotificationTemplates");
+ });
+
+ modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("AddedAt");
+
+ b.Property("PlayerId");
+
+ b.Property("UserId");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("NotificationUserId");
+ });
+
+ modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("AccessFailedCount");
+
+ b.Property("Alias");
+
+ b.Property("ConcurrencyStamp")
+ .IsConcurrencyToken();
+
+ b.Property("Email")
+ .HasMaxLength(256);
+
+ b.Property("EmailConfirmed");
+
+ b.Property("EmbyConnectUserId");
+
+ b.Property("EpisodeRequestLimit");
+
+ b.Property("LastLoggedIn");
+
+ b.Property("LockoutEnabled");
+
+ b.Property("LockoutEnd");
+
+ b.Property("MovieRequestLimit");
+
+ b.Property("NormalizedEmail")
+ .HasMaxLength(256);
+
+ b.Property("NormalizedUserName")
+ .HasMaxLength(256);
+
+ b.Property("PasswordHash");
+
+ b.Property("PhoneNumber");
+
+ b.Property("PhoneNumberConfirmed");
+
+ b.Property("ProviderUserId");
+
+ b.Property("SecurityStamp");
+
+ b.Property("TwoFactorEnabled");
+
+ b.Property("UserAccessToken");
+
+ b.Property("UserName")
+ .HasMaxLength(256);
+
+ b.Property("UserType");
+
+ b.HasKey("Id");
+
+ b.HasIndex("NormalizedEmail")
+ .HasName("EmailIndex");
+
+ b.HasIndex("NormalizedUserName")
+ .IsUnique()
+ .HasName("UserNameIndex");
+
+ b.ToTable("AspNetUsers");
+ });
+
+ modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("EpisodeNumber");
+
+ b.Property("GrandparentKey");
+
+ b.Property("Key");
+
+ b.Property("ParentKey");
+
+ b.Property("SeasonNumber");
+
+ b.Property("Title");
+
+ b.HasKey("Id");
+
+ b.HasIndex("GrandparentKey");
+
+ b.ToTable("PlexEpisode");
+ });
+
+ modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("ParentKey");
+
+ b.Property("PlexContentId");
+
+ b.Property("PlexServerContentId");
+
+ b.Property("SeasonKey");
+
+ b.Property("SeasonNumber");
+
+ b.HasKey("Id");
+
+ b.HasIndex("PlexServerContentId");
+
+ b.ToTable("PlexSeasonsContent");
+ });
+
+ modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("AddedAt");
+
+ b.Property("ImdbId");
+
+ b.Property("Key");
+
+ b.Property("Quality");
+
+ b.Property("ReleaseYear");
+
+ b.Property("TheMovieDbId");
+
+ b.Property("Title");
+
+ b.Property("TvDbId");
+
+ b.Property("Type");
+
+ b.Property("Url");
+
+ b.HasKey("Id");
+
+ b.ToTable("PlexServerContent");
+ });
+
+ modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("HasFile");
+
+ b.Property("TheMovieDbId");
+
+ b.HasKey("Id");
+
+ b.ToTable("RadarrCache");
+ });
+
+ modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("AddedAt");
+
+ b.Property("ContentId");
+
+ b.Property("ContentType");
+
+ b.Property("EpisodeNumber");
+
+ b.Property("SeasonNumber");
+
+ b.Property("Type");
+
+ b.HasKey("Id");
+
+ b.ToTable("RecentlyAddedLog");
+ });
+
+ modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Approved");
+
+ b.Property("Available");
+
+ b.Property("Denied");
+
+ b.Property("DeniedReason");
+
+ b.Property("IssueId");
+
+ b.Property("MarkedAsApproved");
+
+ b.Property("MarkedAsAvailable");
+
+ b.Property("MarkedAsDenied");
+
+ b.Property("ParentRequestId");
+
+ b.Property("RequestType");
+
+ b.Property("RequestedDate");
+
+ b.Property("RequestedUserId");
+
+ b.Property("SeriesType");
+
+ b.Property("Title");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ParentRequestId");
+
+ b.HasIndex("RequestedUserId");
+
+ b.ToTable("ChildRequests");
+ });
+
+ modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Value");
+
+ b.HasKey("Id");
+
+ b.ToTable("IssueCategory");
+ });
+
+ modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Comment");
+
+ b.Property("Date");
+
+ b.Property("IssuesId");
+
+ b.Property("UserId");
+
+ b.HasKey("Id");
+
+ b.HasIndex("IssuesId");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("IssueComments");
+ });
+
+ modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Description");
+
+ b.Property("IssueCategoryId");
+
+ b.Property("IssueId");
+
+ b.Property("ProviderId");
+
+ b.Property("RequestId");
+
+ b.Property("RequestType");
+
+ b.Property("ResovledDate");
+
+ b.Property("Status");
+
+ b.Property("Subject");
+
+ b.Property("Title");
+
+ b.Property("UserReportedId");
+
+ b.HasKey("Id");
+
+ b.HasIndex("IssueCategoryId");
+
+ b.HasIndex("IssueId");
+
+ b.HasIndex("UserReportedId");
+
+ b.ToTable("Issues");
+ });
+
+ modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Approved");
+
+ b.Property("Available");
+
+ b.Property("Background");
+
+ b.Property("Denied");
+
+ b.Property("DeniedReason");
+
+ b.Property("DigitalReleaseDate");
+
+ b.Property("ImdbId");
+
+ b.Property("IssueId");
+
+ b.Property("MarkedAsApproved");
+
+ b.Property("MarkedAsAvailable");
+
+ b.Property("MarkedAsDenied");
+
+ b.Property("Overview");
+
+ b.Property("PosterPath");
+
+ b.Property("QualityOverride");
+
+ b.Property("ReleaseDate");
+
+ b.Property("RequestType");
+
+ b.Property("RequestedDate");
+
+ b.Property