2 * MP3/MPlayer plugin to VDR (C++)
4 * (C) 2001-2009 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)
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));
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 int cSong::Compare(const cListObject &ListObject) const
146 cSong *song=(cSong *)&ListObject;
147 return strcasecmp(obj->Path(),song->obj->Path());
150 cSongInfo *cSong::Info(bool get)
154 if(decoder) si=decoder->SongInfo(get);
158 cDecoder *cSong::Decoder(void)
161 if(!decoder && !decoderFailed) {
162 decoder=cDecoders::FindDecoder(obj);
163 if(!decoder) decoderFailed=true;
169 void cSong::Convert(void)
171 char *Name=Convert2Unix(obj->Name());
177 char *cSong::Convert2Unix(const char *name) const
179 char *Name=strdup(name);
190 char *cSong::Convert2Dos(const char *name)
192 char *Name=strdup(name);
203 bool cSong::Parse(char *s, const char *reldir) const
205 s=skipspace(stripspace(s));
207 if(s[0]=='/' || !reldir)
219 bool cSong::Save(FILE *f, const char *reldir) const
221 const char *path=obj->Path();
223 int l=strlen(reldir);
224 if(!strncasecmp(path,reldir,l)) path+=l+1;
226 return fprintf(f,"%s\n",path)>0;
229 bool cSong::FindImage(void)
231 if(image) return true;
233 char base[strlen(obj->Path())+32];
234 strcpy(base,obj->Path());
235 di(printf("image: checking image for %s\n",obj->Path()))
237 // song specific image
238 char *m=rindex(base,'.');
240 if((image=CheckImage(base))) return true;
242 // album specific image in song directory
243 if(!(m=rindex(base,'/'))) m=base-1;
245 if((image=CheckImage(base))) return true;
247 // artist specific image in parent directory
248 if((m=rindex(base,'/'))) {
250 if(!(m=rindex(base,'/'))) m=base-1;
251 strcpy(m+1,"artist");
252 if((image=CheckImage(base))) return true;
255 // default image in source basedir
256 if((image=CheckImage("background"))) return true;
258 // default user supplied image
259 if(def_usr_img && (image=strdup(def_usr_img))) return true;
261 di(printf("image: no image for %s\n",obj->Path()))
265 const char *cSong::CheckImage(const char *base) const
268 char *p=aprintf("%s/%s.%n ",obj->Source()->BaseDir(),base,&n);
269 for(const char **s=img_suff; *s; s++) {
271 if(strlen(*s)>5) printf("ERROR: buffer overflow in CheckImage ext=%s\n",*s);
274 di(printf("image: check %s\n",p))
275 if(!access(p,R_OK)) {
276 di(printf("image: found\n"))
284 #include "data-mp3-image.c"
285 extern void PropagateImage(const char *image);
287 bool cSong::Image(unsigned char * &mem, int &len)
291 if(!conv->Status()) {
292 di(printf("image: still queued\n"))
300 if(image || FindImage()) {
301 di(printf("image: loading image %s\n",image))
302 char *m=aprintf("%s%s.mpg",imagecache,image);
304 di(printf("image: not cached\n"))
306 di(printf("image: obviously convert failed...\n"))
309 if(!conv) conv=new cImageConvert;
310 if(conv && conv->Convert(image)) {
311 di(printf("image: convert queued\n"))
316 di(printf("image: queueing failed\n"))
322 di(printf("image: cached\n"))
323 int f=open(m,O_RDONLY);
328 mem=MALLOC(unsigned char,len);
330 if(read(f,mem,len)==len) res=1;
339 PropagateImage(res==1 ? image : 0);
342 di(printf("image: using static default image\n"))
343 len=sizeof(defaultImage);
344 mem=MALLOC(unsigned char,len);
346 memcpy(mem,defaultImage,len);
352 // -- cPlayList --------------------------------------------------------------
354 cPlayList::cPlayList(cFileObj *Obj)
356 obj=new cFileObj(Obj);
360 cPlayList::cPlayList(cFileSource *Source, const char *Subdir, const char *Name)
362 obj=new cFileObj(Source,Subdir,Name,otFile);
366 cPlayList::cPlayList(cPlayList *List)
368 obj=new cFileObj(List->obj);
372 cPlayList::~cPlayList()
376 obj->Source()->Unblock();
380 void cPlayList::Init(void)
382 extbuffer=basename=0;
384 obj->Source()->Block();
388 void cPlayList::Set(void)
390 free(basename); basename=0;
392 basename=strdup(obj->Name());
393 int l=strlen(basename)-strlen(PLAYLISTEXT);
394 if(l>0 && !strcasecmp(basename+l,PLAYLISTEXT)) basename[l]=0;
398 int cPlayList::Compare(const cListObject &ListObject) const
400 cPlayList *list=(cPlayList *)&ListObject;
401 return strcasecmp(obj->Name(),list->obj->Name());
404 bool cPlayList::Load(void)
408 FILE *f=fopen(obj->FullPath(),"r");
412 while(fgets(buffer,sizeof(buffer),f)>0) {
414 if(!strncmp(buffer,WINAMPEXT,strlen(WINAMPEXT))) {
415 d(printf("mp3: detected WinAmp style playlist\n"))
420 if(!isempty(buffer)) {
421 cSong *song=new cSong(obj->Source(),0,0);
422 if(song->Parse(buffer,obj->Subdir())) Add(song);
424 esyslog("error loading playlist %s\n",obj->FullPath());
433 else LOG_ERROR_STR(obj->FullPath());
435 if(result && isWinAmp) {
437 while(song) { // if this is a WinAmp playlist, convert \ to /
439 song=cList<cSong>::Next(song);
445 bool cPlayList::Save(void)
448 cSafeFile f(obj->FullPath());
452 if(!song->Save(f,obj->Subdir())) {
456 song=cList<cSong>::Next(song);
458 if(!f.Close()) result=false;
464 bool cPlayList::Exists(void)
466 return obj->Exists();
469 bool cPlayList::TestName(const char *newName)
471 return obj->TestName(AddExt(newName,PLAYLISTEXT));
474 bool cPlayList::Rename(const char *newName)
476 bool r=obj->Rename(AddExt(newName,PLAYLISTEXT));
481 bool cPlayList::Create(const char *newName)
483 bool r=obj->Create(AddExt(newName,PLAYLISTEXT));
491 bool cPlayList::Delete(void)
493 return obj->Delete();
496 const char *cPlayList::AddExt(const char *FileName, const char *Ext)
499 extbuffer=aprintf("%s%s",FileName,Ext);
503 // -- cInstantPlayList ------------------------------------------------------
505 cInstantPlayList::cInstantPlayList(cFileObj *Obj)
508 if(!Obj->Name()) Obj->SetName("instant");
511 bool cInstantPlayList::Load(void)
515 switch(obj->Type()) {
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();
529 d(printf("instant: dir %s\n",obj->Name()))
530 res=ScanDir(obj->Source(),obj->Path(),stFile,obj->Source()->Include(),excl_pl,true);
535 d(printf("instant: base\n"))
536 res=ScanDir(obj->Source(),0,stFile,obj->Source()->Include(),excl_pl,true);
544 void cInstantPlayList::DoItem(cFileSource *src, const char *subdir, const char *name)
546 Add(new cSong(src,subdir,name));
549 // -- cPlayLists --------------------------------------------------------------
551 bool cPlayLists::Load(cFileSource *Source)
553 static const char *spec[] = { "*"PLAYLISTEXT,0 };
555 bool res=ScanDir(Source,0,stFile,spec,0,false);
560 void cPlayLists::DoItem(cFileSource *src, const char *subdir, const char *name)
562 Add(new cPlayList(src,subdir,name));