mirror of
https://github.com/jayfunc/BetterLyrics.git
synced 2026-01-12 19:24:55 +08:00
chores: adjust layout and fix bugs
This commit is contained in:
@@ -97,7 +97,7 @@
|
||||
<Setter Property="VerticalAlignment" Value="Top" />
|
||||
<Setter Property="CornerRadius" Value="4" />
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
<Setter Property="Padding" Value="14,6,14,9" />
|
||||
<Setter Property="Padding" Value="16,9,16,9" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
</Style>
|
||||
<Style x:Key="GhostButtonStyle" TargetType="Button">
|
||||
|
||||
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Folder.png
Normal file
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Folder.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
@@ -170,6 +170,9 @@
|
||||
<Content Update="Assets\EmptyState.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\Folder.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\foobar2000.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
||||
@@ -50,21 +50,24 @@
|
||||
SelectionMode="None">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="models:MediaFolder">
|
||||
<dev:SettingsExpander Description="{x:Bind ConnectionSummary, Mode=OneWay}" IsExpanded="True">
|
||||
<dev:SettingsExpander IsExpanded="True">
|
||||
|
||||
<dev:SettingsExpander.HeaderIcon>
|
||||
<FontIcon FontFamily="{StaticResource IconFontFamily}" Glyph="{x:Bind SourceType, Converter={StaticResource FileSourceTypeToIconConverter}, Mode=OneWay}" />
|
||||
</dev:SettingsExpander.HeaderIcon>
|
||||
<dev:SettingsExpander.Header>
|
||||
<HyperlinkButton
|
||||
Padding="0"
|
||||
Click="LocalFolderHyperlinkButton_Click"
|
||||
Content="{x:Bind ConnectionSummary, Mode=OneWay}" />
|
||||
<TextBlock IsTextSelectionEnabled="True" Text="{x:Bind Name, Mode=OneWay}" />
|
||||
</dev:SettingsExpander.Header>
|
||||
<dev:SettingsExpander.Description>
|
||||
<TextBlock IsTextSelectionEnabled="True" Text="{x:Bind ConnectionSummary, Mode=OneWay}" />
|
||||
</dev:SettingsExpander.Description>
|
||||
|
||||
<ToggleSwitch IsOn="{x:Bind IsEnabled, Mode=TwoWay}" />
|
||||
|
||||
<dev:SettingsExpander.Items>
|
||||
<dev:SettingsCard x:Uid="MediaSettingsControlNameSetting">
|
||||
<TextBox VerticalAlignment="Center" Text="{x:Bind Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
</dev:SettingsCard>
|
||||
<dev:SettingsCard x:Uid="MediaSettingsControlLastSyncTime" Description="{x:Bind LastSyncTime.ToString(), Mode=OneWay, TargetNullValue=N/A}">
|
||||
<Button
|
||||
x:Uid="MediaSettingsControlSyncNow"
|
||||
@@ -128,14 +131,17 @@
|
||||
<MenuFlyout>
|
||||
<MenuFlyoutItem
|
||||
x:Uid="SettingsPageLocalFolder"
|
||||
Command="{x:Bind ViewModel.SelectAndAddFolderCommand}"
|
||||
CommandParameter="{Binding ElementName=RootGrid}"
|
||||
Icon="Folder" />
|
||||
Command="{x:Bind ViewModel.AddMediaSourceCommand}"
|
||||
CommandParameter="Local">
|
||||
<MenuFlyoutItem.Icon>
|
||||
<FontIcon FontFamily="{StaticResource IconFontFamily}" Glyph="" />
|
||||
</MenuFlyoutItem.Icon>
|
||||
</MenuFlyoutItem>
|
||||
|
||||
<MenuFlyoutSeparator />
|
||||
|
||||
<MenuFlyoutItem
|
||||
Command="{x:Bind ViewModel.AddRemoteSourceCommand}"
|
||||
Command="{x:Bind ViewModel.AddMediaSourceCommand}"
|
||||
CommandParameter="SMB"
|
||||
Text="SMB">
|
||||
<MenuFlyoutItem.Icon>
|
||||
@@ -144,7 +150,7 @@
|
||||
</MenuFlyoutItem>
|
||||
|
||||
<MenuFlyoutItem
|
||||
Command="{x:Bind ViewModel.AddRemoteSourceCommand}"
|
||||
Command="{x:Bind ViewModel.AddMediaSourceCommand}"
|
||||
CommandParameter="FTP"
|
||||
Text="FTP">
|
||||
<MenuFlyoutItem.Icon>
|
||||
@@ -153,7 +159,7 @@
|
||||
</MenuFlyoutItem>
|
||||
|
||||
<MenuFlyoutItem
|
||||
Command="{x:Bind ViewModel.AddRemoteSourceCommand}"
|
||||
Command="{x:Bind ViewModel.AddMediaSourceCommand}"
|
||||
CommandParameter="WebDAV"
|
||||
Text="WebDAV">
|
||||
<MenuFlyoutItem.Icon>
|
||||
|
||||
@@ -27,15 +27,6 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
ViewModel.RemoveFolder(folder);
|
||||
}
|
||||
|
||||
private async void LocalFolderHyperlinkButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var folder = (MediaFolder)((FrameworkElement)sender).DataContext;
|
||||
if (Uri.TryCreate(folder.UriString, UriKind.Absolute, out var uri))
|
||||
{
|
||||
await Launcher.LaunchUriAsync(uri);
|
||||
}
|
||||
}
|
||||
|
||||
private void SyncNowButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var folder = (MediaFolder)((FrameworkElement)sender).DataContext;
|
||||
|
||||
@@ -9,56 +9,85 @@
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid>
|
||||
<StackPanel Width="400" Spacing="16">
|
||||
<ProgressBar
|
||||
x:Name="ProgressBar"
|
||||
IsIndeterminate="True"
|
||||
Visibility="Collapsed" />
|
||||
<InfoBar
|
||||
x:Name="ErrorInfoBar"
|
||||
IsClosable="True"
|
||||
IsOpen="False"
|
||||
Severity="Error" />
|
||||
<ScrollViewer>
|
||||
<StackPanel Width="400" Spacing="16">
|
||||
<ProgressBar
|
||||
x:Name="ProgressBar"
|
||||
IsIndeterminate="True"
|
||||
Visibility="Collapsed" />
|
||||
<InfoBar
|
||||
x:Name="ErrorInfoBar"
|
||||
IsClosable="True"
|
||||
IsOpen="False"
|
||||
Severity="Error" />
|
||||
|
||||
<Grid ColumnDefinitions="*, Auto" ColumnSpacing="12">
|
||||
<TextBox
|
||||
x:Name="HostBox"
|
||||
x:Uid="RemoteServerConfigControlServerAddress"
|
||||
Grid.Column="0"
|
||||
InputScope="Url"
|
||||
PlaceholderText="192.168.1.x"
|
||||
x:Name="NameBox"
|
||||
x:Uid="RemoteServerConfigControlName"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<NumberBox
|
||||
x:Name="PortBox"
|
||||
x:Uid="RemoteServerConfigControlPort"
|
||||
Grid.Column="1"
|
||||
MinWidth="100"
|
||||
LargeChange="10"
|
||||
SmallChange="1"
|
||||
SpinButtonPlacementMode="Inline"
|
||||
ToolTipService.ToolTip="80"
|
||||
Value="80" />
|
||||
</Grid>
|
||||
<StackPanel x:Name="RemoteFieldsPanel" Spacing="16">
|
||||
<Grid ColumnDefinitions="*, Auto" ColumnSpacing="12">
|
||||
<TextBox
|
||||
x:Name="HostBox"
|
||||
x:Uid="RemoteServerConfigControlServerAddress"
|
||||
Grid.Column="0"
|
||||
InputScope="Url"
|
||||
PlaceholderText="192.168.1.x"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<TextBox
|
||||
x:Name="PathBox"
|
||||
x:Uid="RemoteServerConfigControlPath"
|
||||
TextWrapping="Wrap" />
|
||||
<NumberBox
|
||||
x:Name="PortBox"
|
||||
x:Uid="RemoteServerConfigControlPort"
|
||||
Grid.Column="1"
|
||||
MinWidth="100"
|
||||
LargeChange="10"
|
||||
SmallChange="1"
|
||||
SpinButtonPlacementMode="Inline"
|
||||
ToolTipService.ToolTip="80"
|
||||
Value="80" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
<Grid ColumnDefinitions="*, *" ColumnSpacing="12">
|
||||
<TextBox
|
||||
x:Name="UserBox"
|
||||
x:Uid="RemoteServerConfigControlUsername"
|
||||
Grid.Column="0"
|
||||
TextWrapping="Wrap" />
|
||||
<Grid ColumnDefinitions="*, Auto" ColumnSpacing="8">
|
||||
<TextBox
|
||||
x:Name="PathBox"
|
||||
x:Uid="RemoteServerConfigControlPath"
|
||||
Grid.Column="0"
|
||||
TextChanged="PathBox_TextChanged"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<PasswordBox
|
||||
x:Name="PwdBox"
|
||||
x:Uid="RemoteServerConfigControlPassword"
|
||||
Grid.Column="1"
|
||||
PasswordRevealMode="Peek" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
<Button
|
||||
x:Name="BrowseButton"
|
||||
x:Uid="RemoteServerConfigControlBrowse"
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Bottom"
|
||||
Click="BrowseButton_Click"
|
||||
Visibility="Collapsed" />
|
||||
</Grid>
|
||||
|
||||
<InfoBar
|
||||
x:Name="PathWarningBar"
|
||||
IsClosable="False"
|
||||
IsOpen="False"
|
||||
Severity="Warning" />
|
||||
|
||||
<StackPanel x:Name="AuthFieldsPanel" Spacing="16">
|
||||
<Grid ColumnDefinitions="*, *" ColumnSpacing="12">
|
||||
<TextBox
|
||||
x:Name="UserBox"
|
||||
x:Uid="RemoteServerConfigControlUsername"
|
||||
Grid.Column="0"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<PasswordBox
|
||||
x:Name="PwdBox"
|
||||
x:Uid="RemoteServerConfigControlPassword"
|
||||
Grid.Column="1"
|
||||
PasswordRevealMode="Peek" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Services.LocalizationService;
|
||||
using BetterLyrics.WinUI3.Views;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
@@ -19,24 +21,41 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
_protocolType = protocolType;
|
||||
|
||||
SetupDefaults();
|
||||
CheckPathForWarning();
|
||||
}
|
||||
|
||||
private void SetupDefaults()
|
||||
{
|
||||
switch (_protocolType.ToUpper())
|
||||
if (_protocolType.Equals("Local", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
case "SMB":
|
||||
PortBox.Value = 445; // SMB Ĭ<>϶˿<CFB6>
|
||||
PathBox.PlaceholderText = "SharedMusic";
|
||||
break;
|
||||
case "FTP":
|
||||
PortBox.Value = 21; // FTP Ĭ<>϶˿<CFB6>
|
||||
PathBox.PlaceholderText = "/pub/music";
|
||||
break;
|
||||
case "WEBDAV":
|
||||
PortBox.Value = 80; // WebDAV Ĭ<>϶˿<CFB6>
|
||||
PathBox.PlaceholderText = "/dav/music";
|
||||
break;
|
||||
RemoteFieldsPanel.Visibility = Visibility.Collapsed;
|
||||
AuthFieldsPanel.Visibility = Visibility.Collapsed;
|
||||
|
||||
BrowseButton.Visibility = Visibility.Visible;
|
||||
|
||||
PathBox.PlaceholderText = @"D:\Music";
|
||||
}
|
||||
else
|
||||
{
|
||||
BrowseButton.Visibility = Visibility.Collapsed;
|
||||
RemoteFieldsPanel.Visibility = Visibility.Visible;
|
||||
AuthFieldsPanel.Visibility = Visibility.Visible;
|
||||
|
||||
switch (_protocolType.ToUpper())
|
||||
{
|
||||
case "SMB":
|
||||
PortBox.Value = 445;
|
||||
PathBox.PlaceholderText = "SharedMusic";
|
||||
break;
|
||||
case "FTP":
|
||||
PortBox.Value = 21;
|
||||
PathBox.PlaceholderText = "/pub/music";
|
||||
break;
|
||||
case "WEBDAV":
|
||||
PortBox.Value = 80;
|
||||
PathBox.PlaceholderText = "/dav/music";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,17 +79,46 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
|
||||
public MediaFolder GetConfig()
|
||||
{
|
||||
string finalName = HostBox.Text.Trim();
|
||||
|
||||
if (_protocolType.Equals("Local", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(PathBox.Text))
|
||||
throw new ArgumentException(_localizationService.GetLocalizedString("RemoteServerConfigControlPathRequired"));
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(NameBox.Text))
|
||||
finalName = NameBox.Text.Trim();
|
||||
else
|
||||
finalName = PathBox.Text.TrimEnd(System.IO.Path.DirectorySeparatorChar);
|
||||
|
||||
return new MediaFolder
|
||||
{
|
||||
Name = finalName,
|
||||
SourceType = FileSourceType.Local,
|
||||
UriScheme = "file",
|
||||
UriPath = PathBox.Text.Trim(),
|
||||
};
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(HostBox.Text))
|
||||
throw new ArgumentException(_localizationService.GetLocalizedString("RemoteServerConfigControlServerAddressRequired"));
|
||||
|
||||
string name = $"{_protocolType} - {HostBox.Text}";
|
||||
if (!string.IsNullOrWhiteSpace(NameBox.Text))
|
||||
{
|
||||
finalName = NameBox.Text.Trim();
|
||||
}
|
||||
else
|
||||
{
|
||||
finalName = $"{_protocolType} - {HostBox.Text}";
|
||||
}
|
||||
|
||||
Enum.TryParse(_protocolType, true, out FileSourceType sourceType);
|
||||
|
||||
string scheme = GetScheme();
|
||||
|
||||
var folder = new MediaFolder
|
||||
{
|
||||
Name = name,
|
||||
Name = finalName,
|
||||
SourceType = sourceType,
|
||||
|
||||
UriScheme = scheme,
|
||||
@@ -86,10 +134,10 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
return folder;
|
||||
}
|
||||
|
||||
public void ShowError(string message)
|
||||
public void ShowError(string? message)
|
||||
{
|
||||
ErrorInfoBar.Message = message;
|
||||
ErrorInfoBar.IsOpen = true;
|
||||
ErrorInfoBar.IsOpen = !string.IsNullOrWhiteSpace(message);
|
||||
}
|
||||
|
||||
public void SetProgressBarVisibility(Visibility visibility)
|
||||
@@ -97,5 +145,53 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
ProgressBar.Visibility = visibility;
|
||||
}
|
||||
|
||||
private void PathBox_TextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
CheckPathForWarning();
|
||||
}
|
||||
|
||||
private void CheckPathForWarning()
|
||||
{
|
||||
string? path = PathBox.Text?.Trim();
|
||||
|
||||
bool isSymbolRoot = string.IsNullOrEmpty(path) ||
|
||||
path == "/" ||
|
||||
path == "\\";
|
||||
|
||||
bool isDriveRoot = false;
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
var normalized = path.TrimEnd('\\', '/');
|
||||
isDriveRoot = normalized.EndsWith(":") && normalized.Length == 2;
|
||||
}
|
||||
|
||||
bool isRoot = isSymbolRoot || isDriveRoot;
|
||||
|
||||
if (isRoot)
|
||||
{
|
||||
PathWarningBar.Message = _localizationService.GetLocalizedString("FileSystemServiceRootDirectoryWarning");
|
||||
PathWarningBar.IsOpen = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
PathWarningBar.IsOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async void BrowseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var folder = await PickerHelper.PickSingleFolderAsync<SettingsWindow>();
|
||||
if (folder != null)
|
||||
{
|
||||
PathBox.Text = folder.Path;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ShowError(ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,13 +18,7 @@
|
||||
<TextBlock x:Uid="AppSettingsControlGeneral" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageConfigName" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<StackPanel
|
||||
Margin="0,6,0,0"
|
||||
Orientation="Horizontal"
|
||||
Spacing="6">
|
||||
<TextBox Text="{x:Bind LyricsWindowStatus.Name, Mode=TwoWay}" TextWrapping="Wrap" />
|
||||
<Button Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, FontSize=12, Glyph=}" Style="{StaticResource GhostButtonStyle}" />
|
||||
</StackPanel>
|
||||
<TextBox Text="{x:Bind LyricsWindowStatus.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" TextWrapping="Wrap" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsExpander
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Collections.ObjectModel;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
|
||||
public static class FolderTreeBuilder
|
||||
{
|
||||
public static ObservableCollection<FolderNode> Build(List<ExtendedTrack> tracks, List<MediaFolder> folderConfigs)
|
||||
{
|
||||
var rootNodes = new ObservableCollection<FolderNode>();
|
||||
|
||||
// 按 MediaFolderId 分组
|
||||
var folderGroups = tracks.GroupBy(t => t.MediaFolderId);
|
||||
|
||||
foreach (var group in folderGroups)
|
||||
{
|
||||
var config = folderConfigs.FirstOrDefault(f => f.Id == group.Key);
|
||||
if (config == null) continue;
|
||||
|
||||
string baseUri = config.GetStandardUri().AbsoluteUri.TrimEnd('/');
|
||||
|
||||
var rootNode = new FolderNode
|
||||
{
|
||||
SourceType = config.SourceType,
|
||||
FolderName = config.Name ?? config.ConnectionSummary, // 显示用户自定义的名字
|
||||
MediaFolderId = group.Key,
|
||||
FolderPath = baseUri,
|
||||
IsExpanded = true
|
||||
};
|
||||
|
||||
foreach (var track in group)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!track.Uri.StartsWith(baseUri)) continue; // 防御性编程
|
||||
|
||||
string relativePart = track.Uri.Substring(baseUri.Length);
|
||||
|
||||
var segments = relativePart
|
||||
.Split('/', StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(s => System.Net.WebUtility.UrlDecode(s))
|
||||
.ToArray();
|
||||
|
||||
if (segments.Length > 1) // 长度大于1说明在子文件夹里
|
||||
{
|
||||
var folderSegments = segments.Take(segments.Length - 1).ToArray();
|
||||
CreateFolderStructure(rootNode, folderSegments, baseUri);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
rootNodes.Add(rootNode);
|
||||
}
|
||||
|
||||
return rootNodes;
|
||||
}
|
||||
|
||||
private static void CreateFolderStructure(FolderNode parent, string[] segments, string rootBaseUri)
|
||||
{
|
||||
var current = parent;
|
||||
string currentFullPath = parent.FolderPath;
|
||||
|
||||
foreach (var segmentName in segments)
|
||||
{
|
||||
var existingChild = current.SubFolders.FirstOrDefault(f => f.FolderName == segmentName);
|
||||
|
||||
currentFullPath += "/" + System.Net.WebUtility.UrlEncode(segmentName);
|
||||
|
||||
if (existingChild == null)
|
||||
{
|
||||
var newFolder = new FolderNode
|
||||
{
|
||||
FolderName = segmentName,
|
||||
FolderPath = currentFullPath, // 存完整的 URI
|
||||
MediaFolderId = parent.MediaFolderId
|
||||
};
|
||||
current.SubFolders.Add(newFolder);
|
||||
current = newFolder;
|
||||
}
|
||||
else
|
||||
{
|
||||
current = existingChild;
|
||||
currentFullPath = existingChild.FolderPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,20 +8,17 @@ namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
public class ExtendedTrack
|
||||
{
|
||||
// 标准 URI (file:///..., smb://..., http://...)
|
||||
public string Uri { get; private set; } = "";
|
||||
|
||||
// 对于本地文件,返回 C:\Music\Song.mp3
|
||||
// 对于远程文件,返回解码后的路径部分 /Music/Song.mp3
|
||||
public string UriPath
|
||||
public string DecodedAbsoluteUri
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(Uri)) return "";
|
||||
try
|
||||
{
|
||||
var u = new System.Uri(Uri);
|
||||
return u.IsFile ? u.LocalPath : System.Net.WebUtility.UrlDecode(u.AbsolutePath);
|
||||
var u = new Uri(Uri);
|
||||
return u.IsFile ? u.LocalPath : System.Net.WebUtility.UrlDecode(u.AbsoluteUri);
|
||||
}
|
||||
catch { return Uri; }
|
||||
}
|
||||
@@ -105,6 +102,7 @@ namespace BetterLyrics.WinUI3.Models
|
||||
}
|
||||
}
|
||||
}
|
||||
public string MediaFolderId { get; set; } = "";
|
||||
|
||||
public string Title { get; set; } = "";
|
||||
public string Artist { get; set; } = "";
|
||||
@@ -142,6 +140,7 @@ namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
if (entity == null) return;
|
||||
|
||||
this.MediaFolderId = entity.MediaFolderId;
|
||||
this.Uri = entity.Uri;
|
||||
|
||||
this.Title = entity.Title;
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace BetterLyrics.WinUI3.Models
|
||||
[Indexed(Unique = true)]
|
||||
public string Uri { get; set; }
|
||||
|
||||
public string FileName { get; set; }
|
||||
public string FileName { get; set; } = "";
|
||||
|
||||
public bool IsDirectory { get; set; }
|
||||
|
||||
@@ -39,17 +39,17 @@ namespace BetterLyrics.WinUI3.Models
|
||||
public DateTime? LastModified { get; set; }
|
||||
|
||||
// ------ 元数据部分 (保持不变) ------
|
||||
public string? Title { get; set; }
|
||||
public string? Artists { get; set; }
|
||||
public string? Album { get; set; }
|
||||
public string Title { get; set; } = "";
|
||||
public string Artists { get; set; } = "";
|
||||
public string Album { get; set; } = "";
|
||||
public int? Year { get; set; }
|
||||
public int Bitrate { get; set; }
|
||||
public double SampleRate { get; set; }
|
||||
public int BitDepth { get; set; }
|
||||
public int Duration { get; set; } // 建议明确单位,例如 DurationMs
|
||||
public string? AudioFormatName { get; set; }
|
||||
public string? AudioFormatShortName { get; set; }
|
||||
public string? Encoder { get; set; }
|
||||
public int Duration { get; set; }
|
||||
public string AudioFormatName { get; set; } = "";
|
||||
public string AudioFormatShortName { get; set; } = "";
|
||||
public string Encoder { get; set; } = "";
|
||||
public string? EmbeddedLyrics { get; set; }
|
||||
public string? LocalAlbumArtPath { get; set; }
|
||||
public bool IsMetadataParsed { get; set; }
|
||||
|
||||
24
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/FolderNode.cs
Normal file
24
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/FolderNode.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Text;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
public partial class FolderNode : ObservableObject
|
||||
{
|
||||
public FileSourceType SourceType { get; set; } = FileSourceType.Local;
|
||||
|
||||
public string FolderName { get; set; } = "";
|
||||
|
||||
public string FolderPath { get; set; } = "";
|
||||
|
||||
public string MediaFolderId { get; set; } = "";
|
||||
|
||||
public ObservableCollection<FolderNode> SubFolders { get; set; } = new();
|
||||
|
||||
[ObservableProperty] public partial bool IsExpanded { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -64,7 +64,6 @@ namespace BetterLyrics.WinUI3.Models
|
||||
Scheme = UriScheme ?? "file",
|
||||
Host = UriHost,
|
||||
Port = UriPort,
|
||||
UserName = UserName
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(UriPath))
|
||||
|
||||
@@ -6,37 +6,18 @@ namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
public partial class SongsTabInfo : BaseViewModel
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Name { get; set; } = "";
|
||||
|
||||
public string Icon { get; set; }
|
||||
public string Icon { get; set; } = "";
|
||||
|
||||
public bool IsClosable { get; set; }
|
||||
public CommonSongProperty FilterProperty { get; set; } = CommonSongProperty.Title;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool IsStarred { get; set; }
|
||||
public string FilterValue { get; set; } = "";
|
||||
|
||||
public CommonSongProperty FilterProperty { get; set; }
|
||||
|
||||
public string FilterValue { get; set; }
|
||||
public bool IsDefault => Icon == "\uE8A9";
|
||||
|
||||
public SongsTabInfo()
|
||||
{
|
||||
Name = string.Empty;
|
||||
Icon = string.Empty;
|
||||
IsClosable = true;
|
||||
IsStarred = false;
|
||||
FilterProperty = CommonSongProperty.Title;
|
||||
FilterValue = string.Empty;
|
||||
}
|
||||
|
||||
public SongsTabInfo(string name, string icon, bool isClosable, bool isStarred, CommonSongProperty filterProperty, string filterValue)
|
||||
{
|
||||
Name = name;
|
||||
Icon = icon;
|
||||
IsClosable = isClosable;
|
||||
IsStarred = isStarred;
|
||||
FilterProperty = filterProperty;
|
||||
FilterValue = filterValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,6 +88,7 @@ namespace BetterLyrics.WinUI3.Services.AlbumArtSearchService
|
||||
if (enabledIds.Count == 0) return null;
|
||||
|
||||
var allFiles = await _fileSystemService.GetParsedFilesAsync(enabledIds);
|
||||
allFiles = allFiles.Where(x => FileHelper.MusicExtensions.Contains(Path.GetExtension(x.FileName))).ToList();
|
||||
|
||||
FileCacheEntity? bestMatch = null;
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SQLite;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -28,7 +29,12 @@ namespace BetterLyrics.WinUI3.Services.FileSystemService
|
||||
|
||||
private readonly SQLiteAsyncConnection _db;
|
||||
private bool _isInitialized = false;
|
||||
private readonly System.Collections.Concurrent.ConcurrentDictionary<string, CancellationTokenSource> _folderTimerTokens = new();
|
||||
|
||||
// 定时器字典
|
||||
private readonly ConcurrentDictionary<string, CancellationTokenSource> _folderTimerTokens = new();
|
||||
// 当前正在执行的扫描任务字典
|
||||
private readonly ConcurrentDictionary<string, CancellationTokenSource> _activeScanTokens = new();
|
||||
|
||||
private static readonly SemaphoreSlim _dbLock = new(1, 1);
|
||||
private static readonly SemaphoreSlim _folderScanLock = new(1, 1);
|
||||
|
||||
@@ -238,23 +244,34 @@ namespace BetterLyrics.WinUI3.Services.FileSystemService
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
folder.CleaningUpStatusText = _localizationService.GetLocalizedString("FileSystemServiceCleaningCache");
|
||||
folder.CleaningUpStatusText = _localizationService.GetLocalizedString("FileSystemServicePrepareToClean");
|
||||
folder.IsCleaningUp = true;
|
||||
});
|
||||
|
||||
if (_folderTimerTokens.TryRemove(folder.Id, out var cts))
|
||||
if (_folderTimerTokens.TryRemove(folder.Id, out var timerCts))
|
||||
{
|
||||
cts.Cancel();
|
||||
cts.Dispose();
|
||||
timerCts.Cancel();
|
||||
timerCts.Dispose();
|
||||
_logger.LogInformation("DeleteCacheForMediaFolderAsync: {}", "cts.Dispose();");
|
||||
}
|
||||
|
||||
if (_activeScanTokens.TryGetValue(folder.Id, out var activeScanCts))
|
||||
{
|
||||
activeScanCts.Cancel();
|
||||
// 强制终止正在扫描的操作
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await _folderScanLock.WaitAsync();
|
||||
|
||||
try
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
folder.CleaningUpStatusText = _localizationService.GetLocalizedString("FileSystemServiceCleaningCache");
|
||||
});
|
||||
|
||||
await InitializeAsync();
|
||||
|
||||
await _dbLock.WaitAsync();
|
||||
@@ -292,6 +309,9 @@ namespace BetterLyrics.WinUI3.Services.FileSystemService
|
||||
{
|
||||
if (folder == null || !folder.IsEnabled) return;
|
||||
|
||||
using var scanCts = CancellationTokenSource.CreateLinkedTokenSource(token);
|
||||
_activeScanTokens[folder.Id] = scanCts;
|
||||
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
folder.IsIndexing = true;
|
||||
@@ -301,7 +321,7 @@ namespace BetterLyrics.WinUI3.Services.FileSystemService
|
||||
|
||||
try
|
||||
{
|
||||
await _folderScanLock.WaitAsync(token);
|
||||
await _folderScanLock.WaitAsync(scanCts.Token);
|
||||
|
||||
_dispatcherQueue.TryEnqueue(() => folder.IndexingStatusText = _localizationService.GetLocalizedString("FileSystemServiceConnecting"));
|
||||
|
||||
@@ -322,7 +342,7 @@ namespace BetterLyrics.WinUI3.Services.FileSystemService
|
||||
|
||||
while (foldersToScan.Count > 0)
|
||||
{
|
||||
if (token.IsCancellationRequested) return;
|
||||
if (scanCts.Token.IsCancellationRequested) return;
|
||||
|
||||
var currentParent = foldersToScan.Dequeue();
|
||||
|
||||
@@ -350,7 +370,7 @@ namespace BetterLyrics.WinUI3.Services.FileSystemService
|
||||
|
||||
foreach (var item in filesToProcess)
|
||||
{
|
||||
if (token.IsCancellationRequested) return;
|
||||
if (scanCts.Token.IsCancellationRequested) return;
|
||||
|
||||
current++;
|
||||
|
||||
@@ -383,7 +403,7 @@ namespace BetterLyrics.WinUI3.Services.FileSystemService
|
||||
else
|
||||
{
|
||||
using var memStream = new MemoryStream();
|
||||
await originalStream.CopyToAsync(memStream, token);
|
||||
await originalStream.CopyToAsync(memStream, scanCts.Token);
|
||||
memStream.Position = 0;
|
||||
track = new ExtendedTrack(item, memStream);
|
||||
}
|
||||
@@ -459,6 +479,8 @@ namespace BetterLyrics.WinUI3.Services.FileSystemService
|
||||
{
|
||||
_folderScanLock.Release();
|
||||
|
||||
_activeScanTokens.TryRemove(folder.Id, out _);
|
||||
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
folder.IsIndexing = false;
|
||||
@@ -481,8 +503,8 @@ namespace BetterLyrics.WinUI3.Services.FileSystemService
|
||||
|
||||
// SQL 逻辑: SELECT * FROM FileCache WHERE IsMetadataParsed = 1 AND MediaFolderId IN (...)
|
||||
var results = await _db.Table<FileCacheEntity>()
|
||||
.Where(x => x.IsMetadataParsed && idList.Contains(x.MediaFolderId))
|
||||
.ToListAsync();
|
||||
.Where(x => x.IsMetadataParsed && idList.Contains(x.MediaFolderId))
|
||||
.ToListAsync();
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@ using FluentFTP;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net; // 用于 WebUtility.UrlDecode
|
||||
using System.Text; // ★ 修复 Encoding 报错的关键
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Services.FileSystemService.Providers
|
||||
@@ -17,16 +18,16 @@ namespace BetterLyrics.WinUI3.Services.FileSystemService.Providers
|
||||
{
|
||||
_config = config ?? throw new ArgumentNullException(nameof(config));
|
||||
|
||||
// 初始化 FluentFTP 配置
|
||||
var ftpConfig = new FtpConfig
|
||||
{
|
||||
ConnectTimeout = 5000,
|
||||
// 根据需要配置编码,防止中文乱码
|
||||
// Encoding = System.Text.Encoding.GetEncoding("GB2312")
|
||||
DataConnectionConnectTimeout = 5000,
|
||||
ReadTimeout = 10000,
|
||||
|
||||
// 忽略证书错误
|
||||
ValidateAnyCertificate = true
|
||||
};
|
||||
|
||||
// FluentFTP 构造函数接收主机、用户、密码、端口
|
||||
// 端口如果为 -1 (MediaFolder 默认值),则让 FluentFTP 使用默认 21
|
||||
int port = _config.UriPort > 0 ? _config.UriPort : 0;
|
||||
|
||||
_client = new AsyncFtpClient(
|
||||
@@ -42,11 +43,13 @@ namespace BetterLyrics.WinUI3.Services.FileSystemService.Providers
|
||||
{
|
||||
try
|
||||
{
|
||||
await _client.AutoConnect();
|
||||
if (_client.IsConnected) return true;
|
||||
await _client.AutoConnect(); // AutoConnect 会自动尝试 FTP/FTPS
|
||||
return _client.IsConnected;
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"FTP连接失败: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -55,98 +58,122 @@ namespace BetterLyrics.WinUI3.Services.FileSystemService.Providers
|
||||
{
|
||||
var result = new List<FileCacheEntity>();
|
||||
|
||||
// 1. 确定目标服务器路径
|
||||
// 1. 确定 FTP 服务器上的绝对路径
|
||||
string targetServerPath;
|
||||
Uri parentUri;
|
||||
|
||||
if (parentFolder == null)
|
||||
{
|
||||
// 根目录:从配置中提取路径 (例如 /Music)
|
||||
// GetStandardUri().AbsolutePath 会返回带前导斜杠的路径
|
||||
// 根目录:从配置中提取
|
||||
var rootUri = _config.GetStandardUri();
|
||||
targetServerPath = rootUri.AbsolutePath; // "/Music"
|
||||
targetServerPath = rootUri.AbsolutePath;
|
||||
parentUri = rootUri;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 子目录:将标准 URI 转换为 FTP 服务器路径
|
||||
// 子目录:从实体中提取
|
||||
targetServerPath = GetServerPathFromUri(parentFolder.Uri);
|
||||
parentUri = new Uri(parentFolder.Uri);
|
||||
}
|
||||
|
||||
// 确保路径合法性 (FluentFTP 喜欢 Unix 风格斜杠)
|
||||
targetServerPath = targetServerPath.Replace("\\", "/");
|
||||
// 2. 路径清洗:解码 URL (比如 %20 -> 空格),并统一分隔符
|
||||
targetServerPath = WebUtility.UrlDecode(targetServerPath).Replace("\\", "/");
|
||||
if (string.IsNullOrEmpty(targetServerPath)) targetServerPath = "/";
|
||||
|
||||
// 2. 获取列表
|
||||
var items = await _client.GetListing(targetServerPath);
|
||||
|
||||
// 3. 准备 Base URI 用于拼接子项
|
||||
// FTP URI 基础部分: ftp://host:port
|
||||
string baseUriStr = $"{parentUri.Scheme}://{parentUri.Host}";
|
||||
if (parentUri.Port > 0) baseUriStr += $":{parentUri.Port}";
|
||||
|
||||
foreach (var item in items)
|
||||
try
|
||||
{
|
||||
// 排除 . 和 ..
|
||||
if (item.Name == "." || item.Name == "..") continue;
|
||||
// 3. 获取列表 (FluentFTP 自动处理列表解析)
|
||||
var items = await _client.GetListing(targetServerPath, FtpListOption.Auto);
|
||||
|
||||
// 构建完整的标准 URI
|
||||
// item.FullName 是服务器上的绝对路径 (例如 /Music/Song.mp3)
|
||||
// 我们需要把它拼成 ftp://host:port/Music/Song.mp3
|
||||
// 注意:Path.Combine 在 Windows 上可能会用反斜杠,这里手动拼接更安全
|
||||
// 准备 Base URI Scheme (ftp://192.168.1.5:21) 用于拼接子项
|
||||
string baseUriSchema = $"{parentUri.Scheme}://{parentUri.Host}";
|
||||
if (parentUri.Port > 0) baseUriSchema += $":{parentUri.Port}";
|
||||
|
||||
string itemFullPath = item.FullName.StartsWith("/") ? item.FullName : "/" + item.FullName;
|
||||
string standardUri = baseUriStr + itemFullPath; // Uri 构造函数会自动处理编码
|
||||
|
||||
result.Add(new FileCacheEntity
|
||||
foreach (var item in items)
|
||||
{
|
||||
MediaFolderId = _config.Id,
|
||||
// 跳过 . 和 ..
|
||||
if (item.Name == "." || item.Name == "..") continue;
|
||||
|
||||
// 记录父级 URI
|
||||
// 如果 parentFolder 为空,则父级是 Config 的根 URI
|
||||
ParentUri = parentFolder?.Uri ?? _config.GetStandardUri().AbsoluteUri,
|
||||
// 只处理文件和文件夹
|
||||
if (item.Type != FtpObjectType.File && item.Type != FtpObjectType.Directory) continue;
|
||||
|
||||
Uri = standardUri, // 标准化 URI
|
||||
// 4. 构建标准 URI
|
||||
// FluentFTP 的 item.FullName 通常是 "/Music/Song.mp3"
|
||||
// 我们用 UriBuilder 把它封装成 "ftp://192.168.1.5:21/Music/Song.mp3"
|
||||
// UriBuilder 会自动处理路径中的特殊字符编码
|
||||
var builder = new UriBuilder(baseUriSchema)
|
||||
{
|
||||
Path = item.FullName
|
||||
};
|
||||
|
||||
FileName = item.Name,
|
||||
IsDirectory = item.Type == FtpObjectType.Directory,
|
||||
result.Add(new FileCacheEntity
|
||||
{
|
||||
MediaFolderId = _config.Id,
|
||||
// 如果是根目录扫描,ParentUri 用 Config 的;否则用传入文件夹的
|
||||
ParentUri = parentFolder?.Uri ?? _config.GetStandardUri().AbsoluteUri,
|
||||
|
||||
FileSize = item.Size,
|
||||
LastModified = item.Modified
|
||||
});
|
||||
Uri = builder.Uri.AbsoluteUri, // 标准化 URI
|
||||
|
||||
FileName = item.Name,
|
||||
IsDirectory = item.Type == FtpObjectType.Directory,
|
||||
FileSize = item.Size,
|
||||
// 防止某些服务器返回 MinValue
|
||||
LastModified = item.Modified == DateTime.MinValue ? DateTime.Now : item.Modified,
|
||||
|
||||
IsMetadataParsed = false
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"FTP列表获取失败: {targetServerPath} - {ex.Message}");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<Stream?> OpenReadAsync(FileCacheEntity entity)
|
||||
public async Task<Stream?> OpenReadAsync(FileCacheEntity file)
|
||||
{
|
||||
if (entity == null) return null;
|
||||
if (file == null) return null;
|
||||
|
||||
// 从标准 URI 还原回 FTP 服务器路径
|
||||
string serverPath = GetServerPathFromUri(entity.Uri);
|
||||
try
|
||||
{
|
||||
// 1. 还原服务器路径
|
||||
string serverPath = GetServerPathFromUri(file.Uri);
|
||||
|
||||
return await _client.OpenRead(serverPath);
|
||||
// 2. 解码 (Uri 里的空格是 %20,FTP 需要真实空格)
|
||||
serverPath = WebUtility.UrlDecode(serverPath);
|
||||
|
||||
// 3. 返回流
|
||||
// 注意:FluentFTP 的 OpenRead 依赖于连接保持活跃
|
||||
return await _client.OpenRead(serverPath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"打开文件流失败: {file.FileName} - {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DisconnectAsync() => await _client.Disconnect();
|
||||
public void Dispose() => _client?.Dispose();
|
||||
public async Task DisconnectAsync()
|
||||
{
|
||||
if (_client.IsConnected)
|
||||
{
|
||||
await _client.Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// ★ 私有辅助方法:URI -> FTP Path
|
||||
// =========================================================
|
||||
public void Dispose()
|
||||
{
|
||||
_client?.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
// 私有辅助方法
|
||||
private string GetServerPathFromUri(string uriString)
|
||||
{
|
||||
// 输入: ftp://192.168.1.5:21/Music/Song.mp3
|
||||
// 输出: /Music/Song.mp3
|
||||
|
||||
var uri = new Uri(uriString);
|
||||
|
||||
// Uri.AbsolutePath 自动包含了路径部分 (例如 /Music/Song.mp3)
|
||||
// 并且会自动进行 URL Decode (比如 %20 -> 空格)
|
||||
// 这正是 FluentFTP 需要的格式
|
||||
return uri.AbsolutePath;
|
||||
return uri.AbsolutePath; // 这里拿到的比如是 "/Music/Song%201.mp3"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -299,6 +299,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
if (enabledIds.Count == 0) return lyricsSearchResult;
|
||||
|
||||
var allFiles = await _fileSystemService.GetParsedFilesAsync(enabledIds);
|
||||
allFiles = allFiles.Where(x => FileHelper.LyricExtensions.Contains(Path.GetExtension(x.FileName))).ToList();
|
||||
|
||||
foreach (var item in allFiles)
|
||||
{
|
||||
@@ -342,6 +343,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
if (enabledIds.Count == 0) return lyricsSearchResult;
|
||||
|
||||
var allFiles = await _fileSystemService.GetParsedFilesAsync(enabledIds);
|
||||
allFiles = allFiles.Where(x => FileHelper.MusicExtensions.Contains(Path.GetExtension(x.FileName))).ToList();
|
||||
|
||||
FileCacheEntity? bestFile = null;
|
||||
int maxScore = 0;
|
||||
|
||||
@@ -24,11 +24,13 @@ namespace BetterLyrics.WinUI3.Services.SettingsService
|
||||
public partial class SettingsService : BaseViewModel, ISettingsService
|
||||
{
|
||||
private readonly DispatcherQueueTimer _writeAppSettingsTimer;
|
||||
private readonly ILocalizationService _localizationService;
|
||||
|
||||
public AppSettings AppSettings { get; set; }
|
||||
|
||||
public SettingsService()
|
||||
public SettingsService(ILocalizationService localizationService)
|
||||
{
|
||||
_localizationService = localizationService;
|
||||
_writeAppSettingsTimer = _dispatcherQueue.CreateTimer();
|
||||
|
||||
AppSettings = ReadAppSettings();
|
||||
@@ -60,6 +62,7 @@ namespace BetterLyrics.WinUI3.Services.SettingsService
|
||||
AppSettings.Version = MetadataHelper.AppVersion;
|
||||
|
||||
EnsureMediaSourceProvidersInfo();
|
||||
EnsureStarredPlaylists();
|
||||
}
|
||||
|
||||
private void EnsureMediaSourceProvidersInfo()
|
||||
@@ -102,6 +105,20 @@ namespace BetterLyrics.WinUI3.Services.SettingsService
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureStarredPlaylists()
|
||||
{
|
||||
if (!AppSettings.StarredPlaylists.Any(x => x.IsDefault))
|
||||
{
|
||||
AppSettings.StarredPlaylists.Insert(0, new SongsTabInfo
|
||||
{
|
||||
Name = _localizationService.GetLocalizedString("MusicGalleryPageAllSongs"),
|
||||
Icon = "\uE8A9",
|
||||
FilterProperty = CommonSongProperty.Title,
|
||||
FilterValue = string.Empty
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void AppSettings_ItemPropertyChanged(object? sender, ItemPropertyChangedEventArgs e)
|
||||
{
|
||||
WriteAppSettings();
|
||||
|
||||
@@ -180,6 +180,12 @@
|
||||
<data name="FileSystemServiceParsing" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServicePrepareToClean" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServiceRootDirectoryWarning" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServiceWaitingForScan" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
@@ -378,6 +384,12 @@
|
||||
<data name="MediaSettingsControlLastSyncTime.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlLocalFolder" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlNameSetting.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlSyncNow.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
@@ -438,6 +450,9 @@
|
||||
<data name="MusicGalleryPageFileNotFound.Text" xml:space="preserve">
|
||||
<value>لم يتم العثور على أغاني في مكتبة الوسائط</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFolder.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MusicGalleryPageImportFromFile.Text" xml:space="preserve">
|
||||
<value>استيراد من ملف</value>
|
||||
</data>
|
||||
@@ -528,12 +543,27 @@
|
||||
<data name="PrivacyPolicy.Content" xml:space="preserve">
|
||||
<value>سياسة الخصوصية</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlBrowse.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlName.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlName.PlaceholderText" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPassword.Header" xml:space="preserve">
|
||||
<value>كلمة المرور</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPath.Header" xml:space="preserve">
|
||||
<value>المسار</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPathNotExisted" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPathRequired" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPort.Header" xml:space="preserve">
|
||||
<value>المنفذ</value>
|
||||
</data>
|
||||
|
||||
@@ -180,6 +180,12 @@
|
||||
<data name="FileSystemServiceParsing" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServicePrepareToClean" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServiceRootDirectoryWarning" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServiceWaitingForScan" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
@@ -378,6 +384,12 @@
|
||||
<data name="MediaSettingsControlLastSyncTime.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlLocalFolder" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlNameSetting.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlSyncNow.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
@@ -438,6 +450,9 @@
|
||||
<data name="MusicGalleryPageFileNotFound.Text" xml:space="preserve">
|
||||
<value>Keine Songs in der Medienbibliothek gefunden</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFolder.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MusicGalleryPageImportFromFile.Text" xml:space="preserve">
|
||||
<value>Aus Datei importieren</value>
|
||||
</data>
|
||||
@@ -528,12 +543,27 @@
|
||||
<data name="PrivacyPolicy.Content" xml:space="preserve">
|
||||
<value>Datenschutzrichtlinie</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlBrowse.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlName.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlName.PlaceholderText" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPassword.Header" xml:space="preserve">
|
||||
<value>Passwort</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPath.Header" xml:space="preserve">
|
||||
<value>Pfad</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPathNotExisted" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPathRequired" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPort.Header" xml:space="preserve">
|
||||
<value>Port</value>
|
||||
</data>
|
||||
|
||||
@@ -180,8 +180,14 @@
|
||||
<data name="FileSystemServiceParsing" xml:space="preserve">
|
||||
<value>Parsing...</value>
|
||||
</data>
|
||||
<data name="FileSystemServicePrepareToClean" xml:space="preserve">
|
||||
<value>Preparing to clean cache...</value>
|
||||
</data>
|
||||
<data name="FileSystemServiceRootDirectoryWarning" xml:space="preserve">
|
||||
<value>The root directory path has been detected. A full disk index may contain a large number of non-media files and cause the scan to take too long. It is recommended to specify a specific subdirectory.</value>
|
||||
</data>
|
||||
<data name="FileSystemServiceWaitingForScan" xml:space="preserve">
|
||||
<value>Waiting for scan...</value>
|
||||
<value>Preparing to scan...</value>
|
||||
</data>
|
||||
<data name="FullscreenMode" xml:space="preserve">
|
||||
<value>Fullscreen Mode</value>
|
||||
@@ -378,6 +384,12 @@
|
||||
<data name="MediaSettingsControlLastSyncTime.Header" xml:space="preserve">
|
||||
<value>Last Sync Time</value>
|
||||
</data>
|
||||
<data name="MediaSettingsControlLocalFolder" xml:space="preserve">
|
||||
<value>Local Folder</value>
|
||||
</data>
|
||||
<data name="MediaSettingsControlNameSetting.Header" xml:space="preserve">
|
||||
<value>Name</value>
|
||||
</data>
|
||||
<data name="MediaSettingsControlSyncNow.Content" xml:space="preserve">
|
||||
<value>Sync now</value>
|
||||
</data>
|
||||
@@ -391,7 +403,7 @@
|
||||
<value>Next item</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageAddToPlayingQueue.Text" xml:space="preserve">
|
||||
<value>Add to queue</value>
|
||||
<value>Add to playing queue</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageAllSongs" xml:space="preserve">
|
||||
<value>All Music</value>
|
||||
@@ -438,11 +450,14 @@
|
||||
<data name="MusicGalleryPageFileNotFound.Text" xml:space="preserve">
|
||||
<value>No songs found in media library</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFolder.Text" xml:space="preserve">
|
||||
<value>Folders</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageImportFromFile.Text" xml:space="preserve">
|
||||
<value>Import from file</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageNewPlaylist.Text" xml:space="preserve">
|
||||
<value>Create Playlist</value>
|
||||
<value>Create playlist</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPagePlayingQueue.Text" xml:space="preserve">
|
||||
<value>Playing Queue</value>
|
||||
@@ -451,7 +466,7 @@
|
||||
<value>Playing queue is empty</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPagePlaylist.Text" xml:space="preserve">
|
||||
<value>Playlist</value>
|
||||
<value>Playlists</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageQueueLoop.Text" xml:space="preserve">
|
||||
<value>Loop List</value>
|
||||
@@ -528,12 +543,27 @@
|
||||
<data name="PrivacyPolicy.Content" xml:space="preserve">
|
||||
<value>Privacy Policy</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlBrowse.Content" xml:space="preserve">
|
||||
<value>Browse</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlName.Header" xml:space="preserve">
|
||||
<value>Name</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlName.PlaceholderText" xml:space="preserve">
|
||||
<value>Leaving it blank will automatically generate a default name.</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPassword.Header" xml:space="preserve">
|
||||
<value>Password</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPath.Header" xml:space="preserve">
|
||||
<value>Path</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPathNotExisted" xml:space="preserve">
|
||||
<value>The specified folder path could not be found</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPathRequired" xml:space="preserve">
|
||||
<value>Path is required</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPort.Header" xml:space="preserve">
|
||||
<value>Port</value>
|
||||
</data>
|
||||
|
||||
@@ -180,6 +180,12 @@
|
||||
<data name="FileSystemServiceParsing" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServicePrepareToClean" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServiceRootDirectoryWarning" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServiceWaitingForScan" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
@@ -378,6 +384,12 @@
|
||||
<data name="MediaSettingsControlLastSyncTime.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlLocalFolder" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlNameSetting.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlSyncNow.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
@@ -438,6 +450,9 @@
|
||||
<data name="MusicGalleryPageFileNotFound.Text" xml:space="preserve">
|
||||
<value>No se encontraron canciones en la biblioteca multimedia</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFolder.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MusicGalleryPageImportFromFile.Text" xml:space="preserve">
|
||||
<value>Importar desde archivo</value>
|
||||
</data>
|
||||
@@ -528,12 +543,27 @@
|
||||
<data name="PrivacyPolicy.Content" xml:space="preserve">
|
||||
<value>Política de privacidad</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlBrowse.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlName.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlName.PlaceholderText" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPassword.Header" xml:space="preserve">
|
||||
<value>Contraseña</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPath.Header" xml:space="preserve">
|
||||
<value>Ruta</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPathNotExisted" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPathRequired" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPort.Header" xml:space="preserve">
|
||||
<value>Puerto</value>
|
||||
</data>
|
||||
|
||||
@@ -180,6 +180,12 @@
|
||||
<data name="FileSystemServiceParsing" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServicePrepareToClean" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServiceRootDirectoryWarning" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServiceWaitingForScan" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
@@ -378,6 +384,12 @@
|
||||
<data name="MediaSettingsControlLastSyncTime.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlLocalFolder" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlNameSetting.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlSyncNow.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
@@ -438,6 +450,9 @@
|
||||
<data name="MusicGalleryPageFileNotFound.Text" xml:space="preserve">
|
||||
<value>Aucune chanson trouvée dans la bibliothèque multimédia</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFolder.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MusicGalleryPageImportFromFile.Text" xml:space="preserve">
|
||||
<value>Importer depuis un fichier</value>
|
||||
</data>
|
||||
@@ -528,12 +543,27 @@
|
||||
<data name="PrivacyPolicy.Content" xml:space="preserve">
|
||||
<value>Politique de confidentialité</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlBrowse.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlName.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlName.PlaceholderText" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPassword.Header" xml:space="preserve">
|
||||
<value>Mot de passe</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPath.Header" xml:space="preserve">
|
||||
<value>Chemin</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPathNotExisted" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPathRequired" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPort.Header" xml:space="preserve">
|
||||
<value>Port</value>
|
||||
</data>
|
||||
|
||||
@@ -180,6 +180,12 @@
|
||||
<data name="FileSystemServiceParsing" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServicePrepareToClean" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServiceRootDirectoryWarning" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServiceWaitingForScan" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
@@ -378,6 +384,12 @@
|
||||
<data name="MediaSettingsControlLastSyncTime.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlLocalFolder" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlNameSetting.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlSyncNow.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
@@ -438,6 +450,9 @@
|
||||
<data name="MusicGalleryPageFileNotFound.Text" xml:space="preserve">
|
||||
<value>मीडिया लाइब्रेरी में कोई गाना नहीं मिला</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFolder.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MusicGalleryPageImportFromFile.Text" xml:space="preserve">
|
||||
<value>फ़ाइल से आयात करें</value>
|
||||
</data>
|
||||
@@ -528,12 +543,27 @@
|
||||
<data name="PrivacyPolicy.Content" xml:space="preserve">
|
||||
<value>गोपनीयता नीति</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlBrowse.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlName.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlName.PlaceholderText" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPassword.Header" xml:space="preserve">
|
||||
<value>पासवर्ड</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPath.Header" xml:space="preserve">
|
||||
<value>पथ</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPathNotExisted" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPathRequired" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPort.Header" xml:space="preserve">
|
||||
<value>पोर्ट</value>
|
||||
</data>
|
||||
|
||||
@@ -180,6 +180,12 @@
|
||||
<data name="FileSystemServiceParsing" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServicePrepareToClean" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServiceRootDirectoryWarning" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServiceWaitingForScan" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
@@ -378,6 +384,12 @@
|
||||
<data name="MediaSettingsControlLastSyncTime.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlLocalFolder" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlNameSetting.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlSyncNow.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
@@ -438,6 +450,9 @@
|
||||
<data name="MusicGalleryPageFileNotFound.Text" xml:space="preserve">
|
||||
<value>Tidak ada lagu yang ditemukan di pustaka media</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFolder.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MusicGalleryPageImportFromFile.Text" xml:space="preserve">
|
||||
<value>Impor dari file</value>
|
||||
</data>
|
||||
@@ -528,12 +543,27 @@
|
||||
<data name="PrivacyPolicy.Content" xml:space="preserve">
|
||||
<value>Kebijakan Privasi</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlBrowse.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlName.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlName.PlaceholderText" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPassword.Header" xml:space="preserve">
|
||||
<value>Kata Sandi</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPath.Header" xml:space="preserve">
|
||||
<value>Jalur</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPathNotExisted" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPathRequired" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPort.Header" xml:space="preserve">
|
||||
<value>Port</value>
|
||||
</data>
|
||||
|
||||
@@ -180,6 +180,12 @@
|
||||
<data name="FileSystemServiceParsing" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServicePrepareToClean" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServiceRootDirectoryWarning" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServiceWaitingForScan" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
@@ -378,6 +384,12 @@
|
||||
<data name="MediaSettingsControlLastSyncTime.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlLocalFolder" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlNameSetting.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlSyncNow.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
@@ -438,6 +450,9 @@
|
||||
<data name="MusicGalleryPageFileNotFound.Text" xml:space="preserve">
|
||||
<value>メディアライブラリに曲が見つかりません</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFolder.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MusicGalleryPageImportFromFile.Text" xml:space="preserve">
|
||||
<value>ファイルからインポート</value>
|
||||
</data>
|
||||
@@ -528,12 +543,27 @@
|
||||
<data name="PrivacyPolicy.Content" xml:space="preserve">
|
||||
<value>個人情報保護方針</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlBrowse.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlName.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlName.PlaceholderText" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPassword.Header" xml:space="preserve">
|
||||
<value>パスワード</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPath.Header" xml:space="preserve">
|
||||
<value>パス</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPathNotExisted" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPathRequired" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPort.Header" xml:space="preserve">
|
||||
<value>ポート</value>
|
||||
</data>
|
||||
|
||||
@@ -180,6 +180,12 @@
|
||||
<data name="FileSystemServiceParsing" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServicePrepareToClean" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServiceRootDirectoryWarning" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServiceWaitingForScan" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
@@ -378,6 +384,12 @@
|
||||
<data name="MediaSettingsControlLastSyncTime.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlLocalFolder" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlNameSetting.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlSyncNow.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
@@ -438,6 +450,9 @@
|
||||
<data name="MusicGalleryPageFileNotFound.Text" xml:space="preserve">
|
||||
<value>미디어 라이브러리에서 곡을 찾지 못했습니다</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFolder.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MusicGalleryPageImportFromFile.Text" xml:space="preserve">
|
||||
<value>파일에서 가져오기</value>
|
||||
</data>
|
||||
@@ -528,12 +543,24 @@
|
||||
<data name="PrivacyPolicy.Content" xml:space="preserve">
|
||||
<value>개인정보 처리방침</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlBrowse.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlName.PlaceholderText" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPassword.Header" xml:space="preserve">
|
||||
<value>비밀번호</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPath.Header" xml:space="preserve">
|
||||
<value>경로</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPathNotExisted" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPathRequired" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPort.Header" xml:space="preserve">
|
||||
<value>포트</value>
|
||||
</data>
|
||||
|
||||
@@ -180,6 +180,12 @@
|
||||
<data name="FileSystemServiceParsing" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServicePrepareToClean" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServiceRootDirectoryWarning" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServiceWaitingForScan" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
@@ -378,6 +384,12 @@
|
||||
<data name="MediaSettingsControlLastSyncTime.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlLocalFolder" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlNameSetting.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlSyncNow.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
@@ -438,6 +450,9 @@
|
||||
<data name="MusicGalleryPageFileNotFound.Text" xml:space="preserve">
|
||||
<value>Tiada lagu ditemui dalam pustaka media</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFolder.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MusicGalleryPageImportFromFile.Text" xml:space="preserve">
|
||||
<value>Import dari fail</value>
|
||||
</data>
|
||||
@@ -528,12 +543,27 @@
|
||||
<data name="PrivacyPolicy.Content" xml:space="preserve">
|
||||
<value>Dasar Privasi</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlBrowse.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlName.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlName.PlaceholderText" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPassword.Header" xml:space="preserve">
|
||||
<value>Kata Laluan</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPath.Header" xml:space="preserve">
|
||||
<value>Laluan</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPathNotExisted" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPathRequired" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPort.Header" xml:space="preserve">
|
||||
<value>Port</value>
|
||||
</data>
|
||||
|
||||
@@ -180,6 +180,12 @@
|
||||
<data name="FileSystemServiceParsing" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServicePrepareToClean" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServiceRootDirectoryWarning" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServiceWaitingForScan" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
@@ -378,6 +384,12 @@
|
||||
<data name="MediaSettingsControlLastSyncTime.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlLocalFolder" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlNameSetting.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlSyncNow.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
@@ -438,6 +450,9 @@
|
||||
<data name="MusicGalleryPageFileNotFound.Text" xml:space="preserve">
|
||||
<value>Nenhuma música encontrada na biblioteca multimédia</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFolder.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MusicGalleryPageImportFromFile.Text" xml:space="preserve">
|
||||
<value>Importar de ficheiro</value>
|
||||
</data>
|
||||
@@ -528,12 +543,27 @@
|
||||
<data name="PrivacyPolicy.Content" xml:space="preserve">
|
||||
<value>Política de Privacidade</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlBrowse.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlName.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlName.PlaceholderText" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPassword.Header" xml:space="preserve">
|
||||
<value>Palavra-passe</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPath.Header" xml:space="preserve">
|
||||
<value>Caminho</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPathNotExisted" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPathRequired" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPort.Header" xml:space="preserve">
|
||||
<value>Porta</value>
|
||||
</data>
|
||||
|
||||
@@ -180,6 +180,12 @@
|
||||
<data name="FileSystemServiceParsing" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServicePrepareToClean" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServiceRootDirectoryWarning" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServiceWaitingForScan" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
@@ -378,6 +384,12 @@
|
||||
<data name="MediaSettingsControlLastSyncTime.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlLocalFolder" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlNameSetting.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlSyncNow.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
@@ -438,6 +450,9 @@
|
||||
<data name="MusicGalleryPageFileNotFound.Text" xml:space="preserve">
|
||||
<value>Песни в медиатеке не найдены</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFolder.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MusicGalleryPageImportFromFile.Text" xml:space="preserve">
|
||||
<value>Импорт из файла</value>
|
||||
</data>
|
||||
@@ -528,12 +543,27 @@
|
||||
<data name="PrivacyPolicy.Content" xml:space="preserve">
|
||||
<value>Политика конфиденциальности</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlBrowse.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlName.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlName.PlaceholderText" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPassword.Header" xml:space="preserve">
|
||||
<value>Пароль</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPath.Header" xml:space="preserve">
|
||||
<value>Путь</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPathNotExisted" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPathRequired" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPort.Header" xml:space="preserve">
|
||||
<value>Порт</value>
|
||||
</data>
|
||||
|
||||
@@ -180,6 +180,12 @@
|
||||
<data name="FileSystemServiceParsing" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServicePrepareToClean" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServiceRootDirectoryWarning" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServiceWaitingForScan" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
@@ -378,6 +384,12 @@
|
||||
<data name="MediaSettingsControlLastSyncTime.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlLocalFolder" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlNameSetting.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlSyncNow.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
@@ -438,6 +450,9 @@
|
||||
<data name="MusicGalleryPageFileNotFound.Text" xml:space="preserve">
|
||||
<value>ไม่พบเพลงในไลบรารีสื่อ</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFolder.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MusicGalleryPageImportFromFile.Text" xml:space="preserve">
|
||||
<value>นำเข้าจากไฟล์</value>
|
||||
</data>
|
||||
@@ -528,12 +543,27 @@
|
||||
<data name="PrivacyPolicy.Content" xml:space="preserve">
|
||||
<value>นโยบายความเป็นส่วนตัว</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlBrowse.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlName.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlName.PlaceholderText" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPassword.Header" xml:space="preserve">
|
||||
<value>รหัสผ่าน</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPath.Header" xml:space="preserve">
|
||||
<value>เส้นทาง</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPathNotExisted" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPathRequired" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPort.Header" xml:space="preserve">
|
||||
<value>พอร์ต</value>
|
||||
</data>
|
||||
|
||||
@@ -180,6 +180,12 @@
|
||||
<data name="FileSystemServiceParsing" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServicePrepareToClean" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServiceRootDirectoryWarning" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServiceWaitingForScan" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
@@ -378,6 +384,12 @@
|
||||
<data name="MediaSettingsControlLastSyncTime.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlLocalFolder" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlNameSetting.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlSyncNow.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
@@ -438,6 +450,9 @@
|
||||
<data name="MusicGalleryPageFileNotFound.Text" xml:space="preserve">
|
||||
<value>Không tìm thấy bài hát nào trong thư viện phương tiện</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFolder.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MusicGalleryPageImportFromFile.Text" xml:space="preserve">
|
||||
<value>Nhập từ tệp</value>
|
||||
</data>
|
||||
@@ -528,12 +543,27 @@
|
||||
<data name="PrivacyPolicy.Content" xml:space="preserve">
|
||||
<value>Chính sách bảo mật</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlBrowse.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlName.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlName.PlaceholderText" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPassword.Header" xml:space="preserve">
|
||||
<value>Mật khẩu</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPath.Header" xml:space="preserve">
|
||||
<value>Đường dẫn</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPathNotExisted" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPathRequired" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPort.Header" xml:space="preserve">
|
||||
<value>Cổng</value>
|
||||
</data>
|
||||
|
||||
@@ -166,22 +166,28 @@
|
||||
<value>无法连接到 LX 音乐服务器,请转到设置 - 播放源 - LX Music - LX 音乐服务器以检查是否正确输入链接</value>
|
||||
</data>
|
||||
<data name="FileSystemServiceCleaningCache" xml:space="preserve">
|
||||
<value>清理缓存中...</value>
|
||||
<value>正在清理缓存...</value>
|
||||
</data>
|
||||
<data name="FileSystemServiceConnectFailed" xml:space="preserve">
|
||||
<value>连接失败</value>
|
||||
</data>
|
||||
<data name="FileSystemServiceConnecting" xml:space="preserve">
|
||||
<value>连接中...</value>
|
||||
<value>正在连接...</value>
|
||||
</data>
|
||||
<data name="FileSystemServiceFetchingFileList" xml:space="preserve">
|
||||
<value>获取文件列表中...</value>
|
||||
<value>正在获取文件列表...</value>
|
||||
</data>
|
||||
<data name="FileSystemServiceParsing" xml:space="preserve">
|
||||
<value>解析中...</value>
|
||||
<value>正在解析...</value>
|
||||
</data>
|
||||
<data name="FileSystemServicePrepareToClean" xml:space="preserve">
|
||||
<value>正在准备清理...</value>
|
||||
</data>
|
||||
<data name="FileSystemServiceRootDirectoryWarning" xml:space="preserve">
|
||||
<value>检测到根目录路径。全盘索引可能包含大量非媒体文件并导致扫描耗时过长,建议指定具体的子目录。</value>
|
||||
</data>
|
||||
<data name="FileSystemServiceWaitingForScan" xml:space="preserve">
|
||||
<value>等待扫描中...</value>
|
||||
<value>正在准备扫描...</value>
|
||||
</data>
|
||||
<data name="FullscreenMode" xml:space="preserve">
|
||||
<value>全屏模式</value>
|
||||
@@ -378,6 +384,12 @@
|
||||
<data name="MediaSettingsControlLastSyncTime.Header" xml:space="preserve">
|
||||
<value>上次同步时间</value>
|
||||
</data>
|
||||
<data name="MediaSettingsControlLocalFolder" xml:space="preserve">
|
||||
<value>本地文件夹</value>
|
||||
</data>
|
||||
<data name="MediaSettingsControlNameSetting.Header" xml:space="preserve">
|
||||
<value>名称</value>
|
||||
</data>
|
||||
<data name="MediaSettingsControlSyncNow.Content" xml:space="preserve">
|
||||
<value>立即同步</value>
|
||||
</data>
|
||||
@@ -438,6 +450,9 @@
|
||||
<data name="MusicGalleryPageFileNotFound.Text" xml:space="preserve">
|
||||
<value>未在媒体库内找到任何歌曲</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFolder.Text" xml:space="preserve">
|
||||
<value>文件夹</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageImportFromFile.Text" xml:space="preserve">
|
||||
<value>从文件导入</value>
|
||||
</data>
|
||||
@@ -528,12 +543,27 @@
|
||||
<data name="PrivacyPolicy.Content" xml:space="preserve">
|
||||
<value>隐私政策</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlBrowse.Content" xml:space="preserve">
|
||||
<value>浏览</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlName.Header" xml:space="preserve">
|
||||
<value>名称</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlName.PlaceholderText" xml:space="preserve">
|
||||
<value>留空则自动生成默认名称</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPassword.Header" xml:space="preserve">
|
||||
<value>密码</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPath.Header" xml:space="preserve">
|
||||
<value>路径</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPathNotExisted" xml:space="preserve">
|
||||
<value>找不到指定的文件夹路径</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPathRequired" xml:space="preserve">
|
||||
<value>路径为必填项</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPort.Header" xml:space="preserve">
|
||||
<value>端口</value>
|
||||
</data>
|
||||
|
||||
@@ -180,6 +180,12 @@
|
||||
<data name="FileSystemServiceParsing" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServicePrepareToClean" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServiceRootDirectoryWarning" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="FileSystemServiceWaitingForScan" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
@@ -378,6 +384,12 @@
|
||||
<data name="MediaSettingsControlLastSyncTime.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlLocalFolder" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlNameSetting.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MediaSettingsControlSyncNow.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
@@ -438,6 +450,9 @@
|
||||
<data name="MusicGalleryPageFileNotFound.Text" xml:space="preserve">
|
||||
<value>未在媒體櫃內找到任何歌曲</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFolder.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="MusicGalleryPageImportFromFile.Text" xml:space="preserve">
|
||||
<value>從檔案匯入</value>
|
||||
</data>
|
||||
@@ -528,12 +543,27 @@
|
||||
<data name="PrivacyPolicy.Content" xml:space="preserve">
|
||||
<value>隱私權政策</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlBrowse.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlName.Header" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlName.PlaceholderText" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPassword.Header" xml:space="preserve">
|
||||
<value>密碼</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPath.Header" xml:space="preserve">
|
||||
<value>路徑</value>
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPathNotExisted" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPathRequired" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="RemoteServerConfigControlPort.Header" xml:space="preserve">
|
||||
<value>連接埠</value>
|
||||
</data>
|
||||
|
||||
@@ -37,33 +37,6 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
AppSettings = _settingsService.AppSettings;
|
||||
}
|
||||
|
||||
private void AddFolderAsync(string path)
|
||||
{
|
||||
var normalizedPath = Path.GetFullPath(path).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;
|
||||
|
||||
if (AppSettings.LocalMediaFolders.Any(x => Path.GetFullPath(x.UriPath).TrimEnd(Path.DirectorySeparatorChar).Equals(normalizedPath.TrimEnd(Path.DirectorySeparatorChar), StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
ToastHelper.ShowToast("SettingsPagePathExistedInfo", null, InfoBarSeverity.Warning);
|
||||
}
|
||||
else if (AppSettings.LocalMediaFolders.Any(item => normalizedPath.StartsWith(Path.GetFullPath(item.UriPath).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
// 添加的文件夹是现有文件夹的子文件夹
|
||||
ToastHelper.ShowToast("SettingsPagePathBeIncludedInfo", null, InfoBarSeverity.Warning);
|
||||
}
|
||||
else if (AppSettings.LocalMediaFolders.Any(item => Path.GetFullPath(item.UriPath).TrimEnd(Path.DirectorySeparatorChar).StartsWith(normalizedPath, StringComparison.OrdinalIgnoreCase))
|
||||
)
|
||||
{
|
||||
// 添加的文件夹是现有文件夹的父文件夹
|
||||
ToastHelper.ShowToast("SettingsPagePathIncludingOthersInfo", null, InfoBarSeverity.Warning);
|
||||
}
|
||||
else
|
||||
{
|
||||
var tempFolder = new MediaFolder(path);
|
||||
AppSettings.LocalMediaFolders.Add(tempFolder);
|
||||
_ = Task.Run(async () => await _fileSystemService.ScanMediaFolderAsync(tempFolder));
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveFolder(MediaFolder folder)
|
||||
{
|
||||
_ = Task.Run(async () =>
|
||||
@@ -84,24 +57,13 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task SelectAndAddFolderAsync(UIElement sender)
|
||||
{
|
||||
var folder = await PickerHelper.PickSingleFolderAsync<SettingsWindow>();
|
||||
|
||||
if (folder != null)
|
||||
{
|
||||
AddFolderAsync(folder.Path);
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task AddRemoteSourceAsync(string protocolType)
|
||||
private async Task AddMediaSourceAsync(string protocolType)
|
||||
{
|
||||
var dialog = new ContentDialog
|
||||
{
|
||||
XamlRoot = WindowHook.GetWindow<SettingsWindow>()?.Content.XamlRoot,
|
||||
Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style,
|
||||
Title = protocolType,
|
||||
Title = protocolType == "Local" ? _localizationService.GetLocalizedString("MediaSettingsControlLocalFolder") : protocolType,
|
||||
PrimaryButtonText = _localizationService.GetLocalizedString("Add"),
|
||||
CloseButtonText = _localizationService.GetLocalizedString("Cancel"),
|
||||
DefaultButton = ContentDialogButton.Primary,
|
||||
@@ -111,50 +73,100 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
dialog.PrimaryButtonClick += async (s, e) =>
|
||||
{
|
||||
var configControl = (RemoteServerConfigControl)dialog.Content;
|
||||
|
||||
var deferral = e.GetDeferral();
|
||||
|
||||
e.Cancel = true;
|
||||
e.Cancel = true; // 默认阻止关闭,直到验证通过
|
||||
|
||||
dialog.IsPrimaryButtonEnabled = false;
|
||||
configControl.IsEnabled = false;
|
||||
configControl.SetProgressBarVisibility(Visibility.Visible);
|
||||
// 清除之前的错误信息
|
||||
configControl.ShowError(null);
|
||||
|
||||
var tempFolder = configControl.GetConfig();
|
||||
|
||||
bool isConnected = await Task.Run(async () =>
|
||||
try
|
||||
{
|
||||
try
|
||||
var tempFolder = configControl.GetConfig();
|
||||
|
||||
if (protocolType == "Local")
|
||||
{
|
||||
using var provider = tempFolder.CreateFileSystem();
|
||||
if (provider == null) return false;
|
||||
string path = tempFolder.UriPath;
|
||||
|
||||
return await provider.ConnectAsync();
|
||||
if (!System.IO.Directory.Exists(path))
|
||||
{
|
||||
throw new System.IO.DirectoryNotFoundException(_localizationService.GetLocalizedString("RemoteServerConfigControlPathNotExisted"));
|
||||
}
|
||||
|
||||
var normalizedPath = Path.GetFullPath(path).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;
|
||||
|
||||
// 是否完全重复
|
||||
if (AppSettings.LocalMediaFolders.Any(x => Path.GetFullPath(x.UriPath).TrimEnd(Path.DirectorySeparatorChar).Equals(normalizedPath.TrimEnd(Path.DirectorySeparatorChar), StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
configControl.ShowError(_localizationService.GetLocalizedString("SettingsPagePathExistedInfo"));
|
||||
deferral.Complete();
|
||||
return;
|
||||
}
|
||||
// 是否是子文件夹
|
||||
else if (AppSettings.LocalMediaFolders.Any(item => normalizedPath.StartsWith(Path.GetFullPath(item.UriPath).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
configControl.ShowError(_localizationService.GetLocalizedString("SettingsPagePathBeIncludedInfo"));
|
||||
deferral.Complete();
|
||||
return;
|
||||
}
|
||||
// 是否是父文件夹
|
||||
else if (AppSettings.LocalMediaFolders.Any(item => Path.GetFullPath(item.UriPath).TrimEnd(Path.DirectorySeparatorChar).StartsWith(normalizedPath, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
configControl.ShowError(_localizationService.GetLocalizedString("SettingsPagePathIncludingOthersInfo"));
|
||||
deferral.Complete();
|
||||
return;
|
||||
}
|
||||
|
||||
AppSettings.LocalMediaFolders.Add(tempFolder);
|
||||
_ = Task.Run(async () => await _fileSystemService.ScanMediaFolderAsync(tempFolder));
|
||||
|
||||
e.Cancel = false; // 允许关闭
|
||||
}
|
||||
catch (Exception ex)
|
||||
else
|
||||
{
|
||||
ShowErrorTip(configControl, ex.Message);
|
||||
return false;
|
||||
bool isConnected = await Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
using var provider = tempFolder.CreateFileSystem();
|
||||
if (provider == null) return false;
|
||||
return await provider.ConnectAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine(ex);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if (isConnected)
|
||||
{
|
||||
AppSettings.LocalMediaFolders.Add(tempFolder);
|
||||
PasswordVaultHelper.Save(Constants.App.AppName, tempFolder.VaultKey, tempFolder.Password);
|
||||
_ = Task.Run(async () => await _fileSystemService.ScanMediaFolderAsync(tempFolder));
|
||||
e.Cancel = false; // 允许关闭
|
||||
}
|
||||
else
|
||||
{
|
||||
configControl.ShowError(_localizationService.GetLocalizedString("SettingsPageServerTestFailedInfo"));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (isConnected)
|
||||
{
|
||||
AppSettings.LocalMediaFolders.Add(tempFolder);
|
||||
PasswordVaultHelper.Save(Constants.App.AppName, tempFolder.VaultKey, tempFolder.Password);
|
||||
|
||||
_ = Task.Run(async () => await _fileSystemService.ScanMediaFolderAsync(tempFolder));
|
||||
|
||||
e.Cancel = false;
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
ShowErrorTip(configControl, _localizationService.GetLocalizedString("SettingsPageServerTestFailedInfo"));
|
||||
configControl.ShowError(ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (e.Cancel)
|
||||
{
|
||||
dialog.IsPrimaryButtonEnabled = true;
|
||||
configControl.IsEnabled = true;
|
||||
configControl.SetProgressBarVisibility(Visibility.Collapsed);
|
||||
}
|
||||
}
|
||||
|
||||
dialog.IsPrimaryButtonEnabled = true;
|
||||
configControl.IsEnabled = true;
|
||||
configControl.SetProgressBarVisibility(Visibility.Collapsed);
|
||||
|
||||
deferral.Complete();
|
||||
};
|
||||
|
||||
@@ -34,7 +34,8 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
public partial class MusicGalleryPageViewModel : BaseViewModel,
|
||||
IRecipient<PropertyChangedMessage<DateTime?>>,
|
||||
IRecipient<PropertyChangedMessage<bool>>
|
||||
IRecipient<PropertyChangedMessage<bool>>,
|
||||
IRecipient<PropertyChangedMessage<string>>
|
||||
{
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly ILocalizationService _localizationService;
|
||||
@@ -51,9 +52,9 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
private IUnifiedFileSystem? _currentProvider;
|
||||
|
||||
// All songs
|
||||
private List<ExtendedTrack> _tracks = [];
|
||||
// Songs in current playlist
|
||||
private List<ExtendedTrack> _playlistTracks = [];
|
||||
private List<ExtendedTrack> _allTracks = [];
|
||||
// Songs in current playlist or songs in current file tree
|
||||
private List<ExtendedTrack> _middleTracks = [];
|
||||
// Filtered songs based on search query for current playlist
|
||||
private List<ExtendedTrack> _filteredTracks = [];
|
||||
|
||||
@@ -86,13 +87,10 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
[ObservableProperty]
|
||||
public partial CommonSongProperty SongOrderType { get; set; } = CommonSongProperty.Title;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial ObservableCollection<SongsTabInfo> SongsTabInfoList { get; set; } = [];
|
||||
|
||||
[ObservableProperty]
|
||||
public partial int SelectedSongsTabInfoIndex { get; set; } = 0;
|
||||
|
||||
public SongsTabInfo? SelectedSongsTabInfo => SongsTabInfoList.ElementAtOrDefault(SelectedSongsTabInfoIndex);
|
||||
public SongsTabInfo? SelectedSongsTabInfo => AppSettings.StarredPlaylists.ElementAtOrDefault(SelectedSongsTabInfoIndex);
|
||||
|
||||
[ObservableProperty] public partial bool IsDataLoading { get; set; } = false;
|
||||
|
||||
@@ -101,6 +99,8 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
[ObservableProperty]
|
||||
public partial string SongSearchQuery { get; set; } = string.Empty;
|
||||
|
||||
public ObservableCollection<FolderNode> FolderRoots { get; } = new();
|
||||
|
||||
public MusicGalleryPageViewModel(
|
||||
ISettingsService settingsService,
|
||||
ILocalizationService localizationService,
|
||||
@@ -118,8 +118,6 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
TrackPlayingQueue = [.. AppSettings.MusicGallerySettings.PlayQueuePaths.Select(x => new PlayQueueItem(new ExtendedTrack(x)))];
|
||||
TrackPlayingQueue.CollectionChanged += TrackPlayingQueue_CollectionChanged;
|
||||
|
||||
SongsTabInfoList.Add(new SongsTabInfo(_localizationService.GetLocalizedString("MusicGalleryPageAllSongs"), "\uE8A9", false, false, CommonSongProperty.Title, string.Empty));
|
||||
|
||||
RefreshSongs();
|
||||
|
||||
_settingsService.AppSettings.LocalMediaFolders.CollectionChanged += LocalMediaFolders_CollectionChanged;
|
||||
@@ -142,7 +140,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
private void TrackPlayingQueue_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
AppSettings.MusicGallerySettings.PlayQueuePaths = [.. TrackPlayingQueue.Select(x => x.Track.UriPath)];
|
||||
AppSettings.MusicGallerySettings.PlayQueuePaths = [.. TrackPlayingQueue.Select(x => x.Track.DecodedAbsoluteUri)];
|
||||
}
|
||||
|
||||
private void LocalMediaFolders_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
||||
@@ -286,6 +284,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
.ToList();
|
||||
|
||||
var cachedFiles = await _fileSystemService.GetParsedFilesAsync(enabledFolderIds);
|
||||
cachedFiles = cachedFiles.Where(x => FileHelper.MusicExtensions.Contains(Path.GetExtension(x.FileName))).ToList();
|
||||
|
||||
var newTrackList = cachedFiles
|
||||
.Select(x => new ExtendedTrack(x))
|
||||
@@ -293,7 +292,10 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
_tracks = newTrackList;
|
||||
_allTracks = newTrackList;
|
||||
|
||||
// 更新文件夹树
|
||||
RefreshTreeView();
|
||||
|
||||
// 应用过滤器
|
||||
ApplyPlaylist();
|
||||
@@ -313,23 +315,23 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
if (SelectedSongsTabInfo?.FilterValue == string.Empty)
|
||||
{
|
||||
_playlistTracks = _tracks;
|
||||
_middleTracks = _allTracks;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (SelectedSongsTabInfo?.FilterProperty)
|
||||
{
|
||||
case CommonSongProperty.Title:
|
||||
_playlistTracks = _tracks.Where(t => t.Title.Equals(SelectedSongsTabInfo.FilterValue, StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
_middleTracks = _allTracks.Where(t => t.Title.Equals(SelectedSongsTabInfo.FilterValue, StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
break;
|
||||
case CommonSongProperty.Album:
|
||||
_playlistTracks = _tracks.Where(t => t.Album.Equals(SelectedSongsTabInfo.FilterValue, StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
_middleTracks = _allTracks.Where(t => t.Album.Equals(SelectedSongsTabInfo.FilterValue, StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
break;
|
||||
case CommonSongProperty.Artist:
|
||||
_playlistTracks = _tracks.Where(t => t.Artist.Equals(SelectedSongsTabInfo.FilterValue, StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
_middleTracks = _allTracks.Where(t => t.Artist.Equals(SelectedSongsTabInfo.FilterValue, StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
break;
|
||||
case CommonSongProperty.Folder:
|
||||
_playlistTracks = _tracks.Where(t => t.ParentFolderPath.Equals(SelectedSongsTabInfo.FilterValue, StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
_middleTracks = _allTracks.Where(t => t.ParentFolderPath.Equals(SelectedSongsTabInfo.FilterValue, StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
break;
|
||||
case CommonSongProperty.M3UFilePath:
|
||||
if (SelectedSongsTabInfo.FilterValue is string path)
|
||||
@@ -337,11 +339,11 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
if (File.Exists(path))
|
||||
{
|
||||
var m3uFileContent = File.ReadAllText(path);
|
||||
_playlistTracks = _tracks.Where(t => m3uFileContent.Contains(t.UriPath)).ToList();
|
||||
_middleTracks = _allTracks.Where(t => m3uFileContent.Contains(t.DecodedAbsoluteUri)).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
_playlistTracks = [];
|
||||
_middleTracks = [];
|
||||
ToastHelper.ShowToast("PlaylistViewFailed", path, InfoBarSeverity.Success);
|
||||
}
|
||||
}
|
||||
@@ -359,10 +361,10 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(SongSearchQuery))
|
||||
{
|
||||
_filteredTracks = _playlistTracks;
|
||||
_filteredTracks = _middleTracks;
|
||||
return;
|
||||
}
|
||||
_filteredTracks = _playlistTracks.Where(t =>
|
||||
_filteredTracks = _middleTracks.Where(t =>
|
||||
t.Title.Contains(SongSearchQuery, StringComparison.OrdinalIgnoreCase) ||
|
||||
t.Artist.Contains(SongSearchQuery, StringComparison.OrdinalIgnoreCase) ||
|
||||
t.Album.Contains(SongSearchQuery, StringComparison.OrdinalIgnoreCase) ||
|
||||
@@ -403,17 +405,52 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateSelectedPlaylist(SongsTabInfo playlist)
|
||||
private void RefreshTreeView()
|
||||
{
|
||||
var found = SongsTabInfoList.FirstOrDefault(x => x.FilterProperty == playlist.FilterProperty && x.FilterValue == playlist.FilterValue);
|
||||
var roots = FolderTreeBuilder.Build(_allTracks, AppSettings.LocalMediaFolders.ToList());
|
||||
|
||||
FolderRoots.Clear();
|
||||
foreach (var r in roots) FolderRoots.Add(r);
|
||||
}
|
||||
|
||||
public void SelectFolder(FolderNode? folder)
|
||||
{
|
||||
if (folder == null) return;
|
||||
if (_allTracks == null) return;
|
||||
|
||||
string baseUri = folder.FolderPath;
|
||||
if (!baseUri.EndsWith("/")) baseUri += "/";
|
||||
|
||||
_middleTracks = _allTracks.Where(track =>
|
||||
{
|
||||
if (track.MediaFolderId != folder.MediaFolderId) return false;
|
||||
|
||||
string trackUriDecoded = System.Net.WebUtility.UrlDecode(track.Uri);
|
||||
|
||||
if (!trackUriDecoded.StartsWith(baseUri, StringComparison.OrdinalIgnoreCase)) return false;
|
||||
|
||||
string relativePart = trackUriDecoded.Substring(baseUri.Length);
|
||||
|
||||
return !relativePart.Contains('/');
|
||||
}).ToList();
|
||||
|
||||
ApplySongSearchQuery();
|
||||
IsLocalMediaNotFound = !_filteredTracks.Any();
|
||||
ApplySongOrderType();
|
||||
}
|
||||
|
||||
public void AddToPlaylists(SongsTabInfo playlist)
|
||||
{
|
||||
var starredPlaylists = AppSettings.StarredPlaylists;
|
||||
var found = starredPlaylists.FirstOrDefault(x => x.FilterProperty == playlist.FilterProperty && x.FilterValue == playlist.FilterValue);
|
||||
if (found == null)
|
||||
{
|
||||
SongsTabInfoList.Add(playlist);
|
||||
SelectedSongsTabInfoIndex = SongsTabInfoList.Count - 1;
|
||||
starredPlaylists.Add(playlist);
|
||||
SelectedSongsTabInfoIndex = starredPlaylists.Count - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedSongsTabInfoIndex = SongsTabInfoList.IndexOf(found);
|
||||
SelectedSongsTabInfoIndex = starredPlaylists.IndexOf(found);
|
||||
}
|
||||
ApplyPlaylist();
|
||||
}
|
||||
@@ -428,6 +465,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
_timelineController.Pause();
|
||||
_mediaPlayer.Source = null;
|
||||
|
||||
// 清理旧资源
|
||||
_currentStream?.Dispose();
|
||||
_currentNetStream?.Dispose();
|
||||
_currentStream = null;
|
||||
@@ -445,50 +483,66 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
try
|
||||
{
|
||||
// ★ 1. 查找对应的 MediaFolder 配置
|
||||
// 现在的 PlayingTrack.Uri 是标准的完整 URI (例如 smb://host/share/file.mp3)
|
||||
// 我们通过对比前缀来找到它属于哪个 MediaFolder
|
||||
var targetFolder = _settingsService.AppSettings.LocalMediaFolders.FirstOrDefault(f =>
|
||||
PlayingTrack.Uri.StartsWith(f.GetStandardUri().AbsoluteUri, StringComparison.OrdinalIgnoreCase));
|
||||
{
|
||||
var fUri = f.GetStandardUri().AbsoluteUri;
|
||||
return PlayingTrack.Uri.StartsWith(fUri, StringComparison.OrdinalIgnoreCase);
|
||||
});
|
||||
|
||||
if (targetFolder == null)
|
||||
{
|
||||
|
||||
throw new Exception($"找不到文件 {PlayingTrack.FileName} 对应的存储配置。请检查服务器设置是否已启用。");
|
||||
}
|
||||
|
||||
// ★ 2. 创建 Provider 并连接
|
||||
_currentProvider = targetFolder.CreateFileSystem();
|
||||
if (_currentProvider == null) return;
|
||||
|
||||
await _currentProvider.ConnectAsync();
|
||||
|
||||
// ★ 3. 构造实体对象进行读取
|
||||
// FileSystemService.OpenFileAsync 现在只需要 entity.Uri 就能工作
|
||||
var fileCacheStub = new FileCacheEntity
|
||||
{
|
||||
Uri = PlayingTrack.Uri
|
||||
};
|
||||
|
||||
_currentNetStream = await _fileSystemService.OpenFileAsync(_currentProvider, fileCacheStub);
|
||||
var sourceStream = await _fileSystemService.OpenFileAsync(_currentProvider, fileCacheStub);
|
||||
|
||||
if (sourceStream == null)
|
||||
{
|
||||
throw new FileNotFoundException("无法打开文件流");
|
||||
}
|
||||
|
||||
if (sourceStream.CanSeek)
|
||||
{
|
||||
_currentNetStream = sourceStream;
|
||||
}
|
||||
else
|
||||
{
|
||||
var memStream = new MemoryStream();
|
||||
|
||||
await sourceStream.CopyToAsync(memStream);
|
||||
memStream.Position = 0;
|
||||
|
||||
sourceStream.Dispose();
|
||||
|
||||
_currentNetStream = memStream;
|
||||
}
|
||||
|
||||
_currentStream = _currentNetStream.AsRandomAccessStream();
|
||||
|
||||
// 获取 MIME 类型 (使用 FileName 或 Uri 都可以)
|
||||
string contentType = GetMimeType(PlayingTrack.FileName);
|
||||
var mediaSource = MediaSource.CreateFromStream(_currentStream, contentType);
|
||||
|
||||
_mediaPlayer.Source = mediaSource;
|
||||
|
||||
// --- SMTC 更新逻辑 (基本保持不变) ---
|
||||
var updater = _smtc.DisplayUpdater;
|
||||
updater.Type = MediaPlaybackType.Music;
|
||||
|
||||
updater.MusicProperties.Title = PlayingTrack.Title ?? PlayingTrack.FileName;
|
||||
updater.MusicProperties.Artist = PlayingTrack.Artist ?? "Unknown Artist";
|
||||
updater.MusicProperties.Artist = PlayingTrack.Artist ?? "";
|
||||
updater.MusicProperties.AlbumTitle = PlayingTrack.Album ?? "";
|
||||
|
||||
updater.MusicProperties.Genres.Clear();
|
||||
// 注意:这里改用 FileName 获取文件名,因为 UriPath 已被移除
|
||||
updater.MusicProperties.Genres.Add($"{ExtendedGenreFiled.FileName}{Path.GetFileNameWithoutExtension(PlayingTrack.FileName)}");
|
||||
|
||||
updater.AppMediaId = Package.Current.Id.FullName;
|
||||
@@ -507,10 +561,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 建议:播放失败时弹个 Toast 或者在 UI 上显示错误
|
||||
System.Diagnostics.Debug.WriteLine($"PlayTrackAsync Error: {ex.Message}");
|
||||
|
||||
// 自动跳过或停止
|
||||
ToastHelper.ShowToast($"PlayTrackAsync: Error", ex.Message, InfoBarSeverity.Error);
|
||||
_timelineController.Pause();
|
||||
}
|
||||
}
|
||||
@@ -552,8 +603,6 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
FilterProperty = CommonSongProperty.M3UFilePath,
|
||||
FilterValue = file.Path,
|
||||
Icon = "\uE7BC",
|
||||
IsStarred = true,
|
||||
IsClosable = true,
|
||||
Name = file.Name
|
||||
});
|
||||
}
|
||||
@@ -619,5 +668,16 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<string> message)
|
||||
{
|
||||
if (message.Sender is MediaFolder)
|
||||
{
|
||||
if (message.PropertyName == nameof(MediaFolder.Name))
|
||||
{
|
||||
RefreshTreeView();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,12 +29,169 @@
|
||||
</Page.Resources>
|
||||
|
||||
<Grid>
|
||||
<Grid Padding="12,0,12,64" ColumnSpacing="12">
|
||||
<Grid Padding="12,8,12,64" ColumnSpacing="12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="3*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid x:Name="SongViewer" Grid.Column="0">
|
||||
|
||||
<ScrollViewer Grid.Column="0">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock
|
||||
x:Uid="MusicGalleryPagePlaylist"
|
||||
Grid.Row="0"
|
||||
Margin="1,4,0,6"
|
||||
Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<!-- 播放列表 -->
|
||||
<ListView
|
||||
x:Name="StarredPlaylistsListView"
|
||||
Grid.Row="0"
|
||||
ItemsSource="{x:Bind ViewModel.AppSettings.StarredPlaylists, Mode=OneWay}"
|
||||
SelectedIndex="{x:Bind ViewModel.SelectedSongsTabInfoIndex, Mode=TwoWay}">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="models:SongsTabInfo">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid
|
||||
Grid.Column="0"
|
||||
ColumnSpacing="6"
|
||||
Tapped="PlaylistGrid_Tapped">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<FontIcon
|
||||
Grid.Column="0"
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="16"
|
||||
Glyph="{x:Bind Icon}" />
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Margin="0,8"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
Text="{x:Bind Name}"
|
||||
TextWrapping="Wrap" />
|
||||
</Grid>
|
||||
<!-- 从播放列表移除 -->
|
||||
<Button
|
||||
Grid.Column="1"
|
||||
Click="RemoveFromPlaylistButton_Click"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
FontSize=16,
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}"
|
||||
Visibility="{x:Bind IsDefault, Converter={StaticResource BoolNegationToVisibilityConverter}}">
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock x:Uid="MusicGalleryPageRemoveFromCustomList" />
|
||||
</ToolTipService.ToolTip>
|
||||
</Button>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
<NavigationViewItemSeparator Grid.Row="1" />
|
||||
<!-- 命令区域 -->
|
||||
<Grid Grid.Row="2">
|
||||
<StackPanel>
|
||||
<!-- 创建播放列表 -->
|
||||
<Button
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
Background="Transparent"
|
||||
BorderBrush="Transparent"
|
||||
Command="{x:Bind ViewModel.CreatePlaylistCommand}">
|
||||
<Button.Content>
|
||||
<StackPanel
|
||||
Padding="4,0"
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal"
|
||||
Spacing="6">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="16"
|
||||
Glyph="" />
|
||||
<TextBlock x:Uid="MusicGalleryPageNewPlaylist" />
|
||||
</StackPanel>
|
||||
</Button.Content>
|
||||
</Button>
|
||||
<!-- 导入播放列表 -->
|
||||
<Button
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
Background="Transparent"
|
||||
BorderBrush="Transparent"
|
||||
Command="{x:Bind ViewModel.ImportPlaylistCommand}">
|
||||
<Button.Content>
|
||||
<StackPanel
|
||||
Padding="4,0"
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal"
|
||||
Spacing="6">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="16"
|
||||
Glyph="" />
|
||||
<TextBlock x:Uid="MusicGalleryPageImportFromFile" />
|
||||
</StackPanel>
|
||||
</Button.Content>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<NavigationViewItemSeparator Grid.Row="2" />
|
||||
|
||||
<TextBlock
|
||||
x:Uid="MusicGalleryPageFolder"
|
||||
Grid.Row="3"
|
||||
Margin="1,4,0,6"
|
||||
Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<TreeView
|
||||
x:Name="FolderTreeView"
|
||||
Grid.Row="4"
|
||||
AllowDrop="False"
|
||||
CanDragItems="False"
|
||||
ItemInvoked="FolderTreeView_ItemInvoked"
|
||||
ItemsSource="{x:Bind ViewModel.FolderRoots, Mode=OneWay}"
|
||||
SelectionMode="Single">
|
||||
|
||||
<TreeView.ItemTemplate>
|
||||
<DataTemplate x:DataType="models:FolderNode">
|
||||
<TreeViewItem IsExpanded="{x:Bind IsExpanded, Mode=TwoWay}" ItemsSource="{x:Bind SubFolders}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="10">
|
||||
<FontIcon FontSize="16" Glyph="{x:Bind SourceType, Converter={StaticResource FileSourceTypeToIconConverter}}" />
|
||||
<TextBlock VerticalAlignment="Center" Text="{x:Bind FolderName}" />
|
||||
</StackPanel>
|
||||
</TreeViewItem>
|
||||
</DataTemplate>
|
||||
</TreeView.ItemTemplate>
|
||||
</TreeView>
|
||||
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
|
||||
<Grid x:Name="SongViewer" Grid.Column="1">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
@@ -78,10 +235,7 @@
|
||||
<uc:PropertyRow x:Uid="MusicGalleryPageFileInfoBitDepth" Value="{x:Bind ViewModel.TrackRightTapped.BitDepth, Mode=OneWay}" />
|
||||
<uc:PropertyRow x:Uid="MusicGalleryPageFileInfoFormat" Value="{x:Bind ViewModel.TrackRightTapped.AudioFormatName, Mode=OneWay}" />
|
||||
<uc:PropertyRow x:Uid="MusicGalleryPageFileInfoEncoder" Value="{x:Bind ViewModel.TrackRightTapped.Encoder, Mode=OneWay}" />
|
||||
<uc:PropertyRow
|
||||
x:Uid="MusicGalleryPageFileInfoPath"
|
||||
Link="{x:Bind ViewModel.TrackRightTapped.Uri, Mode=OneWay}"
|
||||
Value="{x:Bind ViewModel.TrackRightTapped.Uri, Mode=OneWay}" />
|
||||
<uc:PropertyRow x:Uid="MusicGalleryPageFileInfoPath" Value="{x:Bind ViewModel.TrackRightTapped.DecodedAbsoluteUri, Mode=OneWay}" />
|
||||
<uc:PropertyRow x:Uid="MusicGalleryPageFileInfoLyrics" Value="{x:Bind ViewModel.TrackRightTapped.RawLyrics, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
@@ -91,199 +245,9 @@
|
||||
|
||||
<StackPanel Grid.Row="0" Spacing="6">
|
||||
|
||||
<Grid VerticalAlignment="Top">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button
|
||||
x:Name="PlaylistButton"
|
||||
Grid.Column="0"
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<Button.Content>
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="16"
|
||||
Glyph="" />
|
||||
<TextBlock x:Uid="MusicGalleryPagePlaylist" />
|
||||
</StackPanel>
|
||||
</Button.Content>
|
||||
<Button.Flyout>
|
||||
<Flyout FlyoutPresenterStyle="{StaticResource FlyoutGhostStyle}">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Button
|
||||
Grid.Row="0"
|
||||
Margin="4"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
Background="Transparent"
|
||||
BorderBrush="Transparent"
|
||||
Command="{x:Bind ViewModel.CreatePlaylistCommand}">
|
||||
<Button.Content>
|
||||
<StackPanel
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal"
|
||||
Spacing="6">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="16"
|
||||
Glyph="" />
|
||||
<TextBlock x:Uid="MusicGalleryPageNewPlaylist" />
|
||||
</StackPanel>
|
||||
</Button.Content>
|
||||
</Button>
|
||||
<Button
|
||||
Grid.Row="1"
|
||||
Margin="4,1,4,2"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
Background="Transparent"
|
||||
BorderBrush="Transparent"
|
||||
Command="{x:Bind ViewModel.ImportPlaylistCommand}">
|
||||
<Button.Content>
|
||||
<StackPanel
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal"
|
||||
Spacing="6">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="16"
|
||||
Glyph="" />
|
||||
<TextBlock x:Uid="MusicGalleryPageImportFromFile" />
|
||||
</StackPanel>
|
||||
</Button.Content>
|
||||
</Button>
|
||||
<ListView
|
||||
x:Name="StarredPlaylistsListView"
|
||||
Grid.Row="2"
|
||||
ItemsSource="{x:Bind ViewModel.AppSettings.StarredPlaylists, Mode=OneWay}">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="models:SongsTabInfo">
|
||||
<Grid Tapped="StarredPlaylistsListViewItemGrid_Tapped">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="16"
|
||||
Glyph="{x:Bind Icon}" />
|
||||
<TextBlock
|
||||
Margin="0,0,0,2"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
Text="{x:Bind Name}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</Grid>
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
|
||||
<ListView
|
||||
Grid.Column="1"
|
||||
ItemContainerStyle="{StaticResource ListViewStretchedItemContainerStyle}"
|
||||
ItemsSource="{x:Bind ViewModel.SongsTabInfoList, Mode=OneWay}"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
|
||||
ScrollViewer.HorizontalScrollMode="Enabled"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Disabled"
|
||||
ScrollViewer.VerticalScrollMode="Disabled"
|
||||
SelectedIndex="{x:Bind ViewModel.SelectedSongsTabInfoIndex, Mode=TwoWay}">
|
||||
<ListView.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<ItemsStackPanel Orientation="Horizontal" />
|
||||
</ItemsPanelTemplate>
|
||||
</ListView.ItemsPanel>
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="models:SongsTabInfo">
|
||||
<Grid Padding="12,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="1*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button
|
||||
Grid.Column="0"
|
||||
Click="PlaylistFavButton_Click"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Style="{StaticResource GhostButtonStyle}"
|
||||
Visibility="{x:Bind IsClosable, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}">
|
||||
<ToolTipService.ToolTip>
|
||||
<Grid>
|
||||
<TextBlock x:Uid="MusicGalleryPageAddToCustomList" Visibility="{x:Bind IsStarred, Converter={StaticResource BoolNegationToVisibilityConverter}, Mode=OneWay}" />
|
||||
<TextBlock x:Uid="MusicGalleryPageRemoveFromCustomList" Visibility="{x:Bind IsStarred, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}" />
|
||||
</Grid>
|
||||
</ToolTipService.ToolTip>
|
||||
<Button.Content>
|
||||
<Grid>
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="16"
|
||||
Glyph=""
|
||||
Opacity="{x:Bind IsStarred, Converter={StaticResource BoolNegationToOpacityConverter}, Mode=OneWay}">
|
||||
<FontIcon.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</FontIcon.OpacityTransition>
|
||||
</FontIcon>
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="16"
|
||||
Glyph=""
|
||||
Opacity="{x:Bind IsStarred, Converter={StaticResource BoolToOpacityConverter}, Mode=OneWay}">
|
||||
<FontIcon.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</FontIcon.OpacityTransition>
|
||||
</FontIcon>
|
||||
</Grid>
|
||||
</Button.Content>
|
||||
</Button>
|
||||
<Grid
|
||||
Grid.Column="1"
|
||||
Background="Transparent"
|
||||
ColumnSpacing="6"
|
||||
Tapped="PlaylistGrid_Tapped">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<FontIcon
|
||||
Grid.Column="0"
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="16"
|
||||
Glyph="{x:Bind Icon}" />
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Margin="0,0,0,2"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
Text="{x:Bind Name}" />
|
||||
</Grid>
|
||||
<Button
|
||||
Grid.Column="2"
|
||||
Click="PlaylistCloseButton_Click"
|
||||
Content="{ui:FontIcon FontSize=16,
|
||||
FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Style="{StaticResource GhostButtonStyle}"
|
||||
Visibility="{x:Bind IsClosable, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</Grid>
|
||||
|
||||
<AutoSuggestBox
|
||||
x:Name="SongSearchBox"
|
||||
x:Uid="MusicGalleryPageSongSearchBox"
|
||||
Margin="0,-8,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
QueryIcon="Find"
|
||||
Text="{x:Bind ViewModel.SongSearchQuery, Mode=TwoWay}" />
|
||||
@@ -388,70 +352,53 @@
|
||||
DoubleTapped="SongListViewItem_DoubleTapped">
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1*" />
|
||||
<ColumnDefinition Width="4*" />
|
||||
<ColumnDefinition Width="4*" />
|
||||
<ColumnDefinition Width="2*" />
|
||||
<ColumnDefinition Width="2*" />
|
||||
<ColumnDefinition Width="2*" />
|
||||
<ColumnDefinition Width="1*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid
|
||||
Grid.Column="0"
|
||||
MaxWidth="48"
|
||||
MaxHeight="48"
|
||||
CornerRadius="4">
|
||||
<Image Source="{x:Bind LocalAlbumArtPath, Mode=OneWay, Converter={StaticResource PathToImageConverter}}" Stretch="Uniform" />
|
||||
<!-- 标题 -->
|
||||
<Grid Grid.Column="0">
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Bind Title}"
|
||||
TextWrapping="Wrap" />
|
||||
</Grid>
|
||||
|
||||
<!-- 基本信息 -->
|
||||
<!-- 艺术家 -->
|
||||
<Grid Grid.Column="1">
|
||||
<StackPanel VerticalAlignment="Center" Spacing="6">
|
||||
<TextBlock Text="{x:Bind Title}" TextWrapping="Wrap" />
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<Grid Background="{ThemeResource AccentAcrylicBackgroundFillColorBaseBrush}" CornerRadius="4">
|
||||
<TextBlock
|
||||
Margin="4,2"
|
||||
FontSize="12"
|
||||
Text="{x:Bind AudioFormatShortName}" />
|
||||
</Grid>
|
||||
<HyperlinkButton Padding="0" Click="ArtistHyperlibkButton_Click">
|
||||
<TextBlock Text="{x:Bind Artist}" TextWrapping="Wrap" />
|
||||
</HyperlinkButton>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<HyperlinkButton Click="ArtistHyperlibkButton_Click">
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock x:Uid="MusicGalleryPageAddToCustomList" />
|
||||
</ToolTipService.ToolTip>
|
||||
<TextBlock Text="{x:Bind Artist}" TextWrapping="Wrap" />
|
||||
</HyperlinkButton>
|
||||
</Grid>
|
||||
|
||||
<HyperlinkButton Grid.Column="2" Click="AlbumHyperlibkButton_Click">
|
||||
<TextBlock Text="{x:Bind Album}" TextWrapping="Wrap" />
|
||||
</HyperlinkButton>
|
||||
|
||||
<!-- 年份 -->
|
||||
<TextBlock
|
||||
Grid.Column="3"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="{x:Bind Year}"
|
||||
TextWrapping="Wrap" />
|
||||
<!-- 专辑 -->
|
||||
<Grid Grid.Column="2">
|
||||
<HyperlinkButton Click="AlbumHyperlibkButton_Click">
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock x:Uid="MusicGalleryPageAddToCustomList" />
|
||||
</ToolTipService.ToolTip>
|
||||
<TextBlock Text="{x:Bind Album}" TextWrapping="Wrap" />
|
||||
</HyperlinkButton>
|
||||
</Grid>
|
||||
|
||||
<!-- 歌曲时长 -->
|
||||
<TextBlock
|
||||
Grid.Column="4"
|
||||
Grid.Column="3"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="{x:Bind Duration, Converter={StaticResource SecondsToFormattedTimeConverter}}"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<!-- 路径 -->
|
||||
<HyperlinkButton
|
||||
Grid.Column="5"
|
||||
VerticalAlignment="Center"
|
||||
Click="PathHyperlibkButton_Click"
|
||||
Content="{x:Bind ParentFolderName}" />
|
||||
|
||||
<!-- 更多 -->
|
||||
<Button
|
||||
Grid.Column="6"
|
||||
Grid.Column="4"
|
||||
HorizontalAlignment="Right"
|
||||
Click="SongListViewItemMoreButton_Click"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
FontSize=12,
|
||||
@@ -515,136 +462,134 @@
|
||||
|
||||
</Grid>
|
||||
|
||||
<Grid x:Name="PlayQueue" Grid.Column="1">
|
||||
<Grid x:Name="PlayQueue" Grid.Column="2">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel Grid.Row="0" Spacing="6">
|
||||
<Grid ColumnSpacing="3">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
x:Uid="MusicGalleryPagePlayingQueue"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource BodyStrongTextBlockStyle}" />
|
||||
<StackPanel
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind ViewModel.AppSettings.MusicGallerySettings.PlayQueueIndex, Mode=OneWay, Converter={StaticResource IndexToDisplayConverter}}" />
|
||||
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="/" />
|
||||
<TextBlock Text="{x:Bind ViewModel.TrackPlayingQueue.Count, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
<Grid Grid.Row="0">
|
||||
<TextBlock
|
||||
x:Uid="MusicGalleryPagePlayingQueue"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource BodyStrongTextBlockStyle}" />
|
||||
</Grid>
|
||||
|
||||
<!-- Stop media session -->
|
||||
<Button
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Right"
|
||||
Command="{x:Bind ViewModel.StopTrackCommand}"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
FontSize=16,
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock x:Uid="MusicGalleryPageStopTrack" />
|
||||
</ToolTipService.ToolTip>
|
||||
</Button>
|
||||
<Grid Grid.Row="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Playback order -->
|
||||
<Button
|
||||
Grid.Column="3"
|
||||
HorizontalAlignment="Right"
|
||||
Command="{x:Bind ViewModel.SwitchPlaybackOrderCommand}"
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip>
|
||||
<Grid>
|
||||
<TextBlock x:Name="PlaybackRepeatAllHint" x:Uid="MusicGalleryPageQueueLoop" />
|
||||
<TextBlock x:Name="PlaybackRepeatOneHint" x:Uid="MusicGalleryPageSingleLoop" />
|
||||
<TextBlock x:Name="PlaybackShuffleHint" x:Uid="MusicGalleryPageQueueRandom" />
|
||||
</Grid>
|
||||
</ToolTip>
|
||||
</ToolTipService.ToolTip>
|
||||
<Button.Content>
|
||||
<StackPanel
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind ViewModel.AppSettings.MusicGallerySettings.PlayQueueIndex, Mode=OneWay, Converter={StaticResource IndexToDisplayConverter}}" />
|
||||
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="/" />
|
||||
<TextBlock Text="{x:Bind ViewModel.TrackPlayingQueue.Count, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Stop media session -->
|
||||
<Button
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Right"
|
||||
Command="{x:Bind ViewModel.StopTrackCommand}"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
FontSize=16,
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock x:Uid="MusicGalleryPageStopTrack" />
|
||||
</ToolTipService.ToolTip>
|
||||
</Button>
|
||||
<!-- Playback order -->
|
||||
<Button
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Right"
|
||||
Command="{x:Bind ViewModel.SwitchPlaybackOrderCommand}"
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip>
|
||||
<Grid>
|
||||
<!-- Repeat all -->
|
||||
<FontIcon
|
||||
x:Name="PlaybackRepeatAll"
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="16"
|
||||
Glyph="">
|
||||
<FontIcon.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</FontIcon.OpacityTransition>
|
||||
</FontIcon>
|
||||
<!-- Repeat one -->
|
||||
<FontIcon
|
||||
x:Name="PlaybackRepeatOne"
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="16"
|
||||
Glyph="">
|
||||
<FontIcon.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</FontIcon.OpacityTransition>
|
||||
</FontIcon>
|
||||
<!-- Shuffle -->
|
||||
<FontIcon
|
||||
x:Name="PlaybackShuffle"
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="16"
|
||||
Glyph="">
|
||||
<FontIcon.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</FontIcon.OpacityTransition>
|
||||
</FontIcon>
|
||||
<TextBlock x:Name="PlaybackRepeatAllHint" x:Uid="MusicGalleryPageQueueLoop" />
|
||||
<TextBlock x:Name="PlaybackRepeatOneHint" x:Uid="MusicGalleryPageSingleLoop" />
|
||||
<TextBlock x:Name="PlaybackShuffleHint" x:Uid="MusicGalleryPageQueueRandom" />
|
||||
</Grid>
|
||||
</Button.Content>
|
||||
</Button>
|
||||
|
||||
<!-- Scroll to playing item -->
|
||||
<Button
|
||||
Grid.Column="4"
|
||||
HorizontalAlignment="Right"
|
||||
Click="ScrollToPlayingItemButton_Click"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
FontSize=16,
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock x:Uid="MusicGalleryPageScrollToPlayingItem" />
|
||||
</ToolTipService.ToolTip>
|
||||
</Button>
|
||||
|
||||
<!-- Empty play queue -->
|
||||
<Button
|
||||
Grid.Column="5"
|
||||
HorizontalAlignment="Right"
|
||||
Click="EmptyPlayingQueueButton_Click"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
FontSize=16,
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock x:Uid="MusicGalleryPageEmptyPlayingQueue" />
|
||||
</ToolTipService.ToolTip>
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
</StackPanel>
|
||||
</ToolTip>
|
||||
</ToolTipService.ToolTip>
|
||||
<Button.Content>
|
||||
<Grid>
|
||||
<!-- Repeat all -->
|
||||
<FontIcon
|
||||
x:Name="PlaybackRepeatAll"
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="16"
|
||||
Glyph="">
|
||||
<FontIcon.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</FontIcon.OpacityTransition>
|
||||
</FontIcon>
|
||||
<!-- Repeat one -->
|
||||
<FontIcon
|
||||
x:Name="PlaybackRepeatOne"
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="16"
|
||||
Glyph="">
|
||||
<FontIcon.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</FontIcon.OpacityTransition>
|
||||
</FontIcon>
|
||||
<!-- Shuffle -->
|
||||
<FontIcon
|
||||
x:Name="PlaybackShuffle"
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="16"
|
||||
Glyph="">
|
||||
<FontIcon.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</FontIcon.OpacityTransition>
|
||||
</FontIcon>
|
||||
</Grid>
|
||||
</Button.Content>
|
||||
</Button>
|
||||
<!-- Scroll to playing item -->
|
||||
<Button
|
||||
Grid.Column="3"
|
||||
HorizontalAlignment="Right"
|
||||
Click="ScrollToPlayingItemButton_Click"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
FontSize=16,
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock x:Uid="MusicGalleryPageScrollToPlayingItem" />
|
||||
</ToolTipService.ToolTip>
|
||||
</Button>
|
||||
<!-- Empty play queue -->
|
||||
<Button
|
||||
Grid.Column="4"
|
||||
HorizontalAlignment="Right"
|
||||
Click="EmptyPlayingQueueButton_Click"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
FontSize=16,
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock x:Uid="MusicGalleryPageEmptyPlayingQueue" />
|
||||
</ToolTipService.ToolTip>
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<ListView
|
||||
x:Name="PlayingQueueListView"
|
||||
Grid.Row="1"
|
||||
Grid.Row="3"
|
||||
ItemsSource="{x:Bind ViewModel.TrackPlayingQueue, Mode=OneWay}"
|
||||
SelectedIndex="{x:Bind ViewModel.AppSettings.MusicGallerySettings.PlayQueueIndex, Mode=TwoWay}">
|
||||
<ListView.ItemTemplate>
|
||||
@@ -676,7 +621,7 @@
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
|
||||
<Grid Grid.Row="1">
|
||||
<Grid Grid.Row="3">
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.TrackPlayingQueue.Count, Mode=OneWay}"
|
||||
@@ -703,6 +648,7 @@
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
|
||||
<Grid Background="{ThemeResource SolidBackgroundFillColorBaseBrush}" Visibility="{x:Bind ViewModel.IsDataLoading, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace BetterLyrics.WinUI3.Views
|
||||
|
||||
private async void SongPathHyperlinkButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await LauncherHelper.SelectAndShowFile(((ExtendedTrack)((HyperlinkButton)sender).DataContext).UriPath);
|
||||
await LauncherHelper.SelectAndShowFile(((ExtendedTrack)((HyperlinkButton)sender).DataContext).DecodedAbsoluteUri);
|
||||
}
|
||||
|
||||
private async void PlayingQueueListVireItemGrid_Tapped(object sender, TappedRoutedEventArgs e)
|
||||
@@ -140,36 +140,47 @@ namespace BetterLyrics.WinUI3.Views
|
||||
private void ArtistHyperlibkButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var artist = ((ExtendedTrack)((FrameworkElement)sender).DataContext).Artist;
|
||||
var playlist = new SongsTabInfo(artist, "\uEFA9", true, false, CommonSongProperty.Artist, artist);
|
||||
ViewModel.UpdateSelectedPlaylist(playlist);
|
||||
var playlist = new SongsTabInfo
|
||||
{
|
||||
Name = artist,
|
||||
Icon = "\uEFA9",
|
||||
FilterProperty = CommonSongProperty.Artist,
|
||||
FilterValue = artist
|
||||
};
|
||||
ViewModel.AddToPlaylists(playlist);
|
||||
}
|
||||
|
||||
private void AlbumHyperlibkButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var album = ((ExtendedTrack)((FrameworkElement)sender).DataContext).Album;
|
||||
var playlist = new SongsTabInfo(album, "\uE93C", true, false, CommonSongProperty.Album, album);
|
||||
ViewModel.UpdateSelectedPlaylist(playlist);
|
||||
var playlist = new SongsTabInfo
|
||||
{
|
||||
Name = album,
|
||||
Icon = "\uE93C",
|
||||
FilterProperty = CommonSongProperty.Album,
|
||||
FilterValue = album
|
||||
};
|
||||
ViewModel.AddToPlaylists(playlist);
|
||||
}
|
||||
|
||||
private void PathHyperlibkButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var track = ((ExtendedTrack)((FrameworkElement)sender).DataContext);
|
||||
var playlist = new SongsTabInfo(track.ParentFolderName, "\uE8B7", true, false, CommonSongProperty.Folder, track.ParentFolderPath);
|
||||
ViewModel.UpdateSelectedPlaylist(playlist);
|
||||
var playlist = new SongsTabInfo
|
||||
{
|
||||
Name = track.ParentFolderName,
|
||||
Icon = "\uE8B7",
|
||||
FilterProperty = CommonSongProperty.Folder,
|
||||
FilterValue = track.ParentFolderPath
|
||||
};
|
||||
ViewModel.AddToPlaylists(playlist);
|
||||
}
|
||||
|
||||
private void PlaylistGrid_Tapped(object sender, TappedRoutedEventArgs e)
|
||||
{
|
||||
FolderTreeView.SelectedItem = null;
|
||||
var playlist = (SongsTabInfo)((FrameworkElement)sender).DataContext;
|
||||
ViewModel.UpdateSelectedPlaylist(playlist);
|
||||
}
|
||||
|
||||
private void PlaylistCloseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var playlist = (SongsTabInfo)((FrameworkElement)sender).DataContext;
|
||||
ViewModel.SongsTabInfoList.Remove(playlist);
|
||||
ViewModel.SelectedSongsTabInfoIndex = 0;
|
||||
ViewModel.ApplyPlaylist();
|
||||
ViewModel.AddToPlaylists(playlist);
|
||||
}
|
||||
|
||||
private void Page_Unloaded(object sender, RoutedEventArgs e)
|
||||
@@ -181,28 +192,12 @@ namespace BetterLyrics.WinUI3.Views
|
||||
}
|
||||
}
|
||||
|
||||
private void PlaylistFavButton_Click(object sender, RoutedEventArgs e)
|
||||
private void RemoveFromPlaylistButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var playlist = (SongsTabInfo)((FrameworkElement)sender).DataContext;
|
||||
var targetStatus = !playlist.IsStarred;
|
||||
if (targetStatus)
|
||||
{
|
||||
ViewModel.AppSettings.StarredPlaylists.Add(playlist);
|
||||
}
|
||||
else
|
||||
{
|
||||
ViewModel.AppSettings.StarredPlaylists.Remove(playlist);
|
||||
}
|
||||
playlist.IsStarred = targetStatus;
|
||||
}
|
||||
|
||||
private void StarredPlaylistsListViewItemGrid_Tapped(object sender, TappedRoutedEventArgs e)
|
||||
{
|
||||
var songsTabInfo = ((SongsTabInfo)((FrameworkElement)sender).DataContext);
|
||||
if (!ViewModel.SongsTabInfoList.Contains(songsTabInfo))
|
||||
{
|
||||
ViewModel.SongsTabInfoList.Add(songsTabInfo);
|
||||
}
|
||||
ViewModel.AppSettings.StarredPlaylists.Remove(playlist);
|
||||
ViewModel.SelectedSongsTabInfoIndex = 0;
|
||||
ViewModel.ApplyPlaylist();
|
||||
}
|
||||
|
||||
private void SongListViewItemMoreButton_Click(object sender, RoutedEventArgs e)
|
||||
@@ -223,7 +218,7 @@ namespace BetterLyrics.WinUI3.Views
|
||||
|
||||
private void AddToPlaylistMenuFlyoutItem_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
((MenuFlyoutItem)sender).ContextFlyout.ShowAt(PlaylistButton);
|
||||
//((MenuFlyoutItem)sender).ContextFlyout.ShowAt(PlaylistButton);
|
||||
}
|
||||
|
||||
private void ToBeAddedPlaylistsListViewItemGrid_Tapped(object sender, TappedRoutedEventArgs e)
|
||||
@@ -236,7 +231,7 @@ namespace BetterLyrics.WinUI3.Views
|
||||
if (File.Exists(path))
|
||||
{
|
||||
var content = File.ReadAllText(path);
|
||||
foreach (var item in ViewModel.SelectedTracks.Select(x => x.UriPath).ToList())
|
||||
foreach (var item in ViewModel.SelectedTracks.Select(x => x.DecodedAbsoluteUri).ToList())
|
||||
{
|
||||
if (!content.Contains(item))
|
||||
{
|
||||
@@ -279,5 +274,13 @@ namespace BetterLyrics.WinUI3.Views
|
||||
ScrollToPlayingItem();
|
||||
}
|
||||
|
||||
private void FolderTreeView_ItemInvoked(TreeView sender, TreeViewItemInvokedEventArgs args)
|
||||
{
|
||||
ViewModel.SelectedSongsTabInfoIndex = -1;
|
||||
if (args.InvokedItem is FolderNode selectedFolder)
|
||||
{
|
||||
ViewModel.SelectFolder(selectedFolder);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
Loaded="RootGrid_Loaded"
|
||||
Unloaded="RootGrid_Unloaded">
|
||||
|
||||
<local:MusicGalleryPage x:Name="MusicGalleryPage" Margin="0,40,0,0" />
|
||||
<local:MusicGalleryPage x:Name="MusicGalleryPage" />
|
||||
|
||||
<local:NowPlayingPage
|
||||
x:Name="NowPlayingPage"
|
||||
|
||||
@@ -227,8 +227,8 @@
|
||||
<Flyout FlyoutPresenterStyle="{StaticResource FlyoutGhostStyle}" ShouldConstrainToRootBounds="False">
|
||||
<Grid
|
||||
x:Name="TopCommandFlyoutContainer"
|
||||
Width="400"
|
||||
Height="36" />
|
||||
Width="450"
|
||||
Height="38" />
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
|
||||
@@ -11,9 +11,13 @@
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid x:Name="RootGrid">
|
||||
<Frame x:Name="RootFrame" Margin="0,40,0,0" />
|
||||
<Frame x:Name="RootFrame" />
|
||||
|
||||
<StackPanel VerticalAlignment="Top" Orientation="Horizontal">
|
||||
<StackPanel
|
||||
Margin="0,0,140,0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Top"
|
||||
Orientation="Horizontal">
|
||||
|
||||
<Button Click="LyricsWindowSwitchButton_Click" Style="{StaticResource TitleBarButtonStyle}">
|
||||
<FontIcon
|
||||
|
||||
Reference in New Issue
Block a user