Simple last.fm streaming works
This commit is contained in:
parent
4cbad8929d
commit
7a3678e806
@ -11,14 +11,15 @@
|
||||
const char* LastFMService::kSettingsGroup = "Last.fm";
|
||||
|
||||
LastFMService::LastFMService(QObject* parent)
|
||||
: RadioService("Last.fm", parent)
|
||||
: RadioService("Last.fm", parent),
|
||||
tuner_(NULL)
|
||||
{
|
||||
lastfm::ws::ApiKey = "75d20fb472be99275392aefa2760ea09";
|
||||
lastfm::ws::SharedSecret = "d3072b60ae626be12be69448f5c46e70";
|
||||
|
||||
QSettings settings;
|
||||
settings.beginGroup(kSettingsGroup);
|
||||
lastfm::ws::Username = settings.value("user").toString();
|
||||
lastfm::ws::Username = settings.value("username").toString();
|
||||
lastfm::ws::SessionKey = settings.value("session").toString();
|
||||
|
||||
config_ = new LastFMConfig(this);
|
||||
@ -63,6 +64,7 @@ RadioItem* LastFMService::CreateStationItem(ItemType type, const QString& name,
|
||||
RadioItem* ret = new RadioItem(this, type, name, parent);
|
||||
ret->lazy_loaded = true;
|
||||
ret->icon = QIcon(icon);
|
||||
ret->playable = true;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -104,3 +106,90 @@ void LastFMService::AuthenticateReplyFinished() {
|
||||
|
||||
emit AuthenticationComplete(true);
|
||||
}
|
||||
|
||||
QList<QUrl> LastFMService::UrlsForItem(RadioItem* item) {
|
||||
QList<QUrl> ret;
|
||||
|
||||
switch (item->type) {
|
||||
case Type_MyRecommendations:
|
||||
ret << QUrl("lastfm://user/" + lastfm::ws::Username + "/recommended");
|
||||
break;
|
||||
|
||||
case Type_MyLoved:
|
||||
ret << QUrl("lastfm://user/" + lastfm::ws::Username + "/loved");
|
||||
break;
|
||||
|
||||
case Type_MyNeighbourhood:
|
||||
ret << QUrl("lastfm://user/" + lastfm::ws::Username + "/neighbours");
|
||||
break;
|
||||
|
||||
case Type_MyRadio:
|
||||
ret << QUrl("lastfm://user/" + lastfm::ws::Username + "/library");
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void LastFMService::StartLoading(const QUrl& url) {
|
||||
if (url.scheme() != "lastfm")
|
||||
return;
|
||||
if (lastfm::ws::SessionKey.isEmpty())
|
||||
return;
|
||||
|
||||
emit LoadingStarted();
|
||||
|
||||
delete tuner_;
|
||||
last_url_ = url;
|
||||
tuner_ = new lastfm::RadioTuner(lastfm::RadioStation(url));
|
||||
connect(tuner_, SIGNAL(trackAvailable()), SLOT(TunerTrackAvailable()));
|
||||
connect(tuner_, SIGNAL(error(lastfm::ws::Error)), SLOT(TunerError(lastfm::ws::Error)));
|
||||
}
|
||||
|
||||
void LastFMService::TunerError(lastfm::ws::Error error) {
|
||||
emit LoadingFinished();
|
||||
|
||||
if (error == lastfm::ws::NotEnoughContent) {
|
||||
emit StreamFinished();
|
||||
return;
|
||||
}
|
||||
|
||||
emit StreamError(ErrorString(error));
|
||||
qDebug() << "Last.fm error" << error;
|
||||
}
|
||||
|
||||
QString LastFMService::ErrorString(lastfm::ws::Error error) const {
|
||||
switch (error) {
|
||||
case lastfm::ws::InvalidService: return "Invalid service";
|
||||
case lastfm::ws::InvalidMethod: return "Invalid method";
|
||||
case lastfm::ws::AuthenticationFailed: return "Authentication failed";
|
||||
case lastfm::ws::InvalidFormat: return "Invalid format";
|
||||
case lastfm::ws::InvalidParameters: return "Invalid parameters";
|
||||
case lastfm::ws::InvalidResourceSpecified: return "Invalid resource specified";
|
||||
case lastfm::ws::OperationFailed: return "Operation failed";
|
||||
case lastfm::ws::InvalidSessionKey: return "Invalid session key";
|
||||
case lastfm::ws::InvalidApiKey: return "Invalid API key";
|
||||
case lastfm::ws::ServiceOffline: return "Service offline";
|
||||
case lastfm::ws::SubscribersOnly: return "This stream is for paid subscribers only";
|
||||
|
||||
case lastfm::ws::TryAgainLater: return "Last.fm is currently busy, please try again in a few minutes";
|
||||
|
||||
case lastfm::ws::NotEnoughContent: return "Not enough content";
|
||||
case lastfm::ws::NotEnoughMembers: return "Not enough members";
|
||||
case lastfm::ws::NotEnoughFans: return "Not enough fans";
|
||||
case lastfm::ws::NotEnoughNeighbours: return "Not enough neighbours";
|
||||
|
||||
case lastfm::ws::MalformedResponse: return "Malformed response";
|
||||
|
||||
case lastfm::ws::UnknownError:
|
||||
default:
|
||||
return "Unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
void LastFMService::TunerTrackAvailable() {
|
||||
emit LoadingFinished();
|
||||
|
||||
lastfm::Track track = tuner_->takeNextTrack();
|
||||
emit StreamReady(last_url_, track.url());
|
||||
}
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
#include "radioservice.h"
|
||||
|
||||
#include <lastfm/RadioTuner>
|
||||
|
||||
class LastFMConfig;
|
||||
|
||||
class LastFMService : public RadioService {
|
||||
@ -21,8 +23,11 @@ class LastFMService : public RadioService {
|
||||
Type_MyNeighbourhood,
|
||||
};
|
||||
|
||||
// RadioService
|
||||
RadioItem* CreateRootItem(RadioItem* parent);
|
||||
void LazyPopulate(RadioItem *item);
|
||||
QList<QUrl> UrlsForItem(RadioItem* item);
|
||||
void StartLoading(const QUrl& url);
|
||||
|
||||
void Authenticate(const QString& username, const QString& password);
|
||||
|
||||
@ -32,12 +37,18 @@ class LastFMService : public RadioService {
|
||||
private slots:
|
||||
void AuthenticateReplyFinished();
|
||||
|
||||
void TunerTrackAvailable();
|
||||
void TunerError(lastfm::ws::Error error);
|
||||
|
||||
private:
|
||||
RadioItem* CreateStationItem(ItemType type, const QString& name,
|
||||
const QString& icon, RadioItem* parent);
|
||||
QString ErrorString(lastfm::ws::Error error) const;
|
||||
|
||||
private:
|
||||
LastFMConfig* config_;
|
||||
lastfm::RadioTuner* tuner_;
|
||||
QUrl last_url_;
|
||||
};
|
||||
|
||||
#endif // LASTFMSERVICE_H
|
||||
|
@ -23,10 +23,10 @@ const char* MainWindow::kSettingsGroup = "MainWindow";
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent)
|
||||
: QMainWindow(parent),
|
||||
radio_model_(new RadioModel(this)),
|
||||
playlist_(new Playlist(this)),
|
||||
player_(new Player(playlist_, this)),
|
||||
library_(new Library(player_->GetEngine(), this)),
|
||||
radio_model_(new RadioModel(this)),
|
||||
library_sort_model_(new QSortFilterProxyModel(this)),
|
||||
tray_icon_(new SystemTrayIcon(this))
|
||||
{
|
||||
@ -137,6 +137,13 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
library_menu->addAction("Configure library...", library_, SLOT(ShowConfig()));
|
||||
ui_.library_options->setMenu(library_menu);
|
||||
|
||||
// Radio connections
|
||||
connect(radio_model_, SIGNAL(LoadingStarted()), ui_.playlist, SLOT(StartRadioLoading()));
|
||||
connect(radio_model_, SIGNAL(LoadingFinished()), ui_.playlist, SLOT(StopRadioLoading()));
|
||||
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)));
|
||||
|
||||
// Tray icon
|
||||
QMenu* tray_menu = new QMenu(this);
|
||||
tray_menu->addAction(ui_.action_previous_track);
|
||||
|
@ -55,10 +55,10 @@ class MainWindow : public QMainWindow {
|
||||
|
||||
Ui::MainWindow ui_;
|
||||
|
||||
RadioModel* radio_model_;
|
||||
Playlist* playlist_;
|
||||
Player* player_;
|
||||
Library* library_;
|
||||
RadioModel* radio_model_;
|
||||
|
||||
QSortFilterProxyModel* library_sort_model_;
|
||||
|
||||
|
@ -99,5 +99,21 @@ Engine::State Player::GetState() const {
|
||||
|
||||
void Player::PlayAt(int index) {
|
||||
playlist_->set_current_item(index);
|
||||
engine_->play(playlist_->item_at(index)->Url());
|
||||
|
||||
PlaylistItem* item = playlist_->item_at(index);
|
||||
|
||||
if (!item->StartLoading())
|
||||
engine_->play(item->Url());
|
||||
}
|
||||
|
||||
void Player::StreamReady(const QUrl& original_url, const QUrl& media_url) {
|
||||
int current_index = playlist_->current_item();
|
||||
if (current_index == -1)
|
||||
return;
|
||||
|
||||
PlaylistItem* item = playlist_->item_at(current_index);
|
||||
if (!item || item->Url() != original_url)
|
||||
return;
|
||||
|
||||
engine_->play(media_url);
|
||||
}
|
||||
|
@ -27,6 +27,8 @@ class Player : public QObject {
|
||||
void Stop();
|
||||
void SetVolume(int value);
|
||||
|
||||
void StreamReady(const QUrl& original_url, const QUrl& media_url);
|
||||
|
||||
signals:
|
||||
void Playing();
|
||||
void Paused();
|
||||
|
@ -1,6 +1,8 @@
|
||||
#include "playlist.h"
|
||||
#include "songmimedata.h"
|
||||
#include "songplaylistitem.h"
|
||||
#include "radiomimedata.h"
|
||||
#include "radioplaylistitem.h"
|
||||
|
||||
#include <QtDebug>
|
||||
#include <QMimeData>
|
||||
@ -117,10 +119,12 @@ bool Playlist::dropMimeData(const QMimeData* data, Qt::DropAction action, int ro
|
||||
if (action == Qt::IgnoreAction)
|
||||
return false;
|
||||
|
||||
QList<PlaylistItem*> added_items;
|
||||
if (const SongMimeData* song_data = qobject_cast<const SongMimeData*>(data)) {
|
||||
// Dragged from the library
|
||||
InsertSongs(song_data->songs, row);
|
||||
} else if (const RadioMimeData* radio_data = qobject_cast<const RadioMimeData*>(data)) {
|
||||
// Dragged from the Radio pane
|
||||
InsertRadioStations(radio_data->services, radio_data->urls(), row);
|
||||
} else if (data->hasFormat(kRowsMimetype)) {
|
||||
// Dragged from the playlist
|
||||
// Rearranging it is tricky...
|
||||
@ -232,6 +236,17 @@ QModelIndex Playlist::InsertSongs(const SongList& songs, int after) {
|
||||
return InsertItems(items, after);
|
||||
}
|
||||
|
||||
QModelIndex Playlist::InsertRadioStations(const QList<RadioService*>& services,
|
||||
const QList<QUrl>& urls, int after) {
|
||||
Q_ASSERT(services.count() == urls.count());
|
||||
|
||||
QList<PlaylistItem*> items;
|
||||
for (int i=0 ; i<services.count() ; ++i) {
|
||||
items << new RadioPlaylistItem(services[i], urls[i]);
|
||||
}
|
||||
return InsertItems(items, after);
|
||||
}
|
||||
|
||||
QMimeData* Playlist::mimeData(const QModelIndexList& indexes) const {
|
||||
QMimeData* data = new QMimeData;
|
||||
|
||||
|
@ -7,6 +7,8 @@
|
||||
#include "playlistitem.h"
|
||||
#include "song.h"
|
||||
|
||||
class RadioService;
|
||||
|
||||
class Playlist : public QAbstractListModel {
|
||||
Q_OBJECT
|
||||
|
||||
@ -49,6 +51,8 @@ class Playlist : public QAbstractListModel {
|
||||
// Changing the playlist
|
||||
QModelIndex InsertItems(const QList<PlaylistItem*>& items, int after = -1);
|
||||
QModelIndex InsertSongs(const SongList& items, int after = -1);
|
||||
QModelIndex InsertRadioStations(const QList<RadioService*>& services,
|
||||
const QList<QUrl>& urls, int after = -1);
|
||||
QModelIndex InsertPaths(QList<QUrl> urls, int after = -1);
|
||||
void StopAfter(int row);
|
||||
|
||||
|
@ -1,11 +1,13 @@
|
||||
#include "playlistitem.h"
|
||||
#include "songplaylistitem.h"
|
||||
#include "radioplaylistitem.h"
|
||||
|
||||
#include <QtDebug>
|
||||
|
||||
QString PlaylistItem::type_string() const {
|
||||
switch (type()) {
|
||||
case Type_Song: return "Song";
|
||||
case Type_Radio: return "Radio";
|
||||
default:
|
||||
qWarning() << "Invalid PlaylistItem type:" << type();
|
||||
return QString::null;
|
||||
@ -15,6 +17,8 @@ QString PlaylistItem::type_string() const {
|
||||
PlaylistItem* PlaylistItem::NewFromType(const QString& type) {
|
||||
if (type == "Song")
|
||||
return new SongPlaylistItem;
|
||||
if (type == "Radio")
|
||||
return new RadioPlaylistItem;
|
||||
|
||||
qWarning() << "Invalid PlaylistItem type:" << type;
|
||||
return NULL;
|
||||
|
@ -15,6 +15,7 @@ class PlaylistItem {
|
||||
|
||||
enum Type {
|
||||
Type_Song,
|
||||
Type_Radio,
|
||||
};
|
||||
|
||||
virtual Type type() const = 0;
|
||||
@ -29,6 +30,11 @@ class PlaylistItem {
|
||||
virtual int Length() const = 0;
|
||||
virtual int Track() const = 0;
|
||||
|
||||
// If the item needs to do anything special before it can play (eg. start
|
||||
// streaming the radio stream), then it should implement StartLoading() and
|
||||
// return true. If it returns false then the URL from Url() will be passed
|
||||
// directly to xine instead.
|
||||
virtual bool StartLoading() { return false; }
|
||||
virtual QUrl Url() = 0;
|
||||
};
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "playlistview.h"
|
||||
#include "playlist.h"
|
||||
#include "playlistheader.h"
|
||||
#include "radioloadingindicator.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QHeaderView>
|
||||
@ -9,6 +10,7 @@
|
||||
#include <QTimer>
|
||||
#include <QKeyEvent>
|
||||
#include <QMenu>
|
||||
#include <QScrollBar>
|
||||
|
||||
#include <math.h>
|
||||
|
||||
@ -98,7 +100,8 @@ PlaylistView::PlaylistView(QWidget *parent)
|
||||
row_height_(-1),
|
||||
currenttrack_play_(":currenttrack_play.png"),
|
||||
currenttrack_pause_(":currenttrack_pause.png"),
|
||||
menu_(new QMenu(this))
|
||||
menu_(new QMenu(this)),
|
||||
radio_loading_(new RadioLoadingIndicator(this))
|
||||
{
|
||||
setItemDelegate(new PlaylistDelegateBase(this));
|
||||
setItemDelegateForColumn(Playlist::Column_Length, new LengthItemDelegate(this));
|
||||
@ -114,6 +117,8 @@ PlaylistView::PlaylistView(QWidget *parent)
|
||||
|
||||
menu_->addAction(QIcon(":media-playback-stop.png"), "Stop playing after track",
|
||||
this, SLOT(StopAfter()));
|
||||
|
||||
radio_loading_->hide();
|
||||
}
|
||||
|
||||
void PlaylistView::setModel(QAbstractItemModel *model) {
|
||||
@ -281,3 +286,24 @@ void PlaylistView::StopAfter() {
|
||||
|
||||
playlist->StopAfter(menu_index_.row());
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
@ -4,6 +4,8 @@
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QTreeView>
|
||||
|
||||
class RadioLoadingIndicator;
|
||||
|
||||
class PlaylistDelegateBase : public QStyledItemDelegate {
|
||||
public:
|
||||
PlaylistDelegateBase(QTreeView* view);
|
||||
@ -28,6 +30,9 @@ 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;
|
||||
@ -40,6 +45,9 @@ class PlaylistView : public QTreeView {
|
||||
void StopGlowing();
|
||||
void StartGlowing();
|
||||
|
||||
void StartRadioLoading();
|
||||
void StopRadioLoading();
|
||||
|
||||
protected:
|
||||
void hideEvent(QHideEvent* event);
|
||||
void showEvent(QShowEvent* event);
|
||||
@ -74,6 +82,8 @@ class PlaylistView : public QTreeView {
|
||||
|
||||
QMenu* menu_;
|
||||
QModelIndex menu_index_;
|
||||
|
||||
RadioLoadingIndicator* radio_loading_;
|
||||
};
|
||||
|
||||
#endif // PLAYLISTVIEW_H
|
||||
|
@ -3,6 +3,7 @@
|
||||
RadioItem::RadioItem(RadioService* _service, int type, const QString& key,
|
||||
RadioItem* parent)
|
||||
: SimpleTreeItem<RadioItem>(type, key, parent),
|
||||
service(_service)
|
||||
service(_service),
|
||||
playable(false)
|
||||
{
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ class RadioItem : public SimpleTreeItem<RadioItem> {
|
||||
|
||||
QIcon icon;
|
||||
RadioService* service;
|
||||
bool playable;
|
||||
};
|
||||
|
||||
#endif // RADIOITEM_H
|
||||
|
18
src/radioloadingindicator.cpp
Normal file
18
src/radioloadingindicator.cpp
Normal file
@ -0,0 +1,18 @@
|
||||
#include "radioloadingindicator.h"
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
RadioLoadingIndicator::RadioLoadingIndicator(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
ui_.setupUi(this);
|
||||
}
|
||||
|
||||
void RadioLoadingIndicator::paintEvent(QPaintEvent*) {
|
||||
QPainter p(this);
|
||||
|
||||
p.setRenderHint(QPainter::Antialiasing, true);
|
||||
p.setOpacity(0.15);
|
||||
p.setBrush(palette().color(QPalette::Text));
|
||||
p.drawRoundedRect(rect(), 10, 10);
|
||||
}
|
19
src/radioloadingindicator.h
Normal file
19
src/radioloadingindicator.h
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef RADIOLOADINGINDICATOR_H
|
||||
#define RADIOLOADINGINDICATOR_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include "ui_radioloadingindicator.h"
|
||||
|
||||
class RadioLoadingIndicator : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
RadioLoadingIndicator(QWidget *parent = 0);
|
||||
|
||||
void paintEvent(QPaintEvent *);
|
||||
|
||||
private:
|
||||
Ui::RadioLoadingIndicator ui_;
|
||||
};
|
||||
|
||||
#endif // RADIOLOADINGINDICATOR_H
|
60
src/radioloadingindicator.ui
Normal file
60
src/radioloadingindicator.ui
Normal file
@ -0,0 +1,60 @@
|
||||
<?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>160</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">
|
||||
<property name="text">
|
||||
<string><span style=" font-weight:600;">Loading radio stream...</span></string>
|
||||
</property>
|
||||
</widget>
|
||||
</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>
|
15
src/radiomimedata.h
Normal file
15
src/radiomimedata.h
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef RADIOMIMEDATA_H
|
||||
#define RADIOMIMEDATA_H
|
||||
|
||||
#include <QMimeData>
|
||||
|
||||
class RadioService;
|
||||
|
||||
class RadioMimeData : public QMimeData {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QList<RadioService*> services;
|
||||
};
|
||||
|
||||
#endif // RADIOMIMEDATA_H
|
@ -1,15 +1,38 @@
|
||||
#include "radiomodel.h"
|
||||
#include "radioservice.h"
|
||||
#include "lastfmservice.h"
|
||||
#include "radiomimedata.h"
|
||||
|
||||
#include <QMimeData>
|
||||
#include <QtDebug>
|
||||
|
||||
QMap<QString, RadioService*> RadioModel::sServices;
|
||||
|
||||
RadioModel::RadioModel(QObject* parent)
|
||||
: SimpleTreeModel<RadioItem>(new RadioItem(NULL, RadioItem::Type_Root), parent)
|
||||
{
|
||||
Q_ASSERT(sServices.isEmpty());
|
||||
|
||||
root_->lazy_loaded = true;
|
||||
|
||||
LastFMService* lastfm = new LastFMService(this);
|
||||
services_ << lastfm;
|
||||
lastfm->CreateRootItem(root_);
|
||||
AddService(new LastFMService(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(StreamReady(QUrl,QUrl)), SIGNAL(StreamReady(QUrl,QUrl)));
|
||||
connect(service, SIGNAL(StreamFinished()), SIGNAL(StreamFinished()));
|
||||
connect(service, SIGNAL(StreamError(QString)), SIGNAL(StreamError(QString)));
|
||||
}
|
||||
|
||||
RadioService* RadioModel::ServiceByName(const QString& name) {
|
||||
if (sServices.contains(name))
|
||||
return sServices[name];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
QVariant RadioModel::data(const QModelIndex& index, int role) const {
|
||||
@ -43,3 +66,44 @@ void RadioModel::LazyPopulate(RadioItem* parent) {
|
||||
if (parent->service)
|
||||
parent->service->LazyPopulate(parent);
|
||||
}
|
||||
|
||||
Qt::ItemFlags RadioModel::flags(const QModelIndex& index) const {
|
||||
RadioItem* item = IndexToItem(index);
|
||||
if (item->playable)
|
||||
return Qt::ItemIsSelectable |
|
||||
Qt::ItemIsEnabled |
|
||||
Qt::ItemIsDragEnabled;
|
||||
|
||||
return Qt::ItemIsSelectable |
|
||||
Qt::ItemIsEnabled;
|
||||
}
|
||||
|
||||
QStringList RadioModel::mimeTypes() const {
|
||||
return QStringList() << "text/uri-list";
|
||||
}
|
||||
|
||||
QMimeData* RadioModel::mimeData(const QModelIndexList& indexes) const {
|
||||
QList<QUrl> urls;
|
||||
QList<RadioService*> services;
|
||||
|
||||
foreach (const QModelIndex& index, indexes) {
|
||||
RadioItem* item = IndexToItem(index);
|
||||
if (!item || !item->service || !item->playable)
|
||||
continue;
|
||||
|
||||
QList<QUrl> service_urls(item->service->UrlsForItem(item));
|
||||
foreach (const QUrl& url, service_urls) {
|
||||
urls << url;
|
||||
services << item->service;
|
||||
}
|
||||
}
|
||||
|
||||
if (urls.isEmpty())
|
||||
return NULL;
|
||||
|
||||
RadioMimeData* data = new RadioMimeData;
|
||||
data->setUrls(urls);
|
||||
data->services = services;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
@ -7,6 +7,8 @@
|
||||
class RadioService;
|
||||
|
||||
class RadioModel : public SimpleTreeModel<RadioItem> {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
RadioModel(QObject* parent = 0);
|
||||
|
||||
@ -16,17 +18,31 @@ class RadioModel : public SimpleTreeModel<RadioItem> {
|
||||
Role_Key,
|
||||
};
|
||||
|
||||
// Needs to be static for RadioPlaylistItem::restore
|
||||
static RadioService* ServiceByName(const QString& name);
|
||||
|
||||
// QAbstractItemModel
|
||||
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
|
||||
Qt::ItemFlags flags(const QModelIndex& index) const;
|
||||
QStringList mimeTypes() const;
|
||||
QMimeData* mimeData(const QModelIndexList& indexes) const;
|
||||
|
||||
signals:
|
||||
void LoadingStarted();
|
||||
void LoadingFinished();
|
||||
void StreamReady(const QUrl& original_url, const QUrl& media_url);
|
||||
void StreamFinished();
|
||||
void StreamError(const QString& message);
|
||||
|
||||
protected:
|
||||
void LazyPopulate(RadioItem* parent);
|
||||
|
||||
private:
|
||||
QVariant data(const RadioItem* item, int role) const;
|
||||
void AddService(RadioService* service);
|
||||
|
||||
private:
|
||||
QList<RadioService*> services_;
|
||||
static QMap<QString, RadioService*> sServices;
|
||||
};
|
||||
|
||||
#endif // RADIOMODEL_H
|
||||
|
59
src/radioplaylistitem.cpp
Normal file
59
src/radioplaylistitem.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
#include "radioplaylistitem.h"
|
||||
#include "radioservice.h"
|
||||
#include "radiomodel.h"
|
||||
|
||||
#include <QSettings>
|
||||
|
||||
RadioPlaylistItem::RadioPlaylistItem()
|
||||
: service_(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
RadioPlaylistItem::RadioPlaylistItem(RadioService* service, const QUrl& url)
|
||||
: service_(service),
|
||||
url_(url)
|
||||
{
|
||||
}
|
||||
|
||||
void RadioPlaylistItem::Save(QSettings& settings) const {
|
||||
settings.setValue("service", service_->name());
|
||||
settings.setValue("url", url_.toString());
|
||||
}
|
||||
|
||||
void RadioPlaylistItem::Restore(const QSettings& settings) {
|
||||
service_ = RadioModel::ServiceByName(settings.value("service").toString());
|
||||
url_ = settings.value("url").toString();
|
||||
}
|
||||
|
||||
QString RadioPlaylistItem::Title() const {
|
||||
if (service_)
|
||||
return url_.toString();
|
||||
return "Radio service couldn't be loaded :-(";
|
||||
}
|
||||
|
||||
QString RadioPlaylistItem::Artist() const {
|
||||
return QString::null;
|
||||
}
|
||||
|
||||
QString RadioPlaylistItem::Album() const {
|
||||
return QString::null;
|
||||
}
|
||||
|
||||
int RadioPlaylistItem::Length() const {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int RadioPlaylistItem::Track() const {
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool RadioPlaylistItem::StartLoading() {
|
||||
if (service_)
|
||||
service_->StartLoading(url_);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QUrl RadioPlaylistItem::Url() {
|
||||
return url_;
|
||||
}
|
34
src/radioplaylistitem.h
Normal file
34
src/radioplaylistitem.h
Normal file
@ -0,0 +1,34 @@
|
||||
#ifndef RADIOPLAYLISTITEM_H
|
||||
#define RADIOPLAYLISTITEM_H
|
||||
|
||||
#include "playlistitem.h"
|
||||
|
||||
#include <QUrl>
|
||||
|
||||
class RadioService;
|
||||
|
||||
class RadioPlaylistItem : public PlaylistItem {
|
||||
public:
|
||||
RadioPlaylistItem();
|
||||
RadioPlaylistItem(RadioService* service, const QUrl& url);
|
||||
|
||||
Type type() const { return Type_Radio; }
|
||||
|
||||
void Save(QSettings& settings) const;
|
||||
void Restore(const QSettings& settings);
|
||||
|
||||
QString Title() const;
|
||||
QString Artist() const;
|
||||
QString Album() const;
|
||||
int Length() const;
|
||||
int Track() const;
|
||||
|
||||
bool StartLoading();
|
||||
QUrl Url();
|
||||
|
||||
private:
|
||||
RadioService* service_;
|
||||
QUrl url_;
|
||||
};
|
||||
|
||||
#endif // RADIOPLAYLISTITEM_H
|
@ -2,6 +2,8 @@
|
||||
#define RADIOSERVICE_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QList>
|
||||
#include <QUrl>
|
||||
|
||||
class RadioItem;
|
||||
|
||||
@ -17,6 +19,16 @@ class RadioService : public QObject {
|
||||
virtual RadioItem* CreateRootItem(RadioItem* parent) = 0;
|
||||
virtual void LazyPopulate(RadioItem* item) = 0;
|
||||
|
||||
virtual QList<QUrl> UrlsForItem(RadioItem* item) = 0;
|
||||
virtual void StartLoading(const QUrl& url) = 0;
|
||||
|
||||
signals:
|
||||
void LoadingStarted();
|
||||
void LoadingFinished();
|
||||
void StreamReady(const QUrl& original_url, const QUrl& media_url);
|
||||
void StreamFinished();
|
||||
void StreamError(const QString& message);
|
||||
|
||||
private:
|
||||
QString name_;
|
||||
};
|
||||
|
@ -1 +0,0 @@
|
||||
#include "songmimedata.h"
|
17
src/src.pro
17
src/src.pro
@ -2,7 +2,9 @@
|
||||
# Project created by QtCreator 2009-12-15T18:38:35
|
||||
# -------------------------------------------------
|
||||
QT += sql \
|
||||
network opengl xml
|
||||
network \
|
||||
opengl \
|
||||
xml
|
||||
TARGET = tangerine
|
||||
TEMPLATE = app
|
||||
SOURCES += main.cpp \
|
||||
@ -24,7 +26,6 @@ SOURCES += main.cpp \
|
||||
backgroundthread.cpp \
|
||||
librarywatcher.cpp \
|
||||
song.cpp \
|
||||
songmimedata.cpp \
|
||||
songplaylistitem.cpp \
|
||||
libraryview.cpp \
|
||||
libraryconfig.cpp \
|
||||
@ -38,7 +39,9 @@ SOURCES += main.cpp \
|
||||
lastfmservice.cpp \
|
||||
radiomodel.cpp \
|
||||
lastfmconfig.cpp \
|
||||
busyindicator.cpp
|
||||
busyindicator.cpp \
|
||||
radioplaylistitem.cpp \
|
||||
radioloadingindicator.cpp
|
||||
HEADERS += mainwindow.h \
|
||||
player.h \
|
||||
library.h \
|
||||
@ -76,11 +79,15 @@ HEADERS += mainwindow.h \
|
||||
simpletreemodel.h \
|
||||
radiomodel.h \
|
||||
lastfmconfig.h \
|
||||
busyindicator.h
|
||||
busyindicator.h \
|
||||
radiomimedata.h \
|
||||
radioplaylistitem.h \
|
||||
radioloadingindicator.h
|
||||
FORMS += mainwindow.ui \
|
||||
libraryconfig.ui \
|
||||
fileview.ui \
|
||||
lastfmconfig.ui
|
||||
lastfmconfig.ui \
|
||||
radioloadingindicator.ui
|
||||
RESOURCES += ../data/data.qrc
|
||||
OTHER_FILES += ../data/schema.sql \
|
||||
../data/mainwindow.css
|
||||
|
Loading…
x
Reference in New Issue
Block a user