Add cross-platform path handling (#89)
Add cross-platform path handling support - Add sanitizePath, joinPath, buildOutputPath utilities - Add operatingSystem to Settings interface - Replace hardcoded Windows paths with dynamic path handling - Support Windows, Linux, and macOS
This commit is contained in:
+15
-16
@@ -36,6 +36,7 @@ import {
|
|||||||
TooltipTrigger,
|
TooltipTrigger,
|
||||||
} from "@/components/ui/tooltip";
|
} from "@/components/ui/tooltip";
|
||||||
import { Spinner } from "@/components/ui/spinner";
|
import { Spinner } from "@/components/ui/spinner";
|
||||||
|
import { joinPath, sanitizePath } from "./lib/utils";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [spotifyUrl, setSpotifyUrl] = useState("");
|
const [spotifyUrl, setSpotifyUrl] = useState("");
|
||||||
@@ -125,32 +126,30 @@ function App() {
|
|||||||
const query = trackName && artistName ? `${trackName} ${artistName}` : undefined;
|
const query = trackName && artistName ? `${trackName} ${artistName}` : undefined;
|
||||||
|
|
||||||
// Build output directory based on settings
|
// Build output directory based on settings
|
||||||
|
const os = settings.operatingSystem;
|
||||||
|
|
||||||
|
// Base download path
|
||||||
let outputDir = settings.downloadPath;
|
let outputDir = settings.downloadPath;
|
||||||
|
|
||||||
// For playlist or artist discography downloads
|
// For playlist or artist discography downloads
|
||||||
|
|
||||||
|
// Playlist or discography
|
||||||
if (playlistName) {
|
if (playlistName) {
|
||||||
const sanitizedPlaylist = playlistName.replace(/[<>:"/\\|?*]/g, '_').trim();
|
outputDir = joinPath(os, outputDir, sanitizePath(playlistName, os));
|
||||||
outputDir = `${settings.downloadPath}\\${sanitizedPlaylist}`;
|
|
||||||
|
|
||||||
// For artist discography: only use album subfolder (artist is redundant)
|
|
||||||
if (isArtistDiscography) {
|
if (isArtistDiscography) {
|
||||||
// Only add album subfolder if enabled
|
// Only album subfolder
|
||||||
if (settings.albumSubfolder && albumName) {
|
if (settings.albumSubfolder && albumName) {
|
||||||
const sanitizedAlbum = albumName.replace(/[<>:"/\\|?*]/g, '_').trim();
|
outputDir = joinPath(os, outputDir, sanitizePath(albumName, os));
|
||||||
outputDir = `${outputDir}\\${sanitizedAlbum}`;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// For playlist: use both artist and album subfolders if enabled
|
// Playlist rules:
|
||||||
// Add artist subfolder if enabled
|
|
||||||
if (settings.artistSubfolder && artistName) {
|
if (settings.artistSubfolder && artistName) {
|
||||||
const sanitizedArtist = artistName.replace(/[<>:"/\\|?*]/g, '_').trim();
|
outputDir = joinPath(os, outputDir, sanitizePath(artistName, os));
|
||||||
outputDir = `${outputDir}\\${sanitizedArtist}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add album subfolder if enabled
|
|
||||||
if (settings.albumSubfolder && albumName) {
|
if (settings.albumSubfolder && albumName) {
|
||||||
const sanitizedAlbum = albumName.replace(/[<>:"/\\|?*]/g, '_').trim();
|
outputDir = joinPath(os, outputDir, sanitizePath(albumName, os));
|
||||||
outputDir = `${outputDir}\\${sanitizedAlbum}`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ export interface Settings {
|
|||||||
artistSubfolder: boolean;
|
artistSubfolder: boolean;
|
||||||
albumSubfolder: boolean;
|
albumSubfolder: boolean;
|
||||||
trackNumber: boolean;
|
trackNumber: boolean;
|
||||||
|
operatingSystem: "Windows" | "linux/MacOS"
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DEFAULT_SETTINGS: Settings = {
|
export const DEFAULT_SETTINGS: Settings = {
|
||||||
@@ -20,16 +21,20 @@ export const DEFAULT_SETTINGS: Settings = {
|
|||||||
artistSubfolder: false,
|
artistSubfolder: false,
|
||||||
albumSubfolder: false,
|
albumSubfolder: false,
|
||||||
trackNumber: false,
|
trackNumber: false,
|
||||||
|
operatingSystem: "Windows"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: add mac/linux defaults
|
||||||
|
const DEFAULT_PATH : string = "C:\\Users\\Public\\Music";
|
||||||
|
|
||||||
async function fetchDefaultPath(): Promise<string> {
|
async function fetchDefaultPath(): Promise<string> {
|
||||||
try {
|
try {
|
||||||
const data = await GetDefaults();
|
const data = await GetDefaults();
|
||||||
return data.downloadPath || "C:\\Users\\Public\\Music";
|
return data.downloadPath || DEFAULT_PATH;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to fetch default path:", error);
|
console.error("Failed to fetch default path:", error);
|
||||||
}
|
}
|
||||||
return "C:\\Users\\Public\\Music";
|
return DEFAULT_PATH;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SETTINGS_KEY = "spotiflac-settings";
|
const SETTINGS_KEY = "spotiflac-settings";
|
||||||
|
|||||||
@@ -1,6 +1,35 @@
|
|||||||
import { clsx, type ClassValue } from "clsx"
|
import { clsx, type ClassValue } from "clsx"
|
||||||
import { twMerge } from "tailwind-merge"
|
import { twMerge } from "tailwind-merge"
|
||||||
|
import type { Settings } from "./settings";
|
||||||
|
|
||||||
export function cn(...inputs: ClassValue[]) {
|
export function cn(...inputs: ClassValue[]) {
|
||||||
return twMerge(clsx(inputs))
|
return twMerge(clsx(inputs))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function sanitizePath(input: string, os: string): string {
|
||||||
|
if (os === "Windows") {
|
||||||
|
return input.replace(/[<>:"/\\|?*]/g, "_");
|
||||||
|
}
|
||||||
|
|
||||||
|
// unix-based OS
|
||||||
|
return input.replace(/\//g, "_");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function joinPath(os: string, ...parts: string[]): string {
|
||||||
|
const sep = os === "Windows" ? "\\" : "/";
|
||||||
|
|
||||||
|
return parts
|
||||||
|
.filter(Boolean)
|
||||||
|
.map(p => p.replace(/^[/\\]+|[/\\]+$/g, ""))
|
||||||
|
.join(sep);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildOutputPath(settings: Settings, folder?: string) {
|
||||||
|
const os = settings.operatingSystem;
|
||||||
|
|
||||||
|
const base = settings.downloadPath || "";
|
||||||
|
const sanitized = folder ? sanitizePath(folder, os) : undefined;
|
||||||
|
|
||||||
|
return sanitized ? joinPath(os, base, sanitized) : base;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user