00001
00002 #include <sys/types.h>
00003 #include <sys/stat.h>
00004 #include <stdlib.h>
00005 #include <unistd.h>
00006
00007
00008 #include <qdir.h>
00009 #include <qapplication.h>
00010 #include <qregexp.h>
00011
00012
00013 #include <mythtv/mythcontext.h>
00014 #include <mythtv/mythplugin.h>
00015 #include <mythtv/mythmediamonitor.h>
00016 #include <mythtv/mythdbcon.h>
00017 #include <mythtv/mythpluginapi.h>
00018 #include <mythtv/libmythui/myththemedmenu.h>
00019 #include <mythtv/compat.h>
00020
00021
00022 #include "decoder.h"
00023 #include "metadata.h"
00024 #include "maddecoder.h"
00025 #include "vorbisdecoder.h"
00026 #include "databasebox.h"
00027 #include "playbackbox.h"
00028 #include "playlist.h"
00029 #include "globalsettings.h"
00030 #include "dbcheck.h"
00031 #include "filescanner.h"
00032 #include "musicplayer.h"
00033 #include "config.h"
00034 #ifndef USING_MINGW
00035 #include "cdrip.h"
00036 #include "importmusic.h"
00037 #endif
00038
00039
00040 #ifdef HAVE_CDAUDIO
00041 #include <cdaudio.h>
00042 #endif
00043
00044
00045 QString gCDdevice;
00046
00050 QString chooseCD(void)
00051 {
00052 if (gCDdevice.length())
00053 return gCDdevice;
00054
00055 return MediaMonitor::defaultCDdevice();
00056 }
00057
00058 void CheckFreeDBServerFile(void)
00059 {
00060 char filename[1024];
00061 if (getenv("HOME") == NULL)
00062 {
00063 VERBOSE(VB_IMPORTANT, "main.o: You don't have a HOME environment variable. CD lookup will almost certainly not work.");
00064 return;
00065 }
00066 sprintf(filename, "%s/.cdserverrc", getenv("HOME"));
00067
00068 QFile file(filename);
00069
00070 if (!file.exists())
00071 {
00072 #ifdef HAVE_CDAUDIO
00073 struct cddb_conf cddbconf;
00074 struct cddb_serverlist list;
00075 struct cddb_host proxy_host;
00076
00077 memset(&cddbconf, 0, sizeof(cddbconf));
00078
00079 cddbconf.conf_access = CDDB_ACCESS_REMOTE;
00080 list.list_len = 1;
00081 strncpy(list.list_host[0].host_server.server_name,
00082 "freedb.freedb.org", 256);
00083 strncpy(list.list_host[0].host_addressing, "~cddb/cddb.cgi", 256);
00084 list.list_host[0].host_server.server_port = 80;
00085 list.list_host[0].host_protocol = CDDB_MODE_HTTP;
00086
00087 cddb_write_serverlist(cddbconf, list, proxy_host.host_server);
00088 #endif
00089 }
00090 }
00091
00092 void SavePending(int pending)
00093 {
00094
00095
00096
00097 MSqlQuery query(MSqlQuery::InitCon());
00098 query.prepare("SELECT * FROM settings "
00099 "WHERE value = :LASTPUSH "
00100 "AND hostname = :HOST ;");
00101 query.bindValue(":LASTPUSH", "LastMusicPlaylistPush");
00102 query.bindValue(":HOST", gContext->GetHostName());
00103
00104 if (query.exec() && query.size() == 0)
00105 {
00106
00107 query.prepare("INSERT INTO settings (value,data,hostname) VALUES "
00108 "(:LASTPUSH, :DATA, :HOST );");
00109 query.bindValue(":LASTPUSH", "LastMusicPlaylistPush");
00110 query.bindValue(":DATA", pending);
00111 query.bindValue(":HOST", gContext->GetHostName());
00112
00113 query.exec();
00114 }
00115 else if (query.size() == 1)
00116 {
00117
00118 query.prepare("UPDATE settings SET data = :DATA WHERE "
00119 "WHERE value = :LASTPUSH "
00120 "AND hostname = :HOST ;");
00121 query.bindValue(":DATA", pending);
00122 query.bindValue(":LASTPUSH", "LastMusicPlaylistPush");
00123 query.bindValue(":HOST", gContext->GetHostName());
00124
00125 query.exec();
00126 }
00127 else
00128 {
00129
00130
00131
00132 query.prepare("DELETE FROM settings WHERE "
00133 "WHERE value = :LASTPUSH "
00134 "AND hostname = :HOST ;");
00135 query.bindValue(":LASTPUSH", "LastMusicPlaylistPush");
00136 query.bindValue(":HOST", gContext->GetHostName());
00137 query.exec();
00138
00139 query.prepare("INSERT INTO settings (value,data,hostname) VALUES "
00140 "(:LASTPUSH, :DATA, :HOST );");
00141 query.bindValue(":LASTPUSH", "LastMusicPlaylistPush");
00142 query.bindValue(":DATA", pending);
00143 query.bindValue(":HOST", gContext->GetHostName());
00144
00145 query.exec();
00146 }
00147 }
00148
00149 void startPlayback(void)
00150 {
00151 PlaybackBoxMusic *pbb;
00152 pbb = new PlaybackBoxMusic(gContext->GetMainWindow(),
00153 "music_play", "music-", chooseCD(), "music_playback");
00154 qApp->unlock();
00155 pbb->exec();
00156 qApp->lock();
00157
00158 qApp->processEvents();
00159
00160 delete pbb;
00161 }
00162
00163 void startDatabaseTree(void)
00164 {
00165 DatabaseBox *dbbox = new DatabaseBox(gContext->GetMainWindow(),
00166 chooseCD(), "music_select", "music-", "music database");
00167 qApp->unlock();
00168 dbbox->exec();
00169 qApp->lock();
00170
00171 delete dbbox;
00172
00173 gPlayer->constructPlaylist();
00174 }
00175
00176 bool startRipper(void)
00177 {
00178 #ifndef USING_MINGW
00179 Ripper rip(chooseCD(), gContext->GetMainWindow(), "cd ripper");
00180
00181 qApp->unlock();
00182 rip.exec();
00183 qApp->lock();
00184
00185 if (rip.somethingWasRipped())
00186 return true;
00187 #endif
00188 return false;
00189 }
00190
00191 bool startImport(void)
00192 {
00193 #ifndef USING_MINGW
00194 ImportMusicDialog import(gContext->GetMainWindow(), "import music");
00195
00196 qApp->unlock();
00197 import.exec();
00198 qApp->lock();
00199
00200 if (import.somethingWasImported())
00201 return true;
00202 #endif
00203 return false;
00204 }
00205
00206 void RebuildMusicTree(void)
00207 {
00208 if (!gMusicData->all_music || !gMusicData->all_playlists)
00209 return;
00210
00211 MythBusyDialog *busy = new MythBusyDialog(
00212 QObject::tr("Rebuilding music tree"));
00213
00214 busy->start();
00215 gMusicData->all_music->startLoading();
00216 while (!gMusicData->all_music->doneLoading())
00217 {
00218 qApp->processEvents();
00219 usleep(50000);
00220 }
00221 gMusicData->all_playlists->postLoad();
00222 busy->Close();
00223 busy->deleteLater();
00224 }
00225
00226 static void postMusic(void);
00227
00228 void MusicCallback(void *data, QString &selection)
00229 {
00230 (void) data;
00231
00232 QString sel = selection.lower();
00233 if (sel == "music_create_playlist")
00234 startDatabaseTree();
00235 else if (sel == "music_play")
00236 startPlayback();
00237 else if (sel == "music_rip")
00238 {
00239 if (startRipper())
00240 {
00241
00242
00243
00244 RebuildMusicTree();
00245 }
00246 }
00247 else if (sel == "music_import")
00248 {
00249 if (startImport())
00250 RebuildMusicTree();
00251 }
00252 else if (sel == "settings_scan")
00253 {
00254 if ("" != gMusicData->startdir)
00255 {
00256 FileScanner *fscan = new FileScanner();
00257 fscan->SearchDir(gMusicData->startdir);
00258 RebuildMusicTree();
00259 }
00260 }
00261 else if (sel == "music_set_general")
00262 {
00263 MusicGeneralSettings settings;
00264 settings.exec();
00265 }
00266 else if (sel == "music_set_player")
00267 {
00268 MusicPlayerSettings settings;
00269 settings.exec();
00270 }
00271 else if (sel == "music_set_ripper")
00272 {
00273 MusicRipperSettings settings;
00274 settings.exec();
00275 }
00276 else if (sel == "exiting_menu")
00277 {
00278 if (gMusicData)
00279 {
00280 if (gMusicData->runPost)
00281 postMusic();
00282 }
00283 }
00284 }
00285
00286 void runMenu(QString which_menu)
00287 {
00288 QString themedir = gContext->GetThemeDir();
00289
00290 MythThemedMenu *diag = new MythThemedMenu(themedir.ascii(), which_menu,
00291 GetMythMainWindow()->GetMainStack(),
00292 "music menu");
00293
00294 diag->setCallback(MusicCallback, NULL);
00295 diag->setKillable();
00296
00297 if (diag->foundTheme())
00298 {
00299 if (class LCD * lcd = LCD::Get())
00300 {
00301 lcd->switchToTime();
00302 }
00303 GetMythMainWindow()->GetMainStack()->AddScreen(diag);
00304 }
00305 else
00306 {
00307 VERBOSE(VB_IMPORTANT, QString("Couldn't find theme %1").arg(themedir));
00308 delete diag;
00309 }
00310 }
00311
00312 void runMusicPlayback(void);
00313 void runMusicSelection(void);
00314 void runRipCD(void);
00315 void runScan(void);
00316 void showMiniPlayer(void);
00317
00318 void handleMedia(MythMediaDevice *cd)
00319 {
00320
00321
00322
00323 if (!cd)
00324 return;
00325
00326 if (cd->isUsable())
00327 {
00328 QString newDevice;
00329
00330 #ifdef Q_OS_MAC
00331 newDevice = cd->getMountPath();
00332 #else
00333 newDevice = cd->getDevicePath();
00334 #endif
00335
00336 if (gCDdevice.length() && gCDdevice != newDevice)
00337 {
00338
00339
00340
00341 gCDdevice = QString::null;
00342 VERBOSE(VB_MEDIA, "MythMusic: Forgetting existing CD");
00343 }
00344 else
00345 {
00346 gCDdevice = newDevice;
00347 VERBOSE(VB_MEDIA, "MythMusic: Storing CD device " + gCDdevice);
00348 }
00349 }
00350 else
00351 {
00352 gCDdevice = QString::null;
00353 return;
00354 }
00355
00356 if (gContext->GetNumSetting("AutoPlayCD", 0))
00357 runMusicPlayback();
00358 else
00359 mythplugin_run();
00360 }
00361
00362 void setupKeys(void)
00363 {
00364 REG_JUMP("Play music", "", "", runMusicPlayback);
00365 REG_JUMP("Select music playlists", "", "", runMusicSelection);
00366 REG_JUMP("Rip CD", "", "", runRipCD);
00367 REG_JUMP("Scan music", "", "", runScan);
00368 REG_JUMPEX("Show Music Miniplayer","", "", showMiniPlayer, false);
00369
00370 REG_KEY("Music", "DELETE", "Delete track from playlist", "D");
00371 REG_KEY("Music", "NEXTTRACK", "Move to the next track", ">,.,Z,End");
00372 REG_KEY("Music", "PREVTRACK", "Move to the previous track", ",,<,Q,Home");
00373 REG_KEY("Music", "FFWD", "Fast forward", "PgDown");
00374 REG_KEY("Music", "RWND", "Rewind", "PgUp");
00375 REG_KEY("Music", "PAUSE", "Pause/Start playback", "P");
00376 REG_KEY("Music", "PLAY", "Start playback", "");
00377 REG_KEY("Music", "STOP", "Stop playback", "O");
00378 REG_KEY("Music", "VOLUMEDOWN", "Volume down", "[,{,F10,Volume Down");
00379 REG_KEY("Music", "VOLUMEUP", "Volume up", "],},F11,Volume Up");
00380 REG_KEY("Music", "MUTE", "Mute", "|,\\,F9,Volume Mute");
00381 REG_KEY("Music", "CYCLEVIS", "Cycle visualizer mode", "6");
00382 REG_KEY("Music", "BLANKSCR", "Blank screen", "5");
00383 REG_KEY("Music", "THMBUP", "Increase rating", "9");
00384 REG_KEY("Music", "THMBDOWN", "Decrease rating", "7");
00385 REG_KEY("Music", "REFRESH", "Refresh music tree", "8");
00386 REG_KEY("Music", "FILTER", "Filter All My Music", "F");
00387 REG_KEY("Music", "INCSEARCH", "Show incremental search dialog", "Ctrl+S");
00388 REG_KEY("Music", "INCSEARCHNEXT", "Incremental search find next match", "Ctrl+N");
00389 REG_KEY("Music", "SPEEDUP", "Increase Play Speed", "W");
00390 REG_KEY("Music", "SPEEDDOWN", "Decrease Play Speed", "X");
00391
00392 REG_MEDIA_HANDLER("MythMusic Media Handler 1/2", "", "", handleMedia,
00393 MEDIATYPE_AUDIO | MEDIATYPE_MIXED, QString::null);
00394 REG_MEDIA_HANDLER("MythMusic Media Handler 2/2", "", "", handleMedia,
00395 MEDIATYPE_MMUSIC, "ogg,mp3,aac,flac");
00396 }
00397
00398 int mythplugin_init(const char *libversion)
00399 {
00400 if (!gContext->TestPopupVersion("mythmusic", libversion,
00401 MYTH_BINARY_VERSION))
00402 return -1;
00403
00404 gContext->ActivateSettingsCache(false);
00405 if (!UpgradeMusicDatabaseSchema())
00406 {
00407 VERBOSE(VB_IMPORTANT,
00408 "Couldn't upgrade database to new schema, exiting.");
00409 return -1;
00410 }
00411 gContext->ActivateSettingsCache(true);
00412
00413 MusicGeneralSettings general;
00414 general.load();
00415 general.save();
00416
00417 MusicPlayerSettings settings;
00418 settings.load();
00419 settings.save();
00420
00421 MusicRipperSettings ripper;
00422 ripper.load();
00423 ripper.save();
00424
00425 setupKeys();
00426
00427 Decoder::SetLocationFormatUseTags();
00428
00429 gPlayer = new MusicPlayer(NULL, chooseCD());
00430 gMusicData = new MusicData();
00431
00432 return 0;
00433 }
00434
00435 static void preMusic()
00436 {
00437 srand(time(NULL));
00438
00439 CheckFreeDBServerFile();
00440
00441 MSqlQuery count_query(MSqlQuery::InitCon());
00442 count_query.exec("SELECT COUNT(*) FROM music_songs;");
00443
00444 bool musicdata_exists = false;
00445 if (count_query.isActive())
00446 {
00447 if(count_query.next() &&
00448 0 != count_query.value(0).toInt())
00449 {
00450 musicdata_exists = true;
00451 }
00452 }
00453
00454
00455 QString startdir = gContext->GetSetting("MusicLocation");
00456 startdir = QDir::cleanDirPath(startdir);
00457 if (!startdir.endsWith("/"));
00458 startdir += "/";
00459
00460 Metadata::SetStartdir(startdir);
00461
00462 Decoder::SetLocationFormatUseTags();
00463
00464
00465
00466
00467 if (startdir != "" && !musicdata_exists)
00468 {
00469 FileScanner *fscan = new FileScanner();
00470 fscan->SearchDir(startdir);
00471 }
00472
00473 QString paths = gContext->GetSetting("TreeLevels");
00474
00475
00476 Metadata::setArtistAndTrackFormats();
00477
00478 AllMusic *all_music = new AllMusic(paths, startdir);
00479
00480
00481 PlaylistsContainer *all_playlists = new PlaylistsContainer(all_music, gContext->GetHostName());
00482
00483 gMusicData->paths = paths;
00484 gMusicData->startdir = startdir;
00485 gMusicData->all_playlists = all_playlists;
00486 gMusicData->all_music = all_music;
00487 }
00488
00489 static void postMusic()
00490 {
00491
00492 if (gMusicData->all_music->cleanOutThreads())
00493 {
00494 gMusicData->all_music->save();
00495 }
00496
00497 if (gMusicData->all_playlists->cleanOutThreads())
00498 {
00499 gMusicData->all_playlists->save();
00500 int x = gMusicData->all_playlists->getPending();
00501 SavePending(x);
00502 }
00503
00504 delete gMusicData->all_music;
00505 gMusicData->all_music = NULL;
00506 delete gMusicData->all_playlists;
00507 gMusicData->all_playlists = NULL;
00508 }
00509
00510 int mythplugin_run(void)
00511 {
00512 gMusicData->runPost = true;
00513
00514 preMusic();
00515 runMenu("musicmenu.xml");
00516
00517 return 0;
00518 }
00519
00520 int mythplugin_config(void)
00521 {
00522 gMusicData->runPost = false;
00523 gMusicData->paths = gContext->GetSetting("TreeLevels");
00524 gMusicData->startdir = gContext->GetSetting("MusicLocation");
00525 gMusicData->startdir = QDir::cleanDirPath(gMusicData->startdir);
00526
00527 if (!gMusicData->startdir.endsWith("/"))
00528 gMusicData->startdir += "/";
00529
00530 Metadata::SetStartdir(gMusicData->startdir);
00531
00532 Decoder::SetLocationFormatUseTags();
00533
00534 runMenu("music_settings.xml");
00535
00536 return 0;
00537 }
00538
00539 void mythplugin_destroy(void)
00540 {
00541 delete gPlayer;
00542 delete gMusicData;
00543 }
00544
00545 void runMusicPlayback(void)
00546 {
00547 gContext->addCurrentLocation("playmusic");
00548 preMusic();
00549 startPlayback();
00550 postMusic();
00551 gContext->removeCurrentLocation();
00552 }
00553
00554 void runMusicSelection(void)
00555 {
00556 gContext->addCurrentLocation("musicplaylists");
00557 preMusic();
00558 startDatabaseTree();
00559 postMusic();
00560 gContext->removeCurrentLocation();
00561 }
00562
00563 void runRipCD(void)
00564 {
00565 gContext->addCurrentLocation("ripcd");
00566 preMusic();
00567 if (startRipper())
00568 {
00569
00570
00571 FileScanner *fscan = new FileScanner();
00572 fscan->SearchDir(gMusicData->startdir);
00573 RebuildMusicTree();
00574 }
00575 postMusic();
00576 gContext->removeCurrentLocation();
00577 }
00578
00579 void runScan(void)
00580 {
00581 preMusic();
00582
00583 if ("" != gMusicData->startdir)
00584 {
00585 FileScanner *fscan = new FileScanner();
00586 fscan->SearchDir(gMusicData->startdir);
00587 RebuildMusicTree();
00588 }
00589
00590 postMusic();
00591 }
00592
00593 void showMiniPlayer(void)
00594 {
00595
00596 if (!gPlayer->hasClient())
00597 gPlayer->showMiniPlayer();
00598 }