mirror of
https://github.com/jayfunc/BetterLyrics.git
synced 2026-01-12 19:24:55 +08:00
fix: ignore system, hidden files and unsupported format files
This commit is contained in:
@@ -46,10 +46,6 @@ namespace BetterLyrics.WinUI3.Services.FileSystemService
|
||||
_db = new SQLiteAsyncConnection(PathHelper.FilesCachePath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化(连接)数据库
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
if (_isInitialized) return;
|
||||
@@ -136,14 +132,15 @@ namespace BetterLyrics.WinUI3.Services.FileSystemService
|
||||
await _db.RunInTransactionAsync(conn =>
|
||||
{
|
||||
var dbItems = conn.Table<FileCacheEntity>()
|
||||
.Where(x => x.MediaFolderId == configId && x.ParentUri == targetParentUri)
|
||||
.ToList();
|
||||
.Where(x => x.MediaFolderId == configId && x.ParentUri == targetParentUri)
|
||||
.ToList();
|
||||
|
||||
var dbMap = dbItems.ToDictionary(x => x.Uri, x => x);
|
||||
|
||||
var remoteMap = remoteItems.GroupBy(x => x.Uri)
|
||||
.Select(g => g.First())
|
||||
.ToDictionary(x => x.Uri, x => x);
|
||||
var remoteMap = remoteItems
|
||||
.GroupBy(x => x.Uri)
|
||||
.Select(g => g.First())
|
||||
.ToDictionary(x => x.Uri, x => x);
|
||||
|
||||
var toInsert = new List<FileCacheEntity>();
|
||||
var toUpdate = new List<FileCacheEntity>();
|
||||
|
||||
@@ -10,6 +10,10 @@ namespace BetterLyrics.WinUI3.Services.FileSystemService
|
||||
{
|
||||
public interface IFileSystemService
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始化(连接)数据库
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task InitializeAsync();
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using FluentFTP;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -58,34 +59,28 @@ namespace BetterLyrics.WinUI3.Services.FileSystemService.Providers
|
||||
{
|
||||
var result = new List<FileCacheEntity>();
|
||||
|
||||
// 1. 确定 FTP 服务器上的绝对路径
|
||||
string targetServerPath;
|
||||
Uri parentUri;
|
||||
|
||||
if (parentFolder == null)
|
||||
{
|
||||
// 根目录:从配置中提取
|
||||
var rootUri = _config.GetStandardUri();
|
||||
targetServerPath = rootUri.AbsolutePath;
|
||||
parentUri = rootUri;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 子目录:从实体中提取
|
||||
targetServerPath = GetServerPathFromUri(parentFolder.Uri);
|
||||
parentUri = new Uri(parentFolder.Uri);
|
||||
}
|
||||
|
||||
// 2. 路径清洗:解码 URL (比如 %20 -> 空格),并统一分隔符
|
||||
targetServerPath = WebUtility.UrlDecode(targetServerPath).Replace("\\", "/");
|
||||
if (string.IsNullOrEmpty(targetServerPath)) targetServerPath = "/";
|
||||
|
||||
try
|
||||
{
|
||||
// 3. 获取列表 (FluentFTP 自动处理列表解析)
|
||||
var items = await _client.GetListing(targetServerPath, FtpListOption.Auto);
|
||||
|
||||
// 准备 Base URI Scheme (ftp://192.168.1.5:21) 用于拼接子项
|
||||
string baseUriSchema = $"{parentUri.Scheme}://{parentUri.Host}";
|
||||
if (parentUri.Port > 0) baseUriSchema += $":{parentUri.Port}";
|
||||
|
||||
@@ -97,10 +92,13 @@ namespace BetterLyrics.WinUI3.Services.FileSystemService.Providers
|
||||
// 只处理文件和文件夹
|
||||
if (item.Type != FtpObjectType.File && item.Type != FtpObjectType.Directory) continue;
|
||||
|
||||
// 4. 构建标准 URI
|
||||
// FluentFTP 的 item.FullName 通常是 "/Music/Song.mp3"
|
||||
// 我们用 UriBuilder 把它封装成 "ftp://192.168.1.5:21/Music/Song.mp3"
|
||||
// UriBuilder 会自动处理路径中的特殊字符编码
|
||||
// 只处理特定后缀文件
|
||||
if (item.Type == FtpObjectType.File)
|
||||
{
|
||||
string extension = Path.GetExtension(item.Name);
|
||||
if (string.IsNullOrEmpty(extension) || !FileHelper.AllSupportedExtensions.Contains(extension)) continue;
|
||||
}
|
||||
|
||||
var builder = new UriBuilder(baseUriSchema)
|
||||
{
|
||||
Path = item.FullName
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@@ -33,13 +34,11 @@ namespace BetterLyrics.WinUI3.Services.FileSystemService.Providers
|
||||
{
|
||||
if (parentFolder == null)
|
||||
{
|
||||
// 根目录
|
||||
targetPath = _rootLocalPath;
|
||||
parentUriString = _config.GetStandardUri().AbsoluteUri;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 子目录:从标准 URI (file:///...) 还原为本地路径 (C:\...)
|
||||
var uri = new Uri(parentFolder.Uri);
|
||||
targetPath = uri.LocalPath;
|
||||
parentUriString = parentFolder.Uri;
|
||||
@@ -49,16 +48,23 @@ namespace BetterLyrics.WinUI3.Services.FileSystemService.Providers
|
||||
|
||||
var dirInfo = new DirectoryInfo(targetPath);
|
||||
|
||||
foreach (var item in dirInfo.GetFileSystemInfos())
|
||||
foreach (var item in dirInfo.EnumerateFileSystemInfos())
|
||||
{
|
||||
// 生成标准 URI 作为唯一 ID
|
||||
// new Uri("C:\Path\File") 会自动生成 file:///C:/Path/File
|
||||
var itemUri = new Uri(item.FullName).AbsoluteUri;
|
||||
// 跳过系统/隐藏文件
|
||||
if ((item.Attributes & FileAttributes.Hidden) != 0 || (item.Attributes & FileAttributes.System) != 0) continue;
|
||||
|
||||
bool isDir = (item.Attributes & FileAttributes.Directory) == FileAttributes.Directory;
|
||||
|
||||
if (!isDir)
|
||||
{
|
||||
// 过滤后缀名
|
||||
if (string.IsNullOrEmpty(item.Extension) || !FileHelper.AllSupportedExtensions.Contains(item.Extension)) continue;
|
||||
}
|
||||
|
||||
var itemUri = new Uri(item.FullName).AbsoluteUri;
|
||||
|
||||
long size = 0;
|
||||
|
||||
// DirectoryInfo 没有 Length 属性,只有 FileInfo 有
|
||||
if (!isDir && item is FileInfo fi)
|
||||
{
|
||||
size = fi.Length;
|
||||
@@ -70,7 +76,7 @@ namespace BetterLyrics.WinUI3.Services.FileSystemService.Providers
|
||||
|
||||
ParentUri = parentUriString, // 记录父级 URI
|
||||
|
||||
Uri = itemUri, // 标准化 URI (file:///...)
|
||||
Uri = itemUri,
|
||||
|
||||
FileName = item.Name,
|
||||
IsDirectory = isDir,
|
||||
@@ -92,7 +98,6 @@ namespace BetterLyrics.WinUI3.Services.FileSystemService.Providers
|
||||
{
|
||||
if (entity == null) return null;
|
||||
|
||||
// 将标准 URI (file:///C:/...) 还原为本地路径 (C:\...)
|
||||
string localPath = new Uri(entity.Uri).LocalPath;
|
||||
|
||||
// 使用 FileShare.Read 允许其他程序同时读取
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using SMBLibrary;
|
||||
using SMBLibrary.Client;
|
||||
using System;
|
||||
@@ -88,7 +89,6 @@ namespace BetterLyrics.WinUI3.Services.FileSystemService.Providers
|
||||
|
||||
if (statusRet != NTStatus.STATUS_SUCCESS) return result;
|
||||
|
||||
// 确保 parentUriString 总是以 / 结尾,方便后续拼接
|
||||
string parentUriString = parentFolder?.Uri ?? _config.GetStandardUri().AbsoluteUri;
|
||||
|
||||
List<QueryDirectoryFileInformation> fileInfo;
|
||||
@@ -97,7 +97,7 @@ namespace BetterLyrics.WinUI3.Services.FileSystemService.Providers
|
||||
{
|
||||
statusRet = _fileStore.QueryDirectory(out fileInfo, handle, "*", FileInformationClass.FileDirectoryInformation);
|
||||
|
||||
// 【安全检查】如果查询失败或者没有更多文件,fileInfo 可能是 null,直接跳出
|
||||
// 如果查询失败或者没有更多文件,fileInfo 可能是 null,直接跳出
|
||||
if (statusRet != NTStatus.STATUS_SUCCESS && statusRet != NTStatus.STATUS_NO_MORE_FILES)
|
||||
{
|
||||
break;
|
||||
@@ -110,35 +110,32 @@ namespace BetterLyrics.WinUI3.Services.FileSystemService.Providers
|
||||
{
|
||||
if (item.FileName == "." || item.FileName == "..") continue;
|
||||
|
||||
// ==================================================
|
||||
// ★ 修正后的 URI 构建逻辑
|
||||
// ==================================================
|
||||
// 过滤隐藏文件和系统文件
|
||||
if ((item.FileAttributes & SMBLibrary.FileAttributes.Hidden) == SMBLibrary.FileAttributes.Hidden ||
|
||||
(item.FileAttributes & SMBLibrary.FileAttributes.System) == SMBLibrary.FileAttributes.System)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bool isDir = (item.FileAttributes & SMBLibrary.FileAttributes.Directory) == SMBLibrary.FileAttributes.Directory;
|
||||
|
||||
// 后缀名过滤
|
||||
if (!isDir)
|
||||
{
|
||||
string extension = Path.GetExtension(item.FileName);
|
||||
if (string.IsNullOrEmpty(extension) || !FileHelper.AllSupportedExtensions.Contains(extension)) continue;
|
||||
}
|
||||
|
||||
// 方法 A (推荐): 使用 Uri 构造函数自动合并
|
||||
// 1. 确保 Base Uri 以 / 结尾 (否则 "folder" + "file" 会变成 "file" 替换掉 "folder")
|
||||
if (!parentUriString.EndsWith("/")) parentUriString += "/";
|
||||
var baseUri = new Uri(parentUriString);
|
||||
|
||||
// 2. 直接利用 Uri 的构造函数处理相对路径
|
||||
// new Uri(baseUri, "filename") 会自动处理编码和斜杠
|
||||
// 注意:如果 item.FileName 包含特殊字符,Uri 类会自动帮我们编码
|
||||
var newUri = new Uri(baseUri, item.FileName);
|
||||
|
||||
// 如果你还是想用 UriBuilder (手动控制更强),请用下面这行代替上面:
|
||||
/*
|
||||
var builder = new UriBuilder(baseUri);
|
||||
// 关键:先 Unescape 解码,变回原始字符串,再拼接,最后赋值给 builder 让它重新编码
|
||||
string cleanBasePath = Uri.UnescapeDataString(baseUri.AbsolutePath);
|
||||
builder.Path = Path.Combine(cleanBasePath, item.FileName).Replace("\\", "/");
|
||||
var newUri = builder.Uri;
|
||||
*/
|
||||
|
||||
result.Add(new FileCacheEntity
|
||||
{
|
||||
MediaFolderId = _config.Id,
|
||||
ParentUri = parentFolder?.Uri ?? _config.GetStandardUri().AbsoluteUri, // 保持原始父级 URI (不带末尾斜杠的)
|
||||
ParentUri = parentFolder?.Uri ?? _config.GetStandardUri().AbsoluteUri,
|
||||
|
||||
Uri = newUri.AbsoluteUri, // 使用修正后的 URI
|
||||
Uri = newUri.AbsoluteUri,
|
||||
|
||||
FileName = item.FileName,
|
||||
IsDirectory = (item.FileAttributes & SMBLibrary.FileAttributes.Directory) == SMBLibrary.FileAttributes.Directory,
|
||||
|
||||
@@ -126,6 +126,40 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
else
|
||||
{
|
||||
var newUriString = tempFolder.GetStandardUri().AbsoluteUri.TrimEnd('/') + "/";
|
||||
|
||||
foreach (var existingFolder in AppSettings.LocalMediaFolders)
|
||||
{
|
||||
// 只比对同类型的远程源 (可选,或者是比对所有源)
|
||||
// 这里建议比对所有,防止逻辑上的冲突
|
||||
|
||||
var existingUriString = existingFolder.GetStandardUri().AbsoluteUri.TrimEnd('/') + "/";
|
||||
|
||||
// 是否完全重复 (忽略大小写)
|
||||
if (newUriString.Equals(existingUriString, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
configControl.ShowError(_localizationService.GetLocalizedString("SettingsPagePathExistedInfo"));
|
||||
deferral.Complete();
|
||||
return;
|
||||
}
|
||||
|
||||
// 新路径是否是现有路径的“子文件夹”
|
||||
if (newUriString.StartsWith(existingUriString, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
configControl.ShowError(_localizationService.GetLocalizedString("SettingsPagePathBeIncludedInfo"));
|
||||
deferral.Complete();
|
||||
return;
|
||||
}
|
||||
|
||||
// 新路径是否是现有路径的“父文件夹”
|
||||
if (existingUriString.StartsWith(newUriString, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
configControl.ShowError(_localizationService.GetLocalizedString("SettingsPagePathIncludingOthersInfo"));
|
||||
deferral.Complete();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool isConnected = await Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
</Page.Resources>
|
||||
|
||||
<Grid>
|
||||
<Grid Padding="12,8,12,64" ColumnSpacing="12">
|
||||
<Grid Padding="12,16,12,64" ColumnSpacing="12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="3*" />
|
||||
@@ -49,7 +49,7 @@
|
||||
<TextBlock
|
||||
x:Uid="MusicGalleryPagePlaylist"
|
||||
Grid.Row="0"
|
||||
Margin="1,4,0,6"
|
||||
Margin="1,0,0,6"
|
||||
Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<Grid Grid.Row="1">
|
||||
|
||||
Reference in New Issue
Block a user