diff --git a/WebDavClientService.cs b/WebDavClientService.cs index fb3a6af..2b0276b 100644 --- a/WebDavClientService.cs +++ b/WebDavClientService.cs @@ -8,31 +8,55 @@ namespace Jellyfin.Plugin.Webdav using System; using System.Collections.Generic; using System.IO; + using System.Net; using System.Threading.Tasks; using Jellyfin.Plugin.Webdav.Configuration; + using WebDav.Client; /// /// Service for interacting with WebDAV endpoints. /// public sealed class WebDavClientService { - private readonly PluginConfiguration _configuration; + private readonly PluginConfiguration _config; + private readonly WebDavClient _client; - public WebDavClientService(PluginConfiguration configuration) + /// + /// Initializes a new instance of the class. + /// + /// Plugin configuration. + public WebDavClientService(PluginConfiguration config) { - _configuration = configuration; + _config = config; + var parameters = new WebDavClientParams + { + BaseAddress = new Uri(_config.BaseUrl.TrimEnd('/')), + Credentials = new NetworkCredential(_config.Username, _config.Password), + PreAuthenticate = true + }; + _client = new WebDavClient(parameters); } - /// List resources at the specified WebDAV path. - public Task> ListAsync(string path) + /// + /// Lists resources at the specified WebDAV path. + /// + /// Remote path. + /// A collection of WebDAV resources. + public async Task> ListAsync(string path) { - return Task.FromResult>(Array.Empty()); + var response = await _client.Propfind(path).ConfigureAwait(false); + return response.Resources; } - /// Get a raw file stream from the WebDAV endpoint. - public Task GetStreamAsync(string path) + /// + /// Gets a raw file stream from the WebDAV endpoint. + /// + /// Remote file path. + /// A stream for the remote file. + public async Task GetStreamAsync(string path) { - return Task.FromResult(Stream.Null); + var response = await _client.GetRawFile(path).ConfigureAwait(false); + return response.Stream; } } } \ No newline at end of file diff --git a/WebDavSyncService.cs b/WebDavSyncService.cs index 673d654..e1f5a6b 100644 --- a/WebDavSyncService.cs +++ b/WebDavSyncService.cs @@ -6,28 +6,39 @@ namespace Jellyfin.Plugin.Webdav { using System.IO; + using System.Linq; using System.Threading; using System.Threading.Tasks; - using MediaBrowser.Controller; + using Jellyfin.Plugin.Webdav.Configuration; using MediaBrowser.Controller.Library; - using MediaBrowser.Model.Configuration; using Microsoft.Extensions.Hosting; /// - /// Hosted service that registers the WebDAV cache as a virtual folder on startup. + /// Hosted service that synchronizes WebDAV content into the local cache directory + /// and registers it as a Jellyfin virtual folder. /// public sealed class WebDavSyncService : IHostedService { + private readonly WebDavClientService client; + private readonly PluginConfiguration config; private readonly IServerApplicationPaths appPaths; private readonly ILibraryManager libraryManager; /// /// Initializes a new instance of the class. /// + /// The WebDAV client service. + /// The plugin configuration. /// Server application paths. /// Library manager. - public WebDavSyncService(IServerApplicationPaths appPaths, ILibraryManager libraryManager) + public WebDavSyncService( + WebDavClientService client, + PluginConfiguration config, + IServerApplicationPaths appPaths, + ILibraryManager libraryManager) { + this.client = client; + this.config = config; this.appPaths = appPaths; this.libraryManager = libraryManager; } @@ -35,15 +46,52 @@ namespace Jellyfin.Plugin.Webdav /// public async Task StartAsync(CancellationToken cancellationToken) { - var localRoot = Path.Combine(this.appPaths.DefaultUserViewsPath, "WebDAV"); - Directory.CreateDirectory(localRoot); + var localRoot = config.CacheDirectory; + if (!Directory.Exists(localRoot)) + { + Directory.CreateDirectory(localRoot); + } + + await SyncDirectoryAsync(config.RootPath.Trim('/'), localRoot, cancellationToken) + .ConfigureAwait(false); var options = new LibraryOptions(); - await this.libraryManager + await libraryManager .AddVirtualFolder("WebDAV", null, options, true) .ConfigureAwait(false); } + private async Task SyncDirectoryAsync(string remotePath, string localPath, CancellationToken token) + { + var resources = await client.ListAsync(remotePath).ConfigureAwait(false); + foreach (var item in resources.Where(r => !string.IsNullOrEmpty(r.DisplayName))) + { + if (token.IsCancellationRequested) + { + break; + } + + var name = item.DisplayName!; + var remoteItem = $"{remotePath}/{name}"; + var localItem = Path.Combine(localPath, name); + + if (item.IsCollection) + { + if (!Directory.Exists(localItem)) + { + Directory.CreateDirectory(localItem); + } + await SyncDirectoryAsync(remoteItem, localItem, token).ConfigureAwait(false); + } + else + { + using var stream = await client.GetStreamAsync(remoteItem).ConfigureAwait(false); + using var fs = File.Create(localItem); + await stream.CopyToAsync(fs, token).ConfigureAwait(false); + } + } + } + /// public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; }