mirror of
https://github.com/spalencsar/bearwave.git
synced 2026-07-06 14:24:14 +02:00
Bump version to 1.0.1, implement automatic online search fallback with debounce, cover art notifications, and stream details edit
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
project(bearwave VERSION 1.0.0 LANGUAGES CXX)
|
||||
project(bearwave VERSION 1.0.1 LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
4
PKGBUILD
4
PKGBUILD
@@ -1,5 +1,5 @@
|
||||
pkgname=bearwave-git
|
||||
pkgver=1.0.0
|
||||
pkgver=1.0.1
|
||||
pkgrel=1
|
||||
pkgdesc="A KDE-focused desktop internet radio app"
|
||||
arch=('x86_64')
|
||||
@@ -14,7 +14,7 @@ md5sums=('SKIP')
|
||||
|
||||
pkgver() {
|
||||
cd "${srcdir}/${pkgname%-git}"
|
||||
printf "1.0.0.r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)"
|
||||
printf "1.0.1.r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)"
|
||||
}
|
||||
|
||||
build() {
|
||||
|
||||
@@ -17,7 +17,7 @@ int main(int argc, char *argv[])
|
||||
QApplication::setApplicationName(QStringLiteral("BearWave"));
|
||||
QApplication::setDesktopFileName(QStringLiteral("org.kde.bearwave.desktop"));
|
||||
QApplication::setOrganizationName(QStringLiteral("BearWave"));
|
||||
QApplication::setApplicationVersion(QStringLiteral("1.0.0"));
|
||||
QApplication::setApplicationVersion(QStringLiteral("1.0.1"));
|
||||
|
||||
QApplication app(argc, argv);
|
||||
app.setWindowIcon(QIcon::fromTheme(QStringLiteral("org.kde.bearwave")));
|
||||
|
||||
@@ -217,8 +217,19 @@ QVariantMap MprisPlayerAdaptor::metadata() const
|
||||
if (!artist.isEmpty()) {
|
||||
map.insert(QStringLiteral("xesam:artist"), QStringList{artist});
|
||||
}
|
||||
if (!coverUrl.isEmpty()) {
|
||||
map.insert(QStringLiteral("mpris:artUrl"), coverUrl);
|
||||
QString artUrl = coverUrl;
|
||||
if (artUrl.isEmpty() && m_backend) {
|
||||
QObject *stationObj = m_backend->currentStation();
|
||||
if (stationObj) {
|
||||
QString fav = stationObj->property("favicon").toString();
|
||||
if (fav.startsWith(QLatin1String("https://"))) {
|
||||
artUrl = fav;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!artUrl.isEmpty()) {
|
||||
map.insert(QStringLiteral("mpris:artUrl"), artUrl);
|
||||
} else {
|
||||
QString iconPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("icons/hicolor/256x256/apps/org.kde.bearwave.png"));
|
||||
if (!iconPath.isEmpty()) {
|
||||
|
||||
@@ -80,20 +80,31 @@ void NotificationManager::onTrackInfoChanged()
|
||||
return;
|
||||
}
|
||||
|
||||
if (coverUrl.isEmpty()) {
|
||||
QString targetUrl = coverUrl;
|
||||
if (targetUrl.isEmpty() && m_backend) {
|
||||
QObject *stationObj = m_backend->currentStation();
|
||||
if (stationObj) {
|
||||
targetUrl = stationObj->property("favicon").toString();
|
||||
if (!targetUrl.startsWith(QLatin1String("https://"))) {
|
||||
targetUrl.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (targetUrl.isEmpty()) {
|
||||
if (!m_notifyTimer.isActive()) {
|
||||
m_notifyTimer.start(800);
|
||||
}
|
||||
} else {
|
||||
m_notifiedCoverUrl = coverUrl;
|
||||
const QString hash = QString(QCryptographicHash::hash(coverUrl.toUtf8(), QCryptographicHash::Md5).toHex());
|
||||
m_notifiedCoverUrl = targetUrl;
|
||||
const QString hash = QString(QCryptographicHash::hash(targetUrl.toUtf8(), QCryptographicHash::Md5).toHex());
|
||||
const QString filePath = m_coversDir + "/" + hash + ".jpg";
|
||||
|
||||
if (QFile::exists(filePath)) {
|
||||
m_activeCoverPath = filePath;
|
||||
triggerNotification();
|
||||
} else {
|
||||
downloadCover(coverUrl);
|
||||
downloadCover(targetUrl);
|
||||
if (!m_notifyTimer.isActive()) {
|
||||
m_notifyTimer.start(800);
|
||||
}
|
||||
|
||||
213
src/qml/Main.qml
213
src/qml/Main.qml
@@ -133,7 +133,7 @@ ApplicationWindow {
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: compactMode ? (headerContent.implicitHeight + 20) : 176
|
||||
Layout.preferredHeight: headerContent.implicitHeight + 20
|
||||
radius: 12
|
||||
color: panel
|
||||
border.color: cardBorder
|
||||
@@ -338,6 +338,13 @@ ApplicationWindow {
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 1
|
||||
color: cardBorder
|
||||
opacity: 0.6
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
visible: !compactMode
|
||||
Layout.fillWidth: true
|
||||
@@ -351,6 +358,7 @@ ApplicationWindow {
|
||||
if (backend) {
|
||||
backend.filterQuery = text
|
||||
}
|
||||
searchTimer.restart()
|
||||
}
|
||||
onAccepted: {
|
||||
if (text.length < 2 || !backend) return
|
||||
@@ -491,6 +499,7 @@ ApplicationWindow {
|
||||
text: qsTr("Genre:")
|
||||
color: textMuted
|
||||
font.pixelSize: 11
|
||||
rightPadding: 10
|
||||
}
|
||||
|
||||
Button {
|
||||
@@ -526,12 +535,13 @@ ApplicationWindow {
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.preferredWidth: 10 }
|
||||
Item { Layout.preferredWidth: 20 }
|
||||
|
||||
Label {
|
||||
text: qsTr("Country:")
|
||||
color: textMuted
|
||||
font.pixelSize: 11
|
||||
rightPadding: 10
|
||||
}
|
||||
|
||||
Button {
|
||||
@@ -596,7 +606,10 @@ ApplicationWindow {
|
||||
color: textMuted
|
||||
font.pixelSize: 11
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
padding: 8
|
||||
leftPadding: 8
|
||||
rightPadding: 16
|
||||
topPadding: 8
|
||||
bottomPadding: 8
|
||||
}
|
||||
|
||||
Button {
|
||||
@@ -643,7 +656,10 @@ ApplicationWindow {
|
||||
color: textMuted
|
||||
font.pixelSize: 11
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
padding: 8
|
||||
leftPadding: 8
|
||||
rightPadding: 16
|
||||
topPadding: 8
|
||||
bottomPadding: 8
|
||||
}
|
||||
|
||||
Button {
|
||||
@@ -791,6 +807,7 @@ ApplicationWindow {
|
||||
border.color: accent
|
||||
|
||||
Image {
|
||||
id: stationFavicon
|
||||
anchors.fill: parent
|
||||
anchors.margins: 3
|
||||
source: (stationCard.modelData.favicon && stationCard.modelData.favicon.startsWith("https://"))
|
||||
@@ -802,12 +819,13 @@ ApplicationWindow {
|
||||
visible: source !== "" && status === Image.Ready
|
||||
}
|
||||
|
||||
Label {
|
||||
anchors.centerIn: parent
|
||||
text: qsTr("FM")
|
||||
color: "#d8ecff"
|
||||
font.bold: true
|
||||
visible: !parent.children[0].visible
|
||||
Image {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 6
|
||||
source: "qrc:/assets/app/bearwave.png"
|
||||
fillMode: Image.PreserveAspectFit
|
||||
smooth: true
|
||||
visible: !stationFavicon.visible
|
||||
}
|
||||
}
|
||||
|
||||
@@ -889,6 +907,7 @@ ApplicationWindow {
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: stationCard.modelData.country + " • "
|
||||
+ (stationCard.modelData.codec && stationCard.modelData.codec !== "unknown" && stationCard.modelData.codec !== "" ? stationCard.modelData.codec.toUpperCase() + " • " : "")
|
||||
+ (stationCard.modelData.bitrate > 0 ? stationCard.modelData.bitrate + " kbps" : qsTr("Stream"))
|
||||
color: textMuted
|
||||
font.pixelSize: 11
|
||||
@@ -896,6 +915,19 @@ ApplicationWindow {
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
visible: !stationCard.modelData.uuid
|
||||
Layout.preferredWidth: compactMode ? 34 : 40
|
||||
Layout.preferredHeight: 40
|
||||
text: "✏"
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: qsTr("Edit Station")
|
||||
onClicked: {
|
||||
editDialog.stationObject = stationCard.modelData
|
||||
editDialog.setupAndOpen()
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.preferredWidth: compactMode ? 34 : 40
|
||||
Layout.preferredHeight: 40
|
||||
@@ -972,11 +1004,27 @@ ApplicationWindow {
|
||||
Image {
|
||||
id: coverImage
|
||||
anchors.fill: parent
|
||||
source: backend && backend.player && backend.player.currentCoverArtUrl ? backend.player.currentCoverArtUrl : "qrc:/assets/app/bearwave.png"
|
||||
fillMode: backend && backend.player && backend.player.currentCoverArtUrl ? Image.PreserveAspectCrop : Image.PreserveAspectFit
|
||||
source: {
|
||||
if (backend && backend.player && backend.player.currentCoverArtUrl && backend.player.currentCoverArtUrl !== "") {
|
||||
return backend.player.currentCoverArtUrl;
|
||||
}
|
||||
if (backend && backend.currentStation && backend.currentStation.favicon && backend.currentStation.favicon.startsWith("https://")) {
|
||||
return backend.currentStation.favicon;
|
||||
}
|
||||
return "qrc:/assets/app/bearwave.png";
|
||||
}
|
||||
fillMode: (backend && backend.player && backend.player.currentCoverArtUrl && backend.player.currentCoverArtUrl !== "") ? Image.PreserveAspectCrop : Image.PreserveAspectFit
|
||||
smooth: true
|
||||
asynchronous: true
|
||||
anchors.margins: backend && backend.player && backend.player.currentCoverArtUrl ? 0 : 16
|
||||
anchors.margins: {
|
||||
if (backend && backend.player && backend.player.currentCoverArtUrl && backend.player.currentCoverArtUrl !== "") {
|
||||
return 0;
|
||||
}
|
||||
if (backend && backend.currentStation && backend.currentStation.favicon && backend.currentStation.favicon.startsWith("https://")) {
|
||||
return 8;
|
||||
}
|
||||
return 16;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -985,13 +1033,58 @@ ApplicationWindow {
|
||||
Layout.fillHeight: true
|
||||
spacing: 6
|
||||
|
||||
Label {
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
text: backend && backend.player ? (backend.player.currentStationName || qsTr("No station selected")) : qsTr("No station selected")
|
||||
color: textMain
|
||||
font.pixelSize: 16
|
||||
font.bold: true
|
||||
elide: Text.ElideRight
|
||||
spacing: 8
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: backend && backend.player ? (backend.player.currentStationName || qsTr("No station selected")) : qsTr("No station selected")
|
||||
color: textMain
|
||||
font.pixelSize: 16
|
||||
font.bold: true
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: codecBadge
|
||||
visible: backend && backend.currentStation && backend.currentStation.codec && backend.currentStation.codec !== "unknown" && backend.currentStation.codec !== ""
|
||||
height: 18
|
||||
width: codecLabel.implicitWidth + 12
|
||||
radius: 4
|
||||
color: "transparent"
|
||||
border.color: accent
|
||||
border.width: 1
|
||||
|
||||
Label {
|
||||
id: codecLabel
|
||||
anchors.centerIn: parent
|
||||
text: (backend && backend.currentStation && backend.currentStation.codec) ? backend.currentStation.codec.toUpperCase() : ""
|
||||
color: accent
|
||||
font.pixelSize: 9
|
||||
font.bold: true
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: bitrateBadge
|
||||
visible: backend && backend.currentStation && backend.currentStation.bitrate > 0
|
||||
height: 18
|
||||
width: bitrateLabel.implicitWidth + 12
|
||||
radius: 4
|
||||
color: accent
|
||||
border.color: accent
|
||||
border.width: 1
|
||||
|
||||
Label {
|
||||
id: bitrateLabel
|
||||
anchors.centerIn: parent
|
||||
text: (backend && backend.currentStation) ? backend.currentStation.bitrate + " kbps" : ""
|
||||
color: "#ffffff"
|
||||
font.pixelSize: 9
|
||||
font.bold: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
@@ -1050,9 +1143,25 @@ ApplicationWindow {
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
text: qsTr("Volume")
|
||||
color: textMuted
|
||||
Button {
|
||||
id: muteButton
|
||||
text: (backend && backend.player && backend.player.volume > 0) ? "🔊" : "🔇"
|
||||
Layout.preferredWidth: 44
|
||||
property real lastVolume: 0.5
|
||||
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: (backend && backend.player && backend.player.volume > 0) ? qsTr("Mute") : qsTr("Unmute")
|
||||
|
||||
onClicked: {
|
||||
if (backend && backend.player) {
|
||||
if (backend.player.volume > 0) {
|
||||
lastVolume = backend.player.volume
|
||||
backend.player.setVolume(0)
|
||||
} else {
|
||||
backend.player.setVolume(lastVolume > 0 ? lastVolume : 0.5)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Slider {
|
||||
@@ -1138,6 +1247,51 @@ ApplicationWindow {
|
||||
}
|
||||
}
|
||||
|
||||
Dialog {
|
||||
id: editDialog
|
||||
title: qsTr("Edit station")
|
||||
modal: true
|
||||
anchors.centerIn: parent
|
||||
width: compactMode ? 320 : 420
|
||||
standardButtons: Dialog.Ok | Dialog.Cancel
|
||||
|
||||
property var stationObject: null
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
spacing: 8
|
||||
TextField { id: editName; Layout.fillWidth: true; placeholderText: qsTr("Name") }
|
||||
TextField { id: editUrl; Layout.fillWidth: true; placeholderText: qsTr("Stream URL (http/https)") }
|
||||
TextField { id: editCountry; Layout.fillWidth: true; placeholderText: qsTr("Country (optional)") }
|
||||
}
|
||||
|
||||
function setupAndOpen() {
|
||||
if (stationObject) {
|
||||
editName.text = stationObject.name || ""
|
||||
editUrl.text = stationObject.url || ""
|
||||
editCountry.text = (stationObject.country === qsTr("Manual") ? "" : (stationObject.country || ""))
|
||||
open()
|
||||
}
|
||||
}
|
||||
|
||||
onAccepted: {
|
||||
if (backend && stationObject) {
|
||||
backend.editManualStation(stationObject, editName.text, editUrl.text, editCountry.text)
|
||||
toast(qsTr("Station updated"))
|
||||
}
|
||||
stationObject = null
|
||||
editName.text = ""
|
||||
editUrl.text = ""
|
||||
editCountry.text = ""
|
||||
}
|
||||
|
||||
onRejected: {
|
||||
stationObject = null
|
||||
editName.text = ""
|
||||
editUrl.text = ""
|
||||
editCountry.text = ""
|
||||
}
|
||||
}
|
||||
|
||||
Dialog {
|
||||
id: aboutDialog
|
||||
modal: true
|
||||
@@ -1357,6 +1511,21 @@ ApplicationWindow {
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: searchTimer
|
||||
interval: 600
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
var text = searchField.text.trim()
|
||||
if (text.length < 2 || !backend) return
|
||||
|
||||
if (currentPage === "search" || stationList.count === 0) {
|
||||
currentPage = "search"
|
||||
backend.searchStations(text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: contentFadeRestart
|
||||
interval: 120
|
||||
|
||||
@@ -148,6 +148,7 @@ void RadioBackend::searchStations(const QString &query)
|
||||
if (query.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
qDebug() << "RadioBackend: searchStations called with query:" << query;
|
||||
setLoading(true);
|
||||
m_radioBrowser->search(query);
|
||||
}
|
||||
@@ -692,6 +693,74 @@ void RadioBackend::playCurrentSelection()
|
||||
m_player->playUrl(url, station->name());
|
||||
}
|
||||
|
||||
QObject* RadioBackend::currentStation() const
|
||||
{
|
||||
for (RadioStation *s : m_favorites) {
|
||||
if (matchesStation(s, m_currentStationUuid, m_currentStationUrl)) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
for (RadioStation *s : m_stations) {
|
||||
if (matchesStation(s, m_currentStationUuid, m_currentStationUrl)) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void RadioBackend::editManualStation(QObject *stationObj, const QString &name, const QString &url, const QString &country)
|
||||
{
|
||||
RadioStation *station = qobject_cast<RadioStation*>(stationObj);
|
||||
if (!station || name.trimmed().isEmpty() || url.trimmed().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString oldUrl = station->url();
|
||||
QString oldUrlResolved = station->urlResolved();
|
||||
|
||||
station->setName(name.trimmed());
|
||||
station->setUrl(url.trimmed());
|
||||
station->setUrlResolved(url.trimmed());
|
||||
station->setCountry(country.trimmed().isEmpty() ? tr("Manual") : country.trimmed());
|
||||
|
||||
// Update in favorites list if present
|
||||
for (RadioStation *fav : m_favorites) {
|
||||
if (fav == station || (fav->uuid().isEmpty() && (fav->url() == oldUrl || fav->urlResolved() == oldUrlResolved))) {
|
||||
fav->setName(station->name());
|
||||
fav->setUrl(station->url());
|
||||
fav->setUrlResolved(station->urlResolved());
|
||||
fav->setCountry(station->country());
|
||||
}
|
||||
}
|
||||
|
||||
// Save updated favorites
|
||||
saveFavorites();
|
||||
|
||||
// Update in main stations list if present
|
||||
for (RadioStation *s : m_stations) {
|
||||
if (s == station || (s->uuid().isEmpty() && (s->url() == oldUrl || s->urlResolved() == oldUrlResolved))) {
|
||||
s->setName(station->name());
|
||||
s->setUrl(station->url());
|
||||
s->setUrlResolved(station->urlResolved());
|
||||
s->setCountry(station->country());
|
||||
}
|
||||
}
|
||||
|
||||
// If it's the currently playing station, update player stream info
|
||||
if (m_currentStationUrl == oldUrl || m_currentStationUrl == oldUrlResolved) {
|
||||
m_currentStationUrl = station->urlResolved();
|
||||
m_lastStationUrl = station->urlResolved();
|
||||
m_lastStationName = station->name();
|
||||
saveState();
|
||||
emit resumeStateChanged();
|
||||
emit currentStationChanged();
|
||||
}
|
||||
|
||||
emit stationsChanged();
|
||||
emit favoritesChanged();
|
||||
emit listsChanged();
|
||||
}
|
||||
|
||||
QVariantMap RadioBackend::toVariantMap(const RadioStation *station)
|
||||
{
|
||||
QVariantMap map;
|
||||
@@ -705,6 +774,7 @@ QVariantMap RadioBackend::toVariantMap(const RadioStation *station)
|
||||
map.insert(QStringLiteral("isFavorite"), station->isFavorite());
|
||||
map.insert(QStringLiteral("favicon"), station->favicon());
|
||||
map.insert(QStringLiteral("urlResolved"), station->urlResolved());
|
||||
map.insert(QStringLiteral("codec"), station->codec());
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ class RadioBackend : public QObject
|
||||
Q_PROPERTY(QVariantList recentStations READ recentStations NOTIFY listsChanged)
|
||||
Q_PROPERTY(QString currentStationUuid READ currentStationUuid NOTIFY currentStationChanged)
|
||||
Q_PROPERTY(QString currentStationUrl READ currentStationUrl NOTIFY currentStationChanged)
|
||||
Q_PROPERTY(QObject* currentStation READ currentStation NOTIFY currentStationChanged)
|
||||
|
||||
public:
|
||||
explicit RadioBackend(QObject *parent = nullptr);
|
||||
@@ -34,6 +35,7 @@ public:
|
||||
QList<QObject*> stations() const;
|
||||
QList<QObject*> favoriteStations() const;
|
||||
QVariantList recentStations() const;
|
||||
QObject* currentStation() const;
|
||||
QString currentStationUuid() const { return m_currentStationUuid; }
|
||||
QString currentStationUrl() const { return m_currentStationUrl; }
|
||||
BearPlayer* player() const { return m_player; }
|
||||
@@ -61,6 +63,7 @@ public:
|
||||
Q_INVOKABLE void toggleFavorite(int index);
|
||||
Q_INVOKABLE void toggleFavoriteById(const QString &uuid, const QString &urlResolved);
|
||||
Q_INVOKABLE void addManualStation(const QString &name, const QString &url, const QString &country);
|
||||
Q_INVOKABLE void editManualStation(QObject *stationObj, const QString &name, const QString &url, const QString &country);
|
||||
Q_INVOKABLE void sortStations(const QString &mode);
|
||||
Q_INVOKABLE QVariantList getRecentStations() const;
|
||||
Q_INVOKABLE QVariantList getFavoriteStations() const;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QCryptographicHash>
|
||||
#include <QDebug>
|
||||
|
||||
namespace {
|
||||
QString apiCacheDir()
|
||||
@@ -25,7 +26,15 @@ RadioBrowser::RadioBrowser(QObject *parent)
|
||||
|
||||
void RadioBrowser::search(const QString &query)
|
||||
{
|
||||
QString endpoint = "/stations/byname/" + QUrl::toPercentEncoding(query);
|
||||
QUrlQuery queryParams;
|
||||
queryParams.addQueryItem("name", query);
|
||||
queryParams.addQueryItem("hidebroken", "true");
|
||||
queryParams.addQueryItem("limit", "50");
|
||||
queryParams.addQueryItem("order", "votes");
|
||||
queryParams.addQueryItem("reverse", "true");
|
||||
|
||||
QString endpoint = "/stations/search?" + queryParams.toString(QUrl::FullyEncoded);
|
||||
qDebug() << "RadioBrowser: Searching with endpoint:" << endpoint;
|
||||
makeRequest(endpoint);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user