{ ******************************************************************************** SPackGui Copyright (C) 2012-2013 Geoffray Levasseur . Copyright (C) http://www.geoffray-levasseur.org/ http://0.tuxfamilly.org/ This software is governed by the CeCILL license under French law and abiding by the rules of distribution of free software. You can use, modify and/ or redistribute the software under the terms of the CeCILL license as circulated by CEA, CNRS and INRIA at the following URL "http://www.cecill.info". As a counterpart to the access to the source code and rights to copy, modify and redistribute granted by the license, users are provided only with a limited warranty and the software's author, the holder of the economic rights, and the successive licensors have only limited liability. In this respect, the user's attention is drawn to the risks associated with loading, using, modifying and/or developing or reproducing the software by the user in light of its specific status of free software, that may mean that it is complicated to manipulate, and that also therefore means that it is reserved for developers and experienced professionals having in-depth computer knowledge. Users are therefore encouraged to load and test the software's suitability as regards their requirements in conditions enabling the security of their systems and/or data to be ensured and, more generally, to use and operate it in the same conditions as regards security. The fact that you are presently reading this means that you have had knowledge of the CeCILL license and that you accept its terms. ******************************************************************************** Description: full download manager class ******************************************************************************** } unit uDownloadManager; {$include defines.inc} interface uses Classes, SysUtils, uDownload, ExtCtrls; type TProxy = record Address: string; Port: Integer; User: string; Pass: string; end; TDownloadListLoop = class(TThread) private FOnNextFile: TDownloadProc; FOnTerminate: TDownloadProc; FOnProgress: TDownloadProc; FOnError: TDownloadProc; FOnNewDownload: TDownloadProc; public FTargetDir: string; PDownloadIndex: ^Integer; PDownloadList: ^TStringList; PDownloader: ^TDownloader; constructor Create; virtual; procedure Execute; override; property OnNextFile: TDownloadProc read FOnNextFile write FOnNextFile; property OnTerminate: TDownloadProc read FOnTerminate write FOnTerminate; property OnProgress: TDownloadProc read FOnProgress write FOnProgress; property OnError: TDownloadProc read FOnError write FOnError; property OnNewDownload: TDownloadProc read FOnNewDownload write FOnNewDownload; end; TStateChangeEvent = procedure(Sender: TObject; var State: TDownloadResult) of object; TDownloadManager = class(TComponent) private FOnCreate: TNotifyEvent; FOnDestroy: TNotifyEvent; FOnNextFile: TNotifyEvent; FOnNewDownload: TNotifyEvent; FOnProgress: TNotifyEvent; FOnError: TNotifyEvent; FOnStartDownload: TNotifyEvent; FOnEndDownload: TNotifyEvent; FOnCalcSpeeds: TNotifyEvent; FOnGetSize: TNotifyEvent; FOnStateChange: TStateChangeEvent; FOnCancelFile: TNotifyEvent; FOnCancelAll: TNotifyEvent; FDownloadList: TStringList; FErrorList: TStringList; FDwlLoop: TDownloadListLoop; FDestination: string; FTotalSize: LongInt; FFileSize: LongInt; FTotalDownloadedSize: LongInt; FDwlSizeSum: LongInt; FFileDownloadedSize: LongInt; FPause: Boolean; FDownloadIndex: Integer; FDownloader: TDownloader; FState, FOldState: TDownloadResult; FCurrentSpeed: LongInt; // calculated every seconds in B/s FTotalTime: LongInt; //in seconds FFileTime: LongInt; FFileMaxSpeed: LongInt; FFileMinSpeed: LongInt; FFileAverageSpeed: LongInt; FTotalMaxSpeed: LongInt; FTotalMinSpeed: LongInt; FTotalAverageSpeed: LongInt; FTimer: TTimer; // used to calculate speeds FHttpProxy: TProxy; FFtpProxy: TProxy; FFtpUser: string; FFtpPassword: string; FDownloaderStatusText: string; procedure ResetFileVars; procedure DwlNextFile; procedure DwlTerminate; procedure DwlProgress; procedure DwlError; procedure DwlNewFile; procedure CheckState; procedure SetDwlList(Value: TStringList); procedure SetDownloadIndex(Value: Integer); procedure TimerExecute(Sender: TObject); procedure DownloadFeedback(Txt: string); public constructor Create(AOwner: TComponent); virtual; destructor Destroy; override; procedure Cancel(OnlyCurrent: Boolean); procedure StartDownload; function GetFileSize(Index: integer): LongInt; procedure GetTotalSize; procedure Pause; property DownloadList: TStringList read FDownloadList write SetDwlList; property DownloadIndex: Integer read FDownloadIndex write SetDownLoadIndex; property ErrorList: TStringList read FErrorList; property Destination: string read FDestination write FDestination; property Status: TDownloadResult read FState; property TotalSize: LongInt read FTotalSize; property CurrentSpeed: LongInt read FCurrentSpeed; property FileDownloadedSize: LongInt read FFileDownloadedSize; property FileMinSpeed: LongInt read FFileMinSpeed; property FileMaxSpeed: LongInt read FFileMaxSpeed; property FileAverageSpeed: LongInt read FFileAverageSpeed; property TotalDownloadedSize: LongInt read FTotalDownloadedSize; property TotalMinSpeed: LongInt read FTotalMinSpeed; property TotalMaxSpeed: LongInt read FTotalMaxSpeed; property TotalAverageSpeed: LongInt read FTotalAverageSpeed; property DownloaderStatus: string read FDownloaderStatusText; property HttpProxy: TProxy read FHttpProxy write FHttpProxy; property FtpProxy: TProxy read FFtpProxy write FFtpProxy; property FtpUser: string read FFtpUser write FFtpUser; property FtpPassword: string read FFtpPassword write FFtpPassword; property OnCreate: TNotifyEvent read FOnCreate write FOnCreate; property OnDestroy: TNotifyEvent read FOnDestroy write FOnDestroy; property OnNextFile: TNotifyEvent read FOnNextFile write FOnNextFile; property OnProgress: TNotifyEvent read FOnProgress write FOnProgress; property OnNewDownload: TNotifyEvent read FOnNewDownload write FOnNewDownload; property OnError: TNotifyEvent read FOnError write FOnError; property OnCalcSpeeds: TNotifyEvent read FOnCalcSpeeds write FOnCalcSpeeds; property OnStartDownload: TNotifyEvent read FOnStartDownload write FOnStartDownload; property OnEndDownload: TNotifyEvent read FOnEndDownload write FOnEndDownload; property OnGetSize: TNotifyEvent read FOnGetSize write FOnGetSize; property OnStateChange: TStateChangeEvent read FOnStateChange write FOnStateChange; property OnCancelFile: TNotifyEvent read FOnCancelFile write FOnCancelFile; property OnCancelAll: TNotifyEvent read FOnCancelAll write FOnCancelAll; end; implementation uses uDebug, uStrings; constructor TDownloadListLoop.Create; begin FreeOnTerminate := True; inherited Create(True); end; procedure TDownloadListLoop.Execute; var I, J: integer; begin PrintLnDbg(rsDebugDownloadListLoopStarted); try while PDownloadIndex^ <= PDownloadList^.Count - 1 do begin if Terminated then Break; if PDownloadIndex^ < 0 then begin PrintLnDbg(Format(rsErrorDownloadIndex, [PDownloadIndex^]), vlLow); Exit; end; if Assigned(FOnNewDownload) then Synchronize(FOnNewDownload); PDownloader^.StartDownload; repeat if Assigned(FOnProgress) then Synchronize(FOnProgress); until (PDownloader^.State = drDone) or (PDownloader^.State = drError) or (PDownloader^.State = drCanceled); if PDownloader^.State = drError then if Assigned(FOnError) then Synchronize(FOnError); //downloading again needs manual index management if PDownloadIndex^ < 0 then //in case of cancellation Break; if Assigned(FOnNextFile) then Synchronize(FOnNextFile) //manual management of download index else Inc(PDownloadIndex^); //or automatic (forced) end; if Assigned(FOnTerminate) then Synchronize(FOnTerminate); except on E: Exception do heHandleException.ThreadException(E, Self); end; PrintLnDbg(rsDebugDownloadListLoopStopped); end; constructor TDownloadManager.Create(AOwner: TComponent); begin inherited Create(AOwner); FDownloader := TDownloader.Create; FDownloadList := TStringList.Create; FErrorList := TStringList.Create; FTimer := TTimer.Create(nil); FTimer.Interval := 1000; FTimer.Enabled := False; FTimer.OnTimer := @TimerExecute; FFtpPassword := ''; FFtpUser := ''; FHttpProxy.Address := ''; FHttpProxy.Port := 0; FHttpProxy.User := ''; FHttpProxy.Pass := ''; FFtpProxy.Address := ''; FFtpProxy.Port := 0; FFtpProxy.User := ''; FFtpProxy.Pass := ''; FDownloadIndex := -1; FDestination := ''; ResetFileVars; FTotalDownloadedSize := 0; FTotalSize := 0; FTotalTime := 0; FTotalAverageSpeed := 0; FTotalMaxSpeed := 0; FTotalMinSpeed := 0; FCurrentSpeed := 0; FState := drNone; FDownloader.OnFeedback := @DownloadFeedback; FDownloaderStatusText := ''; if Assigned(FOnCreate) then FOnCreate(Self); end; procedure TDownloadManager.DownloadFeedback(Txt: string); begin FDownloaderStatusText := Txt; {$IFDEF Debug} PrintLnDbg(rsDebugDownloadFeedback + Txt, vlHigh); {$ENDIF} end; destructor TDownloadManager.Destroy; begin if Assigned(FOnDestroy) then FOnDestroy(Self); if (FState = drDownloading) or (FState = drPaused) then FDownloader.Cancel; FDownloader.Free; FDownloadList.Free; FTimer.Free; inherited Destroy; end; procedure TDownloadManager.CheckState; begin if FState <> FDownloader.State then begin FState := FDownloader.State; if Assigned(FOnStateChange) then FOnStateChange(Self, FState); end; end; procedure TDownloadManager.ResetFileVars; begin FFileTime := 0; FFileSize := 0; FFileAverageSpeed := 0; FFileMaxSpeed := 0; FFileMinSpeed := 0; FFileDownloadedSize := 0; end; procedure TDownloadManager.DwlNextFile; begin CheckState; if (FState = drDone) or (FState = drError) then begin FTimer.Enabled := False; FDwlSizeSum := FDwlSizeSum + FFileSize; if Assigned(FOnNextFile) then FOnNextFile(Self) else Inc(FDownloadIndex); end; end; procedure TDownloadManager.DwlTerminate; begin CheckState; ResetFileVars; if Assigned(FOnEndDownload) then FOnEndDownload(Self); FDwlLoop.OnError := nil; FDwlLoop.OnNewDownload := nil; FDwlLoop.OnNextFile := nil; FDwlLoop.OnProgress := nil; FDwlLoop.OnTerminate := nil; FDownloaderStatusText := ''; end; procedure TDownloadManager.DwlProgress; begin CheckState; FFileDownloadedSize := FDownloader.DownloadedSize; FTotalDownloadedSize := FDwlSizeSum + FFileDownloadedSize; if Assigned(FOnProgress) then FOnProgress(Self); end; procedure TDownloadManager.DwlError; begin FErrorList.Add(FDownloadList[FDownloadIndex]); if Assigned(FOnError) then FOnError(Self); FDownloaderStatusText := ''; end; procedure TDownloadManager.DwlNewFile; begin ResetFileVars; if Assigned(FOnNewDownload) then FOnNewDownload(Self); FDownloader.URL := FDownloadList[FDownloadIndex]; FFileSize := FDownloader.FileSize; FTimer.Enabled := True; FDownloader.SaveToFile := FDestination; FDownloader.FtpUserName := FFtpUser; FDownloader.FtpPassword := FFtpPassword; end; procedure TDownloadManager.SetDwlList(Value: TStringList); begin if Value <> FDownloadList then if (FState = drDownloading) or (FState = drPaused) then begin raise Exception.Create('Can''t change of StringList while downloading.'); Exit; end else FDownloadList := Value; end; procedure TDownloadManager.SetDownloadIndex(Value: integer); begin if (FState = drDownloading) or (FState = drPaused) then begin raise Exception.Create('Can''t change index while downloading.'); Exit; end; FDownloadIndex := Value; end; procedure TDownloadManager.Cancel(OnlyCurrent: Boolean); begin if (FState = drDownloading) or (FState = drPaused) then if OnlyCurrent then begin FDownloader.Cancel; if Assigned(FOnCancelFile) then FOnCancelFile(Self); end else begin FDownloadIndex := -1; FDwlLoop.OnError := nil; FDwlLoop.OnNewDownload := nil; FDwlLoop.OnNextFile := nil; FDwlLoop.OnProgress := nil; FDwlLoop.OnTerminate := nil; FDownloader.Cancel; end; FDownloaderStatusText := ''; end; procedure TDownloadManager.StartDownload; begin FTotalDownloadedSize := 0; FTotalSize := 0; FTotalTime := 0; FTotalAverageSpeed := 0; FTotalMaxSpeed := 0; FTotalMinSpeed := 0; FCurrentSpeed := 0; if DownloadList.Count > 0 then begin FDownloader.HttpProxy := FHttpProxy.Address; FDownloader.HttpProxyPass := FHttpProxy.Pass; FDownloader.HttpProxyPort := IntToStr(FHttpProxy.Port); FDownloader.HttpProxyUser := FHttpProxy.User; FDownloader.FtpFwHost := FFtpProxy.Address; FDownloader.FtpFwPort := IntToStr(FFtpProxy.Port); FDownloader.FtpFwUserName := FFtpProxy.User; FDownloader.FtpFwPassword := FFtpProxy.Pass; FErrorList.Clear; FDwlLoop := TDownloadListLoop.Create; FDwlLoop.OnError := @DwlError; FDwlLoop.OnNewDownload := @DwlNewFile; FDwlLoop.OnNextFile := @DwlNextFile; FDwlLoop.OnProgress := @DwlProgress; FDwlLoop.OnTerminate := @DwlTerminate; FDwlLoop.PDownloader := @FDownloader; FDwlLoop.PDownloadIndex := @FDownloadIndex; FDwlLoop.PDownloadList := @FDownloadList; if Assigned(FOnStartDownload) then FOnStartDownload(Self) else FDownloadIndex := 0; FDwlLoop.PDownloadIndex := @FDownloadIndex; FTimer.Enabled := True; FDwlLoop.Execute; end; end; function TDownloadManager.GetFileSize(Index: Integer): LongInt; begin FDownloader.URL := FDownloadList[Index]; Result := FDownloader.FileSize; end; procedure TDownloadManager.GetTotalSize; var I, Size: Integer; begin FTotalSize := 0; for I := 0 to FDownloadList.Count - 1 do try Size := GetFileSize(I); if (Size < 0) and Assigned(FOnError) then FOnError(Self); FTotalSize := FTotalSize + Size; if Assigned(FOnGetSize) then FOnGetSize(Self); except PrintLnDbg(Format(rsErrorCantGetFileSze, [FDownloadList[I]]), vlLow); end; end; procedure TDownloadManager.Pause; begin CheckState; FDownloader.Pause; FTimer.Enabled := FDownloader.State <> drDownloading; CheckState; end; procedure TDownloadManager.TimerExecute(Sender: TObject); begin Inc(FTotalTime); Inc(FFileTime); //do the stuff if Assigned(FOnCalcSpeeds) then FOnCalcSpeeds(Self); end; end.