#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "playerwidget.h" #include "beetview.h" #include "indexerdialog.h" #include "globals.h" #include "helper.h" #include "webdownloader.h" #include "webradiodialog.h" PlayerWidget::PlayerWidget(QWidget *parent) : QWidget(parent), mDurSecs(0), mPlayListLength(0){ mStarting = true; setupGui(); createActions(); mWebDownloader = new WebDownloader(this); connect(mWebDownloader, &WebDownloader::done, this, &PlayerWidget::webDlDone); mStarting = false; } PlayerWidget::~PlayerWidget(){ writeSettings(); } void PlayerWidget::setupGui(){ //the Player mPlayer = new QMediaPlayer(this); connect(mPlayer, &QMediaPlayer::positionChanged, this, &PlayerWidget::setPosition); connect(mPlayer, &QMediaPlayer::mediaStatusChanged, this, &PlayerWidget::continuePlaying); //tray icon mTrayIcon = new QSystemTrayIcon(this); mTrayIcon->setIcon(QIcon(":/stop.png")); mTrayIcon->show(); mVolumeTimer = new QTimer(this); mVolumeTimer->setSingleShot(true); connect(mVolumeTimer, &QTimer::timeout, this, &PlayerWidget::showVolume); //THE view mView = new BeetView; mView->setAlternatingRowColors(true); mViewModel = new QStandardItemModel; mView->setModel(mViewModel); mSearchModel = new QStandardItemModel; mView->setSelectionMode(QAbstractItemView::ExtendedSelection); connect(mView, &BeetView::doubleClicked, this, &PlayerWidget::viewDoubleClicked); mFolderModel = new QStandardItemModel; mWebRadioModel = new QStandardItemModel; mCurrentModel = mViewModel; QToolBar *viewTB = new QToolBar; QActionGroup *viewAG = new QActionGroup(this); viewAG->setExclusive(true); mViewByArtistA = new QAction(QIcon(":/artist.png"), tr("View by artist"), this); mViewByArtistA->setCheckable(true); viewAG->addAction(mViewByArtistA); connect(mViewByArtistA, &QAction::triggered, this, &PlayerWidget::doPopulateByArtist); QAction *viewByAlbumA = new QAction(QIcon(":/album.png"), tr("View by album"), this); viewByAlbumA->setCheckable(true); viewAG->addAction(viewByAlbumA); connect(viewByAlbumA, &QAction::triggered, this, &PlayerWidget::doPopulateByAlbum); QAction *viewBySongA = new QAction(QIcon(":/song.png"), tr("View by song"), this); viewBySongA->setCheckable(true); viewAG->addAction(viewBySongA); connect(viewBySongA, &QAction::triggered, this, &PlayerWidget::doPopulateBySong); QAction *viewByGenreA = new QAction(QIcon(":/genre.png"), tr("View by genre"), this); viewByGenreA->setCheckable(true); viewAG->addAction(viewByGenreA); connect(viewByGenreA, &QAction::triggered, this, &PlayerWidget::doPopulateByGenre); QAction *viewByDateA = new QAction(QIcon(":/sissyd.png"), tr("View by date"), this); viewByDateA->setCheckable(true); viewAG->addAction(viewByDateA); connect(viewByDateA, &QAction::triggered, this, &PlayerWidget::doPopulateByDate); viewAG->addAction(Helper::createSeparator(this)); QAction *viewByFolderA = new QAction(QIcon(":/folder.png"), tr("View by folder"), this); viewByFolderA->setCheckable(true); viewAG->addAction(viewByFolderA); connect(viewByFolderA, &QAction::triggered, this, &PlayerWidget::doPopulateByFolder); viewAG->addAction(Helper::createSeparator(this)); mSearchA = new QAction(QIcon(":/gaping_ass.png"), tr("View search"), this); mSearchA->setCheckable(true); viewAG->addAction(mSearchA); connect(mSearchA, &QAction::triggered, this, &PlayerWidget::doFilter); QAction *viewByWebradioA = new QAction(QIcon(":/dog_hood.png"), tr("Webradio"), this); viewByWebradioA->setCheckable(true); viewAG->addAction(Helper::createSeparator(this)); viewAG->addAction(viewByWebradioA); connect(viewByWebradioA, &QAction::triggered, this, &PlayerWidget::doPopulateByWebradio); viewTB->addActions(viewAG->actions()); mSelectFilesA = new QAction(QIcon(":/bizarre_amputee.png"), tr("Select files..."), this); mSelectFilesA->setShortcut(tr("CTRL++")); connect(mSelectFilesA, &QAction::triggered, this, &PlayerWidget::doSelectFiles); mDeselectAllA = new QAction(QIcon(":/gaping_ass.png"), tr("Clear Selection"), this); mDeselectAllA->setShortcut(tr("CTRL+-")); connect(mDeselectAllA, &QAction::triggered, this, &PlayerWidget::doDeleteFiles); mDeleteFilesA = new QAction(QIcon(":/delete.png"), tr("Delete files..."), this); mDeleteFilesA->setShortcut(QKeySequence::Delete); connect(mDeleteFilesA, &QAction::triggered, this, &PlayerWidget::doDeleteFiles); mRefreshA = new QAction(QIcon(":/refresh.png"), tr("Refresh"), this); connect(mRefreshA, &QAction::triggered, this, &PlayerWidget::doPopulateByFolder); //filter QGroupBox *filterGB = new QGroupBox(tr("Search")); mSearch = new QLineEdit; connect(mSearch, &QLineEdit::returnPressed, this, &PlayerWidget::doFilter); QToolBar *searchTB = new QToolBar; QAction *clearSearchA = new QAction(QIcon(":/clean_tampon.png"), tr("Clear search"), this); connect(clearSearchA, &QAction::triggered, this, &PlayerWidget::clearFilter); searchTB->addAction(clearSearchA); QAction *doSearchA = new QAction(QIcon(":/stomp.png"), tr("Go searching!"), this); connect(doSearchA, &QAction::triggered, this, &PlayerWidget::doFilter); searchTB->addAction(doSearchA); QHBoxLayout *filterLayout = new QHBoxLayout; filterLayout->addWidget(mSearch); filterLayout->addWidget(searchTB); filterGB->setLayout(filterLayout); //directories QGroupBox *dirGB = new QGroupBox(QString(tr("Current Directory"))); mDir = new QLineEdit; mDir->setEnabled(false); QToolBar *dirTB = new QToolBar; QImage upImg(":/stomp.png"); upImg = upImg.mirrored(); QIcon upDirIcon = QPixmap::fromImage(upImg); QAction *upDirA = new QAction(upDirIcon, tr("Up directory"), this); connect(upDirA, &QAction::triggered, this, &PlayerWidget::dirUp); dirTB->addAction(upDirA); QAction *homeDirA = new QAction(QIcon(":/home.png"), tr("Go home"), this); connect(homeDirA, &QAction::triggered, this, &PlayerWidget::dirHome); dirTB->addAction(homeDirA); dirTB->addAction(mRefreshA); QHBoxLayout *dirLayout = new QHBoxLayout; dirLayout->addWidget(mDir); dirLayout->addWidget(dirTB); dirGB->setLayout(dirLayout); //stack it up! mSearchDirStack = new QStackedLayout; mSearchDirStack->addWidget(filterGB); mSearchDirStack->addWidget(dirGB); //left widget QWidget *leftWidget = new QWidget; QVBoxLayout *leftWidgetL = new QVBoxLayout; leftWidgetL->addLayout(mSearchDirStack); leftWidgetL->addWidget(mView, 999); QHBoxLayout *selViewL = new QHBoxLayout; selViewL->addWidget(new QLabel(tr("View by:"))); selViewL->addStretch(); selViewL->addWidget(viewTB); selViewL->addStretch(); leftWidgetL->addLayout(selViewL); leftWidget->setLayout(leftWidgetL); connect(this, &PlayerWidget::modelChanged, this, &PlayerWidget::doModelChanged); //now playing label mNowPlayingL = new QLabel; mNowPlayingL->setAlignment(Qt::AlignCenter); mNowPlayingL->setFont(QFont("courier new", 20, QFont::Bold)); mNowPlayingL->setText(tr("(none)")); //song slider QLabel *l1 = new QLabel(tr("Song")); mSongSlider = new QSlider; mSongSlider->setOrientation(Qt::Horizontal); connect(mSongSlider, &QSlider::sliderMoved, this, &PlayerWidget::slide); mPos = new QLabel(tr("00:00")); mPos->setFont(QFont("courier")); QHBoxLayout *songSliderL = new QHBoxLayout; songSliderL->addWidget(l1); songSliderL->addWidget(mSongSlider); songSliderL->addWidget(mPos); //now playing info QString currentInfoGBS = QString(tr("Now Playing %1")).arg(QChar(0x26A5)); QGroupBox *currentInfoGB = new QGroupBox(currentInfoGBS); mCurrentTE = new QTextEdit; mCurrentTE->setFont(QFont("courier")); mCurrentTE->setReadOnly(true); QVBoxLayout *currentInfoL = new QVBoxLayout; currentInfoL->addWidget(mCurrentTE); currentInfoGB->setLayout(currentInfoL); //misc info QString leftInfoGBS = QString(tr("%1 Misc. %1")).arg(QChar(0x26A5)); QGroupBox *leftInfoGB = new QGroupBox(leftInfoGBS); mLeftTE = new QTextBrowser; mLeftTE->setOpenExternalLinks(true); mLeftTE->setFont(QFont("courier")); mLeftTE->setReadOnly(true); QVBoxLayout *leftInfoL = new QVBoxLayout; leftInfoL->addWidget(mLeftTE); leftInfoGB->setLayout(leftInfoL); //current right info QString rightInfoGBS = QString(tr("Selected %1")).arg(QChar(0x26A5)); QGroupBox *rightInfoGB = new QGroupBox(rightInfoGBS); mRightTE = new QTextEdit; mRightTE->setFont(QFont("courier")); mRightTE->setReadOnly(true); QVBoxLayout *rightInfoL = new QVBoxLayout; rightInfoL->addWidget(mRightTE); rightInfoGB->setLayout(rightInfoL); //volume slider QLabel *l2 = new QLabel(tr("Volume")); mVolumeSlider = new QSlider; mVolumeSlider->setOrientation(Qt::Horizontal); mVolumeSlider->setMinimum(0); mVolumeSlider->setMaximum(100); mVolumePos = new QLabel(tr("000 %")); mVolumePos->setFont(QFont("courier")); connect(mVolumeSlider, &QSlider::valueChanged, mPlayer, &QMediaPlayer::setVolume); connect(mVolumeSlider, &QSlider::valueChanged, this, &PlayerWidget::volumeChanged); mVolumeSlider->setValue(33); QHBoxLayout *volumeL = new QHBoxLayout; volumeL->addWidget(l2); volumeL->addWidget(mVolumeSlider); volumeL->addWidget(mVolumePos); //center widget QWidget *centerWidget = new QWidget; QVBoxLayout *centerWidgetL = new QVBoxLayout; QSplitter *centerSplitter = new QSplitter; centerSplitter->setOrientation(Qt::Vertical); centerSplitter->addWidget(currentInfoGB); centerSplitter->addWidget(leftInfoGB); centerSplitter->addWidget(rightInfoGB); mToolBar = new QToolBar; centerWidgetL->addWidget(mToolBar); centerWidgetL->addWidget(mNowPlayingL); centerWidgetL->addLayout(songSliderL); centerWidgetL->addWidget(centerSplitter); centerWidgetL->addLayout(volumeL); centerWidget->setLayout(centerWidgetL); //playlist mPlayListModel = new QStandardItemModel; mPlayListModel->setHorizontalHeaderLabels(QStringList() << "Title"); mPlayListView = new BeetView; mPlayListView->setAlternatingRowColors(true); mPlayListView->setModel(mPlayListModel); mPlayListView->setRootIsDecorated(false); mPlayListView->setSelectionMode(QAbstractItemView::ExtendedSelection); connect(mPlayListView, &BeetView::doubleClicked, this, &PlayerWidget::playCurrent); connect(mPlayListView->selectionModel(), &QItemSelectionModel::currentChanged, this, &PlayerWidget::rightCurrentChanged); QGroupBox *playListGB = new QGroupBox(tr("Playlist")); QVBoxLayout *playListL = new QVBoxLayout; playListL->addWidget(mPlayListView); playListGB->setLayout(playListL); //right widget QWidget *rightWidget = new QWidget; QVBoxLayout *rightWidgetL = new QVBoxLayout; rightWidgetL->addWidget(playListGB); rightWidget->setLayout(rightWidgetL); //stream data connect(this, &PlayerWidget::streamDataNeedsUpdate, this, &PlayerWidget::updateStreamData); //put it all together QSplitter *splitter = new QSplitter; splitter->addWidget(leftWidget); splitter->addWidget(centerWidget); splitter->addWidget(rightWidget); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(splitter); setLayout(mainLayout); } void PlayerWidget::createActions(){ QActionGroup *playAG = new QActionGroup(this); playAG->setExclusive(true); mPlayA = new QAction(QIcon(":/play.png"), tr("Play"), this); mPlayA->setCheckable(true); playAG->addAction(mPlayA); connect(mPlayA, &QAction::triggered, this, &PlayerWidget::doPlay); mPauseA = new QAction(QIcon(":/pause.png"), tr("Pause"), this); mPauseA->setCheckable(true); playAG->addAction(mPauseA); connect(mPauseA, &QAction::triggered, this, &PlayerWidget::doPause); mStopA = new QAction(QIcon(":/stop.png"), tr("Stop"), this); mStopA->setCheckable(true); playAG->addAction(mStopA); mStopA->setChecked(true); connect(mStopA, &QAction::triggered, this, &PlayerWidget::doStop); QAction *playOrPauseA = new QAction(tr("Play or Pause"), this); playOrPauseA->setObjectName("beetPlayerDoPlayOrPause"); connect(playOrPauseA, &QAction::triggered, this, &PlayerWidget::doPlayOrPause); KGlobalAccel::self()->setShortcut(playOrPauseA, QList() << QKeySequence(Qt::Key_MediaPlay), KGlobalAccel::Autoloading); QAction *previousA = new QAction(QIcon(":/previous.png"), tr("Previous"), this); previousA->setObjectName("beetPlayerPrevious"); connect(previousA, &QAction::triggered, this, &PlayerWidget::previous); KGlobalAccel::self()->setShortcut(previousA, QList() << QKeySequence(Qt::Key_Launch2), KGlobalAccel::Autoloading); QAction *nextA = new QAction(QIcon(":/next.png"), tr("Next"), this); nextA->setObjectName("beetPlayerNext"); connect(nextA, &QAction::triggered, this, &PlayerWidget::next); KGlobalAccel::self()->setShortcut(nextA, QList() << QKeySequence(Qt::Key_Launch3), KGlobalAccel::Autoloading); QAction *addToPlayListA = new QAction(QIcon(":/belly_right.png"), tr("Add to playlist"), this); connect(addToPlayListA, &QAction::triggered, this, &PlayerWidget::addToPlayList); QAction *addToPlayListAndClearA = new QAction(QIcon(":/belly_right_and_clear.png"), tr("Clear and add"), this); connect(addToPlayListAndClearA, &QAction::triggered, this, &PlayerWidget::addToPlayListAndClear); QAction *expandA = new QAction(Helper::iconFromQChar(QChar(0x2640), 90), tr("Expand"), this); connect(expandA, &QAction::triggered, this, &PlayerWidget::expand); QAction *collapseAllA = new QAction(Helper::iconFromQChar(QChar(0x2642), 120), tr("Collapse all"), this); connect(collapseAllA, &QAction::triggered, mView, &BeetView::collapseAll); QAction *removeFromPlayListA = new QAction(QIcon(":/belly_left.png"), tr("Remove from playlist"), this); connect(removeFromPlayListA, &QAction::triggered, this, &PlayerWidget::removeFromPlayList); QAction *clearPlayListA = new QAction(QIcon(":/delete.png"), tr("Clear Playlist"), this); connect(clearPlayListA, &QAction::triggered, this, &PlayerWidget::clearPlayList); QAction *refreshA = new QAction(QIcon(":/refresh.png"), tr("Refresh..."), this); connect(refreshA, &QAction::triggered, this, &PlayerWidget::reindex); QAction *shufflePlayistA = new QAction(QIcon(":/shuffle.png"), tr("Shuffle playlist"), this); connect(shufflePlayistA, &QAction::triggered, this, &PlayerWidget::shufflePlayList); QAction *randomPlayA = new QAction(QIcon(":/dice.png"), tr("Play random"), this); connect(randomPlayA, &QAction::triggered, this, &PlayerWidget::randomPlay); QAction *muteA = new QAction(QIcon(":/mute.png"), tr("Mute"), this); muteA->setObjectName("beetPlayerMute"); muteA->setCheckable(true); connect(muteA, &QAction::triggered, this, &PlayerWidget::mute); KGlobalAccel::self()->setShortcut(muteA, QList() << QKeySequence(Qt::Key_VolumeMute), KGlobalAccel::Autoloading); QAction *volumeUpA = new QAction(tr("Increase volume"), this); volumeUpA->setObjectName("beetPlayerIncVolume"); connect(volumeUpA, &QAction::triggered, this, &PlayerWidget::volumeUp); KGlobalAccel::self()->setShortcut(volumeUpA, QList() << QKeySequence(Qt::Key_VolumeUp), KGlobalAccel::Autoloading); QAction *volumeDownA = new QAction(tr("Decrease volume"), this); volumeDownA->setObjectName("beetPlayerDecVolume"); connect(volumeDownA, &QAction::triggered, this, &PlayerWidget::volumeDown); KGlobalAccel::self()->setShortcut(volumeDownA, QList() << QKeySequence(Qt::Key_VolumeDown), KGlobalAccel::Autoloading); QAction *configA = Globals::instance()->action(Globals::ConfigAction); QAction *helpA = new QAction(Helper::iconFromQChar(QChar(0x00BF), 50), tr("Help"), this); QMenu *helpM = new QMenu; QAction *aboutThisA = new QAction(tr("About..."), this); connect(aboutThisA, &QAction::triggered, this, &PlayerWidget::aboutDlg);; helpM->addAction(aboutThisA); QAction *aboutQtA = new QAction(tr("About Qt..."), this); connect(aboutQtA, &QAction::triggered, qApp, &QApplication::aboutQt); helpM->addAction(aboutQtA); helpA->setMenu(helpM); QAction *miscPrintA = new QAction(QIcon(":/clean_tampon.png"), tr("List selected"), this); connect(miscPrintA, &QAction::triggered, this, &PlayerWidget::printList); QAction *miscMusicBrainzRightA = new QAction(QIcon(":/bizarre_amputee.png"), tr("Search Musicbrainz"), this); connect(miscMusicBrainzRightA, &QAction::triggered, this, &PlayerWidget::searchMusicbrainzRight); QAction *miscMusicBrainzLeftA = new QAction(QIcon(":/bizarre_amputee.png"), tr("Search Musicbrainz"), this); connect(miscMusicBrainzLeftA, &QAction::triggered, this, &PlayerWidget::searchMusicbrainzLeft); QAction *miscFilterFromPlaylistA = new QAction(QIcon(":/chastity_belt.png"), tr("Filter artist"), this); connect(miscFilterFromPlaylistA, &QAction::triggered, this, &PlayerWidget::filterFromPlaylist); QAction *addToWebRadioA = new QAction(QIcon(":/dog_hood.png"), tr("Add Webradio"), this); connect(addToWebRadioA, &QAction::triggered, this, &PlayerWidget::addWebRadio); mView->addAction(addToPlayListA); mView->addAction(addToPlayListAndClearA); mView->addAction(Helper::createSeparator(this)); mView->addAction(expandA); mView->addAction(collapseAllA); mView->addAction(Helper::createSeparator(this)); mView->addAction(mSelectFilesA); mView->addAction(mDeselectAllA); mView->addAction(mDeleteFilesA); mView->addAction(Helper::createSeparator(this)); mView->addAction(miscPrintA); mView->addAction(miscMusicBrainzLeftA); mView->addAction(Helper::createSeparator(this)); mView->addAction(mRefreshA); mView->addAction(Helper::createSeparator(this)); mView->addAction(addToWebRadioA); mView->addAction(Helper::createSeparator(this)); mView->addAction(randomPlayA); mPlayListView->addAction(removeFromPlayListA); mPlayListView->addAction(Helper::createSeparator(this)); mPlayListView->addAction(miscMusicBrainzRightA); mPlayListView->addAction(miscFilterFromPlaylistA); mPlayListView->addAction(Helper::createSeparator(this)); mPlayListView->addAction(shufflePlayistA); mPlayListView->addAction(clearPlayListA); QWidget* spacer1 = new QWidget(); spacer1->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); mToolBar->addWidget(spacer1); mToolBar->addAction(mPlayA); mToolBar->addAction(mPauseA); mToolBar->addAction(mStopA); mToolBar->addSeparator(); mToolBar->addAction(previousA); mToolBar->addAction(nextA); mToolBar->addSeparator(); mToolBar->addAction(addToPlayListA); mToolBar->addAction(removeFromPlayListA); mToolBar->addAction(clearPlayListA); mToolBar->addAction(shufflePlayistA); mToolBar->addAction(randomPlayA); mToolBar->addSeparator(); mToolBar->addAction(refreshA); mToolBar->addSeparator(); mToolBar->addAction(muteA); mToolBar->addSeparator(); mToolBar->addAction(configA); mToolBar->addSeparator(); mToolBar->addAction(helpA); QWidget* spacer2 = new QWidget(); spacer2->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); mToolBar->addWidget(spacer2); QMenu *trayMenu = new QMenu; trayMenu->addAction(mPlayA); trayMenu->addAction(mStopA); trayMenu->addAction(mPauseA); trayMenu->addSeparator(); trayMenu->addAction(nextA); trayMenu->addAction(previousA); trayMenu->addSeparator(); QAction *quitA = new QAction(tr("Quit"), this); connect(quitA, &QAction::triggered, qApp, &QApplication::quit); trayMenu->addAction(quitA); mTrayIcon->setContextMenu(trayMenu); } void PlayerWidget::populateByArtist(QStandardItem *parent, const QString &filter){ QSqlDatabase db = QSqlDatabase::database("beetplayerdb"); QStandardItem *root = parent; QSqlQuery artistsQ(db); if(!filter.isEmpty()){ artistsQ.prepare("SELECT iartists_id, tartists_name FROM artists WHERE tartists_name ~ :f ORDER BY tartists_name ASC"); artistsQ.bindValue(":f", filter); }else{ artistsQ.prepare("SELECT iartists_id, tartists_name FROM artists ORDER BY tartists_name ASC"); } 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, 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"); //read data artistsQ.exec(); while(artistsQ.next()){ QStandardItem *curArtist = new QStandardItem; curArtist->setEditable(false); curArtist->setFont(QFont("courier")); curArtist->setText(artistsQ.value(1).toString()); curArtist->setIcon(artistIcon); curArtist->setData(Artist, TypeRole); curArtist->setData(artistsQ.value(0).toInt(), IdRole); curArtist->setData(artistsQ.value(1), ArtistRole); root->appendRow(curArtist); albumQ.bindValue(":artistid", artistsQ.value(0)); albumQ.exec(); while(albumQ.next()){ QStandardItem *curAlbum = new QStandardItem; curAlbum->setEditable(false); curAlbum->setFont(QFont("courier")); QString albumText = QString(tr("%1 - %2")).arg(QString::number(albumQ.value(2).toInt())).arg(albumQ.value(1).toString()); curAlbum->setText(albumText); curAlbum->setIcon(albumIcon); curAlbum->setData(Album, TypeRole); curAlbum->setData(albumQ.value(0), IdRole); curAlbum->setData(artistsQ.value(1), ArtistRole); curAlbum->setData(albumQ.value(1), AlbumRole); curArtist->appendRow(curAlbum); songQ.bindValue(":alid", albumQ.value(0)); songQ.bindValue(":arid", artistsQ.value(0)); songQ.exec(); while(songQ.next()){ QStandardItem *curSong = new QStandardItem; curSong->setEditable(false); curSong->setFont(QFont("courier")); QString songText = QString(tr("%1 - %2")).arg(songQ.value(0).toInt(), 3, 10, QChar('0')).arg(songQ.value(1).toString()); curSong->setText(songText); curSong->setIcon(songIcon); curSong->setData(Song, TypeRole); curSong->setData(songQ.value(0), IdRole); curSong->setData(songQ.value(2), FullPathRole); 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); } } } } void PlayerWidget::populateByAlbum(QStandardItem *parent, const QVariant &filter, int type){ QSqlDatabase db = QSqlDatabase::database("beetplayerdb"); QIcon albumIcon(":/album.png"); QIcon songIcon(":/song.png"); QSqlQuery albumQ(db); if(type == EmptyType){ albumQ.prepare("SELECT DISTINCT(ialbums_id), talbum_name, siyear FROM albums ORDER BY talbum_name"); }else if(type == FilterType){ albumQ.prepare("SELECT DISTINCT(songs.ialbums_id), talbum_name, siyear FROM songs, albums WHERE talbum_name ~ :album AND songs.ialbums_id = albums.ialbums_id ORDER BY siyear ASC"); albumQ.bindValue(":album", filter); } albumQ.exec(); while(albumQ.next()){ QHash artistcount; QStandardItem *curAlbum = new QStandardItem; curAlbum->setEditable(false); curAlbum->setFont(QFont("courier")); curAlbum->setIcon(albumIcon); curAlbum->setData(Album, TypeRole); 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, 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()){ QStandardItem *curSong = new QStandardItem; curSong->setEditable(false); curSong->setFont(QFont("courier")); QString songText = QString(tr("%1 - %2 - %3")).arg(songQ.value(0).toInt(), 3, 10, QChar('0')).arg(songQ.value(1).toString()).arg(songQ.value(4).toString()); curSong->setText(songText); curSong->setIcon(songIcon); curSong->setData(Song, TypeRole); curSong->setData(songQ.value(0), IdRole); curSong->setData(songQ.value(2), FullPathRole); curSong->setData(songQ.value(3), GenreRole); curSong->setData(songQ.value(4), ArtistRole); ++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; if(artistcount.keys().count() > 1){ albumText = QString(tr("%1 - VA - (%2)")).arg(albumQ.value(1).toString()).arg(QString::number(albumQ.value(2).toInt())); }else{ albumText = QString(tr("%1 - %2 - (%3)")).arg(albumQ.value(1).toString()).arg(artistcount.keys().first()). arg(QString::number(albumQ.value(2).toInt())); } curAlbum->setText(albumText); } } void PlayerWidget::populateBySong(QStandardItem *parent, const QVariant &filter, int type){ QSqlDatabase db = QSqlDatabase::database("beetplayerdb"); QStandardItem *root = parent; 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, 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, 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"); songQ.bindValue(":id", filter); } songQ.exec(); while(songQ.next()){ QStandardItem *curSong = new QStandardItem; curSong->setEditable(false); curSong->setFont(QFont("courier")); QString songText; if(type == IdType){ songText = QString(tr("%1 - %2")).arg(songQ.value(0).toInt(), 3, 10, QChar('0')).arg(songQ.value(1).toString()); }else{ songText = QString(tr("%1 (%2)")).arg(songQ.value(1).toString()).arg(songQ.value(4).toString()); } curSong->setText(songText); curSong->setIcon(songIcon); curSong->setData(Song, TypeRole); curSong->setData(songQ.value(0), IdRole); curSong->setData(songQ.value(2), FullPathRole); curSong->setData(songQ.value(3), GenreRole); 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); } } void PlayerWidget::populateByDate(QStandardItem *parent){ QSqlDatabase db = QSqlDatabase::database("beetplayerdb"); QStandardItem *root = parent; QIcon dateIcon(":/bizarre_amputee.png"); root->setIcon(dateIcon); QIcon albumIcon(":/album.png"); QIcon songIcon(":/song.png"); QSqlQuery q1("SELECT DISTINCT(albums.ialbums_id), talbum_name, dadded, tartists_name FROM albums, artists, songs WHERE albums.ialbums_id = songs.ialbums_id AND songs.iartists_id = artists.iartists_id ORDER BY dadded DESC", db); QSqlQuery songQ(db); 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"); QHash years; QHash yearMonths; while(q1.next()){ QDate added = q1.value(2).toDate(); int year = added.year(); QStandardItem *yearItem = nullptr; QStandardItem *ymItem = nullptr; if(years.contains(year)){ yearItem = years.value(year); }else{ yearItem = new QStandardItem; yearItem->setText(QString::number(year)); years.insert(year, yearItem); yearItem->setIcon(dateIcon); root->appendRow(yearItem); } QString yearMonth = QString("%1-%2").arg(QString::number(added.year())).arg(added.month(), 2, 10, QChar('0')); if(yearMonths.contains(yearMonth)){ ymItem = yearMonths.value(yearMonth); }else{ ymItem = new QStandardItem; ymItem->setText(yearMonth); ymItem->setIcon(dateIcon); yearMonths.insert(yearMonth, ymItem); yearItem->appendRow(ymItem); } QStandardItem *albumItem = new QStandardItem; QString albumText = QString("%1 - %2").arg(q1.value(3).toString()).arg(q1.value(1).toString()); albumItem->setText(albumText); albumItem->setIcon(albumIcon); ymItem->appendRow(albumItem); songQ.bindValue(":id", q1.value(0)); songQ.exec(); while(songQ.next()){ QStandardItem *curSong = new QStandardItem; curSong->setEditable(false); curSong->setFont(QFont("courier")); QString songText = QString(tr("%1 - %2 - %3")).arg(songQ.value(0).toInt(), 3, 10, QChar('0')).arg(songQ.value(1).toString()).arg(songQ.value(4).toString()); curSong->setText(songText); curSong->setIcon(songIcon); curSong->setData(Song, TypeRole); curSong->setData(songQ.value(0), IdRole); curSong->setData(songQ.value(2), FullPathRole); curSong->setData(songQ.value(3), GenreRole); curSong->setData(songQ.value(4), ArtistRole); curSong->setData(songQ.value(1), TitleRole); curSong->setData(songQ.value(5), AlbumRole); curSong->setData(songQ.value(6), LengthRole); albumItem->appendRow(curSong); } } } void PlayerWidget::populateByGenre(QStandardItem *parent, const QString &filter){ QSqlDatabase db = QSqlDatabase::database("beetplayerdb"); QStandardItem *root = parent; QIcon songIcon(":/song.png"); QIcon genreIcon(":/genre.png"); QSqlQuery genreQ(db); if(filter.isEmpty()){ genreQ.prepare("SELECT igenres_id, tgenres_name FROM genres ORDER BY tgenres_name"); }else{ genreQ.prepare("SELECT igenres_id, tgenres_name FROM genres WHERE tgenres_name ~ :f"); genreQ.bindValue(":f", filter); } genreQ.exec(); while(genreQ.next()){ QStandardItem *curGenre = new QStandardItem; curGenre->setEditable(false); curGenre->setFont(QFont("courier")); curGenre->setText(genreQ.value(1).toString()); curGenre->setIcon(genreIcon); curGenre->setData(Genre, TypeRole); 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, 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()){ QStandardItem *curSong = new QStandardItem; curSong->setEditable(false); curSong->setFont(QFont("courier")); QString songText = QString(tr("%1 (%2)")).arg(songQ.value(1).toString()).arg(songQ.value(4).toString()); curSong->setText(songText); curSong->setIcon(songIcon); curSong->setData(Song, TypeRole); curSong->setData(songQ.value(0), IdRole); curSong->setData(songQ.value(2), FullPathRole); curSong->setData(songQ.value(3), GenreRole); 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); } } } void PlayerWidget::populateByWebradio(QStandardItem *parent){ QSqlDatabase db = QSqlDatabase::database("beetplayerdb"); QStandardItem *root = parent; QIcon wrIcon(":/dog_hood.png"); QSqlQuery wrQ = QSqlQuery(db); wrQ.prepare("SELECT tdescription, turl FROM webradio ORDER BY tdescription DESC"); wrQ.exec(); while(wrQ.next()){ QStandardItem *curWr = new QStandardItem; curWr->setEditable(false); curWr->setFont(QFont("courier")); curWr->setText(wrQ.value(0).toString()); curWr->setIcon(wrIcon); curWr->setData(wrQ.value(1), UrlRole); root->appendRow(curWr); } } void PlayerWidget::doPopulateByFolder(){ mCurrentModel = mFolderModel; mFolderModel->clear(); mFolderModel->setHorizontalHeaderLabels(QStringList() << tr("Name")); QDir d(mCurDir); if(!d.exists()){ d = QDir(QDir::homePath()); mCurDir = d.absolutePath(); } QMimeDatabase db; QStandardItem *root = mFolderModel->invisibleRootItem(); QIcon songIcon(":/song.png"); QIcon otherIcon(":/belly_right_and_clear.png"); QIcon dirIcon(":/folder.png"); QFileInfoList fl = d.entryInfoList(QStringList() << "*", QDir::Files | QDir::Dirs | QDir::NoDot, QDir::Name | QDir::DirsFirst); foreach(QFileInfo fi, fl){ QStandardItem *cur = new QStandardItem; cur->setEditable(false); cur->setText(fi.fileName()); if(fi.isDir()){ cur->setIcon(dirIcon); }else if(fi.isFile()){ QMimeType mt = db.mimeTypeForFile(fi); if(mt.name().startsWith("audio")){ cur->setIcon(songIcon); cur->setData(Song, TypeRole); cur->setData(-1, IdRole); TagLib::FileRef file(fi.absoluteFilePath().toUtf8()); if(!file.isNull()){ QString artist = QString::fromStdWString(file.tag()->artist().toWString()); cur->setData(artist.toLower(), ArtistRole); QString album = QString::fromStdWString(file.tag()->album().toWString()); cur->setData(album.toLower(), AlbumRole); QString genre = QString::fromStdWString(file.tag()->genre().toWString()); cur->setData(genre.toLower(), GenreRole); QString title = QString::fromStdWString(file.tag()->title().toWString()); cur->setData(title.toLower(), TitleRole); } }else{ cur->setIcon(otherIcon); } } cur->setData(fi.absoluteFilePath(), FullPathRole); root->appendRow(cur); } mView->setModel(mFolderModel); emit viewModeChanged(tr("Folder")); emit modelChanged(); } void PlayerWidget::doPopulateByWebradio(){ mCurrentModel = mWebRadioModel; mWebRadioModel->clear(); mWebRadioModel->setHorizontalHeaderLabels(QStringList() << tr("Description")); QStandardItem *root = mWebRadioModel->invisibleRootItem(); populateByWebradio(root); mView->setModel(mWebRadioModel); emit viewModeChanged("WebRadio"); emit modelChanged(); } void PlayerWidget::doModelChanged(){ if(mCurrentModel == mFolderModel){ mSelectFilesA->setEnabled(true); mDeselectAllA->setEnabled(true); mDeleteFilesA->setEnabled(true); mRefreshA->setEnabled(true); mSearchDirStack->setCurrentIndex(1); }else{ mSelectFilesA->setEnabled(false); mDeselectAllA->setEnabled(false); mDeleteFilesA->setEnabled(false); mRefreshA->setEnabled(false); mSearchDirStack->setCurrentIndex(0); } } void PlayerWidget::viewDoubleClicked(const QModelIndex &idx){ mPlayer->stop(); const QStandardItemModel *model = static_cast(idx.model()); if(model == mViewModel || model == mSearchModel){ addToPlayList(); return; } if(model == mWebRadioModel){ QString url(idx.data(UrlRole).toString()); mPlayListModel->clear(); mPlayListModel->setHorizontalHeaderLabels(QStringList() << tr("Title")); mRightTE->clear(); playUrl(url); return; } QString fp(idx.data(FullPathRole).toString()); QFileInfo fi(fp); if(fi.isDir()){ mCurDir = fp; mDir->setText(fp); doPopulateByFolder(); }else{ addToPlayList(); } } void PlayerWidget::rightCurrentChanged(const QModelIndex &cur, const QModelIndex &prev){ Q_UNUSED(prev) QString fullPath(cur.data(FullPathRole).toString()); TagLib::FileRef file(QString(fullPath).toUtf8()); fillWithText(mRightTE, file); } void PlayerWidget::doPlay(){ if(mPlayer->state() == QMediaPlayer::PausedState){ mPlayer->play(); mPlayA->setChecked(true); emit playModeChanged(tr("Playing")); emit setWinTitle(mCurWinTitle); mTrayIcon->setIcon(QIcon(":/play.png")); mTrayIcon->setToolTip(mCurToolTip); return; } int playListCount = mPlayListModel->rowCount(); if(playListCount == 0){ emit message(tr("Playlist is empty! Dazed and confused, but trying to continue...")); doStop(); return; } QModelIndexList sel = mPlayListView->selectionModel()->selectedRows(); if(sel.isEmpty()){ mPlayListView->selectionModel()->select(mPlayListModel->index(0, 0), QItemSelectionModel::SelectCurrent); sel = mPlayListView->selectionModel()->selectedRows(); } playCurrent(sel.first()); } void PlayerWidget::doStop(){ mPlayer->stop(); mStopA->setChecked(true); emit playModeChanged(tr("Stopped")); QString winTitle = QString(tr("%1 [Stopped]")).arg(qApp->applicationName()); emit setWinTitle(winTitle); mTrayIcon->setIcon(QIcon(":/stop.png")); mTrayIcon->setToolTip(tr("[Stopped]")); } void PlayerWidget::doPause(){ mPlayer->pause(); mPauseA->setChecked(true); emit playModeChanged(tr("Paused")); QString winTitle = QString(tr("%1 [Paused]")).arg(qApp->applicationName()); emit setWinTitle(winTitle); mTrayIcon->setIcon(QIcon(":/pause.png")); mTrayIcon->setToolTip(tr("[Paused]")); } void PlayerWidget::doPlayOrPause(){ if(mPlayA->isChecked()){ doPause(); }else if(mPauseA->isChecked() || mStopA->isChecked()){ doPlay(); } } void PlayerWidget::doSelectFiles(){ bool ok; QString pattern = QInputDialog::getText(this, tr("Select files..."), tr("Pattern:"), QLineEdit::Normal, QString(), &ok); if(ok && !pattern.isEmpty()){ QList items = mFolderModel->findItems(pattern, Qt::MatchWildcard); mView->selectionModel()->clear(); foreach(QStandardItem *i, items){ QModelIndex idx = mFolderModel->indexFromItem(i); mView->selectionModel()->select(idx, QItemSelectionModel::Rows | QItemSelectionModel::Toggle); } } } void PlayerWidget::doDeselect(){ mView->selectionModel()->clearSelection(); } void PlayerWidget::doDeleteFiles(){ QModelIndexList sel = mView->selectionModel()->selectedRows(); if(!sel.isEmpty()){ QString msg = QString(tr("Really delete %1 file(s)?")).arg(QString::number(sel.count())); int r = QMessageBox::question(this, tr("Delete files..."), msg); if(r == QMessageBox::Yes){ foreach(QModelIndex i, sel){ QString cur = i.data(FullPathRole).toString(); QFileInfo file(cur); if(file.isFile()){ QFile::remove(cur); continue; } if(file.isDir()){ QString d = file.absoluteFilePath(); QDir toDel(d); toDel.removeRecursively(); } } doPopulateByFolder(); } } } void PlayerWidget::volumeUp(){ adjustVolume(2); } void PlayerWidget::volumeDown(){ adjustVolume(-2); } void PlayerWidget::dirUp(){ QDir d(mCurDir); d.cdUp(); mDir->setText(d.absolutePath()); mCurDir = d.absolutePath(); doPopulateByFolder(); } void PlayerWidget::dirHome(){ mCurDir = QDir::homePath(); mDir->setText(mCurDir); doPopulateByFolder(); } void PlayerWidget::showVolume(){ int volume = mVolumeSlider->value(); QString msg = QString(tr("Volume: %1%")).arg(volume, 3, 10, QChar('0')); mTrayIcon->showMessage(msg, QString(), QSystemTrayIcon::Information, 2000); } void PlayerWidget::adjustVolume(int by){ int curVol = mVolumeSlider->value(); int newVol = curVol + by; if(newVol > 100){ newVol = 100; } if(newVol < 0){ newVol = 0; } mVolumeSlider->setValue(newVol); } void PlayerWidget::fillWithText(QTextEdit *te, const TagLib::FileRef &fr){ if(fr.isNull()){ return; } QString artist = QString::fromStdWString(fr.tag()->artist().toWString()); QString album = QString::fromStdWString(fr.tag()->album().toWString()); QString title = QString::fromStdWString(fr.tag()->title().toWString()); QString genre = QString::fromStdWString(fr.tag()->genre().toWString()); quint16 track = fr.tag()->track(); quint16 year = fr.tag()->year(); te->clear(); te->append(QString("%1 %2").arg(tr("Artist:"), -20).arg(artist)); te->append(QString("%1 %2").arg(tr("Album:"), -20).arg(album)); te->append(QString("%1 %2").arg(tr("Title:"), -20).arg(title)); te->append(QString("%1 %2").arg(tr("Genre:"), -20).arg(genre)); te->append(QString("%1 %2").arg(tr("Track:"), -20).arg(track, 3, 10, QChar('0'))); te->append(QString("%1 %2").arg(tr("Year:"), -20).arg(year, 4, 10)); te->append(QString("%1 %2 kb/s").arg(tr("Bitrate:"), -20).arg(fr.audioProperties()->bitrate(), 4, 10, QChar('0'))); } void PlayerWidget::recurse(const QModelIndex &parent){ for(int i = 0; i < mCurrentModel->rowCount(parent); ++i){ QModelIndex cur = mCurrentModel->index(i, 0, parent); int type = cur.data(TypeRole).toInt(); if(type == Song){ addSong(cur); }else{ recurse(cur); } } } void PlayerWidget::addSong(const QModelIndex &idx){ QStandardItem *root = mPlayListModel->invisibleRootItem(); QString title = idx.data(TitleRole).toString(); QString artist = idx.data(ArtistRole).toString(); QString album = idx.data(AlbumRole).toString(); QStringList args = QStringList() << title << artist << album; foreach(QString s, args){ if(s.isEmpty()){ s = QString(tr("n/a")); } } QString display = QString(tr("%1 - %2 - %3")).arg(artist).arg(title).arg(album); QStandardItem *item = new QStandardItem; item->setEditable(false); item->setFont(QFont("courier")); 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); item->setData(artist, ArtistRole); item->setData(album, AlbumRole); root->appendRow(item); mPlayListLength += len; } void PlayerWidget::doPopulateByArtist(){ qApp->setOverrideCursor(Qt::BusyCursor); mView->setModel(mViewModel); mCurrentModel = mViewModel; mViewModel->clear(); mViewModel->setHorizontalHeaderLabels(QStringList() << tr("Artist name")); QStandardItem *root = mViewModel->invisibleRootItem(); emit message(QString(tr("Populating by artist... Please wait!"))); qApp->processEvents(); populateByArtist(root, QString()); qApp->restoreOverrideCursor(); mViewByArtistA->setChecked(true); emit viewModeChanged(tr("Artist")); emit message(QString(tr("Done!"))); emit modelChanged(); } void PlayerWidget::doPopulateByAlbum(){ qApp->setOverrideCursor(Qt::BusyCursor); mView->setModel(mViewModel); mCurrentModel = mViewModel; mViewModel->clear(); mViewModel->setHorizontalHeaderLabels(QStringList() << tr("Album name")); QStandardItem *root = mViewModel->invisibleRootItem(); emit message(QString(tr("Populating by album... Please wait!"))); qApp->processEvents(); populateByAlbum(root, QString(), EmptyType); qApp->restoreOverrideCursor(); emit viewModeChanged(tr("Album")); emit message(QString(tr("Done!"))); emit modelChanged(); } void PlayerWidget::doPopulateByGenre(){ qApp->setOverrideCursor(Qt::BusyCursor); mView->setModel(mViewModel); mCurrentModel = mViewModel; mViewModel->clear(); mViewModel->setHorizontalHeaderLabels(QStringList() << tr("Genre name")); QStandardItem *root = mViewModel->invisibleRootItem(); emit message(QString(tr("Populating by genre... Please wait!"))); qApp->processEvents(); populateByGenre(root, QString()); qApp->restoreOverrideCursor(); emit viewModeChanged(tr("Genre")); emit message(QString(tr("Done!"))); emit modelChanged(); } void PlayerWidget::doPopulateBySong(){ qApp->setOverrideCursor(Qt::BusyCursor); mView->setModel(mViewModel); mCurrentModel = mViewModel; mViewModel->clear(); mViewModel->setHorizontalHeaderLabels(QStringList() << tr("Genre name")); QStandardItem *root = mViewModel->invisibleRootItem(); emit message(QString(tr("Populating by song... Please wait!"))); qApp->processEvents(); populateBySong(root, QString(), EmptyType); qApp->restoreOverrideCursor(); emit viewModeChanged(tr("Song")); emit message(QString(tr("Done!"))); emit modelChanged(); } void PlayerWidget::doPopulateByDate(){ qApp->setOverrideCursor(Qt::BusyCursor); mView->setModel(mViewModel); mCurrentModel = mViewModel; mViewModel->clear(); mViewModel->setHorizontalHeaderLabels(QStringList() << tr("Date added")); QStandardItem *root = mViewModel->invisibleRootItem(); emit message(QString(tr("Populating by date... Please wait!"))); qApp->processEvents(); populateByDate(root); qApp->restoreOverrideCursor(); emit viewModeChanged(tr("Date")); emit message(QString(tr("Done!"))); emit modelChanged(); } void PlayerWidget::doFilter(){ mSearchDirStack->setCurrentIndex(0); QString filter = mSearch->text(); mSearchModel->clear(); mSearchModel->setHorizontalHeaderLabels(QStringList() << tr("Name")); mView->setModel(mSearchModel); mCurrentModel = mSearchModel; emit viewModeChanged(tr("Search")); mSearchA->setChecked(true); if(filter.isEmpty()){ emit message(QString(tr("No search term set! Cowardly refusing to do anything!"))); return; } QString msg = QString(tr("Searching for \"%1\"")).arg(filter); emit message(msg); qApp->setOverrideCursor(Qt::BusyCursor); QStandardItem *root = mSearchModel->invisibleRootItem(); QStandardItem *artistI = new QStandardItem(QIcon(":/quadd.png"), tr("Artists")); artistI->setFont(QFont("courier", -1, QFont::Bold)); root->appendRow(artistI); populateByArtist(artistI, filter); QStandardItem *albumI = new QStandardItem(QIcon(":/quadd.png"), tr("Albums")); albumI->setFont(QFont("courier", -1, QFont::Bold)); root->appendRow(albumI); populateByAlbum(albumI, filter, FilterType); QStandardItem *genreI = new QStandardItem(QIcon(":/quadd.png"), tr("Genres")); genreI->setFont(QFont("courier", -1, QFont::Bold)); root->appendRow(genreI); populateByGenre(genreI, filter); QStandardItem *songI = new QStandardItem(QIcon(":/quadd.png"), tr("Songs")); songI->setFont(QFont("courier", -1, QFont::Bold)); root->appendRow(songI); populateBySong(songI, filter, FilterType); for(int i = 0; i < mCurrentModel->rowCount(); ++i){ QModelIndex exp = mCurrentModel->index(i, 0, QModelIndex()); if(exp.isValid()){ mView->expand(exp); QModelIndex next = mCurrentModel->index(0, 0, exp); if(next.isValid()){ mView->expand(next); } } } qApp->restoreOverrideCursor(); emit viewModeChanged(tr("Search")); emit modelChanged(); } void PlayerWidget::clearFilter(){ mSearch->clear(); mView->setModel(mViewModel); mCurrentModel = mViewModel; } void PlayerWidget::reindex(){ IndexerDialog dlg(this); dlg.exec(); } void PlayerWidget::addToPlayList(){ QModelIndexList sel = mView->selectionModel()->selectedRows(); if(sel.isEmpty()){ return; } foreach(QModelIndex i, sel){ int type = i.data(TypeRole).toInt(); if(type == Song){ addSong(i); }else{ recurse(i); } } QStandardItem *playListRoot = mPlayListModel->invisibleRootItem(); emit numFilesChanged(playListRoot->rowCount()); emit playListLengthChanged(mPlayListLength); } void PlayerWidget::addToPlayListAndClear(){ clearPlayList(); addToPlayList(); } void PlayerWidget::removeFromPlayList(){ QModelIndexList sel = mPlayListView->selectionModel()->selectedRows(); QList 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(){ QVector items; for(int i = 0; i < mPlayListModel->rowCount(); ++i){ QStandardItem *cur = mPlayListModel->item(i, 0)->clone(); items << cur; } std::random_shuffle(items.begin(), items.end()); mPlayListModel->clear(); mPlayListModel->setHorizontalHeaderLabels(QStringList() << "Title"); QStandardItem *root = mPlayListModel->invisibleRootItem(); foreach(QStandardItem *i, items){ root->appendRow(i); } } void PlayerWidget::randomPlay(){ 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, 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()); QStandardItem *item = new QStandardItem; item->setEditable(false); item->setFont(QFont("courier")); item->setText(display); item->setIcon(songIcon); item->setData(randomQ.value(2), FullPathRole); item->setData(randomQ.value(6), LengthRole); item->setData(randomQ.value(4), ArtistRole); item->setData(randomQ.value(5), AlbumRole); mPlayListLength += randomQ.value(6).toInt(); root->appendRow(item); } emit numFilesChanged(root->rowCount()); emit playListLengthChanged(mPlayListLength); } void PlayerWidget::playCurrent(const QModelIndex &index){ int isRemote = index.data(RemoteRole).toInt(); if(isRemote){ return; } mPlayer->stop(); QString fullPath = index.data(FullPathRole).toString(); play(fullPath); mTrayIcon->setIcon(QIcon(":/play.png")); mTrayIcon->setToolTip(mCurToolTip); } void PlayerWidget::printList(){ QModelIndexList sel = mView->selectionModel()->selectedRows(); if(sel.isEmpty()){ return; } QString output; foreach(QModelIndex i, sel){ QString fp = i.data(FullPathRole).toString(); TagLib::FileRef fr(fp.toUtf8()); if(!fr.isNull()){ quint16 track = fr.tag()->track(); QString title = QString::fromStdWString(fr.tag()->title().toWString()); int length = fr.audioProperties()->lengthInSeconds(); int minutes = length / 60; int seconds = length % 60; QString t = QString("%1 - %2 (%3:%4)\n").arg(track, 2, 10, QChar('0')).arg(title).arg(minutes, 2, 10, QChar('0')).arg(seconds, 2, 10, QChar('0')); output.append(t); } } mLeftTE->clear(); mLeftTE->setPlainText(output); } void PlayerWidget::searchMusicbrainzRight(){ QModelIndex idx = mPlayListView->selectionModel()->currentIndex(); if(!idx.isValid()){ return; } QString artist = idx.data(ArtistRole).toString(); QString album = idx.data(AlbumRole).toString(); mWebDownloader->fetchData(artist, album); } void PlayerWidget::searchMusicbrainzLeft(){ QModelIndex idx = mView->selectionModel()->currentIndex(); if(!idx.isValid()){ return; } QString artist = idx.data(ArtistRole).toString().toLower(); QString album = idx.data(AlbumRole).toString().toLower(); mWebDownloader->fetchData(artist, album); } void PlayerWidget::webDlDone(){ QString aId = mWebDownloader->artistId(); QString text; if(!aId.isEmpty()){ text.append(QString(tr("Musicbrainz:"))); text.append(QString(tr("")).arg(aId).arg(mWebDownloader->artist())); foreach(QString alId, mWebDownloader->data()){ text.append(QString("").arg(alId).arg(mWebDownloader->album())); } text.append("
Artist%2
Album%2
"); const QMap other = mWebDownloader->otherData(); text.append(QString(tr("All Albums (%1):
    ")).arg(QString::number(other.count()))); QStringList albums = other.values(); std::sort(albums.begin(), albums.end()); foreach(QString a, albums){ QString id = other.key(a); text.append(QString("
  • %2
  • ").arg(id).arg(a)); } text.append("
"); }else{ text.append(QString(tr("Musicbrainz: No match!
"))); const QMap alt = mWebDownloader->alternateArtists(); if(!alt.isEmpty()){ text.append(QString(tr("Guesses:
    "))); for(QMap::const_iterator it = alt.constBegin(); it != alt.constEnd(); ++it){ text.append(QString("
  • %2
  • ").arg(it.key()).arg(it.value())); } text.append("
"); } } mLeftTE->setText(text); } void PlayerWidget::filterFromPlaylist(){ QModelIndex idx = mPlayListView->selectionModel()->currentIndex(); if(!idx.isValid()){ return; } QString artist = idx.data(ArtistRole).toString(); mSearch->setText(artist); doFilter(); } void PlayerWidget::addWebRadio(){ WebRadioDialog dlg(this); dlg.exec(); QString desc = dlg.description(); QString url = dlg.url(); QSqlDatabase db = QSqlDatabase::database("beetplayerdb"); QSqlQuery wrQ(db); wrQ.prepare("INSERT INTO webradio (tdescription, turl) VALUES(:d, :u)"); wrQ.bindValue(":d", desc); wrQ.bindValue(":u", url); wrQ.exec(); } void PlayerWidget::doMetadataChange(const QString &key, const QVariant &value){ if(key == "Title"){ QString np = value.toString(); QRegularExpression re("\\s?-\\s?"); QStringList titleParts = np.split(re); QString artist(tr("n/a")), title(tr("n/a")); if(titleParts.count() >= 2){ artist = titleParts.at(0); title = titleParts.at(1); } QStandardItem *plRoot = mPlayListModel->invisibleRootItem(); QStandardItem *nowP = new QStandardItem; nowP->setFont(QFont("courier")); nowP->setEditable(false); nowP->setIcon(QIcon(":/dog_hood.png")); nowP->setData(1, RemoteRole); nowP->setData(artist, ArtistRole); nowP->setData(title, TitleRole); nowP->setText(np); plRoot->appendRow(nowP); mCurrentTE->clear(); mCurrentTE->append(QString("%1 %2").arg(tr("Artist:"), -20).arg(artist)); mCurrentTE->append(QString("%1 %2").arg(tr("Title:"), -20).arg(title)); mNowPlayingL->setText(np); }else{ if(mOtherMeta[key] != value){ mOtherMeta[key] = value; emit streamDataNeedsUpdate(); } } } void PlayerWidget::updateStreamData(){ QString retval; retval.append(""); retval.append(QString(tr("")).arg(mOtherMeta.value("Genre").toString())); retval.append(QString(tr("")).arg(mOtherMeta.value("Publisher").toString())); retval.append(QString(tr("")).arg(mOtherMeta.value("location").toString())); retval.append(QString(tr("")).arg(mOtherMeta.value("AudioCodec").toString())); retval.append("
Genre%1
Publisher%1
Location%1
Audio Codec%1
"); mLeftTE->setText(retval); } void PlayerWidget::play(const QString &fullPath){ disconnect(mPlayer, static_cast(&QMediaObject::metaDataChanged), this, &PlayerWidget::doMetadataChange); mLeftTE->clear(); mPlayer->setMedia(QUrl::fromLocalFile(fullPath)); TagLib::FileRef file(QString(fullPath).toUtf8()); fillWithText(mCurrentTE, file); if(file.isNull()){ return; } QString artist = QString::fromStdWString(file.tag()->artist().toWString()); QString album = QString::fromStdWString(file.tag()->album().toWString()); QString title = QString::fromStdWString(file.tag()->title().toWString()); mNowPlayingL->setText(title); int length = file.audioProperties()->lengthInSeconds(); mDurSecs = length; mSongSlider->setMinimum(0); mSongSlider->setMaximum(mDurSecs); int minutes = mDurSecs / 60; int seconds = mDurSecs % 60; mCurrentTE->append(QString("%1 %2:%3").arg(tr("Length:"), -20).arg(minutes, 2, 10, QChar('0')).arg(seconds, 2, 10, QChar('0'))); QString msg = QString("File: %1").arg(fullPath); emit message(msg); mCurWinTitle = QString(tr("%1 - [%2] - [%3] - [%4]")).arg(qApp->applicationName()).arg(artist).arg(album).arg(title); mCurToolTip = QString(tr("%1: [%2] - [%3]")).arg(artist).arg(album).arg(title); mTrayIcon->setToolTip(mCurToolTip); mTrayIcon->showMessage(QString(tr("Now Playing:")), mCurToolTip, QSystemTrayIcon::Information, 5000); emit setWinTitle(mCurWinTitle); mPlayer->play(); mPlayA->setChecked(true); emit playModeChanged(tr("Playing")); } void PlayerWidget::playUrl(const QString &url){ disconnect(mPlayer, static_cast(&QMediaObject::metaDataChanged), this, &PlayerWidget::doMetadataChange); connect(mPlayer, static_cast(&QMediaObject::metaDataChanged), this, &PlayerWidget::doMetadataChange); mPlayer->setMedia(QUrl(url)); mPlayer->play(); } void PlayerWidget::volumeChanged(int volume){ QString s = QString("%1 %").arg(volume, 3, 10, QChar('0')); mVolumePos->setText(s); if(!mStarting){ if(!mVolumeTimer->isActive()){ mVolumeTimer->start(500); } } } void PlayerWidget::mute(bool triggered){ mPlayer->setMuted(triggered); } void PlayerWidget::next(){ advance(1); } void PlayerWidget::previous(){ advance(-1); } void PlayerWidget::advance(int numSongs){ mPlayer->stop(); QModelIndexList sel = mPlayListView->selectionModel()->selectedRows(); if(sel.isEmpty()){ QStandardItem *root = mPlayListModel->invisibleRootItem(); if(root->rowCount() > 0){ QModelIndex first = mPlayListModel->index(0, 0); mPlayListView->selectionModel()->setCurrentIndex(first, QItemSelectionModel::ClearAndSelect); playCurrent(first); return; } return; } QModelIndex cur = sel.first(); QModelIndex nextIdx = mPlayListModel->index(cur.row() + numSongs, 0); if(nextIdx.isValid()){ mPlayListView->selectionModel()->setCurrentIndex(nextIdx, QItemSelectionModel::ClearAndSelect); playCurrent(nextIdx); } } void PlayerWidget::slide(int value){ qint64 newValue = value * 1000; mPlayer->setPosition(newValue); } void PlayerWidget::setPosition(qint64 pos){ int curPos = pos / 1000; mSongSlider->setValue(curPos); int minutes = curPos / 60; int seconds = curPos % 60; QString posString = QString("%1:%2").arg(minutes, 2, 10, QChar('0')).arg(seconds, 2, 10, QChar('0')); mPos->setText(posString); } void PlayerWidget::continuePlaying(QMediaPlayer::MediaStatus state){ if(state == QMediaPlayer::EndOfMedia){ next(); } } void PlayerWidget::expand(){ QModelIndexList sel = mView->selectionModel()->selectedRows(); foreach(QModelIndex i, sel){ mView->expand(i); expandRecursive(i); } } void PlayerWidget::expandRecursive(const QModelIndex &idx){ const QStandardItemModel *model = static_cast(idx.model()); QStandardItem *item = model->itemFromIndex(idx); for(int i = 0; i < item->rowCount(); ++i){ QModelIndex cur = model->indexFromItem(item->child(i, 0)); if(cur.isValid()){ mView->expand(cur); } } } void PlayerWidget::readSettings(){ mStarting = true; QSettings s; int vol = s.value("volume").toInt(); mVolumeSlider->setValue(vol); QString dir = s.value("folderdir", QDir::homePath()).toString(); mCurDir = dir; mDir->setText(mCurDir); mStarting = false; } void PlayerWidget::writeSettings(){ QSettings s; s.setValue("volume", mVolumeSlider->value()); s.setValue("folderdir", mCurDir); } void PlayerWidget::aboutDlg(){ QString title = QString(tr("About %1")).arg(qApp->applicationName()); QString aboutText = QString(tr("

%1: A basic music player without all the fancy stuff implemented in eg. Amarok

")).arg(qApp->applicationName()); aboutText.append(""); aboutText.append(QString(tr("")).arg(QChar(0x26A4))); aboutText.append(QString(tr("")).arg(qApp->organizationName())); aboutText.append(QString(tr("")).arg(qApp->applicationVersion())); aboutText.append(QString(tr(""))); aboutText.append(tr("")); aboutText.append(tr("
AuthorSissy herself %1
Organization%1
Version%1
Build:/* __debug build__ */
DependsQt PostgreSQL TagLib
LicenseGPL 2 or later
")); QMessageBox::about(this, title, aboutText); }