2 * MP3/MPlayer plugin to VDR (C++)
4 * (C) 2001-2007 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
29 #include <vdr/menuitems.h>
30 #include <vdr/status.h>
31 #include <vdr/plugin.h>
32 #if APIVERSNUM >= 10307
33 #include <vdr/interface.h>
34 #include <vdr/skins.h>
38 #include "setup-mp3.h"
41 #include "player-mp3.h"
43 #include "menu-async.h"
53 const char *sourcesSub=0;
54 cFileSources MP3Sources;
56 static const char *plugin_name=0;
57 const char *i18n_name=0;
59 // --- cMenuSetupMP3 --------------------------------------------------------
61 class cMenuSetupMP3 : public cMenuSetupPage {
65 const char *cddb[3], *disp[2], *scan[3], *bgr[3];
66 const char *aout[AUDIOOUTMODES];
67 int amode, amodes[AUDIOOUTMODES];
69 virtual void Store(void);
74 cMenuSetupMP3::cMenuSetupMP3(void)
76 static const char allowed[] = { "abcdefghijklmnopqrstuvwxyz0123456789-_" };
78 aout[numModes]=trVDR("DVB"); amodes[numModes]=AUDIOOUTMODE_DVB; numModes++;
80 aout[numModes]=tr("OSS"); amodes[numModes]=AUDIOOUTMODE_OSS; numModes++;
84 for(int i=0; i<numModes; i++)
85 if(amodes[i]==data.AudioOutMode) { amode=i; break; }
87 SetSection(tr("MP3"));
88 Add(new cMenuEditStraItem(tr("Setup.MP3$Audio output mode"), &amode,numModes,aout));
89 Add(new cMenuEditBoolItem(tr("Setup.MP3$Audio mode"), &data.AudioMode, tr("Round"), tr("Dither")));
90 Add(new cMenuEditBoolItem(tr("Setup.MP3$Use 48kHz mode only"), &data.Only48kHz));
91 #if APIVERSNUM >= 10307
92 disp[0]=tr("classic");
93 disp[1]=tr("via skin");
94 Add(new cMenuEditStraItem(tr("Setup.MP3$Replay display"), &data.ReplayDisplay, 2, disp));
96 Add(new cMenuEditIntItem( tr("Setup.MP3$Display mode"), &data.DisplayMode, 1, 3));
100 Add(new cMenuEditStraItem(tr("Setup.MP3$Background mode"), &data.BackgrMode, 3, bgr));
101 Add(new cMenuEditBoolItem(tr("Setup.MP3$Initial loop mode"), &data.InitLoopMode));
102 Add(new cMenuEditBoolItem(tr("Setup.MP3$Initial shuffle mode"), &data.InitShuffleMode));
103 Add(new cMenuEditBoolItem(tr("Setup.MP3$Abort player at end of list"),&data.AbortAtEOL));
104 scan[0]=tr("disabled");
105 scan[1]=tr("ID3 only");
106 scan[2]=tr("ID3 & Level");
107 Add(new cMenuEditStraItem(tr("Setup.MP3$Background scan"), &data.BgrScan, 3, scan));
108 Add(new cMenuEditBoolItem(tr("Setup.MP3$Editor display mode"), &data.EditorMode, tr("Filenames"), tr("ID3 names")));
109 Add(new cMenuEditBoolItem(tr("Setup.MP3$Mainmenu mode"), &data.MenuMode, tr("Playlists"), tr("Browser")));
110 Add(new cMenuEditBoolItem(tr("Setup.MP3$Keep selection menu"), &data.KeepSelect));
111 Add(new cMenuEditBoolItem(tr("Setup.MP3$Title/Artist order"), &data.TitleArtistOrder, tr("Normal"), tr("Reversed")));
112 Add(new cMenuEditBoolItem(tr("Hide mainmenu entry"), &data.HideMainMenu));
113 Add(new cMenuEditIntItem( tr("Setup.MP3$Normalizer level"), &data.TargetLevel, 0, MAX_TARGET_LEVEL));
114 Add(new cMenuEditIntItem( tr("Setup.MP3$Limiter level"), &data.LimiterLevel, MIN_LIMITER_LEVEL, 100));
115 Add(new cMenuEditBoolItem(tr("Setup.MP3$Use HTTP proxy"), &data.UseProxy));
116 Add(new cMenuEditStrItem( tr("Setup.MP3$HTTP proxy host"), data.ProxyHost,MAX_HOSTNAME,allowed));
117 Add(new cMenuEditIntItem( tr("Setup.MP3$HTTP proxy port"), &data.ProxyPort,1,65535));
118 cddb[0]=tr("disabled");
119 cddb[1]=tr("local only");
120 cddb[2]=tr("local&remote");
121 Add(new cMenuEditStraItem(tr("Setup.MP3$CDDB for CD-Audio"), &data.UseCddb,3,cddb));
122 Add(new cMenuEditStrItem( tr("Setup.MP3$CDDB server"), data.CddbHost,MAX_HOSTNAME,allowed));
123 Add(new cMenuEditIntItem( tr("Setup.MP3$CDDB port"), &data.CddbPort,1,65535));
126 void cMenuSetupMP3::Store(void)
128 data.AudioOutMode=amodes[amode];
131 SetupStore("InitLoopMode", MP3Setup.InitLoopMode );
132 SetupStore("InitShuffleMode", MP3Setup.InitShuffleMode);
133 SetupStore("AudioMode", MP3Setup.AudioMode );
134 SetupStore("AudioOutMode", MP3Setup.AudioOutMode );
135 SetupStore("BgrScan", MP3Setup.BgrScan );
136 SetupStore("EditorMode", MP3Setup.EditorMode );
137 SetupStore("DisplayMode", MP3Setup.DisplayMode );
138 SetupStore("BackgrMode", MP3Setup.BackgrMode );
139 SetupStore("MenuMode", MP3Setup.MenuMode );
140 SetupStore("TargetLevel", MP3Setup.TargetLevel );
141 SetupStore("LimiterLevel", MP3Setup.LimiterLevel );
142 SetupStore("Only48kHz", MP3Setup.Only48kHz );
143 SetupStore("UseProxy", MP3Setup.UseProxy );
144 SetupStore("ProxyHost", MP3Setup.ProxyHost );
145 SetupStore("ProxyPort", MP3Setup.ProxyPort );
146 SetupStore("UseCddb", MP3Setup.UseCddb );
147 SetupStore("CddbHost", MP3Setup.CddbHost );
148 SetupStore("CddbPort", MP3Setup.CddbPort );
149 SetupStore("AbortAtEOL", MP3Setup.AbortAtEOL );
150 #if APIVERSNUM >= 10307
151 SetupStore("ReplayDisplay", MP3Setup.ReplayDisplay );
153 SetupStore("HideMainMenu", MP3Setup.HideMainMenu );
154 SetupStore("KeepSelect", MP3Setup.KeepSelect );
155 SetupStore("TitleArtistOrder", MP3Setup.TitleArtistOrder);
158 // --- cAsyncStatus ------------------------------------------------------------
160 cAsyncStatus asyncStatus;
162 cAsyncStatus::cAsyncStatus(void)
168 cAsyncStatus::~cAsyncStatus()
173 void cAsyncStatus::Set(const char *Text)
177 text=Text ? strdup(Text) : 0;
182 const char *cAsyncStatus::Begin(void)
188 void cAsyncStatus::Finish(void)
194 // --- --------------------------------------------------------------------
196 static const char *TitleArtist(const char *title, const char *artist)
198 static char buf[256]; // clearly not multi-thread save!
200 if(artist && artist[0]) {
201 if(MP3Setup.TitleArtistOrder) fmt="%2$s - %1$s";
205 snprintf(buf,sizeof(buf),fmt,title,artist);
209 // --- cMP3Control --------------------------------------------------------
211 #if APIVERSNUM >= 10307
212 #define clrBackground clrGray50
213 #define eDvbColor int
217 #define MAXROWS MAXOSDHEIGHT
218 #define INLINE inline
221 class cMP3Control : public cControl {
223 #if APIVERSNUM >= 10307
226 cSkinDisplayReplay *disp;
228 bool statusInterfaceOpen;
230 int bw, bh, bwc, fw, fh;
233 bool visible, shown, bigwin, statusActive;
234 time_t timeoutShow, greentime, oktime;
235 int lastkeytime, number;
238 cMP3PlayInfo *lastMode;
239 time_t fliptime, listtime;
240 int hashlist[MAXROWS];
241 int flip, flipint, top, rows;
242 int lastIndex, lastTotal, lastTop;
245 bool jumpactive, jumphide, jumpsecs;
248 void ShowTimed(int Seconds=0);
249 void ShowProgress(bool open=false, bool bigWin=false);
250 void ShowStatus(bool force);
251 void HideStatus(void);
252 void DisplayInfo(const char *s=0);
253 void JumpDisplay(void);
254 void JumpProcess(eKeys Key);
257 INLINE void Write(int x, int y, int w, const char *text, eDvbColor fg=clrWhite, eDvbColor bg=clrBackground);
258 INLINE void Fill(int x, int y, int w, int h, eDvbColor fg);
259 inline void Flush(void);
262 virtual ~cMP3Control();
263 virtual eOSState ProcessKey(eKeys Key);
264 virtual void Show(void) { ShowTimed(); }
265 virtual void Hide(void);
266 bool Visible(void) { return visible; }
267 static bool SetPlayList(cPlayList *plist);
270 cMP3Control::cMP3Control(void)
271 :cControl(player=new cMP3Player)
273 visible=shown=bigwin=selecting=jumpactive=jumphide=statusActive=false;
274 timeoutShow=greentime=oktime=0;
275 lastkeytime=number=0;
277 framesPerSecond=SecondsToFrames(1);
278 #if APIVERSNUM >= 10307
280 font=cFont::GetFont(fontOsd);
282 statusInterfaceOpen=false;
284 #if APIVERSNUM >= 10338
285 cStatus::MsgReplaying(this,"MP3",0,true);
287 cStatus::MsgReplaying(this,"MP3");
291 cMP3Control::~cMP3Control()
298 void cMP3Control::Stop(void)
300 #if APIVERSNUM >= 10338
301 cStatus::MsgReplaying(this,0,0,false);
303 cStatus::MsgReplaying(this,0);
305 delete player; player=0;
307 mgr->Flush(); //XXX remove later
310 bool cMP3Control::SetPlayList(cPlayList *plist)
313 cControl *control=cControl::Control();
314 // is there a running MP3 player?
315 if(control && typeid(*control)==typeid(cMP3Control)) {
316 // add songs to running playlist
323 cControl::Launch(new cMP3Control);
330 void cMP3Control::ShowTimed(int Seconds)
334 timeoutShow=(Seconds>0) ? time(0)+Seconds : 0;
338 void cMP3Control::Hide(void)
343 #if APIVERSNUM >= 10307
349 visible=bigwin=false;
350 #if APIVERSNUM >= 10500
351 SetNeedsFastResponse(false);
353 needsFastResponse=false;
358 void cMP3Control::ShowStatus(bool force)
360 if((asyncStatus.Changed() || (force && !statusActive)) && !jumpactive) {
361 const char *text=asyncStatus.Begin();
363 #if APIVERSNUM >= 10307
364 if(MP3Setup.ReplayDisplay || !osd) {
365 if(statusActive) Skins.Message(mtStatus,0);
366 Skins.Message(mtStatus,text);
369 if(!statusActive) osd->SaveRegion(0,bh-2*fh,bw-1,bh-fh-1);
370 osd->DrawText(0,bh-2*fh,text,clrBlack,clrCyan,font,bw,fh,taCenter);
374 if(!Interface->IsOpen()) {
375 Interface->Open(0,-1);
376 statusInterfaceOpen=true;
378 Interface->Status(text);
385 asyncStatus.Finish();
389 void cMP3Control::HideStatus(void)
392 #if APIVERSNUM >= 10307
393 if(MP3Setup.ReplayDisplay || !osd)
394 Skins.Message(mtStatus,0);
396 osd->RestoreRegion();
400 if(statusInterfaceOpen) {
402 statusInterfaceOpen=false;
405 Interface->Status(0);
413 #define CTAB 11 // some tabbing values for the progress display
416 void cMP3Control::Write(int x, int y, int w, const char *text, eDvbColor fg, eDvbColor bg)
418 #if APIVERSNUM >= 10307
420 //d(printf("write x=%d y=%d w=%d ->",x,y,w))
421 x*=fw; if(x<0) x+=bw;
422 y*=fh; if(y<0) y+=bh;
423 osd->DrawText(x,y,text,fg,bg,font,w*fw);
424 //d(printf(" x=%d y=%d w=%d\n",x,y,w*fw))
427 if(w>0) Fill(x,y,w,1,bg);
428 Interface->Write(x,y,text,fg,bg);
432 void cMP3Control::Fill(int x, int y, int w, int h, eDvbColor fg)
434 #if APIVERSNUM >= 10307
436 //d(printf("fill x=%d y=%d w=%d h=%d ->",x,y,w,h))
437 x*=fw; if(x<0) x+=bw;
438 y*=fh; if(y<0) y+=bh;
439 osd->DrawRectangle(x,y,x+w*fw-1,y+h*fh-1,fg);
440 //d(printf(" x=%d y=%d x2=%d y2=%d\n",x,y,x+h*fh-1,y+w*fw-1))
443 Interface->Fill(x,y,w,h,fg);
447 void cMP3Control::Flush(void)
449 #if APIVERSNUM >= 10307
450 if(MP3Setup.ReplayDisplay) Skins.Flush();
451 else if(osd) osd->Flush();
457 void cMP3Control::ShowProgress(bool open, bool bigWin)
461 if(player->GetIndex(index,total) && total>=0) {
462 if(!visible && open) {
464 #if APIVERSNUM >= 10307
465 if(MP3Setup.ReplayDisplay) {
466 disp=Skins.Current()->DisplayReplay(false);
472 fw=font->Width(' ')*2;
479 rows=(bh-bp-fh/3)/fh;
484 bt=Setup.OSDHeight-bh;
488 //d(printf("mp3: bw=%d bh=%d bt=%d bp=%d bwc=%d rows=%d fw=%d fh=%d\n",
489 // bw,bh,bt,bp,bwc,rows,fw,fh))
490 osd=cOsdProvider::NewOsd(Setup.OSDLeft,Setup.OSDTop+bt);
493 tArea Areas[] = { { 0,0,bw-1,bh-bp-1,2 }, { 0,bh-bp,bw-1,bh-1,4 } };
494 osd->SetAreas(Areas,sizeof(Areas)/sizeof(tArea));
497 tArea Areas[] = { { 0,0,bw-1,bh-1,4 } };
498 osd->SetAreas(Areas,sizeof(Areas)/sizeof(tArea));
500 osd->DrawRectangle(0,0,bw-1,bh-1,clrGray50);
504 fw=cOsd::CellWidth();
505 fh=cOsd::LineHeight();
506 bh=bigWin ? Setup.OSDheight : -2;
507 bw=bwc=Setup.OSDwidth;
508 rows=Setup.OSDheight-3;
509 Interface->Open(bw,bh);
515 #if APIVERSNUM >= 10500
516 SetNeedsFastResponse(true);
518 needsFastResponse=true;
520 fliptime=listtime=0; flipint=0; flip=-1; top=lastTop=-1; lastIndex=lastTotal=-1;
521 delete lastMode; lastMode=0;
524 cMP3PlayInfo *mode=new cMP3PlayInfo;
525 bool valid=mgr->Info(-1,mode);
526 bool changed=(!lastMode || mode->Hash!=lastMode->Hash);
528 if(changed) { d(printf("mp3-ctrl: mode change detected\n")) }
530 if(valid) { // send progress to status monitor
531 if(changed || mode->Loop!=lastMode->Loop || mode->Shuffle!=lastMode->Shuffle) {
532 snprintf(buf,sizeof(buf),"[%c%c] (%d/%d) %s",
533 mode->Loop?'L':'.',mode->Shuffle?'S':'.',mode->Num,mode->MaxNum,TitleArtist(mode->Title,mode->Artist));
534 #if APIVERSNUM >= 10338
535 cStatus::MsgReplaying(this,buf,mode->Filename[0]?mode->Filename:0,true);
537 cStatus::MsgReplaying(this,buf);
542 if(visible) { // refresh the OSD progress display
545 #if APIVERSNUM >= 10307
546 if(MP3Setup.ReplayDisplay) {
548 if(total>0) disp->SetProgress(index,total);
549 disp->SetCurrent(IndexToHMSF(index));
550 disp->SetTotal(IndexToHMSF(total));
553 if(GetReplayMode(Play,Forward,Speed))
554 disp->SetMode(Play, Forward, Speed);
560 if(!selecting && changed && !statusActive) {
561 snprintf(buf,sizeof(buf),"(%d/%d)",mode->Num,mode->MaxNum);
562 Write(0,-2,CTAB,buf);
566 if(!lastMode || mode->Loop!=lastMode->Loop) {
567 if(mode->Loop) Write(-4,-1,0,"L",clrBlack,clrYellow);
568 else Fill(-4,-1,2,1,clrBackground);
571 if(!lastMode || mode->Shuffle!=lastMode->Shuffle) {
572 if(mode->Shuffle) Write(-2,-1,0,"S",clrWhite,clrRed);
573 else Fill(-2,-1,2,1,clrBackground);
577 index/=framesPerSecond; total/=framesPerSecond;
578 if(index!=lastIndex || total!=lastTotal) {
580 #if APIVERSNUM >= 10307
581 cProgressBar ProgressBar(bw-(CTAB+CTAB2)*fw,fh,index,total);
582 osd->DrawBitmap(CTAB*fw,bh-fh,ProgressBar);
584 cProgressBar ProgressBar((bw-CTAB-CTAB2)*fw,fh,index,total);
585 Interface->SetBitmap(CTAB*fw,(abs(bh)-1)*fh,ProgressBar);
588 snprintf(buf,sizeof(buf),total?"%02d:%02d/%02d:%02d":"%02d:%02d",index/60,index%60,total/60,total%60);
592 #if APIVERSNUM >= 10307
598 if(MP3Setup.ReplayDisplay && (!lastMode || mode->Loop!=lastMode->Loop || mode->Shuffle!=lastMode->Shuffle))
600 if(!valid || changed) {
601 fliptime=time(0); flip=0;
604 else if(time(0)>fliptime+flipint) {
606 flip++; if(flip>=MP3Setup.DisplayMode) flip=0;
616 snprintf(buf,sizeof(buf),"%s",TitleArtist(mode->Title,mode->Artist));
621 snprintf(buf,sizeof(buf),mode->Year>0?"from: %s (%d)":"from: %s",mode->Album,mode->Year);
627 if(mode->MaxBitrate>0)
628 snprintf(buf,sizeof(buf),"%.1f kHz, %d-%d kbps, %s",mode->SampleFreq/1000.0,mode->Bitrate/1000,mode->MaxBitrate/1000,mode->SMode);
630 snprintf(buf,sizeof(buf),"%.1f kHz, %d kbps, %s",mode->SampleFreq/1000.0,mode->Bitrate/1000,mode->SMode);
635 #if APIVERSNUM >= 10307
636 if(MP3Setup.ReplayDisplay) {
638 snprintf(buf2,sizeof(buf2),"[%c%c] (%d/%d) %s",
639 mode->Loop?'L':'.',mode->Shuffle?'S':'.',mode->Num,mode->MaxNum,buf);
640 disp->SetTitle(buf2);
649 else { d(printf("mp3-ctrl: display info skip due to status active\n")) }
650 #if APIVERSNUM >= 10307
658 bool all=(top!=lastTop || changed);
659 if(all || time(0)>listtime+2) {
660 int num=(top>0 && mode->Num==lastMode->Num) ? top : mode->Num - rows/2;
661 if(num+rows>mode->MaxNum) num=mode->MaxNum-rows+1;
664 for(int i=0 ; i<rows && i<MAXROWS && num<=mode->MaxNum ; i++,num++) {
666 mgr->Info(num,&pi); if(!pi.Title[0]) break;
667 snprintf(buf,sizeof(buf),"%d.\t%s",num,TitleArtist(pi.Title,pi.Artist));
668 eDvbColor fg=clrWhite, bg=clrBackground;
669 int hash=MakeHash(buf);
670 if(num==mode->Num) { fg=clrBlack; bg=clrCyan; hash=(hash^77) + 23; }
671 if(all || hash!=hashlist[i]) {
672 char *s=rindex(buf,'\t');
675 Write(0,i,5,buf,fg,bg);
676 Write(5,i,bwc-5,s,fg,bg);
679 Write(0,i,bwc,buf,fg,bg);
684 listtime=time(0); lastTop=top;
691 lastIndex=index; lastTotal=total;
692 delete lastMode; lastMode=mode;
696 void cMP3Control::DisplayInfo(const char *s)
698 if(s) Write(CTAB,-2,bwc-CTAB,s);
699 else Fill(CTAB,-2,bwc-CTAB,1,clrBackground);
702 void cMP3Control::JumpDisplay(void)
705 const char *j=trVDR("Jump: "), u=jumpsecs?'s':'m';
706 if(!jumpmm) sprintf(buf,"%s- %c", j,u);
707 else sprintf(buf,"%s%d- %c",j,jumpmm,u);
708 #if APIVERSNUM >= 10307
709 if(MP3Setup.ReplayDisplay) {
715 #if APIVERSNUM >= 10307
720 void cMP3Control::JumpProcess(eKeys Key)
722 int n=Key-k0, d=jumpsecs?1:60;
725 if(jumpmm*10+n <= lastTotal/d) jumpmm=jumpmm*10+n;
740 player->SkipSeconds(jumpmm*d * ((Key==kLeft || Key==kFastRew) ? -1:1));
749 #if APIVERSNUM >= 10307
750 else if(MP3Setup.ReplayDisplay) disp->SetJump(0);
755 void cMP3Control::Jump(void)
757 jumpmm=0; jumphide=jumpsecs=false;
759 ShowTimed(); if(!visible) return;
763 jumpactive=true; fliptime=0; flip=-1;
766 eOSState cMP3Control::ProcessKey(eKeys Key)
768 if(!player->Active()) return osEnd;
770 if(timeoutShow && time(0)>timeoutShow) Hide();
772 #if APIVERSNUM >= 10307
773 ShowStatus(Key==kNone && !Skins.IsOpen());
775 ShowStatus(Key==kNone && !Interface->IsOpen());
778 if(jumpactive && Key!=kNone) { JumpProcess(Key); return osContinue; }
783 #if APIVERSNUM >= 10347
787 mgr->Next(); player->Play();
791 #if APIVERSNUM >= 10347
795 if(!player->PrevCheck()) mgr->Prev();
801 if(top>0) { top-=rows; if(top<1) top=1; }
806 case kFastRew|k_Repeat:
807 if(!player->IsStream()) player->SkipSeconds(-JUMPSIZE);
810 case kRight|k_Repeat:
817 case kFastFwd|k_Repeat:
818 if(!player->IsStream()) player->SkipSeconds(JUMPSIZE);
821 if(!player->IsStream()) Jump();
825 if(time(0)>greentime) {
826 if(lastMode->Loop || (!lastMode->Loop && !lastMode->Shuffle)) mgr->ToggleLoop();
827 if(lastMode->Shuffle) mgr->ToggleShuffle();
830 if(!lastMode->Loop) mgr->ToggleLoop();
831 else if(!lastMode->Shuffle) mgr->ToggleShuffle();
832 else mgr->ToggleLoop();
834 greentime=time(0)+MULTI_TIMEOUT;
842 if(!player->IsStream()) player->Pause();
851 #if APIVERSNUM >= 10332
852 cRemote::CallPlugin(plugin_name);
859 number=number*10+Key-k0;
860 if(lastMode && number>0 && number<=lastMode->MaxNum) {
861 if(!visible) ShowTimed(SELECTHIDE_TIMEOUT);
862 else if(timeoutShow>0) timeoutShow=time(0)+SELECTHIDE_TIMEOUT;
863 selecting=true; lastkeytime=time_ms();
865 #if APIVERSNUM >= 10307
866 if(MP3Setup.ReplayDisplay) {
867 snprintf(buf,sizeof(buf),"%s%d-/%d",trVDR("Jump: "),number,lastMode->MaxNum);
872 snprintf(buf,sizeof(buf),"(%d-/%d)",number,lastMode->MaxNum);
873 Write(0,-2,CTAB,buf);
874 #if APIVERSNUM >= 10307
880 number=0; lastkeytime=0;
883 if(selecting && time_ms()-lastkeytime>SELECT_TIMEOUT) {
884 if(number>0) { mgr->Goto(number); player->Play(); }
885 if(lastMode) lastMode->Hash=-1;
886 number=0; selecting=false;
887 #if APIVERSNUM >= 10307
888 if(MP3Setup.ReplayDisplay && disp) disp->SetJump(0);
893 if(time(0)>oktime || MP3Setup.ReplayDisplay) {
894 visible ? Hide() : ShowTimed();
897 if(visible && !bigwin) { Hide(); ShowProgress(true,true); }
898 else { Hide(); ShowTimed(); }
900 oktime=time(0)+MULTI_TIMEOUT;
909 // --- cMenuID3Info ------------------------------------------------------------
911 class cMenuID3Info : public cOsdMenu {
913 cOsdItem *Item(const char *name, const char *text);
914 cOsdItem *Item(const char *name, const char *format, const float num);
915 void Build(cSongInfo *info, const char *name);
917 cMenuID3Info(cSong *song);
918 cMenuID3Info(cSongInfo *si, const char *name);
919 virtual eOSState ProcessKey(eKeys Key);
922 cMenuID3Info::cMenuID3Info(cSong *song)
923 :cOsdMenu(tr("ID3 information"),12)
925 Build(song->Info(),song->Name());
928 cMenuID3Info::cMenuID3Info(cSongInfo *si, const char *name)
929 :cOsdMenu(tr("ID3 information"),12)
934 void cMenuID3Info::Build(cSongInfo *si, const char *name)
937 Item(tr("Filename"),name);
938 if(si->HasInfo() && si->Total>0) {
940 asprintf(&buf,"%02d:%02d",si->Total/60,si->Total%60);
941 Item(tr("Length"),buf);
943 Item(tr("Title"),si->Title);
944 Item(tr("Artist"),si->Artist);
945 Item(tr("Album"),si->Album);
946 Item(tr("Year"),0,(float)si->Year);
947 Item(tr("Samplerate"),"%.1f kHz",si->SampleFreq/1000.0);
948 Item(tr("Bitrate"),"%.f kbit/s",si->Bitrate/1000.0);
949 Item(trVDR("Channels"),0,(float)si->Channels);
955 cOsdItem *cMenuID3Info::Item(const char *name, const char *format, const float num)
960 asprintf(&buf,format?format:"%.f",num);
964 else item=Item(name,"");
968 cOsdItem *cMenuID3Info::Item(const char *name, const char *text)
971 asprintf(&buf,"%s:\t%s",name,text?text:"");
972 cOsdItem *item = new cOsdItem(buf,osBack);
973 #if APIVERSNUM >= 10307
974 item->SetSelectable(false);
976 item->SetColor(clrWhite, clrBackground);
979 Add(item); return item;
982 eOSState cMenuID3Info::ProcessKey(eKeys Key)
984 eOSState state = cOsdMenu::ProcessKey(Key);
986 if(state==osUnknown) {
991 case kBlue: return osContinue;
992 case kMenu: return osEnd;
999 // --- cMenuInstantBrowse -------------------------------------------------------
1001 class cMenuInstantBrowse : public cMenuBrowse {
1003 const char *selecttext, *alltext;
1004 virtual void SetButtons(void);
1005 virtual eOSState ID3Info(void);
1007 cMenuInstantBrowse(cFileSource *Source, const char *Selecttext, const char *Alltext);
1008 virtual eOSState ProcessKey(eKeys Key);
1011 cMenuInstantBrowse::cMenuInstantBrowse(cFileSource *Source, const char *Selecttext, const char *Alltext)
1012 :cMenuBrowse(Source,true,true,tr("Directory browser"),excl_br)
1014 selecttext=Selecttext; alltext=Alltext;
1018 void cMenuInstantBrowse::SetButtons(void)
1020 SetHelp(selecttext, currentdir?tr("Parent"):0, currentdir?0:alltext, tr("ID3 info"));
1024 eOSState cMenuInstantBrowse::ID3Info(void)
1026 cFileObj *item=CurrentItem();
1027 if(item && item->Type()==otFile) {
1028 cSong *song=new cSong(item);
1030 if(song && (si=song->Info())) {
1031 AddSubMenu(new cMenuID3Info(si,item->Path()));
1038 eOSState cMenuInstantBrowse::ProcessKey(eKeys Key)
1040 eOSState state=cOsdMenu::ProcessKey(Key);
1041 if(state==osUnknown) {
1043 case kYellow: lastselect=new cFileObj(source,0,0,otBase);
1048 if(state==osUnknown) state=cMenuBrowse::ProcessStdKey(Key,state);
1052 // --- cMenuPlayListItem -------------------------------------------------------
1054 class cMenuPlayListItem : public cOsdItem {
1059 cMenuPlayListItem(cSong *Song, bool showid3);
1060 cSong *Song(void) { return song; }
1061 virtual void Set(void);
1062 void Set(bool showid3);
1065 cMenuPlayListItem::cMenuPlayListItem(cSong *Song, bool showid3)
1071 void cMenuPlayListItem::Set(bool showid3)
1077 void cMenuPlayListItem::Set(void)
1080 cSongInfo *si=song->Info(false);
1081 if(showID3 && !si) si=song->Info();
1082 if(showID3 && si && si->Title)
1083 asprintf(&buffer, "%d.\t%s",song->Index()+1,TitleArtist(si->Title,si->Artist));
1085 asprintf(&buffer, "%d.\t<%s>",song->Index()+1,song->Name());
1086 SetText(buffer,false);
1089 // --- cMenuPlayList ------------------------------------------------------
1091 class cMenuPlayList : public cOsdMenu {
1093 cPlayList *playlist;
1094 bool browsing, showid3;
1096 void Refresh(bool all = false);
1098 virtual void Move(int From, int To);
1099 eOSState Remove(void);
1100 eOSState ShowID3(void);
1101 eOSState ID3Info(void);
1103 cMenuPlayList(cPlayList *Playlist);
1104 virtual eOSState ProcessKey(eKeys Key);
1107 cMenuPlayList::cMenuPlayList(cPlayList *Playlist)
1108 :cOsdMenu(tr("Playlist editor"),4)
1110 browsing=showid3=false;
1112 if(MP3Setup.EditorMode) showid3=true;
1114 cSong *mp3 = playlist->First();
1116 cOsdMenu::Add(new cMenuPlayListItem(mp3,showid3));
1117 mp3 = playlist->cList<cSong>::Next(mp3);
1119 Buttons(); Display();
1122 void cMenuPlayList::Buttons(void)
1124 SetHelp(tr("Add"), showid3?tr("Filenames"):tr("ID3 names"), tr("Remove"), trVDR(BUTTON"Mark"));
1127 void cMenuPlayList::Refresh(bool all)
1129 cMenuPlayListItem *cur=(cMenuPlayListItem *)((all || Count()<2) ? First() : Get(Current()));
1132 cur=(cMenuPlayListItem *)Next(cur);
1136 void cMenuPlayList::Add(void)
1138 cFileObj *item=cMenuInstantBrowse::GetSelected();
1140 Status(tr("Scanning directory..."));
1141 cInstantPlayList *newpl=new cInstantPlayList(item);
1143 if(newpl->Count()) {
1144 if(newpl->Count()==1 || Interface->Confirm(tr("Add recursivly?"))) {
1145 cSong *mp3=newpl->First();
1147 cSong *n=new cSong(mp3);
1149 cMenuPlayListItem *current=(cMenuPlayListItem *)Get(Current());
1150 playlist->Add(n,current->Song());
1151 cOsdMenu::Add(new cMenuPlayListItem(n,showid3),true,current);
1155 cOsdMenu::Add(new cMenuPlayListItem(n,showid3),true);
1157 mp3=newpl->cList<cSong>::Next(mp3);
1160 Refresh(); Display();
1163 else Error(tr("Empty directory!"));
1165 else Error(tr("Error scanning directory!"));
1171 void cMenuPlayList::Move(int From, int To)
1173 playlist->Move(From,To); playlist->Save();
1174 cOsdMenu::Move(From,To);
1175 Refresh(true); Display();
1178 eOSState cMenuPlayList::ShowID3(void)
1181 Buttons(); Refresh(true); Display();
1185 eOSState cMenuPlayList::ID3Info(void)
1188 cMenuPlayListItem *current = (cMenuPlayListItem *)Get(Current());
1189 AddSubMenu(new cMenuID3Info(current->Song()));
1194 eOSState cMenuPlayList::Remove(void)
1197 cMenuPlayListItem *current = (cMenuPlayListItem *)Get(Current());
1198 if(Interface->Confirm(tr("Remove entry?"))) {
1199 playlist->Del(current->Song()); playlist->Save();
1200 cOsdMenu::Del(Current());
1201 Refresh(); Display();
1207 eOSState cMenuPlayList::ProcessKey(eKeys Key)
1209 eOSState state = cOsdMenu::ProcessKey(Key);
1211 if(browsing && !HasSubMenu() && state==osContinue) { Add(); browsing=false; }
1213 if(state==osUnknown) {
1215 case kOk: return ID3Info();
1216 case kRed: browsing=true;
1217 return AddSubMenu(new cMenuInstantBrowse(MP3Sources.GetSource(),tr("Add"),tr("Add all")));
1218 case kGreen: return ShowID3();
1219 case kYellow: return Remove();
1220 case kBlue: Mark(); return osContinue;
1221 case kMenu: return osEnd;
1228 // --- cPlaylistRename --------------------------------------------------------
1230 class cPlaylistRename : public cOsdMenu {
1232 static char *newname;
1233 const char *oldname;
1236 cPlaylistRename(const char *Oldname);
1237 virtual eOSState ProcessKey(eKeys Key);
1238 static const char *GetNewname(void) { return newname; }
1241 char *cPlaylistRename::newname = NULL;
1243 cPlaylistRename::cPlaylistRename(const char *Oldname)
1244 :cOsdMenu(tr("Rename playlist"), 15)
1246 free(newname); newname=0;
1250 asprintf(&buf,"%s\t%s",tr("Old name:"),oldname);
1251 cOsdItem *old = new cOsdItem(buf,osContinue);
1252 #if APIVERSNUM >= 10307
1253 old->SetSelectable(false);
1255 old->SetColor(clrWhite, clrBackground);
1261 Add(new cMenuEditStrItem( tr("New name"), data, sizeof(data)-1, tr(FileNameChars)),true);
1264 eOSState cPlaylistRename::ProcessKey(eKeys Key)
1266 eOSState state = cOsdMenu::ProcessKey(Key);
1268 if (state == osUnknown) {
1270 case kOk: if(data[0] && strcmp(data,oldname)) newname=strdup(data);
1275 case kBlue: return osContinue;
1282 // --- cMenuMP3Item -----------------------------------------------------
1284 class cMenuMP3Item : public cOsdItem {
1286 cPlayList *playlist;
1287 virtual void Set(void);
1289 cMenuMP3Item(cPlayList *PlayList);
1290 cPlayList *List(void) { return playlist; }
1293 cMenuMP3Item::cMenuMP3Item(cPlayList *PlayList)
1299 void cMenuMP3Item::Set(void)
1302 asprintf(&buffer," %s",playlist->BaseName());
1303 SetText(buffer,false);
1306 // --- cMenuMP3 --------------------------------------------------------
1308 class cMenuMP3 : public cOsdMenu {
1311 bool renaming, sourcing, instanting;
1313 eOSState Play(void);
1314 eOSState Edit(void);
1316 eOSState Delete(void);
1317 eOSState Rename(bool second);
1318 eOSState Source(bool second);
1319 eOSState Instant(bool second);
1320 void ScanLists(void);
1321 eOSState SetButtons(int num);
1325 virtual eOSState ProcessKey(eKeys Key);
1328 cMenuMP3::cMenuMP3(void)
1329 :cOsdMenu(tr("MP3"))
1331 renaming=sourcing=instanting=false;
1332 lists=new cPlayLists;
1333 ScanLists(); SetButtons(1);
1334 if(MP3Setup.MenuMode) Instant(false);
1337 cMenuMP3::~cMenuMP3(void)
1342 eOSState cMenuMP3::SetButtons(int num)
1346 SetHelp(trVDR(BUTTON"Edit"), tr("Source"), tr("Browse"), ">>");
1349 SetHelp("<<", trVDR(BUTTON"New"), trVDR(BUTTON"Delete"), tr("Rename"));
1352 buttonnum=num; Display();
1356 void cMenuMP3::ScanLists(void)
1359 Status(tr("Scanning playlists..."));
1360 bool res=lists->Load(MP3Sources.GetSource());
1363 cPlayList *plist=lists->First();
1365 Add(new cMenuMP3Item(plist));
1366 plist=lists->Next(plist);
1369 else Error(tr("Error scanning playlists!"));
1372 eOSState cMenuMP3::Delete(void)
1375 if(Interface->Confirm(tr("Delete playlist?")) &&
1376 Interface->Confirm(tr("Are you sure?")) ) {
1377 cPlayList *plist = ((cMenuMP3Item *)Get(Current()))->List();
1378 if(plist->Delete()) {
1380 cOsdMenu::Del(Current());
1383 else Error(tr("Error deleting playlist!"));
1389 eOSState cMenuMP3::New(void)
1391 cPlayList *plist=new cPlayList(MP3Sources.GetSource(),0,0);
1395 if(i) sprintf(name,"%s%d",tr("unnamed"),i++);
1396 else { strcpy(name,tr("unnamed")); i++; }
1397 } while(plist->TestName(name));
1399 if(plist->Create(name)) {
1401 Add(new cMenuMP3Item(plist), true);
1403 isyslog("MP3: playlist %s added", plist->Name());
1404 return AddSubMenu(new cMenuPlayList(plist));
1406 Error(tr("Error creating playlist!"));
1411 eOSState cMenuMP3::Rename(bool second)
1413 if(HasSubMenu() || Count() == 0) return osContinue;
1415 cPlayList *plist = ((cMenuMP3Item *)Get(Current()))->List();
1418 return AddSubMenu(new cPlaylistRename(plist->BaseName()));
1421 const char *newname=cPlaylistRename::GetNewname();
1423 if(plist->Rename(newname)) {
1425 DisplayCurrent(true);
1427 else Error(tr("Error renaming playlist!"));
1432 eOSState cMenuMP3::Edit(void)
1434 if(HasSubMenu() || Count() == 0) return osContinue;
1436 cPlayList *plist = ((cMenuMP3Item *)Get(Current()))->List();
1437 if(!plist->Load()) Error(tr("Error loading playlist!"));
1438 else if(!plist->IsWinAmp()) {
1439 isyslog("MP3: editing playlist %s", plist->Name());
1440 return AddSubMenu(new cMenuPlayList(plist));
1442 else Error(tr("Can't edit a WinAmp playlist!"));
1446 eOSState cMenuMP3::Play(void)
1448 if(HasSubMenu() || Count() == 0) return osContinue;
1450 Status(tr("Loading playlist..."));
1451 cPlayList *newpl=new cPlayList(((cMenuMP3Item *)Get(Current()))->List());
1452 if(newpl->Load() && newpl->Count()) {
1453 isyslog("mp3: playback started with playlist %s", newpl->Name());
1454 cMP3Control::SetPlayList(newpl);
1455 if(MP3Setup.KeepSelect) { Status(0); return osContinue; }
1460 Error(tr("Error loading playlist!"));
1464 eOSState cMenuMP3::Source(bool second)
1466 if(HasSubMenu()) return osContinue;
1470 return AddSubMenu(new cMenuSource(&MP3Sources,tr("MP3 source")));
1473 cFileSource *src=cMenuSource::GetSelected();
1475 MP3Sources.SetSource(src);
1482 eOSState cMenuMP3::Instant(bool second)
1484 if(HasSubMenu()) return osContinue;
1488 return AddSubMenu(new cMenuInstantBrowse(MP3Sources.GetSource(),trVDR(BUTTON"Play"),tr("Play all")));
1491 cFileObj *item=cMenuInstantBrowse::GetSelected();
1493 Status(tr("Building playlist..."));
1494 cInstantPlayList *newpl = new cInstantPlayList(item);
1495 if(newpl->Load() && newpl->Count()) {
1496 isyslog("mp3: playback started with instant playlist %s", newpl->Name());
1497 cMP3Control::SetPlayList(newpl);
1498 if(MP3Setup.KeepSelect) { Status(0); return Instant(false); }
1503 Error(tr("Error building playlist!"));
1508 eOSState cMenuMP3::ProcessKey(eKeys Key)
1510 eOSState state = cOsdMenu::ProcessKey(Key);
1512 if(!HasSubMenu() && state==osContinue) { // eval the return value from submenus
1513 if(renaming) return Rename(true);
1514 if(sourcing) return Source(true);
1515 if(instanting) return Instant(true);
1518 if(state == osUnknown) {
1520 case kOk: return Play();
1521 case kRed: return (buttonnum==1 ? Edit() : SetButtons(1));
1522 case kGreen: return (buttonnum==1 ? Source(false) : New());
1523 case kYellow: return (buttonnum==1 ? Instant(false) : Delete());
1524 case kBlue: return (buttonnum==1 ? SetButtons(2) : Rename(false));
1525 case kMenu: return osEnd;
1532 // --- PropagateImage ----------------------------------------------------------
1534 void PropagateImage(const char *image)
1536 cPlugin *graphtft=cPluginManager::GetPlugin("graphtft");
1537 if(graphtft) graphtft->SetupParse("CoverImage",image ? image:"");
1540 // --- cPluginMP3 --------------------------------------------------------------
1542 static const char *DESCRIPTION = trNOOP("A versatile audio player");
1543 static const char *MAINMENUENTRY = "MP3";
1545 class cPluginMp3 : public cPlugin {
1547 #if APIVERSNUM >= 10330
1548 bool ExternalPlay(const char *path, bool test);
1552 virtual ~cPluginMp3();
1553 virtual const char *Version(void) { return PluginVersion; }
1554 virtual const char *Description(void) { return tr(DESCRIPTION); }
1555 virtual const char *CommandLineHelp(void);
1556 virtual bool ProcessArgs(int argc, char *argv[]);
1557 #if APIVERSNUM >= 10131
1558 virtual bool Initialize(void);
1560 virtual bool Start(void);
1562 virtual void Housekeeping(void);
1563 virtual const char *MainMenuEntry(void);
1564 virtual cOsdObject *MainMenuAction(void);
1565 virtual cMenuSetupPage *SetupMenu(void);
1566 virtual bool SetupParse(const char *Name, const char *Value);
1567 #if APIVERSNUM >= 10330
1568 virtual bool Service(const char *Id, void *Data);
1569 #if APIVERSNUM >= 10331
1570 virtual const char **SVDRPHelpPages(void);
1571 virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode);
1576 cPluginMp3::cPluginMp3(void)
1578 // Initialize any member varaiables here.
1579 // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL
1580 // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT!
1583 cPluginMp3::~cPluginMp3()
1585 InfoCache.Shutdown();
1589 const char *cPluginMp3::CommandLineHelp(void)
1591 static char *help_str=0;
1593 free(help_str); // for easier orientation, this is column 80|
1594 asprintf(&help_str," -m CMD, --mount=CMD use CMD to mount/unmount/eject mp3 sources\n"
1596 " -n CMD, --network=CMD execute CMD before & after network access\n"
1598 " -C DIR, --cache=DIR store ID3 cache file in DIR\n"
1600 " -B DIR, --cddb=DIR search CDDB files in DIR\n"
1602 " -D DEV, --dsp=DEV device for OSS output\n"
1604 " -i CMD, --iconv=CMD use CMD to convert background images\n"
1606 " -I IMG, --defimage=IMG use IMG as default background image\n"
1607 " (default: none)\n"
1608 " -c DIR, --icache=DIR cache converted images in DIR\n"
1610 " -S SUB, --sources=SUB search sources config in SUB subdirectory\n"
1614 netscript ? netscript:"none",
1615 cachedir ? cachedir:"video dir",
1628 sourcesSub ? sourcesSub:"empty"
1633 bool cPluginMp3::ProcessArgs(int argc, char *argv[])
1635 static struct option long_options[] = {
1636 { "mount", required_argument, NULL, 'm' },
1637 { "network", required_argument, NULL, 'n' },
1638 { "cddb", required_argument, NULL, 'B' },
1639 { "dsp", required_argument, NULL, 'D' },
1640 { "cache", required_argument, NULL, 'C' },
1641 { "icache", required_argument, NULL, 'c' },
1642 { "iconv", required_argument, NULL, 'i' },
1643 { "defimage", required_argument, NULL, 'I' },
1644 { "sources", required_argument, NULL, 'S' },
1648 int c, option_index = 0;
1649 while((c=getopt_long(argc,argv,"c:i:m:n:B:C:D:S:",long_options,&option_index))!=-1) {
1651 case 'i': imageconv=optarg; break;
1652 case 'c': imagecache=optarg; break;
1653 case 'm': mountscript=optarg; break;
1654 case 'n': netscript=optarg; break;
1655 case 'C': cachedir=optarg; break;
1656 case 'I': def_usr_img=optarg; break;
1657 case 'S': sourcesSub=optarg; break;
1660 cddbpath=optarg; break;
1662 fprintf(stderr, "mp3: libsndfile support has not been compiled in!\n"); return false;
1666 dspdevice=optarg; break;
1668 fprintf(stderr, "mp3: OSS output has not been compiled in!\n"); return false;
1670 default: return false;
1676 #if APIVERSNUM >= 10131
1677 bool cPluginMp3::Initialize(void)
1679 bool cPluginMp3::Start(void)
1682 if(!CheckVDRVersion(1,1,29,"mp3")) return false;
1684 #if APIVERSNUM < 10507
1687 i18n_name="vdr-mp3";
1689 MP3Sources.Load(AddDirectory(ConfigDirectory(sourcesSub),"mp3sources.conf"));
1690 if(MP3Sources.Count()<1) {
1691 esyslog("ERROR: you should have defined at least one source in mp3sources.conf");
1692 fprintf(stderr,"No source(s) defined in mp3sources.conf\n");
1696 #if APIVERSNUM < 10507
1697 RegisterI18n(Phrases);
1699 mgr=new cPlayManager;
1701 esyslog("ERROR: creating playmanager failed");
1702 fprintf(stderr,"Creating playmanager failed\n");
1705 d(printf("mp3: using %s\n",mad_version))
1706 d(printf("mp3: compiled with %s\n",MAD_VERSION))
1710 void cPluginMp3::Housekeeping(void)
1715 const char *cPluginMp3::MainMenuEntry(void)
1717 return MP3Setup.HideMainMenu ? 0 : tr(MAINMENUENTRY);
1720 cOsdObject *cPluginMp3::MainMenuAction(void)
1722 return new cMenuMP3;
1725 cMenuSetupPage *cPluginMp3::SetupMenu(void)
1727 return new cMenuSetupMP3;
1730 bool cPluginMp3::SetupParse(const char *Name, const char *Value)
1732 if (!strcasecmp(Name, "InitLoopMode")) MP3Setup.InitLoopMode = atoi(Value);
1733 else if (!strcasecmp(Name, "InitShuffleMode")) MP3Setup.InitShuffleMode = atoi(Value);
1734 else if (!strcasecmp(Name, "AudioMode")) MP3Setup.AudioMode = atoi(Value);
1735 else if (!strcasecmp(Name, "BgrScan")) MP3Setup.BgrScan = atoi(Value);
1736 else if (!strcasecmp(Name, "EditorMode")) MP3Setup.EditorMode = atoi(Value);
1737 else if (!strcasecmp(Name, "DisplayMode")) MP3Setup.DisplayMode = atoi(Value);
1738 else if (!strcasecmp(Name, "BackgrMode")) MP3Setup.BackgrMode = atoi(Value);
1739 else if (!strcasecmp(Name, "MenuMode")) MP3Setup.MenuMode = atoi(Value);
1740 else if (!strcasecmp(Name, "TargetLevel")) MP3Setup.TargetLevel = atoi(Value);
1741 else if (!strcasecmp(Name, "LimiterLevel")) MP3Setup.LimiterLevel = atoi(Value);
1742 else if (!strcasecmp(Name, "Only48kHz")) MP3Setup.Only48kHz = atoi(Value);
1743 else if (!strcasecmp(Name, "UseProxy")) MP3Setup.UseProxy = atoi(Value);
1744 else if (!strcasecmp(Name, "ProxyHost")) strn0cpy(MP3Setup.ProxyHost,Value,MAX_HOSTNAME);
1745 else if (!strcasecmp(Name, "ProxyPort")) MP3Setup.ProxyPort = atoi(Value);
1746 else if (!strcasecmp(Name, "UseCddb")) MP3Setup.UseCddb = atoi(Value);
1747 else if (!strcasecmp(Name, "CddbHost")) strn0cpy(MP3Setup.CddbHost,Value,MAX_HOSTNAME);
1748 else if (!strcasecmp(Name, "CddbPort")) MP3Setup.CddbPort = atoi(Value);
1749 else if (!strcasecmp(Name, "AbortAtEOL")) MP3Setup.AbortAtEOL = atoi(Value);
1750 else if (!strcasecmp(Name, "AudioOutMode")) {
1751 MP3Setup.AudioOutMode = atoi(Value);
1753 if(MP3Setup.AudioOutMode==AUDIOOUTMODE_OSS) {
1754 esyslog("WARNING: AudioOutMode OSS not supported, falling back to DVB");
1755 MP3Setup.AudioOutMode=AUDIOOUTMODE_DVB;
1759 #if APIVERSNUM >= 10307
1760 else if (!strcasecmp(Name, "ReplayDisplay")) MP3Setup.ReplayDisplay = atoi(Value);
1762 else if (!strcasecmp(Name, "HideMainMenu")) MP3Setup.HideMainMenu = atoi(Value);
1763 else if (!strcasecmp(Name, "KeepSelect")) MP3Setup.KeepSelect = atoi(Value);
1764 else if (!strcasecmp(Name, "TitleArtistOrder")) MP3Setup.TitleArtistOrder = atoi(Value);
1769 #if APIVERSNUM >= 10330
1771 bool cPluginMp3::ExternalPlay(const char *path, bool test)
1773 char real[PATH_MAX+1];
1774 if(realpath(path,real)) {
1775 cFileSource *src=MP3Sources.FindSource(real);
1777 cFileObj *item=new cFileObj(src,0,0,otFile);
1779 item->SplitAndSet(real);
1780 if(item->GuessType()) {
1781 if(item->Exists()) {
1782 cInstantPlayList *pl=new cInstantPlayList(item);
1783 if(pl && pl->Load() && pl->Count()) {
1784 if(!test) cMP3Control::SetPlayList(pl);
1789 else dsyslog("MP3 service: error building playlist");
1792 else dsyslog("MP3 service: cannot play '%s'",path);
1794 else dsyslog("MP3 service: GuessType() failed for '%s'",path);
1798 else dsyslog("MP3 service: cannot find source for '%s', real '%s'",path,real);
1800 else if(errno!=ENOENT && errno!=ENOTDIR)
1801 esyslog("ERROR: realpath: %s: %s",path,strerror(errno));
1805 bool cPluginMp3::Service(const char *Id, void *Data)
1807 if(!strcasecmp(Id,"MP3-Play-v1")) {
1809 struct MPlayerServiceData *msd=(struct MPlayerServiceData *)Data;
1810 msd->result=ExternalPlay(msd->data.filename,false);
1814 else if(!strcasecmp(Id,"MP3-Test-v1")) {
1816 struct MPlayerServiceData *msd=(struct MPlayerServiceData *)Data;
1817 msd->result=ExternalPlay(msd->data.filename,true);
1824 #if APIVERSNUM >= 10331
1826 const char **cPluginMp3::SVDRPHelpPages(void)
1828 static const char *HelpPages[] = {
1830 " Triggers playback of file 'filename'.",
1832 " Tests is playback of file 'filename' is possible.",
1834 " Returns filename of song currently being replayed.",
1840 cString cPluginMp3::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
1842 if(!strcasecmp(Command,"PLAY")) {
1844 if(ExternalPlay(Option,false)) return "Playback triggered";
1845 else { ReplyCode=550; return "Playback failed"; }
1847 else { ReplyCode=501; return "Missing filename"; }
1849 else if(!strcasecmp(Command,"TEST")) {
1851 if(ExternalPlay(Option,true)) return "Playback possible";
1852 else { ReplyCode=550; return "Playback not possible"; }
1854 else { ReplyCode=501; return "Missing filename"; }
1856 else if(!strcasecmp(Command,"CURR")) {
1857 cControl *control=cControl::Control();
1858 if(control && typeid(*control)==typeid(cMP3Control)) {
1860 if(mgr->Info(-1,&mode)) return mode.Filename;
1861 else return "<unknown>";
1863 else { ReplyCode=550; return "No running playback"; }
1871 VDRPLUGINCREATOR(cPluginMp3); // Don't touch this!