@@ -6,70 +6,96 @@ namespace LMP.Core.Services;
66
77/// <summary>
88/// Фабрика для создания TrackItemViewModel.
9- /// Использует кэширование на основе WeakReference .
9+ /// Использует TrackRegistry для Identity Map и кэширует ViewModel-и .
1010/// </summary>
11- public class TrackViewModelFactory (
12- AudioEngine audio ,
13- LibraryService library ,
14- DownloadService downloads ,
15- MusicLibraryManager manager )
11+ public class TrackViewModelFactory
1612{
17- private readonly AudioEngine _audio = audio ;
18- private readonly LibraryService _library = library ;
19- private readonly DownloadService _downloads = downloads ;
20- private readonly MusicLibraryManager _manager = manager ;
13+ private readonly AudioEngine _audio ;
14+ private readonly LibraryService _library ;
15+ private readonly DownloadService _downloads ;
16+ private readonly MusicLibraryManager _manager ;
17+ private readonly TrackRegistry _registry ;
18+ private readonly StreamCacheManager _cacheManager ;
2119
2220 private readonly ConcurrentDictionary < string , WeakReference < TrackItemViewModel > > _cache = new ( ) ;
2321
22+ public TrackViewModelFactory (
23+ AudioEngine audio ,
24+ LibraryService library ,
25+ DownloadService downloads ,
26+ MusicLibraryManager manager ,
27+ TrackRegistry registry ,
28+ StreamCacheManager cacheManager )
29+ {
30+ _audio = audio ;
31+ _library = library ;
32+ _downloads = downloads ;
33+ _manager = manager ;
34+ _registry = registry ;
35+ _cacheManager = cacheManager ;
36+ }
37+
2438 /// <summary>
25- /// Возвращает существующую или создает новую ViewModel для трека.
39+ /// Возвращает существующую или создаёт новую ViewModel для трека.
2640 /// </summary>
2741 public TrackItemViewModel GetOrCreate ( TrackInfo track , Action < TrackInfo > ? playAction = null )
2842 {
29- // 1. Проверяем кэш
30- if ( _cache . TryGetValue ( track . Id , out var weakRef ) &&
31- weakRef . TryGetTarget ( out var existing ) &&
43+ // Identity Map — получаем канонический экземпляр
44+ var canonical = _registry . RegisterOrUpdate ( track ) ;
45+
46+ // Проверяем кэш
47+ if ( _cache . TryGetValue ( canonical . Id , out var weakRef ) &&
48+ weakRef . TryGetTarget ( out var existing ) &&
3249 ! existing . IsDisposed )
3350 {
34- // Обновляем контекст использования
3551 existing . UpdatePlayAction ( playAction ) ;
3652 existing . IsQueueContext = false ;
3753
38- // НЕ нужно синхронизировать IsLiked/IsDownloaded —
39- // они автоматически берутся из Track через ObservableAsPropertyHelper
40-
41- return existing ;
54+ if ( ! ReferenceEquals ( existing . Track , canonical ) )
55+ {
56+ Log . Warn ( $ "[TrackFactory] VM track mismatch for { canonical . Id } , recreating") ;
57+ existing . Dispose ( ) ;
58+ }
59+ else
60+ {
61+ return existing ;
62+ }
4263 }
4364
44- // 2. Создаем новую
65+ // Создаём новую VM
4566 var vm = new TrackItemViewModel (
46- track ,
67+ canonical ,
4768 _audio ,
4869 _library ,
4970 _downloads ,
5071 _manager ,
72+ _cacheManager , // ← Передаём через DI
5173 playAction ) ;
5274
53- // 3. Сохраняем в кэш
54- _cache [ track . Id ] = new WeakReference < TrackItemViewModel > ( vm ) ;
75+ _cache [ canonical . Id ] = new WeakReference < TrackItemViewModel > ( vm ) ;
5576
5677 return vm ;
5778 }
5879
59- /// <summary>
60- /// Создает VM для использования в очереди воспроизведения.
61- /// </summary>
6280 public TrackItemViewModel CreateForQueue ( TrackInfo track , Action < TrackInfo > ? playAction = null )
6381 {
6482 var vm = GetOrCreate ( track , playAction ) ;
6583 vm . IsQueueContext = true ;
6684 return vm ;
6785 }
6886
69- /// <summary>
70- /// Удаляет "мертвые" ссылки из кэша.
71- /// </summary>
72- public void CleanupCache ( )
87+ public TrackItemViewModel ? TryGet ( string trackId )
88+ {
89+ if ( _cache . TryGetValue ( trackId , out var weakRef ) &&
90+ weakRef . TryGetTarget ( out var vm ) &&
91+ ! vm . IsDisposed )
92+ {
93+ return vm ;
94+ }
95+ return null ;
96+ }
97+
98+ public int CleanupCache ( )
7399 {
74100 var deadKeys = _cache
75101 . Where ( kvp => ! kvp . Value . TryGetTarget ( out var target ) || target . IsDisposed )
@@ -81,11 +107,10 @@ public void CleanupCache()
81107
82108 if ( deadKeys . Count > 0 )
83109 Log . Info ( $ "[TrackFactory] Cleaned { deadKeys . Count } dead items.") ;
110+
111+ return deadKeys . Count ;
84112 }
85113
86- /// <summary>
87- /// Полностью очищает кэш.
88- /// </summary>
89114 public void Clear ( )
90115 {
91116 foreach ( var kvp in _cache )
0 commit comments