Imports System.ComponentModel Imports System.Data.Common Imports System.Data.SQLite Imports System.IO Imports AxWMPLib Imports DevExpress.XtraGrid Imports DevExpress.XtraGrid.Columns Imports DevExpress.XtraGrid.Views.Base Imports DevExpress.XtraGrid.Views.Grid Imports DevExpress.XtraGrid.Views.Grid.ViewInfo Imports DevExpress.XtraReports.UI Public Class frmMain Public Property Songs As New List(Of Song) Dim ErrorMsgs As New List(Of String) Event ReadProgress(Caption As String, Description As String) Private Sub frmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load prepareGrid() GetSongsFromDB() End Sub Private Sub prepareGrid() grdSongs.DataSource = Songs grdvSongs.PopulateColumns() With grdvSongs .OptionsBehavior.Editable = True For Each col As GridColumn In grdvSongs.Columns col.OptionsColumn.AllowEdit = False Next .Columns(NameOf(Song.IsFavorite)).OptionsColumn.AllowEdit = True .Columns(NameOf(Song.ID)).Visible = False .Columns(NameOf(Song.SongInfos)).Visible = False .Columns(NameOf(Song.Coverfile)).Visible = False .Columns(NameOf(Song.Songfile)).Visible = False .Columns(NameOf(Song.Videofile)).Visible = False .Columns(NameOf(Song.HasError)).Visible = False .Columns(NameOf(Song.ErrorText)).Visible = False .Columns(NameOf(Song.RootDir)).Visible = False .Columns(NameOf(Song.SubDirectory)).Visible = False .Columns(NameOf(Song.InfoFile)).Visible = False '.Columns(NameOf(Song.Language)).Visible = False '.Columns(NameOf(Song.Genre)).Visible = False .Columns(NameOf(Song.BPM)).Visible = False .Columns(NameOf(Song.GAP)).Visible = False .Columns(NameOf(Song.Previewstart)).Visible = False .Columns(NameOf(Song.FullPath)).Visible = False .Columns(NameOf(Song.FolderName)).Visible = False .Columns(NameOf(Song.ExistsInFavDir)).Visible = False .Columns(NameOf(Song.Songfilename)).Visible = False .Columns(NameOf(Song.VideoFilename)).Visible = False .Columns(NameOf(Song.CoverFilename)).Visible = False .Columns(NameOf(Song.Artist)).SortIndex = 0 .Columns(NameOf(Song.Artist)).Caption = "Künstler" .Columns(NameOf(Song.Title)).SortIndex = 1 .Columns(NameOf(Song.Title)).Caption = "Titel" .Columns(NameOf(Song.ParentFolderName)).SortIndex = 2 .Columns(NameOf(Song.ParentFolderName)).Caption = "Ordnername" .Columns(NameOf(Song.Language)).Caption = "Sprache" .Columns(NameOf(Song.Year)).Caption = "Jahr" .Columns(NameOf(Song.IsFavorite)).Caption = "Favorit" .Columns(NameOf(Song.IsFavorite)).VisibleIndex = 0 .Columns(NameOf(Song.SongInfoCount)).Visible = False ' .Columns(NameOf(Song.ParentFolderName)).GroupIndex = 0 grdvSongs.ActiveFilter.Add(.Columns(NameOf(Song.HasError)), New ColumnFilterInfo($"[{NameOf(Song.HasError)}] = FALSE")) Dim rule As GridFormatRule rule = New GridFormatRule With { .Column = grdvSongs.Columns(NameOf(Song.IsFavorite)), .ApplyToRow = True, .Rule = New DevExpress.XtraEditors.FormatConditionRuleValue With { .Condition = DevExpress.XtraEditors.FormatCondition.Equal, .Value1 = True } } rule.RuleCast(Of DevExpress.XtraEditors.FormatConditionRuleValue).Appearance.BackColor = Color.LightYellow grdvSongs.FormatRules.Add(rule) rule = New GridFormatRule With { .Column = grdvSongs.Columns(NameOf(Song.ExistsInFavDir)), .ApplyToRow = True, .Rule = New DevExpress.XtraEditors.FormatConditionRuleValue With { .Condition = DevExpress.XtraEditors.FormatCondition.Equal, .Value1 = True } } rule.RuleCast(Of DevExpress.XtraEditors.FormatConditionRuleValue).Appearance.BackColor = Color.LightGreen grdvSongs.FormatRules.Add(rule) End With End Sub Private Sub btnSettings_ItemClick(sender As Object, e As DevExpress.XtraBars.ItemClickEventArgs) Handles btnSettings.ItemClick Dim frm As New frmSettings frm.ShowDialog() End Sub Private Sub btnReread_ItemClick(sender As Object, e As DevExpress.XtraBars.ItemClickEventArgs) Handles btnReread.ItemClick RereadFolders() End Sub Private Async Sub GetSongsFromDB() grdvSongs.BeginDataUpdate() Dim con = DB.getConnection Dim cmd As New SQLiteCommand(Nothing, con) Dim reader As DbDataReader Await con.OpenAsync cmd.CommandText = "SELECT * FROM t_songs" reader = Await cmd.ExecuteReaderAsync Songs.Clear() Dim s As Song Do While Await reader.ReadAsync s = New Song s.ID = CInt(reader("S_ID")) s.RootDir = CStr(reader("S_Path")) s.SubDirectory = CStr(reader("S_Subpath")) s.Artist = TryCast(reader("S_Info_Artist"), String) s.Title = TryCast(reader("S_Info_Title"), String) s.Year = CInt(reader("S_Info_Year")) s.Language = TryCast(reader("S_Info_Language"), String) s.Genre = TryCast(reader("S_Info_Genre"), String) s.Previewstart = CDec(reader("S_Info_Previewstart")) s.BPM = CInt(reader("S_Info_BPM")) s.GAP = CInt(reader("S_Info_GAP")) s.IsFavorite = CBool(reader("S_Favorite")) s.InfoFile = TryCast(reader("S_File_Info"), String) s.Songfilename = TryCast(reader("S_File_Song"), String) s.CoverFilename = TryCast(reader("S_File_Cover"), String) s.VideoFilename = TryCast(reader("S_File_Video"), String) s.SongInfos = Newtonsoft.Json.JsonConvert.DeserializeObject(Of Dictionary(Of String, String))(TryCast(reader("S_Info_Additional"), String)) Songs.Add(s) 'ToDo:Songinfos laden (oder erst beim Anklicken des Liedes) Loop reader.Close() con.Close() CheckFavorites() grdvSongs.EndDataUpdate() grdvSongs.BestFitColumns() End Sub Private Async Sub RereadFolders() Dim con = DB.getConnection Dim cmd As New SQLiteCommand(Nothing, con) Dim reader As DbDataReader Dim Pfade As New List(Of String) 'Pfade aus Datenbank auslesen Await con.OpenAsync() cmd.CommandText = "SELECT p_path FROM t_paths ORDER BY p_path" reader = Await cmd.ExecuteReaderAsync Do While Await reader.ReadAsync Pfade.Add(CStr(reader("p_path"))) Loop con.Close() 'Ordner auslesen prgMain.Visible = True Await Task.Run(Sub() ImportSongsToDB(Pfade)) prgMain.Visible = False ErrorMsgs.Clear() If Pfade.Count = 0 Then ErrorMsgs.Add("Es sind keine Pfade hinterlegt.") End If If ErrorMsgs.Count > 0 Then MessageBox.Show(String.Join(vbCrLf, ErrorMsgs), Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error) End If GetSongsFromDB() End Sub Private Sub ImportSongsToDB(Pfade As List(Of String)) Dim FavDirExists As Boolean = Directory.Exists(My.Settings.FavDir) Dim FavDir As String = My.Settings.FavDir If FavDir.EndsWith("\") = False Then FavDir &= "\" Dim songinfofiles() As String Dim duetinfofiles() As String Dim song As Song Dim songsread As UInt64 Dim templist As New List(Of Song) For Each pfad As String In Pfade If Directory.Exists(pfad) Then For Each ordner As String In Directory.GetDirectories(pfad, "*", SearchOption.AllDirectories) 'ordner = Einzelne liederordner If Directory.GetFiles(ordner, "*.mp3").Count > 0 Then 'Wenn es sich um einen Ordner handelt, der einen Song enthält (und nicht irgendein übergeordneter Ordner ist) songinfofiles = Directory.GetFiles(ordner, "*.txt") duetinfofiles = Directory.GetFiles(ordner, "*.txd") song = New Song song.RootDir = pfad & "\" song.SubDirectory = ordner.Substring(pfad.Length + 1) If songinfofiles.Count = 0 AndAlso duetinfofiles.Count = 0 Then song.ErrorText = $"{songinfofiles.Count} Textdateien gefunden" song.HasError = True Else If songinfofiles.Count > 0 Then song.InfoFile = songinfofiles.FirstOrDefault Else song.InfoFile = duetinfofiles.FirstOrDefault End If song.ReadInfoFile() End If If FavDirExists Then song.IsFavorite = Directory.Exists(FavDir & song.FolderName) templist.Add(song) End If songsread += CType(1, UInt64) RaiseEvent ReadProgress("Songs werden eingelesen...", $"{songsread} Songs") Next Else ErrorMsgs.Add($"Der Pfad {pfad} existiert nicht oder konnte nicht gelesen werden.") End If Next SaveSongsInDB(templist) End Sub Private Sub CheckFavorites() Dim FavDir As String = My.Settings.FavDir If FavDir.EndsWith("\") = False Then FavDir &= "\" If Directory.Exists(FavDir) Then For Each s As Song In Songs If Directory.Exists(FavDir & s.FolderName) Then s.ExistsInFavDir = True Else s.ExistsInFavDir = False End If Next Else For Each s As Song In Songs s.ExistsInFavDir = False Next End If End Sub Private Sub ReadSongFolder_Progress(Caption As String, Description As String) Handles Me.ReadProgress prgMain.Invoke(Sub() prgMain.Caption = Caption prgMain.Description = Description End Sub) End Sub Private Async Sub SaveSongsInDB(songs As List(Of Song)) Dim con = DB.getConnection Dim cmd As New SQLiteCommand(Nothing, con) With cmd.Parameters .Add("@path", DbType.String) .Add("@subpath", DbType.String) .Add("@artist", DbType.String) .Add("@title", DbType.String) .Add("@year", DbType.Int32) .Add("@language", DbType.String) .Add("@genre", DbType.String) .Add("@previewstart", DbType.Double) .Add("@bpm", DbType.Int32) .Add("@gap", DbType.Int32) .Add("@additional", DbType.String) .Add("@favorite", DbType.Boolean) .Add("@fileinfo", DbType.String) .Add("@filesong", DbType.String) .Add("@filecover", DbType.String) .Add("@filevideo", DbType.String) End With Await con.OpenAsync() cmd.CommandText = "DELETE FROM t_songs" Await cmd.ExecuteNonQueryAsync() cmd.CommandText = "INSERT INTO t_songs ( S_Path, S_Subpath, S_Info_Artist, S_Info_Title, S_Info_Year, S_Info_Language, S_Info_Genre, S_Info_Previewstart, S_Info_BPM, S_Info_GAP, S_Info_Additional, S_Favorite, S_File_Info, S_File_Song, S_File_Cover, S_File_Video ) VALUES ( @path, @subpath, @artist, @title, @year, @language, @genre, @previewstart, @bpm, @gap, @additional, @favorite, @fileinfo, @filesong, @filecover, @filevideo )" For Each s As Song In songs cmd.Parameters("@path").Value = s.RootDir cmd.Parameters("@subpath").Value = s.SubDirectory cmd.Parameters("@artist").Value = s.Artist cmd.Parameters("@title").Value = s.Title cmd.Parameters("@year").Value = s.Year cmd.Parameters("@language").Value = s.Language cmd.Parameters("@genre").Value = s.Genre cmd.Parameters("@previewstart").Value = s.Previewstart cmd.Parameters("@bpm").Value = s.BPM cmd.Parameters("@gap").Value = s.GAP cmd.Parameters("@additional").Value = Newtonsoft.Json.JsonConvert.SerializeObject(s.SongInfos) cmd.Parameters("@favorite").Value = s.IsFavorite cmd.Parameters("@fileinfo").Value = s.InfoFile cmd.Parameters("@filesong").Value = s.Songfilename cmd.Parameters("@filecover").Value = s.CoverFilename cmd.Parameters("@filevideo").Value = s.VideoFilename Await cmd.ExecuteNonQueryAsync() Next con.Close() End Sub Private Sub grdvSongs_RowClick(sender As Object, e As RowClickEventArgs) Handles grdvSongs.RowClick If e.Clicks = 2 AndAlso e.Button = MouseButtons.Left Then Dim s As Song = CType(grdvSongs.GetFocusedRow, Song) avPlayer.Play(s) End If End Sub Private Sub grdvSongs_FocusedRowChanged(sender As Object, e As FocusedRowChangedEventArgs) Handles grdvSongs.FocusedRowChanged Dim s As Song = TryCast(grdvSongs.GetFocusedRow, Song) If s Is Nothing Then grdSongInfos.DataSource = Nothing Exit Sub End If If s.SongInfos Is Nothing Then grdSongInfos.DataSource = Nothing Else grdSongInfos.DataSource = s.SongInfos grdvSongInfos.PopulateColumns() End If End Sub Private Sub menCopy_Click(sender As Object, e As EventArgs) Handles menCopy.Click Dim pfade As New Specialized.StringCollection For Each rh As Integer In grdvSongs.GetSelectedRows pfade.Add(CStr(grdvSongs.GetRowCellValue(rh, NameOf(Song.FullPath)))) Next Dim datas As New DataObject datas.SetFileDropList(pfade) Clipboard.SetDataObject(datas) End Sub Private Sub menOpenFolder_Click(sender As Object, e As EventArgs) Handles menOpenFolder.Click Dim pfad As String = CStr(grdvSongs.GetRowCellValue(grdvSongs.FocusedRowHandle, NameOf(Song.FullPath))) If pfad IsNot Nothing AndAlso Directory.Exists(pfad) Then Process.Start("explorer.exe", pfad) End If End Sub Private downHitInfo As GridHitInfo = Nothing Private Sub grdvSongs_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles grdvSongs.MouseDown Dim view As GridView = TryCast(sender, GridView) downHitInfo = Nothing Dim hitInfo As GridHitInfo = view.CalcHitInfo(New Point(e.X, e.Y)) If Control.ModifierKeys <> Keys.None Then Return End If If e.Button = MouseButtons.Left AndAlso hitInfo.InRow AndAlso hitInfo.RowHandle <> GridControl.NewItemRowHandle Then downHitInfo = hitInfo End If End Sub Private Sub grdvSongs_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles grdvSongs.MouseMove Dim view As GridView = TryCast(sender, GridView) If e.Button = MouseButtons.Left AndAlso downHitInfo IsNot Nothing Then Dim dragSize As Size = SystemInformation.DragSize Dim dragRect As New Rectangle(New Point(downHitInfo.HitPoint.X - dragSize.Width \ 2, downHitInfo.HitPoint.Y - dragSize.Height \ 2), dragSize) If (Not dragRect.Contains(New Point(e.X, e.Y))) Then Dim pfade As New Specialized.StringCollection For Each rh As Integer In grdvSongs.GetSelectedRows pfade.Add(CStr(grdvSongs.GetRowCellValue(rh, NameOf(Song.FullPath)))) Next Dim datas As New DataObject datas.SetFileDropList(pfade) 'view.GridControl.DoDragDrop(downHitInfo, DragDropEffects.All) view.GridControl.DoDragDrop(datas, DragDropEffects.Copy) downHitInfo = Nothing End If End If End Sub Private Sub cmsSongs_Opening(sender As Object, e As CancelEventArgs) Handles cmsSongs.Opening Dim hi As GridHitInfo = grdvSongs.CalcHitInfo(grdSongs.PointToClient(Cursor.Position)) If hi.InRow = False Then e.Cancel = True End If End Sub Private Async Sub btnSave_ItemClick(sender As Object, e As DevExpress.XtraBars.ItemClickEventArgs) Handles btnSave.ItemClick grdvSongs.CloseEditor() Dim FavDir = My.Settings.FavDir Dim ToAddToFavs As List(Of Song) = Songs.Where(Function(s) s.IsFavorite = True AndAlso s.ExistsInFavDir = False).ToList Dim ToDeleteFromFavs As List(Of Song) = Songs.Where(Function(s) s.IsFavorite = False AndAlso s.ExistsInFavDir = True).ToList If ToDeleteFromFavs.Count > 0 Then If MessageBox.Show($"Sind Sie sicher, dass Sie {ToDeleteFromFavs.Count} Lieder aus dem Favoritenverzeichnis entfernen möchten?{vbCrLf}Diese werden nicht in die ursprünglichen Verzeichnisse kopiert, daher sollten Sie ggf. sicherstellen ob diese Songs noch in den ursprünglichen Verzeichnissen vorhanden sind.{vbCrLf}{vbCrLf}Möchten Sie fortfahren?", "Löschen von Favoriten", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) <> DialogResult.Yes Then Exit Sub End If End If grdvSongs.BeginDataUpdate() prgMain.Visible = True Await Task.Run(Sub() SyncFavDir(Songs)) prgMain.Visible = False grdvSongs.EndDataUpdate() End Sub Private Async Sub SyncFavDir(songlist As List(Of Song)) Dim ToAddToFavs As List(Of Song) = songlist.Where(Function(s) s.IsFavorite = True AndAlso s.ExistsInFavDir = False).ToList Dim ToDeleteFromFavs As List(Of Song) = songlist.Where(Function(s) s.IsFavorite = False AndAlso s.ExistsInFavDir = True).ToList Dim FavDir = My.Settings.FavDir Dim errors As New List(Of String) Dim con As SQLiteConnection = DB.getConnection Dim cmd As New SQLiteCommand(Nothing, con) cmd.Parameters.Add("@id", DbType.Int32) cmd.Parameters.Add("@favorite", DbType.Boolean) cmd.CommandText = "UPDATE t_songs SET S_Favorite = @favorite WHERE S_ID = @id" Await con.OpenAsync 'Favoriten, die noch nicht im FavDir existieren, setzen Dim songcount As Integer = 1 For Each song In ToAddToFavs RaiseEvent ReadProgress("Aktualisiere Favoriten...", $"{songcount}\{ToAddToFavs.Count} Songs kopiert") Dim targetdir As String = FavDir & "\" & song.FolderName Try 'In Favoriten hinzufügen, auch wenn kopieren fehlschlägt cmd.Parameters("@id").Value = song.ID cmd.Parameters("@favorite").Value = song.IsFavorite Await cmd.ExecuteNonQueryAsync() FileFunctions.CopyDirectory(song.FullPath, targetdir) Catch ex As Exception errors.Add($"Kopieren von '{song.FolderName}' ins Favoritenverzeichnis fehlgeschlagen:{vbCrLf} -> {ex.Message}") End Try If Directory.Exists(targetdir) Then song.ExistsInFavDir = True End If songcount += 1 Next 'Favoriten, die keine Favoriten mehr sind, entfernen songcount = 1 For Each song In ToDeleteFromFavs RaiseEvent ReadProgress("Aktualisiere Favoriten...", $"{songcount}\{ToDeleteFromFavs.Count} Songs entfernt") Dim targetdir As String = FavDir & "\" & song.FolderName Try 'Von Favoriten entfernen, auch wenn löschen fehlschlägt. cmd.Parameters("@id").Value = song.ID cmd.Parameters("@favorite").Value = song.IsFavorite Await cmd.ExecuteNonQueryAsync Directory.Delete(targetdir, True) Catch ex As Exception errors.Add($"Löschen von '{song.FolderName}' aus dem Favoritenverzeichnis fehlgeschlagen:{vbCrLf} -> {ex.Message}") End Try If Directory.Exists(targetdir) = False Then song.ExistsInFavDir = False songcount += 1 Next con.Close() If errors.Count > 0 Then Me.Invoke(Sub() Dim frm As New frmErrorList(errors) frm.ShowDialog() End Sub) End If End Sub Private Sub btnPrint_ItemClick(sender As Object, e As DevExpress.XtraBars.ItemClickEventArgs) Handles btnPrint.ItemClick If Songs.LongCount(Function(s) s.ExistsInFavDir = False AndAlso s.IsFavorite = True) > 0 Then End If Dim rep As New repFavoritesList rep.DataSource = Songs.Where(Function(s) s.ExistsInFavDir = True) If SFD.ShowDialog = DialogResult.OK Then Try rep.ExportToPdf(SFD.FileName) Catch ex As Exception MessageBox.Show($"Die Datei konnte nicht gespeichert werden: {ex.Message}", "Fehler beim Speichern der Datei", MessageBoxButtons.OK, MessageBoxIcon.Error) End Try If File.Exists(SFD.FileName) Then Try Process.Start(SFD.FileName) Catch ex As Exception MessageBox.Show($"Die Datei konnte nicht geöffnet werden: {ex.Message}", "Fehler beim Öffnen der Datei", MessageBoxButtons.OK, MessageBoxIcon.Error) End Try End If End If 'rep.ShowPreview() End Sub Private Sub frmMain_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing avPlayer.Stop() End Sub End Class