metafetch 1.0.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.
@@ -0,0 +1,170 @@
1
+ Metadata-Version: 2.4
2
+ Name: metafetch
3
+ Version: 1.0.0
4
+ Summary: A beautiful, fast, and comprehensive system information tool
5
+ Home-page: https://github.com/volksgeistt/metafetch
6
+ Author: Ujjawal Singh / @volksgeistt
7
+ Author-email: unrealvolksgeist@gmail.com
8
+ License: MIT
9
+ Project-URL: Bug Reports, https://github.com/volksgeistt/metafetch/issues
10
+ Project-URL: Source, https://github.com/volksgeistt/metafetch
11
+ Project-URL: Documentation, https://github.com/volksgeistt/metafetch/blob/main/README.md
12
+ Keywords: system information neofetch system-info cli terminal
13
+ Classifier: Development Status :: 5 - Production/Stable
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: End Users/Desktop
16
+ Classifier: Intended Audience :: System Administrators
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.6
21
+ Classifier: Programming Language :: Python :: 3.7
22
+ Classifier: Programming Language :: Python :: 3.8
23
+ Classifier: Programming Language :: Python :: 3.9
24
+ Classifier: Programming Language :: Python :: 3.10
25
+ Classifier: Programming Language :: Python :: 3.11
26
+ Classifier: Topic :: System :: Systems Administration
27
+ Classifier: Topic :: Utilities
28
+ Requires-Python: >=3.6
29
+ Description-Content-Type: text/markdown
30
+ License-File: LICENSE
31
+ Requires-Dist: psutil>=5.8.0
32
+ Dynamic: author
33
+ Dynamic: author-email
34
+ Dynamic: classifier
35
+ Dynamic: description
36
+ Dynamic: description-content-type
37
+ Dynamic: home-page
38
+ Dynamic: keywords
39
+ Dynamic: license
40
+ Dynamic: license-file
41
+ Dynamic: project-url
42
+ Dynamic: requires-dist
43
+ Dynamic: requires-python
44
+ Dynamic: summary
45
+
46
+ # MetaFetch
47
+ A beautiful, fast, and comprehensive system information tool inspired by neofetch, written in Python with enhanced cross-platform support.
48
+ ```bash
49
+ - john@desktop
50
+ .o+` -------------
51
+ `ooo/ OS: Ubuntu 22.04.3 LTS
52
+ `+oooo: Kernel: 5.15.0-84-generic
53
+ `+oooooo: Uptime: 2d 14h 32m
54
+ -+oooooo+: Packages: 2847 (apt), 23 (pip)
55
+ `/:-:++oooo+: Shell: bash 5.1.16
56
+ `/++++/+++++++: Desktop: GNOME
57
+ `/++++++++++++++: Terminal: gnome-terminal
58
+ `/+++ooooooooo+/` CPU: Intel i7-9700K (8C)
59
+ ./ooosssso++osssssso+` GPU: NVIDIA GeForce RTX 3070
60
+ .oossssso-````/ossssss+` Memory: 8.2GB / 32.0GB (26%)
61
+ -osssssso. :ssssssso. Disk: 245.8GB / 931.5GB (26%)
62
+ :osssssss/ osssso+++. Network: eth0 (192.168.1.100)
63
+ /ossssssss/ +ssssooo/- Resolution: 2560x1440
64
+ `/ossssso+/:- -:/+osssso+-
65
+ `+sso+:-` `.-/+oso: ███████████████████████████
66
+ `++:. `-/+/ ███████████████████████████
67
+ .` `/
68
+ ```
69
+ ---
70
+ ## 🌟 Features
71
+ - Cross-Platform Support: Works seamlessly on Linux, macOS, and Windows
72
+ - Comprehensive System Info: Display detailed information about your system
73
+ - Beautiful ASCII Art: Platform-specific ASCII logos with colorful output
74
+ - Multiple Display Modes: Full, minimal, and color palette display options
75
+ - Package Manager Detection: Supports multiple package managers (apt, dnf, pacman, brew, pip, npm, etc.)
76
+ - Desktop Environment Detection: Automatically detects DE and WM
77
+ - Network Interface Information: Shows active network interfaces
78
+ - Performance Monitoring: Real-time memory, disk, and swap usage
79
+ - Customizable Output: Easy to extend and modify
80
+ ---
81
+ ## 🚀 Installation
82
+ **Prerequisites**
83
+ - Make sure you have Python 3.6 or higher installed:
84
+ ```bash
85
+ python --version
86
+ ```
87
+ **Install from Source**
88
+ 1. Clone the repository:
89
+ ```bash
90
+ git clone https://github.com/volksgeistt/metafetch
91
+ cd metafetch
92
+ ```
93
+ 2. Install dependencies:
94
+ ```bash
95
+ pip install -r requirements.txt
96
+ ```
97
+ 3. Make it executable (Linux/macOS):
98
+ ```bash
99
+ chmod +x metafetch.py
100
+ ```
101
+ **System-wide Installation (Optional)**
102
+ Create a link to use `metafetch` from anywhere:
103
+ ```bash
104
+ # Linux/macOS
105
+ sudo ln -s $(pwd)/metafetch.py /usr/local/bin/metafetch
106
+
107
+ # Or add to your PATH
108
+ export PATH="$PATH:$(pwd)"
109
+ ```
110
+ ---
111
+ ## 📖 Usage
112
+ **Basic:**
113
+ ```bash
114
+ python metafetch.py
115
+ # or if installed globally
116
+ metafetch
117
+ ```
118
+ **Command Line Options:**
119
+ ```bash
120
+ metafetch [OPTIONS]
121
+
122
+ Options:
123
+ -m, --minimal Display minimal system information
124
+ -c, --colors Show color palette demonstration
125
+ --version Show version information
126
+ -h, --help Show help message
127
+ ```
128
+ ---
129
+ ## Examples
130
+ **Full system information (default):**
131
+ ```bash
132
+ metafetch
133
+ ```
134
+ **Minimal display:**
135
+ ```
136
+ metafetch -m
137
+ ```
138
+ **Color palette:**
139
+ ```bash
140
+ metafetch -c
141
+ ```
142
+ ---
143
+ ## 🎨 Customization
144
+ MetaFetch is highly customizable. You can modify:
145
+ - Colors: Edit the colors dictionary in the `metafetch` class
146
+ - ASCII Art: Modify the `get_ascii_art()` method
147
+ - Information Fields: Add or remove fields in the `collect_info()` method
148
+ - Display Format: Customize the `display()` method
149
+ ---
150
+ ## 🔧 Supported Systems
151
+ **Operating Systems**
152
+ - Linux: All major distributions (Ubuntu, Debian, Fedora, Arch, etc.)
153
+ - macOS: macOS 10.12+ (Sierra and later)
154
+ - Windows: Windows 10/11
155
+
156
+ **Package Managers**
157
+ - Linux: apt, dnf/yum, pacman, portage, xbps, apk
158
+ - macOS: brew
159
+ - Cross-platform: pip, conda, npm, gem, cargo
160
+
161
+ **Desktop Environments**
162
+ - GNOME, KDE Plasma, XFCE, MATE, Cinnamon
163
+ - Window Managers: i3, bspwm, awesome, dwm, openbox, fluxbox
164
+ ---
165
+ <div align="center">
166
+ <sub>Built with ❤️ by <a href="https://github.com/volksgeistt">Ujjawal Singh</a></sub>
167
+ </div>
168
+
169
+
170
+
@@ -0,0 +1,7 @@
1
+ metafetch.py,sha256=f-V5IlvpBtY6b_lzYLAOEaThpidUUUy6H5tfYq6I2qw,29919
2
+ metafetch-1.0.0.dist-info/licenses/LICENSE,sha256=EkaekgGZMSldbtJnJ8mAXrFuo-6XgZu6ZB_HLT3AA7w,1085
3
+ metafetch-1.0.0.dist-info/METADATA,sha256=YChxCI5OawohRVzQpv--Whd5q31VXEbr6q14Yym79Lo,5982
4
+ metafetch-1.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
5
+ metafetch-1.0.0.dist-info/entry_points.txt,sha256=4r8dmmBIrqQxqoEzVLsvIdWVi5V-xWk56j6dKxAdJkg,45
6
+ metafetch-1.0.0.dist-info/top_level.txt,sha256=AyAxDxab5rzLhFwkAuPguQUXOjEWiUYSapFzhd4ag5I,10
7
+ metafetch-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ metafetch = metafetch:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 MetaFetch
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
+ metafetch
metafetch.py ADDED
@@ -0,0 +1,650 @@
1
+ import os
2
+ import platform
3
+ import subprocess
4
+ import socket
5
+ import psutil
6
+ import getpass
7
+ import time
8
+ import sys
9
+ from datetime import datetime, timedelta
10
+ import re
11
+
12
+ class metafetch:
13
+ def __init__(self):
14
+ self.info = {}
15
+ self.colors = {
16
+ 'red': '\033[91m',
17
+ 'green': '\033[92m',
18
+ 'yellow': '\033[93m',
19
+ 'blue': '\033[94m',
20
+ 'purple': '\033[95m',
21
+ 'cyan': '\033[96m',
22
+ 'white': '\033[97m',
23
+ 'gray': '\033[90m',
24
+ 'bold': '\033[1m',
25
+ 'end': '\033[0m'
26
+ }
27
+
28
+ def run_command(self, cmd, timeout=3):
29
+ try:
30
+ if isinstance(cmd, str):
31
+ result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=timeout)
32
+ else:
33
+ result = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)
34
+ if result.returncode == 0:
35
+ return result.stdout.strip()
36
+ except:
37
+ pass
38
+ return None
39
+
40
+ def get_os_info(self):
41
+ try:
42
+ system = platform.system()
43
+
44
+ if system == "Linux":
45
+ sources = ['/etc/os-release', '/etc/lsb-release', '/etc/redhat-release']
46
+ for source in sources:
47
+ try:
48
+ with open(source, 'r') as f:
49
+ content = f.read()
50
+ if 'PRETTY_NAME=' in content:
51
+ for line in content.split('\n'):
52
+ if line.startswith('PRETTY_NAME='):
53
+ return line.split('=')[1].strip().strip('"')
54
+ elif source == '/etc/redhat-release':
55
+ return content.strip()
56
+ except:
57
+ continue
58
+
59
+ lsb = self.run_command(['lsb_release', '-d'])
60
+ if lsb:
61
+ return lsb.split('\t')[1] if '\t' in lsb else lsb
62
+
63
+ return f"Linux {platform.release()}"
64
+
65
+ elif system == "Darwin":
66
+ version = self.run_command(['sw_vers', '-productVersion'])
67
+ name = self.run_command(['sw_vers', '-productName'])
68
+ if version and name:
69
+ return f"{name} {version}"
70
+ return f"macOS {platform.release()}"
71
+
72
+ elif system == "Windows":
73
+ try:
74
+ import winreg
75
+ key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\Microsoft\Windows NT\CurrentVersion")
76
+ product_name = winreg.QueryValueEx(key, "ProductName")[0]
77
+ build = winreg.QueryValueEx(key, "CurrentBuild")[0]
78
+ return f"{product_name} (Build {build})"
79
+ except:
80
+ pass
81
+ return f"Windows {platform.release()}"
82
+ else:
83
+ return f"{system} {platform.release()}"
84
+ except:
85
+ return "Unknown OS"
86
+
87
+ def get_kernel(self):
88
+ try:
89
+ system = platform.system()
90
+ if system == "Linux":
91
+ return platform.release()
92
+ elif system == "Darwin":
93
+ return f"Darwin {platform.release()}"
94
+ elif system == "Windows":
95
+ return f"NT {platform.version()}"
96
+ else:
97
+ return platform.release()
98
+ except:
99
+ return "Unknown"
100
+
101
+ def get_uptime(self):
102
+ try:
103
+ boot_time = psutil.boot_time()
104
+ uptime_seconds = time.time() - boot_time
105
+ uptime = timedelta(seconds=int(uptime_seconds))
106
+
107
+ days = uptime.days
108
+ hours = uptime.seconds // 3600
109
+ minutes = (uptime.seconds % 3600) // 60
110
+
111
+ parts = []
112
+ if days > 0:
113
+ parts.append(f"{days}d")
114
+ if hours > 0:
115
+ parts.append(f"{hours}h")
116
+ if minutes > 0:
117
+ parts.append(f"{minutes}m")
118
+
119
+ return " ".join(parts) if parts else "0m"
120
+ except:
121
+ return "Unknown"
122
+
123
+ def get_packages(self):
124
+ try:
125
+ counts = []
126
+
127
+ managers = [
128
+ ('apt', ['dpkg', '--get-selections'], lambda x: len([l for l in x.split('\n') if '\tinstall' in l])),
129
+ ('dnf/yum', ['rpm', '-qa'], lambda x: len(x.split('\n'))),
130
+ ('pacman', ['pacman', '-Q'], lambda x: len(x.split('\n'))),
131
+ ('portage', ['qlist', '-I'], lambda x: len(x.split('\n'))),
132
+ ('xbps', ['xbps-query', '-l'], lambda x: len(x.split('\n'))),
133
+ ('apk', ['apk', 'info'], lambda x: len(x.split('\n'))),
134
+ ('brew', ['brew', 'list'], lambda x: len(x.split('\n'))),
135
+ ('pip', ['pip', 'list'], lambda x: len(x.split('\n')) - 2 if len(x.split('\n')) > 2 else 0),
136
+ ('conda', ['conda', 'list'], lambda x: len([l for l in x.split('\n') if l and not l.startswith('#')])),
137
+ ('npm', ['npm', 'list', '-g', '--depth=0'], lambda x: len([l for l in x.split('\n') if '├──' in l or '└──' in l])),
138
+ ('gem', ['gem', 'list'], lambda x: len(x.split('\n'))),
139
+ ('cargo', ['cargo', 'install', '--list'], lambda x: len([l for l in x.split('\n') if l and not l.startswith(' ')]))
140
+ ]
141
+
142
+ for name, cmd, counter in managers:
143
+ output = self.run_command(cmd)
144
+ if output:
145
+ try:
146
+ count = counter(output)
147
+ if count > 0:
148
+ counts.append(f"{count} ({name})")
149
+ except:
150
+ continue
151
+
152
+ return ", ".join(counts[:3]) if counts else "Unknown"
153
+ except:
154
+ return "Unknown"
155
+
156
+ def get_shell(self):
157
+ try:
158
+ shell_path = os.environ.get('SHELL', '')
159
+ if shell_path:
160
+ shell_name = os.path.basename(shell_path)
161
+ version = self.run_command([shell_name, '--version'])
162
+ if version:
163
+ version_match = re.search(r'(\d+\.\d+(?:\.\d+)?)', version.split('\n')[0])
164
+ if version_match:
165
+ return f"{shell_name} {version_match.group(1)}"
166
+ return shell_name
167
+ else:
168
+ if platform.system() == "Windows":
169
+ return "PowerShell" if "powershell" in os.environ.get('PSModulePath', '').lower() else "cmd"
170
+ return "Unknown"
171
+ except:
172
+ return "Unknown"
173
+
174
+ def get_desktop(self):
175
+ try:
176
+ de_vars = [
177
+ 'XDG_CURRENT_DESKTOP',
178
+ 'DESKTOP_SESSION',
179
+ 'XDG_SESSION_DESKTOP',
180
+ 'CURRENT_DESKTOP'
181
+ ]
182
+
183
+ for var in de_vars:
184
+ de = os.environ.get(var)
185
+ if de:
186
+ return de
187
+
188
+ if os.environ.get('GNOME_DESKTOP_SESSION_ID'):
189
+ return 'GNOME'
190
+ elif os.environ.get('KDE_FULL_SESSION'):
191
+ return 'KDE'
192
+ elif os.environ.get('MATE_DESKTOP_SESSION_ID'):
193
+ return 'MATE'
194
+ elif os.environ.get('XFCE4_SESSION'):
195
+ return 'XFCE4'
196
+
197
+ try:
198
+ processes = [p.name() for p in psutil.process_iter(['name'])]
199
+ if 'gnome-shell' in processes:
200
+ return 'GNOME'
201
+ elif 'kwin' in processes or 'plasmashell' in processes:
202
+ return 'KDE'
203
+ elif 'xfwm4' in processes:
204
+ return 'XFCE4'
205
+ elif 'marco' in processes:
206
+ return 'MATE'
207
+ elif 'openbox' in processes:
208
+ return 'Openbox'
209
+ except:
210
+ pass
211
+
212
+ return "Unknown"
213
+ except:
214
+ return "Unknown"
215
+
216
+ def get_window_manager(self):
217
+ try:
218
+ wm = os.environ.get('WINDOW_MANAGER')
219
+ if wm:
220
+ return os.path.basename(wm)
221
+
222
+
223
+ wms = ['i3', 'bspwm', 'awesome', 'dwm', 'openbox', 'fluxbox', 'jwm']
224
+ try:
225
+ processes = [p.name() for p in psutil.process_iter(['name'])]
226
+ for wm in wms:
227
+ if wm in processes:
228
+ return wm
229
+ except:
230
+ pass
231
+
232
+ return None
233
+ except:
234
+ return None
235
+
236
+ def get_cpu(self):
237
+ try:
238
+ cpu_info = ""
239
+ cpu_count = psutil.cpu_count(logical=False)
240
+ cpu_count_logical = psutil.cpu_count(logical=True)
241
+
242
+ if platform.system() == "Linux":
243
+ try:
244
+ with open('/proc/cpuinfo', 'r') as f:
245
+ for line in f:
246
+ if 'model name' in line:
247
+ cpu_info = line.split(':')[1].strip()
248
+ break
249
+ except:
250
+ pass
251
+ elif platform.system() == "Darwin":
252
+ cpu_info = self.run_command(['sysctl', '-n', 'machdep.cpu.brand_string'])
253
+ elif platform.system() == "Windows":
254
+ cpu_info = self.run_command('wmic cpu get name /value').split('=')[1] if self.run_command('wmic cpu get name /value') else ""
255
+
256
+ if not cpu_info:
257
+ cpu_info = platform.processor()
258
+
259
+
260
+ cpu_info = re.sub(r'\s+', ' ', cpu_info)
261
+ cpu_info = cpu_info.replace('(R)', '').replace('(TM)', '').replace('CPU', '').replace('@', '').strip()
262
+
263
+ if cpu_count != cpu_count_logical:
264
+ cpu_info += f" ({cpu_count}C/{cpu_count_logical}T)"
265
+ else:
266
+ cpu_info += f" ({cpu_count}C)"
267
+
268
+ return cpu_info
269
+ except:
270
+ return "Unknown CPU"
271
+
272
+ def get_gpu(self):
273
+ try:
274
+ gpus = []
275
+
276
+ if platform.system() == "Linux":
277
+ lspci = self.run_command(['lspci'])
278
+ if lspci:
279
+ for line in lspci.split('\n'):
280
+ if 'VGA' in line or 'Display' in line or '3D' in line:
281
+ gpu = line.split(': ')[-1]
282
+ gpus.append(gpu)
283
+
284
+ nvidia = self.run_command(['nvidia-smi', '--query-gpu=name', '--format=csv,noheader'])
285
+ if nvidia:
286
+ gpus.extend(nvidia.split('\n'))
287
+
288
+ elif platform.system() == "Windows":
289
+ wmic = self.run_command('wmic path win32_VideoController get name /value')
290
+ if wmic:
291
+ for line in wmic.split('\n'):
292
+ if 'Name=' in line and line.split('=')[1].strip():
293
+ gpus.append(line.split('=')[1].strip())
294
+
295
+ elif platform.system() == "Darwin":
296
+ system_profiler = self.run_command(['system_profiler', 'SPDisplaysDataType'])
297
+ if system_profiler:
298
+ for line in system_profiler.split('\n'):
299
+ if 'Chipset Model:' in line:
300
+ gpu = line.split(':')[1].strip()
301
+ gpus.append(gpu)
302
+
303
+ return gpus[0] if gpus else "Unknown"
304
+ except:
305
+ return "Unknown"
306
+
307
+ def get_memory(self):
308
+ try:
309
+ mem = psutil.virtual_memory()
310
+ used_gb = mem.used / (1024**3)
311
+ total_gb = mem.total / (1024**3)
312
+ return f"{used_gb:.1f}GB / {total_gb:.1f}GB ({mem.percent:.0f}%)"
313
+ except:
314
+ return "Unknown"
315
+
316
+ def get_swap(self):
317
+ try:
318
+ swap = psutil.swap_memory()
319
+ if swap.total > 0:
320
+ used_gb = swap.used / (1024**3)
321
+ total_gb = swap.total / (1024**3)
322
+ percent = (swap.used / swap.total * 100) if swap.total > 0 else 0
323
+ return f"{used_gb:.1f}GB / {total_gb:.1f}GB ({percent:.0f}%)"
324
+ return "Not configured"
325
+ except:
326
+ return "Unknown"
327
+
328
+ def get_disk(self):
329
+ try:
330
+ if platform.system() == "Windows":
331
+ disk = psutil.disk_usage('C:')
332
+ else:
333
+ disk = psutil.disk_usage('/')
334
+ used_gb = disk.used / (1024**3)
335
+ total_gb = disk.total / (1024**3)
336
+ percent = (disk.used / disk.total) * 100
337
+ return f"{used_gb:.1f}GB / {total_gb:.1f}GB ({percent:.0f}%)"
338
+ except:
339
+ return "Unknown"
340
+
341
+ def get_network(self):
342
+ try:
343
+ interfaces = psutil.net_if_addrs()
344
+ active_interfaces = []
345
+
346
+ for interface, addresses in interfaces.items():
347
+ if interface == 'lo' or interface.startswith('docker'):
348
+ continue
349
+ for addr in addresses:
350
+ if addr.family == socket.AF_INET and not addr.address.startswith('127.'):
351
+ active_interfaces.append(f"{interface} ({addr.address})")
352
+ break
353
+
354
+ return ", ".join(active_interfaces[:2]) if active_interfaces else "Unknown"
355
+ except:
356
+ return "Unknown"
357
+
358
+ def get_resolution(self):
359
+
360
+ try:
361
+ if platform.system() == "Linux":
362
+ xrandr = self.run_command(['xrandr', '--current'])
363
+ if xrandr:
364
+ resolutions = []
365
+ for line in xrandr.split('\n'):
366
+ if '*' in line and 'x' in line:
367
+ match = re.search(r'(\d+x\d+)', line)
368
+ if match:
369
+ resolutions.append(match.group(1))
370
+ return ", ".join(set(resolutions)) if resolutions else "Unknown"
371
+
372
+ xdpyinfo = self.run_command(['xdpyinfo'])
373
+ if xdpyinfo:
374
+ for line in xdpyinfo.split('\n'):
375
+ if 'dimensions:' in line:
376
+ match = re.search(r'(\d+x\d+)', line)
377
+ if match:
378
+ return match.group(1)
379
+
380
+ elif platform.system() == "Windows":
381
+ wmic = self.run_command('wmic desktopmonitor get screenheight,screenwidth /value')
382
+ if wmic:
383
+ width = height = None
384
+ for line in wmic.split('\n'):
385
+ if 'ScreenWidth=' in line:
386
+ width = line.split('=')[1].strip()
387
+ elif 'ScreenHeight=' in line:
388
+ height = line.split('=')[1].strip()
389
+ if width and height and width != '0':
390
+ return f"{width}x{height}"
391
+
392
+ elif platform.system() == "Darwin":
393
+ system_profiler = self.run_command(['system_profiler', 'SPDisplaysDataType'])
394
+ if system_profiler:
395
+ for line in system_profiler.split('\n'):
396
+ if 'Resolution:' in line:
397
+ match = re.search(r'(\d+\s*x\s*\d+)', line)
398
+ if match:
399
+ return match.group(1).replace(' ', '')
400
+
401
+ return "Unknown"
402
+ except:
403
+ return "Unknown"
404
+
405
+ def get_terminal(self):
406
+ try:
407
+ term_vars = ['TERM_PROGRAM', 'TERMINAL_EMULATOR', 'TERM']
408
+ for var in term_vars:
409
+ term = os.environ.get(var)
410
+ if term and term not in ['xterm', 'xterm-256color', 'screen']:
411
+ return term
412
+
413
+ try:
414
+ ppid = os.getppid()
415
+ parent = psutil.Process(ppid)
416
+ parent_name = parent.name()
417
+
418
+ terminals = ['gnome-terminal', 'konsole', 'xfce4-terminal', 'mate-terminal',
419
+ 'terminator', 'tilix', 'alacritty', 'kitty', 'urxvt', 'xterm',
420
+ 'iTerm', 'Terminal', 'WindowsTerminal', 'ConEmu']
421
+
422
+ for term in terminals:
423
+ if term.lower() in parent_name.lower():
424
+ return term
425
+
426
+ return parent_name
427
+ except:
428
+ pass
429
+
430
+ return os.environ.get('TERM', 'Unknown')
431
+ except:
432
+ return "Unknown"
433
+
434
+ def get_ascii_art(self):
435
+ system = platform.system().lower()
436
+
437
+ if "linux" in system:
438
+ return [
439
+ f"{self.colors['blue']} -`{self.colors['end']}",
440
+ f"{self.colors['blue']} .o+`{self.colors['end']}",
441
+ f"{self.colors['blue']} `ooo/{self.colors['end']}",
442
+ f"{self.colors['blue']} `+oooo:{self.colors['end']}",
443
+ f"{self.colors['blue']} `+oooooo:{self.colors['end']}",
444
+ f"{self.colors['blue']} -+oooooo+:{self.colors['end']}",
445
+ f"{self.colors['blue']} `/:-:++oooo+:{self.colors['end']}",
446
+ f"{self.colors['blue']} `/++++/+++++++:{self.colors['end']}",
447
+ f"{self.colors['blue']} `/++++++++++++++:{self.colors['end']}",
448
+ f"{self.colors['blue']} `/+++ooooooooo+/`{self.colors['end']}",
449
+ f"{self.colors['blue']} ./ooosssso++osssssso+`{self.colors['end']}",
450
+ f"{self.colors['blue']} .oossssso-````/ossssss+`{self.colors['end']}",
451
+ f"{self.colors['blue']} -osssssso. :ssssssso.{self.colors['end']}",
452
+ f"{self.colors['blue']} :osssssss/ osssso+++.{self.colors['end']}",
453
+ f"{self.colors['blue']} /ossssssss/ +ssssooo/-{self.colors['end']}",
454
+ f"{self.colors['blue']} `/ossssso+/:- -:/+osssso+-{self.colors['end']}",
455
+ f"{self.colors['blue']} `+sso+:-` `.-/+oso:{self.colors['end']}",
456
+ f"{self.colors['blue']} `++:. `-/+/{self.colors['end']}",
457
+ f"{self.colors['blue']} .` `/{self.colors['end']}"
458
+ ]
459
+ elif "darwin" in system:
460
+ return [
461
+ f"{self.colors['green']} 'c.{self.colors['end']}",
462
+ f"{self.colors['green']} ,xNMM.{self.colors['end']}",
463
+ f"{self.colors['green']} .OMMMMo{self.colors['end']}",
464
+ f"{self.colors['green']} OMMM0,{self.colors['end']}",
465
+ f"{self.colors['green']} .;loddo:' loolloddol;.{self.colors['end']}",
466
+ f"{self.colors['green']} cKMMMMMMMMMMNWMMMMMMMMMM0:{self.colors['end']}",
467
+ f"{self.colors['yellow']} .KMMMMMMMMMMMMMMMMMMMMMMMWd.{self.colors['end']}",
468
+ f"{self.colors['yellow']} XMMMMMMMMMMMMMMMMMMMMMMMX.{self.colors['end']}",
469
+ f"{self.colors['red']};MMMMMMMMMMMMMMMMMMMMMMMM:{self.colors['end']}",
470
+ f"{self.colors['red']}:MMMMMMMMMMMMMMMMMMMMMMMM:{self.colors['end']}",
471
+ f"{self.colors['red']}.MMMMMMMMMMMMMMMMMMMMMMMMX.{self.colors['end']}",
472
+ f"{self.colors['purple']} kMMMMMMMMMMMMMMMMMMMMMMMMWd.{self.colors['end']}",
473
+ f"{self.colors['purple']} .XMMMMMMMMMMMMMMMMMMMMMMMMMMk{self.colors['end']}",
474
+ f"{self.colors['blue']} .XMMMMMMMMMMMMMMMMMMMMMMMMK.{self.colors['end']}",
475
+ f"{self.colors['blue']} kMMMMMMMMMMMMMMMMMMMMMMd{self.colors['end']}",
476
+ f"{self.colors['cyan']} ;KMMMMMMMWXXWMMMMMMMk.{self.colors['end']}",
477
+ f"{self.colors['cyan']} .cooc,. .,coo:.{self.colors['end']}"
478
+ ]
479
+ elif "windows" in system:
480
+ return [
481
+ f"{self.colors['blue']} ,.=:!!t3Z3z.,{self.colors['end']}",
482
+ f"{self.colors['blue']} :tt:::tt333EE3{self.colors['end']}",
483
+ f"{self.colors['blue']} Et:::ztt33EEEL{self.colors['cyan']} @@@@@@@@@@@@@@@@@@@@@{self.colors['end']}",
484
+ f"{self.colors['blue']} Et:::zt333EEE{self.colors['cyan']} @@@@@@@@@@@@@@@@@@@@@{self.colors['end']}",
485
+ f"{self.colors['blue']} Et:::zt333EEE{self.colors['cyan']} @@@@@@@@@@@@@@@@@@@@@{self.colors['end']}",
486
+ f"{self.colors['blue']} Et:::zt333EEE{self.colors['cyan']} @@@@@@@@@@@@@@@@@@@@@{self.colors['end']}",
487
+ f"{self.colors['blue']} Et:::zt333EEE{self.colors['cyan']} @@@@@@@@@@@@@@@@@@@@{self.colors['end']}",
488
+ f"{self.colors['blue']} Et:::zt333EEE{self.colors['cyan']} @@@@@@@@@@@@@@@@@@@@{self.colors['end']}",
489
+ f"{self.colors['blue']} Et:::zt333EEE{self.colors['cyan']} @@@@@@@@@@@@@@@@@@@@{self.colors['end']}",
490
+ f"{self.colors['blue']} Et:::zt333EEE{self.colors['cyan']} @@@@@@@@@@@@@@@@@@@@{self.colors['end']}",
491
+ f"{self.colors['blue']} Et:::zt333EEE{self.colors['cyan']} @@@@@@@@@@@@@@@@@@@@{self.colors['end']}",
492
+ f"{self.colors['blue']} Et:::zt333EEE{self.colors['cyan']} @@@@@@@@@@@@@@@@@@@@{self.colors['end']}",
493
+ f"{self.colors['blue']} Et:::zt333EEE{self.colors['cyan']} @@@@@@@@@@@@@@@@@@@@{self.colors['end']}",
494
+ f"{self.colors['blue']} Et:::zt333EEE{self.colors['end']}",
495
+ f"{self.colors['blue']} Et:::zt333EEE{self.colors['end']}",
496
+ f"{self.colors['blue']} Et:::zt333EEE{self.colors['end']}",
497
+ f"{self.colors['blue']} Et:::zt333EEE{self.colors['end']}"
498
+ ]
499
+ else:
500
+ return [
501
+ f"{self.colors['cyan']} .-\"\"\"\"\"-. {self.colors['end']}",
502
+ f"{self.colors['cyan']} .' '. {self.colors['end']}",
503
+ f"{self.colors['cyan']} / O O \\ {self.colors['end']}",
504
+ f"{self.colors['cyan']} : : {self.colors['end']}",
505
+ f"{self.colors['cyan']} | | {self.colors['end']}",
506
+ f"{self.colors['cyan']} : __ : {self.colors['end']}",
507
+ f"{self.colors['cyan']} \\ .-\"` `\"-. / {self.colors['end']}",
508
+ f"{self.colors['cyan']} '. .' {self.colors['end']}",
509
+ f"{self.colors['cyan']} '-.......-' {self.colors['end']}",
510
+ "",
511
+ f"{self.colors['bold']} Unknown System{self.colors['end']}"
512
+ ]
513
+
514
+ def collect_info(self):
515
+ self.info = {
516
+ 'user': getpass.getuser(),
517
+ 'hostname': socket.gethostname(),
518
+ 'os': self.get_os_info(),
519
+ 'kernel': self.get_kernel(),
520
+ 'uptime': self.get_uptime(),
521
+ 'packages': self.get_packages(),
522
+ 'shell': self.get_shell(),
523
+ 'desktop': self.get_desktop(),
524
+ 'wm': self.get_window_manager(),
525
+ 'terminal': self.get_terminal(),
526
+ 'cpu': self.get_cpu(),
527
+ 'gpu': self.get_gpu(),
528
+ 'memory': self.get_memory(),
529
+ 'swap': self.get_swap(),
530
+ 'disk': self.get_disk(),
531
+ 'network': self.get_network(),
532
+ 'resolution': self.get_resolution()
533
+ }
534
+
535
+ def display(self):
536
+ self.collect_info()
537
+ ascii_art = self.get_ascii_art()
538
+
539
+ user_host = f"{self.colors['bold']}{self.colors['green']}{self.info['user']}{self.colors['red']}@{self.colors['blue']}{self.info['hostname']}{self.colors['end']}"
540
+ separator = f"{self.colors['gray']}{'-' * (len(self.info['user']) + len(self.info['hostname']) + 1)}{self.colors['end']}"
541
+
542
+ info_lines = [
543
+ user_host,
544
+ separator,
545
+ f"{self.colors['bold']}{self.colors['red']}OS{self.colors['end']}: {self.info['os']}",
546
+ f"{self.colors['bold']}{self.colors['yellow']}Kernel{self.colors['end']}: {self.info['kernel']}",
547
+ f"{self.colors['bold']}{self.colors['green']}Uptime{self.colors['end']}: {self.info['uptime']}",
548
+ f"{self.colors['bold']}{self.colors['blue']}Packages{self.colors['end']}: {self.info['packages']}",
549
+ f"{self.colors['bold']}{self.colors['purple']}Shell{self.colors['end']}: {self.info['shell']}",
550
+ f"{self.colors['bold']}{self.colors['cyan']}Desktop{self.colors['end']}: {self.info['desktop']}",
551
+ ]
552
+
553
+ if self.info['wm'] and self.info['wm'].lower() not in self.info['desktop'].lower():
554
+ info_lines.append(f"{self.colors['bold']}{self.colors['white']}WM{self.colors['end']}: {self.info['wm']}")
555
+
556
+ info_lines.extend([
557
+ f"{self.colors['bold']}{self.colors['red']}Terminal{self.colors['end']}: {self.info['terminal']}",
558
+ f"{self.colors['bold']}{self.colors['yellow']}CPU{self.colors['end']}: {self.info['cpu']}",
559
+ f"{self.colors['bold']}{self.colors['green']}GPU{self.colors['end']}: {self.info['gpu']}",
560
+ f"{self.colors['bold']}{self.colors['blue']}Memory{self.colors['end']}: {self.info['memory']}"
561
+ ])
562
+
563
+ if self.info['swap'] != "Not configured":
564
+ info_lines.append(f"{self.colors['bold']}{self.colors['purple']}Swap{self.colors['end']}: {self.info['swap']}")
565
+
566
+ info_lines.extend([
567
+ f"{self.colors['bold']}{self.colors['cyan']}Disk{self.colors['end']}: {self.info['disk']}",
568
+ f"{self.colors['white']}Network{self.colors['end']}: {self.info['network']}",
569
+ f"{self.colors['bold']}{self.colors['gray']}Resolution{self.colors['end']}: {self.info['resolution']}"
570
+ ])
571
+
572
+ info_lines.append("")
573
+ colors_line1 = ""
574
+ colors_line2 = ""
575
+ color_names = ['red', 'green', 'yellow', 'blue', 'purple', 'cyan', 'white', 'gray']
576
+
577
+ for color in color_names:
578
+ colors_line1 += f"{self.colors[color]}███{self.colors['end']}"
579
+ colors_line2 += f"{self.colors[color]}███{self.colors['end']}"
580
+
581
+ info_lines.extend([colors_line1, colors_line2])
582
+
583
+ max_lines = max(len(ascii_art), len(info_lines))
584
+
585
+ for i in range(max_lines):
586
+ ascii_line = ascii_art[i] if i < len(ascii_art) else " " * 40
587
+ info_line = info_lines[i] if i < len(info_lines) else ""
588
+
589
+ clean_info = re.sub(r'\x1b\[[0-9;]*m', '', info_line)
590
+ padding = " " * (3)
591
+
592
+ print(f"{ascii_line}{padding}{info_line}")
593
+
594
+ def get_colors_demo(self):
595
+ print("\nColor Palette:")
596
+ for name, code in self.colors.items():
597
+ if name != 'end':
598
+ print(f"{code}{name.capitalize():10}{self.colors['end']}: {code}████████{self.colors['end']}")
599
+
600
+ def get_minimal_info(self):
601
+ return {
602
+ 'user_host': f"{getpass.getuser()}@{socket.gethostname()}",
603
+ 'os': platform.system(),
604
+ 'uptime': self.get_uptime(),
605
+ 'memory': self.get_memory()
606
+ }
607
+
608
+ def minimal_display(self):
609
+ info = self.get_minimal_info()
610
+ print(f"{self.colors['bold']}{self.colors['cyan']}{info['user_host']}{self.colors['end']}")
611
+ print(f"{self.colors['yellow']}OS{self.colors['end']}: {info['os']}")
612
+ print(f"{self.colors['green']}Uptime{self.colors['end']}: {info['uptime']}")
613
+ print(f"{self.colors['blue']}Memory{self.colors['end']}: {info['memory']}")
614
+
615
+
616
+ def fetch(minimal=False, colors=False):
617
+ pf = metafetch()
618
+
619
+ if colors:
620
+ pf.get_colors_demo()
621
+ elif minimal:
622
+ pf.minimal_display()
623
+ else:
624
+ pf.display()
625
+
626
+
627
+ def main():
628
+ import argparse
629
+
630
+ parser = argparse.ArgumentParser(description='metafetch - Enhanced system information tool')
631
+ parser.add_argument('-m', '--minimal', action='store_true',
632
+ help='Display minimal information')
633
+ parser.add_argument('-c', '--colors', action='store_true',
634
+ help='Display color palette')
635
+ parser.add_argument('--version', action='version', version='metafetch 1.0.0')
636
+
637
+ args = parser.parse_args()
638
+
639
+ try:
640
+ fetch(minimal=args.minimal, colors=args.colors)
641
+ except KeyboardInterrupt:
642
+ print(f"\n{metafetch().colors['red']}Interrupted by user{metafetch().colors['end']}")
643
+ sys.exit(1)
644
+ except Exception as e:
645
+ print(f"{metafetch().colors['red']}Error: {e}{metafetch().colors['end']}")
646
+ sys.exit(1)
647
+
648
+
649
+ if __name__ == "__main__":
650
+ main()