Files
shark/src/lib/library/m3u8.ts

92 lines
2.8 KiB
TypeScript

import { writeFile } from '@tauri-apps/plugin-fs';
import { sanitizeFilename } from '$lib/services/deezer/paths';
import { encodeEmojis } from '$lib/utils/emoji';
export interface M3U8Track {
duration: number; // in seconds
artist: string;
title: string;
path: string; // relative path from playlist file (e.g., ../Music/Artist/Album/01 - Track.flac)
}
/**
* Write an M3U8 playlist file
* Format: Extended M3U format with EXTINF metadata
*
* @param playlistName - Name of the playlist (will be sanitized)
* @param tracks - Array of tracks to include
* @param playlistsFolder - Absolute path to playlists folder
* @returns Absolute path to created m3u8 file
*/
export async function writeM3U8(
playlistName: string,
tracks: M3U8Track[],
playlistsFolder: string
): Promise<string> {
// Encode emojis and sanitize playlist name for filename
const encodedName = encodeEmojis(playlistName);
const sanitizedName = sanitizeFilename(encodedName);
const playlistPath = `${playlistsFolder}/${sanitizedName}.m3u8`;
// Build m3u8 content
const lines: string[] = [
'#EXTM3U',
`#PLAYLIST:${encodedName}`,
'#EXTENC:UTF-8',
''
];
for (const track of tracks) {
// EXTINF format: #EXTINF:duration,artist - title
const durationSeconds = Math.round(track.duration);
const extinf = `#EXTINF:${durationSeconds},${track.artist} - ${track.title}`;
lines.push(extinf);
lines.push(track.path);
}
// Add trailing newline
lines.push('');
const content = lines.join('\n');
const encoder = new TextEncoder();
const data = encoder.encode(content);
await writeFile(playlistPath, data);
return playlistPath;
}
/**
* Convert absolute music file path to relative path from playlists folder
* Assumes music folder and playlists folder are 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')
* @returns Relative path from playlists folder
*/
export function makeRelativePath(
absoluteMusicPath: string,
musicFolderName: string = 'Music'
): string {
// Split path into parts
const parts = absoluteMusicPath.split('/');
// 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;
}
// Take everything from music folder onwards
const relativeParts = parts.slice(musicIndex);
// Prepend ../ to go up from playlists folder
return `../${relativeParts.join('/')}`;
}