Main Page   Namespace List   Alphabetical List   Compound List   File List   Compound Members   File Members  

PlayListConverter.cpp

Go to the documentation of this file.
00001 
00024 #include "PlayListConverter.h"
00025 #include "EmpParser.h"
00026 #include "xmlutils.h"
00027 #include "StringUtils.h"
00028 #include <errno.h> 
00029 #include <sys/types.h> 
00030 #include <sys/stat.h> 
00031 #include <sys/wait.h>
00032 #include <unistd.h> 
00033 #include <fstream>
00034 #include <sstream> 
00035 #include <algorithm>
00036 
00037 using namespace std;
00038 using namespace xmlpp;
00039 
00040 PlayListConverter::PlayListConverter(){}
00041 
00042 void PlayListConverter::OutputFormat::clear()
00043 {
00044   filename = "";
00045   begin = "";
00046   track = "";
00047   end = "";
00048   exec = "";
00049 }
00050 
00052 bool PlayListConverter::load_config()
00053 {
00054   bool success(false);
00055   string filename;  
00056 
00057   if (get_config_file(filename)) 
00058   {
00059      m_config.parse_file(filename);
00060 
00061      Node* root = m_config.get_root_node();
00062 
00063      if (root != NULL) {
00064         Node* opts =  getFirstChild(root, "options");
00065         if (opts != NULL) {
00066           m_options.workingdir = getPropValue(opts, "working-dir");
00067           const Node::NodeList& formats = opts->get_children("default-format");
00068           Node::NodeList::const_iterator format = formats.begin();
00069           for(; format != formats.end(); ++format) {
00070              string def_format = getPropValue(*format, "name");
00071              if (! def_format.empty()) {
00072                 m_options.formats.push_back(def_format);
00073              }
00074           }
00075         }
00076 
00077         expand_tilde(m_options.workingdir);
00078      
00079         success = true;
00080      } 
00081   }
00082   else 
00083   {
00084      cerr << "cant find a config file" << endl;
00085   }
00086   
00087   return success;
00088 }
00089 
00091 
00095 bool PlayListConverter::parse_rmp(const char* filename)
00096 {
00097   m_session.emp_file = filename;
00098   EmpParser parser;
00099   return parser.parse_file(filename, m_session, m_tracks);
00100 }
00101 
00103 bool PlayListConverter::write_output(OutputFormat& format)
00104 {
00105   bool success(false);
00106   if (m_tracks.empty()) return success; // need to have at least one track
00107 
00108   // run any chained formats first  
00109   StringList::const_iterator cformat = format.chained_formats.begin();
00110     for(; cformat != format.chained_formats.end(); ++cformat) {
00111       process_format(*cformat);
00112     } 
00113 
00114     // build the filename
00115     RmpTrack& firstTrack = *m_tracks.begin();
00116     m_session.output_file = make_subs(format.filename, m_session, firstTrack);
00117 
00118     // open file
00119     ostream* os = &cout;
00120     ofstream ofs;
00121     if (! m_session.output_file.empty()) {
00122       open_output(ofs);
00123       os = &ofs;
00124     }
00125 
00126     if (os && *os) {
00127       // write  the beginning
00128       if (! format.begin.empty()) {
00129          *os << make_subs(format.begin, m_session, firstTrack) << endl;
00130       }
00131 
00132       // write out all the track entries
00133       if (! format.track.empty()) {
00134          TrackList::const_iterator iter = m_tracks.begin();
00135          for(; iter != m_tracks.end(); ++iter) {
00136             *os << make_subs(format.track, m_session, *iter) << endl;
00137          }
00138       }
00139 
00140       // write end 
00141       if (! format.end.empty()) {
00142          *os << make_subs(format.end, m_session, firstTrack) << endl;
00143       }
00144 
00145      // unescape the command to execute
00146      format.exec = make_subs(format.exec, m_session, firstTrack);
00147 
00148      success = true;
00149   } else {
00150      cerr << "failed opening output file: " << m_session.output_file << endl;
00151   }
00152 
00153   return success;
00154 }
00155 
00157 
00162 bool PlayListConverter::exec_output(const OutputFormat& format, bool wait_for_child)
00163 {  
00164   bool success(false);
00165 
00166   if (format.exec.length() > 0) 
00167   {
00168      char* args[] = {"sh", "-c", const_cast<char*>(format.exec.c_str()), 0};
00169      
00170      int fork_result = fork();
00171      
00172      switch (fork_result) {
00173      case -1:
00174         break;
00175         
00176      case 0:
00177         // were the child process
00178         cout << "executing: "  << format.exec << endl;
00179         execvp("sh",args);
00180         break;
00181         
00182      default:
00183         // were the parent prpcess
00184         if (wait_for_child) {
00185           int status;
00186           waitpid(fork_result, &status, 0);
00187         }
00188         success = true;
00189      }
00190   } else {
00191      // no command just return
00192      success = true;
00193   }
00194   return success;
00195 }
00196 
00198 
00202 bool PlayListConverter::get_config_file(std::string& filename)
00203 {
00204   bool success(true);
00205   string home_config("~/.empxformrc");
00206   expand_tilde(home_config);
00207   const char* configs[] = {home_config.c_str(), 
00208                                     "/usr/share/empxform/empxformrc",
00209                                     "/usr/local/share/empxform/empxformrc",
00210                                     "/etc/empxformrc", 0};
00211 
00212   struct stat st; 
00213   unsigned int i;
00214   for(i=0; (configs[i] != 0) && (stat(configs[i], &st) != 0); ++i){}
00215 
00216   if (i < (sizeof(configs) / sizeof(char*) - 1)) {
00217      filename = configs[i];
00218   } else {
00219      success = false;
00220   }
00221 
00222   return success;
00223 }
00224 
00226 
00230 void PlayListConverter::add_format_line(string &entry, const string newline) {
00231   if (entry.size()) {
00232      entry += "\n";
00233   }
00234   entry += newline;
00235 }
00236 
00238 
00244 string PlayListConverter::make_subs(const string& templ, const RmpSession& session, const RmpTrack& trk)
00245 {
00246   string result(templ);
00247 
00248   size_t pos(0);
00249 
00250   while ((pos= result.find('%', pos)) != string::npos) {
00251      if (pos <  result.length() -1) {
00252         // check for modifiers
00253         int offset = is_command_modifier(result[pos +1]) ? 2 : 1;
00254         
00255         switch (result[pos +offset]) {
00256         case 'O': //  - generated output file (created by emp2m3u rules)
00257           insert_subst(result, pos, m_session.output_file);
00258           break;
00259         case 'E': //  - name of the emp file
00260           insert_subst(result, pos, m_session.emp_file);
00261           break;
00262         case 'P': //  - Provider name
00263           insert_subst(result, pos, m_session.provider);
00264           break;
00265         case 'T': //  - package title (Album name on emusic)
00266           insert_subst(result, pos, m_session.package_title);
00267           break;
00268         case 'A': // package artist
00269           insert_subst(result, pos, m_session.package_artist);
00270           break;
00271         case 'W': // Program working directory
00272           insert_subst(result, pos, m_options.workingdir);
00273           break;
00274         case 't': //  - title
00275           insert_subst(result, pos, trk.title);
00276           break;
00277         case 'a': //  - artist
00278           insert_subst(result, pos, trk.artist);
00279           break;
00280         case 'n': //  - track number
00281           insert_subst(result, pos, trk.track_num);
00282           break;
00283         case 'f': //  - file name
00284           insert_subst(result, pos, trk.file_name);
00285           break;
00286         case 'd': //  - id of the current track
00287           insert_subst(result, pos, trk.track_id);
00288           break;
00289         case 'g': //  - genre
00290           insert_subst(result, pos, trk.genre);
00291           break;
00292         case 'F': //  - format
00293           insert_subst(result, pos, trk.format);
00294           break;
00295         case 'q': //  - quality
00296           insert_subst(result, pos, trk.quality);
00297           break;
00298         case 'c': //  - album art (cover picture)
00299           insert_subst(result, pos, trk.cover);
00300           break;
00301         case 'U': //  - complete url of the current track
00302           insert_subst(result, pos, trk.url);
00303           break;          
00304         case 'D': //  - duation in seconds
00305           insert_subst(result, pos, trk.duration);
00306           break;          
00307         case 'C': //  - the number of channels
00308           insert_subst(result, pos, trk.channels);
00309           break;
00310         case '%': //  -  a literal % 
00311           insert_subst(result, pos, "%");
00312           break;                  
00313         default:
00314           ++pos; // unrecognsed flag just ignore it
00315         }
00316      }
00317   }
00318   return result;
00319 }
00320 
00321 
00323 
00328 void PlayListConverter::insert_subst(string& to_change, size_t& pos, const string& toinsert) {
00329 
00330 
00331   if (! is_command_modifier(to_change[pos+1])) {
00332      to_change.replace(pos, 2, toinsert);
00333      pos += toinsert.length();
00334      
00335   } else {
00336      // Handle escape sequence modifiers
00337      string new_val(toinsert);
00338 
00339      switch (to_change[pos+1]) {
00340      case '\\': // escape shell characters
00341         escape_shell_chars(new_val);
00342         break;
00343 
00344      case '_': // remove shell charachers, spaces to underscores
00345         replace_str(new_val, " ", "_");
00346         remove_shell_chars(new_val);
00347         break;
00348         
00349      case '-': // remove shell characters
00350         remove_shell_chars(new_val);
00351         break;      
00352 
00353      case '!': // convert \ to -
00354         replace_str(new_val, "/", "-");
00355         break;      
00356 
00357      case '\'': // remove quotes 
00358         replace_str(new_val, "\"", "");
00359         replace_str(new_val, "'", "");
00360         break;      
00361      }
00362      
00363      to_change.replace(pos, 3, new_val);
00364      pos += new_val.length();
00365   }
00366 
00367 }
00368 
00370 void PlayListConverter::change_working_dir()
00371 {
00372   if (m_options.workingdir.empty()) {
00373      m_options.workingdir = "~";
00374      expand_tilde(m_options.workingdir);
00375   }
00376 
00377   if (! make_dir(m_options.workingdir)) {
00378      cerr << "error creating working directory: " << m_options.workingdir << endl;
00379   }
00380   
00381   if (chdir(m_options.workingdir.c_str()) != 0) {
00382      cerr << "error changing to working directory: "<< m_options.workingdir << endl;
00383   }  
00384   
00385 }
00386 
00388 
00392 bool PlayListConverter::is_command_modifier(char c)
00393 {
00394   switch (c) {
00395   case '!':
00396   case '\\':
00397   case '-':
00398   case '_':
00399   case '\'':
00400      return true;
00401   default:
00402      return false;
00403   }
00404 }
00405 
00406 
00411 void PlayListConverter::set_options(const Options& new_opt)
00412 {
00413   m_options.wait_for_child = new_opt.wait_for_child;
00414 
00415   if (! new_opt.workingdir.empty()) {
00416      m_options.workingdir = new_opt.workingdir;
00417      void change_working_dir();
00418   } 
00419 
00420   if (! new_opt.formats.empty()) {
00421      m_options.formats.clear();
00422      transform(new_opt.formats.begin(), new_opt.formats.end(), 
00423                   back_inserter(m_options.formats),
00424                   bind1st(mem_fun(&PlayListConverter::copy_format_name), this));
00425   }
00426 }
00427 
00428 
00430 
00439 string PlayListConverter::copy_format_name(string format) 
00440 {
00441   if (format.length() && (format[0] != '<')) {
00442      return format;
00443   } else {
00444      // its a format specified inline
00445      static int format_num = 0; // counter for naming anonymous formats
00446      stringstream tmp_name;
00447      tmp_name << "anonymous-" << format_num++ << '\0';
00448      string format_name(tmp_name.str());
00449 
00450      format = "<format name=\"" + format_name + "\">" + format + "</format>";
00451 
00452      // now  parse the xml & add the new format to the ones read from the config file
00453      DomParser tree;
00454      tree.parse_memory(format);
00455      if (! tree) {
00456         cerr << "Error: cant parse anonymous format: " << format << endl;
00457      } else {
00458         Node* new_format = tree.get_root_node();
00459         Node* conf_root = m_config.get_root_node();
00460         if (conf_root && new_format) {
00461 //        conf_root->add_child(new Node(*new_format));
00462           addChild(conf_root, new Node(*new_format));
00463         }
00464      }
00465      
00466      return format_name;
00467   }
00468 }
00469 
00474 bool PlayListConverter::process_rmp(const char* rmp_file)
00475 {
00476   bool success = parse_rmp(rmp_file);
00477   change_working_dir();
00478 
00479   if (success) {
00480      StringList::const_iterator iter = m_options.formats.begin();
00481 
00482      for(; iter != m_options.formats.end(); ++iter) {
00483         success &= process_format(*iter);
00484      } 
00485   }
00486   return success;
00487 }
00488 
00490 
00494 bool PlayListConverter::process_format(const std::string& format_name) 
00495 {
00496   bool success;
00497   OutputFormat format;
00498   success = load_format(format_name.c_str(), format);
00499   success &= write_output(format);
00500   success &= exec_output(format, m_options.wait_for_child);
00501 }
00502 
00504 
00509 bool PlayListConverter::load_format(const char* format_name, OutputFormat& oformat)
00510 {
00511   bool success(false);
00512   string filename;  
00513   oformat.clear();
00514 
00515   if (m_config) 
00516   {
00517      Node* root = m_config.get_root_node();
00518 
00519      const Node::NodeList& formats = root->get_children("format");
00520      Node::NodeList::const_iterator format = formats.begin();
00521      while ((format != formats.end()) && 
00522               static_cast<Element*>((*format))->get_attribute("name")->get_value() != format_name){++format; }
00523      if (format != formats.end()) {
00524         // found the format we want
00525         const Node::NodeList& children = (*format)->get_children();
00526         Node::NodeList::const_iterator curr_child = children.begin();
00527         for(; curr_child != children.end(); ++curr_child) {
00528           if ((*curr_child)->get_name() == "filename") 
00529           {
00530               oformat.filename = getChildContent(*curr_child);
00531               expand_tilde(oformat.filename);
00532           } 
00533           else if ((*curr_child)->get_name() == "run-format") 
00534           {
00535              oformat.chained_formats.push_back(getPropValue(*curr_child, "name"));
00536           } 
00537           else if ((*curr_child)->get_name() == "begin") 
00538           {
00539              add_format_line(oformat.begin, getChildContent(*curr_child));
00540           } 
00541           else if ((*curr_child)->get_name() == "track") 
00542           {
00543              add_format_line(oformat.track, getChildContent(*curr_child));
00544           } 
00545           else if ((*curr_child)->get_name() == "end") 
00546           {
00547              add_format_line(oformat.end, getChildContent(*curr_child));
00548           } 
00549           else if ((*curr_child)->get_name() == "exec") 
00550           {
00551              oformat.exec = getChildContent(*curr_child);
00552           }
00553         }
00554         success = true;
00555      } else {
00556         cerr << "cant find the requested format \"" << format_name << "\"" << endl;
00557      }
00558      
00559   } 
00560   else 
00561   {
00562      cerr << "Cant find a config file" << endl;
00563   }
00564   
00565   return success;
00566 
00567 }
00568 
00570 
00574 int PlayListConverter::get_formats(FormatDescList& format_desc)
00575 {
00576   bool success(false);
00577   string filename;  
00578   format_desc.clear();
00579 
00580   if (get_config_file(filename)) 
00581   {
00582      DomParser config(filename);
00583 
00584      Node* root = config.get_root_node();
00585 
00586      Node::NodeList formats = root->get_children("format");
00587      Node::NodeList::const_iterator iter = formats.begin();
00588      for(; iter != formats.end(); ++iter) {
00589         FormatDesc a_format;
00590         a_format.name = getPropValue(*iter, "name");
00591         getChildContent(*iter, "desc", a_format.desc);
00592         format_desc.push_back(a_format);
00593      }
00594 
00595      format_desc.sort();
00596   }
00597   return format_desc.size();
00598 }
00599 
00601 
00604 void PlayListConverter::open_output(ofstream& os) 
00605 {
00606   if (! m_session.output_file.empty()) {
00607      string& filename = m_session.output_file;
00608      size_t slashpos = filename.rfind('/');
00609 
00610      if (slashpos != string::npos) {
00611         // theres a directory component, create as necessary        
00612         make_dir(filename.substr(0, slashpos));
00613      }
00614 
00615      os.open(filename.c_str());
00616   }
00617 }

Generated on Sat May 3 09:16:13 2003 for empxform by doxygen1.3-rc3