summaryrefslogtreecommitdiff
path: root/mg_db.c
diff options
context:
space:
mode:
Diffstat (limited to 'mg_db.c')
-rw-r--r--mg_db.c1137
1 files changed, 391 insertions, 746 deletions
diff --git a/mg_db.c b/mg_db.c
index a310d51..9fa656d 100644
--- a/mg_db.c
+++ b/mg_db.c
@@ -13,20 +13,9 @@
#include "vdr_setup.h"
#include "mg_tools.h"
-/*! \brief a RAM copy of the genres table for faster access
- * and in order to avoid having to use genre as genre 1 etc.
- */
-static map < string, string > genres;
-
-//! \brief adds string n to string s, using string sep to separate them
-static string addsep (string & s, string sep, string n);
-
//! \brief adds string n to string s, using a comma to separate them
static string comma (string & s, string n);
-//! \brief adds string n to string s, using AND to separate them
-static string und (string & s, string n);
-
/*! \brief returns a random integer within some range
*/
unsigned int
@@ -38,16 +27,6 @@ randrange (const unsigned int high)
}
-//! \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;
-}
-
static string
comma (string & s, string n)
{
@@ -55,46 +34,6 @@ comma (string & s, string n)
}
-static string
-und (string & s, string n)
-{
- return addsep (s, " AND ", n);
-}
-
-
-static string
-commalist (string prefix,list < string > v,bool sort=true)
-{
- string result = "";
- if (sort) v.sort ();
- v.unique ();
- for (list < string >::iterator it = v.begin (); it != v.end (); it++)
- {
- comma (result, *it);
- }
- if (!result.empty())
- result.insert(0," "+prefix+" ");
- 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 ();
-}
-
static string zerostring;
size_t
@@ -122,75 +61,8 @@ mgSelection::mgSelStrings::setOwner(mgSelection* sel)
m_sel = sel;
}
-mgValmap::mgValmap(const char *key) {
- m_key = key;
-}
-
-void mgValmap::Read(FILE *f) {
- char *line=(char*)malloc(1000);
- char *prefix=(char*)malloc(strlen(m_key)+2);
- strcpy(prefix,m_key);
- strcat(prefix,".");
- rewind(f);
- while (fgets(line,1000,f)) {
- if (strncmp(line,prefix,strlen(prefix))) continue;
- if (line[strlen(line)-1]=='\n')
- line[strlen(line)-1]=0;
- char *name = line + strlen(prefix);
- char *eq = strchr(name,'=');
- if (!eq) continue;
- *(eq-1)=0;
- char *value = eq + 2;
- (*this)[string(name)]=string(value);
- }
- free(prefix);
- free(line);
-}
-
-void mgValmap::Write(FILE *f) {
- for (mgValmap::const_iterator it=begin();it!=end();++it) {
- char b[1000];
- sprintf(b,"%s.%s = %s\n",
- m_key,it->first.c_str(),
- it->second.c_str());
- fputs(b,f);
- }
-}
-
-void mgValmap::put(const char* name, const string value) {
- if (value.empty() || value==EMPTY) return;
- (*this)[string(name)] = value;
-}
-
-void mgValmap::put(const char* name, const char* value) {
- if (!value || *value==0) return;
- (*this)[string(name)] = value;
-}
-
-void mgValmap::put(const char* name, const int value) {
- put(name,ltos(value));
-}
-
-void mgValmap::put(const char* name, const unsigned int value) {
- put(name,ltos(value));
-}
-
-void mgValmap::put(const char* name, const long value) {
- put(name,ltos(value));
-}
-
-void mgValmap::put(const char* name, const bool value) {
- string s;
- if (value)
- s = "true";
- else
- s = "false";
- put(name,s);
-}
-
-
void
-mgSelection::clearCache()
+mgSelection::clearCache() const
{
m_current_values = "";
m_current_tracks = "";
@@ -202,25 +74,29 @@ mgSelection::getCurrentValue()
return values[gotoPosition()];
}
-MYSQL_RES *
-mgSelection::exec_sql (string query)
+string
+mgSelection::getKeyValue(const unsigned int level) const
{
- mgDebug(3,query.c_str());
- if (!m_db) return NULL;
- if (mysql_query (m_db, (query + ';').c_str ()))
- {
- mgError("SQL Error in %s: %s",query.c_str(),mysql_error (m_db));
- return NULL;
- }
- return mysql_store_result (m_db);
+ return order.getKeyValue(level);
+}
+
+mgKeyTypes
+mgSelection::getKeyType (const unsigned int level) const
+{
+ return order.getKeyType(level);
}
+MYSQL_RES*
+mgSelection::exec_sql(string query) const
+{
+ return ::exec_sql(m_db, query);
+}
/*! \brief executes a query and returns the first columnu of the
* first row.
* \param query the SQL query string to be executed
*/
-string mgSelection::get_col0 (string query)
+string mgSelection::get_col0 (string query) const
{
MYSQL_RES * sql_result = exec_sql (query);
if (!sql_result)
@@ -239,87 +115,12 @@ string mgSelection::get_col0 (string query)
unsigned long
-mgSelection::exec_count (string query)
+mgSelection::exec_count (string query) const
{
return atol (get_col0 (query).c_str ());
}
-/*! \brief extract table names. All words preceding a . are supposed to be
- * table names. Table names are supposed to only contain letters. That is
- * sufficient for GiantDisc
- * \par spar the SQL command
- * \return a list of table names
- * \todo is this thread safe?
- */
-static list < string >
-tables (const string spar)
-{
- list < string > result;
- string s = spar;
- string::size_type dot;
- while ((dot = s.rfind ('.')) != string::npos)
- {
- s.erase (dot, string::npos); // cut the rest
- string::size_type lword = s.size ();
- while (strchr
- ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
- s[lword - 1]))
- {
- lword--;
- if (lword == 0)
- break;
- }
- result.push_back (s.substr (lword));
- }
- return result;
-}
-
-
-/*! \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
- * \param spar the sql command. It will be edited in place
- * \return the new sql command is also returned
- */
-static string
-optimize (string & spar)
-{
- string s = spar;
- string::size_type tmp = s.find (" WHERE");
- if (tmp != string::npos)
- s.erase (tmp, 9999);
- tmp = s.find (" ORDER");
- if (tmp != string::npos)
- s.erase (tmp, 9999);
- string::size_type frompos = s.find (" FROM ") + 6;
- if (s.substr (frompos).find (",") == string::npos)
- {
- string from = s.substr (frompos, 999) + '.';
- string::size_type track;
- while ((track = spar.find (from)) != string::npos)
- {
- spar.erase (track, from.size ());
- }
- }
- return spar;
-}
-
-string keyfield::sql_string(const string s) const
-{
- return selection->sql_string(s);
-}
-
-keyfield::keyfield (const string choice)
-{
- if (choice.empty())
- mgError("keyfield::keyfield: choice is empty");
- m_choice = choice;
- m_id = EMPTY;
- m_value = "";
- m_filter = "";
-}
-
string
mgSelection::sql_string (const string s) const
@@ -331,140 +132,96 @@ mgSelection::sql_string (const string s) const
return result;
}
+string
+mgContentItem::getKeyValue(mgKeyTypes kt)
+{
+ if (m_id<0)
+ return "";
+ switch (kt) {
+ case keyGenre1: return getGenre1();
+ case keyGenre2: return getGenre2();
+ case keyArtist: return getArtist();
+ case keyAlbum: return getAlbum();
+ case keyYear: return string(ltos(getYear()));
+ case keyDecade: return string(ltos(int((getYear() % 100) / 10) * 10));
+ case keyTitle: return getTitle();
+ case keyTrack: return getTitle();
+ default: return "";
+ }
+}
-string keyfield::restrict (string & result) const
+mgContentItem *
+mgSelection::getTrack (unsigned int position)
{
- string id = "";
- string op;
- if (m_id == EMPTY)
- return result;
- if (m_id == "NULL")
- {
- op = " is ";
- id = "NULL";
- }
- else
- {
- op = "=";
- id = sql_string (m_id);
- }
- if (idfield () == valuefield ())
- undequal (result, idfield (), op, id);
- else
- undequal (result, basefield (), op, id);
- return result;
+ if (position >= getNumTracks ())
+ return NULL;
+ return &(m_tracks[position]);
}
-string keyfield::join () const
+string mgContentItem::getGenre () const
{
- string result;
- if (need_join ())
- return undequal (result, basefield (), "=", idfield ());
- else
- return "";
+ return m_genre1;
}
-bool keyfield::need_join () const
+string mgContentItem::getGenre1 () const
{
- return lookup;
+ return m_genre1;
}
-void
-keyfield::set(const string id,const string value)
-{
- if (id != EMPTY)
- if (m_id == id && m_value == value) return;
- m_id = id;
- m_value = value;
- if (selection)
- selection->clearCache();
-}
-void
-keyfield::writeAt (ostream & s) const
+string mgContentItem::getGenre2 () const
{
- if (m_id == EMPTY)
- s << choice () << '/';
- else
- s << choice () << '=' << m_id;
+ return m_genre2;
}
-const char *
-toCString (mgSelection::ShuffleMode m)
+string mgContentItem::getBitrate () const
{
- static const char *modes[] =
- {
- "SM_NONE", "SM_NORMAL", "SM_PARTY"
- };
- return modes[m];
+ return m_bitrate;
}
-string
-toString (mgSelection::ShuffleMode m)
+string mgContentItem::getImageFile () const
{
- return toCString (m);
+ return "Name of Imagefile";
}
-//! \brief dump keyfield
-ostream & operator<< (ostream & s, keyfield & k)
+
+string mgContentItem::getAlbum () const
{
- k.writeAt (s);
- return s;
+ return m_albumtitle;
}
-string
-addsep (string & s, string sep, string n)
+int mgContentItem::getYear () const
{
- if (!n.empty ())
- {
- if (!s.empty ())
- s.append (sep);
- s.append (n);
- }
- return s;
+ return m_year;
}
-mgContentItem *
-mgSelection::getTrack (unsigned int position)
+int mgContentItem::getRating () const
{
- if (position >= getNumTracks ())
- return NULL;
- return &(m_tracks[position]);
+ return m_rating;
}
-void
-mgSelection::loadgenres ()
+int mgContentItem::getDuration () const
{
- MYSQL_RES *rows = exec_sql ("select id,genre from genre;");
- if (rows)
- {
- MYSQL_ROW row;
- while ((row = mysql_fetch_row (rows)) != NULL)
- {
- if (row[0] && row[1])
- genres[row[0]] = row[1];
- }
- mysql_free_result (rows);
- }
+ return m_duration;
}
-string mgContentItem::getGenre1 ()
+int mgContentItem::getSampleRate () const
{
- return genres[m_genre1];
+ return m_samplerate;
}
-string mgContentItem::getGenre2 ()
+int mgContentItem::getChannels () const
{
- return genres[m_genre2];
+ return m_channels;
}
@@ -476,13 +233,13 @@ mgSelection::ShuffleMode mgSelection::toggleShuffleMode ()
{
case SM_NONE:
{
- long id = m_tracks[m_tracks_position].getId ();
+ long id = m_tracks[getTrackPosition()].getId ();
m_current_tracks = ""; // force a reload
tracksize = getNumTracks(); // getNumTracks also reloads
for (unsigned int i = 0; i < tracksize; i++)
if (m_tracks[i].getId () == id)
{
- m_tracks_position = i;
+ setTrackPosition(i);
break;
}
}
@@ -491,10 +248,10 @@ mgSelection::ShuffleMode mgSelection::toggleShuffleMode ()
case SM_NORMAL:
{
// play all, beginning with current track:
- mgContentItem tmp = m_tracks[m_tracks_position];
- m_tracks[m_tracks_position]=m_tracks[0];
+ mgContentItem tmp = m_tracks[getTrackPosition()];
+ m_tracks[getTrackPosition()]=m_tracks[0];
m_tracks[0]=tmp;
- m_tracks_position=0;
+ setTrackPosition(0);
// randomize all other tracks
for (unsigned int i = 1; i < tracksize; i++)
{
@@ -551,9 +308,9 @@ mgSelection::AddToCollection (const string Name)
string sql = "";
for (unsigned int i = 0; i < tracksize; i++)
{
- string value = "(" + listid + "," + ltos (high + 1 + i) + "," +
+ string item = "(" + listid + "," + ltos (high + 1 + i) + "," +
ltos (m_tracks[i].getId ()) + ")";
- comma(sql, value);
+ comma(sql, item);
if ((i%100)==99)
{
exec_sql (sql_prefix+sql);
@@ -570,14 +327,8 @@ unsigned int
mgSelection::RemoveFromCollection (const string Name)
{
if (!m_db) return 0;
- string listid = get_col0
- ("SELECT id FROM playlist WHERE title=" + sql_string (Name));
- where();
- m_fromtables.push_back("playlistitem");
- m_fromtables.push_back("playlist");
- string sql = "DELETE playlistitem" + commalist("FROM",m_fromtables)
- + m_where + " AND tracks.id = playlistitem.trackid "
- " AND playlistitem.playlist = " + listid;
+ mgParts p = order.Parts(m_level,false);
+ string sql = p.sql_delete_from_collection(id(keyCollection,Name));
exec_sql (sql);
unsigned int removed = mysql_affected_rows (m_db);
if (inCollection(Name)) clearCache ();
@@ -588,6 +339,7 @@ mgSelection::RemoveFromCollection (const string Name)
bool mgSelection::DeleteCollection (const string Name)
{
if (!m_db) return false;
+ ClearCollection(Name);
exec_sql ("DELETE FROM playlist WHERE title=" + sql_string (Name));
if (isCollectionlist()) clearCache ();
return (mysql_affected_rows (m_db) == 1);
@@ -597,9 +349,8 @@ bool mgSelection::DeleteCollection (const string Name)
void mgSelection::ClearCollection (const string Name)
{
if (!m_db) return;
- exec_sql ("DELETE playlistitem FROM playlist,playlistitem "
- "WHERE playlistitem.playlist=playlist.id "
- " AND playlist.title=" + sql_string (Name));
+ string listid = id(keyCollection,Name);
+ exec_sql ("DELETE FROM playlistitem WHERE playlist="+listid);
if (inCollection(Name)) clearCache ();
}
@@ -628,10 +379,10 @@ string mgSelection::exportM3U ()
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 ());
+ mgContentItem& t = m_tracks[i];
+ fprintf (listfile, "#EXTINF:%d,%s\n", t.getDuration (),
+ t.getTitle ().c_str ());
+ fprintf (listfile, "%s", t.getSourceFile ().c_str ());
}
fclose (listfile);
return fn;
@@ -640,7 +391,7 @@ string mgSelection::exportM3U ()
bool
mgSelection::empty()
{
- if (m_level>= keys.size ()-1)
+ if (m_level>= order.size ()-1)
return ( getNumTracks () == 0);
else
return ( values.size () == 0);
@@ -649,24 +400,24 @@ mgSelection::empty()
void
mgSelection::setPosition (unsigned int position)
{
- if (m_level < keys.size ())
+ if (m_level < order.size ())
m_position[m_level] = position;
- if (m_level >= keys.size ()-1)
- m_tracks_position = position;
+ if (m_level >= order.size ()-1)
+ setTrackPosition(position);
}
void
-mgSelection::setTrack (unsigned int position)
+mgSelection::setTrackPosition (unsigned int position)
{
m_tracks_position = position;
}
unsigned int
-mgSelection::getPosition (unsigned int level) const
+mgSelection::getPosition (unsigned int level) const
{
- if (level == keys.size ())
+ if (level == order.size ())
return getTrackPosition();
else
return m_position[m_level];
@@ -675,10 +426,10 @@ mgSelection::getPosition (unsigned int level) const
unsigned int
mgSelection::gotoPosition (unsigned int level)
{
- if (level>keys.size())
- mgError("mgSelection::gotoPosition: level %u > keys.size %u",
- level,keys.size());
- if (level == keys.size ())
+ if (level>order.size())
+ mgError("mgSelection::gotoPosition: level %u > order.size %u",
+ level,order.size());
+ if (level == order.size ())
return gotoTrackPosition();
else
{
@@ -694,6 +445,11 @@ mgSelection::gotoPosition (unsigned int level)
unsigned int
mgSelection::getTrackPosition() const
{
+ if (m_tracks_position>=m_tracks.size())
+ if (m_tracks.size()==0)
+ m_tracks_position=0;
+ else
+ m_tracks_position = m_tracks.size()-1;
return m_tracks_position;
}
@@ -702,9 +458,9 @@ mgSelection::gotoTrackPosition()
{
unsigned int tracksize = getNumTracks ();
if (tracksize == 0)
- m_tracks_position = 0;
+ setTrackPosition(0);
else if (m_tracks_position >= tracksize)
- m_tracks_position = tracksize -1;
+ setTrackPosition(tracksize -1);
return m_tracks_position;
}
@@ -715,7 +471,7 @@ bool mgSelection::skipTracks (int steps)
return false;
if (m_loop_mode == LM_SINGLE)
return true;
- unsigned int old_pos = getTrackPosition();
+ unsigned int old_pos = m_tracks_position;
unsigned int new_pos;
if (old_pos + steps < 0)
{
@@ -727,16 +483,11 @@ bool mgSelection::skipTracks (int steps)
new_pos = old_pos + steps;
if (new_pos >= tracksize)
{
- clearCache();
- tracksize = getNumTracks();
- if (new_pos >= tracksize)
- {
- if (m_loop_mode == LM_NONE)
- return false;
- new_pos = 0;
- }
+ if (m_loop_mode == LM_NONE)
+ return false;
+ new_pos = 0;
}
- setTrack (new_pos);
+ setTrackPosition (new_pos);
return (new_pos == gotoTrackPosition());
}
@@ -753,102 +504,72 @@ mgSelection::getLength ()
unsigned long
-mgSelection::getCompletedLength ()
+mgSelection::getCompletedLength () const
{
unsigned long result = 0;
tracks (); // make sure they are loaded
- for (unsigned int i = 0; i < m_tracks_position; i++)
+ for (unsigned int i = 0; i < getTrackPosition(); i++)
result += m_tracks[i].getDuration ();
return result;
}
-string mgSelection::getListname ()
+
+string mgSelection::getListname () const
{
string
result = "";
for (unsigned int i = 0; i < m_level; i++)
- addsep (result, ":", keys[i]->value ());
- if (m_level==keys.size())
- addsep (result,":",getCurrentValue());
+ addsep (result, ":", getKeyValue(i));
if (result.empty ())
- result = string(tr(keys[0]->choice ().c_str()));
+ if (order.size()>0)
+ result = string(ktName(order.Key(0)->Type ()));
return result;
}
-
string mgSelection::ListFilename ()
{
string res = getListname ();
-#if 0
- geht so noch gar
- nicht ... while (string::size_type p = res.find (" "))
- res.replace (p, "");
- while (string::size_type p = res.find ("/"))
- res.replace (p, '-');
- while (string::size_type p = res.find ("\\"))
- res.replace (p, '-');
-#endif
+ // convert char set ?
return res;
}
-void
-mgSelection::AddOrder(const string sql,list<string>& orderlist, const string item)
-{
- string::size_type dot = item.rfind ('.');
- string itemtable = item.substr(0,dot);
- if (sql.find(itemtable) != string::npos)
- orderlist.push_back(item);
-}
-
const vector < mgContentItem > &
-mgSelection::tracks ()
-{
- list < string > orderby;
- orderby.clear();
- if (keys.empty())
- mgError("mgSelection::tracks(): keys is empty");
- if (genres.size () == 0)
- loadgenres ();
- string sql = "SELECT tracks.id, tracks.title, tracks.mp3file, "
- "tracks.artist, album.title, tracks.genre1, tracks.genre2, "
- "tracks.bitrate, tracks.year, tracks.rating, "
- "tracks.length, tracks.samplerate, tracks.channels ";
- sql += where (true);
- for (unsigned int i = m_level; i<keys.size(); i++)
- {
- AddOrder(sql,orderby,keys[i]->order ());
-}
- if (m_level>= keys.size ()-1)
- if (inCollection())
- AddOrder(sql,orderby,"playlistitem.tracknumber");
- else
- AddOrder(sql,orderby,"tracks.title");
-
-
- sql += commalist("ORDER BY",orderby,false);
-
- optimize (sql);
- if (m_current_tracks != sql)
- {
- m_current_tracks = sql;
- m_tracks.clear ();
- MYSQL_RES *rows = exec_sql (sql);
+mgSelection::tracks () const
+{
+ if (!m_db) return m_tracks;
+ if (!m_current_tracks.empty()) return m_tracks;
+ mgParts p = order.Parts(m_level);
+ p.fields.clear();
+ p.fields.push_back("tracks.id");
+ p.fields.push_back("tracks.title");
+ p.fields.push_back("tracks.mp3file");
+ p.fields.push_back("tracks.artist");
+ p.fields.push_back("album.title");
+ p.fields.push_back("tracks.genre1");
+ p.fields.push_back("tracks.genre2");
+ p.fields.push_back("tracks.bitrate");
+ p.fields.push_back("tracks.year");
+ p.fields.push_back("tracks.rating");
+ p.fields.push_back("tracks.length");
+ p.fields.push_back("tracks.samplerate");
+ p.fields.push_back("tracks.channels");
+ 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;
+ 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)
{
- m_tracks.push_back (mgContentItem (row, m_ToplevelDir));
+ m_tracks.push_back (mgContentItem (this,row));
}
mysql_free_result (rows);
}
- if (m_tracks_position>=m_tracks.size())
- if (m_tracks.size()==0)
- m_tracks_position=0;
- else
- m_tracks_position = m_tracks.size()-1;
- }
return m_tracks;
}
@@ -870,7 +591,7 @@ mgContentItem::mgContentItem (const mgContentItem* c)
m_channels = c->m_channels;
}
-mgContentItem::mgContentItem (const MYSQL_ROW row, const string ToplevelDir)
+mgContentItem::mgContentItem (const mgSelection* sel,const MYSQL_ROW row)
{
m_id = atol (row[0]);
if (row[1])
@@ -878,8 +599,7 @@ mgContentItem::mgContentItem (const MYSQL_ROW row, const string ToplevelDir)
else
m_title = "NULL";
if (row[2])
- // m_mp3file = ToplevelDir + row[2];
- m_mp3file = row[2];
+ m_mp3file = row[2];
else
m_mp3file = "NULL";
if (row[3])
@@ -891,11 +611,11 @@ mgContentItem::mgContentItem (const MYSQL_ROW row, const string ToplevelDir)
else
m_albumtitle = "NULL";
if (row[5])
- m_genre1 = row[5];
+ m_genre1 = sel->value(keyGenre1,row[5]);
else
m_genre1 = "NULL";
if (row[6])
- m_genre2 = row[6];
+ m_genre2 = sel->value(keyGenre2,row[5]);
else
m_genre2 = "NULL";
if (row[7])
@@ -924,29 +644,8 @@ mgContentItem::mgContentItem (const MYSQL_ROW row, const string ToplevelDir)
m_channels = 0;
};
-string mgContentItem::getAlbum ()
-{
- return m_albumtitle;
-}
-
-
-string mgContentItem::getImageFile ()
-{
- return "Name of Imagefile";
-}
-
-
-void
-mgSelection::initkey (keyfield & f)
-{
- f.setOwner(this);
- all_keys[f.choice ()] = &f;
- trall_keys[string(tr(f.choice ().c_str()))] = &f;
-}
-
void mgSelection::InitSelection() {
m_Directory=".";
- m_ToplevelDir = string("/");
InitDatabase();
m_level = 0;
m_position.reserve (20);
@@ -955,28 +654,12 @@ void mgSelection::InitSelection() {
m_shuffle_mode = SM_NONE;
m_loop_mode = LM_NONE;
clearCache();
- initkey (kartist);
- initkey (kgenre1);
- initkey (kgenre2);
- initkey (klanguage);
- initkey (krating);
- initkey (kyear);
- initkey (kdecade);
- initkey (ktitle);
- initkey (ktrack);
- initkey (kalbum);
- initkey (kcollection);
- initkey (kcollectionitem);
- keys.clear();
- keys.push_back (&kartist);
- keys.push_back (&kalbum);
- keys.push_back (&ktitle);
values.setOwner(this);
}
mgSelection::mgSelection()
{
- m_db = NULL;
+ setDB(0);
m_Host = "";
m_User = "";
m_Password = "";
@@ -986,7 +669,7 @@ mgSelection::mgSelection()
mgSelection::mgSelection (const string Host, const string User, const string Password, const bool fall_through)
{
- m_db = NULL;
+ setDB(0);
m_Host = Host;
m_User = User;
m_Password = Password;
@@ -996,42 +679,44 @@ mgSelection::mgSelection (const string Host, const string User, const string Pas
mgSelection::mgSelection (const mgSelection &s)
{
- m_db = NULL;
InitFrom(&s);
}
mgSelection::mgSelection (const mgSelection* s)
{
- m_db = NULL;
InitFrom(s);
}
mgSelection::mgSelection (mgValmap& nv)
{
- // this is analog to the copy constructor, please keep in sync.
-
- m_db = NULL;
InitFrom(nv);
}
void
+mgSelection::setDB(MYSQL *db)
+{
+ m_db = db;
+ order.setDB(db);
+}
+
+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");
- m_ToplevelDir = nv.getstr("ToplevelDir");
for (unsigned int i = 0; i < 99 ; i++)
{
char *idx;
asprintf(&idx,"Keys.%u.Choice",i);
- string v = nv.getstr(idx);
+ unsigned int v = nv.getuint(idx);
free(idx);
- if (v.empty()) break;
- setKey (i,v );
+ if (v==0) break;
+ setKey (i,mgKeyTypes(v) );
}
while (m_level < nv.getuint("Level"))
{
@@ -1045,8 +730,8 @@ mgSelection::InitFrom(mgValmap& nv)
m_trackid = nv.getlong("TrackId");
// TODO do we really need Position AND TrackPosition in muggle.state?
setPosition(nv.getlong("Position"));
- if (m_level>=keys.size()-1)
- setTrack(nv.getlong("TrackPosition"));
+ if (m_level>=order.size()-1)
+ setTrackPosition(nv.getlong("TrackPosition"));
setShuffleMode(ShuffleMode(nv.getuint("ShuffleMode")));
setLoopMode(LoopMode(nv.getuint("LoopMode")));
}
@@ -1054,24 +739,25 @@ mgSelection::InitFrom(mgValmap& nv)
mgSelection::~mgSelection ()
{
- mysql_close (m_db);
+ 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;
- m_ToplevelDir = s->m_ToplevelDir;
- keys.clear();
- for (unsigned int i = 0; i < s->keys.size (); i++)
- {
- keys.push_back(findKey(s->keys[i]->choice()));
- keys[i]->set(s->keys[i]->id(),s->keys[i]->value());
- }
+ map_values = s->map_values;
+ 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++)
@@ -1092,48 +778,12 @@ const mgSelection& mgSelection::operator=(const mgSelection &s)
}
-void
-mgSelection::writeAt (ostream & s)
-{
- for (unsigned int i = 0; i < keys.size (); i++)
- {
- if (i == level ())
- s << '*';
- s << *keys[i] << ' ';
- if (i == level ())
- {
- for (unsigned int j = 0; j < values.size (); j++)
- {
- s << values[j];
- if (values[j] != m_ids[j])
- s << '(' << m_ids[j] << ")";
- s << ", ";
- if (j == 7)
- {
- s << "(von " << values.size () << ") ";
- break;
- }
- }
- }
- }
- s << endl;
-}
-
-
-ostream & operator<< (ostream & s, mgSelection & sl)
-{
- sl.writeAt (s);
- return s;
-}
-
-
unsigned int
-mgSelection::size ()
+mgSelection::ordersize ()
{
- return keys.size ();
+ return order.size ();
}
-
unsigned int
mgSelection::valindex (const string val,const bool second_try)
{
@@ -1153,213 +803,107 @@ mgSelection::valindex (const string val,const bool second_try)
}
-string mgSelection::where (bool want_trackinfo)
-{
- m_from = "";
- m_where = "";
- m_fromtables.clear();
- if (m_level < keys.size ())
- {
- for (unsigned int i = 0; i <= m_level; i++)
- {
- keyfield * k = keys[i];
- k->lookup = want_trackinfo || (i == m_level);
- list < string > l = tables (k->join () + ' ' + k->basefield ());
- m_fromtables.merge (l);
- und (m_where, k->join ());
- k->restrict (m_where);
- }
- }
- else
- {
- m_fromtables.push_back ("tracks");
- m_where = "tracks.id='" + ltos (m_trackid) + "'";
- }
- if (want_trackinfo)
- {
- if (m_level == keys.size () || !UsedBefore (&kalbum, m_level + 1))
- {
- kalbum.lookup = false;
- list < string > l =
- tables (kalbum.join () + ' ' + kalbum.basefield ());
- m_fromtables.merge (l);
- und (m_where, kalbum.join ());
- }
- }
- m_from = commalist ("FROM",m_fromtables);
- if (!m_where.empty ())
- m_where.insert (0, " WHERE ");
- return m_from + m_where;
-}
-
-
void
-mgSelection::refreshValues ()
+mgSelection::refreshValues () const
{
+ if (!m_db) return;
+ mgOrder o1 = order;
if (m_current_values.empty())
{
- m_current_values = sql_values();
+ mgParts p = order.Parts(m_level);
+ m_current_values = p.sql_select();
values.strings.clear ();
m_ids.clear ();
MYSQL_RES *rows = exec_sql (m_current_values);
if (rows)
{
- unsigned int num_fields = mysql_num_fields(rows);
+ unsigned int num_fields = mysql_num_fields(rows);
MYSQL_ROW row;
while ((row = mysql_fetch_row (rows)) != NULL)
{
- string r0,r1;
+ string r0 = "NULL";
if (row[0])
r0 = row[0];
- else
- r0 = "NULL";
- if (row[1])
- r1 = row[1];
- else
- r1 = "NULL";
- values.strings.push_back (r0);
if (num_fields==2)
+ {
+ string r1 = "NULL";
+ if (row[1])
+ r1 = row[1];
+ values.strings.push_back (r0);
m_ids.push_back (r1);
+ }
else
+ {
+ values.strings.push_back (value(order.Key(m_level),r0));
m_ids.push_back (r0);
+ }
}
mysql_free_result (rows);
}
- if (m_position[m_level]>=values.size())
- if (values.size()==0)
- m_position[m_level]=0;
- else
- m_position[m_level] = values.size()-1;
- }
-}
-
-
-string mgSelection::sql_values ()
-{
- if (keys.empty())
- mgError("mgSelection::sql_values(): keys is empty");
- string result;
- if (m_level < keys.size ())
- {
- keyfield * last = keys[m_level];
- result = "SELECT ";
- if (m_level<keys.size()-1) result += "DISTINCT ";
- result += last->valuefield ();
- if (last->valuefield() != last->idfield())
- result += ',' + last->idfield ();
- result += where (false);
- result += " ORDER BY " + last->order ();
}
- else
- {
- result = "SELECT title,id from tracks where id='" + ltos (m_trackid) + "'";
- }
- optimize (result);
- return result;
}
-
unsigned int
-mgSelection::count ()
+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);
- m_db = NULL;
+ setDB(0);
}
if (m_Host == "") return;
- m_db = mysql_init (0);
- if (m_db == NULL)
+ 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);
- m_db = NULL;
+ setDB(0);
return;
}
return;
}
-string keyfield::KeyCountquery ()
-{
- lookup = false;
- string from;
- from = commalist ("FROM",tables (countfield () + ' ' + countjoin ()));
- string query = "SELECT COUNT(DISTINCT " + countfield () + ") " + from;
- if (!countjoin ().empty ())
- query += " WHERE " + countjoin ();
- optimize (query);
- return query;
-}
-
-keyfield* mgSelection::findKey(const string name)
-{
- if (all_keys.find(name) != all_keys.end())
- return all_keys.find(name)->second;
- if (trall_keys.find(name) != trall_keys.end())
- return trall_keys.find(name)->second;
- return NULL;
-}
-
void
-mgSelection::setKey (const unsigned int level, const string name)
+mgSelection::setKey (const unsigned int level, const mgKeyTypes kt)
{
- keyfield *newkey = findKey(name);
- if (newkey == NULL)
- mgError("mgSelection::setKey(%u,%s): keyname wrong",
- level,name.c_str());
- if (level == 0 && newkey == &kcollection)
+ mgKey *newkey = ktGenerate(kt,m_db);
+ if (level == 0 && kt == keyCollection)
{
- keys.clear ();
- keys.push_back (&kcollection);
- keys.push_back (&kcollectionitem);
+ order.clear ();
+ order += newkey;
+ order += ktGenerate(keyCollectionItem,m_db);
return;
}
- if (level == keys.size ())
+ if (level == order.size ())
{
- keys.push_back (newkey);
+ order += newkey;
}
else
{
- if (level >= keys.size())
- mgError("mgSelection::setKey(%u,%s): level greater than keys.size() %u",
- level,name.c_str(),keys.size());
- keys[level] = newkey;
-// remove this key from following lines:
- for (unsigned int i = level + 1; i < keys.size (); i++)
- if (keys[i] == keys[level])
- keys.erase (keys.begin () + i);
+ 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;
}
-// remove redundant lines:
- bool album_found = false;
- bool track_found = false;
- bool title_found = false;
- for (unsigned int i = 0; i < keys.size (); i++)
- {
- album_found |= (keys[i] == &kalbum);
- track_found |= (keys[i] == &ktrack);
- title_found |= (keys[i] == &ktitle);
- if (track_found || (album_found && title_found))
- {
- keys.erase (keys.begin () + i + 1, keys.end ());
- break;
- }
- }
+ order.clean();
// clear values for this and following levels (needed for copy constructor)
- for (unsigned int i = level; i < keys.size (); i++)
- keys[i]->set (EMPTY, "");
+ for (unsigned int i = level; i < order.size (); i++)
+ order[i]->set ("",EMPTY);
if (m_level > level)
m_level = level;
@@ -1369,8 +913,8 @@ mgSelection::setKey (const unsigned int level, const string name)
bool mgSelection::enter (unsigned int position)
{
- if (keys.empty())
- mgError("mgSelection::enter(%u): keys is empty", position);
+ if (order.empty())
+ mgWarning("mgSelection::enter(%u): order is empty", position);
if (empty())
return false;
setPosition (position);
@@ -1379,13 +923,14 @@ bool mgSelection::enter (unsigned int position)
string id = m_ids[position];
while (1)
{
- mgDebug(2,"enter(level=%u,pos=%u, value=%s)",m_level,position,value.c_str());
- if (m_level >= keys.size () - 1)
+ mgDebug(3,"enter(level=%u,pos=%u, id=%s)",m_level,position,id.c_str());
+ if (m_level >= order.size () - 1)
return false;
- keys[m_level++]->set (id, value);
- if (m_level >= keys.size())
- mgError("mgSelection::enter(%u): level greater than keys.size() %u",
- m_level,keys.size());
+ order[m_level++]->set (value,id);
+ clearCache();
+ 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;
@@ -1394,10 +939,10 @@ bool mgSelection::enter (unsigned int position)
if (count () > 1)
break;
if (count () == 1)
- {
+ {
+ value = values[0];
id = m_ids[0];
- value = values[0];
- }
+ }
}
return true;
}
@@ -1405,13 +950,15 @@ bool mgSelection::enter (unsigned int position)
bool mgSelection::select (unsigned int position)
{
- mgDebug(2,"select(pos=%u)",position);
- if (m_level == keys.size () - 1)
+ mgDebug(3,"select(%u) on Level %d",position,m_level);
+ if (m_level == order.size () - 1)
{
if (getNumTracks () <= position)
return false;
+ order[m_level]->set (values[position],m_ids[position]);
m_level++;
m_trackid = m_tracks[position].getId ();
+
clearCache();
return true;
}
@@ -1422,9 +969,12 @@ bool mgSelection::select (unsigned int position)
bool mgSelection::leave ()
{
- if (keys.empty())
- mgError("mgSelection::leave(): keys is empty");
- if (m_level == keys.size ())
+ if (order.empty())
+ {
+ mgWarning("mgSelection::leave(): order is empty");
+ return false;
+ }
+ if (m_level == order.size ())
{
m_level--;
m_trackid = -1;
@@ -1435,7 +985,8 @@ bool mgSelection::leave ()
{
if (m_level < 1)
return false;
- keys[--m_level]->set (EMPTY, "");
+ order[--m_level]->set ("",EMPTY);
+ clearCache();
if (!m_fall_through)
break;
if (count () > 1)
@@ -1444,81 +995,141 @@ bool mgSelection::leave ()
return true;
}
+string
+mgSelection::value(mgKeyTypes kt, string id) const
+{
+ if (kt==keyGenre2) kt = keyGenre1;
+ if (loadvalues (kt))
+ {
+ map<string,string>& valmap = map_values[kt];
+ map<string,string>::iterator it;
+ it = valmap.find(id);
+ if (it!=valmap.end())
+ {
+ string r = it->second;
+ if (!r.empty())
+ return r;
+ }
+ map_ids[kt].clear();
+ loadvalues(kt);
+ it = valmap.find(id);
+ if (it!=valmap.end())
+ return valmap[id];
+ }
+ return id;
+}
+
+string
+mgSelection::value(mgKey* k, string id) const
+{
+ return value(k->Type(),id);
+}
-bool mgSelection::UsedBefore (keyfield const *k, unsigned int level)
+string
+mgSelection::value(mgKey* k) const
+{
+ return value(k,k->id());
+}
+
+string
+mgSelection::id(mgKeyTypes kt, string val) const
{
- if (level >= keys.size ())
- level = keys.size () - 1;
- for (unsigned int i = 0; i < level; i++)
- if (keys[i] == k)
- return true;
- return false;
+ if (kt==keyGenre2) kt = keyGenre1;
+ if (loadvalues (kt))
+ {
+ map<string,string>& idmap = map_ids[kt];
+ return idmap[val];
+ }
+ else
+ return val;
}
+string
+mgSelection::id(mgKey* k, string val) const
+{
+ return id(k->Type(),val);
+}
-bool mgSelection::isCollectionlist ()
+string
+mgSelection::id(mgKey* k) const
{
- return (keys[0] == &kcollection && m_level == 0);
+ return k->id();
}
bool
-mgSelection::inCollection(const string Name)
+mgSelection::UsedBefore(const mgKeyTypes kt,unsigned int level) const
{
- bool result = (keys[0] == &kcollection && m_level == 1);
+ if (level>=order.size())
+ level = order.size() -1;
+ for (unsigned int lx = 0; lx < level; lx++)
+ if (order.Key(lx)->Type()==kt)
+ return true;
+ return false;
+}
+
+
+bool mgSelection::isCollectionlist () const
+{
+ if (order.size()==0) return false;
+ return (order.Key(0)->Type() == 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);
if (result)
- if (keys[1] != &kcollectionitem)
- mgError("inCollection: key[1] is not kcollectionitem");
+ if (order.Key(1)->Type() != keyCollectionItem)
+ mgError("inCollection: key[1] is not keyCollectionItem");
if (!Name.empty())
- result &= (keys[0]->value() == Name);
+ result &= (order.getKeyValue(0) == Name);
return result;
}
-const strvector &
-mgSelection::keychoice (const unsigned int level)
+#if 0
+void
+keychoice(mgOrder& o,const unsigned int level)
{
- m_keychoice.clear ();
- if (level > keys.size ())
- return m_keychoice;
- map < string, keyfield * >::iterator it;
- map < string, keyfield * > possible_keys;
- for (it = all_keys.begin (); it != all_keys.end (); it++)
- {
- keyfield*f = (*it).second;
- if (keycounts.find (f->choice ()) == keycounts.end ())
- {
- keycounts[f->choice ()] = exec_count (f->KeyCountquery ());
- }
- unsigned int i = keycounts[f->choice ()];
- if ((&(*f) != &kcollection) && (&(*f) != &kcollectionitem) && (i < 2))
- ;
- else
- possible_keys[string(tr(f->choice ().c_str()))] = &(*f);
- }
-
- for (it = possible_keys.begin (); it != possible_keys.end (); it++)
+ if (level > o.size ())
+ return;
+ std::cout<<"possible choices:";
+ for (mgKeyTypes kt = mgKeyTypes(1); kt <= mgKeyTypesHigh; kt = mgKeyTypes(int(kt)+1))
{
- keyfield *k = (*it).second;
- if (level != 0 && k == &kcollection)
- continue;
- if (level != 1 && k == &kcollectionitem)
- continue;
- if (level == 1 && keys[0] != &kcollection && k == &kcollectionitem)
- continue;
- if (level == 1 && keys[0] == &kcollection && k != &kcollectionitem)
- continue;
- if (level > 1 && keys[0] == &kcollection)
- break;
- if (k == &kdecade && UsedBefore (&kyear, level))
- continue;
- if (!UsedBefore (k, level))
- m_keychoice.push_back (string(tr((*it).second->choice ().c_str())));
+ 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]<<")";
+ }
}
- return m_keychoice;
+ std::cout<<endl;
}
+#endif
-
-void mgSelection::DumpState(mgValmap& nv)
+void mgSelection::DumpState(mgValmap& nv) const
{
nv.put("Host",m_Host);
nv.put("User",m_User);
@@ -1527,40 +1138,74 @@ void mgSelection::DumpState(mgValmap& nv)
nv.put("ShuffleMode",int(m_shuffle_mode));
nv.put("LoopMode",int(m_loop_mode));
nv.put("Directory",m_Directory);
- nv.put("ToplevelDir",m_ToplevelDir);
nv.put("Level",int(m_level));
- for (unsigned int i=0;i<keys.size();i++)
+ for (unsigned int i=0;i<order.size();i++)
{
char *n;
asprintf(&n,"Keys.%d.Choice",i);
- nv.put(n,keys[i]->choice());
- asprintf(&n,"Keys.%d.Filter",i);
- nv.put(n,keys[i]->filter());
+ 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]);
+ free(n);
}
}
nv.put("TrackId",m_trackid);
- if (m_level == keys.size ())
- nv.put("Position",m_tracks_position);
+ if (m_level == order.size ())
+ nv.put("Position",getTrackPosition());
else
nv.put("Position",m_position[m_level]);
- nv.put("TrackPosition",m_tracks_position);
+ nv.put("TrackPosition",getTrackPosition());
}
-map <string, string> *
-mgSelection::UsedKeyValues()
+map <mgKeyTypes, string> *
+mgSelection::UsedKeyValues()
{
- map <string, string> *result = new map<string, string>;
+ map <mgKeyTypes, string> *result = new map<mgKeyTypes, string>;
for (unsigned int idx = 0 ; idx < level() ; idx++)
{
- (*result)[keys[idx]->choice()] = keys[idx]->value();
+ (*result)[order.Key(idx)->Type()] = order.getKeyValue(idx);
}
- if (level() < keys.size()-1)
+ if (level() < order.size()-1)
{
- string ch = keys[level()]->choice();
+ mgKeyTypes ch = order.getKeyType(level());
(*result)[ch] = getCurrentValue();
}
return result;
}
+
+bool
+mgSelection::loadvalues (mgKeyTypes kt) const
+{
+ 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());
+ MYSQL_RES *rows = exec_sql (string(b));
+ free(b);
+ if (rows)
+ {
+ MYSQL_ROW row;
+ while ((row = mysql_fetch_row (rows)) != NULL)
+ {
+ if (row[0] && row[1])
+ valmap[row[0]] = row[1];
+ idmap[row[1]] = row[0];
+ }
+ mysql_free_result (rows);
+ }
+ delete k;
+ return true;
+}