data-mp3.c
author nathan
Fri, 02 Jan 2009 13:46:58 +0100
branchtrunk
changeset 19 306cc35c7faa
parent 0 474a1293c3c0
child 22 93aaf15c145a
permissions -rw-r--r--
add commandline option for user defined default background image
     1 /*
     2  * MP3/MPlayer plugin to VDR (C++)
     3  *
     4  * (C) 2001-2006 Stefan Huelswitt <s.huelswitt@gmx.de>
     5  *
     6  * This code is free software; you can redistribute it and/or
     7  * modify it under the terms of the GNU General Public License
     8  * as published by the Free Software Foundation; either version 2
     9  * of the License, or (at your option) any later version.
    10  *
    11  * This code is distributed in the hope that it will be useful,
    12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  * GNU General Public License for more details.
    15  *
    16  * You should have received a copy of the GNU General Public License
    17  * along with this program; if not, write to the Free Software
    18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    19  * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
    20  */
    21 
    22 #include <stdlib.h>
    23 #include <stdio.h>
    24 #include <unistd.h>
    25 
    26 #include "common.h"
    27 #include "data-mp3.h"
    28 #include "data.h"
    29 #include "decoder.h"
    30 
    31 #define DEBUG_IMAGE
    32 
    33 #ifdef DEBUG_IMAGE
    34 #define di(x) { (x); }
    35 #else
    36 #define di(x) ; 
    37 #endif
    38 
    39 const char *imagecache = "/var/cache/images/mp3";
    40 const char *imageconv  = "image_convert.sh";
    41 const char *def_usr_img = 0;
    42 
    43 // image suffixes to search
    44 const char *img_suff[] = { "jpg","png","gif",0 };
    45 // exclude list for instant playlist creation
    46 const char *excl_pl[] = { "*"PLAYLISTEXT,"*.jpg","*.gif","*.png",0 };
    47 // exclude list for song browser
    48 const char *excl_br[] = { ".*","*.jpg","*.gif","*.png",0 };
    49 
    50 // --- cImageConvert -----------------------------------------------------------
    51 
    52 class cImageConvert : private cThread {
    53 private:
    54   char *image;
    55   enum eStatus { stNone, stRun, stFin };
    56   eStatus status;
    57 protected:
    58   virtual void Action(void);
    59 public:
    60   cImageConvert(void);
    61   ~cImageConvert();
    62   bool Convert(const char *Image);
    63   bool Status(void);
    64   };
    65 
    66 cImageConvert::cImageConvert(void)
    67 {
    68   image=0; status=stNone;
    69 }
    70 
    71 cImageConvert::~cImageConvert()
    72 {
    73   if(status==stRun) Cancel(10);
    74   free(image);
    75 }
    76 
    77 bool cImageConvert::Convert(const char *Image)
    78 {
    79   if(status==stNone) {
    80     image=strdup(Image);
    81     status=stRun;
    82     Start();
    83     return true;
    84     }
    85   return false;
    86 }
    87 
    88 bool cImageConvert::Status(void)
    89 {
    90   if(status==stRun && !Active()) status=stFin;
    91   return status==stFin;
    92 }
    93 
    94 void cImageConvert::Action(void)
    95 {
    96   nice(3);
    97   char *m, *cmd, *qp, *qm;
    98   asprintf(&m,"%s%s.mpg",imagecache,image);
    99   di(printf("image: convert started %s -> %s\n",image,m))
   100   asprintf(&cmd,"%s \"%s\" \"%s\"",imageconv,qp=Quote(image),qm=Quote(m));
   101   int r=system(cmd);
   102   if(r!=0) di(printf("image: convert returned with code %d. Failed?\n",r))
   103   free(cmd); free(qp); free(qm); free(m);
   104   di(printf("image: convert finished\n"))
   105   status=stFin;
   106 }
   107 
   108 // --- cSong -------------------------------------------------------------------
   109 
   110 cSong::cSong(cFileObj *Obj)
   111 {
   112   obj=new cFileObj(Obj);
   113   Init();
   114 }
   115 
   116 cSong::cSong(cFileSource *Source, const char *Subdir, const char *Name)
   117 {
   118   obj=new cFileObj(Source,Subdir,Name,otFile);
   119   Init();
   120 }
   121 
   122 cSong::cSong(cSong *Song)
   123 {
   124   obj=new cFileObj(Song->obj);
   125   Init();
   126 }
   127 
   128 cSong::~cSong()
   129 {
   130   delete conv;
   131   delete decoder;
   132   obj->Source()->Unblock();
   133   delete obj;
   134   free((char *)image);
   135 }
   136 
   137 void cSong::Init(void)
   138 {
   139   decoder=0; user=0; image=0; conv=0; queueStat=0;
   140   fromDOS=decoderFailed=false;
   141   obj->Source()->Block();
   142 }
   143 
   144 #if APIVERSNUM >= 10315
   145 int cSong::Compare(const cListObject &ListObject) const
   146 #else
   147 bool cSong::operator<(const cListObject &ListObject)
   148 #endif
   149 {
   150   cSong *song=(cSong *)&ListObject;
   151 #if APIVERSNUM >= 10315
   152   return strcasecmp(obj->Path(),song->obj->Path());
   153 #else
   154   return strcasecmp(obj->Path(),song->obj->Path())<0;
   155 #endif
   156 }
   157 
   158 cSongInfo *cSong::Info(bool get)
   159 {
   160   Decoder();
   161   cSongInfo *si=0;
   162   if(decoder) si=decoder->SongInfo(get);
   163   return si;
   164 }
   165 
   166 cDecoder *cSong::Decoder(void)
   167 {
   168   decLock.Lock();
   169   if(!decoder && !decoderFailed) {
   170     decoder=cDecoders::FindDecoder(obj);
   171     if(!decoder) decoderFailed=true;
   172     }
   173   decLock.Unlock();
   174   return decoder;
   175 }
   176 
   177 void cSong::Convert(void)
   178 {
   179   char *Name=Convert2Unix(obj->Name());
   180   obj->SetName(Name);
   181   fromDOS=true;
   182   free(Name);
   183 }
   184 
   185 char *cSong::Convert2Unix(const char *name) const
   186 {
   187   char *Name=strdup(name);
   188   char *p=Name;
   189   while(*p) {
   190     if(*p=='/') *p='?';
   191     if(*p=='\\') *p='/';
   192     p++;
   193     }
   194   return Name;
   195 }
   196 
   197 /*
   198 char *cSong::Convert2Dos(const char *name)
   199 {
   200   char *Name=strdup(name);
   201   char *p=Name;
   202   while(*p) {
   203     if(*p=='\\') *p='?';
   204     if(*p=='/') *p='\\';
   205     p++;
   206     }
   207   return Name;
   208 }
   209 */
   210 
   211 bool cSong::Parse(char *s, const char *reldir) const
   212 {
   213   s=skipspace(stripspace(s));
   214   if(*s) {
   215     if(s[0]=='/' || !reldir)
   216       obj->SplitAndSet(s);
   217     else {
   218       s=AddPath(reldir,s);
   219       obj->SplitAndSet(s);
   220       free(s);
   221       }
   222     return true;
   223     }
   224   return false;
   225 }
   226 
   227 bool cSong::Save(FILE *f, const char *reldir) const
   228 {
   229   const char *path=obj->Path();
   230   if(reldir) {
   231     int l=strlen(reldir);
   232     if(!strncasecmp(path,reldir,l)) path+=l+1;
   233     }
   234   return fprintf(f,"%s\n",path)>0;
   235 }
   236 
   237 bool cSong::FindImage(void)
   238 {
   239   if(image) return true;
   240 
   241   char base[strlen(obj->Path())+32];
   242   strcpy(base,obj->Path());
   243   di(printf("image: checking image for %s\n",obj->Path()))
   244 
   245   // song specific image
   246   char *m=rindex(base,'.');
   247   if(m) *m=0;
   248   if((image=CheckImage(base))) return true;
   249 
   250   // album specific image in song directory
   251   if(!(m=rindex(base,'/'))) m=base-1;
   252   strcpy(m+1,"cover");
   253   if((image=CheckImage(base))) return true;
   254 
   255   // artist specific image in parent directory
   256   if((m=rindex(base,'/'))) {
   257     *m=0;
   258     if(!(m=rindex(base,'/'))) m=base-1;
   259     strcpy(m+1,"artist");
   260     if((image=CheckImage(base))) return true;
   261     }
   262 
   263   // default image in source basedir
   264   if((image=CheckImage("background"))) return true;
   265 
   266   // default user supplied image
   267   if(def_usr_img && (image=strdup(def_usr_img))) return true;
   268 
   269   di(printf("image: no image for %s\n",obj->Path()))
   270   return false;
   271 }
   272 
   273 const char *cSong::CheckImage(const char *base) const
   274 {
   275   char *p;
   276   int n;
   277   asprintf(&p,"%s/%s.%n     ",obj->Source()->BaseDir(),base,&n);
   278   for(const char **s=img_suff; *s; s++) {
   279 #ifdef DEBUG
   280     if(strlen(*s)>5) printf("ERROR: buffer overflow in CheckImage ext=%s\n",*s);
   281 #endif
   282     strcpy(&p[n],*s);
   283     di(printf("image: check %s\n",p))
   284     if(!access(p,R_OK)) {
   285       di(printf("image: found\n"))
   286       return p;
   287       }
   288     }
   289   free(p);
   290   return 0;
   291 }
   292 
   293 #include "data-mp3-image.c"
   294 extern void PropagateImage(const char *image);
   295 
   296 bool cSong::Image(unsigned char * &mem, int &len)
   297 {
   298   mem=0;
   299   if(queueStat>0) {
   300     if(!conv->Status()) {
   301       di(printf("image: still queued\n"))
   302       return false;
   303       }
   304     queueStat=-1;
   305     delete conv; conv=0;
   306     }
   307 
   308   int res=0;
   309   if(image || FindImage()) {
   310     di(printf("image: loading image %s\n",image))
   311     char *m;
   312     asprintf(&m,"%s%s.mpg",imagecache,image);
   313     if(access(m,R_OK)) {
   314       di(printf("image: not cached\n"))
   315       if(queueStat<0) {
   316         di(printf("image: obviously convert failed...\n"))
   317         }
   318       else {
   319         if(!conv) conv=new cImageConvert;
   320         if(conv && conv->Convert(image)) {
   321           di(printf("image: convert queued\n"))
   322           queueStat=1;
   323           res=-1;
   324           }
   325         else {
   326           di(printf("image: queueing failed\n"))
   327           queueStat=-1;
   328           }
   329         }
   330       }
   331     else {
   332       di(printf("image: cached\n"))
   333       int f=open(m,O_RDONLY);
   334       if(f>=0) {
   335         struct stat64 st;
   336         fstat64(f,&st);
   337         len=st.st_size;
   338         mem=MALLOC(unsigned char,len);
   339         if(mem) {
   340           if(read(f,mem,len)==len) res=1;
   341           else free(mem);
   342           }
   343         close(f);
   344         }
   345       }
   346     free(m);
   347     }
   348 
   349   PropagateImage(res==1 ? image : 0);
   350 
   351   if(res<=0) {
   352     di(printf("image: using static default image\n"))
   353     len=sizeof(defaultImage);
   354     mem=MALLOC(unsigned char,len);
   355     if(mem) {
   356       memcpy(mem,defaultImage,len);
   357       }
   358     }
   359   return res>=0;
   360 }
   361 
   362 // -- cPlayList --------------------------------------------------------------
   363 
   364 cPlayList::cPlayList(cFileObj *Obj)
   365 {
   366   obj=new cFileObj(Obj);
   367   Init();
   368 }
   369 
   370 cPlayList::cPlayList(cFileSource *Source, const char *Subdir, const char *Name)
   371 {
   372   obj=new cFileObj(Source,Subdir,Name,otFile);
   373   Init();
   374 }
   375 
   376 cPlayList::cPlayList(cPlayList *List)
   377 {
   378   obj=new cFileObj(List->obj);
   379   Init();
   380 }
   381 
   382 cPlayList::~cPlayList()
   383 {
   384   free(basename);
   385   free(extbuffer);
   386   obj->Source()->Unblock();
   387   delete obj;
   388 }
   389 
   390 void cPlayList::Init(void)
   391 {
   392   extbuffer=basename=0;
   393   isWinAmp=false;
   394   obj->Source()->Block();
   395   Set();
   396 }
   397 
   398 void cPlayList::Set(void)
   399 {
   400   free(basename); basename=0;
   401   if(obj->Name()) {
   402     basename=strdup(obj->Name());
   403     int l=strlen(basename)-strlen(PLAYLISTEXT);
   404     if(l>0 && !strcasecmp(basename+l,PLAYLISTEXT)) basename[l]=0;
   405     }
   406 }
   407 
   408 #if APIVERSNUM >= 10315
   409 int cPlayList::Compare(const cListObject &ListObject) const
   410 #else
   411 bool cPlayList::operator<(const cListObject &ListObject)
   412 #endif
   413 {
   414   cPlayList *list=(cPlayList *)&ListObject;
   415 #if APIVERSNUM >= 10315
   416   return strcasecmp(obj->Name(),list->obj->Name());
   417 #else
   418   return strcasecmp(obj->Name(),list->obj->Name())<0;
   419 #endif
   420 }
   421 
   422 bool cPlayList::Load(void)
   423 {
   424   Clear();
   425   bool result=false;
   426   FILE *f=fopen(obj->FullPath(),"r");
   427   if(f) {
   428     char buffer[512];
   429     result=true;
   430     while(fgets(buffer,sizeof(buffer),f)>0) {
   431       if(buffer[0]=='#') {
   432         if(!strncmp(buffer,WINAMPEXT,strlen(WINAMPEXT))) {
   433           d(printf("mp3: detected WinAmp style playlist\n"))
   434           isWinAmp=true;
   435           }
   436         continue;
   437         }
   438       if(!isempty(buffer)) {
   439         cSong *song=new cSong(obj->Source(),0,0);
   440         if(song->Parse(buffer,obj->Subdir())) Add(song);
   441         else {
   442           esyslog("error loading playlist %s\n",obj->FullPath());
   443           delete song;
   444           result=false;
   445           break;
   446           }
   447         }
   448       }
   449     fclose(f);
   450     }
   451   else LOG_ERROR_STR(obj->FullPath());
   452 
   453   if(result && isWinAmp) {
   454     cSong *song=First();
   455     while(song) {   // if this is a WinAmp playlist, convert \ to /
   456       song->Convert();
   457       song=cList<cSong>::Next(song);
   458       }
   459     }
   460   return result;
   461 }
   462 
   463 bool cPlayList::Save(void)
   464 {
   465   bool result=true;
   466   cSafeFile f(obj->FullPath());
   467   if(f.Open()) {
   468     cSong *song=First();
   469     while(song) {
   470       if(!song->Save(f,obj->Subdir())) {
   471          result=false;
   472          break;
   473          }
   474       song=cList<cSong>::Next(song);
   475       }
   476     if(!f.Close()) result=false;
   477     }
   478   else result=false;
   479   return result;
   480 }
   481  
   482 bool cPlayList::Exists(void)
   483 {
   484   return obj->Exists();
   485 }
   486 
   487 bool cPlayList::TestName(const char *newName)
   488 {
   489   return obj->TestName(AddExt(newName,PLAYLISTEXT));
   490 }
   491 
   492 bool cPlayList::Rename(const char *newName)
   493 {
   494   bool r=obj->Rename(AddExt(newName,PLAYLISTEXT));
   495   if(r) Set();
   496   return r;
   497 }
   498 
   499 bool cPlayList::Create(const char *newName)
   500 {
   501   bool r=obj->Create(AddExt(newName,PLAYLISTEXT));
   502   if(r) {
   503     Set();
   504     r=Load();
   505     }
   506   return r;
   507 }
   508 
   509 bool cPlayList::Delete(void)
   510 {
   511   return obj->Delete();
   512 }
   513 
   514 const char *cPlayList::AddExt(const char *FileName, const char *Ext)
   515 {
   516   free(extbuffer); extbuffer=0;
   517   asprintf(&extbuffer,"%s%s",FileName,Ext);
   518   return extbuffer;
   519 }
   520 
   521 // -- cInstantPlayList ------------------------------------------------------
   522 
   523 cInstantPlayList::cInstantPlayList(cFileObj *Obj)
   524 :cPlayList(Obj)
   525 {
   526   if(!Obj->Name()) Obj->SetName("instant");
   527 }
   528 
   529 bool cInstantPlayList::Load(void)
   530 {
   531   bool res=false;
   532   Clear();
   533   switch(obj->Type()) {
   534     case otFile:
   535       d(printf("instant: file %s\n",obj->Name()))
   536       if(strcasecmp(obj->Name(),basename)) {
   537         d(printf("instant: detected as playlist\n"))
   538         res=cPlayList::Load();
   539         }
   540       else {
   541         Add(new cSong(obj));
   542         res=true;
   543         }
   544       break;
   545     case otDir:
   546       {
   547       d(printf("instant: dir %s\n",obj->Name()))
   548       res=ScanDir(obj->Source(),obj->Path(),stFile,obj->Source()->Include(),excl_pl,true);
   549       Sort();
   550       break;
   551       }
   552     case otBase:
   553       d(printf("instant: base\n"))
   554       res=ScanDir(obj->Source(),0,stFile,obj->Source()->Include(),excl_pl,true);
   555       Sort();
   556       break;
   557     default: break;
   558     }
   559   return res;
   560 }
   561 
   562 void cInstantPlayList::DoItem(cFileSource *src, const char *subdir, const char *name)
   563 {
   564   Add(new cSong(src,subdir,name));
   565 }
   566 
   567 // -- cPlayLists --------------------------------------------------------------
   568 
   569 bool cPlayLists::Load(cFileSource *Source)
   570 {
   571   static const char *spec[] = { "*"PLAYLISTEXT,0 };
   572   Clear();
   573   bool res=ScanDir(Source,0,stFile,spec,0,false);
   574   Sort();
   575   return res;
   576 }
   577 
   578 void cPlayLists::DoItem(cFileSource *src, const char *subdir, const char *name)
   579 {
   580   Add(new cPlayList(src,subdir,name));
   581 }