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
|
||||
* Assumes music folder and playlists folder are siblings:
|
||||
* Compute a relative path from the playlists folder to a music file.
|
||||
* Music and playlists folders are expected to be siblings:
|
||||
* /path/to/Music/Artist/Album/Track.flac
|
||||
* /path/to/Playlists/playlist.m3u8
|
||||
* Becomes: ../Music/Artist/Album/Track.flac
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
export function makeRelativePath(
|
||||
absoluteMusicPath: string,
|
||||
musicFolderName: string = 'Music'
|
||||
playlistsFolder: string
|
||||
): string {
|
||||
// Split path into parts
|
||||
const parts = absoluteMusicPath.split('/');
|
||||
const fileParts = absoluteMusicPath.split('/').filter(Boolean);
|
||||
const baseParts = playlistsFolder.replace(/\/$/, '').split('/').filter(Boolean);
|
||||
|
||||
// Find the music folder index
|
||||
const musicIndex = parts.findIndex(part => part === musicFolderName);
|
||||
|
||||
if (musicIndex === -1) {
|
||||
// Fallback: if music folder not found, use the path as-is
|
||||
console.warn(`[M3U8] Could not find "${musicFolderName}" in path: ${absoluteMusicPath}`);
|
||||
return absoluteMusicPath;
|
||||
// Find common prefix length
|
||||
let common = 0;
|
||||
while (common < baseParts.length && common < fileParts.length && baseParts[common] === fileParts[common]) {
|
||||
common++;
|
||||
}
|
||||
|
||||
// Take everything from music folder onwards
|
||||
const relativeParts = parts.slice(musicIndex);
|
||||
// Go up from playlists folder to common ancestor, then down to the file
|
||||
const ups = baseParts.length - common;
|
||||
const remaining = fileParts.slice(common);
|
||||
|
||||
// Prepend ../ to go up from playlists folder
|
||||
return `../${relativeParts.join('/')}`;
|
||||
return [...Array(ups).fill('..'), ...remaining].join('/');
|
||||
}
|
||||
|
||||
@@ -277,23 +277,22 @@ export async function findPlaylistCoverFallback(
|
||||
*/
|
||||
export async function loadPlaylistTracks(
|
||||
playlistPath: string,
|
||||
playlistName: string,
|
||||
baseFolder: string
|
||||
playlistName: string
|
||||
): Promise<PlaylistWithTracks> {
|
||||
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
|
||||
const tracks: Track[] = await Promise.all(
|
||||
parsedTracks.map(async (parsedTrack) => {
|
||||
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(':\\')
|
||||
? trackPath // Absolute path
|
||||
: `${baseFolder}/${trackPath}`; // Relative path
|
||||
|
||||
// Normalize path to remove .. and . segments for Tauri security
|
||||
fullPath = normalizePath(fullPath);
|
||||
? trackPath
|
||||
: normalizePath(`${playlistDir}/${trackPath}`);
|
||||
|
||||
// Try to find the actual file (handles track number mismatches)
|
||||
const actualPath = await findActualFilePath(fullPath);
|
||||
|
||||
@@ -93,7 +93,7 @@ export async function downloadDeezerPlaylist(
|
||||
const absolutePath = `${paths.filepath}/${paths.filename}`;
|
||||
|
||||
// Convert to relative path from playlists folder
|
||||
const relativePath = makeRelativePath(absolutePath, 'Music');
|
||||
const relativePath = makeRelativePath(absolutePath, playlistsFolder);
|
||||
|
||||
return {
|
||||
duration: track.duration,
|
||||
|
||||
@@ -221,7 +221,7 @@ export async function downloadSpotifyPlaylist(
|
||||
const m3u8Tracks: M3U8Track[] = successfulTracks.map(({ deezerTrack }) => {
|
||||
const paths = generateTrackPath(deezerTrack, musicFolder, appSettings.deezerFormat, false);
|
||||
const absolutePath = `${paths.filepath}/${paths.filename}`;
|
||||
const relativePath = makeRelativePath(absolutePath, 'Music');
|
||||
const relativePath = makeRelativePath(absolutePath, playlistsFolder);
|
||||
|
||||
return {
|
||||
duration: deezerTrack.duration,
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
|
||||
// Load tracks and cover art in parallel
|
||||
const [tracksData, coverPath] = await Promise.all([
|
||||
loadPlaylistTracks(playlist.path, playlist.name, $settings.musicFolder),
|
||||
loadPlaylistTracks(playlist.path, playlist.name),
|
||||
findPlaylistArt(playlist.path)
|
||||
]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user