dotfiles

My personal shell configs and stuff
git clone git://git.alex.balgavy.eu/dotfiles.git
Log | Files | Refs | Submodules | README | LICENSE

commit 28976eb024752700db95175760c68d87857937f0
parent 5e160dcb897b7971e45120f343def68c50144b8b
Author: Alex Balgavy <alex@balgavy.eu>
Date:   Wed, 22 Sep 2021 12:54:12 +0200

Start cleaning up scripts

Getting rid of useless ones, and adding descriptions to each script.

Diffstat:
MREADME.md | 46+++-------------------------------------------
Mscripts/base | 8+++++++-
Mscripts/battery | 3+++
Mscripts/bcdl | 3+++
Mscripts/cheat | 5+++--
Mscripts/checklinks | 10++++++----
Mscripts/chmu | 7+++++--
Mscripts/clc | 4+---
Dscripts/clonedisk | 55-------------------------------------------------------
Dscripts/clonedisk-ignore | 15---------------
Mscripts/clp | 4+---
Mscripts/create_pocketbook_logo | 1+
Mscripts/curlhub | 3+++
Dscripts/decrypt-dir | 14--------------
Mscripts/discogs | 1+
Mscripts/discord-new-music | 4++++
Mscripts/dmenuman | 1+
Mscripts/dwmblock-battery | 1+
Mscripts/dwmblock-brightness | 1+
Mscripts/dwmblock-cpu | 1+
Mscripts/dwmblock-internet | 2+-
Dscripts/encrypt-dir | 21---------------------
Dscripts/epr.py | 433-------------------------------------------------------------------------------
Dscripts/executable | 42------------------------------------------
Dscripts/feh-background | 13-------------
Mscripts/ffmpres | 2++
Mscripts/fix-virtualenv-after-py-upgrade | 2+-
Mscripts/get-frontwin-title | 3++-
Mscripts/ghp | 3+++
Dscripts/git-review | 41-----------------------------------------
Mscripts/global-git-status | 13+++++++------
Dscripts/gnome-background | 55-------------------------------------------------------
Dscripts/gnome-settings | 2--
Mscripts/google_hangouts_parser.rb | 1+
Mscripts/httpc | 1+
Dscripts/img-to-background | 20--------------------
Mscripts/imgpreview | 1+
Mscripts/import-to-music-library | 3+++
Mscripts/it-style | 3+++
Dscripts/joplin-html-fix | 17-----------------
Dscripts/joplin-interface | 56--------------------------------------------------------
Dscripts/linkdir | 41-----------------------------------------
Mscripts/mac-battery-warning | 11++++++++++-
Mscripts/mailsync | 1+
Mscripts/markdown2epub | 1+
Dscripts/mdvl | 651-------------------------------------------------------------------------------
Dscripts/medium_reader.rb | 39---------------------------------------
Mscripts/modified_cfscrape.py | 2++
Dscripts/mount_phone | 31-------------------------------
Mscripts/mp3-find-broken | 1+
Mscripts/mp3-tags-to-folders | 2++
Mscripts/mpcq | 1+
Mscripts/mpvq | 1+
Mscripts/newsboat-dl | 7++++---
Mscripts/newsboat-export-opml-tagged | 2+-
Mscripts/newsboatify-youtube-subscriptions | 1+
Mscripts/newsrefreshd | 1+
Mscripts/notify | 1+
58 files changed, 97 insertions(+), 1618 deletions(-)

diff --git a/README.md b/README.md @@ -15,51 +15,11 @@ You choose which configs you want to install, and it'll only install those (and Get more info by running `scripts/conf -h`, and investigate the dotfile mappings in `./dot.map`. ## Scripts in `dotfiles/scripts`: -Before you use a custom script, read what it does. Some may be a bit buggy, I haven't tested them on all systems. +Before you use a custom script, read what it does. +Scripts are described in a short comment at the top of the file. +Some may be a bit buggy, I haven't tested them on all systems. If you read a script and see some improvements that could be made, let me know. I'm always down to learn more stuff. -* `cheat`: query http://cheat.sh and output the result into `less` -* `checklinks`: recursively check any website for broken links -* `clonedisk`: clones one disk to another -* `colgen.rb`: a Ruby script to generate Vim colorschemes from a much simpler syntax file. -* `conf`: the script to manage dotfiles. run `conf -h` to show available commands. -* `create_pocketbook_logo`: take an image and convert it into a logo for my PocketBook 603. Run with the `-h` flag for usage. -* `dashedit.rb`: script to allow Dash submissions from the commandline. Largely useless to anyone except me. -* `epr.py`: a [Python-based EPUB reader](https://github.com/wustho/epr) -* `epub-convert`: uses Calibre's ebook converter program to convert anything to an epub -* `executable`: my first script. Makes any file executable, along with further options. Usage: `executable file.sh` -* `ffmpres`: ffmpeg presets for various files -* `ffmpeg-split.py`: split videos using ffmpeg. [Here's the README.](scripts/ffmpeg-split-README.md) -* `global-git-status`: git status of all of your cloned git repositories -* `google_hangouts_parser.rb`: parse the JSON from google hangouts into a more coherent format. Go to a directory with a "Hangouts.json" file and run the script. -* `httpc`: an interactive http request console using httpie -* `it-style`: can find and set the genre in a track in iTunes (along with its Ruby helper), using the Discogs database (you need an API key, set $DISCOGS_API_TOKEN in your profile). Usage: click/play a track in iTunes, and run the program. -* `linkdir`: symlink all files/folders from source dir to target dir. Usage: `linkdir $source` to create symlinks in cwd, `linkdir $source $target` otherwise. -* `mdvl`: markdown renderer in Python -* `medium_reader.rb`: create a simplified Medium article and open it in a browser. Pass the URL as argument, or run interactively. -* `modified_cfscrape.py`: can scrape and download CloudFlare-protected websites. Usage: `python3 modified_cfscrape.py http://url.com` -* `mp3tagger.jar`: a Java application to tag MP3 files -* `pass-import-txt`: imports passwords into `pass` from a text file that's formatted as `username:password` (newline-separated) -* `percerr.rb`: a very simple thing for calculating percent error. Just run the Ruby script. -* `play`: a command-line music player. Shows album art, or a visualiser if there is no album art. Set a $MUSIC_DIR in your profile, then run `play`. -* `pwnedpass`: test a password against the pwned passwords database, uses k-anonymity -* `rplayer`: a command-line subreddit player. Enter any subreddit that has music, and it'll play all of the links using `mpv` (except for Spotify). -* `safari_history`: print safari history with timestamps, by default it prints 10 items, change this by passing a number as the argument -* `smack.pl`: change macos spaces by smacking the side of your screen (if you have a laptop). Yeah, like physically. -* `ss-interceptor`: temporarily change where macOS stores your screenshots. Run with the new directory as the first argument. -* `strip-html-tags`: remove HTML tags from a file -* `tag`: a command-line `mp3tagger.jar`. Usage: `tag filename.mp3` -* `topdf`: converts a file to a PDF using cupsfilter. Works well on docx files. Usage: `topdf file [file2 file3 file4...]` -* `tput-colors`: a simple script to print out a `tput` color table -* `track`: time tracking script. Run `track` to start, `track stop` to stop and show time elapsed in seconds. -* `updatemaster`: the ultimate all-in-one update script (Brew, Cask, MAS, pip, etc.). Run with `-h` to see options. -* `usbmux`, `tcprelay`: port forwarding. If you want to SSH to a jailbroken iPhone over USB, you can run `tcprelay 22:2222` to forward local port 2222 to the iPhone's SSH port (22), and then `ssh -p 2222 root@localhost`. -* `todos`: print out the todos in the current directory or a specific file. Prints it out in a vim-parseable format. -* `vimwiki_md`: convert vimwiki files to markdown, using the vimwiki_md_ex expressions file for sed. Still a work-in-progress and probably very buggy. -* `vwtags.py`: generate ctags tag files for vimwiki documents. -* `wattpad-scrape`: downloads a Wattpad book as an EPUB file. Usage: `wattpad-scrape $wattpad_url` -* `weather`: show the weather for a city, pass the city as an argument. uses http://wttr.in. - ## Binaries in `dotfiles/bin`: These are third-party binaries that I didn't write. I don't take credit for any of them. I only have them in the folder for convenience. diff --git a/scripts/base b/scripts/base @@ -1,4 +1,6 @@ #!/bin/sh +# Simple base converter +# base [-x|-d|-o|-b] [0x|0d|0o|0b|]NUM command -v bc >/dev/null 2>&1 || { printf "bc not installed.\n" && exit 1; } usage() { @@ -12,18 +14,22 @@ while [ $(($#)) -ne 0 ]; do case "$1" in -x|--hex) obase=16 + baseprefix='0x' shift ;; -d|--decimal) obase=10 + baseprefix='' shift ;; -o|--octal) obase=8 + baseprefix='0o' shift ;; -b|--binary) obase=2 + baseprefix='0b' shift ;; -h|--help) @@ -55,7 +61,7 @@ case "$1" in esac if [ -n "$obase" ]; then - printf "obase=%s; ibase=%s; %s\n" "$obase" "$ibase" "$val" | bc -w | tr -d '\n' + printf "%s%s" "$baseprefix" "$(printf "obase=%s; ibase=%s; %s\n" "$obase" "$ibase" "$val" | bc -w | tr -d '\n')" else printf "Dec: %s\n" "$(printf "obase=10; ibase=%s; %s\n" "$ibase" "$val" | bc -w)" \ && printf "Hex: %s\n" "$(printf "obase=16; ibase=%s; %s\n" "$ibase" "$val" | bc -w)" \ diff --git a/scripts/battery b/scripts/battery @@ -1,4 +1,7 @@ #!/bin/sh +# Cross-platform battery information script +# With no arguments, prints battery percentage and power source, separated by newline + os=$(uname -s | tr '[:upper:]' '[:lower:]') case "$os" in linux*) diff --git a/scripts/bcdl b/scripts/bcdl @@ -1,4 +1,7 @@ #!/bin/sh +# Wrapper for youtube-dl, configured specifically for Bandcamp +# $1 is the URL + DOWNLOAD_DIR="$HOME"/Downloads/songs/listen\ to cd "$DOWNLOAD_DIR" || { printf "Couldn't cd into download directory %s\n" "$DOWNLOAD_DIR"; exit 1; } command -v youtube-dl 1>/dev/null 2>&1 || { printf "Youtube-dl required.\n"; exit 1; } diff --git a/scripts/cheat b/scripts/cheat @@ -1,5 +1,6 @@ -#!/usr/bin/env bash -if ! command -v curl &>/dev/null; then +#!/bin/sh +# Wrapper script for `curl cheat.sh`, also sets colors depending on system theme +if ! command -v curl 1>/dev/null 2>&1; then echo "curl not installed." >&2 exit 1 fi diff --git a/scripts/checklinks b/scripts/checklinks @@ -1,6 +1,7 @@ -#!/usr/bin/env bash +#!/bin/sh +# Script that crawls a URL and checks broken links set -x -if ! command -v wget &> /dev/null; then +if ! command -v wget 1>/dev/null 2>&1; then echo "Please install wget." exit 1 elif [ $# -ne 2 ]; then @@ -9,9 +10,10 @@ elif [ $# -ne 2 ]; then exit 1 fi -flags=(--spider --recursive --no-directories --no-verbose --page-requisites --level inf --wait 2 -e robots=off) +# params: LOGFILE, URL +wget_with_flags() { wget --spider --recursive --no-directories --no-verbose --page-requisites --level inf --wait 2 -e robots=off -o "$2" "$1"; } # flags=(--spider --recursive --no-directories --no-verbose --page-requisites --level inf -e robots=off) -wget "${flags[@]}" -o "$2" "$1" +wget_with_flags "$2" "$1" grep -B 1 'broken link!' "$2" | less diff --git a/scripts/chmu b/scripts/chmu @@ -1,2 +1,5 @@ #!/bin/sh -curl -sL 'https://www.chmi.cz/files/portal/docs/meteo/om/predpovedi/CR/p_CR.html' | pup 'pre text{}' | $PAGER- \ No newline at end of file +# Get the latest forecast from CHMU +curl -sL 'https://www.chmi.cz/files/portal/docs/meteo/om/predpovedi/CR/p_CR.html' \ + | pup 'pre text{}' \ + | $PAGER+ \ No newline at end of file diff --git a/scripts/clc b/scripts/clc @@ -1,7 +1,5 @@ #!/bin/sh -# clc - Copy data to clipboard -# -# Usage: +# clc - Cross-platform (cl)ipboard (c)opy # # <command> | clc - copies stdin to clipboard # diff --git a/scripts/clonedisk b/scripts/clonedisk @@ -1,55 +0,0 @@ -#!/usr/bin/env bash - -# Disc backup/clone script -# Requires rsync 3 - -# Ask for the administrator password upfront -sudo -v - -if [ $# -ne 2 ]; then - echo "Usage: clonedisk [source] [destination]" - exit 1 -else - SRC=$1 - DST=$2 -fi - -EXCLUDE="clonedisk-ignore" - -PROG=$0 - -if [ ! -r "$SRC" ]; then - logger -t $PROG "Source $SRC not readable - Cannot start the sync process" - exit; -fi - -if [ ! -w "$DST" ]; then - logger -t $PROG "Destination $DST not writeable - Cannot start the sync process" - exit; -fi - -logger -t $PROG "Start rsync" - -sudo /usr/local/bin/rsync --acls \ - --archive \ - --delete \ - --delete-excluded \ - --exclude-from=$EXCLUDE \ - --hard-links \ - --one-file-system \ - --sparse \ - --verbose \ - --xattrs \ - "$SRC" "$DST" - -logger -t $PROG "End rsync" - -echo -n "Make the backup disk bootable? [Y/n]: " -read -nq shouldboot - -if [ $shouldboot == "y" ]; then - # Make the backup bootable - sudo bless -folder "$DST"/System/Library/CoreServices -fi - -exit 0 diff --git a/scripts/clonedisk-ignore b/scripts/clonedisk-ignore @@ -1,15 +0,0 @@ -.Spotlight-*/ -.Trashes -/afs/* -/automount/* -/cores/* -/dev/* -/Network/* -/private/tmp/* -/private/var/run/* -/private/var/spool/postfix/* -/private/var/vm/* -/Previous Systems.localized -/tmp/* -/Volumes/* -*/.Trash diff --git a/scripts/clp b/scripts/clp @@ -1,7 +1,5 @@ #!/bin/sh -# clp - "Paste" data from clipboard to stdout -# -# Usage: +# clp - Cross-platform (cl)ipboard (p)aste # # clp - writes clipboard's contents to stdout # diff --git a/scripts/create_pocketbook_logo b/scripts/create_pocketbook_logo @@ -1,4 +1,5 @@ #!/usr/bin/env bash +# Converts an image to a Pocketbook-compatible version, for use e.g. as a screen saver image if ! command -v convert &> /dev/null; then echo "imagemagick not installed." >&2 exit 1 diff --git a/scripts/curlhub b/scripts/curlhub @@ -1,4 +1,7 @@ #!/bin/sh +# Curl wrapper to simplify downloading a raw file from Github. +# e.g. to download https://github.com/koalaman/shellcheck/blob/master/src/ShellCheck/AST.hs +# do `curlhub koalaman/shellcheck src/ShellCheck/AST.h` command -v curl 1>/dev/null 2>&1 || { printf "curl not installed\n" && exit 1; } [ $# -lt 2 ] && { printf "Usage:\tcurlhub username/repository filename [branch]\n" && exit 1; } lnk="https://raw.githubusercontent.com/$1/${3:-master}/$2" diff --git a/scripts/decrypt-dir b/scripts/decrypt-dir @@ -1,14 +0,0 @@ -#!/usr/bin/env bash -if ! command -v gpg &>/dev/null; then - echo "gpg not installed." >&2 - exit 1 -fi - -if [ ! -z "$2" ]; then - mkdir -p "$2" - gpg --decrypt "$1" | tar xz -C "$2" -echo "Decrypted $1 into $2" -else - gpg --decrypt "$1" | tar xz -echo "Decrypted $1 into ./" -fi diff --git a/scripts/discogs b/scripts/discogs @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# Client to query Discogs require "discogs-wrapper" # Needed for API access my_user_token = ENV['DISCOGS_API_TOKEN'] diff --git a/scripts/discord-new-music b/scripts/discord-new-music @@ -1,4 +1,8 @@ #!/usr/bin/env ruby +# Script that uses selenium-webdriver to go through unread messages in a +# server's channels, extract any links, and download them. Then marks those +# messages as read. +# Useful for downloading new music posted to channels. require 'selenium-webdriver' require 'json' diff --git a/scripts/dmenuman b/scripts/dmenuman @@ -1,4 +1,5 @@ #!/bin/sh +# Use dmenu to open manpages. term="${TERMINAL:-st}" manual="$(man -k . | dmenu -p "Manual:" -l 10 | awk '{print $1}')" diff --git a/scripts/dwmblock-battery b/scripts/dwmblock-battery @@ -1,4 +1,5 @@ #!/bin/sh +# dwmblocks script for battery information case "$BUTTON" in 6) "$TERMINAL" -e "$EDITOR" "$0" ;; esac diff --git a/scripts/dwmblock-brightness b/scripts/dwmblock-brightness @@ -1,4 +1,5 @@ #!/bin/sh +# dwmblocks script for brightness information case "$BUTTON" in 1) pgrep -f backlight-brightness || setsid -f "$TERMINAL" -e backlight-brightness diff --git a/scripts/dwmblock-cpu b/scripts/dwmblock-cpu @@ -1,4 +1,5 @@ #!/bin/sh +# dwmblocks script for CPU information # Cache in tmpfs to improve speed and reduce SSD load cache=/tmp/cpubarscache diff --git a/scripts/dwmblock-internet b/scripts/dwmblock-internet @@ -1,5 +1,5 @@ #!/bin/sh - +# dwmblocks script for internet information # Show wifi 📶 and percent strength or 📡 if none. # Show 🌐 if connected to ethernet or ❎ if none. # Show 🔒 if a vpn connection is active diff --git a/scripts/encrypt-dir b/scripts/encrypt-dir @@ -1,21 +0,0 @@ -#!/usr/bin/env bash -if ! command -v gpg &>/dev/null; then - echo "gpg not installed." >&2 - exit 1 -fi - -export GPG_TTY=$(tty) - -if [ -f "$1" ]; then -elif [ -d "$1" ]; then -else - echo "File doesn't exist or not supported." -fi -if [ ! -z "$2" ]; then - mkdir -p "$2" - (cd "${1%/*}" && tar cz "${1##*/}") | gpg -c --cipher-algo aes256 -o "${2%/}"/"${1##*/}.tgz.gpg" - echo "Encrypted $1 into $2" -else - (cd "${1%/*}" && tar cz "${1##*/}") | gpg -c --cipher-algo aes256 -o "${1##*/}.tgz.gpg" - echo "Encrypted $1 into ." -fi diff --git a/scripts/epr.py b/scripts/epr.py @@ -1,432 +0,0 @@ -#!/usr/bin/env python3 -""" -Usage: - epr.py [EPUBFILE] - -Key binding: - Help : ? - Quit : q - Scroll down : ARROW DOWN j - Scroll up : ARROW UP k - Page down : PGDN J SPC - Page up : PGUP K - Next chapter : ARROW RIGHT l - Prev chapter : ARROW LEFT h - Beginning of ch : HOME g - End of ch : END G - Shrink : - - Enlarge : = - TOC : t - Metadata : m - -Source: - https://github.com/wustho/epr.git - -""" - -import curses -import zipfile -import locale -import sys -import re -import os -import textwrap -import json -import xml.etree.ElementTree as ET -from urllib.parse import unquote -from html.entities import html5 - -locale.setlocale(locale.LC_ALL, "") -# code = locale.getpreferredencoding() - -statefile = os.path.join(os.getenv("HOME"), ".config/.epr") -if os.path.exists(statefile): - with open(statefile, "r") as f: - state = json.load(f) -else: - state = {} - -# key bindings -SCROLL_DOWN = {curses.KEY_DOWN, ord("j")} -SCROLL_UP = {curses.KEY_UP, ord("k")} -PAGE_DOWN = {curses.KEY_NPAGE, ord("J"), ord(" ")} -PAGE_UP = {curses.KEY_PPAGE, ord("K")} -CH_NEXT = {curses.KEY_RIGHT, ord("l")} -CH_PREV = {curses.KEY_LEFT, ord("h")} -CH_HOME = {curses.KEY_HOME, ord("g")} -CH_END = {curses.KEY_END, ord("G")} -SHRINK = ord("-") -WIDEN = ord("=") -META = ord("m") -TOC = ord("t") -FOLLOW = 10 -QUIT = {ord("q"), 3} -HELP = {ord("?")} - -NS = {"DAISY" : "http://www.daisy.org/z3986/2005/ncx/", - "OPF" : "http://www.idpf.org/2007/opf", - "CONT" : "urn:oasis:names:tc:opendocument:xmlns:container", - "XHTML" : "http://www.w3.org/1999/xhtml", - "EPUB" : "http://www.idpf.org/2007/ops"} - -RIGHTPADDING = 2 -LINEPRSRV = 0 # default = 2 - -class Epub: - def __init__(self, fileepub): - self.path = os.path.abspath(fileepub) - self.file = zipfile.ZipFile(fileepub, "r") - cont = ET.parse(self.file.open("META-INF/container.xml")) - self.rootfile = cont.find("CONT:rootfiles/CONT:rootfile", NS).attrib["full-path"] - self.rootdir = os.path.dirname(self.rootfile) + "/" if os.path.dirname(self.rootfile) != "" else "" - cont = ET.parse(self.file.open(self.rootfile)) - # EPUB3 - self.version = cont.getroot().get("version") - if self.version == "2.0": - self.toc = self.rootdir + cont.find("OPF:manifest/*[@id='ncx']", NS).get("href") - elif self.version == "3.0": - self.toc = self.rootdir + cont.find("OPF:manifest/*[@properties='nav']", NS).get("href") - - def get_meta(self): - meta = [] - # why self.file.read(self.rootfile) problematic - cont = ET.fromstring(self.file.open(self.rootfile).read()) - for i in cont.findall("OPF:metadata/*", NS): - if i.text != None: - meta.append([re.sub("{.*?}", "", i.tag), i.text]) - return meta - - def get_contents(self): - contents = [] - cont = ET.parse(self.file.open(self.rootfile)).getroot() - manifest = [] - for i in cont.findall("OPF:manifest/*", NS): - # EPUB3 - if i.get("id") != "ncx" and i.get("properties") != "nav": - manifest.append([ - i.get("id"), - i.get("href") - ]) - else: - toc = self.rootdir + unquote(i.get("href")) - - spine = [] - for i in cont.findall("OPF:spine/*", NS): - spine.append(i.get("idref")) - for i in spine: - for j in manifest: - if i == j[0]: - contents.append(unquote(j[1])) - manifest.remove(j) - # TODO: test is break necessary - break - - namedcontents = [] - toc = ET.parse(self.file.open(toc)).getroot() - # EPUB3 - if self.version == "2.0": - navPoints = toc.findall("DAISY:navMap//DAISY:navPoint", NS) - elif self.version == "3.0": - navPoints = toc.findall("XHTML:body/XHTML:nav[@EPUB:type='toc']//XHTML:a", NS) - for i in contents: - name = "unknown" - for j in navPoints: - # EPUB3 - if self.version == "2.0": - if i == unquote(j.find("DAISY:content", NS).get("src")): - name = j.find("DAISY:navLabel/DAISY:text", NS).text - break - elif self.version == "3.0": - if i == unquote(j.get("href")): - name = "".join(list(j.itertext())) - break - - namedcontents.append([ - name, - self.rootdir + i - ]) - - return namedcontents - -def toc(stdscr, ebook, index, width): - rows, cols = stdscr.getmaxyx() - hi, wi = rows - 4, cols - 4 - Y, X = 2, 2 - toc = curses.newwin(hi, wi, Y, X) - toc.box() - toc.keypad(True) - toc.addstr(1,2, "Table of Contents") - toc.addstr(2,2, "-----------------") - key_toc = 0 - - def pad(src, id, top=0): - pad = curses.newpad(len(src), wi - 2 ) - pad.keypad(True) - pad.clear() - for i in range(len(src)): - if i == id: - pad.addstr(i, 0, "> " + src[i][0], curses.A_REVERSE) - else: - pad.addstr(i, 0, " " + src[i][0]) - # scrolling up - if top == id and top > 0: - top = top - 1 - # steady - elif id - top <= rows - Y -9: - top = top - # scrolling down - else: - top = id - rows + Y + 9 - - pad.refresh(top,0, Y+4,X+4, rows - 5, cols - 6) - return top - - src = ebook.get_contents() - toc.refresh() - top = pad(src, index) - - while key_toc != TOC and key_toc not in QUIT: - if key_toc in SCROLL_UP and index > 0: - index -= 1 - top = pad(src, index, top) - if key_toc in SCROLL_DOWN and index + 1 < len(src): - index += 1 - top = pad(src, index, top) - if key_toc == FOLLOW: - reader(stdscr, ebook, index, width, 0) - key_toc = toc.getch() - - toc.clear() - toc.refresh() - return - -def meta(stdscr, ebook): - rows, cols = stdscr.getmaxyx() - hi, wi = rows - 4, cols - 4 - Y, X = 2, 2 - meta = curses.newwin(hi, wi, Y, X) - meta.box() - meta.keypad(True) - meta.addstr(1,2, "Metadata") - meta.addstr(2,2, "--------") - key_meta = 0 - - mdata = [] - src = "" - for i in ebook.get_meta(): - data = re.sub("<[^>]*>", "", i[1]) - data = re.sub("\t", "", data) - mdata += textwrap.fill(i[0] + " : " + data, wi - 6).splitlines() - src_lines = mdata - - pad = curses.newpad(len(src_lines), wi - 2 ) - pad.keypad(True) - for i in range(len(src_lines)): - pad.addstr(i, 0, src_lines[i]) - y = 0 - meta.refresh() - pad.refresh(y,0, Y+4,X+4, rows - 5, cols - 6) - - while key_meta != META and key_meta not in QUIT: - if key_meta in SCROLL_UP and y > 0: - y -= 1 - if key_meta in SCROLL_DOWN and y < len(src_lines) - hi + 4: - y += 1 - pad.refresh(y,0, 6,5, rows - 5, cols - 5) - key_meta = meta.getch() - - meta.clear() - meta.refresh() - return - -def help(stdscr): - rows, cols = stdscr.getmaxyx() - hi, wi = rows - 4, cols - 4 - Y, X = 2, 2 - help = curses.newwin(hi, wi, Y, X) - help.box() - help.keypad(True) - help.addstr(1,2, "Help") - help.addstr(2,2, "----") - key_help = 0 - - src = __doc__ - src_lines = src.split("\n") - - pad = curses.newpad(len(src_lines), wi - 2 ) - pad.keypad(True) - for i in range(len(src_lines)): - pad.addstr(i, 0, src_lines[i]) - y = 0 - help.refresh() - pad.refresh(y,0, Y+4,X+4, rows - 5, cols - 6) - - while key_help not in HELP and key_help not in QUIT: - if key_help == SCROLL_UP and y > 0: - y -= 1 - if key_help == SCROLL_DOWN and y < len(src_lines) - hi + 4: - y += 1 - if key_help == curses.KEY_RESIZE: - break - pad.refresh(y,0, 6,5, rows - 5, cols - 5) - key_help = help.getch() - - help.clear() - help.refresh() - return - -def to_text(src, width): - while True: - try: - root = ET.fromstring(src) - break - except Exception as ent: - ent = str(ent) - ent = re.search("(?<=undefined entity &).*?;(?=:)", ent).group() - src = re.sub("&" + ent, html5[ent], src.decode("utf-8")).encode("utf-8") - - body = root.find("XHTML:body", NS) - text = [] - # for i in body.findall("*", NS): - # for i in body.findall(".//XHTML:p", NS): - for i in body.findall(".//*"): - if re.match("{"+NS["XHTML"]+"}h[0-9]", i.tag) != None: - for j in i.itertext(): - text.append(j.rjust(width//2 + len(j)//2 - RIGHTPADDING)) - text.append("") - elif re.match("{"+NS["XHTML"]+"}p", i.tag) != None: - par = ET.tostring(i, encoding="utf-8").decode("utf-8") - par = re.sub("<[^>]*>", "", par) - par = re.sub("\t", "", par) - par = textwrap.fill(par, width) - text += par.splitlines() + [""] - - return text + [""] - -def reader(stdscr, ebook, index, width, y=0): - k = 0 - rows, cols = stdscr.getmaxyx() - x = (cols - width) // 2 - stdscr.clear() - stdscr.refresh() - - content = ebook.file.open(ebook.get_contents()[index][1]).read() - - src_lines = to_text(content, width) - - pad = curses.newpad(len(src_lines), width + 2) # + 2 unnecessary - pad.keypad(True) - for i in range(len(src_lines)): - pad.addstr(i, 0, src_lines[i]) - pad.addstr(i, width//2 - 10 - RIGHTPADDING, "-- End of Chapter --", curses.A_REVERSE) - pad.refresh(y,0, 0,x, rows-1,x+width) - - while True: - # if k == QUIT or k == 3: - if k in QUIT: - for i in state: - state[i]["lastread"] = str(0) - state[ebook.path]["lastread"] = str(1) - state[ebook.path]["index"] = str(index) - state[ebook.path]["width"] = str(width) - state[ebook.path]["pos"] = str(y) - with open(statefile, "w") as f: - json.dump(state, f, indent=4) - exit() - if k in SCROLL_UP: - if y > 0: - y -= 1 - # if y == 0 and index > 0: - # reader(stdscr, ebook, index-1, width) - if k in PAGE_UP: - if y >= rows - LINEPRSRV: - y -= rows - LINEPRSRV - else: - y = 0 - if k in SCROLL_DOWN: - if y < len(src_lines) - rows: - y += 1 - # if y + rows >= len(src_lines): - # reader(stdscr, ebook, index+1, width) - if k in PAGE_DOWN: - if y + rows - 2 <= len(src_lines) - rows: - y += rows - LINEPRSRV - else: - y = len(src_lines) - rows - if y < 0: - y = 0 - if k in CH_NEXT and index < len(ebook.get_contents()) - 1: - reader(stdscr, ebook, index+1, width) - if k in CH_PREV and index > 0: - reader(stdscr, ebook, index-1, width) - if k in CH_HOME: - y = 0 - if k in CH_END: - y = len(src_lines) - rows - if y < 0: - y = 0 - if k == TOC: - toc(stdscr, ebook, index, width) - if k == META: - meta(stdscr, ebook) - if k in HELP: - help(stdscr) - if k == WIDEN and (width + 2) < cols: - width += 2 - reader(stdscr, ebook, index, width) - return - if k == SHRINK and width >= 22: - width -= 2 - reader(stdscr, ebook, index, width) - return - if k == curses.KEY_RESIZE: - curses.resize_term(rows, cols) - rows, cols = stdscr.getmaxyx() - # TODO - if cols <= width: - width = cols - 2 - reader(stdscr, ebook, index, width) - - pad.refresh(y,0, 0,x, rows-1,x+width) - k = pad.getch() - -def main(stdscr, file): - stdscr.keypad(True) - curses.curs_set(0) - stdscr.clear() - stdscr.refresh() - rows, cols = stdscr.getmaxyx() - epub = Epub(file) - - if epub.path in state: - idx = int(state[epub.path]["index"]) - width = int(state[epub.path]["width"]) - y = int(state[epub.path]["pos"]) - else: - state[epub.path] = {} - idx = 1 - y = 0 - width = 80 - - if cols <= width: - width = cols - 2 - y = 0 - reader(stdscr, epub, idx, width, y) - -if __name__ == "__main__": - if len(sys.argv) == 1: - file = False - for i in state: - if not os.path.exists(i): - del state[i] - elif state[i]["lastread"] == str(1): - file = i - if not file: - print("ERROR: Found no last read file.") - print(__doc__) - else: - curses.wrapper(main, file) - elif len(sys.argv) == 2 and sys.argv[1] not in ("-h", "--help"): - curses.wrapper(main, sys.argv[1]) - else: - print(__doc__)- \ No newline at end of file diff --git a/scripts/executable b/scripts/executable @@ -1,41 +0,0 @@ -#!/usr/bin/env bash -# CREATED BY ALEX BALGAVY OF INSIGHTDEV -# COPYRIGHT (C)2013 ALEX BALGAVY -# FREE TO USE OR SHARE - -# Sets the parameters equal to variables -FILENAME="${1}" - -# Sets the exit error -NO_FILE="Exit error 85: No file found." - -# Starts the parameter case -# Checks if $FILENAME exists -if [ ! -e "$FILENAME" ]; then - echo "There is no file called $FILENAME" - echo "Please check spelling" - exit -else - echo "Congratulations found $FILENAME. " - - # Does some stuff to make it look cool - ls -lah $FILENAME - read -p "Process $FILENAME? Y or N:" PROCEED - case $PROCEED in - Y* | y*) - chmod +x $FILENAME - echo "Successfully changed to executable." - read -p "Do you want to run $FILENAME? Y or N:" RUN_OR_NOT - case $RUN_OR_NOT in - Y* | y*) - ./$FILENAME - ;; - *) - echo "File not run. You can manually run by doing ./$FILENAME" - ;; - *) - echo "File not processed." - ;; - esac -esac -fi- \ No newline at end of file diff --git a/scripts/feh-background b/scripts/feh-background @@ -1,13 +0,0 @@ -#!/usr/bin/env bash -set -x -if ! command -v gsettings &>/dev/null; then - echo "gsettings not installed." >&2 - exit 1 -fi - -theme=$(gsettings get org.gnome.Terminal.Legacy.Settings theme-variant | tr -d "'") -if [ "$theme" = "light" ]; then - feh --bg-scale "/home/zeroalpha/Pictures/Backgrounds/light.jpg" -else - feh --bg-fill "/home/zeroalpha/Pictures/Backgrounds/dark.jpg" -fi diff --git a/scripts/ffmpres b/scripts/ffmpres @@ -1,4 +1,6 @@ #!/usr/bin/env bash +# ffmpeg wrapper with various presets. + if ! command -v ffmpeg &> /dev/null; then echo "ffmpeg not found" && exit 1; fi main() { case "$1" in diff --git a/scripts/fix-virtualenv-after-py-upgrade b/scripts/fix-virtualenv-after-py-upgrade @@ -1,5 +1,5 @@ #!/usr/bin/env bash - +# Upgrading python on macOS sometimes breaks virtualenvs. This fixes them again. [ -z "$WORKON_HOME" ] && { echo '$WORKON_HOME not set'; exit 1; } if ! command -v gfind &>/dev/null; then echo "gfind not installed." >&2 diff --git a/scripts/get-frontwin-title b/scripts/get-frontwin-title @@ -1,4 +1,5 @@ #!/usr/bin/env bash +# on macOS, gets the title of the frontmost window. if ! command -v osascript 1>/dev/null 2>&1; then echo "osascript not installed." >&2 exit 1 @@ -38,4 +39,4 @@ else end if end tell YEET - fi +fi diff --git a/scripts/ghp b/scripts/ghp @@ -1,4 +1,7 @@ #!/usr/bin/env ruby +# A script that lets you check build status of github pages and trigger new +# builds. You can check one or more pages at once, the script minimizes +# requests using etag values. require 'open-uri' require 'net/http' require 'json' diff --git a/scripts/git-review b/scripts/git-review @@ -1,41 +0,0 @@ -#!/usr/bin/env bash -case "$1" in - files) - # list files which have changed since REVIEW_BASE - # (REVIEW_BASE defaults to 'master' in my zshrc) - git diff --name-only $(git merge-base HEAD "${REVIEW_BASE:-master}") - ;; - - stat) - # Same as above, but with a diff stat instead of just names - # (better for interactive use) - git diff --stat $(git merge-base HEAD "${REVIEW_BASE:-master}") | sort -t \| -k 2nr | less -R - ;; - - diff) - # Open all files changed since REVIEW_BASE in Vim tabs - # Then, run fugitive's :Gdiff in each tab, and finally - # tell vim-gitgutter to show +/- for changes since REVIEW_BASE - vim -p $(git review files) +"tabdo Gdiff ${REVIEW_BASE:-master}" +"let g:gitgutter_diff_base = '${REVIEW_BASE:-master}'" - ;; - - edit) - # Open all files changed since REVIEW_BASE in Vim - vim $(git review files) - ;; - - one) - # Same as the above, except specify names of files as arguments, - # instead of opening all files: - # git reviewone foo.js bar.js - vim -p +"tabdo Gdiff ${REVIEW_BASE:-master}" +"let g:gitgutter_diff_base = '${REVIEW_BASE:-master}'" "$@" - ;; - - *) - echo "Commands:" - echo -e " - files" - echo -e " - stat" - echo -e " - diff" - echo -e " - edit" - echo -e " - one" -esac diff --git a/scripts/global-git-status b/scripts/global-git-status @@ -1,4 +1,5 @@ #!/usr/bin/env bash +# Gives git status output for all git repositories on the system. spin() { sp='/-\|' printf ' ' @@ -31,14 +32,14 @@ count=0 echo -ne '\r' IFS=$'\n' -for i in ${gits[@]}; do +for i in ${gits[@]}; do ((count++)) echo -ne "Processed $count\r" - echo "git: $i" >> $logfile; - cd "$i/../" &>/dev/null; - git fetch --all &> /dev/null; - git status >> $logfile; - hr >> $logfile; + echo "git: $i" >> $logfile; + cd "$i/../" &>/dev/null; + git fetch --all &> /dev/null; + git status >> $logfile; + hr >> $logfile; done echo vim $logfile -c "let @d=\"j?^git: V/# d\"" -c "w|1" --cmd "echo 'Execute register D to remove a status report.'" diff --git a/scripts/gnome-background b/scripts/gnome-background @@ -1,55 +0,0 @@ -#!/usr/bin/env bash -die() { - echo "$1" >&2 - exit 1 -} -loop_backgrounds() { - for i in /home/zeroalpha/Pictures/Backgrounds/"$1"/*; do - gsettings set org.gnome.desktop.background picture-uri "$i"; - echo -n "$i: "; - echo "press enter to continue, c to copy"; - read -rsn 1 key; - if [ "$key" = "c" ]; then - if ! command -v xclip &>/dev/null; then - echo "xclip not installed." >&2 - else - xclip -sel clip <<< "$i" - echo "copied" - fi - fi - done -} -temp_set_background() { - echo "Setting to $1" - gsettings set org.gnome.desktop.background picture-uri "file://$1"; - echo "Press enter to continue." - read -rsn 1 c; - reset_background -} -[ -d /home/zeroalpha/Pictures/Backgrounds ] || die "No backgrounds folder." - -save_current() { - current=$(gsettings get org.gnome.desktop.background picture-uri) - trap reset_background INT TERM KILL EXIT -} -reset_background() { - gsettings set org.gnome.desktop.background picture-uri "$current"; - trap - INT TERM KILL EXIT - exit 0 -} -if [ "$1" = "audition" ]; then - [ $# -eq 2 ] || die "Light or dark?" - save_current - loop_backgrounds "$2" -elif [ "$1" = "test" ]; then - [ $# -eq 2 ] || die "No background provided." - save_current - temp_set_background "$2" -elif [ "$1" = "print" ]; then - gsettings get org.gnome.desktop.background picture-uri; -else - if [ $# -eq 1 ]; then - echo "Setting background to $1" - gsettings set org.gnome.desktop.background picture-uri "file://$1"; - fi -fi diff --git a/scripts/gnome-settings b/scripts/gnome-settings @@ -1,2 +0,0 @@ -#!/usr/bin/env bash -XDG_CURRENT_DESKTOP=GNOME gnome-control-center diff --git a/scripts/google_hangouts_parser.rb b/scripts/google_hangouts_parser.rb @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# Parses a Hangouts.json file downloaded from Google Hangouts require 'json' File.open("Hangouts.json") do |f| t=JSON.parse(f.read) diff --git a/scripts/httpc b/scripts/httpc @@ -1,4 +1,5 @@ #!/usr/bin/env bash +# A wrapper around httpie, providing a prompt for repeated querying of the same host. if ! command -v http &> /dev/null; then echo "Please install httpie" exit 1 diff --git a/scripts/img-to-background b/scripts/img-to-background @@ -1,20 +0,0 @@ -#!/usr/bin/env bash -# set the aspect ratio -# 16:10 == 1440x900 -aspect_ratio="16:9" - -if ! command -v convert &>/dev/null; then - echo "convert not installed." >&2 - exit 1 -fi -die() { - echo "$1" >&2 - exit 1 -} - -[ $# -eq 1 ] || die "Filename argument needed." -[ -e "$1" ] || die "$1 does not exist." -[ -f "$1" ] || die "$1 is not a file." -newname="${1/#/siz-}" -convert "$1" -resize "$aspect_ratio" "$newname" -printf "Resized image written to %s\n" "$newname" diff --git a/scripts/imgpreview b/scripts/imgpreview @@ -1,4 +1,5 @@ #!/bin/sh +# Cross-platform terminal image preview script. HEIGHT="$2" [ -z "$2" ] && HEIGHT="$(tput lines)" os=$(uname -s | tr '[:upper:]' '[:lower:]') diff --git a/scripts/import-to-music-library b/scripts/import-to-music-library @@ -1,4 +1,7 @@ #!/bin/sh +# Meant to be used with mpd and mpc. +# Import everything in the current folder to $MUSIC_DIR. Then update the +# recently added playlist in $MUSIC_DIR, and run an mpd database update. die() { printf '%s\n' "$1" >&2 && exit 1; } checkdeps() { for com in "$@"; do diff --git a/scripts/it-style b/scripts/it-style @@ -1,4 +1,7 @@ #!/usr/bin/env bash +# Set a track's genre in iTunes from Discogs. +# No longer updated, I stopped using iTunes. + oldifs="$IFS" console=0 print_help() { diff --git a/scripts/joplin-html-fix b/scripts/joplin-html-fix @@ -1,17 +0,0 @@ -#!/usr/bin/env bash -read -rp "Execute cleanup in $(pwd)?" -n 1 -s conf -case "$conf" in - Y|y) - ;; - *) - echo "OK nothing happens" - exit 1 - ;; -esac - -set -x -sed -i 's:\.\./_resources:\./_resources:g' *.html -mv * ../ -cd .. -find . -type d -empty -depth -delete - diff --git a/scripts/joplin-interface b/scripts/joplin-interface @@ -1,56 +0,0 @@ -#!/usr/bin/env ruby -require 'faraday' -require 'json' - -class JoplinApi - def get endpoint, params=nil, headers=nil - if params - resp = Faraday.get(@uri+endpoint, {token: @token}.merge(params), headers) - else - resp = Faraday.get(@uri+endpoint, nil, headers) - end - - if resp.success? - if resp.body - begin - JSON.parse(resp.body) - rescue JSON::ParserError - {} - end - else - nil - end - end - end - - def get_notes - notes = get("/folders/#{@notebook}/notes") - notes.map do |note| - { title: note["title"], - id: note["id"], - body: (get("/notes/#{note["id"]}", {fields: "body"}))["body"] - } - end - end - - def initialize notebook - @token = ENV["JOPLIN_API_KEY"] - @uri = "http://localhost:41184" - - parts = notebook.split "/" - folders = get("/folders") - current = 0 - part = parts[current] - notebook = folders.find { |x| x["title"] == part } - - until part == parts.last - folders = notebook["children"] - current += 1 - part = parts[current] - notebook = folders.find { |x| x["title"] == part } - end - - @notebook = notebook["id"] - - end -end diff --git a/scripts/linkdir b/scripts/linkdir @@ -1,41 +0,0 @@ -#!/usr/bin/env bash -if [ $# -eq 0 ]; then - echo "Needs at least one argument (the source directory)." - echo "Usage: linkdir source_directory [target_directory]" - exit 1 -fi -if [[ "$1" != */ ]]; then - source_dir="${1}/" -else - source_dir="$1" -fi - -target_dir="./" - -if [ $# -ge 2 ]; then - if [[ "$2" != */ ]]; then - target_dir="${2}/" - else - target_dir="$2" - fi -fi - -echo "Linking content of $source_dir to $target_dir..." - -cr=0 -ca=0 - -for f in "${source_dir}"*; do - if [ -L "${target_dir}${f##*/}" ]; then - rm "${target_dir}${f##*/}" - ((cr++)) - fi - - ln -s "$f" "${target_dir}${f##*/}" - echo "'$f' -> '${target_dir}${f##*/}'" - ((ca++)) -done - -echo "Removed $cr local symlinks with originals in $source_dir." -echo "Linked $ca directories into $target_dir." -unset cr ca source_dir target_dir diff --git a/scripts/mac-battery-warning b/scripts/mac-battery-warning @@ -1,4 +1,13 @@ -#!/usr/local/bin/bash +#!/usr/bin/env bash +# On macOS, send a notification warning if battery level is under/over a certain limit. +checkdeps() { + for com in "$@"; do + command -v "$com" >/dev/null 2>&1 \ + || { printf '%s required but not found.\n' "$com" >&2 && exit 1; } + done +} +checkdeps terminal-notifier + batt_status=$(pmset -g batt) battery_pct=$(awk '/InternalBattery/ { print $3 }' <<< "$batt_status" | cut -d% -f1) if { grep -q "from 'Battery Power'" <<< "$batt_status"; } && [ "$battery_pct" -le 15 ]; then diff --git a/scripts/mailsync b/scripts/mailsync @@ -1,4 +1,5 @@ #!/bin/sh +# Sync my email using mbsync, reindex via notmuch, print the amount of new messages. rbw unlock mbsync -c /Users/alex/.config/mbsync/mbsyncrc -Va notmuch-hook diff --git a/scripts/markdown2epub b/scripts/markdown2epub @@ -1,4 +1,5 @@ #!/bin/sh +# Convert a markdown file to a formatted EPUB, via pandoc. command -v pandoc 1>/dev/null 2>&1 || { printf "error: pandoc not installed.\n"; exit 1; } [ $# -eq 2 ] || { printf "Usage: markdown2epub input.md output.epub\n"; exit 1; } [ -f "$1" ] || { printf "File %s not found.\n" "$1"; exit 1; } diff --git a/scripts/mdvl b/scripts/mdvl @@ -1,651 +0,0 @@ -#!/usr/bin/env python -Ss -# coding: utf-8 -# Some systems do not accept shebang with args. Use python -Ss mdvl.py then. -''' -# Lightweight Simple Markdown Renderer for the Terminal - -## Usage - - mdvl <markdown source | markdown file> - cat <markdown file> | mdvl - -## Config - -``` -%s -``` - -### Colors - -``` -%%s -``` - -## Debugging Parsing Errors - - export mdvl_debug=1 - -See also https://github.com/axiros/mdvl - -''' -__version__ = "2017.07.16.7" # count up for new pip versions -__author__ = "Gunther Klessinger" - -from textwrap import fill -from operator import setitem as set -import re, os - -debug=os.environ.get('mdvl_debug') - -# check environ for value and cast into bools if necessary: -_b = {'True': True, 'False': False} -env = lambda k, d=None: _b.get(k, os.environ.get(k, d)) - -# ----------------------------------------------------------------- Config Mgmt -class Cfg: - ''' - Base class for osenv and kw configurable instances. - - We have defaults, overridable by environ keys, overridable by **kws - - problem is that color codes should be givabable as ints - but on the - terminal we usually have them as full ansi escape str. - Thats why `get_val` is present and adapts for that in the Colors cls. - ''' - _parms = None # our (relevant) keys and values - - def setup(self, kw): - ''' find all our key value defaults and override with env and **kw''' - self._parms = [] - kv = [(k, getattr(self, k)) - for k in dir(self) if not k.startswith('_')] - self._parms = [(k, v) for k, v in kv if not hasattr(v, '__code__')] - [setattr(self, k, self.get_val(k, v, kw)) for k, v in self._parms] - - def get_val(self, k, dflt, kw): - try: - return type(dflt)(kw.get(k, env(k, dflt))) - except Exception as ex: - # show reason clearer: - raise Exception( - 'Could not cast type %s - have %s %s %s %s' % ( - type(dflt), k, dflt, kw, env(k, dflt))) - - -class Colors(Cfg): - ''' - Color namespae with efault color scheme (greenish). - The 'C' in the main method. - x_ -> x with ansi escapes in __init__, with env precedence - ''' - O = '\x1B[0m' - GRAY = 240 - CODE = 245 - L = env('L', 66) - H1 = env('I', 158) - H2 = env('G', 115) - H3 = env('M', 72) - H4 = env('CODE', 66) - emph = env('I', 158) - ital = env('M', 72) - - def get_val(self, k, dflt, kw): - # see Cfg for expl. - v = kw.get(k, env(k, dflt)) - v = str(v) - if '\x1B' in v: - pass - elif '[' in v: - v = '\x1B' + v[2:] - else: - v = '\x1B[1;38;5;%sm' % v - return v - - def H(s, lev): - return getattr(s, 'H%s' % lev, s.L) - - -class Facts(Cfg): - ''' features config ''' - debug = False - term_width = 80 - no_print = False - bq_mark = '┃' - code_mark = '│' - light_bg = False - no_smart_indent = False - horiz_rule = '─' - single_line_mode = False - # left and right global indents: - indent = 0 - rindent = 0 - width = 0 # if set > 0 we set rindent accordingly - header_numbering = 50 # -1: off, min number of lines to do autonumbering - header_numb_level_min= 1 # min header level to show the numbers - header_numb_level_max= 6 # max header level to show the numbers - header_underlining = '*' # e.g. '*-' to underline H1 with *** and H2 with --- - opts_tbl_start = '-' - opts_tbl_end = ':' - - - def __init__(f, md, **kw): - # first check if the config contains color codes and set to C: - # now overriding our defaults with kw then with env - if md.split('\n', 1)[0] == md: - f.single_line_mode = True - f.indent = 0 - f.setup(kw) - f.colr = Colors(); f.colr.setup(kw) - -# ------------------------------------------------ end config - begin rendering -# helper funcs: -def get_subseq_light_table_indent(l0): - p = '**' if l0.startswith('**') else '*' - keywrd, l1 = l0[2:].split(p, 1) - keywrd = l0[:2] + keywrd + p - l1 = l1 - offs = 1 if l1 and l1[0] == ' ' else 2 - return len(l0) - len(l1[offs:].lstrip()) - (2 * len(p)) - - -def block_quote_status(l, g): - 'blockquote' - if not l.startswith('>'): - return 0, l, '' - _ = l.split(' ', 1) - lev = len(_[0]) - g['max_bq_depth'] = max(lev, g['max_bq_depth']) - return lev, _[1], _[0] - - -h_rules_col = {'-': 'L', '_': 'H3', '*': 'H1'} # different colors -list_markup = {'- ': ('\x03 ', 'L', '❖ '), '* ': ('\x04 ', 'H2', '▪ ')} -h_rules = '---', '___', '***' -def _main(md, f): - C, cur_colr = f.colr, 'cur_colr' - cols = int(f.term_width) - if f.width: - f.rindent = cols - f.indent - f.width + f.rindent - cols = cols - f.indent - f.rindent - - g = {} # glob parsing state (current color, code blocks) - - - - # ------------ line tools requiring facts instance, possible ctx g as well: - def is_opts_tbl(l, b=f.opts_tbl_start, e=f.opts_tbl_end): - fw = first_word(l) - if fw and fw.startswith(b) and fw.endswith(e): - return l.replace(fw, '*%s*' % fw[:-len(e)]), len(fw) - return l, None - - def is_rule(l): - if not l[:3] in h_rules: - return - ll = len(l) - return True if l in (ll * '-', ll * '*', ll * '_') else False - - - - # Line Tools: - first_word = lambda l: l.split(' ', 1)[0] - is_header = lambda l: l.startswith('#') - is_list = lambda l: l.lstrip()[:2] in list_markup - is_empty = lambda l: l.strip() == '' - is_md_link = lambda l: l[0] == '[' and 'http' in l and ']' in l - - is_new_block = lambda l: ( - is_header(l) or - is_list(l) or - is_opts_tbl(l)[1] or - is_empty(l) or - is_md_link(l) or - l[0] in ('\x02', ) or - is_rule(l) - ) - # ------------------------------------------------------------------------- - - - md = md.strip() - - # FENCED CODE BLOCKS: - # we take them out before all parsing,see http://stackoverflow.com/a/587518 - apo, apos = chr(96), chr(96) * 3 # chr 96 is backtick. - _ = r'^({apos}[^\n]+)\n((?:[^{apo}]+\n{apo}{apo})+)'.format(apos=apos, apo=apo) - fncd = re.compile(_, re.MULTILINE) # finds fenced code - md = md.replace('\n~~~', apos) # alternative markup for fenced - # remembering the blocks by their occurance number (len(g)) - [set(g, len(g), '\n'.join(m.groups()) + apo ) for m in fncd.finditer(md)] - blocks = len(g) - for i in range(blocks): - md = md.replace(g[i], '\x02%s' % i) - - g['max_bq_depth'] = 0 - - - # LINESPROCESSOR: - lines, out = md.splitlines(), [] - - g['header_numbering'] = False - if f.header_numbering > -1 and len(lines) > f.header_numbering: - g['header_numbering'] = True - g['header_level'] = {} # storing the current header numberings - - # remove boundary effects: - lines.insert(0, '') - lines.append('') - - while lines: - - line = lines.pop(0) - if is_empty(line): - out.append('') - continue - if debug: - print('procesing: ', line) - if is_rule(line): - out.append(getattr(C, h_rules_col[line[0]])+ (cols * f.horiz_rule)) - continue - - cb = None # indentd code blocks: - while line.startswith(' '): - cb = cb or [] - cb.append(line[4:]) - line = lines.pop(0) - if cb: - if out[-1] == '': - out.pop() - g[blocks] = '\n%s\n' % '\n'.join(cb) - out.append('\x02%s' % blocks) - blocks += 1 - lines.insert(0, line) - continue - - ssi = None # subseq indent for textwrap - - # TEXTBLOCKS: Concat lines which must be wrapped: - bqm = '' # blockquote mark. e.g. '>>'. - bq_lev, line, bqm = block_quote_status(line, g) - - src_line_nr = 0 - - # we derive the (static) opts table ssi for a new textblox: - line, opts_tbl_ssi = is_opts_tbl(line) - # now we find all other lines belonging to that text block and - # concat (pop from lines) all of them: - while ( lines and not line.endswith(' ') - and not is_header(line) ): - - src_line_nr += 1 - nl, l0 = lines[0], line.lstrip() # next line, this line - - bqnl = block_quote_status(nl, g) - if bqnl[0] == bq_lev: - lines[0] = nl = bqnl[1] # remove redundant '>' - - elif bqnl[0] != bq_lev and bqnl[0] > 0: - break # next line different blockquote level -> new text block - - # finding subseq. indent for textwrap.fill: - - # Little md violation: If first word is starred, we set a ssi to - # position: first line second word start. - # Gives easy 2 col wrappable tables when first col is hilited. - - #if 'xyz' in line: - # import pdb; pdb.set_trace() - if ssi == None: - if is_list(l0): - # replace "- " and "* " with tags: - line = list_markup[l0[:2]][0] + l0[2:] - ssi = 2 - elif opts_tbl_ssi: - ssi = opts_tbl_ssi - elif ( l0.startswith('*') and - not f.no_smart_indent and - src_line_nr == 1 ): - ssi = get_subseq_light_table_indent(l0) - - if is_new_block(nl): - # line is now one wrapable textblock - if bqnl[0]: # block quote new line - # adapt next line to parse: - lines[0] = (bqnl[2] + ' ') + lines[0] - break - else: - line = line.rstrip() + ' ' + lines.pop(0).lstrip() - - ssi = 0 if ssi is None else ssi - # lines are now blocks - - g[cur_colr] = C.O # reset color - ind = len(line) - len(line.lstrip()) - if bqm: - bqm += ' ' - line = bqm + line - - - if is_header(line): - cont = line.lstrip('#') - level = len(line) - len(cont) - line = cont.lstrip() - - u = getattr(f, 'header_underlining', '') - if len(u) >= level: - lines.insert(0, 3 * u[level-1]) - - if g['header_numbering']: - hl = g['header_level'] - hl[level] = hl.get(level, 0) + 1 - [set(hl, i, 0) for i in hl if i > level] - nr = '.'.join([str(hl[ll]) for ll in range(1, level + 1)]) - if f.header_numb_level_max > level - 1: - if f.header_numb_level_min > 1: - nr = nr.split('.')[f.header_numb_level_min-1:] - nr = '.'.join(nr) - if nr: - line = nr + ' ' + line - - g[cur_colr] = C.H(level) - - # WRAP: - if len(line) > cols: - s = (bqm + ' ' * (ind + ssi)) - line = fill(line, subsequent_indent=s, width=cols) - if is_md_link(line): - g[cur_colr] = C.GRAY - out.append(g[cur_colr] + line) - - - # --------------- Leaving line/block scanning, reWork complete document now - g[cur_colr] = C.O - out = '\n'.join(out) - - # INLINE MARKUP, *, **, backticks - # Alternating replacements, e.g. code, emph. requires a first space char: - altern = lambda s, c, r: re.sub( - r'([^{c}]+){c}([^{c}]+){c}?'.format(c=c), - r'\1%s\2%s' % (r, g[cur_colr]), ' ' + s)[1:] # removing space again - - # Star must be replaced, else the re would not work :(( - # currently no way to find single stars and not process them.. - out = out.replace('*', '\x01') - out = altern(out, apo , C.CODE) # code - out = altern(out, '\x01\x01', C.emph) # ** - out = altern(out, '\x01' , C.ital) # * - - # rearrange resets, to be *before* the line breaks, not after... - out = out.replace('\n' + C.O, C.O + '\n') - # ... so that we can look for blockquotes: - for i in range(g['max_bq_depth'], 0, -1): - # coloring, take header levels. bq_mark is "|": - m = '' - for j in range(1, i + 1): - m += C.H(j) + f.bq_mark - m += C.O - out = out.replace('\n' + '>' * i, '\n' + m) - - # Insert back the stored code blocks: - code_fmt = lambda c: c.replace('\n', '\n%s%s %s' % (C.L, f.code_mark, C.CODE) - ).rsplit('\n', 1)[0] - for i in range(blocks): - out = out.replace('\x02%s' % i, - '%s%s%s' % (C.CODE, code_fmt(g[i]), C.O)) - out = out.replace(apos + '\n', '') # before - out = out.replace(apos, '') # after - - for k, v in list_markup.items(): - out = out.replace(v[0], getattr(C, v[1]) + v[2] + C.O) - - out = strip_it(out, C.O) - if not f.single_line_mode: - out = '\n' + out + '\n' - li, ri = f.indent * ' ', f.rindent * ' ' - if li or ri: - out = li + out.replace('\n', '%s\n%s' % (ri, li)) - out += C.O # reset - if not f.no_print: - print (out) - return out - -def strip_it(out, rst): - 'clumsy way to strip at start at end, including color resets' - sc = {' ': 1, rst: len(rst), '\n': 1} - while 1: - m = False - for k in sc: - if out.startswith(k): - out = out[sc[k]:] - m = True - if out.endswith(k): - out = out[:-sc[k]] - m = True - if m: - break - if not m: - break - return out - - -def main(md, **kw): - f = Facts(md, **kw) - #return _main(md, f), f # we also return to the client the config - if debug or f.debug: - return _main(md, f), f # we also return to the client the config - try: - return _main(md, f), f # we also return to the client the config - except Exception as ex: - print (md) # clear text - print ('md error: %s %s ' % (f.colr.CODE, ex)) - -def render(md, cols, **kw): - kw['term_width'] = cols - return main(md, **kw)[0] - -def get_help(cols, PY2): - ff = Facts('\n', term_width=cols) - md, C = __doc__, ff.colr - for o in ff, C: - mmd = () - for k, d in sorted(o._parms): - v = getattr(o, k) - if o == ff: # need the perceived len here: - v = C.H2 + (str(u'%5s' % str(v)) if PY2 else '%5s' % v) + C.O - mmd += ('%s %s [%s]' % (v, k, d),) - md = md % ('\n'.join(mmd)) - return md - -# allow to adapt $COLUMNS by setting $term_width: -get_cols = lambda: (env('term_width') or - os.popen('tput cols 2>/dev/null').read().strip() or '80' ) - -def sys_main(): - import sys - PY2 = sys.version_info[0] == 2 - if PY2: - reload(sys); sys.setdefaultencoding('utf-8') - import os - from stat import S_ISFIFO - err = None - try: - cols = get_cols() - except Exception as ex: - err = str(ex) - cols = 80 - if S_ISFIFO(os.fstat(0).st_mode): # pipe mode - md = sys.stdin.read() - else: - if not len(sys.argv) > 1 or '-h' in sys.argv: - md = get_help(cols, PY2) - else: - md = sys.argv[1] - if os.path.exists(md): - with open(md) as fd: - md = fd.read() - if err: - print(err) - print md - else: - main(md, term_width=cols) - -# ============================================== Script Formatters =========== - -def format_bash(dev_help, cols, lines, script, *args): - ''' - Renders help for a bash script nicely, given it follows some conventions. - - These are: - - 1. An `md_doc` function is required, returning general docu as markdown, - containing the string "<auto_command_doc>" - - 2. All public functions must be in this format: - - : 'optional doculines before...' - function myfunc { - : 'optional inner doculines (params...)' - <code lines> - } - - 3. In the command parsing part then this: `show_help $sourced $0 $*` - with that function elsewhere in your tools: - - show_help () { - local sourced=$1; shift - local dev_help=false - test "${2:-}x" == "make_docx" && { md_doc; exit 0; } - test "${@: -1}" == "-hh" 2>/dev/null && dev_help=true || { - test "${@: -1}" == "-h" 2>/dev/null || return 0 - } - local cols=`stty size | cut -d ' ' -f 2` - mdvl -f $dev_help $cols "$*"; $sourced && return 1 || exit - } - - ''' - dev_help = True if str(dev_help) in ('True', 'true', '1') else False - single_func_doc=False; l = lines; funcs = [] - start = ": '" - is_func = lambda l: l.startswith('function ') - is_cmt_end = lambda l: l.rstrip().endswith("'") - is_cmt_start= lambda l: l.lstrip().startswith(start) - - def clean(s, head_sub): - s = s.strip() - s = s[len(start):] if s.startswith(start) else s - s = s[:-1] if s.endswith("'") else s - s = (('\n' + s).replace('\n#', '\n%s#' % head_sub))[1:] - return s - - def render_func(m, single_func_doc): - fn = m.keys()[0] - hf, hs = ('# `Function` **%s**', '##') if single_func_doc else ( - '### %s', '###') - nr, pre, post, code = m.values()[0] - md = [hf % fn] - pre and md.append(clean('\n'.join(pre), hs)) - post and md.append(clean('\n'.join(post), hs)) - if code: - code = '\n'.join(code) - if post or pre: - md.append('') - md.append(code) - md.extend(['---', '']) - md = '\n'.join(md) - return md - - fm = {} - for i in range(len(l)): - if is_func(l[i]): - fn = (l[i] + ' ').split(' ', 2)[1] - funcs.append({fn: [i, [], [], []]}) - fm[fn] = len(funcs) - 1 - - if not '-h' in args[0]: - match = args[0] - f = [] - for m in funcs: - if match in m.keys()[0]: - f.append(m) - if f: - funcs = f - single_func_doc = True - - for m in funcs: - nr, pre, post, code = m.values()[0] - # pre: - if is_cmt_end(l[nr-1]): - i = nr - while True: - i = i -1 - pre.insert(0, l[i]) - if is_cmt_start(l[i]): - break - if i > 1 and l[i-1].rstrip().endswith('}'): - pre = [] # err - break - - # post: - i = nr - if is_cmt_start(l[nr + 1]): - while True: - i += 1 - post.append(l[i][4:]) - if is_cmt_end(l[i]) and not l[i].strip() == start: - break - if l[i+1].rstrip().endswith('}'): - post = []; i = nr # err - break - if dev_help: - i += 1 - while True: - if l[i].strip() == '}': - break - code.append(l[i]) - i += 1 - - Facts.indent = 0 - if single_func_doc: - for m in funcs: - md = render_func(m, single_func_doc) - main(md, term_width=cols) - print - return - - # now the full doc. convention is to call with make_doc arg: - Facts.no_print = True - Facts.header_numbering = 10 - Facts.header_numb_level_min = 2 - Facts.header_numb_level_max = 2 - - full = os.popen(script.split(' ')[0] + ' make_doc').read() - acd = '<auto_command_doc>' - full = full.replace(acd, '## Commands\n\n' + acd) - md = main(full, term_width=cols)[0] - - Facts.header_numbering = -1 - rfuncs, sep = '', '\n\n' - if dev_help: - sep = '' - for m in funcs: - rfuncs = rfuncs + sep + render_func(m, single_func_doc) - mdf = main(rfuncs, term_width=cols)[0] - print(md.replace(acd, mdf)) - - - - -def format_file(dev_help, cols, fn, *args): - if not os.path.exists(fn): - raise Exception('Not found' + fn) - with open(fn) as fd: - lines = fd.read().splitlines() - if 'bash' in lines[0]: - format_bash(dev_help, cols, lines, fn, *args) - else: - raise Exception('Not supported format') - -if __name__ == '__main__': - import os, sys - if len(sys.argv) > 1 and sys.argv[1] == '-f': - format_file(*sys.argv[2:]) - else: - sys_main() - diff --git a/scripts/medium_reader.rb b/scripts/medium_reader.rb @@ -1,39 +0,0 @@ -#!/usr/bin/env ruby - -require 'open-uri' -require 'nokogiri' -require 'tempfile' - -if ARGV.empty? - print "Enter url: " - url = gets.chomp -else - url = ARGV[0] -end - -if ! URI.parse(url).host.downcase.include? 'medium.com' - abort "Not a Medium page." -end -doc = Nokogiri(open url) - -f = Tempfile.new(['article', '.html']) -begin - f.puts "<!DOCTYPE html><html><head>" - doc.css('head meta').each do |e| - f.puts e.to_html - end - doc.css('head link').each do |e| - f.puts e.to_html - end - f.puts doc.css('head title').first.to_html - f.puts "</head><body>" - doc.css('main div.section-content').each do |e| - f.puts e.to_html - end - f.puts "</body></html>" - - `open 'file://#{f.path}' && sleep 5` -ensure - f.close - f.unlink -end diff --git a/scripts/modified_cfscrape.py b/scripts/modified_cfscrape.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 +# Can scrape cloudflare-protected websites import cfscrape, sys scraper=cfscrape.create_scraper() url=sys.argv[1] diff --git a/scripts/mount_phone b/scripts/mount_phone @@ -1,31 +0,0 @@ -#!/usr/bin/env bash -# set -x -if ! command -v simple-mtpfs &>/dev/null; then - echo "simple-mtpfs not installed." >&2 - exit 1 -fi - -mountdir="/Users/alex/phone" -clean_up() { - echo "Exiting..." - if kill -0 "$mtpfs_pid" &> /dev/null; then - kill "$mtpfs_pid" - wait "$mtpfs_pid" - fi - [ ! -d "$mountpoint" ] || rmdir "$mountpoint"; - [ ! -L "$mountdir" ] || unlink "$mountdir"; -} -trap clean_up INT TERM EXIT - -try_to_mount() { - echo "Mounting phone..." - simple-mtpfs -f "$mountpoint" & - mtpfs_pid=$! -} - -mountpoint=$(mktemp -d) -try_to_mount -ln -s "$mountpoint" "$mountdir" -echo "Mounted at $mountdir (=> $mountpoint)." -echo "Send interrupt to unmount." -wait diff --git a/scripts/mp3-find-broken b/scripts/mp3-find-broken @@ -1,4 +1,5 @@ #!/bin/sh +# Finds broken MP3s, via mp3val. die() { printf "%s\n" "$1" >&2; exit 1; } command -v mp3val 1>/dev/null 2>&1 || die "mp3val not installed." [ $# -eq 1 ] || die "Argument required: path to music directory" diff --git a/scripts/mp3-tags-to-folders b/scripts/mp3-tags-to-folders @@ -1,4 +1,6 @@ #!/bin/sh +# Places all MP3 files in a directory into folders based on the artist and album. +# If you want to make changes in the current dir, run `mp3-tags-to-folders . .` die() { printf "%s\n" "$1" >&2; exit 1; } command -v ffprobe 1>/dev/null 2>&1 || die "ffprobe not installed." [ $# -eq 2 ] || die "Arguments required: path to music directory, path to destination" diff --git a/scripts/mpcq b/scripts/mpcq @@ -1,4 +1,5 @@ #!/bin/sh +# Queue one or more files in mpd via mpc printfq() { printf "'%s'\\n" "$(printf '%s' "$1" | sed -e "s/'/'\\\\''/g")"; } die() { printf '%s\n' "$1" >&2 && exit 1; } diff --git a/scripts/mpvq b/scripts/mpvq @@ -1,4 +1,5 @@ #!/bin/sh +# Queue one or more files in mpv if ! [ -S "$HOME"/.cache/mpv/input ]; then mkfifo "$HOME"/.cache/mpv/input fi diff --git a/scripts/newsboat-dl b/scripts/newsboat-dl @@ -1,4 +1,5 @@ -#!/usr/bin/env bash +#!/bin/sh +# Download audio/video from newsboat. This script generally isn't executed manually, but via newsboat macros. [ $# -eq 2 ] || { terminal-notifier -title "Not enough arguments" -message "Need exactly 2 arguments." && exit 1; } what="$1" where="$2" @@ -12,10 +13,10 @@ terminal-notifier -title "Started $what download" -message "Downloading \"$title case "$what" in "aonly") mkdir -p "$audiodir" - youtube-dl --add-metadata -xic -f bestaudio/best -o "$audiodir/%(title)s-%(creator)s.%(ext)s" "$where" 2>&1 >> "$logfile" + youtube-dl --add-metadata -xic -f bestaudio/best -o "$audiodir/%(title)s-%(creator)s.%(ext)s" "$where" >>"$logfile" 2>&1 ;; "av") - youtube-dl --add-metadata -ic --write-sub --embed-subs -o "$videodir/%(title)s-%(creator)s.%(ext)s" "$where" 2>&1 >> "$logfile" + youtube-dl --add-metadata -ic --write-sub --embed-subs -o "$videodir/%(title)s-%(creator)s.%(ext)s" "$where" >>"$logfile" 2>&1 ;; *) terminal-notifier -title "Error" -message "Download option not valid" -group "$where" diff --git a/scripts/newsboat-export-opml-tagged b/scripts/newsboat-export-opml-tagged @@ -1,5 +1,5 @@ #!/usr/bin/env ruby - +# Exports a newsboat URLs file to an OPML, with tags, for use in e.g. Miniflux. ( warn "Usage: newsboat-export-opml-tagged /path/to/urls /path/to/destination.opml"; exit 1 ) unless ARGV.length == 2 ( warn "#{ARGV[0]} is not readable."; exit 1 ) unless File.readable? ARGV[0] urls = File.readlines(ARGV[0]) diff --git a/scripts/newsboatify-youtube-subscriptions b/scripts/newsboatify-youtube-subscriptions @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# Convert a youtube subscriptions HTML file to a newsboat URLs file. require 'nokogiri' doc = Nokogiri::HTML File.read("source.html") hrefs = doc.css("a.channel-link").inject([]) { |arr,c| arr << c.attr("href") unless c.attr("href").nil? }.uniq! diff --git a/scripts/newsrefreshd b/scripts/newsrefreshd @@ -1,4 +1,5 @@ #!/bin/sh +# This script refreshes newsboat. Runs via a LaunchAgent or cronjob. set -e notidie() { diff --git a/scripts/notify b/scripts/notify @@ -1,4 +1,5 @@ #!/bin/sh +# Cross-platform notification script. [ $# -ge 2 ] || { printf "Arguments: title, message\n" && exit 1; } group=${3:-$TERM}