diff options
author | LarsAC <LarsAC@e10066b5-e1e2-0310-b819-94efdf66514b> | 2005-02-07 09:17:20 +0000 |
---|---|---|
committer | LarsAC <LarsAC@e10066b5-e1e2-0310-b819-94efdf66514b> | 2005-02-07 09:17:20 +0000 |
commit | 14f5032e141da131f3d1f72979cf415d6105d819 (patch) | |
tree | 2977f746c00056a1ad1420b99b98dc46920ff365 | |
parent | 19b5fbe6f03d263ab8e427022156ed7d2bfcaea1 (diff) | |
download | vdr-plugin-muggle-14f5032e141da131f3d1f72979cf415d6105d819.tar.gz vdr-plugin-muggle-14f5032e141da131f3d1f72979cf415d6105d819.tar.bz2 |
Merged most recent stuff from osd_extensions trunk for next release (0.1.2)
git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@447 e10066b5-e1e2-0310-b819-94efdf66514b
-rw-r--r-- | Makefile | 28 | ||||
-rw-r--r-- | i18n.c | 51 | ||||
-rw-r--r-- | mg_actions.c | 469 | ||||
-rw-r--r-- | mg_actions.h | 40 | ||||
-rw-r--r-- | mg_db.c | 698 | ||||
-rw-r--r-- | mg_db.h | 89 | ||||
-rw-r--r-- | mg_order.c | 492 | ||||
-rw-r--r-- | mg_order.h | 41 | ||||
-rw-r--r-- | mg_tools.c | 3 | ||||
-rw-r--r-- | muggle.c | 2 | ||||
-rwxr-xr-x | mugglei.c | 168 | ||||
-rwxr-xr-x | scripts/languages.txt | 1 | ||||
-rw-r--r-- | vdr_decoder.c | 20 | ||||
-rw-r--r-- | vdr_decoder_flac.c | 9 | ||||
-rw-r--r-- | vdr_decoder_mp3.c | 4 | ||||
-rw-r--r-- | vdr_decoder_ogg.c | 3 | ||||
-rw-r--r-- | vdr_menu.c | 373 | ||||
-rw-r--r-- | vdr_menu.h | 53 | ||||
-rw-r--r-- | vdr_player.c | 22 | ||||
-rw-r--r-- | vdr_setup.c | 51 | ||||
-rw-r--r-- | vdr_setup.h | 1 | ||||
-rw-r--r-- | vdr_sound.c | 7 | ||||
-rw-r--r-- | vdr_stream.c | 2 |
23 files changed, 1657 insertions, 970 deletions
@@ -9,6 +9,15 @@ # PLUGIN = muggle +#if you want ogg / flac support, remove the trailing x in those two +#definitions: +define have_vorbisfile +1 +endef +define have_flac +1 +endef + ### The version number of this plugin (taken from the main source file): VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ print $$6 }' | sed -e 's/[";]//g') @@ -16,7 +25,7 @@ VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ pri ### The C++ compiler and options: CXX ?= g++-3.3 -CXXFLAGS ?= -fPIC -O2 -Wall -Woverloaded-virtual -Wno-deprecated -g +CXXFLAGS ?= -fPIC -O0 -Wall -Woverloaded-virtual -Wno-deprecated -g ### The directory environment: @@ -44,7 +53,7 @@ PACKAGE = vdr-$(ARCHIVE) INCLUDES += -I$(VDRDIR) -I$(VDRDIR)/include -I$(DVBDIR)/include \ -I/usr/include/mysql/ -I/usr/include/taglib -DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' # -DHAVE_VORBISFILE -DHAVE_FLAC +DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' MIFLAGS += -I/usr/include/taglib -lmysqlclient @@ -52,11 +61,22 @@ MIFLAGS += -I/usr/include/taglib -lmysqlclient OBJS = $(PLUGIN).o i18n.o mg_valmap.o mg_order.o mg_db.o mg_actions.o vdr_menu.o mg_tools.o \ vdr_decoder_mp3.o vdr_stream.o vdr_decoder.o vdr_player.o \ - vdr_setup.o vdr_decoder_ogg.o vdr_decoder_flac.o + vdr_setup.o -LIBS = -lmad -lmysqlclient # -lvorbisfile -lvorbis -lFLAC++ +LIBS = -lmad -lmysqlclient MILIBS = -lmysqlclient -ltag +ifdef have_vorbisfile +DEFINES += -DHAVE_VORBISFILE +OBJS += vdr_decoder_ogg.o +LIBS += -lvorbisfile -lvorbis +endif +ifdef have_flac +DEFINES += -DHAVE_FLAC +OBJS += vdr_decoder_flac.o +LIBS += -lFLAC++ +endif + ### Targets: all: libvdr-$(PLUGIN).so mugglei @@ -13,6 +13,40 @@ const tI18nPhrase Phrases[] = { { + "Key %d", + "Schlüsselfeld %d", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Key %d", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Create", + "Neu", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Nouveau", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { "Search", "Suchen", "", // TODO @@ -98,6 +132,23 @@ const tI18nPhrase Phrases[] = "", // TODO }, { + "Create order", + "Sortierung neu anlegen", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Créer un ordre nouveaux", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { "Create collection", "Sammlung neu anlegen", "", // TODO diff --git a/mg_actions.c b/mg_actions.c index 48b06f3..42337d9 100644 --- a/mg_actions.c +++ b/mg_actions.c @@ -36,33 +36,48 @@ IsEntry(mgActions i) class mgOsdItem : public mgAction, public cOsdItem { public: - eOSState ProcessKey(eKeys key); - protected: - virtual eOSState Process(eKeys key) { return osUnknown; } + eOSState ProcessKey(eKeys key) { return mgAction::ProcessKey(key); } }; + eOSState -mgOsdItem::ProcessKey(eKeys key) +mgAction::ProcessKey(eKeys key) { + if (key!=kNone) + mgDebug(1,"mgAction::ProcessKey(%d)",key); eOSState result = Process(key); if (result != osUnknown) IgnoreNextEvent = true; return result; } +class mgNone: public mgOsdItem +{ public: + void Notify() {}; + bool Enabled(mgActions on) { return false; } + eOSState Process(eKeys key) { return osUnknown; } +}; + //! \brief used for normal data base items class mgEntry : public mgOsdItem { public: void Notify(); bool Enabled(mgActions on) { return IsEntry(on);} - const char *ButtonName() { return ""; } const char *MenuName (const unsigned int idx,const string value); - virtual eOSState Process(eKeys key); + eOSState Process(eKeys key); void Execute(); eOSState Back(); }; +class mgKeyItem : public mgAction, public cMenuEditStraItem +{ + public: + mgKeyItem(const char *Name, int *Value, int NumStrings, const char *const *Strings) : cMenuEditStraItem(Name, Value, NumStrings, Strings) {} + eOSState ProcessKey(eKeys key) { return mgAction::ProcessKey(key); } + eOSState Process(eKeys key); +}; + class mgDoCollEntry : public mgEntry { public: @@ -100,7 +115,6 @@ mgDoCollEntry::Process(eKeys key) eOSState result = osContinue; switch (key) { - case kBlue: case kBack: break; case kOk: @@ -202,9 +216,60 @@ class mgCommand : public mgOsdItem public: bool Enabled(mgActions on); virtual eOSState Process(eKeys key); +}; + +class mgActOrder : public mgOsdItem +{ + public: + const char* MenuName(const unsigned int idx,const string value); + virtual eOSState Process(eKeys key); void Execute(); }; +const char* +mgActOrder::MenuName(const unsigned int idx,const string value) +{ + return strdup(value.c_str()); +} + +eOSState +mgActOrder::Process(eKeys key) +{ + mgMenu *n = osd ()->newmenu; + osd ()->newmenu = NULL; + eOSState result = osContinue; + switch (key) + { + case kBack: + break; + case kOk: + Execute (); + break; + default: + osd ()->newmenu = n; // wrong key: stay in submenu + result = osUnknown; + break; + } + return result; +} + +void +mgActOrder::Execute() +{ + mgSelection *s = osd()->selection(); + mgOrder oldorder = s->getOrder(); + mgContentItem o; + s->select(); + if (s->getNumTracks()==1) + o = s->getTrack(0); + osd()->UseNormalSelection(); // Default for all orders + osd()->setOrder(s,osd()->Current()); + mgSelection *newsel = osd()->selection(); + newsel->selectfrom(oldorder,&o); + osd()->newposition = newsel->getPosition(); + osd()->SaveState(); +} + bool mgCommand::Enabled(mgActions on) { @@ -321,7 +386,6 @@ mgCommand::Process(eKeys key) else parent->TreeYellowAction = Type(); break; - case kBlue: case kBack: break; case kOk: @@ -335,16 +399,11 @@ mgCommand::Process(eKeys key) return result; } -void -mgCommand::Execute() -{ -} class mgExternal : public mgCommand { public: const char *ButtonName(); - const char *MenuName (const unsigned int idx,const string value); void Execute(); bool Enabled(mgActions on) { return true; } private: @@ -385,12 +444,6 @@ mgExternal::ButtonName() return ""; } -const char* -mgExternal::MenuName(const unsigned int idx,const string value) -{ - return strdup(ButtonName()); -} - cCommand * mgExternal::Command() { @@ -426,7 +479,17 @@ mgExternal::Execute() if (!m3u_file.empty ()) { /*char *result = (char *)*/ - command->Execute (m3u_file.c_str ()); + string quoted = "'" + m3u_file + "'"; + char prev[1000]; + if (!getcwd(prev,1000)) + mgError("current path too long"); + if (chdir(the_setup.ToplevelDir)) + mgError("cannnot change to directory %s", + the_setup.ToplevelDir); + command->Execute (quoted.c_str ()); + chdir(prev); + selection()->clearCache(); + osd()->forcerefresh = true; // the ext cmd could change the database /* What to do? Recode cMenuText (not much)? if (result) { @@ -475,17 +538,91 @@ mgChooseOrder::Process(eKeys key) void mgChooseOrder::Execute() { osd ()->newmenu = new mgMenuOrders; + osd ()->newposition = osd()->getCurrentOrder(); + +} + +class mgEditOrder : public mgCommand +{ + public: + bool Enabled(mgActions on) { return true; } + eOSState Process(eKeys key); + void Execute () { osd ()->newmenu = new mgMenuOrder; } + const char *ButtonName() { return tr("Edit"); } +}; + +eOSState +mgEditOrder::Process(eKeys key) +{ + if (key == kOk) + { + Execute(); + return osContinue; + } + else + return mgCommand::Process(key); +} + +class mgCreateOrder : public mgCommand +{ + public: + bool Enabled(mgActions on) { return true; } + void Execute (); + const char *ButtonName() { return tr("Create"); } +}; +void +mgCreateOrder::Execute() +{ + osd()->AddOrder(); + osd()->SaveState(); + osd()->forcerefresh = true; } +class mgDeleteOrder : public mgCommand +{ + public: + bool Enabled(mgActions on) { return true; } + void Execute (); + const char *ButtonName() { return tr("Delete"); } +}; + +void +mgDeleteOrder::Execute() +{ + osd()->DeleteOrder(); + osd()->SaveState(); + osd()->forcerefresh = true; + osd()->newposition = osd()->Current(); +} + +//! \brief show the normal selection list +class mgShowList: public mgOsdItem +{ + public: + bool Enabled(mgActions) { return true; } + const char *ButtonName () { return tr("List"); } + void Execute() { osd()->newmenu=NULL; } +}; + + +//! \brief show the command submenu +class mgShowCommands: public mgOsdItem +{ + public: + bool Enabled(mgActions on) { return true; } + const char *ButtonName () { return tr("Commands"); } + void Execute() { osd()->newmenu = new mgSubmenu; } +}; + + //! \brief toggles between the normal and the collection selection class mgToggleSelection:public mgCommand { public: - bool Enabled(mgActions on = mgActions(0)) { return true; } + bool Enabled(mgActions on) { return true; } void Execute (); const char *ButtonName (); - const char *MenuName (const unsigned int idx,const string value); }; const char * @@ -497,17 +634,6 @@ mgToggleSelection::ButtonName () return tr ("Collections"); } - -const char * -mgToggleSelection::MenuName (const unsigned int idx,const string value) -{ - if (osd ()->UsingCollection) - return strdup(tr ("Search")); - else - return strdup(tr ("Collections")); -} - - void mgToggleSelection::Execute () { @@ -526,7 +652,7 @@ mgToggleSelection::Execute () class mgSetDefaultCollection:public mgCommand { public: - bool Enabled(mgActions on = mgActions(0)); + bool Enabled(mgActions on); void Execute (); const char *ButtonName () { @@ -567,11 +693,7 @@ class mgSetButton : public mgCommand { public: bool Enabled(mgActions on) { return true; } - const char *ButtonName() - { - return tr("Set"); - } - const char *MenuName(const unsigned int idx,const string value) { return strdup(""); } + const char *ButtonName() { return tr("Set"); } }; @@ -579,19 +701,9 @@ class mgSetButton : public mgCommand class mgInstantPlay : public mgCommand { public: void Execute (); - const char *ButtonName () - { - return tr ("Instant play"); - } - const char *MenuName (const unsigned int idx,const string value); + const char *ButtonName () { return tr ("Instant play"); } }; -const char * -mgInstantPlay::MenuName (const unsigned int idx,const string value) -{ - return strdup(tr("Instant play")); -} - void mgInstantPlay::Execute() { @@ -636,7 +748,8 @@ mgAddAllToCollection::ExecuteMove() char *b; asprintf(&b,tr("'%s' to collection"),selection()->getCurrentValue().c_str()); osd ()->newmenu = new mgTreeAddToCollSelector(string(b)); - osd ()->newposition = osd()->collselection()->getPosition(0); + osd ()->collselection()->leave_all(); + osd ()->newposition = osd()->collselection()->getPosition(); free(b); } @@ -699,7 +812,7 @@ mgAddAllToDefaultCollection::ExecuteSelection (mgSelection *s) class mgAddThisToCollection:public mgAddAllToCollection { public: - bool Enabled(mgActions on = mgActions(0)); + bool Enabled(mgActions on); void Execute (); const char *ButtonName (); const char *MenuName (const unsigned int idx,const string value); @@ -738,7 +851,7 @@ mgAddThisToCollection::MenuName (const unsigned int idx,const string value) class mgAddThisToDefaultCollection:public mgAddAllToDefaultCollection { public: - bool Enabled(mgActions on = mgActions(0)); + bool Enabled(mgActions on); void Execute (); const char *ButtonName (); const char *MenuName (const unsigned int idx,const string value); @@ -778,60 +891,6 @@ mgAddThisToDefaultCollection::MenuName (const unsigned int idx,const string valu return b; } -class mgOrderNew : public mgCommand -{ - protected: - virtual void NewOrder() = 0; - public: - bool Enabled(mgActions on = mgActions(0)) { return true; } - void Execute(); -}; - -void -mgOrderNew::Execute() -{ - mgSelection *s = osd()->selection(); - map<mgKeyTypes,string>* oldkeys = s->UsedKeyValues(); - mgContentItem o; - s->select(); - if (s->getNumTracks()==1) - { - o = s->getTrack(0); - } - osd()->UseNormalSelection(); // Default for all orders - NewOrder(); - mgSelection *newsel = osd()->selection(); - newsel->leave(0); - for (unsigned int idx = 0; idx < newsel->ordersize(); idx++) - { - string selval = ""; - mgKeyTypes new_kt = newsel->getKeyType(idx); - if (oldkeys->count(new_kt)>0) - selval=(*oldkeys)[new_kt]; - else if (o.getId()>=0) - selval=o.getKeyValue(new_kt); - if (!selval.empty()) - { - if (idx<newsel->ordersize()-1) - newsel->select(selval); - else - newsel->setPosition(selval); - continue; - } - break; - } - delete oldkeys; - osd()->newposition = newsel->getPosition(newsel->level()); - newsel->clearCache(); -} - -class mgOrderCollItem : public mgOrderNew -{ - const char *MenuName(const unsigned int idx,const string value) { return strdup(tr("Collection -> Item")); } - void NewOrder() { osd ()->UseCollectionSelection (); } -}; - - //! \brief remove selected items from default collection class mgRemoveAllFromCollection:public mgCommand { @@ -864,7 +923,7 @@ mgRemoveAllFromCollection::MenuName (const unsigned int idx,const string value) class mgClearCollection : public mgCommand { public: - bool Enabled(mgActions on = mgActions(0)); + bool Enabled(mgActions on); void Execute (); const char *ButtonName () { @@ -922,50 +981,45 @@ mgRemoveThisFromCollection::MenuName (const unsigned int idx,const string value) return strdup(tr("Remove from a collection")); } -/*! \brief create collection directly in the collection list - */ -class mgCreateCollection: public mgAction, public cMenuEditStrItem +class mgEditAction : public mgAction +{ + public: + mgEditAction() : mgAction() { memset(m_value,0,30); } + protected: + char m_value[30]; +}; + +class mgCreate : public mgEditAction, public cMenuEditStrItem { public: - mgCreateCollection(); + mgCreate(const char* mn); + virtual bool Enabled(mgActions on)=0; void Notify(); - bool Enabled(mgActions on = mgActions(0)); - eOSState ProcessKey(eKeys key); - void Execute (); - const char *MenuName (const unsigned int idx=0,const string value=""); - private: + eOSState ProcessKey(eKeys key) { return mgAction::ProcessKey(key); } + eOSState Process(eKeys key); + protected: bool Editing(); - char value[30]; }; +mgCreate::mgCreate(const char *mn) : mgEditAction(), cMenuEditStrItem(mn,m_value,30,tr(FileNameChars)) +{ +} bool -mgCreateCollection::Editing() +mgCreate::Editing() { return (strchr(cOsdItem::Text(),'[') && strchr(cOsdItem::Text(),']')); } - void -mgCreateCollection::Notify() +mgCreate::Notify() { if (!Editing()) m->SetHelpKeys(); } -const char* -mgCreateCollection::MenuName(const unsigned int idx,const string value) -{ - return strdup(tr ("Create collection")); -} - -mgCreateCollection::mgCreateCollection() : mgAction(), cMenuEditStrItem(MenuName(),value,30,tr(FileNameChars)) -{ - value[0]=0; -} - eOSState -mgCreateCollection::ProcessKey(eKeys key) +mgCreate::Process(eKeys key) { if (key == kOk) if (Editing()) @@ -978,16 +1032,29 @@ mgCreateCollection::ProcessKey(eKeys key) return osUnknown; } -bool -mgCreateCollection::Enabled(mgActions on) +class mgCreateCollection : public mgCreate +{ + public: + mgCreateCollection(); + bool Enabled(mgActions on); + void Execute (); + const char *MenuName (const unsigned int idx=0,const string value=""); +}; + +mgCreateCollection::mgCreateCollection() : mgCreate(MenuName()) { - return selection()->isCollectionlist(); +} + +const char* +mgCreateCollection::MenuName(const unsigned int idx,const string value) +{ + return strdup(tr ("Create collection")); } void mgCreateCollection::Execute () { - string name = trim(value); + string name = trim(m_value); if (name.empty()) return; bool created = selection ()->CreateCollection (name); if (created) @@ -1006,12 +1073,19 @@ mgCreateCollection::Execute () osd()->Message1 ("Collection '%s' NOT created", name); } +bool +mgCreateCollection::Enabled(mgActions on) +{ + return selection()->isCollectionlist(); +} + + //! \brief delete collection class mgDeleteCollection:public mgCommand { public: void Execute (); - bool Enabled(mgActions on = mgActions(0)); + bool Enabled(mgActions on); const char *ButtonName () { return tr ("Delete"); @@ -1079,69 +1153,11 @@ mgExportTracklist::Execute () osd()->Message1 ("written to %s", m3u_file); } -class mgOrderArtistAlbumTitle : public mgOrderNew -{ - const char *MenuName(const unsigned int idx,const string value) { return strdup(tr("Artist -> Album -> Track")); } - void NewOrder() - { - selection ()->setKey (0, keyArtist); - selection ()->setKey (1, keyAlbum); - selection ()->setKey (2, keyTrack); - } -}; - - -class mgOrderAlbumTitle : public mgOrderNew -{ - const char *MenuName(const unsigned int idx,const string value) { return strdup(tr("Album -> Track")); } - void NewOrder() - { - selection ()->setKey (0, keyAlbum); - selection ()->setKey (1, keyTrack); - } -}; - - -class mgOrderGenreYearTitle : public mgOrderNew -{ - const char *MenuName(const unsigned int idx,const string value) { return strdup(tr("Genre 1 -> Year -> Track")); } - void NewOrder() - { - selection ()->setKey (0, keyGenre1); - selection ()->setKey (1, keyYear); - selection ()->setKey (2, keyTrack); - } -}; - - -class mgOrderGenreArtistAlbumTitle : public mgOrderNew -{ - const char *MenuName(const unsigned int idx,const string value) { return strdup(tr("Genre 1 -> Artist ->Album -> Track")); } - void NewOrder() - { - selection ()->setKey (0, keyGenre1); - selection ()->setKey (1, keyArtist); - selection ()->setKey (2, keyAlbum); - selection ()->setKey (3, keyTrack); - } -}; - - -class mgOrderArtistTitle : public mgOrderNew -{ - const char *MenuName(const unsigned int idx,const string value) { return strdup(tr("Artist -> Track")); } - void NewOrder() - { - selection ()->setKey (0, keyArtist); - selection ()->setKey (1, keyTrack); - } -}; - - mgActions mgAction::Type() { const type_info& t = typeid(*this); + if (t == typeid(mgNone)) return actNone; if (t == typeid(mgChooseOrder)) return actChooseOrder; if (t == typeid(mgToggleSelection)) return actToggleSelection; if (t == typeid(mgClearCollection)) return actClearCollection; @@ -1159,13 +1175,13 @@ mgAction::Type() if (t == typeid(mgRemoveThisFromCollection)) return actRemoveThisFromCollection; if (t == typeid(mgEntry)) return actEntry; if (t == typeid(mgSetButton)) return actSetButton; - if (t == typeid(mgOrderCollItem)) return ActOrderCollItem; - if (t == typeid(mgOrderArtistAlbumTitle)) return ActOrderArtistAlbumTitle; - if (t == typeid(mgOrderArtistTitle)) return ActOrderArtistTitle; - if (t == typeid(mgOrderAlbumTitle)) return ActOrderAlbumTitle; - if (t == typeid(mgOrderGenreYearTitle)) return ActOrderGenreYearTitle; - if (t == typeid(mgOrderGenreArtistAlbumTitle)) return ActOrderArtistAlbumTitle; + if (t == typeid(mgShowList)) return actShowList; + if (t == typeid(mgShowCommands)) return actShowCommands; if (t == typeid(mgSetDefaultCollection)) return actSetDefaultCollection; + if (t == typeid(mgActOrder)) return actOrder; + if (t == typeid(mgCreateOrder)) return actCreateOrder; + if (t == typeid(mgDeleteOrder)) return actDeleteOrder; + if (t == typeid(mgEditOrder)) return actEditOrder; if (t == typeid(mgExternal0)) return actExternal0; if (t == typeid(mgExternal1)) return actExternal1; if (t == typeid(mgExternal2)) return actExternal2; @@ -1186,16 +1202,22 @@ mgAction::Type() if (t == typeid(mgExternal17)) return actExternal17; if (t == typeid(mgExternal18)) return actExternal18; if (t == typeid(mgExternal19)) return actExternal19; - mgError("Unknown mgAction %s",t.name()); return mgActions(0); } mgAction* +actGenerateKeyItem(const char *Name, int *Value, int NumStrings, const char * const * Strings) +{ + return new mgKeyItem(Name,Value,NumStrings,Strings); +} + +mgAction* actGenerate(const mgActions action) { mgAction * result = NULL; switch (action) { + case actNone: result = new mgNone;break; case actChooseOrder: result = new mgChooseOrder;break; case actToggleSelection: result = new mgToggleSelection;break; case actClearCollection: result = new mgClearCollection;break; @@ -1213,13 +1235,15 @@ actGenerate(const mgActions action) case actRemoveThisFromCollection: result = new mgRemoveThisFromCollection;break; case actEntry: result = new mgEntry;break; case actSetButton: result = new mgSetButton;break; - case ActOrderCollItem: result = new mgOrderCollItem;break; - case ActOrderArtistAlbumTitle: result = new mgOrderArtistAlbumTitle;break; - case ActOrderArtistTitle: result = new mgOrderArtistTitle;break; - case ActOrderAlbumTitle: result = new mgOrderAlbumTitle;break; - case ActOrderGenreYearTitle: result = new mgOrderGenreYearTitle;break; - case ActOrderGenreArtistAlbumTitle: result = new mgOrderGenreArtistAlbumTitle;break; + case actShowList: result = new mgShowList;break; + case actShowCommands: result = new mgShowCommands;break; + case actUnused5: break; case actSetDefaultCollection: result = new mgSetDefaultCollection;break; + case actOrder: result = new mgActOrder;break; + case actUnused6: break; + case actCreateOrder: result = new mgCreateOrder;break; + case actDeleteOrder: result = new mgDeleteOrder;break; + case actEditOrder: result = new mgEditOrder;break; case actExternal0: result = new mgExternal0;break; case actExternal1: result = new mgExternal1;break; case actExternal2: result = new mgExternal2;break; @@ -1258,3 +1282,28 @@ mgAction::collselection() return osd()->collselection(); } +eOSState +mgKeyItem::Process(eKeys key) +{ + mgMenuOrder *menu = dynamic_cast<mgMenuOrder*>(m); + if (key==kOk) + { + if (menu->ChangeOrder(key)) + return osContinue; + else + { + menu->SaveOrder(); + osd ()->newmenu = NULL; + return osContinue; + } + } else if (key==kBack) + { + osd ()->newmenu = NULL; + return osContinue; + } + if (key==kUp || key==kDown) + if (menu->ChangeOrder(key)) + return osContinue; + return cMenuEditStraItem::ProcessKey(key); +} + diff --git a/mg_actions.h b/mg_actions.h index 3712191..3994b10 100644 --- a/mg_actions.h +++ b/mg_actions.h @@ -27,10 +27,11 @@ class mgMainMenu; /*! \brief defines all actions which can appear in command submenus. * Since these values are saved in muggle.state, new actions should - * always be appended. The order does not matter. Value 0 means undefined. + * always be appended. The order does not matter. actNone should be 0. */ enum mgActions { - actChooseOrder=1, //!< show a menu with all possible orders + actNone, + actChooseOrder, //!< show a menu with all possible orders actToggleSelection, //!< toggle between search and collection view actClearCollection, //!< clear a collection, actCreateCollection, @@ -45,15 +46,17 @@ enum mgActions { actRemoveThisFromCollection, //!< remove selected item from default collection actEntry, //!< used for normal data base items actSetButton, //!< connect a button with an action - ActOrderCollItem, //!< order by collections - ActOrderArtistAlbumTitle, //!< order by Artist/Album/Title - ActOrderArtistTitle, //!< order by Artist/Title - ActOrderAlbumTitle, //!< order by Album/Title - ActOrderGenreYearTitle, //!< order by Genre1/Year/Title - ActOrderGenreArtistAlbumTitle, //!< order by Genre1/Artist/Album/Title + actShowList, + actShowCommands, + actCreateOrder, + actDeleteOrder, + actUnused5, //!< order by Genre1/Artist/Album/Title actAddAllToDefaultCollection, actAddThisToDefaultCollection, actSetDefaultCollection, + actOrder, + actUnused6, + actEditOrder, actExternal0 = 1000, //!< used for external commands, the number is the entry number in the .conf file starting with line 0 actExternal1, //!< used for external commands, the number is the entry number in the .conf file actExternal2, //!< used for external commands, the number is the entry number in the .conf file @@ -88,7 +91,7 @@ class mgAction virtual bool Enabled(mgActions on = mgActions(0)); //! \brief the action to be executed - virtual void Execute () = 0; + virtual void Execute () {} //! \brief handles the kBack key virtual eOSState Back(); @@ -98,14 +101,17 @@ class mgAction */ virtual const char *ButtonName () { - return NULL; + return ""; } /*! \brief the name for a menu entry. If empty, no button will be able * to execute this. The returned C string must be freeable at any time. * \param value a string that can be used for building the menu name. */ - virtual const char *MenuName (const unsigned int idx=0,const string value="") = 0; + virtual const char *MenuName (const unsigned int idx=0,const string value="") + { + return strdup(ButtonName()); + } //! \brief default constructor mgAction (); @@ -149,12 +155,24 @@ class mgAction mgSelection* playselection (); virtual void Notify(); + eOSState ProcessKey(eKeys key); + virtual eOSState Process(eKeys key) { return osUnknown; } private: mgMainMenu *m_osd; }; +class mgActionWithIntValue: public mgAction +{ + public: + mgActionWithIntValue() { intval=0; } + void setValue(int i) { intval = i; } + protected: + int intval; +}; + //! \brief generate an mgAction for action mgAction* actGenerate(const mgActions action); +mgAction* actGenerateKeyItem(const char *Name, int *Value, int NumStrings, const char * const * Strings); #endif @@ -13,6 +13,9 @@ #include "vdr_setup.h" #include "mg_tools.h" +bool needGenre2; +bool needGenre2_set; + //! \brief adds string n to string s, using a comma to separate them static string comma (string & s, string n); @@ -36,11 +39,21 @@ comma (string & s, string n) static string zerostring; +bool +mgSelection::mgSelStrings::operator==(const mgSelStrings&x) const +{ + bool result = strings.size()==x.strings.size(); + if (result) + for (unsigned int i=0;i<size();i++) + result &= strings[i]==x.strings[i]; + return result; +} + size_t -mgSelection::mgSelStrings::size() +mgSelection::mgSelStrings::size() const { if (!m_sel) - mgError("mgSelStrings: m_sel is NULL"); + mgError("mgSelStrings: m_sel is 0"); m_sel->refreshValues(); return strings.size(); } @@ -49,7 +62,7 @@ string& mgSelection::mgSelStrings::operator[](unsigned int idx) { if (!m_sel) - mgError("mgSelStrings: m_sel is NULL"); + mgError("mgSelStrings: m_sel is 0"); m_sel->refreshValues(); if (idx>=strings.size()) return zerostring; return strings[idx]; @@ -77,9 +90,16 @@ mgSelection::getCurrentValue() string mgSelection::getKeyValue(const unsigned int level) const { - return order.getKeyValue(level); + return order.getKeyValue(level); } +unsigned int +mgSelection::getKeyIndex(const unsigned int level) const +{ + return valindex(getKeyValue(level)); +} + + mgKeyTypes mgSelection::getKeyType (const unsigned int level) const { @@ -98,26 +118,14 @@ mgSelection::exec_sql(string query) const */ string mgSelection::get_col0 (string query) const { - MYSQL_RES * sql_result = exec_sql (query); - if (!sql_result) - return "NULL"; - MYSQL_ROW row = mysql_fetch_row (sql_result); - string result; - if (row == NULL) - result = "NULL"; - else if (row[0] == NULL) - result = "NULL"; - else - result = row[0]; - mysql_free_result (sql_result); - return result; + return ::get_col0(m_db, query); } unsigned long mgSelection::exec_count (string query) const { - return atol (get_col0 (query).c_str ()); + return ::exec_count(m_db, query); } @@ -133,13 +141,29 @@ mgSelection::sql_string (const string s) const } string +mgContentItem::getKeyId(mgKeyTypes kt) +{ + if (m_id<0) + return ""; + switch (kt) { + case keyGenres: + case keyGenre1: + case keyGenre2: + case keyGenre3: return m_genre1_id; + default: return getKeyValue(kt); + } +} + +string mgContentItem::getKeyValue(mgKeyTypes kt) { if (m_id<0) return ""; switch (kt) { - case keyGenre1: return getGenre1(); - case keyGenre2: return getGenre2(); + case keyGenres: + case keyGenre1: + case keyGenre2: + case keyGenre3: return getGenre(); case keyArtist: return getArtist(); case keyAlbum: return getAlbum(); case keyYear: return string(ltos(getYear())); @@ -154,26 +178,23 @@ mgContentItem * mgSelection::getTrack (unsigned int position) { if (position >= getNumTracks ()) - return NULL; + return 0; return &(m_tracks[position]); } string mgContentItem::getGenre () const { - return m_genre1; -} - - -string mgContentItem::getGenre1 () const -{ - return m_genre1; -} - - -string mgContentItem::getGenre2 () const -{ - return m_genre2; + string result=""; + if (m_genre1!="NULL") + result = m_genre1; + if (m_genre2!="NULL") + { + if (!result.empty()) + result += "/"; + result += m_genre2; + } + return result; } @@ -350,7 +371,7 @@ void mgSelection::ClearCollection (const string Name) { if (!m_db) return; string listid = id(keyCollection,Name); - exec_sql ("DELETE FROM playlistitem WHERE playlist="+listid); + exec_sql ("DELETE FROM playlistitem WHERE playlist="+sql_string(listid)); if (inCollection(Name)) clearCache (); } @@ -371,18 +392,19 @@ string mgSelection::exportM3U () { // open a file for writing - string fn = m_Directory + '/' + ListFilename () + ".m3u"; + string fn = "/tmp/" + ListFilename () + ".m3u"; FILE * listfile = fopen (fn.c_str (), "w"); if (!listfile) return ""; - fprintf (listfile, "#EXTM3U"); + fprintf (listfile, "#EXTM3U\n"); unsigned int tracksize = getNumTracks (); for (unsigned i = 0; i < tracksize; i++) { mgContentItem& t = m_tracks[i]; fprintf (listfile, "#EXTINF:%d,%s\n", t.getDuration (), t.getTitle ().c_str ()); - fprintf (listfile, "%s", t.getSourceFile ().c_str ()); + fprintf (listfile, "#MUGGLE:%d\n", t.getId()); + fprintf (listfile, "%s\n", t.getSourceFile (false).c_str ()); } fclose (listfile); return fn; @@ -400,45 +422,40 @@ mgSelection::empty() void mgSelection::setPosition (unsigned int position) { - if (m_level < order.size ()) - m_position[m_level] = position; - if (m_level >= order.size ()-1) - setTrackPosition(position); + if (m_level == order.size()) + setTrackPosition(position); + else + m_position = position; } - void mgSelection::setTrackPosition (unsigned int position) { - m_tracks_position = position; + m_tracks_position = position; } - unsigned int -mgSelection::getPosition (unsigned int level) const +mgSelection::getPosition () const { - if (level == order.size ()) - return getTrackPosition(); + if (m_level == order.size()) + return getTrackPosition(); else - return m_position[m_level]; + return m_position; } unsigned int -mgSelection::gotoPosition (unsigned int level) +mgSelection::gotoPosition () { - if (level>order.size()) - mgError("mgSelection::gotoPosition: level %u > order.size %u", - level,order.size()); - if (level == order.size ()) + if (m_level == order.size ()) return gotoTrackPosition(); else { unsigned int valsize = values.size(); if (valsize==0) - m_position[m_level] = 0; - else if (m_position[m_level] >= valsize) - m_position[m_level] = valsize -1; - return m_position[m_level]; + m_position = 0; + else if (m_position >= valsize) + m_position = valsize -1; + return m_position; } } @@ -517,13 +534,18 @@ mgSelection::getCompletedLength () const string mgSelection::getListname () const { - string - result = ""; + list<string> st; for (unsigned int i = 0; i < m_level; i++) - addsep (result, ":", getKeyValue(i)); + st.push_back(order.getKeyValue(i)); + st.unique(); + string result=""; + for (list < string >::iterator it = st.begin (); it != st.end (); ++it) + { + addsep (result, ":", *it); + } if (result.empty ()) if (order.size()>0) - result = string(ktName(order.Key(0)->Type ())); + result = string(ktName(order.getKeyType(0))); return result; } @@ -531,6 +553,20 @@ string mgSelection::ListFilename () { string res = getListname (); // convert char set ? + string::iterator it; + for (it=res.begin();it!=res.end();it++) + { + char& c = *it; + switch (c) + { + case '\'': + case '/': + case '\\': + case ' ': + case ')': + case '(': c = '_';break; + } + } return res; } @@ -557,23 +593,26 @@ mgSelection::tracks () const p.tables.push_back("tracks"); p.tables.push_back("album"); for (unsigned int i = m_level; i<order.size(); i++) - p.orders += order.Key(i)->Parts(true).orders; + p += order.Key(i)->Parts(true); m_current_tracks = p.sql_select(false); m_tracks.clear (); MYSQL_RES *rows = exec_sql (m_current_tracks); if (rows) { MYSQL_ROW row; - while ((row = mysql_fetch_row (rows)) != NULL) - { + while ((row = mysql_fetch_row (rows)) != 0) m_tracks.push_back (mgContentItem (this,row)); - } mysql_free_result (rows); } return m_tracks; } +mgContentItem::mgContentItem () +{ + m_id = -1; +} + mgContentItem::mgContentItem (const mgContentItem* c) { m_id = c->m_id; @@ -581,6 +620,8 @@ mgContentItem::mgContentItem (const mgContentItem* c) m_mp3file = c->m_mp3file; m_artist = c->m_artist; m_albumtitle = c->m_albumtitle; + m_genre1_id = c->m_genre1_id; + m_genre2_id = c->m_genre2_id; m_genre1 = c->m_genre1; m_genre2 = c->m_genre2; m_bitrate = c->m_bitrate; @@ -591,6 +632,55 @@ mgContentItem::mgContentItem (const mgContentItem* c) m_channels = c->m_channels; } +#if VDRVERSNUM >= 10318 +static char *readline(FILE *f) +{ + static char buffer[MAXPARSEBUFFER]; + if (fgets(buffer, sizeof(buffer), f) > 0) { + int l = strlen(buffer) - 1; + if (l >= 0 && buffer[l] == '\n') + buffer[l] = 0; + return buffer; + } + return 0; +} +#endif + +static const char *FINDCMD = "cd '%s' 2>/dev/null && find -follow -name '%s' -print 2>/dev/null"; + +static string +GdFindFile( string tld, string mp3file ) +{ + string result = ""; + char *cmd = 0; + asprintf( &cmd, FINDCMD, tld.c_str(), mp3file.c_str() ); + FILE *p = popen( cmd, "r" ); + if (p) + { + char *s; + if( (s = readline(p) ) != 0) + result = tld + s; + pclose(p); + } + + free( cmd ); + + return result; +} + +string +mgContentItem::getSourceFile(bool AbsolutePath) const +{ + const string& tld = the_setup.ToplevelDir; + string result=""; + if (AbsolutePath) result = tld; + if (the_setup.GdCompatibility) + result += GdFindFile(tld,m_mp3file); + else + result += m_mp3file; + return result; +} + mgContentItem::mgContentItem (const mgSelection* sel,const MYSQL_ROW row) { m_id = atol (row[0]); @@ -605,17 +695,23 @@ mgContentItem::mgContentItem (const mgSelection* sel,const MYSQL_ROW row) if (row[3]) m_artist = row[3]; else - m_artist = row[3]; + m_artist = "NULL"; if (row[4]) m_albumtitle = row[4]; else m_albumtitle = "NULL"; if (row[5]) - m_genre1 = sel->value(keyGenre1,row[5]); + { + m_genre1_id = row[5]; + m_genre1 = sel->value(keyGenres,row[5]); + } else m_genre1 = "NULL"; if (row[6]) - m_genre2 = sel->value(keyGenre2,row[5]); + { + m_genre2_id = row[6]; + m_genre2 = sel->value(keyGenres,row[6]); + } else m_genre2 = "NULL"; if (row[7]) @@ -648,31 +744,24 @@ void mgSelection::InitSelection() { m_Directory="."; InitDatabase(); m_level = 0; - m_position.reserve (20); + m_position = 0; m_tracks_position = 0; m_trackid = -1; m_shuffle_mode = SM_NONE; m_loop_mode = LM_NONE; clearCache(); values.setOwner(this); + if (!needGenre2_set) + { + needGenre2_set=true; + needGenre2=exec_count("SELECT COUNT(DISTINCT genre2) from tracks")>1; + } } -mgSelection::mgSelection() -{ - setDB(0); - m_Host = ""; - m_User = ""; - m_Password = ""; - InitSelection (); - m_fall_through = false; -} -mgSelection::mgSelection (const string Host, const string User, const string Password, const bool fall_through) +mgSelection::mgSelection (const bool fall_through) { setDB(0); - m_Host = Host; - m_User = User; - m_Password = Password; InitSelection (); m_fall_through = fall_through; } @@ -700,36 +789,35 @@ mgSelection::setDB(MYSQL *db) } void +mgSelection::setOrder(mgOrder* o) +{ + if (o) + { + order = *o; + order.setDB(m_db); + } + else + mgWarning("mgSelection::setOrder(0)"); +} + +void mgSelection::InitFrom(mgValmap& nv) { setDB(0); - m_Host = nv.getstr("Host"); - m_User = nv.getstr("User"); - m_Password = nv.getstr("Password"); InitSelection(); m_fall_through = nv.getbool("FallThrough"); m_Directory = nv.getstr("Directory"); - for (unsigned int i = 0; i < 99 ; i++) - { - char *idx; - asprintf(&idx,"Keys.%u.Choice",i); - unsigned int v = nv.getuint(idx); - free(idx); - if (v==0) break; - setKey (i,mgKeyTypes(v) ); - } while (m_level < nv.getuint("Level")) { char *idx; - asprintf(&idx,"Keys.%u.Position",m_level); - unsigned int newpos = nv.getuint(idx); + asprintf(&idx,"order.Keys.%u.Position",m_level); + string newval = nv.getstr(idx); free(idx); - if (!enter (newpos)) - if (!select (newpos)) break; + if (!enter (newval)) + if (!select (newval)) break; } m_trackid = nv.getlong("TrackId"); - // TODO do we really need Position AND TrackPosition in muggle.state? - setPosition(nv.getlong("Position")); + setPosition(nv.getstr("Position")); if (m_level>=order.size()-1) setTrackPosition(nv.getlong("TrackPosition")); setShuffleMode(ShuffleMode(nv.getuint("ShuffleMode"))); @@ -740,18 +828,12 @@ mgSelection::InitFrom(mgValmap& nv) mgSelection::~mgSelection () { if (m_db) - { - mgDebug(3,"%X: closing m_db %X",this,m_db); mysql_close (m_db); - } } void mgSelection::InitFrom(const mgSelection* s) { setDB(0); - m_Host = s->m_Host; - m_User = s->m_User; - m_Password = s->m_Password; InitSelection(); m_fall_through = s->m_fall_through; m_Directory = s->m_Directory; @@ -759,9 +841,7 @@ void mgSelection::InitFrom(const mgSelection* s) map_ids = s->map_ids; order = s->order; m_level = s->m_level; - m_position.reserve (s->m_position.capacity()); - for (unsigned int i = 0; i < s->m_position.capacity(); i++) - m_position[i] = s->m_position[i]; + m_position = s->m_position; m_trackid = s->m_trackid; m_tracks_position = s->m_tracks_position; setShuffleMode (s->getShuffleMode ()); @@ -770,7 +850,7 @@ void mgSelection::InitFrom(const mgSelection* s) const mgSelection& mgSelection::operator=(const mgSelection &s) { - if ((&m_Host)==&(s.m_Host)) { // prevent s = s + if (this==&s) { // prevent s = s return *this; } InitFrom(&s); @@ -785,7 +865,7 @@ mgSelection::ordersize () } unsigned int -mgSelection::valindex (const string val,const bool second_try) +mgSelection::valindex (const string val,const bool second_try) const { for (unsigned int i = 0; i < values.size (); i++) { @@ -794,8 +874,9 @@ mgSelection::valindex (const string val,const bool second_try) } // nochmal mit neuen Werten: clearCache(); + refreshValues(); if (second_try) { - mgWarning("valindex: Gibt es nicht:%s",val.c_str()); + esyslog("valindex: Gibt es nicht:%s",val.c_str()); return 0; } else @@ -803,13 +884,32 @@ mgSelection::valindex (const string val,const bool second_try) } +unsigned int +mgSelection::idindex (const string id,const bool second_try) const +{ + for (unsigned int i = 0; i < m_ids.size (); i++) + if (m_ids[i] == id) + return i; + // nochmal mit neuen Werten: + clearCache(); + refreshValues(); + if (second_try) { + mgWarning("idindex: Gibt es nicht:%s",id.c_str()); + return 0; + } + else + return idindex(id,true); +} + + void mgSelection::refreshValues () const { + assert(this); if (!m_db) return; - mgOrder o1 = order; if (m_current_values.empty()) { + mgOrder o1 = order; mgParts p = order.Parts(m_level); m_current_values = p.sql_select(); values.strings.clear (); @@ -819,16 +919,16 @@ mgSelection::refreshValues () const { unsigned int num_fields = mysql_num_fields(rows); MYSQL_ROW row; - while ((row = mysql_fetch_row (rows)) != NULL) + while ((row = mysql_fetch_row (rows)) != 0) { - string r0 = "NULL"; - if (row[0]) - r0 = row[0]; + if (!row[0]) continue; + string r0 = row[0]; + if (r0=="NULL") // there is a genre NULL! + continue; if (num_fields==2) { - string r1 = "NULL"; - if (row[1]) - r1 = row[1]; + if (!row[1]) continue; + string r1 = row[1]; values.strings.push_back (r0); m_ids.push_back (r1); } @@ -848,66 +948,57 @@ mgSelection::count () const { return values.size (); } - void mgSelection::InitDatabase () { if (m_db) { - mgDebug(3,"%X: InitDatabase closes %X",this,m_db); mysql_close (m_db); setDB(0); } - if (m_Host == "") return; + if (the_setup.DbHost == "") return; setDB(mysql_init (0)); - mgDebug(3,"%X: InitDatabase opens %X",this, m_db); if (!m_db) return; - if (mysql_real_connect (m_db, m_Host.c_str (), m_User.c_str (), m_Password.c_str (), - "GiantDisc", 0, NULL, 0) == NULL) { - mgWarning("Failed to connect to host '%s' as User '%s', Password '%s': Error: %s", - m_Host.c_str(),m_User.c_str(),m_Password.c_str(),mysql_error(m_db)); - mysql_close (m_db); - setDB(0); - return; - } - return; -} - - -void -mgSelection::setKey (const unsigned int level, const mgKeyTypes kt) -{ - mgKey *newkey = ktGenerate(kt,m_db); - if (level == 0 && kt == keyCollection) + bool success; + if (the_setup.DbSocket != NULL) { - order.clear (); - order += newkey; - order += ktGenerate(keyCollectionItem,m_db); - return; + mgDebug(1,"Using socket %s for connecting to Database %s as user %s.", + the_setup.DbSocket, + the_setup.DbName, + the_setup.DbUser); + mgDebug(3,"DbPassword is: '%s'",the_setup.DbPass); + success = (mysql_real_connect( m_db, + "", + the_setup.DbUser, + the_setup.DbPass, + the_setup.DbName, + 0, + the_setup.DbSocket, 0 ) != 0 ); } - if (level == order.size ()) + else { - order += newkey; + mgDebug(1,"Using TCP-%s for connecting to Database %s as user %s.", + the_setup.DbHost, + the_setup.DbName, + the_setup.DbUser); + mgDebug(3,"DbPassword is: '%s'",the_setup.DbPass); + success = ( mysql_real_connect( m_db, + the_setup.DbHost, + the_setup.DbUser, + the_setup.DbPass, + the_setup.DbName, + the_setup.DbPort, + 0, 0 ) != 0 ); } - else + if (!success) { - if (level >= order.size()) - mgError("mgSelection::setKey(%u,%s): level greater than order.size() %u", - level,ktName(kt),order.size()); - delete order[level]; - order[level] = newkey; + mgWarning("Failed to connect to host '%s' as User '%s', Password '%s': Error: %s", + the_setup.DbHost,the_setup.DbUser,the_setup.DbPass,mysql_error(m_db)); + mysql_close (m_db); + setDB(0); } - - order.clean(); - -// clear values for this and following levels (needed for copy constructor) - for (unsigned int i = level; i < order.size (); i++) - order[i]->set ("",EMPTY); - - if (m_level > level) - m_level = level; - if (m_level == level) setPosition(0); + return; } @@ -921,9 +1012,11 @@ bool mgSelection::enter (unsigned int position) position = gotoPosition(); // reload adjusted position string value = values[position]; string id = m_ids[position]; + mgSelStrings prev; + if (m_fall_through && values.size()<10) + prev=values; while (1) { - mgDebug(3,"enter(level=%u,pos=%u, id=%s)",m_level,position,id.c_str()); if (m_level >= order.size () - 1) return false; order[m_level++]->set (value,id); @@ -931,18 +1024,18 @@ bool mgSelection::enter (unsigned int position) if (m_level >= order.size()) mgError("mgSelection::enter(%u): level greater than order.size() %u", m_level,order.size()); - if (m_position.capacity () == m_position.size ()) - m_position.reserve (m_position.capacity () + 10); - m_position[m_level] = 0; + m_position = 0; + refreshValues(); + if (count()==0) + break; + value = values[0]; + id = m_ids[0]; if (!m_fall_through) break; - if (count () > 1) + if (m_level==order.size()-1) + break; + if (count () > 1 && !(prev==values)) break; - if (count () == 1) - { - value = values[0]; - id = m_ids[0]; - } } return true; } @@ -950,7 +1043,6 @@ bool mgSelection::enter (unsigned int position) bool mgSelection::select (unsigned int position) { - mgDebug(3,"select(%u) on Level %d",position,m_level); if (m_level == order.size () - 1) { if (getNumTracks () <= position) @@ -966,9 +1058,10 @@ bool mgSelection::select (unsigned int position) return enter (position); } - -bool mgSelection::leave () +bool +mgSelection::leave () { + string prevvalue; if (order.empty()) { mgWarning("mgSelection::leave(): order is empty"); @@ -977,29 +1070,103 @@ bool mgSelection::leave () if (m_level == order.size ()) { m_level--; + prevvalue=order.getKeyValue(m_level); + order[m_level]->set("",EMPTY); m_trackid = -1; clearCache(); + setPosition(prevvalue); return true; } + mgSelStrings prev; + if (m_fall_through && values.size()<10) + prev=values; while (1) { if (m_level < 1) return false; - order[m_level]->set ("",EMPTY); - order[--m_level]->set ("",EMPTY); + if (m_level<order.size()) order[m_level]->set ("",EMPTY); + m_level--; + prevvalue=order.getKeyValue(m_level); + if (m_level<order.size()) order[m_level]->set ("",EMPTY); clearCache(); if (!m_fall_through) break; - if (count () > 1) + if (count () > 1 && !(prev==values)) break; } + setPosition(prevvalue); return true; } +void +mgSelection::leave_all () +{ + m_level=0; + for (unsigned int i=0;i<ordersize();i++) + order[i]->set ("",EMPTY); + clearCache(); +} + +void +mgSelection::selectfrom(mgOrder& oldorder,mgContentItem* o) +{ + leave_all(); + string selval; + string selid; + assert(m_level==0); + for (unsigned int idx = 0; idx < ordersize(); idx++) + { + selval = EMPTY; + selid = EMPTY; + mgKeyTypes new_kt = getKeyType(idx); + for (unsigned int i=0;i<oldorder.size();i++) + { + mgKeyTypes old_kt = oldorder.getKeyType(i); + if (old_kt==new_kt) + { + selval = oldorder.getKeyValue(i); + selid = oldorder.getKeyId(i); + } + else if (old_kt>new_kt + && iskeyGenre(old_kt) + && iskeyGenre(new_kt)) + { + selid = id(new_kt,value(new_kt,oldorder.getKeyId(i))); + selval= value(new_kt,selid); + } + if (selid!=EMPTY) break; + } + if (selid==EMPTY && o && o->getId()>=0) + { + selval = o->getKeyValue(new_kt); + selid = o->getKeyId(new_kt); + } + if (selid==EMPTY) + break; + if (m_level<ordersize()-1) + { + order[m_level++]->set (selval, selid); + } + else + { + setPosition(selval); + return; + } + } + if (m_level>0) + { + m_level--; + selval = order.getKeyValue(m_level); + order[m_level]->set("",EMPTY); + setPosition(selval); + order[m_level+1]->set("",EMPTY); + } + assert(m_level<ordersize()); +} + string mgSelection::value(mgKeyTypes kt, string id) const { - if (kt==keyGenre2) kt = keyGenre1; if (loadvalues (kt)) { map<string,string>& valmap = map_values[kt]; @@ -1035,11 +1202,14 @@ mgSelection::value(mgKey* k) const string mgSelection::id(mgKeyTypes kt, string val) const { - if (kt==keyGenre2) kt = keyGenre1; if (loadvalues (kt)) { map<string,string>& idmap = map_ids[kt]; - return idmap[val]; + string v = idmap[val]; + if (kt==keyGenre1) v=v.substr(0,1); + if (kt==keyGenre2) v=v.substr(0,2); + if (kt==keyGenre3) v=v.substr(0,3); + return v; } else return val; @@ -1058,12 +1228,12 @@ mgSelection::id(mgKey* k) const } bool -mgSelection::UsedBefore(const mgKeyTypes kt,unsigned int level) const +mgSelection::UsedBefore(mgOrder *o,const mgKeyTypes kt,unsigned int level) const { - if (level>=order.size()) - level = order.size() -1; + if (level>=o->size()) + level = o->size() -1; for (unsigned int lx = 0; lx < level; lx++) - if (order.Key(lx)->Type()==kt) + if (o->getKeyType(lx)==kt) return true; return false; } @@ -1072,16 +1242,16 @@ mgSelection::UsedBefore(const mgKeyTypes kt,unsigned int level) const bool mgSelection::isCollectionlist () const { if (order.size()==0) return false; - return (order.Key(0)->Type() == keyCollection && m_level == 0); + return (order.getKeyType(0) == keyCollection && m_level == 0); } bool mgSelection::inCollection(const string Name) const { if (order.size()==0) return false; - bool result = (order.Key(0)->Type() == keyCollection && m_level == 1); + bool result = (order.getKeyType(0) == keyCollection && m_level == 1); if (result) - if (order.Key(1)->Type() != keyCollectionItem) + if (order.getKeyType(1) != keyCollectionItem) mgError("inCollection: key[1] is not keyCollectionItem"); if (!Name.empty()) result &= (order.getKeyValue(0) == Name); @@ -1089,52 +1259,8 @@ mgSelection::inCollection(const string Name) const } -#if 0 -void -keychoice(mgOrder& o,const unsigned int level) -{ - if (level > o.size ()) - return; - std::cout<<"possible choices:"; - for (mgKeyTypes kt = mgKeyTypes(1); kt <= mgKeyTypesHigh; kt = mgKeyTypes(int(kt)+1)) - { - if (level !=0 && kt == keyCollection) - continue; - if (level !=1 && kt == keyCollectionItem) - continue; - if (level == 1 && o[0]->Type() != keyCollection && kt == keyCollectionItem) - continue; - if (level == 1 && o[0]->Type() == keyCollection && kt != keyCollectionItem) - continue; - if (level >1 && o[0]->Type() == keyCollection) - break; - if (kt == keyDecade && UsedBefore(o,keyYear,level)) - continue; - if (o[0]->Type() == keyCollection) - { - std::cout<<" "<<ktName(kt); - } - else if (!UsedBefore(o,kt,level)) - { - if (keycounts[kt]==-1) - { - mgOrder oc(db); - oc += ktGenerate(kt,db); - keycounts[kt]=atol(get_col0(oc.Parts(0).sql_count()).c_str()); - } - if (keycounts[kt]>1) - std::cout<<" "<<ktName(kt)<<"("<<keycounts[kt]<<")"; - } - } - std::cout<<endl; -} -#endif - void mgSelection::DumpState(mgValmap& nv) const { - nv.put("Host",m_Host); - nv.put("User",m_User); - nv.put("Password",m_Password); nv.put("FallThrough",m_fall_through); nv.put("ShuffleMode",int(m_shuffle_mode)); nv.put("LoopMode",int(m_loop_mode)); @@ -1142,36 +1268,31 @@ void mgSelection::DumpState(mgValmap& nv) const nv.put("Level",int(m_level)); for (unsigned int i=0;i<order.size();i++) { - char *n; - asprintf(&n,"Keys.%d.Choice",i); - nv.put(n,int(order.Key(i)->Type())); - free(n); if (i<m_level) { - asprintf(&n,"Keys.%d.Position",i); - nv.put(n,m_position[i]); + char *n; + asprintf(&n,"order.Keys.%u.Position",i); + nv.put(n,getKeyValue(i)); free(n); } } nv.put("TrackId",m_trackid); - if (m_level == order.size ()) - nv.put("Position",getTrackPosition()); - else - nv.put("Position",m_position[m_level]); - nv.put("TrackPosition",getTrackPosition()); + nv.put("Position",values[m_position]); + if (m_level>=order.size()-1) + nv.put("TrackPosition",getTrackPosition()); } -map <mgKeyTypes, string> * +map <mgKeyTypes, string> mgSelection::UsedKeyValues() { - map <mgKeyTypes, string> *result = new map<mgKeyTypes, string>; + map <mgKeyTypes, string> result; for (unsigned int idx = 0 ; idx < level() ; idx++) { - (*result)[order.Key(idx)->Type()] = order.getKeyValue(idx); + result[order.getKeyType(idx)] = order.getKeyValue(idx); } if (level() < order.size()-1) { mgKeyTypes ch = order.getKeyType(level()); - (*result)[ch] = getCurrentValue(); + result[ch] = getCurrentValue(); } return result; } @@ -1179,18 +1300,15 @@ mgSelection::UsedKeyValues() bool mgSelection::loadvalues (mgKeyTypes kt) const { + if (map_ids.count(kt)>0) + return true; + map<string,string>& idmap = map_ids[kt]; mgKey* k = ktGenerate(kt,m_db); if (k->map_idfield().empty()) { delete k; return false; } - map<string,string>& idmap = map_ids[kt]; - if (!idmap.empty()) - { - delete k; - return true; - } map<string,string>& valmap = map_values[kt]; char *b; asprintf(&b,"select %s,%s from %s;",k->map_idfield().c_str(),k->map_valuefield().c_str(),k->map_valuetable().c_str()); @@ -1199,14 +1317,82 @@ mgSelection::loadvalues (mgKeyTypes kt) const if (rows) { MYSQL_ROW row; - while ((row = mysql_fetch_row (rows)) != NULL) + while ((row = mysql_fetch_row (rows)) != 0) { if (row[0] && row[1]) + { valmap[row[0]] = row[1]; idmap[row[1]] = row[0]; + } } mysql_free_result (rows); } delete k; return true; } + +static vector<int> keycounts; + +unsigned int +mgSelection::keycount(mgKeyTypes kt) +{ + assert(strlen(m_db->host)); + if (keycounts.size()==0) + { + for (unsigned int ki=int(mgKeyTypesLow);ki<=int(mgKeyTypesHigh);ki++) + { + keycounts.push_back(-1); + } + } + int& count = keycounts[int(kt-mgKeyTypesLow)]; + if (count==-1) + { + mgKey* k = ktGenerate(kt,m_db); + count = exec_count(k->Parts(true).sql_count()); + delete k; + } + return count; +} + + +vector <const char *> +mgSelection::choices(mgOrder *o,unsigned int level, unsigned int *current) +{ + vector<const char*> result; + if (level>o->size()) + { + *current = 0; + return result; + } + for (unsigned int ki=int(mgKeyTypesLow);ki<=int(mgKeyTypesHigh);ki++) + { + mgKeyTypes kt = mgKeyTypes(ki); + if (kt==o->getKeyType(level)) + { + *current = result.size(); + result.push_back(ktName(kt)); + continue; + } + if (UsedBefore(o,kt,level)) + continue; + if (kt==keyDecade && UsedBefore(o,keyYear,level)) + continue; + if (kt==keyGenre1 && UsedBefore(o,keyGenres,level)) + continue; + if (kt==keyGenre2 && UsedBefore(o,keyGenres,level)) + continue; + if (kt==keyGenre3 && UsedBefore(o,keyGenres,level)) + continue; + if (kt==keyGenre1 && UsedBefore(o,keyGenre3,level)) + continue; + if (kt==keyGenre2 && UsedBefore(o,keyGenre3,level)) + continue; + if (kt==keyGenre1 && UsedBefore(o,keyGenre2,level)) + continue; + if (kt==keyCollection || kt==keyCollectionItem) + result.push_back(ktName(kt)); + else if (keycount(kt)>1) + result.push_back(ktName(kt)); + } + return result; +} @@ -37,11 +37,10 @@ class mgSelection; class mgContentItem { public: - mgContentItem () - { - } + mgContentItem (); string getKeyValue(mgKeyTypes kt); + string getKeyId(mgKeyTypes kt); //! \brief copy constructor mgContentItem(const mgContentItem* c); @@ -61,10 +60,7 @@ class mgContentItem } //! \brief returns filename - string getSourceFile () const - { - return m_mp3file; - } + string getSourceFile (bool AbsolutePath=true) const; //! \brief returns artist string getArtist () const @@ -75,13 +71,7 @@ class mgContentItem //! \brief returns the name of the album string getAlbum () const; -//! \brief returns the name of genre 1 - string getGenre1 () const; - -//! \brief returns the name of genre 2 - string getGenre2 () const; - -//! \brief returns the name of genre 1 +//! \brief returns the name of genre string getGenre () const; //! \brief returns the bitrate @@ -111,6 +101,8 @@ class mgContentItem string m_mp3file; string m_artist; string m_albumtitle; + string m_genre1_id; + string m_genre2_id; string m_genre1; string m_genre2; string m_bitrate; @@ -140,20 +132,18 @@ class mgSelection void setOwner(mgSelection* sel); public: string& operator[](unsigned int idx); - size_t size(); + bool operator==(const mgSelStrings&x) const; + size_t size() const; }; - public: +//! \brief defines an order to be used + void setOrder(mgOrder *o); + + mgOrder& getOrder() { return order; } + /*! \brief define various ways to play music in random order * \todo Party mode is not implemented, does same as SM_NORMAL */ -/*! \brief defines a field to be used as key for selection - * - * \param level 0 is the top level - * \param kt type of the key field. For possible values see mg_order.h - */ - void setKey (const unsigned int level, const mgKeyTypes kt); - enum ShuffleMode { SM_NONE, //!< \brief play normal sequence @@ -172,20 +162,12 @@ class mgSelection //! \brief escapes special characters string sql_string(const string s) const; -//! \brief the default constructor. Does not start a DB connection. - mgSelection(); - /*! \brief the main constructor - * \param Host where the data base lives. If not localhost, TCP/IP is used. - * \param User if empty, the current user is used. - * \param Password no comment * \param fall_through if TRUE: If enter() returns a choice * containing only one item, that item is automatically entered. * The analog happens with leave() */ - mgSelection (const string Host, const string User = - "", const string Password = "", const bool fall_through = - false); + mgSelection ( const bool fall_through = false); /*! \brief a copy constructor. Does a deep copy. * Some of the data base content will only be retrieved by the @@ -226,13 +208,14 @@ class mgSelection //! \brief return the current value of this key string getKeyValue (const unsigned int level) const; + unsigned int getKeyIndex(const unsigned int level) const; /*! \brief returns the current item from the value() list */ string getCurrentValue(); //! \brief returns a map (new allocated) for all used key fields and their values - map<mgKeyTypes,string> * UsedKeyValues(); + map<mgKeyTypes,string> UsedKeyValues(); //! \brief the number of key fields used for the query unsigned int ordersize (); @@ -240,18 +223,12 @@ class mgSelection //! \brief the number of music items currently selected unsigned int count () const; -//! \brief the current position in the current level - unsigned int gotoPosition () - { - return gotoPosition (m_level); - } - //! \brief the current position - unsigned int getPosition (unsigned int level)const; + unsigned int getPosition ()const; //! \brief go to the current position. If it does not exist, // go to the nearest. - unsigned int gotoPosition (unsigned int level); + unsigned int gotoPosition (); //! \brief the current position in the tracks list @@ -307,6 +284,13 @@ class mgSelection return select (valindex(value)); } + bool selectid (const string id) + { + return select(idindex(id)); + } + + void selectfrom(mgOrder& oldorder,mgContentItem* o); + /*! \brief leave the current level, go one up in the tree. * If fall_through (see constructor) is set to true, and the * level entered by leave() contains only one item, automatically @@ -322,12 +306,7 @@ class mgSelection * goes up further until a level with more than one item is reached. * \return returns false if there is no further upper level */ - bool leave (const unsigned int target_level) - { - while (m_level>target_level) - if (!leave()) return false; - return true; - } + void leave_all (); //! \brief the current level in the tree unsigned int level () const @@ -529,6 +508,8 @@ class mgSelection string id(mgKeyTypes kt, string val) const; string id(mgKey* k, string val) const; string id(mgKey* k) const; + unsigned int keycount(mgKeyTypes kt); + vector <const char *> choices(mgOrder *o,unsigned int level, unsigned int *current); private: mutable map <mgKeyTypes, map<string,string> > map_values; @@ -541,20 +522,17 @@ class mgSelection //! \brief initializes maps for id/value mapping in both direction bool loadvalues (mgKeyTypes kt) const; bool m_fall_through; - vector < unsigned int >m_position; + unsigned int m_position; mutable unsigned int m_tracks_position; ShuffleMode m_shuffle_mode; LoopMode m_loop_mode; MYSQL *m_db; void setDB(MYSQL *db); - string m_Host; - string m_User; - string m_Password; unsigned int m_level; long m_trackid; mgOrder order; - bool UsedBefore (const mgKeyTypes kt, unsigned int level) const; + bool UsedBefore (mgOrder *o,const mgKeyTypes kt, unsigned int level) const; void InitSelection (); void InitDatabase (); /*! \brief returns the SQL command for getting all values. @@ -565,7 +543,8 @@ class mgSelection * entries and the wrong tracks might be played. */ string sql_values (); - unsigned int valindex (const string val,const bool second_try=false); + unsigned int valindex (const string val,const bool second_try=false) const; + unsigned int idindex (const string val,const bool second_try=false) const; string ListFilename (); string m_Directory; void loadgenres (); @@ -579,9 +558,9 @@ class mgSelection * returning only one row. * \param query the SQL query to be executed */ - unsigned long mgSelection::exec_count (string query) const; - + unsigned long exec_count (string query) const; + }; @@ -2,6 +2,12 @@ #include "mg_tools.h" #include "i18n.h" + +bool iskeyGenre(mgKeyTypes kt) +{ + return kt>=keyGenre1 && kt <= keyGenres; +} + class mgRefParts : public mgParts { public: mgRefParts(const mgReference& r); @@ -27,16 +33,6 @@ sql_string (MYSQL *db, const string s) return result; } -//! \brief adds n1=n2 to string s, using AND to separate several such items -static string -undequal (string & s, string n1, string op, string n2) -{ - if (n1.compare (n2) || op != "=") - return addsep (s, " AND ", n1 + op + n2); - else - return s; -} - /*! \brief if the SQL command works on only 1 table, remove all table * qualifiers. Example: SELECT tracks.title FROM tracks becomes SELECT title * FROM tracks @@ -83,28 +79,98 @@ exec_sql (MYSQL *db,string query) return mysql_store_result (db); } +string +get_col0(MYSQL *db, string query) +{ + MYSQL_RES * sql_result = exec_sql (db, query); + if (!sql_result) + return "NULL"; + MYSQL_ROW row = mysql_fetch_row (sql_result); + string result; + if (row == NULL) + result = "NULL"; + else if (row[0] == NULL) + result = "NULL"; + else + result = row[0]; + mysql_free_result (sql_result); + return result; +} + +int +exec_count(MYSQL *db, string query) +{ + return atol (get_col0 (db, query).c_str ()); +} + + +string& +addsep (string & s, string sep, string n) +{ + if (!n.empty ()) + { + if (!s.empty ()) + s.append (sep); + s.append (n); + } + return s; +} + + +static string +sql_list (string prefix,strlist v,string sep=",",string postfix="") +{ + string result = ""; + for (list < string >::iterator it = v.begin (); it != v.end (); ++it) + { + addsep (result, sep, *it); + } + if (!result.empty()) + { + result.insert(0," "+prefix+" "); + result += postfix; + } + return result; +} + +//! \brief converts long to string +string +itos (int i) +{ + stringstream s; + s << i; + return s.str (); +} + +//! \brief convert long to string +string +ltos (long l) +{ + stringstream s; + s << l; + return s.str (); +} class mgKeyNormal : public mgKey { public: mgKeyNormal(const mgKeyNormal& k); - mgKeyNormal(const mgKeyTypes kt, string table, string field); + mgKeyNormal(const mgKeyTypes kt, string table, string field); virtual mgParts Parts(bool orderby=false) const; string value() const; string id() const; void set(string value,string id); mgKeyTypes Type() const { return m_kt; } - virtual string expr() const { return field(); } - virtual string field() const { return m_table + "." + m_field; } + virtual string expr() const { return m_table + "." + m_field; } virtual string table() const { return m_table; } protected: - virtual string orderfield() const { return expr(); } + string IdClause(string what,string::size_type start=0,string::size_type len=string::npos) const; void AddIdClause(mgParts &result,string what) const; + string m_id; private: mgKeyTypes m_kt; string m_field; string m_table; string m_value; - string m_id; }; class mgKeyTrack : public mgKeyNormal { @@ -113,24 +179,96 @@ class mgKeyTrack : public mgKeyNormal { mgParts Parts(bool orderby=false) const; }; -class mgKeyGenre1 : public mgKeyNormal { +class mgKeyGenres : public mgKeyNormal { public: - mgKeyGenre1() : mgKeyNormal(keyGenre1,"tracks","genre1") {}; + mgKeyGenres() : mgKeyNormal(keyGenres,"tracks","genre1") {}; + mgKeyGenres(mgKeyTypes kt) : mgKeyNormal(kt,"tracks","genre1") {}; mgParts Parts(bool orderby=false) const; string map_idfield() const { return "id"; } string map_valuefield() const { return "genre"; } string map_valuetable() const { return "genre"; } + string GenreClauses(bool orderby) const; + virtual unsigned int genrelevel() const { return 4; } }; -class mgKeyGenre2 : public mgKeyNormal { +class mgKeyGenre1 : public mgKeyGenres +{ public: - mgKeyGenre2() : mgKeyNormal(keyGenre2,"tracks","genre2") {}; - mgParts Parts(bool orderby=false) const; - string map_idfield() const { return "id"; } - string map_valuefield() const { return "genre"; } - string map_valuetable() const { return "genre"; } + mgKeyGenre1() : mgKeyGenres(keyGenre1) {} + unsigned int genrelevel() const { return 1; } +}; + +class mgKeyGenre2 : public mgKeyGenres +{ + public: + mgKeyGenre2() : mgKeyGenres(keyGenre2) {} + unsigned int genrelevel() const { return 2; } +}; + +class mgKeyGenre3 : public mgKeyGenres +{ + public: + mgKeyGenre3() : mgKeyGenres(keyGenre3) {} + unsigned int genrelevel() const { return 3; } }; +string +mgKeyGenres::GenreClauses(bool orderby) const +{ + strlist g1; + strlist g2; + + if (orderby) + if (genrelevel()==4) + { + g1.push_back("tracks.genre1=genre.id"); + g2.push_back("tracks.genre2=genre.id"); + } + else + { + g1.push_back("substring(tracks.genre1,1,"+ltos(genrelevel())+")=genre.id"); + g2.push_back("substring(tracks.genre2,1,"+ltos(genrelevel())+")=genre.id"); + } + + if (id() != EMPTY) + { + unsigned int len=genrelevel(); + if (len==4) len=0; + g1.push_back(IdClause("tracks.genre1",0,genrelevel())); + g2.push_back(IdClause("tracks.genre2",0,genrelevel())); + } + + extern bool needGenre2; + if (needGenre2) + { + string o1=sql_list("(",g1," AND ",")"); + if (o1.empty()) + return ""; + string o2=sql_list("(",g2," AND ",")"); + return string("(") + o1 + " OR " + o2 + string(")"); + } + else + return sql_list("",g1," AND "); +} + + +mgParts +mgKeyGenres::Parts(bool orderby) const +{ + mgParts result; + result.clauses.push_back(GenreClauses(orderby)); + result.tables.push_back("tracks"); + if (orderby) + { + result.fields.push_back("genre.genre"); + result.fields.push_back("genre.id"); + result.tables.push_back("genre"); + result.orders.push_back("genre.genre"); + } + return result; +} + + class mgKeyLanguage : public mgKeyNormal { public: mgKeyLanguage() : mgKeyNormal(keyLanguage,"tracks","lang") {}; @@ -182,6 +320,11 @@ mgKey::~mgKey() { } +void +mgKey::setdb(MYSQL *db) +{ + m_db = db; +} mgKeyNormal::mgKeyNormal(const mgKeyNormal& k) { @@ -226,34 +369,35 @@ mgKeyNormal::Parts(bool orderby) const if (orderby) { result.fields.push_back(expr()); - result.orders.push_back(orderfield()); + result.orders.push_back(expr()); } return result; } -void -mgKeyNormal::AddIdClause(mgParts &result,string what) const +string +mgKeyNormal::IdClause(string what,string::size_type start,string::size_type len) const { assert(strlen(m_db->host)); - if (id() != EMPTY) + if (len==0) + len=string::npos; + if (id() == "'NULL'") + return what + " is NULL"; + else if (len==string::npos) + return what + "=" + sql_string(m_db,id()); + else { - string op; - string xid; - if (id() == "'NULL'") - { - op = "is"; - xid = "NULL"; - } - else - { - op = "="; - xid = sql_string(m_db,id()); - } - string clause = ""; - result.clauses.push_back(undequal(clause,what,op,xid)); + return "substring("+what + ","+ltos(start+1)+","+ltos(len)+")=" + + sql_string(m_db,id().substr(start,len)); } } +void +mgKeyNormal::AddIdClause(mgParts &result,string what) const +{ + if (id() != EMPTY) + result.clauses.push_back(IdClause(what)); +} + mgParts mgKeyTrack::Parts(bool orderby) const { @@ -271,38 +415,6 @@ mgKeyTrack::Parts(bool orderby) const } mgParts -mgKeyGenre1::Parts(bool orderby) const -{ - mgParts result; - AddIdClause(result,"tracks.genre1"); - result.tables.push_back("tracks"); - if (orderby) - { - result.fields.push_back("genre.genre"); - result.fields.push_back("tracks.genre1"); - result.tables.push_back("genre"); - result.orders.push_back("genre.genre"); - } - return result; -} - -mgParts -mgKeyGenre2::Parts(bool orderby) const -{ - mgParts result; - AddIdClause(result,"tracks.genre2"); - result.tables.push_back("tracks"); - if (orderby) - { - result.fields.push_back("genre.genre"); - result.fields.push_back("tracks.genre2"); - result.tables.push_back("genre"); - result.orders.push_back("genre.genre"); - } - return result; -} - -mgParts mgKeyLanguage::Parts(bool orderby) const { mgParts result; @@ -366,54 +478,6 @@ mgParts::operator+=(mgParts a) return *this; } - -string& -addsep (string & s, string sep, string n) -{ - if (!n.empty ()) - { - if (!s.empty ()) - s.append (sep); - s.append (n); - } - return s; -} - - -static string -sql_list (string prefix,list < string > v,string sep=",",string postfix="") -{ - string result = ""; - for (list < string >::iterator it = v.begin (); it != v.end (); it++) - { - addsep (result, sep, *it); - } - if (!result.empty()) - { - result.insert(0," "+prefix+" "); - result += postfix; - } - return result; -} - -//! \brief converts long to string -string -itos (int i) -{ - stringstream s; - s << i; - return s.str (); -} - -//! \brief convert long to string -string -ltos (long l) -{ - stringstream s; - s << l; - return s.str (); -} - mgRefParts::mgRefParts(const mgReference& r) { tables.push_back(r.t1()); @@ -428,7 +492,7 @@ mgParts::Prepare() tables.unique(); strlist::reverse_iterator it; string prevtable = ""; - for (it = tables.rbegin(); it != tables.rend(); it++) + for (it = tables.rbegin(); it != tables.rend(); ++it) { if (!prevtable.empty()) *this += ref.Connect(prevtable,*it); @@ -475,7 +539,7 @@ mgParts::sql_count() bool mgParts::UsesTracks() { - for (list < string >::iterator it = tables.begin (); it != tables.end (); it++) + for (list < string >::iterator it = tables.begin (); it != tables.end (); ++it) if (*it == "tracks") return true; return false; } @@ -525,6 +589,10 @@ mgReference::mgReference(string t1,string f1,string t2,string f2) mgOrder::mgOrder() { + setDB(0); + setKey (0,keyArtist); + setKey (1,keyAlbum); + setKey (2,keyTrack); } mgKey* @@ -540,10 +608,22 @@ mgOrder::operator[](unsigned int idx) return Keys[idx]; } +bool +operator==(const mgOrder& a, const mgOrder &b) +{ + bool result = a.size()==b.size(); + if (result) + for (unsigned int i=0; i<a.size();i++) + { + result &= a.Key(i)->Type()==b.Key(i)->Type(); + if (!result) break; + } + return result; +} + const mgOrder& mgOrder::operator=(const mgOrder& from) { - Name = from.Name; Keys.clear(); for (unsigned int i = 0; i < from.size();i++) { @@ -551,7 +631,7 @@ mgOrder::operator=(const mgOrder& from) k->set(from.getKeyValue(i),from.getKeyId(i)); Keys.push_back(k); } - setDB(from.m_db); + if (from.m_db) setDB(from.m_db); return *this; } @@ -562,6 +642,77 @@ mgOrder::operator+=(mgKey* k) { return *this; } +string +mgOrder::Name() +{ + string result=""; + for (unsigned int idx=0;idx<size();idx++) + { + if (!result.empty()) result += ":"; + result += ktName(Keys[idx]->Type()); + } + return result; +} + +void +mgOrder::setKey (const unsigned int level, const mgKeyTypes kt) +{ + mgKey *newkey = ktGenerate(kt,m_db); + if (level == 0 && kt == keyCollection) + { + clear (); + Keys.push_back(newkey); + Keys.push_back(ktGenerate(keyCollectionItem,m_db)); + return; + } + if (level == size ()) + { + Keys.push_back(newkey); + } + else + { + if (level >= Keys.size()) + mgError("mgOrder::setKey(%u,%s): level greater than size() %u", + level,ktName(kt),Keys.size()); + delete Keys[level]; + Keys[level] = newkey; + } + +// clear values for this and following levels (needed for copy constructor) + for (unsigned int i = level; i < Keys.size (); i++) + Keys[i]->set ("",EMPTY); +} + +mgOrder::mgOrder(mgValmap& nv,char *prefix) +{ + setDB(0); + for (unsigned int i = 0; i < 999 ; i++) + { + char *idx; + asprintf(&idx,"%s.Keys.%u.Type",prefix,i); + unsigned int v = nv.getuint(idx); + free(idx); + if (v==0) break; + setKey (i,mgKeyTypes(v) ); + } +} + +mgOrder::mgOrder(vector<mgKeyTypes> kt) +{ + setDB(0); + setKeys(kt); +} + +void +mgOrder::setKeys(vector<mgKeyTypes> kt) +{ + clear(); + for (unsigned int i=0;i<kt.size();i++) + setKey(size(),kt[i]); + clean(); +} + + mgKeyTypes mgOrder::getKeyType(unsigned int idx) const { @@ -588,7 +739,7 @@ mgOrder::setDB(MYSQL *db) { m_db = db; keyvector::iterator i; - for (i = Keys.begin () ; i != Keys.end (); i++) + for (i = Keys.begin () ; i != Keys.end (); ++i) { (*i)->setdb(db); } @@ -598,7 +749,7 @@ mgKey* mgOrder::find(const mgKeyTypes kt) { keyvector::iterator i; - for (i = Keys.begin () ; i != Keys.end (); i++) + for (i = Keys.begin () ; i != Keys.end (); ++i) { if ((*i)->Type() == kt) return *i; @@ -625,25 +776,47 @@ mgOrder::clean() bool album_found = false; bool tracknb_found = false; bool title_found = false; - for (i = Keys.begin () ; i != Keys.end (); i++) + bool is_unique = false; + for (i = Keys.begin () ; i != Keys.end (); ++i) { mgKeyNormal* k = dynamic_cast<mgKeyNormal*>(*i); album_found |= (k->Type()==keyAlbum); tracknb_found |= (k->Type()==keyTrack); title_found |= (k->Type()==keyTitle); - if (tracknb_found || (album_found && title_found)) + is_unique = tracknb_found || (album_found && title_found); + if (is_unique) { - for (j = i+1 ; j !=Keys.end(); j++) + for (j = i+1 ; j !=Keys.end(); ++j) delete *j; Keys.erase(i+1,Keys.end ()); break; } - for (j = i+1 ; j != Keys.end(); j++) - if (*i == *j) { + if (k->Type()==keyYear) + { + for (j = i+1 ; j != Keys.end(); ++j) + if ((*j)->Type() == keyDecade) + { + delete *j; + Keys.erase(j); + break; + } + } +cleanagain: + for (j = i+1 ; j != Keys.end(); ++j) + if ((*i)->Type() == (*j)->Type()) + { delete *j; Keys.erase(j); + goto cleanagain; } } + if (!is_unique) + { + if (!album_found) + Keys.push_back(ktGenerate(keyAlbum,m_db)); + if (!title_found) + Keys.push_back(ktGenerate(keyTitle,m_db)); + } } @@ -657,18 +830,37 @@ mgOrder::Parts(unsigned int level,bool orderby) const if (i==Keys.size()) break; mgKeyNormal *k = dynamic_cast<mgKeyNormal*>(Keys[i]); k->setdb(m_db); + mgKeyTypes kt = k->Type(); + if (iskeyGenre(kt)) + { + for (unsigned int j=i+1;j<=level;j++) + { + if (j>=Keys.size()) + break; + mgKeyNormal *kn = dynamic_cast<mgKeyNormal*>(Keys[j]); + if (kn) + { + mgKeyTypes knt = kn->Type(); + if (iskeyGenre(knt) + && knt>kt && kn->id()!=EMPTY) + goto next; + } + } + } result += k->Parts(orderby && (i==level)); +next: + continue; } return result; } +//! \brief right now thread locking should not be needed here mgReferences::mgReferences() { push_back(mgReference ("tracks","id","playlistitem","trackid")); push_back(mgReference ("playlist","id","playlistitem","playlist")); push_back(mgReference ("tracks","sourceid","album","cddbid")); - push_back(mgReference ("tracks","genre1","genre","id")); - push_back(mgReference ("tracks","genre2","genre","id")); + push_back(mgReference ("tracks","lang","language","id")); } bool @@ -691,6 +883,8 @@ mgParts mgReferences::ConnectToTracks(string table) const { mgParts result; + if (table=="tracks") + return result; result += FindConnectionBetween(table,"tracks"); if (result.empty()) { @@ -711,6 +905,8 @@ mgReferences::Connect(string table1, string table2) const mgParts result; // same table? if (table1 == table2) return result; + if (table1=="genre") return ConnectToTracks(table2); + if (table2=="genre") return ConnectToTracks(table1); // do not connect aliases. See sql_delete_from_collection if (table1.find(" as ")!=string::npos) return result; if (table2.find(" as ")!=string::npos) return result; @@ -734,8 +930,10 @@ ktGenerate(const mgKeyTypes kt,MYSQL* db) mgKey* result = 0; switch (kt) { + case keyGenres: result = new mgKeyGenres;break; case keyGenre1: result = new mgKeyGenre1;break; case keyGenre2: result = new mgKeyGenre2;break; + case keyGenre3: result = new mgKeyGenre3;break; case keyArtist: result = new mgKeyNormal(kt,"tracks","artist");break; case keyTitle: result = new mgKeyNormal(kt,"tracks","title");break; case keyTrack: result = new mgKeyTrack;break; @@ -757,8 +955,10 @@ ktName(const mgKeyTypes kt) const char * result = ""; switch (kt) { - case keyGenre1: result = "Genre";break; - case keyGenre2: result = "Genre 2";break; + case keyGenres: result = "Genre";break; + case keyGenre1: result = "Genre1";break; + case keyGenre2: result = "Genre2";break; + case keyGenre3: result = "Genre3";break; case keyArtist: result = "Artist";break; case keyTitle: result = "Title";break; case keyTrack: result = "Track";break; @@ -773,3 +973,23 @@ ktName(const mgKeyTypes kt) return tr(result); } +mgKeyTypes +ktValue(const char * name) +{ + for (int kt=int(mgKeyTypesLow);kt<int(mgKeyTypesHigh);kt++) + if (!strcmp(name,ktName(mgKeyTypes(kt)))) + return mgKeyTypes(kt); + mgError("ktValue(%s): unknown name",name); + return mgKeyTypes(0); +} + + +vector<const char*> +ktNames() +{ + static vector<const char*> result; + for (unsigned int i = int(mgKeyTypesLow); i <= int(mgKeyTypesHigh); i++) + result.push_back(ktName(mgKeyTypes(i))); + return result; +} + @@ -12,6 +12,8 @@ #include <sstream> #include <ostream> +#include "mg_valmap.h" + using namespace std; typedef list<string> strlist; @@ -24,20 +26,26 @@ static const string EMPTY = "XNICHTGESETZTX"; string& addsep (string & s, string sep, string n); enum mgKeyTypes { - keyGenre1 = 1, + keyGenre1=1, // the genre types must have exactly this order! keyGenre2, + keyGenre3, + keyGenres, + keyDecade, + keyYear, keyArtist, + keyAlbum, keyTitle, keyTrack, - keyDecade, - keyCollection, - keyCollectionItem, - keyAlbum, keyLanguage, keyRating, - keyYear, + keyCollection, + keyCollectionItem, }; -const mgKeyTypes mgKeyTypesHigh = keyYear; +const mgKeyTypes mgKeyTypesLow = keyGenre1; +const mgKeyTypes mgKeyTypesHigh = keyCollectionItem; +const unsigned int mgKeyTypesNr = keyCollectionItem; + +bool iskeyGenre(mgKeyTypes kt); class mgParts; @@ -79,7 +87,7 @@ class mgKey { virtual string map_idfield() const { return ""; } virtual string map_valuefield() const { return ""; } virtual string map_valuetable() const { return ""; } - void setdb(MYSQL *db) { m_db = db; } + void setdb(MYSQL *db); protected: MYSQL *m_db; }; @@ -88,9 +96,10 @@ class mgKey { mgKey* ktGenerate(const mgKeyTypes kt,MYSQL *db); -const char * const -ktName(const mgKeyTypes kt); - +const char * const ktName(const mgKeyTypes kt); +mgKeyTypes ktValue(const char * name); +vector < const char*> ktNames(); + typedef vector<mgKey*> keyvector; class mgParts { @@ -117,6 +126,8 @@ string sql_string (MYSQL *db, const string s); MYSQL_RES * exec_sql (MYSQL *db,string query); +string get_col0 (MYSQL *db,string query); +int exec_count (MYSQL *db,string query); //! \brief converts long to string string itos (int i); @@ -130,9 +141,10 @@ const unsigned int MaxKeys = 20; class mgOrder { public: mgOrder(); + mgOrder(mgValmap& nv, char *prefix); + mgOrder(vector<mgKeyTypes> kt); void setDB(MYSQL *db); mgParts Parts(unsigned int level,bool orderby=true) const; - string Name; const mgOrder& operator=(const mgOrder& from); mgOrder& operator+=(mgKey* k); mgKey*& operator[](unsigned int idx); @@ -146,9 +158,14 @@ public: mgKeyTypes getKeyType(unsigned int idx) const; string getKeyValue(unsigned int idx) const; string getKeyId(unsigned int idx) const; + void setKeys(vector<mgKeyTypes> kt); + string Name(); private: MYSQL *m_db; keyvector Keys; + void setKey (const unsigned int level, const mgKeyTypes kt); }; +bool operator==(const mgOrder& a,const mgOrder&b); //! \brief compares only the order, not the current key values + #endif // _MG_SQL_H @@ -84,7 +84,8 @@ mgWarning (const char *fmt, ...) isyslog ("Warning: %s\n", buffer); #endif } - + extern void showmessage(const char*); + showmessage(buffer); va_end (ap); } @@ -21,7 +21,7 @@ #include <getopt.h> #include <config.h> -static const char *VERSION = "0.1.1"; +static const char *VERSION = "0.1.2"; static const char *DESCRIPTION = "Media juggle plugin for VDR"; static const char *MAINMENUENTRY = "Muggle"; @@ -23,6 +23,9 @@ #include <stdlib.h> #include <tag.h> +#include <mpegfile.h> +#include <flacfile.h> +#include <id3v2tag.h> #include <fileref.h> #include "mg_tools.h" @@ -35,6 +38,10 @@ bool import_assorted, delete_mode; #define MAX_QUERY_BUFLEN 2048 static char querybuf[MAX_QUERY_BUFLEN]; +void showmessage(const char *msg) +{ +} + MYSQL_RES* mgSqlReadQuery(MYSQL *db, const char *fmt, ...) { va_list ap; @@ -45,10 +52,6 @@ MYSQL_RES* mgSqlReadQuery(MYSQL *db, const char *fmt, ...) { mgError( "SQL error in MUGGLE:\n%s\n", querybuf ); } - -#ifdef VERBOSE - std::cout << querybuf << std::endl; -#endif MYSQL_RES *result = mysql_store_result(db); @@ -67,10 +70,6 @@ void mgSqlWriteQuery(MYSQL *db, const char *fmt, ...) mgError( "SQL error in MUGGLE:\n%s\n", querybuf ); } -#ifdef VERBOSE - std::cout << querybuf << std::endl; -#endif - va_end(ap); } @@ -142,23 +141,14 @@ time_t get_db_modification_time( long uid ) TagLib::String escape_string( MYSQL *db, TagLib::String s ) { - TagLib::String r; + char *buf = strdup( s.toCString() ); + char *escbuf = (char *) malloc( 2*strlen( buf ) + 1 ); - if( !s.isNull() && !s.isEmpty() ) - { - char *buf = strdup( s.toCString() ); - char *escbuf = (char *) malloc( 2*strlen( buf ) + 1 ); + mysql_real_escape_string( db, escbuf, s.toCString(), s.size() ); + TagLib::String r = TagLib::String( escbuf ); - mysql_real_escape_string( db, escbuf, s.toCString(), s.size() ); - r = TagLib::String( escbuf ); - - free( escbuf ); - free( buf); - } - else - { - r = s; - } + free( escbuf ); + free( buf); return r; } @@ -204,88 +194,48 @@ TagLib::String find_genre_id( TagLib::String genre ) // read tags from the mp3 file and store them into the corresponding database entry void update_db( long uid, std::string filename ) { - TagLib::String title, album, artist, genre, cddbid; + TagLib::String title, album, artist, genre, cddbid, language; uint trackno, year; // ID3_Tag filetag( filename.c_str() ); TagLib::FileRef f( filename.c_str() ); - if( !f.isNull() ) + if( !f.isNull() && f.tag() ) { -#ifdef VERBOSE - std::cout << "Evaluating " << filename << std::endl; -#endif + // std::cout << "Evaluating " << filename << std::endl; TagLib::Tag *tag = f.tag(); - if( tag ) - { - // obtain tag information - title = tag->title(); - album = tag->album(); - year = tag->year(); - artist = tag->artist(); - trackno = tag->track(); - genre = tag->genre(); - -#ifdef VERBOSE - std::cout << "-- TAG --" << std::endl; - std::cout << "title - '" << tag->title() << "'" << std::endl; - std::cout << "artist - '" << tag->artist() << "'" << std::endl; - std::cout << "album - '" << tag->album() << "'" << std::endl; - std::cout << "year - '" << tag->year() << "'" << std::endl; - std::cout << "comment - '" << tag->comment() << "'" << std::endl; - std::cout << "track - '" << tag->track() << "'" << std::endl; - std::cout << "genre - '" << tag->genre() << "'" << std::endl; -#endif - } - else - { -#ifdef VERBOSE - std::cerr << "No id3 tag found." << std::endl; -#endif - - // use basename - TagLib::String file( filename.c_str() ); - - int pos = title.size(); - while( pos > 0 && title[pos] != '\\' ) - { - pos --; - } - - title = file.substr( pos ); - - // will be associated to an Unassigned album for Unknown - album = ""; - artist = "Unknown"; - - year = 0; - trackno = 0; - } - - if( title.isNull() || title.isEmpty() ) - { -#ifdef VERBOSE - std::cout << "No title tag found." << std::endl; -#endif - // use basename - TagLib::String file( filename.c_str() ); - - int pos = title.size(); - while( pos > 0 && title[pos] != '\\' ) - { - pos --; - } - - title = file.substr( pos ); - } - if( artist.isNull() || artist.isEmpty() ) - { - artist = "Unknown"; -#ifdef VERBOSE - std::cout << "No artist tag found." << std::endl; -#endif - } + // obtain tag information + title = tag->title(); + album = tag->album(); + year = tag->year(); + artist = tag->artist(); + trackno = tag->track(); + genre = tag->genre(); + language = ""; + TagLib::ID3v2::Tag * id3v2tag=0; + if (filename.substr(filename.size()-5)==".flac") + { + TagLib::FLAC::File f(filename.c_str()); + id3v2tag = f.ID3v2Tag(); + if (id3v2tag) + { + TagLib::ID3v2::FrameList l = id3v2tag->frameListMap()["TLAN"]; + if (!l.isEmpty()) + language = l.front()->toString(); + } + } + else if (filename.substr(filename.size()-4)==".mp3") + { + TagLib::MPEG::File f(filename.c_str()); + id3v2tag = f.ID3v2Tag(); + if (id3v2tag) + { + TagLib::ID3v2::FrameList l = id3v2tag->frameListMap()["TLAN"]; + if (!l.isEmpty()) + language = l.front()->toString(); + } + } TagLib::String gid = find_genre_id( genre ); @@ -304,7 +254,7 @@ void update_db( long uid, std::string filename ) // finally update the database // obtain associated album or create - if( album.isNull() || album.isEmpty() ) + if( album == "" ) { // no album found, create default album for artist MYSQL_RES *result = mgSqlReadQuery( db, "SELECT cddbid FROM album WHERE title=\"Unassigned\" AND artist=\"%s\"", artist.toCString() ); MYSQL_ROW row = mysql_fetch_row( result ); @@ -385,20 +335,32 @@ void update_db( long uid, std::string filename ) mgSqlWriteQuery( db, "UPDATE tracks SET artist=\"%s\", title=\"%s\", year=\"%d\"," "sourceid=\"%s\", mp3file=\"%s\", length=%d, bitrate=\"%d\"," - "samplerate=%d, channels=%d, genre1=\"%s\" WHERE id=%d", + "samplerate=%d, channels=%d, genre1=\"%s\", lang=\"%s\" WHERE id=%d", artist.toCString(), title.toCString(), year, cddbid.toCString(), filename.c_str(), len, bitrate, - sample, channels, gid.toCString(), uid ); + sample, channels, gid.toCString(), uid, language.toCString() ); } else { // the entry does not exist, create it mgSqlWriteQuery( db, "INSERT INTO tracks " - "(artist, title, year,sourceid,tracknb,mp3file,length,bitrate,samplerate,channels,genre1,genre2) VALUES" - "(\"%s\", \"%s\", %d, \"%s\", %d, \"%s\", %d, \"%d\", %d, %d, \"%s\",\"\")", + "(artist, title, year,sourceid,tracknb,mp3file,length,bitrate,samplerate,channels,genre1,genre2,lang) VALUES" + "(\"%s\", \"%s\", %d, \"%s\", %d, \"%s\", %d, \"%d\", %d, %d, \"%s\",\"\",\"%s\")", artist.toCString(), title.toCString(), year, cddbid.toCString(), - trackno, filename.c_str(), len, bitrate, sample, channels, gid.toCString() ); + trackno, filename.c_str(), len, bitrate, sample, channels, gid.toCString(), + language.toCString()); +#ifdef VERBOSE + std::cout << "-- TAG --" << std::endl; + std::cout << "title - '" << tag->title() << "'" << std::endl; + std::cout << "artist - '" << tag->artist() << "'" << std::endl; + std::cout << "album - '" << tag->album() << "'" << std::endl; + std::cout << "year - '" << tag->year() << "'" << std::endl; + std::cout << "comment - '" << tag->comment() << "'" << std::endl; + std::cout << "track - '" << tag->track() << "'" << std::endl; + std::cout << "genre - '" << tag->genre() << "'" << std::endl; + std::cout << "language- '" << language << "'" << std::endl; +#endif } } } diff --git a/scripts/languages.txt b/scripts/languages.txt index 68dfdec..a24d25b 100755 --- a/scripts/languages.txt +++ b/scripts/languages.txt @@ -13,6 +13,7 @@ en English eo Esperanto es Spanish fi Finnish +fon Fon fr French hi Hindi hu Hungarian diff --git a/vdr_decoder.c b/vdr_decoder.c index d5e8004..93dc540 100644 --- a/vdr_decoder.c +++ b/vdr_decoder.c @@ -25,6 +25,8 @@ #include "vdr_decoder.h" #include "vdr_decoder_mp3.h" +extern void showmessage(const char *); + #ifdef HAVE_VORBISFILE #include "vdr_decoder_ogg.h" #endif @@ -60,13 +62,21 @@ mgMediaType mgDecoders::getMediaType (std::string s) { if (!strcmp (p, ".ogg")) { +#ifdef HAVE_VORBISFILE mt = MT_OGG; +#else + mgWarning("Support for vorbis not compiled in, define HAVE_VORBISFILE in Makefile"); +#endif } else { if (!strcmp (p, ".flac")) { +#ifdef HAVE_FLAC mt = MT_FLAC; +#else + mgWarning("Support for flac not compiled in, define HAVE_FLAC in Makefile"); +#endif } } } @@ -79,11 +89,19 @@ mgDecoders::findDecoder (mgContentItem * item) { mgDecoder *decoder = 0; - std::string filename = the_setup.getFilename( item->getSourceFile () ); + std::string filename = item->getSourceFile (); struct stat st; if (stat (filename.c_str (), &st)) { + char *b=0; + int nsize = filename.size(); + if (nsize<30) + asprintf(&b,tr("%s not readable"),filename.c_str()); + else + asprintf(&b,tr("%s..%s not readable"),filename.substr(0,20).c_str(),filename.substr(nsize-20).c_str());; + showmessage(b); + free(b); esyslog ("ERROR: cannot stat %s. Meaning not found, not a valid file, or no access rights", filename.c_str ()); return 0; } diff --git a/vdr_decoder_flac.c b/vdr_decoder_flac.c index c640205..f18ceab 100644 --- a/vdr_decoder_flac.c +++ b/vdr_decoder_flac.c @@ -34,8 +34,7 @@ mgFlacDecoder::mgFlacDecoder( mgContentItem *item ) { mgLog lg( "mgFlacDecoder::mgFlacDecoder" ); - m_filename = the_setup.getFilename( item->getSourceFile () ); - // m_filename = item->getSourceFile(); + m_filename = item->getSourceFile(); m_pcm = 0; m_reservoir = 0; @@ -100,10 +99,10 @@ bool mgFlacDecoder::clean() if( m_reservoir ) { - delete m_reservoir[0]; - delete m_reservoir[1]; + delete[] m_reservoir[0]; + delete[] m_reservoir[1]; } - delete m_reservoir; + delete[] m_reservoir; // why false? true? return true; diff --git a/vdr_decoder_mp3.c b/vdr_decoder_mp3.c index 754ab39..2ad6bc3 100644 --- a/vdr_decoder_mp3.c +++ b/vdr_decoder_mp3.c @@ -60,8 +60,7 @@ mgMP3Decoder::mgMP3Decoder (mgContentItem * item, bool preinit):mgDecoder m_stream = 0; m_isStream = false; - m_filename = the_setup.getFilename( item->getSourceFile () ); - // m_filename = item->getSourceFile (); + m_filename = item->getSourceFile (); if (preinit) { @@ -102,6 +101,7 @@ mgMP3Decoder::init () m_playtime = mad_timer_zero; m_skiptime = mad_timer_zero; m_framenum = m_framemax = 0; + m_frameinfo = 0; m_mute = m_errcount = 0; } diff --git a/vdr_decoder_ogg.c b/vdr_decoder_ogg.c index 24d253d..96c44cf 100644 --- a/vdr_decoder_ogg.c +++ b/vdr_decoder_ogg.c @@ -239,8 +239,7 @@ mgOggFile::stream (short *buffer, int samples) mgOggDecoder::mgOggDecoder (mgContentItem * item):mgDecoder (item) { - // m_filename = item->getSourceFile (); - m_filename = the_setup.getFilename( item->getSourceFile () ); + m_filename = item->getSourceFile (); m_file = new mgOggFile (m_filename); m_pcm = 0; init (); @@ -45,7 +45,8 @@ mgStatus::OsdCurrentItem(const char* Text) mgAction * a = dynamic_cast<mgAction *>(i); if (!a) mgError("mgStatus::OsdCurrentItem expected an mgAction*"); - a->TryNotify(); + if (a) + a->TryNotify(); } void Play(mgSelection *sel,const bool select) { @@ -82,6 +83,27 @@ mgMainMenu::PlayInstant(const bool select) } void +mgMainMenu::setOrder(mgSelection *sel,unsigned int idx) +{ + mgOrder* o = getOrder(idx); + if (o->size()>0) + { + m_current_order = idx; + sel->setOrder(o); + } + else + mgWarning("mgMainMenu::setOrder: orders[%u] is empty",idx); +} + +mgOrder* mgMainMenu::getOrder(unsigned int idx) +{ + if (idx>=orders.size()) + mgError("mgMainMenu::getOrder(%u): orders.size() is %d", + idx,orders.size()); + return orders[idx]; +} + +void mgMainMenu::CollectionChanged(string name) { delete moveselection; @@ -151,7 +173,7 @@ mgMenu::GenerateAction(const mgActions action,mgActions on) return result; } -void +eOSState mgMenu::ExecuteAction(const mgActions action,mgActions on) { mgAction *a = GenerateAction (action,on); @@ -159,7 +181,9 @@ mgMenu::ExecuteAction(const mgActions action,mgActions on) { a->Execute (); delete a; + return osContinue; } + return osUnknown; } @@ -178,19 +202,42 @@ PlayerControl () mgMenu::mgMenu () { m_osd = NULL; - TreeRedAction = mgActions(0); - TreeGreenAction = mgActions(0); - TreeYellowAction = mgActions(0); - CollRedAction = mgActions(0); - CollGreenAction = mgActions(0); - CollYellowAction = mgActions(0); + m_parent_index=-1; + TreeRedAction = actNone; + TreeGreenAction = actNone; + TreeYellowAction = actNone; + TreeBlueAction = actNone; + CollRedAction = actNone; + CollGreenAction = actNone; + CollYellowAction = actNone; + CollBlueAction = actNone; } // ----------------------- mgMainMenu ---------------------- -void mgMainMenu::SaveState() +void +mgMainMenu::DumpOrders(mgValmap& nv) +{ + map<string,mgOrder*>::iterator it; + for (unsigned int idx=0;idx<orders.size();idx++) + { + mgOrder *o = orders[idx]; + if (!o) + mgError("DumpOrders:order[%u] is 0",idx); + char *n; + for (unsigned int i=0;i<o->size();i++) + { + asprintf(&n,"order%u.Keys.%d.Type",idx,i); + nv.put(n,int(o->Key(i)->Type())); + free(n); + } + } +} + +void +mgMainMenu::SaveState() { char *b; asprintf(&b,"%s/muggle.state",cPlugin::ConfigDirectory ("muggle")); @@ -206,6 +253,8 @@ void mgMainMenu::SaveState() nmain.put("CollRedAction",int(Menus.front()->CollRedAction)); nmain.put("CollGreenAction",int(Menus.front()->CollGreenAction)); nmain.put("CollYellowAction",int(Menus.front()->CollYellowAction)); + nmain.put("CurrentOrder",m_current_order); + DumpOrders(nmain); mgValmap nsel("tree"); m_treesel.DumpState(nsel); mgValmap ncol("collection"); @@ -216,7 +265,7 @@ void mgMainMenu::SaveState() fclose(f); } -mgMainMenu::mgMainMenu ():cOsdMenu ("") +mgMainMenu::mgMainMenu ():cOsdMenu ("",25) { m_Status = new mgStatus(this); m_message = NULL; @@ -230,11 +279,7 @@ mgMainMenu::mgMainMenu ():cOsdMenu ("") mgValmap nmain("MainMenu"); // define defaults for values missing in state file: - nsel.put("Keys.0.Choice",keyArtist); - nsel.put("Keys.1.Choice",keyAlbum); - nsel.put("Keys.2.Choice",keyTitle); - ncol.put("Keys.0.Choice",keyCollection); - ncol.put("Keys.1.Choice",keyCollectionItem); + nsel.put("FallThrough",true); nmain.put("DefaultCollection",play_collection); nmain.put("UsingCollection","false"); nmain.put("TreeRedAction",int(actAddThisToCollection)); @@ -257,21 +302,31 @@ mgMainMenu::mgMainMenu ():cOsdMenu ("") } // get values from mgValmaps + LoadOrders(nmain); default_collection = nmain.getstr("DefaultCollection"); UsingCollection = nmain.getbool("UsingCollection"); InitMapFromSetup(nsel); - m_treesel.InitFrom (nsel); InitMapFromSetup(ncol); + m_treesel.setOrder(getOrder(m_current_order)); + m_treesel.InitFrom (nsel); + m_treesel.CreateCollection(default_collection); + if (default_collection!=play_collection) + m_treesel.CreateCollection(play_collection); + vector<mgKeyTypes> kt; + kt.push_back(keyCollection); + mgOrder o; + o.setKeys(kt); + m_collectionsel.setOrder(&o); m_collectionsel.InitFrom (ncol); + m_playsel.setOrder(&o); m_playsel.InitFrom(ncol); // initialize - m_collectionsel.CreateCollection(default_collection); - m_collectionsel.CreateCollection(play_collection); - m_playsel.setKey(0,keyCollection); - m_playsel.setKey(1,keyCollectionItem); - m_playsel.leave(0); - m_playsel.enter(play_collection); + if (m_playsel.level()!=1) + { + m_playsel.leave_all(); + m_playsel.enter(play_collection); + } UseNormalSelection (); unsigned int posi = selection()->gotoPosition(); LoadExternalCommands(); // before AddMenu() @@ -290,6 +345,57 @@ mgMainMenu::mgMainMenu ():cOsdMenu ("") } void +mgMainMenu::AddOrder() +{ + orders.push_back(new mgOrder); +} + +void +mgMainMenu::DeleteOrder() +{ + orders.erase(orders.begin()+Current()); +} + +void +mgMainMenu::LoadOrders(mgValmap& nv) +{ + for (unsigned int idx=0;idx<10;idx++) + { + char b[10]; + sprintf(b,"order%u",idx); + mgOrder *o = new mgOrder(nv,b); + if (o->size()==0) + { + delete o; + continue; + } + orders.push_back(o); + } + m_current_order = nv.getuint("CurrentOrder"); + if (m_current_order >= orders.size()) + m_current_order=0; + if (orders.size()>0) return; + + nv.put("order1.Keys.0.Type",keyArtist); + nv.put("order1.Keys.1.Type",keyAlbum); + nv.put("order1.Keys.2.Type",keyTrack); + + nv.put("order2.Keys.0.Type",keyAlbum); + nv.put("order2.Keys.1.Type",keyTrack); + + nv.put("order3.Keys.0.Type",keyGenres); + nv.put("order3.Keys.1.Type",keyArtist); + nv.put("order3.Keys.2.Type",keyAlbum); + nv.put("order3.Keys.3.Type",keyTrack); + + nv.put("order4.Keys.0.Type",keyArtist); + nv.put("order4.Keys.1.Type",keyTrack); + + nv.put("CurrentOrder",0); + LoadOrders(nv); +} + +void mgMainMenu::LoadExternalCommands() { // Read commands for collections in etc. /video/muggle/playlist_commands.conf @@ -326,11 +432,7 @@ void mgMainMenu::InitMapFromSetup (mgValmap& nv) { // values from setup override saved values - nv["Host"] = the_setup.DbHost; - nv["User"] = the_setup.DbUser; - nv["Password"] = the_setup.DbPass; nv["Directory"] = cPlugin::ConfigDirectory ("muggle"); - nv["ToplevelDir"] = the_setup.ToplevelDir; } void @@ -353,13 +455,29 @@ mgMenu::AddAction (const mgActions action, mgActions on,const bool hotkey) void mgMenu::AddExternalAction(const mgActions action, const char *title) { - mgAction *a = GenerateAction(action,mgActions(0)); + mgAction *a = GenerateAction(action,actNone); if (!a) return; a->SetText(osd()->hk(title)); osd()->AddItem(a); } void +mgMainMenu::AddOrderActions(mgMenu* m) +{ + map<string,mgOrder*>::iterator it; + for (unsigned int idx=0;idx<orders.size();idx++) + { + mgOrder *o = orders[idx]; + if (!o) + mgError("AddOrderAction:orders[%u] is 0",idx); + mgAction *a = m->GenerateAction(actOrder,actNone); + assert(a); + a->SetText(hk(o->Name().c_str())); + AddItem(a); + } +} + +void mgMenu::AddSelectionItems (mgSelection *sel,mgActions act) { for (unsigned int i = 0; i < sel->values.size (); i++) @@ -371,7 +489,7 @@ mgMenu::AddSelectionItems (mgSelection *sel,mgActions act) } if (osd()->ShowingCollections ()) { - mgAction *a = GenerateAction(actCreateCollection,mgActions(0)); + mgAction *a = GenerateAction(actCreateCollection,actNone); if (a) { a->SetText(a->MenuName(),false); @@ -381,20 +499,6 @@ mgMenu::AddSelectionItems (mgSelection *sel,mgActions act) } -const char * -mgMenu::BlueName (mgActions on) -{ - // do not use typeid because we want to catch descendants too - if (dynamic_cast<mgTreeCollSelector*>(this)) - return tr ("List"); - else if (osd()->Current()<0) - return tr("Commands"); - else if (dynamic_cast<mgTree*>(this)) - return tr("Commands"); - else - return tr ("List"); -} - const char* mgMenu::HKey(const mgActions act,mgActions on) { @@ -411,23 +515,25 @@ mgMenu::HKey(const mgActions act,mgActions on) void mgMenu::SetHelpKeys(mgActions on) { - mgActions r,g,y; + mgActions r,g,y,b; if (osd()->UsingCollection) { r = CollRedAction; g = CollGreenAction; y = CollYellowAction; + b = CollBlueAction; } else { r = TreeRedAction; g = TreeGreenAction; y = TreeYellowAction; + b = TreeBlueAction; } osd()->SetHelpKeys(HKey(r,on), HKey(g,on), HKey(y,on), - BlueName(on)); + HKey(b,on)); } @@ -463,8 +569,7 @@ mgSubmenu::BuildOsd () snprintf(b,99,tr("Commands:%s"),trim(osd()->selection()->getCurrentValue()).c_str()); mgActions on = osd()->CurrentType(); InitOsd (b); - mgMenu *p = osd ()->Parent (); - if (!p) + if (!osd ()->Parent ()) return; AddAction(actInstantPlay,on); AddAction(actAddThisToCollection,on); @@ -502,7 +607,7 @@ mgSubmenu::BuildOsd () mgActions mgMainMenu::CurrentType() { - mgActions result = mgActions(0); + mgActions result = actNone; cOsdItem* c = Get(Current()); if (c) { @@ -518,37 +623,36 @@ eOSState mgMenu::ExecuteButton(eKeys key) { mgActions on = osd()->CurrentType(); - switch (key) - { - case kRed: - if (osd()->UsingCollection) - ExecuteAction (CollRedAction,on); - else - ExecuteAction (TreeRedAction,on); - return osContinue; - case kGreen: - if (osd()->UsingCollection) - ExecuteAction (CollGreenAction,on); - else - ExecuteAction (TreeGreenAction,on); - return osContinue; - case kYellow: - if (osd()->UsingCollection) - ExecuteAction (CollYellowAction,on); - else - ExecuteAction (TreeYellowAction,on); - return osContinue; - case kBlue: - osd()->newmenu = new mgSubmenu; - return osContinue; - default: - break; - } - return osUnknown; + mgActions action = actNone; + if (osd()->UsingCollection) + switch (key) + { + case kRed: action = CollRedAction; break; + case kGreen: action = CollGreenAction; break; + case kYellow: action = CollYellowAction; break; + case kBlue: action = CollBlueAction; break; + default: break; + } + else + switch (key) + { + case kRed: action = TreeRedAction; break; + case kGreen: action = TreeGreenAction; break; + case kYellow: action = TreeYellowAction; break; + case kBlue: action = TreeBlueAction; break; + default: break; + } + return ExecuteAction(action,on); +} + +mgTree::mgTree() +{ + TreeBlueAction = actShowCommands; + CollBlueAction = actShowCommands; } eOSState -mgTree::Process (eKeys key) +mgMenu::Process (eKeys key) { return ExecuteButton(key); } @@ -695,6 +799,7 @@ void mgMainMenu::CloseMenu() { mgMenu* m = Menus.back(); + if (newposition==-1) newposition = m->getParentIndex(); Menus.pop_back (); delete m; } @@ -704,24 +809,32 @@ mgMainMenu::showMessage() { if (m_message) { + showmessage(m_message); + free(m_message); + m_message = NULL; + } +} + +void +showmessage(const char * msg) +{ #if VDRVERSNUM >= 10307 - Skins.Message (mtInfo, m_message,2); + Skins.Message (mtInfo, msg,2); Skins.Flush (); #else - Interface->Status (m_message); + Interface->Status (msg); Interface->Flush (); #endif - free(m_message); - m_message = NULL; - } } - void mgMainMenu::AddMenu (mgMenu * m,unsigned int position) { Menus.push_back (m); m->setosd (this); + m->setParentIndex(Current()); + if (Get(Current())) + m->setParentName(Get(Current())->Text()); m->Display (position); } @@ -732,25 +845,94 @@ mgMenu::setosd(mgMainMenu *osd) m_prevUsingCollection = osd->UsingCollection; } -eOSState -mgSubmenu::Process (eKeys key) +mgSubmenu::mgSubmenu() { - return osUnknown; + TreeBlueAction = actShowList; + CollBlueAction = actShowList; } void mgMenuOrders::BuildOsd () { - InitOsd (tr ("Select an order")); - AddAction(ActOrderCollItem); - AddAction(ActOrderArtistAlbumTitle); - AddAction(ActOrderArtistTitle); - AddAction(ActOrderAlbumTitle); - AddAction(ActOrderGenreYearTitle); - AddAction(ActOrderGenreArtistAlbumTitle); + TreeRedAction = actEditOrder; + TreeGreenAction = actCreateOrder; + TreeYellowAction = actDeleteOrder; + InitOsd (tr ("Select an order")); + osd()->AddOrderActions(this); +} + +mgMenuOrder::mgMenuOrder() +{ + m_order=0; } +mgMenuOrder::~mgMenuOrder() +{ + if (m_order) + delete m_order; +} + +void +mgMenuOrder::BuildOsd () +{ + if (!m_order) + { + m_order = new mgOrder; + *m_order = *(osd()->getOrder(getParentIndex())); + } + InitOsd (m_order->Name().c_str()); + m_keytypes.clear(); + m_keytypes.reserve(mgKeyTypesNr+1); + m_keynames.clear(); + m_keynames.reserve(50); + for (unsigned int i=0;i<m_order->size();i++) + { + unsigned int kt; + m_keynames.push_back(selection()->choices(m_order,i,&kt)); + m_keytypes.push_back(kt); + char buf[20]; + sprintf(buf,tr("Key %d"),i+1); + mgAction *a = actGenerateKeyItem(buf,(int*)&m_keytypes[i],m_keynames[i].size(),&m_keynames[i][0]); + a->SetMenu(this); + osd()->AddItem(a); + } +} + +bool +mgMenuOrder::ChangeOrder(eKeys key) +{ + vector <mgKeyTypes> newtypes; + newtypes.clear(); + for (unsigned int i=0; i<m_keytypes.size();i++) + newtypes.push_back(ktValue(m_keynames[i][m_keytypes[i]])); + mgOrder n = mgOrder(newtypes); + bool result = !(n == *m_order); + *m_order = n; + if (result) + { + osd()->forcerefresh = true; + int np = osd()->Current(); + if (key==kUp && np) np--; + if (key==kDown) np++; + osd()->newposition = np; + } + return result; +} + +void +mgMenuOrder::SaveOrder() +{ + *(osd()->getOrder(getParentIndex())) = *m_order; + osd()->SaveState(); +} + + +mgTreeCollSelector::mgTreeCollSelector() +{ + TreeBlueAction = actShowList; + CollBlueAction = actShowList; +} mgTreeCollSelector::~mgTreeCollSelector() { @@ -763,10 +945,10 @@ mgTreeCollSelector::BuildOsd () osd()->UsingCollection = true; mgSelection *coll = osd()->collselection(); InitOsd (m_title.c_str()); - coll->leave(0); + coll->leave_all(); coll->setPosition(osd()->default_collection); AddSelectionItems (coll,coll_action()); - osd()->newposition = coll->gotoPosition(0); + osd()->newposition = coll->gotoPosition(); cOsdItem *c = osd()->Get(osd()->newposition); mgAction *a = dynamic_cast<mgAction *>(c); a->IgnoreNextEvent = true; @@ -787,13 +969,12 @@ mgMainMenu::DisplayGoto (unsigned int select) { if (select >= 0) { + if ((int)select>=Count()) + select = Count() -1; SetCurrent (Get (select)); RefreshCurrent (); } Display (); -#if VDRVERSNUM >= 10307 - DisplayMenu()->SetTabs(25); -#endif } @@ -31,6 +31,8 @@ using namespace std; //! \param select if true, play only what the current position selects void Play(mgSelection *sel,const bool select=false); +void showmessage(const char *msg); + class cCommands; class mgSelection; @@ -68,7 +70,18 @@ class mgMainMenu:public cOsdMenu char *m_message; void showMessage(); void LoadExternalCommands(); + vector<mgOrder*> orders; + unsigned int m_current_order; + void DumpOrders(mgValmap& nv); + void LoadOrders(mgValmap& nv); public: + void AddOrder(); + void DeleteOrder(); + void AddOrderActions(mgMenu *m); + unsigned int getCurrentOrder() { return m_current_order; } + mgOrder* getOrder(unsigned int idx); + void setOrder(mgSelection *sel, unsigned int idx); + mgSelection *moveselection; mgActions CurrentType(); @@ -243,7 +256,8 @@ class mgMenu // creation. void AddSelectionItems (mgSelection *sel,mgActions act = actEntry); //! \brief the name of the blue button depends of where we are - const char *mgMenu::BlueName (mgActions on); + int m_parent_index; + string m_parent_name; public: /*! sets the correct help keys. * \todo without data from mysql, no key is shown, @@ -254,11 +268,16 @@ class mgMenu mgAction* GenerateAction(const mgActions action,mgActions on); //! \brief executes the wanted action - void ExecuteAction (const mgActions action, const mgActions on); + eOSState ExecuteAction (const mgActions action, const mgActions on); //! \brief sets the pointer to the owning mgMainMenu void setosd (mgMainMenu* osd); + void setParentIndex(int idx) { m_parent_index = idx; } + int getParentIndex() { return m_parent_index; } + void setParentName(string name) { m_parent_name = name; } + string getParentName() { return m_parent_name; } + //! \brief the pointer to the owning mgMainMenu mgMainMenu* osd () { @@ -299,10 +318,7 @@ class mgMenu * value for a new display. If NULL is returned, the caller will display * the previous menu. */ - virtual eOSState Process (eKeys Key) - { - return osUnknown; - } + virtual eOSState Process (eKeys Key); //! \brief the ID of the action defined by the red button. mgActions TreeRedAction; @@ -315,13 +331,17 @@ class mgMenu //! \brief the action defined by the yellow button. mgActions TreeYellowAction; mgActions CollYellowAction; + +//! \brief the action defined by the blue button. + mgActions TreeBlueAction; + mgActions CollBlueAction; }; //! \brief an mgMenu class for navigating through the data base class mgTree:public mgMenu { public: - eOSState Process (eKeys Key); + mgTree(); protected: void BuildOsd (); }; @@ -330,7 +350,7 @@ class mgTree:public mgMenu class mgSubmenu:public mgMenu { public: - eOSState Process (eKeys Key); + mgSubmenu::mgSubmenu(); protected: void BuildOsd (); }; @@ -342,10 +362,27 @@ class mgMenuOrders:public mgMenu void BuildOsd (); }; +class mgMenuOrder : public mgMenu +{ + public: + mgMenuOrder(); + ~mgMenuOrder(); + bool ChangeOrder(eKeys key); + void SaveOrder(); + protected: + void BuildOsd (); + private: + void AddKeyActions(mgMenu *m,mgOrder *o); + mgOrder * m_order; + vector<int> m_keytypes; + vector < vector <const char*> > m_keynames; +}; + //! \brief an mgMenu class for selecting a collection class mgTreeCollSelector:public mgMenu { public: + mgTreeCollSelector(); ~mgTreeCollSelector(); protected: void BuildOsd (); diff --git a/vdr_player.c b/vdr_player.c index e4dd7ef..cea9e3b 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -323,10 +323,10 @@ mgPCMPlayer::SetPlayMode (ePlayMode mode) { m_playmode_mutex.Lock (); if (mode != m_playmode) - { + { m_playmode = mode; m_playmode_cond.Broadcast (); - } + } m_playmode_mutex.Unlock (); } @@ -336,10 +336,10 @@ mgPCMPlayer::WaitPlayMode (ePlayMode mode, bool inv) { // must be called with m_playmode_mutex LOCKED !!! - while( m_active - && ((!inv && mode != m_playmode) || (inv && mode == m_playmode)) ) + while (m_active + && ((!inv && mode != m_playmode) || (inv && mode == m_playmode))) { - m_playmode_cond.Wait (m_playmode_mutex); + m_playmode_cond.Wait (m_playmode_mutex); } } @@ -412,18 +412,20 @@ mgPCMPlayer::Action (void) if (m_playing) { - if ((m_decoder = mgDecoders::findDecoder (m_playing)) - && m_decoder->start ()) + std::string filename = m_playing->getSourceFile (); + if ((m_decoder = mgDecoders::findDecoder (m_playing)) + && m_decoder->start ()) { levelgood = true; haslevel = false; - scale.Init (); level.Init (); m_state = msDecode; break; } + else + mgWarning("found no decoder for %s",filename.c_str()); } m_state = msEof; } @@ -620,8 +622,8 @@ mgPCMPlayer::Action (void) { if (m_ringbuffer->Available () == 0) { - // m_active = false; - SetPlayMode (pmStopped); + m_active = false; + SetPlayMode (pmStopped); } } break; diff --git a/vdr_setup.c b/vdr_setup.c index 2239191..0334fea 100644 --- a/vdr_setup.c +++ b/vdr_setup.c @@ -24,8 +24,6 @@ mgSetup the_setup; -std::string GdFindFile( std::string mp3file, std::string ToplevelDir ); -char *readline(FILE *f); // --- mgMenuSetup ----------------------------------------------------------- @@ -92,52 +90,3 @@ mgSetup::mgSetup () Only48kHz = 0; ToplevelDir = "/mnt/music/"; } - -std::string -mgSetup::getFilename( std::string basename ) -{ - if( GdCompatibility ) - { - return GdFindFile( basename, std::string( ToplevelDir ) ); - } - else - { - return std::string( ToplevelDir ) + basename; - } -} - -#define FINDCMD "cd '%s' && find -follow -name '%s' 2> /dev/null" - -char *readline(FILE *f) -{ - static char buffer[MAXPARSEBUFFER]; - if (fgets(buffer, sizeof(buffer), f) > 0) { - int l = strlen(buffer) - 1; - if (l >= 0 && buffer[l] == '\n') - buffer[l] = 0; - return buffer; - } - return NULL; -} - -std::string GdFindFile( std::string mp3file, std::string tld ) -{ - std::string fullname = ""; - char *cmd = NULL; - asprintf( &cmd, FINDCMD, tld.c_str(), mp3file.c_str() ); - FILE *p = popen( cmd, "r" ); - if (p) - { - char *s; - while( (s = readline(p) ) != NULL) - { - // printf( "Found: %s", s ); - fullname = std::string( tld ) + std::string( s ); - } - pclose(p); - } - - free( cmd ); - - return fullname; -} diff --git a/vdr_setup.h b/vdr_setup.h index b820857..f17aa2a 100644 --- a/vdr_setup.h +++ b/vdr_setup.h @@ -58,7 +58,6 @@ class mgSetup public: mgSetup (void); - std::string getFilename( std::string basename ); }; extern mgSetup the_setup; diff --git a/vdr_sound.c b/vdr_sound.c index 7fc8361..7c4304f 100644 --- a/vdr_sound.c +++ b/vdr_sound.c @@ -343,7 +343,7 @@ cNormalize::cNormalize (void) cNormalize::~cNormalize () { #ifdef USE_FAST_LIMITER - delete table; + delete[] table; #endif } @@ -550,15 +550,14 @@ class cScale inline unsigned long Prng (unsigned long state); inline signed long LinearDither (mad_fixed_t sample, struct dither *dither); public: - void Init (void); + cScale(); void Stats (void); unsigned int ScaleBlock (unsigned char *data, unsigned int size, unsigned int &nsamples, const mad_fixed_t * &left, const mad_fixed_t * &right, eAudioMode mode); }; -void -cScale::Init (void) +cScale::cScale() { #ifdef DEBUG clipped_samples = 0; diff --git a/vdr_stream.c b/vdr_stream.c index 4f79224..872ac4b 100644 --- a/vdr_stream.c +++ b/vdr_stream.c @@ -139,7 +139,7 @@ mgStream::close (void) else { #endif - delete m_buffer; + delete[] m_buffer; m_buffer = 0; #ifdef USE_MMAP } |