Класс для управления MPD
Добавлено: 08.07.2008 15:38:00
Наваял, для своих нужд, небольшой класс для работы с MPD (Music Player Daemon).
Мож пригодится кому. Для его работы нужны компоненты LNet.
Пример использования:
Edit: Класс был чуток подправлен.
Мож пригодится кому. Для его работы нужны компоненты LNet.
- Код: Выделить всё
unit umpd;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,lNet,lNetComponents,ExtCtrls;
type
TCurrentStatus=record
AVolume:integer;
ARepeat:Boolean;
ARandom:Boolean;
APlayList:Longint;
APlayListLength:integer;
AXfade:integer;
AState:0..3;
ASong:integer;
ASongId:integer;
ATimeElapsed:integer;
ATimeTotal:integer;
ABitrate:integer;
AAudioSampleRate:integer;
AAudioBits:integer;
AAudioChanels:integer;
AUpDbJobId:integer;
AError:string;
AResult:string;
end;
TCurrentSong=record
AFile:string;
ATime:integer;
AAlbum:string;
ADate:string;
AArtist:string;
ATitle:string;
AGenre:string;
ATrack:integer;
APos:Integer;
AId:integer;
AResult:string;
end;
TUpdateInfoProc=procedure (const Status:TCurrentStatus);
TUpdateSongProc=procedure (const Song:TCurrentSong);
TGetInfoProc=procedure (Cmd,AResult:string; const ANames,AValues:TStrings);
{ TMpd }
TMpd = class
private
LClient:TLTCPComponent;
FTimer:TTimer;
FBackInfoProc:TUpdateInfoProc;
FBackSongProc:TUpdateSongProc;
FBackGetInfo:TGetInfoProc;
Fbuf:TStringList;
FLastCmd:TStringList;
FNames,FValues:TStrings;
//---------------------
procedure LClientReceive(aSocket: TLSocket);
procedure MpdOnTimer(Sender: TObject);
//---------------------
Function GetHost:string;
Function GetPort:Word;
Function GetInterval:Cardinal;
Function GetConnected:boolean;
procedure ParseInfo(str: string);
procedure ParseSong(str: string);
procedure ParseStatus(str: string);
//---------------------
procedure SetHost(AValue:string);
procedure SetPort(AValue:Word);
procedure SetInterval(AValue:Cardinal);
public
constructor Create;
destructor Destroy; override;
//---------------------
procedure ExecCmd(ACmd:string);
function Connect(AHost:String; APort:Word):boolean;
function Connect:boolean;
procedure Disconnect;
//---------------------
property Host:String read GetHost write SetHost;
property Port:Word read GetPort write SetPort;
property UpdateInterval:Cardinal read GetInterval write SetInterval;
property Connected:boolean read GetConnected;
property OnUpdateInfo:TUpdateInfoProc read FBackInfoProc write FBackInfoProc;
property OnUpdateSong:TUpdateSongProc read FBackSongProc write FBackSongProc;
Property OnGetInfo:TGetInfoProc read FBackGetInfo write FBackGetInfo;
property LastCmd:TStringList read FLastCmd;
end;
implementation
{ TMpd }
procedure TMpd.ParseInfo(str:string);
var st,s,ar:string; p,i:integer;
begin
if not assigned(FBackGetInfo) then exit;
st:=str;
FNames.Clear;
FValues.Clear;
Fbuf.Clear;
Fbuf.NameValueSeparator:=':';
i:=1;
while i<=length(st) do
begin
if st[i]=#10 then
begin
Fbuf.Add(s);
s:='';
end
else
s:=s+st[i];
i:=i+1;
end;
if (pos('OK',Fbuf[Fbuf.Count-1])>0) or (pos('ACK',Fbuf[Fbuf.Count-1])>0) then
p:=2 else p:=1;
if (Fbuf.Count>1) then
for i:=0 to Fbuf.Count-p do
begin
FNames.Add(Fbuf.Names[i]);
FValues.Add(copy(Fbuf.ValueFromIndex[i],2,length(Fbuf.ValueFromIndex[i])))
end;
if (pos('OK',Fbuf[Fbuf.Count-1])>0) or (pos('ACK',Fbuf[Fbuf.Count-1])>0) then
ar:=Fbuf[Fbuf.Count-1] else ar:='';
if (Fbuf.Count>0) and (Assigned(FBackGetInfo)) then
FBackGetInfo(LastCmd[LastCmd.Count-1],ar,FNames,FValues);
end;
procedure TMpd.ParseSong(str:string);
var st,s:string; p,i:integer; inf:TCurrentSong;
begin
if not assigned(FBackSongProc) then exit;
st:=str;
Fbuf.Clear;
Fbuf.NameValueSeparator:=':';
i:=1;
while i<=length(st) do
begin
if st[i]=#10 then
begin
Fbuf.Add(s);
s:='';
end
else
s:=s+st[i];
i:=i+1;
end;
p:=Fbuf.IndexOfName('file');
if p>-1 then
begin
Inf.AFile:=copy(Fbuf.ValueFromIndex[p],2,length(Fbuf.ValueFromIndex[p]));
end
else Inf.AFile:='';
p:=Fbuf.IndexOfName('Time');
if p>-1 then
begin
if not TryStrToInt(copy(Fbuf.ValueFromIndex[p],2,length(Fbuf.ValueFromIndex[p])),Inf.ATime) then
Inf.ATime:=0;
end
else Inf.ATime:=0;
p:=Fbuf.IndexOfName('Artist');
if p>-1 then
begin
Inf.AArtist:=copy(Fbuf.ValueFromIndex[p],2,length(Fbuf.ValueFromIndex[p]));
end
else Inf.AArtist:='';
p:=Fbuf.IndexOfName('Title');
if p>-1 then
begin
Inf.ATitle:=copy(Fbuf.ValueFromIndex[p],2,length(Fbuf.ValueFromIndex[p]));
end
else Inf.ATitle:='';
p:=Fbuf.IndexOfName('Album');
if p>-1 then
begin
Inf.AAlbum:=copy(Fbuf.ValueFromIndex[p],2,length(Fbuf.ValueFromIndex[p]));
end
else Inf.AAlbum:='';
p:=Fbuf.IndexOfName('Date');
if p>-1 then
begin
Inf.ADate:=copy(Fbuf.ValueFromIndex[p],2,length(Fbuf.ValueFromIndex[p]));
end
else Inf.ADate:='';
p:=Fbuf.IndexOfName('Genre');
if p>-1 then
begin
Inf.AGenre:=copy(Fbuf.ValueFromIndex[p],2,length(Fbuf.ValueFromIndex[p]));
end
else Inf.AGenre:='';
p:=Fbuf.IndexOfName('Pos');
if p>-1 then
begin
if not TryStrToInt(copy(Fbuf.ValueFromIndex[p],2,length(Fbuf.ValueFromIndex[p])),Inf.APos) then
Inf.APos:=0;
end
else Inf.APos:=0;
p:=Fbuf.IndexOfName('Id');
if p>-1 then
begin
if not TryStrToInt(copy(Fbuf.ValueFromIndex[p],2,length(Fbuf.ValueFromIndex[p])),Inf.AId) then
Inf.AId:=0;
end
else Inf.AId:=0;
p:=Fbuf.IndexOfName('Track');
if p>-1 then
begin
if not TryStrToInt(copy(Fbuf.ValueFromIndex[p],2,length(Fbuf.ValueFromIndex[p])),Inf.ATrack) then
Inf.ATrack:=0;
end
else Inf.ATrack:=0;
Inf.AResult:=Fbuf[Fbuf.Count-1];
if Assigned(FBackSongProc) then
FBackSongProc(Inf);
end;
//------------------------------------------------------
procedure TMpd.ParseStatus(str:string);
var st,s:string; p,i:integer; inf:TCurrentStatus;
begin
if not assigned(FBackInfoProc) then exit;
st:=str;
Fbuf.Clear;
Fbuf.NameValueSeparator:=':';
i:=1;
while i<=length(st) do
begin
if st[i]=#10 then
begin
Fbuf.Add(s);
s:='';
end
else
s:=s+st[i];
i:=i+1;
end;
p:=Fbuf.IndexOfName('volume');
if p>-1 then
begin
if not TryStrToInt(copy(Fbuf.ValueFromIndex[p],2,length(Fbuf.ValueFromIndex[p])),Inf.AVolume) then Inf.AVolume:=0;
end
else Inf.AVolume:=0;
p:=Fbuf.IndexOfName('repeat');
if p>-1 then
begin
if not TryStrToBool(copy(Fbuf.ValueFromIndex[p],2,length(Fbuf.ValueFromIndex[p])),Inf.ARepeat) then Inf.ARepeat:=false;
end
else Inf.ARepeat:=false;
p:=Fbuf.IndexOfName('random');
if p>-1 then
begin
if not TryStrToBool(copy(Fbuf.ValueFromIndex[p],2,length(Fbuf.ValueFromIndex[p])),Inf.ARandom) then Inf.ARandom:=false;
end
else Inf.ARandom:=false;
p:=Fbuf.IndexOfName('playlist');
if p>-1 then
begin
if not TryStrToInt(copy(Fbuf.ValueFromIndex[p],2,length(Fbuf.ValueFromIndex[p])),Inf.APlayList) then Inf.APlayList:=0;
end
else Inf.APlayList:=0;
p:=Fbuf.IndexOfName('playlistlength');
if p>-1 then
begin
if not TryStrToInt(copy(Fbuf.ValueFromIndex[p],2,length(Fbuf.ValueFromIndex[p])),Inf.APlayListLength) then Inf.APlayListLength:=0;
end
else Inf.APlayListLength:=0;
p:=Fbuf.IndexOfName('xfade');
if p>-1 then
begin
if not TryStrToInt(copy(Fbuf.ValueFromIndex[p],2,length(Fbuf.ValueFromIndex[p])),Inf.AXfade) then Inf.AXfade:=0;
end
else Inf.AXfade:=0;
p:=Fbuf.IndexOfName('state');
if p>-1 then
begin
if copy(Fbuf.ValueFromIndex[p],2,length(Fbuf.ValueFromIndex[p]))='play' then Inf.AState:=1
else if copy(Fbuf.ValueFromIndex[p],2,length(Fbuf.ValueFromIndex[p]))='stop' then Inf.AState:=2
else if copy(Fbuf.ValueFromIndex[p],2,length(Fbuf.ValueFromIndex[p]))='pause' then Inf.AState:=3
else Inf.AXfade:=0;
end
else Inf.AState:=0;
p:=Fbuf.IndexOfName('song');
if p>-1 then
begin
if not TryStrToInt(copy(Fbuf.ValueFromIndex[p],2,length(Fbuf.ValueFromIndex[p])),Inf.ASong) then Inf.ASong:=0;
end
else Inf.ASong:=0;
p:=Fbuf.IndexOfName('songid');
if p>-1 then
begin
if not TryStrToInt(copy(Fbuf.ValueFromIndex[p],2,length(Fbuf.ValueFromIndex[p])),Inf.ASongId) then Inf.ASongId:=0;
end
else Inf.ASongId:=0;
p:=Fbuf.IndexOfName('time');
if p>-1 then
begin
s:=copy(Fbuf.ValueFromIndex[p],2,length(Fbuf.ValueFromIndex[p]));
if not TryStrToInt( copy(s,1,pos(':',s)-1),Inf.ATimeElapsed) then Inf.ATimeElapsed:=0;
delete(s,1,pos(':',s));
if not TryStrToInt(s,Inf.ATimeTotal) then Inf.ATimeTotal:=0;
end
else
begin
Inf.ATimeTotal:=0;
Inf.ATimeElapsed:=0;
end;
p:=Fbuf.IndexOfName('bitrate');
if p>-1 then
begin
if not TryStrToInt(copy(Fbuf.ValueFromIndex[p],2,length(Fbuf.ValueFromIndex[p])),Inf.ABitrate) then Inf.ABitrate:=0;
end
else Inf.ABitrate:=0;
p:=Fbuf.IndexOfName('audio');
if p>-1 then
begin
s:=copy(Fbuf.ValueFromIndex[p],2,length(Fbuf.ValueFromIndex[p]));
if not TryStrToInt( copy(s,1,pos(':',s)-1),Inf.AAudioSampleRate) then Inf.AAudioSampleRate:=0;
delete(s,1,pos(':',s));
if not TryStrToInt(copy(s,1,pos(':',s)-1),Inf.AAudioBits) then Inf.AAudioBits:=0;
delete(s,1,pos(':',s));
if not TryStrToInt(s,Inf.AAudioChanels) then Inf.AAudioChanels:=0;
end
else
begin
Inf.AAudioSampleRate:=0;
Inf.AAudioBits:=0;
Inf.AAudioChanels:=0;
end;
p:=Fbuf.IndexOfName('updating_db');
if p>-1 then
begin
if not TryStrToInt(copy(Fbuf.ValueFromIndex[p],2,length(Fbuf.ValueFromIndex[p])),Inf.AUpDbJobId) then Inf.AUpDbJobId:=0;
end
else Inf.AUpDbJobId:=0;
p:=Fbuf.IndexOfName('error');
if (p>-1) and (p<Fbuf.Count-1) then
begin
Inf.AError:=copy(Fbuf.ValueFromIndex[p],2,length(Fbuf.ValueFromIndex[p]));
end;
Inf.AResult:=Fbuf[Fbuf.Count-1];
if Assigned(FBackInfoProc) then
FBackInfoProc(Inf);
end;
procedure TMpd.LClientReceive(aSocket: TLSocket);
var s:string; song:TCurrentSong;
begin
if aSocket.GetMessage(s)>0 then
begin
while FLastCmd.Count>0 do
begin
if (FLastCmd.Count>0) and (FLastCmd[FLastCmd.Count-1]='status+') then
begin
ParseStatus(s);
FLastCmd.Delete(FLastCmd.Count-1);
end else
if (FLastCmd.Count>0) and (FLastCmd[FLastCmd.Count-1]='currentsong+') then
begin
ParseSong(s);
FLastCmd.Delete(FLastCmd.Count-1);
end else
if (FLastCmd.Count>0) then
begin
ParseInfo(s);
FLastCmd.Delete(FLastCmd.Count-1);
end;
end;
LClient.IterReset;
end;
end;
procedure TMpd.MpdOnTimer(Sender: TObject);
begin
if not LClient.Connected then exit;
// запрос статуса и песни для проц FBackInfoProc и FBackSongProc;
if Assigned(LClient.Iterator) then
begin
FLastCmd.Add('status+');
LClient.SendMessage('status'+#13#10,LClient.Iterator);
FLastCmd.Add('currentsong+');
LClient.SendMessage('currentsong'+#13#10,LClient.Iterator);
end;
end;
function TMpd.GetHost: string;
begin
if not assigned(LClient) then exit;
Result:=LClient.Host;
end;
function TMpd.GetPort: Word;
begin
if not assigned(LClient) then exit;
Result:=LClient.Port;
end;
function TMpd.GetInterval: Cardinal;
begin
Result:=FTimer.Interval;
end;
function TMpd.GetConnected: boolean;
begin
Result:=LClient.Connected;
end;
procedure TMpd.SetHost(AValue: string);
begin
LClient.Host:=AValue;
end;
procedure TMpd.SetPort(AValue: Word);
begin
LClient.Port:=AValue;
end;
procedure TMpd.SetInterval(AValue: Cardinal);
begin
FTimer.Interval:=AValue;
end;
constructor TMpd.Create;
begin
LClient:=TLTCPComponent.Create(nil);
LClient.Host:='localhost';
LClient.Port:=6600;
LClient.OnReceive:=@LClientReceive;
FTimer:=TTimer.Create(nil);
FTimer.Interval:=1000;
FTimer.OnTimer:=@MpdOnTimer;
FBackInfoProc:=nil;
Fbuf:=TStringList.Create;
Fbuf.CaseSensitive:=true;
FLastCmd:=TStringList.Create;
FNames:=TStringList.Create;
FValues:=TStringList.Create;
end;
destructor TMpd.Destroy;
begin
if Assigned(LClient) then FreeAndNil(LClient);
if Assigned(FTimer) then FreeAndNil(FTimer);
if Assigned(Fbuf) then FreeAndNil(Fbuf);
if Assigned(FLastCmd) then FreeAndNil(FLastCmd);
if Assigned(FNames) then FreeAndNil(FNames);
if Assigned(FValues) then FreeAndNil(FValues);
inherited Destroy;
end;
procedure TMpd.ExecCmd(ACmd: string);
begin
if Assigned(LClient.Iterator) then
begin
FLastCmd.Add(ACmd);
LClient.SendMessage(ACmd+#13#10,LClient.Iterator);
end;
end;
function TMpd.Connect(AHost: String; APort: Word): boolean;
begin
LClient.Port:=APort;
LClient.Host:=AHost;
Result:=LClient.Connect;
FTimer.Enabled:=true;
end;
function TMpd.Connect: boolean;
begin
Result:=LClient.Connect;
FTimer.Enabled:=true;
end;
procedure TMpd.Disconnect;
begin
LClient.Disconnect;
FTimer.Enabled:=False;
end;
end.
Пример использования:
- Код: Выделить всё
...
var
Form1: TForm1;
Mpd:TMpd;
implementation
{ TForm1 }
procedure OnStatus(const Info:TCurrentStatus);
begin
form1.Label2.Caption:=inttostr(info.AVolume);
form1.Label3.Caption:=BoolToStr(info.ARepeat,true);
form1.Label4.Caption:=inttostr(info.ATimeElapsed)+' / '+inttostr(info.ATimeTotal);
form1.pb1.Max:=info.ATimeTotal;
form1.pb1.Position:=info.ATimeElapsed;
form1.Label5.Caption:=inttostr(info.AAudioSampleRate)+':'+inttostr(info.AAudioBits)+':'+inttostr(info.AAudioChanels);
form1.Label6.Caption:=inttostr(Info.ABitrate);
form1.Label7.Caption:=inttostr(Info.APlayListLength);
form1.Label8.Caption:=booltostr(Info.ARandom,true);
form1.Label9.Caption:=inttostr(Info.ASong);
form1.Label10.Caption:=inttostr(Info.ASongId);
case Info.AState of
0: form1.Label11.Caption:='Stop';
1: form1.Label11.Caption:='Play';
2: form1.Label11.Caption:='Stop';
3: form1.Label11.Caption:='Pause';
end;
Form1.Label12.Caption:='>'+info.AResult;
end;
procedure OnSong(const Song:TCurrentSong);
begin
form1.Label13.Caption:=Inttostr(Song.AId);
form1.Label14.Caption:=Inttostr(Song.ATrack);
form1.Label15.Caption:=Song.AArtist;
form1.Label16.Caption:=Song.ATitle;
form1.Label17.Caption:=Song.AGenre;
form1.Label18.Caption:=Song.ADate;
form1.Label19.Caption:=Song.AAlbum;
form1.Label20.Caption:=Song.AFile;
form1.Label21.Caption:=Inttostr(Song.APos);
form1.Label22.Caption:=Inttostr(Song.ATime);
end;
procedure OnGetInfo(cmd,AResult:string; const ANames,AValues:TStrings);
begin
form1.ListBox1.Clear;
form1.ListBox2.Clear;
form1.ListBox3.Clear;
form1.ListBox1.items.AddStrings(ANames);
form1.ListBox2.items.AddStrings(AValues);
form1.ListBox3.items.Add(Cmd);
form1.ListBox3.items.Add(AResult);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Mpd:=TMpd.Create;
mpd.OnUpdateInfo:=@OnStatus;
mpd.OnUpdateSong:=@OnSong;
mpd.OnGetInfo:=@OnGetInfo;
mpd.UpdateInterval:=600;
end;
procedure TForm1.Button4Click(Sender: TObject);
begin
mpd.Connect;
end;
procedure TForm1.Button6Click(Sender: TObject);
begin
mpd.ExecCmd('stats');
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
mpd.ExecCmd(Edit2.text);
end;
procedure TForm1.Button5Click(Sender: TObject);
begin
mpd.Disconnect;
end;
procedure TForm1.Timer2Timer(Sender: TObject);
begin
CheckBox1.Checked:=mpd.Connected;
end;
...
Edit: Класс был чуток подправлен.