From 72eeca9938fdc507e2cff917a5fefe13c5707658 Mon Sep 17 00:00:00 2001 From: Brad Date: Mon, 23 Nov 2020 14:51:28 -0500 Subject: [PATCH] Updates --- TraktLists/Api/TraktURIs.cs | 4 +- TraktLists/Channels/ChannelHelper.cs | 28 ++-- .../Channels/PreConfigured/ChannelP10.cs | 127 ++++++++++++++++++ .../Channels/PreConfigured/ChannelP11.cs | 127 ++++++++++++++++++ .../Channels/PreConfigured/ChannelP3.cs | 127 ++++++++++++++++++ .../Channels/PreConfigured/ChannelP4.cs | 127 ++++++++++++++++++ .../Channels/PreConfigured/ChannelP5.cs | 127 ++++++++++++++++++ .../Channels/PreConfigured/ChannelP6.cs | 127 ++++++++++++++++++ .../Channels/PreConfigured/ChannelP7.cs | 127 ++++++++++++++++++ .../Channels/PreConfigured/ChannelP8.cs | 127 ++++++++++++++++++ .../Channels/PreConfigured/ChannelP9.cs | 127 ++++++++++++++++++ TraktLists/Config/configPage.html | 72 +++++++--- TraktLists/Plugin.cs | 2 +- .../ChannelUpdateScheduledTaskP10.cs | 51 +++++++ .../ChannelUpdateScheduledTaskP11.cs | 51 +++++++ .../ChannelUpdateScheduledTaskP3.cs | 51 +++++++ .../ChannelUpdateScheduledTaskP4.cs | 51 +++++++ .../ChannelUpdateScheduledTaskP5.cs | 51 +++++++ .../ChannelUpdateScheduledTaskP6.cs | 51 +++++++ .../ChannelUpdateScheduledTaskP7.cs | 51 +++++++ .../ChannelUpdateScheduledTaskP8.cs | 51 +++++++ .../ChannelUpdateScheduledTaskP9.cs | 51 +++++++ TraktLists/thumb.png | Bin 21794 -> 14924 bytes 23 files changed, 1669 insertions(+), 39 deletions(-) create mode 100644 TraktLists/Channels/PreConfigured/ChannelP10.cs create mode 100644 TraktLists/Channels/PreConfigured/ChannelP11.cs create mode 100644 TraktLists/Channels/PreConfigured/ChannelP3.cs create mode 100644 TraktLists/Channels/PreConfigured/ChannelP4.cs create mode 100644 TraktLists/Channels/PreConfigured/ChannelP5.cs create mode 100644 TraktLists/Channels/PreConfigured/ChannelP6.cs create mode 100644 TraktLists/Channels/PreConfigured/ChannelP7.cs create mode 100644 TraktLists/Channels/PreConfigured/ChannelP8.cs create mode 100644 TraktLists/Channels/PreConfigured/ChannelP9.cs create mode 100644 TraktLists/ScheduledTasks/PreConfigured/ChannelUpdateScheduledTaskP10.cs create mode 100644 TraktLists/ScheduledTasks/PreConfigured/ChannelUpdateScheduledTaskP11.cs create mode 100644 TraktLists/ScheduledTasks/PreConfigured/ChannelUpdateScheduledTaskP3.cs create mode 100644 TraktLists/ScheduledTasks/PreConfigured/ChannelUpdateScheduledTaskP4.cs create mode 100644 TraktLists/ScheduledTasks/PreConfigured/ChannelUpdateScheduledTaskP5.cs create mode 100644 TraktLists/ScheduledTasks/PreConfigured/ChannelUpdateScheduledTaskP6.cs create mode 100644 TraktLists/ScheduledTasks/PreConfigured/ChannelUpdateScheduledTaskP7.cs create mode 100644 TraktLists/ScheduledTasks/PreConfigured/ChannelUpdateScheduledTaskP8.cs create mode 100644 TraktLists/ScheduledTasks/PreConfigured/ChannelUpdateScheduledTaskP9.cs diff --git a/TraktLists/Api/TraktURIs.cs b/TraktLists/Api/TraktURIs.cs index fd87161..503987f 100644 --- a/TraktLists/Api/TraktURIs.cs +++ b/TraktLists/Api/TraktURIs.cs @@ -2,8 +2,8 @@ { public static class TraktUris { - public const string Id = "c44548028dcd8f31e9bee55318562e6e5deb8524f5ca3e77e167fd3b1c9ce380"; - public const string Secret = "d453bc07bcf42f72e3915715a5275d99de8381ff007c84d20e89ed1070310c89"; + public const string Id = "780354638b11da9a2e93e0c4498d6a1a0e8428a6da3cca70c98c19c8f0caeb52"; + public const string Secret = "113ea33e3283ef7349dfe0cb675aee83fe49be9ee540eb81fb1aec3f676b9f71"; #region POST URI's diff --git a/TraktLists/Channels/ChannelHelper.cs b/TraktLists/Channels/ChannelHelper.cs index dce48a8..7810e7f 100644 --- a/TraktLists/Channels/ChannelHelper.cs +++ b/TraktLists/Channels/ChannelHelper.cs @@ -24,7 +24,7 @@ public async Task GetChannelItemResult(IUserManager userManag ILibraryManager libraryManager, TraktChannel channelToConfigure, ILogger logger, TraktApi traktApi, CancellationToken cancellationToken) { - await Plugin.Instance.SchedTaskResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); + //await Plugin.Instance.SchedTaskResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); try { @@ -139,13 +139,7 @@ public async Task GetChannelItemResult(IUserManager userManag logger.Info(channelToConfigure.ChannelName); logger.Info("Found " + channelLibraryItem.Length.ToString()); - - // ReSharper disable once ComplexConditionExpression - var ids = libraryManager.GetInternalItemIds(new InternalItemsQuery - { - IncludeItemTypes = new[] {"Movie"} - }); var libraryItems = libraryManager.GetInternalItemIds(new InternalItemsQuery { @@ -177,15 +171,21 @@ public async Task GetChannelItemResult(IUserManager userManag logger.Info($"Count of items in emby {mediaItems.Count}"); var foundMovies = new List(); - var notFoundMovies = new List(); + //var notFoundMovies = new List(); logger.Info("Internal Ids: " + string.Join(", ", libraryItems.Select(x => x.ToString()).ToArray())); - foreach (var movie in mediaItems.OfType()) + foreach (var movie in mediaItems.OfType().OrderBy(x => x.Name)) { + if (channels.Select(x => x.ParentId).Contains(movie.Parent.ParentId)) + { + continue; + } + //Logger.Info($"Movie {movie.Name}"); //logger.Info(movie.InternalId.ToString()); + if (libraryItems.Contains(movie.InternalId)) continue; var foundMovie = Match.FindMatch(movie, listData); @@ -224,11 +224,6 @@ public async Task GetChannelItemResult(IUserManager userManag // } } } - else - { - notFoundMovies.Add(movie.Name); - //logger.Debug($"Movie {movie.Name} not found"); - } } logger.Info($"Count of items in List to add {newItems.Count}"); @@ -238,8 +233,7 @@ public async Task GetChannelItemResult(IUserManager userManag logger.Info($"Count of distinct items in List to add {distinctItems.Count}"); logger.Info("Found Movies: " + string.Join(", ", foundMovies.Select(x => x.ToString()).ToArray())); - logger.Info( - "Not Found Movies: " + string.Join(", ", notFoundMovies.Select(x => x.ToString()).ToArray())); + //logger.Info("Not Found Movies: " + string.Join(", ", notFoundMovies.Select(x => x.ToString()).ToArray())); logger.Info( "Not Found Movies: " + string.Join(", ", distinctItems.Select(x => x.Id.ToString()).ToArray())); @@ -252,7 +246,7 @@ public async Task GetChannelItemResult(IUserManager userManag } finally { - Plugin.Instance.SchedTaskResourcePool.Release(); + //Plugin.Instance.SchedTaskResourcePool.Release(); } } } diff --git a/TraktLists/Channels/PreConfigured/ChannelP10.cs b/TraktLists/Channels/PreConfigured/ChannelP10.cs new file mode 100644 index 0000000..19f7ce0 --- /dev/null +++ b/TraktLists/Channels/PreConfigured/ChannelP10.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Channels; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Channels; +using MediaBrowser.Model.Drawing; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Serialization; +using TraktLists.Api; +using TraktLists.Channels; + +namespace TraktLists +{ + public class ChannelP10 : IChannel, IHasCacheKey, IRequiresMediaInfoCallback + { + private IHttpClient HttpClient { get; } + private ILogger Logger { get; } + private IJsonSerializer JsonSerializer { get; } + private ILibraryManager LibraryManager { get; } + private IUserManager UserManager { get; } + private TraktApi TraktApi { get; } + + private readonly TraktChannel _channelToConfigure = Plugin.Instance.PluginConfiguration.PreConfiguredTraktChannels.Single(x => x.Id == 10); + + public ChannelP10(IHttpClient httpClient, ILogManager logManager, IJsonSerializer jsonSerializer, ILibraryManager lib, TraktApi traktApi, IUserManager userManager) + { + HttpClient = httpClient; + Logger = logManager.GetLogger(GetType().Name); + JsonSerializer = jsonSerializer; + LibraryManager = lib; + TraktApi = traktApi; + UserManager = userManager; + } + + public string DataVersion => "12"; + + public InternalChannelFeatures GetChannelFeatures() + { + return new InternalChannelFeatures + { + ContentTypes = new List + { + ChannelMediaContentType.Movie + }, + + MediaTypes = new List + { + ChannelMediaType.Video + }, + + SupportsContentDownloading = true, + SupportsSortOrderToggle = true, + }; + } + + private async Task GetChannelItemsInternal(CancellationToken cancellationToken) + { + var helper = new ChannelHelper(); + return await helper.GetChannelItemResult(UserManager, LibraryManager, _channelToConfigure, Logger, TraktApi, cancellationToken); + } + + public bool IsEnabledFor(string userId) + { + return true; + } + + public async Task GetChannelItems(InternalChannelItemQuery query, CancellationToken cancellationToken) + { + return await GetChannelItemsInternal(cancellationToken); + } + + public async Task GetChannelImage(ImageType type, CancellationToken cancellationToken) + { + switch (type) + { + case ImageType.Backdrop: + { + var path = GetType().Namespace + ".Images." + type.ToString().ToLower() + ".png"; + + return await Task.FromResult(new DynamicImageResponse + { + Format = ImageFormat.Png, + HasImage = true, + Stream = GetType().Assembly.GetManifestResourceStream(path) + }); + } + default: + throw new ArgumentException("Unsupported image type: " + type); + + } + } + + public IEnumerable GetSupportedChannelImages() + { + return new List { ImageType.Primary, ImageType.Thumb }; + } + + public string Name => _channelToConfigure.ChannelName; + public string Description { get; private set; } + public string HomePageUrl + { + get { return ""; } + } + + public ChannelParentalRating ParentalRating + { + get { return ChannelParentalRating.GeneralAudience; } + } + + public string GetCacheKey(string userId) + { + return Guid.NewGuid().ToString("N"); + } + + public Task> GetChannelItemMediaInfo(string id, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/TraktLists/Channels/PreConfigured/ChannelP11.cs b/TraktLists/Channels/PreConfigured/ChannelP11.cs new file mode 100644 index 0000000..f6f579f --- /dev/null +++ b/TraktLists/Channels/PreConfigured/ChannelP11.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Channels; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Channels; +using MediaBrowser.Model.Drawing; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Serialization; +using TraktLists.Api; +using TraktLists.Channels; + +namespace TraktLists +{ + public class ChannelP11 : IChannel, IHasCacheKey, IRequiresMediaInfoCallback + { + private IHttpClient HttpClient { get; } + private ILogger Logger { get; } + private IJsonSerializer JsonSerializer { get; } + private ILibraryManager LibraryManager { get; } + private IUserManager UserManager { get; } + private TraktApi TraktApi { get; } + + private readonly TraktChannel _channelToConfigure = Plugin.Instance.PluginConfiguration.PreConfiguredTraktChannels.Single(x => x.Id == 11); + + public ChannelP11(IHttpClient httpClient, ILogManager logManager, IJsonSerializer jsonSerializer, ILibraryManager lib, TraktApi traktApi, IUserManager userManager) + { + HttpClient = httpClient; + Logger = logManager.GetLogger(GetType().Name); + JsonSerializer = jsonSerializer; + LibraryManager = lib; + TraktApi = traktApi; + UserManager = userManager; + } + + public string DataVersion => "12"; + + public InternalChannelFeatures GetChannelFeatures() + { + return new InternalChannelFeatures + { + ContentTypes = new List + { + ChannelMediaContentType.Movie + }, + + MediaTypes = new List + { + ChannelMediaType.Video + }, + + SupportsContentDownloading = true, + SupportsSortOrderToggle = true, + }; + } + + private async Task GetChannelItemsInternal(CancellationToken cancellationToken) + { + var helper = new ChannelHelper(); + return await helper.GetChannelItemResult(UserManager, LibraryManager, _channelToConfigure, Logger, TraktApi, cancellationToken); + } + + public bool IsEnabledFor(string userId) + { + return true; + } + + public async Task GetChannelItems(InternalChannelItemQuery query, CancellationToken cancellationToken) + { + return await GetChannelItemsInternal(cancellationToken); + } + + public async Task GetChannelImage(ImageType type, CancellationToken cancellationToken) + { + switch (type) + { + case ImageType.Backdrop: + { + var path = GetType().Namespace + ".Images." + type.ToString().ToLower() + ".png"; + + return await Task.FromResult(new DynamicImageResponse + { + Format = ImageFormat.Png, + HasImage = true, + Stream = GetType().Assembly.GetManifestResourceStream(path) + }); + } + default: + throw new ArgumentException("Unsupported image type: " + type); + + } + } + + public IEnumerable GetSupportedChannelImages() + { + return new List { ImageType.Primary, ImageType.Thumb }; + } + + public string Name => _channelToConfigure.ChannelName; + public string Description { get; private set; } + public string HomePageUrl + { + get { return ""; } + } + + public ChannelParentalRating ParentalRating + { + get { return ChannelParentalRating.GeneralAudience; } + } + + public string GetCacheKey(string userId) + { + return Guid.NewGuid().ToString("N"); + } + + public Task> GetChannelItemMediaInfo(string id, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/TraktLists/Channels/PreConfigured/ChannelP3.cs b/TraktLists/Channels/PreConfigured/ChannelP3.cs new file mode 100644 index 0000000..c0144b3 --- /dev/null +++ b/TraktLists/Channels/PreConfigured/ChannelP3.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Channels; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Channels; +using MediaBrowser.Model.Drawing; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Serialization; +using TraktLists.Api; +using TraktLists.Channels; + +namespace TraktLists +{ + public class ChannelP3 : IChannel, IHasCacheKey, IRequiresMediaInfoCallback + { + private IHttpClient HttpClient { get; } + private ILogger Logger { get; } + private IJsonSerializer JsonSerializer { get; } + private ILibraryManager LibraryManager { get; } + private IUserManager UserManager { get; } + private TraktApi TraktApi { get; } + + private readonly TraktChannel _channelToConfigure = Plugin.Instance.PluginConfiguration.PreConfiguredTraktChannels.Single(x => x.Id == 3); + + public ChannelP3(IHttpClient httpClient, ILogManager logManager, IJsonSerializer jsonSerializer, ILibraryManager lib, TraktApi traktApi, IUserManager userManager) + { + HttpClient = httpClient; + Logger = logManager.GetLogger(GetType().Name); + JsonSerializer = jsonSerializer; + LibraryManager = lib; + TraktApi = traktApi; + UserManager = userManager; + } + + public string DataVersion => "12"; + + public InternalChannelFeatures GetChannelFeatures() + { + return new InternalChannelFeatures + { + ContentTypes = new List + { + ChannelMediaContentType.Movie + }, + + MediaTypes = new List + { + ChannelMediaType.Video + }, + + SupportsContentDownloading = true, + SupportsSortOrderToggle = true, + }; + } + + private async Task GetChannelItemsInternal(CancellationToken cancellationToken) + { + var helper = new ChannelHelper(); + return await helper.GetChannelItemResult(UserManager, LibraryManager, _channelToConfigure, Logger, TraktApi, cancellationToken); + } + + public bool IsEnabledFor(string userId) + { + return true; + } + + public async Task GetChannelItems(InternalChannelItemQuery query, CancellationToken cancellationToken) + { + return await GetChannelItemsInternal(cancellationToken); + } + + public async Task GetChannelImage(ImageType type, CancellationToken cancellationToken) + { + switch (type) + { + case ImageType.Backdrop: + { + var path = GetType().Namespace + ".Images." + type.ToString().ToLower() + ".png"; + + return await Task.FromResult(new DynamicImageResponse + { + Format = ImageFormat.Png, + HasImage = true, + Stream = GetType().Assembly.GetManifestResourceStream(path) + }); + } + default: + throw new ArgumentException("Unsupported image type: " + type); + + } + } + + public IEnumerable GetSupportedChannelImages() + { + return new List { ImageType.Primary, ImageType.Thumb }; + } + + public string Name => _channelToConfigure.ChannelName; + public string Description { get; private set; } + public string HomePageUrl + { + get { return ""; } + } + + public ChannelParentalRating ParentalRating + { + get { return ChannelParentalRating.GeneralAudience; } + } + + public string GetCacheKey(string userId) + { + return Guid.NewGuid().ToString("N"); + } + + public Task> GetChannelItemMediaInfo(string id, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/TraktLists/Channels/PreConfigured/ChannelP4.cs b/TraktLists/Channels/PreConfigured/ChannelP4.cs new file mode 100644 index 0000000..7a7bde2 --- /dev/null +++ b/TraktLists/Channels/PreConfigured/ChannelP4.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Channels; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Channels; +using MediaBrowser.Model.Drawing; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Serialization; +using TraktLists.Api; +using TraktLists.Channels; + +namespace TraktLists +{ + public class ChannelP4 : IChannel, IHasCacheKey, IRequiresMediaInfoCallback + { + private IHttpClient HttpClient { get; } + private ILogger Logger { get; } + private IJsonSerializer JsonSerializer { get; } + private ILibraryManager LibraryManager { get; } + private IUserManager UserManager { get; } + private TraktApi TraktApi { get; } + + private readonly TraktChannel _channelToConfigure = Plugin.Instance.PluginConfiguration.PreConfiguredTraktChannels.Single(x => x.Id == 4); + + public ChannelP4(IHttpClient httpClient, ILogManager logManager, IJsonSerializer jsonSerializer, ILibraryManager lib, TraktApi traktApi, IUserManager userManager) + { + HttpClient = httpClient; + Logger = logManager.GetLogger(GetType().Name); + JsonSerializer = jsonSerializer; + LibraryManager = lib; + TraktApi = traktApi; + UserManager = userManager; + } + + public string DataVersion => "12"; + + public InternalChannelFeatures GetChannelFeatures() + { + return new InternalChannelFeatures + { + ContentTypes = new List + { + ChannelMediaContentType.Movie + }, + + MediaTypes = new List + { + ChannelMediaType.Video + }, + + SupportsContentDownloading = true, + SupportsSortOrderToggle = true, + }; + } + + private async Task GetChannelItemsInternal(CancellationToken cancellationToken) + { + var helper = new ChannelHelper(); + return await helper.GetChannelItemResult(UserManager, LibraryManager, _channelToConfigure, Logger, TraktApi, cancellationToken); + } + + public bool IsEnabledFor(string userId) + { + return true; + } + + public async Task GetChannelItems(InternalChannelItemQuery query, CancellationToken cancellationToken) + { + return await GetChannelItemsInternal(cancellationToken); + } + + public async Task GetChannelImage(ImageType type, CancellationToken cancellationToken) + { + switch (type) + { + case ImageType.Backdrop: + { + var path = GetType().Namespace + ".Images." + type.ToString().ToLower() + ".png"; + + return await Task.FromResult(new DynamicImageResponse + { + Format = ImageFormat.Png, + HasImage = true, + Stream = GetType().Assembly.GetManifestResourceStream(path) + }); + } + default: + throw new ArgumentException("Unsupported image type: " + type); + + } + } + + public IEnumerable GetSupportedChannelImages() + { + return new List { ImageType.Primary, ImageType.Thumb }; + } + + public string Name => _channelToConfigure.ChannelName; + public string Description { get; private set; } + public string HomePageUrl + { + get { return ""; } + } + + public ChannelParentalRating ParentalRating + { + get { return ChannelParentalRating.GeneralAudience; } + } + + public string GetCacheKey(string userId) + { + return Guid.NewGuid().ToString("N"); + } + + public Task> GetChannelItemMediaInfo(string id, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/TraktLists/Channels/PreConfigured/ChannelP5.cs b/TraktLists/Channels/PreConfigured/ChannelP5.cs new file mode 100644 index 0000000..80e2373 --- /dev/null +++ b/TraktLists/Channels/PreConfigured/ChannelP5.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Channels; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Channels; +using MediaBrowser.Model.Drawing; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Serialization; +using TraktLists.Api; +using TraktLists.Channels; + +namespace TraktLists +{ + public class ChannelP5 : IChannel, IHasCacheKey, IRequiresMediaInfoCallback + { + private IHttpClient HttpClient { get; } + private ILogger Logger { get; } + private IJsonSerializer JsonSerializer { get; } + private ILibraryManager LibraryManager { get; } + private IUserManager UserManager { get; } + private TraktApi TraktApi { get; } + + private readonly TraktChannel _channelToConfigure = Plugin.Instance.PluginConfiguration.PreConfiguredTraktChannels.Single(x => x.Id == 5); + + public ChannelP5(IHttpClient httpClient, ILogManager logManager, IJsonSerializer jsonSerializer, ILibraryManager lib, TraktApi traktApi, IUserManager userManager) + { + HttpClient = httpClient; + Logger = logManager.GetLogger(GetType().Name); + JsonSerializer = jsonSerializer; + LibraryManager = lib; + TraktApi = traktApi; + UserManager = userManager; + } + + public string DataVersion => "12"; + + public InternalChannelFeatures GetChannelFeatures() + { + return new InternalChannelFeatures + { + ContentTypes = new List + { + ChannelMediaContentType.Movie + }, + + MediaTypes = new List + { + ChannelMediaType.Video + }, + + SupportsContentDownloading = true, + SupportsSortOrderToggle = true, + }; + } + + private async Task GetChannelItemsInternal(CancellationToken cancellationToken) + { + var helper = new ChannelHelper(); + return await helper.GetChannelItemResult(UserManager, LibraryManager, _channelToConfigure, Logger, TraktApi, cancellationToken); + } + + public bool IsEnabledFor(string userId) + { + return true; + } + + public async Task GetChannelItems(InternalChannelItemQuery query, CancellationToken cancellationToken) + { + return await GetChannelItemsInternal(cancellationToken); + } + + public async Task GetChannelImage(ImageType type, CancellationToken cancellationToken) + { + switch (type) + { + case ImageType.Backdrop: + { + var path = GetType().Namespace + ".Images." + type.ToString().ToLower() + ".png"; + + return await Task.FromResult(new DynamicImageResponse + { + Format = ImageFormat.Png, + HasImage = true, + Stream = GetType().Assembly.GetManifestResourceStream(path) + }); + } + default: + throw new ArgumentException("Unsupported image type: " + type); + + } + } + + public IEnumerable GetSupportedChannelImages() + { + return new List { ImageType.Primary, ImageType.Thumb }; + } + + public string Name => _channelToConfigure.ChannelName; + public string Description { get; private set; } + public string HomePageUrl + { + get { return ""; } + } + + public ChannelParentalRating ParentalRating + { + get { return ChannelParentalRating.GeneralAudience; } + } + + public string GetCacheKey(string userId) + { + return Guid.NewGuid().ToString("N"); + } + + public Task> GetChannelItemMediaInfo(string id, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/TraktLists/Channels/PreConfigured/ChannelP6.cs b/TraktLists/Channels/PreConfigured/ChannelP6.cs new file mode 100644 index 0000000..0871167 --- /dev/null +++ b/TraktLists/Channels/PreConfigured/ChannelP6.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Channels; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Channels; +using MediaBrowser.Model.Drawing; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Serialization; +using TraktLists.Api; +using TraktLists.Channels; + +namespace TraktLists +{ + public class ChannelP6 : IChannel, IHasCacheKey, IRequiresMediaInfoCallback + { + private IHttpClient HttpClient { get; } + private ILogger Logger { get; } + private IJsonSerializer JsonSerializer { get; } + private ILibraryManager LibraryManager { get; } + private IUserManager UserManager { get; } + private TraktApi TraktApi { get; } + + private readonly TraktChannel _channelToConfigure = Plugin.Instance.PluginConfiguration.PreConfiguredTraktChannels.Single(x => x.Id == 6); + + public ChannelP6(IHttpClient httpClient, ILogManager logManager, IJsonSerializer jsonSerializer, ILibraryManager lib, TraktApi traktApi, IUserManager userManager) + { + HttpClient = httpClient; + Logger = logManager.GetLogger(GetType().Name); + JsonSerializer = jsonSerializer; + LibraryManager = lib; + TraktApi = traktApi; + UserManager = userManager; + } + + public string DataVersion => "12"; + + public InternalChannelFeatures GetChannelFeatures() + { + return new InternalChannelFeatures + { + ContentTypes = new List + { + ChannelMediaContentType.Movie + }, + + MediaTypes = new List + { + ChannelMediaType.Video + }, + + SupportsContentDownloading = true, + SupportsSortOrderToggle = true, + }; + } + + private async Task GetChannelItemsInternal(CancellationToken cancellationToken) + { + var helper = new ChannelHelper(); + return await helper.GetChannelItemResult(UserManager, LibraryManager, _channelToConfigure, Logger, TraktApi, cancellationToken); + } + + public bool IsEnabledFor(string userId) + { + return true; + } + + public async Task GetChannelItems(InternalChannelItemQuery query, CancellationToken cancellationToken) + { + return await GetChannelItemsInternal(cancellationToken); + } + + public async Task GetChannelImage(ImageType type, CancellationToken cancellationToken) + { + switch (type) + { + case ImageType.Backdrop: + { + var path = GetType().Namespace + ".Images." + type.ToString().ToLower() + ".png"; + + return await Task.FromResult(new DynamicImageResponse + { + Format = ImageFormat.Png, + HasImage = true, + Stream = GetType().Assembly.GetManifestResourceStream(path) + }); + } + default: + throw new ArgumentException("Unsupported image type: " + type); + + } + } + + public IEnumerable GetSupportedChannelImages() + { + return new List { ImageType.Primary, ImageType.Thumb }; + } + + public string Name => _channelToConfigure.ChannelName; + public string Description { get; private set; } + public string HomePageUrl + { + get { return ""; } + } + + public ChannelParentalRating ParentalRating + { + get { return ChannelParentalRating.GeneralAudience; } + } + + public string GetCacheKey(string userId) + { + return Guid.NewGuid().ToString("N"); + } + + public Task> GetChannelItemMediaInfo(string id, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/TraktLists/Channels/PreConfigured/ChannelP7.cs b/TraktLists/Channels/PreConfigured/ChannelP7.cs new file mode 100644 index 0000000..c1defc2 --- /dev/null +++ b/TraktLists/Channels/PreConfigured/ChannelP7.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Channels; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Channels; +using MediaBrowser.Model.Drawing; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Serialization; +using TraktLists.Api; +using TraktLists.Channels; + +namespace TraktLists +{ + public class ChannelP7 : IChannel, IHasCacheKey, IRequiresMediaInfoCallback + { + private IHttpClient HttpClient { get; } + private ILogger Logger { get; } + private IJsonSerializer JsonSerializer { get; } + private ILibraryManager LibraryManager { get; } + private IUserManager UserManager { get; } + private TraktApi TraktApi { get; } + + private readonly TraktChannel _channelToConfigure = Plugin.Instance.PluginConfiguration.PreConfiguredTraktChannels.Single(x => x.Id == 7); + + public ChannelP7(IHttpClient httpClient, ILogManager logManager, IJsonSerializer jsonSerializer, ILibraryManager lib, TraktApi traktApi, IUserManager userManager) + { + HttpClient = httpClient; + Logger = logManager.GetLogger(GetType().Name); + JsonSerializer = jsonSerializer; + LibraryManager = lib; + TraktApi = traktApi; + UserManager = userManager; + } + + public string DataVersion => "12"; + + public InternalChannelFeatures GetChannelFeatures() + { + return new InternalChannelFeatures + { + ContentTypes = new List + { + ChannelMediaContentType.Movie + }, + + MediaTypes = new List + { + ChannelMediaType.Video + }, + + SupportsContentDownloading = true, + SupportsSortOrderToggle = true, + }; + } + + private async Task GetChannelItemsInternal(CancellationToken cancellationToken) + { + var helper = new ChannelHelper(); + return await helper.GetChannelItemResult(UserManager, LibraryManager, _channelToConfigure, Logger, TraktApi, cancellationToken); + } + + public bool IsEnabledFor(string userId) + { + return true; + } + + public async Task GetChannelItems(InternalChannelItemQuery query, CancellationToken cancellationToken) + { + return await GetChannelItemsInternal(cancellationToken); + } + + public async Task GetChannelImage(ImageType type, CancellationToken cancellationToken) + { + switch (type) + { + case ImageType.Backdrop: + { + var path = GetType().Namespace + ".Images." + type.ToString().ToLower() + ".png"; + + return await Task.FromResult(new DynamicImageResponse + { + Format = ImageFormat.Png, + HasImage = true, + Stream = GetType().Assembly.GetManifestResourceStream(path) + }); + } + default: + throw new ArgumentException("Unsupported image type: " + type); + + } + } + + public IEnumerable GetSupportedChannelImages() + { + return new List { ImageType.Primary, ImageType.Thumb }; + } + + public string Name => _channelToConfigure.ChannelName; + public string Description { get; private set; } + public string HomePageUrl + { + get { return ""; } + } + + public ChannelParentalRating ParentalRating + { + get { return ChannelParentalRating.GeneralAudience; } + } + + public string GetCacheKey(string userId) + { + return Guid.NewGuid().ToString("N"); + } + + public Task> GetChannelItemMediaInfo(string id, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/TraktLists/Channels/PreConfigured/ChannelP8.cs b/TraktLists/Channels/PreConfigured/ChannelP8.cs new file mode 100644 index 0000000..96d7506 --- /dev/null +++ b/TraktLists/Channels/PreConfigured/ChannelP8.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Channels; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Channels; +using MediaBrowser.Model.Drawing; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Serialization; +using TraktLists.Api; +using TraktLists.Channels; + +namespace TraktLists +{ + public class ChannelP8 : IChannel, IHasCacheKey, IRequiresMediaInfoCallback + { + private IHttpClient HttpClient { get; } + private ILogger Logger { get; } + private IJsonSerializer JsonSerializer { get; } + private ILibraryManager LibraryManager { get; } + private IUserManager UserManager { get; } + private TraktApi TraktApi { get; } + + private readonly TraktChannel _channelToConfigure = Plugin.Instance.PluginConfiguration.PreConfiguredTraktChannels.Single(x => x.Id == 8); + + public ChannelP8(IHttpClient httpClient, ILogManager logManager, IJsonSerializer jsonSerializer, ILibraryManager lib, TraktApi traktApi, IUserManager userManager) + { + HttpClient = httpClient; + Logger = logManager.GetLogger(GetType().Name); + JsonSerializer = jsonSerializer; + LibraryManager = lib; + TraktApi = traktApi; + UserManager = userManager; + } + + public string DataVersion => "12"; + + public InternalChannelFeatures GetChannelFeatures() + { + return new InternalChannelFeatures + { + ContentTypes = new List + { + ChannelMediaContentType.Movie + }, + + MediaTypes = new List + { + ChannelMediaType.Video + }, + + SupportsContentDownloading = true, + SupportsSortOrderToggle = true, + }; + } + + private async Task GetChannelItemsInternal(CancellationToken cancellationToken) + { + var helper = new ChannelHelper(); + return await helper.GetChannelItemResult(UserManager, LibraryManager, _channelToConfigure, Logger, TraktApi, cancellationToken); + } + + public bool IsEnabledFor(string userId) + { + return true; + } + + public async Task GetChannelItems(InternalChannelItemQuery query, CancellationToken cancellationToken) + { + return await GetChannelItemsInternal(cancellationToken); + } + + public async Task GetChannelImage(ImageType type, CancellationToken cancellationToken) + { + switch (type) + { + case ImageType.Backdrop: + { + var path = GetType().Namespace + ".Images." + type.ToString().ToLower() + ".png"; + + return await Task.FromResult(new DynamicImageResponse + { + Format = ImageFormat.Png, + HasImage = true, + Stream = GetType().Assembly.GetManifestResourceStream(path) + }); + } + default: + throw new ArgumentException("Unsupported image type: " + type); + + } + } + + public IEnumerable GetSupportedChannelImages() + { + return new List { ImageType.Primary, ImageType.Thumb }; + } + + public string Name => _channelToConfigure.ChannelName; + public string Description { get; private set; } + public string HomePageUrl + { + get { return ""; } + } + + public ChannelParentalRating ParentalRating + { + get { return ChannelParentalRating.GeneralAudience; } + } + + public string GetCacheKey(string userId) + { + return Guid.NewGuid().ToString("N"); + } + + public Task> GetChannelItemMediaInfo(string id, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/TraktLists/Channels/PreConfigured/ChannelP9.cs b/TraktLists/Channels/PreConfigured/ChannelP9.cs new file mode 100644 index 0000000..1d3a4c2 --- /dev/null +++ b/TraktLists/Channels/PreConfigured/ChannelP9.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Channels; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Channels; +using MediaBrowser.Model.Drawing; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Serialization; +using TraktLists.Api; +using TraktLists.Channels; + +namespace TraktLists +{ + public class ChannelP9 : IChannel, IHasCacheKey, IRequiresMediaInfoCallback + { + private IHttpClient HttpClient { get; } + private ILogger Logger { get; } + private IJsonSerializer JsonSerializer { get; } + private ILibraryManager LibraryManager { get; } + private IUserManager UserManager { get; } + private TraktApi TraktApi { get; } + + private readonly TraktChannel _channelToConfigure = Plugin.Instance.PluginConfiguration.PreConfiguredTraktChannels.Single(x => x.Id == 9); + + public ChannelP9(IHttpClient httpClient, ILogManager logManager, IJsonSerializer jsonSerializer, ILibraryManager lib, TraktApi traktApi, IUserManager userManager) + { + HttpClient = httpClient; + Logger = logManager.GetLogger(GetType().Name); + JsonSerializer = jsonSerializer; + LibraryManager = lib; + TraktApi = traktApi; + UserManager = userManager; + } + + public string DataVersion => "12"; + + public InternalChannelFeatures GetChannelFeatures() + { + return new InternalChannelFeatures + { + ContentTypes = new List + { + ChannelMediaContentType.Movie + }, + + MediaTypes = new List + { + ChannelMediaType.Video + }, + + SupportsContentDownloading = true, + SupportsSortOrderToggle = true, + }; + } + + private async Task GetChannelItemsInternal(CancellationToken cancellationToken) + { + var helper = new ChannelHelper(); + return await helper.GetChannelItemResult(UserManager, LibraryManager, _channelToConfigure, Logger, TraktApi, cancellationToken); + } + + public bool IsEnabledFor(string userId) + { + return true; + } + + public async Task GetChannelItems(InternalChannelItemQuery query, CancellationToken cancellationToken) + { + return await GetChannelItemsInternal(cancellationToken); + } + + public async Task GetChannelImage(ImageType type, CancellationToken cancellationToken) + { + switch (type) + { + case ImageType.Backdrop: + { + var path = GetType().Namespace + ".Images." + type.ToString().ToLower() + ".png"; + + return await Task.FromResult(new DynamicImageResponse + { + Format = ImageFormat.Png, + HasImage = true, + Stream = GetType().Assembly.GetManifestResourceStream(path) + }); + } + default: + throw new ArgumentException("Unsupported image type: " + type); + + } + } + + public IEnumerable GetSupportedChannelImages() + { + return new List { ImageType.Primary, ImageType.Thumb }; + } + + public string Name => _channelToConfigure.ChannelName; + public string Description { get; private set; } + public string HomePageUrl + { + get { return ""; } + } + + public ChannelParentalRating ParentalRating + { + get { return ChannelParentalRating.GeneralAudience; } + } + + public string GetCacheKey(string userId) + { + return Guid.NewGuid().ToString("N"); + } + + public Task> GetChannelItemMediaInfo(string id, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/TraktLists/Config/configPage.html b/TraktLists/Config/configPage.html index 2b32481..913f5c3 100644 --- a/TraktLists/Config/configPage.html +++ b/TraktLists/Config/configPage.html @@ -1,7 +1,7 @@  - AppleTv + Trakt Lists @@ -21,7 +21,7 @@

Channel Setup

Custom Trakt Lists

-

Pre Configured Trak Lists

+

Pre Configured Trakt Lists

@@ -82,6 +82,36 @@

Pre Configured Trak Lists

return this; }; + var preConfig = []; + preConfig.push({Uri: "https://trakt.tv/users/mmounirou/lists/imdb-top-250-movies?sort=rank,asc", Enabled: false, ChannelName: "IMDB Top 250 Movies", Id: 1}); + preConfig.push({Uri: "https://trakt.tv/users/justin/lists/imdb-top-rated-movies?sort=rank,asc", Enabled: false, ChannelName: "IMDB Top Rated Movies", Id: 2}); + preConfig.push({Uri: "https://trakt.tv/users/captainnapalm/lists/1001-greatest-movies-of-all-time?sort=rank,asc", Enabled: false, ChannelName: "1001 Greatest Movies of All Time", Id: 3}); + preConfig.push({Uri: "https://trakt.tv/users/maxwelldeux/lists/tspdt-s-21st-century-s-most-acclaimed-films?sort=rank,asc", Enabled: false, ChannelName: "21st Century's Most Acclaimed Films", Id: 4}); + preConfig.push({Uri: "https://trakt.tv/users/sp1ti/lists/afi-s-100-years-100-movies-10th-anniversary-edition?sort=rank,asc", Enabled: false, ChannelName: "AFIs 100 Years... 100 Movies (10th Anniversary Edition)", Id: 5}); + preConfig.push({Uri: "https://trakt.tv/users/maxwelldeux/lists/the-criterion-collection?sort=rank,asc", Enabled: false, ChannelName: "The Criterion Collection", Id: 6}); + preConfig.push({Uri: "https://trakt.tv/users/themoodymuck/lists/disney?sort=released,asc", Enabled: false, ChannelName: "Disney ", Id: 7}); + preConfig.push({Uri: "https://trakt.tv/users/maximm/lists/empire-magazines-500-greatest-films-of-all-time", Enabled: false, ChannelName: "Empire Top 500", Id: 8}); + preConfig.push({Uri: "https://trakt.tv/users/teokir/lists/all-time-1000-8-films-to-see-before-you-die-the-guardian?sort=rank,asc", Enabled: false, ChannelName: "The Guardian 1000 Films to See Before You Die", Id: 9}); + preConfig.push({Uri: "https://trakt.tv/users/any/lists/james-bond?sort=rank,asc", Enabled: false, ChannelName: "James Bond", Id: 10}); + preConfig.push({Uri: "https://trakt.tv/users/movistapp/lists/marvel?sort=rank,asc", Enabled: false, ChannelName: "Marvel", Id: 11}); + /*preConfig.push({Uri: "", Enabled: false, ChannelName: "", Id: 12}); + preConfig.push({Uri: "", Enabled: false, ChannelName: "", Id: 13}); + preConfig.push({Uri: "", Enabled: false, ChannelName: "", Id: 14}); + preConfig.push({Uri: "", Enabled: false, ChannelName: "", Id: 15}); + preConfig.push({Uri: "", Enabled: false, ChannelName: "", Id: 16});*/ + + var customConfig = []; + customConfig.push({Uri: "", Enabled: false, ChannelName: "Channel 1", Id: 1}); + customConfig.push({Uri: "", Enabled: false, ChannelName: "Channel 2", Id: 2}); + customConfig.push({Uri: "", Enabled: false, ChannelName: "Channel 3", Id: 3}); + customConfig.push({Uri: "", Enabled: false, ChannelName: "Channel 4", Id: 4}); + customConfig.push({Uri: "", Enabled: false, ChannelName: "Channel 5", Id: 5}); + customConfig.push({Uri: "", Enabled: false, ChannelName: "Channel 6", Id: 6}); + customConfig.push({Uri: "", Enabled: false, ChannelName: "Channel 7", Id: 7}); + customConfig.push({Uri: "", Enabled: false, ChannelName: "Channel 8", Id: 8}); + customConfig.push({Uri: "", Enabled: false, ChannelName: "Channel 9", Id: 9}); + customConfig.push({Uri: "", Enabled: false, ChannelName: "Channel 10", Id: 10}); + var ConfigurationPage = { pluginUniqueId: "CD48A6A8-D26B-48EB-8857-D06A62875039", @@ -96,13 +126,19 @@

Pre Configured Trak Lists

$('#txtChannelName', page).val(config.ChannelName); } - if(config.PreConfiguredTraktChannels === null || config.PreConfiguredTraktChannels.length === 0){ - var preConfig = []; - preConfig.push({Uri: "https://trakt.tv/users/mmounirou/lists/imdb-top-250-movies?sort=rank,asc", Enabled: true, ChannelName: "IMDB Top 250 Movies", Id: 1}); - preConfig.push({Uri: "https://trakt.tv/users/justin/lists/imdb-top-rated-movies?sort=rank,asc", Enabled: true, ChannelName: "IMDB Top Rated Movies", Id: 2}); + if(config.PreConfiguredTraktChannels === null || config.PreConfiguredTraktChannels.length === 0){ config.PreConfiguredTraktChannels = preConfig; } + if(preConfig.length !== config.PreConfiguredTraktChannels.length) + { + for (var i = 0, length = preConfig.length; i < length; i++) { + if(config.PreConfiguredTraktChannels.indexOf(i) === -1) { + config.PreConfiguredTraktChannels.push(preConfig[i]); + } + } + } + if(config.PreConfiguredTraktChannels) { var html = ""; @@ -132,21 +168,19 @@

Pre Configured Trak Lists

} - if(config.CustomTraktChannels === null || config.CustomTraktChannels.length === 0){ - var customConfig = []; - customConfig.push({Uri: "", Enabled: false, ChannelName: "Channel 1", Id: 1}); - customConfig.push({Uri: "", Enabled: false, ChannelName: "Channel 2", Id: 2}); - customConfig.push({Uri: "", Enabled: false, ChannelName: "Channel 3", Id: 3}); - customConfig.push({Uri: "", Enabled: false, ChannelName: "Channel 4", Id: 4}); - customConfig.push({Uri: "", Enabled: false, ChannelName: "Channel 5", Id: 5}); - customConfig.push({Uri: "", Enabled: false, ChannelName: "Channel 6", Id: 6}); - customConfig.push({Uri: "", Enabled: false, ChannelName: "Channel 7", Id: 7}); - customConfig.push({Uri: "", Enabled: false, ChannelName: "Channel 8", Id: 8}); - customConfig.push({Uri: "", Enabled: false, ChannelName: "Channel 9", Id: 9}); - customConfig.push({Uri: "", Enabled: false, ChannelName: "Channel 10", Id: 10}); + if(config.CustomTraktChannels === null || config.CustomTraktChannels.length === 0){ config.CustomTraktChannels = customConfig; } + if(customConfig.length !== config.CustomTraktChannels.length) + { + for (var i = 0, length = preConfig.length; i < length; i++) { + if(config.CustomTraktChannels.indexOf(i) === -1) { + config.CustomTraktChannels.push(customConfig[i]); + } + } + } + if(config.CustomTraktChannels) { var html = ""; @@ -177,7 +211,7 @@

Pre Configured Trak Lists

$(":checkbox").click(function(){ //alert(" you checked"); - alert("Before = "+$(this).val()); + //alert("Before = "+$(this).val()); if($(this).is(':checked')) { $(this).val('true'); diff --git a/TraktLists/Plugin.cs b/TraktLists/Plugin.cs index 795ccfe..5a61f1e 100644 --- a/TraktLists/Plugin.cs +++ b/TraktLists/Plugin.cs @@ -19,7 +19,7 @@ public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer) } public SemaphoreSlim TraktResourcePool = new SemaphoreSlim(1, 1); - public SemaphoreSlim SchedTaskResourcePool = new SemaphoreSlim(1, 1); + //public SemaphoreSlim SchedTaskResourcePool = new SemaphoreSlim(1, 1); public static Plugin Instance { get; private set; } public override Guid Id => PluginConfiguration.Guid; diff --git a/TraktLists/ScheduledTasks/PreConfigured/ChannelUpdateScheduledTaskP10.cs b/TraktLists/ScheduledTasks/PreConfigured/ChannelUpdateScheduledTaskP10.cs new file mode 100644 index 0000000..c04ed8f --- /dev/null +++ b/TraktLists/ScheduledTasks/PreConfigured/ChannelUpdateScheduledTaskP10.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Model.Tasks; + +namespace TraktLists +{ + public class ChannelUpdateScheduledTaskP10 : IScheduledTask, IConfigurableScheduledTask + { + private ITaskManager TaskManager { get; set; } + + public ChannelUpdateScheduledTaskP10(ITaskManager taskMan) + { + TaskManager = taskMan; + } + public async Task Execute(CancellationToken cancellationToken, IProgress progress) + { + foreach (var t in TaskManager.ScheduledTasks) + { + if (t.Name == "Refresh Internet Channels") + { + await TaskManager.Execute(t, new TaskOptions()); + } + } + } + + public IEnumerable GetDefaultTriggers() + { + return new[] + { + new TaskTriggerInfo + { + Type = TaskTriggerInfo.TriggerInterval, + IntervalTicks = TimeSpan.FromDays(1).Ticks + } + }; + } + + private readonly TraktChannel _traktChannel = + Plugin.Instance.PluginConfiguration.PreConfiguredTraktChannels.Single(x => x.Id == 10); + public string Name => "Update Channel: " + _traktChannel.ChannelName; + public string Key => "PreConfigured" + _traktChannel.Id; + public string Description => "Create/Update channel from trakt list."; + public string Category => "Trakt List Channels"; + public bool IsHidden => false; + public bool IsEnabled => true; + public bool IsLogged => true; + } +} \ No newline at end of file diff --git a/TraktLists/ScheduledTasks/PreConfigured/ChannelUpdateScheduledTaskP11.cs b/TraktLists/ScheduledTasks/PreConfigured/ChannelUpdateScheduledTaskP11.cs new file mode 100644 index 0000000..22f177b --- /dev/null +++ b/TraktLists/ScheduledTasks/PreConfigured/ChannelUpdateScheduledTaskP11.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Model.Tasks; + +namespace TraktLists +{ + public class ChannelUpdateScheduledTaskP11 : IScheduledTask, IConfigurableScheduledTask + { + private ITaskManager TaskManager { get; set; } + + public ChannelUpdateScheduledTaskP11(ITaskManager taskMan) + { + TaskManager = taskMan; + } + public async Task Execute(CancellationToken cancellationToken, IProgress progress) + { + foreach (var t in TaskManager.ScheduledTasks) + { + if (t.Name == "Refresh Internet Channels") + { + await TaskManager.Execute(t, new TaskOptions()); + } + } + } + + public IEnumerable GetDefaultTriggers() + { + return new[] + { + new TaskTriggerInfo + { + Type = TaskTriggerInfo.TriggerInterval, + IntervalTicks = TimeSpan.FromDays(1).Ticks + } + }; + } + + private readonly TraktChannel _traktChannel = + Plugin.Instance.PluginConfiguration.PreConfiguredTraktChannels.Single(x => x.Id == 11); + public string Name => "Update Channel: " + _traktChannel.ChannelName; + public string Key => "PreConfigured" + _traktChannel.Id; + public string Description => "Create/Update channel from trakt list."; + public string Category => "Trakt List Channels"; + public bool IsHidden => false; + public bool IsEnabled => true; + public bool IsLogged => true; + } +} \ No newline at end of file diff --git a/TraktLists/ScheduledTasks/PreConfigured/ChannelUpdateScheduledTaskP3.cs b/TraktLists/ScheduledTasks/PreConfigured/ChannelUpdateScheduledTaskP3.cs new file mode 100644 index 0000000..215fccf --- /dev/null +++ b/TraktLists/ScheduledTasks/PreConfigured/ChannelUpdateScheduledTaskP3.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Model.Tasks; + +namespace TraktLists +{ + public class ChannelUpdateScheduledTaskP3 : IScheduledTask, IConfigurableScheduledTask + { + private ITaskManager TaskManager { get; set; } + + public ChannelUpdateScheduledTaskP3(ITaskManager taskMan) + { + TaskManager = taskMan; + } + public async Task Execute(CancellationToken cancellationToken, IProgress progress) + { + foreach (var t in TaskManager.ScheduledTasks) + { + if (t.Name == "Refresh Internet Channels") + { + await TaskManager.Execute(t, new TaskOptions()); + } + } + } + + public IEnumerable GetDefaultTriggers() + { + return new[] + { + new TaskTriggerInfo + { + Type = TaskTriggerInfo.TriggerInterval, + IntervalTicks = TimeSpan.FromDays(1).Ticks + } + }; + } + + private readonly TraktChannel _traktChannel = + Plugin.Instance.PluginConfiguration.PreConfiguredTraktChannels.Single(x => x.Id == 3); + public string Name => "Update Channel: " + _traktChannel.ChannelName; + public string Key => "PreConfigured" + _traktChannel.Id; + public string Description => "Create/Update channel from trakt list."; + public string Category => "Trakt List Channels"; + public bool IsHidden => false; + public bool IsEnabled => true; + public bool IsLogged => true; + } +} \ No newline at end of file diff --git a/TraktLists/ScheduledTasks/PreConfigured/ChannelUpdateScheduledTaskP4.cs b/TraktLists/ScheduledTasks/PreConfigured/ChannelUpdateScheduledTaskP4.cs new file mode 100644 index 0000000..d6e2919 --- /dev/null +++ b/TraktLists/ScheduledTasks/PreConfigured/ChannelUpdateScheduledTaskP4.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Model.Tasks; + +namespace TraktLists +{ + public class ChannelUpdateScheduledTaskP4 : IScheduledTask, IConfigurableScheduledTask + { + private ITaskManager TaskManager { get; set; } + + public ChannelUpdateScheduledTaskP4(ITaskManager taskMan) + { + TaskManager = taskMan; + } + public async Task Execute(CancellationToken cancellationToken, IProgress progress) + { + foreach (var t in TaskManager.ScheduledTasks) + { + if (t.Name == "Refresh Internet Channels") + { + await TaskManager.Execute(t, new TaskOptions()); + } + } + } + + public IEnumerable GetDefaultTriggers() + { + return new[] + { + new TaskTriggerInfo + { + Type = TaskTriggerInfo.TriggerInterval, + IntervalTicks = TimeSpan.FromDays(1).Ticks + } + }; + } + + private readonly TraktChannel _traktChannel = + Plugin.Instance.PluginConfiguration.PreConfiguredTraktChannels.Single(x => x.Id == 4); + public string Name => "Update Channel: " + _traktChannel.ChannelName; + public string Key => "PreConfigured" + _traktChannel.Id; + public string Description => "Create/Update channel from trakt list."; + public string Category => "Trakt List Channels"; + public bool IsHidden => false; + public bool IsEnabled => true; + public bool IsLogged => true; + } +} \ No newline at end of file diff --git a/TraktLists/ScheduledTasks/PreConfigured/ChannelUpdateScheduledTaskP5.cs b/TraktLists/ScheduledTasks/PreConfigured/ChannelUpdateScheduledTaskP5.cs new file mode 100644 index 0000000..c46ffad --- /dev/null +++ b/TraktLists/ScheduledTasks/PreConfigured/ChannelUpdateScheduledTaskP5.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Model.Tasks; + +namespace TraktLists +{ + public class ChannelUpdateScheduledTaskP5 : IScheduledTask, IConfigurableScheduledTask + { + private ITaskManager TaskManager { get; set; } + + public ChannelUpdateScheduledTaskP5(ITaskManager taskMan) + { + TaskManager = taskMan; + } + public async Task Execute(CancellationToken cancellationToken, IProgress progress) + { + foreach (var t in TaskManager.ScheduledTasks) + { + if (t.Name == "Refresh Internet Channels") + { + await TaskManager.Execute(t, new TaskOptions()); + } + } + } + + public IEnumerable GetDefaultTriggers() + { + return new[] + { + new TaskTriggerInfo + { + Type = TaskTriggerInfo.TriggerInterval, + IntervalTicks = TimeSpan.FromDays(1).Ticks + } + }; + } + + private readonly TraktChannel _traktChannel = + Plugin.Instance.PluginConfiguration.PreConfiguredTraktChannels.Single(x => x.Id == 5); + public string Name => "Update Channel: " + _traktChannel.ChannelName; + public string Key => "PreConfigured" + _traktChannel.Id; + public string Description => "Create/Update channel from trakt list."; + public string Category => "Trakt List Channels"; + public bool IsHidden => false; + public bool IsEnabled => true; + public bool IsLogged => true; + } +} \ No newline at end of file diff --git a/TraktLists/ScheduledTasks/PreConfigured/ChannelUpdateScheduledTaskP6.cs b/TraktLists/ScheduledTasks/PreConfigured/ChannelUpdateScheduledTaskP6.cs new file mode 100644 index 0000000..6f38b5d --- /dev/null +++ b/TraktLists/ScheduledTasks/PreConfigured/ChannelUpdateScheduledTaskP6.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Model.Tasks; + +namespace TraktLists +{ + public class ChannelUpdateScheduledTaskP6 : IScheduledTask, IConfigurableScheduledTask + { + private ITaskManager TaskManager { get; set; } + + public ChannelUpdateScheduledTaskP6(ITaskManager taskMan) + { + TaskManager = taskMan; + } + public async Task Execute(CancellationToken cancellationToken, IProgress progress) + { + foreach (var t in TaskManager.ScheduledTasks) + { + if (t.Name == "Refresh Internet Channels") + { + await TaskManager.Execute(t, new TaskOptions()); + } + } + } + + public IEnumerable GetDefaultTriggers() + { + return new[] + { + new TaskTriggerInfo + { + Type = TaskTriggerInfo.TriggerInterval, + IntervalTicks = TimeSpan.FromDays(1).Ticks + } + }; + } + + private readonly TraktChannel _traktChannel = + Plugin.Instance.PluginConfiguration.PreConfiguredTraktChannels.Single(x => x.Id == 6); + public string Name => "Update Channel: " + _traktChannel.ChannelName; + public string Key => "PreConfigured" + _traktChannel.Id; + public string Description => "Create/Update channel from trakt list."; + public string Category => "Trakt List Channels"; + public bool IsHidden => false; + public bool IsEnabled => true; + public bool IsLogged => true; + } +} \ No newline at end of file diff --git a/TraktLists/ScheduledTasks/PreConfigured/ChannelUpdateScheduledTaskP7.cs b/TraktLists/ScheduledTasks/PreConfigured/ChannelUpdateScheduledTaskP7.cs new file mode 100644 index 0000000..8b83a46 --- /dev/null +++ b/TraktLists/ScheduledTasks/PreConfigured/ChannelUpdateScheduledTaskP7.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Model.Tasks; + +namespace TraktLists +{ + public class ChannelUpdateScheduledTaskP7 : IScheduledTask, IConfigurableScheduledTask + { + private ITaskManager TaskManager { get; set; } + + public ChannelUpdateScheduledTaskP7(ITaskManager taskMan) + { + TaskManager = taskMan; + } + public async Task Execute(CancellationToken cancellationToken, IProgress progress) + { + foreach (var t in TaskManager.ScheduledTasks) + { + if (t.Name == "Refresh Internet Channels") + { + await TaskManager.Execute(t, new TaskOptions()); + } + } + } + + public IEnumerable GetDefaultTriggers() + { + return new[] + { + new TaskTriggerInfo + { + Type = TaskTriggerInfo.TriggerInterval, + IntervalTicks = TimeSpan.FromDays(1).Ticks + } + }; + } + + private readonly TraktChannel _traktChannel = + Plugin.Instance.PluginConfiguration.PreConfiguredTraktChannels.Single(x => x.Id == 7); + public string Name => "Update Channel: " + _traktChannel.ChannelName; + public string Key => "PreConfigured" + _traktChannel.Id; + public string Description => "Create/Update channel from trakt list."; + public string Category => "Trakt List Channels"; + public bool IsHidden => false; + public bool IsEnabled => true; + public bool IsLogged => true; + } +} \ No newline at end of file diff --git a/TraktLists/ScheduledTasks/PreConfigured/ChannelUpdateScheduledTaskP8.cs b/TraktLists/ScheduledTasks/PreConfigured/ChannelUpdateScheduledTaskP8.cs new file mode 100644 index 0000000..766c5f8 --- /dev/null +++ b/TraktLists/ScheduledTasks/PreConfigured/ChannelUpdateScheduledTaskP8.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Model.Tasks; + +namespace TraktLists +{ + public class ChannelUpdateScheduledTaskP8 : IScheduledTask, IConfigurableScheduledTask + { + private ITaskManager TaskManager { get; set; } + + public ChannelUpdateScheduledTaskP8(ITaskManager taskMan) + { + TaskManager = taskMan; + } + public async Task Execute(CancellationToken cancellationToken, IProgress progress) + { + foreach (var t in TaskManager.ScheduledTasks) + { + if (t.Name == "Refresh Internet Channels") + { + await TaskManager.Execute(t, new TaskOptions()); + } + } + } + + public IEnumerable GetDefaultTriggers() + { + return new[] + { + new TaskTriggerInfo + { + Type = TaskTriggerInfo.TriggerInterval, + IntervalTicks = TimeSpan.FromDays(1).Ticks + } + }; + } + + private readonly TraktChannel _traktChannel = + Plugin.Instance.PluginConfiguration.PreConfiguredTraktChannels.Single(x => x.Id == 8); + public string Name => "Update Channel: " + _traktChannel.ChannelName; + public string Key => "PreConfigured" + _traktChannel.Id; + public string Description => "Create/Update channel from trakt list."; + public string Category => "Trakt List Channels"; + public bool IsHidden => false; + public bool IsEnabled => true; + public bool IsLogged => true; + } +} \ No newline at end of file diff --git a/TraktLists/ScheduledTasks/PreConfigured/ChannelUpdateScheduledTaskP9.cs b/TraktLists/ScheduledTasks/PreConfigured/ChannelUpdateScheduledTaskP9.cs new file mode 100644 index 0000000..3ce66b1 --- /dev/null +++ b/TraktLists/ScheduledTasks/PreConfigured/ChannelUpdateScheduledTaskP9.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Model.Tasks; + +namespace TraktLists +{ + public class ChannelUpdateScheduledTaskP9 : IScheduledTask, IConfigurableScheduledTask + { + private ITaskManager TaskManager { get; set; } + + public ChannelUpdateScheduledTaskP9(ITaskManager taskMan) + { + TaskManager = taskMan; + } + public async Task Execute(CancellationToken cancellationToken, IProgress progress) + { + foreach (var t in TaskManager.ScheduledTasks) + { + if (t.Name == "Refresh Internet Channels") + { + await TaskManager.Execute(t, new TaskOptions()); + } + } + } + + public IEnumerable GetDefaultTriggers() + { + return new[] + { + new TaskTriggerInfo + { + Type = TaskTriggerInfo.TriggerInterval, + IntervalTicks = TimeSpan.FromDays(1).Ticks + } + }; + } + + private readonly TraktChannel _traktChannel = + Plugin.Instance.PluginConfiguration.PreConfiguredTraktChannels.Single(x => x.Id == 9); + public string Name => "Update Channel: " + _traktChannel.ChannelName; + public string Key => "PreConfigured" + _traktChannel.Id; + public string Description => "Create/Update channel from trakt list."; + public string Category => "Trakt List Channels"; + public bool IsHidden => false; + public bool IsEnabled => true; + public bool IsLogged => true; + } +} \ No newline at end of file diff --git a/TraktLists/thumb.png b/TraktLists/thumb.png index 8db64ee9ae12e704739d756d6c96f1f4110675e5..e46c6f46b76da0d5e178fe7daa81d7a2ec556656 100644 GIT binary patch literal 14924 zcmY+rWmuH&6E+M;=hEGX)Y6@@bV=xH$|`~0s`*WmmkCt*K#Wa1d(keS!o?Vlj9t; z><@DHBOhBOx^0`XE^GpAQ{>{ZbIT0WrNzZ}b%cjJEJiR358hMb1wO~eL5Ybd^Pwnm z_{b?X(i(K9Sw}8ot!Xz>AH=`)_;31tv=RcZ1W8>=`)uszD48gQJY@OXFh{(N%}?GZ z*|jk?FwX&luaf)g8w&zfKNSSZ&x^!lh^fSrv+p*qoagDjzB;R1xBM*xqq^6#KBSia zkH~=Yw$HVb{6bh1qoz2 zztiC$T9C@3CM}L48M7}nvN3F0-fPe}ZrynopVLCpB#{0?K$r5*SZ*aS$H3zI=yARf@ zLTkFtRa2w9sJLN082|l$7>r#2C{a0JsQYb2WhME7zHNlCiHL-K-lCu$TwT2cHuX0Z zD484E_6EGXa4PSYa^th1_L`&v%SC7n$oEak(N<;Zw`Kb8JA;CtH%3th>A_rSgWN&FUQX1FL zoYF$6(c{(2Y3iB+JDV%RR8vS6Wi)3zh-(&j0tmJ3Y&aw!s_eTB8cyyQeAPAq>A;9` z`f&KeXJX`2o}XeVT00tlfVRI$m9Yrzkc(Hmzrx_?R9n@Nfk1r{XFaO1g;%6hiR(qkJ~5Y@?>@j#OqZqgQC6@1$uo= zPtSnS%i_IgE*av%OG>3Xv7pnM*-4I+@JXr$@lHg(%H?jP!nsb8_;&YA22R-U-wch; zk>}&Go-a+GhvzKhbjrys&3y%@ts5FzEpEW+GgxEbGTnC z-ksF@HL%0wnpBt@oeDCHWiP00EC|)dIOey6AO!72;DeUG>BCGn)SZ zaeyLU<1`x!SukM`1&n>FX@;n?{{sG0KQem2h}C_bI&=)aN7OfF1?qC6eki93_)cV~ zdoFAFx{Dl+COSas&SwWoL(`u;tGAW~dQ9PhPNU!DPro}RtGsd-1$c!t749Bs+r3A& zT|oZ@059cddcdmdcq_Jz@n7CvA9~^0Lka$>_4-{nz7>Zcq{Q;-^TLB|ILT|(@>$BI zC+Cqkp%s&W4c}AajaApP4gPhppS-6){ky%jwfkQG<2tT`$GxJh6QG;S0J4vZeWn+0 z4wp47O0mqpZaJaa-1-g{t>x$Yf?T?*w?%UMuGOx<9ZKo!h6I&EMDd#R7a)bTAKIT_wDcIYUiIB$-MCO|MxGhSQPp$_skmX>nVYlJT7~{j^x-FfkdUL%;6- zc#~9;uG~cpGc#yBjhZHIrx+W`U_#yQwYXiZvA}Fr9P)RqB)2#B{+nr4@Sp32A(A`F z&qs0ioh7rAjm}K-f>%8+-x>@aJnlr4;ufK>5EQs7KkMYzo#KV-NNiyRP5mngk{yRpqR&bhYXjhRYrDR{Q zR5`|~YvYIe0vuu9I}aUW1QC(MRQgNVDlfGnN>Kkvw2aJ2{G|J@-Je9Cnw99-V6A<+ zhDgC4XU|s>Iqa6jsEcQ)`o`2nEcut-Fy-~0U)HgJsW11&kklk`7b8-0AeE_fb28ju zi8>?#di5mLK|IcVY~@q@d3X8%03tOlr%fDoGbB$BLpExf!Z=}mian+;k(N5bGX+z0 zoPpxdcZF5n-r`D6RAu^ck%zul%xLmW{c+%MzNM;-3zr{Q&U=1Sd6bM5j(jTr?@}Fc zEg#Ri^JNcn1u=?c!ffwR5|6sPioc#=>h<4i9i&G*k9dz_%@9O1En(_@N^Yu8)+FXd zA@Ye))>RN%;9hEs;i*hX*}a~(YHjeIXYNMI#d`7oL}X@tZ4TWred#YN!PCoh6z7p^ zfjPT=>X}z+Xv6Sl7daMK;)Xj*38U`P5qr(+y(LDoRTJQidiR0)-ys>(`9wPr=OCfO z^7vA+)$rW_Nz)IBLLUti5^?GmpQYMJBPm~68EgLwH9!$KsUUpIxpN-c16yFJPx(zt zOa(!u_B?Za6g)!tJK=#@DU2MtNg;_m9UTd9G)I)Pt;&+eiy1$U?823=`SpXem1Ma! zi3Srd_Qg+P_+(JM`4<@R=3c*2L=lKC{V)=yF*w_Lrlg?fB844O2-S7dk?{IXe>NL1 z(&tnRt-WU}-wD%6mao%@Ii_pl_F&Yuz!EqO7sd_G$+A!Q{7{-qZv&jOOdvi>dkJoC z?HAkAEyw3r7-W_Oj&`pVWyNbQZNx5$?ICod?20hsuVZl4JkjB)&WE4u<@t1m~Z!C8swca3mLqZaFar_{UYGv zUXX4vKK8Bzb~g8){{7r|t*unXcTnYUqH1|$q;Z6ALvK?vzLBXAlrjJ9fWt9?WLo68 zgR2iqFTRF(?H1<(>L);tQiaj2{~SZZv7SW2rds$#Fg!jy%Oy!I9!sKcioXMt$+(p%~C3mj@^XqZ-9grl=y-Gkgh>iqB@Gl!4A;KW3khz@lhc9I6++4%Bs1QbJ+j48^CYDp|^!W2PNLD%!S&Nl&& zC9rmd(=m2nUUyuKS;S%|@<+#xMVV!4n24fdqOnaYhs41|LW%bE%L_jAYNMiR?9s3{Vz1E8_A%yx|Lj}0LfM5*9N&cSPt)i7#V-|mh4GwYKh#)8{-&yMh6fU$ zc5i)9mn1(qC6eC8Wg!12Laa$|}0zF&-{)GG^EFz*lW zV^4tZ$}Vd=PoS+c^5D$TI!U7p4lq(d1%iaAo&H+}^_HTdSA|1<_lITlwSlE-S*X($ z%>ALn@Sv!a2pP-x=vnqJIrR7AJ>zYV?75M9h*70gC05OxAoV7Cj{AYp7Z?q}HC!q^ zLZ6U^?1=&L;#!Jn>=~{@BQUKl;3Lu)4;|OpC90i}@_4cK6c~|0~Y_5%OCNF-fS`H)OWkG>j}nNh;%JvBsvEW-FwmLAK@}gd@`WRH7NZl;_YN zZ{Axc*FO1~4!^C27ReFKJ^%Z7ut8LA*3QkQJ)ZeSdRFuCkKWIsoWdlOq{IbXcJ1c& zr+R#ltgY>SGQGV>1tb1u&=9fyTd%ys7C<761jWwaQiCPh;4vOUBr*uHq+`0hhwV4R zmU9}uy7d)*$oJW~tBF)g_brOL7;3=I)ZO_^J?Wfa0{%GNzayD5s5gTG(|xbu@o0Lf zGK!`-j0mWSniC2_)6AZ?Hda_NtVo^@QHnyzb=nlpj^qZ7wYUvLVoZ4@Ic;>2yBzR! z6At`L6vKsfNEc1NJ$Rx6Dbf|wbc>T_ep*7hug4q{ z06zcPw_*^Lgh0E^YFqD!8n9>7rIup$QM}bgVzU*(b-BGwxb^JbPXs<-A27zbPGCcd zlDZj_qV59>942f;A0wwuZ)9d!cZ!m3U)`>2g25O!R|y*!=8jdPthqd>*klOq7;ht! z-zrLc)L(|Kty`(uY=gX?KNB|>J0fuFzCJ<`_z_Av{Uz~c6P4?hG=#f(i|@nAWloh|`QkzTog$Zx_w(=Py(TZeeuDzEZ;zC>ffGC=Kfe6Z7e(8!2NKUN|MJpDMxeeN===Jtr#zaKB?WjQ${<>{ymTJfp+H~pKa zx_Z%}C7|165r+k#gOoe?{_62DD3(w|LnC?1fQtW3e$2Hs@A8-ZTrBr>w*|1RG+OYh z&nlmqCM79Pj$SEYY*YKLAubNK|6MSo=Ok}zYD$o26n8-cgTYv&oRPRYcgJm;sJK+< z?nngB`qFe%E;`EzqdkK?{p9fp3u9ZJghRa{#5r6^Ls#8q#Ay|V6H>24w+OsW$4C2Y z2elb&-*|^~5oPbR_r4{LnGj%q=rM8YjhY4Cm&ufdLrT!Quz zy*#R`SkDJ}t&^3Nem%KLMog-G(kNTC2Og|II`H65I}iMD>o_BK?s}U#p*sauzLA>z z{m7rnSm`YK&ZIYw&xNY$lH_7PF{Hbo_2Jy5{nvVBVM#*UR7-?_-J6?DR=k z4+<3%$ZrA6@Z8DxR@U{<1-#+A8BB^Hd|2!Pzh{a0I5Cy_9_gd2OXyl@I>EfE=%DEI z;mODWax|+Nt1W3gqdV1X3d{gWA@day2_<&^hSQr3;xp)eT#$5g?*&KB8*_en+nUjL z1>tI@vv9wKbz#X_)$B74zK0=FwC+hrm z6A2fUo=Vb)sf-mB`EMMli60^aKRGWCPnw+EbAKWVf(V#q4*-A?{$mg@xW^dn|x58*g$gnJVzLAV3;u{KV_H40=>O*sOP=5aV)0@BR z0jcrTi_hkuUFssa16d8MkPZl$jdaF;HA&t%)b1q~HC#Ub)|o;|_%$VSRUq1_wPA$6 zFXA2LR702-*7oiBPx?Yi=6BiX1s38rYo35b_IzSQuAW;4*~kV14matyrrBUT(9)}a zrBHYU4~O8Ik?GDp2ZL5uudb}zd)A2JgOKPkPADO3{luX3R&U8rF_vK_Tj7TZCX)dZjoltU0{+2* ze^Dd-SWkQ>N2brE0?o?ro2S5FBwcv7X{@$A*w8W#Yb zdsEFqtTn}aKWavG{A*j3G=KFWmc8;wmQj@TJro;8AgTeQN+fP^j!$9v@gMul z=xJ&*pj8_4&0P?^tE4rD_EgIyf+LVd*8ysqLj$)OS`EO&Yg%r~tIlsP(>9+T(Wmpa z@<)wf8z>SXRxx!0gWlq$ev|!753kgi`h(J2b9T@RRF@lmOWaN&NGiMw;h;-FGCBKQ zF@+G*TK2q8maV3eb`megtN3M0RkH%w(OgYLOT9dSV5vKO;sYOqPd)p-iMw(jBXsZ> zA;yj-6iKK79b?;l_t01wof=D6IO=T8b27>&5R0G5?B--0K%XBYGPEI4VuD4F^jVQl z79As!=~Lap5JrK{a>Jg??NlU2i`b!6B%mr^=dkC|)J-a8;qE57JHK)l%2&Xa*gh1G z{=DD~lTT==pBrwJei5Z5UGq0?J_!T8Q!bfM|9iLRpZvpsH<54jNcx-J;lW)lLJi%R zbFtn1Xa4lY%vbX(c#;-Devc@P6!LUu8><-IgHDeGQF`;AFiUle*iqu|vFP>s#lPIF z#BbT)I;(&8#QxTAaz@awmbp*s3sGZWNB4a~@2T&A;j~CQr_ptPn9!zTft#i@!}GSN za}XbMxx?GBvUDUs7)Ha;VZJ+-4tUdNr+U9M`VVtETf}qA)uQWaMrBH4<)vn33LmA~ zM(fUIg!xHNdWAfp7kY23Z=saMJ<4xhe`icw3kv$}nmG!~4;=}Ruj+>wI7xG%Ius#4 zFZGvSp~+$;or!>YH#euJ!=GSN3&YO>uZLyuB#@o|;?(G20pr|v)Acu|ahhy?fa(x; zki{=X8K%n#FE=knwf{feF{KHJGOd1lj}P2JsmnvXakC3NLR7H9 zZFD(dAJQZ2xn**ot)SyAYF;>~tO3;0VV~T;&xi^(*Jb^)pSMKgby9eV7sh4v3niER zR-{4U-NM0PSspGvJY0~qw6p-`&}w_10_-^F-=bm6HUU5y^24@yL(V3XMTLb(J2MFX zMG*q9f+D4rM4lR+i3W$*@J(ON5|(oM$#XWEF4anI5ShO;q}0bW=$+YvD5Pf4LEnOK zf)q7mdGsKei=SJMkaFj=m1^w)tBB{SiDbt+vhBS0b5gc<^Mw;PQiGfSSNPms5`ina1jH#X?C=5{wKr=c&|LQ*76z|rOK@Kd*Ig+zz; zab~kRw9y?$tgwV{3i1U2XF#Q+qo^pB#fdqvg(X)=>I(-MSv?f0VjuD~dW=}BSqgE_{09`40^pJ3avKC(=7^!RP z$442en1z^AcPn$QgD)qW8bbxg-=4R@GF_iFthj+^4>pj`F>q*Px}lh#O&&Lc0a3{yA`o{?$Zd`^z~m`p(wFDTl%mRzNMuvT4oV za4~8TbFSM{7xg#jj~yw~F?@2)2b3|rrAU+LBxGkn1xmCRodtr*$;-QQ#KneK-~1!j zTQ-{ze-A-W278}g^Oh$=u20sna$dh}1jXLo?dQ>hu&+?*IpzLKFmwfkzlF}#%N5uT zMl<~O&uj_tYw(^HQ_>ao$0YbhF!(`e}{Z<1xA29i61cO_?)~F4J);% zpF+b{gZc?88VY=K@)&~u4(GCF%A)*6jw>FCKftE~bP zTW}b65bfM!|0nJQpFL=8P}9;J-mdA}XO26){$rm&W7@Jr)-)<2ys>H%*_}V&88I4b z_h-GSo-~bZUH|>D*yU(<;+nX;=~i>$DXDbn!3l8ma$8kXobdo29O!LGwGG1RDEJF} zp2+O$t?OnDK6wDGFj+)RY1WzuJ1_{He7Hq*osJ{NL3h6^Utfs(joH6~UsBDY0Elqu zAkFB|>yf_jSKBl^rK*`o?%xoE;#0Q1k#&=R23J}f>2+X(d8(8DLAGej#Y#j4BV}$H zLJn8z2(L_nr)%jW2j$pz(4ne~X2W0|y+n7nlch z$|Ga1*Hp>d=6f2Lk287E4@~+HNeA424ojDlDQ?r%D!da8e-< ziYF~GLrO3E)=ubZw|PDXg8=cWug%7KVs=+7raa~hsO*adllIl*F-%jz5#}dfuWcnDp=~wL zqfv&5F`~F?m>so1Z*Gw}c@sHurpwP8=-66EO13A}KH2egmn2nY8Ghwpg zL+`(9O4)csxR_rt|Dj-!z{Ay*TQe6!%KDa*Af+tU+mCeRf$&BKeFx=z&%+9P-IUHu;#O*d>XvZem6-7=_cQ+EH`0ab-9vnd&1 z8e1#VHhRgQC`oE-bYi#Ctk?|5!9^^R6|z~^%z-B+)v4PKg^vn9>am{b8fs3d$iM^2 z<~WP?9@e2W?=zn1+gVMr_zYHP5Q9$gH_4V}Ar+L=-J&E(&}M)*ybGJ^hif%?Rq}6V zXQxEvb@=2fLE{HzK#6WL$<&Mn%3xk4L$me)tifJH-iEBAUY%M}!_NdkmioqffVK@NYr z)w1FhN|40t*($p2y^G8k?exO3FQ|x}rbs5eimS+Xy-wId8PWmN3BYck zYtWYZ`nTfR{Njv?f94sg4h^q)GeB?*jwQ#E1Z2zUY_=p%8)hO&@Ke05_bg&mSZNs; z1U2BylfOdiyM#C8kq4v`O>)N~6}t+ygB_LXrVd!A8-rDgsg`;88Eiey~1#B!9{w?zkide{*Sm>~tmQ7&?EI8$97w%5oh= zgElAMpIP{tsE2Kw)Pa87N7(r_Y;2HFd>ZQe;akRCTPR+e{Q1N!o`cl2=z06Ww9Iz+ zf73;P(X7hRY_RXvj6l85YLXMX=dNbP(|n5N=m$34|lz~ z-;{sMtjS6f39tMZk1#2#9mQAZB)0L zf)YBm@>UBPv3h4UmU2j<(d2Y{(C!S6^E;_hyOuat%bQ>3nkS)cXBlvHvfTTb{hEu=nMZSdZQ&&QXkB7J0LOeb3n{^XcyKP zRt7ES{>`u$TL2j9m-tI1zuNMJC7{enw?y?r^Mic-QKzjDX7{NH|{NFvk5j_B1fucB`_+bZdf z7bF3{kZvR1Qlg#04()aO6{R7HRp|q&(`Hq(_HAKYUT3z*GW~k`au~M*QmxWJG<|D< zA_U-IL?O7b73J}jAAYUF7ls)sgY!ASb9uTpqTY?`M8mumAX4J3UfFwvR+trsaS_#iKCpAo zh~kq=;aX$=gpb|wg-yl2B{yRa#piv{en+Ll0Y`dO4?)NtVCjw#_>nK8c9Y=q4;@=e z|Eie4`p?&aUrK#qKb1w{4gSW%HfRhB z7FOg#c@(Gy_L`TmoKRb=ch*K8GJ3%Obo{3vF`}wLaBe~hk!+iz%>zv?<#&^{b(z*b z_p7b`AN0UTa>r|w?Ol&Er$ACpLi9++58;hQtp&qFKR)-16wXYv7*mc79^9Ip_rPGa zIm7pDbk>EsV;W6nzZOotGZc8`N-V0FSB!P7mq_>aQGJ}kI%wQYJ#c-u>DZ2K9G1~N zb_f{KLAOcdmwF+O{aU!EJ#9|AGard%=)x3^&n)vGk53)0QCVmWPGz=VKblJ2Z(%D- zkrj%l=3MT?;cXf|E_4Dd>Lizxdz%jazLvNSxrn?Z?dsn7n0NOeZq_xyrC;0rZx8E& z4$kqTf*Ki^+r)PY8q1*MDHrJ0JFy4NiQLkBJ{(P8AC8Y)P%Q`_lv{H#mZs&Q3q4VA(D`xk(&atdVWKcZ_h@P0}q@ndMhH z&E_Gt_ehaUbd$Nn^M(q9&i0&a-dY7?8<^?FUhNKS3_VlNmTiy& z)0+xLF|m^xFl%e`kzW>K?p(?{wkYoYT|%vg3b)thtvxrU?IPb_jo;)w6Thy#iT2!k z>pdXuerLL6180}S@>pq%#NLzCl0eP{7u4P)g-`m5!wI#AqhoTz4eUL%a1l_A-9Bm@ zi;`p7AiwH$iWJ4Iz0eZXKjGlASO&nEMMrC@j&o~5Q9<;M1vHe>iH33R2$34?KoG;4 zU=Q}mX$wHvp@36qmz+nZkqY0PD+ZilY) z9GTpdl_E#?wljQHf_cs0N3WR7!74NgBwUa-!&8~C4kyNOti zHhidyfY9KkMk~`%HLBK@8dUPTGy)t8UZ{9y7)fvD8b`o3U;SUB$zuUwCX_^AMlv@N zLwJ^pvyH>VXgRWWiZYlJIc5w!qR|xN=Gw@mI=pHFm|R81Qsj{L&j0D__*U-maD8gx zzx%v5(QSEu6|XMt?qqJhsJDU#B<1KjBpD`KKfJO|Ar{IFYDcFEJ$`O*TJ7*QI^*=RkfE97eZS!3cb_`!5%dI!9y=Z#8i`|0tc#t-xTkFL1v4brl_ zL0|N(?ji&Kh`EX63Pmb>sy`~5{J~Sj50Hj4&Gi4ruMFkU?SuS}UwO+5m}I!BE{3Us_ID(gqu$~vk4OZ zovKg(fd%t-|6U(TdOWRltK^+;L{Hiwkl~O{sDETabCFUH$yL!Ftav_*V1n z{%>D2e1jXlqeT%`&ObIxgg0VLzmNnyKAH z68azWwfWE5ogzlnR+=)OI2uyk6X^h~*W?=8Na>TYsW_g{NXiw7BcCbpI!V`?jrKsy zHC4EB{ZXx|$#qEwY)OV4;p+>T)yH4_&%TfrU0&W~$uEd_yoNQ@Z)Q;LkY?naZaxCL zVsp`(RYjvFK3ry3jayj4>AUGE_|r7P2%uQu!&J0wi-C5%UTh)X>8x(~Q*b-#4#8ei z;2rxt1{^)(VTi5?GT$gGq73H4`r1H%QjDR8s7uFM-h@S_w$8$vhH z02!}<_xDdco~I!$-!9T0R#vt`cuczg>a@qeQv!{Vmj0Mtn$QTXRyd{>krhssx)|rL z4K!1P^`Fm&j+@j%9npF`0YIQOFU2cujmWYC0uninkSM)jvqD`_NUa&B8t~`y!u1Sc z0j|*1v;;M6&x8TiR6^IbMsX~)Tx&4nAASecE<=#I7kgxH$jwIevggTR$^*@(o6V}u zt43dturNE}6G=(H6)WX++_CKMf2a&$)u%&(Kdv7i5sgkW5nrKU`M^Yec9G-q6t(|d z52S~}l&SRv&3x>~5-cmOSTz8J~`hm0a#N ztIDy@H$!5Kix$F;m}>;`duQ2zH5#*8uXET-O8jf@EpC1()m-l13KgDC<%YaY!e8pC zy-REGD{|{sR_IcNvOQT?1uB&_!GOtJ8Jh%CSA@7!Gmw^Qsk8Cx(`{h-|9jcF`;*Lhz`o+S0W7Po8av{WB* zljp{H#Y%!6tLd?qlcHkO6TFMs{^BC+|98hh`UxvnK-Na3&b~X8B#4knm1Tjmm&LFK zjRUcD8_eZ3a-1%ECeY-SF1lFg!Px#?d0s|XNtI* zy1!fWY8zxlIXtU&QPDh}xs6?|onT<(Hr| zF>UW<8Cc(l`()7F_Fbpnyv?-Im`y6X3?0=TSxaLAqXRe)gDZkri>!(G)VVad4JOs# zdHm+=$CQ*;fMH%ppRT^L3NcJt7AeIqFd4tP(yy?AU;~?8TA-WZ*amp@^D zAU)FmW?N<329fq3F3S{U-h zoNeULV4hSyHFZZ|o7r;M-jGX9+9n~2y|E(_gaQHWQZ)9g+ zF?YygTVgv@hf+vL7Ly8WBw#a=lJROEP2=e6L3pEON4n6vZQt%r-H*kYXn}Q&?N3+G zb({+wDyz02pT@>2S1K=Po}|+C^%Jsqcc1Pq^Wa1yxp zQSc<@XupYG;Ij9PU;RB$Ct}2N)hZI=iP}5)PD}Lv?wqmEz0lt8 zV~mupLmlH*6>zM9*LuaVqvh`d1m3;B6=4|_`0Q-J?2hrgKJcZZw^YLvNK!d#VLLu9 z#IKjMMxOZNELtl-xB|8%B zr2zuKCCW@a`tajEYToC1X?v6r)pXr@iC-SOSTCYH zscucLvW9ml-){c5F-4`52L4N$>CG;`=)wiW6`X4_^96 z>T`?!hA(Q(s%d#oy3v+>xD}7pRL$B`ZGO0;@r|R(n+yuRi&6XX|8{ALHAX$-J(@VH z>nEx?0;bFimNmMazfVaN{mzyd&nX!)z)Hbn09JDr#ONQ>X!nL#{WDB2@g)-wT3C6q snX$j*je#$R9>IC#+xg6lKc5lD{2qF{6w|ul8=weEa_?koL1xhZ4_0trS^xk5 literal 21794 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 a>gOyB_1_V5rW2SV7(8A5T-G@yGywn}br3!P