├── .gitattributes
├── .gitignore
├── Medusa.iss
├── README.md
├── appveyor.yml
├── assets
├── Wizard.bmp
├── WizardSmall.bmp
├── github.ico
└── medusa.ico
├── idp
├── idp.iss
└── unicode
│ ├── idp.dll
│ └── idplang
│ ├── ChineseSimplified.iss
│ ├── belarusian.iss
│ ├── brazilianPortuguese.iss
│ ├── bulgarian.iss
│ ├── czech.iss
│ ├── default.iss
│ ├── finnish.iss
│ ├── french.iss
│ ├── german.iss
│ ├── hungarian.iss
│ ├── italian.iss
│ ├── polish.iss
│ ├── russian.iss
│ ├── slovak.iss
│ └── spanish.iss
├── run_installer.cmd
├── seed.ini
├── seed_data.cmd
└── utils
├── 7za-License.txt
├── 7za.exe
├── nssm-License.txt
├── nssm32.exe
└── nssm64.exe
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 | * eol=lf
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | Output/
2 | repo/
3 | files/
4 | .vscode/
5 |
--------------------------------------------------------------------------------
/Medusa.iss:
--------------------------------------------------------------------------------
1 | #include <./idp/idp.iss>
2 |
3 | #define MedusaInstallerVersion "v0.6"
4 |
5 | #define AppId "{{991BED37-186A-5451-9E77-C3DCE91D56C7}"
6 | #define AppName "Medusa"
7 | #define AppPublisher "Medusa"
8 | #define AppURL "https://github.com/pymedusa/Medusa"
9 | #define AppServiceName AppName
10 | #define AppServiceDescription "Automatic Video Library Manager for TV Shows"
11 | #define ServiceStartIcon "{group}\Start " + AppName + " Service"
12 | #define ServiceStopIcon "{group}\Stop " + AppName + " Service"
13 | #define ServiceEditIcon "{group}\Edit " + AppName + " Service"
14 |
15 | #define DefaultPort 8081
16 |
17 | #define InstallerVersion 10006
18 | #define InstallerSeedUrl "https://raw.githubusercontent.com/pymedusa/MedusaInstaller/master/seed.ini"
19 | #define AppRepoUrl "https://github.com/pymedusa/Medusa.git"
20 | #define AppSize 246784000
21 |
22 | [Setup]
23 | AppId={#AppId}
24 | AppName={#AppName}
25 | AppVerName={#AppName}
26 | AppPublisher={#AppPublisher}
27 | AppPublisherURL={#AppURL}
28 | AppSupportURL={#AppURL}
29 | AppUpdatesURL={#AppURL}
30 | DefaultDirName={sd}\{#AppName}
31 | DefaultGroupName={#AppName}
32 | AllowNoIcons=yes
33 | DisableWelcomePage=no
34 | DisableDirPage=no
35 | DisableProgramGroupPage=no
36 | ArchitecturesInstallIn64BitMode=x64
37 | OutputBaseFilename={#AppName}Installer
38 | SolidCompression=yes
39 | UninstallDisplayIcon={app}\Installer\medusa.ico
40 | UninstallFilesDir={app}\Installer
41 | SetupIconFile=assets\medusa.ico
42 | WizardImageFile=assets\Wizard.bmp
43 | WizardSmallImageFile=assets\WizardSmall.bmp
44 | WizardStyle=modern
45 | WizardResizable=no
46 |
47 | [Types]
48 | Name: "full"; Description: "Full installation"
49 | Name: "custom"; Description: "Custom installation"; Flags: iscustom
50 |
51 | [Components]
52 | Name: "application"; Description: {#AppName}; ExtraDiskSpaceRequired: {#AppSize}; Types: "full custom"; Flags: fixed
53 | Name: "python"; Description: "Python"; ExtraDiskSpaceRequired: 36000000; Types: "full custom"
54 | Name: "git"; Description: "Git"; ExtraDiskSpaceRequired: 55000000; Types: "full custom"
55 |
56 | [Tasks]
57 | Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
58 |
59 | [Files]
60 | Source: "utils\7za.exe"; Flags: dontcopy
61 | Source: "assets\medusa.ico"; DestDir: "{app}\Installer"
62 | Source: "assets\github.ico"; DestDir: "{app}\Installer"
63 | Source: "utils\nssm32.exe"; DestDir: "{app}\Installer"; DestName: "nssm.exe"; Check: not Is64BitInstallMode
64 | Source: "utils\nssm64.exe"; DestDir: "{app}\Installer"; DestName: "nssm.exe"; Check: Is64BitInstallMode
65 |
66 | [Dirs]
67 | Name: "{app}\Data"
68 |
69 | [Icons]
70 | ;Desktop
71 | Name: "{commondesktop}\{#AppName}"; Filename: "http://localhost:{code:GetWebPort}/"; IconFilename: "{app}\Installer\medusa.ico"; Tasks: desktopicon
72 | ;Start menu
73 | Name: "{group}\{#AppName}"; Filename: "http://localhost:{code:GetWebPort}/"; IconFilename: "{app}\Installer\medusa.ico"
74 | Name: "{group}\{#AppName} on GitHub"; Filename: "{#AppURL}"; IconFilename: "{app}\Installer\github.ico"; Flags: excludefromshowinnewinstall
75 | Name: "{#ServiceStartIcon}"; Filename: "{app}\Installer\nssm.exe"; Parameters: "start ""{#AppServiceName}"""; Flags: excludefromshowinnewinstall
76 | Name: "{#ServiceStopIcon}"; Filename: "{app}\Installer\nssm.exe"; Parameters: "stop ""{#AppServiceName}"""; Flags: excludefromshowinnewinstall
77 | Name: "{#ServiceEditIcon}"; Filename: "{app}\Installer\nssm.exe"; Parameters: "edit ""{#AppServiceName}"""; AfterInstall: ModifyServiceLinks; Flags: excludefromshowinnewinstall
78 |
79 | [Run]
80 | ;Medusa
81 | Filename: "{code:GetGitExecutable}"; Parameters: "clone ""{param:LOCALREPO|{#AppRepoUrl}}"" ""{app}\{#AppName}"" --branch {code:GetBranch}"; StatusMsg: "Installing {#AppName}..."
82 | ;Service
83 | Filename: "{app}\Installer\nssm.exe"; Parameters: "start ""{#AppServiceName}"""; Flags: runhidden; BeforeInstall: CreateService; StatusMsg: "Starting {#AppName} service..."
84 | ;Open
85 | Filename: "http://localhost:{code:GetWebPort}/"; Flags: postinstall shellexec; Description: "Open {#AppName} in browser"
86 |
87 | [UninstallRun]
88 | ;Service
89 | Filename: "{app}\Installer\nssm.exe"; Parameters: "remove ""{#AppServiceName}"" confirm"; Flags: runhidden
90 |
91 | [UninstallDelete]
92 | Type: filesandordirs; Name: "{app}\Python"
93 | Type: filesandordirs; Name: "{app}\Git"
94 | Type: filesandordirs; Name: "{app}\Installer"
95 | Type: filesandordirs; Name: "{app}\{#AppName}"
96 | Type: dirifempty; Name: "{app}"
97 |
98 | [Messages]
99 | WelcomeLabel2=This will install [name/ver] on your computer.%n%nYou will need Internet connectivity in order to download the required packages.
100 | SelectComponentsLabel2=Select the components you want to install; clear the components you do not want to install. Click Next when you are ready to continue. \
101 | %n%nPlease note: \
102 | %n By default, this installer provides the dependencies required to run [name]. \
103 | %n If you have Git or Python already installed on your system, and you would prefer to use those versions, \
104 | %n you may deselect the dependencies you already have and provide a path to the already-installed versions.
105 | AboutSetupNote=MedusaInstaller {#MedusaInstallerVersion}
106 | BeveledLabel=MedusaInstaller {#MedusaInstallerVersion}
107 |
108 | [INI]
109 | Filename: "{app}\Data\config.ini"; Section: "General"; Key: "web_port"; String: "{code:GetWebPort}"; Flags: createkeyifdoesntexist
110 |
111 | [Code]
112 | type
113 | TDependency = record
114 | Name: String;
115 | URL: String;
116 | Filename: String;
117 | Size: Integer;
118 | SHA1: String;
119 | Version: String;
120 | end;
121 |
122 | TBrowseOptions = record
123 | Caption: TNewStaticText;
124 | Path: TNewStaticText;
125 | Browse: TNewButton;
126 | end;
127 |
128 | TInstallOptions = record
129 | Page: TWizardPage;
130 | WebPort: TNewEdit;
131 | Branch: TNewCheckListBox;
132 | Python: TBrowseOptions;
133 | Git: TBrowseOptions;
134 | end;
135 |
136 | IShellLinkW = interface(IUnknown)
137 | '{000214F9-0000-0000-C000-000000000046}'
138 | procedure Dummy;
139 | procedure Dummy2;
140 | procedure Dummy3;
141 | function GetDescription(pszName: String; cchMaxName: Integer): HResult;
142 | function SetDescription(pszName: String): HResult;
143 | function GetWorkingDirectory(pszDir: String; cchMaxPath: Integer): HResult;
144 | function SetWorkingDirectory(pszDir: String): HResult;
145 | function GetArguments(pszArgs: String; cchMaxPath: Integer): HResult;
146 | function SetArguments(pszArgs: String): HResult;
147 | function GetHotkey(var pwHotkey: Word): HResult;
148 | function SetHotkey(wHotkey: Word): HResult;
149 | function GetShowCmd(out piShowCmd: Integer): HResult;
150 | function SetShowCmd(iShowCmd: Integer): HResult;
151 | function GetIconLocation(pszIconPath: String; cchIconPath: Integer;
152 | out piIcon: Integer): HResult;
153 | function SetIconLocation(pszIconPath: String; iIcon: Integer): HResult;
154 | function SetRelativePath(pszPathRel: String; dwReserved: DWORD): HResult;
155 | function Resolve(Wnd: HWND; fFlags: DWORD): HResult;
156 | function SetPath(pszFile: String): HResult;
157 | end;
158 |
159 | IPersist = interface(IUnknown)
160 | '{0000010C-0000-0000-C000-000000000046}'
161 | function GetClassID(var classID: TGUID): HResult;
162 | end;
163 |
164 | IPersistFile = interface(IPersist)
165 | '{0000010B-0000-0000-C000-000000000046}'
166 | function IsDirty: HResult;
167 | function Load(pszFileName: String; dwMode: Longint): HResult;
168 | function Save(pszFileName: String; fRemember: BOOL): HResult;
169 | function SaveCompleted(pszFileName: String): HResult;
170 | function GetCurFile(out pszFileName: String): HResult;
171 | end;
172 |
173 | IShellLinkDataList = interface(IUnknown)
174 | '{45E2B4AE-B1C3-11D0-B92F-00A0C90312E1}'
175 | procedure Dummy;
176 | procedure Dummy2;
177 | procedure Dummy3;
178 | function GetFlags(out dwFlags: DWORD): HResult;
179 | function SetFlags(dwFlags: DWORD): HResult;
180 | end;
181 |
182 | const
183 | MinPort = 1;
184 | MaxPort = 65535;
185 | WM_CLOSE = $0010;
186 | GENERIC_WRITE = $40000000;
187 | GENERIC_READ = $80000000;
188 | OPEN_EXISTING = 3;
189 | INVALID_HANDLE_VALUE = $FFFFFFFF;
190 | SLDF_RUNAS_USER = $00002000;
191 | CLSID_ShellLink = '{00021401-0000-0000-C000-000000000046}';
192 |
193 | var
194 | // This lets AbortInstallation() terminate setup without prompting the user
195 | CancelWithoutPrompt: Boolean;
196 | ErrorMessage, LocalFilesDir: String;
197 | SeedDownloadPageId, DependencyDownloadPageId: Integer;
198 | PythonDep, GitDep: TDependency;
199 | InstallDepPage: TOutputProgressWizardPage;
200 | InstallOptions: TInstallOptions;
201 | // Uninstall variables
202 | UninstallRemoveData: Boolean;
203 |
204 | // Import some Win32 functions
205 | function CreateFile(
206 | lpFileName: String;
207 | dwDesiredAccess: LongWord;
208 | dwSharedMode: LongWord;
209 | lpSecurityAttributes: LongWord;
210 | dwCreationDisposition: LongWord;
211 | dwFlagsAndAttributes: LongWord;
212 | hTemplateFile: LongWord): LongWord;
213 | external 'CreateFileW@kernel32.dll stdcall';
214 |
215 | function CloseHandle(hObject: LongWord): Boolean;
216 | external 'CloseHandle@kernel32.dll stdcall';
217 |
218 | procedure AbortInstallation(ErrorMessage: String);
219 | begin
220 | MsgBox(ErrorMessage + #13#10#13#10 'Setup will now terminate.', mbError, 0)
221 | CancelWithoutPrompt := True
222 | PostMessage(WizardForm.Handle, WM_CLOSE, 0, 0);
223 | end;
224 |
225 | procedure CheckInstallerVersion(SeedFile: String);
226 | var
227 | InstallerVersion, CurrentVersion: Integer;
228 | DownloadUrl: String;
229 | begin
230 | InstallerVersion := StrToInt(ExpandConstant('{#InstallerVersion}'))
231 |
232 | CurrentVersion := GetIniInt('Installer', 'Version', 0, 0, MaxInt, SeedFile)
233 |
234 | if CurrentVersion = 0 then begin
235 | AbortInstallation('Unable to parse configuration.')
236 | end;
237 |
238 | if CurrentVersion > InstallerVersion then begin
239 | DownloadUrl := GetIniString('Installer', 'DownloadUrl', ExpandConstant('{#AppURL}'), SeedFile)
240 | AbortInstallation(ExpandConstant('This is an old version of the {#AppName} installer. Please get the latest version at:') + #13#10#13#10 + DownloadUrl)
241 | end;
242 | end;
243 |
244 | function GetDependencyVersion(Dependency: TDependency): String;
245 | var
246 | StartIndex: Integer;
247 | begin
248 | Result := Dependency.Filename;
249 | // Handle Git dependency
250 | if Pos('git', Lowercase(Dependency.Name)) <> 0 then begin
251 | // --> MinGit-2.22.0-32-bit.zip
252 | // --> MinGit-2.22.0-64-bit.zip
253 | StartIndex := Pos('-', Result) + 1;
254 | Result := Copy(Result, StartIndex, Length(Result));
255 | // <-- 2.22.0-64-bit.zip
256 | StartIndex := Pos('-', Result);
257 | Delete(Result, StartIndex, Length(Result));
258 | // <-- 2.22.0
259 | // Handle Python dependency
260 | end else if Pos('python', Lowercase(Dependency.Name)) <> 0 then begin
261 | // --> pythonx86.3.7.3.nupkg
262 | // --> python.3.7.3.nupkg
263 | StartIndex := Pos('.', Result) + 1;
264 | Result := Copy(Result, StartIndex, Length(Result));
265 | // <-- 3.7.3.nupkg
266 | StartIndex := Pos('nupkg', Result) - 1;
267 | Delete(Result, StartIndex, Length(Result));
268 | // <-- 3.7.3
269 | end else begin
270 | Result := '';
271 | end;
272 | end;
273 |
274 | procedure UpdateComponentsPageDependencyVersions();
275 | var
276 | Idx: Integer;
277 | Version: String;
278 | begin
279 | for Idx := 0 to WizardForm.ComponentsList.Items.Count - 1 do
280 | begin
281 | Version := '';
282 |
283 | if Pos('python', Lowercase(WizardForm.ComponentsList.ItemCaption[Idx])) <> 0 then begin
284 | Version := ' (v' + PythonDep.Version + ')';
285 | end;
286 |
287 | if Pos('git', Lowercase(WizardForm.ComponentsList.ItemCaption[Idx])) <> 0 then begin
288 | Version := ' (v' + GitDep.Version + ')';
289 | end;
290 |
291 | WizardForm.ComponentsList.ItemCaption[Idx] := WizardForm.ComponentsList.ItemCaption[Idx] + Version;
292 | end;
293 | end;
294 |
295 | procedure ParseDependency(var Dependency: TDependency; Name, SeedFile: String);
296 | begin
297 | Dependency.Name := Name;
298 | Dependency.URL := GetIniString(Name, 'url', '', SeedFile)
299 | Dependency.Filename := GetIniString(Name, 'filename', '', SeedFile)
300 | Dependency.Size := GetIniInt(Name, 'size', 0, 0, MaxInt, SeedFile)
301 | Dependency.SHA1 := GetIniString(Name, 'sha1', '', SeedFile)
302 | Dependency.Version := '';
303 |
304 | if (Dependency.URL = '') or (Dependency.Size = 0) or (Dependency.SHA1 = '') then begin
305 | AbortInstallation('Error parsing dependency information for ' + Name + '.')
306 | end;
307 |
308 | // If a filename was not supplied, use the last part of the URL as the filename
309 | if Dependency.Filename = '' then begin
310 | Dependency.Filename := Dependency.URL
311 | while Pos('/', Dependency.Filename) <> 0 do begin
312 | Delete(Dependency.Filename, 1, Pos('/', Dependency.Filename))
313 | end;
314 | end;
315 |
316 | Dependency.Version := GetDependencyVersion(Dependency);
317 | end;
318 |
319 | procedure ParseSeedFile();
320 | var
321 | SeedFile: String;
322 | Arch: String;
323 | begin
324 | SeedFile := ExpandConstant('{tmp}\installer.ini')
325 |
326 | // Make sure we're running the latest version of the installer
327 | CheckInstallerVersion(SeedFile)
328 |
329 | if Is64BitInstallMode then
330 | Arch := 'x64'
331 | else
332 | Arch := 'x86';
333 |
334 | ParseDependency(PythonDep, 'Python.' + Arch, SeedFile)
335 | ParseDependency(GitDep, 'Git.' + Arch, SeedFile)
336 | end;
337 |
338 | procedure InitializeSeedDownload();
339 | var
340 | DownloadPage: TWizardPage;
341 | Seed: String;
342 | IsRemote: Boolean;
343 | begin
344 | IsRemote := True
345 |
346 | Seed := ExpandConstant('{param:SEED}')
347 | if (Lowercase(Copy(Seed, 1, 7)) <> 'http://') and (Lowercase(Copy(Seed, 1, 8)) <> 'https://') then begin
348 | if Seed = '' then begin
349 | Seed := ExpandConstant('{#InstallerSeedUrl}')
350 | end else begin
351 | if FileExists(Seed) then begin
352 | IsRemote := False
353 | end else begin
354 | MsgBox('Invalid SEED specified: ' + Seed, mbError, 0)
355 | Seed := ExpandConstant('{#InstallerSeedUrl}')
356 | end;
357 | end;
358 | end;
359 |
360 | if not IsRemote then begin
361 | FileCopy(Seed, ExpandConstant('{tmp}\installer.ini'), False)
362 | ParseSeedFile()
363 | UpdateComponentsPageDependencyVersions()
364 | end else begin
365 | // Download the installer seed INI file
366 | idpAddFile(Seed, ExpandConstant('{tmp}\installer.ini'))
367 |
368 | SeedDownloadPageId := idpCreateDownloadForm(wpWelcome)
369 | DownloadPage := PageFromID(SeedDownloadPageId)
370 | DownloadPage.Caption := 'Downloading Installer Configuration'
371 | DownloadPage.Description := 'Setup is downloading its configuration file...'
372 |
373 | idpConnectControls()
374 | end;
375 | end;
376 |
377 | function CheckFileInUse(Filename: String): Boolean;
378 | var
379 | FileHandle: LongWord;
380 | begin
381 | if not FileExists(Filename) then begin
382 | Result := False
383 | exit
384 | end;
385 |
386 | FileHandle := CreateFile(Filename, GENERIC_READ or GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0)
387 | if (FileHandle <> 0) and (FileHandle <> INVALID_HANDLE_VALUE) then begin
388 | CloseHandle(FileHandle)
389 | Result := False
390 | end else begin
391 | Result := True
392 | end;
393 | end;
394 |
395 | function GetPythonExecutable(Param: String): String;
396 | begin
397 | if WizardIsComponentSelected('python') then begin
398 | Result := ExpandConstant('{app}\Python\python.exe')
399 | end else begin
400 | Result := InstallOptions.Python.Path.Caption
401 | end;
402 | end;
403 |
404 | function GetGitExecutable(Param: String): String;
405 | begin
406 | if WizardIsComponentSelected('git') then begin
407 | Result := ExpandConstant('{app}\Git\cmd\git.exe')
408 | end else begin
409 | Result := InstallOptions.Git.Path.Caption
410 | end;
411 | end;
412 |
413 | function GetWebPort(Param: String): String;
414 | begin
415 | Result := InstallOptions.WebPort.Text
416 | end;
417 |
418 | function GetBranch(Param: String): String;
419 | var
420 | Idx: Integer;
421 | begin
422 | for Idx := 0 to InstallOptions.Branch.Items.Count - 1 do
423 | begin
424 | if InstallOptions.Branch.Checked[Idx] then begin
425 | Result := InstallOptions.Branch.ItemCaption[Idx]
426 | break;
427 | end;
428 | end;
429 | end;
430 |
431 | function ConfigureCustomPython(Python: String): String;
432 | var
433 | VirtualEnvPath: String;
434 | ResultCode: Integer;
435 | begin
436 | VirtualEnvPath := ExpandConstant('{app}\Python')
437 | Result := VirtualEnvPath + '\Scripts\python.exe'
438 |
439 | Exec(Python, '-m venv "'+VirtualEnvPath+'"', '', SW_HIDE, ewWaitUntilTerminated, ResultCode)
440 | if ResultCode = 0 then begin
441 | // Success using venv (Included in Python >= 3.3)
442 | exit;
443 | end;
444 |
445 | // Fall back to installing latest `virtualenv` using Pip and using that.
446 | // Some versions of Pip for Python 2.7 on Windows have an issue where having a progress bar can fail the install.
447 | Exec(Python, '-m pip install --progress-bar off --upgrade virtualenv', '', SW_HIDE, ewWaitUntilTerminated, ResultCode)
448 | if ResultCode = 0 then begin
449 | // Install/Upgrade successful
450 | Exec(Python, '-m virtualenv "'+VirtualEnvPath+'"', '', SW_HIDE, ewWaitUntilTerminated, ResultCode)
451 | if ResultCode = 0 then begin
452 | // Success using virtualenv (external package)
453 | exit;
454 | end;
455 | end;
456 |
457 | // Fall back to non-virtualenv (not recommended).
458 | Result := Python;
459 | MsgBox(
460 | 'Failed to configure custom Python:' #13#10#13#10 'Unable to install/upgrade or configure virtualenv.' + \
461 | #13#10 'Falling back to using the custom Python executable as-is (not recommended).',
462 | mbError, MB_OK
463 | );
464 | end;
465 |
466 | procedure CreateService();
467 | var
468 | PythonExecutable: String;
469 | GitPath: String;
470 | Nssm: String;
471 | ResultCode: Integer;
472 | OldProgressString: String;
473 | WindowsVersion: TWindowsVersion;
474 | begin
475 | PythonExecutable := GetPythonExecutable('')
476 | GitPath := ExtractFileDir(GetGitExecutable(''))
477 |
478 | OldProgressString := WizardForm.StatusLabel.Caption;
479 |
480 | // Python was not selected, let's isolate this install using v(irtual)env
481 | if not WizardIsComponentSelected('python') then begin
482 | WizardForm.StatusLabel.Caption := ExpandConstant('Configuring virtual environment for custom Python...');
483 | // Returns the path to the new virtual env's Python executable.
484 | PythonExecutable := ConfigureCustomPython(PythonExecutable);
485 | end;
486 |
487 | Nssm := ExpandConstant('{app}\Installer\nssm.exe')
488 | GetWindowsVersionEx(WindowsVersion);
489 |
490 | WizardForm.StatusLabel.Caption := ExpandConstant('Installing {#AppName} service...')
491 |
492 | // Keep extra quotes; See "Quoting issues" on https://nssm.cc/usage
493 | Exec(Nssm, ExpandConstant('install "{#AppServiceName}" "'+PythonExecutable+'" """{app}\{#AppName}\start.py""" --nolaunch --datadir="""{app}\Data"""'), '', SW_HIDE, ewWaitUntilTerminated, ResultCode)
494 | Exec(Nssm, ExpandConstant('set "{#AppServiceName}" AppDirectory "{app}\Data"'), '', SW_HIDE, ewWaitUntilTerminated, ResultCode)
495 | Exec(Nssm, ExpandConstant('set "{#AppServiceName}" Description "{#AppServiceDescription}"'), '', SW_HIDE, ewWaitUntilTerminated, ResultCode)
496 | Exec(Nssm, ExpandConstant('set "{#AppServiceName}" AppStopMethodSkip 6'), '', SW_HIDE, ewWaitUntilTerminated, ResultCode)
497 | Exec(Nssm, ExpandConstant('set "{#AppServiceName}" AppStopMethodConsole 20000'), '', SW_HIDE, ewWaitUntilTerminated, ResultCode)
498 | Exec(Nssm, ExpandConstant('set "{#AppServiceName}" AppEnvironmentExtra "PATH='+GitPath+';%PATH%"'), '', SW_HIDE, ewWaitUntilTerminated, ResultCode)
499 |
500 | if WindowsVersion.NTPlatform and (WindowsVersion.Major = 10) and (WindowsVersion.Minor = 0) and (WindowsVersion.Build > 14393) then begin
501 | Exec(Nssm, ExpandConstant('set "{#AppServiceName}" AppNoConsole 1'), '', SW_HIDE, ewWaitUntilTerminated, ResultCode)
502 | end;
503 |
504 | WizardForm.StatusLabel.Caption := OldProgressString;
505 | end;
506 |
507 | procedure StopService();
508 | var
509 | Nssm: String;
510 | ResultCode: Integer;
511 | Retries: Integer;
512 | OldProgressString: String;
513 | begin
514 | Retries := 30
515 |
516 | OldProgressString := UninstallProgressForm.StatusLabel.Caption;
517 | UninstallProgressForm.StatusLabel.Caption := ExpandConstant('Stopping {#AppName} service...')
518 |
519 | Nssm := ExpandConstant('{app}\Installer\nssm.exe')
520 | Exec(Nssm, ExpandConstant('stop "{#AppServiceName}"'), '', SW_HIDE, ewWaitUntilTerminated, ResultCode)
521 |
522 | while (Retries > 0) and (CheckFileInUse(Nssm)) do begin
523 | UninstallProgressForm.StatusLabel.Caption := ExpandConstant('Waiting for {#AppName} service to stop (') + IntToStr(Retries) + ')...'
524 | Sleep(1000)
525 | Retries := Retries - 1
526 | end;
527 |
528 | UninstallProgressForm.StatusLabel.Caption := OldProgressString;
529 | end;
530 |
531 | procedure CleanPython();
532 | var
533 | PythonPath: String;
534 | begin
535 | PythonPath := ExpandConstant('{app}\Python')
536 | DelTree(PythonPath + '\Tools', True, True, True)
537 | end;
538 |
539 | procedure InstallPython();
540 | var
541 | ResultCode: Integer;
542 | begin
543 | InstallDepPage.SetText('Installing Python...', '')
544 | Exec(ExpandConstant('{tmp}\7za.exe'), ExpandConstantEx('x "{tmp}\{filename}" -o"{app}" tools', 'filename', PythonDep.Filename), '', SW_HIDE, ewWaitUntilTerminated, ResultCode)
545 | RenameFile(ExpandConstant('{app}\tools'), ExpandConstant('{app}\Python'))
546 | CleanPython()
547 | InstallDepPage.SetProgress(InstallDepPage.ProgressBar.Position+1, InstallDepPage.ProgressBar.Max)
548 | end;
549 |
550 | procedure InstallGit();
551 | var
552 | ResultCode: Integer;
553 | begin
554 | InstallDepPage.SetText('Installing Git...', '')
555 | Exec(ExpandConstant('{tmp}\7za.exe'), ExpandConstantEx('x "{tmp}\{filename}" -o"{app}\Git"', 'filename', GitDep.Filename), '', SW_HIDE, ewWaitUntilTerminated, ResultCode)
556 | InstallDepPage.SetProgress(InstallDepPage.ProgressBar.Position+1, InstallDepPage.ProgressBar.Max)
557 | end;
558 |
559 | function VerifyDependency(Dependency: TDependency): Boolean;
560 | begin
561 | Result := True
562 |
563 | InstallDepPage.SetText('Verifying dependency files...', Dependency.Filename)
564 | if GetSHA1OfFile(ExpandConstant('{tmp}\') + Dependency.Filename) <> Dependency.SHA1 then begin
565 | MsgBox('SHA1 hash of ' + Dependency.Filename + ' does not match.', mbError, 0)
566 | Result := False
567 | end;
568 | InstallDepPage.SetProgress(InstallDepPage.ProgressBar.Position+1, InstallDepPage.ProgressBar.Max)
569 | end;
570 |
571 | function VerifyDependencies(): Boolean;
572 | begin
573 | Result := True
574 |
575 | if WizardIsComponentSelected('python') then begin
576 | Result := Result and VerifyDependency(PythonDep)
577 | end;
578 | if WizardIsComponentSelected('git') then begin
579 | Result := Result and VerifyDependency(GitDep)
580 | end;
581 | end;
582 |
583 | procedure OnPythonBrowseClick(Sender: TObject);
584 | var
585 | Filename: String;
586 | FilenameLength: Integer;
587 | begin
588 | // Browse for a custom Python executable
589 | if GetOpenFilename('Path to Python executable:', Filename, '', 'Python executable (python.exe)|python.exe', 'python.exe') then begin
590 | FilenameLength := Length(Filename);
591 | if Copy(Filename, FilenameLength - Length('\python.exe') + 1, FilenameLength) = '\python.exe' then begin
592 | InstallOptions.Python.Path.Caption := Filename;
593 | end;
594 | end;
595 | end;
596 |
597 | procedure OnGitBrowseClick(Sender: TObject);
598 | var
599 | Filename: String;
600 | FilenameLength: Integer;
601 | begin
602 | // Browse for a custom Git executable
603 | if GetOpenFilename('Path to Git executable:', Filename, '', 'Git executable (git.exe)|git.exe', 'git.exe') then begin
604 | FilenameLength := Length(Filename);
605 | if Copy(Filename, FilenameLength - Length('\git.exe') + 1, FilenameLength) = '\git.exe' then begin
606 | InstallOptions.Git.Path.Caption := Filename;
607 | end;
608 | end;
609 | end;
610 |
611 | procedure AppendDependency(Dependency: TDependency);
612 | var
613 | LocalFile: String;
614 | DownloadTarget: String;
615 | begin
616 | // Add the dependency to the download list (or use it from a local file)
617 | if LocalFilesDir <> '' then begin
618 | LocalFile := LocalFilesDir + '\' + Dependency.Filename
619 | end;
620 |
621 | DownloadTarget := ExpandConstant('{tmp}\') + Dependency.Filename
622 | if (LocalFile <> '') and (FileExists(LocalFile)) then begin
623 | FileCopy(LocalFile, DownloadTarget, True)
624 | end else begin
625 | idpAddFileSize(Dependency.URL, DownloadTarget, Dependency.Size)
626 | end;
627 | end;
628 |
629 | procedure PrepareDependencies();
630 | var
631 | DownloadPage: TWizardPage;
632 | PythonSelected, GitSelected: Boolean;
633 | begin
634 | PythonSelected := WizardIsComponentSelected('python')
635 | GitSelected := WizardIsComponentSelected('git')
636 |
637 | if not (PythonSelected or GitSelected) then begin
638 | // No dependencies selected, skip creating a "Downloading Dependencies" page
639 | exit;
640 | end;
641 |
642 | if PythonSelected then AppendDependency(PythonDep);
643 | if GitSelected then AppendDependency(GitDep);
644 |
645 | DependencyDownloadPageId := idpCreateDownloadForm(wpPreparing)
646 | DownloadPage := PageFromID(DependencyDownloadPageId)
647 | DownloadPage.Caption := 'Downloading Dependencies'
648 | DownloadPage.Description := ExpandConstant('Setup is downloading {#AppName} dependencies...')
649 |
650 | idpSetOption('DetailedMode', '1')
651 | idpSetOption('DetailsButton', '0')
652 |
653 | idpConnectControls()
654 | end;
655 |
656 | function PrepareToInstall(var NeedsRestart: Boolean): String;
657 | begin
658 | if ErrorMessage <> '' then begin
659 | Result := ErrorMessage
660 | exit;
661 | end;
662 |
663 | PrepareDependencies();
664 | end;
665 |
666 | procedure InstallDependencies();
667 | var
668 | PythonSelected: Boolean;
669 | GitSelected: Boolean;
670 | MaxProgress: Integer;
671 | begin
672 | PythonSelected := WizardIsComponentSelected('python')
673 | GitSelected := WizardIsComponentSelected('git')
674 |
675 | if not (PythonSelected or GitSelected) then begin
676 | exit;
677 | end;
678 |
679 | try
680 | InstallDepPage.Show
681 |
682 | MaxProgress := 0;
683 | // Stages for each dependency - Verify + Install
684 | if PythonSelected then MaxProgress := MaxProgress + 2;
685 | if GitSelected then MaxProgress := MaxProgress + 2;
686 |
687 | InstallDepPage.SetProgress(0, MaxProgress)
688 |
689 | if VerifyDependencies() then begin
690 | ExtractTemporaryFile('7za.exe')
691 | if PythonSelected then InstallPython();
692 | if GitSelected then InstallGit();
693 | end else begin
694 | ErrorMessage := 'There was an error installing the required dependencies.'
695 | end;
696 | finally
697 | InstallDepPage.Hide
698 | end;
699 | end;
700 |
701 | procedure InitializeWizard();
702 | var
703 | WebPortCaption: TNewStaticText;
704 | begin
705 | InitializeSeedDownload()
706 |
707 | idpInitMessages()
708 |
709 | InstallDepPage := CreateOutputProgressPage('Installing Dependencies', ExpandConstant('Setup is installing {#AppName} dependencies...'));
710 |
711 | // Custom Options Page
712 | InstallOptions.Page := CreateCustomPage(wpSelectProgramGroup, 'Additional Options', ExpandConstant('Additional {#AppName} configuration options'));
713 |
714 | WebPortCaption := TNewStaticText.Create(InstallOptions.Page);
715 | WebPortCaption.Anchors := [akLeft, akRight];
716 | WebPortCaption.Caption := ExpandConstant('{#AppName} Web Server Port:');
717 | WebPortCaption.AutoSize := True;
718 | WebPortCaption.Parent := InstallOptions.Page.Surface;
719 |
720 | InstallOptions.WebPort := TNewEdit.Create(InstallOptions.Page);
721 | InstallOptions.WebPort.Top := WebPortCaption.Top + WebPortCaption.Height + ScaleY(8);
722 | InstallOptions.WebPort.Text := ExpandConstant('{#DefaultPort}');
723 | InstallOptions.WebPort.Parent := InstallOptions.Page.Surface;
724 | InstallOptions.WebPort.Width := ScaleX(50);
725 |
726 | InstallOptions.Branch := TNewCheckListBox.Create(InstallOptions.Page);
727 | InstallOptions.Branch.Top := InstallOptions.WebPort.Top + InstallOptions.WebPort.Height;
728 | InstallOptions.Branch.Width := ScaleX(50);
729 | InstallOptions.Branch.Height := ScaleY(70);
730 | InstallOptions.Branch.Anchors := [akLeft, akRight];
731 | InstallOptions.Branch.BorderStyle := bsNone;
732 | InstallOptions.Branch.ParentColor := True;
733 | InstallOptions.Branch.MinItemHeight := WizardForm.TasksList.MinItemHeight;
734 | InstallOptions.Branch.ShowLines := False;
735 | InstallOptions.Branch.WantTabs := True;
736 | InstallOptions.Branch.Parent := InstallOptions.Page.Surface;
737 | InstallOptions.Branch.AddGroup('Branch:', '', 0, nil);
738 | InstallOptions.Branch.AddRadioButton('master', '(stable)', 0, True, True, nil);
739 | InstallOptions.Branch.AddRadioButton('develop', '(development)', 0, False, True, nil);
740 |
741 | InstallOptions.Python.Caption := TNewStaticText.Create(InstallOptions.Page);
742 | InstallOptions.Python.Caption.Anchors := [akLeft, akRight];
743 | InstallOptions.Python.Caption.Top := InstallOptions.Branch.Top + InstallOptions.Branch.Height + ScaleY(8);
744 | InstallOptions.Python.Caption.Caption := 'Path to Python executable:';
745 | InstallOptions.Python.Caption.AutoSize := True;
746 | InstallOptions.Python.Caption.Parent := InstallOptions.Page.Surface;
747 | InstallOptions.Python.Caption.Visible := False;
748 |
749 | InstallOptions.Python.Path := TNewStaticText.Create(InstallOptions.Page);
750 | InstallOptions.Python.Path.Anchors := [akLeft];
751 | InstallOptions.Python.Path.Top := InstallOptions.Python.Caption.Top;
752 | InstallOptions.Python.Path.Left := InstallOptions.Python.Caption.Left + InstallOptions.Python.Caption.Width + ScaleX(2);
753 | InstallOptions.Python.Path.AutoSize := True;
754 | InstallOptions.Python.Path.Parent := InstallOptions.Page.Surface;
755 | InstallOptions.Python.Path.Caption := '';
756 | InstallOptions.Python.Path.Visible := False;
757 |
758 | InstallOptions.Python.Browse := TNewButton.Create(InstallOptions.Page);
759 | InstallOptions.Python.Browse.Anchors := [akLeft];
760 | InstallOptions.Python.Browse.Top := InstallOptions.Python.Path.Top + InstallOptions.Python.Path.Height;
761 | InstallOptions.Python.Browse.Parent := InstallOptions.Page.Surface;
762 | InstallOptions.Python.Browse.Width := ScaleX(75);
763 | InstallOptions.Python.Browse.Height := ScaleY(23);
764 | InstallOptions.Python.Browse.Caption := 'Browse...';
765 | InstallOptions.Python.Browse.Visible := False;
766 | InstallOptions.Python.Browse.OnClick := @OnPythonBrowseClick;
767 |
768 | InstallOptions.Git.Caption := TNewStaticText.Create(InstallOptions.Page);
769 | InstallOptions.Git.Caption.Anchors := [akLeft, akRight];
770 | InstallOptions.Git.Caption.Top := InstallOptions.Python.Browse.Top + InstallOptions.Python.Browse.Height + ScaleY(8);
771 | InstallOptions.Git.Caption.Caption := 'Path to Git executable:';
772 | InstallOptions.Git.Caption.AutoSize := True;
773 | InstallOptions.Git.Caption.Parent := InstallOptions.Page.Surface;
774 | InstallOptions.Git.Caption.Visible := False;
775 |
776 | InstallOptions.Git.Path := TNewStaticText.Create(InstallOptions.Page);
777 | InstallOptions.Git.Path.Anchors := [akLeft];
778 | InstallOptions.Git.Path.Top := InstallOptions.Git.Caption.Top;
779 | InstallOptions.Git.Path.Left := InstallOptions.Git.Caption.Left + InstallOptions.Git.Caption.Width + ScaleX(2);
780 | InstallOptions.Git.Path.AutoSize := True;
781 | InstallOptions.Git.Path.Parent := InstallOptions.Page.Surface;
782 | InstallOptions.Git.Path.Caption := '';
783 | InstallOptions.Git.Path.Visible := False;
784 |
785 | InstallOptions.Git.Browse := TNewButton.Create(InstallOptions.Page);
786 | InstallOptions.Git.Browse.Anchors := [akLeft];
787 | InstallOptions.Git.Browse.Top := InstallOptions.Git.Path.Top + InstallOptions.Git.Path.Height;
788 | InstallOptions.Git.Browse.Parent := InstallOptions.Page.Surface;
789 | InstallOptions.Git.Browse.Width := ScaleX(75);
790 | InstallOptions.Git.Browse.Height := ScaleY(23);
791 | InstallOptions.Git.Browse.Caption := 'Browse...';
792 | InstallOptions.Git.Browse.Visible := False;
793 | InstallOptions.Git.Browse.OnClick := @OnGitBrowseClick;
794 | end;
795 |
796 | function ShellLinkRunAsAdmin(LinkFilename: String): Boolean;
797 | var
798 | Obj: IUnknown;
799 | SL: IShellLinkW;
800 | PF: IPersistFile;
801 | DL: IShellLinkDataList;
802 | Flags: DWORD;
803 | begin
804 | try
805 | Obj := CreateComObject(StringToGuid(CLSID_ShellLink));
806 |
807 | SL := IShellLinkW(Obj);
808 | PF := IPersistFile(Obj);
809 |
810 | // Load the ShellLink
811 | OleCheck(PF.Load(LinkFilename, 0));
812 |
813 | // Set the SLDF_RUNAS_USER flag
814 | DL := IShellLinkDataList(Obj);
815 | OleCheck(DL.GetFlags(Flags))
816 | OleCheck(DL.SetFlags(Flags or SLDF_RUNAS_USER))
817 |
818 | // Save the ShellLink
819 | OleCheck(PF.Save(LinkFilename, True));
820 |
821 | Result := True
822 | except
823 | Result := False
824 | end;
825 | end;
826 |
827 | procedure ModifyServiceLinks();
828 | begin
829 | ShellLinkRunAsAdmin(ExpandConstant('{#ServiceStartIcon}.lnk'))
830 | ShellLinkRunAsAdmin(ExpandConstant('{#ServiceStopIcon}.lnk'))
831 | ShellLinkRunAsAdmin(ExpandConstant('{#ServiceEditIcon}.lnk'))
832 | end;
833 |
834 | function InitializeSetup(): Boolean;
835 | begin
836 | CancelWithoutPrompt := False
837 |
838 | LocalFilesDir := ExpandConstant('{param:LOCALFILES}')
839 | if (LocalFilesDir <> '') and (not DirExists(LocalFilesDir)) then begin
840 | MsgBox('Invalid LOCALFILES specified: ' + LocalFilesDir, mbError, 0)
841 | LocalFilesDir := ''
842 | end;
843 |
844 | // Don't allow installations over top
845 | if RegKeyExists(HKEY_LOCAL_MACHINE, ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\{#AppId}_is1')) then begin
846 | MsgBox(ExpandConstant('{#AppName} is already installed. If you''re reinstalling or upgrading, please uninstall the current version first.'), mbError, 0)
847 | Result := False
848 | end else begin
849 | Result := True
850 | end;
851 | end;
852 |
853 | function NextButtonClick(CurPageID: Integer): Boolean;
854 | var
855 | Port: Integer;
856 | begin
857 | Result := True;
858 |
859 | if CurPageID = SeedDownloadPageId then begin
860 | ParseSeedFile()
861 | UpdateComponentsPageDependencyVersions()
862 | end else if CurPageId = wpSelectComponents then begin
863 | // Make options visible depending on component selection
864 | InstallOptions.Python.Caption.Visible := not WizardIsComponentSelected('python');
865 | InstallOptions.Python.Path.Visible := not WizardIsComponentSelected('python');
866 | InstallOptions.Python.Path.Caption := '';
867 | InstallOptions.Python.Browse.Visible := not WizardIsComponentSelected('python');
868 |
869 | InstallOptions.Git.Caption.Visible := not WizardIsComponentSelected('git');
870 | InstallOptions.Git.Path.Visible := not WizardIsComponentSelected('git');
871 | InstallOptions.Git.Path.Caption := '';
872 | InstallOptions.Git.Browse.Visible := not WizardIsComponentSelected('git');
873 | end else if CurPageId = InstallOptions.Page.ID then begin
874 | // Make sure valid port is specified
875 | Port := StrToIntDef(GetWebPort(''), 0)
876 | if (Port = 0) or (Port < MinPort) or (Port > MaxPort) then begin
877 | MsgBox(FmtMessage('Please specify a valid port between %1 and %2.', [IntToStr(MinPort), IntToStr(MaxPort)]), mbError, 0)
878 | Result := False;
879 | exit;
880 | end;
881 |
882 | // Make sure custom Python path is supplied
883 | if not WizardIsComponentSelected('python') then begin
884 | if InstallOptions.Python.Path.Caption = '' then begin
885 | MsgBox('Please specify the path to the Python executable.', mbError, 0)
886 | Result := False;
887 | exit;
888 | end;
889 | end;
890 |
891 | // Make sure custom Git path is supplied
892 | if not WizardIsComponentSelected('git') then begin
893 | if InstallOptions.Git.Path.Caption = '' then begin
894 | MsgBox('Please specify the path to the Git executable.', mbError, 0)
895 | Result := False;
896 | exit;
897 | end;
898 | end;
899 | end;
900 | end;
901 |
902 | function UninstallShouldRemoveData(): Boolean;
903 | begin
904 | Result := MsgBox(ExpandConstant('Do you want to remove your {#AppName} database and configuration?' #13#10#13#10 'Select No if you plan on reinstalling {#AppName}.'), mbConfirmation, MB_YESNO or MB_DEFBUTTON2) = IDYES;
905 | end;
906 |
907 | procedure CancelButtonClick(CurPageID: Integer; var Cancel, Confirm: Boolean);
908 | begin
909 | Confirm := not CancelWithoutPrompt;
910 | end;
911 |
912 | procedure InitializeUninstallProgressForm();
913 | begin
914 | UninstallRemoveData := UninstallShouldRemoveData()
915 | end;
916 |
917 | procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
918 | begin
919 | if CurUninstallStep = usUninstall then begin
920 | // Stop service
921 | StopService()
922 |
923 | // Remove data if requested
924 | if UninstallRemoveData then begin
925 | DelTree(ExpandConstant('{app}\Data'), True, True, True)
926 | end;
927 | end;
928 | end;
929 |
930 | function Append(const Lines, S, NewLine: String): String;
931 | begin
932 | Result := S;
933 | if Lines <> '' then begin
934 | if Result <> '' then
935 | Result := Result + NewLine + NewLine;
936 | Result := Result + Lines;
937 | end;
938 | end;
939 |
940 | procedure InjectDependencyVersions(var MemoComponentsInfo: String);
941 | var
942 | SearchStr: String;
943 | Position: Integer;
944 | begin
945 | if MemoComponentsInfo = '' then begin
946 | exit;
947 | end;
948 |
949 | // Inject dependency versions into MemoComponentsInfo
950 | if WizardIsComponentSelected('git') and (GitDep.Version <> '') then begin
951 | SearchStr := ' Git'
952 | Position := Pos(SearchStr, MemoComponentsInfo)
953 | if Position <> 0 then begin
954 | Insert(' (v' + GitDep.Version + ')', MemoComponentsInfo, Position + Length(SearchStr));
955 | end;
956 | end;
957 |
958 | if WizardIsComponentSelected('python') and (PythonDep.Version <> '') then begin
959 | SearchStr := ' Python'
960 | Position := Pos(SearchStr, MemoComponentsInfo)
961 | if Position <> 0 then begin
962 | Insert(' (v' + PythonDep.Version + ')', MemoComponentsInfo, Position + Length(SearchStr));
963 | end;
964 | end;
965 | end;
966 |
967 | function UpdateReadyMemo(Space, NewLine, MemoUserInfoInfo, MemoDirInfo, MemoTypeInfo,
968 | MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo: String): String;
969 | var
970 | S: String;
971 | begin
972 | S := '';
973 |
974 | S := Append(MemoDirInfo, S, NewLine);
975 | S := Append(MemoGroupInfo, S, NewLine);
976 | // S := Append(MemoTypeInfo, S, NewLine);
977 |
978 | InjectDependencyVersions(MemoComponentsInfo);
979 |
980 | S := Append(MemoComponentsInfo, S, NewLine);
981 |
982 | S := Append('Web server port:' + NewLine + Space + GetWebPort(''), S, NewLine);
983 | S := Append('Branch: ' + NewLine + Space + GetBranch(''), S, NewLine);
984 |
985 | S := Append(MemoTasksInfo, S, NewLine);
986 |
987 | Result := S;
988 | end;
989 |
990 | procedure CurStepChanged(CurStep: TSetupStep);
991 | begin
992 | if CurStep = ssInstall then begin
993 | InstallDependencies()
994 | end;
995 | end;
996 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MedusaInstaller
2 | A Windows Installer for Medusa
3 |
4 | ### **Please note:**
5 | By default, this installer provides the dependencies required to run Medusa.
6 | If you have Git or Python already installed on your system, and you would prefer to use those versions,
7 | you may deselect the dependencies you already have and provide a path to the already-installed versions.
8 |
9 | Features
10 | --------
11 | Here are some of the features of MedusaInstaller:
12 | - Downloads Medusa dependencies (Git, Python)
13 | - Installs everything (Medusa and dependencies) in a self-contained directory
14 | - Installs Medusa as a Windows service (handled by NSSM)
15 | - Detects 32-bit and 64-bit architectures and installs appropriate dependencies
16 | - Creates Start Menu shortcuts
17 | - When uninstalling, asks user if they want to delete or keep their database and configuration
18 | - Allows configuring the web UI port during install
19 | - Allows configuring the branch during install
20 |
21 | The install script is written using the excellent [Inno Setup](http://www.jrsoftware.org/isinfo.php) by Jordan Russell.
22 |
23 | Download
24 | --------
25 | Head on over to the [releases](https://github.com/pymedusa/MedusaInstaller/releases) tab.
26 |
27 | How It Works
28 | ------------
29 | First, the installer will grab a 'seed' file, located [here](https://raw.githubusercontent.com/pymedusa/MedusaInstaller/master/seed.ini). This has a list of the dependencies, the URLs they can be downloaded from, their size, and an SHA1 hash. It also uses this file to make sure the user is running the latest version of the installer.
30 |
31 | Once the user steps through the pages of the wizard, the installer downloads the dependency files and verifies the SHA1 hash. It then installs them into the directory chosen by the user. Once the dependencies are installed, it uses Git to clone the Medusa repository.
32 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | image: Visual Studio 2017
2 | version: '{build}'
3 | max_jobs: 1
4 | clone_depth: 5
5 |
6 | pull_requests:
7 | do_not_increment_build_number: true
8 | skip_tags: true
9 | only_commits:
10 | files:
11 | - Medusa.iss
12 | - seed.ini
13 | - idp/**
14 | - assets/**
15 | - utils/**
16 | - appveyor.yml
17 |
18 | install:
19 | - ps: choco install -y --no-progress InnoSetup
20 |
21 | before_build:
22 | - ps: |
23 | $env:InstallerVersion = Select-String -Path .\Medusa.iss `
24 | -Pattern '^#define\sInstallerVersion\s([\d]+)' | `
25 | % { $($_.matches.groups[1]).Value };
26 |
27 | - ps: |
28 | $env:SeedVersion = Select-String -Path .\seed.ini `
29 | -Pattern '^Version=([\d]+)' | `
30 | % { $($_.matches.groups[1]).Value };
31 |
32 | - ps: |
33 | If ($env:InstallerVersion -ne $env:SeedVersion -and $env:APPVEYOR_REPO_BRANCH -eq "master") {
34 | $message = "Seed version differs from version in source code!";
35 | $details = `
36 | "`tVersion in source code: $env:InstallerVersion`n" +
37 | "`tVersion in seed file: $env:SeedVersion`n" +
38 | "Make sure to update the value before pushing to master.";
39 | Add-AppveyorMessage `
40 | -Message "$message" `
41 | -Category Warning `
42 | -Details "$details";
43 | Write-Warning "$message`n$details";
44 | }
45 |
46 | - ps: |
47 | $env:ReleaseVersion = Select-String -Path .\Medusa.iss `
48 | -Pattern '^#define\sMedusaInstallerVersion\s"v([\d.]+)"' | `
49 | % { $($_.matches.groups[1]).Value };
50 |
51 | build_script:
52 | # On pull requests, set output file to: "Output\MedusaInstaller-[commit_hash[:7]].exe"
53 | # On other commits, set output file to: "Output\MedusaInstaller-[release_version].exe"
54 | - ps: |
55 | If ($env:APPVEYOR_PULL_REQUEST_NUMBER) {
56 | $FileVersion = $env:APPVEYOR_REPO_COMMIT.Substring(0, 7);
57 | } Else {
58 | $FileVersion = $env:ReleaseVersion;
59 | }
60 | $env:OutputFile = "MedusaInstaller-$FileVersion";
61 | - ps: '& "${env:ProgramFiles(x86)}\Inno Setup 6\iscc.exe" /O"Output" /F"$env:OutputFile" /Q Medusa.iss;'
62 | - ps: $env:InstallerFile = "Output\${env:OutputFile}.exe";
63 |
64 | test: off
65 |
66 | notifications:
67 | - provider: GitHubPullRequest
68 | template: >
69 | {{#passed}}:white_check_mark:{{/passed}}{{#failed}}:x:{{/failed}} [Build {{&projectName}} {{buildVersion}} {{status}}]({{buildUrl}}) (commit {{commitUrl}} by @{{&commitAuthorUsername}})
70 | {{#jobs}}{{#artifacts}}
Artifact: [{{fileName}}]({{permalink}}) ({{size}} bytes){{/artifacts}}{{/jobs}}
71 |
Build messages:
72 |
73 | {{#jobs}}
74 | {{#messages}}
75 | - :warning: {{message}}
76 | {{details}}
77 |
78 | {{/messages}}
79 | {{/jobs}}
80 |
81 |
82 | artifacts:
83 | - path: $(InstallerFile)
84 | name: MedusaInstaller
85 |
86 | environment:
87 | GH_TOKEN:
88 | secure: epQRjuQ/hfy8BkTBL3NYVnmkFnsVlYEDKX5XB7RcY2Ort3Fo2sXX8LZdW+sNyRa3
89 |
90 | before_deploy:
91 | - ps: |
92 | If ($env:InstallerVersion -ne $env:SeedVersion) {
93 | $message = "Seed version differs from version in source code!";
94 | $details = `
95 | "`tVersion in source code: $env:InstallerVersion`n" +
96 | "`tVersion in seed file: $env:SeedVersion`n" +
97 | "You must to update the value before releasing.";
98 | Add-AppveyorMessage `
99 | -Message "$message" `
100 | -Category Error `
101 | -Details "$details";
102 | throw "$message`n$details";
103 | }
104 |
105 | - ps: |
106 | $headers = @{
107 | "Authorization" = "Bearer $env:GH_TOKEN"
108 | "Content-type" = "application/json"
109 | };
110 | $LatestGitHubRelease = Invoke-RestMethod `
111 | -Uri "https://api.github.com/repos/$env:APPVEYOR_REPO_NAME/releases/latest" `
112 | -Headers $headers `
113 | -Method GET;
114 | If ($env:ReleaseVersion -eq $LatestGitHubRelease.tag_name -and -not $LatestGitHubRelease.draft) {
115 | Write-Warning "Not deploying because release $env:ReleaseVersion is already published.";
116 | Exit-AppveyorBuild;
117 | }
118 |
119 | deploy:
120 | - provider: GitHub
121 | tag: $(ReleaseVersion)
122 | release: Release $(ReleaseVersion)
123 | force_update: true # replaces current release
124 | draft: true # release as a draft
125 | auth_token: $(GH_TOKEN)
126 | artifact: MedusaInstaller # artifact name
127 | on:
128 | branch: master # release from master branch only
129 |
--------------------------------------------------------------------------------
/assets/Wizard.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pymedusa/MedusaInstaller/aeb8cd07e142c3e0aa5ddf14f1b99f903bac881d/assets/Wizard.bmp
--------------------------------------------------------------------------------
/assets/WizardSmall.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pymedusa/MedusaInstaller/aeb8cd07e142c3e0aa5ddf14f1b99f903bac881d/assets/WizardSmall.bmp
--------------------------------------------------------------------------------
/assets/github.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pymedusa/MedusaInstaller/aeb8cd07e142c3e0aa5ddf14f1b99f903bac881d/assets/github.ico
--------------------------------------------------------------------------------
/assets/medusa.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pymedusa/MedusaInstaller/aeb8cd07e142c3e0aa5ddf14f1b99f903bac881d/assets/medusa.ico
--------------------------------------------------------------------------------
/idp/idp.iss:
--------------------------------------------------------------------------------
1 | ; Inno Download Plugin
2 | ; (c)2013-2014 Mitrich Software
3 | ; http://mitrichsoftware.wordpress.com/
4 | ; https://code.google.com/p/inno-download-plugin/
5 |
6 | #define IDPROOT ExtractFilePath(__PATHFILENAME__)
7 |
8 | #ifdef UNICODE
9 | #pragma include __INCLUDE__ + ";" + IDPROOT + "\unicode"
10 | #else
11 | #pragma include __INCLUDE__ + ";" + IDPROOT + "\ansi"
12 | #endif
13 |
14 | ; If IDP_DEBUG is defined before including idp.iss, script will use debug version of idp.dll (not included, you need to build it yourself).
15 | ; Debug dll messages can be viewed with SysInternals DebugView (http://technet.microsoft.com/en-us/sysinternals/bb896647.aspx)
16 | #ifdef IDP_DEBUG
17 | #define DBGSUFFIX " debug"
18 | #else
19 | #define DBGSUFFIX
20 | #endif
21 |
22 | #ifdef UNICODE
23 | #define IDPDLLDIR IDPROOT + "\unicode" + DBGSUFFIX
24 | #else
25 | #define IDPDLLDIR IDPROOT + "\ansi" + DBGSUFFIX
26 | #endif
27 |
28 | #define IDP_VER_MAJOR
29 | #define IDP_VER_MINOR
30 | #define IDP_VER_REV
31 | #define IDP_VER_BUILD
32 |
33 | #expr ParseVersion(IDPDLLDIR + "\idp.dll", IDP_VER_MAJOR, IDP_VER_MINOR, IDP_VER_REV, IDP_VER_BUILD)
34 | #define IDP_VER EncodeVer(IDP_VER_MAJOR, IDP_VER_MINOR, IDP_VER_REV, IDP_VER_BUILD)
35 |
36 | #define IDP_VER_STR GetFileVersion(IDPDLLDIR + "\idp.dll")
37 |
38 | [Files]
39 | Source: "{#IDPDLLDIR}\idp.dll"; Flags: dontcopy;
40 |
41 | [Code]
42 | procedure idpAddFile(url, filename: String); external 'idpAddFile@files:idp.dll cdecl';
43 | procedure idpAddFileComp(url, filename, components: String); external 'idpAddFileComp@files:idp.dll cdecl';
44 | procedure idpAddMirror(url, mirror: String); external 'idpAddMirror@files:idp.dll cdecl';
45 | procedure idpAddFtpDir(url, mask, destdir: String; recursive: Boolean); external 'idpAddFtpDir@files:idp.dll cdecl';
46 | procedure idpAddFtpDirComp(url, mask, destdir: String; recursive: Boolean; components: String); external 'idpAddFtpDirComp@files:idp.dll cdecl';
47 | procedure idpClearFiles; external 'idpClearFiles@files:idp.dll cdecl';
48 | function idpFilesCount: Integer; external 'idpFilesCount@files:idp.dll cdecl';
49 | function idpFtpDirsCount: Integer; external 'idpFtpDirsCount@files:idp.dll cdecl';
50 | function idpFileDownloaded(url: String): Boolean; external 'idpFileDownloaded@files:idp.dll cdecl';
51 | function idpFilesDownloaded: Boolean; external 'idpFilesDownloaded@files:idp.dll cdecl';
52 | function idpDownloadFile(url, filename: String): Boolean; external 'idpDownloadFile@files:idp.dll cdecl';
53 | function idpDownloadFiles: Boolean; external 'idpDownloadFiles@files:idp.dll cdecl';
54 | function idpDownloadFilesComp: Boolean; external 'idpDownloadFilesComp@files:idp.dll cdecl';
55 | function idpDownloadFilesCompUi: Boolean; external 'idpDownloadFilesCompUi@files:idp.dll cdecl';
56 | procedure idpStartDownload; external 'idpStartDownload@files:idp.dll cdecl';
57 | procedure idpStopDownload; external 'idpStopDownload@files:idp.dll cdecl';
58 | procedure idpSetLogin(login, password: String); external 'idpSetLogin@files:idp.dll cdecl';
59 | procedure idpSetProxyMode(mode: String); external 'idpSetProxyMode@files:idp.dll cdecl';
60 | procedure idpSetProxyName(name: String); external 'idpSetProxyName@files:idp.dll cdecl';
61 | procedure idpSetProxyLogin(login, password: String); external 'idpSetProxyLogin@files:idp.dll cdecl';
62 | procedure idpConnectControl(name: String; Handle: HWND); external 'idpConnectControl@files:idp.dll cdecl';
63 | procedure idpAddMessage(name, message: String); external 'idpAddMessage@files:idp.dll cdecl';
64 | procedure idpSetInternalOption(name, value: String); external 'idpSetInternalOption@files:idp.dll cdecl';
65 | procedure idpSetDetailedMode(mode: Boolean); external 'idpSetDetailedMode@files:idp.dll cdecl';
66 | procedure idpSetComponents(components: String); external 'idpSetComponents@files:idp.dll cdecl';
67 | procedure idpReportError; external 'idpReportError@files:idp.dll cdecl';
68 | procedure idpTrace(text: String); external 'idpTrace@files:idp.dll cdecl';
69 |
70 | #if defined(UNICODE) && (Ver >= 0x05050300)
71 | procedure idpAddFileSize(url, filename: String; size: Int64); external 'idpAddFileSize@files:idp.dll cdecl';
72 | procedure idpAddFileSizeComp(url, filename: String; size: Int64; components: String); external 'idpAddFileSize@files:idp.dll cdecl';
73 | function idpGetFileSize(url: String; var size: Int64): Boolean; external 'idpGetFileSize@files:idp.dll cdecl';
74 | function idpGetFilesSize(var size: Int64): Boolean; external 'idpGetFilesSize@files:idp.dll cdecl';
75 | #else
76 | procedure idpAddFileSize(url, filename: String; size: Dword); external 'idpAddFileSize32@files:idp.dll cdecl';
77 | procedure idpAddFileSizeComp(url, filename: String; size: Dword; components: String); external 'idpAddFileSize32@files:idp.dll cdecl';
78 | function idpGetFileSize(url: String; var size: Dword): Boolean; external 'idpGetFileSize32@files:idp.dll cdecl';
79 | function idpGetFilesSize(var size: Dword): Boolean; external 'idpGetFilesSize32@files:idp.dll cdecl';
80 | #endif
81 |
82 | type TIdpForm = record
83 | Page : TWizardPage;
84 | TotalProgressBar : TNewProgressBar;
85 | FileProgressBar : TNewProgressBar;
86 | TotalProgressLabel: TNewStaticText;
87 | CurrentFileLabel : TNewStaticText;
88 | TotalDownloaded : TNewStaticText;
89 | FileDownloaded : TNewStaticText;
90 | FileNameLabel : TNewStaticText;
91 | SpeedLabel : TNewStaticText;
92 | StatusLabel : TNewStaticText;
93 | ElapsedTimeLabel : TNewStaticText;
94 | RemainingTimeLabel: TNewStaticText;
95 | FileName : TNewStaticText;
96 | Speed : TNewStaticText;
97 | Status : TNewStaticText;
98 | ElapsedTime : TNewStaticText;
99 | RemainingTime : TNewStaticText;
100 | DetailsButton : TNewButton;
101 | GIDetailsButton : HWND; //Graphical Installer
102 | DetailsVisible : Boolean;
103 | InvisibleButton : TNewButton;
104 | end;
105 |
106 | TIdpOptions = record
107 | DetailedMode : Boolean;
108 | NoDetailsButton: Boolean;
109 | NoRetryButton : Boolean;
110 | NoSkinnedButton: Boolean; //Graphical Installer
111 | end;
112 |
113 | var IDPForm : TIdpForm;
114 | IDPOptions: TIdpOptions;
115 |
116 | function StrToBool(value: String): Boolean;
117 | var s: String;
118 | begin
119 | s := LowerCase(value);
120 |
121 | if s = 'true' then result := true
122 | else if s = 't' then result := true
123 | else if s = 'yes' then result := true
124 | else if s = 'y' then result := true
125 | else if s = 'false' then result := false
126 | else if s = 'f' then result := false
127 | else if s = 'no' then result := false
128 | else if s = 'n' then result := false
129 | else result := StrToInt(value) > 0;
130 | end;
131 |
132 | function WizardVerySilent: Boolean;
133 | var i: Integer;
134 | begin
135 | for i := 1 to ParamCount do
136 | begin
137 | if UpperCase(ParamStr(i)) = '/VERYSILENT' then
138 | begin
139 | result := true;
140 | exit;
141 | end;
142 | end;
143 |
144 | result := false;
145 | end;
146 |
147 | function WizardSupressMsgBoxes: Boolean;
148 | var i: Integer;
149 | begin
150 | for i := 1 to ParamCount do
151 | begin
152 | if UpperCase(ParamStr(i)) = '/SUPPRESSMSGBOXES' then
153 | begin
154 | result := true;
155 | exit;
156 | end;
157 | end;
158 |
159 | result := false;
160 | end;
161 |
162 | procedure idpSetOption(name, value: String);
163 | var key: String;
164 | begin
165 | key := LowerCase(name);
166 |
167 | if key = 'detailedmode' then IDPOptions.DetailedMode := StrToBool(value)
168 | else if key = 'detailsvisible' then IDPOptions.DetailedMode := StrToBool(value) //alias
169 | else if key = 'detailsbutton' then IDPOptions.NoDetailsButton := not StrToBool(value)
170 | else if key = 'skinnedbutton' then IDPOptions.NoSkinnedButton := not StrToBool(value)
171 | else if key = 'retrybutton' then
172 | begin
173 | IDPOptions.NoRetryButton := StrToInt(value) = 0;
174 | idpSetInternalOption('RetryButton', value);
175 | end
176 | else
177 | idpSetInternalOption(name, value);
178 | end;
179 |
180 | procedure idpShowDetails(show: Boolean);
181 | begin
182 | IDPForm.FileProgressBar.Visible := show;
183 | IDPForm.CurrentFileLabel.Visible := show;
184 | IDPForm.FileDownloaded.Visible := show;
185 | IDPForm.FileNameLabel.Visible := show;
186 | IDPForm.SpeedLabel.Visible := show;
187 | IDPForm.StatusLabel.Visible := show;
188 | IDPForm.ElapsedTimeLabel.Visible := show;
189 | IDPForm.RemainingTimeLabel.Visible := show;
190 | IDPForm.FileName.Visible := show;
191 | IDPForm.Speed.Visible := show;
192 | IDPForm.Status.Visible := show;
193 | IDPForm.ElapsedTime.Visible := show;
194 | IDPForm.RemainingTime.Visible := show;
195 |
196 | IDPForm.DetailsVisible := show;
197 |
198 | if IDPForm.DetailsVisible then
199 | begin
200 | IDPForm.DetailsButton.Caption := ExpandConstant('{cm:IDP_HideButton}');
201 | IDPForm.DetailsButton.Top := ScaleY(184);
202 | end
203 | else
204 | begin
205 | IDPForm.DetailsButton.Caption := ExpandConstant('{cm:IDP_DetailsButton}');
206 | IDPForm.DetailsButton.Top := ScaleY(44);
207 | end;
208 |
209 | idpSetDetailedMode(show);
210 | end;
211 |
212 | procedure idpDetailsButtonClick(Sender: TObject);
213 | begin
214 | idpShowDetails(not IDPForm.DetailsVisible);
215 | end;
216 |
217 | #ifdef GRAPHICAL_INSTALLER_PROJECT
218 | procedure idpGIDetailsButtonClick(hButton: HWND);
219 | begin
220 | idpShowDetails(not IDPForm.DetailsVisible);
221 |
222 | if IDPForm.DetailsVisible then
223 | begin
224 | ButtonSetText(IDPForm.GIDetailsButton, PAnsiChar(ExpandConstant('{cm:IDP_HideButton}')));
225 | ButtonSetPosition(IDPForm.GIDetailsButton, IDPForm.DetailsButton.Left-ScaleX(5), ScaleY(184), ButtonWidth, ButtonHeight);
226 | end
227 | else
228 | begin
229 | ButtonSetText(IDPForm.GIDetailsButton, PAnsiChar(ExpandConstant('{cm:IDP_DetailsButton}')));
230 | ButtonSetPosition(IDPForm.GIDetailsButton, IDPForm.DetailsButton.Left-ScaleX(5), ScaleY(44), ButtonWidth, ButtonHeight);
231 | end;
232 |
233 | ButtonRefresh(hButton);
234 | end;
235 |
236 | procedure idpCreateGIDetailsButton;
237 | var swButtonNormalColor : TColor;
238 | swButtonFocusedColor : TColor;
239 | swButtonPressedColor : TColor;
240 | swButtonDisabledColor: TColor;
241 | begin
242 | swButtonNormalColor := SwitchColorFormat(ExpandConstant('{#ButtonNormalColor}'));
243 | swButtonFocusedColor := SwitchColorFormat(ExpandConstant('{#ButtonFocusedColor}'));
244 | swButtonPressedColor := SwitchColorFormat(ExpandConstant('{#ButtonPressedColor}'));
245 | swButtonDisabledColor := SwitchColorFormat(ExpandConstant('{#ButtonDisabledColor}'));
246 |
247 | with IDPForm.DetailsButton do
248 | begin
249 | IDPForm.GIDetailsButton := ButtonCreate(IDPForm.Page.Surface.Handle, Left-ScaleX(5), Top, ButtonWidth, ButtonHeight,
250 | ExpandConstant('{tmp}\{#ButtonPicture}'), coButtonShadow, False);
251 |
252 | ButtonSetEvent(IDPForm.GIDetailsButton, ButtonClickEventID, WrapButtonCallback(@idpGIDetailsButtonClick, 1));
253 | ButtonSetFont(IDPForm.GIDetailsButton, ButtonFont.Handle);
254 | ButtonSetFontColor(IDPForm.GIDetailsButton, swButtonNormalColor, swButtonFocusedColor, swButtonPressedColor, swButtonDisabledColor);
255 | ButtonSetText(IDPForm.GIDetailsButton, PAnsiChar(Caption));
256 | ButtonSetVisibility(IDPForm.GIDetailsButton, true);
257 | ButtonSetEnabled(IDPForm.GIDetailsButton, true);
258 | end;
259 | end;
260 | #endif
261 |
262 | procedure idpFormActivate(Page: TWizardPage);
263 | begin
264 | if WizardSilent then
265 | idpSetOption('RetryButton', '0');
266 |
267 | if WizardSupressMsgBoxes then
268 | idpSetInternalOption('ErrorDialog', 'none');
269 |
270 | if not IDPOptions.NoRetryButton then
271 | WizardForm.BackButton.Caption := ExpandConstant('{cm:IDP_RetryButton}');
272 |
273 | idpShowDetails(IDPOptions.DetailedMode);
274 | IDPForm.DetailsButton.Visible := not IDPOptions.NoDetailsButton;
275 |
276 | #ifdef GRAPHICAL_INSTALLER_PROJECT
277 | idpSetInternalOption('RedrawBackground', '1');
278 | idpConnectControl('GIBackButton', hBackButton);
279 | idpConnectControl('GINextButton', hNextButton);
280 |
281 | if not IDPOptions.NoSkinnedButton then
282 | begin
283 | IDPForm.DetailsButton.Visible := false;
284 | if IDPForm.GIDetailsButton = 0 then
285 | idpCreateGIDetailsButton;
286 | end;
287 |
288 | if IDPOptions.NoRetryButton then
289 | WizardForm.BackButton.Enabled := false
290 | else
291 | WizardForm.BackButton.Visible := false;
292 |
293 | WizardForm.NextButton.Enabled := false;
294 | #endif
295 | idpSetComponents(WizardSelectedComponents(false));
296 |
297 | if WizardVerySilent then
298 | idpDownloadFilesComp
299 | else if WizardSilent then
300 | begin
301 | WizardForm.Show;
302 | WizardForm.Repaint;
303 | idpDownloadFilesCompUi;
304 | WizardForm.Hide;
305 | end
306 | else
307 | idpStartDownload;
308 | end;
309 |
310 | function idpShouldSkipPage(Page: TWizardPage): Boolean;
311 | begin
312 | idpSetComponents(WizardSelectedComponents(false));
313 | Result := ((idpFilesCount = 0) and (idpFtpDirsCount = 0)) or idpFilesDownloaded;
314 | end;
315 |
316 | function idpBackButtonClick(Page: TWizardPage): Boolean;
317 | begin
318 | if not IDPOptions.NoRetryButton then // Retry button clicked
319 | begin
320 | idpStartDownload;
321 | Result := False;
322 | end
323 | else
324 | Result := true;
325 | end;
326 |
327 | function idpNextButtonClick(Page: TWizardPage): Boolean;
328 | begin
329 | Result := True;
330 | end;
331 |
332 | procedure idpCancelButtonClick(Page: TWizardPage; var Cancel, Confirm: Boolean);
333 | begin
334 | if ExitSetupMsgBox then
335 | begin
336 | IDPForm.Status.Caption := ExpandConstant('{cm:IDP_CancellingDownload}');
337 | WizardForm.Repaint;
338 | idpStopDownload;
339 | Cancel := true;
340 | Confirm := false;
341 | end
342 | else
343 | Cancel := false;
344 | end;
345 |
346 | procedure idpReportErrorHelper(Sender: TObject);
347 | begin
348 | idpReportError; //calling idpReportError in main thread for compatibility with VCL Styles for IS
349 | end;
350 |
351 | function idpCreateDownloadForm(PreviousPageId: Integer): Integer;
352 | begin
353 | IDPForm.Page := CreateCustomPage(PreviousPageId, ExpandConstant('{cm:IDP_FormCaption}'), ExpandConstant('{cm:IDP_FormDescription}'));
354 |
355 | IDPForm.TotalProgressBar := TNewProgressBar.Create(IDPForm.Page);
356 | with IDPForm.TotalProgressBar do
357 | begin
358 | Parent := IDPForm.Page.Surface;
359 | Left := ScaleX(0);
360 | Top := ScaleY(16);
361 | Width := ScaleX(410);
362 | Height := ScaleY(20);
363 | Min := 0;
364 | Max := 100;
365 | end;
366 |
367 | IDPForm.TotalProgressLabel := TNewStaticText.Create(IDPForm.Page);
368 | with IDPForm.TotalProgressLabel do
369 | begin
370 | Parent := IDPForm.Page.Surface;
371 | Caption := ExpandConstant('{cm:IDP_TotalProgress}');
372 | Left := ScaleX(0);
373 | Top := ScaleY(0);
374 | Width := ScaleX(200);
375 | Height := ScaleY(14);
376 | AutoSize := False;
377 | TabOrder := 1;
378 | end;
379 |
380 | IDPForm.CurrentFileLabel := TNewStaticText.Create(IDPForm.Page);
381 | with IDPForm.CurrentFileLabel do
382 | begin
383 | Parent := IDPForm.Page.Surface;
384 | Caption := ExpandConstant('{cm:IDP_CurrentFile}');
385 | Left := ScaleX(0);
386 | Top := ScaleY(48);
387 | Width := ScaleX(200);
388 | Height := ScaleY(14);
389 | AutoSize := False;
390 | TabOrder := 2;
391 | end;
392 |
393 | IDPForm.FileProgressBar := TNewProgressBar.Create(IDPForm.Page);
394 | with IDPForm.FileProgressBar do
395 | begin
396 | Parent := IDPForm.Page.Surface;
397 | Left := ScaleX(0);
398 | Top := ScaleY(64);
399 | Width := ScaleX(410);
400 | Height := ScaleY(20);
401 | Min := 0;
402 | Max := 100;
403 | end;
404 |
405 | IDPForm.TotalDownloaded := TNewStaticText.Create(IDPForm.Page);
406 | with IDPForm.TotalDownloaded do
407 | begin
408 | Parent := IDPForm.Page.Surface;
409 | Caption := '';
410 | Left := ScaleX(290);
411 | Top := ScaleY(0);
412 | Width := ScaleX(120);
413 | Height := ScaleY(14);
414 | AutoSize := False;
415 | TabOrder := 4;
416 | end;
417 |
418 | IDPForm.FileDownloaded := TNewStaticText.Create(IDPForm.Page);
419 | with IDPForm.FileDownloaded do
420 | begin
421 | Parent := IDPForm.Page.Surface;
422 | Caption := '';
423 | Left := ScaleX(290);
424 | Top := ScaleY(48);
425 | Width := ScaleX(120);
426 | Height := ScaleY(14);
427 | AutoSize := False;
428 | TabOrder := 5;
429 | end;
430 |
431 | IDPForm.FileNameLabel := TNewStaticText.Create(IDPForm.Page);
432 | with IDPForm.FileNameLabel do
433 | begin
434 | Parent := IDPForm.Page.Surface;
435 | Caption := ExpandConstant('{cm:IDP_File}');
436 | Left := ScaleX(0);
437 | Top := ScaleY(100);
438 | Width := ScaleX(116);
439 | Height := ScaleY(14);
440 | AutoSize := False;
441 | TabOrder := 6;
442 | end;
443 |
444 | IDPForm.SpeedLabel := TNewStaticText.Create(IDPForm.Page);
445 | with IDPForm.SpeedLabel do
446 | begin
447 | Parent := IDPForm.Page.Surface;
448 | Caption := ExpandConstant('{cm:IDP_Speed}');
449 | Left := ScaleX(0);
450 | Top := ScaleY(116);
451 | Width := ScaleX(116);
452 | Height := ScaleY(14);
453 | AutoSize := False;
454 | TabOrder := 7;
455 | end;
456 |
457 | IDPForm.StatusLabel := TNewStaticText.Create(IDPForm.Page);
458 | with IDPForm.StatusLabel do
459 | begin
460 | Parent := IDPForm.Page.Surface;
461 | Caption := ExpandConstant('{cm:IDP_Status}');
462 | Left := ScaleX(0);
463 | Top := ScaleY(132);
464 | Width := ScaleX(116);
465 | Height := ScaleY(14);
466 | AutoSize := False;
467 | TabOrder := 8;
468 | end;
469 |
470 | IDPForm.ElapsedTimeLabel := TNewStaticText.Create(IDPForm.Page);
471 | with IDPForm.ElapsedTimeLabel do
472 | begin
473 | Parent := IDPForm.Page.Surface;
474 | Caption := ExpandConstant('{cm:IDP_ElapsedTime}');
475 | Left := ScaleX(0);
476 | Top := ScaleY(148);
477 | Width := ScaleX(116);
478 | Height := ScaleY(14);
479 | AutoSize := False;
480 | TabOrder := 9;
481 | end;
482 |
483 | IDPForm.RemainingTimeLabel := TNewStaticText.Create(IDPForm.Page);
484 | with IDPForm.RemainingTimeLabel do
485 | begin
486 | Parent := IDPForm.Page.Surface;
487 | Caption := ExpandConstant('{cm:IDP_RemainingTime}');
488 | Left := ScaleX(0);
489 | Top := ScaleY(164);
490 | Width := ScaleX(116);
491 | Height := ScaleY(14);
492 | AutoSize := False;
493 | TabOrder := 10;
494 | end;
495 |
496 | IDPForm.FileName := TNewStaticText.Create(IDPForm.Page);
497 | with IDPForm.FileName do
498 | begin
499 | Parent := IDPForm.Page.Surface;
500 | Caption := '';
501 | Left := ScaleX(120);
502 | Top := ScaleY(100);
503 | Width := ScaleX(280);
504 | Height := ScaleY(14);
505 | AutoSize := False;
506 | TabOrder := 11;
507 | end;
508 |
509 | IDPForm.Speed := TNewStaticText.Create(IDPForm.Page);
510 | with IDPForm.Speed do
511 | begin
512 | Parent := IDPForm.Page.Surface;
513 | Caption := '';
514 | Left := ScaleX(120);
515 | Top := ScaleY(116);
516 | Width := ScaleX(280);
517 | Height := ScaleY(14);
518 | AutoSize := False;
519 | TabOrder := 12;
520 | end;
521 |
522 | IDPForm.Status := TNewStaticText.Create(IDPForm.Page);
523 | with IDPForm.Status do
524 | begin
525 | Parent := IDPForm.Page.Surface;
526 | Caption := '';
527 | Left := ScaleX(120);
528 | Top := ScaleY(132);
529 | Width := ScaleX(280);
530 | Height := ScaleY(14);
531 | AutoSize := False;
532 | TabOrder := 13;
533 | end;
534 |
535 | IDPForm.ElapsedTime := TNewStaticText.Create(IDPForm.Page);
536 | with IDPForm.ElapsedTime do
537 | begin
538 | Parent := IDPForm.Page.Surface;
539 | Caption := '';
540 | Left := ScaleX(120);
541 | Top := ScaleY(148);
542 | Width := ScaleX(280);
543 | Height := ScaleY(14);
544 | AutoSize := False;
545 | TabOrder := 14;
546 | end;
547 |
548 | IDPForm.RemainingTime := TNewStaticText.Create(IDPForm.Page);
549 | with IDPForm.RemainingTime do
550 | begin
551 | Parent := IDPForm.Page.Surface;
552 | Caption := '';
553 | Left := ScaleX(120);
554 | Top := ScaleY(164);
555 | Width := ScaleX(280);
556 | Height := ScaleY(14);
557 | AutoSize := False;
558 | TabOrder := 15;
559 | end;
560 |
561 | IDPForm.DetailsButton := TNewButton.Create(IDPForm.Page);
562 | with IDPForm.DetailsButton do
563 | begin
564 | Parent := IDPForm.Page.Surface;
565 | Caption := ExpandConstant('{cm:IDP_DetailsButton}');
566 | Left := ScaleX(336);
567 | Top := ScaleY(184);
568 | Width := ScaleX(75);
569 | Height := ScaleY(23);
570 | TabOrder := 16;
571 | OnClick := @idpDetailsButtonClick;
572 | end;
573 |
574 | IDPForm.InvisibleButton := TNewButton.Create(IDPForm.Page);
575 | with IDPForm.InvisibleButton do
576 | begin
577 | Parent := IDPForm.Page.Surface;
578 | Caption := ExpandConstant('You must not see this button');
579 | Left := ScaleX(0);
580 | Top := ScaleY(0);
581 | Width := ScaleX(10);
582 | Height := ScaleY(10);
583 | TabOrder := 17;
584 | Visible := False;
585 | OnClick := @idpReportErrorHelper;
586 | end;
587 |
588 | with IDPForm.Page do
589 | begin
590 | OnActivate := @idpFormActivate;
591 | OnShouldSkipPage := @idpShouldSkipPage;
592 | OnBackButtonClick := @idpBackButtonClick;
593 | OnNextButtonClick := @idpNextButtonClick;
594 | OnCancelButtonClick := @idpCancelButtonClick;
595 | end;
596 |
597 | Result := IDPForm.Page.ID;
598 | end;
599 |
600 | procedure idpConnectControls;
601 | begin
602 | idpConnectControl('TotalProgressLabel', IDPForm.TotalProgressLabel.Handle);
603 | idpConnectControl('TotalProgressBar', IDPForm.TotalProgressBar.Handle);
604 | idpConnectControl('FileProgressBar', IDPForm.FileProgressBar.Handle);
605 | idpConnectControl('TotalDownloaded', IDPForm.TotalDownloaded.Handle);
606 | idpConnectControl('FileDownloaded', IDPForm.FileDownloaded.Handle);
607 | idpConnectControl('FileName', IDPForm.FileName.Handle);
608 | idpConnectControl('Speed', IDPForm.Speed.Handle);
609 | idpConnectControl('Status', IDPForm.Status.Handle);
610 | idpConnectControl('ElapsedTime', IDPForm.ElapsedTime.Handle);
611 | idpConnectControl('RemainingTime', IDPForm.RemainingTime.Handle);
612 | idpConnectControl('InvisibleButton', IDPForm.InvisibleButton.Handle);
613 | idpConnectControl('WizardPage', IDPForm.Page.Surface.Handle);
614 | idpConnectControl('WizardForm', WizardForm.Handle);
615 | idpConnectControl('BackButton', WizardForm.BackButton.Handle);
616 | idpConnectControl('NextButton', WizardForm.NextButton.Handle);
617 | idpConnectControl('LabelFont', IDPForm.TotalDownloaded.Font.Handle);
618 | end;
619 |
620 | procedure idpInitMessages;
621 | begin
622 | idpAddMessage('Total progress', ExpandConstant('{cm:IDP_TotalProgress}'));
623 | idpAddMessage('KB/s', ExpandConstant('{cm:IDP_KBs}'));
624 | idpAddMessage('MB/s', ExpandConstant('{cm:IDP_MBs}'));
625 | idpAddMessage('%.2f of %.2f', ExpandConstant('{cm:IDP_X_of_X}'));
626 | idpAddMessage('KB', ExpandConstant('{cm:IDP_KB}'));
627 | idpAddMessage('MB', ExpandConstant('{cm:IDP_MB}'));
628 | idpAddMessage('GB', ExpandConstant('{cm:IDP_GB}'));
629 | idpAddMessage('Initializing...', ExpandConstant('{cm:IDP_Initializing}'));
630 | idpAddMessage('Getting file information...', ExpandConstant('{cm:IDP_GettingFileInformation}'));
631 | idpAddMessage('Starting download...', ExpandConstant('{cm:IDP_StartingDownload}'));
632 | idpAddMessage('Connecting...', ExpandConstant('{cm:IDP_Connecting}'));
633 | idpAddMessage('Downloading...', ExpandConstant('{cm:IDP_Downloading}'));
634 | idpAddMessage('Download complete', ExpandConstant('{cm:IDP_DownloadComplete}'));
635 | idpAddMessage('Download failed', ExpandConstant('{cm:IDP_DownloadFailed}'));
636 | idpAddMessage('Cannot connect', ExpandConstant('{cm:IDP_CannotConnect}'));
637 | idpAddMessage('Unknown', ExpandConstant('{cm:IDP_Unknown}'));
638 | idpAddMessage('Download cancelled', ExpandConstant('{cm:IDP_DownloadCancelled}'));
639 | idpAddMessage('HTTP error %d', ExpandConstant('{cm:IDP_HTTPError_X}'));
640 | idpAddMessage('400', ExpandConstant('{cm:IDP_400}'));
641 | idpAddMessage('401', ExpandConstant('{cm:IDP_401}'));
642 | idpAddMessage('404', ExpandConstant('{cm:IDP_404}'));
643 | idpAddMessage('407', ExpandConstant('{cm:IDP_407}'));
644 | idpAddMessage('500', ExpandConstant('{cm:IDP_500}'));
645 | idpAddMessage('502', ExpandConstant('{cm:IDP_502}'));
646 | idpAddMessage('503', ExpandConstant('{cm:IDP_503}'));
647 | idpAddMessage('Retry', ExpandConstant('{cm:IDP_RetryButton}'));
648 | idpAddMessage('Ignore', ExpandConstant('{cm:IDP_IgnoreButton}'));
649 | idpAddMessage('Cancel', SetupMessage(msgButtonCancel));
650 | idpAddMessage('The following files were not downloaded:', ExpandConstant('{cm:IDP_FilesNotDownloaded}'));
651 | idpAddMessage('Check your connection and click ''Retry'' to try downloading the files again, or click ''Next'' to continue installing anyway.', ExpandConstant('{cm:IDP_RetryNext}'));
652 | idpAddMessage('Check your connection and click ''Retry'' to try downloading the files again, or click ''Cancel'' to terminate setup.', ExpandConstant('{cm:IDP_RetryCancel}'));
653 | end;
654 |
655 | procedure idpDownloadAfter(PageAfterId: Integer);
656 | begin
657 | idpCreateDownloadForm(PageAfterId);
658 | idpConnectControls;
659 | idpInitMessages;
660 | end;
661 |
662 | #include
663 |
--------------------------------------------------------------------------------
/idp/unicode/idp.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pymedusa/MedusaInstaller/aeb8cd07e142c3e0aa5ddf14f1b99f903bac881d/idp/unicode/idp.dll
--------------------------------------------------------------------------------
/idp/unicode/idplang/ChineseSimplified.iss:
--------------------------------------------------------------------------------
1 | [CustomMessages]
2 | zh.IDP_FormCaption =下载文件
3 | zh.IDP_FormDescription =请等待安装程序下载文件...
4 | zh.IDP_TotalProgress =总进度
5 | zh.IDP_CurrentFile =当前文件
6 | zh.IDP_File =文件:
7 | zh.IDP_Speed =速度:
8 | zh.IDP_Status =状态:
9 | zh.IDP_ElapsedTime =已用时间:
10 | zh.IDP_RemainingTime =剩余时间:
11 | zh.IDP_DetailsButton =详情
12 | zh.IDP_HideButton =隐藏
13 | zh.IDP_RetryButton =重试
14 | zh.IDP_IgnoreButton =忽略
15 | zh.IDP_KBs =KB/s
16 | zh.IDP_MBs =MB/s
17 | zh.IDP_X_of_X =%.2f of %.2f
18 | zh.IDP_KB =KB
19 | zh.IDP_MB =MB
20 | zh.IDP_GB =GB
21 | zh.IDP_Initializing =正在初始化...
22 | zh.IDP_GettingFileInformation=正在获取文件信息...
23 | zh.IDP_StartingDownload =开始下载...
24 | zh.IDP_Connecting =正在连接...
25 | zh.IDP_Downloading =正在下载...
26 | zh.IDP_DownloadComplete =下载完成
27 | zh.IDP_DownloadFailed =下载失败
28 | zh.IDP_CannotConnect =不能连接
29 | zh.IDP_CancellingDownload =正在取消下载...
30 | zh.IDP_Unknown =未知
31 | zh.IDP_DownloadCancelled =下载被取消
32 | zh.IDP_RetryNext =检查你的网络连接,点击“重试”重新下载,或点击“下一步”继续安装。
33 | zh.IDP_RetryCancel =检查你的网络连接,点击“重试”重新下载,或点击“取消”中止安装。
34 | zh.IDP_FilesNotDownloaded =下面文件不能下载:
35 | zh.IDP_HTTPError_X =HTTP 错误 %d
36 | zh.IDP_400 =错误的请求 (400)
37 | zh.IDP_401 =访问被拒绝 (401)
38 | zh.IDP_404 =未找到文件 (404)
39 | zh.IDP_407 =需要验证代理 (407)
40 | zh.IDP_500 =服务器内部错误 (500)
41 | zh.IDP_502 =错误的网关 (502)
42 | zh.IDP_503 =服务暂时不可用 (503)
--------------------------------------------------------------------------------
/idp/unicode/idplang/belarusian.iss:
--------------------------------------------------------------------------------
1 | [CustomMessages]
2 | be.IDP_FormCaption =Спампоўванне дадатковых файлаў
3 | be.IDP_FormDescription =Калі ласка, пачакайце, пакуль усталёўнік пампуе дадатковыя файлы...
4 | be.IDP_TotalProgress =Агульны прагрэс
5 | be.IDP_CurrentFile =Бягучы файл
6 | be.IDP_File =Файл:
7 | be.IDP_Speed =Хуткасць:
8 | be.IDP_Status =Стан:
9 | be.IDP_ElapsedTime =Мінула часу:
10 | be.IDP_RemainingTime =Засталося часу:
11 | be.IDP_DetailsButton =Падрабязней
12 | be.IDP_HideButton =Схаваць
13 | be.IDP_RetryButton =Паўтарыць
14 | be.IDP_IgnoreButton =
15 | be.IDP_KBs =КБ/с
16 | be.IDP_MBs =МБ/с
17 | be.IDP_X_of_X =%.2f з %.2f
18 | be.IDP_KB =КБ
19 | be.IDP_MB =МБ
20 | be.IDP_GB =ГБ
21 | be.IDP_Initializing =Ініцыялізацыя...
22 | be.IDP_GettingFileInformation=Атрыманне звестак пра файл...
23 | be.IDP_StartingDownload =Спампоўванне пачынаецца...
24 | be.IDP_Connecting =Злучэнне...
25 | be.IDP_Downloading =Спампоўванне...
26 | be.IDP_DownloadComplete =Спампоўванне скончылася
27 | be.IDP_DownloadFailed =Не ўдалося спампаваць
28 | be.IDP_CannotConnect =Нельга злучыцца
29 | be.IDP_CancellingDownload =Скасаванне спампоўвання...
30 | be.IDP_Unknown =Невядома
31 | be.IDP_DownloadCancelled =Загрузка отменена
32 | be.IDP_RetryNext =Праверце сваё злучэнне з Сецівам і націсніце 'Паўтарыць' каб пачаць спампоўванне нанова, або націсніце 'Далей' каб працягнуць усталяванне.
33 | be.IDP_RetryCancel =Праверце сваё злучэнне з Сецівам і націсніце 'Паўтарыць' каб пачаць спампоўванне нанова, або націсніце 'Скасаваць' каб скасаваць усталяванне.
34 | be.IDP_FilesNotDownloaded =
35 | be.IDP_HTTPError_X =Памылка HTTP %d
36 | be.IDP_400 =Памылковы запыт (400)
37 | be.IDP_401 =Доступ забаронены (401)
38 | be.IDP_404 =Файл не знойдзены (404)
39 | be.IDP_407 =Неабходна аўтарызацыя прокси (407)
40 | be.IDP_500 =Унутраная памылка сервера (500)
41 | be.IDP_502 =Памылковы шлюз (502)
42 | be.IDP_503 =Сервер часовы недаступны (503)
43 |
--------------------------------------------------------------------------------
/idp/unicode/idplang/brazilianPortuguese.iss:
--------------------------------------------------------------------------------
1 | [CustomMessages]
2 | pt_br.IDP_FormCaption =Baixando arquivos
3 | pt_br.IDP_FormDescription =Por favor aguarde, enquanto recebe arquivos adicionais...
4 | pt_br.IDP_TotalProgress =Progresso total
5 | pt_br.IDP_CurrentFile =Arquivo atual
6 | pt_br.IDP_File =Arquivo:
7 | pt_br.IDP_Speed =Velocidade:
8 | pt_br.IDP_Status =Estado:
9 | pt_br.IDP_ElapsedTime =Tempo decorrido:
10 | pt_br.IDP_RemainingTime =Tempo remanescente:
11 | pt_br.IDP_DetailsButton =Detalhes
12 | pt_br.IDP_HideButton =Ocultar
13 | pt_br.IDP_RetryButton =Repetir
14 | pt_br.IDP_IgnoreButton =
15 | pt_br.IDP_KBs =KB/s
16 | pt_br.IDP_MBs =MB/s
17 | pt_br.IDP_X_of_X =%.2f de %.2f
18 | pt_br.IDP_KB =KB
19 | pt_br.IDP_MB =MB
20 | pt_br.IDP_GB =GB
21 | pt_br.IDP_Initializing =Inicializando...
22 | pt_br.IDP_GettingFileInformation=Recebendo informações do arquivo...
23 | pt_br.IDP_StartingDownload =Iniciando o download...
24 | pt_br.IDP_Connecting =Conectando...
25 | pt_br.IDP_Downloading =Baixando...
26 | pt_br.IDP_DownloadComplete =Download finalizado
27 | pt_br.IDP_DownloadFailed =Falha no download
28 | pt_br.IDP_CannotConnect =Não pode conectar
29 | pt_br.IDP_CancellingDownload =Cancelando o download...
30 | pt_br.IDP_Unknown =Desconhecido
31 | pt_br.IDP_DownloadCancelled =Download cancelado
32 | pt_br.IDP_RetryNext =Verifique sua conexão e clique em 'Repetir' para tentar novamente o download dos arquivos, ou clique em 'Próximo' para continuar a instalação mesmo assim.
33 | pt_br.IDP_RetryCancel =Verifique sua conexão e clique em 'Repetir' para tentar novamente o download dos arquivos, ou clique em 'Cancel' para finalizar a Instalação.
34 | pt_br.IDP_FilesNotDownloaded =
35 | pt_br.IDP_HTTPError_X =erro HTTP %d
36 | pt_br.IDP_400 =Requisição inválida (400)
37 | pt_br.IDP_401 =Acesso negado (401)
38 | pt_br.IDP_404 =Arquivo não encontrado (404)
39 | pt_br.IDP_407 =Autenticação de proxy necessária (407)
40 | pt_br.IDP_500 =Erro interno do servidor (500)
41 | pt_br.IDP_502 =Bad Gateway (502)
42 | pt_br.IDP_503 =Serviço temporariamente indisponível (503)
43 |
--------------------------------------------------------------------------------
/idp/unicode/idplang/bulgarian.iss:
--------------------------------------------------------------------------------
1 | [CustomMessages]
2 | bg.IDP_FormCaption =Изтегляне на допълнителни файлове
3 | bg.IDP_FormDescription =Моля, изчакайте, докато съветникът изтегля допълнителните файлове...
4 | bg.IDP_TotalProgress =Общ ход
5 | bg.IDP_CurrentFile =Текущ файл
6 | bg.IDP_File =Файл:
7 | bg.IDP_Speed =Скорост:
8 | bg.IDP_Status =Състояние:
9 | bg.IDP_ElapsedTime =Изминало време:
10 | bg.IDP_RemainingTime =Оставащо време:
11 | bg.IDP_DetailsButton =Повече информация
12 | bg.IDP_HideButton =Скрий
13 | bg.IDP_RetryButton =Опитай отново
14 | bg.IDP_IgnoreButton =Пренебрегни
15 | bg.IDP_KBs =КБ/с
16 | bg.IDP_MBs =МБ/с
17 | bg.IDP_X_of_X =%.2f от %.2f
18 | bg.IDP_KB =КБ
19 | bg.IDP_MB =МБ
20 | bg.IDP_GB =ГБ
21 | bg.IDP_Initializing =Стартиране...
22 | bg.IDP_GettingFileInformation=Получаване на информация за файла...
23 | bg.IDP_StartingDownload =Започване на изтеглянето...
24 | bg.IDP_Connecting =Свързване...
25 | bg.IDP_Downloading =Изтегляне...
26 | bg.IDP_DownloadComplete =Изтеглянето е завършено
27 | bg.IDP_DownloadFailed =Изтеглянето беше неуспешно
28 | bg.IDP_CannotConnect =Връзката е невъзможна
29 | bg.IDP_CancellingDownload =Отмяна на изтеглянето...
30 | bg.IDP_Unknown =Неизвестно
31 | bg.IDP_DownloadCancelled =Изтеглянето е отменено
32 | bg.IDP_RetryNext =Проверете Вашата Интернет връзка и кликнете „Опитай отново“, за да направите повторен опит за изтегляне на файловете, или кликнете „Напред“, за да продължите с инсталацията въпреки това.
33 | bg.IDP_RetryCancel =Проверете Вашата Интернет връзка и кликнете „Опитай отново“, за да направите повторен опит за изтегляне на файловете, или кликнете „Отмени“, за да прекратите инсталацията.
34 | bg.IDP_FilesNotDownloaded =Следните файлове не бяха изтеглени:
35 | bg.IDP_HTTPError_X =HTTP грешка %d
36 | bg.IDP_400 =Грешна заявка (400)
37 | bg.IDP_401 =Достъпът е отказан (401)
38 | bg.IDP_404 =Файлът не е открит (404)
39 | bg.IDP_407 =Изисква се идентификация на прокси (407)
40 | bg.IDP_500 =Вътрешна сървърна грешка (500)
41 | bg.IDP_502 =Грешен шлюз (502)
42 | bg.IDP_503 =Услугата е временно недостъпна (503)
43 |
--------------------------------------------------------------------------------
/idp/unicode/idplang/czech.iss:
--------------------------------------------------------------------------------
1 | [CustomMessages]
2 | cz.IDP_FormCaption =Stahování souborů
3 | cz.IDP_FormDescription =Prosím čekejte, než instalátor stáhne dodatečné soubory...
4 | cz.IDP_TotalProgress =Celkový průběh stahování
5 | cz.IDP_CurrentFile =Aktuální soubor
6 | cz.IDP_File =Soubor:
7 | cz.IDP_Speed =Rychlost:
8 | cz.IDP_Status =Stav:
9 | cz.IDP_ElapsedTime =Uplynulý čas:
10 | cz.IDP_RemainingTime =Zbývá:
11 | cz.IDP_DetailsButton =Detaily
12 | cz.IDP_HideButton =Skrýt
13 | cz.IDP_RetryButton =Znovu
14 | cz.IDP_IgnoreButton =Ignorovat
15 | cz.IDP_KBs =KB/s
16 | cz.IDP_MBs =MB/s
17 | cz.IDP_X_of_X =%.2f of %.2f
18 | cz.IDP_KB =KB
19 | cz.IDP_MB =MB
20 | cz.IDP_GB =GB
21 | cz.IDP_Initializing =Inicializace...
22 | cz.IDP_GettingFileInformation=Stahování informací o souboru...
23 | cz.IDP_StartingDownload =Začínám stahovat...
24 | cz.IDP_Connecting =Navazování spojení...
25 | cz.IDP_Downloading =Stahování...
26 | cz.IDP_DownloadComplete =Stahování ukončeno
27 | cz.IDP_DownloadFailed =Stahování selhalo
28 | cz.IDP_CannotConnect =Není možné navázat spojení
29 | cz.IDP_CancellingDownload =Ukončení stahování...
30 | cz.IDP_Unknown =Neznámé
31 | cz.IDP_DownloadCancelled =Stahování zrušeno
32 | cz.IDP_RetryNext =Zkontrolujte připojení k internetu a klikněte na "Znovu" pro opakované stahování souborů, nebo klikměte na "Další" a pokračujte v instalaci.
33 | cz.IDP_RetryCancel =Zkontrolujte připojení k internetu a klikněte na "Znovu" pro opakované stahování souborů, nebo klikměte na "Zrušit" a zrušte instalaci.
34 | cz.IDP_FilesNotDownloaded =Následující soubory nebyly staženy:
35 | cz.IDP_HTTPError_X =Chyba HTTP %d
36 | cz.IDP_400 =Nesprávný požadavek (400)
37 | cz.IDP_401 =Přístup zakázán (401)
38 | cz.IDP_404 =Soubor nenalezen (404)
39 | cz.IDP_407 =Vyžadováno ověření proxy (407)
40 | cz.IDP_500 =Chyba serveru (500)
41 | cz.IDP_502 =Chyba brány (502)
42 | cz.IDP_503 =Služba je dočasně nedostupná (503)
--------------------------------------------------------------------------------
/idp/unicode/idplang/default.iss:
--------------------------------------------------------------------------------
1 | [CustomMessages]
2 | IDP_FormCaption =Downloading additional files
3 | IDP_FormDescription =Please wait while Setup is downloading additional files...
4 | IDP_TotalProgress =Total progress
5 | IDP_CurrentFile =Current file
6 | IDP_File =File:
7 | IDP_Speed =Speed:
8 | IDP_Status =Status:
9 | IDP_ElapsedTime =Elapsed time:
10 | IDP_RemainingTime =Remaining time:
11 | IDP_DetailsButton =Details
12 | IDP_HideButton =Hide
13 | IDP_RetryButton =Retry
14 | IDP_IgnoreButton =Ignore
15 | IDP_KBs =KB/s
16 | IDP_MBs =MB/s
17 | IDP_X_of_X =%.2f of %.2f
18 | IDP_KB =KB
19 | IDP_MB =MB
20 | IDP_GB =GB
21 | IDP_Initializing =Initializing...
22 | IDP_GettingFileInformation=Getting file information...
23 | IDP_StartingDownload =Starting download...
24 | IDP_Connecting =Connecting...
25 | IDP_Downloading =Downloading...
26 | IDP_DownloadComplete =Download complete
27 | IDP_DownloadFailed =Download failed
28 | IDP_CannotConnect =Cannot connect
29 | IDP_CancellingDownload =Cancelling download...
30 | IDP_Unknown =Unknown
31 | IDP_DownloadCancelled =Download cancelled
32 | IDP_RetryNext =Check your connection and click 'Retry' to try downloading the files again, or click 'Next' to continue installing anyway.
33 | IDP_RetryCancel =Check your connection and click 'Retry' to try downloading the files again, or click 'Cancel' to terminate setup.
34 | IDP_FilesNotDownloaded =The following files were not downloaded:
35 | IDP_HTTPError_X =HTTP error %d
36 | IDP_400 =Bad request (400)
37 | IDP_401 =Access denied (401)
38 | IDP_404 =File not found (404)
39 | IDP_407 =Proxy authentication required (407)
40 | IDP_500 =Server internal error (500)
41 | IDP_502 =Bad gateway (502)
42 | IDP_503 =Service temporaily unavailable (503)
43 |
--------------------------------------------------------------------------------
/idp/unicode/idplang/finnish.iss:
--------------------------------------------------------------------------------
1 | [CustomMessages]
2 | fi.IDP_FormCaption =Tiedostojen lataus
3 | fi.IDP_FormDescription =Odota, asennusohjelma lataa nyt tiedostoja koneellesi...
4 | fi.IDP_TotalProgress =Latauksien edistyminen
5 | fi.IDP_CurrentFile =Nyt ladattava tiedosto
6 | fi.IDP_File =Tiedosto:
7 | fi.IDP_Speed =Nopeus:
8 | fi.IDP_Status =Tila:
9 | fi.IDP_ElapsedTime =Aikaa käytetty:
10 | fi.IDP_RemainingTime =Aikaa jäljellä:
11 | fi.IDP_DetailsButton =Tiedot
12 | fi.IDP_HideButton =Piilota
13 | fi.IDP_RetryButton =Yritä uudelleen
14 | fi.IDP_IgnoreButton =Hylkää
15 | fi.IDP_KBs =KT/s
16 | fi.IDP_MBs =MT/s
17 | fi.IDP_X_of_X =%.2f of %.2f
18 | fi.IDP_KB =KT
19 | fi.IDP_MB =MT
20 | fi.IDP_GB =GT
21 | fi.IDP_Initializing =Alustetaan...
22 | fi.IDP_GettingFileInformation=Haetaan tiedostojen tietoja...
23 | fi.IDP_StartingDownload =Aloitetaan latausta...
24 | fi.IDP_Connecting =Yhdistetään...
25 | fi.IDP_Downloading =Ladataan...
26 | fi.IDP_DownloadComplete =Lataus valmis
27 | fi.IDP_DownloadFailed =Lataus epäonnistui
28 | fi.IDP_CannotConnect =Virhe yhdistettäessä
29 | fi.IDP_CancellingDownload =Peruutetaan latausta...
30 | fi.IDP_Unknown =Tuntematon
31 | fi.IDP_DownloadCancelled =Lataus peruttiin
32 | fi.IDP_RetryNext =Tarkista nettiyhteytesi tila ja klikkaa 'Yritä uudelleen' jatkaaksesi tiedostojen lataamista, tai klikkaa 'Seuraava' jatkaaksesi asennusta ilman ladattuja tiedostoja.
33 | fi.IDP_RetryCancel =Tarkista nettiyhteytesi tila ja klikkaa 'Yritä uudelleen' jatkaaksesi tiedostojen lataamista, tai klikkaa 'Peruuta' keskeyttääksesi asennus.
34 | fi.IDP_FilesNotDownloaded =Seuraavia tiedostoja ei pystytty lataamaan:
35 | fi.IDP_HTTPError_X =HTTP virhe %d
36 | fi.IDP_400 =Virheellinen pyyntö (400)
37 | fi.IDP_401 =Käyttö estetty (401)
38 | fi.IDP_404 =Tiedostoa ei löydy (404)
39 | ;fi.IDP_407 =???
40 | fi.IDP_500 =Palvelimen sisäinen virhe (500)
41 | fi.IDP_502 =Virheellinen gateway (502)
42 | fi.IDP_503 =Palvelu väliaikaisesti ei saatavilla (503)
43 |
--------------------------------------------------------------------------------
/idp/unicode/idplang/french.iss:
--------------------------------------------------------------------------------
1 | [CustomMessages]
2 | fr.IDP_FormCaption =Téléchargement des fichiers additionnels
3 | fr.IDP_FormDescription =Veuillez patienter durant le téléchargement des fichiers additionnels...
4 | fr.IDP_TotalProgress =Progression générale:
5 | fr.IDP_CurrentFile =Fichier en cours:
6 | fr.IDP_File =Fichier:
7 | fr.IDP_Speed =Vitesse:
8 | fr.IDP_Status =Status:
9 | fr.IDP_ElapsedTime =Temps écoulé:
10 | fr.IDP_RemainingTime =Temps restant:
11 | fr.IDP_DetailsButton =Détails
12 | fr.IDP_HideButton =Cacher
13 | fr.IDP_RetryButton =Réessayer
14 | fr.IDP_IgnoreButton =Ignorer
15 | fr.IDP_KBs =KO/s
16 | fr.IDP_MBs =MO/s
17 | fr.IDP_X_of_X =%.2f de %.2f
18 | fr.IDP_KB =KO
19 | fr.IDP_MB =MO
20 | fr.IDP_GB =GB
21 | fr.IDP_Initializing =Initializing...
22 | fr.IDP_GettingFileInformation=Récupération du fichier d'information...
23 | fr.IDP_StartingDownload =Début du téléchargement...
24 | fr.IDP_Connecting =Connexion...
25 | fr.IDP_Downloading =Téléchargement...
26 | fr.IDP_DownloadComplete =Téléchargement terminé
27 | fr.IDP_DownloadFailed =Téléchargement interrompu
28 | fr.IDP_CannotConnect =Impossible de se connecter
29 | fr.IDP_CancellingDownload =Annulation de téléchargement...
30 | fr.IDP_Unknown =Inconnu
31 | fr.IDP_DownloadCancelled =Télécharger annulé
32 | fr.IDP_RetryNext =Désolé, le fichier n'a pu être téléchargé. Cliquez réessayer pour essayer à nouveau de le télécharger, ou cliquez suivant pour continuer l'installation.
33 | fr.IDP_RetryCancel =Désolé, le fichier n'a pu être téléchargé. Cliquez réessayer pour essayer à nouveau de le télécharger, ou cliquez annuler pour terminer l'installation.
34 | fr.IDP_FilesNotDownloaded =Les fichiers suivants n'ont pas put téléchargés:
35 | fr.IDP_HTTPError_X =Erreur HTTP %d
36 | fr.IDP_400 =Requête incorrecte (400)
37 | fr.IDP_401 =Accès Refusé (401)
38 | fr.IDP_404 =Fichier introuvable (404)
39 | fr.IDP_407 =Authentification proxy requise (407)
40 | fr.IDP_500 =Erreur interne du serveur (500)
41 | fr.IDP_502 =Mauvaise Passerelle (502)
42 | fr.IDP_503 =Service Temporairement Indisponible (503)
43 |
--------------------------------------------------------------------------------
/idp/unicode/idplang/german.iss:
--------------------------------------------------------------------------------
1 | [CustomMessages]
2 | de.IDP_FormCaption =Download zusätzlicher Dateien
3 | de.IDP_FormDescription =Bitte warten, das Setup lädt nun zusätzliche Dateien...
4 | de.IDP_TotalProgress =Gesamter Fortschritt:
5 | de.IDP_CurrentFile =Aktuelle Datei:
6 | de.IDP_File =Datei:
7 | de.IDP_Speed =Geschwindigkeit:
8 | de.IDP_Status =Status:
9 | de.IDP_ElapsedTime =Vergangene Zeit:
10 | de.IDP_RemainingTime =Verbleibende Zeit:
11 | de.IDP_DetailsButton =Details
12 | de.IDP_HideButton =Verstecken
13 | de.IDP_RetryButton =Wiederholen
14 | de.IDP_IgnoreButton =
15 | de.IDP_KBs =kB/s
16 | de.IDP_MBs =MB/s
17 | de.IDP_X_of_X =%.2f von %.2f
18 | de.IDP_KB =KB
19 | de.IDP_MB =MB
20 | de.IDP_GB =GB
21 | de.IDP_Initializing =Initialisieren...
22 | de.IDP_GettingFileInformation=Empfange Dateiinformationen...
23 | de.IDP_StartingDownload =Starte Download...
24 | de.IDP_Connecting =Verbinde...
25 | de.IDP_Downloading =Downloade...
26 | de.IDP_DownloadComplete =Download abgeschlossen
27 | de.IDP_DownloadFailed =Download fehlgeschlagen
28 | de.IDP_CannotConnect =Die Verbindung konnte nicht hergestellt werden
29 | de.IDP_CancellingDownload =Download wird abgebrochen...
30 | de.IDP_Unknown =Unbekannt
31 | de.IDP_DownloadCancelled =Download abgebrochen
32 | de.IDP_RetryNext =Prüfen Sie Ihre Verbindung und klicken Sie auf 'Wiederholen' für einen erneuten Versuch oder klicken Sie auf 'Weiter' um dennoch fortzusetzen.
33 | de.IDP_RetryCancel =Prüfen Sie Ihre Verbindung und klicken Sie auf 'Wiederholen' für einen erneuten Versuch oder klicken Sie auf 'Abbrechen' um das Setup zu verlassen.
34 | de.IDP_FilesNotDownloaded =
35 | de.IDP_HTTPError_X =HTTP Fehler %d
36 | de.IDP_400 =Ungültige Anforderung (400)
37 | de.IDP_401 =Nicht autorisiert (401)
38 | de.IDP_404 =Datei nicht gefunden (404)
39 | ;de.IDP_407 =???
40 | de.IDP_500 =Interner Serverfehler (500)
41 | de.IDP_502 =Falsches Gateway (502)
42 | de.IDP_503 =Service nicht verfügbar (503)
43 |
--------------------------------------------------------------------------------
/idp/unicode/idplang/hungarian.iss:
--------------------------------------------------------------------------------
1 | [CustomMessages]
2 | hu.IDP_FormCaption =További fájlok letöltése
3 | hu.IDP_FormDescription =Kérjük várjon, amíg a telepítő további fájlokat tölt le az internetről...
4 | hu.IDP_TotalProgress =Teljes folyamat:
5 | hu.IDP_CurrentFile =Jelenlegi fájl:
6 | hu.IDP_File =Fájl:
7 | hu.IDP_Speed =Sebesség:
8 | hu.IDP_Status =Állapot:
9 | hu.IDP_ElapsedTime =Eltelt idő:
10 | hu.IDP_RemainingTime =Fennmaradó idő:
11 | hu.IDP_DetailsButton =Részletek
12 | hu.IDP_HideButton =Elrejtés
13 | hu.IDP_RetryButton =Ismét
14 | hu.IDP_IgnoreButton =Kihagy
15 | hu.IDP_KBs =kB/s
16 | hu.IDP_MBs =MB/s
17 | hu.IDP_X_of_X =%.2f / %.2f
18 | hu.IDP_KB =KB
19 | hu.IDP_MB =MB
20 | hu.IDP_GB =GB
21 | hu.IDP_Initializing =Felkészülés...
22 | hu.IDP_GettingFileInformation=A fájlinformációk lekérése...
23 | hu.IDP_StartingDownload =A letöltés megkezdése...
24 | hu.IDP_Connecting =Csatlakozás...
25 | hu.IDP_Downloading =Letöltés...
26 | hu.IDP_DownloadComplete =Letöltés befejezve
27 | hu.IDP_DownloadFailed =Letöltés megszakítva
28 | hu.IDP_CannotConnect =Nem sikert csatlakozni a kiszolgálóhoz
29 | hu.IDP_CancellingDownload =A letöltés megszaktása...
30 | hu.IDP_Unknown =Ismeretlen
31 | hu.IDP_DownloadCancelled =Felhasználó által megszakítva
32 | hu.IDP_RetryNext =Ellenőrizze az internetkapcsolatot, majd kattintson az "Ismét" gombra az újrapróbálkozáshoz, vagy a "Tovább" gombra a fájlok kihagyásához.
33 | hu.IDP_RetryCancel =Ellenőrizze az internetkapcsolatot, majd kattintson az "Ismét" gombra az újrapróbálkozáshoz, vagy a "Mégse" gombra a telepítés megszakításához.
34 | hu.IDP_FilesNotDownloaded =Az alábbi fájlok nem lettek letöltve:
35 | hu.IDP_HTTPError_X =HTTP hiba %d
36 | hu.IDP_400 =Nem megfelelő kérés (400)
37 | hu.IDP_401 =Hozzáférés megtagadva (401)
38 | hu.IDP_404 =A fájl nem található (404)
39 | hu.IDP_407 =Proxy hitelesítés szükséges (407)
40 | hu.IDP_500 =A kiszolgáló belső hibája (500)
41 | hu.IDP_502 =Nem megfelelő átjáró (502)
42 | hu.IDP_503 =A szolgáltatás átmenetileg nem elérhető (503)
43 |
--------------------------------------------------------------------------------
/idp/unicode/idplang/italian.iss:
--------------------------------------------------------------------------------
1 | [CustomMessages]
2 | it.IDP_FormCaption =Download file aggiuntivi
3 | it.IDP_FormDescription =Attendere prego, download dei file aggiuntivi in corso...
4 | it.IDP_TotalProgress =Progresso totale
5 | it.IDP_CurrentFile =File corrente
6 | it.IDP_File =File:
7 | it.IDP_Speed =Velocita':
8 | it.IDP_Status =Status:
9 | it.IDP_ElapsedTime =Tempo trascorso:
10 | it.IDP_RemainingTime =Tempo rimanente:
11 | it.IDP_DetailsButton =Dettagli
12 | it.IDP_HideButton =Nascondi
13 | it.IDP_RetryButton =Riprova
14 | it.IDP_IgnoreButton =Ignora
15 | it.IDP_KBs =KB/s
16 | it.IDP_MBs =MB/s
17 | it.IDP_X_of_X =%.2f of %.2f
18 | it.IDP_KB =KB
19 | it.IDP_MB =MB
20 | it.IDP_GB =GB
21 | it.IDP_Initializing =Inizializzazione...
22 | it.IDP_GettingFileInformation=Acquisizione informazioni sui file...
23 | it.IDP_StartingDownload =Avvio download...
24 | it.IDP_Connecting =Connessione in corso...
25 | it.IDP_Downloading =Download in corso...
26 | it.IDP_DownloadComplete =Download completato
27 | it.IDP_DownloadFailed =Download fallito
28 | it.IDP_CannotConnect =Impossibile connettersi
29 | it.IDP_CancellingDownload =Annullamento del download...
30 | it.IDP_Unknown =Sconosciuto
31 | it.IDP_DownloadCancelled =Download annullato
32 | it.IDP_RetryNext =Controlla la tua connessione e clicca 'Riprova' per provare nuovamente a scaricare i file, o clicca 'Avanti' per proseguire con l'installazione comunque.
33 | it.IDP_RetryCancel =Controlla la tua connessione e clicca 'Riprova' per provare nuovamente a scaricare i file, o clicca 'Annulla' per uscire dall'installazione.
34 | it.IDP_FilesNotDownloaded =I seguenti file non sono stati scaricati:
35 | it.IDP_HTTPError_X =Errore HTTP %d
36 | it.IDP_400 =Bad request (400)
37 | it.IDP_401 =Access denied (401)
38 | it.IDP_404 =File not found (404)
39 | ;it.IDP_407 =???
40 | it.IDP_500 =Server internal error (500)
41 | it.IDP_502 =Bad gateway (502)
42 | it.IDP_503 =Service temporaily unavailable (503)
43 |
--------------------------------------------------------------------------------
/idp/unicode/idplang/polish.iss:
--------------------------------------------------------------------------------
1 | [CustomMessages]
2 | pl.IDP_FormCaption =Pobieranie dodatkowych plików
3 | pl.IDP_FormDescription =Proszę czekać, instalator pobiera dodatkowe pliki...
4 | pl.IDP_TotalProgress =Postęp całkowity
5 | pl.IDP_CurrentFile =Bieżący plik
6 | pl.IDP_File =Plik:
7 | pl.IDP_Speed =Transfer:
8 | pl.IDP_Status =Status:
9 | pl.IDP_ElapsedTime =Czas pobierania:
10 | pl.IDP_RemainingTime =Czas pozostały:
11 | pl.IDP_DetailsButton =Szczegóły
12 | pl.IDP_HideButton =Ukryj
13 | pl.IDP_RetryButton =Powtórz
14 | pl.IDP_IgnoreButton =
15 | pl.IDP_KBs =KB/s
16 | pl.IDP_MBs =MB/s
17 | pl.IDP_X_of_X =%.2f z %.2f
18 | pl.IDP_MB =KB
19 | pl.IDP_MB =MB
20 | pl.IDP_MB =GB
21 | pl.IDP_Initializing =Trwa inicjalizacja...
22 | pl.IDP_GettingFileInformation=Pobieranie informacji o pliku...
23 | pl.IDP_StartingDownload =Rozpoczęcie pobierania...
24 | pl.IDP_Connecting =Nawiązywanie połączenia...
25 | pl.IDP_Downloading =Pobieranie...
26 | pl.IDP_DownloadComplete =Pobieranie zakończone
27 | pl.IDP_DownloadFailed =Pobieranie nieudane
28 | pl.IDP_CannotConnect =Nie można nazwiązać połączenia
29 | pl.IDP_CancellingDownload =Anulowanie pobierania...
30 | pl.IDP_Unknown =Nieznany
31 | pl.IDP_DownloadCancelled =Pobieranie anulowane
32 | pl.IDP_RetryNext =Sprawdź połączenie sieciowe i kliknij 'Powtórz' aby pobrać pliki ponownie lub kliknij 'Dalej' aby kontynuować instalację.
33 | pl.IDP_RetryCancel =Sprawdź połączenie sieciowe i kliknij 'Powtórz' aby pobrać pliki ponownie lub kliknij 'Anuluj' aby przerwać instalację.
34 | pl.IDP_FilesNotDownloaded =
35 | pl.IDP_HTTPError_X =Błąd HTTP %d
36 | pl.IDP_400 =Nieprawidłowe żądanie (400)
37 | pl.IDP_401 =Dostęp zabroniony (401)
38 | pl.IDP_404 =Plik nie został znaleziony (404)
39 | ;pl.IDP_407 =???
40 | pl.IDP_500 =Błąd wewnętrzny serwera (500)
41 | pl.IDP_502 =Błąd bramy sieciowej (502)
42 | pl.IDP_503 =Usługa czasowo niedostępna (503)
43 |
--------------------------------------------------------------------------------
/idp/unicode/idplang/russian.iss:
--------------------------------------------------------------------------------
1 | [CustomMessages]
2 | ru.IDP_FormCaption =Скачивание дополнительных файлов
3 | ru.IDP_FormDescription =Пожалуйста подождите, пока инсталлятор скачает дополнительные файлы...
4 | ru.IDP_TotalProgress =Общий прогресс
5 | ru.IDP_CurrentFile =Текущий файл
6 | ru.IDP_File =Файл:
7 | ru.IDP_Speed =Скорость:
8 | ru.IDP_Status =Состояние:
9 | ru.IDP_ElapsedTime =Прошло времени:
10 | ru.IDP_RemainingTime =Осталось времени:
11 | ru.IDP_DetailsButton =Подробно
12 | ru.IDP_HideButton =Скрыть
13 | ru.IDP_RetryButton =Повтор
14 | ru.IDP_IgnoreButton =Пропустить
15 | ru.IDP_KBs =КБ/с
16 | ru.IDP_MBs =МБ/с
17 | ru.IDP_X_of_X =%.2f из %.2f
18 | ru.IDP_KB =КБ
19 | ru.IDP_MB =МБ
20 | ru.IDP_GB =ГБ
21 | ru.IDP_Initializing =Инициализация...
22 | ru.IDP_GettingFileInformation=Получение информации о файле...
23 | ru.IDP_StartingDownload =Начало загрузки...
24 | ru.IDP_Connecting =Соединение...
25 | ru.IDP_Downloading =Загрузка...
26 | ru.IDP_DownloadComplete =Загрузка завершена
27 | ru.IDP_DownloadFailed =Загрузка не удалась
28 | ru.IDP_CannotConnect =Невозможно соединиться
29 | ru.IDP_CancellingDownload =Отмена загрузки...
30 | ru.IDP_Unknown =Неизвестно
31 | ru.IDP_DownloadCancelled =Загрузка отменена
32 | ru.IDP_RetryNext =Проверьте ваше подключение к сети Интернет и нажмите 'Повторить' чтобы начать скачивание заново, или нажмите 'Далее' для продолжения установки.
33 | ru.IDP_RetryCancel =Проверьте ваше подключение к сети Интернет и нажмите 'Повторить' чтобы начать скачивание заново, или нажмите 'Отмена' чтобы прервать установку.
34 | ru.IDP_FilesNotDownloaded =Не удалось загрузить следующие файлы:
35 | ru.IDP_HTTPError_X =Ошибка HTTP %d
36 | ru.IDP_400 =Неверный запрос (400)
37 | ru.IDP_401 =Доступ запрещен (401)
38 | ru.IDP_404 =Файл не найден (404)
39 | ru.IDP_407 =Необходима авторизация прокси (407)
40 | ru.IDP_500 =Внутренняя ошибка сервера (500)
41 | ru.IDP_502 =Неправильный шлюз (502)
42 | ru.IDP_503 =Сервер временно недоступен (503)
43 |
--------------------------------------------------------------------------------
/idp/unicode/idplang/slovak.iss:
--------------------------------------------------------------------------------
1 | [CustomMessages]
2 | sk.IDP_FormCaption =Preberanie súborov
3 | sk.IDP_FormDescription =Prosím čakajte, kým inštalátor prevezme dodatočné súbory...
4 | sk.IDP_TotalProgress =Celkový progres
5 | sk.IDP_CurrentFile =Aktuálny súbor
6 | sk.IDP_File =Súbor:
7 | sk.IDP_Speed =Rýchlosť:
8 | sk.IDP_Status =Stav:
9 | sk.IDP_ElapsedTime =Ubehnutý čas:
10 | sk.IDP_RemainingTime =Zostáva:
11 | sk.IDP_DetailsButton =Detaily
12 | sk.IDP_HideButton =Skryť
13 | sk.IDP_RetryButton =Znova
14 | sk.IDP_IgnoreButton =Ignorovať
15 | sk.IDP_KBs =KB/s
16 | sk.IDP_MBs =MB/s
17 | sk.IDP_X_of_X =%.2f of %.2f
18 | sk.IDP_KB =KB
19 | sk.IDP_MB =MB
20 | sk.IDP_GB =GB
21 | sk.IDP_Initializing =Inicializácia...
22 | sk.IDP_GettingFileInformation=Preberanie informácií o súbore...
23 | sk.IDP_StartingDownload =Začínam prebereť...
24 | sk.IDP_Connecting =Nadväzovanie spojenia...
25 | sk.IDP_Downloading =Preberanie...
26 | sk.IDP_DownloadComplete =Preberanie ukončené
27 | sk.IDP_DownloadFailed =Preberanie zlyhalo
28 | sk.IDP_CannotConnect =Nie je možné nadviazať spojenie
29 | sk.IDP_CancellingDownload =Ukončenie preberania...
30 | sk.IDP_Unknown =Neznáme
31 | sk.IDP_DownloadCancelled =Preberanie zrušené
32 | sk.IDP_RetryNext =Skontrolujte nastavenie siete a kliknite na 'Znova' na opätovné prebratie súborov, alebo kliknite na 'Ďalej' a pokračujte v inštalácii.
33 | sk.IDP_RetryCancel =Skontrolujte nastavenie siete a kliknite na 'Znova' na opätovné prebratie súborov, alebo kliknite na 'Zrušiť' a zrušte inštaláciu.
34 | sk.IDP_FilesNotDownloaded =Nasledujúce súbory neboli stiahnuté:
35 | sk.IDP_HTTPError_X =Chyba HTTP %d
36 | sk.IDP_400 =Nesprávna požiadavka (400)
37 | sk.IDP_401 =Prístup zakázaný (401)
38 | sk.IDP_404 =Súbor nenájdený (404)
39 | sk.IDP_407 =Proxy authentication required (407)
40 | sk.IDP_500 =Chyba servera (500)
41 | sk.IDP_502 =Chyba gateway (502)
42 | sk.IDP_503 =Služba je dočasne nedostupná (503)
43 |
--------------------------------------------------------------------------------
/idp/unicode/idplang/spanish.iss:
--------------------------------------------------------------------------------
1 | [CustomMessages]
2 | es.IDP_FormCaption =Descargando Archivos adicionales
3 | es.IDP_FormDescription =Por favor, espere mientras se descargan archivos adicionales...
4 | es.IDP_TotalProgress =Progreso Total
5 | es.IDP_CurrentFile =Archivo actual
6 | es.IDP_File =Archivo:
7 | es.IDP_Speed =Velocidad:
8 | es.IDP_Status =Estado:
9 | es.IDP_ElapsedTime =Tiempo Transcurrido:
10 | es.IDP_RemainingTime =Tiempo Restante:
11 | es.IDP_DetailsButton =Detalles
12 | es.IDP_HideButton =Ocultar
13 | es.IDP_RetryButton =Reintentar
14 | es.IDP_IgnoreButton =Ignorar
15 | es.IDP_KBs =KB/s
16 | es.IDP_MBs =MB/s
17 | es.IDP_X_of_X =%.2f de %.2f
18 | es.IDP_KB =KB
19 | es.IDP_MB =MB
20 | es.IDP_GB =GB
21 | es.IDP_Initializing =Iniciando...
22 | es.IDP_GettingFileInformation=Obteniendo información del Archivo...
23 | es.IDP_StartingDownload =Iniciando descarga...
24 | es.IDP_Connecting =Conectando...
25 | es.IDP_Downloading =Descargando...
26 | es.IDP_DownloadComplete =Descarga completada
27 | es.IDP_DownloadFailed =Descarga fallida
28 | es.IDP_CannotConnect =No se puede conectar
29 | es.IDP_CancellingDownload =Cancelando descarga...
30 | es.IDP_Unknown =Desconocido
31 | es.IDP_DownloadCancelled =Descarga cancelada
32 | es.IDP_RetryNext =Compruebe su conexión y pulse "Reintentar' para tratar de descargar de nuevo, o pulse 'Siguiente' para continuar instalando de todos modos.
33 | es.IDP_RetryCancel =Compruebe su conexión y pulse "Reintentar' para tratar de descargar de nuevo, o pulse 'Cancelar' para descartar la descarga.
34 | es.IDP_FilesNotDownloaded =Los siguientesArchivos no han sido descargados:
35 | es.IDP_HTTPError_X =Error HTTP %d
36 | es.IDP_400 =Error 'Bad request' (400)
37 | es.IDP_401 =Acceso denegado (401)
38 | es.IDP_404 =Archivo no encontrado (404)
39 | es.IDP_407 =Autentificación de Proxy requerida (407)
40 | es.IDP_500 =Error interno del servidor (500)
41 | es.IDP_502 =Error 'Bad gateway' (502)
42 | es.IDP_503 =Servicio no disponible temporalmente (503)
43 |
--------------------------------------------------------------------------------
/run_installer.cmd:
--------------------------------------------------------------------------------
1 | :: Run the installer with the local seed and external files
2 |
3 | @ECHO OFF
4 | SETLOCAL EnableDelayedExpansion
5 |
6 | CALL :NORMALIZEPATH "."
7 | SET ROOT=%RETVAL%
8 | SET INSTALLER="%ROOT%\Output\MedusaInstaller.exe"
9 |
10 | IF NOT EXIST %INSTALLER% (
11 | ECHO Installer not found, have you compiled the .iss file?
12 | EXIT /B 1
13 | )
14 |
15 | IF "%~1" NEQ "" (
16 | SET REPO=%~dpfn1
17 | IF NOT EXIST !REPO!\ (
18 | ECHO Provided local repo location does not exist.
19 | EXIT /B 1
20 | )
21 | SET LOCALREPO=/LOCALREPO="!REPO!"
22 | ) ELSE (
23 | SET LOCALREPO=
24 | )
25 |
26 | :: Runs the installer with the local seed and local install files
27 | %INSTALLER% ^
28 | /SEED="%ROOT%\seed.ini" ^
29 | /LOCALFILES="%ROOT%\files" ^
30 | /LOG="%ROOT%\Output\installer.log" ^
31 | %LOCALREPO%
32 |
33 | :: ========== FUNCTIONS ==========
34 | EXIT /B %ERRORLEVEL%
35 |
36 | :NORMALIZEPATH
37 | SET RETVAL=%~dpfn1
38 | EXIT /B
39 |
--------------------------------------------------------------------------------
/seed.ini:
--------------------------------------------------------------------------------
1 | [Installer]
2 | Version=10006
3 | DownloadUrl=https://github.com/pymedusa/MedusaInstaller
4 |
5 | [Python.x86]
6 | url=https://www.nuget.org/api/v2/package/pythonx86/3.7.3
7 | filename=pythonx86.3.7.3.nupkg
8 | size=10974884
9 | sha1=46330a0e3b40d1b9d16593e9d4993aac28b6c163
10 |
11 | [Python.x64]
12 | url=https://www.nuget.org/api/v2/package/python/3.7.3
13 | filename=python.3.7.3.nupkg
14 | size=11466309
15 | sha1=1aeffcacdfd5b85855120ee321595b2af43720f3
16 |
17 | [Git.x86]
18 | url=https://github.com/git-for-windows/git/releases/download/v2.22.0.windows.1/MinGit-2.22.0-32-bit.zip
19 | size=22627458
20 | sha1=7bca1deafe61ce9252704281e2232de6f9b5e2a6
21 |
22 | [Git.x64]
23 | url=https://github.com/git-for-windows/git/releases/download/v2.22.0.windows.1/MinGit-2.22.0-64-bit.zip
24 | size=22608951
25 | sha1=8c23e5a093db42aab389e9ff14e9b5039bffb8e2
26 |
--------------------------------------------------------------------------------
/seed_data.cmd:
--------------------------------------------------------------------------------
1 | :: Generate seed data for seed.ini
2 |
3 | @ECHO OFF
4 |
5 | IF NOT EXIST %1 (
6 | ECHO %1 is not a valid file
7 | EXIT /B 1
8 | )
9 |
10 | SET FILENAME=%~nx1
11 | SET SIZE=%~z1
12 | FOR /F "tokens=*" %%i IN ('CertUtil -hashfile "%~1" SHA1 ^| find /i /v "SHA1" ^| find /i /v "certutil"') do (
13 | SET SHA1=%%i
14 | )
15 |
16 | echo filename=%FILENAME%
17 | echo size=%SIZE%
18 | echo sha1=%SHA1%
19 |
--------------------------------------------------------------------------------
/utils/7za-License.txt:
--------------------------------------------------------------------------------
1 | 7-Zip Extra
2 | ~~~~~~~~~~~
3 | License for use and distribution
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 |
6 | Copyright (C) 1999-2019 Igor Pavlov.
7 |
8 | 7-Zip Extra files are under the GNU LGPL license.
9 |
10 |
11 | Notes:
12 | You can use 7-Zip Extra on any computer, including a computer in a commercial
13 | organization. You don't need to register or pay for 7-Zip.
14 |
15 |
16 | GNU LGPL information
17 | --------------------
18 |
19 | This library is free software; you can redistribute it and/or
20 | modify it under the terms of the GNU Lesser General Public
21 | License as published by the Free Software Foundation; either
22 | version 2.1 of the License, or (at your option) any later version.
23 |
24 | This library is distributed in the hope that it will be useful,
25 | but WITHOUT ANY WARRANTY; without even the implied warranty of
26 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 | Lesser General Public License for more details.
28 |
29 | You can receive a copy of the GNU Lesser General Public License from
30 | http://www.gnu.org/
31 |
--------------------------------------------------------------------------------
/utils/7za.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pymedusa/MedusaInstaller/aeb8cd07e142c3e0aa5ddf14f1b99f903bac881d/utils/7za.exe
--------------------------------------------------------------------------------
/utils/nssm-License.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016, Alexander
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/utils/nssm32.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pymedusa/MedusaInstaller/aeb8cd07e142c3e0aa5ddf14f1b99f903bac881d/utils/nssm32.exe
--------------------------------------------------------------------------------
/utils/nssm64.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pymedusa/MedusaInstaller/aeb8cd07e142c3e0aa5ddf14f1b99f903bac881d/utils/nssm64.exe
--------------------------------------------------------------------------------