namespace Jellyfin.Plugin.Webdav { using System.IO; using MediaBrowser.Controller; using System.Linq; using System.Threading; using System.Threading.Tasks; using Jellyfin.Plugin.Webdav.Configuration; using MediaBrowser.Controller.Library; using System.Collections.Generic; using Microsoft.Extensions.Hosting; using MediaBrowser.Model.Configuration; /// /// 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( WebDavClientService client, PluginConfiguration config, IServerApplicationPaths appPaths, ILibraryManager libraryManager) { this.client = client; this.config = config; this.appPaths = appPaths; this.libraryManager = libraryManager; } /// public async Task StartAsync(CancellationToken cancellationToken) { // Determine cache root: either configured directory or default user views path var baseCache = string.IsNullOrWhiteSpace(config.CacheDirectory) ? appPaths.DefaultUserViewsPath : config.CacheDirectory; var localRoot = Path.Combine(baseCache, "WebDAV"); if (!Directory.Exists(localRoot)) { Directory.CreateDirectory(localRoot); } await SyncDirectoryAsync(config.RootPath.Trim('/'), localRoot, cancellationToken) .ConfigureAwait(false); var options = new LibraryOptions(); await libraryManager .AddVirtualFolder("WebDAV", null, options, true) .ConfigureAwait(false); libraryManager.AddMediaPath("WebDAV", new MediaPathInfo { Path = localRoot }); } 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; } }