ソースを参照

Fix drive handling, folder listing and artist scrolling

Toby Chui 2 週間 前
コミット
73327d498f

+ 28 - 5
src/web/Musicify/backend/common.js

@@ -39,12 +39,35 @@ function getMusicRoots() {
     var roots = filelib.glob("/");
     var musicRoots = [];
     for (var i = 0; i < roots.length; i++) {
-        var musicPath = roots[i] + "Music/";
-        if ( roots[i] == "tmp:/" ||  roots[i] == "trash:/") continue; // skip tmp:/ and trash:/
-        if (!filelib.fileExists(musicPath)) {
-            filelib.mkdir(musicPath);
+        var root = roots[i];
+        if (root === "tmp:/" || root === "trash:/") continue;
+
+        // Normalise: ensure the root always has a trailing slash so that
+        // path concatenation works correctly on all platforms (e.g. "D:"
+        // vs "D:/").
+        if (root.charAt(root.length - 1) !== '/') root = root + "/";
+
+        var musicPath = root + "Music/";
+
+        if (root === "user:/") {
+            // For the user home drive auto-create the Music folder when absent.
+            if (!filelib.fileExists(musicPath)) {
+                filelib.mkdir(musicPath);
+            }
+            musicRoots.push(musicPath);
+        } else {
+            // For every other mounted drive (e.g. D:/ on Windows) only call
+            // filelib.fileExists – do NOT call mkdir.  On many mounted drives
+            // mkdir silently fails or creates a directory in an unexpected
+            // location, which then causes walk/readdir to return empty results
+            // even when the real Music folder is present.
+            if (filelib.fileExists(musicPath)) {
+                musicRoots.push(musicPath);
+            }
+            // If Music/ does not yet exist on this drive we skip it rather
+            // than creating an empty directory – the user can create their own
+            // folder structure and Musicify will pick it up on the next scan.
         }
-        musicRoots.push(musicPath);
     }
     return musicRoots;
 }

+ 26 - 10
src/web/Musicify/backend/listFolder.js

@@ -2,6 +2,13 @@
     Musicify - List Folder Contents
     Parameters: folder (vpath, URL-encoded)
     Returns: { folders: [string], songs: [{filepath, name, ext, filesize, hsize, mtime}] }
+
+    NOTE: We use filelib.readdir() rather than filelib.aglob() here because
+    aglob fails silently on non-user:/ virtual drives (e.g. "D:/" on Windows)
+    regardless of the sort/scope parameter supplied.  filelib.readdir() uses a
+    different internal code-path that works across every mounted drive, which is
+    why the legacy Music module (the only other backend that lists drives) also
+    uses readdir exclusively.
 */
 includes("common.js");
 requirelib("filelib");
@@ -13,18 +20,27 @@ function main() {
     }
 
     var decodedFolder = decodeURIComponent(folder);
-    // Remove trailing wildcard/slash if present
-    decodedFolder = decodedFolder.replace(/\/\*$/, "").replace(/\/$/, ""); // I tried to comment this but not working
-    var results = filelib.aglob(decodedFolder + "/*", "user");
+    // Normalise: strip any trailing "/*" glob suffix or bare trailing slash
+    decodedFolder = decodedFolder.replace(/\/\*$/, "").replace(/\/$/, "");
+
+    // filelib.readdir returns an array of entry objects:
+    //   { Filepath, Filename, IsDir, Ext, Filesize, LastModified }
+    // This works on all mounted drives including Windows drive-letter roots
+    // (D:/, E:/ …) where filelib.aglob fails silently.
+    var entries = filelib.readdir(decodedFolder);
     var subfolders = [];
     var songs = [];
-    for (var i = 0; i < results.length; i++) {
-        var f = results[i];
-        if (isHiddenFile(f)) continue;
-        if (filelib.isDir(f)) {
-            subfolders.push(f);
-        } else if (isMusicFile(f)) {
-            songs.push(buildSongEntry(f));
+
+    if (entries) {
+        for (var i = 0; i < entries.length; i++) {
+            var entry = entries[i];
+            var f = entry.Filepath;
+            if (!f || isHiddenFile(f)) continue;
+            if (entry.IsDir) {
+                subfolders.push(f);
+            } else if (isMusicFile(f)) {
+                songs.push(buildSongEntry(f));
+            }
         }
     }
 

+ 4 - 45
src/web/Musicify/index.html

@@ -231,8 +231,8 @@
         .artist-expand { color: var(--text3); transition: transform .2s; }
         .artist-expand.open { transform: rotate(90deg); }
         #artist-content-body{
-            height: calc(100% - 265px);
-            overflow-y:auto;
+            height: auto;
+            overflow: visible;
         }
         /* ── Song List ──────────────────────────────────────────────────── */
         .song-list { width: 100%; }
@@ -1028,9 +1028,8 @@
                     </template>
                    
                     <template x-if="!artistDetailOpen">
-                        <div id="artist-content-body" x-on:scroll="onArtistScroll($event)">
-                            <div :style="'height:' + artistTopSpacerHeight() + 'px'"></div>
-                            <template x-for="artist in visibleArtists()" :key="artist.path">
+                        <div id="artist-content-body">
+                            <template x-for="artist in artists" :key="artist.path">
                                 <div class="artist-row" x-on:click="selectArtist(artist)">
                                     <div class="artist-avatar" x-text="artist.name.charAt(0)"></div>
                                     <div class="artist-info">
@@ -1043,7 +1042,6 @@
                                     <i class="ui chevron right icon artist-expand" style="margin:0;"></i>
                                 </div>
                             </template>
-                            <div :style="'height:' + artistBottomSpacerHeight() + 'px'"></div>
                         </div>
                     </template>
 
@@ -1091,45 +1089,6 @@
                             </div>
                         </div>
                     </template>
-                    <!-- 
-                    <template x-for="artist in artists" :key="artist.path">
-                        <div>
-                            <div class="artist-row" x-on:click="selectArtist(artist)">
-                                <div class="artist-avatar" x-text="artist.name.charAt(0)"></div>
-                                <div class="artist-info">
-                                    <div class="artist-name"  x-text="artist.name"></div>
-                                    <div class="artist-count" x-text="artist.songCount + ' tracks'"></div>
-                                </div>
-                                <button class="ctrl-btn" style="margin-right:4px;" x-on:click.stop="playList(artist.songs, 0)">
-                                    <i class="ui play icon" style="margin:0;"></i>
-                                </button>
-                                <i class="ui chevron right icon artist-expand"
-                                   :class="{open: selectedArtist && selectedArtist.path === artist.path}"
-                                   style="margin:0;"></i>
-                            </div>
-                            
-                            <div x-show="selectedArtist && selectedArtist.path === artist.path"
-                                 style="background:var(--bg2);border-radius:0 0 8px 8px;margin-bottom:4px;">
-                                <template x-for="(song, idx) in artist.songs" :key="song.filepath">
-                                    <div class="song-row" :class="{active: isCurrentTrack(song)}"
-                                         style="grid-template-columns: 28px 36px 1fr 80px 28px;"
-                                         x-on:click="playSong(song, artist.songs, $event)">
-                                        <span class="song-idx"  x-text="idx + 1"></span>
-                                        <span class="song-play"><i class="ui play icon" style="margin:0;"></i></span>
-                                        <div class="song-cover-placeholder"><img :src="getCoverUrl(song)" loading="lazy" x-on:error="$event.target.src='img/placeholder.png'; $event.target.onerror=null"></div>
-                                        <div class="song-info">
-                                            <div class="song-name" x-text="song.name"></div>
-                                        </div>
-                                        <span class="song-size" x-text="song.hsize"></span>
-                                        <span class="song-menu" x-on:click.stop="promptAddToPlaylist(song, $event)">
-                                            <i class="ui ellipsis vertical icon" style="margin:0;font-size:14px;"></i>
-                                        </span>
-                                    </div>
-                                </template>
-                            </div>
-                        </div>
-                    </template>
-                    -->
                 </div>
             </div>
 

+ 32 - 4
src/web/Musicify/musicify.js

@@ -494,17 +494,32 @@ function musicifyApp() {
             return document.getElementById('artist-selected-content-body');
         },
 
+        _getArtistListContainer() {
+            return document.getElementById('artist-content-body');
+        },
+
         _getMainContentContainer() {
             return document.getElementById('mainContent');
         },
 
+        _getArtistViewportHeight() {
+            var artistListContainer = this._getArtistListContainer();
+            if (artistListContainer && artistListContainer.clientHeight) {
+                return artistListContainer.clientHeight;
+            }
+            var mainContainer = this._getMainContentContainer();
+            if (mainContainer && mainContainer.clientHeight) {
+                return mainContainer.clientHeight;
+            }
+            return window.innerHeight;
+        },
+
         selectArtist(artist) {
             var mainContainer = this._getMainContentContainer();
             if (mainContainer) {
                 this.artistListScrollTop = mainContainer.scrollTop;
                 this.artistScrollTop = mainContainer.scrollTop;
             }
-            console.log(this.artistListScrollTop,  this.artistScrollTop);
 
             this.selectedArtist = artist;
             this.artistDetailOpen = true;
@@ -535,7 +550,7 @@ function musicifyApp() {
         },
 
         visibleArtists() {
-            const viewportHeight = window.innerHeight;
+            const viewportHeight = this._getArtistViewportHeight();
 
             const start =
                 Math.max(
@@ -576,8 +591,21 @@ function musicifyApp() {
         },
 
         onArtistScroll(e) {
-            this.artistScrollTop = e.target.scrollTop;
-            this.artistListScrollTop = e.target.scrollTop;
+            var eventScrollTop = e && e.target ? e.target.scrollTop : 0;
+            var artistListContainer = this._getArtistListContainer();
+            var mainContainer = this._getMainContentContainer();
+            var scrollTop = Math.max(
+                eventScrollTop,
+                artistListContainer ? artistListContainer.scrollTop : 0,
+                mainContainer ? mainContainer.scrollTop : 0
+            );
+            this.artistScrollTop = scrollTop;
+            this.artistListScrollTop = scrollTop;
+        },
+
+        onMainContentScroll(e) {
+            if (this.view !== 'artists' || this.artistDetailOpen) return;
+            this.onArtistScroll(e);
         },
 
         onSelectedArtistListScroll(e) {