mcmu 2.0.0.dev2__tar.gz → 2.0.0.dev3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## [v2.0.0.dev3] - 2026-03-18
4
+
5
+ ### Added
6
+
7
+ * Now installs a mods required dependency's
8
+
9
+ ### Changed
10
+
11
+ * Changed help message to display version at bottom
12
+
3
13
  ## [v2.0.0.dev2] - 2026-03-17
4
14
 
5
15
  ### Changed
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcmu
3
- Version: 2.0.0.dev2
3
+ Version: 2.0.0.dev3
4
4
  Summary: A utility to update Minecraft java edition mods from Modrinth
5
5
  Project-URL: homepage, https://github.com/Josiah-Jarvis/MCMU
6
6
  Project-URL: source, https://github.com/Josiah-Jarvis/MCMU/
7
7
  Project-URL: download, https://pypi.org/project/mcmu/#files
8
8
  Project-URL: changelog, https://github.com/Josiah-Jarvis/MCMU/blob/master/CHANGELOG.md
9
- Project-URL: releasenotes, https://github.com/Josiah-Jarvis/MCMU/compare/v2.0.0.dev1...v2.0.0.dev2
9
+ Project-URL: releasenotes, https://github.com/Josiah-Jarvis/MCMU/compare/v2.0.0.dev2...v2.0.0.dev3
10
10
  Project-URL: documentation, https://github.com/Josiah-Jarvis/MCMU/blob/master/README.md
11
11
  Project-URL: issues, https://github.com/Josiah-Jarvis/MCMU/issues
12
12
  Author: Josiah Jarvis
@@ -4,11 +4,10 @@
4
4
 
5
5
  ## In Progress
6
6
 
7
- [ ] Install mod dependencies
8
- [ ] Get rid of the config file (might have found a solution!)
9
-
10
7
  ## Completed
11
8
 
9
+ [x] Install mod dependencies - 2026-03-18
10
+ [x] Get rid of the config file (might have found a solution!) - 2026-03-17
12
11
  [x] Add comments to the code - 2026-03-16
13
12
  [x] Search mods from Modrinth - 2026-03-15
14
13
  [x] Add logging - 2026-03-11
@@ -51,7 +51,7 @@ homepage = "https://github.com/Josiah-Jarvis/MCMU"
51
51
  source = "https://github.com/Josiah-Jarvis/MCMU/"
52
52
  download = "https://pypi.org/project/mcmu/#files"
53
53
  changelog = "https://github.com/Josiah-Jarvis/MCMU/blob/master/CHANGELOG.md"
54
- releasenotes = "https://github.com/Josiah-Jarvis/MCMU/compare/v2.0.0.dev1...v2.0.0.dev2" # Change before every release
54
+ releasenotes = "https://github.com/Josiah-Jarvis/MCMU/compare/v2.0.0.dev2...v2.0.0.dev3" # Change before every release
55
55
  documentation = "https://github.com/Josiah-Jarvis/MCMU/blob/master/README.md"
56
56
  issues = "https://github.com/Josiah-Jarvis/MCMU/issues"
57
57
 
@@ -10,13 +10,17 @@ from requests import get # Get files from the CDN
10
10
  from argparse import ArgumentParser # Command line arguments class
11
11
  from .ModrinthAPI import ModrinthAPI # Modrinth API code (Local import)
12
12
 
13
- __version__ = "2.0.0.dev2"
13
+ __version__ = "2.0.0.dev3"
14
14
  __author__ = "Josiah Jarvis"
15
15
 
16
16
  logger = getLogger(__name__)
17
- basicConfig(format="%(levelname)s: %(message)s") # Set logging config
17
+ basicConfig(format="%(levelname)s: %(message)s") # Set logging format
18
18
 
19
- parser = ArgumentParser(prog=f"MCMU {__version__}", description="Downloads Minecraft mods from Modrinth")
19
+ parser = ArgumentParser(
20
+ prog="mcmu",
21
+ epilog=f"Version: {__version__}",
22
+ description="Downloads Minecraft mods from Modrinth"
23
+ )
20
24
  group = parser.add_mutually_exclusive_group() # Get mutually exclusive group set up
21
25
  group.add_argument("-u", "--update", help="Updates installed mods", action="store_true")
22
26
  group.add_argument("-r", "--remove", help="Removes an installed mod")
@@ -76,12 +80,23 @@ def list_mods(mod_path: Path):
76
80
  return mods
77
81
 
78
82
 
83
+ def install_mod(file: str, path: Path):
84
+ response = get(file, stream=True) # Get mod jar file
85
+ if response.status_code != 200:
86
+ return False
87
+ with open(path, 'wb') as file: # Write to the jar file
88
+ for chunk in response.iter_content(chunk_size=1024):
89
+ if chunk:
90
+ file.write(chunk)
91
+ return True
92
+
93
+
79
94
  def main():
80
95
  """Main function
81
96
 
82
97
  Returns:
83
- 1: Failure
84
98
  0: Success
99
+ 1: Failure
85
100
  """
86
101
  mod_path = Path(args.minecraft_dir, "mods/") # Path to folder where the mod jar's are stored
87
102
  mods = list_mods(mod_path)
@@ -92,25 +107,37 @@ def main():
92
107
  for mod_name in mods:
93
108
  latest_version = check_update(mod_name, mods[mod_name]['version']) # Check for update
94
109
  if latest_version == 404: # Thats weird
95
- print("Mod does not exist on Modrinth.")
96
- sys.exit(1)
110
+ print(f"Mod: {mod_name}does not exist on Modrinth.")
97
111
  elif latest_version is None: # If latest version is None no newer version was found
98
112
  print("No version found for the specified game version and loader.")
99
- return 1
100
113
  elif latest_version: # If latest version is a dict it should return True
101
114
  old_file = Path(mod_path, mods[mod_name]['file']) # Path to the old mod file
102
115
  additional_storage = latest_version['files'][0]['size'] - old_file.stat().st_size # Calculate how much more storage will be taken up
103
116
  if input(f"{mods[mod_name]['name']} will take up: {additional_storage} additional bytes, would you like to install? [Y/n]: ") is ("" or "Y"): # Ask them if they want to install it
104
- old_file.unlink() # Delete old file
105
- response = get(latest_version['files'][0]['url'], stream=True) # Get the new file
106
- if response.status_code != 200: # If its not 200 fail
107
- print(f"Failed to download the mod. Status code: {response.status_code}")
117
+ for dependency in latest_version['dependencies']:
118
+ mod_data = ModAPI.project(dependency['project_id'])
119
+ if (not mod_data['slug'] in mods) and (dependency['dependency_type'] == "required"):
120
+ dependency_latest_version = check_update(mod_data['slug'], 0)
121
+ mod_jar_file = Path(mod_path, f"{mod_data['slug']}_version_{dependency_latest_version['version_number']}.jar")
122
+ if install_mod(dependency_latest_version['files'][0]['url'], mod_jar_file):
123
+ print(f"\tDownloaded required dependency at {mod_jar_file} successfully.") # Print the success
124
+ else:
125
+ print("Failed to download required dependency.")
126
+ return 1
127
+ elif (not mod_data['slug'] in mods) and (dependency['dependency_type'] == "optional"):
128
+ dependency_latest_version = check_update(mod_data['slug'], 0)
129
+ print(f"Optional dependency: {mod_data['slug']} not installed")
130
+ elif (mod_data['slug'] in mods) and (dependency['dependency_type'] == "incompatible"):
131
+ print(f"Incompatible dependency: {mod_data['slug']} installed, please remove.")
132
+ return 1
133
+ mod_jar_file = Path(mod_path, f"{args.install}_version_{latest_version['version_number']}.jar")
134
+ if install_mod(latest_version['files'][0]['url'], mod_jar_file):
135
+ print(f"Downloaded mod at {mod_jar_file} successfully.") # Print the success
136
+ print(f"Deleting old file: {old_file}")
137
+ old_file.unlink() # Delete old file
138
+ else:
139
+ print("Failed to download the mod.")
108
140
  return 1
109
- with open(f"{mod_path}/{args.install}_version_{latest_version['version_number']}.jar", 'wb') as file: # IF everything good write to the file
110
- for chunk in response.iter_content(chunk_size=1024):
111
- if chunk:
112
- file.write(chunk)
113
- print(f"Downloaded mod at {mod_path}/{args.install}_version_{latest_version['version_number']}.jar successfully.") # Print the success
114
141
  else:
115
142
  print("Canceling.")
116
143
  return 0
@@ -126,15 +153,28 @@ def main():
126
153
  return 1
127
154
  if latest_version: # Should be True if it is a dict
128
155
  if input(f"{args.install} will take up: {latest_version['files'][0]['size']} bytes, would you like to install? [Y/n]: ") is ("" or "Y"): # Ask if the want to install it
129
- response = get(latest_version['files'][0]['url'], stream=True) # Get mod jar file
130
- if response.status_code != 200:
131
- print(f"Failed to download the mod. Status code: {response.status_code}")
132
- return False
133
- with open(f"{mod_path}/{args.install}_version_{latest_version['version_number']}.jar", 'wb') as file: # Write to the jar file
134
- for chunk in response.iter_content(chunk_size=1024):
135
- if chunk:
136
- file.write(chunk)
137
- print(f"Downloaded mod at {mod_path}/{args.install}_version_{latest_version['version_number']}.jar successfully.") # Print the success
156
+ for dependency in latest_version['dependencies']:
157
+ mod_data = ModAPI.project(dependency['project_id'])
158
+ if (not mod_data['slug'] in mods) and (dependency['dependency_type'] == "required"):
159
+ dependency_latest_version = check_update(mod_data['slug'], 0)
160
+ mod_jar_file = Path(mod_path, f"{mod_data['slug']}_version_{dependency_latest_version['version_number']}.jar")
161
+ if install_mod(dependency_latest_version['files'][0]['url'], mod_jar_file):
162
+ print(f"\tDownloaded required dependency at {mod_jar_file} successfully.") # Print the success
163
+ else:
164
+ print("Failed to download required dependency.")
165
+ return 1
166
+ elif (not mod_data['slug'] in mods) and (dependency['dependency_type'] == "optional"):
167
+ dependency_latest_version = check_update(mod_data['slug'], 0)
168
+ print(f"Optional dependency: {mod_data['slug']} not installed")
169
+ elif (mod_data['slug'] in mods) and (dependency['dependency_type'] == "incompatible"):
170
+ print(f"Incompatible dependency: {mod_data['slug']} installed, please remove.")
171
+ return 1
172
+ mod_jar_file = Path(mod_path, f"{args.install}_version_{latest_version['version_number']}.jar")
173
+ if install_mod(latest_version['files'][0]['url'], mod_jar_file):
174
+ print(f"Downloaded mod at {mod_jar_file} successfully.") # Print the success
175
+ else:
176
+ print("Failed to download the mod.")
177
+ return 1
138
178
  else:
139
179
  print("Canceling.")
140
180
  return 0
@@ -152,8 +192,6 @@ def main():
152
192
  else:
153
193
  print("Canceling.")
154
194
  return 0
155
- except FileNotFoundError: # Oops file not found must already be deleted
156
- logger.warn("Mod's file already deleted.")
157
195
  except PermissionError: # No permission to delete the file
158
196
  logger.critical("No permission to delete mod file.")
159
197
  return 1
@@ -165,7 +203,7 @@ def main():
165
203
  elif args.search: # If we are searching for a mod
166
204
  response = ModAPI.search(args.search, '[["categories:fabric"],["project_type:mod"]]')
167
205
  if response == 410: # That means the API is deprecated: https://docs.modrinth.com/api/
168
- logger.critical("API is deprecated, you probably have to update MCMU")
206
+ logger.critical("API is deprecated, try updating MCMU")
169
207
  elif response == 400: # Response of 400 means request was invalid: https://docs.modrinth.com/api/operations/searchprojects/
170
208
  logger.error("Request invalid") # Print error
171
209
  else:
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes