cometusb 1.0__py3-none-any.whl
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.
- cometusb/__init__.py +1 -0
- cometusb/cometusb.py +442 -0
- cometusb-1.0.dist-info/METADATA +95 -0
- cometusb-1.0.dist-info/RECORD +8 -0
- cometusb-1.0.dist-info/WHEEL +5 -0
- cometusb-1.0.dist-info/entry_points.txt +2 -0
- cometusb-1.0.dist-info/licenses/LICENSE +21 -0
- cometusb-1.0.dist-info/top_level.txt +1 -0
cometusb/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.0"
|
cometusb/cometusb.py
ADDED
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import json
|
|
3
|
+
import sys
|
|
4
|
+
import argparse
|
|
5
|
+
import glob
|
|
6
|
+
from tabulate import tabulate
|
|
7
|
+
import requests
|
|
8
|
+
from tqdm import tqdm
|
|
9
|
+
import zipfile as zf
|
|
10
|
+
import os
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def main() -> None:
|
|
14
|
+
parser = argparse.ArgumentParser(
|
|
15
|
+
prog= "CometUSB.",
|
|
16
|
+
description="Create linux bootable USB."
|
|
17
|
+
)
|
|
18
|
+
parser.add_argument("-l", "--list-os", action="store_true", help="Shows list of the available Operating Systems.")
|
|
19
|
+
parser.add_argument("-o", "--operating-system", help="Name of the Operating System.")
|
|
20
|
+
parser.add_argument("-b","--bios-type", help="BIOS type (e.g., UEFI or Legacy), check what your TARGET SYSTEM supports.")
|
|
21
|
+
args = parser.parse_args()
|
|
22
|
+
|
|
23
|
+
# List of available Opereating System
|
|
24
|
+
OS = ["linuxmint"]
|
|
25
|
+
|
|
26
|
+
# Shows list of available Operating Systems.
|
|
27
|
+
if args.list_os:
|
|
28
|
+
for number in range(len(OS)):
|
|
29
|
+
print(number + 1, OS[number], sep=". ")
|
|
30
|
+
sys.exit() # Exits after showing the OS list
|
|
31
|
+
|
|
32
|
+
if not (args.operating_system and args.bios_type):
|
|
33
|
+
sys.exit("Argument missing\nType cometusb -h to see the usage.")
|
|
34
|
+
operating_system = Operating_System(args.operating_system.lower(), args.bios_type.lower())
|
|
35
|
+
print(operating_system)
|
|
36
|
+
operating_system.create()
|
|
37
|
+
|
|
38
|
+
class Operating_System():
|
|
39
|
+
"""
|
|
40
|
+
This is the main object that contains all the information of the Operating System, target device and it's partition, bios-type etc.
|
|
41
|
+
Once the create method is invoked it will start the process which includes wiping and formatting the target disk, downloading and extracting of files and
|
|
42
|
+
applying bootloader.
|
|
43
|
+
"""
|
|
44
|
+
def __init__(self, name: str, bios_type: str):
|
|
45
|
+
self.name = name
|
|
46
|
+
self._path_url = f"https://github.com/CometUSB/CometUSB/releases/download/{self.name}/"
|
|
47
|
+
self.disk_size_reqd = self.name
|
|
48
|
+
self.bios_type = bios_type
|
|
49
|
+
self.partition_style = self.bios_type
|
|
50
|
+
self.target_disk = get_disk_details()
|
|
51
|
+
self.disk_partitions = format_disk(self.target_disk, self.bios_type, self.disk_size_reqd) # Dictionary of newly created partitions with labels
|
|
52
|
+
self.files = self.name
|
|
53
|
+
self._architecture= "64 Bit"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def __str__(self) -> str:
|
|
57
|
+
return f"\nOS = {self.name.upper()}\nArchitecture = {self._architecture}\nTarget System BIOS Type = {self.bios_type}\nTarget Device = {self.target_disk}\nPartition Style = {self.partition_style.upper()}\nFiles to be Downloaded = {[name for name in self.files.keys()]}\n"
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def name(self) -> str:
|
|
61
|
+
return self._name
|
|
62
|
+
|
|
63
|
+
@name.setter
|
|
64
|
+
def name(self, name: str) -> None:
|
|
65
|
+
"""
|
|
66
|
+
Sets name of the Operating System.
|
|
67
|
+
|
|
68
|
+
:param name: Name of the Operating System.
|
|
69
|
+
:type name: str
|
|
70
|
+
"""
|
|
71
|
+
OS: list = ["linuxmint"]
|
|
72
|
+
if name not in OS:
|
|
73
|
+
sys.exit("[!] Invalid or Unsupported Operating System.\nEnter 'cometusb.py --OS-list' without quotes to see the supported list of Operating systems")
|
|
74
|
+
|
|
75
|
+
self._name: str = name
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def partition_style(self) -> str:
|
|
79
|
+
return self._partition_style
|
|
80
|
+
|
|
81
|
+
@partition_style.setter
|
|
82
|
+
def partition_style(self, bios_type: str) -> None:
|
|
83
|
+
"""
|
|
84
|
+
Sets the partition style i.e, MBR or GPT.
|
|
85
|
+
|
|
86
|
+
:param bios_type: BIOS firmare type i.e, UEFI or Legacy.
|
|
87
|
+
:type bios_type: str
|
|
88
|
+
"""
|
|
89
|
+
if bios_type == "uefi":
|
|
90
|
+
self._partition_style: str = "GPT"
|
|
91
|
+
|
|
92
|
+
else:
|
|
93
|
+
self._partition_style: str = "MBR"
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def bios_type(self) -> str:
|
|
98
|
+
return self._bios_type
|
|
99
|
+
@bios_type.setter
|
|
100
|
+
def bios_type(self, bios_type: str) -> None:
|
|
101
|
+
"""
|
|
102
|
+
Sets the BIOS type.
|
|
103
|
+
|
|
104
|
+
:param bios_type: BIOS firmare type i.e, UEFI or Legacy.
|
|
105
|
+
:type bios_type: str
|
|
106
|
+
"""
|
|
107
|
+
if bios_type in ["uefi", "legacy"]:
|
|
108
|
+
self._bios_type: str = bios_type
|
|
109
|
+
|
|
110
|
+
else:
|
|
111
|
+
sys.exit("[!] Invalid BIOS type.")
|
|
112
|
+
|
|
113
|
+
@property
|
|
114
|
+
def files(self) -> dict:
|
|
115
|
+
return self._files
|
|
116
|
+
|
|
117
|
+
@files.setter
|
|
118
|
+
def files(self, name: str) -> None:
|
|
119
|
+
"""
|
|
120
|
+
Selects files of given Operating System for Installation.
|
|
121
|
+
|
|
122
|
+
:param name: Name of the Operating System.
|
|
123
|
+
:type name: str
|
|
124
|
+
"""
|
|
125
|
+
if len(self.disk_partitions) == 2:
|
|
126
|
+
self._boot_partition, self._file_partition = self.disk_partitions.keys()
|
|
127
|
+
elif len(self.disk_partitions) == 1:
|
|
128
|
+
self._file_partition: str = [partition for partition in self.disk_partitions.keys()][0] # files_partition and boot_partition are the labels of boot partition and files partitions
|
|
129
|
+
self._boot_partition: str = self._file_partition
|
|
130
|
+
|
|
131
|
+
# Contains OS name and it's corresponding files and it's download path.
|
|
132
|
+
OS_FILES: dict = {
|
|
133
|
+
"linuxmint": {
|
|
134
|
+
"boot.zip": f"/mnt/{self._boot_partition}/",
|
|
135
|
+
"directories.zip":f"/mnt/{self._file_partition}/",
|
|
136
|
+
"filesystem.squashfs.aa": f"/mnt/{self._file_partition}/",
|
|
137
|
+
"filesystem.squashfs.ab": f"/mnt/{self._file_partition}/"
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
self._files: dict = OS_FILES[name]
|
|
142
|
+
|
|
143
|
+
@property
|
|
144
|
+
def disk_size_reqd(self):
|
|
145
|
+
return self._disk_size_reqd
|
|
146
|
+
@disk_size_reqd.setter
|
|
147
|
+
def disk_size_reqd(self, name):
|
|
148
|
+
"""
|
|
149
|
+
Calculates the size of disk required for process.
|
|
150
|
+
|
|
151
|
+
:param name: Name of the Operating System.
|
|
152
|
+
"""
|
|
153
|
+
OS_FILES = {
|
|
154
|
+
"linuxmint": [
|
|
155
|
+
"boot.zip",
|
|
156
|
+
"directories.zip",
|
|
157
|
+
"filesystem.squashfs.aa",
|
|
158
|
+
"filesystem.squashfs.ab"
|
|
159
|
+
]
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
total_size: int = 0
|
|
163
|
+
for filename in OS_FILES[name]:
|
|
164
|
+
with requests.get(self._path_url + filename, stream = True) as response:
|
|
165
|
+
response.raise_for_status()
|
|
166
|
+
total_size += int(response.headers.get("content-length", 0))
|
|
167
|
+
|
|
168
|
+
total_size = total_size / (1024 * 1024 * 1024) # Total files size.
|
|
169
|
+
|
|
170
|
+
USB_SIZES = [4, 8, 16, 32, 64]
|
|
171
|
+
for usb_size in USB_SIZES:
|
|
172
|
+
if usb_size / (total_size * 2) > 1: # Divided by twice of the total_size because of merging of OS images will require space for merged file. Although splitted image files will be removed.
|
|
173
|
+
self._disk_size_reqd = usb_size
|
|
174
|
+
break
|
|
175
|
+
|
|
176
|
+
@property
|
|
177
|
+
def target_disk(self) -> str:
|
|
178
|
+
return self._target_disk
|
|
179
|
+
|
|
180
|
+
@target_disk.setter
|
|
181
|
+
def target_disk(self, target_disk: str) -> None:
|
|
182
|
+
"""
|
|
183
|
+
Sets the target disk.
|
|
184
|
+
|
|
185
|
+
:param target_disk: Name of the target disk.
|
|
186
|
+
:type target_disk: str
|
|
187
|
+
"""
|
|
188
|
+
self._target_disk: str = target_disk
|
|
189
|
+
|
|
190
|
+
@property
|
|
191
|
+
def disk_partitions(self) -> dict:
|
|
192
|
+
return self._disk_partitions
|
|
193
|
+
|
|
194
|
+
@disk_partitions.setter
|
|
195
|
+
def disk_partitions(self, partitions: dict) -> None:
|
|
196
|
+
"""
|
|
197
|
+
Sets partitions with of the target disk.
|
|
198
|
+
|
|
199
|
+
:param partitions: Dictionary of all the newly created partition of target disk with corresponding labels.
|
|
200
|
+
:type partitions: dict
|
|
201
|
+
"""
|
|
202
|
+
self._disk_partitions: dict = partitions
|
|
203
|
+
|
|
204
|
+
def bootloader(self) -> None:
|
|
205
|
+
"""
|
|
206
|
+
Applies bootloader to the partition of the target disk containing boot files.
|
|
207
|
+
"""
|
|
208
|
+
print(f"Applying bootloader on {self.target_disk} for {self.bios_type} systems...")
|
|
209
|
+
if self.bios_type == "uefi":
|
|
210
|
+
subprocess.run(["sudo", "grub-install" ,"--target=x86_64-efi", f"--efi-directory=/mnt/{self._boot_partition}", f"--boot-directory=/mnt/{self._boot_partition}/boot", "--removable"])
|
|
211
|
+
|
|
212
|
+
else:
|
|
213
|
+
subprocess.run(["sudo", "grub-install" ,"--target=i386-pc", f"--boot-directory=/mnt/{self._boot_partition}/boot", f"{self.target_disk}"])
|
|
214
|
+
|
|
215
|
+
def create(self) -> None:
|
|
216
|
+
"""
|
|
217
|
+
This method calls all the required functions in sequence to perform the necessary operations to make the bootable media.
|
|
218
|
+
"""
|
|
219
|
+
# Mounting newly created partitions.
|
|
220
|
+
mount_usb(self.disk_partitions)
|
|
221
|
+
|
|
222
|
+
# Disk configuration info.
|
|
223
|
+
print(f"\n[*] Disk {self.target_disk} configuration.")
|
|
224
|
+
subprocess.run(["lsblk", self.target_disk, "-o", "NAME,SIZE,FSTYPE,FSVER,LABEL,MOUNTPOINTS"])
|
|
225
|
+
|
|
226
|
+
for filename, download_dir in self.files.items():
|
|
227
|
+
print() #To create gap between progress bars.
|
|
228
|
+
print(f"Downloading {filename} into {download_dir}")
|
|
229
|
+
downloader(self._path_url + filename, download_dir)
|
|
230
|
+
print() #To create gap between progress bars.
|
|
231
|
+
if filename.endswith(".zip"):
|
|
232
|
+
# Extracting to create the directory tree structure
|
|
233
|
+
extractor(download_dir + filename, download_dir)
|
|
234
|
+
print() #To create gap between progress bars.
|
|
235
|
+
os.remove(download_dir + filename) # Removing the zip file after extracting to free space in the USB.
|
|
236
|
+
if filename.endswith(".aa"):
|
|
237
|
+
image_name, download_dir = filename.rstrip(".aa"), download_dir
|
|
238
|
+
print(f"[*] Making OS Image {image_name} ready for installation.\n[*] This may take a while depending upon your removable disk {self.target_disk}.")
|
|
239
|
+
subprocess.run(f"sudo cat {download_dir}{image_name}.* > {download_dir}casper/{image_name}", shell=True, stdout=subprocess.DEVNULL)
|
|
240
|
+
|
|
241
|
+
print("[*] Cleaning unnecessary file...")
|
|
242
|
+
for splitted_image_file in glob.glob(f"{download_dir}{image_name}.*"):
|
|
243
|
+
os.remove(splitted_image_file)
|
|
244
|
+
|
|
245
|
+
# Applying bootloader
|
|
246
|
+
self.bootloader()
|
|
247
|
+
|
|
248
|
+
print("Media created succesfully\nNOTE: Linux disk sometimes is not detected in BIOS, try disabling secure boot of your BIOS if facing any issues while booting.")
|
|
249
|
+
|
|
250
|
+
def get_disk_details() -> str:
|
|
251
|
+
"""
|
|
252
|
+
Executes the 'lsblk' command to retrieve detailed information for physical
|
|
253
|
+
block disks using JSON output for robust, programmatic parsing.
|
|
254
|
+
|
|
255
|
+
:return: A string of target device.
|
|
256
|
+
:rtype: str
|
|
257
|
+
"""
|
|
258
|
+
|
|
259
|
+
LSBLK_CMD = ['lsblk', '-d','-J', '-o', 'NAME,SIZE,VENDOR,MODEL,RM']
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
# Execute the command, capture output, and ensure success
|
|
263
|
+
result = subprocess.run(
|
|
264
|
+
LSBLK_CMD,
|
|
265
|
+
capture_output=True,
|
|
266
|
+
text=True,
|
|
267
|
+
check=True
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
# Parse the JSON output into a Python dictionary
|
|
271
|
+
data = json.loads(result.stdout)
|
|
272
|
+
|
|
273
|
+
disks = [
|
|
274
|
+
[
|
|
275
|
+
disk.get('name', 'N/A'),
|
|
276
|
+
disk.get('size', 'N/A'),
|
|
277
|
+
disk.get('vendor', 'N/A'),
|
|
278
|
+
disk.get('model', 'N/A')
|
|
279
|
+
]
|
|
280
|
+
for disk in data.get('blockdevices', []) if disk.get('rm', 'N/A') == True
|
|
281
|
+
]
|
|
282
|
+
|
|
283
|
+
if not disks:
|
|
284
|
+
sys.exit("No USB/removable media found.")
|
|
285
|
+
headers = [header.capitalize() for header in data.get("blockdevices")[0].keys()]
|
|
286
|
+
headers[2] = "Interface" # Renaming Vendor column to Interface
|
|
287
|
+
print(tabulate(disks, headers=headers, tablefmt="grid"))
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
return f"/dev/{input("Enter disk: ")}"
|
|
291
|
+
|
|
292
|
+
def format_disk(disk: str, bios_type: str, size: int) -> str:
|
|
293
|
+
"""
|
|
294
|
+
Formats the target disk, converts it into GPT or MBR then create partitions and filesystems compatible for the given BIOS Type i.e, UEFI or Legacy.
|
|
295
|
+
|
|
296
|
+
:param disk: Name of target disk.
|
|
297
|
+
:type disk: str
|
|
298
|
+
:param bios_type: BIOS firmware type.
|
|
299
|
+
:type bios_type: str
|
|
300
|
+
:param size: Size of the target disk required.
|
|
301
|
+
:return: Dictionary of all the partition of target disk with corresponding labels.
|
|
302
|
+
:rtype: str
|
|
303
|
+
"""
|
|
304
|
+
# Confirming to Format the USB
|
|
305
|
+
print(f"\n[!] MINIMUM {size} GB Disk is required.\n[*] This will ERASE all data on {disk}")
|
|
306
|
+
if input("Type 'yes' to continue: ").strip().lower() != "yes":
|
|
307
|
+
sys.exit("Aborted by user.")
|
|
308
|
+
|
|
309
|
+
partitions = glob.glob(disk + "?")
|
|
310
|
+
unmount_usb(partitions)
|
|
311
|
+
try:
|
|
312
|
+
|
|
313
|
+
# Wipe partition table
|
|
314
|
+
print(f"\n[*] Wiping disk {disk}")
|
|
315
|
+
subprocess.run(["sudo", "wipefs", "-a", disk], check=True)
|
|
316
|
+
|
|
317
|
+
print(f"\n[*] Creating partitions for {bios_type} systems...")
|
|
318
|
+
if bios_type == "uefi":
|
|
319
|
+
# Create new partition table and partition
|
|
320
|
+
subprocess.run(["sudo", "parted", "-s", disk, "mklabel", "gpt"], check=True)
|
|
321
|
+
subprocess.run(["sudo", "parted", "-s", disk, "mkpart", "primary", "1MiB", "501MiB"], check=True)
|
|
322
|
+
partition = glob.glob(disk + "?")
|
|
323
|
+
boot_partition = partition[0]
|
|
324
|
+
subprocess.run(["sudo", "parted", "-s", disk, "mkpart", "primary", "501MiB", "100%"], check=True)
|
|
325
|
+
partition = glob.glob(disk + "?")
|
|
326
|
+
partition.remove(boot_partition)
|
|
327
|
+
files_partition = partition[0]
|
|
328
|
+
|
|
329
|
+
# Refreshing the partitions
|
|
330
|
+
subprocess.run(["sudo", "partprobe", disk], check=True)
|
|
331
|
+
subprocess.run(["sudo", "udevadm", "settle"], check=True)
|
|
332
|
+
|
|
333
|
+
# Creating the filesystems
|
|
334
|
+
print(f"\n[*] Creating filesystems ...")
|
|
335
|
+
subprocess.run(["sudo", "mkfs.fat", "-F", "32", "-n", "COMET_BOOT", boot_partition], check=True)
|
|
336
|
+
subprocess.run(["sudo", "parted", "-s", disk, "set", "1", "esp", "on"], check=True)
|
|
337
|
+
subprocess.run(["sudo", "mkfs.ntfs", "-f", files_partition, "-L", "COMET_FILES"], check=True)
|
|
338
|
+
|
|
339
|
+
elif bios_type == "legacy":
|
|
340
|
+
# Create new partition table and partition
|
|
341
|
+
subprocess.run(["sudo", "parted", "-s", disk, "mklabel", "msdos"], check=True)
|
|
342
|
+
subprocess.run(["sudo", "parted", "-s", disk, "mkpart", "primary", "0%", "100%"], check=True)
|
|
343
|
+
partition = glob.glob(disk + "?")
|
|
344
|
+
|
|
345
|
+
files_partition = partition[0] # Only one partition is here same for installation and boot files.
|
|
346
|
+
|
|
347
|
+
# Refreshing the partitions
|
|
348
|
+
subprocess.run(["sudo", "partprobe", disk], check=True)
|
|
349
|
+
subprocess.run(["sudo", "udevadm", "settle"], check=True)
|
|
350
|
+
|
|
351
|
+
# Creating the filesystems
|
|
352
|
+
print(f"\n[*] Creating filesystems ...")
|
|
353
|
+
|
|
354
|
+
subprocess.run(["sudo", "mkfs.ntfs", "-f", files_partition, "-L", "COMET"], check=True)
|
|
355
|
+
subprocess.run(["sudo", "parted", "-s", disk, "set", "1", "boot", "on"], check=True)
|
|
356
|
+
|
|
357
|
+
print(f"[*] USB {disk} formatted successfully!\n")
|
|
358
|
+
|
|
359
|
+
except subprocess.CalledProcessError:
|
|
360
|
+
sys.exit("[*] Something went wrong, please retry.")
|
|
361
|
+
|
|
362
|
+
if len(glob.glob(disk + "?")) == 2:
|
|
363
|
+
return {"COMET_BOOT": boot_partition, "COMET_FILES": files_partition}
|
|
364
|
+
else:
|
|
365
|
+
return {"COMET": files_partition}
|
|
366
|
+
|
|
367
|
+
def unmount_usb(partitions: list) -> None:
|
|
368
|
+
"""
|
|
369
|
+
Unmounts all the existing partitions of the target disk to intitate the formatting process by format_disk function.
|
|
370
|
+
|
|
371
|
+
:param partitions: List of all the partitions in the target disk.
|
|
372
|
+
:type partitions: list
|
|
373
|
+
"""
|
|
374
|
+
|
|
375
|
+
for part in partitions:
|
|
376
|
+
print(f"Unmounting: {part}")
|
|
377
|
+
subprocess.run(["sudo", "umount", "-f", part])
|
|
378
|
+
|
|
379
|
+
def mount_usb(partitions: dict) -> None:
|
|
380
|
+
"""
|
|
381
|
+
Mounts all the newly created partitions of the target disk by format_disk function.
|
|
382
|
+
|
|
383
|
+
:param partitions: Dictionary of all the newly created partition of target disk with corresponding labels.
|
|
384
|
+
:type partitions: dict
|
|
385
|
+
"""
|
|
386
|
+
for part_label in partitions.keys():
|
|
387
|
+
print(f"Mounting: {partitions[part_label]} on /mnt/{part_label}")
|
|
388
|
+
subprocess.run(["sudo", "mkdir", "-p", f"/mnt/{part_label}"])
|
|
389
|
+
result = subprocess.run(["sudo", "mount", partitions[part_label], f"/mnt/{part_label}"])
|
|
390
|
+
if result.returncode > 8:
|
|
391
|
+
sys.exit(f"\n[*] Failed to mount {partitions[part_label]} on /mnt/{part_label}.\n[*] Please retry...")
|
|
392
|
+
|
|
393
|
+
def downloader(url: str, download_dir: str) -> None:
|
|
394
|
+
"""
|
|
395
|
+
Downloads all the files from github release page of the corresponding operating system of the CometUSB organization.
|
|
396
|
+
|
|
397
|
+
:param url: Download URL of individual files.
|
|
398
|
+
:type url: str
|
|
399
|
+
:param download_dir: Download location of the given files.
|
|
400
|
+
:type download_dir: str
|
|
401
|
+
"""
|
|
402
|
+
with requests.get(url, stream = True) as response:
|
|
403
|
+
response.raise_for_status()
|
|
404
|
+
total_size: int = int(response.headers.get("content-length", 0))
|
|
405
|
+
chunk_size: int = 1024 * 200 # 200KB chunk for smoother progress update.
|
|
406
|
+
with open(f"{download_dir}{os.path.basename(url)}", "wb") as file, tqdm(
|
|
407
|
+
total = total_size,
|
|
408
|
+
unit = "B",
|
|
409
|
+
unit_scale = True,
|
|
410
|
+
unit_divisor = 1024,
|
|
411
|
+
desc = f"{os.path.basename(url)}"
|
|
412
|
+
) as progress:
|
|
413
|
+
for chunk in response.iter_content(chunk_size = chunk_size):
|
|
414
|
+
download = file.write(chunk)
|
|
415
|
+
progress.update(download)
|
|
416
|
+
|
|
417
|
+
def extractor(archive_path: str, extract_dir: str) -> None:
|
|
418
|
+
"""
|
|
419
|
+
Extracts the compressed archive to given location.
|
|
420
|
+
|
|
421
|
+
:param archive_path: Complete path of the compressed archives.
|
|
422
|
+
:type archive_path: str
|
|
423
|
+
:param extract_dir: Extract folder for the corresponding archive.
|
|
424
|
+
:type extract_dir: str
|
|
425
|
+
"""
|
|
426
|
+
with zf.ZipFile(archive_path, "r") as archive, tqdm(
|
|
427
|
+
total = sum(file_info.file_size for file_info in archive.infolist()),
|
|
428
|
+
unit = "B",
|
|
429
|
+
unit_scale = True,
|
|
430
|
+
unit_divisor = 1024,
|
|
431
|
+
desc = f"Extracting {os.path.basename(archive_path)}"
|
|
432
|
+
) as progress:
|
|
433
|
+
|
|
434
|
+
for file_info in archive.infolist():
|
|
435
|
+
|
|
436
|
+
archive.extract(file_info, path = extract_dir)
|
|
437
|
+
progress.update(file_info.file_size)
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
if __name__ == "__main__":
|
|
441
|
+
main()
|
|
442
|
+
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cometusb
|
|
3
|
+
Version: 1.0
|
|
4
|
+
Summary: Create Linux bootable USB from CLI
|
|
5
|
+
Author-email: Shahid Rahaman <shahidrahman333@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/CometUSB/CometUSB
|
|
8
|
+
Project-URL: Documentation, https://CometUSB.github.io/CometUSB/
|
|
9
|
+
Project-URL: Changelog, https://github.com/CometUSB/CometUSB/releases
|
|
10
|
+
Requires-Python: >=3.8
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Requires-Dist: requests
|
|
14
|
+
Requires-Dist: tqdm
|
|
15
|
+
Requires-Dist: tabulate
|
|
16
|
+
Dynamic: license-file
|
|
17
|
+
|
|
18
|
+
# CometUSB
|
|
19
|
+
##### _Python package for Linux distributions to create bootable USB._
|
|
20
|
+
_**See full documentation at: https://CometUSB.github.io/CometUSB/**_
|
|
21
|
+
## Description
|
|
22
|
+
This is a python package exclusively for linux distributions. It has list of linux distributions you can choose to create the bootable media. In the corresponding release section you will find the installation files of several linux distributions.
|
|
23
|
+
|
|
24
|
+
## Features
|
|
25
|
+
---
|
|
26
|
+
- Create UEFI with GPT and Legacy BIOS with MBR bootable USBs.
|
|
27
|
+
- Dual-partition layout: In case of UEFI system two partitions first small FAT32 for boot then NTFS in rest of the space for other files are created.
|
|
28
|
+
- Single-partition layout: In case of legacy systems only one NTFS partition is created in the entire disk for both boot and installation files.
|
|
29
|
+
- Automatically install GRUB for UEFI or legacy systems.
|
|
30
|
+
- Only shows removable disk to format which prevents wiping your main HDD/SSD. This avoids significant data loss.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
# Installation
|
|
35
|
+
|
|
36
|
+
CometUSB is a Python package available on PyPI. It requires Python 3.8 or higher.
|
|
37
|
+
|
|
38
|
+
### Prerequisites
|
|
39
|
+
|
|
40
|
+
Before installation, ensure you have:
|
|
41
|
+
|
|
42
|
+
* **Linux Distribution:** CometUSB is exclusively for Linux.
|
|
43
|
+
* **Python 3.8+:** Installed on your system.
|
|
44
|
+
* **Administrative Rights (`sudo`):** The tool requires `sudo` privileges to manage disks and partitions.
|
|
45
|
+
|
|
46
|
+
### Install via pip
|
|
47
|
+
|
|
48
|
+
The most straightforward way to install CometUSB is using `pip`:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pip install cometusb
|
|
52
|
+
or
|
|
53
|
+
# Using python -m pip (recommended for virtual environments)
|
|
54
|
+
python -m pip install cometusb
|
|
55
|
+
or
|
|
56
|
+
python3 -m pip install cometusb
|
|
57
|
+
```
|
|
58
|
+
_If none of the above commands work, find out how to install python package in your system._
|
|
59
|
+
|
|
60
|
+
# Usage Guide
|
|
61
|
+
|
|
62
|
+
CometUSB is run directly from the command line, requiring administrative privileges (`sudo`) because it must access and format your physical disk devices.
|
|
63
|
+
|
|
64
|
+
### Basic Command Structure
|
|
65
|
+
|
|
66
|
+
You must always specify the Operating System name (`-o`) and the target system's BIOS type (`-b`).
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
`sudo cometusb -o <OS_NAME> -b <BIOS_TYPE>`
|
|
70
|
+
|
|
71
|
+
or
|
|
72
|
+
|
|
73
|
+
`sudo cometusb --operating-system <OS_Name> --bios-type <BIOS_TYPE>`
|
|
74
|
+
|
|
75
|
+
_e.g, `sudo cometusb -o linuxmint -b uefi`_
|
|
76
|
+
|
|
77
|
+
_Type `cometusb -h` or `cometusb --help` to see the usage_
|
|
78
|
+
|
|
79
|
+
_Type `cometusb --list-os` or `cometusb -l` to see the list of available Operating System_
|
|
80
|
+
|
|
81
|
+
# CLI Reference
|
|
82
|
+
|
|
83
|
+
### Required Arguments
|
|
84
|
+
|
|
85
|
+
| Option | Long Option | Description | Example |
|
|
86
|
+
| :--- | :--- | :--- | :--- |
|
|
87
|
+
| `-o` | `--operating-system` | **Name of the Operating System** to download and install.| `-o linuxmint` |
|
|
88
|
+
| `-b` | `--bios-type` | **BIOS type** (boot mode) for the target system. Specifies the partitioning scheme (GPT/MBR) and GRUB installation method. | `-b uefi` |
|
|
89
|
+
|
|
90
|
+
### Optional Arguments
|
|
91
|
+
|
|
92
|
+
| Option | Long Option | Description |
|
|
93
|
+
| :--- | :--- | :--- |
|
|
94
|
+
| `-l` | `--list-os` | Shows a list of the currently available and supported Operating Systems. |
|
|
95
|
+
| `-h` | `--help` | Show the help message and exit. |
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
cometusb/__init__.py,sha256=2HXaaHC9Xl9BqTkMCzF1KrnViBZbueeO8znZSwwWG4c,19
|
|
2
|
+
cometusb/cometusb.py,sha256=oz0YgeQYMggnQ8uKdLhk4iTx0t4yeD3lL2y6T4hUfRc,17444
|
|
3
|
+
cometusb-1.0.dist-info/licenses/LICENSE,sha256=8qFj_2PByO82AFLB_JtLyieuvxC3mx59F1GZrqyM_Do,1083
|
|
4
|
+
cometusb-1.0.dist-info/METADATA,sha256=ME3uj1gS9ZaVZtbI3PmKFhSln0cM19FdGyVGbwdJ8V8,3474
|
|
5
|
+
cometusb-1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
6
|
+
cometusb-1.0.dist-info/entry_points.txt,sha256=iQZz0EMQuvYR1q8t_c_EbDCMd6jDJxmtJMWQ1lrvrbc,52
|
|
7
|
+
cometusb-1.0.dist-info/top_level.txt,sha256=qFT-0B2ImDxACh5UPwEUzPGLRgYoIX_OLKPeMsdTkog,9
|
|
8
|
+
cometusb-1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 CometUSB by Shahid Rahaman
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
cometusb
|