Skip to content

Commit ec402df

Browse files
committed
Initial Commit
0 parents  commit ec402df

File tree

3 files changed

+364
-0
lines changed

3 files changed

+364
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*.conf
2+
*.code-workspace

modio_downloader.bat

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
@echo off
2+
powershell.exe -ExecutionPolicy Bypass -Command "& '%~dp0modio_downloader.ps1'"
3+
rem pause
4+
exit

modio_downloader.ps1

Lines changed: 358 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,358 @@
1+
$IsDotSourced = $MyInvocation.InvocationName -eq '.' -or $MyInvocation.Line -eq ''
2+
$DefaultTextColor = (Get-Host).UI.RawUI.ForegroundColor
3+
$SearchAttemptsUsed = 0
4+
5+
6+
7+
#region General use
8+
function Confirm-EmptyVariable($Variable)
9+
{
10+
return $null -eq $Variable -or $Variable -eq ''
11+
}
12+
13+
function Set-Title()
14+
{
15+
$Host.UI.RawUI.WindowTitle = 'Mod.IO Downloader'
16+
}
17+
18+
function Exit-WithMessageAndPause()
19+
{
20+
Write-Host "The script will now exit."
21+
pause
22+
if ($IsDotSourced)
23+
{
24+
return
25+
}
26+
exit
27+
}
28+
29+
function Test-RequiredModule([string]$Module)
30+
{
31+
if (Get-Module -ListAvailable -Name $Module)
32+
{
33+
return
34+
}
35+
36+
Write-Host "The module " -NoNewline
37+
Write-Host "$Module" -ForegroundColor Green -NoNewline
38+
Write-Host " does not exist!"
39+
Write-Host "This is needed for the script to run. Do you want to install it?"
40+
Write-Host "Type " -NoNewline
41+
Write-Host "Y " -ForegroundColor Yellow -NoNewline
42+
Write-Host "or " -NoNewline
43+
Write-Host "N" -ForegroundColor Yellow -NoNewline
44+
Write-Host ", then press " -NoNewline
45+
Write-Host "ENTER" -ForegroundColor Cyan -NoNewline
46+
Write-Host ": " -NoNewline
47+
$Prompt = Read-Host
48+
if ($Prompt -ne "y")
49+
{
50+
Exit-WithMessageAndPause
51+
}
52+
$OldErrorActionPreference = $ErrorActionPreference
53+
Install-Module $Module -Scope CurrentUser
54+
if (!$?)
55+
{
56+
Write-Host "An error occurred! " -ForegroundColor Red -NoNewline
57+
Write-Host " You may need to run PowerShell in administrator mode to install the module ""$Module""."
58+
pause
59+
exit
60+
}
61+
else
62+
{
63+
Write-Host "Done! Module """ -NoNewline
64+
Write-Host "$Module" -ForegroundColor Green -NoNewline
65+
Write-Host """ should have been installed for the current user!"
66+
pause
67+
}
68+
$ErrorActionPreference = $OldErrorActionPreference
69+
Clear-Host
70+
}
71+
72+
function Get-ScriptConfigOptions()
73+
{
74+
[system.gc]::Collect()
75+
76+
$FileName = $PSCommandPath.Split("\")[-1]
77+
$FileNameNoext = $FileName.SubString(0, $FileName.Length - 4)
78+
$Global:ScriptConfigFileName = "$FileNameNoExt.conf"
79+
if (!(Test-Path $Global:ScriptConfigFileName))
80+
{
81+
$DownloadsFolderLocation = (New-Object -ComObject Shell.Application).NameSpace('shell:Downloads').Self.Path
82+
New-Item $Global:ScriptConfigFileName | Out-Null
83+
Set-Content $Global:ScriptConfigFileName "ApiKey=put-api-key-here`nDownloadLocation=$DownloadsFolderLocation`nModSearchAttempts=5"
84+
}
85+
foreach ($Line in (Get-Content $Global:ScriptConfigFileName))
86+
{
87+
if (Confirm-EmptyVariable $Line)
88+
{
89+
continue
90+
}
91+
92+
$LineSplit = $Line.Split("=")
93+
$OptionName = $LineSplit[0]
94+
$OptionResult = $LineSplit[1].Split("#")[0].Trim() # The extra functions remove the comments found in the conf file by default
95+
Set-Variable $OptionName $OptionResult -Scope Global
96+
if ($OptionName -eq "ApiKey" -and $OptionResult -eq "put-api-key-here")
97+
{
98+
Write-Color "ERROR! ","The ","default value ","for the API key is still in the config file! Please change it to your own." Red,$DefaultTextColor,Yellow,$DefaultTextColor
99+
Exit-WithMessageAndPause
100+
return
101+
}
102+
if ($OptionName -eq "DownloadLocation")
103+
{
104+
if (!(Test-Path $Global:DownloadLocation))
105+
{
106+
Write-Color "ERROR! ","The download location ",$Global:DownloadLocation," is invalid! Please provide a path to an existing folder, or make the folder at the path provided." Red,$DefaultTextColor,Yellow,$DefaultTextColor
107+
Exit-WithMessageAndPause
108+
return
109+
}
110+
}
111+
if ($IsDotSourced)
112+
{
113+
Write-Host "The config option ""$OptionName"" is ""$OptionResult"""
114+
}
115+
}
116+
}
117+
118+
function Start-YesOrNoPrompt([string]$Question = 'Agree?', [string]$YesDescription = 'Yes', [string]$NoDescription = 'No', [string]$DefaultOptionStr = 'Yes')
119+
{
120+
switch ($DefaultOptionStr)
121+
{
122+
'Yes'
123+
{
124+
$DefaultOptionInt = 0
125+
}
126+
'No'
127+
{
128+
$DefaultOptionInt = 1
129+
}
130+
}
131+
$Choices = @(
132+
[System.Management.Automation.Host.ChoiceDescription]::New("&Yes", $YesDescription)
133+
[System.Management.Automation.Host.ChoiceDescription]::New("&No", $NoDescription)
134+
)
135+
return ![bool]$Host.UI.PromptForChoice('', $Question, $Choices, $DefaultOptionInt)
136+
}
137+
#endregion
138+
139+
#region Downloading process
140+
function Test-ApiKey()
141+
{
142+
Write-Color "Testing the provided ","API key","..." $DefaultTextColor,Yellow,$DefaultTextColor
143+
try
144+
{
145+
Invoke-RestMethod -Method Get -Uri "https://api.mod.io/v1/games?api_key=$Global:ApiKey" | Out-Null
146+
}
147+
catch
148+
{
149+
if ($null -eq $_.ErrorDetails.Message)
150+
{
151+
Write-Color "The provided API key is ","valid","!" $DefaultTextColor,Green,$DefaultTextColor
152+
return
153+
}
154+
$ErrorCode = (($_.ErrorDetails.Message) | ConvertFrom-Json).error.error_ref
155+
switch ($ErrorCode)
156+
{
157+
{$_ -in 11000..11002}
158+
{
159+
Write-Color "ERROR! ","Your API key is " Red,$DefaultTextColor -NoNewLine
160+
}
161+
11000
162+
{
163+
Write-Color "missing","! Please provide your own API key." Red,$DefaultTextColor
164+
}
165+
11001
166+
{
167+
Write-Color "malformed","! Please make sure you put in your API key correctly." Red,$DefaultTextColor
168+
}
169+
11002
170+
{
171+
Write-Color "invalid","! Please provide a new API key for your account." Red,$DefaultTextColor
172+
}
173+
default
174+
{
175+
Write-Color "ERROR! ","The error code is ",$ErrorCode Red,$DefaultTextColor,Yellow
176+
}
177+
}
178+
Exit-WithMessageAndPause
179+
return
180+
}
181+
Write-Color "The provided API key is ","valid","!" $DefaultTextColor,Green,$DefaultTextColor
182+
return
183+
}
184+
185+
function Get-URLs()
186+
{
187+
[system.gc]::Collect()
188+
189+
$Global:UrlSet = Read-Host "Paste in the URLs of the mod(s) you'd like to download. If you have multiple URLs, split each one with a comma and no spaces"
190+
if ($UrlSet.Contains(",")) # Multiple URLs provided
191+
{
192+
$Global:UrlArray = $UrlSet.Split(",")
193+
$Global:IsUrlSetMultiple = $true
194+
$Global:IsUrlSetMultiple > $null
195+
196+
$HasValidUrl = $false
197+
foreach ($Url in $Global:UrlArray)
198+
{
199+
if ($Url.Contains("mod.io"))
200+
{
201+
$HasValidUrl = $true
202+
break
203+
}
204+
}
205+
if (!$HasValidUrl)
206+
{
207+
Clear-Host
208+
Write-Host "No mod.io URLs provided!" -ForegroundColor Red
209+
Get-URLs
210+
}
211+
}
212+
else
213+
{
214+
$Global:IsUrlSetMultiple = $false
215+
if (!$UrlSet.Contains("mod.io"))
216+
{
217+
Clear-Host
218+
Write-Host "That was not a valid mod.io URL!" -ForegroundColor Red
219+
Get-URLs
220+
}
221+
else
222+
{
223+
Clear-Host
224+
$Global:Url = $Global:UrlSet
225+
}
226+
}
227+
}
228+
229+
function Get-GameNameIdFromUrl([string]$Url)
230+
{
231+
return $Url.Split("/")[4]
232+
}
233+
234+
function Get-ModNameIdFromUrl([string]$Url)
235+
{
236+
return $Url.Split("/")[-1]
237+
}
238+
239+
function Get-GameIdFromUrl([string]$Url)
240+
{
241+
[system.gc]::Collect()
242+
243+
Write-Color "Getting the ","game ID","..." $DefaultTextColor,Green,$DefaultTextColor
244+
$ModIoData = (Invoke-RestMethod -Method Get -Uri "https://api.mod.io/v1/games?api_key=$ApiKey").data
245+
if (Confirm-EmptyVariable $ModIoData)
246+
{
247+
Write-Color "ERROR! ","Could not get mod.io data! The API key provided may no longer work!" Red,$DefaultTextColor
248+
Exit-WithMessageAndPause
249+
return
250+
}
251+
252+
foreach ($SetOfData in $ModIoData)
253+
{
254+
if ($SetOfData.name_id -eq (Get-GameNameIdFromUrl($Url)))
255+
{
256+
return $SetOfData.id
257+
}
258+
}
259+
Write-Color "Could not get the ","game ID ","from the URL ",$Url,"!" $DefaultTextColor,Green,$DefaultTextColor,Yellow,$DefaultTextColor
260+
Exit-WithMessageAndPause
261+
return
262+
}
263+
264+
function Get-ModIdFromUrlAndGameId([string]$Url, [int]$GameId, [int]$ResultsOffset = 0)
265+
{
266+
[system.gc]::Collect()
267+
268+
$ModNameId = Get-ModNameIdFromUrl $Url
269+
$CurrentAttemptNum = $Global:SearchAttemptsUsed + 1
270+
Write-Color "Searching mod.io for the mod ID (","Attempt $CurrentAttemptNum",")" $DefaultTextColor,Yellow,$DefaultTextColor
271+
$ModIoGameData = (Invoke-RestMethod -Method Get -Uri "https://api.mod.io/v1/games/$GameId/mods?api_key=$ApiKey&_offset=$ResultsOffset&_q=$ModNameId").data
272+
273+
foreach ($SetOfData in $ModIoGameData)
274+
{
275+
if ($SetOfData.profile_url -eq $Url)
276+
{
277+
return $SetOfData.id
278+
}
279+
}
280+
$Global:SearchAttemptsUsed += 1
281+
if ($Global:SearchAttemptsUsed -eq $Global:ModSearchAttempts)
282+
{
283+
Write-Color "Could not get the ","mod ID ","from the URL ",$Url," in ",$Global:ModSearchAttempts," attempts!" $DefaultTextColor,Green,$DefaultTextColor,Yellow,$DefaultTextColor,Yellow,$DefaultTextColor
284+
Write-Color "Continue searching for another ",$Global:ModSearchAttempts," attempts?" $DefaultTextColor,Green,$DefaultTextColor
285+
switch (Start-YesOrNoPrompt -Question '' -YesDescription "Continue searching for 10 more attempts." -NoDescription "Do not continue searching, and exit the script.")
286+
{
287+
$false
288+
{
289+
Exit-WithMessageAndPause
290+
return
291+
}
292+
$true
293+
{
294+
$Global:SearchAttemptsUsed = 0
295+
}
296+
}
297+
}
298+
Get-ModIdFromUrl $Url $GameId ($ResultsOffset + $ModIoGameData.Length)
299+
}
300+
301+
function Get-ModZipFileFromUrl([string]$Url)
302+
{
303+
[system.gc]::Collect()
304+
305+
Write-Color "Processing ",$Url,"..." $DefaultTextColor,Green,$DefaultTextColor
306+
$GameId = Get-GameIdFromUrl $Url
307+
if (Confirm-EmptyVariable $GameId)
308+
{
309+
return
310+
}
311+
$ModId = Get-ModIdFromUrlAndGameId $Url $GameId
312+
313+
$ModData = (Invoke-RestMethod -Method Get -Uri "https://api.mod.io/v1/games/$GameId/mods/$ModId`?api_key=$ApiKey")
314+
$DownloadUrl = $ModData.modfile.download.binary_url
315+
$DownloadFileName = $ModData.modfile.filename
316+
if (Test-Path "$Global:DownloadLocation\$DownloadFileName")
317+
{
318+
Write-Color "WARNING! ","The file ",$DownloadFileName," has already been downloaded!" Yellow,$DefaultTextColor,Green,$DefaultTextColor
319+
switch (Start-YesOrNoPrompt -Question 'Do you want to replace the file?' -YesDescription 'The file will be removed before being downloaded.' -NoDescription 'The script will skip this file.')
320+
{
321+
$true
322+
{
323+
Remove-Item "$Global:DownloadLocation\$DownloadFileName"
324+
}
325+
$false
326+
{
327+
return
328+
}
329+
}
330+
}
331+
Write-Color "Downloading ",$DownloadFileName," to ",$Global:DownloadLocation,"..." $DefaultTextColor,Green,$DefaultTextColor,Green,$DefaultTextColor
332+
Invoke-WebRequest $DownloadUrl -OutFile "$Global:DownloadLocation\$DownloadFileName"
333+
}
334+
#endregion
335+
336+
337+
338+
if (!$IsDotSourced)
339+
{
340+
Set-Title
341+
Test-RequiredModule "PSWriteColor"
342+
Get-ScriptConfigOptions
343+
Test-ApiKey
344+
345+
Get-URLs
346+
if ($Global:IsUrlSetMultiple)
347+
{
348+
foreach ($Url in $Global:UrlArray)
349+
{
350+
Get-ModZipFileFromUrl $Url
351+
}
352+
Write-Host "Done!" -ForegroundColor Green
353+
Exit-WithMessageAndPause
354+
}
355+
Get-ModZipFileFromUrl $Global:Url
356+
Write-Host "Done!" -ForegroundColor Green
357+
Exit-WithMessageAndPause
358+
}

0 commit comments

Comments
 (0)