data-mp3.c
author nathan
Sun, 12 Dec 2010 12:15:17 +0100
branchtrunk
changeset 40 31537cd8ec5e
parent 29 640ce9201139
permissions -rw-r--r--
fixed image_convert.sh for newer mjpegtools
     1 /*
     2  * MP3/MPlayer plugin to VDR (C++)
     3  *
     4  * (C) 2001-2009 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   if(nice(3)<0);
    97   char *qp, *qm;
    98   char *m=aprintf("%s%s.mpg",imagecache,image);
    99   di(printf("image: convert started %s -> %s\n",image,m))
   100   char *cmd=aprintf("%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 int cSong::Compare(const cListObject &ListObject) const
   145 {
   146   cSong *song=(cSong *)&ListObject;
   147   return strcasecmp(obj->Path(),song->obj->Path());
   148 }
   149 
   150 cSongInfo *cSong::Info(bool get)
   151 {
   152   Decoder();
   153   cSongInfo *si=0;
   154   if(decoder) si=decoder->SongInfo(get);
   155   return si;
   156 }
   157 
   158 cDecoder *cSong::Decoder(void)
   159 {
   160   decLock.Lock();
   161   if(!decoder && !decoderFailed) {
   162     decoder=cDecoders::FindDecoder(obj);
   163     if(!decoder) decoderFailed=true;
   164     }
   165   decLock.Unlock();
   166   return decoder;
   167 }
   168 
   169 void cSong::Convert(void)
   170 {
   171   char *Name=Convert2Unix(obj->Name());
   172   obj->SetName(Name);
   173   fromDOS=true;
   174   free(Name);
   175 }
   176 
   177 char *cSong::Convert2Unix(const char *name) const
   178 {
   179   char *Name=strdup(name);
   180   char *p=Name;
   181   while(*p) {
   182     if(*p=='/') *p='?';
   183     if(*p=='\\') *p='/';
   184     p++;
   185     }
   186   return Name;
   187 }
   188 
   189 /*
   190 char *cSong::Convert2Dos(const char *name)
   191 {
   192   char *Name=strdup(name);
   193   char *p=Name;
   194   while(*p) {
   195     if(*p=='\\') *p='?';
   196     if(*p=='/') *p='\\';
   197     p++;
   198     }
   199   return Name;
   200 }
   201 */
   202 
   203 bool cSong::Parse(char *s, const char *reldir) const
   204 {
   205   s=skipspace(stripspace(s));
   206   if(*s) {
   207     if(s[0]=='/' || !reldir)
   208       obj->SplitAndSet(s);
   209     else {
   210       s=AddPath(reldir,s);
   211       obj->SplitAndSet(s);
   212       free(s);
   213       }
   214     return true;
   215     }
   216   return false;
   217 }
   218 
   219 bool cSong::Save(FILE *f, const char *reldir) const
   220 {
   221   const char *path=obj->Path();
   222   if(reldir) {
   223     int l=strlen(reldir);
   224     if(!strncasecmp(path,reldir,l)) path+=l+1;
   225     }
   226   return fprintf(f,"%s\n",path)>0;
   227 }
   228 
   229 bool cSong::FindImage(void)
   230 {
   231   if(image) return true;
   232 
   233   char base[strlen(obj->Path())+32];
   234   strcpy(base,obj->Path());
   235   di(printf("image: checking image for %s\n",obj->Path()))
   236 
   237   // song specific image
   238   char *m=rindex(base,'.');
   239   if(m) *m=0;
   240   if((image=CheckImage(base))) return true;
   241 
   242   // album specific image in song directory
   243   if(!(m=rindex(base,'/'))) m=base-1;
   244   strcpy(m+1,"cover");
   245   if((image=CheckImage(base))) return true;
   246 
   247   // artist specific image in parent directory
   248   if((m=rindex(base,'/'))) {
   249     *m=0;
   250     if(!(m=rindex(base,'/'))) m=base-1;
   251     strcpy(m+1,"artist");
   252     if((image=CheckImage(base))) return true;
   253     }
   254 
   255   // default image in source basedir
   256   if((image=CheckImage("background"))) return true;
   257 
   258   // default user supplied image
   259   if(def_usr_img && (image=strdup(def_usr_img))) return true;
   260 
   261   di(printf("image: no image for %s\n",obj->Path()))
   262   return false;
   263 }
   264 
   265 const char *cSong::CheckImage(const char *base) const
   266 {
   267   int n;
   268   char *p=aprintf("%s/%s.%n     ",obj->Source()->BaseDir(),base,&n);
   269   for(const char **s=img_suff; *s; s++) {
   270 #ifdef DEBUG
   271     if(strlen(*s)>5) printf("ERROR: buffer overflow in CheckImage ext=%s\n",*s);
   272 #endif
   273     strcpy(&p[n],*s);
   274     di(printf("image: check %s\n",p))
   275     if(!access(p,R_OK)) {
   276       di(printf("image: found\n"))
   277       return p;
   278       }
   279     }
   280   free(p);
   281   return 0;
   282 }
   283 
   284 #include "data-mp3-image.c"
   285 extern void PropagateImage(const char *image);
   286 
   287 bool cSong::Image(unsigned char * &mem, int &len)
   288 {
   289   mem=0;
   290   if(queueStat>0) {
   291     if(!conv->Status()) {
   292       di(printf("image: still queued\n"))
   293       return false;
   294       }
   295     queueStat=-1;
   296     delete conv; conv=0;
   297     }
   298 
   299   int res=0;
   300   if(image || FindImage()) {
   301     di(printf("image: loading image %s\n",image))
   302     char *m=aprintf("%s%s.mpg",imagecache,image);
   303     if(access(m,R_OK)) {
   304       di(printf("image: not cached\n"))
   305       if(queueStat<0) {
   306         di(printf("image: obviously convert failed...\n"))
   307         }
   308       else {
   309         if(!conv) conv=new cImageConvert;
   310         if(conv && conv->Convert(image)) {
   311           di(printf("image: convert queued\n"))
   312           queueStat=1;
   313           res=-1;
   314           }
   315         else {
   316           di(printf("image: queueing failed\n"))
   317           queueStat=-1;
   318           }
   319         }
   320       }
   321     else {
   322       di(printf("image: cached\n"))
   323       int f=open(m,O_RDONLY);
   324       if(f>=0) {
   325         struct stat64 st;
   326         fstat64(f,&st);
   327         len=st.st_size;
   328         mem=MALLOC(unsigned char,len);
   329         if(mem) {
   330           if(read(f,mem,len)==len) res=1;
   331           else free(mem);
   332           }
   333         close(f);
   334         }
   335       }
   336     free(m);
   337     }
   338 
   339   PropagateImage(res==1 ? image : 0);
   340 
   341   if(res<=0) {
   342     di(printf("image: using static default image\n"))
   343     len=sizeof(defaultImage);
   344     mem=MALLOC(unsigned char,len);
   345     if(mem) {
   346       memcpy(mem,defaultImage,len);
   347       }
   348     }
   349   return res>=0;
   350 }
   351 
   352 // -- cPlayList --------------------------------------------------------------
   353 
   354 cPlayList::cPlayList(cFileObj *Obj)
   355 {
   356   obj=new cFileObj(Obj);
   357   Init();
   358 }
   359 
   360 cPlayList::cPlayList(cFileSource *Source, const char *Subdir, const char *Name)
   361 {
   362   obj=new cFileObj(Source,Subdir,Name,otFile);
   363   Init();
   364 }
   365 
   366 cPlayList::cPlayList(cPlayList *List)
   367 {
   368   obj=new cFileObj(List->obj);
   369   Init();
   370 }
   371 
   372 cPlayList::~cPlayList()
   373 {
   374   free(basename);
   375   free(extbuffer);
   376   obj->Source()->Unblock();
   377   delete obj;
   378 }
   379 
   380 void cPlayList::Init(void)
   381 {
   382   extbuffer=basename=0;
   383   isWinAmp=false;
   384   obj->Source()->Block();
   385   Set();
   386 }
   387 
   388 void cPlayList::Set(void)
   389 {
   390   free(basename); basename=0;
   391   if(obj->Name()) {
   392     basename=strdup(obj->Name());
   393     int l=strlen(basename)-strlen(PLAYLISTEXT);
   394     if(l>0 && !strcasecmp(basename+l,PLAYLISTEXT)) basename[l]=0;
   395     }
   396 }
   397 
   398 int cPlayList::Compare(const cListObject &ListObject) const
   399 {
   400   cPlayList *list=(cPlayList *)&ListObject;
   401   return strcasecmp(obj->Name(),list->obj->Name());
   402 }
   403 
   404 bool cPlayList::Load(void)
   405 {
   406   Clear();
   407   bool result=false;
   408   FILE *f=fopen(obj->FullPath(),"r");
   409   if(f) {
   410     char buffer[512];
   411     result=true;
   412     while(fgets(buffer,sizeof(buffer),f)>0) {
   413       if(buffer[0]=='#') {
   414         if(!strncmp(buffer,WINAMPEXT,strlen(WINAMPEXT))) {
   415           d(printf("mp3: detected WinAmp style playlist\n"))
   416           isWinAmp=true;
   417           }
   418         continue;
   419         }
   420       if(!isempty(buffer)) {
   421         cSong *song=new cSong(obj->Source(),0,0);
   422         if(song->Parse(buffer,obj->Subdir())) Add(song);
   423         else {
   424           esyslog("error loading playlist %s\n",obj->FullPath());
   425           delete song;
   426           result=false;
   427           break;
   428           }
   429         }
   430       }
   431     fclose(f);
   432     }
   433   else LOG_ERROR_STR(obj->FullPath());
   434 
   435   if(result && isWinAmp) {
   436     cSong *song=First();
   437     while(song) {   // if this is a WinAmp playlist, convert \ to /
   438       song->Convert();
   439       song=cList<cSong>::Next(song);
   440       }
   441     }
   442   return result;
   443 }
   444 
   445 bool cPlayList::Save(void)
   446 {
   447   bool result=true;
   448   cSafeFile f(obj->FullPath());
   449   if(f.Open()) {
   450     cSong *song=First();
   451     while(song) {
   452       if(!song->Save(f,obj->Subdir())) {
   453          result=false;
   454          break;
   455          }
   456       song=cList<cSong>::Next(song);
   457       }
   458     if(!f.Close()) result=false;
   459     }
   460   else result=false;
   461   return result;
   462 }
   463  
   464 bool cPlayList::Exists(void)
   465 {
   466   return obj->Exists();
   467 }
   468 
   469 bool cPlayList::TestName(const char *newName)
   470 {
   471   return obj->TestName(AddExt(newName,PLAYLISTEXT));
   472 }
   473 
   474 bool cPlayList::Rename(const char *newName)
   475 {
   476   bool r=obj->Rename(AddExt(newName,PLAYLISTEXT));
   477   if(r) Set();
   478   return r;
   479 }
   480 
   481 bool cPlayList::Create(const char *newName)
   482 {
   483   bool r=obj->Create(AddExt(newName,PLAYLISTEXT));
   484   if(r) {
   485     Set();
   486     r=Load();
   487     }
   488   return r;
   489 }
   490 
   491 bool cPlayList::Delete(void)
   492 {
   493   return obj->Delete();
   494 }
   495 
   496 const char *cPlayList::AddExt(const char *FileName, const char *Ext)
   497 {
   498   free(extbuffer);
   499   extbuffer=aprintf("%s%s",FileName,Ext);
   500   return extbuffer;
   501 }
   502 
   503 // -- cInstantPlayList ------------------------------------------------------
   504 
   505 cInstantPlayList::cInstantPlayList(cFileObj *Obj)
   506 :cPlayList(Obj)
   507 {
   508   if(!Obj->Name()) Obj->SetName("instant");
   509 }
   510 
   511 bool cInstantPlayList::Load(void)
   512 {
   513   bool res=false;
   514   Clear();
   515   switch(obj->Type()) {
   516     case otFile:
   517       d(printf("instant: file %s\n",obj->Name()))
   518       if(strcasecmp(obj->Name(),basename)) {
   519         d(printf("instant: detected as playlist\n"))
   520         res=cPlayList::Load();
   521         }
   522       else {
   523         Add(new cSong(obj));
   524         res=true;
   525         }
   526       break;
   527     case otDir:
   528       {
   529       d(printf("instant: dir %s\n",obj->Name()))
   530       res=ScanDir(obj->Source(),obj->Path(),stFile,obj->Source()->Include(),excl_pl,true);
   531       Sort();
   532       break;
   533       }
   534     case otBase:
   535       d(printf("instant: base\n"))
   536       res=ScanDir(obj->Source(),0,stFile,obj->Source()->Include(),excl_pl,true);
   537       Sort();
   538       break;
   539     default: break;
   540     }
   541   return res;
   542 }
   543 
   544 void cInstantPlayList::DoItem(cFileSource *src, const char *subdir, const char *name)
   545 {
   546   Add(new cSong(src,subdir,name));
   547 }
   548 
   549 // -- cPlayLists --------------------------------------------------------------
   550 
   551 bool cPlayLists::Load(cFileSource *Source)
   552 {
   553   static const char *spec[] = { "*"PLAYLISTEXT,0 };
   554   Clear();
   555   bool res=ScanDir(Source,0,stFile,spec,0,false);
   556   Sort();
   557   return res;
   558 }
   559 
   560 void cPlayLists::DoItem(cFileSource *src, const char *subdir, const char *name)
   561 {
   562   Add(new cPlayList(src,subdir,name));
   563 }