{$include compilers.inc} {$IFDEF COMPILER_16_UP} {$WEAKLINKRTTI ON} {.$RTTI EXPLICIT METHODS([]) PROPERTIES([]) FIELDS([])} {$ENDIF} {$IMAGEBASE $13000000} library WATrack; uses // FastMM not compatible with FPC, internal for delphi xe // {$IFNDEF COMPILER_16_UP}{$IFNDEF FPC}fastmm4,{$ENDIF}{$ENDIF} m_api,dbsettings,{activex,}winampapi, Windows,messages,commctrl,//uxtheme, srv_format,srv_player,srv_getinfo,wat_api,wrapper, common,syswin,HlpDlg,mirutils ,global,waticons,io,macros, msninfo ,myshows in 'myshows\myshows.pas' ,lastfm in 'lastfm\lastfm.pas' ,statlog in 'stat\statlog.pas' ,popups in 'popup\popups.pas' ,proto in 'proto\proto.pas' ,status in 'status\status.pas' ,tmpl in 'status\tmpl.pas' ,templates in 'templates\templates.pas' {$IFDEF KOL_MCK} ,kolframe in 'kolframe\kolframe.pas' {$ENDIF} {$include lst_players.inc} {$include lst_formats.inc} ; {$include res\i_const.inc} {$Resource res\watrack.res} {$include i_vars.inc} const MenuDisablePos = 500050000; function MirandaPluginInfoEx(mirandaVersion:dword):PPLUGININFOEX; cdecl; begin result:=@PluginInfo; PluginInfo.cbSize :=SizeOf(TPLUGININFOEX); PluginInfo.shortName :=PluginName; PluginInfo.version :=$0000060C; PluginInfo.description:='Paste played music info into message window or status text'; PluginInfo.author :='Awkward'; PluginInfo.authorEmail:='panda75@bk.ru; awk1975@ya.ru'; PluginInfo.copyright :='(c) 2005-2012 Awkward'; PluginInfo.homepage :='http://code.google.com/p/delphi-miranda-plugins/'; PluginInfo.flags :=UNICODE_AWARE; PluginInfo.uuid :=MIID_WATRACK; end; {$include i_options.inc} {$include i_timer.inc} {$include i_gui.inc} {$include i_opt_dlg.inc} {$include i_cover.inc} function ReturnInfo(enc:WPARAM;cp:LPARAM=CP_ACP):pointer; begin if enc<>WAT_INF_UNICODE then begin ClearSongInfoData(tSongInfo(SongInfoA)); move(SongInfo,SongInfoA,SizeOf(tSongInfo)); with SongInfoA do begin if enc=WAT_INF_ANSI then begin WideToAnsi(SongInfo.artist ,artist ,cp); WideToAnsi(SongInfo.title ,title ,cp); WideToAnsi(SongInfo.album ,album ,cp); WideToAnsi(SongInfo.genre ,genre ,cp); WideToAnsi(SongInfo.comment,comment,cp); WideToAnsi(SongInfo.year ,year ,cp); WideToAnsi(SongInfo.mfile ,mfile ,cp); WideToAnsi(SongInfo.wndtext,wndtext,cp); WideToAnsi(SongInfo.player ,player ,cp); WideToAnsi(SongInfo.txtver ,txtver ,cp); WideToAnsi(SongInfo.lyric ,lyric ,cp); WideToAnsi(SongInfo.cover ,cover ,cp); WideToAnsi(SongInfo.url ,url ,cp); end else begin WideToUTF8(SongInfo.artist ,artist); WideToUTF8(SongInfo.title ,title); WideToUTF8(SongInfo.album ,album); WideToUTF8(SongInfo.genre ,genre); WideToUTF8(SongInfo.comment,comment); WideToUTF8(SongInfo.year ,year); WideToUTF8(SongInfo.mfile ,mfile); WideToUTF8(SongInfo.wndtext,wndtext); WideToUTF8(SongInfo.player ,player); WideToUTF8(SongInfo.txtver ,txtver); WideToUTF8(SongInfo.lyric ,lyric); WideToUTF8(SongInfo.cover ,cover); WideToUTF8(SongInfo.url ,url); end; end; result:=@SongInfoA; end else result:=@SongInfo; end; function WATReturnGlobal(wParam:WPARAM;lParam:LPARAM):int_ptr;cdecl; begin if wParam=0 then wParam:=WAT_INF_UNICODE; if lParam=0 then lParam:=MirandaCP; result:=int_ptr(ReturnInfo(wParam,lParam)); end; function WATGetFileInfo(wParam:WPARAM;lParam:LPARAM):int_ptr;cdecl; var // si:TSongInfo; dst:pSongInfo; extw:array [0..7] of WideChar; f:THANDLE; p:PWideChar; begin result:=1; if (lParam=0) or (pSongInfo(lParam).mfile=nil) then exit; dst:=pointer(lParam); p:=dst^.mfile; ClearTrackInfo(dst^); dst^.mfile:=p; // FillChar(dst,SizeOf(dst),0); // FillChar(si,SizeOf(si),0); { if flags and WAT_INF_ANSI<>0 then AnsiToWide(dst^.mfile,si.mfile) else if flags and WAT_INF_UTF<>0 then UTFToWide(dst^.mfile,si.mfile) else si.mfile:=dst^.mfile; } f:=Reset(dst^.mfile); if THANDLE(f)<>INVALID_HANDLE_VALUE then GetFileTime(f,nil,nil,@dst^.date); CloseHandle(f); dst^.fsize:=GetFSize(dst^.mfile); GetExt(dst^.mfile,extw); if GetFileFormatInfo(dst^)<>WAT_RES_NOTFOUND then begin with dst^ do begin if (cover=nil) or (cover^=#0) then GetCover(cover,mfile); if (lyric=nil) or (lyric^=#0) then GetLyric(lyric,mfile); end; result:=0; // ReturnInfo(si,dst,wParam and $FF); end; end; function WATGetMusicInfo(wParam:WPARAM;lParam:LPARAM):int_ptr;cdecl; type ppointer = ^pointer; const giused:cardinal=0; var flags:cardinal; buf:PWideChar; newplayer, newstatus, newtrack :bool; OldPlayerStatus:integer; begin result:=WAT_RES_NOTFOUND; if DisablePlugin=dsPermanent then exit; //----- Return old info if main timer ----- if giused<>0 then begin result:=WAT_RES_OK; if lParam<>0 then ppointer(lParam)^:=ReturnInfo(wParam and $FF); exit; end; giused:=1; OldPlayerStatus:=WorkSI.status; newstatus:=false; newtrack :=false; //----- Checking player ----- flags:=0; if CheckAll<>BST_UNCHECKED then flags:=flags or WAT_OPT_CHECKALL; result:=GetPlayerInfo(WorkSI,flags); newplayer:=result=WAT_RES_NEWPLAYER; if newplayer then result:=WAT_RES_OK; //----- Checking player status ----- if result=WAT_RES_OK then begin // player stopped - no need file info if WorkSI.status=WAT_PLS_STOPPED then begin newstatus:=OldPlayerStatus<>WAT_PLS_STOPPED; end //----- Get file (no file, new file, maybe new) ----- else begin // file info will be replaced (name most important only) flags:=0; if CheckUnknown<>BST_UNCHECKED then flags:=flags or WAT_OPT_UNKNOWNFMT; if CheckTime <>BST_UNCHECKED then flags:=flags or WAT_OPT_CHECKTIME; if UseImplant <>BST_UNCHECKED then flags:=flags or WAT_OPT_IMPLANTANT; if KeepOld <>BST_UNCHECKED then flags:=flags or WAT_OPT_KEEPOLD; // requirement - old file name result:=GetFileInfo(WorkSI,flags,0); if (WorkSI.status=WAT_PLS_UNKNOWN ) and // player in unknown state (result =WAT_RES_NOTFOUND) then // and known media not found WorkSI.status:=WAT_PLS_STOPPED; newstatus:=OldPlayerStatus<>WorkSI.status; // now time for changes (window text, volume) // just when music presents if WorkSI.status<>WAT_PLS_STOPPED then begin GetChangingInfo(WorkSI,flags); // full info requires // "no music" case blocked {?? WorkSI.status=WAT_PLS_PLAYING and result=WAT_RES_UNKNOWN } if (result=WAT_RES_NEWFILE) or // new file (result=WAT_RES_UNKNOWN) or // unknown file (enabled by flag in GetFileInfo) ((result=WAT_RES_OK) and // if not new but... (((wParam and WAT_INF_CHANGES)=0) or // ... ask for full info (StrPosW(WorkSI.mfile,'://')<>nil) or // ... or remote file isContainer(WorkSI.mfile))) then // ... or container like CUE begin // requirement: old artist/title for remote files newtrack:=result=WAT_RES_NEWFILE; result:=GetInfo(WorkSI,flags); if not newtrack then newtrack:=result=WAT_RES_NEWFILE; end; end; end; end //----- Player not found ----- else begin if OldPlayerStatus<>WorkSI.status then begin WorkSI.status:=WAT_PLS_NOTFOUND; newstatus:=true; end; end; //----- Copy all data to public (WorkSI to SongInfo) ----- SongInfo.status:=WorkSI.status; if WorkSI.status=WAT_PLS_NOTFOUND then begin ClearSongInfoData(WorkSI); ClearSongInfoData(SongInfo); if lParam<>0 then ppointer(lParam)^:=nil; end else begin if not newplayer then // for another player instance SongInfo.plwnd:=WorkSI.plwnd else begin ClearPlayerInfo(SongInfo); CopyPlayerInfo (WorkSI,SongInfo); end; if (WorkSI.status=WAT_PLS_STOPPED) or // no music (result=WAT_RES_NOTFOUND) then // or unknown media file begin ClearFileInfo (WorkSI); ClearChangingInfo(WorkSI); ClearTrackInfo (WorkSI); ClearFileInfo (SongInfo); ClearChangingInfo(SongInfo); ClearTrackInfo (SongInfo); end else begin ClearChangingInfo(SongInfo); CopyChangingInfo (WorkSI,SongInfo); if newtrack then begin // covers if (WorkSI.cover=nil) or (WorkSI.cover^=#0) then GetCover(WorkSI.cover,WorkSI.mfile) else begin mGetMem(buf,MAX_PATH*SizeOf(WideChar)); GetTempPathW(MAX_PATH,buf); if StrCmpW(buf,WorkSI.cover,StrLenW(buf))=0 then begin GetExt(WorkSI.cover,StrCatEW(buf,'\wat_cover.')); DeleteFileW(buf); MoveFileW(WorkSI.cover,buf); mFreeMem(WorkSI.cover); WorkSI.cover:=buf; end else mFreeMem(buf); end; // lyric if (WorkSI.lyric=nil) or (WorkSI.lyric^=#0) then GetLyric(WorkSI.lyric,WorkSI.mfile); ClearFileInfo(SongInfo); CopyFileInfo (WorkSI,SongInfo); ClearTrackInfo(SongInfo); CopyTrackInfo (WorkSI,SongInfo); end; end; if lParam<>0 then ppointer(lParam)^:=ReturnInfo(wParam and $FF); end; //----- Events ----- if newplayer then NotifyEventHooks(hHookWATStatus,WAT_EVENT_NEWPLAYER ,tlparam(@SongInfo)); if newstatus then NotifyEventHooks(hHookWATStatus,WAT_EVENT_PLAYERSTATUS,SongInfo.status); if newtrack then NotifyEventHooks(hHookWATStatus,WAT_EVENT_NEWTRACK ,tlparam(@SongInfo)); giused:=0; end; function PressButton(wParam:WPARAM;lParam:LPARAM):int_ptr;cdecl; var flags:integer; begin if DisablePlugin=dsPermanent then result:=0 else begin flags:=0; if UseImplant<>BST_UNCHECKED then flags:=flags or WAT_OPT_IMPLANTANT; if mmkeyemu <>BST_UNCHECKED then flags:=flags or WAT_OPT_APPCOMMAND; if CheckAll <>BST_UNCHECKED then flags:=flags or WAT_OPT_CHECKALL; result:=SendCommand(wParam,lParam,flags); end; end; function WATPluginStatus(wParam:WPARAM;lParam:LPARAM):int;cdecl; var f1:integer; begin if wParam=2 then begin result:=PluginInfo.version; exit; end; if DisablePlugin=dsPermanent then result:=1 else result:=0; if (integer(wParam)<0) or (wParam=MenuDisablePos) then begin if result=0 then wParam:=1 else wParam:=0; end; case wParam of 0: begin if DisablePlugin=dsPermanent then //?? begin StartTimer; DisablePlugin:=dsEnabled; end; f1:=0; end; 1: begin StopTimer; DisablePlugin:=dsPermanent; f1:=CMIF_CHECKED; end; else exit; end; DBWriteByte(0,PluginShort,opt_disable,DisablePlugin); ChangeMenuIcons(f1); NotifyEventHooks(hHookWATStatus,WAT_EVENT_PLUGINSTATUS,DisablePlugin); end; function WaitAllModules(wParam:WPARAM;lParam:LPARAM):int;cdecl; var ptr:pwModule; begin result:=0; CallService(MS_SYSTEM_REMOVEWAIT,wParam,0); ptr:=ModuleLink; while ptr<>nil do begin if @ptr^.Init<>nil then ptr^.ModuleStat:=ptr^.Init(true); ptr:=ptr^.Next; end; if mTimer<>0 then TimerProc(0,0,0,0); StartTimer; NotifyEventHooks(hHookWATLoaded,0,0); CloseHandle(hEvent); end; procedure DoTheDew(load:boolean); var ptr:pwModule; newstate:boolean; begin ptr:=ModuleLink; while ptr<>nil do begin if @ptr^.Check<>nil then ptr^.Check(load); ptr:=ptr^.Next; end; // TTB newstate:=ServiceExists(MS_TTB_ADDBUTTON)<>0; if newstate=(ttbState<>0) then exit; if ttbState=0 then begin { onloadhook:=0; OnTTBLoaded(0,0); if ttbState=0 then } HookEvent(ME_TTB_MODULELOADED,@OnTTBLoaded); end else begin if ServiceExists(MS_TTB_REMOVEBUTTON)>0 then CallService(MS_TTB_REMOVEBUTTON,WPARAM(ttbState),0); ttbState:=0; end; end; function OnPluginLoad(wParam:WPARAM;lParam:LPARAM):int;cdecl; begin DoTheDew(true); result:=0; end; function OnPluginUnload(wParam:WPARAM;lParam:LPARAM):int;cdecl; begin DoTheDew(false); result:=0; end; type tdbetd = record flags:dword; event:int; descr:pAnsiChar; end; const cdbetd: array [0..3] of tdbetd = ( (flags:DETF_HISTORY or DETF_MSGWINDOW; event:EVENTTYPE_WAT_REQUEST; descr:'WATrack: information request'), (flags:DETF_HISTORY or DETF_MSGWINDOW; event:EVENTTYPE_WAT_ANSWER ; descr:nil), (flags:DETF_HISTORY or DETF_MSGWINDOW; event:EVENTTYPE_WAT_ERROR ; descr:'WATrack: request denied'), (flags:DETF_HISTORY or DETF_NONOTIFY ; event:EVENTTYPE_WAT_MESSAGE; descr:nil) ); function OnModulesLoaded(wParam:WPARAM;lParam:LPARAM):int;cdecl; var p:PAnsiChar; dbetd:TDBEVENTTYPEDESCR; i:integer; begin hTimer:=0; //!! OleInitialize(nil); if RegisterIcons then HookEvent(ME_SKIN2_ICONSCHANGED,@IconChanged); CreateMenus; if ServiceExists(MS_TTB_ADDBUTTON)<>0 then HookEvent(ME_TTB_MODULELOADED,@OnTTBLoaded) else ttbState:=0; ProcessFormatLink; ProcessPlayerLink; // next 2 blocks here coz GetAddonFileName uses some Miranda things p:=GetAddonFileName(nil,'player','plugins','ini'); if p<>nil then begin LoadFromFile(p); mFreeMem(p); end; p:=GetAddonFileName(nil,'watrack_icons','icons','dll'); if p<>nil then begin SetPlayerIcons(p); mFreeMem(p); end; IsMultiThread:=true; // Register WATrack events dbetd.cbSize :=SizeOf(TDBEVENTTYPEDESCR); dbetd.module :=PluginShort; dbetd.textService:=nil; dbetd.iconService:=nil; p:='WATrack_Info'; dbetd.eventIcon :=CallService(MS_SKIN2_GETICON,0,tlparam(p)); //!! from popups for i:=0 to HIGH(cdbetd) do begin dbetd.flags :=cdbetd[i].flags; dbetd.eventType :=cdbetd[i].event; dbetd.descr :=cdbetd[i].descr; CallService(MS_DB_EVENT_REGISTERTYPE,0,TLPARAM(@dbetd)); end; // Load WATrack modules hEvent:=CreateEvent(nil,true,true,nil); if hEvent<>0 then begin p:='WAT_INIT'; CreateServiceFunction(p,@WaitAllModules); CallService(MS_SYSTEM_WAITONHANDLE,hEvent,tlparam(p)); end; LoadOpt; if DisablePlugin=dsPermanent then CallService(MS_WAT_PLUGINSTATUS,1,0); StartMSNHook; result:=0; HookEvent(ME_SYSTEM_MODULELOAD ,@OnPluginLoad); HookEvent(ME_SYSTEM_MODULEUNLOAD,@OnPluginUnload); end; procedure FreeVariables; begin ClearSongInfoData(SongInfo); ClearSongInfoData(tSongInfo(SongInfoA)); ClearSongInfoData(WorkSI); mFreeMem(CoverPaths); ClearFormats; ClearPlayers; end; function PreShutdown(wParam:WPARAM;lParam:LPARAM):int;cdecl; var buf:array [0..511] of WideChar; fdata:WIN32_FIND_DATAW; fi:THANDLE; p:PWideChar; ptr:pwModule; begin StopMSNHook; NotifyEventHooks(hHookWATStatus,WAT_EVENT_PLAYERSTATUS,WAT_PLS_NOTFOUND); if hwndTooltip<>0 then DestroyWindow(hwndTooltip); if ttbState<>0 then begin if ServiceExists(MS_TTB_REMOVEBUTTON)>0 then CallService(MS_TTB_REMOVEBUTTON,TWPARAM(ttbState),0); ttbState:=0; end; StopTimer; ptr:=ModuleLink; while ptr<>nil do begin if @ptr^.DeInit<>nil then ptr^.DeInit(false); ptr:=ptr^.Next; end; FreeVariables; DestroyHookableEvent(hHookWATLoaded); DestroyHookableEvent(hHookWATStatus); //!! OleUnInitialize; //delete cover files buf[0]:=#0; GetTempPathW(511,buf); p:=StrEndW(buf); StrCopyW(p,'wat_cover.*'); fi:=FindFirstFileW(buf,fdata); if fi<>THANDLE(INVALID_HANDLE_VALUE) then begin repeat StrCopyW(p,fdata.cFileName); DeleteFileW(buf); until not FindNextFileW(fi,fdata); FindClose(fi); end; result:=0; end; function Load:int; cdecl; begin result:=0; Langpack_register; DisablePlugin:=dsPermanent; hHookWATLoaded:=CreateHookableEvent(ME_WAT_MODULELOADED); hHookWATStatus:=CreateHookableEvent(ME_WAT_NEWSTATUS); HookEvent(ME_SYSTEM_OKTOEXIT,@PreShutdown); HookEvent(ME_OPT_INITIALISE ,@OnOptInitialise); CreateServiceFunction(MS_WAT_GETFILEINFO ,@WATGetFileInfo); CreateServiceFunction(MS_WAT_RETURNGLOBAL ,@WATReturnGlobal); CreateServiceFunction(MS_WAT_GETMUSICINFO ,@WATGetMusicInfo); CreateServiceFunction(MS_WAT_PLUGINSTATUS ,@WATPluginStatus); CreateServiceFunction(MS_WAT_PRESSBUTTON ,@PressButton); CreateServiceFunction(MS_WAT_WINAMPINFO ,@WinampGetInfo); CreateServiceFunction(MS_WAT_WINAMPCOMMAND,@WinampCommand); CreateServiceFunction(MS_WAT_FORMAT,@ServiceFormat); CreateServiceFunction(MS_WAT_PLAYER,@ServicePlayer); FillChar(SongInfoA,SizeOf(SongInfoA),0); FillChar(SongInfo ,SizeOf(SongInfo ),0); FillChar(WorkSI ,SizeOf(SongInfo ),0); HookEvent(ME_SYSTEM_MODULESLOADED,@OnModulesLoaded); end; function Unload:int; cdecl; begin result:=0; end; exports Load, Unload, MirandaPluginInfoEx; begin DisableThreadLibraryCalls(hInstance); end.