diff --git a/NonsPlayer.Core/Contracts/Managers/IConfigManagers.cs b/NonsPlayer.Core/Contracts/Managers/IConfigManagers.cs
new file mode 100644
index 0000000..03c5e82
--- /dev/null
+++ b/NonsPlayer.Core/Contracts/Managers/IConfigManagers.cs
@@ -0,0 +1,12 @@
+using ATL;
+using NonsPlayer.Core.Resources;
+using System.Text.Json;
+using System.Text;
+
+namespace NonsPlayer.Core.Contracts.Managers;
+
+public interface IConfigManager
+{
+ void Load();
+ void Save();
+}
\ No newline at end of file
diff --git a/NonsPlayer.Core/Models/AppSettings.cs b/NonsPlayer.Core/Models/AppSettings.cs
new file mode 100644
index 0000000..5f28270
--- /dev/null
+++ b/NonsPlayer.Core/Models/AppSettings.cs
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/NonsPlayer/Dialogs/ArtistSeparator.xaml b/NonsPlayer/Dialogs/ArtistSeparator.xaml
new file mode 100644
index 0000000..fb4e51d
--- /dev/null
+++ b/NonsPlayer/Dialogs/ArtistSeparator.xaml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/NonsPlayer/Dialogs/ArtistSeparator.xaml.cs b/NonsPlayer/Dialogs/ArtistSeparator.xaml.cs
new file mode 100644
index 0000000..2e0593e
--- /dev/null
+++ b/NonsPlayer/Dialogs/ArtistSeparator.xaml.cs
@@ -0,0 +1,63 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+using NonsPlayer.Core.Services;
+using NonsPlayer.Helpers;
+using System.Collections.ObjectModel;
+
+namespace NonsPlayer.Dialogs;
+
+[INotifyPropertyChanged]
+public sealed partial class ArtistSeparator : Page
+{
+ public ObservableCollection Models = new();
+
+ public ArtistSeparator()
+ {
+ InitializeComponent();
+ Init();
+ AddNewButton.Content = "AddNew".GetLocalized();
+ this.Tag = Models;
+ }
+
+ private void Init()
+ {
+ Models.Clear();
+ foreach (string s in ConfigManager.Instance.Settings.LocalArtistSep)
+ {
+ Models.Add(new SeparatorModel { Text = s, Command = DelSepCommand });
+ }
+ }
+
+ [RelayCommand]
+ public void DelSep(string content)
+ {
+ var itemsToRemove = new List();
+ foreach (SeparatorModel separatorModel in Models)
+ {
+ if (separatorModel.Text.Equals(content))
+ {
+ itemsToRemove.Add(separatorModel);
+ }
+ }
+
+ foreach (var item in itemsToRemove)
+ {
+ Models.Remove(item);
+ }
+ }
+
+ [RelayCommand]
+ public void Add()
+ {
+ Models.Add(new SeparatorModel { Command = DelSepCommand });
+ }
+}
+
+[INotifyPropertyChanged]
+public partial class SeparatorModel
+{
+ [ObservableProperty] private string text;
+ public IRelayCommand Command;
+}
\ No newline at end of file
diff --git a/NonsPlayer/Dialogs/LocalProperties.xaml b/NonsPlayer/Dialogs/LocalProperties.xaml
new file mode 100644
index 0000000..ce08fc5
--- /dev/null
+++ b/NonsPlayer/Dialogs/LocalProperties.xaml
@@ -0,0 +1,220 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/NonsPlayer/Dialogs/LocalProperties.xaml.cs b/NonsPlayer/Dialogs/LocalProperties.xaml.cs
new file mode 100644
index 0000000..85ca367
--- /dev/null
+++ b/NonsPlayer/Dialogs/LocalProperties.xaml.cs
@@ -0,0 +1,143 @@
+using ATL;
+using System.Windows.Forms;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using CommunityToolkit.WinUI.UI.Controls;
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+using Microsoft.UI.Xaml.Input;
+using Microsoft.UI.Xaml.Media;
+using Microsoft.UI.Xaml.Media.Animation;
+using NonsPlayer.Components.Models;
+using NonsPlayer.Components.ViewModels;
+using NonsPlayer.Core.Contracts.Adapters;
+using NonsPlayer.Core.Contracts.Models.Music;
+using NonsPlayer.Core.Models;
+using NonsPlayer.Core.Nons.Player;
+using NonsPlayer.Core.Services;
+using NonsPlayer.Helpers;
+using NonsPlayer.Services;
+using NonsPlayer.ViewModels;
+using System.Collections.ObjectModel;
+using NonsPlayer.Updater.Update;
+using System.Text.RegularExpressions;
+using System.Diagnostics;
+
+// To learn more about WinUI, the WinUI project structure,
+// and more about our project templates, see: http://aka.ms/winui-project-info.
+
+namespace NonsPlayer.Dialogs;
+
+[INotifyPropertyChanged]
+public sealed partial class LocalProperties : Page
+{
+ private LocalMusic currentMusic;
+ [ObservableProperty] private string title;
+ [ObservableProperty] private string artist;
+ [ObservableProperty] private string album;
+ [ObservableProperty] private string albumArtists;
+ [ObservableProperty] private string trackNumber;
+ [ObservableProperty] private string genre;
+ [ObservableProperty] private string date;
+ [ObservableProperty] private string duration;
+ [ObservableProperty] private string bitRate;
+ [ObservableProperty] private string codec;
+ [ObservableProperty] private string fileSize;
+ [ObservableProperty] private string filePath;
+
+ private LocalTrackModel Model = new();
+ public LocalProperties(IMusic music)
+ {
+ InitializeComponent();
+ if (music is LocalMusic)
+ {
+ currentMusic = music as LocalMusic;
+ Init(currentMusic.FilePath);
+ }
+
+ this.Tag = Model;
+ TitleTextBlock.Text = "Title".GetLocalized();
+ ArtistTextBlock.Text = "Artist".GetLocalized();
+ AlbumTextBlock.Text = "Album".GetLocalized();
+ AlbumArtistsTextBlock.Text = "AlbumArtists".GetLocalized();
+ TrackNumberTextBlock.Text = "TrackNumber".GetLocalized();
+ GenreTextBlock.Text = "Genre".GetLocalized();
+ DateTextBlock.Text = "Date".GetLocalized();
+ DurationTextBlock.Text = "Duration".GetLocalized();
+ BitRateTextBlock.Text = "BitRate".GetLocalized();
+ CodecTextBlock.Text = "Codec".GetLocalized();
+ FileSizeTextBlock.Text = "FileSize".GetLocalized();
+ FilePathTextBlock.Text = "FilePath".GetLocalized();
+ }
+
+ public void Init(string path)
+ {
+ var track = new Track(path);
+ var fileInfo = new FileInfo(path);
+ Title = track.Title;
+ Artist = track.Artist;
+ Album = track.Album;
+ AlbumArtists = track.AlbumArtist;
+ TrackNumber = track.TrackNumber.ToString();
+ Genre = track.Genre;
+ Date = track.Year.ToString();
+ Duration = TimeSpan.FromMilliseconds(track.DurationMs).ToString();
+ BitRate = track.Bitrate + " kbps";
+ Codec = fileInfo.Extension;
+ double mb = 1 << 20;
+ FileSize = $"{fileInfo.Length / mb:F2} MB";
+ FilePath = path;
+ }
+
+ [RelayCommand]
+ public void OpenFilePath()
+ {
+ Process.Start("explorer.exe", $"/select,\"{FilePath}\"");
+ }
+
+ partial void OnTitleChanged(string value)
+ {
+ Model.Title = value;
+ }
+
+ partial void OnArtistChanged(string value)
+ {
+ Model.Artist = value;
+ }
+
+ partial void OnAlbumChanged(string value)
+ {
+ Model.Album = value;
+ }
+
+ partial void OnAlbumArtistsChanged(string value)
+ {
+ Model.AlbumArtists = value;
+ }
+
+ partial void OnTrackNumberChanged(string value)
+ {
+ Model.TrackNumber = value;
+ }
+
+ partial void OnGenreChanged(string value)
+ {
+ Model.Genre = value;
+ }
+
+ partial void OnDateChanged(string value)
+ {
+ Model.Date = value;
+ }
+
+ private void OnPreviewKeyDown(object sender, KeyRoutedEventArgs e)
+ {
+ e.Handled = !IsTextAllowed(((NumberBox)sender).Text);
+ }
+
+ private static bool IsTextAllowed(string text)
+ {
+ var regex = new Regex("^[1-9][0-9]*$");
+ return regex.IsMatch(text);
+ }
+}
\ No newline at end of file
diff --git a/NonsPlayer/Helpers/ImageHelpers.cs b/NonsPlayer/Helpers/ImageHelpers.cs
new file mode 100644
index 0000000..8f4646f
--- /dev/null
+++ b/NonsPlayer/Helpers/ImageHelpers.cs
@@ -0,0 +1,80 @@
+using Microsoft.UI.Xaml.Media.Imaging;
+using Microsoft.UI.Xaml.Media;
+using Windows.Storage.Streams;
+
+namespace NonsPlayer.Helpers;
+
+public static class ImageHelpers
+{
+ public static async Task GetImageBrushAsyncFromBytes(byte[] cover)
+ {
+ if (cover == null) return null;
+
+ using (var stream = new InMemoryRandomAccessStream())
+ {
+ // 使用DataWriter将字节数组写入流
+ using (DataWriter writer = new DataWriter(stream.GetOutputStreamAt(0)))
+ {
+ writer.WriteBytes(cover);
+ await writer.StoreAsync();
+ }
+
+ // 创建BitmapImage并从流中加载图像
+ var bitmapImage = new BitmapImage();
+ await bitmapImage.SetSourceAsync(stream);
+ return new ImageBrush { ImageSource = bitmapImage };
+ }
+ }
+
+ public static async Task GetBitmapImageFromServer(string imageUrl)
+ {
+ using (var httpClient = new HttpClient())
+ {
+ try
+ {
+ var response = await httpClient.GetAsync(imageUrl);
+ response.EnsureSuccessStatusCode();
+ var imageBytes = await response.Content.ReadAsByteArrayAsync();
+ using (var stream = new InMemoryRandomAccessStream())
+ {
+ using (var writer = new DataWriter(stream.GetOutputStreamAt(0)))
+ {
+ writer.WriteBytes(imageBytes);
+ await writer.StoreAsync();
+ }
+
+ var bitmap = new BitmapImage();
+ await bitmap.SetSourceAsync(stream);
+ return bitmap;
+ }
+ }
+ catch (Exception ex)
+ {
+ return null;
+ }
+ }
+ }
+
+ public static async Task GetImageStreamFromServer(string imageUrl)
+ {
+ using var httpClient = new HttpClient();
+ try
+ {
+ var response = await httpClient.GetAsync(imageUrl);
+ response.EnsureSuccessStatusCode();
+ var imageBytes = await response.Content.ReadAsByteArrayAsync();
+ var stream = new InMemoryRandomAccessStream();
+ using (var writer = new DataWriter(stream.GetOutputStreamAt(0)))
+ {
+ writer.WriteBytes(imageBytes);
+ await writer.StoreAsync();
+ }
+
+ return stream;
+ }
+ catch (Exception ex)
+ {
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/NonsPlayer/Models/AppSettings.cs b/NonsPlayer/Models/AppSettings.cs
new file mode 100644
index 0000000..75c2080
--- /dev/null
+++ b/NonsPlayer/Models/AppSettings.cs
@@ -0,0 +1,69 @@
+using Microsoft.UI.Xaml.Media;
+using NonsPlayer.Core.Contracts.Models;
+using System.Text.Json.Serialization;
+
+namespace NonsPlayer.Models;
+
+public class AppSettings
+{
+ #region Player Settings
+
+ ///
+ /// 是否显示翻译歌词
+ ///
+ [JsonPropertyName("is_show_tran_lyric")]
+ public bool IsShowTranLyric { get; set; }
+
+ ///
+ /// 是否启用系统媒体控制
+ ///
+ [JsonPropertyName("smtc_enable")]
+ public bool SMTCEnable { get; set; }
+
+ ///
+ /// 加载下一页歌单的偏移量
+ ///
+ [JsonPropertyName("playlist_load_offset")]
+ public double PlaylistLoadOffset { get; set; }
+
+ ///
+ /// 歌单详情页一次显示的歌曲数量
+ ///
+ [JsonPropertyName("playlist_track_count")]
+ public int PlaylistTrackCount { get; set; }
+
+ ///
+ /// 主页推荐歌单数量
+ ///
+ [JsonPropertyName("home_recommended_count")]
+ public int RecommendedPlaylistCount { get; set; }
+
+ ///
+ /// 按一下音量加减的增量
+ ///
+ [JsonPropertyName("volume_step")]
+ public double VolumeStep { get; set; }
+
+ #endregion
+
+ #region Remote Settings
+
+ ///
+ /// 远程控制端口
+ ///
+ [JsonPropertyName("api_port")]
+ public int ApiPort { get; set; }
+
+ #endregion
+
+ public AppSettings()
+ {
+ IsShowTranLyric = true;
+ SMTCEnable = true;
+ ApiPort = 8080;
+ PlaylistLoadOffset = 500;
+ PlaylistTrackCount = 30;
+ VolumeStep = 10;
+ RecommendedPlaylistCount = 20;
+ }
+}
\ No newline at end of file