Inno Setup torrent download implementation

我与影子孤独终老i 提交于 2019-11-29 08:49:00

Just run the aria2c, redirect its output to a file and poll the file contents for progress of the download.

It's actually very similar to my solution for this answer:
Inno Setup - Make Inno Setup Installer report its installation progress status to master installer


#define TorrentMagnet "magnet:..."

[Files]
Source: aria2c.exe; Flags: dontcopy

[Code]

function BufferToAnsi(const Buffer: string): AnsiString;
var
  W: Word;
  I: Integer;
begin
  SetLength(Result, Length(Buffer) * 2);
  for I := 1 to Length(Buffer) do
  begin
    W := Ord(Buffer[I]);
    Result[(I * 2)] := Chr(W shr 8); { high byte }
    Result[(I * 2) - 1] := Chr(Byte(W)); { low byte }
  end;
end;

function SetTimer(
  Wnd: LongWord; IDEvent, Elapse: LongWord; TimerFunc: LongWord): LongWord;
  external 'SetTimer@user32.dll stdcall';
function KillTimer(hWnd: LongWord; uIDEvent: LongWord): BOOL;
  external 'KillTimer@user32.dll stdcall';

var
  ProgressPage: TOutputProgressWizardPage;
  ProgressFileName: string;

procedure UpdateProgressProc(H: LongWord; Msg: LongWord; Event: LongWord; Time: LongWord);
var
  S: AnsiString;
  I: Integer;
  L: Integer;
  P: Integer;
  Max: Integer;
  Progress: string;
  Buffer: string;
  Stream: TFileStream;
  Transferred: string;
  Percent: Integer;
  Found: Boolean;
begin
  Found := False;
  try
    { Need shared read as the output file is locked for writting, }
    { so we cannot use LoadStringFromFile }
    Stream := TFileStream.Create(ProgressFileName, fmOpenRead or fmShareDenyNone);
    try
      L := Stream.Size;
      Max := 100*2014;
      if L > Max then
      begin
        Stream.Position := L - Max;
        L := Max;
      end;
      SetLength(Buffer, (L div 2) + (L mod 2));
      Stream.ReadBuffer(Buffer, L);
      S := BufferToAnsi(Buffer);
    finally
      Stream.Free;
    end;

    if S = '' then
    begin
      Log(Format('Progress file %s is empty', [ProgressFileName]));
    end;
  except
    Log(Format('Failed to read progress from file %s', [ProgressFileName]));
  end;

  if S <> '' then
  begin
    P := Pos('[#', S);
    if P = 0 then
    begin
      Log('Not found any progress line');
    end
      else
    begin
      repeat
        Delete(S, 1, P - 1);
        P := Pos(']', S);
        Progress := Copy(S, 2, P - 2);
        Delete(S, 1, P);
        P := Pos('[#', S);
      until (P = 0);

      Log(Format('Found progress line: %s', [Progress]));
      P := Pos(' ', Progress);
      if P > 0 then
      begin
        Log('A');
        Delete(Progress, 1, P);
        P := Pos('(', Progress);
        if P > 0 then
        begin
          Log('b');
          Transferred := Copy(Progress, 1, P - 1);
          Delete(Progress, 1, P);
          P := Pos('%)', Progress);
          if P > 0 then
          begin
            Log('c');
            Percent := StrToIntDef(Copy(Progress, 1, P - 1), -1);
            if Percent >= 0 then
            begin
              Log(Format('Transferred: %s, Percent: %d', [Transferred, Percent]));
              ProgressPage.SetProgress(Percent, 100);
              ProgressPage.SetText(Format('Transferred: %s', [Transferred]), '');
              Found := True;
            end;
          end;      
        end;
      end;
    end;
  end;

  if not Found then
  begin
    Log('No new data found');
    { no new progress data, at least pump the message queue }
    ProgressPage.SetProgress(ProgressPage.ProgressBar.Position, 100);
  end;
end;

function PrepareToInstall(var NeedsRestart: Boolean): String;
var
  TorrentDownloaderPath: string;
  TempPath: string;
  CommandLine: string;
  Timer: LongWord;
  InstallError: string;
  ResultCode: Integer;
  S: AnsiString;
begin
  ExtractTemporaryFile('aria2c.exe');

  ProgressPage := CreateOutputProgressPage('Torrent download', 'Downloading torrent...');
  ProgressPage.SetProgress(0, 100);
  ProgressPage.Show;
  try
    Timer := SetTimer(0, 0, 250, CreateCallback(@UpdateProgressProc));

    TempPath := ExpandConstant('{tmp}');
    TorrentDownloaderPath := TempPath + '\aria2c.exe';
    ProgressFileName := ExpandConstant('{tmp}\progress.txt');
    Log(Format('Expecting progress in %s', [ProgressFileName]));
    CommandLine :=
      Format('"%s" "%s" > "%s"', [
        TorrentDownloaderPath, '{#TorrentMagnet}', ProgressFileName]);
    Log(Format('Executing: %s', [CommandLine]));
    CommandLine := Format('/C "%s"', [CommandLine]);
    if not Exec(ExpandConstant('{cmd}'), CommandLine, TempPath, SW_HIDE,
                ewWaitUntilTerminated, ResultCode) then
    begin
      Result := 'Cannot start torrent download';
    end
      else
    if ResultCode <> 0 then
    begin
      LoadStringFromFile(ProgressFileName, S);
      Result := Format('Torrent download failed with code %d', [ResultCode]);
      Log(Result);
      Log('Output: ' + S);
    end;
  finally
    { Clean up }
    KillTimer(0, Timer);
    ProgressPage.Hide;
    DeleteFile(ProgressFileName);
  end;
end;

For CreateCallback function, you need Inno Setup 6. If you are stuck with Inno Setup 5, you can use WrapCallback function from InnoTools InnoCallback library.


The BufferToAnsi and its use is based on:
Inno Setup LoadStringFromFile fails when file is open in another process


易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!