mirror of
https://github.com/clementine-player/Clementine
synced 2024-12-14 02:14:21 +01:00
SomaFM streams
This commit is contained in:
parent
3fcf8233b9
commit
4777b3eab1
3
TODO
3
TODO
@ -10,12 +10,15 @@
|
||||
- osd
|
||||
- Save and load playlists from files
|
||||
- Shuffle and repeat playlist
|
||||
- Autocompletion from library when editing tags
|
||||
|
||||
- Edit tags in playlist view
|
||||
- Disabled fields in tag editor
|
||||
|
||||
Long-term:
|
||||
- iPod
|
||||
- Automatic tagging from musicbrainz
|
||||
- Album cover fetching from last.fm
|
||||
|
||||
Windows:
|
||||
- Playlist delegates
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
const char* LastFMService::kServiceName = "Last.fm";
|
||||
const char* LastFMService::kSettingsGroup = "Last.fm";
|
||||
const char* LastFMService::kLoadingText = "Loading Last.fm radio";
|
||||
const char* LastFMService::kAudioscrobblerClientId = "tng";
|
||||
const char* LastFMService::kApiKey = "75d20fb472be99275392aefa2760ea09";
|
||||
const char* LastFMService::kSecret = "d3072b60ae626be12be69448f5c46e70";
|
||||
@ -80,7 +81,7 @@ void LastFMService::ScrobblingEnabledChangedSlot(bool value) {
|
||||
}
|
||||
|
||||
RadioItem* LastFMService::CreateRootItem(RadioItem* parent) {
|
||||
RadioItem* item = new RadioItem(this, RadioItem::Type_Service, "Last.fm", parent);
|
||||
RadioItem* item = new RadioItem(this, RadioItem::Type_Service, kServiceName, parent);
|
||||
item->icon = QIcon(":last.fm/as.png");
|
||||
return item;
|
||||
}
|
||||
@ -252,7 +253,7 @@ void LastFMService::StartLoading(const QUrl& url) {
|
||||
if (!IsAuthenticated())
|
||||
return;
|
||||
|
||||
emit LoadingStarted();
|
||||
emit TaskStarted(kLoadingText);
|
||||
|
||||
delete tuner_;
|
||||
|
||||
@ -284,7 +285,7 @@ void LastFMService::TunerError(lastfm::ws::Error error) {
|
||||
if (!initial_tune_)
|
||||
return;
|
||||
|
||||
emit LoadingFinished();
|
||||
emit TaskFinished(kLoadingText);
|
||||
|
||||
if (error == lastfm::ws::NotEnoughContent) {
|
||||
emit StreamFinished();
|
||||
@ -325,7 +326,7 @@ QString LastFMService::ErrorString(lastfm::ws::Error error) const {
|
||||
|
||||
void LastFMService::TunerTrackAvailable() {
|
||||
if (initial_tune_) {
|
||||
emit LoadingFinished();
|
||||
emit TaskFinished(kLoadingText);
|
||||
|
||||
LoadNext(last_url_);
|
||||
initial_tune_ = false;
|
||||
|
@ -21,6 +21,7 @@ class LastFMService : public RadioService {
|
||||
|
||||
static const char* kServiceName;
|
||||
static const char* kSettingsGroup;
|
||||
static const char* kLoadingText;
|
||||
static const char* kAudioscrobblerClientId;
|
||||
static const char* kApiKey;
|
||||
static const char* kSecret;
|
||||
|
@ -183,8 +183,8 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
playlist_menu_->addAction(ui_.action_clear_playlist);
|
||||
|
||||
// Radio connections
|
||||
connect(radio_model_, SIGNAL(LoadingStarted()), ui_.playlist, SLOT(StartRadioLoading()));
|
||||
connect(radio_model_, SIGNAL(LoadingFinished()), ui_.playlist, SLOT(StopRadioLoading()));
|
||||
connect(radio_model_, SIGNAL(TaskStarted(QString)), multi_loading_indicator_, SLOT(TaskStarted(QString)));
|
||||
connect(radio_model_, SIGNAL(TaskFinished(QString)), multi_loading_indicator_, SLOT(TaskFinished(QString)));
|
||||
connect(radio_model_, SIGNAL(StreamError(QString)), SLOT(ReportError(QString)));
|
||||
connect(radio_model_, SIGNAL(StreamFinished()), player_, SLOT(Next()));
|
||||
connect(radio_model_, SIGNAL(StreamReady(QUrl,QUrl)), player_, SLOT(StreamReady(QUrl,QUrl)));
|
||||
|
@ -7,6 +7,9 @@ MultiLoadingIndicator::MultiLoadingIndicator(QWidget *parent)
|
||||
}
|
||||
|
||||
void MultiLoadingIndicator::TaskStarted(const QString &name) {
|
||||
if (tasks_.contains(name))
|
||||
return;
|
||||
|
||||
tasks_ << name;
|
||||
|
||||
UpdateText();
|
||||
|
@ -272,7 +272,7 @@ QModelIndex Playlist::InsertRadioStations(const QList<RadioItem*>& items, int af
|
||||
if (!item->playable)
|
||||
continue;
|
||||
|
||||
playlist_items << new RadioPlaylistItem(item->service, item->Url(), item->Title());
|
||||
playlist_items << new RadioPlaylistItem(item->service, item->Url(), item->Title(), item->Artist());
|
||||
}
|
||||
return InsertItems(playlist_items, after);
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
#include "playlistview.h"
|
||||
#include "playlist.h"
|
||||
#include "playlistheader.h"
|
||||
#include "radioloadingindicator.h"
|
||||
#include "trackslider.h"
|
||||
|
||||
#include <QPainter>
|
||||
@ -125,8 +124,7 @@ PlaylistView::PlaylistView(QWidget *parent)
|
||||
glow_intensity_step_(0),
|
||||
row_height_(-1),
|
||||
currenttrack_play_(":currenttrack_play.png"),
|
||||
currenttrack_pause_(":currenttrack_pause.png"),
|
||||
radio_loading_(new RadioLoadingIndicator(this))
|
||||
currenttrack_pause_(":currenttrack_pause.png")
|
||||
{
|
||||
setItemDelegate(new PlaylistDelegateBase(this));
|
||||
setItemDelegateForColumn(Playlist::Column_Length, new LengthItemDelegate(this));
|
||||
@ -140,8 +138,6 @@ PlaylistView::PlaylistView(QWidget *parent)
|
||||
|
||||
glow_timer_->setInterval(1500 / kGlowIntensitySteps);
|
||||
connect(glow_timer_, SIGNAL(timeout()), SLOT(GlowIntensityChanged()));
|
||||
|
||||
radio_loading_->hide();
|
||||
}
|
||||
|
||||
void PlaylistView::setModel(QAbstractItemModel *model) {
|
||||
@ -326,24 +322,3 @@ void PlaylistView::contextMenuEvent(QContextMenuEvent* e) {
|
||||
emit RightClicked(e->globalPos(), indexAt(e->pos()));
|
||||
e->accept();
|
||||
}
|
||||
|
||||
void PlaylistView::resizeEvent(QResizeEvent *event) {
|
||||
const QPoint kPadding(5,5);
|
||||
|
||||
QPoint pos(viewport()->mapTo(this, viewport()->rect().bottomRight()) -
|
||||
kPadding - QPoint(radio_loading_->sizeHint().width(),
|
||||
radio_loading_->sizeHint().height()));
|
||||
|
||||
radio_loading_->move(pos);
|
||||
radio_loading_->resize(radio_loading_->sizeHint());
|
||||
|
||||
QTreeView::resizeEvent(event);
|
||||
}
|
||||
|
||||
void PlaylistView::StartRadioLoading() {
|
||||
radio_loading_->show();
|
||||
}
|
||||
|
||||
void PlaylistView::StopRadioLoading() {
|
||||
radio_loading_->hide();
|
||||
}
|
||||
|
@ -37,9 +37,6 @@ class PlaylistView : public QTreeView {
|
||||
public:
|
||||
PlaylistView(QWidget* parent = 0);
|
||||
|
||||
// QWidget
|
||||
void resizeEvent(QResizeEvent *event);
|
||||
|
||||
// QTreeView
|
||||
void setModel(QAbstractItemModel *model);
|
||||
void drawRow(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;
|
||||
@ -52,9 +49,6 @@ class PlaylistView : public QTreeView {
|
||||
void StopGlowing();
|
||||
void StartGlowing();
|
||||
|
||||
void StartRadioLoading();
|
||||
void StopRadioLoading();
|
||||
|
||||
signals:
|
||||
void PlayPauseItem(const QModelIndex& index);
|
||||
void RightClicked(const QPoint& global_pos, const QModelIndex& index);
|
||||
@ -88,8 +82,6 @@ class PlaylistView : public QTreeView {
|
||||
QList<QPixmap> currenttrack_bar_right_;
|
||||
QPixmap currenttrack_play_;
|
||||
QPixmap currenttrack_pause_;
|
||||
|
||||
RadioLoadingIndicator* radio_loading_;
|
||||
};
|
||||
|
||||
#endif // PLAYLISTVIEW_H
|
||||
|
@ -22,3 +22,7 @@ QUrl RadioItem::Url() const {
|
||||
QString RadioItem::Title() const {
|
||||
return service->TitleForItem(this);
|
||||
}
|
||||
|
||||
QString RadioItem::Artist() const {
|
||||
return service->ArtistForItem(this);
|
||||
}
|
||||
|
@ -21,8 +21,10 @@ class RadioItem : public SimpleTreeItem<RadioItem> {
|
||||
|
||||
QUrl Url() const;
|
||||
QString Title() const;
|
||||
QString Artist() const;
|
||||
|
||||
QIcon icon;
|
||||
QString artist;
|
||||
RadioService* service;
|
||||
bool playable;
|
||||
};
|
||||
|
@ -1,56 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>RadioLoadingIndicator</class>
|
||||
<widget class="QWidget" name="RadioLoadingIndicator">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>191</width>
|
||||
<height>24</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="text"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="BusyIndicator" name="spinner">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>BusyIndicator</class>
|
||||
<extends>QLabel</extends>
|
||||
<header>busyindicator.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@ -1,6 +1,7 @@
|
||||
#include "radiomodel.h"
|
||||
#include "radioservice.h"
|
||||
#include "lastfmservice.h"
|
||||
#include "somafmservice.h"
|
||||
#include "radiomimedata.h"
|
||||
|
||||
#include <QMimeData>
|
||||
@ -16,14 +17,15 @@ RadioModel::RadioModel(QObject* parent)
|
||||
root_->lazy_loaded = true;
|
||||
|
||||
AddService(new LastFMService(this));
|
||||
AddService(new SomaFMService(this));
|
||||
}
|
||||
|
||||
void RadioModel::AddService(RadioService *service) {
|
||||
sServices[service->name()] = service;
|
||||
service->CreateRootItem(root_);
|
||||
|
||||
connect(service, SIGNAL(LoadingStarted()), SIGNAL(LoadingStarted()));
|
||||
connect(service, SIGNAL(LoadingFinished()), SIGNAL(LoadingFinished()));
|
||||
connect(service, SIGNAL(TaskStarted(QString)), SIGNAL(TaskStarted(QString)));
|
||||
connect(service, SIGNAL(TaskFinished(QString)), SIGNAL(TaskFinished(QString)));
|
||||
connect(service, SIGNAL(StreamReady(QUrl,QUrl)), SIGNAL(StreamReady(QUrl,QUrl)));
|
||||
connect(service, SIGNAL(StreamFinished()), SIGNAL(StreamFinished()));
|
||||
connect(service, SIGNAL(StreamError(QString)), SIGNAL(StreamError(QString)));
|
||||
|
@ -35,8 +35,8 @@ class RadioModel : public SimpleTreeModel<RadioItem> {
|
||||
void ShowContextMenu(RadioItem* item, const QPoint& global_pos);
|
||||
|
||||
signals:
|
||||
void LoadingStarted();
|
||||
void LoadingFinished();
|
||||
void TaskStarted(const QString&);
|
||||
void TaskFinished(const QString&);
|
||||
void StreamReady(const QUrl& original_url, const QUrl& media_url);
|
||||
void StreamFinished();
|
||||
void StreamError(const QString& message);
|
||||
|
@ -10,10 +10,11 @@ RadioPlaylistItem::RadioPlaylistItem()
|
||||
}
|
||||
|
||||
RadioPlaylistItem::RadioPlaylistItem(RadioService* service, const QUrl& url,
|
||||
const QString& title)
|
||||
const QString& title, const QString& artist)
|
||||
: service_(service),
|
||||
url_(url),
|
||||
title_(title)
|
||||
title_(title),
|
||||
artist_(artist)
|
||||
{
|
||||
InitMetadata();
|
||||
}
|
||||
@ -22,12 +23,15 @@ void RadioPlaylistItem::Save(QSettings& settings) const {
|
||||
settings.setValue("service", service_->name());
|
||||
settings.setValue("url", url_.toString());
|
||||
settings.setValue("title", title_);
|
||||
if (!artist_.isEmpty())
|
||||
settings.setValue("artist", artist_);
|
||||
}
|
||||
|
||||
void RadioPlaylistItem::Restore(const QSettings& settings) {
|
||||
service_ = RadioModel::ServiceByName(settings.value("service").toString());
|
||||
url_ = settings.value("url").toString();
|
||||
title_ = settings.value("title").toString();
|
||||
artist_ = settings.value("artist").toString();
|
||||
|
||||
InitMetadata();
|
||||
}
|
||||
@ -39,6 +43,8 @@ void RadioPlaylistItem::InitMetadata() {
|
||||
metadata_.set_title(title_);
|
||||
else
|
||||
metadata_.set_title(url_.toString());
|
||||
|
||||
metadata_.set_artist(artist_);
|
||||
}
|
||||
|
||||
Song RadioPlaylistItem::Metadata() const {
|
||||
|
@ -11,7 +11,8 @@ class RadioService;
|
||||
class RadioPlaylistItem : public PlaylistItem {
|
||||
public:
|
||||
RadioPlaylistItem();
|
||||
RadioPlaylistItem(RadioService* service, const QUrl& url, const QString& title);
|
||||
RadioPlaylistItem(RadioService* service, const QUrl& url,
|
||||
const QString& title, const QString& artist);
|
||||
|
||||
Type type() const { return Type_Radio; }
|
||||
Options options() const;
|
||||
@ -36,6 +37,7 @@ class RadioPlaylistItem : public PlaylistItem {
|
||||
RadioService* service_;
|
||||
QUrl url_;
|
||||
QString title_;
|
||||
QString artist_;
|
||||
|
||||
Song metadata_;
|
||||
Song temp_metadata_;
|
||||
|
@ -5,3 +5,19 @@ RadioService::RadioService(const QString& name, QObject *parent)
|
||||
name_(name)
|
||||
{
|
||||
}
|
||||
|
||||
QUrl RadioService::UrlForItem(const RadioItem* item) const {
|
||||
return item->key;
|
||||
}
|
||||
|
||||
QString RadioService::TitleForItem(const RadioItem* item) const {
|
||||
return item->DisplayText();
|
||||
}
|
||||
|
||||
QString RadioService::ArtistForItem(const RadioItem* item) const {
|
||||
return item->artist;
|
||||
}
|
||||
|
||||
void RadioService::LoadNext(const QUrl&) {
|
||||
emit StreamFinished();
|
||||
}
|
||||
|
@ -21,21 +21,23 @@ class RadioService : public QObject {
|
||||
virtual RadioItem* CreateRootItem(RadioItem* parent) = 0;
|
||||
virtual void LazyPopulate(RadioItem* item) = 0;
|
||||
|
||||
virtual QUrl UrlForItem(const RadioItem* item) const = 0;
|
||||
virtual QString TitleForItem(const RadioItem* item) const = 0;
|
||||
virtual QUrl UrlForItem(const RadioItem* item) const;
|
||||
virtual QString TitleForItem(const RadioItem* item) const;
|
||||
virtual QString ArtistForItem(const RadioItem* item) const;
|
||||
|
||||
virtual void ShowContextMenu(RadioItem* item, const QPoint& global_pos) {
|
||||
Q_UNUSED(item); Q_UNUSED(global_pos); }
|
||||
|
||||
virtual void StartLoading(const QUrl& url) = 0;
|
||||
virtual void LoadNext(const QUrl& url) = 0;
|
||||
virtual void LoadNext(const QUrl& url);
|
||||
|
||||
virtual bool IsPauseAllowed() const { return true; }
|
||||
virtual bool ShowLastFmControls() const { return false; }
|
||||
|
||||
signals:
|
||||
void LoadingStarted();
|
||||
void LoadingFinished();
|
||||
void TaskStarted(const QString& name);
|
||||
void TaskFinished(const QString& name);
|
||||
|
||||
void StreamReady(const QUrl& original_url, const QUrl& media_url);
|
||||
void StreamFinished();
|
||||
void StreamError(const QString& message);
|
||||
|
164
src/somafmservice.cpp
Normal file
164
src/somafmservice.cpp
Normal file
@ -0,0 +1,164 @@
|
||||
#include "somafmservice.h"
|
||||
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkReply>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QSettings>
|
||||
#include <QTemporaryFile>
|
||||
#include <QCoreApplication>
|
||||
#include <QtDebug>
|
||||
|
||||
const char* SomaFMService::kServiceName = "SomaFM";
|
||||
const char* SomaFMService::kLoadingChannelsText = "Getting channels";
|
||||
const char* SomaFMService::kLoadingStreamText = "Loading stream";
|
||||
const char* SomaFMService::kChannelListUrl = "http://somafm.com/channels.xml";
|
||||
|
||||
SomaFMService::SomaFMService(QObject* parent)
|
||||
: RadioService(kServiceName, parent),
|
||||
root_(NULL),
|
||||
network_(new QNetworkAccessManager(this))
|
||||
{
|
||||
}
|
||||
|
||||
RadioItem* SomaFMService::CreateRootItem(RadioItem* parent) {
|
||||
root_ = new RadioItem(this, RadioItem::Type_Service, kServiceName, parent);
|
||||
return root_;
|
||||
}
|
||||
|
||||
void SomaFMService::LazyPopulate(RadioItem* item) {
|
||||
switch (item->type) {
|
||||
case RadioItem::Type_Service:
|
||||
RefreshChannels();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
item->lazy_loaded = true;
|
||||
}
|
||||
|
||||
void SomaFMService::ShowContextMenu(RadioItem* item, const QPoint& global_pos) {
|
||||
|
||||
}
|
||||
|
||||
void SomaFMService::StartLoading(const QUrl& url) {
|
||||
// Load the playlist
|
||||
QNetworkRequest request = QNetworkRequest(url);
|
||||
request.setRawHeader("User-Agent", QString("%1 %2").arg(
|
||||
QCoreApplication::applicationName(), QCoreApplication::applicationVersion()).toUtf8());
|
||||
|
||||
QNetworkReply* reply = network_->get(request);
|
||||
connect(reply, SIGNAL(finished()), SLOT(LoadPlaylistFinished()));
|
||||
|
||||
emit TaskStarted(kLoadingStreamText);
|
||||
}
|
||||
|
||||
void SomaFMService::LoadPlaylistFinished() {
|
||||
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
|
||||
emit TaskFinished(kLoadingStreamText);
|
||||
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
// TODO: Error handling
|
||||
qDebug() << reply->errorString();
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Replace with some more robust .pls parsing :(
|
||||
QTemporaryFile temp_file;
|
||||
temp_file.open();
|
||||
temp_file.write(reply->readAll());
|
||||
temp_file.flush();
|
||||
|
||||
QSettings s(temp_file.fileName(), QSettings::IniFormat);
|
||||
s.beginGroup("playlist");
|
||||
|
||||
emit StreamReady(reply->url().toString(), s.value("File1").toString());
|
||||
}
|
||||
|
||||
void SomaFMService::RefreshChannels() {
|
||||
QNetworkRequest request = QNetworkRequest(QUrl(kChannelListUrl));
|
||||
request.setRawHeader("User-Agent", QString("%1 %2").arg(
|
||||
QCoreApplication::applicationName(), QCoreApplication::applicationVersion()).toUtf8());
|
||||
|
||||
QNetworkReply* reply = network_->get(request);
|
||||
connect(reply, SIGNAL(finished()), SLOT(RefreshChannelsFinished()));
|
||||
|
||||
emit TaskStarted(kLoadingChannelsText);
|
||||
}
|
||||
|
||||
void SomaFMService::RefreshChannelsFinished() {
|
||||
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
|
||||
emit TaskFinished(kLoadingChannelsText);
|
||||
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
// TODO: Error handling
|
||||
qDebug() << reply->errorString();
|
||||
return;
|
||||
}
|
||||
|
||||
QXmlStreamReader reader(reply);
|
||||
while (!reader.atEnd()) {
|
||||
reader.readNext();
|
||||
|
||||
if (reader.tokenType() == QXmlStreamReader::StartElement &&
|
||||
reader.name() == "channel") {
|
||||
ReadChannel(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SomaFMService::ReadChannel(QXmlStreamReader& reader) {
|
||||
RadioItem* item = new RadioItem(this, Type_Stream, QString::null);
|
||||
item->lazy_loaded = true;
|
||||
item->playable = true;
|
||||
|
||||
while (!reader.atEnd()) {
|
||||
switch (reader.readNext()) {
|
||||
case QXmlStreamReader::EndElement:
|
||||
if (item->key.isNull()) {
|
||||
// Didn't find a URL
|
||||
delete item;
|
||||
} else {
|
||||
item->InsertNotify(root_);
|
||||
}
|
||||
return;
|
||||
|
||||
case QXmlStreamReader::StartElement:
|
||||
if (reader.name() == "title") {
|
||||
item->display_text = reader.readElementText();
|
||||
} else if (reader.name() == "dj") {
|
||||
item->artist = reader.readElementText();
|
||||
} else if (reader.name() == "fastpls" && reader.attributes().value("format") == "mp3") {
|
||||
item->key = reader.readElementText();
|
||||
} else {
|
||||
ConsumeElement(reader);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
delete item;
|
||||
}
|
||||
|
||||
void SomaFMService::ConsumeElement(QXmlStreamReader& reader) {
|
||||
int level = 1;
|
||||
while (!reader.atEnd()) {
|
||||
switch (reader.readNext()) {
|
||||
case QXmlStreamReader::StartElement: level++; break;
|
||||
case QXmlStreamReader::EndElement: level--; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (level == 0)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QString SomaFMService::TitleForItem(const RadioItem* item) const {
|
||||
return "SomaFM " + item->display_text;
|
||||
}
|
48
src/somafmservice.h
Normal file
48
src/somafmservice.h
Normal file
@ -0,0 +1,48 @@
|
||||
#ifndef SOMAFMSERVICE_H
|
||||
#define SOMAFMSERVICE_H
|
||||
|
||||
#include "radioservice.h"
|
||||
|
||||
class QNetworkAccessManager;
|
||||
class QXmlStreamReader;
|
||||
|
||||
class SomaFMService : public RadioService {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SomaFMService(QObject* parent = 0);
|
||||
|
||||
enum ItemType {
|
||||
Type_Stream = 2000,
|
||||
};
|
||||
|
||||
static const char* kServiceName;
|
||||
static const char* kLoadingChannelsText;
|
||||
static const char* kLoadingStreamText;
|
||||
static const char* kChannelListUrl;
|
||||
|
||||
RadioItem* CreateRootItem(RadioItem* parent);
|
||||
void LazyPopulate(RadioItem* item);
|
||||
|
||||
QString TitleForItem(const RadioItem* item) const;
|
||||
|
||||
void ShowContextMenu(RadioItem* item, const QPoint& global_pos);
|
||||
|
||||
void StartLoading(const QUrl& url);
|
||||
|
||||
private slots:
|
||||
void RefreshChannelsFinished();
|
||||
void LoadPlaylistFinished();
|
||||
|
||||
private:
|
||||
void RefreshChannels();
|
||||
void ReadChannel(QXmlStreamReader& reader);
|
||||
void ConsumeElement(QXmlStreamReader& reader);
|
||||
|
||||
private:
|
||||
RadioItem* root_;
|
||||
|
||||
QNetworkAccessManager* network_;
|
||||
};
|
||||
|
||||
#endif // SOMAFMSERVICE_H
|
28
src/src.pro
28
src/src.pro
@ -1,11 +1,10 @@
|
||||
# Change this line to install Clementine somewhere else
|
||||
install_prefix = /usr
|
||||
|
||||
VERSION = 0.1
|
||||
QT += sql \
|
||||
network \
|
||||
opengl \
|
||||
xml
|
||||
xml \
|
||||
opengl
|
||||
TARGET = clementine
|
||||
TEMPLATE = app
|
||||
SOURCES += main.cpp \
|
||||
@ -49,7 +48,8 @@ SOURCES += main.cpp \
|
||||
trackslider.cpp \
|
||||
edittagdialog.cpp \
|
||||
lineedit.cpp \
|
||||
multiloadingindicator.cpp
|
||||
multiloadingindicator.cpp \
|
||||
somafmservice.cpp
|
||||
HEADERS += mainwindow.h \
|
||||
player.h \
|
||||
library.h \
|
||||
@ -98,12 +98,12 @@ HEADERS += mainwindow.h \
|
||||
trackslider.h \
|
||||
edittagdialog.h \
|
||||
lineedit.h \
|
||||
multiloadingindicator.h
|
||||
multiloadingindicator.h \
|
||||
somafmservice.h
|
||||
FORMS += mainwindow.ui \
|
||||
libraryconfig.ui \
|
||||
fileview.ui \
|
||||
lastfmconfig.ui \
|
||||
radioloadingindicator.ui \
|
||||
lastfmstationdialog.ui \
|
||||
trackslider.ui \
|
||||
edittagdialog.ui \
|
||||
@ -158,6 +158,16 @@ win32:SOURCES += ../3rdparty/qtsingleapplication/qtlockedfile_win32.cpp
|
||||
# Installs
|
||||
target.path = $${install_prefix}/bin/
|
||||
desktop.path = dummy
|
||||
desktop.extra = xdg-icon-resource install --size 64 ../dist/clementine_64.png application-x-clementine ; \
|
||||
xdg-desktop-menu install --novendor ../dist/clementine.desktop
|
||||
INSTALLS += target desktop
|
||||
desktop.extra = xdg-icon-resource \
|
||||
install \
|
||||
--size \
|
||||
64 \
|
||||
../dist/clementine_64.png \
|
||||
application-x-clementine \
|
||||
; \
|
||||
xdg-desktop-menu \
|
||||
install \
|
||||
--novendor \
|
||||
../dist/clementine.desktop
|
||||
INSTALLS += target \
|
||||
desktop
|
||||
|
@ -29,6 +29,9 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="slider">
|
||||
<property name="singleStep">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
|
Loading…
Reference in New Issue
Block a user