diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 333cd4410..15fb00eff 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -55,23 +55,20 @@ jobs: versioning: runs-on: ubuntu-latest + needs: [ build-ui, unit-test ] outputs: changelog: ${{ steps.changelog.outputs.clean_changelog }} tag: ${{ steps.changelog.outputs.tag }} version: ${{ steps.changelog.outputs.version }} steps: - uses: actions/checkout@v2 - # This is only to get the next version number so we can set the version before compile - name: Conventional Changelog Action id: changelog uses: TriPSs/conventional-changelog-action@v3 with: - skip-version-file: 'true' - skip-commit: 'true' - version-file: 'version.json' - output-file: 'false' - skip-on-empty: 'false' - git-push: 'false' + version-file: 'version.json' + skip-on-empty: 'false' + git-message: 'chore(release): :rocket: {version}' - name: Output version run: | @@ -162,14 +159,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Conventional Changelog Action - id: changelog - uses: TriPSs/conventional-changelog-action@v3 - with: - version-file: 'version.json' - skip-on-empty: 'false' - git-message: 'chore(release): :rocket: {version}' - + - name: Download Artifacts id: download uses: actions/download-artifact@v2 @@ -192,9 +182,9 @@ jobs: if: contains(github.ref, 'develop') with: prerelease: true - body: ${{ steps.changelog.outputs.changelog }} - name: ${{ steps.changelog.outputs.tag }} - tag_name: ${{ steps.changelog.outputs.tag }} + body: ${{ needs.versioning.outputs.changelog }} + name: ${{ needs.versioning.outputs.tag }} + tag_name: ${{ needs.versioning.outputs.tag }} files: | artifacts/**/*.tar.gz artifacts/**/*.zip @@ -209,7 +199,7 @@ jobs: url: 'https://api.github.com/repos/Ombi-app/Ombi.Apt/actions/workflows/build-deb.yml/dispatches' method: 'POST' contentType: 'application/json' - data: '{ "ref":"main", "inputs": { "version": "${{ steps.changelog.outputs.tag }}"} }' + data: '{ "ref":"main", "inputs": { "version": "${{ needs.versioning.outputs.tag }}"} }' customHeaders: "{'Accept':'application/vnd.github.v3+json', 'Authorization':'Bearer ${{secrets.APT_PAT}}', 'User-Agent':'Ombi'}" diff --git a/src/Ombi.Helpers/NotificationType.cs b/src/Ombi.Helpers/NotificationType.cs index 8ea542063..bd6bd6f87 100644 --- a/src/Ombi.Helpers/NotificationType.cs +++ b/src/Ombi.Helpers/NotificationType.cs @@ -14,5 +14,6 @@ IssueResolved = 9, IssueComment = 10, Newsletter = 11, + PartiallyAvailable = 12 } } diff --git a/src/Ombi.Notifications.Tests/NotificationMessageCurlysTests.cs b/src/Ombi.Notifications.Tests/NotificationMessageCurlysTests.cs index 655ffeb23..960e552df 100644 --- a/src/Ombi.Notifications.Tests/NotificationMessageCurlysTests.cs +++ b/src/Ombi.Notifications.Tests/NotificationMessageCurlysTests.cs @@ -213,7 +213,51 @@ namespace Ombi.Notifications.Tests [Test] public void TvNotificationTests() { - var notificationOptions = new NotificationOptions(); + var notificationOptions = new NotificationOptions + { + NotificationType = Helpers.NotificationType.PartiallyAvailable + }; + var req = F.Build() + .With(x => x.RequestType, RequestType.TvShow) + .With(x => x.Available, true) + .Create(); + var customization = new CustomizationSettings + { + ApplicationUrl = "url", + ApplicationName = "name" + }; + var userPrefs = new UserNotificationPreferences(); + sut.Setup(notificationOptions, req, customization, userPrefs); + + Assert.That(req.Id.ToString(), Is.EqualTo(sut.RequestId)); + Assert.That(req.ParentRequest.ExternalProviderId.ToString(), Is.EqualTo(sut.ProviderId)); + Assert.That(req.ParentRequest.Title.ToString(), Is.EqualTo(sut.Title)); + Assert.That(req.RequestedUser.UserName, Is.EqualTo(sut.RequestedUser)); + Assert.That(req.RequestedUser.Alias, Is.EqualTo(sut.Alias)); + Assert.That(req.RequestedDate.ToString("D"), Is.EqualTo(sut.RequestedDate)); + Assert.That("TV Show", Is.EqualTo(sut.Type)); + Assert.That(req.ParentRequest.Overview, Is.EqualTo(sut.Overview)); + Assert.That(req.ParentRequest.ReleaseDate.Year.ToString(), Is.EqualTo(sut.Year)); + Assert.That(req.DeniedReason, Is.EqualTo(sut.DenyReason)); + Assert.That(req.MarkedAsAvailable?.ToString("D"), Is.EqualTo(sut.AvailableDate)); + Assert.That("https://image.tmdb.org/t/p/w300/" + req.ParentRequest.PosterPath, Is.EqualTo(sut.PosterImage)); + Assert.That(req.DeniedReason, Is.EqualTo(sut.DenyReason)); + Assert.That(req.RequestedUser.Alias, Is.EqualTo(sut.UserPreference)); + Assert.That(null, Is.EqualTo(sut.AdditionalInformation)); + Assert.That("Available", Is.EqualTo(sut.RequestStatus)); + Assert.That("url", Is.EqualTo(sut.ApplicationUrl)); + Assert.That("name", Is.EqualTo(sut.ApplicationName)); + } + + [Test] + public void TvNotificationPartialAvailablilityTests() + { + var notificationOptions = new NotificationOptions { + NotificationType = Helpers.NotificationType.PartiallyAvailable + }; + + notificationOptions.Substitutes.Add("Season", "1"); + notificationOptions.Substitutes.Add("Episodes", "1, 2"); var req = F.Build() .With(x => x.RequestType, RequestType.TvShow) .With(x => x.Available, true) @@ -244,6 +288,8 @@ namespace Ombi.Notifications.Tests Assert.That("Available", Is.EqualTo(sut.RequestStatus)); Assert.That("url", Is.EqualTo(sut.ApplicationUrl)); Assert.That("name", Is.EqualTo(sut.ApplicationName)); + Assert.That(sut.PartiallyAvailableEpisodeNumbers, Is.EqualTo("1, 2")); + Assert.That(sut.PartiallyAvailableSeasonNumber, Is.EqualTo("1")); } [Test] diff --git a/src/Ombi.Notifications/Agents/DiscordNotification.cs b/src/Ombi.Notifications/Agents/DiscordNotification.cs index 4a0c369fc..0cc0c9b89 100644 --- a/src/Ombi.Notifications/Agents/DiscordNotification.cs +++ b/src/Ombi.Notifications/Agents/DiscordNotification.cs @@ -94,6 +94,10 @@ namespace Ombi.Notifications.Agents { await Run(model, settings, NotificationType.RequestAvailable); } + protected override async Task PartiallyAvailable(NotificationOptions model, DiscordNotificationSettings settings) + { + await Run(model, settings, NotificationType.PartiallyAvailable); + } protected override async Task Send(NotificationMessage model, DiscordNotificationSettings settings) { @@ -166,8 +170,6 @@ namespace Ombi.Notifications.Agents author.name = appName; } - - var embed = new DiscordEmbeds { fields = fields, diff --git a/src/Ombi.Notifications/Agents/EmailNotification.cs b/src/Ombi.Notifications/Agents/EmailNotification.cs index b6d03305b..f41406d0e 100644 --- a/src/Ombi.Notifications/Agents/EmailNotification.cs +++ b/src/Ombi.Notifications/Agents/EmailNotification.cs @@ -219,6 +219,25 @@ namespace Ombi.Notifications.Agents } + protected override async Task PartiallyAvailable(NotificationOptions model, EmailNotificationSettings settings) + { + var message = await LoadTemplate(NotificationType.PartiallyAvailable, model, settings); + if (message == null) + { + return; + } + + var plaintext = await LoadPlainTextMessage(NotificationType.PartiallyAvailable, model, settings); + message.Other.Add("PlainTextBody", plaintext); + + await SendToSubscribers(settings, message); + message.To = model.RequestType == RequestType.Movie + ? MovieRequest.RequestedUser.Email + : TvRequest.RequestedUser.Email; + + await Send(message, settings); + } + protected override async Task RequestApproved(NotificationOptions model, EmailNotificationSettings settings) { var message = await LoadTemplate(NotificationType.RequestApproved, model, settings); diff --git a/src/Ombi.Notifications/Agents/GotifyNotification.cs b/src/Ombi.Notifications/Agents/GotifyNotification.cs index e1c9fc1db..00b62c343 100644 --- a/src/Ombi.Notifications/Agents/GotifyNotification.cs +++ b/src/Ombi.Notifications/Agents/GotifyNotification.cs @@ -112,5 +112,10 @@ namespace Ombi.Notifications.Agents await Send(notification, settings); } + + protected override async Task PartiallyAvailable(NotificationOptions model, GotifySettings settings) + { + await Run(model, settings, NotificationType.PartiallyAvailable); + } } } diff --git a/src/Ombi.Notifications/Agents/LegacyMobileNotification.cs b/src/Ombi.Notifications/Agents/LegacyMobileNotification.cs index 33cec783c..c2d71e0d0 100644 --- a/src/Ombi.Notifications/Agents/LegacyMobileNotification.cs +++ b/src/Ombi.Notifications/Agents/LegacyMobileNotification.cs @@ -316,5 +316,25 @@ namespace Ombi.Notifications.Agents } } } + + protected override async Task PartiallyAvailable(NotificationOptions model, MobileNotificationSettings settings) + { + var parsed = await LoadTemplate(NotificationAgent.Mobile, NotificationType.PartiallyAvailable, model); + if (parsed.Disabled) + { + _logger.LogInformation($"Template {NotificationType.PartiallyAvailable} is disabled for {NotificationAgent.Mobile}"); + return; + } + var notification = new NotificationMessage + { + Message = parsed.Message, + }; + + // Send to user + var playerIds = GetUsers(model, NotificationType.PartiallyAvailable); + + await AddSubscribedUsers(playerIds); + await Send(playerIds, notification, settings, model); + } } } \ No newline at end of file diff --git a/src/Ombi.Notifications/Agents/MattermostNotification.cs b/src/Ombi.Notifications/Agents/MattermostNotification.cs index ed3084c19..39e87e334 100644 --- a/src/Ombi.Notifications/Agents/MattermostNotification.cs +++ b/src/Ombi.Notifications/Agents/MattermostNotification.cs @@ -90,6 +90,10 @@ namespace Ombi.Notifications.Agents { await Run(model, settings, NotificationType.RequestAvailable); } + protected override async Task PartiallyAvailable(NotificationOptions model, MattermostNotificationSettings settings) + { + await Run(model, settings, NotificationType.PartiallyAvailable); + } protected override async Task Send(NotificationMessage model, MattermostNotificationSettings settings) { diff --git a/src/Ombi.Notifications/Agents/MobileNotification.cs b/src/Ombi.Notifications/Agents/MobileNotification.cs index bb6454bfc..b7ada4838 100644 --- a/src/Ombi.Notifications/Agents/MobileNotification.cs +++ b/src/Ombi.Notifications/Agents/MobileNotification.cs @@ -359,5 +359,26 @@ namespace Ombi.Notifications.Agents } } } + + protected override async Task PartiallyAvailable(NotificationOptions model, MobileNotificationSettings settings) + { + var parsed = await LoadTemplate(NotificationAgent.Mobile, NotificationType.PartiallyAvailable, model); + if (parsed.Disabled) + { + _logger.LogInformation($"Template {NotificationType.PartiallyAvailable} is disabled for {NotificationAgent.Mobile}"); + return; + } + + var notification = new NotificationMessage + { + Message = parsed.Message, + Subject = "New Request", + Data = GetNotificationData(parsed, NotificationType.PartiallyAvailable) + }; + + // Get admin devices + var playerIds = await GetAdmins(NotificationType.PartiallyAvailable); + await Send(playerIds, notification, settings, model, true); + } } } \ No newline at end of file diff --git a/src/Ombi.Notifications/Agents/PushbulletNotification.cs b/src/Ombi.Notifications/Agents/PushbulletNotification.cs index 91a8120b2..fd4be12a2 100644 --- a/src/Ombi.Notifications/Agents/PushbulletNotification.cs +++ b/src/Ombi.Notifications/Agents/PushbulletNotification.cs @@ -121,5 +121,10 @@ namespace Ombi.Notifications.Agents await Send(notification, settings); } + + protected override async Task PartiallyAvailable(NotificationOptions model, PushbulletSettings settings) + { + await Run(model, settings, NotificationType.PartiallyAvailable); + } } } diff --git a/src/Ombi.Notifications/Agents/PushoverNotification.cs b/src/Ombi.Notifications/Agents/PushoverNotification.cs index d41e6d911..21352b8d6 100644 --- a/src/Ombi.Notifications/Agents/PushoverNotification.cs +++ b/src/Ombi.Notifications/Agents/PushoverNotification.cs @@ -121,5 +121,10 @@ namespace Ombi.Notifications.Agents }; await Send(notification, settings); } + + protected override async Task PartiallyAvailable(NotificationOptions model, PushoverSettings settings) + { + await Run(model, settings, NotificationType.PartiallyAvailable); + } } } diff --git a/src/Ombi.Notifications/Agents/SlackNotification.cs b/src/Ombi.Notifications/Agents/SlackNotification.cs index 5b82c1434..8dbf14a7d 100644 --- a/src/Ombi.Notifications/Agents/SlackNotification.cs +++ b/src/Ombi.Notifications/Agents/SlackNotification.cs @@ -138,5 +138,10 @@ namespace Ombi.Notifications.Agents notification.Other.Add("image", parsed.Image); await Send(notification, settings); } + + protected override async Task PartiallyAvailable(NotificationOptions model, SlackNotificationSettings settings) + { + await Run(model, settings, NotificationType.PartiallyAvailable); + } } } diff --git a/src/Ombi.Notifications/Agents/TelegramNotification.cs b/src/Ombi.Notifications/Agents/TelegramNotification.cs index 3acfc7331..6561a2494 100644 --- a/src/Ombi.Notifications/Agents/TelegramNotification.cs +++ b/src/Ombi.Notifications/Agents/TelegramNotification.cs @@ -115,5 +115,10 @@ namespace Ombi.Notifications.Agents }; await Send(notification, settings); } + + protected override async Task PartiallyAvailable(NotificationOptions model, TelegramSettings settings) + { + await Run(model, settings, NotificationType.PartiallyAvailable); + } } } diff --git a/src/Ombi.Notifications/Agents/WebhookNotification.cs b/src/Ombi.Notifications/Agents/WebhookNotification.cs index 2339b6abf..c226d048a 100644 --- a/src/Ombi.Notifications/Agents/WebhookNotification.cs +++ b/src/Ombi.Notifications/Agents/WebhookNotification.cs @@ -119,5 +119,10 @@ namespace Ombi.Notifications.Agents await Send(notification, settings); } + + protected override async Task PartiallyAvailable(NotificationOptions model, WebhookSettings settings) + { + await Run(model, settings, NotificationType.PartiallyAvailable); + } } } diff --git a/src/Ombi.Notifications/Agents/WhatsAppNotification.cs b/src/Ombi.Notifications/Agents/WhatsAppNotification.cs index 0f0cd5d09..cf97f498f 100644 --- a/src/Ombi.Notifications/Agents/WhatsAppNotification.cs +++ b/src/Ombi.Notifications/Agents/WhatsAppNotification.cs @@ -123,5 +123,10 @@ namespace Ombi.Notifications.Agents }; await Send(notification, settings); } + + protected override async Task PartiallyAvailable(NotificationOptions model, TwilioSettings settings) + { + await Run(model, settings, NotificationType.PartiallyAvailable); + } } } diff --git a/src/Ombi.Notifications/BaseNotification.cs b/src/Ombi.Notifications/BaseNotification.cs index 46db5d467..ac9de5736 100644 --- a/src/Ombi.Notifications/BaseNotification.cs +++ b/src/Ombi.Notifications/BaseNotification.cs @@ -110,6 +110,15 @@ namespace Ombi.Notifications case NotificationType.IssueComment: await IssueComment(model, notificationSettings); break; + case NotificationType.AdminNote: + break; + case NotificationType.WelcomeEmail: + break; + case NotificationType.Newsletter: + break; + case NotificationType.PartiallyAvailable: + await PartiallyAvailable(model, notificationSettings); + break; default: throw new ArgumentOutOfRangeException(); } @@ -236,6 +245,7 @@ namespace Ombi.Notifications protected abstract Task RequestDeclined(NotificationOptions model, T settings); protected abstract Task RequestApproved(NotificationOptions model, T settings); protected abstract Task AvailableRequest(NotificationOptions model, T settings); + protected abstract Task PartiallyAvailable(NotificationOptions model, T settings); protected abstract Task Send(NotificationMessage model, T settings); protected abstract Task Test(NotificationOptions model, T settings); } diff --git a/src/Ombi.Notifications/NotificationMessageCurlys.cs b/src/Ombi.Notifications/NotificationMessageCurlys.cs index 0dffe0b90..392fc9412 100644 --- a/src/Ombi.Notifications/NotificationMessageCurlys.cs +++ b/src/Ombi.Notifications/NotificationMessageCurlys.cs @@ -33,7 +33,7 @@ namespace Ombi.Notifications UserNotificationPreferences pref) { LoadIssues(opts); - LoadCommon(req, s, pref); + LoadCommon(req, s, pref, opts); LoadTitle(opts, req); ProviderId = req?.TheMovieDbId.ToString() ?? string.Empty; Year = req?.ReleaseDate.Year.ToString(); @@ -47,7 +47,7 @@ namespace Ombi.Notifications UserNotificationPreferences pref) { LoadIssues(opts); - LoadCommon(req, s, pref); + LoadCommon(req, s, pref, opts); LoadTitle(opts, req); ProviderId = req?.ParentRequest?.ExternalProviderId.ToString() ?? string.Empty; Year = req?.ParentRequest?.ReleaseDate.Year.ToString(); @@ -83,7 +83,7 @@ namespace Ombi.Notifications UserNotificationPreferences pref) { LoadIssues(opts); - LoadCommon(req, s, pref); + LoadCommon(req, s, pref, opts); LoadTitle(opts, req); ProviderId = req?.ForeignArtistId ?? string.Empty; Year = req?.ReleaseDate.Year.ToString(); @@ -106,7 +106,7 @@ namespace Ombi.Notifications : string.Empty; } - private void LoadCommon(BaseRequest req, CustomizationSettings s, UserNotificationPreferences pref) + private void LoadCommon(BaseRequest req, CustomizationSettings s, UserNotificationPreferences pref, NotificationOptions opts) { ApplicationName = string.IsNullOrEmpty(s?.ApplicationName) ? "Ombi" : s.ApplicationName; ApplicationUrl = s?.ApplicationUrl.HasValue() ?? false ? s.ApplicationUrl : string.Empty; @@ -137,6 +137,18 @@ namespace Ombi.Notifications { UserPreference = pref.Value.HasValue() ? pref.Value : Alias; } + + if (opts.NotificationType == NotificationType.PartiallyAvailable) + { + if (opts.Substitutes.TryGetValue("Season", out var sNumber)) + { + PartiallyAvailableSeasonNumber = sNumber; + } + if (opts.Substitutes.TryGetValue("Episodes", out var epNumber)) + { + PartiallyAvailableEpisodeNumbers = epNumber; + } + } } private static string HumanizeReturnType(RequestType? requestType) @@ -220,6 +232,8 @@ namespace Ombi.Notifications public string AvailableDate { get; set; } public string RequestStatus { get; set; } public string ProviderId { get; set; } + public string PartiallyAvailableEpisodeNumbers { get; set; } + public string PartiallyAvailableSeasonNumber { get; set; } // System Defined private string LongDate => DateTime.Now.ToString("D"); @@ -259,6 +273,8 @@ namespace Ombi.Notifications { nameof(AvailableDate), AvailableDate }, { nameof(RequestStatus), RequestStatus }, { nameof(ProviderId), ProviderId }, + { nameof(PartiallyAvailableEpisodeNumbers), PartiallyAvailableEpisodeNumbers }, + { nameof(PartiallyAvailableSeasonNumber), PartiallyAvailableSeasonNumber }, }; } } \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/ArrAvailabilityChecker.cs b/src/Ombi.Schedule/Jobs/ArrAvailabilityChecker.cs index d8676ab86..0020bb24f 100644 --- a/src/Ombi.Schedule/Jobs/ArrAvailabilityChecker.cs +++ b/src/Ombi.Schedule/Jobs/ArrAvailabilityChecker.cs @@ -154,24 +154,19 @@ namespace Ombi.Schedule.Jobs.Radarr { availableEpisode.Add(new AvailabilityModel { - Id = episode.Id + Id = episode.Id, + EpisodeNumber = episode.EpisodeNumber, + SeasonNumber = episode.Season.SeasonNumber }); episode.Available = true; } } } - //TODO Partial avilability notifications here if (availableEpisode.Any()) { - //await _hub.Clients.Clients(NotificationHub.AdminConnectionIds) - // .SendAsync(NotificationHub.NotificationEvent, "Sonarr Availability Checker found some new available episodes!"); await _tvRequest.Save(); } - //foreach(var c in availableEpisode) - //{ - // await _tvRepo.MarkEpisodeAsAvailable(c.Id); - //} // Check to see if all of the episodes in all seasons are available for this request var allAvailable = child.SeasonRequests.All(x => x.Episodes.All(c => c.Available)); @@ -193,6 +188,20 @@ namespace Ombi.Schedule.Jobs.Radarr Recipient = child.RequestedUser.Email }); } + else if (availableEpisode.Any()) + { + var notification = new NotificationOptions + { + DateTime = DateTime.Now, + NotificationType = NotificationType.PartiallyAvailable, + RequestId = child.Id, + RequestType = RequestType.TvShow, + Recipient = child.RequestedUser.Email, + }; + notification.Substitutes.Add("Season", availableEpisode.First().SeasonNumber.ToString()); + notification.Substitutes.Add("Episodes", string.Join(", ", availableEpisode.Select(x => x.EpisodeNumber))); + await _notification.Notify(notification); + } } await _tvRequest.Save(); diff --git a/src/Ombi.Schedule/Jobs/Plex/Models/AvailabilityModel.cs b/src/Ombi.Schedule/Jobs/AvailabilityModel.cs similarity index 52% rename from src/Ombi.Schedule/Jobs/Plex/Models/AvailabilityModel.cs rename to src/Ombi.Schedule/Jobs/AvailabilityModel.cs index 44d018404..34506cae0 100644 --- a/src/Ombi.Schedule/Jobs/Plex/Models/AvailabilityModel.cs +++ b/src/Ombi.Schedule/Jobs/AvailabilityModel.cs @@ -1,10 +1,10 @@ -using Ombi.Store.Entities; - -namespace Ombi.Schedule.Jobs.Plex.Models +namespace Ombi.Schedule.Jobs { public class AvailabilityModel { public int Id { get; set; } + public int SeasonNumber { get; set; } + public int EpisodeNumber { get; set; } public string RequestedUser { get; set; } } } diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyAvaliabilityChecker.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyAvaliabilityChecker.cs index 48537ef65..608d6cb82 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyAvaliabilityChecker.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyAvaliabilityChecker.cs @@ -1,31 +1,5 @@ -#region Copyright -// /************************************************************************ -// Copyright (c) 2017 Jamie Rees -// File: EmbyAvaliabilityCheker.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; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.SignalR; @@ -173,6 +147,7 @@ namespace Ombi.Schedule.Jobs.Emby x.Series.Title == child.Title); } + var availableEpisode = new List(); foreach (var season in child.SeasonRequests) { foreach (var episode in season.Episodes) @@ -188,11 +163,22 @@ namespace Ombi.Schedule.Jobs.Emby if (foundEp != null) { + availableEpisode.Add(new AvailabilityModel + { + Id = episode.Id, + EpisodeNumber = episode.EpisodeNumber, + SeasonNumber = episode.Season.SeasonNumber + }); episode.Available = true; } } } + if (availableEpisode.Any()) + { + await _tvRepo.Save(); + } + // Check to see if all of the episodes in all seasons are available for this request var allAvailable = child.SeasonRequests.All(x => x.Episodes.All(c => c.Available)); if (allAvailable) @@ -209,6 +195,20 @@ namespace Ombi.Schedule.Jobs.Emby Recipient = child.RequestedUser.Email }); } + else if (availableEpisode.Any()) + { + var notification = new NotificationOptions + { + DateTime = DateTime.Now, + NotificationType = NotificationType.PartiallyAvailable, + RequestId = child.Id, + RequestType = RequestType.TvShow, + Recipient = child.RequestedUser.Email, + }; + notification.Substitutes.Add("Season", availableEpisode.First().SeasonNumber.ToString()); + notification.Substitutes.Add("Episodes", string.Join(", ", availableEpisode.Select(x => x.EpisodeNumber))); + await _notificationService.Notify(notification); + } } await _tvRepo.Save(); diff --git a/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinAvaliabilityChecker.cs b/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinAvaliabilityChecker.cs index 60d017d93..1a621ebce 100644 --- a/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinAvaliabilityChecker.cs +++ b/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinAvaliabilityChecker.cs @@ -26,6 +26,7 @@ #endregion using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.SignalR; @@ -173,6 +174,7 @@ namespace Ombi.Schedule.Jobs.Jellyfin x.Series.Title == child.Title); } + var availableEpisode = new List(); foreach (var season in child.SeasonRequests) { foreach (var episode in season.Episodes) @@ -188,11 +190,22 @@ namespace Ombi.Schedule.Jobs.Jellyfin if (foundEp != null) { + availableEpisode.Add(new AvailabilityModel + { + Id = episode.Id, + EpisodeNumber = episode.EpisodeNumber, + SeasonNumber = episode.Season.SeasonNumber + }); episode.Available = true; } } } + if (availableEpisode.Any()) + { + await _tvRepo.Save(); + } + // Check to see if all of the episodes in all seasons are available for this request var allAvailable = child.SeasonRequests.All(x => x.Episodes.All(c => c.Available)); if (allAvailable) @@ -209,6 +222,20 @@ namespace Ombi.Schedule.Jobs.Jellyfin Recipient = child.RequestedUser.Email }); } + else if (availableEpisode.Any()) + { + var notification = new NotificationOptions + { + DateTime = DateTime.Now, + NotificationType = NotificationType.PartiallyAvailable, + RequestId = child.Id, + RequestType = RequestType.TvShow, + Recipient = child.RequestedUser.Email, + }; + notification.Substitutes.Add("Season", availableEpisode.First().SeasonNumber.ToString()); + notification.Substitutes.Add("Episodes", string.Join(", ", availableEpisode.Select(x => x.EpisodeNumber))); + await _notificationService.Notify(notification); + } } await _tvRepo.Save(); diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs b/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs index f203f297a..7a05f794b 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs @@ -127,22 +127,19 @@ namespace Ombi.Schedule.Jobs.Plex { availableEpisode.Add(new AvailabilityModel { - Id = episode.Id + Id = episode.Id, + EpisodeNumber = episode.EpisodeNumber, + SeasonNumber = episode.Season.SeasonNumber }); episode.Available = true; } } } - //TODO Partial avilability notifications here if (availableEpisode.Any()) { await _tvRepo.Save(); } - //foreach(var c in availableEpisode) - //{ - // await _tvRepo.MarkEpisodeAsAvailable(c.Id); - //} // Check to see if all of the episodes in all seasons are available for this request var allAvailable = child.SeasonRequests.All(x => x.Episodes.All(c => c.Available)); @@ -162,6 +159,20 @@ namespace Ombi.Schedule.Jobs.Plex Recipient = child.RequestedUser.Email }); } + else if (availableEpisode.Any()) + { + var notification = new NotificationOptions + { + DateTime = DateTime.Now, + NotificationType = NotificationType.PartiallyAvailable, + RequestId = child.Id, + RequestType = RequestType.TvShow, + Recipient = child.RequestedUser.Email, + }; + notification.Substitutes.Add("Season", availableEpisode.First().SeasonNumber.ToString()); + notification.Substitutes.Add("Episodes", string.Join(", " ,availableEpisode.Select(x => x.EpisodeNumber))); + await _notificationService.Notify(notification); + } } await _tvRepo.Save(); diff --git a/src/Ombi.Store/Context/OmbiContext.cs b/src/Ombi.Store/Context/OmbiContext.cs index 3b698a47f..0681d9ca0 100644 --- a/src/Ombi.Store/Context/OmbiContext.cs +++ b/src/Ombi.Store/Context/OmbiContext.cs @@ -208,6 +208,16 @@ namespace Ombi.Store.Context Enabled = true, }; break; + case NotificationType.PartiallyAvailable: + notificationToAdd = new NotificationTemplates + { + NotificationType = notificationType, + Message = "Your TV request is now partially available! Season {PartiallyAvailableSeasonNumber} Episodes {PartiallyAvailableEpisodeNumbers}!", + Subject = "{ApplicationName}: Partially Available Request!", + Agent = agent, + Enabled = true, + }; + break; default: throw new ArgumentOutOfRangeException(); } diff --git a/src/Ombi/.vscode/settings.json b/src/Ombi/.vscode/settings.json index 2d597fd8d..497cd5996 100644 --- a/src/Ombi/.vscode/settings.json +++ b/src/Ombi/.vscode/settings.json @@ -13,6 +13,8 @@ "discord.enabled": true, "conventionalCommits.scopes": [ "discover", - "request-limits" + "request-limits", + "notifications", + "settings" ] } diff --git a/src/Ombi/ClientApp/src/app/interfaces/INotificationSettings.ts b/src/Ombi/ClientApp/src/app/interfaces/INotificationSettings.ts index e1c52103c..f2efc3f6f 100644 --- a/src/Ombi/ClientApp/src/app/interfaces/INotificationSettings.ts +++ b/src/Ombi/ClientApp/src/app/interfaces/INotificationSettings.ts @@ -52,7 +52,7 @@ export enum NotificationType { IssueResolved = 9, IssueComment = 10, Newsletter = 11, - WhatsApp = 12, + PartiallyAvailable = 12, } export interface IDiscordNotifcationSettings extends INotificationSettings { diff --git a/src/Ombi/ClientApp/src/app/settings/plex/plex.component.ts b/src/Ombi/ClientApp/src/app/settings/plex/plex.component.ts index b1e4de171..8d42419fe 100644 --- a/src/Ombi/ClientApp/src/app/settings/plex/plex.component.ts +++ b/src/Ombi/ClientApp/src/app/settings/plex/plex.component.ts @@ -63,6 +63,7 @@ export class PlexComponent implements OnInit, OnDestroy { server.plexAuthToken = selectedServer.accessToken; server.port = parseInt(selectedServer.port); server.ssl = selectedServer.scheme === "http" ? false : true; + server.serverHostname = ""; this.notificationService.success(`Selected ${server.name}!`); } @@ -128,7 +129,7 @@ export class PlexComponent implements OnInit, OnDestroy { let invalid = false; this.settings.servers.forEach(server => { - if (server.serverHostname.length > 0 && !server.serverHostname.startsWith("http")) { + if (server.serverHostname && server.serverHostname.length > 0 && !server.serverHostname.startsWith("http")) { invalid = true; } });