using file_finder__test; using LibVLCSharp.Shared; using ModuleManager.Modules; using NotVPR_SideProjecktForVpr_FreeTime; using ShadowStream; using ShadowStream.LogHelper; using ShadowStream.Modules; using ShadowStream.Obejeckte; using ShadowStream.ObjecktForJason; using ShadowStream.Views; using System.Collections.Concurrent; using System.Collections.ObjectModel; using System.Net.Mime; using System.Text; using System.Text.RegularExpressions; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Threading; using Brushes = System.Windows.Media.Brushes; using Color = System.Windows.Media.Color; using File = System.IO.File; using Path = System.IO.Path; using Point = System.Windows.Point; namespace ModuleManager; /// /// Interaction logic for MainWindow.xaml /// //Quinn and Reda and Yazan public partial class MainWindow : Window { //quinn #region no dont cahnge //adding base components used threw out the code LogWindow logWin = new LogWindow(log.GetEntries()); static LogHelper log = new LogHelper(); PlaylistEditor popupWindow; ObjListBP _playlists = new ObjListBP(); private Catagory Muvie = new Catagory("Muvie"); private Catagory Serie = new Catagory("Serie"); private Catagory Music = new Catagory("Music"); private Catagory Photo = new Catagory("Photos"); private Catagory Favorites; List suportedVidioFiles = new List(); List suportedMusicFiles = new List(); List suportedPhotoFiles = new List(); //root directory List dirs = new List(); private int option = 2; int specificOption = 0; string rootPath = "M:/Media"; FileScanner fileScanner; //video player variables public string _path ; public string _category; StringConversions stringConversions = new StringConversions(); private DispatcherTimer _progressTimer; #endregion //Quinn #region vlc logic private LibVLC _libVLC; private LibVLCSharp.Shared.MediaPlayer _mediaPlayer; #endregion //code start private void MainWindow_Loaded(object sender, RoutedEventArgs e) { FirstCat?.Focus(); } public MainWindow() { #region Init wpf InitializeComponent(); //Initialise but Hide //logWin.Show(); //logWin.Hide(); //this.Hide(); //Begin Login Process var login = new LogIn(); login.Show(); //login.Owner = this; Favorites = new Catagory("Favorites",Favorites_Home); #endregion //Quinn Dont change or remuve!!! #region vlc init Core.Initialize(); // Important: load native libvlc binaries _libVLC = new LibVLC(); _mediaPlayer = new LibVLCSharp.Shared.MediaPlayer(_libVLC); VideoView.MediaPlayer = _mediaPlayer; _mediaPlayer.Volume = Convert.ToInt32(vol.Value); _progressTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) }; _progressTimer.Tick += ProgressTimer_Tick; #endregion #region BlutuethLogic InitBluetoothServer(); #endregion #region only exdend. no remuving of code //adding all extensions... example values added suportedVidioFiles.Add(".mp4"); suportedVidioFiles.Add(".mkv"); suportedMusicFiles.Add(".wav"); suportedMusicFiles.Add(".mp3"); suportedPhotoFiles.Add(".jpg"); Createscan(); //dirs.Add("C:/"); //execute and wait for task completion var tmp = fileScanner.ScanAllDrivesAsync(); //load json parallel to finding drives //add code laiter tmp.Wait(); foreach (var VARIABLE in tmp.Result) { dirs.Add(VARIABLE); } #endregion LoadSavedData(); } //Quinn #region dont change //reminder to self. if issue acures. check if anyone changed folder names/or if paths are being refrenced wrongly #region ScannLogic void Createscan() { //adding all string arrays to one temp array to do the initial scan of the designated drive int count = 0; string[] tmp = new string[suportedMusicFiles.Count + suportedVidioFiles.Count + suportedPhotoFiles.Count]; foreach (var suportedVidioFile in suportedVidioFiles) { tmp[count] = suportedVidioFile; count++; } foreach (var suportedMusicFile in suportedMusicFiles) { tmp[count] = suportedMusicFile; count++; } foreach (var suportedPhotoFile in suportedPhotoFiles) { tmp[count] = suportedPhotoFile; count++; } //initialise the file scanner with all files endings wanted fileScanner = new FileScanner(tmp); } #endregion #region Click Methods //-_- warum klapt es nicht private ProgressBar progressScann; private void OnItemPlayButtonClick(object sender, RoutedEventArgs e) { if (sender is Button btn && btn.Tag is string filePath) { string[] parts = filePath.Split("||"); string left = parts[0]; PlayVideo(left); } } private void OnItemNextButtonClick(object sender, RoutedEventArgs e) { setStrings(); switch (_category.ToLower()) { case "muvie": videoArrows(ref Muvie,true); break; case "serie": videoArrows(ref Serie,true);break; case "music": videoArrows(ref Music,true); break; case "photos": videoArrows(ref Photo,true); break; default: break; } } private void OnItemPriorButtonClick(object sender, RoutedEventArgs e) { setStrings(); switch (_category.ToLower()) { case "muvie": videoArrows(ref Muvie,false); break; case "serie": videoArrows(ref Serie,false);break; case "music": videoArrows(ref Music,false); break; case "photos": videoArrows(ref Photo,false); break; default: break; } } private void OnItemPauseBtn_Click(object sender, RoutedEventArgs e) { if (_mediaPlayer.IsPlaying) { _mediaPlayer.Pause(); // Pauses the video } else { _mediaPlayer.SetPause(false); // Unpauses the video } } private void Close_Player(object sender, RoutedEventArgs e) { _mediaPlayer.Stop(); VideoView.Visibility = Visibility.Collapsed; } private async void scanButton_Click(object sender, RoutedEventArgs e) { try { MessageBoxResult result = MessageBox.Show( "Do you want to replace existing items\nyes will delite existing items/no keeps them", "replace items", MessageBoxButton.YesNoCancel); if (result == MessageBoxResult.Cancel) return; else if (result == MessageBoxResult.Yes) { Muvie.clear(); Serie.clear(); Music.clear(); Photo.clear(); } log.Log("Scanning files..."); List tmp = new List(); switch (option) { case 0: foreach (var VARIABLE in dirs) { var scanResult = await fileScanner.ScanDriveParallel(VARIABLE); tmp.AddRange(scanResult); } break; case 1: var scanResult1 = await fileScanner.ScanDriveParallel(dirs[specificOption]); tmp.AddRange(scanResult1); break; case 2: var scanResult2 = await fileScanner.ScanDriveParallel(rootPath); tmp.AddRange(scanResult2); break; } log.Log($"Total scanned files: {tmp.Count}"); var classifier = new FileClassifier(); var (musicFiles, videoFiles, photoFiles) = await classifier.ClassifyFilesAsync(tmp, suportedMusicFiles, suportedVidioFiles, suportedPhotoFiles); var separator = new VideoSeparator(); var (series, movies) = await separator.SeparateVideosAsync(videoFiles); log.Log($"musicFiles count: {musicFiles.Count}"); log.Log($"videoFiles count: {videoFiles.Count}"); log.Log($"photoFiles count: {photoFiles.Count}"); log.Log($"series count: {series.Count}"); log.Log($"movies count: {movies.Count}"); Console.WriteLine($"Total scanned files: {tmp.Count}"); Console.WriteLine($"Total video files: {videoFiles.Count}"); Console.WriteLine($"Total musicFiles count: {musicFiles.Count}"); Console.WriteLine($"Total photo files: {photoFiles.Count}"); Console.WriteLine($"Total series count: {series.Count}"); Console.WriteLine($"Total muvies count: {movies.Count}"); if (result == MessageBoxResult.No) { var tmp2 = new List(); foreach (var VARIABLE in musicFiles) { if (!Music.getLink().Contains(VARIABLE)) ; tmp2.Add(VARIABLE); } musicFiles.Clear(); musicFiles.AddRange(tmp2); tmp2.Clear(); foreach (var VARIABLE in series) { if (!Serie.getLink().Contains(VARIABLE)) ; tmp2.Add(VARIABLE); } series.Clear(); series.AddRange(tmp2); tmp2.Clear(); foreach (var VARIABLE in movies) { if(!Muvie.getLink().Contains(VARIABLE)) ; tmp2.Add(VARIABLE); } movies.Clear(); movies.AddRange(tmp2); tmp2.Clear(); foreach (var VARIABLE in photoFiles) { if(!Photo.getLink().Contains(VARIABLE)) tmp2.Add(VARIABLE); } photoFiles.Clear(); photoFiles.AddRange(tmp2); tmp2.Clear(); } progressScann = new ProgressBar("ImageGen", musicFiles.Count + videoFiles.Count + photoFiles.Count); progressScann.Show(); videoFiles = null; log.Log("files sorted"); List muviesJS = new List(); List seriesJS = new List(); List photosJS = new List(); List musicJS = new List(); JasonToString jasonToString = new JasonToString(); var movieItems = await ItemCreater(movies, "Muvie", false); foreach (var item in movieItems) { BitmapImage bitmapImage = item.getImage(); if (bitmapImage != null) { try { string base64Image = jasonToString.BitmapToBase64String( BitmapConversions.BitmapImageToBitmap(bitmapImage)); Muvie.addItem(item); muviesJS.Add(new locJason { path = item.getLink(), imageData = base64Image, type = item.getType() }); } catch (Exception exception) { Console.WriteLine($"Error processing movie item {item.getLink()}: {exception.Message}"); log.Error($"Error processing movie item {item.getLink()}: {exception}"); } } else { Console.WriteLine($"Warning: Movie item {item.getLink()} has no image data."); log.Log($"Warning: Movie item {item.getLink()} has no image data."); } } var seriesItems = await ItemCreater(series, "Serie", false); foreach (var item in seriesItems) { BitmapImage bitmapImage = item.getImage(); if (bitmapImage != null) { try { string base64Image = jasonToString.BitmapToBase64String( BitmapConversions.BitmapImageToBitmap(bitmapImage)); Serie.addItem(item); seriesJS.Add(new locJason { path = item.getLink(), imageData = base64Image, type = item.getType() }); } catch (Exception exception) { Console.WriteLine($"Error processing series item {item.getLink()}: {exception.Message}"); log.Error($"Error processing series item {item.getLink()}: {exception}"); } } else { Console.WriteLine($"Warning: Series item {item.getLink()} has no image data."); log.Log($"Warning: Series item {item.getLink()} has no image data."); } } var photoItems = await ItemCreater(photoFiles, "Photo", true); foreach (var item in photoItems) { BitmapImage bitmapImage = item.getImage(); if (bitmapImage != null) { try { string base64Image = jasonToString.BitmapToBase64String( BitmapConversions.BitmapImageToBitmap(bitmapImage)); Photo.addItem(item); photosJS.Add(new locJason { path = item.getLink(), imageData = base64Image, type = item.getType() }); } catch (Exception exception) { Console.WriteLine($"Error processing photo item {item.getLink()}: {exception.Message}"); log.Error($"Error processing photo item {item.getLink()}: {exception}"); } } else { Console.WriteLine($"Warning: Photo item {item.getLink()} has no image data."); log.Log($"Warning: Photo item {item.getLink()} has no image data."); } } var musicItems = await ItemCreater(musicFiles, "Music", false); foreach (var item in musicItems) { BitmapImage bitmapImage = item.getImage(); if (bitmapImage != null) { try { string base64Image = jasonToString.BitmapToBase64String( BitmapConversions.BitmapImageToBitmap(bitmapImage)); Music.addItem(item); musicJS.Add(new locJason { path = item.getLink(), imageData = base64Image, type = item.getType() }); } catch (Exception exception) { Console.WriteLine($"Error processing music item {item.getLink()}: {exception.Message}"); log.Error($"Error processing music item {item.getLink()}: {exception}"); } } else { Console.WriteLine($"Info: Music item {item.getLink()} has no image data."); log.Log($"Info: Music item {item.getLink()} has no image data."); } } log.Log("scan finished"); Jason_Writer jason_Writer = new Jason_Writer(); List tasks = new List { jason_Writer.SaveList("Muvies", muviesJS), jason_Writer.SaveList("Series", seriesJS), jason_Writer.SaveList("Photos", photosJS), jason_Writer.SaveList("Music", musicJS) }; await Task.WhenAll(tasks); musicJS = null; seriesJS = null; photosJS = null; MenueItems(); progressScann.Hide(); MessageBox.Show("Scan finished"); } catch (Exception ex) { MessageBox.Show($"An error occurred during scanning:\n\n{ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error); log.Error($"An error occurred during scanning:\n\n{ex}"); } } private void PlayListButton_Click(object sender, RoutedEventArgs e) { ObservableCollection tmpBP= new ObservableCollection(); tmpBP.Add(Muvie); tmpBP.Add(Serie); tmpBP.Add(Photo); tmpBP.Add(Music); popupWindow = new PlaylistEditor(tmpBP,false,false); popupWindow.Owner = this; popupWindow.ShowInTaskbar = false; popupWindow.Show(); } private void EdditFav_Click(object sender, RoutedEventArgs e) { ObservableCollection tmpBP= new ObservableCollection(); tmpBP.Add(Muvie); tmpBP.Add(Serie); tmpBP.Add(Photo); tmpBP.Add(Music); popupWindow = new PlaylistEditor(tmpBP,true,true,Favorites); popupWindow.Owner = this; popupWindow.ShowInTaskbar = false; popupWindow.Show(); } #region Catagory btns private void Home_OnClick(object sender, RoutedEventArgs e) { Close_Player(); ScrollContentPlaylist.Visibility = Visibility.Collapsed; ScrollContentCat.Visibility = Visibility.Collapsed; ScrollContentHome.Visibility = Visibility.Visible; MenueItems(); SetInitialFocusForCurrentView(); } private void Musik_OnClick(object sender, RoutedEventArgs e) { Close_Player(); Name_of_Catagory_Text1.Visibility = Visibility.Collapsed; ScrollContentPlaylist.Visibility = Visibility.Collapsed; ScrollContentHome.Visibility = Visibility.Collapsed; ScrollContentCat.Visibility = Visibility.Visible; Name_of_Catagory1.Visibility = Visibility.Collapsed; MenueItems(ref Music); SetInitialFocusForCurrentView(); } private void Photo_OnClick(object sender, RoutedEventArgs e) { Close_Player(); Name_of_Catagory_Text1.Visibility = Visibility.Collapsed; ScrollContentPlaylist.Visibility = Visibility.Collapsed; ScrollContentHome.Visibility = Visibility.Collapsed; ScrollContentCat.Visibility = Visibility.Visible; Name_of_Catagory1.Visibility = Visibility.Collapsed; MenueItems(ref Photo); SetInitialFocusForCurrentView(); } private void Video_OnClick(object sender, RoutedEventArgs e) { Close_Player(); Name_of_Catagory_Text1.Visibility = Visibility.Visible; ScrollContentPlaylist.Visibility = Visibility.Collapsed; ScrollContentHome.Visibility = Visibility.Collapsed; ScrollContentCat.Visibility = Visibility.Visible; Name_of_Catagory1.Visibility = Visibility.Visible; MenueItems(ref Muvie,ref Serie); SetInitialFocusForCurrentView(); } private void PlayListButton_OnClick(object sender, RoutedEventArgs e) { Close_Player(); Name_of_Catagory_Text1.Visibility = Visibility.Collapsed; ScrollContentHome.Visibility = Visibility.Collapsed; ScrollContentCat.Visibility = Visibility.Collapsed; Name_of_Catagory1.Visibility = Visibility.Collapsed; PopulateAllCategoriesFromPlaylists(); // <-- This needs to set ScrollContentPlaylist visible SetInitialFocusForCurrentView(); } #endregion #endregion #region itemCreation public async Task> ItemCreater(List paths, string type, bool isFoto) { // Determine if it's music based on 'type' string bool isMusic = (type == "Music"); // Semaphore for concurrency control var semaphore = new SemaphoreSlim(Math.Max(1, Environment.ProcessorCount / 2)); // ConcurrentBag for thread-safe collection of items var itemsBag = new ConcurrentBag(); // Pre-load default music image once, if applicable BitmapImage defaultMusicImage = null; if (isMusic) { string baseDir = AppDomain.CurrentDomain.BaseDirectory; string imagePath = System.IO.Path.Combine(baseDir, "Resources", "Pics", "MusicD.jpeg"); if (System.IO.File.Exists(imagePath)) { try { await Application.Current.Dispatcher.InvokeAsync(() => { defaultMusicImage = new BitmapImage(new Uri(imagePath)); defaultMusicImage.DecodePixelWidth = 150; defaultMusicImage.DecodePixelHeight = 100; defaultMusicImage.Freeze(); // Crucial for cross-thread access }); } catch (Exception ex) { // Log error if default image itself cannot be loaded Console.WriteLine($"Error loading default music image {imagePath}: {ex.Message}"); // Consider adding to your specific logger here: log.Error(...) defaultMusicImage = null; } } else { // Inform the user if the default image is missing MessageBox.Show($"Default music image not found at: {imagePath}"); // Consider adding to your specific logger here: log.Error(...) } } // Create a list of tasks, one for each path var tasks = paths.Select(async path => { await semaphore.WaitAsync(); // Acquire a slot from the semaphore BitmapImage bitmapImage = null; // To hold the image for the current item Item newItem = null; // The Item object to be created try { if (isMusic) // Logic for Music files: extract album art or use default { bitmapImage = defaultMusicImage; // Start with the default image TagLib.File file = null; // TagLib.File object for disposing try { file = TagLib.File.Create(path); // Create TagLib file instance if (file.Tag.Pictures.Length > 0) // Check if album art exists { var pic = file.Tag.Pictures[0]; var ms = new System.IO.MemoryStream(pic.Data.Data); // Get image data as MemoryStream await Application.Current.Dispatcher.InvokeAsync(() => { var img = new BitmapImage(); img.BeginInit(); img.CacheOption = BitmapCacheOption.OnLoad; // Load full image data ms.Position = 0; // Reset stream position img.StreamSource = ms; // Set image source from stream img.EndInit(); img.Freeze(); // Make immutable for performance img.DecodePixelWidth = 150; img.DecodePixelHeight = 100; bitmapImage = img; // Overwrite default with extracted cover }); ms.Dispose(); // Dispose MemoryStream } } catch (Exception tagLibEx) { // Catch errors during TagLib processing (e.g., corrupted file, unsupported format) Console.WriteLine($"Error extracting album art for {path}: {tagLibEx.Message}"); // Consider adding to your specific logger here: log.Error(...) // bitmapImage remains defaultMusicImage or null if default also failed } finally { file?.Dispose(); // Ensure TagLib.File is disposed to release file locks } } else if (isFoto) // Logic for Photo files: direct image loading { try { await Application.Current.Dispatcher.InvokeAsync(() => { bitmapImage = new BitmapImage(); bitmapImage.BeginInit(); bitmapImage.CacheOption = BitmapCacheOption.OnLoad; // Load full image data bitmapImage.UriSource = new Uri(path, UriKind.Absolute); // Set URI source bitmapImage.DecodePixelWidth = 150; bitmapImage.DecodePixelHeight = 100; bitmapImage.EndInit(); bitmapImage.Freeze(); // Make immutable }); } catch (Exception ex) { // Catch errors during photo image loading Console.WriteLine($"Error loading photo image {path}: {ex.Message}"); // Consider adding to your specific logger here: log.Error(...) bitmapImage = null; // Set image to null on failure } } else // Logic for Video files: using VideoFrameExtractor { // VideoFrameExtractor.GetFrame200 should return null on failure (as previously discussed) bitmapImage = await Task.Run(() => VideoFrameExtractor.GetFrame200(path)); } // Create the Item object on the UI thread await Application.Current.Dispatcher.InvokeAsync(() => { // newItem will have the extracted image, default image, or null if all failed newItem = new Item(path, type, bitmapImage, isFoto, OnItemPlayButtonClick); }); itemsBag.Add(newItem); // Add the created item to the thread-safe bag Application.Current.Dispatcher.Invoke(() => progressScann.UpdateProgress(1)); // Update progress on UI thread } catch (Exception ex) { // Catch any unexpected exceptions during the processing of a single item Console.WriteLine($"Unexpected error processing item {path}: {ex.Message}"); // Consider adding to your specific logger here: log.Error(...) // Create a fallback Item with a null image to ensure the task completes await Application.Current.Dispatcher.InvokeAsync(() => { newItem = new Item(path, type, null, isFoto, OnItemPlayButtonClick); }); itemsBag.Add(newItem); // Add this fallback item Application.Current.Dispatcher.Invoke(() => progressScann.UpdateProgress(1)); // Still update progress } finally { semaphore.Release(); // Release the semaphore slot regardless of success or failure } return newItem; // Return the processed item (can have null image) }).ToList(); // Convert the collection of tasks to a List> // Wait for all individual item processing tasks to complete await Task.WhenAll(tasks); // Convert the ConcurrentBag to a List and return return itemsBag.ToList(); } #endregion #region Comunication public void ReciveErrorLog(string logMessage) { log.Error(logMessage); } public void PlayVideo(string filePath) { if(suportedVidioFiles.Contains(Path.GetExtension(filePath))||suportedPhotoFiles.Contains(Path.GetExtension(filePath))) VideoView.Visibility = Visibility.Visible; var media = new Media(_libVLC, filePath, FromType.FromPath); Console.WriteLine(filePath); media.ParsedChanged += (sender, args) => { if (args.ParsedStatus == MediaParsedStatus.Done) { Dispatcher.Invoke(() => { var duration = media.Duration; Console.WriteLine(duration); videoSliderInit(duration / 1000); title.Text=Path.GetFileNameWithoutExtension(filePath); }); } }; media.Parse(MediaParseOptions.ParseLocal); _mediaPlayer.Play(media); } public void setStrings() { #region type? var currentMedia = _mediaPlayer?.Media; if (currentMedia != null) { var mrl = currentMedia.Mrl; // This is the media resource locator _path = new Uri(mrl).LocalPath;; _path = stringConversions.ReplaceFirst(_path, '/', '\\'); Console.WriteLine("Now Playing Path: " + _path); } if (_mediaPlayer.IsPlaying) { _mediaPlayer.Stop(); } if (suportedMusicFiles.Any(keyword => _path.Contains(keyword, StringComparison.OrdinalIgnoreCase))) { _category = "music"; } else if (suportedPhotoFiles.Any(keyword => _path.Contains(keyword, StringComparison.OrdinalIgnoreCase))) { _category = "photos"; } else { if (Regex.IsMatch(_path, @"\b(E|EP|Ep|e|ep)\d+\b", RegexOptions.IgnoreCase)) { _category = "serie"; } else { _category = "muvie"; } } log.Log($"replacing: {_path} from {_category}"); #endregion } public void videoArrows(ref Catagory catagory, bool next) { catagory = catagory as Catagory; var items = catagory?.getAllItems(); if (catagory == null || items == null) { MessageBox.Show("Category or items are null"); return; } int tmp = catagory.contains(_path); if (tmp == -1 || tmp >= items.Count) { MessageBox.Show("Current item not found in category"); return; } Media media = null; int newIndex =0; if (next) { if (tmp + 1 < items.Count) { var link = items[tmp + 1]?.getLink(); if (!string.IsNullOrEmpty(link)) media = new Media(_libVLC, link, FromType.FromPath); } } if (!next) { newIndex = (tmp - 1 >= 0) ? tmp - 1 : items.Count - 1; // wrap around var link = items[newIndex]?.getLink(); Console.WriteLine(link); if (!string.IsNullOrEmpty(link)) media = new Media(_libVLC, link, FromType.FromPath); } if (media != null) { _mediaPlayer.Play(media); title.Text = items[newIndex].Name.ToString(); } else MessageBox.Show("Could not load media. Media or link is null."); } private void Close_Player() { _mediaPlayer.Stop(); VideoView.Visibility = Visibility.Collapsed; } public void RecivePlaylist(Catagory items) { _playlists.SharedRefs.Add(items); RecivePlaylist(); var writer = new Jason_Writer(); var tmp = new List(); int index = 0; foreach (var VARIABLE in items.getAllItems()) { tmp.Add(new locJasonPlaylist()); tmp[index].name = VARIABLE.Name; tmp[index].path = VARIABLE.Path; tmp[index].type = VARIABLE.Type; index++; } writer.SavePlaylistListAsync(items.name,tmp); Console.WriteLine("wrote"+items.name); } public void RecivePlaylist(Catagory items,bool favorite) { if (favorite) { Favorites.clear(); foreach (var VARIABLE in items.getAllItems()) { Favorites.addItem(VARIABLE); } } else { foreach (var VARIABLE in _playlists.SharedRefs) { if (VARIABLE.name == items.name) { VARIABLE.clear(); foreach (var var2 in items.getAllItems()) { VARIABLE.addItem(var2); } break; } } } RecivePlaylist(); var writer = new Jason_Writer(); var tmp = new List(); int index = 0; foreach (var VARIABLE in items.getAllItems()) { tmp.Add(new locJasonPlaylist()); tmp[index].name = VARIABLE.Name; tmp[index].path = VARIABLE.Path; tmp[index].type = VARIABLE.Type; index++; } writer.SavePlaylistListAsync(items.name,tmp); Console.WriteLine("faf updated"); if (favorite) Application.Current.Dispatcher.Invoke(() => { MenueItems(); // safely update UI }); foreach (var VARIABLE in items.getAllItems()) { Console.WriteLine(VARIABLE.Name); } } public void RecivePlaylist() { popupWindow.Close(); } private void PopulateAllCategoriesFromPlaylists() { PlaylistContentPanel.Children.Clear(); // Optional: clear previous entries foreach (Catagory category in _playlists.SharedRefs) { // Create TextBlock for category name var textBlock = new TextBlock { Text = category.name, FontSize = 18, Foreground = Brushes.White, Margin = new Thickness(0, 10, 0, 5) }; // Create WrapPanel for items var wrapPanel = new WrapPanel { Margin = new Thickness(0, 0, 0, 10) }; // Fill WrapPanel with items PopulatePanelWithItems(category.getAllItems(), wrapPanel); // Add both to the ScrollViewer's inner panel PlaylistContentPanel.Children.Add(textBlock); PlaylistContentPanel.Children.Add(wrapPanel); } // Make the ScrollViewer visible ScrollContentPlaylist.Visibility = Visibility.Visible; PopulatePlaylistsWithButtons(); } #region tvRemote BluetoothServer Server1; private void InitBluetoothServer() { Server1 = new BluetoothServer(); Server1.ClientConnected += () => { Dispatcher.Invoke(() => { MessageBox.Show("🔗 Client connected!"); }); }; Server1.MessageReceived += (msg) => { Dispatcher.BeginInvoke(new Action(()=> { //MessageBox.Show($"📨 Message: {msg.Trim()}"); switch (msg.Trim()) { // Arrow Up case "↑": // \u2191 var keyEventArgs = new KeyEventArgs( Keyboard.PrimaryDevice, PresentationSource.FromVisual(this), 0, Key.Up) { RoutedEvent = Keyboard.PreviewKeyDownEvent }; this.RaiseEvent(keyEventArgs); break; // Arrow Down case "↓": // \u2193 var keyEventArgs2 = new KeyEventArgs( Keyboard.PrimaryDevice, PresentationSource.FromVisual(this), 0, Key.Down) { RoutedEvent = Keyboard.PreviewKeyDownEvent }; this.RaiseEvent(keyEventArgs2); break; // Arrow Left case "←": // \u2190 var keyEventArgs3 = new KeyEventArgs( Keyboard.PrimaryDevice, PresentationSource.FromVisual(this), 0, Key.Left) { RoutedEvent = Keyboard.PreviewKeyDownEvent }; this.RaiseEvent(keyEventArgs3); break; // Arrow Right case "→": // \u2192 var keyEventArgs4 = new KeyEventArgs( Keyboard.PrimaryDevice, PresentationSource.FromVisual(this), 0, Key.Right) { RoutedEvent = Keyboard.PreviewKeyDownEvent }; this.RaiseEvent(keyEventArgs4); break; // Double Left case "⇚": // \u21DA var clickEventArgs1 = new RoutedEventArgs(Button.ClickEvent); prior.RaiseEvent(clickEventArgs1); break; // Double Right case "⇛": // \u21DB var clickEventArgs2 = new RoutedEventArgs(Button.ClickEvent); next.RaiseEvent(clickEventArgs2); break; // Center button (Dot) case "●": Dispatcher.BeginInvoke(new Action(() => { if (_mediaPlayer.IsPlaying) { // Simulate pause OnItemPauseBtn_Click(null, null); } else { // Simulate item selection var focused = Keyboard.FocusedElement as Button; focused?.RaiseEvent(new RoutedEventArgs(Button.ClickEvent)); } })); break; case "Vol -": if(_mediaPlayer.Volume>10) _mediaPlayer.Volume = _mediaPlayer.Volume - 10; else _mediaPlayer.Volume = 10; vol.Value = _mediaPlayer.Volume; break; case "Vol +": if(_mediaPlayer.Volume<90) _mediaPlayer.Volume = _mediaPlayer.Volume + 10; else _mediaPlayer.Volume = 100; vol.Value = _mediaPlayer.Volume; break; case string s when s.StartsWith("Time:"): string valuePart = s.Substring("Time:".Length); if (double.TryParse(valuePart, out double timeValue)) { _mediaPlayer.Time= Convert.ToInt32(valuePart);; } else { MessageBox.Show("Invalid time format."); } break; default: MessageBox.Show($"Unrecognized input: '{msg}'"); break; } Console.WriteLine(msg+":recived"); })); // Optional: echo back the message Server1.SendMessage("Echo: " + msg.Trim()); }; Server1.ErrorOccurred += (err) => { Dispatcher.Invoke(() => { MessageBox.Show($"⚠️ Error: {err}"); }); }; // Start server in background new Thread(() => Server1.Init()) { IsBackground = true }.Start(); } #endregion #endregion #endregion #region Menue void MenueItems(ref Catagory catagory) { PopulatePanelWithItemsCatagorised(Name_of_Catagory,ref catagory); } void MenueItems(ref Catagory catagory,ref Catagory catagory1) { PopulatePanelWithItemsCatagorised(Name_of_Catagory,ref catagory); PopulatePanelWithItemsCatagorised(Name_of_Catagory1,ref catagory1); } void MenueItems() { PopulatePanelWithItems(Favorites.getAllItems(), Favorites_Home); PopulatePanelWithItems(Muvie.getAllItems(), Muvies_Home); PopulatePanelWithItems(Serie.getAllItems(), Series_Home); PopulatePanelWithItems(Photo.getAllItems(), Photos_Home); PopulatePanelWithItems(Music.getAllItems(), Music_Home); } #endregion #region PanelPop private void PopulatePanelWithItemsCatagorised(Panel targetPanel,ref Catagory catagory) { if (catagory.name == "Serie") { Name_of_Catagory_Text1.Text = catagory.name; } else { Name_of_Catagory_Text.Text = catagory.name; } PopulatePanelWithItems(catagory.getAllItems(),targetPanel); } private void PopulatePanelWithItems(List items, Panel targetPanel) { targetPanel.Children.Clear(); byte tmp = 0; foreach (var item in items) { if(tmp >= 10) break; if (ScrollContentHome.Visibility == Visibility.Visible) tmp++; if (tmp < 10) { var name = item.getName(); var image = item.getImage(); var isFoto = item.getType() == "Photo"; var path = item.getLink(); var buttonText = isFoto ? "Show" : "Play"; if (image == null) continue; // Skip if image is not available // Container for stacking label, image, and button var container = new Grid { Width = 150, Height = 120, Margin = new Thickness(5) }; // Define rows for layout container.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) }); // Image container.RowDefinitions.Add(new RowDefinition { Height = new GridLength(30) }); // Button // Image var imgControl = new System.Windows.Controls.Image { Source = image, Width = 150, Height = 90, Stretch = Stretch.UniformToFill }; Grid.SetRow(imgControl, 0); container.Children.Add(imgControl); // Label (overlays top-left, optional) var label = new Label { Content = name, HorizontalAlignment = HorizontalAlignment.Left, VerticalAlignment = VerticalAlignment.Top, Background = new SolidColorBrush(Color.FromArgb(180, 0, 0, 0)), Foreground = Brushes.White, Padding = new Thickness(4), FontSize = 12 }; Grid.SetRow(label, 0); container.Children.Add(label); // Button var btn = new Button { Content = buttonText, Height = 25, Width = 140, HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center, Margin = new Thickness(0, 0, 0, 5) }; btn.Click += (s, e) => PlayVideo(path); Grid.SetRow(btn, 1); container.Children.Add(btn); // Add to target UI panel targetPanel.Children.Add(container); } } } private void PopulatePlaylistsWithButtons() { // Make sure the playlist ScrollViewer is visible ScrollContentHome.Visibility = Visibility.Collapsed; ScrollContentCat.Visibility = Visibility.Collapsed; ScrollContentPlaylist.Visibility = Visibility.Visible; // Clear previous content PlaylistContentPanel.Children.Clear(); // Add "New Playlist" button var newPlaylistBtn = new Button { Content = "New Playlist", Background = Brushes.Black, Foreground = Brushes.White, Margin = new Thickness(5) }; newPlaylistBtn.Click += PlayListButton_Click; PlaylistContentPanel.Children.Add(newPlaylistBtn); // Guard: If there are no playlists, show a message if (_playlists?.SharedRefs == null || !_playlists.SharedRefs.Any()) { PlaylistContentPanel.Children.Add(new TextBlock { Text = "No playlists available.", Foreground = Brushes.White, Margin = new Thickness(10) }); return; } // Add playlist sections foreach (var playlistCategory in _playlists.SharedRefs) { var container = new StackPanel { Margin = new Thickness(10), Orientation = Orientation.Vertical, HorizontalAlignment = HorizontalAlignment.Stretch }; // Header row with playlist name and ">>" button var headerRow = new DockPanel { LastChildFill = true, // Let the title take remaining space HorizontalAlignment = HorizontalAlignment.Stretch }; // Button (goes first, docked right) var btn = new Button { Content = ">>", Width = 30, Height = 25, Margin = new Thickness(10, 0, 0, 0), VerticalAlignment = VerticalAlignment.Center }; DockPanel.SetDock(btn, Dock.Right); btn.Click += (s, e) => ShowFullCategoryView(playlistCategory); headerRow.Children.Add(btn); // Playlist name var title = new TextBlock { Text = playlistCategory.name, FontSize = 18, FontWeight = FontWeights.Bold, Foreground = Brushes.White, VerticalAlignment = VerticalAlignment.Center, Margin = new Thickness(5) }; headerRow.Children.Add(title); container.Children.Add(headerRow); // Preview items in WrapPanel var wrap = new WrapPanel(); PopulatePanelWithItems(playlistCategory.getAllItems().Take(9).ToList(), wrap); container.Children.Add(wrap); PlaylistContentPanel.Children.Add(container); } } private void ShowFullCategoryView(Catagory category) { ScrollContentHome.Visibility = Visibility.Collapsed; ScrollContentPlaylist.Visibility = Visibility.Collapsed; ScrollContentCat.Visibility = Visibility.Visible; Name_of_Catagory_Text.Text = category.name; Name_of_Catagory1.Visibility = Visibility.Collapsed; PopulatePanelWithItems(category.getAllItems(), Name_of_Catagory); } #endregion #region load saved data private async void LoadSavedData() { try { Jason_Writer jason_Writer = new Jason_Writer(); // Uses default "Temp Data" folder // Load main media lists var muvies = await jason_Writer.LoadListAsync("Muvies"); var series = await jason_Writer.LoadListAsync("Series"); var photos = await jason_Writer.LoadListAsync("Photos"); var music = await jason_Writer.LoadListAsync("Music"); var favorites = await jason_Writer.LoadListAsync("Favorit"); // Initialize progress bar with total count progressScann = new ProgressBar("Reading saved data", muvies.Count + series.Count + photos.Count + music.Count + favorites.Count); progressScann.Show(); // Create Item lists from JSON List MuvieItems = CreateItemsFromJson(muvies); List SerieItems = CreateItemsFromJson(series); List PhotoItems = CreateItemsFromJson(photos, isPhoto: true); List MusicItems = CreateItemsFromJson(music, isPhoto: false, isMusic: true); Muvie.addItems(MuvieItems); Serie.addItems(SerieItems); Photo.addItems(PhotoItems); Music.addItems(MusicItems); // Combine all media items for playlist/favorite matching var allLocs = muvies.Concat(series).Concat(photos).Concat(music).ToList(); // Enrich favorite items by matching them with main list entries var enrichedFavorites = new List(); foreach (var fav in favorites) { var match = allLocs.FirstOrDefault(item => string.Equals(item.path, fav.path, StringComparison.OrdinalIgnoreCase)); if (match != null) { enrichedFavorites.Add(match); } else { enrichedFavorites.Add(fav); // fallback if not found Console.WriteLine($"[Warning] Favorite not found in main lists: {fav.path}"); } } List FavoriteItems = CreateItemsFromJson(enrichedFavorites); Favorites.addItems(FavoriteItems); // Load playlists var playlistNames = await jason_Writer.GetSavedPlaylistNamesAsync(); foreach (var playlistName in playlistNames) { var playlistEntries = await jason_Writer.LoadPlaylistListAsync(playlistName); var matchedLocs = new List(); foreach (var entry in playlistEntries) { var match = allLocs.FirstOrDefault(item => string.Equals(item.path, entry.path, StringComparison.OrdinalIgnoreCase)); if (match != null) { matchedLocs.Add(match); } else { Console.WriteLine($"[Warning] Playlist '{playlistName}' contains unknown path: {entry.path}"); } } List PlaylistItems = CreateItemsFromJson(matchedLocs); var playlistCategory = new Catagory(playlistName); playlistCategory.addItems(PlaylistItems); _playlists.SharedRefs.Add(playlistCategory); } MenueItems(); // Refresh the UI progressScann.Hide(); log.Log("Loaded saved data from JSON."); } catch (Exception ex) { MessageBox.Show($"Failed to load saved data:\n{ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error); log.Error($"LoadSavedData error: {ex}"); } } private List CreateItemsFromJson(List jsonItems) { return CreateItemsFromJson(jsonItems, isPhoto: false, isMusic: false); } private List CreateItemsFromJson(List jsonItems, bool isPhoto) { return CreateItemsFromJson(jsonItems, isPhoto, isMusic: false); } private List CreateItemsFromJson(List jsonItems, bool isPhoto, bool isMusic) { List items = new List(); JasonToString jasonToString = new JasonToString(); foreach (var loc in jsonItems) { BitmapImage bitmapImage = null; try { if (!string.IsNullOrEmpty(loc.imageData)) { bitmapImage = BitmapConversions.BitmapToBitmapImage( jasonToString.Base64StringToBitmap(loc.imageData) ); } else if (isMusic) { string defaultImagePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources", "Pics", "MusicD.jpeg"); if (File.Exists(defaultImagePath)) { bitmapImage = new BitmapImage(new Uri(defaultImagePath)); bitmapImage.Freeze(); } } else if (isPhoto && File.Exists(loc.path)) { bitmapImage = new BitmapImage(new Uri(loc.path)); bitmapImage.Freeze(); } } catch { // Handle any corrupt or missing image cases } items.Add(new Item(loc.path, loc.type, bitmapImage, isPhoto, OnItemPlayButtonClick)); Application.Current.Dispatcher.Invoke(() => { progressScann.UpdateProgress(1); }); } return items; } #endregion #region sliders private void RangeBase_OnValueChanged(object sender, RoutedPropertyChangedEventArgs e) { if (sender is Slider&&_mediaPlayer != null) { Slider slider = sender as Slider; int value = Convert.ToInt32(slider.Value); _mediaPlayer.Volume = value; Server1.SendMessage("Vol=" + value); log.Log("VolumeChanged"); } } #endregion #region progress slider private void ItemProgress_OnValueChanged(object sender, RoutedPropertyChangedEventArgs e) { if (itemProgress.IsMouseOver&&(itemProgress.Value>_mediaPlayer.Time/1000+2||itemProgress.Value<_mediaPlayer.Time/1000-2)) { ProgressBarValueChange(); } } private void ItemProgress_OnValueChangedMil(long timeStap) { itemProgress.Value = timeStap/1000; itemProgressVisual.Value = itemProgress.Value; if(Server1!=null) Server1.SendMessage("Progress="+itemProgress.Value); } private void ProgressBarValueChange() { itemProgressVisual.Value = itemProgress.Value; if(itemProgress.IsMouseOver) _mediaPlayer.Time = Convert.ToInt64(itemProgress.Value) * 1000; // convert to milliseconds } private void videoSliderInit(long timeStap) { itemProgress.Maximum = timeStap; itemProgressVisual.Maximum = timeStap; itemProgress.Minimum = 0; itemProgressVisual.Minimum = 0; StartProgressTimer(); Console.WriteLine(timeStap); } private void ItemProgress_OnPreviewMouseDown(object sender, MouseButtonEventArgs e) { var slider = sender as Slider; if (slider == null) return; Point position = e.GetPosition(slider); double relativePosition = position.X / slider.ActualWidth; relativePosition = Math.Max(0, Math.Min(1, relativePosition)); double newValue = slider.Minimum + (relativePosition * (slider.Maximum - slider.Minimum)); slider.Value = newValue; ProgressBarValueChange(); } private void ProgressTimer_Tick(object sender, EventArgs e) { if (!_mediaPlayer.IsPlaying) { StopProgressTimer(); } // Get current timestamp from media player in seconds long currentTimeInSeconds = _mediaPlayer.Time; // Update progress bar value ItemProgress_OnValueChangedMil(currentTimeInSeconds); } private void StartProgressTimer() { if (!_progressTimer.IsEnabled) _progressTimer.Start(); } private void StopProgressTimer() { if (_progressTimer.IsEnabled) _progressTimer.Stop(); } private void Key_Up(object sender, KeyEventArgs e) { switch (e.Key) { case Key.Escape: var result = MessageBox.Show( "Do you want to quit?", "Exit", MessageBoxButton.YesNo, MessageBoxImage.Question ); if (result == MessageBoxResult.Yes) { Application.Current.Shutdown(); } break; } if (VideoView.IsVisible) { switch (e.Key) { case Key.Left:_mediaPlayer.Time=_mediaPlayer.Time-10000;break; case Key.Right:_mediaPlayer.Time=_mediaPlayer.Time+10000;break; case Key.Space: if (_mediaPlayer.IsPlaying) _mediaPlayer.Pause(); else _mediaPlayer.Play(); break; } } else { } } #endregion #region arrow nav private void MainWindow_PreviewKeyDown(object sender, KeyEventArgs e) { if (Keyboard.FocusedElement is not DependencyObject focusedElement) return; var wrapPanel = FindAncestor(focusedElement); var stackPanel = FindAncestor(focusedElement); if (wrapPanel != null && wrapPanel.Children.Count > 0) { int focusedIndex = FindFocusedIndexInWrapPanel(wrapPanel, focusedElement); if (focusedIndex == -1) return; int columns = CalculateColumns(wrapPanel); switch (e.Key) { case Key.Left: e.Handled = true; if (IsLeftmostInRow(focusedIndex, columns)) { FirstCat?.Focus(); } else { MoveFocusInWrapPanel(wrapPanel, focusedIndex - 1); } break; case Key.Right: e.Handled = true; MoveFocusInWrapPanel(wrapPanel, focusedIndex + 1); break; case Key.Up: e.Handled = true; if (focusedIndex < columns) { if (stackPanel != null) { int currentWrapPanelIndex = stackPanel.Children.IndexOf(wrapPanel); if (currentWrapPanelIndex > 0) { for (int i = currentWrapPanelIndex - 1; i >= 0; i--) { if (stackPanel.Children[i] is WrapPanel prevWrapPanel && prevWrapPanel.Children.Count > 0) { int targetIndexInPrev = Math.Min(prevWrapPanel.Children.Count - 1, focusedIndex + ((prevWrapPanel.Children.Count / columns) * columns)); MoveFocusInWrapPanel(prevWrapPanel, targetIndexInPrev); return; } } } } } MoveFocusInWrapPanel(wrapPanel, focusedIndex - columns); break; case Key.Down: e.Handled = true; if (focusedIndex >= wrapPanel.Children.Count - columns) { if (stackPanel != null) { int currentWrapPanelIndex = stackPanel.Children.IndexOf(wrapPanel); if (currentWrapPanelIndex != -1 && currentWrapPanelIndex < stackPanel.Children.Count - 1) { for (int i = currentWrapPanelIndex + 1; i < stackPanel.Children.Count; i++) { if (stackPanel.Children[i] is WrapPanel nextWrapPanel && nextWrapPanel.Children.Count > 0) { int targetIndexInNext = Math.Min(columns - 1, nextWrapPanel.Children.Count - 1); MoveFocusInWrapPanel(nextWrapPanel, targetIndexInNext); return; } } } } } MoveFocusInWrapPanel(wrapPanel, focusedIndex + columns); break; } } else if (FirstCat.IsFocused && e.Key == Key.Right) { e.Handled = true; // Always handle the key if we are trying to move from FirstCat var firstFocusable = FindFirstFocusableInVisibleWrapPanel(); if (firstFocusable != null) { firstFocusable.Focus(); } else { // Fallback: Wenn kein spezifisches fokussierbares Element gefunden wurde, versuchen Sie, den ScrollViewer selbst zu fokussieren if (ScrollContentHome.Visibility == Visibility.Visible) ScrollContentHome.Focus(); else if (ScrollContentCat.Visibility == Visibility.Visible) ScrollContentCat.Focus(); else if (ScrollContentPlaylist.Visibility == Visibility.Visible) ScrollContentPlaylist.Focus(); } } else if (catPan.IsAncestorOf(Keyboard.FocusedElement as DependencyObject) && e.Key == Key.Down) { e.Handled = true; var buttons = catPan.Children.OfType