Inno Setup torrent download implementation

前端 未结 1 625
生来不讨喜
生来不讨喜 2020-12-18 15:40

I am currently using Inno Download Plugin to download files for my installer, the biggest problem with this is that it faila to download the file correctly. Because of many

相关标签:
1条回答
  • 2020-12-18 16:35

    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


    0 讨论(0)
提交回复
热议问题