fix: ignore system, hidden files and unsupported format files

This commit is contained in:
Zhe Fang
2025-12-28 21:03:56 -05:00
parent b0a777db8d
commit 884026594b
7 changed files with 90 additions and 55 deletions

View File

@@ -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>();

View File

@@ -10,6 +10,10 @@ namespace BetterLyrics.WinUI3.Services.FileSystemService
{
public interface IFileSystemService
{
/// <summary>
/// 初始化(连接)数据库
/// </summary>
/// <returns></returns>
Task InitializeAsync();
/// <summary>

View File

@@ -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

View File

@@ -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 允许其他程序同时读取

View File

@@ -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,

View File

@@ -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

View File

@@ -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">