Skip to content

Commit 251d59d

Browse files
committed
Feature: Added support for editing album covers
1 parent 82c8d40 commit 251d59d

File tree

7 files changed

+117
-1
lines changed

7 files changed

+117
-1
lines changed

src/Files.App/Data/Models/SelectedItemsPropertiesViewModel.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
using Files.App.ViewModels.Properties;
55
using Files.Shared.Helpers;
66
using System.Windows.Input;
7+
using TagLib;
8+
using Windows.Storage;
9+
using Windows.Storage.FileProperties;
10+
using Windows.Storage.Pickers;
711

812
namespace Files.App.Data.Models
913
{
@@ -480,6 +484,13 @@ public ICommand FormatDriveCommand
480484
get => formatDriveCommand;
481485
set => SetProperty(ref formatDriveCommand, value);
482486
}
487+
488+
private ICommand editAlbumCoverCommand;
489+
public ICommand EditAlbumCoverCommand
490+
{
491+
get => editAlbumCoverCommand;
492+
set => SetProperty(ref editAlbumCoverCommand, value);
493+
}
483494

484495
private bool itemAttributesVisibility = true;
485496
public bool ItemAttributesVisibility
@@ -511,6 +522,7 @@ public bool IsItemSelected
511522

512523
public SelectedItemsPropertiesViewModel()
513524
{
525+
514526
}
515527

516528
private bool isSelectedItemImage = false;
@@ -691,5 +703,26 @@ public bool IsUnblockFileSelected
691703
get => isUnblockFileSelected;
692704
set => SetProperty(ref isUnblockFileSelected, value);
693705
}
706+
707+
private bool isAblumCoverModified;
708+
public bool IsAblumCoverModified
709+
{
710+
get => isAblumCoverModified;
711+
set => SetProperty(ref isAblumCoverModified, value);
712+
}
713+
714+
private bool isEditAlbumCoverVisible;
715+
public bool IsEditAlbumCoverVisible
716+
{
717+
get => isEditAlbumCoverVisible;
718+
set => SetProperty(ref isEditAlbumCoverVisible, value);
719+
}
720+
721+
private Picture modifiedAlbumCover;
722+
public Picture ModifiedAlbumCover
723+
{
724+
get => modifiedAlbumCover;
725+
set => SetProperty(ref modifiedAlbumCover, value);
726+
}
694727
}
695728
}

src/Files.App/Files.App.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393
<PackageReference Include="CommunityToolkit.WinUI.Notifications" Version="7.1.2" />
9494
<PackageReference Include="CommunityToolkit.WinUI.UI.Behaviors" Version="7.1.2" />
9595
<PackageReference Include="CommunityToolkit.WinUI.UI.Controls" Version="7.1.2" />
96+
<PackageReference Include="TagLibSharp" Version="2.3.0" />
9697
<PackageReference Include="Tulpep.ActiveDirectoryObjectPicker" Version="3.0.11" />
9798
<PackageReference Include="Vanara.PInvoke.DwmApi" Version="3.4.17" />
9899
<PackageReference Include="Vanara.Windows.Extensions" Version="3.4.17" />

src/Files.App/ViewModels/Properties/BasePropertiesPage.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
// Copyright(c) 2023 Files Community
22
// Licensed under the MIT License. See the LICENSE.
33

4+
using Files.Shared.Helpers;
45
using Microsoft.UI.Xaml;
56
using Microsoft.UI.Xaml.Controls;
67
using Microsoft.UI.Xaml.Navigation;
8+
using TagLib;
79
using Windows.Storage;
10+
using Windows.Storage.FileProperties;
11+
using Windows.Storage.Pickers;
812

913
namespace Files.App.ViewModels.Properties
1014
{
@@ -53,7 +57,13 @@ protected override void OnNavigatedTo(NavigationEventArgs e)
5357
{
5458
// Selection only contains files
5559
if (items.All(item => item.PrimaryItemAttribute == StorageItemTypes.File || item.IsArchive))
60+
{
5661
BaseProperties = new CombinedFileProperties(ViewModel, np.CancellationTokenSource, DispatcherQueue, items, AppInstance);
62+
63+
ViewModel.IsEditAlbumCoverVisible =
64+
items.All(item => FileExtensionHelpers.IsVideoFile(item.FileExtension)) ||
65+
items.All(item => FileExtensionHelpers.IsAudioFile(item.FileExtension));
66+
}
5767
// Selection includes folders
5868
else
5969
BaseProperties = new CombinedProperties(ViewModel, np.CancellationTokenSource, DispatcherQueue, items, AppInstance);
@@ -69,6 +79,28 @@ protected override void OnNavigatedTo(NavigationEventArgs e)
6979
BaseProperties = new FolderProperties(ViewModel, np.CancellationTokenSource, DispatcherQueue, item, AppInstance);
7080
}
7181

82+
ViewModel.EditAlbumCoverCommand = new RelayCommand(async () =>
83+
{
84+
FileOpenPicker filePicker = new FileOpenPicker();
85+
filePicker.FileTypeFilter.Add(".jpg");
86+
filePicker.FileTypeFilter.Add(".jpeg");
87+
filePicker.FileTypeFilter.Add(".bmp");
88+
filePicker.FileTypeFilter.Add(".png");
89+
90+
var parentWindowId = np.Window.AppWindow.Id;
91+
var handle = Microsoft.UI.Win32Interop.GetWindowFromWindowId(parentWindowId);
92+
WinRT.Interop.InitializeWithWindow.Initialize(filePicker, handle);
93+
94+
StorageFile file = await filePicker.PickSingleFileAsync();
95+
96+
if (file is not null)
97+
{
98+
ViewModel.IsAblumCoverModified = true;
99+
ViewModel.ModifiedAlbumCover = new Picture(file.Path);
100+
ViewModel.IconData = await FileThumbnailHelper.LoadIconFromPathAsync(file.Path, 80, ThumbnailMode.DocumentsView, ThumbnailOptions.ResizeThumbnail, false);
101+
}
102+
});
103+
72104
base.OnNavigatedTo(e);
73105
}
74106

src/Files.App/ViewModels/Properties/Items/CombinedFileProperties.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ public CombinedFileProperties(
1717

1818
public async Task GetSystemFilePropertiesAsync()
1919
{
20-
var queries = await Task.WhenAll(List.AsParallel().Select(async item => {
20+
var queries = await Task.WhenAll(List.AsParallel().Select(async item =>
21+
{
2122
BaseStorageFile file = await FilesystemTasks.Wrap(() => StorageFileExtensions.DangerousGetFileFromPathAsync(item.ItemPath));
2223
if (file is null)
2324
{

src/Files.App/ViewModels/Properties/Items/FileProperties.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ public override void GetBaseProperties()
4545
ViewModel.CustomIconSource = Item.CustomIconSource;
4646
ViewModel.LoadFileIcon = Item.LoadFileIcon;
4747
ViewModel.IsDownloadedFile = NativeFileOperationsHelper.ReadStringFromFile($"{Item.ItemPath}:Zone.Identifier") is not null;
48+
ViewModel.IsEditAlbumCoverVisible =
49+
FileExtensionHelpers.IsVideoFile(Item.FileExtension) ||
50+
FileExtensionHelpers.IsAudioFile(Item.FileExtension);
4851

4952
if (!Item.IsShortcut)
5053
return;

src/Files.App/Views/Properties/GeneralPage.xaml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,21 @@
7878
ItemSize="40"
7979
ViewModel="{x:Bind ViewModel}" />
8080

81+
<!-- Edit Album Cover -->
82+
<Button
83+
x:Name="EditAlbumCoverButton"
84+
Grid.Row="0"
85+
Grid.Column="0"
86+
Width="40"
87+
Height="40"
88+
Margin="{StaticResource PropertyNameMargin}"
89+
x:Load="{x:Bind ViewModel.IsEditAlbumCoverVisible, Mode=OneWay}"
90+
Background="Transparent"
91+
BorderBrush="Transparent"
92+
Command="{x:Bind ViewModel.EditAlbumCoverCommand, Mode=OneWay}">
93+
<FontIcon FontSize="12" Glyph="&#xE70F;" />
94+
</Button>
95+
8196
<!-- Name & Folder/File Count -->
8297
<StackPanel
8398
Grid.Row="0"

src/Files.App/Views/Properties/GeneralPage.xaml.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.Text.RegularExpressions;
99
using Windows.Storage;
1010
using Microsoft.UI.Dispatching;
11+
using TagLib;
1112

1213
namespace Files.App.Views.Properties
1314
{
@@ -135,6 +136,21 @@ async Task<bool> SaveCombinedAsync(IList<ListedItem> fileOrFolders)
135136
await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() =>
136137
UIFilesystemHelpers.SetHiddenAttributeItem(fileOrFolder, ViewModel.IsHidden, itemMM)
137138
);
139+
140+
if (ViewModel.IsAblumCoverModified)
141+
{
142+
TagLib.File mp3File = TagLib.File.Create(fileOrFolder.ItemPath);
143+
IPicture[] pictures = new IPicture[1];
144+
pictures[0] = ViewModel.ModifiedAlbumCover;
145+
mp3File.Tag.Pictures = pictures;
146+
mp3File.Save();
147+
mp3File.Dispose();
148+
149+
await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() =>
150+
{
151+
AppInstance?.FilesystemViewModel?.RefreshItems(null);
152+
});
153+
}
138154
}
139155
}
140156
return true;
@@ -154,6 +170,21 @@ await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() =>
154170
if (ViewModel.IsUnblockFileSelected)
155171
NativeFileOperationsHelper.DeleteFileFromApp($"{item.ItemPath}:Zone.Identifier");
156172

173+
if (ViewModel.IsAblumCoverModified)
174+
{
175+
TagLib.File mp3File = TagLib.File.Create(item.ItemPath);
176+
IPicture[] pictures = new IPicture[1];
177+
pictures[0] = ViewModel.ModifiedAlbumCover;
178+
mp3File.Tag.Pictures = pictures;
179+
mp3File.Save();
180+
mp3File.Dispose();
181+
182+
await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() =>
183+
{
184+
AppInstance?.FilesystemViewModel?.RefreshItems(null);
185+
});
186+
}
187+
157188
if (!GetNewName(out var newName))
158189
return true;
159190

0 commit comments

Comments
 (0)