├── .gitignore ├── README.md ├── assets └── screenshot-1.png ├── default_config ├── ascii │ └── cat.txt ├── config.meow └── info.meow ├── requirements.txt ├── scripts ├── installer.sh └── uninstaller.sh └── src ├── colors.py ├── config.py ├── fetcher.py ├── main.py └── utils.py /.gitignore: -------------------------------------------------------------------------------- 1 | **/*.pyc 2 | __pycache__ 3 | build -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![screenshot](https://github.com/sammwyy/meow-fetch/raw/main/assets/screenshot-1.png) 2 | 3 |

🐱 Meow Fetch

4 |

A fetch done in python based on linux.

5 | 6 | ## Install 7 | 8 | You can install meow fetch by running the following command in your terminal. 9 | ```bash 10 | curl -s https://raw.githubusercontent.com/sammwyy/meow-fetch/main/scripts/installer.sh | sudo bash /dev/stdin arg1 arg2 11 | ``` 12 | -------------------------------------------------------------------------------- /assets/screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sammwyy/meow-fetch/7a258d89e57e9ba93c1099d8091a92784c520684/assets/screenshot-1.png -------------------------------------------------------------------------------- /default_config/ascii/cat.txt: -------------------------------------------------------------------------------- 1 | 2 | ████████████████ 3 | ████░░░░░░░░░░░░░░░░████ 4 | ██████████████░░░░░░░░░░░░░░░░░░░░░░░░████████████ 5 | ██░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░██ 6 | ██░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░██ 7 | ██▓▓▓▓▓▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓▓▓▓▓▓██ 8 | ██▓▓▓▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓▓▓▓██ 9 | ██▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓██ 10 | ██░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░██ 11 | ██░░░░░░████░░░░░░░░░░░░░░████░░░░░░██ 12 | ██░░░░██ ▒▒██░░░░░░░░░░██ ▒▒██░░░░██ 13 | ██░░░░██▒▒▒▒██░░░░░░░░░░██▒▒▒▒██░░░░██ 14 | ██░░░░████░░░░░░░░░░░░░░████░░░░██ 15 | ██░░░░░░░░░░▓▓▓▓▓▓▓▓▓▓░░░░░░░░░░██ 16 | ▓▓▓▓▓▓██░░░░▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓▓▓░░░░██ 17 | ▓▓▒▒▒▒▒▒▓▓██▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓██ 18 | ▓▓▒▒▒▒▒▒▒▒▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▒▒▓▓▒▒▓▓██ 19 | ▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▒▒▒▒▒▒▓▓██ 20 | ▓▓▒▒▒▒▒▒▒▒▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▒▒▓▓▒▒▓▓ 21 | ▓▓▒▒▒▒▒▒▓▓ ▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓ 22 | ▓▓▓▓▓▓ ▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓▓▓ 23 | ▓▓▓▓▓▓▓▓▓▓ 24 | -------------------------------------------------------------------------------- /default_config/config.meow: -------------------------------------------------------------------------------- 1 | # Path to ascii-art text file. 2 | ascii-path = "ascii/cat.txt" 3 | 4 | # Padding for ascii-art and information text. 5 | padding-x = 3 6 | padding-y = 2 7 | 8 | # Space between ascii-art and information text. 9 | space-between = 3 10 | 11 | # color character. 12 | color-char = "" -------------------------------------------------------------------------------- /default_config/info.meow: -------------------------------------------------------------------------------- 1 | 2 | ──────  Hardware Information ─────── 3 | 4 | {blue}塞{reset} {ram_used} / {ram_total} ({ram_usage}) 5 | {cyan}{reset} {cpu_brand} {cpu_max_freq} ({cpu_usage}) 6 | {green}{reset} {screen_size} 7 | {yellow}{reset} {hdd_used} / {hdd_total} ({hdd_usage}) 8 | 9 | ──────  Software Information ─────── 10 | 11 | {red}{reset} {hostname} 12 | {magenta}{reset} {user} 13 | {red}{reset} {os} 14 | {yellow}{reset} {kernel} 15 | {green}{reset} {de} 16 | {cyan}{reset} {shell} 17 | 18 | ᗧ • • • {colors} • • • 19 | ᗧ • • • {colors_light} • • • -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | psutil==5.9.0 2 | screeninfo==0.8 3 | py-cpuinfo==8.0.0 4 | distro==1.5.0 5 | colorama==0.4.4 6 | pathlib==1.0.1 -------------------------------------------------------------------------------- /scripts/installer.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Check if script is running as root or not. 3 | if [ "$EUID" -ne 0 ] 4 | then echo "Please run as root" 5 | exit 6 | fi 7 | 8 | # Check if installer is running from a cloned repo or not. 9 | AUX_FILE=.gitignore 10 | if test -f "$AUX_FILE"; then 11 | cd .. 12 | else 13 | git clone https://github.com/sammwyy/meow-fetch 14 | cd meow-fetch 15 | fi 16 | 17 | # Install python dependencies. 18 | pip install -r requirements.txt > /dev/null 19 | 20 | # Move source files to /usr/share. 21 | sudo mkdir /usr/share/meow-fetch 22 | sudo cp ./src/* /usr/share/meow-fetch 23 | 24 | # Create bin under /usr/bin. 25 | echo "#!/bin/bash" > /usr/bin/meow-fetch 26 | echo "python /usr/share/meow-fetch/main.py" >> /usr/bin/meow-fetch 27 | 28 | sudo chmod 755 /usr/bin/meow-fetch 29 | 30 | # Copy default config 31 | mkdir "/home/$SUDO_USER/.config/meow-fetch" 32 | cp -r ./default_config/* "/home/$SUDO_USER/.config/meow-fetch" 33 | echo "Installed" -------------------------------------------------------------------------------- /scripts/uninstaller.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Check if script is running as root or not. 3 | if [ "$EUID" -ne 0 ] 4 | then echo "Please run as root" 5 | exit 6 | fi 7 | 8 | # Delete files. 9 | sudo rm -rf /usr/share/meow-fetch 10 | 11 | # Delete binary. 12 | sudo rm -rf /usr/bin/meow-fetch 13 | 14 | # Delete config. 15 | sudo rm -rf /home/$SUDO_USER/.config/meow-fetch -------------------------------------------------------------------------------- /src/colors.py: -------------------------------------------------------------------------------- 1 | from colorama import init 2 | from colorama import Fore, Back, Style 3 | 4 | class Colors: 5 | def __init__(self): 6 | init() 7 | 8 | def format(self, text): 9 | return ( 10 | text 11 | .replace("{black}", Fore.BLACK) 12 | .replace("{blue}", Fore.BLUE) 13 | .replace("{cyan}", Fore.CYAN) 14 | .replace("{green}", Fore.GREEN) 15 | .replace("{magenta}", Fore.MAGENTA) 16 | .replace("{red}", Fore.RED) 17 | .replace("{reset}", Fore.RESET) 18 | .replace("{white}", Fore.WHITE) 19 | .replace("{yellow}", Fore.YELLOW) 20 | 21 | .replace("{light_black}", Fore.LIGHTBLACK_EX) 22 | .replace("{light_blue}", Fore.LIGHTBLUE_EX) 23 | .replace("{light_cyan}", Fore.LIGHTCYAN_EX) 24 | .replace("{light_green}", Fore.LIGHTGREEN_EX) 25 | .replace("{light_magenta}", Fore.LIGHTMAGENTA_EX) 26 | .replace("{light_red}", Fore.LIGHTRED_EX) 27 | .replace("{light_white}", Fore.LIGHTWHITE_EX) 28 | .replace("{light_yellow}", Fore.LIGHTYELLOW_EX) 29 | ) 30 | -------------------------------------------------------------------------------- /src/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | def __init__(self, config_path): 3 | self.config_path = config_path 4 | self.config_raw = open(config_path, "r").read() 5 | self.config = {} 6 | 7 | def get(self, key, default_value = None): 8 | if key in self.config: 9 | return self.config[key] 10 | else: 11 | return default_value 12 | 13 | def load(self): 14 | self.config = {} 15 | 16 | for line in self.config_raw.splitlines(): 17 | if line.startswith("#") or line.strip() == "": 18 | continue 19 | 20 | key = line.split("=")[0].strip() 21 | value = line.split("=")[1].strip() 22 | 23 | if value.startswith("\"") and value.endswith("\""): 24 | value = value.replace("\"", "") 25 | elif value == "true" or value == "false": 26 | value = value == "true" 27 | else: 28 | value = int(value) 29 | 30 | self.config[key] = value 31 | -------------------------------------------------------------------------------- /src/fetcher.py: -------------------------------------------------------------------------------- 1 | import platform 2 | import psutil 3 | from screeninfo import get_monitors 4 | import os, platform 5 | import cpuinfo 6 | import distro 7 | 8 | class Fetcher: 9 | def __init__(self, char): 10 | self.char = char 11 | 12 | # System 13 | def get_architecture(self): 14 | return platform.processor() 15 | def get_kernel(self): 16 | return platform.release() 17 | def get_hostname(self): 18 | return platform.node() 19 | def get_user(self): 20 | return os.getlogin() 21 | def get_shell(self): 22 | return os.readlink('/proc/%d/exe' % os.getppid()) 23 | def get_de(self): 24 | de = os.environ.get('DESKTOP_SESSION') 25 | 26 | if de is not None: 27 | de = de.lower() 28 | if de.startswith("ubuntu"): 29 | return "unity" 30 | elif de.startswith("lubuntu"): 31 | return "lxde" 32 | elif de.startswith("kubuntu"): 33 | return "kde" 34 | elif de.startswith("razon"): 35 | return "razor-qt" 36 | elif de.startswith("wmaker"): 37 | return "window-maker" 38 | else: 39 | return de 40 | elif os.environ.get('KDE_FULL_SESSION') == 'true': 41 | return "kde" 42 | elif os.environ.get('GNOME_DESKTOP_SESSION_ID'): 43 | if not "deprecated" in os.environ.get('GNOME_DESKTOP_SESSION_ID'): 44 | return "gnome2" 45 | return "unknown" 46 | 47 | def get_os(self): 48 | os = platform.system() 49 | if os == "Linux": 50 | distro_info = distro.linux_distribution(full_distribution_name=False) 51 | return distro_info[0].capitalize() + " " + str(distro_info[1]) 52 | return os 53 | # Screen 54 | def get_screen_size(self): 55 | for m in get_monitors(): 56 | return str(m.width) + "x" + str(m.height) 57 | return "unknown" 58 | # Disk 59 | def get_hdd_used(self): 60 | return int(psutil.disk_usage("/").used / 1024 / 1024 / 1024) 61 | def get_hdd_total(self): 62 | return int(psutil.disk_usage("/").total / 1024 / 1024 / 1024) 63 | def get_hdd_free(self): 64 | return int(psutil.disk_usage("/").free / 1024 / 1024 / 1024) 65 | def get_hdd_usage(self): 66 | return psutil.disk_usage("/").percent 67 | # CPU 68 | def get_cpu_brand(self): 69 | return cpuinfo.get_cpu_info()['brand_raw'] 70 | def get_cpu_usage(self): 71 | return psutil.cpu_percent() 72 | def get_cpu_count(self): 73 | return psutil.cpu_count() 74 | def get_cpu_max_freq(self): 75 | return psutil.cpu_freq().max / 1000 76 | def get_cpu_min_freq(self): 77 | return psutil.cpu_freq().min / 1000 78 | def get_cpu_freq(self): 79 | return psutil.cpu_freq().current / 1000 80 | # RAM 81 | def get_ram_usage(self): 82 | return psutil.virtual_memory().percent 83 | def get_ram_used(self): 84 | return int(psutil.virtual_memory().used / 1024 / 1024) 85 | def get_ram_free(self): 86 | return int(psutil.virtual_memory().free / 1024 / 1024) 87 | def get_ram_total(self): 88 | return int(psutil.virtual_memory().total / 1024 / 1024) 89 | 90 | def format(self, text): 91 | return (text 92 | # RAM 93 | .replace("{ram_total}", str(self.get_ram_total()) + "MiB") 94 | .replace("{ram_free}", str(self.get_ram_free()) + "MiB") 95 | .replace("{ram_used}", str(self.get_ram_used()) + "MiB") 96 | .replace("{ram_usage}", str(self.get_ram_usage()) + "%") 97 | 98 | # CPU 99 | .replace("{cpu_freq}", str(self.get_cpu_freq()) + "GHz") 100 | .replace("{cpu_min_freq}", str(self.get_cpu_min_freq()) + "GHz") 101 | .replace("{cpu_max_freq}", str(self.get_cpu_max_freq()) + "GHz") 102 | .replace("{cpu_count}", str(self.get_cpu_count())) 103 | .replace("{cpu_usage}", str(self.get_cpu_usage()) + "%") 104 | .replace("{cpu_brand}", str(self.get_cpu_brand())) 105 | 106 | # Disk 107 | .replace("{hdd_usage}", str(self.get_hdd_usage()) + "%") 108 | .replace("{hdd_free}", str(self.get_hdd_free()) + "GiB") 109 | .replace("{hdd_total}", str(self.get_hdd_total()) + "GiB") 110 | .replace("{hdd_used}", str(self.get_hdd_used()) + "GiB") 111 | 112 | # Screen 113 | .replace("{screen_size}", str(self.get_screen_size())) 114 | 115 | # System 116 | .replace("{de}", str(self.get_de())) 117 | .replace("{shell}", str(self.get_shell())) 118 | .replace("{os}", str(self.get_os())) 119 | .replace("{user}", str(self.get_user())) 120 | .replace("{hostname}", str(self.get_hostname())) 121 | .replace("{kernel}", str(self.get_kernel())) 122 | .replace("{arch}", str(self.get_architecture())) 123 | 124 | # Colors 125 | .replace("{colors_light}", "{light_black}{char} {light_red}{char} {light_green}{char} {light_yellow}{char} {light_blue}{char} {light_magenta}{char} {light_cyan}{char} {light_white}{char}") 126 | .replace("{colors}", "{black}{char} {red}{char} {green}{char} {yellow}{char} {blue}{char} {magenta}{char} {cyan}{char} {white}{char}") 127 | 128 | # Extra 129 | .replace("{char}", self.char) 130 | ) 131 | -------------------------------------------------------------------------------- /src/main.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from config import Config 3 | from utils import Utils 4 | from fetcher import Fetcher 5 | from colors import Colors 6 | import os 7 | 8 | class MeowFetch: 9 | def __init__(self): 10 | config_dir = os.path.join(Path.home(), ".config") 11 | working_dir = os.path.join(config_dir, "meow-fetch") 12 | 13 | # Config 14 | config = Config(os.path.join(working_dir, "config.meow")) 15 | config.load() 16 | 17 | # Colors 18 | colors = Colors() 19 | 20 | # Utilities 21 | utils = Utils() 22 | 23 | # Fetcher 24 | fetcher = Fetcher(config.get("color-char", "*")) 25 | 26 | # Ascii 27 | ascii_path = config.get("ascii-path", "ascii/cat.txt") 28 | ascii = open(os.path.join(working_dir, ascii_path)).read() 29 | 30 | # Info 31 | info_path = os.path.join(working_dir, "info.meow") 32 | info = open(info_path).read() 33 | 34 | self.config = config 35 | self.colors = colors 36 | self.fetcher = fetcher 37 | self.utils = utils 38 | self.ascii = ascii 39 | self.info = info 40 | 41 | def fetch(self): 42 | padding_x = self.config.get("padding-x", 3) 43 | padding_y = self.config.get("padding-y", 2) 44 | space_between = self.config.get("space-between", 3) 45 | 46 | ascii = self.utils.normalize(self.ascii) 47 | info = self.utils.add_padding_to_text(self.info, space_between, 0) 48 | 49 | raw_text = self.utils.add_info_to_ascii(ascii, info) 50 | text = self.utils.add_padding_to_text(raw_text, padding_x, padding_y) 51 | 52 | print(self.colors.format(self.fetcher.format(text))) 53 | 54 | if __name__ == "__main__": 55 | MeowFetch().fetch() 56 | -------------------------------------------------------------------------------- /src/utils.py: -------------------------------------------------------------------------------- 1 | class Utils: 2 | def format_info_lines(self, info_lines): 3 | result = "" 4 | 5 | for line in info_lines: 6 | if result != "": 7 | result += "\n" 8 | 9 | result += line 10 | 11 | return result 12 | 13 | def get_width_in_screen(self, text): 14 | length = 0 15 | for line in text.splitlines(): 16 | line_length = len(line) 17 | if length < line_length: 18 | length = line_length 19 | return length 20 | 21 | def normalize(self, text): 22 | length = self.get_width_in_screen(text) 23 | output = "" 24 | 25 | for line in text.splitlines(): 26 | while len(line) < length: 27 | line += " " 28 | 29 | if output != "": 30 | output += "\n" 31 | output += line 32 | return output 33 | 34 | def add_info_to_ascii(self, ascii, info_text): 35 | result = "" 36 | info_lines = info_text.splitlines() 37 | info_length = len(info_lines) 38 | 39 | ascii_lines = ascii.splitlines() 40 | ascii_length = len(ascii_lines) 41 | 42 | i_ascii = 0 43 | i_info = 0 44 | padding = self.get_width_in_screen(ascii) 45 | 46 | limit = max(ascii_length, info_length) 47 | 48 | for _ in range(limit): 49 | if result != "": 50 | result += "\n" 51 | 52 | has_ascci_in_line = False 53 | 54 | if i_ascii < ascii_length: 55 | result += ascii_lines[i_ascii] 56 | i_ascii += 1 57 | has_ascci_in_line = True 58 | if i_info < info_length: 59 | if has_ascci_in_line is False: 60 | result += " " * padding 61 | result += info_lines[i_info] 62 | i_info += 1 63 | return result 64 | 65 | 66 | def add_padding_to_text(self, text, x, y): 67 | result = "" 68 | lines = text.splitlines() 69 | 70 | for l in lines: 71 | if result != "": 72 | result += "\n" 73 | else: 74 | for _ in range(y): 75 | result += "\n" 76 | 77 | lr_padding = " " * x 78 | result += lr_padding + l + lr_padding 79 | return result 80 | --------------------------------------------------------------------------------