fix(share): use Qobuz track URL for song.link instead of ISRC

Odesli API doesn't support direct ISRC lookup. Changed to use
Qobuz track URL (https://open.qobuz.com/track/{id}) which Odesli
can properly resolve to song.link.
This commit is contained in:
vicrodh committed 2026-01-09 18:50:30 -06:00
1 parent 2efa107aa7
commit 657682fc5e
3 files changed
+21 -46

No files matched your search

+5 -14
View File
@@ -2,29 +2,20 @@
use tauri::State;
use crate::share::{ShareError, SongLinkClient, SongLinkResponse};
use crate::share::{ShareError, SongLinkResponse};
use crate::AppState;
/// Get song.link URL for a track
/// Requires ISRC to be present in the track metadata
/// Get song.link URL for a track using Qobuz track ID
#[tauri::command]
pub async fn share_track_songlink(
isrc: Option<String>,
track_id: u64,
state: State<'_, AppState>,
) -> Result<SongLinkResponse, String> {
let isrc = isrc.ok_or_else(|| {
ShareError::MissingIsrc.to_string()
})?;
if isrc.is_empty() {
return Err(ShareError::MissingIsrc.to_string());
}
log::info!("Command: share_track_songlink ISRC={}", isrc);
log::info!("Command: share_track_songlink track_id={}", track_id);
state
.songlink
.get_by_isrc(&isrc)
.get_by_track_id(&track_id.to_string())
.await
.map_err(|e| e.to_string())
}
+9 -7
View File
@@ -48,22 +48,24 @@ impl SongLinkClient {
}
}
/// Get song.link URL for a track by ISRC
pub async fn get_by_isrc(&self, isrc: &str) -> Result<SongLinkResponse, ShareError> {
let cache_key = format!("isrc:{}", isrc);
/// Get song.link URL for a track by Qobuz track ID
pub async fn get_by_track_id(&self, track_id: &str) -> Result<SongLinkResponse, ShareError> {
let cache_key = format!("track:{}", track_id);
// Check cache first
if let Some(cached) = self.get_from_cache(&cache_key) {
log::debug!("Cache hit for ISRC: {}", isrc);
log::debug!("Cache hit for track ID: {}", track_id);
return Ok(cached);
}
log::info!("Fetching song.link for ISRC: {}", isrc);
// Use Qobuz track URL for Odesli lookup
let qobuz_url = format!("https://open.qobuz.com/track/{}", track_id);
log::info!("Fetching song.link for Qobuz track: {}", qobuz_url);
let response = self
.client
.get(ODESLI_API_URL)
.query(&[("isrc", isrc), ("userCountry", "US")])
.query(&[("url", &qobuz_url), ("userCountry", &"US".to_string())])
.send()
.await?;
@@ -77,7 +79,7 @@ impl SongLinkClient {
}
let odesli: OdesliResponse = response.json().await?;
let result = self.convert_response(odesli, isrc.to_string(), ContentType::Track)?;
let result = self.convert_response(odesli, track_id.to_string(), ContentType::Track)?;
// Cache the result
self.store_in_cache(cache_key, result.clone());
+7 -25
View File
@@ -472,28 +472,10 @@
}
}
async function resolveTrackIsrc(trackId: number, isrc?: string): Promise<string | null> {
if (isrc && isrc.trim()) {
return isrc;
}
try {
const fullTrack = await invoke<QobuzTrack>('get_track', { trackId });
return fullTrack.isrc || null;
} catch (err) {
console.error('Failed to fetch track ISRC:', err);
return null;
}
}
async function shareSonglinkTrack(trackId: number, isrc?: string) {
const resolvedIsrc = await resolveTrackIsrc(trackId, isrc);
if (!resolvedIsrc) {
showToast('ISRC not available for this track', 'error');
return;
}
async function shareSonglinkTrack(trackId: number) {
try {
showToast('Fetching Song.link...', 'info');
const response = await invoke<SongLinkResponse>('share_track_songlink', { isrc: resolvedIsrc });
const response = await invoke<SongLinkResponse>('share_track_songlink', { trackId });
await copyToClipboard(response.pageUrl, 'Song.link copied');
} catch (err) {
console.error('Failed to get Song.link:', err);
@@ -1422,7 +1404,7 @@
onTrackAddFavorite={addTrackToFavorites}
onTrackAddToPlaylist={(trackId) => openAddToPlaylist([trackId])}
onTrackShareQobuz={shareQobuzTrackLink}
onTrackShareSonglink={(track) => shareSonglinkTrack(track.id, track.isrc)}
onTrackShareSonglink={(track) => shareSonglinkTrack(track.id)}
onTrackGoToAlbum={handleAlbumClick}
onTrackGoToArtist={handleArtistClick}
onArtistClick={handleArtistClick}
@@ -1444,7 +1426,7 @@
onTrackPlayLater={handleAlbumTrackPlayLater}
onTrackAddFavorite={addTrackToFavorites}
onTrackShareQobuz={shareQobuzTrackLink}
onTrackShareSonglink={(track) => shareSonglinkTrack(track.id, track.isrc)}
onTrackShareSonglink={(track) => shareSonglinkTrack(track.id)}
onTrackGoToAlbum={handleAlbumClick}
onTrackGoToArtist={handleArtistClick}
onPlayAll={handlePlayAllAlbum}
@@ -1463,7 +1445,7 @@
onTrackAddFavorite={addTrackToFavorites}
onTrackAddToPlaylist={(trackId) => openAddToPlaylist([trackId])}
onTrackShareQobuz={shareQobuzTrackLink}
onTrackShareSonglink={(track) => shareSonglinkTrack(track.id, track.isrc)}
onTrackShareSonglink={(track) => shareSonglinkTrack(track.id)}
onTrackGoToAlbum={handleAlbumClick}
onTrackGoToArtist={handleArtistClick}
/>
@@ -1484,7 +1466,7 @@
onTrackAddFavorite={addTrackToFavorites}
onTrackAddToPlaylist={(trackId) => openAddToPlaylist([trackId])}
onTrackShareQobuz={shareQobuzTrackLink}
onTrackShareSonglink={(track) => shareSonglinkTrack(track.id, track.isrc)}
onTrackShareSonglink={(track) => shareSonglinkTrack(track.id)}
onTrackGoToAlbum={handleAlbumClick}
onTrackGoToArtist={handleArtistClick}
/>
@@ -1498,7 +1480,7 @@
onTrackAddFavorite={addTrackToFavorites}
onTrackAddToPlaylist={(trackId) => openAddToPlaylist([trackId])}
onTrackShareQobuz={shareQobuzTrackLink}
onTrackShareSonglink={(track) => shareSonglinkTrack(track.id, track.isrc)}
onTrackShareSonglink={(track) => shareSonglinkTrack(track.id)}
onTrackGoToAlbum={handleAlbumClick}
onTrackGoToArtist={handleArtistClick}
/>