00001
00002 #include <sys/types.h>
00003 #include <sys/stat.h>
00004 #include <stdlib.h>
00005 #include <unistd.h>
00006
00007
00008 #include <QDir>
00009 #include <QApplication>
00010 #include <QRegExp>
00011
00012
00013 #include <mythcontext.h>
00014 #include <mythplugin.h>
00015 #include <mythmediamonitor.h>
00016 #include <mythdbcon.h>
00017 #include <mythdb.h>
00018 #include <mythpluginapi.h>
00019 #include <mythversion.h>
00020 #include <myththemedmenu.h>
00021 #include <compat.h>
00022 #include <mythuihelper.h>
00023 #include <mythprogressdialog.h>
00024 #include <lcddevice.h>
00025
00026
00027 #include "decoder.h"
00028 #include "metadata.h"
00029 #include "playlisteditorview.h"
00030 #include "playlistview.h"
00031 #include "playlistcontainer.h"
00032 #include "dbcheck.h"
00033 #include "filescanner.h"
00034 #include "musicplayer.h"
00035 #include "config.h"
00036 #include "mainvisual.h"
00037 #include "generalsettings.h"
00038 #include "playersettings.h"
00039 #include "visualizationsettings.h"
00040 #include "importsettings.h"
00041 #include "ratingsettings.h"
00042 #include "importmusic.h"
00043
00044 #ifdef HAVE_CDIO
00045 #include "cdrip.h"
00046 #endif
00047
00048
00049
00050 QString gCDdevice;
00051
00055 static QString chooseCD(void)
00056 {
00057 if (gCDdevice.length())
00058 return gCDdevice;
00059
00060 #ifdef Q_OS_MAC
00061 return MediaMonitor::GetMountPath(MediaMonitor::defaultCDdevice());
00062 #endif
00063
00064 return MediaMonitor::defaultCDdevice();
00065 }
00066
00067 static void SavePending(int pending)
00068 {
00069
00070
00071
00072 MSqlQuery query(MSqlQuery::InitCon());
00073 query.prepare("SELECT * FROM settings "
00074 "WHERE value = :LASTPUSH "
00075 "AND hostname = :HOST ;");
00076 query.bindValue(":LASTPUSH", "LastMusicPlaylistPush");
00077 query.bindValue(":HOST", gCoreContext->GetHostName());
00078
00079 if (query.exec() && query.size() == 0)
00080 {
00081
00082 query.prepare("INSERT INTO settings (value,data,hostname) VALUES "
00083 "(:LASTPUSH, :DATA, :HOST );");
00084 query.bindValue(":LASTPUSH", "LastMusicPlaylistPush");
00085 query.bindValue(":DATA", pending);
00086 query.bindValue(":HOST", gCoreContext->GetHostName());
00087
00088 if (!query.exec())
00089 MythDB::DBError("SavePending - inserting LastMusicPlaylistPush",
00090 query);
00091 }
00092 else if (query.size() == 1)
00093 {
00094
00095 query.prepare("UPDATE settings SET data = :DATA "
00096 "WHERE value = :LASTPUSH "
00097 "AND hostname = :HOST ;");
00098 query.bindValue(":DATA", pending);
00099 query.bindValue(":LASTPUSH", "LastMusicPlaylistPush");
00100 query.bindValue(":HOST", gCoreContext->GetHostName());
00101
00102 if (!query.exec())
00103 MythDB::DBError("SavePending - updating LastMusicPlaylistPush",
00104 query);
00105 }
00106 else
00107 {
00108
00109
00110
00111 query.prepare("DELETE FROM settings WHERE "
00112 "WHERE value = :LASTPUSH "
00113 "AND hostname = :HOST ;");
00114 query.bindValue(":LASTPUSH", "LastMusicPlaylistPush");
00115 query.bindValue(":HOST", gCoreContext->GetHostName());
00116 if (!query.exec())
00117 MythDB::DBError("SavePending - deleting LastMusicPlaylistPush",
00118 query);
00119
00120 query.prepare("INSERT INTO settings (value,data,hostname) VALUES "
00121 "(:LASTPUSH, :DATA, :HOST );");
00122 query.bindValue(":LASTPUSH", "LastMusicPlaylistPush");
00123 query.bindValue(":DATA", pending);
00124 query.bindValue(":HOST", gCoreContext->GetHostName());
00125
00126 if (!query.exec())
00127 MythDB::DBError("SavePending - inserting LastMusicPlaylistPush (2)",
00128 query);
00129 }
00130 }
00131
00132 static void loadMusic()
00133 {
00134
00135 if (gMusicData->initialized)
00136 return;
00137
00138 MSqlQuery count_query(MSqlQuery::InitCon());
00139
00140 bool musicdata_exists = false;
00141 if (count_query.exec("SELECT COUNT(*) FROM music_songs;"))
00142 {
00143 if(count_query.next() &&
00144 0 != count_query.value(0).toInt())
00145 {
00146 musicdata_exists = true;
00147 }
00148 }
00149
00150 QString startdir = gCoreContext->GetSetting("MusicLocation");
00151 startdir = QDir::cleanPath(startdir);
00152 if (!startdir.isEmpty() && !startdir.endsWith("/"))
00153 startdir += "/";
00154
00155 gMusicData->musicDir = startdir;
00156
00157 Decoder::SetLocationFormatUseTags();
00158
00159
00160
00161
00162 if (!gMusicData->musicDir.isEmpty() && !musicdata_exists)
00163 {
00164 FileScanner *fscan = new FileScanner();
00165 fscan->SearchDir(startdir);
00166 delete fscan;
00167 }
00168
00169 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
00170 QString message = QObject::tr("Loading Music. Please wait ...");
00171
00172 MythUIBusyDialog *busy = new MythUIBusyDialog(message, popupStack,
00173 "musicscanbusydialog");
00174 if (busy->Create())
00175 popupStack->AddScreen(busy, false);
00176 else
00177 busy = NULL;
00178
00179
00180 Metadata::setArtistAndTrackFormats();
00181
00182 AllMusic *all_music = new AllMusic();
00183
00184
00185 PlaylistContainer *all_playlists = new PlaylistContainer(
00186 all_music, gCoreContext->GetHostName());
00187
00188 gMusicData->all_playlists = all_playlists;
00189 gMusicData->all_music = all_music;
00190 gMusicData->initialized = true;
00191
00192 while (!gMusicData->all_playlists->doneLoading() || !gMusicData->all_music->doneLoading())
00193 {
00194 qApp->processEvents();
00195 usleep(50000);
00196 }
00197 gMusicData->all_playlists->postLoad();
00198
00199 gPlayer->loadPlaylist();
00200
00201 if (busy)
00202 busy->Close();
00203
00204 }
00205
00206 static void startPlayback(void)
00207 {
00208 loadMusic();
00209
00210 MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack();
00211
00212 PlaylistView *view = new PlaylistView(mainStack);
00213
00214 if (view->Create())
00215 mainStack->AddScreen(view);
00216 else
00217 delete view;
00218 }
00219
00220 static void startDatabaseTree(void)
00221 {
00222 loadMusic();
00223
00224 MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack();
00225
00226 QString lastView = gCoreContext->GetSetting("MusicPlaylistEditorView", "tree");
00227 PlaylistEditorView *view = new PlaylistEditorView(mainStack, lastView);
00228
00229 if (view->Create())
00230 mainStack->AddScreen(view);
00231 else
00232 delete view;
00233 }
00234
00235 static void startRipper(void)
00236 {
00237 loadMusic();
00238
00239 #if defined HAVE_CDIO
00240 MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack();
00241
00242 Ripper *rip = new Ripper(mainStack, chooseCD());
00243
00244 if (rip->Create())
00245 {
00246 mainStack->AddScreen(rip);
00247 QObject::connect(rip, SIGNAL(ripFinished()),
00248 gMusicData, SLOT(reloadMusic()),
00249 Qt::QueuedConnection);
00250 }
00251 else
00252 delete rip;
00253 #endif
00254 }
00255
00256 static void runScan(void)
00257 {
00258
00259 if (gMusicData->musicDir.isEmpty())
00260 {
00261 QString startdir = gCoreContext->GetSetting("MusicLocation");
00262 startdir = QDir::cleanPath(startdir);
00263 if (!startdir.isEmpty() && !startdir.endsWith("/"))
00264 startdir += "/";
00265
00266 gMusicData->musicDir = startdir;
00267 }
00268
00269
00270 if (gMusicData->musicDir.isEmpty())
00271 {
00272 ShowOkPopup(QObject::tr("You need to tell me where to find your music on the "
00273 "'General Settings' page of MythMusic's settings pages."));
00274 return;
00275 }
00276
00277 if (!QFile::exists(gMusicData->musicDir))
00278 {
00279 ShowOkPopup(QObject::tr("Can't find your music directory. Have you set it correctly on the "
00280 "'General Settings' page of MythMusic's settings pages?"));
00281 return;
00282 }
00283
00284 LOG(VB_GENERAL, LOG_INFO, QString("Scanning '%1' for music files").arg(gMusicData->musicDir));
00285
00286 FileScanner *fscan = new FileScanner();
00287 fscan->SearchDir(gMusicData->musicDir);
00288
00289
00290 if (gMusicData->all_music && gMusicData->all_music->cleanOutThreads())
00291 gMusicData->all_music->save();
00292
00293 if (gMusicData->all_playlists && gMusicData->all_playlists->cleanOutThreads())
00294 {
00295 gMusicData->all_playlists->save();
00296 int x = gMusicData->all_playlists->getPending();
00297 SavePending(x);
00298 }
00299
00300
00301 gPlayer->stop(true);
00302 delete gMusicData;
00303
00304 gMusicData = new MusicData;
00305 loadMusic();
00306
00307 delete fscan;
00308 }
00309
00310 static void startImport(void)
00311 {
00312 loadMusic();
00313
00314 MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack();
00315
00316 ImportMusicDialog *import = new ImportMusicDialog(mainStack);
00317
00318 if (import->Create())
00319 {
00320 mainStack->AddScreen(import);
00321 QObject::connect(import, SIGNAL(importFinished()),
00322 gMusicData, SLOT(reloadMusic()),
00323 Qt::QueuedConnection);
00324 }
00325 else
00326 delete import;
00327 }
00328
00329 static void MusicCallback(void *data, QString &selection)
00330 {
00331 (void) data;
00332
00333 QString sel = selection.toLower();
00334 if (sel == "music_create_playlist")
00335 startDatabaseTree();
00336 else if (sel == "music_play")
00337 startPlayback();
00338 else if (sel == "music_rip")
00339 {
00340 startRipper();
00341 }
00342 else if (sel == "music_import")
00343 {
00344 startImport();
00345 }
00346 else if (sel == "settings_scan")
00347 {
00348 runScan();
00349 }
00350 else if (sel == "settings_general")
00351 {
00352 MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack();
00353 GeneralSettings *gs = new GeneralSettings(mainStack, "general settings");
00354
00355 if (gs->Create())
00356 mainStack->AddScreen(gs);
00357 else
00358 delete gs;
00359 }
00360 else if (sel == "settings_player")
00361 {
00362 MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack();
00363 PlayerSettings *ps = new PlayerSettings(mainStack, "player settings");
00364
00365 if (ps->Create())
00366 mainStack->AddScreen(ps);
00367 else
00368 delete ps;
00369 }
00370 else if (sel == "settings_rating")
00371 {
00372 MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack();
00373 RatingSettings *rs = new RatingSettings(mainStack, "rating settings");
00374
00375 if (rs->Create())
00376 mainStack->AddScreen(rs);
00377 else
00378 delete rs;
00379 }
00380 else if (sel == "settings_visualization")
00381 {
00382
00383 MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack();
00384 VisualizationSettings *vs = new VisualizationSettings(mainStack, "visualization settings");
00385
00386 if (vs->Create())
00387 mainStack->AddScreen(vs);
00388 else
00389 delete vs;
00390 }
00391 else if (sel == "settings_import")
00392 {
00393 MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack();
00394 ImportSettings *is = new ImportSettings(mainStack, "import settings");
00395
00396 if (is->Create())
00397 mainStack->AddScreen(is);
00398 else
00399 delete is;
00400 }
00401 }
00402
00403 static int runMenu(QString which_menu)
00404 {
00405 QString themedir = GetMythUI()->GetThemeDir();
00406
00407 MythThemedMenu *diag = new MythThemedMenu(
00408 themedir, which_menu, GetMythMainWindow()->GetMainStack(),
00409 "music menu");
00410
00411 diag->setCallback(MusicCallback, NULL);
00412 diag->setKillable();
00413
00414 if (diag->foundTheme())
00415 {
00416 if (LCD *lcd = LCD::Get())
00417 {
00418 lcd->switchToTime();
00419 }
00420 GetMythMainWindow()->GetMainStack()->AddScreen(diag);
00421 return 0;
00422 }
00423 else
00424 {
00425 LOG(VB_GENERAL, LOG_ERR, QString("Couldn't find menu %1 or theme %2")
00426 .arg(which_menu).arg(themedir));
00427 delete diag;
00428 return -1;
00429 }
00430 }
00431
00432 static void runMusicPlayback(void)
00433 {
00434 GetMythUI()->AddCurrentLocation("playmusic");
00435 startPlayback();
00436 GetMythUI()->RemoveCurrentLocation();
00437 }
00438
00439 static void runMusicSelection(void)
00440 {
00441 GetMythUI()->AddCurrentLocation("musicplaylists");
00442 startDatabaseTree();
00443 GetMythUI()->RemoveCurrentLocation();
00444 }
00445
00446 static void runRipCD(void)
00447 {
00448 loadMusic();
00449
00450 #if defined HAVE_CDIO
00451 MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack();
00452
00453 Ripper *rip = new Ripper(mainStack, chooseCD());
00454
00455 if (rip->Create())
00456 mainStack->AddScreen(rip);
00457 else
00458 delete rip;
00459
00460 QObject::connect(rip, SIGNAL(ripFinished()),
00461 gMusicData, SLOT(reloadMusic()),
00462 Qt::QueuedConnection);
00463 #endif
00464 }
00465
00466 static void showMiniPlayer(void)
00467 {
00468 if (!gMusicData->all_music)
00469 return;
00470
00471
00472 if (!gPlayer->hasClient())
00473 gPlayer->showMiniPlayer();
00474 }
00475
00476 #ifdef FIXME // the only call is likewise commented out and needs fixing
00477 static void handleMedia(MythMediaDevice *cd)
00478 {
00479
00480 if (gPlayer->isPlaying())
00481 {
00482 LOG(VB_GENERAL, LOG_NOTICE, "Got a media changed event but ignoring since "
00483 "the music player is already playing");
00484 return;
00485 }
00486 else
00487 LOG(VB_GENERAL, LOG_NOTICE, "Got a media changed event");
00488
00489
00490
00491
00492 if (!cd)
00493 return;
00494
00495 if (cd->isUsable())
00496 {
00497 QString newDevice;
00498
00499 #ifdef Q_OS_MAC
00500 newDevice = cd->getMountPath();
00501 #else
00502 newDevice = cd->getDevicePath();
00503 #endif
00504
00505 if (gCDdevice.length() && gCDdevice != newDevice)
00506 {
00507
00508
00509
00510 gCDdevice = QString::null;
00511 LOG(VB_MEDIA, LOG_INFO, "MythMusic: Forgetting existing CD");
00512 }
00513 else
00514 {
00515 gCDdevice = newDevice;
00516 LOG(VB_MEDIA, LOG_INFO,
00517 "MythMusic: Storing CD device " + gCDdevice);
00518 }
00519 }
00520 else
00521 {
00522 gCDdevice = QString::null;
00523 return;
00524 }
00525
00526 GetMythMainWindow()->JumpTo("Main Menu");
00527
00528 if (gCoreContext->GetNumSetting("AutoPlayCD", 0))
00529 {
00530
00531 if (gMusicData->all_music)
00532 gMusicData->all_music->clearCDData();
00533 if (gMusicData->all_playlists)
00534 gMusicData->all_playlists->clearCDList();
00535
00536 runMusicPlayback();
00537 }
00538 else
00539 mythplugin_run();
00540 }
00541 #endif
00542
00543 static void setupKeys(void)
00544 {
00545 REG_JUMP(QT_TRANSLATE_NOOP("MythControls", "Play music"),
00546 "", "", runMusicPlayback);
00547 REG_JUMP(QT_TRANSLATE_NOOP("MythControls", "Select music playlists"),
00548 "", "", runMusicSelection);
00549 REG_JUMP(QT_TRANSLATE_NOOP("MythControls", "Rip CD"),
00550 "", "", runRipCD);
00551 REG_JUMP(QT_TRANSLATE_NOOP("MythControls", "Scan music"),
00552 "", "", runScan);
00553 REG_JUMPEX(QT_TRANSLATE_NOOP("MythControls", "Show Music Miniplayer"),
00554 "", "", showMiniPlayer, false);
00555
00556 REG_KEY("Music", "NEXTTRACK", QT_TRANSLATE_NOOP("MythControls",
00557 "Move to the next track"), ">,.,Z,End");
00558 REG_KEY("Music", "PREVTRACK", QT_TRANSLATE_NOOP("MythControls",
00559 "Move to the previous track"), ",,<,Q,Home");
00560 REG_KEY("Music", "FFWD", QT_TRANSLATE_NOOP("MythControls",
00561 "Fast forward"), "PgDown");
00562 REG_KEY("Music", "RWND", QT_TRANSLATE_NOOP("MythControls",
00563 "Rewind"), "PgUp");
00564 REG_KEY("Music", "PAUSE", QT_TRANSLATE_NOOP("MythControls",
00565 "Pause/Start playback"), "P");
00566 REG_KEY("Music", "PLAY", QT_TRANSLATE_NOOP("MythControls",
00567 "Start playback"), "");
00568 REG_KEY("Music", "STOP", QT_TRANSLATE_NOOP("MythControls",
00569 "Stop playback"), "O");
00570 REG_KEY("Music", "VOLUMEDOWN", QT_TRANSLATE_NOOP("MythControls",
00571 "Volume down"), "[,{,F10,Volume Down");
00572 REG_KEY("Music", "VOLUMEUP", QT_TRANSLATE_NOOP("MythControls",
00573 "Volume up"), "],},F11,Volume Up");
00574 REG_KEY("Music", "MUTE", QT_TRANSLATE_NOOP("MythControls",
00575 "Mute"), "|,\\,F9,Volume Mute");
00576 REG_KEY("Music", "TOGGLEUPMIX",QT_TRANSLATE_NOOP("MythControls",
00577 "Toggle audio upmixer"), "Ctrl+U");
00578 REG_KEY("Music", "CYCLEVIS", QT_TRANSLATE_NOOP("MythControls",
00579 "Cycle visualizer mode"), "6");
00580 REG_KEY("Music", "BLANKSCR", QT_TRANSLATE_NOOP("MythControls",
00581 "Blank screen"), "5");
00582 REG_KEY("Music", "THMBUP", QT_TRANSLATE_NOOP("MythControls",
00583 "Increase rating"), "9");
00584 REG_KEY("Music", "THMBDOWN", QT_TRANSLATE_NOOP("MythControls",
00585 "Decrease rating"), "7");
00586 REG_KEY("Music", "REFRESH", QT_TRANSLATE_NOOP("MythControls",
00587 "Refresh music tree"), "8");
00588 REG_KEY("Music", "SPEEDUP", QT_TRANSLATE_NOOP("MythControls",
00589 "Increase Play Speed"), "W");
00590 REG_KEY("Music", "SPEEDDOWN", QT_TRANSLATE_NOOP("MythControls",
00591 "Decrease Play Speed"), "X");
00592 REG_KEY("Music", "MARK", QT_TRANSLATE_NOOP("MythControls",
00593 "Toggle track selection"), "T");
00594 REG_KEY("Music", "TOGGLESHUFFLE", QT_TRANSLATE_NOOP("MythControls",
00595 "Toggle shuffle mode"), "");
00596 REG_KEY("Music", "TOGGLEREPEAT", QT_TRANSLATE_NOOP("MythControls",
00597 "Toggle repeat mode"), "");
00598
00599
00600 REG_KEY("Music", "SWITCHTOPLAYLIST", QT_TRANSLATE_NOOP("MythControls",
00601 "Switch to the current playlist view"), "");
00602 REG_KEY("Music", "SWITCHTOPLAYLISTEDITORTREE", QT_TRANSLATE_NOOP("MythControls",
00603 "Switch to the playlist editor tree view"), "");
00604 REG_KEY("Music", "SWITCHTOPLAYLISTEDITORGALLERY", QT_TRANSLATE_NOOP("MythControls",
00605 "Switch to the playlist editor gallery view"), "");
00606 REG_KEY("Music", "SWITCHTOSEARCH", QT_TRANSLATE_NOOP("MythControls",
00607 "Switch to the search view"), "");
00608 REG_KEY("Music", "SWITCHTOVISUALISER", QT_TRANSLATE_NOOP("MythControls",
00609 "Switch to the fullscreen visualiser view"), "");
00610
00611
00612 #ifdef FIXME
00613
00614
00615 REG_MEDIA_HANDLER(QT_TRANSLATE_NOOP("MythControls",
00616 "MythMusic Media Handler 1/2"), "", "", handleMedia,
00617 MEDIATYPE_AUDIO | MEDIATYPE_MIXED, QString::null);
00618 REG_MEDIA_HANDLER(QT_TRANSLATE_NOOP("MythControls",
00619 "MythMusic Media Handler 2/2"), "", "", handleMedia,
00620 MEDIATYPE_MMUSIC, "mp3,mp2,ogg,oga,flac,wma,wav,ac3,"
00621 "oma,omg,atp,ra,dts,aac,m4a,aa3,tta,"
00622 "mka,aiff,swa,wv");
00623 #endif
00624 }
00625
00626 int mythplugin_init(const char *libversion)
00627 {
00628 if (!gContext->TestPopupVersion("mythmusic", libversion,
00629 MYTH_BINARY_VERSION))
00630 return -1;
00631
00632 gCoreContext->ActivateSettingsCache(false);
00633 bool upgraded = UpgradeMusicDatabaseSchema();
00634 gCoreContext->ActivateSettingsCache(true);
00635
00636 if (!upgraded)
00637 {
00638 LOG(VB_GENERAL, LOG_ERR,
00639 "Couldn't upgrade music database schema, exiting.");
00640 return -1;
00641 }
00642
00643 setupKeys();
00644
00645 Decoder::SetLocationFormatUseTags();
00646
00647 gPlayer = new MusicPlayer(NULL, chooseCD());
00648 gMusicData = new MusicData();
00649
00650 return 0;
00651 }
00652
00653
00654 int mythplugin_run(void)
00655 {
00656 return runMenu("musicmenu.xml");
00657 }
00658
00659 int mythplugin_config(void)
00660 {
00661 Decoder::SetLocationFormatUseTags();
00662
00663 return runMenu("music_settings.xml");
00664 }
00665
00666 void mythplugin_destroy(void)
00667 {
00668 gPlayer->stop(true);
00669
00670
00671
00672 if (gMusicData->all_music && gMusicData->all_music->cleanOutThreads())
00673 {
00674 gMusicData->all_music->save();
00675 }
00676
00677 if (gMusicData->all_playlists && gMusicData->all_playlists->cleanOutThreads())
00678 {
00679 gMusicData->all_playlists->save();
00680 int x = gMusicData->all_playlists->getPending();
00681 SavePending(x);
00682 }
00683
00684 delete gPlayer;
00685
00686 delete gMusicData;
00687 }