diff options
-rw-r--r-- | beetplayer.cpp | 76 | ||||
-rw-r--r-- | beetplayer.h | 15 | ||||
-rw-r--r-- | helper.cpp | 12 | ||||
-rw-r--r-- | helper.h | 1 | ||||
-rw-r--r-- | indexerwidget.cpp | 10 | ||||
-rw-r--r-- | indexerwidget.h | 2 | ||||
-rw-r--r-- | playerwidget.cpp | 50 | ||||
-rw-r--r-- | playerwidget.h | 11 |
8 files changed, 161 insertions, 16 deletions
diff --git a/beetplayer.cpp b/beetplayer.cpp index 0a2c962..1fd0aa7 100644 --- a/beetplayer.cpp +++ b/beetplayer.cpp @@ -5,6 +5,8 @@ #include <QHBoxLayout> #include <QAction> #include <QMenuBar> +#include <QStatusBar> +#include <QLabel> #include "beetplayer.h" #include "configurationdialog.h" @@ -18,8 +20,13 @@ BeetPlayer::BeetPlayer(QWidget *parent, Qt::WindowFlags f) : QMainWindow(parent, setMinimumHeight(768); openDatabase(); createGlobalActions(); - PlayerWidget *player = new PlayerWidget; - setCentralWidget(player); + mPlayerWidget = new PlayerWidget; + connect(mPlayerWidget, SIGNAL(viewModeChanged(QString)), this, SLOT(setViewMode(QString))); + connect(mPlayerWidget->player(), SIGNAL(stateChanged(QMediaPlayer::State)), this, SLOT(setPlayMode(QMediaPlayer::State))); + connect(mPlayerWidget, SIGNAL(numFilesChanged(int)), this, SLOT(setNumFiles(int))); + connect(mPlayerWidget, SIGNAL(playListLengthChanged(quint64)), this, SLOT(setPlayListLength(quint64))); + createStatusbar(); + setCentralWidget(mPlayerWidget); } void BeetPlayer::openDatabase(){ @@ -65,3 +72,68 @@ void BeetPlayer::configure(){ openDatabase(); } } + +void BeetPlayer::setViewMode(const QString &viewMode){ + mModeL->setText(viewMode); +} + +void BeetPlayer::setPlayMode(QMediaPlayer::State state){ + if(state == QMediaPlayer::StoppedState){ + mActionL->setText(tr("Stopped")); + }else if(state == QMediaPlayer::PlayingState){ + mActionL->setText("Playing"); + }else if(state == QMediaPlayer::PausedState){ + mActionL->setText("Paused"); + }else{ + mActionL->setText(tr("Unknown")); + } +} + +void BeetPlayer::setNumFiles(int numFiles){ + QString n = QString("%1").arg(numFiles, 4, 10, QChar('0')); + mFilesL->setText(n); +} + +void BeetPlayer::setPlayListLength(quint64 seconds){ + int h = (seconds / 60 / 60); + int min = (seconds / 60) % 60; + int secs = seconds % 60; + QString r = QString("%1:%2:%3").arg(h, 3, 10, QChar('0')).arg(min, 2, 10, QChar('0')).arg(secs, 2, 10, QChar('0')); + mPlaylistDurL->setText(r); +} + +void BeetPlayer::createStatusbar(){ + QLabel *l1 = new QLabel(tr("View:")); + mModeL = new QLabel; + mModeL->setFrameStyle(QFrame::Panel | QFrame::Sunken); + mModeL->setFont(QFont("courier")); + mModeL->setText(tr("(none)")); + statusBar()->addPermanentWidget(l1); + statusBar()->addPermanentWidget(mModeL); + mGeneralL = new QLabel; + mGeneralL->setFrameStyle(QFrame::Panel | QFrame::Sunken); + statusBar()->addPermanentWidget(mGeneralL, 20); //20 is an arbitray value, stretch to max + QLabel *l2 = new QLabel(tr("Status:")); + mActionL = new QLabel; + mActionL->setFrameStyle(QFrame::Panel | QFrame::Sunken); + mActionL->setFont(QFont("courier")); + mActionL->setText(tr("Stopped")); + statusBar()->addPermanentWidget(l2); + statusBar()->addPermanentWidget(mActionL); + QLabel *l3 = new QLabel(tr("NumFiles:")); + mFilesL = new QLabel; + mFilesL->setFrameStyle(QFrame::Panel | QFrame::Sunken); + mFilesL->setFont(QFont("courier")); + mFilesL->setText(tr("0000")); + statusBar()->addPermanentWidget(l3); + statusBar()->addPermanentWidget(mFilesL); + QLabel *l4 = new QLabel(tr("Dur:")); + mPlaylistDurL = new QLabel; + mPlaylistDurL->setFrameStyle(QFrame::Panel | QFrame::Sunken); + mPlaylistDurL->setFont(QFont("courier")); + mPlaylistDurL->setText("000:00:00"); + statusBar()->addPermanentWidget(l4); + statusBar()->addPermanentWidget(mPlaylistDurL); + +} + diff --git a/beetplayer.h b/beetplayer.h index 857b46b..9e7a8ce 100644 --- a/beetplayer.h +++ b/beetplayer.h @@ -1,8 +1,12 @@ #ifndef BEETPLAYER_H #define BEETPLAYER_H +#include <QMediaPlayer> #include <QMainWindow> +class QLabel; +class PlayerWidget; + class BeetPlayer : public QMainWindow { Q_OBJECT public: @@ -11,10 +15,21 @@ class BeetPlayer : public QMainWindow { public slots: void configure(); + void setViewMode(const QString &viewMode); + void setPlayMode(QMediaPlayer::State state); + void setNumFiles(int numFiles); + void setPlayListLength(quint64 seconds); private: void openDatabase(); void createGlobalActions(); + void createStatusbar(); + PlayerWidget *mPlayerWidget; + QLabel *mModeL; + QLabel *mActionL; + QLabel *mFilesL; + QLabel *mPlaylistDurL; + QLabel *mGeneralL; }; #endif // BEETPLAYER_H @@ -3,6 +3,9 @@ #include <QAction> #include <QApplication> +#include <taglib/fileref.h> +#include <taglib/audioproperties.h> + #include "helper.h" namespace Helper { @@ -23,4 +26,13 @@ namespace Helper { a->setSeparator(true); return a; } + + quint64 lengthInSeconds(const QString &file){ + TagLib::FileRef f(QString(file).toUtf8()); + if(f.isNull()){ + return 0; + } + TagLib::AudioProperties *props = f.audioProperties(); + return props->lengthInSeconds(); + } } @@ -9,6 +9,7 @@ class QAction; namespace Helper { QIcon iconFromQChar(const QChar &c, int pixelSize); QAction* createSeparator(QObject *parent); + quint64 lengthInSeconds(const QString &file); } #endif // HELPER_H diff --git a/indexerwidget.cpp b/indexerwidget.cpp index f30de80..49b221a 100644 --- a/indexerwidget.cpp +++ b/indexerwidget.cpp @@ -12,6 +12,8 @@ #include <QProgressBar> #include "taglib/tag.h" +#include "taglib/audioproperties.h" + #include "indexerwidget.h" #include "globals.h" @@ -91,7 +93,7 @@ BeetReader::BeetReader() : mCanceled(false){ mCurAlbumQ = new QSqlQuery(mDb); mCurAlbumQ->prepare("SELECT currval('albums_ialbums_id__seq')"); mInsertSongQ = new QSqlQuery(mDb); - mInsertSongQ->prepare("INSERT INTO songs (ialbums_id, sipos, ttitle, iartists_id, igenres_id, tfullpath) VALUES(:aid, :pos, :title, :iartistid, :igenid, :tfp)"); + mInsertSongQ->prepare("INSERT INTO songs (ialbums_id, sipos, ttitle, iartists_id, igenres_id, tfullpath, ilength) VALUES(:aid, :pos, :title, :iartistid, :igenid, :tfp, :len)"); } void BeetReader::run(){ @@ -124,6 +126,7 @@ void BeetReader::run(){ QString album = toQString(file.tag()->album()); QString title = toQString(file.tag()->title()); QString genre = toQString(file.tag()->genre()); + int length = file.audioProperties()->lengthInSeconds(); quint16 track = file.tag()->track(); quint16 year = file.tag()->year(); @@ -131,7 +134,7 @@ void BeetReader::run(){ int artistId = doArtist(artist); int genreId = doGenre(genre); int albumId = doAlbum(album, year); - doSong(title, track, albumId, genreId, artistId, s); + doSong(title, track, albumId, genreId, artistId, s, length); if(ctr % 100 == 0){ emit progress(ctr); @@ -229,7 +232,7 @@ int BeetReader::doAlbum(QString name, int year){ return retval; } -void BeetReader::doSong(QString title, int pos, int album, int genre, int artist, QString fullpath){ +void BeetReader::doSong(QString title, int pos, int album, int genre, int artist, QString fullpath, int length){ mDb.transaction(); mInsertSongQ->bindValue(":title", title); mInsertSongQ->bindValue(":pos", pos); @@ -237,6 +240,7 @@ void BeetReader::doSong(QString title, int pos, int album, int genre, int artist mInsertSongQ->bindValue(":iartistid", artist); mInsertSongQ->bindValue(":igenid", genre); mInsertSongQ->bindValue(":tfp", fullpath); + mInsertSongQ->bindValue(":len", length); mInsertSongQ->exec(); mDb.commit(); } diff --git a/indexerwidget.h b/indexerwidget.h index d8e70bb..7b907f5 100644 --- a/indexerwidget.h +++ b/indexerwidget.h @@ -52,7 +52,7 @@ class BeetReader : public QThread { int doArtist(QString name); int doGenre(QString name); int doAlbum(QString name, int year); - void doSong(QString title, int pos, int album, int genre, int artist, QString fullpath); + void doSong(QString title, int pos, int album, int genre, int artist, QString fullpath, int length); QMutex mCancelMx; bool mCanceled; QSqlDatabase mDb; diff --git a/playerwidget.cpp b/playerwidget.cpp index 40bca39..ebbbc16 100644 --- a/playerwidget.cpp +++ b/playerwidget.cpp @@ -22,6 +22,7 @@ #include <algorithm> #include <taglib/fileref.h> #include <taglib/tag.h> +#include <taglib/audioproperties.h> #include "playerwidget.h" #include "beetview.h" @@ -29,7 +30,7 @@ #include "globals.h" #include "helper.h" -PlayerWidget::PlayerWidget(QWidget *parent) : QWidget(parent){ +PlayerWidget::PlayerWidget(QWidget *parent) : QWidget(parent), mDurSecs(0), mPlayListLength(0){ setupGui(); createActions(); } @@ -280,7 +281,7 @@ void PlayerWidget::populateByArtist(QStandardItem *parent, const QString &filter QSqlQuery albumQ(db); albumQ.prepare("SELECT DISTINCT(songs.ialbums_id), talbum_name, siyear FROM songs, albums WHERE songs.iartists_id = :artistid AND songs.ialbums_id = albums.ialbums_id ORDER BY siyear ASC"); QSqlQuery songQ(db); - songQ.prepare("SELECT sipos, ttitle, tfullpath, igenres_id FROM songs WHERE ialbums_id = :alid AND iartists_id = :arid ORDER BY sipos ASC"); + songQ.prepare("SELECT sipos, ttitle, tfullpath, igenres_id, ilength FROM songs WHERE ialbums_id = :alid AND iartists_id = :arid ORDER BY sipos ASC"); QIcon songIcon(":/song.png"); QIcon albumIcon(":/album.png"); QIcon artistIcon(":/artist.png"); @@ -324,6 +325,7 @@ void PlayerWidget::populateByArtist(QStandardItem *parent, const QString &filter curSong->setData(songQ.value(3), GenreRole); curSong->setData(artistsQ.value(1), ArtistRole); curSong->setData(songQ.value(1), TitleRole); + curSong->setData(songQ.value(4), LengthRole); curSong->setData(albumQ.value(1), AlbumRole); curAlbum->appendRow(curSong); } @@ -354,7 +356,7 @@ void PlayerWidget::populateByAlbum(QStandardItem *parent, const QVariant &filter curAlbum->setData(albumQ.value(0), IdRole); parent->appendRow(curAlbum); QSqlQuery songQ = QSqlQuery(db); - songQ.prepare("SELECT sipos, ttitle, tfullpath, igenres_id, artists.tartists_name, albums.talbum_name FROM songs, artists, albums WHERE albums.ialbums_id = :id AND songs.iartists_id = artists.iartists_id and songs.ialbums_id = albums.ialbums_id ORDER BY sipos"); + songQ.prepare("SELECT sipos, ttitle, tfullpath, igenres_id, artists.tartists_name, albums.talbum_name, ilength FROM songs, artists, albums WHERE albums.ialbums_id = :id AND songs.iartists_id = artists.iartists_id and songs.ialbums_id = albums.ialbums_id ORDER BY sipos"); songQ.bindValue(":id", albumQ.value(0)); songQ.exec(); while(songQ.next()){ @@ -372,6 +374,7 @@ void PlayerWidget::populateByAlbum(QStandardItem *parent, const QVariant &filter ++artistcount[songQ.value(4).toString()]; curSong->setData(songQ.value(1), TitleRole); curSong->setData(songQ.value(5), AlbumRole); + curSong->setData(songQ.value(6), LengthRole); curAlbum->appendRow(curSong); } QString albumText; @@ -390,9 +393,9 @@ void PlayerWidget::populateBySong(QStandardItem *parent, const QVariant &filter, QIcon songIcon(":/song.png"); QSqlQuery songQ = QSqlQuery(db); if(type == EmptyType){ - songQ.prepare("SELECT sipos, ttitle, tfullpath, igenres_id, artists.tartists_name, albums.talbum_name FROM songs, artists, albums WHERE songs.iartists_id = artists.iartists_id and songs.ialbums_id = albums.ialbums_id ORDER BY ttitle ASC"); + songQ.prepare("SELECT sipos, ttitle, tfullpath, igenres_id, artists.tartists_name, albums.talbum_name, ilength FROM songs, artists, albums WHERE songs.iartists_id = artists.iartists_id and songs.ialbums_id = albums.ialbums_id ORDER BY ttitle ASC"); }else if(type == FilterType){ - songQ.prepare("SELECT sipos, ttitle, tfullpath, igenres_id, artists.tartists_name, albums.talbum_name FROM songs, artists, albums WHERE ttitle ~ :f AND songs.iartists_id = artists.iartists_id and songs.ialbums_id = albums.ialbums_id ORDER BY ttitle ASC"); + songQ.prepare("SELECT sipos, ttitle, tfullpath, igenres_id, artists.tartists_name, albums.talbum_name, ilength FROM songs, artists, albums WHERE ttitle ~ :f AND songs.iartists_id = artists.iartists_id and songs.ialbums_id = albums.ialbums_id ORDER BY ttitle ASC"); songQ.bindValue(":f", filter); }else if(type == IdType){ songQ.prepare("SELECT sipos, ttitle, tfullpath, igenres_id, artists.tartists_name, albums.talbum_name FROM songs, artists, albums WHERE albums.ialbums_id = :id AND songs.iartists_id = artists.iartists_id and songs.ialbums_id = albums.ialbums_id ORDER BY sipos"); @@ -418,6 +421,7 @@ void PlayerWidget::populateBySong(QStandardItem *parent, const QVariant &filter, curSong->setData(songQ.value(4), ArtistRole); curSong->setData(songQ.value(1), TitleRole); curSong->setData(songQ.value(5), AlbumRole); + curSong->setData(songQ.value(6), LengthRole); root->appendRow(curSong); } } @@ -445,7 +449,7 @@ void PlayerWidget::populateByGenre(QStandardItem *parent, const QString &filter) curGenre->setData(genreQ.value(0), IdRole); root->appendRow(curGenre); QSqlQuery songQ = QSqlQuery(db); - songQ.prepare("SELECT sipos, ttitle, tfullpath, igenres_id, artists.tartists_name, albums.talbum_name FROM songs, artists, albums WHERE igenres_id = :id AND songs.iartists_id = artists.iartists_id AND songs.ialbums_id = albums.ialbums_id ORDER BY ttitle ASC"); + songQ.prepare("SELECT sipos, ttitle, tfullpath, igenres_id, artists.tartists_name, albums.talbum_name, ilength FROM songs, artists, albums WHERE igenres_id = :id AND songs.iartists_id = artists.iartists_id AND songs.ialbums_id = albums.ialbums_id ORDER BY ttitle ASC"); songQ.bindValue(":id", genreQ.value(0)); songQ.exec(); while(songQ.next()){ @@ -462,6 +466,7 @@ void PlayerWidget::populateByGenre(QStandardItem *parent, const QString &filter) curSong->setData(songQ.value(4), ArtistRole); curSong->setData(songQ.value(1), TitleRole); curSong->setData(songQ.value(5), AlbumRole); + curSong->setData(songQ.value(6), LengthRole); curGenre->appendRow(curSong); } } @@ -508,6 +513,7 @@ void PlayerWidget::doPopulateByFolder(QString dir){ root->appendRow(cur); } mView->setModel(mFolderModel); + emit viewModeChanged(tr("Folder")); } void PlayerWidget::viewDoubleClicked(const QModelIndex &idx){ @@ -555,7 +561,13 @@ void PlayerWidget::addSong(const QModelIndex &idx){ item->setText(display); item->setIcon(QIcon(":/song.png")); item->setData(idx.data(FullPathRole), FullPathRole); + int len = idx.data(LengthRole).toInt(); + if(len == 0){ + len = Helper::lengthInSeconds(idx.data(FullPathRole).toString()); + } + item->setData(len, LengthRole); root->appendRow(item); + mPlayListLength += len; } void PlayerWidget::doPopulateByArtist(){ @@ -567,6 +579,7 @@ void PlayerWidget::doPopulateByArtist(){ QStandardItem *root = mViewModel->invisibleRootItem(); populateByArtist(root, QString()); qApp->restoreOverrideCursor(); + emit viewModeChanged(tr("Artist")); } void PlayerWidget::doPopulateByAlbum(){ @@ -578,6 +591,7 @@ void PlayerWidget::doPopulateByAlbum(){ QStandardItem *root = mViewModel->invisibleRootItem(); populateByAlbum(root, QString(), EmptyType); qApp->restoreOverrideCursor(); + emit viewModeChanged(tr("Album")); } void PlayerWidget::doPopulateByGenre(){ @@ -589,6 +603,7 @@ void PlayerWidget::doPopulateByGenre(){ QStandardItem *root = mViewModel->invisibleRootItem(); populateByGenre(root, QString()); qApp->restoreOverrideCursor(); + emit viewModeChanged(tr("Genre")); } void PlayerWidget::doPopulateBySong(){ @@ -600,6 +615,7 @@ void PlayerWidget::doPopulateBySong(){ QStandardItem *root = mViewModel->invisibleRootItem(); populateBySong(root, QString(), EmptyType); qApp->restoreOverrideCursor(); + emit viewModeChanged(tr("Song")); } void PlayerWidget::doFilter(){ @@ -618,6 +634,7 @@ void PlayerWidget::doFilter(){ populateByGenre(root, filter); populateBySong(root, filter, FilterType); qApp->restoreOverrideCursor(); + emit viewModeChanged(tr("Search")); } void PlayerWidget::clearFilter(){ @@ -644,6 +661,9 @@ void PlayerWidget::addToPlayList(){ recurse(i); } } + QStandardItem *playListRoot = mPlayListModel->invisibleRootItem(); + emit numFilesChanged(playListRoot->rowCount()); + emit playListLengthChanged(mPlayListLength); } void PlayerWidget::addToPlayListAndClear(){ @@ -654,17 +674,26 @@ void PlayerWidget::addToPlayListAndClear(){ void PlayerWidget::removeFromPlayList(){ QModelIndexList sel = mPlayListView->selectionModel()->selectedRows(); QList<QPersistentModelIndex> persistent; + int subSecs = 0; foreach(QModelIndex i, sel){ + subSecs = i.data(LengthRole).toInt(); persistent << QPersistentModelIndex(i); } foreach(QPersistentModelIndex i, persistent){ mPlayListModel->removeRow(i.row()); } + QStandardItem *root = mPlayListModel->invisibleRootItem(); + emit numFilesChanged(root->rowCount()); + mPlayListLength -= subSecs; + emit playListLengthChanged(mPlayListLength); } void PlayerWidget::clearPlayList(){ mPlayListModel->clear(); mPlayListModel->setHorizontalHeaderLabels(QStringList() << "Title"); + mPlayListLength = 0; + emit numFilesChanged(0); + emit playListLengthChanged(0); } void PlayerWidget::shufflePlayList(){ @@ -683,13 +712,12 @@ void PlayerWidget::shufflePlayList(){ } void PlayerWidget::randomPlay(){ - mPlayListModel->clear(); - mPlayListModel->setHorizontalHeaderLabels(QStringList() << "Title"); + clearPlayList(); QStandardItem *root = mPlayListModel->invisibleRootItem(); QIcon songIcon(":/song.png"); QSqlDatabase db = QSqlDatabase::database("beetplayerdb"); QSqlQuery randomQ(db); - randomQ.prepare("SELECT sipos, ttitle, tfullpath, igenres_id, artists.tartists_name, albums.talbum_name FROM songs, artists, albums WHERE songs.iartists_id = artists.iartists_id AND songs.ialbums_id = albums.ialbums_id ORDER BY random() LIMIT 1000"); + randomQ.prepare("SELECT sipos, ttitle, tfullpath, igenres_id, artists.tartists_name, albums.talbum_name, ilength FROM songs, artists, albums WHERE songs.iartists_id = artists.iartists_id AND songs.ialbums_id = albums.ialbums_id ORDER BY random() LIMIT 1000"); randomQ.exec(); while(randomQ.next()){ QString display = QString(tr("%1 - %2 - %3")).arg(randomQ.value(4).toString()).arg(randomQ.value(1).toString()).arg(randomQ.value(5).toString()); @@ -699,8 +727,12 @@ void PlayerWidget::randomPlay(){ item->setText(display); item->setIcon(songIcon); item->setData(randomQ.value(2), FullPathRole); + item->setData(randomQ.value(6), LengthRole); + mPlayListLength += randomQ.value(6).toInt(); root->appendRow(item); } + emit numFilesChanged(root->rowCount()); + emit playListLengthChanged(mPlayListLength); } void PlayerWidget::playCurrent(const QModelIndex &index){ diff --git a/playerwidget.h b/playerwidget.h index 383cbc3..bbe8333 100644 --- a/playerwidget.h +++ b/playerwidget.h @@ -21,8 +21,9 @@ class PlayerWidget : public QWidget { public: enum ItemType { Artist, Album, Song, Genre }; enum PopulateType { FilterType, IdType, EmptyType }; - enum CustomRoles { TypeRole = Qt::UserRole + 1, IdRole = Qt::UserRole + 2, FullPathRole = Qt::UserRole + 3, GenreRole = Qt::UserRole + 4, ArtistRole = Qt::UserRole + 5, TitleRole = Qt::UserRole + 6, AlbumRole = Qt::UserRole + 7 }; + enum CustomRoles { TypeRole = Qt::UserRole + 1, IdRole = Qt::UserRole + 2, FullPathRole = Qt::UserRole + 3, GenreRole = Qt::UserRole + 4, ArtistRole = Qt::UserRole + 5, TitleRole = Qt::UserRole + 6, AlbumRole = Qt::UserRole + 7, LengthRole = Qt::UserRole + 8 }; explicit PlayerWidget(QWidget *parent = 0); + const QMediaPlayer* player() const { return mPlayer; } public slots: void doPopulateByArtist(); @@ -51,6 +52,13 @@ class PlayerWidget : public QWidget { void continuePlaying(QMediaPlayer::State state); void expand(); + signals: + void viewModeChanged(const QString &viewMode); + void playModeChanged(const QString &playMode); + void numFilesChanged(int numFiles); + void playListLengthChanged(quint64 secs); + void message(const QString &msg); + private: void setupGui(); void createActions(); @@ -82,6 +90,7 @@ class PlayerWidget : public QWidget { QAction *mPlayA; QAction *mStopA; qint64 mDurSecs; + quint64 mPlayListLength; }; #endif // PLAYERWIDGET_H |