fix: resolve nested download paths for covers and lyrics (#219)
This commit fixes an issue where cover art and lyrics files were being saved in deeply nested directories (e.g. Album/Artist/Album/file) instead of the correct Artist/Album/file path. It adds an isAlbum flag to the frontend hooks to prevent redundant path construction when downloading in an album context. Co-authored-by: Harley <git@haileywelsh.me>
This commit is contained in:
@@ -0,0 +1,39 @@
|
|||||||
|
# Development Guide
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
Before running the application locally, ensure you have the following installed:
|
||||||
|
|
||||||
|
1. **Go** (v1.23+ recommended)
|
||||||
|
2. **Node.js** (v16+ recommended)
|
||||||
|
3. **Wails CLI**
|
||||||
|
|
||||||
|
### Installing Wails
|
||||||
|
|
||||||
|
Since you already have Go installed, you can install the Wails CLI by running:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go install github.com/wailsapp/wails/v2/cmd/wails@latest
|
||||||
|
```
|
||||||
|
|
||||||
|
Ensure that your `go/bin` directory is in your PATH. You can check if it's installed by running `wails version`.
|
||||||
|
|
||||||
|
## Running the Application
|
||||||
|
|
||||||
|
To run the application in development mode (with hot reloading for both frontend and backend):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
wails dev
|
||||||
|
```
|
||||||
|
|
||||||
|
This will compiles the application and open it in a window. It also starts a browser-based version at http://localhost:34115.
|
||||||
|
|
||||||
|
## Building for Production
|
||||||
|
|
||||||
|
To create a production build (Application.app):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
wails build
|
||||||
|
```
|
||||||
|
|
||||||
|
The output will be in the `build/bin` directory.
|
||||||
@@ -357,14 +357,14 @@ function App() {
|
|||||||
onToggleSelectAll={toggleSelectAll}
|
onToggleSelectAll={toggleSelectAll}
|
||||||
onDownloadTrack={download.handleDownloadTrack}
|
onDownloadTrack={download.handleDownloadTrack}
|
||||||
onDownloadLyrics={(spotifyId, name, artists, albumName, _folderName, _isArtistDiscography, position, albumArtist, releaseDate, discNumber) =>
|
onDownloadLyrics={(spotifyId, name, artists, albumName, _folderName, _isArtistDiscography, position, albumArtist, releaseDate, discNumber) =>
|
||||||
lyrics.handleDownloadLyrics(spotifyId, name, artists, albumName, album_info.name, position, albumArtist, releaseDate, discNumber)
|
lyrics.handleDownloadLyrics(spotifyId, name, artists, albumName, album_info.name, position, albumArtist, releaseDate, discNumber, true)
|
||||||
}
|
}
|
||||||
onDownloadCover={(coverUrl, trackName, artistName, albumName, _folderName, _isArtistDiscography, position, trackId, albumArtist, releaseDate, discNumber) =>
|
onDownloadCover={(coverUrl, trackName, artistName, albumName, _folderName, _isArtistDiscography, position, trackId, albumArtist, releaseDate, discNumber) =>
|
||||||
cover.handleDownloadCover(coverUrl, trackName, artistName, albumName, album_info.name, position, trackId, albumArtist, releaseDate, discNumber)
|
cover.handleDownloadCover(coverUrl, trackName, artistName, albumName, album_info.name, position, trackId, albumArtist, releaseDate, discNumber, true)
|
||||||
}
|
}
|
||||||
onCheckAvailability={availability.checkAvailability}
|
onCheckAvailability={availability.checkAvailability}
|
||||||
onDownloadAllLyrics={() => lyrics.handleDownloadAllLyrics(track_list, album_info.name)}
|
onDownloadAllLyrics={() => lyrics.handleDownloadAllLyrics(track_list, album_info.name, undefined, true)}
|
||||||
onDownloadAllCovers={() => cover.handleDownloadAllCovers(track_list, album_info.name)}
|
onDownloadAllCovers={() => cover.handleDownloadAllCovers(track_list, album_info.name, true)}
|
||||||
onDownloadAll={() => download.handleDownloadAll(track_list, undefined, true)}
|
onDownloadAll={() => download.handleDownloadAll(track_list, undefined, true)}
|
||||||
onDownloadSelected={() =>
|
onDownloadSelected={() =>
|
||||||
download.handleDownloadSelected(selectedTracks, track_list, undefined, true)
|
download.handleDownloadSelected(selectedTracks, track_list, undefined, true)
|
||||||
@@ -684,7 +684,7 @@ function App() {
|
|||||||
<div className="min-h-screen bg-background flex flex-col">
|
<div className="min-h-screen bg-background flex flex-col">
|
||||||
<TitleBar />
|
<TitleBar />
|
||||||
<Sidebar currentPage={currentPage} onPageChange={setCurrentPage} />
|
<Sidebar currentPage={currentPage} onPageChange={setCurrentPage} />
|
||||||
|
|
||||||
{/* Main content area with sidebar offset */}
|
{/* Main content area with sidebar offset */}
|
||||||
<div className="flex-1 ml-14 mt-10 p-4 md:p-8">
|
<div className="flex-1 ml-14 mt-10 p-4 md:p-8">
|
||||||
<div className="max-w-4xl mx-auto space-y-6">
|
<div className="max-w-4xl mx-auto space-y-6">
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ export function useCover() {
|
|||||||
trackId?: string,
|
trackId?: string,
|
||||||
albumArtist?: string,
|
albumArtist?: string,
|
||||||
releaseDate?: string,
|
releaseDate?: string,
|
||||||
discNumber?: number
|
discNumber?: number,
|
||||||
|
isAlbum?: boolean
|
||||||
) => {
|
) => {
|
||||||
if (!coverUrl) {
|
if (!coverUrl) {
|
||||||
toast.error("No cover URL found for this track");
|
toast.error("No cover URL found for this track");
|
||||||
@@ -54,7 +55,8 @@ export function useCover() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// For playlist/discography, prepend the folder name
|
// For playlist/discography, prepend the folder name
|
||||||
if (playlistName) {
|
// Only do this if it's NOT an album download, to avoid double nesting (AlbumName/Artist/AlbumName)
|
||||||
|
if (playlistName && !isAlbum) {
|
||||||
outputDir = joinPath(os, outputDir, sanitizePath(playlistName.replace(/\//g, " "), os));
|
outputDir = joinPath(os, outputDir, sanitizePath(playlistName.replace(/\//g, " "), os));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,7 +115,8 @@ export function useCover() {
|
|||||||
|
|
||||||
const handleDownloadAllCovers = async (
|
const handleDownloadAllCovers = async (
|
||||||
tracks: TrackMetadata[],
|
tracks: TrackMetadata[],
|
||||||
playlistName?: string
|
playlistName?: string,
|
||||||
|
isAlbum?: boolean // Add isAlbum parameter
|
||||||
) => {
|
) => {
|
||||||
if (tracks.length === 0) {
|
if (tracks.length === 0) {
|
||||||
toast.error("No tracks to download covers");
|
toast.error("No tracks to download covers");
|
||||||
@@ -166,7 +169,8 @@ export function useCover() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// For playlist/discography, prepend the folder name
|
// For playlist/discography, prepend the folder name
|
||||||
if (playlistName) {
|
// Only do this if it's NOT an album download
|
||||||
|
if (playlistName && !isAlbum) {
|
||||||
outputDir = joinPath(os, outputDir, sanitizePath(playlistName.replace(/\//g, " "), os));
|
outputDir = joinPath(os, outputDir, sanitizePath(playlistName.replace(/\//g, " "), os));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ export function useLyrics() {
|
|||||||
position?: number,
|
position?: number,
|
||||||
albumArtist?: string,
|
albumArtist?: string,
|
||||||
releaseDate?: string,
|
releaseDate?: string,
|
||||||
discNumber?: number
|
discNumber?: number,
|
||||||
|
isAlbum?: boolean // Add isAlbum parameter
|
||||||
) => {
|
) => {
|
||||||
if (!spotifyId) {
|
if (!spotifyId) {
|
||||||
toast.error("No Spotify ID found for this track");
|
toast.error("No Spotify ID found for this track");
|
||||||
@@ -51,7 +52,8 @@ export function useLyrics() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// For playlist/discography, prepend the folder name
|
// For playlist/discography, prepend the folder name
|
||||||
if (playlistName) {
|
// Only do this if it's NOT an album download
|
||||||
|
if (playlistName && !isAlbum) {
|
||||||
outputDir = joinPath(os, outputDir, sanitizePath(playlistName.replace(/\//g, " "), os));
|
outputDir = joinPath(os, outputDir, sanitizePath(playlistName.replace(/\//g, " "), os));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,7 +115,8 @@ export function useLyrics() {
|
|||||||
const handleDownloadAllLyrics = async (
|
const handleDownloadAllLyrics = async (
|
||||||
tracks: TrackMetadata[],
|
tracks: TrackMetadata[],
|
||||||
playlistName?: string,
|
playlistName?: string,
|
||||||
_isArtistDiscography?: boolean
|
_isArtistDiscography?: boolean,
|
||||||
|
isAlbum?: boolean // Add isAlbum parameter
|
||||||
) => {
|
) => {
|
||||||
const tracksWithSpotifyId = tracks.filter((track) => track.spotify_id);
|
const tracksWithSpotifyId = tracks.filter((track) => track.spotify_id);
|
||||||
|
|
||||||
@@ -150,12 +153,12 @@ export function useLyrics() {
|
|||||||
|
|
||||||
// Replace forward slashes in template data values to prevent them from being interpreted as path separators
|
// Replace forward slashes in template data values to prevent them from being interpreted as path separators
|
||||||
const placeholder = "__SLASH_PLACEHOLDER__";
|
const placeholder = "__SLASH_PLACEHOLDER__";
|
||||||
|
|
||||||
// Determine if we should use album track number or sequential position
|
// Determine if we should use album track number or sequential position
|
||||||
const useAlbumTrackNumber = settings.folderTemplate?.includes("{album}") || false;
|
const useAlbumTrackNumber = settings.folderTemplate?.includes("{album}") || false;
|
||||||
// Use track.track_number for album context, otherwise use sequential position (consistent with track download)
|
// Use track.track_number for album context, otherwise use sequential position (consistent with track download)
|
||||||
const trackPosition = useAlbumTrackNumber ? (track.track_number || i + 1) : (i + 1);
|
const trackPosition = useAlbumTrackNumber ? (track.track_number || i + 1) : (i + 1);
|
||||||
|
|
||||||
// Build output path using template system
|
// Build output path using template system
|
||||||
const templateData: TemplateData = {
|
const templateData: TemplateData = {
|
||||||
artist: track.artists?.replace(/\//g, placeholder),
|
artist: track.artists?.replace(/\//g, placeholder),
|
||||||
@@ -166,7 +169,8 @@ export function useLyrics() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// For playlist/discography, prepend the folder name
|
// For playlist/discography, prepend the folder name
|
||||||
if (playlistName) {
|
// Only do this if it's NOT an album download
|
||||||
|
if (playlistName && !isAlbum) {
|
||||||
outputDir = joinPath(os, outputDir, sanitizePath(playlistName.replace(/\//g, " "), os));
|
outputDir = joinPath(os, outputDir, sanitizePath(playlistName.replace(/\//g, " "), os));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user