github downloads mit linkerstellung
This commit is contained in:
1 parent
c1a5c74ff9
commit
3df0934b39
4 files changed
+292
-242
No files matched your search
@@ -0,0 +1,291 @@
|
||||
#!/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 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',
|
||||
folderinzip => 'same_as_file'
|
||||
},
|
||||
'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',
|
||||
folderinzip => 'same_as_file'
|
||||
},
|
||||
'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',
|
||||
folderinzip => 'same_as_file'
|
||||
},
|
||||
'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',
|
||||
folderinzip => 'same_as_file'
|
||||
},
|
||||
'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',
|
||||
folderinzip => 'same_as_file'
|
||||
},
|
||||
'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',
|
||||
folderinzip => 'same_as_file'
|
||||
},
|
||||
'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',
|
||||
folderinzip => 'same_as_file'
|
||||
},
|
||||
'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',
|
||||
folderinzip => 'same_as_file'
|
||||
},
|
||||
'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',
|
||||
folderinzip => 'same_as_file'
|
||||
},
|
||||
'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',
|
||||
folderinzip => 'same_as_file'
|
||||
},
|
||||
'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',
|
||||
folderinzip => 'same_as_file'
|
||||
},
|
||||
'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',
|
||||
folderinzip => 'same_as_file'
|
||||
},
|
||||
'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',
|
||||
folderinzip => 'same_as_file'
|
||||
},
|
||||
'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',
|
||||
folderinzip => 'same_as_file'
|
||||
},
|
||||
'Detect_It_Easy' => {
|
||||
apiurl => 'https://api.github.com/repos/horsicq/DIE-engine/releases',
|
||||
apiregex => 'die_win64_portable_(.*)_x64.zip',
|
||||
ext => 'zip',
|
||||
link => 'die.exe',
|
||||
folderinzip => 'die'
|
||||
},
|
||||
'ghidra' => {
|
||||
apiurl => 'https://api.github.com/repos/NationalSecurityAgency/ghidra/releases',
|
||||
apiregex => 'ghidra_(.*)_PUBLIC_20261016.zip',
|
||||
ext => 'zip',
|
||||
link => 'ghidraRun.bat',
|
||||
folderinzip => 'ghidra_XXX_PUBLIC'
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
# 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};
|
||||
$folderinzip = $folderinzip =~ s/XXX/$ver/r;
|
||||
|
||||
if ( -d "D:\\portable\\$app\\$ver" ) {
|
||||
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 {
|
||||
if ( !-d "D:\\portable\\$app" ) {
|
||||
make_path("D:\\portable\\$app");
|
||||
}
|
||||
if ( !-d "D:\\portable\\$app\\$ver" ) {
|
||||
make_path("D:\\portable\\$app\\$ver");
|
||||
}
|
||||
|
||||
my @inhalt = read_dir("D:\\portable\\$app\\$ver");
|
||||
if ( scalar @inhalt == 0 ) {
|
||||
print "entpacke $downloadfolder.$dateiname\n";
|
||||
$obj->extractTree($folderinzip, "D:\\portable\\$app\\$ver"); # extract files
|
||||
}
|
||||
|
||||
unlink($downloadfolder.$dateiname );
|
||||
print "zip datei von $app gelöscht\n";
|
||||
}
|
||||
# } else {
|
||||
# print "keine neue Version gefunden\n";
|
||||
}
|
||||
if ($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 ) {
|
||||
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;
|
||||
+1
-1
@@ -17,7 +17,7 @@ my $delzip = 0;
|
||||
my @folders = (
|
||||
'D:\\vsc\\vsc-insider\\',
|
||||
'D:\\vsc\\vsc-insider-local\\',
|
||||
'D:\\vsc\\vsc-insider-home\\'
|
||||
'D:\\vsc\\vsc-insider-remote\\'
|
||||
);
|
||||
|
||||
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
#!/perl -w
|
||||
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::UserAgent;
|
||||
use JSON;
|
||||
|
||||
$ENV{PERL_NET_HTTPS_SSL_SOCKET_CLASS} = "Net::SSL";
|
||||
$ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0;
|
||||
|
||||
my $delzip = 0;
|
||||
my @folders = (
|
||||
'D:\\vsc\\vs-codium\\',
|
||||
'D:\\vsc\\vs-codium-local\\'
|
||||
);
|
||||
|
||||
for my $f ( @folders ) {
|
||||
mkdir $f unless -d $f;
|
||||
}
|
||||
|
||||
my $ua = Mojo::UserAgent->new();
|
||||
$ua->max_redirects(5);
|
||||
my $res = $ua->insecure(1)->get('https://github.com/VSCodium/vscodium/releases')->result->body;
|
||||
# write_file('test.html', $res);
|
||||
|
||||
# my $res = read_file('test.html');
|
||||
my $downloadfolder = 'D:\\Downloads\\';
|
||||
my $filename = 'vscodium.zip';
|
||||
|
||||
my @url = $res =~ /"(https:\/\/.*win32-x64.*zip?)"/g;
|
||||
print "downloade VS-Codium Version: $url[0]\n";
|
||||
|
||||
if ( -e $downloadfolder.$filename ) {
|
||||
print "entferne $downloadfolder$filename\n";
|
||||
unlink("$downloadfolder.$filename");
|
||||
}
|
||||
|
||||
my $tx = $ua->insecure(1)->get( $url[0] );
|
||||
$tx->res->content->asset->move_to( $downloadfolder.$filename);
|
||||
|
||||
# löschen aller Cached Dateien und Unterordner
|
||||
my @cacheddir = qw(Cache GPUCache CachedData CachedData-x64);
|
||||
|
||||
|
||||
if ( -e $downloadfolder.$filename ) {
|
||||
for my $fold ( @folders ) {
|
||||
opendir (my $dh, $fold);
|
||||
print "$fold --> ";
|
||||
my @fol = readdir $dh;
|
||||
|
||||
my $test = 1;
|
||||
if ( -e $fold.'VSCodium.exe' ) {
|
||||
$test = unlink($fold.'VSCodium.exe');
|
||||
}
|
||||
|
||||
if ( $test == 1 ) {
|
||||
for my $d ( @fol ) {
|
||||
if ( $d =~ /^\.|^\.\./ ) {
|
||||
next;
|
||||
}
|
||||
|
||||
if ( -d "$fold$d" ) {
|
||||
if ( $d ne 'data' ) {
|
||||
rmtree("$fold$d");
|
||||
} else {
|
||||
# Cache löschen
|
||||
print "leere";
|
||||
for my $cdir ( @cacheddir ) {
|
||||
print " $cdir";
|
||||
rmtree( "$fold$d\\user-data\\$cdir", {keep_root => 1} );
|
||||
}
|
||||
}
|
||||
} else{
|
||||
unlink("$fold$d");
|
||||
}
|
||||
}
|
||||
print "--> geloescht, Userdaten behalten\n";
|
||||
$delzip++;
|
||||
}
|
||||
}
|
||||
|
||||
my $obj = Archive::Zip->new(); # new instance
|
||||
my $status = $obj->read("$downloadfolder$filename"); # read file contents
|
||||
|
||||
if ($status != 0) {
|
||||
die('Error in file!');
|
||||
} else {
|
||||
if ( scalar @folders == $delzip ) {
|
||||
for my $f ( @folders ) {
|
||||
print "entpacke neue Version in $f\n";
|
||||
$obj->extractTree(undef, $f); # extract files
|
||||
}
|
||||
unlink("$downloadfolder$filename");
|
||||
print "zip datei von VSCode gelöscht\n";
|
||||
} else {
|
||||
print "zip nicht gelöscht da nicht alle Ordner verarbeitet wurden\n";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
print "keine neue Version gefunden\n";
|
||||
}
|
||||
|
||||
for my $fold ( @folders ) {
|
||||
my $prodjson = $fold.'\\resources\\app\\product.json';
|
||||
# "serviceUrl": "https://marketplace.visualstudio.com/_apis/public/gallery",
|
||||
# "itemUrl": "https://marketplace.visualstudio.com/items",
|
||||
if ( -e $prodjson ) {
|
||||
my $pro = decode_json(read_file($prodjson));
|
||||
$pro->{extensionsGallery}->{serviceUrl} = "https://marketplace.visualstudio.com/_apis/public/gallery";
|
||||
$pro->{extensionsGallery}->{itemUrl} = "https://marketplace.visualstudio.com/items";
|
||||
write_file($prodjson, to_json($pro, { pretty => 1}) );
|
||||
}
|
||||
}
|
||||
|
||||
system("pause");
|
||||
|
||||
1;
|
||||
@@ -1,117 +0,0 @@
|
||||
import os, sys
|
||||
import re
|
||||
import json
|
||||
import shutil
|
||||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
from zipfile import ZipFile
|
||||
import urllib3
|
||||
|
||||
urllib3.disable_warnings()
|
||||
|
||||
folders = [
|
||||
r'D:\\vsc\\vs-codium\\',
|
||||
r'D:\\vsc\\vs-codium-local\\'
|
||||
]
|
||||
|
||||
download_folder = r'D:\\Downloads\\'
|
||||
filename = 'vscodium.zip'
|
||||
|
||||
# Ordner anlegen, falls nicht vorhanden
|
||||
for f in folders:
|
||||
os.makedirs(f, exist_ok=True)
|
||||
|
||||
# Aktuelle Download-URL ermitteln
|
||||
res = requests.get('https://github.com/VSCodium/vscodium-insiders/tags')
|
||||
soup = BeautifulSoup(res.text, 'html.parser')
|
||||
|
||||
# with open("updatevscodium.html", "w", encoding="utf-8") as f:
|
||||
# f.write( soup.prettify() )
|
||||
|
||||
urls = re.findall(r'/VSCodium/vscodium-insiders/releases/tag/(.*)-insider', soup.prettify())
|
||||
|
||||
if not urls:
|
||||
print("Keine neue Version gefunden.")
|
||||
exit()
|
||||
|
||||
# Aktuelle Download-URL ermitteln
|
||||
print(f"https://github.com/VSCodium/vscodium-insiders/releases/download/{urls[0]}-insider/VSCodium-win32-x64-{urls[0]}-insider.zip")
|
||||
|
||||
|
||||
zip_path = os.path.join(download_folder, filename)
|
||||
|
||||
if os.path.exists(zip_path):
|
||||
print(f"Entferne {zip_path}")
|
||||
os.remove(zip_path)
|
||||
|
||||
# Datei herunterladen
|
||||
r = requests.get(f"https://github.com/VSCodium/vscodium-insiders/releases/download/{urls[0]}-insider/VSCodium-win32-x64-{urls[0]}-insider.zip", stream=True, verify=False)
|
||||
with open(zip_path, 'wb') as f:
|
||||
for chunk in r.iter_content(chunk_size=8192):
|
||||
f.write(chunk)
|
||||
|
||||
# Cachedaten löschen
|
||||
cache_dirs = ['Cache', 'GPUCache', 'CachedData', 'CachedData-x64']
|
||||
|
||||
delete_count = 0
|
||||
if os.path.exists(zip_path):
|
||||
si = os.path.getsize(zip_path)
|
||||
|
||||
if si < 20:
|
||||
print("Zip ist zu klein")
|
||||
sys.exit(1)
|
||||
|
||||
print(si)
|
||||
|
||||
for f in folders:
|
||||
print(f"{f} --> ")
|
||||
|
||||
if os.path.exists(os.path.join(f, 'VSCodium.exe')):
|
||||
os.remove(os.path.join(f, 'VSCodium.exe'))
|
||||
|
||||
for item in os.listdir(f):
|
||||
path = os.path.join(f, item)
|
||||
|
||||
if os.path.isdir(path):
|
||||
if item != 'data':
|
||||
shutil.rmtree(path)
|
||||
else:
|
||||
print("Leere", end=' ')
|
||||
for cache in cache_dirs:
|
||||
cache_path = os.path.join(path, 'user-data', cache)
|
||||
print(cache, end=' ')
|
||||
if os.path.exists(cache_path):
|
||||
shutil.rmtree(cache_path, ignore_errors=True)
|
||||
else:
|
||||
os.remove(path)
|
||||
|
||||
print("--> gelöscht, Userdaten behalten")
|
||||
delete_count += 1
|
||||
|
||||
# Entpacken
|
||||
if delete_count == len(folders):
|
||||
for f in folders:
|
||||
print(f"Entpacke neue Version in {f}")
|
||||
with ZipFile(zip_path, 'r') as zip_ref:
|
||||
zip_ref.extractall(f)
|
||||
os.remove(zip_path)
|
||||
print("ZIP Datei von VSCodium gelöscht")
|
||||
else:
|
||||
print("ZIP nicht gelöscht, da nicht alle Ordner verarbeitet wurden")
|
||||
|
||||
# product.json anpassen
|
||||
for f in folders:
|
||||
prod_json = os.path.join(f, 'resources', 'app', 'product.json')
|
||||
if os.path.exists(prod_json):
|
||||
with open(prod_json, 'r', encoding='utf-8') as pj:
|
||||
data = json.load(pj)
|
||||
|
||||
data['extensionsGallery'] = {
|
||||
'serviceUrl': 'https://marketplace.visualstudio.com/_apis/public/gallery',
|
||||
'itemUrl': 'https://marketplace.visualstudio.com/items'
|
||||
}
|
||||
|
||||
with open(prod_json, 'w', encoding='utf-8') as pj:
|
||||
json.dump(data, pj, indent=4)
|
||||
|
||||
input("Fertig. Beliebige Taste drücken...")
|
||||
Reference in new issue
Block a user