Skip to content

Commit d3b1370

Browse files
committed
UI improvements
1 parent 4a22b61 commit d3b1370

18 files changed

Lines changed: 699 additions & 315 deletions

LibVideo/App.xaml.cs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,18 +45,25 @@ protected override void OnStartup(StartupEventArgs e)
4545
LogException(args.Exception);
4646
}
4747

48-
private void LogException(Exception ex)
48+
protected override void OnExit(ExitEventArgs e)
4949
{
50-
if (ex == null) return;
51-
try
50+
if (MainWindow?.DataContext is IDisposable disposableVm)
5251
{
53-
File.AppendAllText("crash.log", $"[{DateTime.Now}] {ex.ToString()}\n\n");
52+
disposableVm.Dispose();
5453
}
55-
catch { }
54+
base.OnExit(e);
55+
}
56+
57+
private void LogException(Exception ex)
58+
{
59+
Logger.Error(ex, "Unhandled Exception");
5660
}
5761

62+
public static string CurrentLanguageCode { get; private set; } = "zh";
63+
5864
public static void ChangeLanguage(string langCode)
5965
{
66+
CurrentLanguageCode = langCode;
6067
var dictionary = new ResourceDictionary();
6168
if (langCode == "en")
6269
{

LibVideo/Data/DatabaseManager.cs

Lines changed: 45 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.Collections.Generic;
23
using System.Linq;
34
using LiteDB;
@@ -6,94 +7,87 @@
67

78
namespace LibVideo.Data
89
{
9-
public class DatabaseManager
10+
public class DatabaseManager : IDisposable
1011
{
1112
private readonly string dbPath;
13+
private readonly LiteDatabase _db;
1214

1315
public DatabaseManager()
1416
{
1517
dbPath = AppPaths.DatabaseFile;
18+
// LiteDatabase instance is thread-safe.
19+
_db = new LiteDatabase($"Filename={dbPath};Connection=Shared");
1620
}
1721

1822
public void SyncDiskItems(IEnumerable<VideoItem> currentFilesList, HashSet<string> scannedRoots)
1923
{
20-
using (var db = new LiteDatabase($"Filename={dbPath};Connection=Shared"))
21-
{
22-
var col = db.GetCollection<VideoItem>("videos");
23-
col.EnsureIndex(x => x.FullName, true);
24-
25-
var allDb = col.FindAll().ToList();
26-
var currentPaths = new HashSet<string>(currentFilesList.Select(x => x.FullName), System.StringComparer.OrdinalIgnoreCase);
24+
var col = _db.GetCollection<VideoItem>("videos");
25+
col.EnsureIndex(x => x.FullName, true);
26+
27+
var allDb = col.FindAll().ToList();
28+
var currentPaths = new HashSet<string>(currentFilesList.Select(x => x.FullName), StringComparer.OrdinalIgnoreCase);
2729

28-
foreach(var dbItem in allDb)
30+
foreach(var dbItem in allDb)
31+
{
32+
bool isUnderScannedRoot = scannedRoots.Any(root => dbItem.FullName.StartsWith(root, StringComparison.OrdinalIgnoreCase));
33+
if (isUnderScannedRoot)
2934
{
30-
bool isUnderScannedRoot = scannedRoots.Any(root => dbItem.FullName.StartsWith(root, System.StringComparison.OrdinalIgnoreCase));
31-
if (isUnderScannedRoot)
35+
if (!currentPaths.Contains(dbItem.FullName))
3236
{
33-
if (!currentPaths.Contains(dbItem.FullName))
34-
{
35-
col.Delete(dbItem.Id);
36-
}
37+
col.Delete(dbItem.Id);
3738
}
3839
}
39-
40-
var existingFiles = new HashSet<string>(col.FindAll().Select(v => v.FullName), System.StringComparer.OrdinalIgnoreCase);
41-
var newItems = currentFilesList.Where(i => !existingFiles.Contains(i.FullName)).ToList();
40+
}
41+
42+
var existingFiles = new HashSet<string>(col.FindAll().Select(v => v.FullName), StringComparer.OrdinalIgnoreCase);
43+
var newItems = currentFilesList.Where(i => !existingFiles.Contains(i.FullName)).ToList();
4244

43-
if (newItems.Count > 0)
44-
{
45-
col.InsertBulk(newItems);
46-
}
45+
if (newItems.Count > 0)
46+
{
47+
col.InsertBulk(newItems);
4748
}
4849
}
4950

5051
public List<VideoItem> GetAllItems()
5152
{
52-
using (var db = new LiteDatabase(dbPath))
53-
{
54-
var col = db.GetCollection<VideoItem>("videos");
55-
return col.FindAll().ToList();
56-
}
53+
var col = _db.GetCollection<VideoItem>("videos");
54+
return col.FindAll().ToList();
5755
}
5856

5957
public void ClearItems()
6058
{
61-
using (var db = new LiteDatabase(dbPath))
62-
{
63-
db.DropCollection("videos");
64-
}
59+
_db.DropCollection("videos");
6560
}
6661

6762
public void RemoveDirectoryItems(string directoryPath)
6863
{
69-
using (var db = new LiteDatabase(dbPath))
64+
var col = _db.GetCollection<VideoItem>("videos");
65+
var itemsToDelete = col.Find(x => x.FullName.StartsWith(directoryPath)).Select(x => x.Id).ToList();
66+
foreach (var id in itemsToDelete)
7067
{
71-
var col = db.GetCollection<VideoItem>("videos");
72-
var itemsToDelete = col.Find(x => x.FullName.StartsWith(directoryPath)).Select(x => x.Id).ToList();
73-
foreach (var id in itemsToDelete)
74-
{
75-
col.Delete(id);
76-
}
68+
col.Delete(id);
7769
}
7870
}
7971

8072
public void UpdateItemMetadata(VideoItem memItem)
8173
{
82-
using (var db = new LiteDatabase(dbPath))
74+
var col = _db.GetCollection<VideoItem>("videos");
75+
var dbItem = col.FindOne(x => x.FullName == memItem.FullName);
76+
if (dbItem != null)
8377
{
84-
var col = db.GetCollection<VideoItem>("videos");
85-
var dbItem = col.FindOne(x => x.FullName == memItem.FullName);
86-
if (dbItem != null)
87-
{
88-
dbItem.MetaTitle = memItem.MetaTitle;
89-
dbItem.MetaPlot = memItem.MetaPlot;
90-
dbItem.MetaGenre = memItem.MetaGenre;
91-
dbItem.MetaPosterPath = memItem.MetaPosterPath;
92-
dbItem.MetaRating = memItem.MetaRating;
93-
dbItem.HasScraped = memItem.HasScraped;
94-
col.Update(dbItem);
95-
}
78+
dbItem.MetaTitle = memItem.MetaTitle;
79+
dbItem.MetaPlot = memItem.MetaPlot;
80+
dbItem.MetaGenre = memItem.MetaGenre;
81+
dbItem.MetaPosterPath = memItem.MetaPosterPath;
82+
dbItem.MetaRating = memItem.MetaRating;
83+
dbItem.HasScraped = memItem.HasScraped;
84+
col.Update(dbItem);
9685
}
9786
}
87+
88+
public void Dispose()
89+
{
90+
_db?.Dispose();
91+
}
9892
}
9993
}

LibVideo/Helpers/AppPaths.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ static AppPaths()
2424
public static string PlayerFile => Path.Combine(_baseDir, "player.txt");
2525
public static string SearchHistoryFile => Path.Combine(_baseDir, "search_history.txt");
2626
public static string DatabaseFile => Path.Combine(_baseDir, "libvideo_db.db");
27+
public static string LogFile => Path.Combine(_baseDir, "crash.log");
2728

2829
/// <summary>
2930
/// One-time migration: copies config files from the exe directory to %AppData%\LibVideo\

LibVideo/Helpers/Debouncer.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using System;
2+
using System.Windows.Threading;
3+
4+
namespace LibVideo.Helpers
5+
{
6+
public class Debouncer
7+
{
8+
private readonly DispatcherTimer _timer;
9+
10+
public Debouncer(TimeSpan delay)
11+
{
12+
_timer = new DispatcherTimer { Interval = delay };
13+
_timer.Tick += (s, e) =>
14+
{
15+
_timer.Stop();
16+
Action?.Invoke();
17+
};
18+
}
19+
20+
private Action Action { get; set; }
21+
22+
public void Debounce(Action action)
23+
{
24+
Action = action;
25+
_timer.Stop();
26+
_timer.Start();
27+
}
28+
29+
public void Cancel()
30+
{
31+
_timer.Stop();
32+
}
33+
}
34+
}

LibVideo/Helpers/Logger.cs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using System;
2+
using System.IO;
3+
4+
namespace LibVideo.Helpers
5+
{
6+
public static class Logger
7+
{
8+
private static readonly object _lock = new object();
9+
10+
public static void Error(Exception ex, string context = "")
11+
{
12+
if (ex == null) return;
13+
Log($"[ERROR] {context}: {ex}");
14+
}
15+
16+
public static void Error(string message)
17+
{
18+
Log($"[ERROR] {message}");
19+
}
20+
21+
public static void Warn(string message)
22+
{
23+
Log($"[WARNING] {message}");
24+
}
25+
26+
public static void Info(string message)
27+
{
28+
Log($"[INFO] {message}");
29+
}
30+
31+
private static void Log(string message)
32+
{
33+
try
34+
{
35+
lock (_lock)
36+
{
37+
File.AppendAllText(AppPaths.LogFile, $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {message}\r\n\r\n");
38+
}
39+
}
40+
catch
41+
{
42+
// Last resort fallback
43+
}
44+
}
45+
}
46+
}

LibVideo/Helpers/TaskExtensions.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
4+
namespace LibVideo.Helpers
5+
{
6+
public static class TaskExtensions
7+
{
8+
/// <summary>
9+
/// Ensures unobserved exceptions inside fire-and-forget tasks are handled
10+
/// by injecting a catch block that logs them.
11+
/// </summary>
12+
public static async void SafeFireAndForget(this Task task)
13+
{
14+
try
15+
{
16+
await task.ConfigureAwait(false);
17+
}
18+
catch (Exception ex)
19+
{
20+
Logger.Error(ex, "SafeFireAndForget task swallowed an exception");
21+
}
22+
}
23+
}
24+
}

LibVideo/LibVideo2.csproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,12 @@
153153
<Compile Include="Helpers\AppPaths.cs" />
154154
<Compile Include="Helpers\MediaExtensions.cs" />
155155
<Compile Include="Helpers\PinyinHelper.cs" />
156+
<Compile Include="Helpers\Logger.cs" />
157+
<Compile Include="Helpers\TaskExtensions.cs" />
158+
<Compile Include="Helpers\Debouncer.cs" />
159+
<Compile Include="Services\PlayerService.cs" />
160+
<Compile Include="Services\SearchHistoryService.cs" />
161+
<Compile Include="Services\FileScanService.cs" />
156162
</ItemGroup>
157163
<ItemGroup>
158164
<Compile Include="Properties\AssemblyInfo.cs">

0 commit comments

Comments
 (0)