Skip to content

Commit 70feed7

Browse files
authored
Added search bar midi repository (#305)
* added search bar to midi repo * add null check for selected song * update filteredList variable type * updated summary * reset search bar when reload * prevent downloading when previous download still in process * prevent downloading when previous download still in process * remove unused namespace * added ability to import to playlist * added ability to import to playlist * update summary * reorder the UI * fixed search function use wrong variable
1 parent e21ee96 commit 70feed7

File tree

3 files changed

+198
-23
lines changed

3 files changed

+198
-23
lines changed

BardMusicPlayer/Controls/MidiRepository.xaml

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,45 @@
44
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
55
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
66
mc:Ignorable="d"
7-
Margin="10,10,10,0"
7+
Margin="10,0,10,0"
88
Background="{DynamicResource MaterialDesignBackground}"
99
d:DesignHeight="450" d:DesignWidth="800">
1010
<Grid>
1111
<Grid.RowDefinitions>
12+
<RowDefinition Height="15" />
1213
<RowDefinition Height="32" />
13-
<RowDefinition Height="25" />
14+
<RowDefinition Height="32" />
15+
<RowDefinition Height="20" />
1416
<RowDefinition Height="*" />
15-
<RowDefinition Height="140" />
17+
<RowDefinition Height="165" />
1618
</Grid.RowDefinitions>
19+
20+
<ProgressBar Grid.Row="0" x:Name="LoadingProgressBar" Grid.Column="1" IsIndeterminate="True"/>
1721

18-
<Button Grid.Row="0" x:Name="BtnGetSongList" Content="Get Song List" Grid.Column="1" Click="Button_Click"/>
19-
20-
<ProgressBar Grid.Row="1" x:Name="LoadingProgressBar" Grid.Column="1" IsIndeterminate="True"/>
22+
<Button Grid.Row="1" x:Name="BtnGetSongList" Content="Get Song List" Click="Button_Click"/>
2123

22-
<ListView Grid.Row="2"
24+
<Grid Grid.Row="2">
25+
<Grid.ColumnDefinitions>
26+
<ColumnDefinition Width="60" />
27+
<ColumnDefinition Width="*" />
28+
</Grid.ColumnDefinitions>
29+
<Label Content="Search : " VerticalAlignment="Center" VerticalContentAlignment="Center"/>
30+
<TextBox x:Name="SongSearchTextBox" Grid.Column="1" TextChanged="SongSearchTextBox_TextChanged" VerticalAlignment="Center" VerticalContentAlignment="Center"/>
31+
</Grid>
32+
<TextBlock x:Name="ResultsCountTextBox" Margin="3,4,0,0" Grid.Row="3" Text="" HorizontalAlignment="Right" />
33+
<ListView Margin="0,10,0,0" Grid.Row="4"
2334
x:Name="MidiRepoContainer"
2435
VerticalAlignment="Stretch" HorizontalAlignment="Stretch" BorderThickness="0"
2536
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
2637
ScrollViewer.VerticalScrollBarVisibility="Visible"
2738
ScrollViewer.CanContentScroll="True" SelectionChanged="MidiRepoContainer_SelectionChanged" MouseDoubleClick="MidiRepoContainer_MouseDoubleClick" />
28-
29-
<Grid Grid.Row="3" x:Name="DownloadPanel" Background="{DynamicResource MaterialDesignPaper}">
39+
<Grid Grid.Row="5" x:Name="DownloadPanel" Background="{DynamicResource MaterialDesignPaper}">
3040
<Grid.RowDefinitions>
3141
<RowDefinition Height="10"/>
3242
<RowDefinition Height="24"/>
3343
<RowDefinition Height="30"/>
3444
<RowDefinition Height="30"/>
45+
<RowDefinition Height="30"/>
3546
<RowDefinition Height="*"/>
3647
</Grid.RowDefinitions>
3748
<TextBlock Grid.Row="1" x:Name="SongTitle" VerticalAlignment="Top" Text="song_title" FontWeight="Bold" />
@@ -41,16 +52,35 @@
4152
<Grid Grid.Row="3">
4253
<Grid.ColumnDefinitions>
4354
<ColumnDefinition Width="99" />
44-
<ColumnDefinition Width="*" />
4555
<ColumnDefinition Width="25" />
56+
<ColumnDefinition Width="*" />
4657
</Grid.ColumnDefinitions>
4758

4859
<Label Grid.Column="0" Margin="-4,0,0,0" Content="Download Path:" VerticalAlignment="Center" HorizontalAlignment="Left" />
49-
<TextBox IsReadOnly="True" Grid.Column="1" Margin="0,0,4,0" x:Name="DownloadPath" Text="" Padding="5" VerticalAlignment="Center" HorizontalAlignment="Stretch" />
50-
<Button x:Name="BtnDownloadPath" Grid.Column="2" Content="..." Margin="0,0 4,0"
60+
<Button x:Name="BtnDownloadPath" Grid.Column="1" Content="..." Margin="0,0 4,0"
5161
Click="SelectPath_Button_Click" Height="18" Padding="5" />
62+
<TextBox IsReadOnly="True" Grid.Column="2" Margin="0,0,4,0" x:Name="DownloadPath" Text="" Padding="5" VerticalAlignment="Center" HorizontalAlignment="Stretch" />
63+
5264
</Grid>
5365
<Grid Grid.Row="4" >
66+
<Grid.ColumnDefinitions>
67+
<ColumnDefinition Width="100"/>
68+
<ColumnDefinition Width="20"/>
69+
<ColumnDefinition Width="250"/>
70+
<ColumnDefinition Width="90"/>
71+
</Grid.ColumnDefinitions>
72+
<Label Grid.Column="0" Content="Add to playlist" Margin="-4,0,0,0" HorizontalAlignment="Left" VerticalAlignment="Center" VerticalContentAlignment="Center" />
73+
<CheckBox Grid.Column="1" x:Name="AddToPlaylistCheckBox" VerticalAlignment="Center" Checked="AddToPlaylistCheckBox_Checked" Unchecked="AddToPlaylistCheckBox_Unchecked" />
74+
<ComboBox Grid.Column="2" x:Name="PlaylistDropdown" Visibility="Hidden" Margin="5,0,0,0" VerticalContentAlignment="Center" ItemsSource="{Binding}">
75+
<ComboBox.ItemTemplate>
76+
<DataTemplate>
77+
<TextBlock Text="{Binding}" />
78+
</DataTemplate>
79+
</ComboBox.ItemTemplate>
80+
</ComboBox>
81+
<Button Grid.Column="3" x:Name="RefreshPlaylistButton" Visibility="Hidden" Height="23" Content="Refresh" Margin="5,0,0,0" VerticalAlignment="Center" Click="RefreshPlaylist_Click"/>
82+
</Grid>
83+
<Grid Grid.Row="5" >
5484
<Grid.ColumnDefinitions>
5585
<ColumnDefinition Width="100" />
5686
<ColumnDefinition Width="*" />

BardMusicPlayer/Controls/MidiRepository.xaml.cs

Lines changed: 136 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
using BardMusicPlayer.Pigeonhole;
1+
using BardMusicPlayer.Coffer;
2+
using BardMusicPlayer.Functions;
3+
using BardMusicPlayer.Pigeonhole;
24
using BardMusicPlayer.Resources;
35
using HtmlAgilityPack;
6+
using System.Diagnostics;
47
using System.IO;
58
using System.Net.Http;
69
using System.Reflection;
@@ -21,7 +24,10 @@ public partial class MidiRepository : UserControl
2124
private const string commentNodeXpath = ".//span[contains(@class, 'r4')]";
2225
private readonly HttpClient httpClient;
2326
private readonly string midiRepoUrl = "https://songs.bardmusicplayer.com";
24-
private List<Song> listSong = new List<Song>();
27+
private List<Song> fullListSong = new List<Song>();
28+
private List<Song> previewListSong = new List<Song>();
29+
private Song? selectedSong;
30+
private bool isDownloading;
2531
public MidiRepository()
2632
{
2733
InitializeComponent();
@@ -31,6 +37,7 @@ public MidiRepository()
3137
DownloadPath.Text = BmpPigeonhole.Instance.MidiDownloadPath;
3238
DownloadProgressLabel.Visibility = Visibility.Hidden;
3339
DownloadProgressBar.Visibility = Visibility.Hidden;
40+
RefreshPlaylistSelector();
3441
}
3542
private class Song
3643
{
@@ -56,7 +63,8 @@ private async Task<string> FetchSongData()
5663
/// <param name="html"></param>
5764
private void RefreshSongList(string html)
5865
{
59-
listSong.Clear();
66+
fullListSong.Clear();
67+
previewListSong.Clear();
6068
HtmlDocument htmlDoc = new HtmlDocument();
6169
htmlDoc.LoadHtml(html);
6270

@@ -70,7 +78,7 @@ private void RefreshSongList(string html)
7078

7179
if (titleNode != null && authorNode != null && commentNode != null)
7280
{
73-
listSong.Add(new Song
81+
fullListSong.Add(new Song
7482
{
7583
Title = titleNode.GetAttributeValue("title", ""),
7684
Author = authorNode.InnerText,
@@ -94,11 +102,14 @@ private async void Button_Click(object sender, RoutedEventArgs e)
94102
var songData = await FetchSongData();
95103

96104
RefreshSongList(songData);
97-
MidiRepoContainer.ItemsSource = listSong.Select(song => song.Title).ToList();
105+
previewListSong = fullListSong;
106+
MidiRepoContainer.ItemsSource = previewListSong.Select(song => song.Title).ToList();
107+
RefreshCountTextBox();
98108

99109
BtnGetSongList.IsEnabled = true;
100110
BtnGetSongList.Content = "Refresh";
101111
LoadingProgressBar.Visibility = Visibility.Hidden;
112+
SongSearchTextBox.Text = "";
102113
}
103114

104115
/// <summary>
@@ -108,10 +119,13 @@ private async void Button_Click(object sender, RoutedEventArgs e)
108119
/// <param name="e"></param>
109120
private void MidiRepoContainer_SelectionChanged(object sender, SelectionChangedEventArgs e)
110121
{
122+
if (MidiRepoContainer.SelectedIndex == -1)
123+
return;
124+
111125
DownloadPanel.Visibility = Visibility.Visible;
112-
Song song = listSong[MidiRepoContainer.SelectedIndex];
113-
SongTitle.Text = $"({song.Author}) {song.Title}";
114-
SongComment.Text = song.Comment;
126+
selectedSong = previewListSong[MidiRepoContainer.SelectedIndex];
127+
SongTitle.Text = $"({selectedSong.Author}) {selectedSong.Title}";
128+
SongComment.Text = selectedSong.Comment;
115129
}
116130

117131
/// <summary>
@@ -145,6 +159,7 @@ private void SelectPath_Button_Click(object sender, RoutedEventArgs e)
145159
/// <param name="fileName"></param>
146160
private async void DownloadFile(string url, string fileName)
147161
{
162+
isDownloading = true;
148163
HttpClient client = new HttpClient();
149164
HttpResponseMessage response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
150165
long? contentLength = response.Content.Headers.ContentLength;
@@ -170,8 +185,18 @@ private async void DownloadFile(string url, string fileName)
170185
string finalFilePath = $"{downloadsPath}/{fileName}.mid";
171186

172187
File.Move(tempFilePath, finalFilePath, true);
173-
DownloadButton.IsEnabled = true;
188+
DownloadPanel.IsEnabled = true;
174189
DownloadProgressLabel.Visibility = Visibility.Visible;
190+
isDownloading = false;
191+
192+
// Add to selected playlist
193+
bool addToPlaylist = AddToPlaylistCheckBox.IsChecked ?? false;
194+
195+
if (addToPlaylist && PlaylistDropdown.SelectedIndex != -1)
196+
{
197+
var playlist = BmpCoffer.Instance.GetPlaylist(PlaylistDropdown.SelectedItem as string);
198+
PlaylistFunctions.AddFileToPlaylist(finalFilePath, playlist);
199+
}
175200
}
176201

177202
/// <summary>
@@ -191,6 +216,7 @@ private void DownloadButtonClick(object sender, RoutedEventArgs e)
191216
/// <param name="e"></param>
192217
private void MidiRepoContainer_MouseDoubleClick(object sender, MouseButtonEventArgs e)
193218
{
219+
selectedSong = previewListSong[MidiRepoContainer.SelectedIndex];
194220
DownloadSelectedMidi();
195221
}
196222

@@ -199,15 +225,114 @@ private void MidiRepoContainer_MouseDoubleClick(object sender, MouseButtonEventA
199225
/// </summary>
200226
private void DownloadSelectedMidi()
201227
{
228+
if (isDownloading)
229+
return;
230+
202231
if (!Directory.Exists(BmpPigeonhole.Instance.MidiDownloadPath))
203232
{
204233
MessageBox.Show("The downloads directory is not valid.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
205234
return;
206235
}
207-
Song selectedSong = listSong[MidiRepoContainer.SelectedIndex];
208-
DownloadButton.IsEnabled = false;
236+
237+
if (selectedSong == null)
238+
return;
239+
240+
DownloadPanel.IsEnabled = false;
209241
DownloadProgressBar.Visibility = Visibility.Visible;
210242
DownloadProgressBar.Value = 0;
211243
DownloadFile($"{midiRepoUrl}/{selectedSong.Url}", $"({selectedSong.Author}) {selectedSong.Title}");
212244
}
245+
246+
/// <summary>
247+
/// Refresh result count textblock
248+
/// </summary>
249+
private void RefreshCountTextBox()
250+
{
251+
ResultsCountTextBox.Text = $"{previewListSong.Count} Results";
252+
}
253+
254+
#region Search Functions
255+
/// <summary>
256+
/// Filter the midi listview based on SongSearchTextBox
257+
/// </summary>
258+
private void SearchSong()
259+
{
260+
if (fullListSong.Count == 0)
261+
return;
262+
263+
var filteredList = new List<string>();
264+
if (SongSearchTextBox.Text != "")
265+
{
266+
previewListSong = fullListSong.FindAll(s => s.Title.ToLower().Contains(SongSearchTextBox.Text.ToLower()));
267+
filteredList = previewListSong.Select(s => s.Title).ToList();
268+
}
269+
else
270+
{
271+
previewListSong = fullListSong;
272+
filteredList = previewListSong.Select(s => s.Title).ToList();
273+
}
274+
275+
MidiRepoContainer.ItemsSource = filteredList;
276+
RefreshCountTextBox();
277+
}
278+
/// <summary>
279+
/// Filter song when textbox value changed
280+
/// </summary>
281+
/// <param name="sender"></param>
282+
/// <param name="e"></param>
283+
private void SongSearchTextBox_TextChanged(object sender, TextChangedEventArgs e)
284+
{
285+
SearchSong();
286+
}
287+
#endregion
288+
289+
#region Import To Playlist Functions
290+
/// <summary>
291+
/// Refresh playlist dropdown when click 'refresh' button
292+
/// </summary>
293+
/// <param name="sender"></param>
294+
/// <param name="e"></param>
295+
private void RefreshPlaylist_Click(object sender, RoutedEventArgs e)
296+
{
297+
RefreshPlaylistSelector();
298+
}
299+
300+
/// <summary>
301+
/// Refresh playlist dropdown
302+
/// </summary>
303+
private void RefreshPlaylistSelector()
304+
{
305+
PlaylistDropdown.DataContext = BmpCoffer.Instance.GetPlaylistNames();
306+
}
307+
308+
/// <summary>
309+
/// Disable 'add to playlist' feature if checkbox is unchecked
310+
/// </summary>
311+
/// <param name="sender"></param>
312+
/// <param name="e"></param>
313+
private void AddToPlaylistCheckBox_Unchecked(object sender, RoutedEventArgs e)
314+
{
315+
RefreshAddToPlaylistMode();
316+
}
317+
318+
/// <summary>
319+
/// Enable 'add to playlist' feature if checkbox is checked
320+
/// </summary>
321+
/// <param name="sender"></param>
322+
/// <param name="e"></param>
323+
private void AddToPlaylistCheckBox_Checked(object sender, RoutedEventArgs e)
324+
{
325+
RefreshAddToPlaylistMode();
326+
}
327+
328+
/// <summary>
329+
/// Hide and show playlist selector while check/uncheck 'add to playlist' checkbox
330+
/// </summary>
331+
private void RefreshAddToPlaylistMode()
332+
{
333+
bool isChecked = AddToPlaylistCheckBox.IsChecked ?? false;
334+
PlaylistDropdown.Visibility = isChecked ? Visibility.Visible : Visibility.Hidden;
335+
RefreshPlaylistButton.Visibility = isChecked ? Visibility.Visible : Visibility.Hidden;
336+
}
337+
#endregion
213338
}

BardMusicPlayer/Functions/PlaylistFunctions.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,26 @@ public static bool AddFilesToPlaylist(IPlaylist? currentPlaylist)
4545
return true;
4646
}
4747

48+
/// <summary>
49+
/// Overload method : Add single file to playlist with using filepath instead of dialog
50+
/// </summary>
51+
/// <param name="filePath"></param>
52+
/// <param name="currentPlaylist"></param>
53+
/// <returns></returns>
54+
public static bool AddFileToPlaylist(string filePath, IPlaylist? currentPlaylist)
55+
{
56+
var song = BmpSong.OpenFile(filePath).Result;
57+
58+
// Check if the song title is not empty or null
59+
if (currentPlaylist != null && !string.IsNullOrEmpty(song.Title) && currentPlaylist.SingleOrDefault(x => x.FilePath.Equals(song.FilePath)) == null)
60+
{
61+
currentPlaylist.Add(song);
62+
BmpCoffer.Instance.SaveSong(song);
63+
}
64+
65+
BmpCoffer.Instance.SavePlaylist(currentPlaylist);
66+
return true;
67+
}
4868
/// <summary>
4969
/// Add a folder + subfolders to the playlist
5070
/// </summary>

0 commit comments

Comments
 (0)