diff --git a/.gitignore b/.gitignore index b8bd026..10109a9 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,10 @@ *.exe *.out *.app + +#qt +*.pro.user +*.pro.user* + +#mac os x +.DS_Store diff --git a/KylinPlayer.icns b/KylinPlayer.icns new file mode 100644 index 0000000..be25345 Binary files /dev/null and b/KylinPlayer.icns differ diff --git a/KylinPlayer.png b/KylinPlayer.png new file mode 100644 index 0000000..57190e7 Binary files /dev/null and b/KylinPlayer.png differ diff --git a/KylinPlayer.pro b/KylinPlayer.pro new file mode 100644 index 0000000..4d0a6b4 --- /dev/null +++ b/KylinPlayer.pro @@ -0,0 +1,36 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2014-07-19T11:54:34 +# +#------------------------------------------------- + +QT += core gui multimedia network sql multimediawidgets +linux { + QT += x11extras + LIBS += -lXext -lX11 +} +mac { + ICON = KylinPlayer.icns +} +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = KylinPlayer +TEMPLATE = app + + +SOURCES += main.cpp\ + playermainwindow.cpp \ + network.cpp \ + database.cpp \ + osdlyricswidget.cpp + +HEADERS += playermainwindow.h \ + network.h \ + common.h \ + database.h \ + osdlyricswidget.h + +FORMS += playermainwindow.ui + +RESOURCES += \ + resource.qrc diff --git a/Qt.png b/Qt.png new file mode 100644 index 0000000..9ecfc26 Binary files /dev/null and b/Qt.png differ diff --git a/README.md b/README.md index 1d25f70..8c1f6fd 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,17 @@ KylinPlayer =========== -A Cross Platform OpenSource Mini Music Player with OSD of BaiduFM on Ubuntu Kylin Linux, Mac OS X and Windows. +A Cross Platform OpenSource Mini Music Player with OSD of BaiduFM on Ubuntu Kylin Linux, Mac OS X and Windows. + +这些个示例项目是2014年7月底在长沙某高校的一个为期三天的一个UbuntuKylin平台的Qt5开发技术的一个培训项目; +主要为了在高校师生群体中推广UbuntuKylin以及开源操作系统和开源软件, 课程借以介绍Qt5的一些简单使用来推广Qt开发. +当时上课没有及时的录制视频,后来单独复述录制了此课程的视频教程,现已把视频公开在51cto学院; +为了代码和视频配套,供网友下载参考,所以把代码也开源在此(Github). + +欢迎大家去学习和改进. 如果有兴趣去进一步完善美化此项目, 可以联系我. + +基本要求: +OS: Ubuntu/Mac OS X/Windows +SDK:Qt5.x +IDE:Qt Creator + diff --git a/common.h b/common.h new file mode 100644 index 0000000..7ce3afb --- /dev/null +++ b/common.h @@ -0,0 +1,44 @@ +#ifndef COMMON_H +#define COMMON_H +#include +#include +#include + + +typedef struct KChannel +{ + QString id; + QString title; +}KChannel; + +typedef struct KMediaInfo +{ + int32_t id; + QString songId; + QString songName; + QString artistName; + QString albumName; + QString songPicUrl; + QString lrcUrl; + QString mp3Url; + bool operator ==(const KMediaInfo &info) + { + return this->id == info.id; + } + KMediaInfo& operator =(const KMediaInfo &info) + { + this->id = info.id; + this->albumName = info.albumName; + this->artistName = info.artistName; + this->lrcUrl = info.lrcUrl; + this->mp3Url = info.mp3Url; + this->songId = info.songId; + this->songName = info.songName; + this->songPicUrl = info.songPicUrl; + return *this; + } +}KMediaInfo; + +#endif // COMMON_H + + diff --git a/database.cpp b/database.cpp new file mode 100644 index 0000000..ecfba7f --- /dev/null +++ b/database.cpp @@ -0,0 +1,102 @@ +#include "database.h" +#include + + +#define DATABASE_CREATE_SQL "CREATE DATABASE \ + `KylinPlayer` DEFAULT CHARACTER SET utf8;" + +#define TABLE_CREATE_SQL \ + "CREATE TABLE FavoriteSong "\ + "(id INTEGER PRIMARY KEY, song_id NUMERIC, song_name TEXT, album_name TEXT, "\ + "singer_name TEXT, song_url TEXT, artcover_url TEXT, lrc_url TEXT,UNIQUE(song_id));" + +#define INSERT_SONG_SQL "REPLACE INTO FavoriteSong \ + (song_id, song_name, album_name, singer_name, \ + song_url, artcover_url, lrc_url) \ + VALUES (?, ?, ?, ?, ?, ?, ?)" + +#define SELECT_SONG_SQL "SELECT song_id, song_name, album_name, \ + singer_name,song_url,artcover_url,lrc_url \ + FROM FavoriteSong;" + + +Database::Database(QObject *parent) : + QObject(parent) +{ +} + +bool Database::initDataBase() +{ + qDebug() << QSqlDatabase::drivers(); + this->database = QSqlDatabase::addDatabase("QSQLITE"); + if(!this->database.isValid()) + { + return false; + } + qDebug() << "isValid"; + QString dbPath = QDir::homePath() + "/.KylinPlayer.db3"; + dbPath = QDir::toNativeSeparators(dbPath); + this->database.setDatabaseName(dbPath);//database name +// this->database.setConnectOptions("CLIENT_SSL=1;CLIENT_IGNORE_SPACE=1");//use SSL + if(this->database.open()) + { + qDebug()<<"open "<database.lastError().driverText(); + }else + { + qDebug()<<"open failed"; + return false; + } + qDebug()<< this->database.databaseName(); + qDebug()<< this->database.tables(); + QSqlQuery query(this->database); +#if 0 + query.exec(DATABASE_CREATE_SQL); + query.exec("use KylinPlayer;"); +#endif + bool ret = query.exec(TABLE_CREATE_SQL); + + return ret; +} + +bool Database::addFavoriteSong(KMediaInfo songInfo) +{ + QSqlQuery query(this->database); + query.prepare(INSERT_SONG_SQL); + query.addBindValue(songInfo.id); + query.addBindValue(songInfo.songName); + query.addBindValue(songInfo.albumName); + query.addBindValue(songInfo.artistName); + query.addBindValue(songInfo.mp3Url); + query.addBindValue(songInfo.songPicUrl); + query.addBindValue(songInfo.lrcUrl); + bool ret = query.exec(); + qDebug() << "qurey exec:" << ret ; + query.finish(); + return ret; +} + +QList *Database::getFavoriteSong() +{ + QSqlQuery query(this->database); + QList *mediaInfoList = new QList(); + bool ret = query.exec(SELECT_SONG_SQL); + if(!ret) + { + return NULL; + } + while(query.next()) + { + KMediaInfo info; + info.id = query.value(0).toInt(); + info.songId = QString("%1").arg(info.id); + info.songName = query.value(1).toString(); + info.albumName = query.value(2).toString(); + info.artistName = query.value(3).toString(); + info.mp3Url = query.value(4).toString(); + info.songPicUrl = query.value(5).toString(); + info.lrcUrl = query.value(6).toString(); + mediaInfoList->append(info); + } + query.finish(); + return mediaInfoList; +} diff --git a/database.h b/database.h new file mode 100644 index 0000000..83f6e81 --- /dev/null +++ b/database.h @@ -0,0 +1,24 @@ +#ifndef DATABASE_H +#define DATABASE_H + +#include +#include +#include +#include "common.h" +class Database : public QObject +{ + Q_OBJECT +public: + explicit Database(QObject *parent = 0); + +signals: + +public slots: + bool initDataBase(); + bool addFavoriteSong(KMediaInfo songInfo); + QList *getFavoriteSong(); +private: + QSqlDatabase database; +}; + +#endif // DATABASE_H diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..cae5fd0 --- /dev/null +++ b/main.cpp @@ -0,0 +1,10 @@ +#include "playermainwindow.h" +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + PlayerMainWindow w; + w.show(); + return a.exec(); +} diff --git a/network.cpp b/network.cpp new file mode 100644 index 0000000..f96526e --- /dev/null +++ b/network.cpp @@ -0,0 +1,217 @@ +#include "network.h" +#include +#include +#include +#include + + +Network::Network(QObject *parent) : + QObject(parent) +{ + this->networkAccessManager = new QNetworkAccessManager(this); +} + +Network::~Network() +{ + delete networkAccessManager; +} + +void Network::getContentOfURL(QString url) +{ + QNetworkRequest request; + request.setUrl(QUrl(url)); + request.setRawHeader("User-Agent", "KylinPlayer 1.0"); + QNetworkReply *reply = this->networkAccessManager->get(request); + + connect(reply, SIGNAL(finished()), this, SLOT(handleGetFinished())); +} + +void Network::fetchChannels(QString url) +{ + QNetworkRequest request; + request.setUrl(QUrl(url)); + request.setRawHeader("User-Agent", "KylinPlayer 1.0"); + QNetworkReply *reply = this->networkAccessManager->get(request); + + connect(reply, SIGNAL(finished()), this, SLOT(handleFetchChannelsFinished())); +} + +void Network::fetchMediaInfoList(QString url) +{ + QNetworkRequest request; + request.setUrl(QUrl(url)); + request.setRawHeader("User-Agent", "KylinPlayer 1.0"); + QNetworkReply *reply = this->networkAccessManager->get(request); + + connect(reply, SIGNAL(finished()), this, SLOT(handleFetchMediaInfoListFinished())); +} + +void Network::fetchSongInfo(QString url) +{ + QNetworkRequest request; + request.setUrl(QUrl(url)); + request.setRawHeader("User-Agent", "KylinPlayer 1.0"); + QNetworkReply *reply = this->networkAccessManager->get(request); + + connect(reply, SIGNAL(finished()), this, SLOT(handleFetchSongInfoFinished())); +} + +void Network::fetchImage(QString url) +{ + QNetworkRequest request; + request.setUrl(QUrl(url)); + request.setRawHeader("User-Agent", "KylinPlayer 1.0"); + QNetworkReply *reply = this->networkAccessManager->get(request); + + connect(reply, SIGNAL(finished()), this, SLOT(handleFetchImageFinished())); +} + +void Network::fetchLyrics(QString url) +{ + QNetworkRequest request; + request.setUrl(QUrl(url)); + request.setRawHeader("User-Agent", "KylinPlayer 1.0"); + QNetworkReply *reply = this->networkAccessManager->get(request); + + connect(reply, SIGNAL(finished()), this, SLOT(handleFetchLyricsFinished())); +} + +void Network::handleGetFinished() +{ + QNetworkReply *reply = (QNetworkReply *)sender(); + if(reply->hasRawHeader("Location")){ + QString strLocation = reply->header(QNetworkRequest::LocationHeader).toString(); + qDebug() << strLocation; + this->getContentOfURL(strLocation); + return; + } + QByteArray strHttpData; + strHttpData = reply->readAll(); + emit this->contentGetted(new QByteArray(strHttpData)); +} + +void Network::handleFetchChannelsFinished() +{ + QNetworkReply *reply = (QNetworkReply *)sender(); + if(reply->hasRawHeader("Location")){ + QString strLocation = reply->header(QNetworkRequest::LocationHeader).toString(); +// qDebug() << strLocation; + this->fetchChannels(strLocation); + return; + } + QByteArray strHttpData; + strHttpData = reply->readAll(); + QJsonDocument jsonDoc = QJsonDocument::fromJson(strHttpData); + if(jsonDoc.isNull() ||!jsonDoc.isArray()) + {// null or not array format + return; + } + QJsonArray channels = jsonDoc.array(); + QList *channelList = new QList(); + for(int i = 0; i < channels.count(); i++) + { + QJsonValue value = channels[i]; + QString id = value.toObject().value("id").toString(); + QString title = value.toObject().value("title").toString(); +// qDebug()<append(channel); + } + emit this->musicChannelsFetched(channelList); +} + +void Network::handleFetchMediaInfoListFinished() +{ + QNetworkReply *reply = (QNetworkReply *)sender(); + if(reply->hasRawHeader("Location")){ + QString strLocation = reply->header(QNetworkRequest::LocationHeader).toString(); + qDebug() << strLocation; + this->fetchMediaInfoList(strLocation); + return; + } + QByteArray strHttpData; + strHttpData = reply->readAll(); + QJsonDocument jsonDoc = QJsonDocument::fromJson(strHttpData); + if(jsonDoc.isNull() ||!jsonDoc.isObject()) + {// null or not object format + return; + } + QJsonObject jsonObj = jsonDoc.object(); + QJsonArray mediaArray = jsonObj.value("list").toArray(); + QList *mediaList = new QList(); + for(int i = 0; i < mediaArray.count(); i++) + { + KMediaInfo mediaInfo; + QJsonObject jsonObj2 = mediaArray[i].toObject(); + mediaInfo.id = jsonObj2.value("id").toInt(); + mediaList->append(mediaInfo); +// qDebug()<<"id:"<mediaInfoListFetched(mediaList); +} + +void Network::handleFetchSongInfoFinished() +{ + QNetworkReply *reply = (QNetworkReply *)sender(); + if(reply->hasRawHeader("Location")){ + QString strLocation = reply->header(QNetworkRequest::LocationHeader).toString(); + qDebug() << strLocation; + this->fetchMediaInfoList(strLocation); + return; + } + QByteArray strHttpData; + strHttpData = reply->readAll(); + QJsonDocument jsonDoc = QJsonDocument::fromJson(strHttpData); + if(jsonDoc.isNull() ||!jsonDoc.isObject()) + {// null or not object format + return; + } + QJsonObject jsonObj = jsonDoc.object(); + QJsonArray songArray = jsonObj.value("data").toObject().value("songList").toArray(); + if(songArray.count()<=0) + { + return; + } + QJsonObject infoObj = songArray.first().toObject(); + KMediaInfo mediaInfo; + mediaInfo.id = infoObj.value("songId").toInt(); + mediaInfo.songName = infoObj.value("songName").toString(); + mediaInfo.artistName = infoObj.value("artistName").toString(); + mediaInfo.albumName = infoObj.value("albumName").toString(); + mediaInfo.songPicUrl = infoObj.value("songPicRadio").toString(); + mediaInfo.mp3Url = infoObj.value("songLink").toString(); + mediaInfo.lrcUrl = infoObj.value("lrcLink").toString(); + + + emit this->mediaInfoFetched(mediaInfo); +} + +void Network::handleFetchImageFinished() +{ + QNetworkReply *reply = (QNetworkReply *)sender(); + if(reply->hasRawHeader("Location")){ + QString strLocation = reply->header(QNetworkRequest::LocationHeader).toString(); + qDebug() << strLocation; + this->fetchMediaInfoList(strLocation); + return; + } + QByteArray strHttpData; + strHttpData = reply->readAll(); + emit this->imageDataFetched(new QByteArray(strHttpData)); +} + +void Network::handleFetchLyricsFinished() +{ + QNetworkReply *reply = (QNetworkReply *)sender(); + if(reply->hasRawHeader("Location")){ + QString strLocation = reply->header(QNetworkRequest::LocationHeader).toString(); + qDebug() << strLocation; + this->fetchMediaInfoList(strLocation); + return; + } + QByteArray strHttpData; + strHttpData = reply->readAll(); + emit this->lyricsDataFetched(new QByteArray(strHttpData)); +} diff --git a/network.h b/network.h new file mode 100644 index 0000000..b43e9c6 --- /dev/null +++ b/network.h @@ -0,0 +1,39 @@ +#ifndef NETWORK_H +#define NETWORK_H + +#include +#include +#include "common.h" + +class Network : public QObject +{ + Q_OBJECT +public: + explicit Network(QObject *parent = 0); + ~Network(); +signals: + void contentGetted(QByteArray *content); + void musicChannelsFetched(QList *channelList); + void mediaInfoListFetched(QList *mediaInfoList); + void mediaInfoFetched(KMediaInfo); + void imageDataFetched(QByteArray *imageData); + void lyricsDataFetched(QByteArray *imageData); +public slots: + void getContentOfURL(QString url); + void fetchChannels(QString url); + void fetchMediaInfoList(QString url); + void fetchSongInfo(QString url); + void fetchImage(QString url); + void fetchLyrics(QString url); + void handleGetFinished(); + void handleFetchChannelsFinished(); + void handleFetchMediaInfoListFinished(); + void handleFetchSongInfoFinished(); + void handleFetchImageFinished(); + void handleFetchLyricsFinished(); +private: + QNetworkAccessManager *networkAccessManager; + +}; + +#endif // NETWORK_H diff --git a/next.png b/next.png new file mode 100644 index 0000000..95a14ea Binary files /dev/null and b/next.png differ diff --git a/osdlyricswidget.cpp b/osdlyricswidget.cpp new file mode 100644 index 0000000..b667300 --- /dev/null +++ b/osdlyricswidget.cpp @@ -0,0 +1,197 @@ +#include "osdlyricswidget.h" +#include +#include +#include +#include +#include +#ifdef Q_OS_LINUX +#include +#include +#endif +#ifdef Q_OS_WIN32 +#include +#endif +OSDLyricsWidget::OSDLyricsWidget(QWidget *parent) : + QWidget(parent) +{ + this->lrcLineList = new QList(); + this->timer = new QTimer(this); + this->timer->setInterval(100); + this->timer->start(); + connect(this->timer, SIGNAL(timeout()), this, SLOT(handleTimeout())); + +// this->currentLyrics = QString("戴维营教育嵌入式Linux Qt5开发培训学习作品"); + this->setFixedSize(1000,100); + this->setWindowFlags(Qt::FramelessWindowHint|Qt::WindowStaysOnTopHint); + this->setAttribute(Qt::WA_TranslucentBackground,true); + this->setAttribute(Qt::WA_TransparentForMouseEvents,true); + this->setAttribute(Qt::WA_MouseTracking,false); + this->setWindowOpacity(1); + QSize size = QApplication::screens().first()->geometry().size(); + this->move( (size.width() - this->width())/2, (size.height() - this->height()) - 100 ); +#ifdef Q_OS_LINUX + XShapeCombineRectangles(QX11Info::display(), winId(), ShapeInput, 0, + 0, NULL, 0, ShapeSet, YXBanded); +#endif +#ifdef Q_OS_WIN + SetWindowLong((HWND)winId(), GWL_EXSTYLE, GetWindowLong((HWND)winId(), GWL_EXSTYLE) | + WS_EX_TRANSPARENT | WS_EX_LAYERED); + SetWindowPos((HWND)winId(),HWND_TOP,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE); +#endif +} + +OSDLyricsWidget::~OSDLyricsWidget() +{ + + delete this->lrcLineList; + delete this->timer; + +} + +void OSDLyricsWidget::mousePressEvent(QMouseEvent *event) +{ + this->windowPos = this->pos(); + QPoint mousePos = event->globalPos(); + this->dPos = mousePos - windowPos; +} + +void OSDLyricsWidget::mouseMoveEvent(QMouseEvent *event) +{ + this->move(event->globalPos() - this->dPos); +} + +void OSDLyricsWidget::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event) + if(this->currentLyrics.isEmpty()) + { + return; + } + QPainter painter(this); + painter.setCompositionMode(QPainter::CompositionMode_Source); + painter.setRenderHints(QPainter::TextAntialiasing|QPainter::Antialiasing); + QFont font = QFont("宋体", 36); + painter.setFont(font); + int textPixalLength = 0; +#if 1 + QFontMetrics fontMetrics = QFontMetrics(font); + textPixalLength = fontMetrics.boundingRect(this->currentLyrics).width(); +#else + textPixalLength = this->rect().width(); +#endif + QRect rect_l = this->rect(); + painter.fillRect(rect_l, Qt::transparent); + + + painter.setPen(QColor("lightgreen")); + painter.drawText(rect_l,Qt::AlignLeft|Qt::AlignVCenter,this->currentLyrics); + + float percent = 1 - (1.0*this->curLyricTimeLeft)/this->curLyricTime; + QRect rect_h(rect_l.x(), rect_l.y(),textPixalLength*percent,rect_l.height()); + painter.setPen(QColor("transparent")); + painter.drawRect(rect_h); + painter.setPen(QColor("red")); + painter.drawText(rect_h,Qt::AlignLeft|Qt::AlignVCenter,this->currentLyrics); +// qDebug()<<"lrc:"<currentLyrics << "percent:"<lrcLineList->clear(); + this->currentLyrics = ""; + if(NULL==lrcData) + { + return; + } + QStringList lrcList = QString(*lrcData).split("\n"); + foreach (QString lrc, lrcList) { + if(lrc.size()<=5) continue; + lrc = lrc.toLower().simplified(); + if(lrc.startsWith("[ti:") || lrc.startsWith("[ar:") || lrc.startsWith("[al:")) + { + continue; + } + //process lyric line + this->processLyricLine(lrc); + } + qSort(*this->lrcLineList); +} + + void OSDLyricsWidget::processLyricLine(QString line) +{ + if(!line.startsWith("[")) return; + int timeStampEndPos = line.lastIndexOf("]"); + if(timeStampEndPos < 9 ) return; + QString lrcLineStr = line.mid(timeStampEndPos+1); + QString timeStampStr = line.left(timeStampEndPos+1); + + QStringList timeStrList = timeStampStr.split("]"); + foreach (QString timeStr, timeStrList) { + if(!timeStr.startsWith("[")) continue; + timeStr.remove(0,1); + int min = 0, sec = 0, msec = 0; + ::sscanf(timeStr.toStdString().data(),"%d:%d.%d", &min, &sec, &msec); + long milliseconds = min*60*1000 + sec*1000 + msec*10; + LrcLine lrc_line; + lrc_line.milliseconds = milliseconds; + lrc_line.lineStr = lrcLineStr; + this->lrcLineList->append(lrc_line); + + } + + +} + +void OSDLyricsWidget::updateLyrics(long timePos) +{ + + for(int i = 1; i < this->lrcLineList->count(); i++) + { + LrcLine line = this->lrcLineList->at(i); + if(line.milliseconds > timePos) + { + if(this->curIndex == i) + { + break; + } + this->curIndex = i; + LrcLine prevLine = this->lrcLineList->at(i-1); + this->currentLyrics = prevLine.lineStr; + this->curLyricTimeLeft = this->curLyricTime = line.milliseconds - prevLine.milliseconds; + break; + } + } + this->update(); + this->updateGeometry(); +} + +void OSDLyricsWidget::handleTimeout() +{ + + if(this->curLyricTimeLeft > (unsigned long)this->timer->interval()) + { + this->curLyricTimeLeft -= this->timer->interval(); + }else + { + this->curLyricTimeLeft = 0; + } +// qDebug()<<"left:"<curLyricTimeLeft; + +} + +void OSDLyricsWidget::pauseTimer(bool stop) +{ + if(stop){ + this->timer->stop(); + }else + { + this->timer->start(); + } +} + + +bool LRCLine::operator <(const LRCLine &line) const +{ + return this->milliseconds < line.milliseconds; +} diff --git a/osdlyricswidget.h b/osdlyricswidget.h new file mode 100644 index 0000000..d25f702 --- /dev/null +++ b/osdlyricswidget.h @@ -0,0 +1,41 @@ +#ifndef OSDLYRICSWIDGET_H +#define OSDLYRICSWIDGET_H + +#include +#include +#include +typedef struct LRCLine{ + long milliseconds; + QString lineStr; + bool operator <(const LRCLine &line) const; +}LrcLine; + + +class OSDLyricsWidget : public QWidget +{ + Q_OBJECT +public: + explicit OSDLyricsWidget(QWidget *parent = 0); + ~OSDLyricsWidget(); + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void paintEvent(QPaintEvent *event); + +signals: + +public slots: + void handleNewLyricsData(QByteArray *lrcData); + void processLyricLine(QString line); + void updateLyrics(long timePos); + void handleTimeout(); + void pauseTimer(bool stop); +private: + QPoint windowPos, dPos; + QString currentLyrics; + int curIndex; + QList *lrcLineList; + unsigned long curLyricTime,curLyricTimeLeft; + QTimer *timer; +}; + +#endif // OSDLYRICSWIDGET_H diff --git a/pause.png b/pause.png new file mode 100644 index 0000000..d3959dc Binary files /dev/null and b/pause.png differ diff --git a/play.png b/play.png new file mode 100644 index 0000000..5c39ba8 Binary files /dev/null and b/play.png differ diff --git a/playermainwindow.cpp b/playermainwindow.cpp new file mode 100644 index 0000000..b03facd --- /dev/null +++ b/playermainwindow.cpp @@ -0,0 +1,413 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "playermainwindow.h" +#include "ui_playermainwindow.h" +#include "network.h" + +#define KMusicChannelsURL "http://raw.github.com/iosnews/test/master/baidu.json" +#define kMusicPlaylistURL "http://fm.baidu.com/dev/api/?tn=playlist&special=flash&prepend=&format=json&&id=" +#define kMusicURL "http://music.baidu.com/data/music/fmlink?type=mp3&rate=2&format=json&songIds=" +#define kMusicLRCURL "http://fm.baidu.com/" +PlayerMainWindow::PlayerMainWindow(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::PlayerMainWindow) +{ + ui->setupUi(this); + this->osdLyricsWidget = new OSDLyricsWidget(); + this->osdLyricsWidget->show(); + this->localMediaPlayList = new QMediaPlaylist(this); + this->onlineMediaPlayList = new QMediaPlaylist(this); + this->onlineMediaList = new QList(); + this->mediaPlayer = new QMediaPlayer(this); + this->mediaPlayer->setPlaylist(this->localMediaPlayList); + this->mediaPlayer->setVolume(50); + this->progressTimer = new QTimer(this); + this->progressTimer->setInterval(100); + this->network = new Network(this); + network->fetchChannels(KMusicChannelsURL); + + this->setupConnections(); + + this->dbOK = db.initDataBase(); + + +} + +PlayerMainWindow::~PlayerMainWindow() +{ + delete ui; + delete localMediaPlayList; + delete mediaPlayer; + delete progressTimer; + delete network; + delete onlineMediaList; + delete onlineMediaPlayList; + delete osdLyricsWidget; + +} + +void PlayerMainWindow::setupConnections() +{ + connect(ui->localAction, SIGNAL(triggered()), this, SLOT(openLocalMedia())); + connect(ui->netAction, SIGNAL(triggered()), this, SLOT(openInternetURL())); + connect(ui->playButton, SIGNAL(clicked()), this, SLOT(playButtonClicked())); + connect(ui->prevButton, SIGNAL(clicked()), this, SLOT(prevButtonClicked())); + connect(ui->nextButton, SIGNAL(clicked()), this, SLOT(nextButtonClicked())); + connect(ui->playTimeSlider, SIGNAL(sliderMoved(int)), this, SLOT(playSliderValueChanged(int))); + connect(ui->volumeDial, SIGNAL(valueChanged(int)), this, SLOT(volumeDialValueChanged(int))); + connect(ui->channelComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(channelIndexChanged(int))); + + connect(this->mediaPlayer, SIGNAL(stateChanged(QMediaPlayer::State)), this, SLOT(playerStateChanged(QMediaPlayer::State))); + connect(this->mediaPlayer, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)), this, SLOT(mediaStatusChanged(QMediaPlayer::MediaStatus))); + + connect(this->progressTimer, SIGNAL(timeout()), this, SLOT(playProgressUpdate())); + connect(this->mediaPlayer, SIGNAL(metaDataChanged()), this, SLOT(metaDataUpdate())); + + connect(this->ui->aboutAction, SIGNAL(triggered()), this,SLOT(showAbout())); + connect(this,SIGNAL(destroyed()), this->osdLyricsWidget, SLOT(close())); + + //networks signals + connect(this->network, SIGNAL(musicChannelsFetched(QList*)), this, SLOT(channelsDownloaded(QList*))); + connect(this->network, SIGNAL(mediaInfoListFetched(QList*)), this, SLOT(mediaInfoListDownloaded(QList*))); + connect(this->network, SIGNAL(mediaInfoFetched(KMediaInfo)), this, SLOT(mediaInfoDownloaded(KMediaInfo))); + connect(this->network, SIGNAL(imageDataFetched(QByteArray*)), this,SLOT(AlbumImageDownloaded(QByteArray*))); + connect(this->network, SIGNAL(lyricsDataFetched(QByteArray*)), this->osdLyricsWidget, SLOT(handleNewLyricsData(QByteArray*))); + + //favorites + connect(this->ui->addFavAction, SIGNAL(triggered()), this, SLOT(addCurrentSongToFavorites())); + connect(this->ui->playFavAction, SIGNAL(triggered()), this, SLOT(playSongInFavorites())); +} + +void PlayerMainWindow::closeEvent(QCloseEvent *event) +{ + this->osdLyricsWidget->close(); + event->accept(); +} + +void PlayerMainWindow::openLocalMedia() +{ + QStringList fileNameList; + fileNameList = QFileDialog::getOpenFileNames(this, + tr("播放本地歌曲"), "", tr("MP3/MPEG4音频 (*.mp3 *.m4a);;")); + + if (!fileNameList.isEmpty()) + { + this->localMediaPlayList->clear(); + foreach (const QString &fileName, fileNameList) { + QMediaContent media = QMediaContent(QUrl::fromLocalFile(fileName)); + this->localMediaPlayList->addMedia(media); + } + + this->localMediaPlayList->setCurrentIndex(0); + } + else + { + //取消 + } + qDebug() << fileNameList; + return ; +} + +void PlayerMainWindow::openInternetURL() +{ + QString urlString; + bool ok; + urlString = QInputDialog::getText(this, + tr("播放网络歌曲"),tr("请输入歌曲网址(尚未实现)"),QLineEdit::Normal,"http://", &ok); + if (ok && !urlString.isEmpty()) + { + //inputed + } + qDebug() << urlString; + + return; +} + +void PlayerMainWindow::playButtonClicked() +{ + if(this->mediaPlayer->state() == QMediaPlayer::PlayingState) + { + this->mediaPlayer->pause(); + qDebug() << "pause"; + }else + { + this->mediaPlayer->setVolume(this->ui->volumeDial->value()); + this->mediaPlayer->play(); + qDebug() << "play"; + } +} + +void PlayerMainWindow::prevButtonClicked() +{ + qDebug() << "prev"; + QMediaPlaylist *currentPlayList = this->onlineMode?this->onlineMediaPlayList:this->onlineMediaPlayList; + currentPlayList->previous(); + this->osdLyricsWidget->handleNewLyricsData(NULL); +} + +void PlayerMainWindow::nextButtonClicked() +{ + qDebug() << "next"; + QMediaPlaylist *currentPlayList = this->onlineMode?this->onlineMediaPlayList:this->onlineMediaPlayList; + currentPlayList->next(); + this->osdLyricsWidget->handleNewLyricsData(NULL); +} + +void PlayerMainWindow::playSliderValueChanged(int value) +{ + qDebug() << "slider changed " << value; + float percent = (value * 1.0) / this->ui->playTimeSlider->maximum(); + int64_t pos = this->mediaPlayer->duration() * percent; + this->mediaPlayer->setPosition(pos); +} + +void PlayerMainWindow::volumeDialValueChanged(int value) +{ + qDebug() << "Dial changed " << value; + this->mediaPlayer->setVolume(value); +} + +void PlayerMainWindow::channelIndexChanged(int index) +{ + qDebug() << "combox changed " << index; + if(index < 0) + { + return; + }else if(index == 0) + { + //local media + this->onlineMode = false; + this->mediaPlayer->setPlaylist(this->localMediaPlayList); + return; + }else if(index == 1) + { + //update internet media channel + network->fetchChannels(KMusicChannelsURL); + this->onlineMode = true; + this->onlineMediaPlayList->clear(); + this->mediaPlayer->setPlaylist(this->onlineMediaPlayList); + return; + } + this->onlineMode = true; + this->onlineMediaPlayList->clear(); + this->mediaPlayer->setPlaylist(this->onlineMediaPlayList); + + QComboBox *channelComboBox = (QComboBox*)sender(); + QString channelID = channelComboBox->itemData(index).toString(); + this->network->fetchMediaInfoList(kMusicPlaylistURL + channelID); +} + +void PlayerMainWindow::playerStateChanged(QMediaPlayer::State state) +{ + if(QMediaPlayer::PlayingState == state) + { +// this->ui->playButton->setText(tr("暂停")); + this->ui->playButton->setIcon(QIcon(":/images/pause.png")); + this->progressTimer->start(); + this->osdLyricsWidget->pauseTimer(false); + }else + { +// this->ui->playButton->setText(tr("播放")); + this->ui->playButton->setIcon(QIcon(":/images/play.png")); + this->progressTimer->stop(); + this->osdLyricsWidget->pauseTimer(true); + } + qDebug() << "state changed " << state; +} + +void PlayerMainWindow::mediaStatusChanged(QMediaPlayer::MediaStatus status) +{ + switch(status) + { + case QMediaPlayer::NoMedia: + case QMediaPlayer::EndOfMedia: + this->mediaPlayer->playlist()->next(); + break; + default: + ; + } + + qDebug() << "status changed " << status; +} + +void PlayerMainWindow::playProgressUpdate() +{ + int64_t pos = this->mediaPlayer->position(); + int64_t duration = this->mediaPlayer->duration(); + int value = 100 * (1.0*pos)/duration; + this->ui->playTimeSlider->setValue(value); + this->osdLyricsWidget->updateLyrics(pos); +} + +void PlayerMainWindow::metaDataUpdate() +{ + QString title, subTitle, albumTitle, albumArtist; + QImage coverArtImage; + QPixmap pixmap; + if(!this->onlineMode) + { + title = this->mediaPlayer->metaData("Title").toString(); + subTitle = this->mediaPlayer->metaData("SubTitle").toString(); + albumTitle = this->mediaPlayer->metaData("AlbumTitle").toString(); + albumArtist = this->mediaPlayer->metaData("AlbumArtist").toString(); + coverArtImage = this->mediaPlayer->metaData("CoverArtImage").value(); + if(coverArtImage.isNull()) + { + pixmap = QPixmap(":/images/Qt.png"); + }else + { + pixmap.convertFromImage(coverArtImage); + } + }else + { + int curIndex = this->onlineMediaPlayList->currentIndex(); + if(curIndex < 0 || curIndex > this->onlineMediaList->count() - 1) + { + return; + } + KMediaInfo curInfo = this->onlineMediaList->at(curIndex); + title = curInfo.songName; + albumTitle = curInfo.albumName; + albumArtist = curInfo.artistName; + this->network->fetchImage(curInfo.songPicUrl); + this->network->fetchLyrics(kMusicLRCURL + curInfo.lrcUrl); + + } + + this->ui->singerLabel->setText("歌手: "+albumArtist); + this->ui->albumLabel->setText("专辑: "+albumTitle); + this->ui->songLabel->setText("歌名: "+title); + if(!pixmap.isNull()) + { + this->ui->artWorkLabel->setPixmap(pixmap.scaled(this->ui->artWorkLabel->size())); + } +} + +void PlayerMainWindow::playOnlineMedia(int index) +{ + this->mediaPlayer->playlist()->setCurrentIndex(index); + this->mediaPlayer->play(); + qDebug()<mediaPlayer->errorString(); +} + +void PlayerMainWindow::addCurrentSongToFavorites() +{ + if(this->onlineMode) + { + int index=this->mediaPlayer->playlist()->currentIndex(); + if(index<0||index>=this->onlineMediaList->count()) + { + return; + } + this->db.addFavoriteSong(this->onlineMediaList->at(index)); + } +} + +void PlayerMainWindow::playSongInFavorites() +{ + QList * mediaInfoList = this->db.getFavoriteSong(); + if(mediaInfoList && mediaInfoList->count()>0) + { + this->onlineMediaPlayList->clear(); + this->onlineMediaList->clear(); + this->mediaPlayer->setPlaylist(this->onlineMediaPlayList); + this->onlineMode = true; + } + for(int i = 0; mediaInfoList&&icount();i++) + { + KMediaInfo mediaInfo = mediaInfoList->at(i); + QUrl url = QUrl(mediaInfo.mp3Url); + QMediaContent content(url); + this->onlineMediaPlayList->addMedia(content); + this->onlineMediaList->append(mediaInfo); + if(this->onlineMediaPlayList->mediaCount() == 1) + { + this->playOnlineMedia(0); + } + } +} + +void PlayerMainWindow::showAbout() +{ + QMessageBox::about(this, tr("关于 ") + qApp->applicationName(), + tr("

UbuntuKylin简易网络播放器


" + " KylinPlayer 是优麒麟为" + "湖南科技职业学院
2014年国培班学员学习Qt5开发的简易音乐播放器.
" + "支持: Ubuntu Kylin/Mac OS X/Windows.

" + "作者: \t大茶园丁(戴维营教育)
" + "邮箱: \t147957232@qq.com.
" + "网站: \thttp://www.ubuntukylin.com.
" + "反馈: \t http://www.ubuntukylin.com/ukylin/

" + "")); +} + +void PlayerMainWindow::channelsDownloaded(QList *channelList) +{ + if (channelList->count() <= 0) + { + return ; + } + this->ui->channelComboBox->clear(); + this->ui->channelComboBox->addItems(QStringList()<<"本地歌曲"<<"网络歌曲"); + for(int i = 0; i < channelList->count(); i++) + { + QString id = channelList->value(i).id; + QString title = channelList->value(i).title; + this->ui->channelComboBox->addItem(title, QVariant(id)); + } + +} + +void PlayerMainWindow::mediaInfoListDownloaded(QList *mediaInfoList) +{ + if(mediaInfoList->count() <= 0) + { + return; + } + int count = mediaInfoList->count(); + this->onlineMediaList->clear(); + this->onlineMediaList->reserve(count); + for(int i = 0; i < mediaInfoList->count(); i++) + { + KMediaInfo mediaInfo = mediaInfoList->at(i); + this->network->fetchSongInfo(kMusicURL+ QString("%1").arg(mediaInfo.id)); + } + qDebug() << mediaInfoList; +} + +void PlayerMainWindow::mediaInfoDownloaded(KMediaInfo mediaInfo) +{ + QUrl url = QUrl(mediaInfo.mp3Url); + QMediaContent content(url); + this->onlineMediaPlayList->addMedia(content); + this->onlineMediaList->append(mediaInfo); + if(this->onlineMediaPlayList->mediaCount() == 1) + { + this->playOnlineMedia(0); + } + + return; +} + +void PlayerMainWindow::AlbumImageDownloaded(QByteArray *imageData) +{ + QImage img; + QPixmap pixmap; + if(imageData->isEmpty()) + { + pixmap = QPixmap(":/images/Qt.png"); + }else + { + img.loadFromData(*imageData); + pixmap.convertFromImage(img); + } + this->ui->artWorkLabel->setPixmap(pixmap.scaled(this->ui->artWorkLabel->size())); +} diff --git a/playermainwindow.h b/playermainwindow.h new file mode 100644 index 0000000..a70c5ac --- /dev/null +++ b/playermainwindow.h @@ -0,0 +1,71 @@ +#ifndef PLAYERMAINWINDOW_H +#define PLAYERMAINWINDOW_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "common.h" +#include "database.h" +#include "osdlyricswidget.h" + +namespace Ui { +class PlayerMainWindow; +} +class Network; +class PlayerMainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit PlayerMainWindow(QWidget *parent = 0); + ~PlayerMainWindow(); + void setupConnections(); + void closeEvent(QCloseEvent *event); +public slots: + void openLocalMedia(); + void openInternetURL(); + void playButtonClicked(); + void prevButtonClicked(); + void nextButtonClicked(); + void playSliderValueChanged(int value); + void volumeDialValueChanged(int value); + void channelIndexChanged(int index); + + void playerStateChanged(QMediaPlayer::State state); + void mediaStatusChanged(QMediaPlayer::MediaStatus status); + + void playProgressUpdate(); + void metaDataUpdate(); + + + void playOnlineMedia(int index); + void addCurrentSongToFavorites(); + void playSongInFavorites(); + void showAbout(); + + //networks + void channelsDownloaded(QList *channelList); + void mediaInfoListDownloaded(QList *mediaInfoList); + void mediaInfoDownloaded(KMediaInfo mediaInfo); + void AlbumImageDownloaded(QByteArray *imageData); +private: + Ui::PlayerMainWindow *ui; + QMediaPlayer *mediaPlayer; + QMediaPlaylist *localMediaPlayList, *onlineMediaPlayList; + QList *onlineMediaList; + QTimer *progressTimer; + Network *network; + bool onlineMode; + Database db; + bool dbOK; + + OSDLyricsWidget *osdLyricsWidget; +}; + +#endif // PLAYERMAINWINDOW_H diff --git a/playermainwindow.ui b/playermainwindow.ui new file mode 100644 index 0000000..a6b2043 --- /dev/null +++ b/playermainwindow.ui @@ -0,0 +1,340 @@ + + + PlayerMainWindow + + + + 0 + 0 + 320 + 480 + + + + + 320 + 480 + + + + + 320 + 480 + + + + KylinPlayer + + + + + + 20 + 320 + 211 + 21 + + + + 歌手: + + + + + + 20 + 290 + 211 + 21 + + + + 专辑: + + + + + + 20 + 260 + 211 + 21 + + + + 歌名: + + + + + + 200 + 314 + 101 + 27 + + + + + 本地歌曲 + + + + + 网络歌曲 + + + + + + + 20 + 360 + 280 + 20 + + + + Qt::NoFocus + + + 0 + + + 0 + + + true + + + Qt::Horizontal + + + false + + + + + + 20 + 390 + 64 + 64 + + + + background-color: rgba(255, 255, 255, 0); +selection-background-color: rgba(255, 255, 255, 0); + + + + + + + :/images/prev.png:/images/prev.png + + + + 48 + 48 + + + + true + + + + + + 130 + 390 + 64 + 64 + + + + Qt::LeftToRight + + + false + + + background-color: rgba(255, 255, 255, 0); +selection-background-color: rgba(255, 255, 255, 0); + + + + + + + :/images/play.png:/images/play.png + + + + 64 + 64 + + + + false + + + true + + + + + + 230 + 390 + 64 + 64 + + + + background-color: rgba(255, 255, 255, 0); +selection-background-color: rgba(255, 255, 255, 0); + + + + + + + + :/images/next.png:/images/next.png + + + + 48 + 48 + + + + true + + + + + + 230 + 250 + 71 + 61 + + + + Qt::WheelFocus + + + Qt::LeftToRight + + + false + + + 40 + + + Qt::Horizontal + + + false + + + false + + + true + + + 3.700000000000000 + + + true + + + + + + 40 + 10 + 240 + 240 + + + + 封面图 + + + Qt::AlignCenter + + + + + + + 0 + 0 + 320 + 17 + + + + + 菜单 + + + + + + + false + + + 关于 + + + + + + 收藏 + + + + + + + + + + + 本地歌曲 + + + + + 网络歌曲 + + + + + 加入收藏 + + + + + 播放收藏 + + + + + 关于KylinPlayer + + + + + + + + + diff --git a/prev.png b/prev.png new file mode 100644 index 0000000..2d8f047 Binary files /dev/null and b/prev.png differ diff --git a/resource.qrc b/resource.qrc new file mode 100644 index 0000000..ba8e903 --- /dev/null +++ b/resource.qrc @@ -0,0 +1,10 @@ + + + Qt.png + play.png + pause.png + next.png + prev.png + ubuntukylin.png + + diff --git a/ubuntukylin.png b/ubuntukylin.png new file mode 100644 index 0000000..57190e7 Binary files /dev/null and b/ubuntukylin.png differ