fix: stabilize seek slider and clean backend lint issues
Some checks failed
Build for Windows / build-windows (push) Has been cancelled
Some checks failed
Build for Windows / build-windows (push) Has been cancelled
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
@@ -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()),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user