Last active
June 24, 2026 16:58
-
-
Save EvilSupahFly/4a453cc8c505ee9b3a1114ec9b7627b1 to your computer and use it in GitHub Desktop.
Python Virtual Environment Manager using PythonZ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # ~/.bashrc: executed by bash(1) for non-login shells. | |
| # see /usr/share/doc/bash/examples/startup-files (in the package bash-doc) | |
| # for examples | |
| ulimit -c unlimited | |
| # Regular Colors Bold | |
| #| Value | Color | Value | Color | | |
| #| -------- | ------ | -------- | ------ | | |
| #| \e[0;30m | Black | \e[1;30m | Black | | |
| #| \e[0;31m | Red | \e[1;31m | Red | | |
| #| \e[0;32m | Green | \e[1;32m | Green | | |
| #| \e[0;33m | Yellow | \e[1;33m | Yellow | | |
| #| \e[0;34m | Blue | \e[1;34m | Blue | | |
| #| \e[0;35m | Purple | \e[1;35m | Purple | | |
| #| \e[0;36m | Cyan | \e[1;36m | Cyan | | |
| #| \e[0;37m | White | \e[1;37m | White | | |
| # Underline Background | |
| #| Value | Color | Value | Color | | |
| #| -------- | ------ | ------ | ------ | | |
| #| \e[4;30m | Black | \e[40m | Black | | |
| #| \e[4;31m | Red | \e[41m | Red | | |
| #| \e[4;32m | Green | \e[42m | Green | | |
| #| \e[4;33m | Yellow | \e[43m | Yellow | | |
| #| \e[4;34m | Blue | \e[44m | Blue | | |
| #| \e[4;35m | Purple | \e[45m | Purple | | |
| #| \e[4;36m | Cyan | \e[46m | Cyan | | |
| #| \e[4;37m | White | \e[47m | White | | |
| # High Intensity Bold High Intensity | |
| #| Value | Color | Value | Color | | |
| #| -------- | ------ | -------- | ------ | | |
| #| \e[0;90m | Black | \e[1;90m | Black | | |
| #| \e[0;91m | Red | \e[1;91m | Red | | |
| #| \e[0;92m | Green | \e[1;92m | Green | | |
| #| \e[0;93m | Yellow | \e[1;93m | Yellow | | |
| #| \e[0;94m | Blue | \e[1;94m | Blue | | |
| #| \e[0;95m | Purple | \e[1;95m | Purple | | |
| #| \e[0;96m | Cyan | \e[1;96m | Cyan | | |
| #| \e[0;97m | White | \e[1;97m | White | | |
| # High Intensity backgrounds | |
| #| Value | Color | | |
| #| --------- | ------ | | |
| #| \e[0;100m | Black | | |
| #| \e[0;101m | Red | | |
| #| \e[0;102m | Green | | |
| #| \e[0;103m | Yellow | | |
| #| \e[0;104m | Blue | | |
| #| \e[0;105m | Purple | | |
| #| \e[0;106m | Cyan | | |
| #| \e[0;107m | White | | |
| # Reset | |
| #| Value | Color | | |
| #| ----- | ------ | | |
| #| \e[0m | Reset | | |
| # other styles | |
| #echo -e "\e[1mbold\e[0m" | |
| #echo -e "\e[3mitalic\e[0m" | |
| #echo -e "\e[3m\e[1mbold italic\e[0m" | |
| #echo -e "\e[4munderline\e[0m" | |
| #echo -e "\e[9mstrikethrough\e[0m" | |
| #echo -e "\e[31mHello World\e[0m" | |
| #echo -e "\x1B[31mHello World\e[0m" | |
| # Changing the first number ("\e[0;32m") can result in effects like bold (3), underline (4), invert with background (7), crossed-out (9) and so on. | |
| # Examples: | |
| # RED="31" | |
| # GREEN="32" | |
| # BOLDGREEN="\e[1;${GREEN}m" | |
| # ITALICRED="\e[3;${RED}m" | |
| # | |
| # echo -e "${BOLDGREEN}Behold! Bold, green text.${ENDCOLOR}" | |
| # echo -e "${ITALICRED}Italian italics${ENDCOLOR}" | |
| # Reset | |
| #CLRTXT="\033[0m" # Text Reset | |
| # Regular Colors | |
| #Black="\033[0;30m" # Black | |
| #Red="\033[0;31m" # Red | |
| #Green="\033[0;32m" # Green | |
| #Yellow="\033[0;33m" # Yellow | |
| #Blue="\033[0;34m" # Blue | |
| #Purple="\033[0;35m" # Purple | |
| #Cyan="\033[0;36m" # Cyan | |
| #White="\033[0;37m" # White | |
| # Bold | |
| #BD_Black="\033[1;30m" # Black | |
| #BD_Red="\033[1;31m" # Red | |
| #BD_Green="\033[1;32m" # Green | |
| #BD_Yellow="\033[1;33m" # Yellow | |
| #BD_Blue="\033[1;34m" # Blue | |
| #BD_Purple="\033[1;35m" # Purple | |
| #BD_Cyan="\033[1;36m" # Cyan | |
| #BD_White="\033[1;37m" # White | |
| # Underline | |
| #UL_Black="\033[4;30m" # Black | |
| #UL_Red="\033[4;31m" # Red | |
| #UL_Green="\033[4;32m" # Green | |
| #UL_Yellow="\033[4;33m" # Yellow | |
| #UL_Blue="\033[4;34m" # Blue | |
| #UL_Purple="\033[4;35m" # Purple | |
| #UL_Cyan="\033[4;36m" # Cyan | |
| #UL_White="\033[4;37m" # White | |
| # Background | |
| #BKG_Black="\033[40m" # Black | |
| #BKG_Red="\033[41m" # Red | |
| #BKG_Green="\033[42m" # Green | |
| #BKG_Yellow="\033[43m" # Yellow | |
| #BKG_Blue="\033[44m" # Blue | |
| #BKG_Purple="\033[45m" # Purple | |
| #BKG_Cyan="\033[46m" # Cyan | |
| #BKG_White="\033[47m" # White | |
| # High Intensity | |
| #HI_Black="\033[0;90m" # Black | |
| #HI_Red="\033[0;91m" # Red | |
| #HI_Green="\033[0;92m" # Green | |
| #HI_Yellow="\033[0;93m" # Yellow | |
| #HI_Blue="\033[0;94m" # Blue | |
| #HI_Purple="\033[0;95m" # Purple | |
| #HI_Cyan="\033[0;96m" # Cyan | |
| #HI_White="\033[0;97m" # White | |
| # Bold High Intensity | |
| #BHI_Black="\033[1;90m" # Black | |
| #BHI_Red="\033[1;91m" # Red | |
| #BHI_Green="\033[1;92m" # Green | |
| #BHI_Yellow="\033[1;93m" # Yellow | |
| #BHI_Blue="\033[1;94m" # Blue | |
| #BHI_Purple="\033[1;95m" # Purple | |
| #BHI_Cyan="\033[1;96m" # Cyan | |
| #BHI_White="\033[1;97m" # White | |
| # High Intensity backgrounds | |
| #HIBKG_Black="\033[0;100m" # Black | |
| #HIBKG_Red="\033[0;101m" # Red | |
| #HIBKG_Green="\033[0;102m" # Green | |
| #HIBKG_Yellow="\033[0;103m" # Yellow | |
| #HIBKG_Blue="\033[0;104m" # Blue | |
| #HIBKG_Purple="\033[0;105m" # Purple | |
| #HIBKG_Cyan="\033[0;106m" # Cyan | |
| #HIBKG_White="\033[0;107m" # White | |
| #IFS=$'\n' | |
| # Define some fancy colourful text with BASH's built-in escape codes. Example: | |
| # echo -e "${BOLD}${YELLOW}This text will be displayed in BOLD YELLOW. ${RESET}While this text is normal." | |
| BOLD="\033[1m" #Bold or Hi-Intensty - depends on your terminal app | |
| ULINE="\033[4m" #Underline | |
| RESET="\033[0m" # Normal | |
| RED="\033[1m\033[1;91m" # Red | |
| GREEN="\033[1m\033[1;92m" # Green | |
| YELLOW="\033[1m\033[1;33m" # Yellow | |
| WHITE="\033[1m\033[1;97m" # White | |
| PURPLE="\033[1m\033[35m" # Magenta (Purple) | |
| # If not running interactively, don't do anything | |
| case $- in | |
| *i*) ;; | |
| *) return;; | |
| esac | |
| #echo -e "\n$HOME/.bashrc\n" | |
| # don't put duplicate lines or lines starting with space in the history. | |
| # See bash(1) for more options | |
| HISTCONTROL=ignoreboth:erasedups | |
| # append to the history file, don't overwrite it | |
| shopt -s histappend | |
| # for setting history length see HISTSIZE and HISTFILESIZE in bash(1) | |
| HISTSIZE=-1 | |
| HISTFILESIZE=-1 | |
| # check the window size after each command and, if necessary, | |
| # update the values of LINES and COLUMNS. | |
| shopt -s checkwinsize | |
| # If set, the pattern "**" used in a pathname expansion context will | |
| # match all files and zero or more directories and subdirectories. | |
| shopt -s globstar | |
| # make less more friendly for non-text input files, see lesspipe(1) | |
| [ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)" | |
| [ -x /usr/bin/morepipe ] && eval "$(SHELL=/bin/sh morepipe)" | |
| # set variable identifying the chroot you work in (used in the prompt below) | |
| #if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then | |
| # debian_chroot=$(cat /etc/debian_chroot) | |
| #fi | |
| # set a fancy prompt (non-color, unless we know we "want" color) | |
| case "$TERM" in | |
| xterm-color) color_prompt=yes;; | |
| esac | |
| # uncomment for a colored prompt, if the terminal has the capability; turned | |
| # off by default to not distract the user: the focus in a terminal window | |
| # should be on the output of commands, not on the prompt | |
| force_color_prompt=yes | |
| #ANSI Colors List Table | |
| #Intensity 0 1 2 3 4 5 6 7 | |
| #Normal Black Red Green Yellow Blue Magenta Cyan White | |
| #Bright Black Red Green Yellow Blue Magenta Cyan White | |
| #Use "\e[0;34m" (or "\x1b[0;34m") for blue, "\e[1;34m" for light blue. | |
| #Vary first number: 1=alt_color, 3=bold, 4=underline, 7=invert, and so on. | |
| #Note: You need to replace digit 0 with 1 to get light colour version: | |
| #Black \e[0;30m White \e[1;37m | |
| #Brown \e[0;33m Yellow \e[1;33m | |
| #Gray \e[0;37m Dark Gray \e[1;30m | |
| #Blue \e[0;34m Light Blue \e[1;34m | |
| #Green \e[0;32m Light Green \e[1;32m | |
| #Cyan \e[0;36m Light Cyan \e[1;36m | |
| #Red \e[0;31m Light Red \e[1;31m | |
| #Purple \e[0;35m Light Purple \e[1;35m | |
| #Remember that varying the first number ("\e[0;32m") can result in some cool effect like bold (3), underline (4), invert with background (7), crossed-out (9) and so on. | |
| if [ -n "$force_color_prompt" ]; then | |
| if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then | |
| # We have color support; assume it's compliant with Ecma-48 | |
| # (ISO/IEC-6429). (Lack of such support is extremely rare, and such | |
| # a case would tend to support setf rather than setaf.) | |
| color_prompt=yes | |
| else | |
| color_prompt=yes | |
| fi | |
| fi | |
| NICEPS1="\e[1;38;5;56;48;5;234m\u \e[38;5;240mon \e[1;38;5;28;48;5;234m\h \e[38;5;240mat \e[1;38;5;56;48;5;234m\[\$(date +'%a %-d %b %Y %H:%M:%S %Z')\\]\e[0m\n\e[0;38;5;56;48;5;234m[\w]\e[0m " | |
| NICEPS2="\e[1;38;5;56m\u \e[38;5;240mon \e[1;38;5;28m\h \e[38;5;240mat \e[1;38;5;56m\[$(date +'%a %-d %b %Y %H:%M:%S %Z')\]\e[0m\n\e[0;38;5;56m[\w]\e[0m " | |
| NICEPS3="\e[1;38;5;45m\u \e[38;5;240mon \e[1;38;5;34m\h \e[38;5;240mat \e[1;38;5;45m\[$(date +'%a %-d %b %Y %H:%M:%S %Z')\]\e[0m\n\e[0;38;5;45m[\w]\e[0m " | |
| NICEPS4="\e[1;38;5;196m\u \e[38;5;240mon \e[1;38;5;82m\h \e[38;5;240mat \e[1;38;5;196m\[$(date +'%a %-d %b %Y %H:%M:%S %Z')\]\e[0m\n\e[0;38;5;196m[\w]\e[0m " | |
| NICEPS5="\[\e[1;38;5;56m\]\u \[\e[1;38;5;240m\]on \[\e[1;38;5;28m\]\h \[\e[1;38;5;240m\]at \[\e[1;38;5;56m\]\[\$(date +'%a %-d %b %Y %H:%M:%S %Z')\]\[\e[0m\]\n\[\e[1;38;5;56m\][\w]\[\e[0m\] " | |
| NICEPS6="\[\e[1;38;5;56m\]\u \[\e[1;38;5;240m\]on \[\e[1;38;5;28m\]\h \[\e[1;38;5;240m\]at \[\e[1;38;5;56m\]\[\$(date +'%a %-d %b %Y %H:%M:%S %Z')\]\[\e[0m\]\n\[\e[1;38;5;56m\][\w]\[\e[0m\] " | |
| NICEPS7="\e[1;38;5;56m\u \e[38;5;240mon \e[1;38;5;28m\h \e[38;5;240mat \e[1;38;5;56m\[\$(date +'%a %-d %b %Y %H:%M:%S %Z')\\]\e[0m\n\e[0;38;5;56m[\w]\e[0m " | |
| NICEPS8="\[\e[1;95m\]│\u \[\e[1;97m\]on \[\e[1;92m\]\h \[\e[1;97m\]- \[\e[1;95m\]\[\$(date +'%a %-d %b %Y %H:%M:%S %Z')\]\[\e[0m\]\n\[\e[1;93m\]╰─>[\w]\[\e[0m\] " | |
| #PS1="$NICEPS5" | |
| if [ "$color_prompt" = yes ]; then | |
| PS1=$NICEPS8 | |
| else | |
| PS1='\u on \h \d \@\n[\w] $ ' | |
| fi | |
| # If this is an xterm set the title to user@host:dir | |
| case "$TERM" in | |
| xterm*|rxvt*) | |
| #PS1="\[\e]0;\u@\h: \w\a\]$PS1" | |
| PS1=$NICEPS8 | |
| ;; | |
| *) | |
| ;; | |
| esac | |
| # enable color support of ls and also add handy aliases | |
| if [ -x /usr/bin/dircolors ]; then | |
| test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)" | |
| alias ls='ls --color=auto' | |
| #alias dir='dir --color=auto' | |
| #alias vdir='vdir --color=auto' | |
| alias grep='grep --color=auto' | |
| alias fgrep='fgrep --color=auto' | |
| alias egrep='egrep --color=auto' | |
| fi | |
| # colored GCC warnings and errors | |
| export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01' | |
| # some more ls aliases | |
| alias ll='ls -alF' | |
| alias la='ls -A' | |
| alias l='ls -CF' | |
| # Some custom aliases | |
| alias dex='dwarfsextract --stdout-progress' | |
| alias ytbest="youtube-dl -f 'bestvideo+bestaudio' --recode-video 'mkv'" | |
| # Add an "alert" alias for long running commands. Use like so: | |
| # sleep 10; alert | |
| alias alert='notify-send --urgency=high -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"' | |
| alias yt-mp3='youtube-dl --yes-playlist --extract-audio --audio-quality 0 --audio-format mp3 --progress' | |
| # Alias definitions. | |
| # You may want to put all your additions into a separate file like | |
| # ~/.bash_aliases, instead of adding them here directly. | |
| # See /usr/share/doc/bash-doc/examples in the bash-doc package. | |
| if [ -f ~/.bash_aliases ]; then | |
| . ~/.bash_aliases | |
| fi | |
| # enable programmable completion features (you don't need to enable | |
| # this, if it's already enabled in /etc/bash.bashrc and /etc/profile | |
| # sources /etc/bash.bashrc). | |
| if ! shopt -oq posix; then | |
| if [ -f /usr/share/bash-completion/bash_completion ]; then | |
| . /usr/share/bash-completion/bash_completion | |
| elif [ -f /etc/bash_completion ]; then | |
| . /etc/bash_completion | |
| fi | |
| fi | |
| # added by pipx (https://github.com/pipxproject/pipx) | |
| export PATH="/home/$(whoami)/.local/bin:$PATH" | |
| wbfs() { | |
| F_IN="$1" | |
| FOUT="${F_IN%.*}.wbfs" | |
| echo -e "${WHITE}Converting ${YELLOW}$F_IN ${WHITE}to ${YELLOW}$FOUT${WHITE}." | |
| wit copy --wbfs "$F_IN" "$FOUT" | |
| } | |
| # Function to check and create a Python virtual environment using pythonz - v4 | |
| pyvenv() { | |
| # Graceful Error-Exit Helper | |
| die() { | |
| echo -e "${RED}Error: $*${RESET}" >&2 | |
| return 1 | |
| } | |
| local CHECK=0 | |
| local LIST=0 | |
| local CUSTOM=0 | |
| local CUST_LOC="" | |
| local v_name="" | |
| local py_ver="" | |
| local usepy="" | |
| local v_home="" | |
| # Force use of system libmpdec to ensure '_decimal' builds cleanly (Python ≥3.13) | |
| export PYTHONZ_CONFIGURE_OPTS="--with-system-libmpdec" | |
| # Parse flags | |
| while [[ "$1" =~ ^-- ]]; do | |
| case "$1" in | |
| --check-ver) | |
| CHECK=1 | |
| shift | |
| ;; | |
| --list) | |
| LIST=1 | |
| shift | |
| ;; | |
| --custom) | |
| CUST_LOC="$2" | |
| v_name="$3" | |
| py_ver="$4" | |
| CUSTOM=1 | |
| shift 4 | |
| if [ -z "$CUST_LOC" ]; then | |
| echo -e "${RED}Error: --custom requires a target path${RESET}" | |
| echo -e "${WHITE}Usage: pyvenv --custom \"PATH/TO/TARGET/\" [NAME] [VERSION]\nNote that while path is ${RED}mandatory${WHITE}, ${YELLOW}[NAME]${WHITE} and ${YELLOW}[VERSION]${WHITE} are optional.${RESET}" | |
| return 1 | |
| fi | |
| ;; | |
| --) | |
| shift | |
| break | |
| ;; | |
| *) | |
| echo -e "${RED}Unknown option: $1${RESET}" | |
| return 1 | |
| ;; | |
| esac | |
| done | |
| # Handle --list | |
| if [ "$LIST" -eq 1 ]; then | |
| if [ "$#" -gt 0 ]; then | |
| echo -e "${YELLOW}Warning: --list does not take additional arguments. Ignoring: $*${RESET}" | |
| fi | |
| if [ -d "$HOME/python-virtual-environs/" ]; then | |
| echo -e "${WHITE}Existing virtual environments:${RESET}" | |
| ls -1A "$HOME/python-virtual-environs/" | |
| else | |
| echo -e "${YELLOW}No virtual environments found.${RESET}" | |
| fi | |
| return 0 | |
| fi | |
| # Handle --check-ver | |
| if [ "$CHECK" -eq 1 ]; then | |
| if [ "$#" -gt 0 ]; then | |
| echo -e "${YELLOW}Warning: --check-ver does not take additional arguments. Ignoring: $*${RESET}" | |
| fi | |
| echo -e "${WHITE}Fetching latest Python releases...${RESET}" | |
| mapfile -t VERSIONS < <( | |
| curl -s https://www.python.org/ftp/python/ | | |
| grep -oP '(?<=href=")[0-9]+\.[0-9]+\.[0-9]+(?=/")' | | |
| grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | | |
| sort -Vr | uniq | while read -r ver; do | |
| url="https://www.python.org/ftp/python/$ver/Python-$ver.tgz" | |
| if curl -sfI "$url" > /dev/null; then | |
| echo "$ver" | |
| fi | |
| done | head -n 10 | |
| ) | |
| if [ "${#VERSIONS[@]}" -eq 0 ]; then | |
| echo -e "${RED}Unable to retrieve version list. You can specify a version manually.${RESET}" | |
| return 1 | |
| fi | |
| echo -e "${WHITE}Available versions:${RESET}" | |
| select VER in "${VERSIONS[@]}" "Cancel"; do | |
| if [ "$VER" = "Cancel" ] || [ -z "$VER" ]; then | |
| echo -e "${WHITE}Cancelled by user request: no virtual environment created.${RESET}" | |
| return 0 | |
| else | |
| VER_NAME="python-website-v${VER}" | |
| echo -e "${GREEN}Python virtual environment $VER_NAME will be created using Python v${VER}.${RESET}" | |
| pyvenv "$VER_NAME" "$VER" | |
| return | |
| fi | |
| done | |
| fi | |
| # Gather args | |
| if [ $CUSTOM -eq 0 ]; then | |
| v_name="$1" | |
| py_ver="$2" | |
| fi | |
| if [ -z "$v_name" ]; then | |
| read -erp "$(echo -e "${WHITE}Enter the virtual environment name: ${RESET}")" v_name | |
| fi | |
| if [[ "$v_name" =~ ^- ]]; then | |
| echo -e "${RED}Error: Virtual environment name cannot start with a dash: '$v_name'${RESET}" | |
| echo -e "${WHITE}Usage: pyvenv NAME [VERSION]${RESET}" | |
| echo -e "${WHITE} pyvenv --list${RESET}" | |
| echo -e "${WHITE} pyvenv --check-ver${RESET}" | |
| return 1 | |
| fi | |
| if [ $CUSTOM -eq 1 ]; then | |
| v_home="$CUST_LOC/$v_name" | |
| else | |
| v_home="$HOME/python-virtual-environs/$v_name" | |
| fi | |
| # Check pythonz | |
| if ! command -v pythonz >/dev/null 2>&1; then | |
| echo -e "${RED}pythonz not found. ${WHITE}Installing...${RESET}" | |
| curl -sL https://raw.github.com/saghul/pythonz/master/pythonz-install | bash | |
| if ! grep -q '\.pythonz/etc/bashrc' "$HOME/.bashrc"; then | |
| echo '[[ -s $HOME/.pythonz/etc/bashrc ]] && source $HOME/.pythonz/etc/bashrc' >> "$HOME/.bashrc" | |
| echo -e "${GREEN}Added pythonz to .bashrc${RESET}" | |
| fi | |
| source "$HOME/.bashrc" | |
| pyvenv "$v_name" "$py_ver" | |
| return | |
| fi | |
| # Create venv if missing | |
| if [ ! -d "$v_home" ]; then | |
| echo -e "${YELLOW}Virtual environment '${WHITE}${v_name}${YELLOW}' does not exist. ${WHITE}Creating it now.${RESET}" | |
| if [ -z "$py_ver" ]; then | |
| read -erp "$(echo -e "${WHITE}Enter the Python version (e.g. 3.12.1): ${RESET}")" py_ver | |
| fi | |
| usepy=$(pythonz locate "$py_ver" 2>/dev/null) | |
| if [[ -z "$usepy" || "$usepy" == "ERROR: "* ]]; then | |
| echo -e "${WHITE}Python ${RED}$py_ver${WHITE} not found. ${WHITE}Installing...${RESET}" | |
| pythonz install --verbose --configure="--without-pydebug" "$py_ver" || die "pythonz installation failed for version $py_ver." | |
| usepy=$(pythonz locate "$py_ver") || die "pythonz locate failed — Python $py_ver not installed properly." | |
| fi | |
| "$usepy" -m venv "$v_home" | |
| echo -e "${GREEN}Virtual environment '${WHITE}${v_name}${YELLOW}' created successfully.${RESET}" | |
| else | |
| usepy="$v_home/bin/python3" | |
| py_ver=$("$usepy" --version 2>/dev/null | awk '{print $2}') | |
| echo -e "${GREEN}Virtual environment '$v_name' already exists.${RESET}" | |
| fi | |
| echo | |
| echo -e "${WHITE} VENV name: ${GREEN}$v_name${RESET}" | |
| echo -e "${WHITE} VENV path: ${GREEN}$v_home${RESET}" | |
| echo -e "${WHITE} Python used: ${GREEN}$usepy${RESET}" | |
| echo -e "${WHITE} Version: ${GREEN}$py_ver${RESET}" | |
| echo | |
| source "$v_home/bin/activate" | |
| echo -e "${WHITE}Activated virtual environment '${YELLOW}${v_name}${WHITE}'. Type 'deactivate' to exit.${RESET}" | |
| } | |
| dirfind() { | |
| # Finds subdirectories of $1 modified within ±1 day of $2 (or "today" if specified) | |
| # Usage: find_subdirs_by_date <directory> <date (YYYY-MM-DD | today)> | |
| local DIRECTORY=$1 | |
| local TARGET_DATE=$2 | |
| if [ $# -ne 2 ]; then | |
| echo "Usage: find_subdirs_by_date <directory> <date (YYYY-MM-DD | today)>" | |
| return 1 | |
| fi | |
| if [ ! -d "$DIRECTORY" ]; then | |
| echo "Error: Directory '$DIRECTORY' does not exist." | |
| return 1 | |
| fi | |
| if [ "${TARGET_DATE,,}" = "today" ]; then | |
| TARGET_DATE=$(date +%Y-%m-%d) | |
| fi | |
| local TARGET_DATE_EPOCH | |
| TARGET_DATE_EPOCH=$(date -d "$TARGET_DATE" +%s 2>/dev/null) | |
| if [ $? -ne 0 ]; then | |
| echo "Error: Invalid date format. Use YYYY-MM-DD or 'today'." | |
| return 1 | |
| fi | |
| local START_DATE_EPOCH=$((TARGET_DATE_EPOCH - 86400)) | |
| local END_DATE_EPOCH=$((TARGET_DATE_EPOCH + 86400)) | |
| local START_DATE | |
| local END_DATE | |
| START_DATE=$(date -d "@$START_DATE_EPOCH" "+%Y-%m-%d %H:%M:%S") | |
| END_DATE=$(date -d "@$END_DATE_EPOCH" "+%Y-%m-%d %H:%M:%S") | |
| echo "Searching in '$DIRECTORY' for subdirectories modified between:" | |
| echo " $START_DATE and $END_DATE" | |
| echo | |
| # Collect results manually (portable alternative to mapfile) | |
| local RESULTS="" | |
| while IFS= read -r dir; do | |
| RESULTS="${RESULTS}${dir}"$'\n' | |
| done < <(find "$DIRECTORY" -mindepth 1 -type d -newermt "$START_DATE" ! -newermt "$END_DATE" 2>/dev/null) | |
| if [ -z "$RESULTS" ]; then | |
| echo "No subdirectories found modified within the specified date range." | |
| return 0 | |
| fi | |
| printf "%-30s %s\n" "DIRECTORY" "LAST MODIFIED" | |
| printf "%-30s %s\n" "---------" "--------------" | |
| # Print each directory and its modification timestamp | |
| while IFS= read -r dir; do | |
| [ -z "$dir" ] && continue | |
| local mtime | |
| mtime=$(stat -c "%y" "$dir" 2>/dev/null | cut -d'.' -f1) | |
| printf "%-30s %s\n" "$dir" "$mtime" | |
| done <<< "$RESULTS" | |
| return 0 | |
| } | |
| # Function to convert media files to PS4-compatible formats using FFMPEG | |
| # The function has been reworked. Now, by calling makePS4, you can convert a single file. No more template requirement. | |
| # I've also added batchPS4() for batch-mode processing, which can handle entire directory trees by running instances of makePS4() | |
| # as a background process. To prevent it from consuming too many system resources, it will default to running one process per CPU | |
| # core unless otherwise directed through use of the --MAX=## argument, which is limited to 1.5x your 'nproc' core count. | |
| # | |
| # If you plan to add these functions to your '~/.bashrc', you can declare the colour codes outside these functions, then remove | |
| # the colour code definitions from inside the top of each function. | |
| # | |
| # Single file use: | |
| # > makePS4 "/path/to/some/file.mkv" | |
| # | |
| # Batch Mode processing: | |
| # > batchPS4 "/path/to/some/folder/Season 1" | |
| # >> OR << | |
| # > batchPS4 "/path/to/some/other/folder/Season 3" --MAX=16 | |
| check_ffmpeg() { | |
| if [ -z "$FFMPEG_OK" ]; then | |
| if ! command -v ffmpeg &> /dev/null; then | |
| echo "FFMPEG is not installed. Attempting to install..." | |
| install_ffmpeg | |
| else | |
| echo "FFMPEG is already installed." | |
| fi | |
| export FFMPEG_OK=1 | |
| fi | |
| return 0 | |
| } | |
| # Function to install FFMPEG using popular package managers | |
| install_ffmpeg() { | |
| # Use sudo if it's available, otherwise run as-is | |
| SUDO="" | |
| if command -v sudo >/dev/null 2>&1; then | |
| SUDO="sudo" | |
| fi | |
| managers="apt dnf yum pacman zypper emerge xbps-install apk brew" | |
| commands=( | |
| "$SUDO apt update && $SUDO apt install -y ffmpeg" | |
| "$SUDO dnf install -y ffmpeg" | |
| "$SUDO yum install -y ffmpeg" | |
| "$SUDO pacman -Sy --noconfirm ffmpeg" | |
| "$SUDO zypper install -y ffmpeg" | |
| "$SUDO emerge --ask=n media-video/ffmpeg" | |
| "$SUDO xbps-install -y ffmpeg" | |
| "$SUDO apk add --no-interactive ffmpeg" | |
| "brew install ffmpeg" | |
| ) | |
| # Special case: Homebrew on Linux (not macOS) | |
| if [ "$(uname -s)" != "Darwin" ] && command -v brew >/dev/null 2>&1; then | |
| echo "Homebrew detected on Linux." | |
| brew install ffmpeg | |
| return $? | |
| fi | |
| local i=0 | |
| for manager in $managers; do | |
| if command -v "$manager" >/dev/null 2>&1; then | |
| echo "Using package manager: $manager" | |
| eval "${commands[$i]}" | |
| return $? | |
| fi | |
| i=$((i + 1)) | |
| done | |
| echo "No supported package manager found. Please install FFMPEG manually." | |
| return 1 | |
| } | |
| makePS4() { | |
| set +H | |
| # Colour Codes | |
| RESET="\033[0m"; RED="\033[1m\033[1;91m"; WHITE="\033[1m\033[1;97m"; GREEN="\033[1m\033[1;92m" | |
| local out_dir | |
| local out_base | |
| if [ -z "$FFMPEG_OK" ]; then | |
| check_ffmpeg | |
| else | |
| echo "FFMPEG_OK = $FFMPEG" | |
| fi | |
| local TARGET="$1" | |
| if [[ -z "$TARGET" ]]; then | |
| echo -e "${RED}Usage: makePS4 FILE${RESET}" | |
| return 1 | |
| fi | |
| if [[ -d "$TARGET" ]]; then | |
| echo -e "${RED}'$TARGET' is a directory." | |
| echo -e "${WHITE}To convert an entire folder, use:" | |
| echo -e "${YELLOW} makePS4_batch \"$TARGET\" --MAX=#" | |
| echo -e "${WHITE} >> or <<" | |
| echo -e "${YELLOW} makePS4_batch \"$TARGET\" --MAX=#${RESET}" | |
| return 1 | |
| fi | |
| if [[ ! -f "$TARGET" ]]; then | |
| echo -e "${RED}File not found: $TARGET${RESET}" | |
| return 1 | |
| fi | |
| [[ -f "$TARGET" ]] || { echo -e "${RED}Target file not found: $TARGET${RESET}"; return 1; } | |
| local ps4_ext="mp4" | |
| # Compliance Check | |
| if check_ps4_compatibility "$TARGET"; then | |
| echo -e "${WHITE}Already PS4 compatible; skipping.${RESET}" | |
| return 0 | |
| else | |
| echo -e "${RED}$TARGET is not PS4 compatible. ${WHITE}Initiating transcode.${RESET}" | |
| sleep 2 | |
| fi | |
| # codec options as arrays | |
| local FOUR_GB=$((4 * 1024 * 1024 * 1024)) | |
| local filesize=$(stat -c '%s' "$TARGET") | |
| local vcodec=() | |
| if (( filesize >= FOUR_GB )); then | |
| echo -e "${WHITE}Large file detected (>= 4GB); enabling bitrate cap.${RESET}" | |
| vcodec=( | |
| -c:v libx264 | |
| -crf 20 | |
| -profile:v high | |
| -level:v 4.2 | |
| -pix_fmt yuv420p | |
| -x264-params ref=4:bframes=3 | |
| -maxrate 8000k | |
| -bufsize 16000k | |
| -vf scale=1920:-2 | |
| ) | |
| else | |
| vcodec=( | |
| -c:v libx264 | |
| -crf 20 | |
| -preset medium | |
| -profile:v high | |
| -level:v 4.2 | |
| -pix_fmt yuv420p | |
| ) | |
| fi | |
| local acodec=(-c:a aac -b:a 192k -ac 2) | |
| # Subtitles will only be kept if they use the 'mov_text' format. | |
| # PS4 doesn't support anything else, outside of flattening the subtitles into each frame as part of the video. | |
| # Loop over all subtitle streams and find an English mov_text track | |
| local subswitch=() | |
| local index="" | |
| local codec="" | |
| local lang="" | |
| while IFS= read -r stream; do | |
| index=$(echo "$stream" | awk -F',' '{print $1}') | |
| codec=$(echo "$stream" | awk -F',' '{print $2}') | |
| lang=$(echo "$stream" | awk -F',' '{print $3}') | |
| lang=${lang,,} | |
| if [[ "$codec" == "mov_text" && ( -z "$lang" || "$lang" == eng* ) ]]; then | |
| subswitch=(-map 0:s:"$index" -c:s mov_text) | |
| echo -e "${WHITE}Copying English mov_text subtitles.${RESET}" | |
| break | |
| fi | |
| done < <(ffprobe -v error -select_streams s \ | |
| -show_entries stream=index,codec_name:stream_tags=language \ | |
| -of csv=p=0 "$TARGET") | |
| if [[ -z "${subswitch[@]}" ]]; then | |
| echo -e "${WHITE}No English mov_text subtitles found; skipping subtitle stream.${RESET}" | |
| fi | |
| # Output file - sanitize for PS4 | |
| out_dir=$(dirname "$TARGET") | |
| out_base=$(basename "${TARGET%.*}-PS4.${ps4_ext}") | |
| out_base="${out_base//[!A-Za-z0-9._() -]/_}" | |
| out_base="${out_base// / }" | |
| out_base="${out_base//__/_}" | |
| PS4="$out_dir/$out_base" | |
| echo -e "${WHITE}Converting '$TARGET' to '$PS4'...${RESET}" | |
| ffmpeg -y -i "$TARGET" \ | |
| -progress pipe:1 \ | |
| -nostats \ | |
| -map 0:v:0 \ | |
| -map 0:a:0 \ | |
| "${vcodec[@]}" "${acodec[@]}" \ | |
| "${subswitch[@]}" \ | |
| "$PS4" || { | |
| echo -e "${RED}Conversion failed: $TARGET${RESET}" | |
| return 1 | |
| } | |
| echo -e "${WHITE}Created: $PS4${RESET}\n" | |
| #set -H | |
| } | |
| # Function to batch convert media files to PS4-compatible formats using makePS4 function above | |
| batchPS4() { | |
| set +m | |
| # Colour codes | |
| RESET="\033[0m"; RED="\033[1m\033[1;91m"; WHITE="\033[1m\033[1;97m"; GREEN="\033[1m\033[1;92m" | |
| local basedir="" | |
| local max_jobs="" | |
| local requested="" | |
| local running=0 | |
| local total=0 | |
| local failed=0 | |
| local showErr=0 | |
| local ERROR_LOG | |
| local real_file="" | |
| ERROR_LOG=$(mktemp) | |
| declare -A pid_to_errlog | |
| declare -A pid_to_file | |
| declare -a failed_files | |
| cleanup() { | |
| echo | |
| echo "Interrupted. Stopping active conversions..." | |
| for pid in "${!pid_to_file[@]}"; do | |
| kill "$pid" 2>/dev/null | |
| done | |
| rm -f "$ERROR_LOG" | |
| for f in "${pid_to_errlog[@]}"; do | |
| rm -f "$f" | |
| done | |
| wait 2>/dev/null | |
| echo "Cleanup complete." | |
| set -m | |
| return 130 | |
| } | |
| trap cleanup INT | |
| # Argument parsing | |
| for arg in "$@"; do | |
| if [[ "$arg" =~ ^--MAX=([0-9]+)$ ]]; then | |
| requested="${BASH_REMATCH[1]}" | |
| elif [[ "$arg" == "--show-errors" ]]; then | |
| showErr=1 | |
| elif [[ -d "$arg" ]]; then | |
| basedir="$arg" | |
| fi | |
| done | |
| [[ -z "$basedir" ]] && { | |
| echo -e "${RED}Error: must specify a directory.${RESET}" | |
| return 1 | |
| } | |
| # Concurrency logic | |
| cpu_count=$(nproc --all 2>/dev/null || getconf _NPROCESSORS_ONLN || echo 4) | |
| absolute_cap=$((cpu_count * 3 / 2)) | |
| if [[ -n "$requested" ]]; then | |
| if (( requested > absolute_cap )); then | |
| echo -e "${RED}Warning: --MAX capped to $absolute_cap (1.5x $cpu_count cores).${RESET}" | |
| max_jobs=$absolute_cap | |
| else | |
| max_jobs=$requested | |
| fi | |
| else | |
| max_jobs=$cpu_count | |
| fi | |
| echo -e "${WHITE}Batch mode: '$basedir' (max $max_jobs concurrent jobs)${RESET}" | |
| # Supports .MP4, .MKV, .MOV, and .AVI | |
| while IFS= read -r file; do | |
| ((total++)) | |
| errfile=$(mktemp) | |
| real_file="$(realpath "$file")" | |
| echo -e "${WHITE}Starting: $real_file ${RESET}\n" | |
| makePS4 "$real_file" \ | |
| # > >(sed -u "s|^|[$(basename "$file")] |") \ | |
| > >(grep -E 'out_time=|speed=' | sed -u "s|^|[$(basename "$file")] |") \ | |
| 2> "$errfile" & | |
| pid=$! | |
| pid_to_file[$pid]="$file" | |
| pid_to_errlog[$pid]="$errfile" | |
| ((running++)) | |
| # Track completion and failures inline | |
| if (( running >= max_jobs )); then | |
| wait -n -p finished_pid | |
| exit_code=$? | |
| errfile="${pid_to_errlog[$finished_pid]}" | |
| current_file="${pid_to_file[$finished_pid]}" | |
| ((running--)) | |
| if (( exit_code != 0 )) || [[ -s "$errfile" ]]; then | |
| failed_file="$current_file" | |
| echo -e "${RED}Failed: $failed_file${RESET}" | |
| failed_files+=("$failed_file") | |
| ((failed++)) | |
| if (( showErr )); then | |
| { | |
| echo "----" | |
| echo "File: $failed_file" | |
| echo "Exit code: $exit_code" | |
| cat "$errfile" | |
| } >> "$ERROR_LOG" | |
| fi | |
| else | |
| echo -e "${WHITE}Processing completed for $current_file${RESET}\n" | |
| fi | |
| unset pid_to_file[$finished_pid] | |
| unset pid_to_errlog[$finished_pid] | |
| rm -f "$errfile" | |
| fi | |
| done < <( | |
| find "$basedir" -type f \( -iname '*.mp4' -o -iname '*.mkv' -o -iname '*.mov' -o -iname '*.avi' \) | |
| ) | |
| while (( running > 0 )); do | |
| wait -n -p finished_pid | |
| exit_code=$? | |
| errfile="${pid_to_errlog[$finished_pid]}" | |
| current_file="${pid_to_file[$finished_pid]}" | |
| ((running--)) | |
| if (( exit_code != 0 )) || [[ -s "$errfile" ]]; then | |
| failed_file="${pid_to_file[$finished_pid]}" | |
| echo -e "${RED}Failed: $failed_file${RESET}" | |
| failed_files+=("$failed_file") | |
| ((failed++)) | |
| if (( showErr )); then | |
| { | |
| echo "----" | |
| echo "File: $failed_file" | |
| echo "Exit code: $exit_code" | |
| cat "$errfile" | |
| } >> "$ERROR_LOG" | |
| fi | |
| else | |
| echo -e "${GREEN}File $current_file completed without errors.${RESET}\n" | |
| fi | |
| unset pid_to_file[$finished_pid] | |
| unset pid_to_errlog[$finished_pid] | |
| rm -f "$errfile" | |
| done | |
| echo -e "${WHITE}$total files processed.${RESET}" | |
| if (( failed > 0 )); then | |
| echo -e "${RED}$failed jobs failed.${RESET}" | |
| else | |
| echo -e "${GREEN}All conversions complete.${RESET}" | |
| fi | |
| # Report failed files explicitly | |
| if (( failed > 0 )); then | |
| echo | |
| echo "Failed files:" | |
| for f in "${failed_files[@]}"; do | |
| echo " - $f" | |
| done | |
| fi | |
| if (( showErr )) && [[ -s "$ERROR_LOG" ]]; then | |
| cat "$ERROR_LOG" | |
| fi | |
| rm -f "$ERROR_LOG" | |
| trap - INT | |
| #wait 2>/dev/null | |
| read -t 0 </dev/tty | |
| set -m | |
| } | |
| check_ps4_compatibility() { | |
| local file="$1" | |
| local good=1 | |
| # Run ffprobe | |
| mapfile -t info < <(ffprobe -hide_banner -v error -show_streams -show_format "$file") | |
| local vcodec acodec pixfmt level profile num_streams data_streams | |
| for line in "${info[@]}"; do | |
| case "$line" in | |
| codec_name=* ) | |
| [[ "$line" == codec_name=h264 && -z $vcodec ]] && vcodec=h264 | |
| [[ "$line" == codec_name=aac && -z $acodec ]] && acodec=aac | |
| ;; | |
| pix_fmt=* ) pixfmt="${line#pix_fmt=}";; | |
| profile=* ) profile="${line#profile=}";; | |
| level=* ) level="${line#level=}";; | |
| codec_type=data ) data_streams=1;; | |
| esac | |
| done | |
| # Check | |
| (( vcodec != h264 )) && echo "$file: [FAIL] video codec = $vcodec" && good=0 | |
| [[ "$acodec" != "aac" ]] && echo "$file: [FAIL] audio codec = $acodec" && good=0 | |
| [[ "$pixfmt" != "yuv420p" ]] && echo "$file: [FAIL] pixel format = $pixfmt" && good=0 | |
| [[ "$profile" != "High" ]] && echo "$file: [FAIL] profile = $profile" && good=0 | |
| (( level > 40 )) && echo "$file: [FAIL] level = $level" && good=0 | |
| [[ -n "$data_streams" ]] && echo "$file: [FAIL] contains data stream" && good=0 | |
| (( good )) && echo "$file: [PASS] OK" | |
| return $((1-good)) | |
| } | |
| check_ps4_batch() { | |
| local dir base ext clean_base new_name new_path | |
| local basedir="$1" | |
| declare -a failed=() | |
| [[ ! -d "$basedir" ]] && { | |
| echo -e "\033[1;91mError: Not a directory: '$basedir'\033[0m" | |
| return 1 | |
| } | |
| echo -e "\033[1;97mChecking compatibility in '$basedir'...\033[0m" | |
| while IFS= read -r file; do | |
| echo -n "Checking: $file... " | |
| if check_ps4_compatibility "$file"; then | |
| echo -e "\033[1;92mOK\033[0m" | |
| else | |
| echo -e "\033[1;91mFAIL\033[0m" | |
| dir=$(dirname "$file") | |
| base=$(basename "$file") | |
| ext="${base##*.}" | |
| base="${base%.*}" | |
| # Strip -PS4 suffix if present | |
| if [[ "${base,,}" == *-ps4 ]]; then | |
| clean_base="${base::-4}" # Removes last 4 characters | |
| new_name="${clean_base}.${ext}" | |
| new_path="${dir}/${new_name}" | |
| if [[ "$file" != "$new_path" ]]; then | |
| echo -e "\033[1;93mRenaming failed file:\033[0m $file → $new_path" | |
| mv -- "$file" "$new_path" | |
| file="$new_path" | |
| fi | |
| fi | |
| # Store absolute path to avoid relative path issues | |
| failed+=("$(realpath "$file")") | |
| fi | |
| done < <(find "$basedir" -type f \( -iname '*.mp4' -o -iname '*.mkv' -o -iname '*.mov' \)) | |
| echo | |
| echo -e "\033[1;97m${#failed[@]} files failed compatibility check.\033[0m" | |
| [[ ${#failed[@]} -eq 0 ]] && return 0 | |
| read -p "Convert failed files now? (y/n) " ans | |
| [[ "$ans" != [Yy] ]] && return 1 | |
| for file in "${failed[@]}"; do | |
| echo -e "\n\033[1;97mConverting: $file\033[0m" | |
| dir=$(dirname "$file") | |
| ( | |
| cd "$dir" || return 1 | |
| makePS4 "$file" | |
| ) | |
| done | |
| } | |
| # Function to convert files using background processes | |
| ff_cvt() { | |
| local file_type="$1" | |
| local output_format="$2" | |
| local files=(*."$file_type") | |
| local failed_files=() | |
| # Function to check if FFMPEG is installed | |
| check_ffmpeg() { | |
| if ! command -v ffmpeg &> /dev/null; then | |
| echo "FFMPEG is not installed. Attempting to install..." | |
| install_ffmpeg | |
| else | |
| echo "FFMPEG is already installed." | |
| fi | |
| } | |
| # Function to install FFMPEG using popular package managers | |
| install_ffmpeg() { | |
| local managers=("apt-get" "yum" "dnf" "brew" "pacman") | |
| for manager in "${managers[@]}"; do | |
| if command -v "$manager" &> /dev/null; then | |
| sudo "$manager" install ffmpeg -y | |
| return | |
| fi | |
| done | |
| echo -e "${RED}No supported package manager found. Please install FFMPEG manually." | |
| return 1 | |
| } | |
| check_ffmpeg | |
| for file in "${files[@]}"; do | |
| { | |
| if ffmpeg -i "$file" "${file%.*}.$output_format"; then | |
| echo "${GREEN}Converted: $file" | |
| else | |
| echo "${RED}Failed to convert: $file" | |
| failed_files+=("$file") | |
| fi | |
| } & | |
| done | |
| wait | |
| if [ ${#failed_files[@]} -ne 0 ]; then | |
| echo "Some files failed to convert. Would you like to try again? (y/n)" | |
| read -r retry | |
| if [[ $retry == "y" ]]; then | |
| for file in "${failed_files[@]}"; do | |
| ffmpeg -i "$file" "${file%.*}.$output_format" || echo "Still failed: $file" | |
| done | |
| fi | |
| fi | |
| echo "Do you want to delete the original files? (y/n)" | |
| read -r delete | |
| if [[ $delete == "y" ]]; then | |
| rm -f *."$file_type" | |
| echo "Original files deleted." | |
| else | |
| echo "Original files kept." | |
| fi | |
| } | |
| ff_join() { | |
| if [ "$#" -ne 3 ]; then | |
| echo "Usage: $0 <input_file1> <input_file2> <output_file>" | |
| return 1 | |
| fi | |
| # Create a temporary file list | |
| base_name=$(basename "$3" .${3##*.}) # Extract base name without extension | |
| temp_file="${base_name}.tmp" # Create temp file with .tmp extension | |
| echo "file '$1'" > "$temp_file" | |
| echo "file '$2'" >> "$temp_file" | |
| ffmpeg -f concat -safe 0 -i "$temp_file" -c copy "$3" | |
| rm "$temp_file" | |
| } | |
| # Function to list all functions and their contents | |
| lsfn() { | |
| # Check if there are any functions defined | |
| if declare -F | grep -q .; then | |
| echo "Listing all functions and their contents:" | |
| # Loop through each function and print its name and content | |
| for func in $(declare -F | awk '{print $3}'); do | |
| echo "Function: $func" | |
| # Print the function content | |
| declare -f "$func" | |
| echo "-----------------------------------" | |
| done | |
| else | |
| echo "No functions defined in the current session." | |
| fi | |
| } | |
| # grep the shell history for something | |
| hgrep() { | |
| grep -i --color=auto "$1" ~/.bash_history | tail -n 20 | |
| } | |
| imgconvert() { | |
| # Usage: imgconvert <source_ext> <target_ext> | |
| # Example: imgconvert jpg png | |
| if [[ -z "$1" || -z "$2" ]]; then | |
| echo "Usage: imgconvert <source_ext> <target_ext>" | |
| echo "Example: imgconvert jpg png" | |
| return 1 | |
| fi | |
| local src_ext="${1#.}" | |
| local tgt_ext="${2#.}" | |
| if [[ "${src_ext,,}" == "${tgt_ext,,}" ]]; then | |
| echo "Error: Source and target extensions cannot be the same (.${src_ext})." | |
| return 1 | |
| fi | |
| # Verify or install ImageMagick | |
| if ! command -v convert &>/dev/null; then | |
| echo "ImageMagick not found. Attempting to install..." | |
| if command -v apt-get &>/dev/null; then | |
| sudo apt-get update && sudo apt-get install -y imagemagick | |
| elif command -v dnf &>/dev/null; then | |
| sudo dnf install -y imagemagick | |
| elif command -v yum &>/dev/null; then | |
| sudo yum install -y imagemagick | |
| elif command -v pacman &>/dev/null; then | |
| sudo pacman -S --noconfirm imagemagick | |
| elif command -v zypper &>/dev/null; then | |
| sudo zypper install -y imagemagick | |
| elif command -v brew &>/dev/null; then | |
| brew install imagemagick | |
| else | |
| echo "Error: No recognized package manager found. Please install ImageMagick manually." | |
| return 1 | |
| fi | |
| if ! command -v convert &>/dev/null; then | |
| echo "Error: ImageMagick installation failed." | |
| return 1 | |
| fi | |
| fi | |
| # Build array of matching files in current directory only | |
| local files=() | |
| while IFS= read -r -d '' f; do | |
| files+=("$f") | |
| done < <(find . -maxdepth 1 -type f -iname "*.${src_ext}" -print0) | |
| if [[ ${#files[@]} -eq 0 ]]; then | |
| echo "No .${src_ext} files found in $(pwd)." | |
| return 1 | |
| fi | |
| echo "Found ${#files[@]} .${src_ext} file(s). Converting to .${tgt_ext}..." | |
| local success=0 | |
| local fail=0 | |
| for f in "${files[@]}"; do | |
| local base="${f%.*}" | |
| echo "convert \"$f\" \"${base}.${tgt_ext}\"" | |
| convert "$f" "${base}.${tgt_ext}" \ | |
| && (( success++ )) \ | |
| || (( fail++ )) | |
| done | |
| echo "Done. ${success} converted, ${fail} failed." | |
| } | |
| [[ -s $HOME/.pythonz/etc/bashrc ]] && source $HOME/.pythonz/etc/bashrc | |
| #history_autosave & | |
| export PROMPT_COMMAND='history -a; history -n' | |
| echo -e "\n${YELLOW}rmv_empty_dirs${WHITE} removes empty folders below the starting point.\n Use ${GREEN}rmv_empty_dirs --help ${WHITE}or ${GREEN}rmv_empty_dirs -h${WHITE} for usage." | |
| echo -e "\n${YELLOW}imgconvert${WHITE} uses ImageMagick to convert all images in the current folder between image formats.\n ${GREEN}imgconvert jpg png" | |
| echo -e "\n${YELLOW}lsfn${WHITE} lists all available functions." | |
| echo -e "\n${WHITE}Function 'pyvenv' starts and enters a Python VENV, creating it if it doesn't exist. Also has a list feature, showing existing VENVs, and a Python version check." | |
| echo -e "${GREEN} pyvenv venv version ${WHITE}|| ${GREEN}pyvenv --list ${WHITE}|| ${GREEN}pyvenv --check-ver\n" | |
| echo -e "${WHITE}Function 'makePS4' converts a single file (ie, movie001.mkv) to be playable on the PS4:" | |
| echo -e "${GREEN} makePS4 \"FILE\"\n" | |
| echo -e "${WHITE}Alternatively, 'batchPS4' will recurse an entire directory tree and convert everything it finds. Use '--MAX=##' to limit thread usage (capped at 1.5x CPU count)." | |
| echo -e "${GREEN} batchPS4 \"FOLDER\"\n" | |
| alias fix-opera='sudo ~root/.scripts/fix-opera.sh' # Opera fix HTML5 media | |
| alias diffuse='/usr/bin/flatpak run --branch=stable --arch=x86_64 --command=diffuse --file-forwarding io.github.mightycreak.Diffuse @@ $1 $2 @@' | |
| # >>> conda initialize >>> | |
| # !! Contents within this block are managed by 'conda init' !! | |
| __conda_setup="$('/home/seann/miniconda3/bin/conda' 'shell.bash' 'hook' 2> /dev/null)" | |
| if [ $? -eq 0 ]; then | |
| eval "$__conda_setup" | |
| else | |
| if [ -f "/home/seann/miniconda3/etc/profile.d/conda.sh" ]; then | |
| . "/home/seann/miniconda3/etc/profile.d/conda.sh" | |
| else | |
| export PATH="/home/seann/miniconda3/bin:$PATH" | |
| fi | |
| fi | |
| unset __conda_setup | |
| # <<< conda initialize <<< |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment