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