commit 44a89ad5892f53c06ba5b5685bac85daea3ac1da Author: Mario Steele Date: Mon Jul 21 13:31:24 2025 -0500 Initial Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1a5f2ee --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +bin/ +obj/ +/packages/ +riderModule.iml +/_ReSharper.Caches/ +.idea/ +*.user diff --git a/FreeTubeSyncer.sln b/FreeTubeSyncer.sln new file mode 100644 index 0000000..412411f --- /dev/null +++ b/FreeTubeSyncer.sln @@ -0,0 +1,16 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FreeTubeSyncer", "FreeTubeSyncer\FreeTubeSyncer.csproj", "{B32732C0-EEA7-4149-90D0-933E4DACE3B0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B32732C0-EEA7-4149-90D0-933E4DACE3B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B32732C0-EEA7-4149-90D0-933E4DACE3B0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B32732C0-EEA7-4149-90D0-933E4DACE3B0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B32732C0-EEA7-4149-90D0-933E4DACE3B0}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/FreeTubeSyncer/App.axaml b/FreeTubeSyncer/App.axaml new file mode 100644 index 0000000..bf82d18 --- /dev/null +++ b/FreeTubeSyncer/App.axaml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/FreeTubeSyncer/App.axaml.cs b/FreeTubeSyncer/App.axaml.cs new file mode 100644 index 0000000..dded1f1 --- /dev/null +++ b/FreeTubeSyncer/App.axaml.cs @@ -0,0 +1,53 @@ +using System; +using System.IO; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Markup.Xaml; +using FreeTubeSyncer.Library; +using FreeTubeSyncer.Models.DatabaseModels; + +namespace FreeTubeSyncer; + +public partial class App : Application +{ + private DBSyncWatcher _watcher; + public override void Initialize() + { + AvaloniaXamlLoader.Load(this); + } + + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + //desktop.MainWindow = new MainWindow(); + var path = ""; + if (OperatingSystem.IsWindows()) + path = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "FreeTube"); + else if (OperatingSystem.IsLinux()) + { + path = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".config", + "FreeTube"); + if (!Path.Exists(path)) + { + path = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".var", "app", "io.freetubeapp.FreeTube", "config", + "FreeTube"); + if (!Path.Exists(path)) + Console.WriteLine("Failed to find Path for FreeTube!"); + } + } + else if (OperatingSystem.IsMacOS()) + path = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "FreeTube"); + + _watcher = new DBSyncWatcher(path); + _watcher.WatchFiles.Add("history.db", typeof(History)); + _watcher.WatchFiles.Add("playlist.db", typeof(Playlist)); + _watcher.WatchFiles.Add("profiles.db", typeof(Profile)); + _watcher.WatchFiles.Add("search-history.db", typeof(SearchHistory)); + _watcher.WatchFiles.Add("settings.db", typeof(Setting)); + } + + base.OnFrameworkInitializationCompleted(); + } +} \ No newline at end of file diff --git a/FreeTubeSyncer/Assets/awaiting_sync.png b/FreeTubeSyncer/Assets/awaiting_sync.png new file mode 100644 index 0000000..32337e5 Binary files /dev/null and b/FreeTubeSyncer/Assets/awaiting_sync.png differ diff --git a/FreeTubeSyncer/Assets/freetube.png b/FreeTubeSyncer/Assets/freetube.png new file mode 100644 index 0000000..98f1e62 Binary files /dev/null and b/FreeTubeSyncer/Assets/freetube.png differ diff --git a/FreeTubeSyncer/Assets/sync_complete.png b/FreeTubeSyncer/Assets/sync_complete.png new file mode 100644 index 0000000..a071175 Binary files /dev/null and b/FreeTubeSyncer/Assets/sync_complete.png differ diff --git a/FreeTubeSyncer/FreeTubeSyncer.csproj b/FreeTubeSyncer/FreeTubeSyncer.csproj new file mode 100644 index 0000000..b76aa99 --- /dev/null +++ b/FreeTubeSyncer/FreeTubeSyncer.csproj @@ -0,0 +1,34 @@ + + + WinExe + net8.0 + enable + true + app.manifest + true + + + + + + + + + + + None + All + + + + + + + + + + + + + + diff --git a/FreeTubeSyncer/Library/DBSyncWatcher.cs b/FreeTubeSyncer/Library/DBSyncWatcher.cs new file mode 100644 index 0000000..865885a --- /dev/null +++ b/FreeTubeSyncer/Library/DBSyncWatcher.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.Json; + +namespace FreeTubeSyncer.Library; + +public class DBSyncWatcher +{ + private FileSystemWatcher _watcher; + public Dictionary WatchFiles { get; set; } = []; + + public delegate void DatabaseChange(string db, object value); + public event DatabaseChange OnDatabaseChange; + + public DBSyncWatcher(string path) + { + _watcher = new FileSystemWatcher(path); + _watcher.NotifyFilter = NotifyFilters.LastWrite | + NotifyFilters.CreationTime; + + _watcher.Changed += HandleChanged; + _watcher.Created += HandleCreated; + _watcher.Error += HandleError; + + _watcher.Filter = "*.db"; + _watcher.IncludeSubdirectories = true; + _watcher.EnableRaisingEvents = true; + } + + private void HandleChanged(object sender, FileSystemEventArgs e) + { + if (e.ChangeType != WatcherChangeTypes.Changed) return; + + var dbName = Path.GetFileName(e.FullPath); + + if (!WatchFiles.Keys.Contains(dbName)) return; + + Console.WriteLine("New Change in {0}", dbName); + + var data = File.ReadAllText(e.FullPath); + foreach (var line in data.Split('\n')) + { + var type = WatchFiles[dbName]; + var item = JsonSerializer.Deserialize(line, type); + if (item == null) continue; + OnDatabaseChange?.Invoke(dbName, item); + } + } + + private void HandleCreated(object sender, FileSystemEventArgs e) + { + if (e.ChangeType != WatcherChangeTypes.Created) return; + var dbName = Path.GetFileName(e.FullPath); + if (!WatchFiles.Keys.Contains(dbName)) return; + var data = File.ReadAllText(e.FullPath); + foreach (var line in data.Split('\n')) + { + var type = WatchFiles[dbName]; + var item = JsonSerializer.Deserialize(line, type); + if (item == null) continue; + OnDatabaseChange?.Invoke(dbName, item); + } + } + + private void HandleError(object sender, ErrorEventArgs e) + { + Console.WriteLine("Error: {0}\n{1}", e.GetException().Message, e.GetException().StackTrace); + } +} \ No newline at end of file diff --git a/FreeTubeSyncer/MainWindow.axaml b/FreeTubeSyncer/MainWindow.axaml new file mode 100644 index 0000000..797d249 --- /dev/null +++ b/FreeTubeSyncer/MainWindow.axaml @@ -0,0 +1,9 @@ + + Welcome to Avalonia! + diff --git a/FreeTubeSyncer/MainWindow.axaml.cs b/FreeTubeSyncer/MainWindow.axaml.cs new file mode 100644 index 0000000..fe27c5f --- /dev/null +++ b/FreeTubeSyncer/MainWindow.axaml.cs @@ -0,0 +1,11 @@ +using Avalonia.Controls; + +namespace FreeTubeSyncer; + +public partial class MainWindow : Window +{ + public MainWindow() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/FreeTubeSyncer/Models/DatabaseModels/History.cs b/FreeTubeSyncer/Models/DatabaseModels/History.cs new file mode 100644 index 0000000..be3eba3 --- /dev/null +++ b/FreeTubeSyncer/Models/DatabaseModels/History.cs @@ -0,0 +1,23 @@ +using System.Diagnostics.CodeAnalysis; + +namespace FreeTubeSyncer.Models.DatabaseModels; + +[SuppressMessage("ReSharper", "InconsistentNaming")] +public class History +{ + public string _id { get; set; } = string.Empty; + public string videoId { get; set; } = string.Empty; + public string title { get; set; } = string.Empty; + public string author { get; set; } = string.Empty; + public string authorId { get; set; } = string.Empty; + public long published { get; set; } + public string description { get; set; } = string.Empty; + public long viewCount { get; set; } + public long lengthSeconds { get; set; } + public float watchProgress { get; set; } + public long timeWatched { get; set; } + public bool isLive { get; set; } + public string type { get; set; } = string.Empty; + public string lastViewedPlaylistType { get; set; } = string.Empty; + public string? lastViewedPlaylistItemId { get; set; } +} \ No newline at end of file diff --git a/FreeTubeSyncer/Models/DatabaseModels/Playlist.cs b/FreeTubeSyncer/Models/DatabaseModels/Playlist.cs new file mode 100644 index 0000000..4e0c6cc --- /dev/null +++ b/FreeTubeSyncer/Models/DatabaseModels/Playlist.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace FreeTubeSyncer.Models.DatabaseModels; + +[SuppressMessage("ReSharper", "InconsistentNaming")] +public class Playlist +{ + public string _id { get; set; } = string.Empty; + public string playlistName { get; set; } = string.Empty; + public bool @protected { get; set; } + public List