@@ -19,8 +19,8 @@ internal partial class ExportVM : ViewModelObject
1919 #region Fields
2020
2121 private static readonly Logger _logger = SpotifyToM3ULogger . GetLogger ( typeof ( ExportVM ) ) ;
22- private SpotifyVM _spotifyVM ;
23- private LibraryVM _libraryVM ;
22+ private readonly SpotifyVM _spotifyVM ;
23+ private readonly LibraryVM _libraryVM ;
2424
2525 #endregion
2626
@@ -47,7 +47,6 @@ public ExportVM(INavigationService navigation) : base(navigation)
4747 _spotifyVM = App . Current . ServiceProvider . GetRequiredService < SpotifyVM > ( ) ;
4848 _libraryVM = App . Current . ServiceProvider . GetRequiredService < LibraryVM > ( ) ;
4949 Navigation . PropertyChanged += Navigation_PropertyChanged ;
50- _libraryVM . AudioFilesModifified += LibraryVM_AudioFilesModifified ;
5150 PropertyChanged += OnTextBoxPropertyChanged ;
5251
5352 _logger . Info ( $ "ExportVM initialized successfully. Default export path: { ExportPath } ") ;
@@ -64,57 +63,94 @@ private void Navigation_PropertyChanged(object? sender, PropertyChangedEventArgs
6463 if ( e . PropertyName == nameof ( Navigation . CurrentView ) )
6564 {
6665 ExportIsVisible = false ;
66+ CalculateExportRoot ( ) ;
6767 }
6868 }
6969
70- public void LibraryVM_AudioFilesModifified ( object ? sender , EventArgs e )
70+ private void CalculateExportRoot ( )
7171 {
72- _logger . Debug ( "Library audio files modified, recalculating export root path" ) ;
73-
74- try
72+ Task . Run ( ( ) =>
7573 {
76- if ( _libraryVM . RootPathes . Count == 0 )
74+ try
7775 {
78- _logger . Debug ( "No root paths available for relative export calculation" ) ;
79- return ;
80- }
76+ List < string ? > actualTrackPaths = _spotifyVM . PlaylistTracks
77+ . Where ( track => track . IsLocal && ! string . IsNullOrEmpty ( track . Path ) )
78+ . Select ( track => Path . GetDirectoryName ( track . Path ) )
79+ . Where ( dir => ! string . IsNullOrEmpty ( dir ) )
80+ . Distinct ( )
81+ . ToList ( ) ;
82+
83+ if ( actualTrackPaths . Count == 0 )
84+ {
85+ _logger . Debug ( "No matched tracks found for relative export calculation" ) ;
86+ return ;
87+ }
8188
82- Task . Run ( ( ) =>
83- {
84- try
89+ _logger . Debug ( $ "Calculating common root from { actualTrackPaths . Count } actual track directories") ;
90+
91+ string commonRoot = FindCommonRoot ( actualTrackPaths ) ;
92+ if ( ! string . IsNullOrEmpty ( commonRoot ) && commonRoot . Length > 3 )
8593 {
86- string [ ] [ ] collection = _libraryVM . RootPathes . ToList ( ) . Select ( x => x . Split ( "\\ " ) ) . ToArray ( ) ;
87- List < string > rootPath = new ( ) ;
88-
89- for ( int j = 0 ; j < collection [ 0 ] . Length ; j ++ )
90- if ( collection . All ( x => x [ j ] == collection [ 0 ] [ j ] ) )
91- rootPath . Add ( collection [ 0 ] [ j ] ) ;
92- string path = string . Join ( "\\ " , rootPath ) ;
93- if ( IOManager . TryGetFullPath ( path , out path ) )
94- {
95- _exportRoot = path + "\\ " ;
96- CanAsRelativ = true ;
97- _logger . Info ( $ "Export root path calculated: { _exportRoot } ") ;
98- }
99- else
100- {
101- CanAsRelativ = false ;
102- _logger . Warn ( $ "Invalid export root path calculated: { path } ") ;
103- }
94+ _exportRoot = ! commonRoot . EndsWith ( "\\ " ) ? commonRoot + "\\ " : commonRoot ;
95+ CanAsRelativ = true ;
96+ _logger . Info ( $ "Export root calculated from actual tracks: { _exportRoot } ") ;
10497 }
105- catch ( Exception ex )
98+ else
10699 {
107- _logger . Error ( ex , "Error calculating export root path" ) ;
108- CanAsRelativ = false ;
100+ _logger . Debug ( $ "Common root too generic or empty: '{ commonRoot } '") ;
109101 }
110- } ) ;
102+ }
103+ catch ( Exception ex )
104+ {
105+ _logger . Error ( ex , "Error calculating export root path" ) ;
106+ }
107+ } ) ;
108+ }
109+
110+ private static string FindCommonRoot ( List < string ? > paths )
111+ {
112+ if ( paths . Count == 0 ) return string . Empty ;
113+ if ( paths . Count == 1 ) return paths [ 0 ] ?? string . Empty ;
114+
115+ try
116+ {
117+ List < string [ ] > pathSegments = paths . ConvertAll ( path => Path . GetFullPath ( path ! ) . Split ( Path . DirectorySeparatorChar , StringSplitOptions . RemoveEmptyEntries ) ) ;
118+
119+ int minSegments = pathSegments . Min ( segments => segments . Length ) ;
120+ List < string > commonSegments = [ ] ;
121+
122+ for ( int i = 0 ; i < minSegments ; i ++ )
123+ {
124+ string currentSegment = pathSegments [ 0 ] [ i ] ;
125+ if ( pathSegments . All ( segments => segments [ i ] . Equals ( currentSegment , StringComparison . OrdinalIgnoreCase ) ) )
126+ commonSegments . Add ( currentSegment ) ;
127+ else
128+ break ;
129+ }
130+
131+ if ( commonSegments . Count == 0 ) return string . Empty ;
132+
133+ string commonPath = string . Join ( "\\ " , commonSegments ) ;
134+ return commonSegments [ 0 ] . EndsWith ( ':' ) ? commonPath + "\\ " : "\\ " + commonPath ;
111135 }
112- catch ( Exception ex )
136+ catch
113137 {
114- _logger . Error ( ex , "Error in LibraryVM_AudioFilesModifified handler" ) ;
138+ return string . Empty ;
115139 }
116140 }
117141
142+ private string GetRelativePath ( string fullPath , string rootPath )
143+ {
144+ if ( string . IsNullOrEmpty ( fullPath ) || string . IsNullOrEmpty ( rootPath ) )
145+ return fullPath ;
146+
147+ string normalizedRoot = rootPath . EndsWith ( "\\ " ) ? rootPath : rootPath + "\\ " ;
148+
149+ return fullPath . StartsWith ( normalizedRoot , StringComparison . OrdinalIgnoreCase )
150+ ? fullPath [ normalizedRoot . Length ..]
151+ : fullPath ;
152+ }
153+
118154 private void OnTextBoxPropertyChanged ( object ? sender , PropertyChangedEventArgs e )
119155 {
120156 if ( e . PropertyName == nameof ( ExportPath ) )
@@ -174,35 +210,27 @@ private void Export()
174210
175211 try
176212 {
177- // Create export directory
178213 Directory . CreateDirectory ( ExportPath ) ;
179- _logger . Debug ( $ "Export directory created/verified: { ExportPath } ") ;
180214
181- // Generate filename
182215 string playlistFileName = IOManager . RemoveInvalidFileNameChars ( _spotifyVM . PlaylistName ) + ".m3u8" ;
183216 string fullPath = Path . Combine ( ExportPath , playlistFileName ) ;
184- _logger . Debug ( $ "Full export file path: { fullPath } ") ;
185217
186- // Build playlist content
187218 List < string > files = new ( )
188219 {
189220 "#EXTM3U" ,
190221 "#" + _spotifyVM . PlaylistName + ".m3u8"
191222 } ;
192223
193- // Get selected tracks for export
194224 List < Track > selectedTracks = _spotifyVM . PlaylistTracks . Where ( t => t . IsIncludedInExport ) . ToList ( ) ;
195225 _logger . Info ( $ "Exporting { selectedTracks . Count } out of { _spotifyVM . PlaylistTracks . Count } total tracks") ;
196226
197- List < string > exportPaths = selectedTracks . Select ( track =>
198- ExportAsRelativ ? track . Path . Remove ( 0 , _exportRoot . Length ) : track . Path ) . ToList ( ) ;
227+ List < string > exportPaths = selectedTracks . ConvertAll ( track =>
228+ ExportAsRelativ ? GetRelativePath ( track . Path , _exportRoot ) : track . Path ) ;
199229
200230 files . AddRange ( exportPaths ) ;
201-
202- // Write the playlist file
203231 File . WriteAllLines ( fullPath , files ) ;
204- _logger . Info ( $ "Playlist exported successfully to: { fullPath } ") ;
205232
233+ _logger . Info ( $ "Playlist exported successfully to: { fullPath } ") ;
206234 ExportIsVisible = true ;
207235 }
208236 catch ( Exception ex )
@@ -220,10 +248,7 @@ private void Browse()
220248
221249 try
222250 {
223- FolderBrowserDialog folderBrowser = new ( )
224- {
225- ShowNewFolderButton = false
226- } ;
251+ FolderBrowserDialog folderBrowser = new ( ) { ShowNewFolderButton = false } ;
227252
228253 if ( folderBrowser . ShowDialog ( ) == DialogResult . OK )
229254 {
@@ -251,4 +276,4 @@ private void Browse()
251276 }
252277 }
253278 }
254- }
279+ }
0 commit comments