1 #!/usr/bin/env nix-shell
2 #!nix-shell -i python3 -p python3Packages.requests python3Packages.dataclasses-json
5 from dataclasses
import dataclass
, field
6 from datetime
import datetime
7 from pathlib
import Path
8 from typing
import Any
, Dict
, List
, Optional
11 from dataclasses_json
import DataClassJsonMixin
, LetterCase
, config
12 from marshmallow
import fields
16 class Download(DataClassJsonMixin
):
23 class Version(DataClassJsonMixin
):
27 time
: datetime
= field(
29 encoder
=datetime
.isoformat
,
30 decoder
=datetime
.fromisoformat
,
31 mm_field
=fields
.DateTime(format
="iso"),
34 release_time
: datetime
= field(
36 encoder
=datetime
.isoformat
,
37 decoder
=datetime
.fromisoformat
,
38 mm_field
=fields
.DateTime(format
="iso"),
39 letter_case
=LetterCase
.CAMEL
,
43 def get_manifest(self
) -> Any
:
44 """Return the version's manifest."""
45 response
= requests
.get(self
.url
)
46 response
.raise_for_status()
47 return response
.json()
49 def get_downloads(self
) -> Dict
[str, Download
]:
51 Return all downloadable files from the version's manifest, in Download
55 download_name
: Download
.from_dict(download_info
)
56 for download_name
, download_info
in self
.get_manifest()["downloads"].items()
59 def get_java_version(self
) -> Any
:
61 Return the java version specified in a version's manifest, if it is
62 present. Versions <= 1.6 do not specify this.
64 return self
.get_manifest().get("javaVersion", {}).get("majorVersion", None)
66 def get_server(self
) -> Optional
[Download
]:
68 If the version has a server download available, return the Download
69 object for the server download. If the version does not have a server
70 download avilable, return None.
72 downloads
= self
.get_downloads()
73 if "server" in downloads
:
74 return downloads
["server"]
78 def get_versions() -> List
[Version
]:
79 """Return a list of Version objects for all available versions."""
80 response
= requests
.get(
81 "https://launchermeta.mojang.com/mc/game/version_manifest.json"
83 response
.raise_for_status()
84 data
= response
.json()
85 return [Version
.from_dict(version
) for version
in data
["versions"]]
88 def get_major_release(version_id
: str) -> str:
90 Return the major release for a version. The major release for 1.17 and
93 if not len(version_id
.split(".")) >= 2:
94 raise ValueError(f
"version not in expected format: '{version_id}'")
95 return ".".join(version_id
.split(".")[:2])
98 def group_major_releases(releases
: List
[Version
]) -> Dict
[str, List
[Version
]]:
100 Return a dictionary containing each version grouped by each major release.
101 The key "1.17" contains a list with two Version objects, one for "1.17"
102 and another for "1.17.1".
104 groups
: Dict
[str, List
[Version
]] = {}
105 for release
in releases
:
106 major_release
= get_major_release(release
.id)
107 if major_release
not in groups
:
108 groups
[major_release
] = []
109 groups
[major_release
].append(release
)
113 def get_latest_major_releases(releases
: List
[Version
]) -> Dict
[str, Version
]:
115 Return a dictionary containing the latest version for each major release.
116 The latest major release for 1.16 is 1.16.5, so the key "1.16" contains a
117 Version object for 1.16.5.
121 (release
for release
in releases
if get_major_release(release
.id) == major_release
),
122 key
=lambda x
: tuple(map(int, x
.id.split('.'))),
124 for major_release
in group_major_releases(releases
)
128 def generate() -> Dict
[str, Dict
[str, str]]:
130 Return a dictionary containing the latest url, sha1 and version for each major
133 versions
= get_versions()
135 filter(lambda version
: version
.type == "release", versions
)
136 ) # remove snapshots and betas
137 latest_major_releases
= get_latest_major_releases(releases
)
140 version
: Download
.schema().dump(download_info
) # Download -> dict
141 for version
, download_info
in {
142 version
: value
.get_server()
143 for version
, value
in latest_major_releases
.items()
145 if download_info
is not None # versions < 1.2 do not have a server
147 for server
in servers
.values():
148 del server
["size"] # don't need it
150 for version
, server
in servers
.items():
151 server
["version"] = latest_major_releases
[version
].id
152 server
["javaVersion"] = latest_major_releases
[version
].get_java_version()
156 if __name__
== "__main__":
157 with
open(Path(__file__
).parent
/ "versions.json", "w") as file:
158 json
.dump(generate(), file, indent
=2)