1919package de .yaacc .upnp .server .contentdirectory ;
2020
2121import android .content .Context ;
22+ import android .content .SharedPreferences ;
2223import android .media .MediaMetadataRetriever ;
2324import android .net .Uri ;
2425import android .provider .DocumentsContract ;
2526import android .util .Base64 ;
2627import android .util .Log ;
2728
2829import androidx .documentfile .provider .DocumentFile ;
30+ import androidx .preference .PreferenceManager ;
2931
3032import org .fourthline .cling .support .model .DIDLObject ;
3133import org .fourthline .cling .support .model .Protocol ;
@@ -100,10 +102,10 @@ public Integer getSize(YaaccContentDirectory contentDirectory, String myId) {
100102
101103 @ Override
102104 public List <Container > browseContainer (YaaccContentDirectory contentDirectory , String myId , long firstResult , long maxResults , SortCriterion [] orderby ) {
103- Log .d ("SafFolderBrowser" , "browseContainer called with myId: " + myId );
105+ Log .d (getClass (). getName () , "browseContainer called with myId: " + myId );
104106 List <Container > result = new ArrayList <>();
105107 if (myId .equals (ContentDirectoryIDs .SAF_FOLDER .getId ())) {
106- Log .d ("SafFolderBrowser" , "Browsing root SAF folder" );
108+ Log .d (getClass (). getName () , "Browsing root SAF folder" );
107109 List <String > sortedPathes = new ArrayList <>(getSelectedSafPathes ());
108110 Collections .sort (sortedPathes );
109111
@@ -117,18 +119,18 @@ public List<Container> browseContainer(YaaccContentDirectory contentDirectory, S
117119 String title = file .getName () != null ? file .getName () : path ;
118120 String base64Str = Base64 .encodeToString (file .getUri ().toString ().getBytes (), Base64 .NO_WRAP );
119121 String folderId = ContentDirectoryIDs .SAF_PREFIX .getId () + base64Str ;
120- Log .d ("SafFolderBrowser" , "Creating root folder: " + title + " with ID: " + folderId );
122+ Log .d (getClass (). getName () , "Creating root folder: " + title + " with ID: " + folderId );
121123 StorageFolder folder = new StorageFolder (folderId , ContentDirectoryIDs .SAF_FOLDER .getId (), title , "yaacc" , 0 , null );
122124 result .add (folder );
123125 }
124126 }
125127 } else {
126128 // Browse subfolder
127- Log .d ("SafFolderBrowser" , "Browsing subfolder with ID: " + myId );
129+ Log .d (getClass (). getName () , "Browsing subfolder with ID: " + myId );
128130 String pathEnc = myId .substring (ContentDirectoryIDs .SAF_PREFIX .getId ().length ());
129- Log .d ("SafFolderBrowser" , "Encoded path: " + pathEnc );
131+ Log .d (getClass (). getName () , "Encoded path: " + pathEnc );
130132 String path = new String (Base64 .decode (pathEnc .getBytes (), Base64 .NO_WRAP ));
131- Log .d ("SafFolderBrowser" , "Decoded path: " + path );
133+ Log .d (getClass (). getName () , "Decoded path: " + path );
132134
133135 Uri uri = Uri .parse (path );
134136 DocumentFile root = null ;
@@ -137,62 +139,62 @@ public List<Container> browseContainer(YaaccContentDirectory contentDirectory, S
137139 if (path .contains ("/tree/" )) {
138140 // This is a tree URI, use it directly
139141 root = DocumentFile .fromTreeUri (getContext (), uri );
140- Log .d ("SafFolderBrowser" , "Using tree URI: " + path );
142+ Log .d (getClass (). getName () , "Using tree URI: " + path );
141143 } else {
142144 // This is a document URI, we need to find it within its parent tree
143- Log .d ("SafFolderBrowser" , "Document URI detected, finding parent tree: " + path );
145+ Log .d (getClass (). getName () , "Document URI detected, finding parent tree: " + path );
144146 // For now, skip these problematic folders to avoid showing parent content
145- Log .w ("SafFolderBrowser" , "Skipping document URI folder to avoid parent content" );
147+ Log .w (getClass (). getName () , "Skipping document URI folder to avoid parent content" );
146148 return result ;
147149 }
148150
149151 if (root != null && root .isDirectory ()) {
150152 DocumentFile [] files = root .listFiles ();
151- Log .d ("SafFolderBrowser" , "Found " + files .length + " files in subfolder" );
153+ Log .d (getClass (). getName () , "Found " + files .length + " files in subfolder" );
152154 int start = (int ) Math .max (0 , firstResult );
153155 int end = (int ) Math .min (files .length , start + maxResults );
154- Log .d ("SafFolderBrowser" , "Parent: " + myId );
156+ Log .d (getClass (). getName () , "Parent: " + myId );
155157 for (int i = start ; i < end ; i ++) {
156158 DocumentFile file = files [i ];
157159 if (file .isDirectory ()) {
158- Log .d ("SafFolderBrowser" , "Child: " + file .getUri (). toString ());
160+ Log .d (getClass (). getName () , "Child: " + file .getUri ());
159161 String title = file .getName () != null ? file .getName () : file .getUri ().toString ();
160162
161163 // Create tree URI for the child folder so it can be browsed properly
162164 try {
163165 String authority = file .getUri ().getAuthority ();
164166 String documentId = DocumentsContract .getDocumentId (file .getUri ());
165167 Uri childTreeUri = DocumentsContract .buildTreeDocumentUri (authority , documentId );
166- Log .d ("SafFolderBrowser" , "Child tree URI: " + childTreeUri );
168+ Log .d (getClass (). getName () , "Child tree URI: " + childTreeUri );
167169
168170 // Test if we can access this tree URI
169171 DocumentFile testAccess = DocumentFile .fromTreeUri (getContext (), childTreeUri );
170- if (testAccess != null && testAccess . canRead () ) {
172+ if (testAccess != null ) {
171173 String base64Str = Base64 .encodeToString (childTreeUri .toString ().getBytes (), Base64 .NO_WRAP );
172174 String childId = ContentDirectoryIDs .SAF_PREFIX .getId () + base64Str ;
173- Log .d ("SafFolderBrowser" , "Creating child folder: " + title + " with ID: " + childId );
175+ Log .d (getClass (). getName () , "Creating child folder: " + title + " with ID: " + childId );
174176 StorageFolder folder = new StorageFolder (childId , myId , title , "yaacc" , 0 , null );
177+ folder .setRestricted (testAccess .canRead ());
175178 result .add (folder );
176179 } else {
177-
178- Log .w ("SafFolderBrowser" , "Cannot access child tree URI, skipping folder: " + title );
180+ Log .w (getClass ().getName (), "Cannot access child tree URI, skipping folder: " + title );
179181 }
180182 } catch (Exception e ) {
181- Log .e ("SafFolderBrowser" , "Error creating tree URI for child, skipping folder: " + title , e );
183+ Log .e (getClass (). getName () , "Error creating tree URI for child, skipping folder: " + title , e );
182184 }
183185 }
184186 }
185187 } else {
186- Log .e ("SafFolderBrowser" , "Root DocumentFile is null or not a directory for path: " + path );
188+ Log .e (getClass (). getName () , "Root DocumentFile is null or not a directory for path: " + path );
187189 }
188190 }
189- Log .d ("SafFolderBrowser" , "Returning " + result .size () + " containers" );
191+ Log .d (getClass (). getName () , "Returning " + result .size () + " containers" );
190192 return result ;
191193 }
192194
193195 @ Override
194196 public List <Item > browseItem (YaaccContentDirectory contentDirectory , String myId , long firstResult , long maxResults , SortCriterion [] orderby ) {
195- Log .d ("SafFolderBrowser" , "browseItem called with myId: " + myId );
197+ Log .d (getClass (). getName () , "browseItem called with myId: " + myId );
196198 List <Item > result = new ArrayList <>();
197199 if (myId .equals (ContentDirectoryIDs .SAF_FOLDER .getId ())) {
198200 List <String > sortedPathes = new ArrayList <>(getSelectedSafPathes ());
@@ -205,38 +207,47 @@ public List<Item> browseItem(YaaccContentDirectory contentDirectory, String myId
205207 String path = sortedPathes .get (i );
206208 DocumentFile file = DocumentFile .fromSingleUri (getContext (), Uri .parse (path ));
207209 if (file != null && !file .isDirectory ()) {
208- addItem (contentDirectory , result , path , file , myId );
210+ addItem (contentDirectory , result , path , file , myId , ! file . canRead () );
209211 }
210212 }
211213 } else {
212214 // Browse subfolder items
213- Log .d ("SafFolderBrowser" , "Browsing subfolder items for: " + myId );
215+ Log .d (getClass (). getName () , "Browsing subfolder items for: " + myId );
214216 String pathEnc = myId .substring (ContentDirectoryIDs .SAF_PREFIX .getId ().length ());
215217 String path = new String (Base64 .decode (pathEnc .getBytes (), Base64 .NO_WRAP ));
216- Log .d ("SafFolderBrowser" , "Decoded path: " + path );
218+ Log .d (getClass (). getName () , "Decoded path: " + path );
217219 DocumentFile root = DocumentFile .fromTreeUri (getContext (), Uri .parse (path ));
218220 if (root != null && root .isDirectory ()) {
219- DocumentFile [] files = root .listFiles ();
220- Log .d ("SafFolderBrowser" , "Found " + files .length + " files in folder" );
221- int start = (int ) Math .max (0 , firstResult );
222- int end = (int ) Math .min (files .length , start + maxResults );
223- for (int i = start ; i < end ; i ++) {
224- DocumentFile file = files [i ];
225- if (!file .isDirectory ()) {
226- addItem (contentDirectory , result , file .getUri ().toString (), file , myId );
221+ if (root .canRead ()) {
222+ DocumentFile [] files = root .listFiles ();
223+ Log .d (getClass ().getName (), "Found " + files .length + " files in folder" );
224+ int start = (int ) Math .max (0 , firstResult );
225+ int end = (int ) Math .min (files .length , start + maxResults );
226+ for (int i = start ; i < end ; i ++) {
227+ DocumentFile file = files [i ];
228+ if (!file .isDirectory ()) {
229+ addItem (contentDirectory , result , file .getUri ().toString (), file , myId , !file .canRead ());
230+ }
227231 }
232+ } else {
233+ Log .w (getClass ().getName (), "Cannot access folder, skipping: " + path );
228234 }
229235 } else {
230- Log .e ("SafFolderBrowser" , "Root DocumentFile is null or not a directory for path: " + path );
236+ Log .e (getClass (). getName () , "Root DocumentFile is null or not a directory for path: " + path );
231237 }
232238 }
233- Log .d ("SafFolderBrowser" , "Returning " + result .size () + " items" );
239+ Log .d (getClass (). getName () , "Returning " + result .size () + " items" );
234240 return result ;
235241 }
236242
237- private void addItem (YaaccContentDirectory contentDirectory , List <Item > result , String path , DocumentFile file , String parentId ) {
243+ private void addItem (YaaccContentDirectory contentDirectory , List <Item > result , String path , DocumentFile file , String parentId , boolean restricted ) {
238244 String mimeTypeStr = file .getType ();
239-
245+ long currentTime = System .currentTimeMillis ();
246+ Log .d (getClass ().getName (), "Adding item for: " + path + " with mime type: " + mimeTypeStr );
247+ if (file .getName ().endsWith ("m3u" )) {
248+ Log .d (getClass ().getName (), "Ignoring m3u file" );
249+ return ;
250+ }
240251 if (mimeTypeStr != null ) {
241252 MimeType mimeType = MimeType .valueOf (mimeTypeStr );
242253 String mimeTypeMain = mimeType .getType ();
@@ -250,24 +261,30 @@ private void addItem(YaaccContentDirectory contentDirectory, List<Item> result,
250261 ProtocolInfo protocolInfo = new ProtocolInfo (Protocol .HTTP_GET , ProtocolInfo .WILDCARD , mimeType .toString (), getDLNAAttributes (mimeType ));
251262
252263 // Create resource without duration first for audio files
253- String duration = extractDuration (file );
264+
265+ String duration = null ;
266+ if (mimeTypeMain .equals ("audio" ) && !restricted ) {
267+ Log .d (getClass ().getName (), "Extracting duration for: " + file .getUri () + " took: " + (System .currentTimeMillis () - currentTime ) + "ms" );
268+ duration = extractDuration (file );
269+ Log .d (getClass ().getName (), "Extracted duration for: " + file .getUri () + " took: " + (System .currentTimeMillis () - currentTime ) + "ms" );
270+ }
254271 Res res = new Res (protocolInfo , file .length (), duration , null , uri );
255272
256273 Item item = null ;
257274 if (mimeTypeMain .equals ("audio" )) {
258275 item = new AudioItem (id , parentId , title , "yaacc" , res );
259- // Load duration asynchronously
260- //loadDurationAsync(file, item, res);
261276 } else if (mimeTypeMain .equals ("video" )) {
262277 item = new VideoItem (id , parentId , title , "yaacc" , res );
263278 } else if (mimeTypeMain .equals ("image" )) {
264279 item = new ImageItem (id , parentId , title , "yaacc" , res );
265280 }
266281
267282 if (item != null ) {
283+ item .setRestricted (restricted );
268284 result .add (item );
269285 }
270286 }
287+ Log .d (getClass ().getName (), "Added item for: " + path + "took: " + (System .currentTimeMillis () - currentTime ) + "ms" );
271288 }
272289
273290 /*
@@ -290,36 +307,43 @@ protected void onPostExecute(String duration) {
290307 // Replace the resource in the item
291308 item.getResources().clear();
292309 item.addResource(newRes);
293- Log.d("SafFolderBrowser" , "Updated duration for: " + item.getTitle() + " -> " + duration);
310+ Log.d(getClass().getName() , "Updated duration for: " + item.getTitle() + " -> " + duration);
294311 } catch (Exception e) {
295- Log.w("SafFolderBrowser" , "Failed to update duration for: " + item.getTitle(), e);
312+ Log.w(getClass().getName() , "Failed to update duration for: " + item.getTitle(), e);
296313 }
297314 }
298- Log.d("SafFolderBrowser" , "Item ready for playback: " + item.getTitle());
315+ Log.d(getClass().getName() , "Item ready for playback: " + item.getTitle());
299316 }
300317 }.execute();
301318 }
302319 */
303320 private String extractDuration (DocumentFile file ) {
304-
305321 MediaMetadataRetriever retriever = null ;
322+ SharedPreferences preferences = PreferenceManager .getDefaultSharedPreferences (getContext ());
323+ if (preferences .contains (getContext ().getString (R .string .settings_duration_format_key ) + file .getUri ())) {
324+ Log .d (getClass ().getName (), "Found duration in cache for: " + file .getUri ());
325+ return preferences .getString (getContext ().getString (R .string .settings_duration_format_key ) + file .getUri (), null );
326+ }
306327 try {
307328 retriever = new MediaMetadataRetriever ();
308329 retriever .setDataSource (getContext (), file .getUri ());
309330 String durationStr = retriever .extractMetadata (MediaMetadataRetriever .METADATA_KEY_DURATION );
310331
311332 if (durationStr != null ) {
312333 long durationMs = Long .parseLong (durationStr );
313- return FormatHelper .parseMillisToTimeStringTo (durationMs );
334+ String durationString = FormatHelper .parseMillisToTimeStringTo (durationMs );
335+ Log .d (getClass ().getName (), "Put duration in cache for: " + file .getUri ());
336+ preferences .edit ().putString (getContext ().getString (R .string .settings_duration_format_key ) + file .getUri (), durationString ).apply ();
337+ return durationString ;
314338 }
315339 } catch (Exception e ) {
316- Log .w ("SafFolderBrowser" , "Could not extract duration from: " + file .getUri (), e );
340+ Log .w (getClass (). getName () , "Could not extract duration from: " + file .getUri (), e );
317341 } finally {
318342 if (retriever != null ) {
319343 try {
320344 retriever .release ();
321345 } catch (Exception e ) {
322- Log .w ("SafFolderBrowser" , "Error releasing MediaMetadataRetriever" , e );
346+ Log .w (getClass (). getName () , "Error releasing MediaMetadataRetriever" , e );
323347 }
324348 }
325349 }
0 commit comments