diff --git a/Jellyshare/Api/HijackConstraint.cs b/Jellyshare/Api/HijackConstraint.cs index 7c05303..b26252c 100644 --- a/Jellyshare/Api/HijackConstraint.cs +++ b/Jellyshare/Api/HijackConstraint.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Text.RegularExpressions; +using Jellyshare.State; using Microsoft.AspNetCore.Mvc.ActionConstraints; namespace Jellyshare.Api; @@ -13,7 +14,10 @@ public class HijackConstraint : IActionConstraint public bool Accept(ActionConstraintContext context) { - var items = Plugin.Instance!.RemoteVideos; + var stateManager = + context.RouteContext.HttpContext.RequestServices.GetService(typeof(StateManager)) + as StateManager; + var items = stateManager.RemoteVideos; var path = context.RouteContext.HttpContext.Request.Path.ToString(); foreach (var match in _guidRegex.Matches(path).Cast()) { diff --git a/Jellyshare/Api/HijackController.cs b/Jellyshare/Api/HijackController.cs index 3d7a9f1..b8ad8d1 100644 --- a/Jellyshare/Api/HijackController.cs +++ b/Jellyshare/Api/HijackController.cs @@ -4,6 +4,7 @@ using System.Text; using System.Threading.Tasks; using System.Web; +using Jellyshare.State; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Entities; using Microsoft.AspNetCore.Authorization; @@ -20,16 +21,19 @@ public class StreamHijackController : ControllerBase private readonly HttpClient _httpClient; private readonly ILibraryManager _libraryManager; private readonly ILogger _logger; + private readonly StateManager _stateManager; public StreamHijackController( HttpClient httpClient, ILibraryManager libraryManager, - ILogger logger + ILogger logger, + StateManager stateManager ) { _httpClient = httpClient; _libraryManager = libraryManager; _logger = logger; + _stateManager = stateManager; } [Hijack] @@ -41,12 +45,12 @@ [FromQuery] Guid userId { var item = _libraryManager.GetItemById(itemId)!; var remoteAddress = new Uri(item.GetProviderId("JellyshareRemoteAddress")); - var remoteUser = Plugin.Instance!.UserMap[(userId, remoteAddress)]; + var remoteUser = _stateManager.RemoteServers[remoteAddress].User; var remoteId = Guid.Parse(item.GetProviderId("JellyshareRemoteId")); var path = $"Items/{remoteId}/PlaybackInfo"; var query = HttpUtility.ParseQueryString(HttpContext.Request.QueryString.ToString()); - query["api_key"] = Plugin.Instance!.RemoteServers[remoteAddress].ApiKey.ToString("N"); + query["api_key"] = _stateManager.RemoteServers[remoteAddress].ApiKey.ToString("N"); query["UserId"] = remoteUser.ToString("N"); query["MediaSourceId"] = remoteId.ToString("N"); @@ -80,7 +84,7 @@ public async Task HlsHijack([FromRoute] Guid itemId, [FromRoute] s var path = $"Videos/{remoteId}/{remainder}"; var query = HttpUtility.ParseQueryString(HttpContext.Request.QueryString.ToString()); - query["api_key"] = Plugin.Instance!.RemoteServers[remoteAddress].ToString(); + query["api_key"] = _stateManager.RemoteServers[remoteAddress].ApiKey.ToString("N"); query["MediaSourceId"] = remoteId.ToString("N"); var address = new Uri(remoteAddress, $"{path}?{query}"); diff --git a/Jellyshare/Configuration/PluginConfiguration.cs b/Jellyshare/Configuration/PluginConfiguration.cs index 150b541..1d0e84d 100644 --- a/Jellyshare/Configuration/PluginConfiguration.cs +++ b/Jellyshare/Configuration/PluginConfiguration.cs @@ -1,8 +1,10 @@ +using System.Collections.Generic; +using Jellyshare.State; using MediaBrowser.Model.Plugins; namespace Jellyshare.Configuration; public class PluginConfiguration : BasePluginConfiguration { - public string RemoteServersRaw { get; set; } = string.Empty; + public List RemoteServers { get; set; } = new(); } diff --git a/Jellyshare/Configuration/configPage.html b/Jellyshare/Configuration/configPage.html index 25490c3..24320e0 100644 --- a/Jellyshare/Configuration/configPage.html +++ b/Jellyshare/Configuration/configPage.html @@ -9,23 +9,87 @@ id="JellyshareConfigPage" data-role="page" class="page type-interior pluginConfigurationPage" - data-require="emby-input,emby-button,emby-select,emby-checkbox,emby-textarea" + data-require="emby-input,emby-button,emby-select,emby-checkbox" >
+
+

+ Remote Servers +

+ +
+
-
- - +
@@ -40,47 +104,110 @@
+
diff --git a/Jellyshare/Plugin.cs b/Jellyshare/Plugin.cs index a161823..89b1adf 100644 --- a/Jellyshare/Plugin.cs +++ b/Jellyshare/Plugin.cs @@ -1,41 +1,29 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.IO; -using System.Linq; -using System.Text.Json; using System.Threading; -using System.Threading.Tasks; -using Jellyfin.Data.Enums; using Jellyshare.Configuration; using Jellyshare.State; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Plugins; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Entities; using MediaBrowser.Model.Plugins; using MediaBrowser.Model.Serialization; -using Microsoft.Extensions.Logging; namespace Jellyshare; public class Plugin : BasePlugin, IHasWebPages { - private readonly ILibraryManager _libraryManager; - private readonly ILogger _logger; + private readonly StateManager _stateManager; public Plugin( IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer, - ILibraryManager libraryManager, - ILogger logger + StateManager stateManager ) : base(applicationPaths, xmlSerializer) { Instance = this; - _libraryManager = libraryManager; - _logger = logger; + _stateManager = stateManager; } public override string Name => "Jellyshare"; @@ -44,13 +32,6 @@ ILogger logger public static Plugin? Instance { get; private set; } - public Dictionary RemoteServers { get; private set; } = new(); - - public HashSet RemoteVideos { get; private set; } = new(); - - // Each (LocalUser, RemoteAddress) has an associated RemoteUser. - public Dictionary<(Guid UserId, Uri RemoteAddress), Guid> UserMap { get; private set; } = new(); - public IEnumerable GetPages() { return new[] @@ -67,94 +48,9 @@ public IEnumerable GetPages() }; } - public override void UpdateConfiguration(BasePluginConfiguration configuration) + public override async void UpdateConfiguration(BasePluginConfiguration configuration) { base.UpdateConfiguration(configuration); - LoadRemoteServers(); - } - - public async Task SaveState(CancellationToken cancellationToken) - { - await SaveUserMap(cancellationToken); - } - - public async Task LoadState(CancellationToken cancellationToken) - { - LoadRemoteServers(); - await LoadUserMap(cancellationToken); - } - - public void RefreshRemoteVideos() - { - var query = new InternalItemsQuery() { IncludeItemTypes = new[] { BaseItemKind.Movie } }; - RemoteVideos = _libraryManager - .GetItemList(query) - .Where(item => item.HasProviderId("JellyshareRemoteAddress")) - .Select(item => item.Id) - .ToHashSet(); - } - - private void LoadRemoteServers() - { - try - { - RemoteServers = JsonSerializer - .Deserialize>(Configuration.RemoteServersRaw) - .ToDictionary(server => server.Address, server => server); - } - catch (Exception ex) - { - _logger.LogError("Failed to deserialize RemoteServers configuration."); - _logger.LogError(ex.Message); - } - } - - private async Task SaveUserMap(CancellationToken cancellationToken) - { - var path = Path.Combine(DataFolderPath, "state-usermap.json"); - await using var stream = File.OpenWrite(path); - // Might not have to do this in .NET 8+. - var intermediate = new List>(); - foreach (var ((userId, remoteAddress), remoteUserId) in UserMap) - { - intermediate.Add( - new[] - { - userId.ToString(), - remoteAddress.ToString(), - remoteUserId.ToString() - }.ToList() - ); - } - await JsonSerializer.SerializeAsync( - stream, - intermediate, - cancellationToken: cancellationToken - ); - } - - private async Task LoadUserMap(CancellationToken cancellationToken) - { - var path = Path.Combine(DataFolderPath, "state-usermap.json"); - if (!File.Exists(path)) - { - return; - } - await using var state = File.OpenRead(path); - // Might not have to do this in .NET 8+. - var intermediate = ( - await JsonSerializer.DeserializeAsync>>( - state, - cancellationToken: cancellationToken - ) - )!; - UserMap.Clear(); - foreach (var entry in intermediate) - { - var userId = Guid.Parse(entry[0]); - var remoteAddress = new Uri(entry[1]); - var remoteUserId = Guid.Parse(entry[2]); - UserMap[(userId, remoteAddress)] = remoteUserId; - } + await _stateManager.Refresh(CancellationToken.None); } } diff --git a/Jellyshare/Providers/JellyshareMovieProvider.cs b/Jellyshare/Providers/JellyshareMovieProvider.cs index 685a253..0a14987 100644 --- a/Jellyshare/Providers/JellyshareMovieProvider.cs +++ b/Jellyshare/Providers/JellyshareMovieProvider.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using Jellyfin.Extensions.Json; +using Jellyshare.State; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; @@ -23,16 +24,19 @@ public class JellyShareProvider : IRemoteMetadataProvider, IHa private readonly HttpClient _httpClient; private readonly ILogger _logger; private readonly ILibraryManager _libraryManager; + private readonly StateManager _stateManager; public JellyShareProvider( HttpClient httpClient, ILogger logger, - ILibraryManager libraryManager + ILibraryManager libraryManager, + StateManager stateManager ) { _httpClient = httpClient; _logger = logger; _libraryManager = libraryManager; + _stateManager = stateManager; } public string Name => "Jellyshare"; @@ -71,10 +75,8 @@ CancellationToken cancellationToken var query = new InternalItemsQuery() { Path = info.Path }; var item = _libraryManager.GetItemList(query).First(); - var userId = Plugin - .Instance!.UserMap.First(pair => pair.Key.RemoteAddress == remoteAddress) - .Value; - var apiKey = Plugin.Instance!.RemoteServers[remoteAddress].ApiKey; + var userId = _stateManager.RemoteServers[remoteAddress].User; + var apiKey = _stateManager.RemoteServers[remoteAddress].ApiKey; var address = new Uri( remoteAddress, $"/Users/{userId}/Items/{remoteId}?api_key={apiKey:N}" diff --git a/Jellyshare/ServiceRegistrator.cs b/Jellyshare/ServiceRegistrator.cs new file mode 100644 index 0000000..6ae2c2a --- /dev/null +++ b/Jellyshare/ServiceRegistrator.cs @@ -0,0 +1,17 @@ +using Jellyshare.State; +using Jellyshare.Synchronize; +using MediaBrowser.Common.Plugins; +using Microsoft.Extensions.DependencyInjection; + +namespace Jellyshare; + +public class ServiceRegistrator : IPluginServiceRegistrator +{ + public void RegisterServices(IServiceCollection serviceCollection) + { + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + } +} diff --git a/Jellyshare/State/RemoteLibrary.cs b/Jellyshare/State/RemoteLibrary.cs deleted file mode 100644 index 4f47a07..0000000 --- a/Jellyshare/State/RemoteLibrary.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; - -namespace Jellyshare.State; - -public class RemoteLibrary -{ - public Guid LocalId { get; init; } - public Guid ExternalId { get; init; } - public Uri RemoteAddress { get; init; } -} diff --git a/Jellyshare/State/RemoteLibraryDto.cs b/Jellyshare/State/RemoteLibraryDto.cs new file mode 100644 index 0000000..e888260 --- /dev/null +++ b/Jellyshare/State/RemoteLibraryDto.cs @@ -0,0 +1,7 @@ +namespace Jellyshare.State; + +public class RemoteLibraryDto +{ + public string RemoteId { get; set; } + public string LocalName { get; set; } +} diff --git a/Jellyshare/State/RemoteServer.cs b/Jellyshare/State/RemoteServer.cs index 2e282ce..bd32e84 100644 --- a/Jellyshare/State/RemoteServer.cs +++ b/Jellyshare/State/RemoteServer.cs @@ -6,6 +6,10 @@ namespace Jellyshare.State; public class RemoteServer { public Uri Address { get; set; } + public Guid ApiKey { get; set; } - public IEnumerable Libraries { get; set; } + + public Dictionary Libraries { get; set; } + + public Guid User { get; set; } } diff --git a/Jellyshare/State/RemoteServerDto.cs b/Jellyshare/State/RemoteServerDto.cs new file mode 100644 index 0000000..503cc13 --- /dev/null +++ b/Jellyshare/State/RemoteServerDto.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace Jellyshare.State; + +public class RemoteServerDto +{ + public string Address { get; set; } + + public string ApiKey { get; set; } + + public List Libraries { get; set; } + + public string User { get; set; } +} diff --git a/Jellyshare/State/RemoteUser.cs b/Jellyshare/State/RemoteUser.cs new file mode 100644 index 0000000..cb5a46c --- /dev/null +++ b/Jellyshare/State/RemoteUser.cs @@ -0,0 +1,9 @@ +using System; + +namespace Jellyshare.State; + +public class RemoteUser +{ + public Guid LocalId { get; set; } + public Guid RemoteId { get; set; } +} diff --git a/Jellyshare/State/RemoteUserDto.cs b/Jellyshare/State/RemoteUserDto.cs new file mode 100644 index 0000000..08981e0 --- /dev/null +++ b/Jellyshare/State/RemoteUserDto.cs @@ -0,0 +1,10 @@ +using System; + +namespace Jellyshare.State; + +public class RemoteUserDto +{ + public string LocalId { get; set; } + public string RemoteId { get; set; } + public string RemoteAddress { get; set; } +} diff --git a/Jellyshare/State/Startup.cs b/Jellyshare/State/Startup.cs index 5df0085..cb68a7a 100644 --- a/Jellyshare/State/Startup.cs +++ b/Jellyshare/State/Startup.cs @@ -7,16 +7,20 @@ namespace Jellyshare.State; public class Startup : IServerEntryPoint { + private readonly StateManager _stateManager; + + public Startup(StateManager stateManager) + { + _stateManager = stateManager; + } + public void Dispose() { - Plugin.Instance?.RemoteVideos.Clear(); - Plugin.Instance?.UserMap.Clear(); GC.SuppressFinalize(this); } public async Task RunAsync() { - await Plugin.Instance!.LoadState(CancellationToken.None); - Plugin.Instance!.RefreshRemoteVideos(); + await _stateManager.Refresh(CancellationToken.None); } } diff --git a/Jellyshare/State/StateManager.cs b/Jellyshare/State/StateManager.cs new file mode 100644 index 0000000..f2f3e54 --- /dev/null +++ b/Jellyshare/State/StateManager.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Jellyfin.Data.Enums; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Entities; + +namespace Jellyshare.State; + +public class StateManager +{ + private readonly ILibraryManager _libraryManager; + + public StateManager(ILibraryManager libraryManager) + { + _libraryManager = libraryManager; + } + + public Dictionary RemoteServers { get; private set; } = new(); + + public HashSet RemoteVideos { get; private set; } = new(); + + public async Task Refresh(CancellationToken cancellationToken) + { + var query = new InternalItemsQuery() { IncludeItemTypes = new[] { BaseItemKind.Movie } }; + RemoteVideos = _libraryManager + .GetItemList(query) + .Where(item => item.HasProviderId("JellyshareRemoteAddress")) + .Select(item => item.Id) + .ToHashSet(); + + RemoteServers = Plugin.Instance!.Configuration.RemoteServers.ToDictionary( + dto => new Uri(dto.Address), + dto => DeserializeRemoteServerDto(dto) + ); + } + + public async Task Save(CancellationToken cancellationToken) + { + var instance = Plugin.Instance!; + instance.Configuration.RemoteServers = RemoteServers + .Values.Select(x => SerializeRemoteServer(x)) + .ToList(); + instance.SaveConfiguration(); + } + + private static RemoteServer DeserializeRemoteServerDto(RemoteServerDto dto) + { + return new RemoteServer() + { + Address = new Uri(dto.Address), + ApiKey = Guid.Parse(dto.ApiKey), + Libraries = dto.Libraries.ToDictionary( + library => Guid.Parse(library.RemoteId), + library => library.LocalName + ), + User = Guid.Parse(dto.User) + }; + } + + private static RemoteServerDto SerializeRemoteServer(RemoteServer server) + { + return new RemoteServerDto() + { + Address = server.Address.ToString(), + ApiKey = server.ApiKey.ToString(), + Libraries = server + .Libraries.Select( + pair => + new RemoteLibraryDto() + { + RemoteId = pair.Key.ToString(), + LocalName = pair.Value + } + ) + .ToList(), + User = server.User.ToString() + }; + } +} diff --git a/Jellyshare/Synchronize/LibrarySync.cs b/Jellyshare/Synchronize/LibrarySync.cs index 9831bed..869da35 100644 --- a/Jellyshare/Synchronize/LibrarySync.cs +++ b/Jellyshare/Synchronize/LibrarySync.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; using System.Linq; using System.Net.Http; @@ -24,45 +25,43 @@ public class LibrarySync { private readonly HttpClient _httpClient; private readonly ILibraryManager _libraryManager; - private readonly ILogger _logger; + private readonly StateManager _stateManager; + private readonly ILogger _logger; public LibrarySync( HttpClient httpClient, ILibraryManager libraryManager, - ILogger logger + StateManager stateManager, + ILogger logger ) { _httpClient = httpClient; _libraryManager = libraryManager; + _stateManager = stateManager; _logger = logger; } public async Task SyncLibraries(CancellationToken cancellationToken) { - var instance = Plugin.Instance!; - foreach (var server in instance.RemoteServers.Values) + var localLibraries = GetLocalLibraries(); + _logger.LogInformation("Hit LibrarySync"); + foreach (var server in _stateManager.RemoteServers.Values) { - var remoteLibraries = await GetRemoteLibraries( - server.Address, - server.ApiKey, - cancellationToken - ); - var localLibraries = GetLocalLibraries(); - - foreach (var remoteLibrary in remoteLibraries) + _logger.LogInformation($"Each: {server.Address}"); + var remoteLibraries = await GetRemoteLibraries(server, cancellationToken); + foreach (var (libraryId, libraryName) in server.Libraries) { - var name = $"{server.Address.Host} {remoteLibrary.Name}"; - if (!localLibraries.Contains(name)) + if (!localLibraries.Contains(libraryName)) { - CreateLocalLibrary(remoteLibrary, server.Address); - localLibraries.Add(name); + var remoteLibrary = remoteLibraries[libraryId]; + CreateLocalLibrary(remoteLibrary, server.Address, libraryName); } } } - await Plugin.Instance!.SaveState(cancellationToken); + await _stateManager.Refresh(cancellationToken); } - private HashSet GetLocalLibraries() + private IEnumerable GetLocalLibraries() { return _libraryManager .GetUserRootFolder() @@ -71,7 +70,7 @@ private HashSet GetLocalLibraries() .ToHashSet(); } - private void CreateLocalLibrary(BaseItemDto remoteLibrary, Uri remoteAddress) + private void CreateLocalLibrary(BaseItemDto remoteLibrary, Uri remoteAddress, string localName) { var collectionType = remoteLibrary.CollectionType switch { @@ -82,16 +81,11 @@ private void CreateLocalLibrary(BaseItemDto remoteLibrary, Uri remoteAddress) "The only libraries supported are movies, and tvshows." ) }; - var name = $"{remoteAddress.Host} {remoteLibrary.Name}"; - var path = Path.Combine( - Plugin.Instance!.DataFolderPath, - "Libraries", - Guid.NewGuid().ToString("N") - ); + var path = Path.Combine(Plugin.Instance!.DataFolderPath, "Libraries", localName); Directory.CreateDirectory(path); _libraryManager.AddVirtualFolder( - name, + localName, collectionType, new LibraryOptions() { @@ -119,26 +113,17 @@ private void CreateLocalLibrary(BaseItemDto remoteLibrary, Uri remoteAddress) }; } - private async Task> GetRemoteLibraries( - Uri remoteAddress, - Guid apiKey, + private async Task> GetRemoteLibraries( + RemoteServer server, CancellationToken cancellationToken ) { - var path = $"/Library/MediaFolders?api_key={apiKey:N}"; - var server = Plugin.Instance!.RemoteServers[remoteAddress]; - + var path = $"/Library/MediaFolders?api_key={server.ApiKey:N}"; var mediaFolders = await _httpClient.GetFromJsonAsync>( - new Uri(remoteAddress, path), + new Uri(server.Address, path), JsonDefaults.Options, cancellationToken ); - return mediaFolders! - .Items.Where( - mediaFolder => - mediaFolder.Type == BaseItemKind.CollectionFolder - && server.Libraries.Contains(mediaFolder.Name) - ) - .ToList(); + return mediaFolders!.Items.ToDictionary(item => item.Id, item => item); } } diff --git a/Jellyshare/Synchronize/UserSync.cs b/Jellyshare/Synchronize/UserSync.cs index ecaa7ec..dafee1d 100644 --- a/Jellyshare/Synchronize/UserSync.cs +++ b/Jellyshare/Synchronize/UserSync.cs @@ -1,16 +1,12 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Net.Http; using System.Net.Http.Json; using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Jellyfin.Data.Entities; -using Jellyfin.Data.Enums; using Jellyfin.Extensions.Json; +using Jellyshare.State; using MediaBrowser.Controller; -using MediaBrowser.Controller.Library; using MediaBrowser.Model.Dto; namespace Jellyshare.Synchronize; @@ -19,71 +15,36 @@ public class UserSync { private readonly HttpClient _httpClient; private readonly IServerApplicationHost _applicationHost; - private readonly IUserManager _userManager; + private readonly StateManager _stateManager; public UserSync( HttpClient httpClient, IServerApplicationHost applicationHost, - IUserManager userManager + StateManager stateManager ) { _httpClient = httpClient; _applicationHost = applicationHost; - _userManager = userManager; + _stateManager = stateManager; } public async Task SyncUsers(CancellationToken cancellationToken) { - var instance = Plugin.Instance!; - foreach (var server in instance.RemoteServers.Values) + foreach (var server in _stateManager.RemoteServers.Values) { - var remoteUsers = await GetRemoteUsers( - server.Address, - server.ApiKey, - cancellationToken - ); - var servername = _applicationHost.FriendlyName; - foreach (var user in GetLocalUsers()) + if (server.User == Guid.Empty) { - if (user.Username.Contains("Jellyshare")) - { - continue; - } - - var username = $"Jellyshare {servername} {user.Username}"; - if (!remoteUsers.Contains(username)) - { - var remoteUser = await CreateRemoteUser( - username, - user.Id.ToString(), - server.Address, - server.ApiKey, - cancellationToken - ); - Plugin.Instance!.UserMap[(user.Id, server.Address)] = remoteUser.Id; - } + var user = await CreateRemoteUser( + $"Jellyshare {_applicationHost.FriendlyName}", + Guid.NewGuid().ToString(), + server.Address, + server.ApiKey, + cancellationToken + ); + server.User = user.Id; } } - } - - private async Task> GetRemoteUsers( - Uri remoteAddress, - Guid apiKey, - CancellationToken cancellationToken - ) - { - var path = $"/Users?isHidden=true&api_key={apiKey:N}"; - var users = await _httpClient.GetFromJsonAsync>( - new Uri(remoteAddress, path), - JsonDefaults.Options, - cancellationToken - ); - return users!.Select(user => user.Name).ToHashSet(); - } - - private IEnumerable GetLocalUsers() - { - return _userManager.Users; + await _stateManager.Save(cancellationToken); } private async Task CreateRemoteUser( diff --git a/Jellyshare/Synchronize/VideoSync.cs b/Jellyshare/Synchronize/VideoSync.cs index c2a4136..c645b1e 100644 --- a/Jellyshare/Synchronize/VideoSync.cs +++ b/Jellyshare/Synchronize/VideoSync.cs @@ -28,16 +28,19 @@ public class VideoSync private readonly HttpClient _httpClient; private readonly ILibraryManager _libraryManager; private readonly ILogger _logger; + private readonly StateManager _stateManager; public VideoSync( HttpClient httpClient, ILibraryManager libraryManager, - ILogger logger + ILogger logger, + StateManager stateManager ) { _httpClient = httpClient; _libraryManager = libraryManager; _logger = logger; + _stateManager = stateManager; } public async Task SyncVideos(CancellationToken cancellationToken) @@ -54,9 +57,8 @@ public async Task SyncVideos(CancellationToken cancellationToken) localVideos.Add(remoteVideo.Id); } } - _ = library.RefreshMetadata(cancellationToken); } - Plugin.Instance!.RefreshRemoteVideos(); + await _stateManager.Refresh(cancellationToken); } private void CreateVideo(BaseItemDto remoteVideo, Folder libraryEntity) @@ -100,7 +102,7 @@ CancellationToken cancellationToken { var address = new Uri(library.GetProviderId("JellyshareRemoteAddress")); var externalId = library.GetProviderId("JellyshareRemoteId"); - var apiKey = Plugin.Instance!.RemoteServers[address].ApiKey; + var apiKey = _stateManager.RemoteServers[address].ApiKey; var path = $"Items/?api_key={apiKey:N}&ParentId={externalId}"; var videos = await _httpClient.GetFromJsonAsync>( new Uri(address, path), diff --git a/Jellyshare/Tasks/SyncTask.cs b/Jellyshare/Tasks/SyncTask.cs index ebe541d..917290a 100644 --- a/Jellyshare/Tasks/SyncTask.cs +++ b/Jellyshare/Tasks/SyncTask.cs @@ -1,14 +1,11 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using Jellyshare.State; using Jellyshare.Synchronize; -using MediaBrowser.Controller; -using MediaBrowser.Controller.Library; using MediaBrowser.Model.Tasks; -using Microsoft.Extensions.Logging; namespace Jellyshare.Tasks; @@ -22,18 +19,19 @@ public class SyncTask : IScheduledTask private readonly LibrarySync _librarySync; private readonly UserSync _userSync; private readonly VideoSync _videoSync; + private readonly StateManager _stateManager; public SyncTask( - ILibraryManager libraryManager, - IServerApplicationHost applicationHost, - IUserManager userManager, - HttpClient httpClient, - ILogger logger + LibrarySync librarySync, + UserSync userSync, + VideoSync videoSync, + StateManager stateManager ) { - _librarySync = new LibrarySync(httpClient, libraryManager, logger); - _userSync = new UserSync(httpClient, applicationHost, userManager); - _videoSync = new VideoSync(httpClient, libraryManager, logger); + _librarySync = librarySync; + _userSync = userSync; + _videoSync = videoSync; + _stateManager = stateManager; } // Create some dummy video file for each remote video. @@ -45,7 +43,7 @@ public async Task ExecuteAsync(IProgress progress, CancellationToken can await _librarySync.SyncLibraries(cancellationToken); await _userSync.SyncUsers(cancellationToken); await _videoSync.SyncVideos(cancellationToken); - await Plugin.Instance!.SaveState(cancellationToken); + await _stateManager.Save(cancellationToken); } public IEnumerable GetDefaultTriggers() => Enumerable.Empty(); diff --git a/README.md b/README.md index ef9dd1c..dd4aee8 100644 --- a/README.md +++ b/README.md @@ -35,25 +35,13 @@ each instance and go through their installers. Create a Movie library in each instance using the relevant media folders discussed previously in the preamble. Create a new API key for each instance. Pick any instance, access its Jellyshare -configuration in the Plugins tab, and insert JSON similar to what's shown below. -Replace any values with your own, as needed. You can repeat this process with -more instances if needed. - - [ - { - "Address": "http://jellyfin_2:8096/", - "ApiKey": "618bb6933b1540bf8916a4ca30096ccc", - "Libraries": ["Movies"] - }, - { - "Address": "http://jellyfin_3:8096/", - "ApiKey": "c4b477a7cd544f1693fb459f0cd51c75" - "Libraries": ["Movies"] - } - ] +configuration in the Plugins tab, and add two new remote servers. Be warned that +error handling is almost non existent. If invalid data is preventing the server +from launching, you may need to first delete +DevData/Config/?/plugins/configuration/Jellyshare.xml. Start the Jellyshare Sync task within the Schedules Tasks tab. The task should -take around 10-15 seconds. +take a few seconds at most. If an error occurs at any step, check the latest log file. Feel free to create an issue with all your relevant information, but remember that this software is