data-mp3.c
author nathan
Sat, 29 Dec 2007 14:49:09 +0100
branchtrunk
changeset 2 4c1f7b705009
parent 0 474a1293c3c0
child 19 306cc35c7faa
permissions -rw-r--r--
release 0.10.1
     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 }