2 * MP3/MPlayer plugin to VDR (C++)
4 * (C) 2001-2006 Stefan Huelswitt <s.huelswitt@gmx.de>
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.
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.
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
34 #define di(x) { (x); }
39 const char *imagecache = "/var/cache/images/mp3";
40 const char *imageconv = "image_convert.sh";
41 const char *def_usr_img = 0;
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 };
50 // --- cImageConvert -----------------------------------------------------------
52 class cImageConvert : private cThread {
55 enum eStatus { stNone, stRun, stFin };
58 virtual void Action(void);
62 bool Convert(const char *Image);
66 cImageConvert::cImageConvert(void)
68 image=0; status=stNone;
71 cImageConvert::~cImageConvert()
73 if(status==stRun) Cancel(10);
77 bool cImageConvert::Convert(const char *Image)
88 bool cImageConvert::Status(void)
90 if(status==stRun && !Active()) status=stFin;
94 void cImageConvert::Action(void)
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));
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"))
108 // --- cSong -------------------------------------------------------------------
110 cSong::cSong(cFileObj *Obj)
112 obj=new cFileObj(Obj);
116 cSong::cSong(cFileSource *Source, const char *Subdir, const char *Name)
118 obj=new cFileObj(Source,Subdir,Name,otFile);
122 cSong::cSong(cSong *Song)
124 obj=new cFileObj(Song->obj);
132 obj->Source()->Unblock();
137 void cSong::Init(void)
139 decoder=0; user=0; image=0; conv=0; queueStat=0;
140 fromDOS=decoderFailed=false;
141 obj->Source()->Block();
144 #if APIVERSNUM >= 10315
145 int cSong::Compare(const cListObject &ListObject) const
147 bool cSong::operator<(const cListObject &ListObject)
150 cSong *song=(cSong *)&ListObject;
151 #if APIVERSNUM >= 10315
152 return strcasecmp(obj->Path(),song->obj->Path());
154 return strcasecmp(obj->Path(),song->obj->Path())<0;
158 cSongInfo *cSong::Info(bool get)
162 if(decoder) si=decoder->SongInfo(get);
166 cDecoder *cSong::Decoder(void)
169 if(!decoder && !decoderFailed) {
170 decoder=cDecoders::FindDecoder(obj);
171 if(!decoder) decoderFailed=true;
177 void cSong::Convert(void)
179 char *Name=Convert2Unix(obj->Name());
185 char *cSong::Convert2Unix(const char *name) const
187 char *Name=strdup(name);
198 char *cSong::Convert2Dos(const char *name)
200 char *Name=strdup(name);
211 bool cSong::Parse(char *s, const char *reldir) const
213 s=skipspace(stripspace(s));
215 if(s[0]=='/' || !reldir)
227 bool cSong::Save(FILE *f, const char *reldir) const
229 const char *path=obj->Path();
231 int l=strlen(reldir);
232 if(!strncasecmp(path,reldir,l)) path+=l+1;
234 return fprintf(f,"%s\n",path)>0;
237 bool cSong::FindImage(void)
239 if(image) return true;
241 char base[strlen(obj->Path())+32];
242 strcpy(base,obj->Path());
243 di(printf("image: checking image for %s\n",obj->Path()))
245 // song specific image
246 char *m=rindex(base,'.');
248 if((image=CheckImage(base))) return true;
250 // album specific image in song directory
251 if(!(m=rindex(base,'/'))) m=base-1;
253 if((image=CheckImage(base))) return true;
255 // artist specific image in parent directory
256 if((m=rindex(base,'/'))) {
258 if(!(m=rindex(base,'/'))) m=base-1;
259 strcpy(m+1,"artist");
260 if((image=CheckImage(base))) return true;
263 // default image in source basedir
264 if((image=CheckImage("background"))) return true;
266 // default user supplied image
267 if(def_usr_img && (image=strdup(def_usr_img))) return true;
269 di(printf("image: no image for %s\n",obj->Path()))
273 const char *cSong::CheckImage(const char *base) const
277 asprintf(&p,"%s/%s.%n ",obj->Source()->BaseDir(),base,&n);
278 for(const char **s=img_suff; *s; s++) {
280 if(strlen(*s)>5) printf("ERROR: buffer overflow in CheckImage ext=%s\n",*s);
283 di(printf("image: check %s\n",p))
284 if(!access(p,R_OK)) {
285 di(printf("image: found\n"))
293 #include "data-mp3-image.c"
294 extern void PropagateImage(const char *image);
296 bool cSong::Image(unsigned char * &mem, int &len)
300 if(!conv->Status()) {
301 di(printf("image: still queued\n"))
309 if(image || FindImage()) {
310 di(printf("image: loading image %s\n",image))
312 asprintf(&m,"%s%s.mpg",imagecache,image);
314 di(printf("image: not cached\n"))
316 di(printf("image: obviously convert failed...\n"))
319 if(!conv) conv=new cImageConvert;
320 if(conv && conv->Convert(image)) {
321 di(printf("image: convert queued\n"))
326 di(printf("image: queueing failed\n"))
332 di(printf("image: cached\n"))
333 int f=open(m,O_RDONLY);
338 mem=MALLOC(unsigned char,len);
340 if(read(f,mem,len)==len) res=1;
349 PropagateImage(res==1 ? image : 0);
352 di(printf("image: using static default image\n"))
353 len=sizeof(defaultImage);
354 mem=MALLOC(unsigned char,len);
356 memcpy(mem,defaultImage,len);
362 // -- cPlayList --------------------------------------------------------------
364 cPlayList::cPlayList(cFileObj *Obj)
366 obj=new cFileObj(Obj);
370 cPlayList::cPlayList(cFileSource *Source, const char *Subdir, const char *Name)
372 obj=new cFileObj(Source,Subdir,Name,otFile);
376 cPlayList::cPlayList(cPlayList *List)
378 obj=new cFileObj(List->obj);
382 cPlayList::~cPlayList()
386 obj->Source()->Unblock();
390 void cPlayList::Init(void)
392 extbuffer=basename=0;
394 obj->Source()->Block();
398 void cPlayList::Set(void)
400 free(basename); basename=0;
402 basename=strdup(obj->Name());
403 int l=strlen(basename)-strlen(PLAYLISTEXT);
404 if(l>0 && !strcasecmp(basename+l,PLAYLISTEXT)) basename[l]=0;
408 #if APIVERSNUM >= 10315
409 int cPlayList::Compare(const cListObject &ListObject) const
411 bool cPlayList::operator<(const cListObject &ListObject)
414 cPlayList *list=(cPlayList *)&ListObject;
415 #if APIVERSNUM >= 10315
416 return strcasecmp(obj->Name(),list->obj->Name());
418 return strcasecmp(obj->Name(),list->obj->Name())<0;
422 bool cPlayList::Load(void)
426 FILE *f=fopen(obj->FullPath(),"r");
430 while(fgets(buffer,sizeof(buffer),f)>0) {
432 if(!strncmp(buffer,WINAMPEXT,strlen(WINAMPEXT))) {
433 d(printf("mp3: detected WinAmp style playlist\n"))
438 if(!isempty(buffer)) {
439 cSong *song=new cSong(obj->Source(),0,0);
440 if(song->Parse(buffer,obj->Subdir())) Add(song);
442 esyslog("error loading playlist %s\n",obj->FullPath());
451 else LOG_ERROR_STR(obj->FullPath());
453 if(result && isWinAmp) {
455 while(song) { // if this is a WinAmp playlist, convert \ to /
457 song=cList<cSong>::Next(song);
463 bool cPlayList::Save(void)
466 cSafeFile f(obj->FullPath());
470 if(!song->Save(f,obj->Subdir())) {
474 song=cList<cSong>::Next(song);
476 if(!f.Close()) result=false;
482 bool cPlayList::Exists(void)
484 return obj->Exists();
487 bool cPlayList::TestName(const char *newName)
489 return obj->TestName(AddExt(newName,PLAYLISTEXT));
492 bool cPlayList::Rename(const char *newName)
494 bool r=obj->Rename(AddExt(newName,PLAYLISTEXT));
499 bool cPlayList::Create(const char *newName)
501 bool r=obj->Create(AddExt(newName,PLAYLISTEXT));
509 bool cPlayList::Delete(void)
511 return obj->Delete();
514 const char *cPlayList::AddExt(const char *FileName, const char *Ext)
516 free(extbuffer); extbuffer=0;
517 asprintf(&extbuffer,"%s%s",FileName,Ext);
521 // -- cInstantPlayList ------------------------------------------------------
523 cInstantPlayList::cInstantPlayList(cFileObj *Obj)
526 if(!Obj->Name()) Obj->SetName("instant");
529 bool cInstantPlayList::Load(void)
533 switch(obj->Type()) {
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();
547 d(printf("instant: dir %s\n",obj->Name()))
548 res=ScanDir(obj->Source(),obj->Path(),stFile,obj->Source()->Include(),excl_pl,true);
553 d(printf("instant: base\n"))
554 res=ScanDir(obj->Source(),0,stFile,obj->Source()->Include(),excl_pl,true);
562 void cInstantPlayList::DoItem(cFileSource *src, const char *subdir, const char *name)
564 Add(new cSong(src,subdir,name));
567 // -- cPlayLists --------------------------------------------------------------
569 bool cPlayLists::Load(cFileSource *Source)
571 static const char *spec[] = { "*"PLAYLISTEXT,0 };
573 bool res=ScanDir(Source,0,stFile,spec,0,false);
578 void cPlayLists::DoItem(cFileSource *src, const char *subdir, const char *name)
580 Add(new cPlayList(src,subdir,name));