mirror of
https://github.com/markuryy/shark.git
synced 2026-06-18 18:41:03 +00:00
fix: m3u8 relative path generation/resolution
This commit is contained in:
@@ -57,35 +57,32 @@ export async function writeM3U8(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert absolute music file path to relative path from playlists folder
|
* Compute a relative path from the playlists folder to a music file.
|
||||||
* Assumes music folder and playlists folder are siblings:
|
* Music and playlists folders are expected to be siblings:
|
||||||
* /path/to/Music/Artist/Album/Track.flac
|
* /path/to/Music/Artist/Album/Track.flac
|
||||||
* /path/to/Playlists/playlist.m3u8
|
* /path/to/Playlists/playlist.m3u8
|
||||||
* Becomes: ../Music/Artist/Album/Track.flac
|
* Becomes: ../Music/Artist/Album/Track.flac
|
||||||
*
|
*
|
||||||
* @param absoluteMusicPath - Absolute path to music file
|
* @param absoluteMusicPath - Absolute path to music file
|
||||||
* @param musicFolderName - Name of music folder (default: 'Music')
|
* @param playlistsFolder - Absolute path to playlists folder
|
||||||
* @returns Relative path from playlists folder
|
* @returns Relative path from playlists folder
|
||||||
*/
|
*/
|
||||||
export function makeRelativePath(
|
export function makeRelativePath(
|
||||||
absoluteMusicPath: string,
|
absoluteMusicPath: string,
|
||||||
musicFolderName: string = 'Music'
|
playlistsFolder: string
|
||||||
): string {
|
): string {
|
||||||
// Split path into parts
|
const fileParts = absoluteMusicPath.split('/').filter(Boolean);
|
||||||
const parts = absoluteMusicPath.split('/');
|
const baseParts = playlistsFolder.replace(/\/$/, '').split('/').filter(Boolean);
|
||||||
|
|
||||||
// Find the music folder index
|
// Find common prefix length
|
||||||
const musicIndex = parts.findIndex(part => part === musicFolderName);
|
let common = 0;
|
||||||
|
while (common < baseParts.length && common < fileParts.length && baseParts[common] === fileParts[common]) {
|
||||||
if (musicIndex === -1) {
|
common++;
|
||||||
// Fallback: if music folder not found, use the path as-is
|
|
||||||
console.warn(`[M3U8] Could not find "${musicFolderName}" in path: ${absoluteMusicPath}`);
|
|
||||||
return absoluteMusicPath;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Take everything from music folder onwards
|
// Go up from playlists folder to common ancestor, then down to the file
|
||||||
const relativeParts = parts.slice(musicIndex);
|
const ups = baseParts.length - common;
|
||||||
|
const remaining = fileParts.slice(common);
|
||||||
|
|
||||||
// Prepend ../ to go up from playlists folder
|
return [...Array(ups).fill('..'), ...remaining].join('/');
|
||||||
return `../${relativeParts.join('/')}`;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -277,23 +277,22 @@ export async function findPlaylistCoverFallback(
|
|||||||
*/
|
*/
|
||||||
export async function loadPlaylistTracks(
|
export async function loadPlaylistTracks(
|
||||||
playlistPath: string,
|
playlistPath: string,
|
||||||
playlistName: string,
|
playlistName: string
|
||||||
baseFolder: string
|
|
||||||
): Promise<PlaylistWithTracks> {
|
): Promise<PlaylistWithTracks> {
|
||||||
const parsedTracks = await parsePlaylist(playlistPath);
|
const parsedTracks = await parsePlaylist(playlistPath);
|
||||||
|
|
||||||
|
// Resolve relative paths against the playlist file's directory
|
||||||
|
const playlistDir = playlistPath.split('/').slice(0, -1).join('/');
|
||||||
|
|
||||||
// Load tracks with metadata in parallel
|
// Load tracks with metadata in parallel
|
||||||
const tracks: Track[] = await Promise.all(
|
const tracks: Track[] = await Promise.all(
|
||||||
parsedTracks.map(async (parsedTrack) => {
|
parsedTracks.map(async (parsedTrack) => {
|
||||||
const trackPath = parsedTrack.path;
|
const trackPath = parsedTrack.path;
|
||||||
|
|
||||||
// Handle relative paths - resolve relative to playlist location or music folder
|
// Resolve path: absolute paths used as-is, relative paths resolved from playlist dir
|
||||||
let fullPath = trackPath.startsWith('/') || trackPath.includes(':\\')
|
let fullPath = trackPath.startsWith('/') || trackPath.includes(':\\')
|
||||||
? trackPath // Absolute path
|
? trackPath
|
||||||
: `${baseFolder}/${trackPath}`; // Relative path
|
: normalizePath(`${playlistDir}/${trackPath}`);
|
||||||
|
|
||||||
// Normalize path to remove .. and . segments for Tauri security
|
|
||||||
fullPath = normalizePath(fullPath);
|
|
||||||
|
|
||||||
// Try to find the actual file (handles track number mismatches)
|
// Try to find the actual file (handles track number mismatches)
|
||||||
const actualPath = await findActualFilePath(fullPath);
|
const actualPath = await findActualFilePath(fullPath);
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ export async function downloadDeezerPlaylist(
|
|||||||
const absolutePath = `${paths.filepath}/${paths.filename}`;
|
const absolutePath = `${paths.filepath}/${paths.filename}`;
|
||||||
|
|
||||||
// Convert to relative path from playlists folder
|
// Convert to relative path from playlists folder
|
||||||
const relativePath = makeRelativePath(absolutePath, 'Music');
|
const relativePath = makeRelativePath(absolutePath, playlistsFolder);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
duration: track.duration,
|
duration: track.duration,
|
||||||
|
|||||||
@@ -221,7 +221,7 @@ export async function downloadSpotifyPlaylist(
|
|||||||
const m3u8Tracks: M3U8Track[] = successfulTracks.map(({ deezerTrack }) => {
|
const m3u8Tracks: M3U8Track[] = successfulTracks.map(({ deezerTrack }) => {
|
||||||
const paths = generateTrackPath(deezerTrack, musicFolder, appSettings.deezerFormat, false);
|
const paths = generateTrackPath(deezerTrack, musicFolder, appSettings.deezerFormat, false);
|
||||||
const absolutePath = `${paths.filepath}/${paths.filename}`;
|
const absolutePath = `${paths.filepath}/${paths.filename}`;
|
||||||
const relativePath = makeRelativePath(absolutePath, 'Music');
|
const relativePath = makeRelativePath(absolutePath, playlistsFolder);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
duration: deezerTrack.duration,
|
duration: deezerTrack.duration,
|
||||||
|
|||||||
@@ -51,7 +51,7 @@
|
|||||||
|
|
||||||
// Load tracks and cover art in parallel
|
// Load tracks and cover art in parallel
|
||||||
const [tracksData, coverPath] = await Promise.all([
|
const [tracksData, coverPath] = await Promise.all([
|
||||||
loadPlaylistTracks(playlist.path, playlist.name, $settings.musicFolder),
|
loadPlaylistTracks(playlist.path, playlist.name),
|
||||||
findPlaylistArt(playlist.path)
|
findPlaylistArt(playlist.path)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user