320 lines
11 KiB
Perl
320 lines
11 KiB
Perl
#!/perl -w
|
|
|
|
use strict;
|
|
use warnings;
|
|
use File::Slurp qw(:std);
|
|
use File::stat;
|
|
use File::Path;
|
|
use Archive::Zip; # imports
|
|
# use IO::Socket::SSL;
|
|
use LWP::UserAgent;
|
|
use Data::Printer;
|
|
use Crypt::SSLeay;
|
|
use Net::SSL;
|
|
use Mojo::DOM;
|
|
use File::Path qw(make_path);
|
|
use Win32::Shortcut;
|
|
use JSON;
|
|
use File::Copy::Recursive qw(dirmove);
|
|
|
|
use LWP::Protocol::http;
|
|
use LWP::Protocol::https;
|
|
push(@LWP::Protocol::http::EXTRA_SOCK_OPTS, MaxLineLength => 16384);
|
|
# push(@LWP::Protocol::https::EXTRA_SOCK_OPTS, MaxLineLength => 16384);
|
|
|
|
$ENV{PERL_NET_HTTPS_SSL_SOCKET_CLASS} = "Net::SSL";
|
|
$ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0;
|
|
|
|
my $downloadfolder = 'D:\\Downloads\\';
|
|
my $mcp_json_pfad = "C:\\Users\\steff\\.lmstudio\\mcp.json";
|
|
|
|
my $mcp_json = read_file($mcp_json_pfad);
|
|
$mcp_json = decode_json($mcp_json);
|
|
|
|
my $mcp_change;
|
|
|
|
my $apps = {
|
|
'solidcraft' => {
|
|
apiurl => 'https://api.github.com/repos/tangency-labs/solidcraft/releases',
|
|
apiregex => 'solidcraft-(.*)-windows-x64-portable.zip',
|
|
ext => 'zip',
|
|
link => 'solidcraft.exe',
|
|
mcp => 'solidcraft-cli.exe'
|
|
},
|
|
'gridcraft' => {
|
|
apiurl => 'https://api.github.com/repos/storytold/gridcraft/releases',
|
|
apiregex => 'gridcraft-(.*)-windows-x64-portable.zip',
|
|
ext => 'zip',
|
|
link => 'gridcraft.exe',
|
|
mcp => 'gridcraft-cli.exe'
|
|
},
|
|
'photocraft' => {
|
|
apiurl => 'https://api.github.com/repos/storytold/photocraft/releases',
|
|
apiregex => 'photocraft-(.*)-windows-x64-portable.zip',
|
|
ext => 'zip',
|
|
link => 'photocraft.exe',
|
|
mcp => 'photocraft-cli.exe'
|
|
},
|
|
'vectorcraft' => {
|
|
apiurl => 'https://api.github.com/repos/storytold/vectorcraft/releases',
|
|
apiregex => 'vectorcraft-(.*)-windows-x64-portable.zip',
|
|
ext => 'zip',
|
|
link => 'vectorcraft.exe',
|
|
mcp => 'vectorcraft-cli.exe'
|
|
},
|
|
'filmcraft' => {
|
|
apiurl => 'https://api.github.com/repos/storytold/filmcraft/releases',
|
|
apiregex => 'filmcraft-(.*)-windows-x64-portable.zip',
|
|
ext => 'zip',
|
|
link => 'filmcraft.exe',
|
|
mcp => 'filmcraft-cli.exe'
|
|
},
|
|
'lightcraft' => {
|
|
apiurl => 'https://api.github.com/repos/storytold/lightcraft/releases',
|
|
apiregex => 'lightcraft-(.*)-windows-x64-portable.zip',
|
|
ext => 'zip',
|
|
link => 'lightcraft.exe',
|
|
mcp => 'lightcraft-cli.exe'
|
|
},
|
|
'pdfcraft' => {
|
|
apiurl => 'https://api.github.com/repos/storytold/pdfcraft/releases',
|
|
apiregex => 'pdfcraft-(.*)-windows-x64-portable.zip',
|
|
ext => 'zip',
|
|
link => 'pdfcraft.exe',
|
|
mcp => 'pdfcraft-cli.exe'
|
|
},
|
|
'effectcraft' => {
|
|
apiurl => 'https://api.github.com/repos/storytold/effectcraft/releases',
|
|
apiregex => 'effectcraft-(.*)-windows-x64-portable.zip',
|
|
ext => 'zip',
|
|
link => 'effectcraft.exe',
|
|
mcp => 'effectcraft-cli.exe'
|
|
},
|
|
'designcraft' => {
|
|
apiurl => 'https://api.github.com/repos/storytold/designcraft/releases',
|
|
apiregex => 'designcraft-(.*)-windows-x64-portable.zip',
|
|
ext => 'zip',
|
|
link => 'designcraft.exe',
|
|
mcp => 'designcraft-cli.exe'
|
|
},
|
|
'figcraft' => {
|
|
apiurl => 'https://api.github.com/repos/storytold/figcraft/releases',
|
|
apiregex => 'figcraft-(.*)-windows-x64-portable.zip',
|
|
ext => 'zip',
|
|
link => 'figcraft.exe',
|
|
mcp => 'figcraft-cli.exe'
|
|
},
|
|
'wordcraft' => {
|
|
apiurl => 'https://api.github.com/repos/storytold/wordcraft/releases',
|
|
apiregex => 'wordcraft-(.*)-windows-x64-portable.zip',
|
|
ext => 'zip',
|
|
link => 'wordcraft.exe',
|
|
mcp => 'wordcraft-cli.exe'
|
|
},
|
|
'soundcraft' => {
|
|
apiurl => 'https://api.github.com/repos/storytold/soundcraft/releases',
|
|
apiregex => 'soundcraft-(.*)-windows-x64-portable.zip',
|
|
ext => 'zip',
|
|
link => 'soundcraft.exe',
|
|
mcp => 'soundcraft-cli.exe'
|
|
},
|
|
'deckcraft' => {
|
|
apiurl => 'https://api.github.com/repos/storytold/deckcraft/releases',
|
|
apiregex => 'deckcraft-(.*)-windows-x64-portable.zip',
|
|
ext => 'zip',
|
|
link => 'deckcraft.exe',
|
|
mcp => 'deckcraft-cli.exe'
|
|
},
|
|
'cadcraft' => {
|
|
apiurl => 'https://api.github.com/repos/storytold/cadcraft/releases',
|
|
apiregex => 'cadcraft-(.*)-windows-x64-portable.zip',
|
|
ext => 'zip',
|
|
link => 'cadcraft.exe',
|
|
mcp => 'cadcraft-cli.exe'
|
|
},
|
|
'Detect_It_Easy' => {
|
|
apiurl => 'https://api.github.com/repos/horsicq/DIE-engine/releases',
|
|
apiregex => 'die_win64_portable_(.*)_x64.zip',
|
|
ext => 'zip',
|
|
link => 'die.exe'
|
|
},
|
|
'ghidra' => {
|
|
apiurl => 'https://api.github.com/repos/NationalSecurityAgency/ghidra/releases',
|
|
apiregex => 'ghidra_(.*)_PUBLIC_20261016.zip',
|
|
ext => 'zip',
|
|
link => 'ghidraRun.bat'
|
|
}
|
|
};
|
|
|
|
|
|
if ( !-d "C:\\temp" ) {
|
|
make_path("C:\\temp");
|
|
}
|
|
if ( !-d "C:\\temp\\github" ) {
|
|
make_path("C:\\temp\\github");
|
|
}
|
|
|
|
my $ua = LWP::UserAgent->new(timeout => 10);
|
|
$ua->max_redirect( 3 );
|
|
$ua->agent('Mozilla/5.0 (compatible; AppDownloader/1.0)');
|
|
# $ua->show_progress( 1 );
|
|
|
|
for my $app ( sort keys %$apps ) {
|
|
my $apiurl = $apps->{$app}->{apiurl};
|
|
my $apiregex = $apps->{$app}->{apiregex};
|
|
my $ext = $apps->{$app}->{ext};
|
|
my $link = $apps->{$app}->{link} // undef;
|
|
my $mcp = $apps->{$app}->{mcp} // undef;
|
|
|
|
if ( !$link ) {
|
|
print "keine ausfuehrbare Datei\n";
|
|
exit;
|
|
}
|
|
|
|
# 3. Request erstellen und Header für JSON setzen
|
|
my $req = HTTP::Request->new(GET => $apiurl);
|
|
$req->header('Accept' => 'application/vnd.github+json');
|
|
# Optional: Falls du einen Token hast (für höhere Limits / private Repos)
|
|
$req->header('Authorization' => 'Bearer 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');
|
|
|
|
# 4. Anfrage absenden
|
|
my $res = $ua->request($req);
|
|
|
|
# 5. Prüfen, ob die Anfrage erfolgreich war
|
|
if ($res->is_success) {
|
|
# Den JSON-String aus dem Response-Body holen
|
|
my $json_text = $res->decoded_content;
|
|
|
|
# JSON in eine Perl-Datenstruktur (Hash) umwandeln
|
|
my $data = decode_json($json_text);
|
|
|
|
my $da = @$data[0];
|
|
my $tagname = $da->{tag_name};
|
|
if ( $tagname =~ /nightly/ ) {
|
|
print "keine Nightly\n";
|
|
next;
|
|
}
|
|
my $dateiname;
|
|
my $downloadurl;
|
|
my $ver;
|
|
my $folder;
|
|
|
|
for my $as ( @{$da->{assets}} ) {
|
|
if ( $as->{name} =~ /$apiregex/ ) {
|
|
$dateiname = $as->{name};
|
|
$ver = $1;
|
|
$downloadurl = $as->{browser_download_url};
|
|
$folder = $dateiname =~ s/\.$ext//r;
|
|
}
|
|
}
|
|
|
|
my $folderinzip = $apps->{$app}->{folderinzip} && $apps->{$app}->{folderinzip} eq 'same_as_file' ? $folder : $apps->{$app}->{folderinzip};
|
|
|
|
if ($folderinzip) {
|
|
$folderinzip = $folderinzip =~ s/XXX/$ver/r;
|
|
}
|
|
|
|
if ( -e "D:\\portable\\$app\\$ver\\$link" ) {
|
|
print "$app V$ver existiert\n";
|
|
} else {
|
|
if ( !-e $downloadfolder.$dateiname ) {
|
|
print "downloade $app\n";
|
|
my $response = $ua->get($downloadurl, ':content_file' => $downloadfolder.$dateiname);
|
|
}
|
|
if ( -e $downloadfolder.$dateiname ) {
|
|
|
|
my $obj = Archive::Zip->new(); # new instance
|
|
my $status = $obj->read($downloadfolder.$dateiname); # read file contents
|
|
|
|
if ($status != 0) {
|
|
die('Error in file!');
|
|
} else {
|
|
# entpacken der DAten in ein tempverzeichnis
|
|
if ( !-d "C:\\temp\\github\\$app" ) {
|
|
make_path("C:\\temp\\github\\$app");
|
|
}
|
|
|
|
if ( !-d "D:\\portable\\$app" ) {
|
|
make_path("D:\\portable\\$app");
|
|
}
|
|
if ( !-d "D:\\portable\\$app\\$ver" ) {
|
|
make_path("D:\\portable\\$app\\$ver");
|
|
}
|
|
# lesen
|
|
my @inhalt = read_dir("D:\\portable\\$app\\$ver");
|
|
if ( scalar @inhalt == 0 ) {
|
|
print "entpacke $downloadfolder$dateiname\n";
|
|
my $err = $obj->extractTree(undef, "C:\\temp\\github\\$app"); # extract files
|
|
}
|
|
|
|
my @inhaltnew = read_dir("C:\\temp\\github\\$app");
|
|
|
|
if ( -e "C:\\temp\\github\\$app\\$link" ) {
|
|
dirmove("C:\\temp\\github\\$app", "D:\\portable\\$app\\$ver");
|
|
} else {
|
|
for my $in ( @inhaltnew ) {
|
|
if ( -d "C:\\temp\\github\\$app\\$in" ) {
|
|
if ( -e "C:\\temp\\github\\$app\\$in\\$link" ) {
|
|
dirmove("C:\\temp\\github\\$app\\$in", "D:\\portable\\$app\\$ver");
|
|
}
|
|
}
|
|
}
|
|
rmtree "C:\\temp\\github\\$app";
|
|
}
|
|
|
|
# alte Versionen löschen
|
|
my @inhaltwithnewversion = read_dir("D:\\portable\\$app");
|
|
for my $in ( @inhaltwithnewversion ) {
|
|
next if $in eq $ver;
|
|
if ( -d "D:\\portable\\$app\\$in" ) {
|
|
rmtree "D:\\portable\\$app\\$in";
|
|
}
|
|
}
|
|
|
|
unlink($downloadfolder.$dateiname );
|
|
print "zip datei von $app gelöscht\n";
|
|
}
|
|
}
|
|
if ($link && -e "D:\\portable\\$app\\$ver\\$link" ) {
|
|
my $linknew = Win32::Shortcut->new();
|
|
|
|
if ( -e "C:\\Users\\steff\\Desktop\\$app.lnk" ) {
|
|
$linknew->Load("C:\\Users\\steff\\Desktop\\$app.lnk");
|
|
print "aendere link D:\\portable\\$app\\$ver\\$link\n";
|
|
} else {
|
|
print "erstelle link D:\\portable\\$app\\$ver\\$link\n";
|
|
}
|
|
$linknew->Path("D:\\portable\\$app\\$ver\\$link");
|
|
$linknew->WorkingDirectory("D:\\portable\\$app\\$ver");
|
|
$linknew->Description("$app-$ver");
|
|
|
|
$linknew->Save("C:\\Users\\steff\\Desktop\\$app.lnk");
|
|
$linknew->Close();
|
|
}
|
|
}
|
|
|
|
if ( $mcp && -e "D:\\portable\\$app\\$ver\\$mcp" ) {
|
|
my $mcpdata = {
|
|
args => [
|
|
"mcp"
|
|
],
|
|
command => "D:\\portable\\$app\\$ver\\$mcp"
|
|
};
|
|
|
|
$mcp_json->{mcpServers}->{$app} = $mcpdata;
|
|
$mcp_change = 1;
|
|
}
|
|
|
|
} else {
|
|
print "Fehler bei der Abfrage: " . $res->status_line . "\n";
|
|
}
|
|
}
|
|
|
|
if ( $mcp_change ) {
|
|
write_file($mcp_json_pfad, to_json($mcp_json, {utf8 => 1, pretty => 1}) )
|
|
}
|
|
|
|
system("pause");
|
|
|
|
1;
|