using dy.net.model.dto; using dy.net.model.entity; using dy.net.extension; using Microsoft.AspNetCore.DataProtection; using SqlSugar; using dy.net.utils; using MediaStorageType = dy.net.model.dto.StorageType; namespace dy.net.service { public class OpenListSettingsService { private const string SettingsId = "default"; private readonly ISqlSugarClient _db; private readonly IDataProtector _protector; private readonly WebDavSettingsService _legacySettings; public OpenListSettingsService( ISqlSugarClient db, IDataProtectionProvider provider, WebDavSettingsService legacySettings) { _db = db; _protector = provider.CreateProtector("dysync.openlist.password.v1"); _legacySettings = legacySettings; } public async Task GetAsync() { var existing = await _db.Queryable().InSingleAsync(SettingsId); if (existing != null) { Normalize(existing); return existing; } var legacy = await _legacySettings.GetAsync(); var password = await _legacySettings.GetPasswordAsync(legacy); var suggested = new OpenListSettings { Id = SettingsId, Endpoint = NormalizeLegacyEndpoint(legacy.Endpoint), BasePath = string.IsNullOrWhiteSpace(legacy.BasePath) ? "/dysync" : legacy.BasePath, LocalStagingPath = GetDefaultLocalStagingPath(), SourcePath = "/dysync-staging", UserName = legacy.UserName, ProtectedPassword = string.IsNullOrWhiteSpace(password) ? null : _protector.Protect(password) }; Normalize(suggested); return suggested; } public async Task GetPasswordAsync(OpenListSettings settings = null) { settings ??= await GetAsync(); if (string.IsNullOrWhiteSpace(settings.ProtectedPassword)) return string.Empty; try { return _protector.Unprotect(settings.ProtectedPassword); } catch (Exception ex) { Serilog.Log.Error(ex, "OpenList 密码解密失败,请重新输入密码"); return string.Empty; } } public async Task BuildCandidateAsync(OpenListTestRequest request) { request ??= new OpenListTestRequest(); var existing = await GetAsync(); var password = string.IsNullOrWhiteSpace(request.Password) ? await GetPasswordAsync(existing) : request.Password; var candidate = new OpenListSettings { Id = SettingsId, Endpoint = string.IsNullOrWhiteSpace(request.Endpoint) ? existing.Endpoint : request.Endpoint.Trim(), BasePath = string.IsNullOrWhiteSpace(request.BasePath) ? existing.BasePath : request.BasePath.Trim(), LocalStagingPath = string.IsNullOrWhiteSpace(request.LocalStagingPath) ? existing.LocalStagingPath : request.LocalStagingPath.Trim(), SourcePath = string.IsNullOrWhiteSpace(request.SourcePath) ? existing.SourcePath : request.SourcePath.Trim(), UserName = string.IsNullOrWhiteSpace(request.UserName) ? existing.UserName : request.UserName.Trim(), ProtectedPassword = string.IsNullOrWhiteSpace(password) ? null : _protector.Protect(password) }; Normalize(candidate); return candidate; } public async Task SaveAsync(OpenListSettings settings, bool tested, string testMessage) { settings.Id = SettingsId; Normalize(settings); settings.LastTestedAt = tested ? DateTime.Now : null; settings.LastTestMessage = testMessage; await _db.Storageable(settings).ExecuteCommandAsync(); } public async Task ToDtoAsync(MediaStorageType storageType) { var settings = await GetAsync(); return new OpenListSettingsDto { StorageType = storageType, Endpoint = settings.Endpoint, BasePath = settings.BasePath, LocalStagingPath = settings.LocalStagingPath, SourcePath = settings.SourcePath, UserName = settings.UserName, HasPassword = !string.IsNullOrWhiteSpace(settings.ProtectedPassword), LastTestedAt = settings.LastTestedAt, LastTestMessage = settings.LastTestMessage }; } public async Task HasSavedSettingsAsync() => await _db.Queryable().Where(x => x.Id == SettingsId).AnyAsync(); public static string GetDefaultLocalStagingPath() { var dataRoot = !string.IsNullOrWhiteSpace(ServiceExtension.FnDataFolder) ? Path.GetDirectoryName(Path.GetFullPath(ServiceExtension.FnDataFolder)) : null; return Path.Combine(dataRoot ?? Environment.CurrentDirectory, "openlist-staging"); } private static string NormalizeLegacyEndpoint(string endpoint) { if (string.IsNullOrWhiteSpace(endpoint)) return endpoint; var value = endpoint.Trim().TrimEnd('/'); var dav = value.LastIndexOf("/dav", StringComparison.OrdinalIgnoreCase); return dav >= 0 && dav + 4 == value.Length ? value[..dav] : value; } private static void Normalize(OpenListSettings settings) { settings.Endpoint = NormalizeLegacyEndpoint(settings.Endpoint); if (string.IsNullOrWhiteSpace(settings.BasePath)) settings.BasePath = string.IsNullOrWhiteSpace(settings.DestinationPath) ? "/dysync" : settings.DestinationPath; if (string.IsNullOrWhiteSpace(settings.LocalStagingPath)) settings.LocalStagingPath = GetDefaultLocalStagingPath(); if (string.IsNullOrWhiteSpace(settings.SourcePath)) settings.SourcePath = "/dysync-staging"; settings.DestinationPath = settings.BasePath; } public async Task InitializeCookiePathsAsync() { var cookies = await _db.Queryable().ToListAsync(); foreach (var cookie in cookies) { ApplyDefaultCookiePaths(cookie); } if (cookies.Count > 0) await _db.Updateable(cookies).ExecuteCommandAsync(); } public void ApplyDefaultCookiePaths(DouyinCookie cookie) { if (cookie == null) return; var root = DouyinFileNameHelper.SanitizeLinuxFileName(cookie.UserName, cookie.Id, true); if (string.IsNullOrWhiteSpace(cookie.WebDavCollectPath)) cookie.WebDavCollectPath = $"/{root}/collect"; if (string.IsNullOrWhiteSpace(cookie.WebDavFavoritePath)) cookie.WebDavFavoritePath = $"/{root}/favorite"; if (string.IsNullOrWhiteSpace(cookie.WebDavFollowPath)) cookie.WebDavFollowPath = $"/{root}/follow"; if (string.IsNullOrWhiteSpace(cookie.WebDavMixPath)) cookie.WebDavMixPath = $"/{root}/mix"; if (string.IsNullOrWhiteSpace(cookie.WebDavSeriesPath)) cookie.WebDavSeriesPath = $"/{root}/series"; } } }