fix: stabilize seek slider and clean backend lint issues
Some checks failed
Build for Windows / build-windows (push) Has been cancelled

This commit is contained in:
joren
2026-03-31 00:57:09 +02:00
parent 5673d6cd30
commit 2aff8fda47
6 changed files with 70 additions and 46 deletions

View File

@@ -1,4 +1,5 @@
//! qobuz-backend: C-ABI library consumed by the Qt frontend. //! qobuz-backend: C-ABI library consumed by the Qt frontend.
#![allow(clippy::missing_safety_doc)]
mod api; mod api;
mod player; mod player;

View File

@@ -157,7 +157,7 @@ impl MediaSource for SegmentStreamSource {
fn http_client() -> &'static reqwest::blocking::Client { fn http_client() -> &'static reqwest::blocking::Client {
static CLIENT: OnceLock<reqwest::blocking::Client> = OnceLock::new(); static CLIENT: OnceLock<reqwest::blocking::Client> = OnceLock::new();
CLIENT.get_or_init(|| reqwest::blocking::Client::new()) CLIENT.get_or_init(reqwest::blocking::Client::new)
} }
fn fetch_segment(url: &str, cancel: &Arc<AtomicBool>) -> Option<Vec<u8>> { fn fetch_segment(url: &str, cancel: &Arc<AtomicBool>) -> Option<Vec<u8>> {
@@ -260,8 +260,10 @@ fn decrypt_and_extract_frames(data: &mut [u8], key: Option<&[u8; 16]>) -> Vec<u8
break; break;
} }
if &data[pos + 4..pos + 8] == b"uuid" && box_size >= 36 { if data[pos + 4..pos + 8] == *b"uuid"
if &data[pos + 8..pos + 24] == QBZ1_UUID { && box_size >= 36
&& data[pos + 8..pos + 24] == QBZ1_UUID
{
let body = pos + 24; let body = pos + 24;
if body + 12 > data.len() { if body + 12 > data.len() {
pos += box_size; pos += box_size;
@@ -284,18 +286,19 @@ fn decrypt_and_extract_frames(data: &mut [u8], key: Option<&[u8; 16]>) -> Vec<u8
let end = offset + size; let end = offset + size;
if end <= data.len() { if end <= data.len() {
if enc && key.is_some() { if enc {
if let Some(k) = key {
let mut iv = [0u8; 16]; let mut iv = [0u8; 16];
iv[..8].copy_from_slice(&data[e + 8..e + 16]); iv[..8].copy_from_slice(&data[e + 8..e + 16]);
Ctr128BE::<Aes128>::new(key.unwrap().into(), (&iv).into()) Ctr128BE::<Aes128>::new(k.into(), (&iv).into())
.apply_keystream(&mut data[offset..end]); .apply_keystream(&mut data[offset..end]);
} }
}
frames.extend_from_slice(&data[offset..end]); frames.extend_from_slice(&data[offset..end]);
} }
offset += size; offset += size;
} }
} }
}
pos += box_size; pos += box_size;
} }
frames frames
@@ -845,7 +848,7 @@ impl Seek for HttpStreamSource {
.send() .send()
{ {
Ok(r) => r, Ok(r) => r,
Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e.to_string())), Err(e) => return Err(io::Error::other(e.to_string())),
}; };
if resp.status() == reqwest::StatusCode::PARTIAL_CONTENT { if resp.status() == reqwest::StatusCode::PARTIAL_CONTENT {
@@ -881,10 +884,7 @@ impl Seek for HttpStreamSource {
self.pos = self.reader_pos; self.pos = self.reader_pos;
Ok(self.pos) Ok(self.pos)
} else { } else {
Err(io::Error::new( Err(io::Error::other(format!("HTTP Error {}", resp.status())))
io::ErrorKind::Other,
format!("HTTP Error {}", resp.status()),
))
} }
} }
} }

View File

@@ -90,6 +90,7 @@ impl AudioOutput {
Ok(()) Ok(())
} }
#[allow(dead_code)]
pub fn flush(&self) { pub fn flush(&self) {
// Wait until the ring buffer is fully emptied by cpal // Wait until the ring buffer is fully emptied by cpal
while !self._ring.is_empty() { while !self._ring.is_empty() {

View File

@@ -297,7 +297,7 @@ void QobuzBackend::onEvent(int eventType, const QString &json)
case EV_SEARCH_OK: case EV_SEARCH_OK:
emit searchResult(obj); emit searchResult(obj);
break; break;
case 26: // EV_MOST_POPULAR_OK case EV_MOST_POPULAR_OK:
emit mostPopularResult(obj); emit mostPopularResult(obj);
break; break;
case EV_SEARCH_ERR: case EV_SEARCH_ERR:
@@ -312,7 +312,7 @@ void QobuzBackend::onEvent(int eventType, const QString &json)
case EV_ARTIST_OK: case EV_ARTIST_OK:
emit artistLoaded(obj); emit artistLoaded(obj);
break; break;
case 24: // EV_ARTIST_RELEASES_OK case EV_ARTIST_RELEASES_OK:
emit artistReleasesLoaded( emit artistReleasesLoaded(
obj["release_type"].toString(), obj["release_type"].toString(),
obj["items"].toArray(), obj["items"].toArray(),
@@ -320,25 +320,25 @@ void QobuzBackend::onEvent(int eventType, const QString &json)
obj["offset"].toInt() obj["offset"].toInt()
); );
break; break;
case 25: // EV_DEEP_SHUFFLE_OK case EV_DEEP_SHUFFLE_OK:
emit deepShuffleTracksLoaded(obj["tracks"].toArray()); emit deepShuffleTracksLoaded(obj["tracks"].toArray());
break; break;
case 29: // EV_DYNAMIC_SUGGEST_OK case EV_DYNAMIC_SUGGEST_OK:
emit dynamicSuggestionsLoaded(obj); emit dynamicSuggestionsLoaded(obj);
break; break;
case 27: // EV_GENRES_OK case EV_GENRES_OK:
emit genresLoaded(obj); emit genresLoaded(obj);
break; break;
case 28: // EV_FEATURED_ALBUMS_OK case EV_FEATURED_ALBUMS_OK:
emit featuredAlbumsLoaded(obj); emit featuredAlbumsLoaded(obj);
break; break;
case 30: // EV_FEATURED_PLAYLISTS_OK case EV_FEATURED_PLAYLISTS_OK:
emit featuredPlaylistsLoaded(obj); emit featuredPlaylistsLoaded(obj);
break; break;
case 31: // EV_DISCOVER_PLAYLISTS_OK case EV_DISCOVER_PLAYLISTS_OK:
emit discoverPlaylistsLoaded(obj); emit discoverPlaylistsLoaded(obj);
break; break;
case 32: // EV_PLAYLIST_SEARCH_OK case EV_PLAYLIST_SEARCH_OK:
emit playlistSearchLoaded(obj); emit playlistSearchLoaded(obj);
break; break;
case EV_ARTIST_ERR: case EV_ARTIST_ERR:
@@ -368,19 +368,19 @@ void QobuzBackend::onEvent(int eventType, const QString &json)
case EV_STATE_CHANGED: case EV_STATE_CHANGED:
emit stateChanged(obj["state"].toString()); emit stateChanged(obj["state"].toString());
break; break;
case 20: // EV_PLAYLIST_CREATED case EV_PLAYLIST_CREATED:
emit playlistCreated(obj); emit playlistCreated(obj);
break; break;
case 21: // EV_PLAYLIST_DELETED case EV_PLAYLIST_DELETED:
emit playlistDeleted(obj); emit playlistDeleted(obj);
break; break;
case 22: // EV_PLAYLIST_TRACK_ADDED case EV_PLAYLIST_TRACK_ADDED:
emit playlistTrackAdded(static_cast<qint64>(obj["playlist_id"].toDouble())); emit playlistTrackAdded(static_cast<qint64>(obj["playlist_id"].toDouble()));
break; break;
case 33: // EV_PLAYLIST_SUBSCRIBED case EV_PLAYLIST_SUBSCRIBED:
emit playlistSubscribed(static_cast<qint64>(obj["playlist_id"].toDouble())); emit playlistSubscribed(static_cast<qint64>(obj["playlist_id"].toDouble()));
break; break;
case 34: // EV_PLAYLIST_UNSUBSCRIBED case EV_PLAYLIST_UNSUBSCRIBED:
emit playlistUnsubscribed(static_cast<qint64>(obj["playlist_id"].toDouble())); emit playlistUnsubscribed(static_cast<qint64>(obj["playlist_id"].toDouble()));
break; break;
case EV_USER_OK: case EV_USER_OK:

View File

@@ -5,6 +5,7 @@
#include <QNetworkRequest> #include <QNetworkRequest>
#include <QResizeEvent> #include <QResizeEvent>
#include <QMenu> #include <QMenu>
#include <QDateTime>
MainToolBar::MainToolBar(QobuzBackend *backend, PlayQueue *queue, QWidget *parent) MainToolBar::MainToolBar(QobuzBackend *backend, PlayQueue *queue, QWidget *parent)
: QToolBar(parent) : QToolBar(parent)
@@ -221,6 +222,10 @@ void MainToolBar::onProgressReleased()
const quint64 dur = m_backend->duration(); const quint64 dur = m_backend->duration();
if (dur > 0) { if (dur > 0) {
const quint64 target = dur * static_cast<quint64>(m_progress->value()) / 1000; const quint64 target = dur * static_cast<quint64>(m_progress->value()) / 1000;
m_seekPending = true;
m_pendingSeekTarget = target;
m_pendingSeekStartedMs = QDateTime::currentMSecsSinceEpoch();
updateProgress(target, dur);
m_backend->seek(target); m_backend->seek(target);
} }
} }
@@ -238,6 +243,8 @@ void MainToolBar::onBackendStateChanged(const QString &state)
void MainToolBar::onTrackChanged(const QJsonObject &track) void MainToolBar::onTrackChanged(const QJsonObject &track)
{ {
m_seekPending = false;
m_seeking = false;
setCurrentTrack(track); setCurrentTrack(track);
const qint64 trackId = static_cast<qint64>(track["id"].toDouble()); const qint64 trackId = static_cast<qint64>(track["id"].toDouble());
@@ -258,6 +265,18 @@ void MainToolBar::onTrackChanged(const QJsonObject &track)
void MainToolBar::onPositionChanged(quint64 position, quint64 duration) void MainToolBar::onPositionChanged(quint64 position, quint64 duration)
{ {
if (m_seekPending) {
const qint64 nowMs = QDateTime::currentMSecsSinceEpoch();
const quint64 delta = (position > m_pendingSeekTarget)
? (position - m_pendingSeekTarget)
: (m_pendingSeekTarget - position);
if (delta > 2 && (nowMs - m_pendingSeekStartedMs) < 1500)
return;
m_seekPending = false;
}
updateProgress(position, duration); updateProgress(position, duration);
} }

View File

@@ -86,6 +86,9 @@ private:
QVector<RecentTrackSeed> m_recentTracks; QVector<RecentTrackSeed> m_recentTracks;
bool m_playing = false; bool m_playing = false;
bool m_seeking = false; bool m_seeking = false;
bool m_seekPending = false;
quint64 m_pendingSeekTarget = 0;
qint64 m_pendingSeekStartedMs = 0;
bool m_fetchingAutoplay = false; bool m_fetchingAutoplay = false;
void requestAutoplaySuggestions(); void requestAutoplaySuggestions();