From 628233969de5793b436c81357fdc571cc318b540 Mon Sep 17 00:00:00 2001 From: Geoffrey Frogeye Date: Tue, 30 Jan 2018 19:16:17 +0100 Subject: [PATCH 01/22] =?UTF-8?q?Transfert=20de=20fichiers=20par=20commit?= =?UTF-8?q?=20interpos=C3=A9s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/machines | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/scripts/machines b/scripts/machines index e9b8a0d..b9c7fde 100755 --- a/scripts/machines +++ b/scripts/machines @@ -52,7 +52,7 @@ function _machines-api { wget $MACHINES_API/$route --content-on-error --quiet --output-document=- "$@" result=$? if [ $result != 0 ]; then - echo "[ERROR] wget returned $result..." + echo "[ERROR] wget returned $result..." > /dev/stderr exit 2 fi } @@ -77,7 +77,7 @@ function _machines-pubFromCrt { function _machines-verifyCertificate { return if openssl verify $MACHINES_CONFIG/machines.crt | grep -v 'error 18' | grep 'error' --quiet; then - echo "[ERROR] Invalid certificate" + echo "[ERROR] Invalid certificate" > /dev/stderr exit 1 fi } @@ -92,7 +92,7 @@ function _machines-ensurePub { if [ $? == 1 ]; then mv $CERT_FILE $MACHINES_CONFIG/machines.crt &> /dev/null else - echo "[ERROR] Certificate rejected." + echo "[ERROR] Certificate rejected." > /dev/stderr exit 1 fi fi @@ -105,7 +105,7 @@ function _machines-ensurePub { function _machines-ensureAdmin { if [ ! -f $MACHINES_CONFIG/machines.key ]; then - echo "[ERROR] You need have to have the private key to do that" + echo "[ERROR] You need have to have the private key to do that" > /dev/stderr exit 1 fi } @@ -147,7 +147,7 @@ function _machines-updateAkey { return 0 else cat $MYKEY_FILE - echo "[ERROR] Authorized keys are not properly signed" + echo "[ERROR] Authorized keys are not properly signed" > /dev/stderr rm $MYKEY_FILE exit 1 fi @@ -368,12 +368,13 @@ function machines_update-all { machines_machine_list | while read machine; do echo "Updating $machine..." ssh $machine 'machines update' & + ssh $machine 'cd .dotfiles && git pull' & done } function machines_regen-keys { if [[ -e $MACHINES_CONFIG/machines.key || -e $MACHINES_CONFIG/machines.pub || -e $MACHINES_CONFIG/machines.crt ]]; then - echo "[ERROR] Please delete the pem files manually to prove you know what you're doing." + echo "[ERROR] Please delete the pem files manually to prove you know what you're doing." > /dev/stderr exit 1 else openssl genrsa -out $MACHINES_CONFIG/machines.key 4096 @@ -389,7 +390,7 @@ function machines_regen-keys { function machines_setup { if [ -e $MACHINES_CONFIG/this.name ]; then - echo "[ERROR] This machine is already set up" + echo "[ERROR] This machine is already set up" > /dev/stderr exit 1 fi From 3c9e2c3279aca1e717d5b5b75b4ab94a712e09d4 Mon Sep 17 00:00:00 2001 From: Geoffrey Frogeye Date: Mon, 19 Mar 2018 07:29:56 +0100 Subject: [PATCH 02/22] vdirsyncer --- bashrc | 1 + config/i3/ashuffle | 7 ++ config/i3/config | 1 + config/khal/config | 38 +++++++++++ config/khard/khard.conf | 43 ++++++++++++ .../default.target.wants/syncthing.service | 1 + .../user/timers.target.wants/vdirsyncer.timer | 1 + config/todoman/todoman.conf | 4 ++ config/vdirsyncer/.dfrecur | 0 config/vdirsyncer/config | 68 +++++++++++++++++++ gitconfig | 5 ++ scripts/arch-kexec | 2 +- scripts/install-prefs | 15 +++- scripts/machines | 4 ++ scripts/rms | 2 + vimrc | 2 +- 16 files changed, 191 insertions(+), 3 deletions(-) create mode 100755 config/i3/ashuffle create mode 100644 config/khal/config create mode 100644 config/khard/khard.conf create mode 120000 config/systemd/user/default.target.wants/syncthing.service create mode 120000 config/systemd/user/timers.target.wants/vdirsyncer.timer create mode 100644 config/todoman/todoman.conf create mode 100644 config/vdirsyncer/.dfrecur create mode 100644 config/vdirsyncer/config create mode 100755 scripts/rms diff --git a/bashrc b/bashrc index 15fcf20..d0f18f4 100644 --- a/bashrc +++ b/bashrc @@ -43,6 +43,7 @@ alias rm='rm -Iv --one-file-system' alias free='free -m' alias df='df -h' alias pacman='pacman --color auto' +alias pacaur='pacaur --color auto' alias dmesg='dmesg --ctime' # Frequent mistakes diff --git a/config/i3/ashuffle b/config/i3/ashuffle new file mode 100755 index 0000000..77168ef --- /dev/null +++ b/config/i3/ashuffle @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +while true +do + ashuffle + sleep 1 +done diff --git a/config/i3/config b/config/i3/config index 04ffddd..ea9bdbe 100644 --- a/config/i3/config +++ b/config/i3/config @@ -371,6 +371,7 @@ exec --no-startup-id dunst # Notifications exec --no-startup-id keynav # Keyboard cursor controller #exec --no-startup-id $HOME/.config/i3/clipmenud # Clipboard manager exec --no-startup-id mpd # Music Player Daemon +exec --no-startup-id ~/.config/i3/ashuffle # MPD Auto-refill exec --no-startup-id autorandr --change # Screen configuration and everything that depends on it set $ignore #ff00000 diff --git a/config/khal/config b/config/khal/config new file mode 100644 index 0000000..f923a51 --- /dev/null +++ b/config/khal/config @@ -0,0 +1,38 @@ +[calendars] +[[perso]] +path = ~/.vdirsyncer/calendars/perso/ +color = light green + +[[clubs]] +path = ~/.vdirsyncer/calendars/clubs/ +color = yellow + +[[cours]] +path = ~/.vdirsyncer/calendars/cours/ +color = light blue +readonly = True + +[[polytech]] +path = ~/.vdirsyncer/calendars/polytech/ +color = dark blue + +[[bnei]] +path = ~/.vdirsyncer/calendars/bnei/ +color = light cyan + +# [[birthdays]] +# type=birthdays +# path = ~/.vdirsyncer/contacts/contacts/ +# color = light magenta + +[locale] +timeformat = %H:%M +dateformat = %d/%m +longdateformat = %d/%m/%Y +datetimeformat = %d/%m %H:%M +longdatetimeformat = %d/%m/%Y %H:%M +local_timezone = Europe/Paris + +[default] +highlight_event_days = True + diff --git a/config/khard/khard.conf b/config/khard/khard.conf new file mode 100644 index 0000000..eee27d6 --- /dev/null +++ b/config/khard/khard.conf @@ -0,0 +1,43 @@ +# example configuration file for khard version >= 0.11.0 +# place it under $HOME/.config/khard/khard.conf + +[addressbooks] +[[contacts]] +path = ~/.vdirsyncer/contacts/contacts/ + +[general] +debug = no +default_action = list +editor = vim +merge_editor = vimdiff + +[contact table] +# display names by first or last name: first_name / last_name +display = first_name +# group by address book: yes / no +group_by_addressbook = no +# reverse table ordering: yes / no +reverse = no +# append nicknames to name column: yes / no +show_nicknames = no +# show uid table column: yes / no +show_uids = yes +# sort by first or last name: first_name / last_name +sort = last_name +# localize dates: yes / no +localize_dates = yes + +[vcard] +# extend contacts with your own private objects +# these objects are stored with a leading "X-" before the object name in the vcard files +# every object label may only contain letters, digits and the - character +# example: +# private_objects = Jabber, Skype, Twitter +private_objects = Jabber, Skype, Twitter +# preferred vcard version: 3.0 / 4.0 +preferred_version = 3.0 +# Look into source vcf files to speed up search queries: yes / no +search_in_source_files = no +# skip unparsable vcard files: yes / no +skip_unparsable = no + diff --git a/config/systemd/user/default.target.wants/syncthing.service b/config/systemd/user/default.target.wants/syncthing.service new file mode 120000 index 0000000..eae00f7 --- /dev/null +++ b/config/systemd/user/default.target.wants/syncthing.service @@ -0,0 +1 @@ +/home/geoffrey/.local/share/systemd/user/syncthing.service \ No newline at end of file diff --git a/config/systemd/user/timers.target.wants/vdirsyncer.timer b/config/systemd/user/timers.target.wants/vdirsyncer.timer new file mode 120000 index 0000000..eed4fe0 --- /dev/null +++ b/config/systemd/user/timers.target.wants/vdirsyncer.timer @@ -0,0 +1 @@ +/home/geoffrey/.local/share/systemd/user/vdirsyncer.timer \ No newline at end of file diff --git a/config/todoman/todoman.conf b/config/todoman/todoman.conf new file mode 100644 index 0000000..677aa91 --- /dev/null +++ b/config/todoman/todoman.conf @@ -0,0 +1,4 @@ +[main] +path = ~/.vdirsyncer/calendars/* +default_list = perso +humanize = True diff --git a/config/vdirsyncer/.dfrecur b/config/vdirsyncer/.dfrecur new file mode 100644 index 0000000..e69de29 diff --git a/config/vdirsyncer/config b/config/vdirsyncer/config new file mode 100644 index 0000000..148fa0e --- /dev/null +++ b/config/vdirsyncer/config @@ -0,0 +1,68 @@ +# An example configuration for vdirsyncer. +# +# Move it to ~/.vdirsyncer/config or ~/.config/vdirsyncer/config and edit it. +# Run `vdirsyncer --help` for CLI usage. +# +# Optional parameters are commented out. +# This file doesn't document all available parameters, see +# http://vdirsyncer.pimutils.org/ for the rest of them. + +[general] +# A folder where vdirsyncer can store some metadata about each pair. +status_path = "~/.vdirsyncer/status/" + +# # CARDDAV +[pair geoffrey_contacts] +# A `[pair ]` block defines two storages `a` and `b` that should be +# synchronized. The definition of these storages follows in `[storage ]` +# blocks. This is similar to accounts in OfflineIMAP. +a = "geoffrey_contacts_local" +b = "geoffrey_contacts_remote" + +# Synchronize all collections that can be found. +# You need to run `vdirsyncer discover` if new calendars/addressbooks are added +# on the server. + +collections = ["from a", "from b"] + +# Synchronize the "display name" property into a local file (~/.contacts/displayname). +metadata = ["displayname"] + +# To resolve a conflict the following values are possible: +# `null` - abort when collisions occur (default) +# `"a wins"` - assume a's items to be more up-to-date +# `"b wins"` - assume b's items to be more up-to-date +#conflict_resolution = null + +[storage geoffrey_contacts_local] +# A storage references actual data on a remote server or on the local disk. +# Similar to repositories in OfflineIMAP. +type = "filesystem" +path = "~/.vdirsyncer/contacts/" +fileext = ".vcf" + +[storage geoffrey_contacts_remote] +type = "carddav" +url = "https://dav.frogeye.fr/caldav.php/" +username = "geoffrey" +password.fetch = ["command", "sh", "-c", "cat ~/.config/vdirsyncer/pass"] + +# CALDAV +[pair geoffrey_calendar] +a = "geoffrey_calendar_local" +b = "geoffrey_calendar_remote" +collections = ["from a", "from b"] + +# Calendars also have a color property +metadata = ["displayname", "color"] + +[storage geoffrey_calendar_local] +type = "filesystem" +path = "~/.vdirsyncer/calendars/" +fileext = ".ics" + +[storage geoffrey_calendar_remote] +type = "caldav" +url = "https://dav.frogeye.fr/caldav.php/" +username = "geoffrey" +password.fetch = ["command", "sh", "-c", "cat ~/.config/vdirsyncer/pass"] diff --git a/gitconfig b/gitconfig index 17d9812..6860844 100644 --- a/gitconfig +++ b/gitconfig @@ -8,3 +8,8 @@ default = matching [alias] git = !exec git +[filter "lfs"] + clean = git-lfs clean -- %f + smudge = git-lfs smudge -- %f + process = git-lfs filter-process + required = true diff --git a/scripts/arch-kexec b/scripts/arch-kexec index b65004e..d5bdc48 100755 --- a/scripts/arch-kexec +++ b/scripts/arch-kexec @@ -1,3 +1,3 @@ #!/usr/bin/env bash -sudo kexec -l /boot/vmlinuz-linux --initrd=/boot/initramfs-linux.img --reuse-cmdlin +sudo kexec -l /boot/vmlinuz-linux --initrd=/boot/initramfs-linux.img --reuse-cmdline sudo systemctl kexec diff --git a/scripts/install-prefs b/scripts/install-prefs index f7e5434..365eab9 100755 --- a/scripts/install-prefs +++ b/scripts/install-prefs @@ -167,6 +167,10 @@ function altInst { done } +function systemdUserUnit { + mkdir -p ~/.local/share/systemd + ln -s /usr/lib/systemd/user/vdirsyncer.timer ~/.local/share/systemd/ +} # Common CLI @@ -315,16 +319,25 @@ if [ $GUI == 1 ]; then fi fi + if [ $EXTRA == 1 ]; then # Extra dev inst cmake clang llvm npm # Extra CLI inst ffmpeg youtube-dl optipng syncthing ccache + systemdUserUnit syncthing.service if [ $ARCH == 1 ]; then inst jq altInst pdftk translate-shell git-lfs js-beautify insect visidata-git + + # Orga + # TODO For others + inst vdirsyncer khard + altInst khal todoman + systemdUserUnit vdirsyncer.service + systemdUserUnit vdirsyncer.timer else # translate-shell curl -L git.io/trans > ~/.bin/trans @@ -337,7 +350,7 @@ if [ $EXTRA == 1 ]; then if [ $ARCH == 1 ]; then inst simplescreenrecorder - altInst pacmixer xcursor-menda-git menda-themes-git menda-maia-icon-theme vimpc-git mpc + altInst pacmixer xcursor-menda-git menda-themes-git menda-maia-icon-theme vimpc-git mpc ashuffle-git fi fi diff --git a/scripts/machines b/scripts/machines index b9c7fde..95fe7e9 100755 --- a/scripts/machines +++ b/scripts/machines @@ -127,6 +127,10 @@ function _machines-getAkey { # network SIGN_FILE=$(mktemp) _machines-api akey/$1 > $KEY_FILE _machines-api akey/$1?signature > $SIGN_FILE + md5sum $KEY_FILE &> /dev/stderr + md5sum $SIGN_FILE &> /dev/stderr + md5sum $MACHINES_CONFIG/machines.pub &> /dev/stderr + openssl dgst -sha256 -verify $MACHINES_CONFIG/machines.pub -signature $SIGN_FILE $KEY_FILE &> /dev/null if [ $? == 0 ]; then cat $KEY_FILE diff --git a/scripts/rms b/scripts/rms new file mode 100755 index 0000000..885b287 --- /dev/null +++ b/scripts/rms @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +find . -name "*.sync-conflict-*" -delete diff --git a/vimrc b/vimrc index 6e10446..c709f72 100644 --- a/vimrc +++ b/vimrc @@ -48,7 +48,7 @@ nmap :UndotreeToggle:UndotreeFocus let g:ctrlp_custom_ignore = { \ 'dir': '\v([\/]\.(git|hg|svn)|log|node_modules|bower_components|__pycache__|vendor|output|buildroot|doc)$', - \ 'file': '\v\.(exe|so|dll|o|pyc)$', + \ 'file': '\v\.(exe|so|dll|o|pyc|a)$', \ 'link': 'SOME_BAD_SYMBOLIC_LINKS', \ } From 501f1f40668ebf49740de3719721ca0c5eea3ace Mon Sep 17 00:00:00 2001 From: Geoffrey Frogeye Date: Tue, 10 Apr 2018 15:38:18 +0200 Subject: [PATCH 03/22] Long time, no commit, big ol' changes though --- bashrc | 42 ++- config/dunst/dunstrc | 337 +++--------------- config/i3/batteryNotify | 55 +++ config/i3/config | 14 +- config/khal/config | 36 +- config/offlineimap.py | 10 + config/polybar/bbswitch | 12 + config/polybar/config | 47 ++- config/polybar/linuxmismatch | 17 + config/polybar/todo | 21 ++ .../user/default.target.wants/dunst.service | 1 + .../user/default.target.wants/mpd.service | 1 + .../default.target.wants/offlineimap.service | 1 + .../default.target.wants/syncthing.service | 2 +- .../user/timers.target.wants/vdirsyncer.timer | 2 +- config/todoman/todoman.conf | 4 +- scripts/install-prefs | 12 +- scripts/rep | 8 +- vimrc | 1 + 19 files changed, 271 insertions(+), 352 deletions(-) create mode 100755 config/i3/batteryNotify create mode 100644 config/offlineimap.py create mode 100755 config/polybar/bbswitch create mode 100755 config/polybar/linuxmismatch create mode 100755 config/polybar/todo create mode 120000 config/systemd/user/default.target.wants/dunst.service create mode 120000 config/systemd/user/default.target.wants/mpd.service create mode 120000 config/systemd/user/default.target.wants/offlineimap.service diff --git a/bashrc b/bashrc index d0f18f4..371fe0f 100644 --- a/bashrc +++ b/bashrc @@ -12,9 +12,9 @@ export BROWSER=qutebrowser # Some programs need those changes export PATH="/usr/lib/ccache/bin/:$PATH" -if [ -d $HOME/.gem/ruby/2.4.0/bin ]; then - export PATH="$HOME/.gem/ruby/2.4.0/bin/:$PATH" -fi +# if [ -d $HOME/.gem/ruby/2.4.0/bin ]; then +# export PATH="$HOME/.gem/ruby/2.4.0/bin/:$PATH" +# fi if [ -d /data/data/com.termux/ ]; then export PATH="$HOME/.termux/scripts:$HOME/.termux/bin:$PATH" fi @@ -63,15 +63,30 @@ alias mc="machines" alias tracefiles="strace -f -t -e trace=file" # Superseding commands with better ones if they are present -if which vim &> /dev/null; then - alias vi='vim' -fi -if which gopass &> /dev/null; then - alias pass='gopass' -fi -if which wakeonlan &> /dev/null; then - alias wol='wakeonlan' -fi +function vi() { + if which vim &> /dev/null; then + alias vi='vim' + fi + vim "$@" +} +function pass() { + if which gopass &> /dev/null; then + alias pass='gopass' + fi + gopass "$@" +} +function wol() { + if which wakeonlan &> /dev/null; then + alias wol='wakeonlan' + fi + wakeonlan "$@" +} +function mutt() { + if which neomutt &> /dev/null; then + alias mutt='neomutt' + fi + neomutt "$@" +} # SHELL CUSTOMIZATION @@ -88,7 +103,8 @@ shopt -s hostcomplete export HISTSIZE=100000 export HISTFILESIZE=${HISTSIZE} -export HISTCONTROL=ignoreboth +export HISTCONTROL=ignorespace:erasedups +export HISTTIMEFORMAT="%d/%m/%y %H:%M:%S " # PROMPT CUSTOMIZATION diff --git a/config/dunst/dunstrc b/config/dunst/dunstrc index eead4c9..63e6c2a 100644 --- a/config/dunst/dunstrc +++ b/config/dunst/dunstrc @@ -1,300 +1,63 @@ [global] - font = Cantarell 10 - - # Allow a small subset of html markup: - # bold - # italic - # strikethrough - # underline - # - # For a complete reference see - # . - # If markup is not allowed, those tags will be stripped out of the - # message. - allow_markup = yes - - # The format of the message. Possible variables are: - # %a appname - # %s summary - # %b body - # %i iconname (including its path) - # %I iconname (without its path) - # %p progress value if set ([ 0%] to [100%]) or nothing - # Markup is allowed - format = "%s %p\n%b" - - # Sort messages by urgency. - sort = no - - # Show how many messages are currently hidden (because of geometry). - indicate_hidden = yes - - # Alignment of message text. - # Possible values are "left", "center" and "right". alignment = left - - # The frequency with wich text that is longer than the notification - # window allows bounces back and forth. - # This option conflicts with "word_wrap". - # Set to 0 to disable. - bounce_freq = 5 - - - # Show age of message if message is older than show_age_threshold - # seconds. - # Set to -1 to disable. - show_age_threshold = 60 - - # Split notifications into multiple lines if they don't fit into - # geometry. - word_wrap = no - - # Ignore newlines '\n' in notifications. - ignore_newline = no - - - # The geometry of the window: - # [{width}]x{height}[+/-{x}+/-{y}] - # The geometry of the message window. - # The height is measured in number of notifications everything else - # in pixels. If the width is omitted but the height is given - # ("-geometry x2"), the message window expands over the whole screen - # (dmenu-like). If width is 0, the window expands to the longest - # message displayed. A positive x is measured from the left, a - # negative from the right side of the screen. Y is measured from - # the top and down respectevly. - # The width can be negative. In this case the actual width is the - # screen width minus the width defined in within the geometry option. - geometry = "0x0-25+25" - - # Shrink window if it's smaller than the width. Will be ignored if - # width is 0. - shrink = yes - - # The transparency of the window. Range: [0; 100]. - # This option will only work if a compositing windowmanager is - # present (e.g. xcompmgr, compiz, etc.). - transparency = 17 - - # Don't remove messages, if the user is idle (no mouse or keyboard input) - # for longer than idle_threshold seconds. - # Set to 0 to disable. - # default 120 - idle_threshold = 120 - - # Which monitor should the notifications be displayed on. - monitor = 0 - - # Display notification on focused monitor. Possible modes are: - # mouse: follow mouse pointer - # keyboard: follow window with keyboard focus - # none: don't follow anything - # - # "keyboard" needs a windowmanager that exports the - # _NET_ACTIVE_WINDOW property. - # This should be the case for almost all modern windowmanagers. - # - # If this option is set to mouse or keyboard, the monitor option - # will be ignored. - follow = none - - # Should a notification popped up from history be sticky or timeout - # as if it would normally do. - sticky_history = yes - - # Maximum amount of notifications kept in history - history_length = 20 - - # Display indicators for URLs (U) and actions (A). - show_indicators = yes - - # The height of a single line. If the height is smaller than the - # font height, it will get raised to the font height. - # This adds empty space above and under the text. - line_height = 0 - - # Draw a line of "separator_height" pixel height between two - # notifications. - # Set to 0 to disable. - separator_height = 1 - - # Padding between text and separator. - # padding = 8 - padding = 8 - - # Horizontal padding. - horizontal_padding = 10 - - # Define a color for the separator. - # possible values are: - # * auto: dunst tries to find a color fitting to the background; - # * foreground: use the same color as the foreground; - # * frame: use the same color as the frame; - # * anything else will be interpreted as a X color. - separator_color = #2ECC71 - - # Print a notification on startup. - # This is mainly for error detection, since dbus (re-)starts dunst - # automatically after a crash. - startup_notification = false - - # dmenu path. + always_run_script = true + browser = /usr/bin/qutebrowser + class = Dunst dmenu = /usr/bin/dmenu -p dunst: - - # Browser for opening urls in context menu. - browser = firefox - - # Align icons left/right/off + ellipsize = middle + follow = none + font = DejaVu Sans 10 + force_xinerama = false + format = "%s %p\n%b" + frame_color = "#A6E22E" + frame_width = 3 + geometry = "500x5-30+20" + hide_duplicate_count = false + history_length = 20 + horizontal_padding = 8 + icon_path = /usr/share/icons/gnome/256x256/status/:/usr/share/icons/gnome/256x256/devices/ icon_position = left - - # Paths to default icons. - icon_folders = /usr/share/icons/gnome/16x16/status/:/usr/share/icons/gnome/16x16/devices/ - -[frame] - width = 1 - color = "#2ECC71" - + idle_threshold = 120 + ignore_newline = no + indicate_hidden = yes + line_height = 0 + markup = full + max_icon_size = 48 + monitor = 0 + notification_height = 0 + padding = 8 + separator_color = frame + separator_height = 2 + show_age_threshold = 60 + show_indicators = yes + shrink = no + sort = yes + stack_duplicates = true + startup_notification = false + sticky_history = yes + title = Dunst + transparency = 0 + verbosity = mesg + word_wrap = yes +[experimental] + per_monitor_dpi = false [shortcuts] - - # Shortcuts are specified as [modifier+][modifier+]...key - # Available modifiers are "ctrl", "mod1" (the alt-key), "mod2", - # "mod3" and "mod4" (windows-key). - # Xev might be helpful to find names for keys. - - # Close notification. - close = mod4+n - - # Close all notifications. - # close_all = ctrl+shift+space close_all = ctrl+mod4+n - - # Redisplay last message(s). - # On the US keyboard layout "grave" is normally above TAB and left - # of "1". - history = shift+mod4+n - - # Context menu. + close = mod4+n context = mod1+mod4+n - + history = shift+mod4+n [urgency_low] - # IMPORTANT: colors have to be defined in quotation marks. - # Otherwise the "#" and following would be interpreted as a comment. - background = "#000000" - foreground = "#888888" + background = "#272822" + foreground = "#F8F8F2" + frame_color = "#A6E22E" timeout = 10 - [urgency_normal] - background = "#000000" - foreground = "#ffffff" + background = "#272822" + foreground = "#F8F8F2" + frame_color = "#F4BF75" timeout = 10 - [urgency_critical] - background = "#900000" - foreground = "#ffffff" + background = "#272822" + foreground = "#F8F8F2" + frame_color = "#F92672" timeout = 0 - - -# Every section that isn't one of the above is interpreted as a rules to -# override settings for certain messages. -# Messages can be matched by "appname", "summary", "body", "icon", "category", -# "msg_urgency" and you can override the "timeout", "urgency", "foreground", -# "background", "new_icon" and "format". -# Shell-like globbing will get expanded. -# -# SCRIPTING -# You can specify a script that gets run when the rule matches by -# setting the "script" option. -# The script will be called as follows: -# script appname summary body icon urgency -# where urgency can be "LOW", "NORMAL" or "CRITICAL". -# -# NOTE: if you don't want a notification to be displayed, set the format -# to "". -# NOTE: It might be helpful to run dunst -print in a terminal in order -# to find fitting options for rules. - -#[espeak] -# summary = "*" -# script = dunst_espeak.sh - -#[script-test] -# summary = "*script*" -# script = dunst_test.sh - -#[ignore] -# # This notification will not be displayed -# summary = "foobar" -# format = "" - -#[signed_on] -# appname = Pidgin -# summary = "*signed on*" -# urgency = low -# -#[signed_off] -# appname = Pidgin -# summary = *signed off* -# urgency = low -# -#[says] -# appname = Pidgin -# summary = *says* -# urgency = critical -# -#[twitter] -# appname = Pidgin -# summary = *twitter.com* -# urgency = normal -# -#[Claws Mail] -# appname = claws-mail -# category = email.arrived -# urgency = normal -# background = "#2F899E" -# foreground = "#FFA247" -# -#[mute.sh] -# appname = mute -# category = mute.sound -# script = mute.sh -# -#[JDownloader] -# appname = JDownloader -# category = JD -# background = "#FFA247" -# foreground = "#FFFFFF" -# -#[newsbeuter] -# summary = *Feeds* -# background = "#A8EB41" -# foreground = "#FFFFFF" -# -[irc] - appname = weechat - timeout = 0 - background = "#0033bb" - foreground = "#dddddd" -# -[weechat hl] - appname = weechat - category = weechat.HL - background = "#FF5C47" - foreground = "#FFFFFF" -# -[weechat pn] - appname = weechat - category = weechat.PM - background = "#D53B84" - foreground = "#FFFFFF" -# -#[CMUS] -# appname = CMUS -# category = cmus -# background = "#6C4AB7" -# foreground = "#FFE756" -# -# -# background = "#30AB70" -# foreground = "#F67245" -# -# vim: ft=cfg diff --git a/config/i3/batteryNotify b/config/i3/batteryNotify new file mode 100755 index 0000000..a6a1ed2 --- /dev/null +++ b/config/i3/batteryNotify @@ -0,0 +1,55 @@ +#!/usr/bin/env bash + +BATT="/sys/class/power_supply/BAT0" +LOW=10 +CRIT=3 +LASTSTATE="$HOME/.cache/batteryState" + +function setState() { # state [...notify-send arguments] + state="$1" + last="$(cat "$LASTSTATE" 2> /dev/null)" + shift + + echo "Battery state: $state" + + if [ "$state" != "$last" ] + then + notify-send "$@" + echo "$state" > "$LASTSTATE" + fi +} + +function computeState() { + acpiStatus="$(cat "$BATT/status")" + acpiCapacity="$(cat "$BATT/capacity")" + + if [ "$acpiStatus" == "Discharging" ] + then + if [ $acpiCapacity -le $CRIT ] + then + setState "CRIT" -u critical -i battery-caution "Battery level is critical" "$acpiCapacity %" + elif [ $acpiCapacity -le $LOW ] + then + setState "LOW" -u critical -i battery-low "Battery level is low" "$acpiCapacity %" + else + setState "DISCHARGING" -i battery-good "Battery is discharging" "$acpiCapacity %" + fi + elif [ "$acpiStatus" == "Charging" ] + then + setState "CHARGING" -u normal -i battery-good-charging "Battery is charging" "$acpiCapacity %" + elif [ "$acpiStatus" == "Full" ] + then + setState "FULL" -u low -i battery-full-charged "Battery is full" "$acpiCapacity %" + fi +} + +if [ "$1" == "-d" ] +then + while true + do + computeState + sleep 10 + done +else + computeState +fi diff --git a/config/i3/config b/config/i3/config index ea9bdbe..77b5749 100644 --- a/config/i3/config +++ b/config/i3/config @@ -22,8 +22,7 @@ hide_edge_borders both # Font for window titles. Will also be used by the bar unless a different font # is used in the bar {} block below. -font pango:Source Code Pro 8 -font pango:DejaVu Sans Mono 8 +font pango:DejaVu Sans 8 font pango:Sans 8 # Use Mouse+$mod to drag floating windows @@ -361,18 +360,17 @@ bindsym $mod+F5 exec --no-startup-id xautolock -enable # Autostart applications -exec --no-startup-id /usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1 # Password remembering -exec --no-startup-id gnome-keyring-daemon # Password remembering +#exec --no-startup-id /usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1 # Password remembering +#exec --no-startup-id gnome-keyring-daemon # Password remembering exec --no-startup-id numlockx on # Activate Num lock -#exec --no-startup-id nm-applet # Network manager tray icon -#exec --no-startup-id compton -b # Compositing manager exec --no-startup-id unclutter # Hide mouse cursor after some time -exec --no-startup-id dunst # Notifications +#exec --no-startup-id dunst # Notifications (handled by systemd) exec --no-startup-id keynav # Keyboard cursor controller #exec --no-startup-id $HOME/.config/i3/clipmenud # Clipboard manager -exec --no-startup-id mpd # Music Player Daemon +#exec --no-startup-id mpd # Music Player Daemon (handled by systemd) exec --no-startup-id ~/.config/i3/ashuffle # MPD Auto-refill exec --no-startup-id autorandr --change # Screen configuration and everything that depends on it +exec --no-startup-id ~/.config/i3/batteryNotify -d # Battery state notification set $ignore #ff00000 diff --git a/config/khal/config b/config/khal/config index f923a51..f32ce2b 100644 --- a/config/khal/config +++ b/config/khal/config @@ -1,24 +1,7 @@ [calendars] -[[perso]] -path = ~/.vdirsyncer/calendars/perso/ -color = light green - -[[clubs]] -path = ~/.vdirsyncer/calendars/clubs/ -color = yellow - -[[cours]] -path = ~/.vdirsyncer/calendars/cours/ -color = light blue -readonly = True - -[[polytech]] -path = ~/.vdirsyncer/calendars/polytech/ -color = dark blue - -[[bnei]] -path = ~/.vdirsyncer/calendars/bnei/ -color = light cyan +[[calendars]] +path = ~/.vdirsyncer/calendars/* +type = discover # [[birthdays]] # type=birthdays @@ -34,5 +17,18 @@ longdatetimeformat = %d/%m/%Y %H:%M local_timezone = Europe/Paris [default] +default_calendar = "Personnel" +default_command = interactive highlight_event_days = True +show_all_days = True +timedelta = 7d +[highlight_days] + +[view] +agenda_day_format = "{bold}{name}, {date-long}{reset}" +agenda_event_format = "{calendar-color}{cancelled}{start-end-time-style} {title}{repeat-symbol} | {location}{reset}" +bold_for_light_color = False +event_format = "{calendar-color}{cancelled}{start}-{end} {title}{repeat-symbol} | {location}{reset}" +event_view_always_visible = True +frame = color diff --git a/config/offlineimap.py b/config/offlineimap.py new file mode 100644 index 0000000..3d80ac9 --- /dev/null +++ b/config/offlineimap.py @@ -0,0 +1,10 @@ +#! /usr/bin/env python2 +from subprocess import check_output + + +def get_pass(account): + return check_output("pass " + account, shell=True).splitlines()[0] + +def beep(): + check_output("play -n synth sine E4 sine A5 remix 1-2 fade 0.5 1.2 0.5 2", shell=True) + diff --git a/config/polybar/bbswitch b/config/polybar/bbswitch new file mode 100755 index 0000000..03507c6 --- /dev/null +++ b/config/polybar/bbswitch @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +state="$(grep -o '\w\+$' /proc/acpi/bbswitch)" +if [ "$state" == "ON" ] +then + echo "" +elif [ "$state" == "OFF" ] +then + echo "" +else + echo "?" +fi diff --git a/config/polybar/config b/config/polybar/config index e259ae7..9106d62 100644 --- a/config/polybar/config +++ b/config/polybar/config @@ -76,7 +76,7 @@ enable-ipc = true inherit = bar/base modules-center = mpd -modules-right = vpncheck eth wlan bbswitch xbacklight volume battery date +modules-right = mail todo vpncheck eth wlan bbswitch xbacklight volume battery shortdate tray-position = right tray-padding = 2 @@ -84,7 +84,7 @@ tray-transparent = false [bar/secondary] inherit = bar/base -modules-right = cpu memory temperature vpncheck ethMore wlanMore filesystem bbswitch xbacklight volume date +modules-right = cpu memory temperature vpncheck ethMore wlanMore filesystem linuxmismatch bbswitch xbacklight volume date [module/filesystem] @@ -176,12 +176,35 @@ toggle-off-foreground = #55 [module/bbswitch] type = custom/script -exec = grep -o '\w\+$' /proc/acpi/bbswitch +exec = ~/.config/polybar/bbswitch exec-if = test -f /proc/acpi/bbswitch interval = 5 -prefix = +format-prefix =  format-foreground = ${theme.redF} +[module/todo] +type = custom/script +exec = ~/.config/polybar/todo +interval = 30 +format-prefix =  +format-foreground = ${theme.yellowF} + +[module/mail] +type = custom/script +exec = cat ~/.cache/mutt/status +interval = 1 +format-prefix =  +format-foreground = ${theme.magentaF} +; format-background = ${theme.magentaB} + +[module/linuxmismatch] +type = custom/script +exec = echo  +exec-if = ~/.config/polybar/linuxmismatch +interval = 30 +format-foreground = ${theme.yellowF} +; format-background = ${theme.yellowF} + [module/xbacklight] type = internal/xbacklight output = ${env:display:LVDS1} @@ -201,7 +224,7 @@ card = intel_backlight [module/cpu] type = internal/cpu -interval = 0.5 +interval = 1 format = format-foreground = ${theme.redF} ramp-coreload-0 = ▁ @@ -215,7 +238,7 @@ ramp-coreload-7 = █ [module/memory] type = internal/memory -interval = 2 +interval = 1 format-foreground = ${theme.greenF} label =  %gb_free% @@ -229,7 +252,7 @@ format-foreground = ${theme.blueF} [module/eth] type = internal/network interface = ${env:ethI:eth0} -interval = 1 +interval = 5 format-connected =  label-connected = %local_ip% @@ -244,7 +267,7 @@ label-connected = ↑%upspeed% ↓%downspeed% [module/wlan] type = internal/network interface = ${env:wlanI:wlan0} -interval = 1 +interval = 5 format-connected =  label-connected = %local_ip% %essid% @@ -281,6 +304,11 @@ format-foreground = ${theme.cyanF} label = %date% %time% +[module/shortdate] +inherit = module/date +date = " %d/%m" + + [module/volume] type = internal/volume @@ -307,11 +335,12 @@ label-full =  format-charging = format-charging-foreground = ${theme.yellowF} +format-charging-prefix =  label-charging = %percentage%% (%time%) format-discharging = format-discharging-foreground = ${self.format-charging-foreground} -format-discharging-background = ${theme.redB} +; format-discharging-background = ${theme.redB} label-discharging = %percentage%% (%time%) format-full-prefix = " " diff --git a/config/polybar/linuxmismatch b/config/polybar/linuxmismatch new file mode 100755 index 0000000..a36e5fe --- /dev/null +++ b/config/polybar/linuxmismatch @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +if ! which pacman &> /dev/null +then + exit 1 +fi + +packageVersion=$(pacman -Q linux | cut -d' ' -f2) +runningVersion=$(uname -r) + +if echo "$runningVersion" | grep "^$packageVersion" &> /dev/null +then + exit 1 +else + exit 0 +fi + diff --git a/config/polybar/todo b/config/polybar/todo new file mode 100755 index 0000000..e739acd --- /dev/null +++ b/config/polybar/todo @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +CALDIR="$HOME/.vdirsyncer/currentCalendars" + +function status() { + ls "$CALDIR" | while read account + do + displayname="$(cat "$CALDIR/$account/displayname")" + color="$(cat "$CALDIR/$account/color")" + nb="$(todo list "$displayname" | grep -v "^$" | wc -l)" + + if [ $nb -gt 0 ] + then + echo -n " %{F$color}$nb%{F-}" + fi + done + # Newline to tell polybar to update in any case + echo +} + +status diff --git a/config/systemd/user/default.target.wants/dunst.service b/config/systemd/user/default.target.wants/dunst.service new file mode 120000 index 0000000..b24d3b1 --- /dev/null +++ b/config/systemd/user/default.target.wants/dunst.service @@ -0,0 +1 @@ +/usr/lib/systemd/user/dunst.service \ No newline at end of file diff --git a/config/systemd/user/default.target.wants/mpd.service b/config/systemd/user/default.target.wants/mpd.service new file mode 120000 index 0000000..0524f66 --- /dev/null +++ b/config/systemd/user/default.target.wants/mpd.service @@ -0,0 +1 @@ +/usr/lib/systemd/user/mpd.service \ No newline at end of file diff --git a/config/systemd/user/default.target.wants/offlineimap.service b/config/systemd/user/default.target.wants/offlineimap.service new file mode 120000 index 0000000..aacc641 --- /dev/null +++ b/config/systemd/user/default.target.wants/offlineimap.service @@ -0,0 +1 @@ +/usr/lib/systemd/user/offlineimap.service \ No newline at end of file diff --git a/config/systemd/user/default.target.wants/syncthing.service b/config/systemd/user/default.target.wants/syncthing.service index eae00f7..d55cc27 120000 --- a/config/systemd/user/default.target.wants/syncthing.service +++ b/config/systemd/user/default.target.wants/syncthing.service @@ -1 +1 @@ -/home/geoffrey/.local/share/systemd/user/syncthing.service \ No newline at end of file +/usr/lib/systemd/user/syncthing.service \ No newline at end of file diff --git a/config/systemd/user/timers.target.wants/vdirsyncer.timer b/config/systemd/user/timers.target.wants/vdirsyncer.timer index eed4fe0..cc84adf 120000 --- a/config/systemd/user/timers.target.wants/vdirsyncer.timer +++ b/config/systemd/user/timers.target.wants/vdirsyncer.timer @@ -1 +1 @@ -/home/geoffrey/.local/share/systemd/user/vdirsyncer.timer \ No newline at end of file +/usr/lib/systemd/user/vdirsyncer.timer \ No newline at end of file diff --git a/config/todoman/todoman.conf b/config/todoman/todoman.conf index 677aa91..74a4616 100644 --- a/config/todoman/todoman.conf +++ b/config/todoman/todoman.conf @@ -1,4 +1,4 @@ [main] -path = ~/.vdirsyncer/calendars/* -default_list = perso +path = ~/.vdirsyncer/currentCalendars/* +default_list = Personnel humanize = True diff --git a/scripts/install-prefs b/scripts/install-prefs index 365eab9..e718c0a 100755 --- a/scripts/install-prefs +++ b/scripts/install-prefs @@ -168,8 +168,8 @@ function altInst { } function systemdUserUnit { - mkdir -p ~/.local/share/systemd - ln -s /usr/lib/systemd/user/vdirsyncer.timer ~/.local/share/systemd/ + systemctl enable "$1" + systemctl start "$1" } # Common CLI @@ -325,9 +325,8 @@ if [ $EXTRA == 1 ]; then inst cmake clang llvm npm # Extra CLI - inst ffmpeg youtube-dl optipng syncthing ccache - systemdUserUnit syncthing.service - + inst ffmpeg youtube-dl optipng syncthing ccache mutt + systemdUserUnit syncthing.service if [ $ARCH == 1 ]; then inst jq altInst pdftk translate-shell git-lfs js-beautify insect visidata-git @@ -335,8 +334,7 @@ if [ $EXTRA == 1 ]; then # Orga # TODO For others inst vdirsyncer khard - altInst khal todoman - systemdUserUnit vdirsyncer.service + altInst khal todoman offlineimap systemdUserUnit vdirsyncer.timer else # translate-shell diff --git a/scripts/rep b/scripts/rep index a7b2d84..789f25f 100755 --- a/scripts/rep +++ b/scripts/rep @@ -2,7 +2,7 @@ # Moves a file to another place and put a symbolic link in place -function proxy_help { +function rep_help { echo "Usage: $0 SOURCE DEST" echo echo "Arguments:" @@ -17,9 +17,9 @@ ln -s "$2" "$1" # MAIN command="$1" shift -if type "proxy_$command" &> /dev/null; then - "proxy_$command" "$@" +if type "rep_$command" &> /dev/null; then + "rep_$command" "$@" else - proxy_help + rep_help fi diff --git a/vimrc b/vimrc index c709f72..487093a 100644 --- a/vimrc +++ b/vimrc @@ -36,6 +36,7 @@ Plugin 'tomtom/tcomment_vim' Plugin 'Shougo/denite.nvim' Plugin 'tomlion/vim-solidity' Plugin 'godlygeek/tabular' +Plugin 'jrozner/vim-antlr' call vundle#end() " required filetype plugin indent on " required From 7fceb52f1dd8482b8d5fe942c5662493797cad18 Mon Sep 17 00:00:00 2001 From: Geoffrey Frogeye Date: Sun, 15 Apr 2018 16:29:27 +0200 Subject: [PATCH 04/22] urxvt <3 --- Xresources | 1 + Xresources.d/urxvt | 56 +++++++++++++++++++++++++++++++++ bashrc | 1 + config/compton.conf | 53 ------------------------------- config/i3/config | 7 +++-- scripts/install-prefs | 2 +- scripts/pdfpages | 4 +++ terminfo/rxvt-unicode | Bin 0 -> 2213 bytes terminfo/rxvt-unicode-256color | Bin 0 -> 2239 bytes 9 files changed, 67 insertions(+), 57 deletions(-) create mode 100644 Xresources.d/urxvt delete mode 100644 config/compton.conf create mode 100755 scripts/pdfpages create mode 100644 terminfo/rxvt-unicode create mode 100644 terminfo/rxvt-unicode-256color diff --git a/Xresources b/Xresources index ebdba57..6a06d00 100644 --- a/Xresources +++ b/Xresources @@ -1,3 +1,4 @@ #include ".Xresources.d/xft" #include ".Xresources.d/theme" #include ".Xresources.d/xterm" +#include ".Xresources.d/urxvt" diff --git a/Xresources.d/urxvt b/Xresources.d/urxvt new file mode 100644 index 0000000..53f39b1 --- /dev/null +++ b/Xresources.d/urxvt @@ -0,0 +1,56 @@ +! Scrollback position +! TODO Do not work + +! do not scroll with output +URxvt*scrollTtyOutput: false + +! scroll in relation to buffer (with mouse scroll or Shift+Page Up) +URxvt*scrollWithBuffer: true + +! scroll back to the bottom on keypress +URxvt*scrollTtyKeypress: true + + +! Scrollback buffer in secondary screen +! TODO Do not work +URxvt.secondaryScreen: 1 +URxvt.secondaryScroll: 0 + + +! Font declaration +URxvt.font: xft:DejaVu Sans Mono for Powerline:size=12:antialias=true + +! Font spacing +URxvt.letterSpace: 0 + +! No scroll bar +URxvt*scrollBar: false + +! Disable Ctrl+Shift default bindings +URxvt.iso14755: false +URxvt.iso14755_52: false + +! Copy/Paste CLIPBOARD selection with Ctrl+Shift+C/V +URxvt.keysym.C-S-C: eval:selection_to_clipboard +URxvt.keysym.C-S-V: eval:paste_clipboard + + +! Extensions +URxvt.perl-ext-common: default,matcher,resize-font + +! Clickable URL (extension: matcher) +URxvt.url-launcher: /usr/bin/xdg-open +URxvt.matcher.button: 1 + +! Changing font size on the fly (extension: resize-font, package: urxvt-resize-font-git) +URxvt.keysym.C-KP_Subtract: resize-font:smaller +URxvt.keysym.C-KP_Add: resize-font:bigger + +! Fake transparency (without compositing manager) +urxvt*transparent: true +urxvt*shading: 30 + +! True transparency (with compositing manager) +!urxvt*depth: 32 +!urxvt*background: rgba:2700/2800/2200/c800 + diff --git a/bashrc b/bashrc index 371fe0f..37a045a 100644 --- a/bashrc +++ b/bashrc @@ -119,6 +119,7 @@ export PS1="\[\e]2;\u@\h \w\a\]\[\e[0;37m\][\[\e[0;${col}m\]\u\[\e[0;37m\]@\[\e[ export PS2="> " export PS3="+ " export PS4="- " +export PROMPT_COMMAND='echo -ne "\033]0;${USER}@${HOSTNAME}:${PWD}\007"' # CUSTOM SCRIPTS diff --git a/config/compton.conf b/config/compton.conf deleted file mode 100644 index 34eb7df..0000000 --- a/config/compton.conf +++ /dev/null @@ -1,53 +0,0 @@ -shadow = false; -no-dnd-shadow = true; -no-dock-shadow = true; -clear-shadow = true; - -menu-opacity = 0.9; -inactive-opacity = 0.93; -active-opacity = 1; -alpha-step = 0.01; -inactive-dim = 0.0; -blur-background = false; -blur-kern = "3x3box"; - -fading = false; -fade-delta = 1; -fade-in-step = 0.03; -fade-out-step = 0.03; -fade-exclude = [ ]; - -backend = "xrender"; -mark-wmwin-focused = true; -mark-ovredir-focused = true; -detect-rounded-corners = true; -detect-client-opacity = true; -unredir-if-possible = true; -refresh-rate = 0; -vsync = "none"; -dbe = false; -paint-on-overlay = true; -focus-exclude = [ "class_g = 'Cairo-clock'" ]; -detect-transient = true; -detect-client-leader = true; -invert-color-include = [ ]; -glx-copy-from-front = false; -glx-swap-method = "undefined"; - -wintypes : -{ - tooltip : - { - fade = true; - shadow = false; - opacity = 0.75; - focus = true; - }; - fullscreen : - { - fade = true; - shadow = false; - opacity = 1; - focus = true; - }; -}; diff --git a/config/i3/config b/config/i3/config index 77b5749..19298d7 100644 --- a/config/i3/config +++ b/config/i3/config @@ -46,7 +46,7 @@ bindsym $mod+dollar exec --no-startup-id ~/.config/i3/sshmenu root bindsym $mod+d exec --no-startup-id ~/.config/i3/dmenu_run # Start Applications -bindsym $mod+Return exec xterm +bindsym $mod+Return exec urxvtc bindsym $mod+p exec thunar bindsym $mod+m exec qutebrowser --override-restore --backend=webengine @@ -59,8 +59,8 @@ bindsym $mod+F7 exec pactl suspend-sink @DEFAULT_SINK@ 1; exec pactl suspend-sin bindsym $mod+F8 exec mpc prev bindsym $mod+F9 exec mpc toggle bindsym $mod+F10 exec mpc next -bindsym $mod+F11 exec xterm -e 'pacmixer' -bindsym $mod+F12 exec xterm -e 'pacmixer' +bindsym $mod+F11 exec urxvtc -e 'pacmixer' +bindsym $mod+F12 exec urxvtc -e 'pacmixer' #Brightness control bindsym XF86MonBrightnessDown exec xbacklight -dec 20 @@ -362,6 +362,7 @@ bindsym $mod+F5 exec --no-startup-id xautolock -enable # Autostart applications #exec --no-startup-id /usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1 # Password remembering #exec --no-startup-id gnome-keyring-daemon # Password remembering +exec --no-startup-id urxvtd -q -f # urxvt daemon exec --no-startup-id numlockx on # Activate Num lock exec --no-startup-id unclutter # Hide mouse cursor after some time #exec --no-startup-id dunst # Notifications (handled by systemd) diff --git a/scripts/install-prefs b/scripts/install-prefs index e718c0a..314a063 100755 --- a/scripts/install-prefs +++ b/scripts/install-prefs @@ -252,7 +252,7 @@ fi if [ $GUI == 1 ]; then # Desktop manager - inst i3 i3lock dunst unclutter xautolock feh numlockx scrot xterm xclip + inst i3 i3lock dunst unclutter xautolock feh numlockx scrot rxvt-unicode xclip curl "https://raw.githubusercontent.com/FortAwesome/Font-Awesome/a8386aae19e200ddb0f6845b5feeee5eb7013687/fonts/fontawesome-webfont.ttf" > ~/.local/share/fonts/fontawesome-webfont.ttf if [ $ARCH == 1 ]; then inst xorg-xinit diff --git a/scripts/pdfpages b/scripts/pdfpages new file mode 100755 index 0000000..e805435 --- /dev/null +++ b/scripts/pdfpages @@ -0,0 +1,4 @@ +#!/usr/bin/bash + +# From https://stackoverflow.com/a/14736593 +pdftk "$1" dump_data | grep NumberOfPages | awk '{print $2}' diff --git a/terminfo/rxvt-unicode b/terminfo/rxvt-unicode new file mode 100644 index 0000000000000000000000000000000000000000..7650d3cfe25183d4d8a251406a8fdee3b5f3f96d GIT binary patch literal 2213 zcmb_cO=w(I6h8O8j8#iP5m7s&rrKNEv`yyy&5zIgwoOA!ZD}kHO;eh5@-ykoWHOoj z&qV2>f_6~^!3uVv2-#F9qG)m9-@--R2!g9_-T8wcbRp00oHt{WCPgZ~;e7Y|&OPVe z`|de!wkwYs>1isey^+$wT=~FkA$x8rGg?_In#!Z4e6}#0Yub};dL>)POwBh9EY6fi z^Lt4t`p1M2G=2=~I6X*D!VzqE($u+QGyk2y2(pL_YM{qyJ3T|YsTm%RdZ?HBX#o5r zjR41JlFGC|74Qpmk=~@sU_o$2@G5;kt8|S%r5p4WJm1p~^b_3z`;~sDKP9F`*(wO) zFn*F4+hARydsM$Vq0;J%%BV?IRCB7LE~wWrcL{b`y{+DrxFEQy-d7*ic(~F=TvgZA z4fTz>seV+q)X(ZSb-R{f7!Md*jUaf$cnlZ>M~pt>q>(mG8AHZL@LV@O7Jm?YW_)3M zX?$yZC;BFEo5>lEnor2sD9E1rv%`EAzUMah>+_qB`w?%Mamn(5o#tV)Pt;4m0bxOq zu8q9LX>-J!FbihcTryuXFPfLkx7N&VzRQ2}zH^`JzGtqQ*UV4M&yn}F$Cn{-L9?iSHspw82wbFF|sqFBU@-2MF(|O7cHIfbFc^wPs{^};%AFNeV8>e95rng0yTP4B z0Fv_jdT=NBP`rlk_^$8yaX;n93BjabiqVcSid%9HLB-i6@wIzvr)V2o^6GqXLXOr6pFli(G9d$X(qAXM ztW5EWLSC*Du1gl?GI)S<7{klCuZu|{t=1nIiYB-tH-7Iq_g-_+)5Mjq^m3xA+fk1}CyCEQ_ROO)gHCPQs_HgfcJ-(P zJEGz><%+UuDlWG5*#l?Ag`q5rT-7yi0<~t(@Gpk{nws&2?dzt$ E0c9C}>i_@% literal 0 HcmV?d00001 diff --git a/terminfo/rxvt-unicode-256color b/terminfo/rxvt-unicode-256color new file mode 100644 index 0000000000000000000000000000000000000000..3f43d0d567f4bc450e2354e00fde5760e82074a3 GIT binary patch literal 2239 zcmb_cO=w(I6h8O8j8#iPDWZ0WPPMl`X_L&m?`LLwCex&C8e;lGV{vGj(j+rUCY`^` zy!_9k(nSUBq6mTtcA*H_R4AfoapB*>McoL3t8U%-gCKMv&vVXuW0NLDD!$=-_dDM` z=idA7Id67|OV1v=E!mds8ut ziE&UZwl@=dC11*x=VL>QH7{4(2SUI<#stvEAAx|A@Gv}uiof9j;a}WLzZ)3AEC3B! z;0f3P&%thJL&b(Z=!ZcVLVg;?5GP<7JXnA_@=I_T-hivfeE%KBYw!WA!gcr*Zo*fn zd=EdsPjDOAukbtk$uS*53Ey|n=t<((h1SDCGi^0T|v7l z-V*O}obO)~?~4yZ9;~zxSH%r+Q+y+Ci66yn@w50%+zB%@?ICTe=KGIn+Yx>Ls5YRT z)-u`|ZAAMBl^fc}to#0F+85fF+PB(w?B7D%rc=h_`jb4iGLq7Kp3$F2ZO;b1IluXM z0OJka;Vc*NpngOjVDC8M5HsJ;gd?qSRv*)+^pfuBOZuz&W&Mi&=9<~fclmGX+Yh+z zd-|$=UH?S?9P_^B+}$m&x2)g$mwSAg{bLhteb&EwcfvLPYS|hIqMwemMs`MIWD9JA z=&;PoiXpSEtSFhSqF_!U^hbx)0FE!A(HKS@y-S0Hes~n6ezdf*3;4MqF;s!wDRd5@ z^#r1;Y*J359fRnIa<@QqZyY_lh+q(;jkuMfAd4VD5`#sIkD0-oUUZ7VPz`emC|LxG z;}s{Q?WtWas(5EN2I1LoyWyP`1WYQ@>!F;WL(v+p=~}MsI&R8!9M_1);{*nRmBP2- zwGU$MAY4Q=R}knJ$#V#XL9Svn6mf$C42!wVm|#pYrU+G>kY&y}jGjXh$A|aU4bd)? z_|@s+m>g>oIsq$mGA2V!GT0=vEKkvjOkQphs!JZtBCrw0aSSf!fi5PAX<>h4A)26$ z;3KAG#gU$CNLM0NNUeYxvSP|Q`ia1|klF!FAWcfQNEs>VVS~sVmRhxKJn*i=|SzT&c{=RI9a`=grQ}&CSm*EG#ZAEiEtC z>y5_BN_$U7@BTvv4#$s7_2sFr&!D9f)OhJc0dSyVouD`2%9^{7aYd8F41xrKBFLgS zL~H`ONX0#%I74ww6_;0W4=C;dGgz7HZ*sgD^bCD=c%}GNW=|n?N(k7biWVj_Q?8Z5 zk7C4fxu$M)a_j}V<7LOTO}io6X8T`f2UsD@?Vf%aGZv^{@0RR91 literal 0 HcmV?d00001 From df61fbfd3a9ff0fd507f82c0bdbe0ab4851e2136 Mon Sep 17 00:00:00 2001 From: Geoffrey Frogeye Date: Sun, 15 Apr 2018 16:31:27 +0200 Subject: [PATCH 05/22] r --- terminfo/{ => r}/rxvt-unicode | Bin terminfo/{ => r}/rxvt-unicode-256color | Bin 2 files changed, 0 insertions(+), 0 deletions(-) rename terminfo/{ => r}/rxvt-unicode (100%) rename terminfo/{ => r}/rxvt-unicode-256color (100%) diff --git a/terminfo/rxvt-unicode b/terminfo/r/rxvt-unicode similarity index 100% rename from terminfo/rxvt-unicode rename to terminfo/r/rxvt-unicode diff --git a/terminfo/rxvt-unicode-256color b/terminfo/r/rxvt-unicode-256color similarity index 100% rename from terminfo/rxvt-unicode-256color rename to terminfo/r/rxvt-unicode-256color From 4a7b5985a61081fd973e91e22828e1ae5eb5c843 Mon Sep 17 00:00:00 2001 From: Geoffrey Frogeye Date: Sun, 15 Apr 2018 18:28:26 +0200 Subject: [PATCH 06/22] Just in case --- terminfo/a/alacritty | Bin 0 -> 2376 bytes terminfo/a/alacritty-256color | Bin 0 -> 2402 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 terminfo/a/alacritty create mode 100644 terminfo/a/alacritty-256color diff --git a/terminfo/a/alacritty b/terminfo/a/alacritty new file mode 100644 index 0000000000000000000000000000000000000000..d02394cd0f368493f0388e36e8b1105b1a78fe64 GIT binary patch literal 2376 zcma)6U1(fI6rQ;=Ym8zQrC_+WR_xePwYhih+}*vsF{@3hF-_QLw8m}&NwfLcWV78Q z+a~E|qo5*1P{aoj9~4Cd`%sXIRS{OopTE7?ZwBn7&f7%7g7&J`+^rR5z>N(j~e2tag3g8!8E1HBdEUWoc8|Ev>K zaB>S&w~_7SA+m?;1&4_AkY3VHUI6|g8HYSYN@S5NYxkGQtK>9!6ZnVZ6Y?40S#pk? zCl|;i@&ow^+E>U`a*h10N7w5u)GVnDaGT}#1MalEUf)e0p^_%)KH5nS&>q@P57VP` z82ZN`3baJ$=>lB={u(_^-)`2e|J3*BN4n<>Jx9N#7wKjCEB%B1MX%FUTaj>Doh{Bi z&MwDuuy((5(CK#uoadcE=L=B2(rUx?>t^c$uY!}+JFHpUrbtc9&tHq*LY*WBH# zY-2mvF5TJ-Ikb95v%g;NVo$RyJIsbyo=vlPR%NfSH`qJuefBXs!#-!{+4t-cyUc!J zzq3EtHFks5SqtC7xAOb=gZyD0@R)b;Lwtbe_$Z&^vwV@i#9!xc@pt(L{8N6Gf62e& z7x|C;XTF&)SR3;jzrwHbRZbGEiMtYO`pvAgfj+;1c5}^U_>%RgCB8|xxg(^7+(F!& zD2TEz^06pOk)H!0n1Iyn=421xAqYe}>Zvtq3cAT{Aaz4zWLShJ0>qa^-JFC*7sL}r zjAe*QKSUpKcYA>03?v^@#`jb-36sj$i@gdc5@m%IX=JHpQpoh02f=1PhMPl8d%)B( zXn3e1$WoU|U6u(acXs%m*WL-kVZCndfikclR!|4g2}-a2ieQDfSuoGydZmx8XCR)1 z7y!i&LCN_kozpr;n573f+l>s#0U$}#ad;Lt!;dAnBEYs}&DNNCGybIe7<9pB1bX3` z;0EZA#dT=X*C6PlmB$+dSEiNT2Eher1?Se}xdy?x3lIuZSRmLtii4!6`Ze*W$R8u_ zs5=BF(*h&@vr%af?X}1gH-#6WkBE#YN1_79E5_hpMC|4~)RLkS`yxgf0+ohJpvq8R zlmoF0?UZRNprN5DplK1q5qFbT*tCOWcz9%Fe0*Y}P?(yUo-P(=W=f^w$IIo}*}1v- z`AX%)iIXQ678VzmmQJ0jR+pD+wG}*sBM_Ms?r0_jXP~g*KNTBHTZ{~Xx7NYiU|>-h^y9#I z2gW-v-huVjy9eF|(-tFx;B9snOzfT*RCZjfjJGo0N_*qa72HYqHOpf=S%Q=ix!Ch% zpu%J-9Sx0wgb_rsir`M!`#q#${XF8iiDUcp8<1gG6MNt-c%D}i?LzLAgY9@rtN+uZ zHtunc;%qPz+!xFp5cm#!QBBuGho}U4Vlk#AKdH5-(GSm1j0JG0W_kJ6Sg IdbO_pUk}w;Gynhq literal 0 HcmV?d00001 diff --git a/terminfo/a/alacritty-256color b/terminfo/a/alacritty-256color new file mode 100644 index 0000000000000000000000000000000000000000..a76418fddfc1a3f7ee6bbfa1cf648641e2730c33 GIT binary patch literal 2402 zcma)6U1(fI6rQ;=Yph}wMKD}jBX(@5Ho151y}NsRV^*71W16thXpP+llJ4eblg(zc zyKRzgHVP_I1Vwxh@j+2Uunz^PXho|}?Sn``1ocUTg4CBj6zNN3J!fWi(_n;zob!G2 z&75!M&d=_&I>`=lC&|;b*mz+)Th3Rj%ll*y&(0U-%PSo%tz$7?o$i2+4%Jg3gsOi8 zASxrle`pN>zZK#>h{hKGj1^RHatlVzB^y}Q-lX!NqDS(+VTqb$c}S%uZuE9?#S z4tt+{%+9dS*?IOoyTmTDU)b;LPj-#nU=7yBxAS}X{rn;R2={ozd--8L%riX8XZQkN z;xF;n`CI&5{sI4#pXFcj@AyUjBmbFizX(M+KJ0tR<b#?Ab3-d`s|GC2RsacXhuD?PIW;axecT~ zh?EQp@I-*PvY?vN(CCGDLW_|MP#J<4B=#N$FqnhnVoJG=A5O!hGIAoP3W`M8$BHDf zL@W8o)S5@Y<^YDBK}~tU)Y5Bu_yv%qDwV1%6-@8yb{(gy2ZqCX?aYHEU|!@y9YhZ( z1L`ZhRbr>XJdNv>F1DV5cot$96gL1R<0e#2svKgL4&-znG9(9q#8JoLY1|AqlHiH} z+mhB>qvuVz)AnP~1)t%mg)4#^pgtDYp~zs9ppQ}>ZxURYQU;m?7oZfJTao9Q1n15} z_?Ut`!QLzmlA`9;#XgZcPVB5b3MbPBBmT2dNf6Do$Pqh%7h#ZylqiLw3dbuZ;9x{# zXB^bxq8hm(LK*;-no6L)rmiS?Vg=d>-S&Y7nkIlIMFdCON4CMH9U^06}DQ?=U4O1-{{hj0`kmB1ZMCE)DS zPeR~sdJ!HLo(U)j>ik-wIz-(HS|aYJ#7mNqzA45Nf?~KP2yjyn^kp$+34=+4p+@N# zgz?JJEJ*rhToh>UKzj$;J22knDn=TU21AYDt#s)c^bGnM-6*cT%x2L^5UWDwNF zUU&q9z5`d(l6BE7s-BuyOeo2XD=lpH b!!r~U9vrG(o_Yow%l%EtU1vkRzoGtLgl%Dg literal 0 HcmV?d00001 From 4424def8057bb384991f1c83558ce4611f49471f Mon Sep 17 00:00:00 2001 From: Geoffrey Frogeye Date: Sat, 19 May 2018 13:48:47 +0200 Subject: [PATCH 07/22] Ol' termux fixes --- termux/scripts/tsu | 6 ++++++ termux/services/syncthing | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100755 termux/scripts/tsu diff --git a/termux/scripts/tsu b/termux/scripts/tsu new file mode 100755 index 0000000..e4f6ab7 --- /dev/null +++ b/termux/scripts/tsu @@ -0,0 +1,6 @@ +#!/data/data/com.termux/files/usr/bin/env bash + +# Force the passing of the environment variables on LineageOS where the --preserve-environment +# option on the su binary doesn't seem to work well + +/data/data/com.termux/files/usr/bin/tsu -s "$(env | sed "s/^\([^=]\+\)=\(.*\)/\1='\2'/" | tr '\n' ' ') $(which bash)" diff --git a/termux/services/syncthing b/termux/services/syncthing index 70bbaca..dc5cbc8 100755 --- a/termux/services/syncthing +++ b/termux/services/syncthing @@ -8,7 +8,7 @@ LOGFILE="$HOME/.local/log/syncthing.log" start() { printf "Starting Syncthing: " - sudo start-stop-daemon -p "$PIDFILE" -x syncthing -S -b -N 5 -m -- -logfile="$LOGFILE" + sudo start-stop-daemon -p "$PIDFILE" -x syncthing -S -b -N 5 -m -- -logfile="$LOGFILE" -home ~/.config/syncthing echo "OK" } From 57f9edbf567e06173be6cc9e037301d37bc95650 Mon Sep 17 00:00:00 2001 From: Geoffrey Frogeye Date: Mon, 21 May 2018 10:57:19 +0200 Subject: [PATCH 08/22] Hiiiii --- Xresources.d/urxvt | 19 +++++++------- bashrc | 1 + config/i3/config | 8 +++--- config/i3/multimediaKey | 26 +++++++++++++++++++ config/khal/config | 2 +- .../user/default.target.wants/mbsync.timer | 1 + gdbinit | 3 +++ scripts/md2html | 3 ++- 8 files changed, 47 insertions(+), 16 deletions(-) create mode 100755 config/i3/multimediaKey create mode 120000 config/systemd/user/default.target.wants/mbsync.timer create mode 100644 gdbinit diff --git a/Xresources.d/urxvt b/Xresources.d/urxvt index 53f39b1..08bf87e 100644 --- a/Xresources.d/urxvt +++ b/Xresources.d/urxvt @@ -1,5 +1,5 @@ ! Scrollback position -! TODO Do not work +! TODO Does not work ! do not scroll with output URxvt*scrollTtyOutput: false @@ -12,20 +12,21 @@ URxvt*scrollTtyKeypress: true ! Scrollback buffer in secondary screen -! TODO Do not work +! TODO Does not work URxvt.secondaryScreen: 1 URxvt.secondaryScroll: 0 +! No scroll bar +URxvt*scrollBar: false + + ! Font declaration URxvt.font: xft:DejaVu Sans Mono for Powerline:size=12:antialias=true ! Font spacing URxvt.letterSpace: 0 -! No scroll bar -URxvt*scrollBar: false - ! Disable Ctrl+Shift default bindings URxvt.iso14755: false URxvt.iso14755_52: false @@ -36,11 +37,7 @@ URxvt.keysym.C-S-V: eval:paste_clipboard ! Extensions -URxvt.perl-ext-common: default,matcher,resize-font - -! Clickable URL (extension: matcher) -URxvt.url-launcher: /usr/bin/xdg-open -URxvt.matcher.button: 1 +URxvt.perl-ext-common: resize-font,bell-command,readline,selection ! Changing font size on the fly (extension: resize-font, package: urxvt-resize-font-git) URxvt.keysym.C-KP_Subtract: resize-font:smaller @@ -54,3 +51,5 @@ urxvt*shading: 30 !urxvt*depth: 32 !urxvt*background: rgba:2700/2800/2200/c800 +! Bell command (extension: bell-command) +URxvt.bell-command: play -n synth sine C5 sine E4 remix 1-2 fade 0.1 0.2 0.1 &> /dev/null diff --git a/bashrc b/bashrc index 37a045a..47fa3ca 100644 --- a/bashrc +++ b/bashrc @@ -61,6 +61,7 @@ alias po='eval $(proxy off)' alias nw="sudo systemctl restart NetworkManager" alias mc="machines" alias tracefiles="strace -f -t -e trace=file" +alias n='urxvtc &' # Superseding commands with better ones if they are present function vi() { diff --git a/config/i3/config b/config/i3/config index 19298d7..94f97fa 100644 --- a/config/i3/config +++ b/config/i3/config @@ -56,9 +56,9 @@ bindsym XF86AudioLowerVolume exec pactl set-sink-mute @DEFAULT_SINK@ false; exec bindsym XF86AudioMute exec pactl set-sink-mute @DEFAULT_SINK@ true bindsym $mod+F7 exec pactl suspend-sink @DEFAULT_SINK@ 1; exec pactl suspend-sink @DEFAULT_SINK@ 0 # Re-synchronize bluetooth headset -bindsym $mod+F8 exec mpc prev -bindsym $mod+F9 exec mpc toggle -bindsym $mod+F10 exec mpc next +bindsym XF86AudioPrev exec mpc prev +bindsym XF86AudioPlay exec mpc toggle +bindsym XF86AudioNext exec mpc next bindsym $mod+F11 exec urxvtc -e 'pacmixer' bindsym $mod+F12 exec urxvtc -e 'pacmixer' @@ -364,7 +364,7 @@ bindsym $mod+F5 exec --no-startup-id xautolock -enable #exec --no-startup-id gnome-keyring-daemon # Password remembering exec --no-startup-id urxvtd -q -f # urxvt daemon exec --no-startup-id numlockx on # Activate Num lock -exec --no-startup-id unclutter # Hide mouse cursor after some time +exec --no-startup-id unclutter -root # Hide mouse cursor after some time #exec --no-startup-id dunst # Notifications (handled by systemd) exec --no-startup-id keynav # Keyboard cursor controller #exec --no-startup-id $HOME/.config/i3/clipmenud # Clipboard manager diff --git a/config/i3/multimediaKey b/config/i3/multimediaKey new file mode 100755 index 0000000..3a4a808 --- /dev/null +++ b/config/i3/multimediaKey @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +# Send a key event to the current multimedia application or to MPD + +# Currently since I did not found a way to test if a keystroke +# is grabbed by a windows or not, we test if MPD is playing + +echo 8 "$1" "$2" "$(xdotool getactivewindow)" >> /tmp/dbg + +if [ $# != 2 ]; then + echo "Usage: $0 KEY MPC_COMMAND" + exit 1 +fi + +if [ $(mpc status | wc -l) -ne 1 ]; then + # If mpd is running + mpc $2 +else + # If mpd is not running + # echo "$1" "$2" "$(xdotool getactivewindow)" >> /tmp/dbg + xdotool key --window $(xdotool getactivewindow) $1 + echo xdotool key --window $(xdotool getactivewindow) $1 >> /tmp/dbg +fi +exit 0 + + diff --git a/config/khal/config b/config/khal/config index f32ce2b..902d4fd 100644 --- a/config/khal/config +++ b/config/khal/config @@ -18,7 +18,7 @@ local_timezone = Europe/Paris [default] default_calendar = "Personnel" -default_command = interactive +default_command = calendar highlight_event_days = True show_all_days = True timedelta = 7d diff --git a/config/systemd/user/default.target.wants/mbsync.timer b/config/systemd/user/default.target.wants/mbsync.timer new file mode 120000 index 0000000..ebd66d7 --- /dev/null +++ b/config/systemd/user/default.target.wants/mbsync.timer @@ -0,0 +1 @@ +/home/geoffrey/.local/share/systemd/user/mbsync.timer \ No newline at end of file diff --git a/gdbinit b/gdbinit new file mode 100644 index 0000000..9569b3d --- /dev/null +++ b/gdbinit @@ -0,0 +1,3 @@ +define hook-quit + set confirm off +end diff --git a/scripts/md2html b/scripts/md2html index 0e79ee1..9564de2 100755 --- a/scripts/md2html +++ b/scripts/md2html @@ -113,7 +113,8 @@ if (latex) { // Conversion htmlString = marked(markdownString, { - renderer: renderer + renderer: renderer, + breaks: true }); // fullHtmlString = htmlString; fullHtmlString = template.replace('%BODY%', () => { return htmlString }); From 81a0bc59c530b09cb5090e760b026b6431564387 Mon Sep 17 00:00:00 2001 From: Geoffrey Frogeye Date: Sun, 24 Jun 2018 18:27:20 +0200 Subject: [PATCH 09/22] vim --recycle --add-fuzzy-matching; bash --add-fuzzy-matching --- bashrc | 11 +++- scripts/install-prefs | 32 +++------- scripts/machines | 20 +++--- vimrc | 143 ++++++++++++++++++++++++------------------ 4 files changed, 107 insertions(+), 99 deletions(-) diff --git a/bashrc b/bashrc index 47fa3ca..45e1ba1 100644 --- a/bashrc +++ b/bashrc @@ -42,8 +42,6 @@ alias dd='dd status=progress' alias rm='rm -Iv --one-file-system' alias free='free -m' alias df='df -h' -alias pacman='pacman --color auto' -alias pacaur='pacaur --color auto' alias dmesg='dmesg --ctime' # Frequent mistakes @@ -105,7 +103,7 @@ shopt -s hostcomplete export HISTSIZE=100000 export HISTFILESIZE=${HISTSIZE} export HISTCONTROL=ignorespace:erasedups -export HISTTIMEFORMAT="%d/%m/%y %H:%M:%S " +export HISTTIMEFORMAT="%y-%m-%d %H:%M:%S " # PROMPT CUSTOMIZATION @@ -132,6 +130,13 @@ export PATH="$HOME/.bin/:$HOME/.scripts/:$PATH" # Bash completion [ -f /etc/bash_completion ] && . /etc/bash_completion +# Fuzzy matching all the way +export FZF_DEFAULT_OPTS="--height 100% --layout=default" +export FZF_CTRL_T_OPTS="--preview '[[ -d {} ]] && ls -l --color=always {} || [[ \$(file --mime {}) =~ binary ]] && echo {} is a binary file || (highlight -O ansi -l {} || coderay {} || rougify {} || cat {}) 2> /dev/null | head -500'" +export FZF_COMPLETION_OPTS="${FZF_CTRL_T_OPTS}" +[ -f /usr/share/fzf/completion.bash ] && source /usr/share/fzf/completion.bash +[ -f /usr/share/fzf/key-bindings.bash ] && source /usr/share/fzf/key-bindings.bash + # Bad day mood-saver function fuck { if which thefuck &> /dev/null diff --git a/scripts/install-prefs b/scripts/install-prefs index 314a063..dac24f8 100755 --- a/scripts/install-prefs +++ b/scripts/install-prefs @@ -174,7 +174,6 @@ function systemdUserUnit { # Common CLI -.Xresources.d/configure # Utils inst coreutils man openssl-tool grep sed sh tar @@ -184,9 +183,10 @@ if [ $TERMUX == 1 ]; then inst tsu fi fi -inst moreutils screen ncdu lsof htop proxytunnel pv curl wget sshfs netcat mosh bash-completion rsync pwgen +inst moreutils screen ncdu lsof htop proxytunnel pv curl wget netcat mosh bash-completion rsync pwgen fzf highlight +# TODO Test those who are on Debian machines and those who aren't if [ $ARCH == 1 ]; then - inst bash-completion + inst bash-completion tldr altInst gopass else inst pass @@ -226,31 +226,12 @@ if [ $DEBIAN == 1 ]; then else inst ctags fi -git clone https://github.com/VundleVim/Vundle.vim.git ~/.vim/bundle/Vundle.vim -vim +PluginInstall +qall - -# YouCompleteMe (vim plugin) -if [ $ARCH == 1 ] && [ $ADMIN == 1 ]; then - if [ $EXTRA == 1 ]; then - altInst vim-youcompleteme-git - else - altInst vim-youcompleteme-core-git - fi -else - if [ $DEBIAN == 1 || $TERMUX == 1 ]; then - inst python-dev python3-dev cmake - fi - YCM_ARGS="" - if [ $TERMUX != 1 ]; then - YCM_ARGS="$YCM_ARGS --clang-completer --tern-completer" - fi - - python $HOME/.vim/bundle/YouCompleteMe/install.py $YCM_ARGS -fi +vim +PlugUpgrade +PlugUpdate +PlugInstall +qall # Common GUI - if [ $GUI == 1 ]; then + .Xresources.d/configure + # Desktop manager inst i3 i3lock dunst unclutter xautolock feh numlockx scrot rxvt-unicode xclip curl "https://raw.githubusercontent.com/FortAwesome/Font-Awesome/a8386aae19e200ddb0f6845b5feeee5eb7013687/fonts/fontawesome-webfont.ttf" > ~/.local/share/fonts/fontawesome-webfont.ttf @@ -323,6 +304,7 @@ fi if [ $EXTRA == 1 ]; then # Extra dev inst cmake clang llvm npm + inst python-rope # Extra CLI inst ffmpeg youtube-dl optipng syncthing ccache mutt diff --git a/scripts/machines b/scripts/machines index 95fe7e9..a99aca5 100755 --- a/scripts/machines +++ b/scripts/machines @@ -52,7 +52,7 @@ function _machines-api { wget $MACHINES_API/$route --content-on-error --quiet --output-document=- "$@" result=$? if [ $result != 0 ]; then - echo "[ERROR] wget returned $result..." > /dev/stderr + echo "[ERROR] wget returned $result..." 1>&2; exit 2 fi } @@ -77,7 +77,7 @@ function _machines-pubFromCrt { function _machines-verifyCertificate { return if openssl verify $MACHINES_CONFIG/machines.crt | grep -v 'error 18' | grep 'error' --quiet; then - echo "[ERROR] Invalid certificate" > /dev/stderr + echo "[ERROR] Invalid certificate" 1>&2; exit 1 fi } @@ -92,7 +92,7 @@ function _machines-ensurePub { if [ $? == 1 ]; then mv $CERT_FILE $MACHINES_CONFIG/machines.crt &> /dev/null else - echo "[ERROR] Certificate rejected." > /dev/stderr + echo "[ERROR] Certificate rejected." 1>&2; exit 1 fi fi @@ -105,7 +105,7 @@ function _machines-ensurePub { function _machines-ensureAdmin { if [ ! -f $MACHINES_CONFIG/machines.key ]; then - echo "[ERROR] You need have to have the private key to do that" > /dev/stderr + echo "[ERROR] You need have to have the private key to do that" 1>&2; exit 1 fi } @@ -127,9 +127,9 @@ function _machines-getAkey { # network SIGN_FILE=$(mktemp) _machines-api akey/$1 > $KEY_FILE _machines-api akey/$1?signature > $SIGN_FILE - md5sum $KEY_FILE &> /dev/stderr - md5sum $SIGN_FILE &> /dev/stderr - md5sum $MACHINES_CONFIG/machines.pub &> /dev/stderr + md5sum $KEY_FILE &1>&2; + md5sum $SIGN_FILE &1>&2; + md5sum $MACHINES_CONFIG/machines.pub &1>&2; openssl dgst -sha256 -verify $MACHINES_CONFIG/machines.pub -signature $SIGN_FILE $KEY_FILE &> /dev/null if [ $? == 0 ]; then @@ -151,7 +151,7 @@ function _machines-updateAkey { return 0 else cat $MYKEY_FILE - echo "[ERROR] Authorized keys are not properly signed" > /dev/stderr + echo "[ERROR] Authorized keys are not properly signed" 1>&2; rm $MYKEY_FILE exit 1 fi @@ -378,7 +378,7 @@ function machines_update-all { function machines_regen-keys { if [[ -e $MACHINES_CONFIG/machines.key || -e $MACHINES_CONFIG/machines.pub || -e $MACHINES_CONFIG/machines.crt ]]; then - echo "[ERROR] Please delete the pem files manually to prove you know what you're doing." > /dev/stderr + echo "[ERROR] Please delete the pem files manually to prove you know what you're doing." 1>&2; exit 1 else openssl genrsa -out $MACHINES_CONFIG/machines.key 4096 @@ -394,7 +394,7 @@ function machines_regen-keys { function machines_setup { if [ -e $MACHINES_CONFIG/this.name ]; then - echo "[ERROR] This machine is already set up" > /dev/stderr + echo "[ERROR] This machine is already set up" 1>&2; exit 1 fi diff --git a/vimrc b/vimrc index 487093a..184db61 100644 --- a/vimrc +++ b/vimrc @@ -5,57 +5,52 @@ set nocompatible filetype off """ PLUGINS MANAGEMENT """ -" Voir :h vundle -set rtp+=~/.vim/bundle/Vundle.vim -call vundle#begin() -Plugin 'gmarik/Vundle.vim' +" NOTE 18-06-24 Got rid of Vundle in favor of vim-plug (why: more recent, supports +" Neovim, simpler). Commented out plugins that seemed useless, feel free to +" uncomment them again if you need to -Plugin 'L9' -Plugin 'rstacruz/sparkup', {'rtp': 'vim/'} -Plugin 'tomasr/molokai' -"Bundle 'Shougo/neosnippet' -"Bundle 'Shougo/neosnippet-snippets' -Plugin 'tpope/vim-surround' -Plugin 'tpope/vim-fugitive' -Plugin 'tpope/vim-repeat' -Plugin 'tpope/tpope-vim-abolish' -Plugin 'vim-airline/vim-airline' -Plugin 'vim-airline/vim-airline-themes' -Plugin 'airblade/vim-gitgutter' -Plugin 'ctrlpvim/ctrlp.vim' -Plugin 'mbbill/undotree' -Plugin 'xolox/vim-misc' -Plugin 'xolox/vim-easytags' -Plugin 'majutsushi/tagbar' -Plugin 'wellle/targets.vim' -Plugin 'Chiel92/vim-autoformat' -" Plugin 'Valloric/YouCompleteMe' -Plugin 'artur-shaik/vim-javacomplete2' -Plugin 'tomtom/tcomment_vim' -Plugin 'Shougo/denite.nvim' -Plugin 'tomlion/vim-solidity' -Plugin 'godlygeek/tabular' -Plugin 'jrozner/vim-antlr' +" Auto-install vim-plug +if empty(glob('~/.vim/autoload/plug.vim')) + silent !curl -fLo ~/.vim/autoload/plug.vim --create-dirs + \ https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim + autocmd VimEnter * PlugInstall --sync | source $MYVIMRC +endif -call vundle#end() " required -filetype plugin indent on " required +" Plugin definition +call plug#begin() + +" Plug 'L9' +" Plug 'rstacruz/sparkup', {'rtp': 'vim/'} +Plug 'tomasr/molokai' +" Plug 'tpope/vim-surround' +" Plug 'tpope/vim-fugitive' +" Plug 'tpope/vim-repeat' +" Plug 'tpope/tpope-vim-abolish' +Plug 'vim-airline/vim-airline' +Plug 'vim-airline/vim-airline-themes' +Plug 'airblade/vim-gitgutter' +Plug 'mbbill/undotree' +Plug 'xolox/vim-misc' " Required for 'xolox/vim-easytags' +Plug 'xolox/vim-easytags' +Plug 'majutsushi/tagbar' +" Plug 'wellle/targets.vim' +Plug 'Chiel92/vim-autoformat' +Plug 'tomtom/tcomment_vim' +" Plug 'Shougo/denite.nvim' +" Plug 'tomlion/vim-solidity' +" Plug 'godlygeek/tabular' +" Plug 'jrozner/vim-antlr' +Plug 'maralla/completor.vim' +Plug 'python-mode/python-mode', { 'branch': 'develop' } +Plug 'junegunn/fzf', {'do': './install --bin'} +Plug 'junegunn/fzf.vim' + +call plug#end() """ UNDOTREE """ -nmap :UndotreeToggle:UndotreeFocus - -""" CTRLP """ - -let g:ctrlp_custom_ignore = { - \ 'dir': '\v([\/]\.(git|hg|svn)|log|node_modules|bower_components|__pycache__|vendor|output|buildroot|doc)$', - \ 'file': '\v\.(exe|so|dll|o|pyc|a)$', - \ 'link': 'SOME_BAD_SYMBOLIC_LINKS', - \ } - -let g:ctrlp_map = '' -let g:ctrlp_cmd = 'CtrlPMixed' -map :CtrlPMRUFiles +nmap :UndotreeToggle """ TAGBAR """ @@ -74,25 +69,47 @@ let g:airline_section_b = airline#section#create(['branch', 'hunks']) let g:airline_section_z = airline#section#create(['%B', '@', '%l', ':', '%c']) let g:airline_theme = 'wombat' -""" YOUCOMPLETEME """ - -" let g:ycm_global_ycm_extra_conf = '~/.config/ycm_extra_conf.py' -let g:ycm_global_ycm_extra_conf = '/usr/share/vim/vimfiles/third_party/ycmd/cpp/ycm/.ycm_extra_conf.py' -let g:ycm_server_python_interpreter = '/usr/bin/python2' - -nmap gTi :YcmCompleter GoToInclude -nmap gTc :YcmCompleter GoToDeclaration -nmap gTf :YcmCompleter GoToDefinition -nmap gt :YcmCompleter GoTo -nmap gT :YcmCompleter GoToImprecise -nmap gTr :YcmCompleter GoToReference - """ AUTOFORMAT """ nmap :Autoformat -""" JAVACOMPLETE """ +""" PYMODE """ + +" let g:pymode_lint_ignore = ["C901"] + +let g:pymode_lint_cwindow = 0 + +""" FZF """ + +let g:fzf_layout = { 'down': '~100%' } +let g:fzf_colors = +\ { 'fg': ['fg', 'Normal'], + \ 'bg': ['bg', 'Normal'], + \ 'hl': ['fg', 'Comment'], + \ 'fg+': ['fg', 'CursorLine', 'CursorColumn', 'Normal'], + \ 'bg+': ['bg', 'CursorLine', 'CursorColumn'], + \ 'hl+': ['fg', 'Statement'], + \ 'info': ['fg', 'PreProc'], + \ 'border': ['fg', 'Ignore'], + \ 'prompt': ['fg', 'Conditional'], + \ 'pointer': ['fg', 'Exception'], + \ 'marker': ['fg', 'Keyword'], + \ 'spinner': ['fg', 'Label'], + \ 'header': ['fg', 'Comment'] } + +nmap gF :Files +nmap gf :GFiles +nmap gb :Buffers +nmap gL :Lines +nmap gl :BLines +nmap gT :Tags +nmap gt :BTags +nmap gm :Marks +nmap gw :Windows +nmap gh :History +nmap gH :History: +nmap gS :History/ +nmap gs :Snippets -autocmd FileType java setlocal omnifunc=javacomplete#Complete """ VIM SETTINGS """ @@ -141,6 +158,9 @@ filetype on filetype plugin on filetype indent on +set wildmode=longest,list +set showcmd + " Put plugins and dictionaries in this dir (also on Windows) let vimDir = '$HOME/.vim' let &runtimepath.=','.vimDir @@ -161,7 +181,8 @@ cmap w!! w !sudo tee > /dev/null % imap jk imap mù -map o + +nmap o nmap :bp nmap :bn nmap kkkkkkkkkkkkkkkkkkkkk From c4a6f653a3738283623a55217f6a59b34497b7e0 Mon Sep 17 00:00:00 2001 From: Geoffrey Frogeye Date: Sun, 24 Jun 2018 18:28:37 +0200 Subject: [PATCH 10/22] Compress all the way --- scripts/compressPictureMovies | 163 ++++++++++++++++++++++++++++++++++ scripts/install-termux | 2 + scripts/md2html | 2 +- scripts/updateCompressedMusic | 95 ++++++++++++++++++++ scripts/updatedate | 3 + scripts/vidcmp | 20 +++++ 6 files changed, 284 insertions(+), 1 deletion(-) create mode 100755 scripts/compressPictureMovies create mode 100755 scripts/updateCompressedMusic create mode 100755 scripts/updatedate create mode 100755 scripts/vidcmp diff --git a/scripts/compressPictureMovies b/scripts/compressPictureMovies new file mode 100755 index 0000000..1a929ba --- /dev/null +++ b/scripts/compressPictureMovies @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 + +import os +import shutil +import subprocess +import sys + +# Constants +PICTURES_FOLDER = os.path.join(os.path.expanduser("~"), "Images") +ORIGNAL_FOLDER = os.path.join(PICTURES_FOLDER, ".Originaux") +MOVIE_EXTENSIONS = ["mov", "avi", "mp4"] +OUTPUT_EXTENSION = "mp4" +OUTPUT_FFMPEG_PARAMETERS = ["-codec:v", "libx265", "-crf", "28", "-preset:v", "slower", "-codec:a", "libfdk_aac", "-movflags", "+faststart", "-vbr", "5"] +OUTPUT_METADATA_FIELD = ["episode_id"] + + +# Walk folders +for root, dirs, files in os.walk(PICTURES_FOLDER): + # If folder is in ORIGINAL_FOLDER, skip it + if root.startswith(ORIGNAL_FOLDER): + continue + # Iterate over files + for inputName in files: + # If the file is not a video, skip it + inputNameBase, inputExt = os.path.splitext(inputName) + inputExt = inputExt[1:].lower() + if inputExt not in MOVIE_EXTENSIONS: + continue + + # Generates all needed filepaths + ## Found file + inputFull = os.path.join(root, inputName) + inputRel = os.path.relpath(inputFull, PICTURES_FOLDER) + ## Original file + originalFull = os.path.join(ORIGNAL_FOLDER, inputRel) + originalRel = inputRel + ## Compressed file + outputFull = os.path.join(root, inputNameBase + "." + OUTPUT_EXTENSION) + + # If the extension is the same of the output one + if inputExt == OUTPUT_EXTENSION: + # Read the metadata of the video + metadataRaw = subprocess.run(["ffmpeg", "-i", inputFull, "-f", "ffmetadata", "-"], stdout=subprocess.PIPE).stdout + # If it has the field with the original file + originalRel = None + wantedPattern = OUTPUT_METADATA_FIELD.encode() + b"=" + for metadataLine in metadataRaw.split('\n'): + if metadataLine.startswith(wantedPattern): + originalRel = metadataLine[len(wantedPattern)+1:] + break + if originalRel: + # If the original file does not exists, warn about it + originalFull = os.path.join(ORIGNAL_FOLDER, originalRel) + if not os.path.isfile(originalFull): + print("WARN {inputRel} states to have {originalRel} as original but this file doesn't exist".format(inputRel=inputRel, originalRel=originalRel)) + # If the original is not aligned with the compressed, warn about it (TODO move it automatically) + if inputRel != originalRel: + print("WARN {inputRel} is not aligned with original {originalRel}".format(inputRel=inputRel, originalRel=originalRel)) + # Skip file + continue + # Initiate a conversion in a temporary file + # If the temporary file does not have the same caracteristics as the original + # Warn about it + # Delete it + # Skip file + # Move the original to the corresponding original folder + # Move the converted file in place of the original + +# TODO Iterate over the orignal folder to find non-matching compressed videos not found in the above pass + +sys.exit(0) + +# Constants +SOURCE_FOLDER = os.path.join(os.path.expanduser("~"), "Musique") +OUTPUT_FOLDER = os.path.join(os.path.expanduser("~"), ".MusiqueCompressed") +CONVERSIONS = {"flac": "m4a"} +FORBIDDEN_EXTENSIONS = ["jpg", "pdf", "ffs_db"] +FORGIVEN_FILENAMES = ["cover.jpg"] +IGNORED_EMPTY_FOLDER = [".stfolder"] + + + +# Listing files +sourceFiles = dict() +for root, dirs, files in os.walk(SOURCE_FOLDER): + for f in files: + fullPath = os.path.join(root, f) + path = os.path.relpath(fullPath, SOURCE_FOLDER) + sourceFiles[path] = os.path.getctime(fullPath) + +outputFiles = dict() +for root, dirs, files in os.walk(OUTPUT_FOLDER): + for f in files: + fullPath = os.path.join(root, f) + path = os.path.relpath(fullPath, OUTPUT_FOLDER) + outputFiles[path] = os.path.getctime(fullPath) + +# Sorting files +remainingConversions = dict() +extraFiles = list(outputFiles.keys()) + +def convertPath(path): + filename, extension = os.path.splitext(path) + extension = extension[1:].lower() + # If the extension isn't allowed + if extension in FORBIDDEN_EXTENSIONS: + basename = os.path.basename(path) + # And the filename is not an exception + if basename not in FORGIVEN_FILENAMES: + # This file shouldn't be copied nor converted + return False + # If this needs a conversion + elif extension in CONVERSIONS: + extension = CONVERSIONS[extension] + return filename + "." + extension + # In all other case, this is a simple copy + return path + +for sourceFile in sourceFiles: + outputFile = convertPath(sourceFile) + # If the file should not be converted, do nothing + if outputFile == False: + continue + # If the file already has something as an output + elif outputFile in outputFiles: + extraFiles.remove(outputFile) + # If the output file is newer than the source file, do not initiate a conversion + if outputFiles[outputFile] >= sourceFiles[sourceFile]: + continue + # If the file needs to be converted, do it + remainingConversions[sourceFile] = outputFile + +# Converting files +for sourceFile in remainingConversions: + outputFile = remainingConversions[sourceFile] + + # Creating folder if it doesn't exists + fullOutputFile = os.path.join(OUTPUT_FOLDER, outputFile) + fullOutputDir = os.path.dirname(fullOutputFile) + os.makedirs(fullOutputDir, exist_ok=True) + + # Converting + fullSourceFile = os.path.join(SOURCE_FOLDER, sourceFile) + print(fullSourceFile, "→", fullOutputFile) + if sourceFile == outputFile: + # shutil.copy(fullSourceFile, fullOutputFile) + os.link(fullSourceFile, fullOutputFile) + else: + subprocess.run(["ffmpeg", "-y", "-i", fullSourceFile, "-codec:a", "libfdk_aac", "-cutoff", "18000", "-movflags", "+faststart", "-vbr", "5", fullOutputFile]) + +# Removing extra files +for extraFile in extraFiles: + fullExtraFile = os.path.join(OUTPUT_FOLDER, extraFile) + os.remove(fullExtraFile) + +# Removing empty dirs +for root, dirs, files in os.walk(OUTPUT_FOLDER): + if not dirs and not files: + dirBasename = os.path.basename(root) + if dirBasename not in IGNORED_EMPTY_FOLDER: + os.rmdir(root) + + diff --git a/scripts/install-termux b/scripts/install-termux index 51e3437..f066717 100755 --- a/scripts/install-termux +++ b/scripts/install-termux @@ -31,6 +31,8 @@ apt upgrade # (needed for install-prefs) apt install coreutils apt install grep +# Used by some of my termux scripts +apt install jq # Config touch ~/.hushlogin diff --git a/scripts/md2html b/scripts/md2html index 9564de2..ca33436 100755 --- a/scripts/md2html +++ b/scripts/md2html @@ -114,7 +114,7 @@ if (latex) { // Conversion htmlString = marked(markdownString, { renderer: renderer, - breaks: true + breaks: false }); // fullHtmlString = htmlString; fullHtmlString = template.replace('%BODY%', () => { return htmlString }); diff --git a/scripts/updateCompressedMusic b/scripts/updateCompressedMusic new file mode 100755 index 0000000..e4aefce --- /dev/null +++ b/scripts/updateCompressedMusic @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 + +import os +import shutil +import subprocess + +# Constants +SOURCE_FOLDER = os.path.join(os.path.expanduser("~"), "Musique") +OUTPUT_FOLDER = os.path.join(os.path.expanduser("~"), ".MusiqueCompressed") +CONVERSIONS = {"flac": "m4a"} +FORBIDDEN_EXTENSIONS = ["jpg", "pdf", "ffs_db"] +FORGIVEN_FILENAMES = ["cover.jpg"] +IGNORED_EMPTY_FOLDER = [".stfolder"] + +# Listing files +sourceFiles = dict() +for root, dirs, files in os.walk(SOURCE_FOLDER): + for f in files: + fullPath = os.path.join(root, f) + path = os.path.relpath(fullPath, SOURCE_FOLDER) + sourceFiles[path] = os.path.getctime(fullPath) + +outputFiles = dict() +for root, dirs, files in os.walk(OUTPUT_FOLDER): + for f in files: + fullPath = os.path.join(root, f) + path = os.path.relpath(fullPath, OUTPUT_FOLDER) + outputFiles[path] = os.path.getctime(fullPath) + +# Sorting files +remainingConversions = dict() +extraFiles = list(outputFiles.keys()) + +def convertPath(path): + filename, extension = os.path.splitext(path) + extension = extension[1:].lower() + # If the extension isn't allowed + if extension in FORBIDDEN_EXTENSIONS: + basename = os.path.basename(path) + # And the filename is not an exception + if basename not in FORGIVEN_FILENAMES: + # This file shouldn't be copied nor converted + return False + # If this needs a conversion + elif extension in CONVERSIONS: + extension = CONVERSIONS[extension] + return filename + "." + extension + # In all other case, this is a simple copy + return path + +for sourceFile in sourceFiles: + outputFile = convertPath(sourceFile) + # If the file should not be converted, do nothing + if outputFile == False: + continue + # If the file already has something as an output + elif outputFile in outputFiles: + extraFiles.remove(outputFile) + # If the output file is newer than the source file, do not initiate a conversion + if outputFiles[outputFile] >= sourceFiles[sourceFile]: + continue + # If the file needs to be converted, do it + remainingConversions[sourceFile] = outputFile + +# Converting files +for sourceFile in remainingConversions: + outputFile = remainingConversions[sourceFile] + + # Creating folder if it doesn't exists + fullOutputFile = os.path.join(OUTPUT_FOLDER, outputFile) + fullOutputDir = os.path.dirname(fullOutputFile) + os.makedirs(fullOutputDir, exist_ok=True) + + # Converting + fullSourceFile = os.path.join(SOURCE_FOLDER, sourceFile) + print(fullSourceFile, "→", fullOutputFile) + if sourceFile == outputFile: + # shutil.copy(fullSourceFile, fullOutputFile) + os.link(fullSourceFile, fullOutputFile) + else: + subprocess.run(["ffmpeg", "-y", "-i", fullSourceFile, "-codec:a", "libfdk_aac", "-cutoff", "18000", "-movflags", "+faststart", "-vbr", "5", fullOutputFile]) + +# Removing extra files +for extraFile in extraFiles: + fullExtraFile = os.path.join(OUTPUT_FOLDER, extraFile) + os.remove(fullExtraFile) + +# Removing empty dirs +for root, dirs, files in os.walk(OUTPUT_FOLDER): + if not dirs and not files: + dirBasename = os.path.basename(root) + if dirBasename not in IGNORED_EMPTY_FOLDER: + os.rmdir(root) + + diff --git a/scripts/updatedate b/scripts/updatedate new file mode 100755 index 0000000..7339128 --- /dev/null +++ b/scripts/updatedate @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +echo ssh "$1" date --set="'$(date -R)'" diff --git a/scripts/vidcmp b/scripts/vidcmp new file mode 100755 index 0000000..f8e27eb --- /dev/null +++ b/scripts/vidcmp @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 + +# Compresses video using FFMPEG using +# FFMPEG's reasonable default settings + +import os +import sys +import subprocess + +files = sys.argv[1:] + +remove = False +if '-r' in files: + files.remove('-r') + remove = True + +for f in files: + print(os.path.splitext(f)) + + From 16f9814d64e15bdfcf0a4bf7a29dfedb45d64074 Mon Sep 17 00:00:00 2001 From: Geoffrey Frogeye Date: Tue, 10 Jul 2018 14:50:07 +0200 Subject: [PATCH 11/22] ouyeah --- config/i3/config | 7 ++----- config/i3/dmenu_cmd | 2 +- config/i3/sshmenu | 2 +- config/vdirsyncer/config | 2 ++ profile | 2 +- scripts/install-prefs | 3 +++ scripts/updateCompressedMusic | 17 ++++++++++++----- scripts/updatedate | 2 +- vimrc | 31 +++++++++++++++++++++++++------ 9 files changed, 48 insertions(+), 20 deletions(-) diff --git a/config/i3/config b/config/i3/config index 94f97fa..35fb8d2 100644 --- a/config/i3/config +++ b/config/i3/config @@ -144,19 +144,14 @@ set $WS10 10 # Workspace output workspace "$WS1" output LVDS1 workspace "$WS2" output VGA1 -workspace "$WS2" output HDMI1 workspace "$WS3" output LVDS1 workspace "$WS4" output VGA1 -workspace "$WS4" output HDMI1 workspace "$WS5" output LVDS1 workspace "$WS6" output VGA1 -workspace "$WS6" output HDMI1 workspace "$WS7" output LVDS1 workspace "$WS8" output VGA1 -workspace "$WS8" output HDMI1 workspace "$WS9" output LVDS1 workspace "$WS10" output VGA1 -workspace "$WS10" output HDMI1 # switch to workspace bindsym $mod+1 workspace $WS1 @@ -244,6 +239,8 @@ for_window [class="qutebrowser"] layout tabbed for_window [window_role="pop-up"] floating enable for_window [window_role="task_dialog"] floating enable +for_window [ title="^pdfpc.*" window_role="presenter" ] move to output left, fullscreen +for_window [ title="^pdfpc.*" window_role="presentation" ] move to output right, fullscreen # switch to workspace with urgent window automatically for_window [urgent=latest] focus diff --git a/config/i3/dmenu_cmd b/config/i3/dmenu_cmd index a0d3180..2806540 100755 --- a/config/i3/dmenu_cmd +++ b/config/i3/dmenu_cmd @@ -1,2 +1,2 @@ #!/bin/sh -dmenu -fn 'DejaVu Sans Mono-10' -nb '#48483e' -nf '#f1ebeb' -sb '#8fc029' -sf '#272822' -l 8 -f "$@" +dmenu -fn 'DejaVu Sans Mono-10' -nb '#48483e' -nf '#f1ebeb' -sb '#8fc029' -sf '#272822' -i -l 8 -f "$@" diff --git a/config/i3/sshmenu b/config/i3/sshmenu index 7b3fdc5..e306c40 100755 --- a/config/i3/sshmenu +++ b/config/i3/sshmenu @@ -5,5 +5,5 @@ if [ "$1" == 'root' ]; then fi machine=$(cat ~/.ssh/config | grep '^Host ' | cut -d ' ' -f 2 | grep -v '*' | sort | $HOME/.config/i3/dmenu_cmd -p "SSH$a to ") if [ ! -z $machine ]; then - xterm -e "ssh $machine $b" + urxvtc -e ssh $machine $b fi diff --git a/config/vdirsyncer/config b/config/vdirsyncer/config index 148fa0e..7ef5e40 100644 --- a/config/vdirsyncer/config +++ b/config/vdirsyncer/config @@ -55,6 +55,8 @@ collections = ["from a", "from b"] # Calendars also have a color property metadata = ["displayname", "color"] +# conflict_resolution = "a wins" +# conflict_resolution = "b wins" [storage geoffrey_calendar_local] type = "filesystem" diff --git a/profile b/profile index 4de32f2..614b95f 100644 --- a/profile +++ b/profile @@ -9,4 +9,4 @@ [ -f "$HOME/.config/linuxColors.sh" ] && . "$HOME/.config/linuxColors.sh" # Bashrc -[ -f ~/.bashrc ] && . ~/.bashrc +[ -f ~/.bashrc ] && source ~/.bashrc diff --git a/scripts/install-prefs b/scripts/install-prefs index dac24f8..055cc90 100755 --- a/scripts/install-prefs +++ b/scripts/install-prefs @@ -244,6 +244,9 @@ if [ $GUI == 1 ]; then inst debhelper cmake libxcb-icccm4-dev libxcb-image0-dev libxcb-randr0-dev libx11-dev libxcb1-dev libxcb-util-dev libx11-xcb-dev linux-libc-dev libboost-dev x11proto-core-dev libxcb-ewmh-dev libxft-dev libasound2-dev libiw-dev libmpdclient-dev xcb-proto python-xcbgen libxcb-xkb-dev i3-wm libcairo2-dev libxcb-xrm-dev # TODO Figure which one are really needed #inst libasound2 libc6 libgcc1 libiw30 libmpdclient2 libstdc++6 libx11-6 libx11-xcb1 libxcb-ewmh2 libxcb-icccm4 libxcb-randr0 libxcb-xkb1 libxcb1 libxft2 + # ↓ really needed + inst libcairo2-dev libxcb-xkb-dev libxcb-randr0-dev xcb-proto libxcb-image0-dev libxcb-icccm4-dev libxcb-ewmh-dev libxcb-util0-dev python-xcbgen + TMP=$(mktemp -d) git clone --branch 3.0.5 --recursive https://github.com/jaagr/polybar $TMP mkdir $TMP/build diff --git a/scripts/updateCompressedMusic b/scripts/updateCompressedMusic index e4aefce..543151e 100755 --- a/scripts/updateCompressedMusic +++ b/scripts/updateCompressedMusic @@ -7,7 +7,7 @@ import subprocess # Constants SOURCE_FOLDER = os.path.join(os.path.expanduser("~"), "Musique") OUTPUT_FOLDER = os.path.join(os.path.expanduser("~"), ".MusiqueCompressed") -CONVERSIONS = {"flac": "m4a"} +CONVERSIONS = {"flac": "opus"} FORBIDDEN_EXTENSIONS = ["jpg", "pdf", "ffs_db"] FORGIVEN_FILENAMES = ["cover.jpg"] IGNORED_EMPTY_FOLDER = [".stfolder"] @@ -31,6 +31,7 @@ for root, dirs, files in os.walk(OUTPUT_FOLDER): remainingConversions = dict() extraFiles = list(outputFiles.keys()) + def convertPath(path): filename, extension = os.path.splitext(path) extension = extension[1:].lower() @@ -48,6 +49,7 @@ def convertPath(path): # In all other case, this is a simple copy return path + for sourceFile in sourceFiles: outputFile = convertPath(sourceFile) # If the file should not be converted, do nothing @@ -56,7 +58,8 @@ for sourceFile in sourceFiles: # If the file already has something as an output elif outputFile in outputFiles: extraFiles.remove(outputFile) - # If the output file is newer than the source file, do not initiate a conversion + # If the output file is newer than the source file, do not initiate a + # conversion if outputFiles[outputFile] >= sourceFiles[sourceFile]: continue # If the file needs to be converted, do it @@ -76,13 +79,19 @@ for sourceFile in remainingConversions: print(fullSourceFile, "→", fullOutputFile) if sourceFile == outputFile: # shutil.copy(fullSourceFile, fullOutputFile) + if os.path.isfile(fullOutputFile): + os.remove(fullOutputFile) os.link(fullSourceFile, fullOutputFile) else: - subprocess.run(["ffmpeg", "-y", "-i", fullSourceFile, "-codec:a", "libfdk_aac", "-cutoff", "18000", "-movflags", "+faststart", "-vbr", "5", fullOutputFile]) + subprocess.run( + ["ffmpeg", "-y", "-i", fullSourceFile, "-c:a", "libopus", + "-movflags", "+faststart", "-b:a", "128k", "-vbr", "on", + "-compression_level", "10", fullOutputFile]) # Removing extra files for extraFile in extraFiles: fullExtraFile = os.path.join(OUTPUT_FOLDER, extraFile) + print("×", fullExtraFile) os.remove(fullExtraFile) # Removing empty dirs @@ -91,5 +100,3 @@ for root, dirs, files in os.walk(OUTPUT_FOLDER): dirBasename = os.path.basename(root) if dirBasename not in IGNORED_EMPTY_FOLDER: os.rmdir(root) - - diff --git a/scripts/updatedate b/scripts/updatedate index 7339128..c2d01c3 100755 --- a/scripts/updatedate +++ b/scripts/updatedate @@ -1,3 +1,3 @@ #!/usr/bin/env bash -echo ssh "$1" date --set="'$(date -R)'" +echo ssh "$1" sudo date --set="'$(date -R)'" diff --git a/vimrc b/vimrc index 184db61..6908367 100644 --- a/vimrc +++ b/vimrc @@ -23,10 +23,10 @@ call plug#begin() " Plug 'L9' " Plug 'rstacruz/sparkup', {'rtp': 'vim/'} Plug 'tomasr/molokai' -" Plug 'tpope/vim-surround' +Plug 'tpope/vim-surround' " Plug 'tpope/vim-fugitive' " Plug 'tpope/vim-repeat' -" Plug 'tpope/tpope-vim-abolish' +Plug 'tpope/tpope-vim-abolish' Plug 'vim-airline/vim-airline' Plug 'vim-airline/vim-airline-themes' Plug 'airblade/vim-gitgutter' @@ -34,7 +34,7 @@ Plug 'mbbill/undotree' Plug 'xolox/vim-misc' " Required for 'xolox/vim-easytags' Plug 'xolox/vim-easytags' Plug 'majutsushi/tagbar' -" Plug 'wellle/targets.vim' +Plug 'wellle/targets.vim' Plug 'Chiel92/vim-autoformat' Plug 'tomtom/tcomment_vim' " Plug 'Shougo/denite.nvim' @@ -45,6 +45,8 @@ Plug 'maralla/completor.vim' Plug 'python-mode/python-mode', { 'branch': 'develop' } Plug 'junegunn/fzf', {'do': './install --bin'} Plug 'junegunn/fzf.vim' +Plug 'ervandew/supertab' +Plug 'dpelle/vim-LanguageTool' call plug#end() @@ -74,10 +76,13 @@ nmap :Autoformat """ PYMODE """ -" let g:pymode_lint_ignore = ["C901"] - +let g:pymode_lint_ignore = ["W0401"] let g:pymode_lint_cwindow = 0 +" TODO Even with magic pymod_motion complains about the option `magic&` being not set :/ +let g:pymode_motion = 0 +set magic + """ FZF """ let g:fzf_layout = { 'down': '~100%' } @@ -110,6 +115,18 @@ nmap gH :History: nmap gS :History/ nmap gs :Snippets +" TODO `gd` → go to tag matching selected word, or show a list with that +" of tags pre-filtered with that word + +""" SUPERTAB """ + +let g:SuperTabDefaultCompletionType = "" " Go down when completing +let g:SuperTabContextDefaultCompletionType = "" + +""" LanguageTool """ + +let g:languagetool_jar = "/usr/share/java/languagetool/languagetool-commandline.jar" + """ VIM SETTINGS """ @@ -143,6 +160,8 @@ set updatetime=250 set cursorcolumn +set splitbelow + syntax enable set background=dark @@ -180,7 +199,7 @@ endif cmap w!! w !sudo tee > /dev/null % imap jk -imap mù +vmap nmap o nmap :bp From 8e43663661aec057398b038af76506f31d8aaac8 Mon Sep 17 00:00:00 2001 From: Geoffrey Frogeye Date: Mon, 30 Jul 2018 10:55:06 +0200 Subject: [PATCH 12/22] COLORS --- Xresources.d/.gitignore | 1 + Xresources.d/theme | 36 ------------------------- bashrc | 8 ++++-- config/i3/config | 60 ++++++++++++++++++++--------------------- scripts/changeColors | 35 ++++++++++++++++++++++++ vimrc | 11 ++++---- 6 files changed, 77 insertions(+), 74 deletions(-) create mode 100644 Xresources.d/.gitignore delete mode 100644 Xresources.d/theme create mode 100755 scripts/changeColors diff --git a/Xresources.d/.gitignore b/Xresources.d/.gitignore new file mode 100644 index 0000000..62fd177 --- /dev/null +++ b/Xresources.d/.gitignore @@ -0,0 +1 @@ +theme diff --git a/Xresources.d/theme b/Xresources.d/theme deleted file mode 100644 index d60c7b3..0000000 --- a/Xresources.d/theme +++ /dev/null @@ -1,36 +0,0 @@ -! special -*.foreground: #f1ebeb -*.background: #272822 -*.cursorColor: #f1ebeb - -! black -*.color0: #48483e -*.color8: #76715e - -! red -*.color1: #dc2566 -*.color9: #fa2772 - -! green -*.color2: #8fc029 -*.color10: #a7e22e - -! yellow -*.color3: #d4c96e -*.color11: #e7db75 - -! blue -*.color4: #55bcce -*.color12: #66d9ee - -! magenta -*.color5: #9358fe -*.color13: #ae82ff - -! cyan -*.color6: #56b7a5 -*.color14: #66efd5 - -! white -*.color7: #acada1 -*.color15: #cfd0c2 diff --git a/bashrc b/bashrc index 45e1ba1..fda1e9a 100644 --- a/bashrc +++ b/bashrc @@ -127,11 +127,15 @@ export PATH="$HOME/.bin/:$HOME/.scripts/:$PATH" # UTILITIES +# Theme +[ -f ~/.bin/colorSchemeApply ] && source ~/.bin/colorSchemeApply +[ -f ~/.bin/colorSchemeApplyFzf ] && source ~/.bin/colorSchemeApplyFzf + # Bash completion -[ -f /etc/bash_completion ] && . /etc/bash_completion +[ -f /etc/bash_completion ] && source /etc/bash_completion # Fuzzy matching all the way -export FZF_DEFAULT_OPTS="--height 100% --layout=default" +export FZF_DEFAULT_OPTS="${FZF_DEFAULT_OPTS} --height 100% --layout=default" export FZF_CTRL_T_OPTS="--preview '[[ -d {} ]] && ls -l --color=always {} || [[ \$(file --mime {}) =~ binary ]] && echo {} is a binary file || (highlight -O ansi -l {} || coderay {} || rougify {} || cat {}) 2> /dev/null | head -500'" export FZF_COMPLETION_OPTS="${FZF_CTRL_T_OPTS}" [ -f /usr/share/fzf/completion.bash ] && source /usr/share/fzf/completion.bash diff --git a/config/i3/config b/config/i3/config index 35fb8d2..df41ee8 100644 --- a/config/i3/config +++ b/config/i3/config @@ -143,15 +143,15 @@ set $WS10 10 # Workspace output workspace "$WS1" output LVDS1 -workspace "$WS2" output VGA1 +workspace "$WS2" output HDMI1 workspace "$WS3" output LVDS1 -workspace "$WS4" output VGA1 +workspace "$WS4" output HDMI1 workspace "$WS5" output LVDS1 -workspace "$WS6" output VGA1 +workspace "$WS6" output HDMI1 workspace "$WS7" output LVDS1 -workspace "$WS8" output VGA1 +workspace "$WS8" output HDMI1 workspace "$WS9" output LVDS1 -workspace "$WS10" output VGA1 +workspace "$WS10" output HDMI1 # switch to workspace bindsym $mod+1 workspace $WS1 @@ -331,24 +331,24 @@ mode "$mode_temp" { } # Colors -set $bg #272822 -set $fg #f1ebeb -set $00 #48483e -set $01 #dc2566 -set $02 #8fc029 -set $03 #d4c96e -set $04 #55bcce -set $05 #9358fe -set $06 #56b7a5 -set $07 #acada1 -set $08 #76715e -set $09 #fa2772 -set $10 #a7e22e -set $11 #e7db75 -set $12 #66d9ee -set $13 #ae82ff -set $14 #66efd5 -set $15 #cfd0c2 +set_from_resource $background i3wm.background #272822 +set_from_resource $foreground i3wm.foreground #f1ebeb +set_from_resource $color00 i3wm.color0 #48483e +set_from_resource $color01 i3wm.color1 #dc2566 +set_from_resource $color02 i3wm.color2 #8fc029 +set_from_resource $color03 i3wm.color3 #d4c96e +set_from_resource $color04 i3wm.color4 #55bcce +set_from_resource $color05 i3wm.color5 #9358fe +set_from_resource $color06 i3wm.color6 #56b7a5 +set_from_resource $color07 i3wm.color7 #acada1 +set_from_resource $color08 i3wm.color8 #76715e +set_from_resource $color09 i3wm.color9 #fa2772 +set_from_resource $color10 i3wm.color10 #a7e22e +set_from_resource $color11 i3wm.color11 #e7db75 +set_from_resource $color12 i3wm.color12 #66d9ee +set_from_resource $color13 i3wm.color13 #ae82ff +set_from_resource $color14 i3wm.color14 #66efd5 +set_from_resource $color15 i3wm.color15 #cfd0c2 # Inactivity settings exec --no-startup-id xautolock -time 10 -locker 'xset dpms force standby' -killtime 1 -killer '$locker' @@ -373,11 +373,11 @@ exec --no-startup-id ~/.config/i3/batteryNotify -d # Battery state notification set $ignore #ff00000 # Theme colors -# class border backgr. text indicator child_border -client.focused $02 $02 $bg $07 $10 -client.focused_inactive $03 $03 $08 $15 $14 -client.unfocused $08 $08 $fg $11 $00 -client.urgent $01 $01 $fg $05 $09 -client.placeholder $ignore $06 $fg $ignore $14 +#class border backgr. text indicator child_border +client.focused $color02 $color02 $background $color08 $color10 +client.focused_inactive $color03 $color03 $color08 $color15 $color14 +client.unfocused $color08 $color08 $color07 $color11 $color00 +client.urgent $color01 $color01 $color07 $foreground $color09 +client.placeholder $ignore $color06 $color07 $ignore $color14 -client.background $15 +client.background $color15 diff --git a/scripts/changeColors b/scripts/changeColors new file mode 100755 index 0000000..4048a85 --- /dev/null +++ b/scripts/changeColors @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +# Fetchs colors from https://github.com/chriskempson/base16 templates +# and apply them into my configuration + +scheme="$1" + +# TODO Verify if scheme is known + +# Shell (allows to use all colors in Vim while still having nice colors in the rest of the terminal) +curl "https://raw.githubusercontent.com/chriskempson/base16-shell/master/scripts/base16-${scheme}.sh" > ~/.bin/colorSchemeApply +chmod +x ~/.bin/colorSchemeApply + +# Xressources (I'm not sure if this is really needed with shell overriding these but i3 load those resources) +curl "https://raw.githubusercontent.com/chriskempson/base16-xresources/master/xresources/base16-${scheme}-256.Xresources" > ~/.Xresources.d/theme + +# Vim +echo -e "let base16colorspace=256\nset termguicolors\ncolorscheme base16-${scheme}" > ~/.vim/colorscheme.vim + +# FZF +curl "https://raw.githubusercontent.com/nicodebo/base16-fzf/master/bash/base16-${scheme}.config" > ~/.bin/colorSchemeApplyFzf +chmod +x ~/.bin/colorSchemeApplyFzf + +# qutebrowser +curl "https://raw.githubusercontent.com/theova/base16-qutebrowser/4a17eea8a39f722c2cee95fb44d4a87f5eb2518f/themes/base16-${scheme}.config.py" > ~/.config/qutebrowser/theme.py + +# TODO dunst (template online, but not to my liking) +# TODO bar (might change bar in the future, so...) +# TODO qutebrowser (need to fiddle with the config thing) +# TODO highlight (there IS a template but the colors look different from vim and mostly the same from when there's no config) + +# Reload a bunch of things to make changes immediate +source ~/.bashrc +xrdb -load ~/.Xresources +i3-msg reload diff --git a/vimrc b/vimrc index 6908367..3f62987 100644 --- a/vimrc +++ b/vimrc @@ -22,7 +22,7 @@ call plug#begin() " Plug 'L9' " Plug 'rstacruz/sparkup', {'rtp': 'vim/'} -Plug 'tomasr/molokai' +Plug 'chriskempson/base16-vim' Plug 'tpope/vim-surround' " Plug 'tpope/vim-fugitive' " Plug 'tpope/vim-repeat' @@ -69,7 +69,7 @@ let g:airline#extensions#tabline#enabled = 1 let g:airline_section_a = airline#section#create(['mode']) let g:airline_section_b = airline#section#create(['branch', 'hunks']) let g:airline_section_z = airline#section#create(['%B', '@', '%l', ':', '%c']) -let g:airline_theme = 'wombat' +let g:airline_theme = 'base16' """ AUTOFORMAT """ nmap :Autoformat @@ -164,10 +164,6 @@ set splitbelow syntax enable -set background=dark -colorscheme molokai -let g:molokai_original = 1 - " From http://stackoverflow.com/a/5004785/2766106 set list set listchars=tab:╾╌,trail:·,extends:↦,precedes:↤,nbsp:_ @@ -184,6 +180,9 @@ set showcmd let vimDir = '$HOME/.vim' let &runtimepath.=','.vimDir +" theme +source $HOME/.vim/colorscheme.vim + " Keep undo history across sessions by storing it in a file if has('persistent_undo') let myUndoDir = expand(vimDir . '/undodir') From d38e1d91807d2de258bfece80149f0392d1c5d0f Mon Sep 17 00:00:00 2001 From: Geoffrey Frogeye Date: Sat, 4 Aug 2018 12:43:13 +0200 Subject: [PATCH 13/22] ReplayGain ? --- scripts/replayGain | 97 +++++++++++++++++++++++++++++++++++ scripts/updateCompressedMusic | 41 +++++++++++---- 2 files changed, 127 insertions(+), 11 deletions(-) create mode 100755 scripts/replayGain diff --git a/scripts/replayGain b/scripts/replayGain new file mode 100755 index 0000000..5e0b7d9 --- /dev/null +++ b/scripts/replayGain @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 + +# Normalisation is done at the default of each program, +# which is usually -89.0 dB + +import os +import subprocess +import coloredlogs +import logging +import progressbar + +coloredlogs.install(level='DEBUG', fmt='%(levelname)s %(message)s') +log = logging.getLogger() + + +# Constants +FORCE = False # TODO UX cli argument +SOURCE_FOLDER = os.path.join(os.path.expanduser("~"), "Musique") +GAIN_COMMANDS = {("flac",): ["metaflac", "--add-replay-gain"], + ("mp3",): ["mp3gain", "-a", "-k"] + (["-s", "r"] if FORCE else []), + ("m4a", "mp4"): ["aacgain", "-a", "-k"] + (["-s", "r"] if FORCE else []), + ("ogg",): ["vorbisgain", "--album"] + ([] if FORCE else ["--fast"]), + } + +# Since metaflac ALWAYS recalculate the tags, we need to specificaly verify +# that the tags are present on every track of an album to skip it +def isFlacTagged(f): + # TODO PERF Run metaflac --show-tag for the whole album and compare the + # output with the number of files tagged + for tag in ["REPLAYGAIN_TRACK_GAIN", "REPLAYGAIN_ALBUM_GAIN"]: + cmd = ["metaflac", "--show-tag", tag, f] + proc = subprocess.run(cmd, stdout=subprocess.PIPE) + res = len(proc.stdout.strip()) > 0 + if not res: + return False + return True + +# TODO UX Do the same thing with other formats so the output is not +# inconsistent + +# Get album paths +albums = set() +for root, dirs, files in os.walk(SOURCE_FOLDER): + + relRoot = os.path.relpath(root, SOURCE_FOLDER) + + # See if it's an album (only 2 components in the path) + head, tail = os.path.split(relRoot) + if not len(head): + continue + head, tail = os.path.split(head) + if len(head): + continue + albums.add(root) + +cmds = list() +for album in albums: + albumName = os.path.relpath(album, SOURCE_FOLDER) + log.info("Processing album {}".format(albumName)) + + musicFiles = dict() + for root, dirs, files in os.walk(album): + for f in files: + ext = os.path.splitext(f)[1][1:].lower() + + for exts in GAIN_COMMANDS.keys(): + if ext in exts: + if exts not in musicFiles.keys(): + musicFiles[exts] = set() + fullPath = os.path.join(root, f) + musicFiles[exts].add(fullPath) + + if len(musicFiles) >= 2: + log.warn("Different extensions for album {}. AlbumGain won't be on par.".format(albumName)) + + for exts, files in musicFiles.items(): + + if exts == ("flac",) and not FORCE: + allTagged = True + for f in files: + if not isFlacTagged(f): + allTaged = False + break + if allTagged: + log.debug("Already tagged (for flac only)!") + break + + cmd = GAIN_COMMANDS[exts] + list(files) + log.debug("Registering command: `{}`".format(" ".join(cmd))) + cmds.append(cmd) + +logging.info("Executing commands") +for cmd in progressbar.progressbar(cmds): + subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + + + diff --git a/scripts/updateCompressedMusic b/scripts/updateCompressedMusic index 543151e..252fc5e 100755 --- a/scripts/updateCompressedMusic +++ b/scripts/updateCompressedMusic @@ -1,8 +1,13 @@ #!/usr/bin/env python3 import os -import shutil import subprocess +import progressbar +import logging +import coloredlogs + +coloredlogs.install(level='DEBUG', fmt='%(levelname)s %(message)s') +log = logging.getLogger() # Constants SOURCE_FOLDER = os.path.join(os.path.expanduser("~"), "Musique") @@ -12,7 +17,11 @@ FORBIDDEN_EXTENSIONS = ["jpg", "pdf", "ffs_db"] FORGIVEN_FILENAMES = ["cover.jpg"] IGNORED_EMPTY_FOLDER = [".stfolder"] +# TODO FEAT Make the directory structure the same as the base one and +# remove IGNORED_EMPTY_FOLDER variable + # Listing files +log.info("Listing files") sourceFiles = dict() for root, dirs, files in os.walk(SOURCE_FOLDER): for f in files: @@ -29,7 +38,7 @@ for root, dirs, files in os.walk(OUTPUT_FOLDER): # Sorting files remainingConversions = dict() -extraFiles = list(outputFiles.keys()) +extraFiles = set(outputFiles.keys()) def convertPath(path): @@ -50,6 +59,7 @@ def convertPath(path): return path +log.info("Determining action over {} files".format(len(sourceFiles))) for sourceFile in sourceFiles: outputFile = convertPath(sourceFile) # If the file should not be converted, do nothing @@ -65,7 +75,9 @@ for sourceFile in sourceFiles: # If the file needs to be converted, do it remainingConversions[sourceFile] = outputFile -# Converting files +log.debug("{} actions will need to be taken".format(len(remainingConversions))) +log.info("Copying files that do not require a conversion") +conversions = set() for sourceFile in remainingConversions: outputFile = remainingConversions[sourceFile] @@ -76,24 +88,31 @@ for sourceFile in remainingConversions: # Converting fullSourceFile = os.path.join(SOURCE_FOLDER, sourceFile) - print(fullSourceFile, "→", fullOutputFile) if sourceFile == outputFile: - # shutil.copy(fullSourceFile, fullOutputFile) + log.debug('{} → {}'.format(fullSourceFile, fullOutputFile)) if os.path.isfile(fullOutputFile): os.remove(fullOutputFile) os.link(fullSourceFile, fullOutputFile) else: - subprocess.run( - ["ffmpeg", "-y", "-i", fullSourceFile, "-c:a", "libopus", - "-movflags", "+faststart", "-b:a", "128k", "-vbr", "on", - "-compression_level", "10", fullOutputFile]) + conversions.add((fullSourceFile, fullOutputFile)) -# Removing extra files +log.info("Removing extra files") for extraFile in extraFiles: fullExtraFile = os.path.join(OUTPUT_FOLDER, extraFile) - print("×", fullExtraFile) + log.debug('× {}'.format(fullExtraFile)) os.remove(fullExtraFile) +log.info("Listing files that will be converted") +for fullSourceFile, fullOutputFile in conversions: + log.debug('{} ⇒ {}'.format(fullSourceFile, fullOutputFile)) + +log.info("Converting files") +for fullSourceFile, fullOutputFile in progressbar.progressbar(conversions): + cmd = ["ffmpeg", "-y", "-i", fullSourceFile, "-c:a", "libopus", + "-movflags", "+faststart", "-b:a", "128k", "-vbr", "on", + "-compression_level", "10", fullOutputFile] + subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + # Removing empty dirs for root, dirs, files in os.walk(OUTPUT_FOLDER): if not dirs and not files: From fe27b3b960616de478cd9ebbcecb1b32755040d4 Mon Sep 17 00:00:00 2001 From: Geoffrey Frogeye Date: Tue, 7 Aug 2018 16:09:41 +0200 Subject: [PATCH 14/22] Python is the new bash --- scripts/compressPictureMovies | 312 ++++++++++++++++++++-------------- scripts/musiqueBof | 39 +++++ scripts/replayGain | 87 ++++------ scripts/tagCreatorPhotos | 17 ++ vimpcrc | 1 + vimrc | 30 +++- 6 files changed, 293 insertions(+), 193 deletions(-) create mode 100755 scripts/musiqueBof create mode 100755 scripts/tagCreatorPhotos diff --git a/scripts/compressPictureMovies b/scripts/compressPictureMovies index 1a929ba..da74b42 100755 --- a/scripts/compressPictureMovies +++ b/scripts/compressPictureMovies @@ -4,20 +4,98 @@ import os import shutil import subprocess import sys +import logging +import coloredlogs +import progressbar +import time +import hashlib +import tempfile +import json +import statistics +import datetime + +coloredlogs.install(level='DEBUG', fmt='%(levelname)s %(message)s') +log = logging.getLogger() # Constants PICTURES_FOLDER = os.path.join(os.path.expanduser("~"), "Images") -ORIGNAL_FOLDER = os.path.join(PICTURES_FOLDER, ".Originaux") -MOVIE_EXTENSIONS = ["mov", "avi", "mp4"] -OUTPUT_EXTENSION = "mp4" -OUTPUT_FFMPEG_PARAMETERS = ["-codec:v", "libx265", "-crf", "28", "-preset:v", "slower", "-codec:a", "libfdk_aac", "-movflags", "+faststart", "-vbr", "5"] -OUTPUT_METADATA_FIELD = ["episode_id"] +ORIGINAL_FOLDER = os.path.join(os.path.expanduser("~"), ".ImagesOriginaux") +MOVIE_EXTENSIONS = ["mov", "avi", "mp4", "3gp", "webm", "mkv"] +OUTPUT_EXTENSION = "webm" +OUTPUT_FFMPEG_PARAMETERS = ["-c:v", "libvpx-vp9", "-crf", "30", "-b:v", "0"] +# OUTPUT_FFMPEG_PARAMETERS = ["-c:v", "libaom-av1", "-crf", "30", "-strict", "experimental", "-c:a", "libopus"] +DURATION_MAX_DEV = 1 +def videoMetadata(filename): + assert os.path.isfile(filename) + cmd = ["ffmpeg", "-i", filename, "-f", "ffmetadata", "-"] + p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL) + p.check_returncode() + metadataRaw = p.stdout + data = dict() + for metadataLine in metadataRaw.split(b'\n'): + # Skip empty lines + if not len(metadataLine): + continue + # Skip comments + if metadataLine.startswith(b';'): + continue + # Parse key-value + metadataLineSplit = metadataLine.split(b'=') + if len(metadataLineSplit) != 2: + log.warning("Unparsed metadata line: `{}`".format(metadataLine)) + continue + key, val = metadataLineSplit + key = key.decode().lower() + val = val.decode() + data[key] = val + return data + +def videoInfos(filename): + assert os.path.isfile(filename) + cmd = ["ffprobe", filename, "-print_format", "json", "-show_streams"] + p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL) + p.check_returncode() + infosRaw = p.stdout + infos = json.loads(infosRaw) + return infos + +from pprint import pprint +def streamDuration(stream): + if "duration" in stream: + return float(stream["duration"]) + elif "sample_rate" in stream and "nb_frames" in stream: + return int(stream["nb_frames"]) / int(stream["sample_rate"]) + elif "tags" in stream and "DURATION" in stream["tags"]: + durRaw = stream["tags"]["DURATION"] + durSplit = durRaw.split(":") + assert len(durSplit) == 3 + durSplitFloat = [float(a) for a in durSplit] + hours, minutes, seconds = durSplitFloat + return (hours * 60 + minutes) * 60 + seconds + else: + raise KeyError("Can't find duration information in stream") + +def videoDuration(filename): + # TODO Doesn't work with VP8 / webm + infos = videoInfos(filename) + durations = [streamDuration(stream) for stream in infos["streams"]] + dev = statistics.stdev(durations) + assert dev <= DURATION_MAX_DEV, "Too much deviation ({} s)".format(dev) + return sum(durations)/len(durations) + + +todos = set() +totalSize = 0 +totalDuration = 0 + # Walk folders +log.info("Listing files in {}".format(PICTURES_FOLDER)) +allVideos = list() for root, dirs, files in os.walk(PICTURES_FOLDER): # If folder is in ORIGINAL_FOLDER, skip it - if root.startswith(ORIGNAL_FOLDER): + if root.startswith(ORIGINAL_FOLDER): continue # Iterate over files for inputName in files: @@ -27,137 +105,109 @@ for root, dirs, files in os.walk(PICTURES_FOLDER): if inputExt not in MOVIE_EXTENSIONS: continue - # Generates all needed filepaths - ## Found file - inputFull = os.path.join(root, inputName) - inputRel = os.path.relpath(inputFull, PICTURES_FOLDER) - ## Original file - originalFull = os.path.join(ORIGNAL_FOLDER, inputRel) - originalRel = inputRel - ## Compressed file - outputFull = os.path.join(root, inputNameBase + "." + OUTPUT_EXTENSION) + allVideos.append((root, inputName)) - # If the extension is the same of the output one - if inputExt == OUTPUT_EXTENSION: - # Read the metadata of the video - metadataRaw = subprocess.run(["ffmpeg", "-i", inputFull, "-f", "ffmetadata", "-"], stdout=subprocess.PIPE).stdout - # If it has the field with the original file - originalRel = None - wantedPattern = OUTPUT_METADATA_FIELD.encode() + b"=" - for metadataLine in metadataRaw.split('\n'): - if metadataLine.startswith(wantedPattern): - originalRel = metadataLine[len(wantedPattern)+1:] - break - if originalRel: - # If the original file does not exists, warn about it - originalFull = os.path.join(ORIGNAL_FOLDER, originalRel) - if not os.path.isfile(originalFull): - print("WARN {inputRel} states to have {originalRel} as original but this file doesn't exist".format(inputRel=inputRel, originalRel=originalRel)) - # If the original is not aligned with the compressed, warn about it (TODO move it automatically) - if inputRel != originalRel: - print("WARN {inputRel} is not aligned with original {originalRel}".format(inputRel=inputRel, originalRel=originalRel)) - # Skip file - continue - # Initiate a conversion in a temporary file - # If the temporary file does not have the same caracteristics as the original - # Warn about it - # Delete it - # Skip file - # Move the original to the corresponding original folder - # Move the converted file in place of the original +log.info("Analyzing videos") +for root, inputName in progressbar.progressbar(allVideos): + inputNameBase, inputExt = os.path.splitext(inputName) + inputExt = inputExt[1:].lower() -# TODO Iterate over the orignal folder to find non-matching compressed videos not found in the above pass + # Generates all needed filepaths + ## Found file + inputFull = os.path.join(root, inputName) + inputRel = os.path.relpath(inputFull, PICTURES_FOLDER) + ## Original file + originalFull = os.path.join(ORIGINAL_FOLDER, inputRel) + originalRel = inputRel + assert not os.path.isfile(originalFull), originalFile + " exists" -sys.exit(0) + ## Compressed file + outputFull = os.path.join(root, inputNameBase + "." + OUTPUT_EXTENSION) -# Constants -SOURCE_FOLDER = os.path.join(os.path.expanduser("~"), "Musique") -OUTPUT_FOLDER = os.path.join(os.path.expanduser("~"), ".MusiqueCompressed") -CONVERSIONS = {"flac": "m4a"} -FORBIDDEN_EXTENSIONS = ["jpg", "pdf", "ffs_db"] -FORGIVEN_FILENAMES = ["cover.jpg"] -IGNORED_EMPTY_FOLDER = [".stfolder"] + # If the extension is the same of the output one + if inputExt == OUTPUT_EXTENSION: + # Read the metadata of the video + meta = videoMetadata(inputFull) - - -# Listing files -sourceFiles = dict() -for root, dirs, files in os.walk(SOURCE_FOLDER): - for f in files: - fullPath = os.path.join(root, f) - path = os.path.relpath(fullPath, SOURCE_FOLDER) - sourceFiles[path] = os.path.getctime(fullPath) - -outputFiles = dict() -for root, dirs, files in os.walk(OUTPUT_FOLDER): - for f in files: - fullPath = os.path.join(root, f) - path = os.path.relpath(fullPath, OUTPUT_FOLDER) - outputFiles[path] = os.path.getctime(fullPath) - -# Sorting files -remainingConversions = dict() -extraFiles = list(outputFiles.keys()) - -def convertPath(path): - filename, extension = os.path.splitext(path) - extension = extension[1:].lower() - # If the extension isn't allowed - if extension in FORBIDDEN_EXTENSIONS: - basename = os.path.basename(path) - # And the filename is not an exception - if basename not in FORGIVEN_FILENAMES: - # This file shouldn't be copied nor converted - return False - # If this needs a conversion - elif extension in CONVERSIONS: - extension = CONVERSIONS[extension] - return filename + "." + extension - # In all other case, this is a simple copy - return path - -for sourceFile in sourceFiles: - outputFile = convertPath(sourceFile) - # If the file should not be converted, do nothing - if outputFile == False: - continue - # If the file already has something as an output - elif outputFile in outputFiles: - extraFiles.remove(outputFile) - # If the output file is newer than the source file, do not initiate a conversion - if outputFiles[outputFile] >= sourceFiles[sourceFile]: + # If it has the field with the original file + if 'original' in meta: + # Skip file continue - # If the file needs to be converted, do it - remainingConversions[sourceFile] = outputFile - -# Converting files -for sourceFile in remainingConversions: - outputFile = remainingConversions[sourceFile] - - # Creating folder if it doesn't exists - fullOutputFile = os.path.join(OUTPUT_FOLDER, outputFile) - fullOutputDir = os.path.dirname(fullOutputFile) - os.makedirs(fullOutputDir, exist_ok=True) - - # Converting - fullSourceFile = os.path.join(SOURCE_FOLDER, sourceFile) - print(fullSourceFile, "→", fullOutputFile) - if sourceFile == outputFile: - # shutil.copy(fullSourceFile, fullOutputFile) - os.link(fullSourceFile, fullOutputFile) else: - subprocess.run(["ffmpeg", "-y", "-i", fullSourceFile, "-codec:a", "libfdk_aac", "-cutoff", "18000", "-movflags", "+faststart", "-vbr", "5", fullOutputFile]) - -# Removing extra files -for extraFile in extraFiles: - fullExtraFile = os.path.join(OUTPUT_FOLDER, extraFile) - os.remove(fullExtraFile) - -# Removing empty dirs -for root, dirs, files in os.walk(OUTPUT_FOLDER): - if not dirs and not files: - dirBasename = os.path.basename(root) - if dirBasename not in IGNORED_EMPTY_FOLDER: - os.rmdir(root) + assert not os.path.isfile(outputFull), outputFull + " exists" + size = os.stat(inputFull).st_size + try: + duration = videoDuration(inputFull) + except Exception as e: + log.warning("Can't determine duration of {}, skipping".format(inputFull)) + log.debug(e, exc_info=True) + continue + + todo = (inputFull, originalFull, outputFull, size, duration) + + totalDuration += duration + totalSize += size + todos.add(todo) + +log.info("Converting {} videos ({})".format(len(todos), datetime.timedelta(seconds=totalDuration))) + +# From https://stackoverflow.com/a/3431838 +def sha256(fname): + hash_sha256 = hashlib.sha256() + with open(fname, "rb") as f: + for chunk in iter(lambda: f.read(131072), b""): + hash_sha256.update(chunk) + return hash_sha256.hexdigest() + +# Progress bar things +totalDataSize = progressbar.widgets.DataSize() +totalDataSize.variable = 'max_value' +barWidgets = [progressbar.widgets.DataSize(), ' of ', totalDataSize, ' ', progressbar.widgets.Bar(), ' ', progressbar.widgets.FileTransferSpeed(), ' ', progressbar.widgets.AdaptiveETA()] +bar = progressbar.DataTransferBar(max_value=totalSize, widgets=barWidgets) +bar.start() +processedSize = 0 + + +for inputFull, originalFull, outputFull, size, duration in todos: + tmpfile = tempfile.mkstemp(prefix="compressPictureMovies", suffix="."+OUTPUT_EXTENSION)[1] + try: + # Calculate the sum of the original file + checksum = sha256(inputFull) + + # Initiate a conversion in a temporary file + originalRel = os.path.relpath(originalFull, ORIGINAL_FOLDER) + originalContent = "{} {}".format(originalRel, checksum) + metadataCmd = ["-metadata", 'original="{}"'.format(originalContent)] + cmd = ["ffmpeg", "-hide_banner", "-y", "-i", inputFull] + OUTPUT_FFMPEG_PARAMETERS + metadataCmd + [tmpfile] + p = subprocess.run(cmd) + p.check_returncode() + + # Verify the durartion of the new file + newDuration = videoDuration(tmpfile) + dev = statistics.stdev((duration, newDuration)) + assert dev < DURATION_MAX_DEV, "Too much deviation in duration" + + # Move the original to the corresponding original folder + originalDir = os.path.dirname(originalFull) + os.makedirs(originalDir, exist_ok=True) + shutil.move(inputFull, originalFull) + + # Move the converted file in place of the original + shutil.move(tmpfile, outputFull) + except Exception as e: + log.error("Couldn't process file {}".format(inputFull)) + log.error(e, exc_info=True) + try: + os.unlink(tmpfile) + except Exception: + pass + # Progress bar things + processedSize += size + bar.update(processedSize) +bar.finish() + + +# TODO Iterate over the already compressed videos to assert the originals are +# in their correct place, else move them diff --git a/scripts/musiqueBof b/scripts/musiqueBof new file mode 100755 index 0000000..b0f9658 --- /dev/null +++ b/scripts/musiqueBof @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 + +import sys +import os +import shutil +import logging +import coloredlogs + +coloredlogs.install(level='DEBUG', fmt='%(levelname)s %(message)s') +log = logging.getLogger() + +MUSICS_FOLDER = os.path.join(os.path.expanduser("~"), "Musique") +BOF_FOLDER = os.path.join(os.path.expanduser("~"), ".MusiqueBof") + +for f in sys.argv[1:]: + src = os.path.realpath(f) + if not os.path.isfile(src): + log.error("{} does not exists".format(src)) + continue + + srcBase = None + if src.startswith(MUSICS_FOLDER): + srcBase = MUSICS_FOLDER + dstBase = BOF_FOLDER + elif src.startswith(BOF_FOLDER): + srcBase = BOF_FOLDER + dstBase = MUSIC_FOLDER + else: + log.error("{} not in any music folder".format(src)) + continue + + common = os.path.relpath(src, srcBase) + dst = os.path.join(dstBase, common) + dstFolder = os.path.dirname(dst) + + log.info("{} → {}".format(src, dst)) + os.makedirs(dstFolder, exist_ok=True) + shutil.move(src, dst) + diff --git a/scripts/replayGain b/scripts/replayGain index 5e0b7d9..c6178ce 100755 --- a/scripts/replayGain +++ b/scripts/replayGain @@ -4,94 +4,65 @@ # which is usually -89.0 dB import os -import subprocess import coloredlogs import logging -import progressbar +import r128gain +import sys coloredlogs.install(level='DEBUG', fmt='%(levelname)s %(message)s') log = logging.getLogger() +# TODO Remove debug # Constants -FORCE = False # TODO UX cli argument -SOURCE_FOLDER = os.path.join(os.path.expanduser("~"), "Musique") -GAIN_COMMANDS = {("flac",): ["metaflac", "--add-replay-gain"], - ("mp3",): ["mp3gain", "-a", "-k"] + (["-s", "r"] if FORCE else []), - ("m4a", "mp4"): ["aacgain", "-a", "-k"] + (["-s", "r"] if FORCE else []), - ("ogg",): ["vorbisgain", "--album"] + ([] if FORCE else ["--fast"]), - } +FORCE = '-f' in sys.argv +if FORCE: + sys.argv.remove('-f') +SOURCE_FOLDER = os.path.realpath(sys.argv[1]) if len(sys.argv) >= 2 else os.path.join(os.path.expanduser("~"), "Musique") -# Since metaflac ALWAYS recalculate the tags, we need to specificaly verify -# that the tags are present on every track of an album to skip it -def isFlacTagged(f): - # TODO PERF Run metaflac --show-tag for the whole album and compare the - # output with the number of files tagged - for tag in ["REPLAYGAIN_TRACK_GAIN", "REPLAYGAIN_ALBUM_GAIN"]: - cmd = ["metaflac", "--show-tag", tag, f] - proc = subprocess.run(cmd, stdout=subprocess.PIPE) - res = len(proc.stdout.strip()) > 0 - if not res: - return False - return True - -# TODO UX Do the same thing with other formats so the output is not -# inconsistent +def isMusic(f): + ext = os.path.splitext(f)[1][1:].lower() + return ext in r128gain.AUDIO_EXTENSIONS # Get album paths +log.info("Listing albums and tracks") albums = set() +singleFiles = set() for root, dirs, files in os.walk(SOURCE_FOLDER): relRoot = os.path.relpath(root, SOURCE_FOLDER) - # See if it's an album (only 2 components in the path) head, tail = os.path.split(relRoot) + # 1 component in the path: save files path as single if not len(head): - continue + for f in files: + if isMusic(f): + fullPath = os.path.join(root, f) + singleFiles.add(fullPath) head, tail = os.path.split(head) if len(head): continue + # 2 components in the path: save album path albums.add(root) -cmds = list() +log.info("Processing single files") +# r128gain.process(list(singleFiles), album_gain=False, skip_tagged=not FORCE, report=True) for album in albums: albumName = os.path.relpath(album, SOURCE_FOLDER) log.info("Processing album {}".format(albumName)) - musicFiles = dict() + musicFiles = set() for root, dirs, files in os.walk(album): for f in files: - ext = os.path.splitext(f)[1][1:].lower() + if isMusic(f): + fullPath = os.path.join(root, f) + musicFiles.add(fullPath) - for exts in GAIN_COMMANDS.keys(): - if ext in exts: - if exts not in musicFiles.keys(): - musicFiles[exts] = set() - fullPath = os.path.join(root, f) - musicFiles[exts].add(fullPath) - - if len(musicFiles) >= 2: - log.warn("Different extensions for album {}. AlbumGain won't be on par.".format(albumName)) - - for exts, files in musicFiles.items(): - - if exts == ("flac",) and not FORCE: - allTagged = True - for f in files: - if not isFlacTagged(f): - allTaged = False - break - if allTagged: - log.debug("Already tagged (for flac only)!") - break - - cmd = GAIN_COMMANDS[exts] + list(files) - log.debug("Registering command: `{}`".format(" ".join(cmd))) - cmds.append(cmd) - -logging.info("Executing commands") -for cmd in progressbar.progressbar(cmds): - subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + # print(musicFiles) + if not len(musicFiles): + continue + r128gain.process(list(musicFiles), album_gain=True, skip_tagged=not FORCE, report=True) + print("==============================") diff --git a/scripts/tagCreatorPhotos b/scripts/tagCreatorPhotos new file mode 100755 index 0000000..0002db7 --- /dev/null +++ b/scripts/tagCreatorPhotos @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 + +import sys +import os +import piexif + +assert len(sys.argv) >= 3, "Usage {} CREATOR FILENAMES...".format(sys.argv[0]) +creator = sys.argv[1] +filenames = sys.argv[2:] + +for filename in filenames: + assert os.path.isfile(filename) + exifDict = piexif.load(filename) + exifDict['0th'][piexif.ImageIFD.Copyright] = creator.encode() + exifBytes = piexif.dump(exifDict) + piexif.insert(exifBytes, filename) + diff --git a/vimpcrc b/vimpcrc index 97f442c..d3f547c 100644 --- a/vimpcrc +++ b/vimpcrc @@ -5,3 +5,4 @@ map ° D:browseA:shuffle:play:playlist set songformat {%a - %b: %t}|{%f}$E$R $H[$H%l$H]$H set libraryformat %n \| {%t}|{%f}$E$R $H[$H%l$H]$H set ignorecase +set sort library diff --git a/vimrc b/vimrc index 3f62987..733c99c 100644 --- a/vimrc +++ b/vimrc @@ -41,15 +41,30 @@ Plug 'tomtom/tcomment_vim' " Plug 'tomlion/vim-solidity' " Plug 'godlygeek/tabular' " Plug 'jrozner/vim-antlr' -Plug 'maralla/completor.vim' +" +" Plug 'maralla/completor.vim' +if has('nvim') + Plug 'Shougo/deoplete.nvim', { 'do': ':UpdateRemotePlugins' } +else + Plug 'Shougo/deoplete.nvim' + Plug 'roxma/nvim-yarp' + Plug 'roxma/vim-hug-neovim-rpc' +endif +Plug 'zchee/deoplete-jedi' + Plug 'python-mode/python-mode', { 'branch': 'develop' } Plug 'junegunn/fzf', {'do': './install --bin'} Plug 'junegunn/fzf.vim' Plug 'ervandew/supertab' Plug 'dpelle/vim-LanguageTool' +Plug 'terryma/vim-smooth-scroll' call plug#end() +""" COMPLETOR """ + +let g:deoplete#enable_at_startup = 1 + """ UNDOTREE """ nmap :UndotreeToggle @@ -69,7 +84,7 @@ let g:airline#extensions#tabline#enabled = 1 let g:airline_section_a = airline#section#create(['mode']) let g:airline_section_b = airline#section#create(['branch', 'hunks']) let g:airline_section_z = airline#section#create(['%B', '@', '%l', ':', '%c']) -let g:airline_theme = 'base16' +let g:airline_theme = 'base16_monokai' """ AUTOFORMAT """ nmap :Autoformat @@ -203,7 +218,14 @@ vmap nmap o nmap :bp nmap :bn -nmap kkkkkkkkkkkkkkkkkkkkk -nmap jjjjjjjjjjjjjjjjjjjjj +if has('nvim') + " nmap 20k + " nmap 20j + noremap :call smooth_scroll#up(20, 5, 1) + noremap :call smooth_scroll#down(20, 5, 1) +else + nmap kkkkkkkkkkkkkkkkkkkkk + nmap jjjjjjjjjjjjjjjjjjjjj +endif From 86bc146125ed66afb0bcd37bc59906cf3f295813 Mon Sep 17 00:00:00 2001 From: Geoffrey Frogeye Date: Mon, 13 Aug 2018 12:20:09 +0200 Subject: [PATCH 15/22] Things --- Xresources.d/urxvt | 2 +- bashrc | 64 +++++++++++------ scripts/mel | 141 ++++++++++++++++++++++++++++++++++++ scripts/melConf | 176 +++++++++++++++++++++++++++++++++++++++++++++ scripts/optimize | 32 +++++---- vimrc | 2 +- 6 files changed, 380 insertions(+), 37 deletions(-) create mode 100755 scripts/mel create mode 100755 scripts/melConf diff --git a/Xresources.d/urxvt b/Xresources.d/urxvt index 08bf87e..e94c0e2 100644 --- a/Xresources.d/urxvt +++ b/Xresources.d/urxvt @@ -22,7 +22,7 @@ URxvt*scrollBar: false ! Font declaration -URxvt.font: xft:DejaVu Sans Mono for Powerline:size=12:antialias=true +URxvt.font: xft:DejaVu Sans Mono for Powerline:size=12:antialias=true,xft:Symbola:size=12:antialias=true ! Font spacing URxvt.letterSpace: 0 diff --git a/bashrc b/bashrc index fda1e9a..3d3c919 100644 --- a/bashrc +++ b/bashrc @@ -43,6 +43,9 @@ alias rm='rm -Iv --one-file-system' alias free='free -m' alias df='df -h' alias dmesg='dmesg --ctime' +alias ffmpeg='ffmpeg -hide_banner' +alias ffprobe='ffprobe -hide_banner' +alias ffplay='ffplay -hide_banner' # Frequent mistakes alias sl=ls @@ -62,31 +65,48 @@ alias tracefiles="strace -f -t -e trace=file" alias n='urxvtc &' # Superseding commands with better ones if they are present -function vi() { - if which vim &> /dev/null; then - alias vi='vim' - fi - vim "$@" +function _do_rank() { # executables... -- arguments... + for ex in "$@" + do + [ "$ex" == "--" ] && break + if which "$ex" &> /dev/null + then + for al in "$@" + do + shift + [ "$al" == "--" ] && break + alias "$al"="$ex" + done + "$ex" "$@" + return $? + fi + done + for ex in "$@" + do + [ "$al" == "--" ] && break + if -z "$list" + then + list=$ex + else + list=$list, $ex + fi + done + echo "Not installed: $list" } -function pass() { - if which gopass &> /dev/null; then - alias pass='gopass' - fi - gopass "$@" -} -function wol() { - if which wakeonlan &> /dev/null; then - alias wol='wakeonlan' - fi - wakeonlan "$@" -} -function mutt() { - if which neomutt &> /dev/null; then - alias mutt='neomutt' - fi - neomutt "$@" + +function _install_rank() { # executables... + for ex in "$@" + do + list=$@ + alias "$ex"="_do_rank $list --" + done } +_install_rank nvim vim vi +_install_rank gopass pass +_install_rank wakeonlan wol +_install_rank neomutt mutt + # SHELL CUSTOMIZATION complete -cf sudo diff --git a/scripts/mel b/scripts/mel new file mode 100755 index 0000000..2eecbe4 --- /dev/null +++ b/scripts/mel @@ -0,0 +1,141 @@ +#!/usr/bin/env python3 + +""" +Meh mail client +""" + +import notmuch +import logging +import coloredlogs +import colorama +import datetime +import os +import progressbar +import time + +colorama.init() +coloredlogs.install(level='DEBUG', fmt='%(levelname)s %(message)s') +log = logging.getLogger() + +log.debug("Loading database") + +db = notmuch.Database(mode=notmuch.Database.MODE.READ_WRITE) +# TODO Open read-only when needed + +log.debug("Database loaded") + +def get_location(msg): + path = msg.get_filename() + path = os.path.dirname(path) + base = db.get_path() + assert path.startswith(base) + path = path[len(base):] + _, mailbox, folder, state = path.split('/') + assert state in {'cur', 'tmp', 'new'} + return (mailbox, folder, state) + +MAILBOX_COLORS = dict() + +def get_mailbox_color(mailbox): + if mailbox not in MAILBOX_COLORS: + colorfile = os.path.join(db.get_path(), mailbox, 'color') + assert os.path.isfile(colorfile) + with open(colorfile, 'r') as f: + colorStr = f.read() + colorStr = colorStr[1:] if colorStr[0] == '#' else colorStr + R = int(colorStr[0:2], 16) + G = int(colorStr[2:4], 16) + B = int(colorStr[4:6], 16) + MAILBOX_COLORS[mailbox] = '\x1b[38;2;{};{};{}m'.format(R, G, B) + return MAILBOX_COLORS[mailbox] + +def format_date(date): + now = datetime.datetime.now() + midnight = datetime.datetime(year=now.year, month=now.month, day=now.day) + if date > midnight: + return date.strftime('%H:%M:%S') + else: + return date.strftime('%d/%m/%y') + + +def print_msg(msg): + line = "" + tags = set(msg.get_tags()) + mailbox, folder, state = get_location(msg) + line += get_mailbox_color(mailbox) + + # Date + date = datetime.datetime.fromtimestamp(msg.get_date()) + line += format_date(date) + + # Icons + line += " " + def tags2col1(tag1, tag2, both, first, second, none): + nonlocal line + if tag1 in tags: + if tag2 in tags: + line += both + else: + line += first + else: + if tag2 in tags: + line += second + else: + line += none + + tags2col1('spam', 'draft', '??', '💥', '📝', ' ') + tags2col1('attachment', 'encrypted', '🔐', '📎', '🔑', ' ') + tags2col1('unread', 'flagged', '🏁', '🏳 ', '🏴', ' ') + tags2col1('sent', 'replied', '?', '↑', '↪', ' ') + + # TODO To: / From: + + # Subject + line += " " + line += msg.get_header("subject") + line += colorama.Style.RESET_ALL + print(line) + + +def retag_msg(msg): + msg.freeze() + mailbox, folder, state = get_location(msg) + + # Search-friendly folder name + if folder.startswith('INBOX.'): + folder = folder[6:] + folder = folder.upper() + + msg.remove_all_tags() + if folder.startswith('JUNK') or folder.startswith('SPAM'): + msg.add_tag('spam') + if folder.startswith('DRAFT'): + msg.add_tag('draft') + if folder.startswith('INBOX'): + msg.add_tag('inbox') + + # TODO 'sent' tag + + # Save + msg.thaw() + msg.tags_to_maildir_flags() + + +def applyMsgs(queryStr, function, useProgressbar=False): + query = notmuch.Query(db, queryStr) + query.set_sort(notmuch.Query.SORT.OLDEST_FIRST) + msgs = query.search_messages() + if useProgressbar: + nbMsgs = query.count_messages() + iterator = progressbar.progressbar(msgs, max_value=nbMsgs) + else: + iterator = msgs + for msg in iterator: + function(msg) + +applyMsgs('tag:inbox', print_msg) +# applyMsgs('tag:spam', print_msg) +# applyMsgs('tag:unread', print_msg) +# applyMsgs('from:geoffrey@frogeye.fr', print_msg) + +# applyMsgs('*', retag_msg, useProgressbar=True) diff --git a/scripts/melConf b/scripts/melConf new file mode 100755 index 0000000..3e850da --- /dev/null +++ b/scripts/melConf @@ -0,0 +1,176 @@ +#!/usr/bin/env python3 + +""" +Meh mail client conf generator for other things +""" + +import configparser +import os +import sys + +# TODO Find config file from XDG +# TODO Alias adresses +# TODO Signature file +# TODO Write ~/.mail/[mailbox]/color file if required by sth? +# Certificate file + +configPath = os.path.join(os.path.expanduser('~'), '.config', 'mel.conf') + +config = configparser.ConfigParser() +config.read(configPath) + +SERVER_DEFAULTS = { + "imap": {"port": 143, "starttls": True}, + "smtp": {"port": 587, "starttls": True}, + } +SERVER_ITEMS = {"host", "port", "user", "pass", "starttls"} + +# Reading sections +accounts = dict() + +for name in config.sections(): + if not name.islower(): + continue + section = config[name] + + data = dict() + for server in SERVER_DEFAULTS.keys(): + for item in SERVER_ITEMS: + key = server + item + try: + val = section.get(key) or section.get(item) or SERVER_DEFAULTS[server][item] + except KeyError: + raise KeyError("{}.{}".format(name, key)) + + if isinstance(val, str): + if val == "True": + val = True + elif val == "False": + val = False + elif val.isnumeric(): + val = int(val) + data[key] = val + + for key in section.keys(): + if key in SERVER_ITEMS: + continue + data[key] = section[key] + + data["name"] = name + data["storage"] = os.path.join(config['GLOBAL']['storage'], name) + data["storageInbox"] = os.path.join(data["storage"], "INBOX") + storageFull = os.path.expanduser(data["storage"]) + os.makedirs(storageFull, exist_ok=True) + accounts[name] = data + +# OfflineIMAP + +OFFLINEIMAP_BEGIN = """[general] +# List of accounts to be synced, separated by a comma. +accounts = {} +maxsyncaccounts = {} +stocktimeout = 60 +pythonfile = ~/.config/offlineimap.py + +[mbnames] +enabled = yes +filename = ~/.mutt/mailboxes +header = "mailboxes " +peritem = "+%(accountname)s/%(foldername)s" +sep = " " +footer = "\\n" + +""" + +OFFLINEIMAP_ACCOUNT = """[Account {name}] +localrepository = {name}-local +remoterepository = {name}-remote +autorefresh = 0.5 +quick = 10 +utf8foldernames = yes +postsynchook = ~/.mutt/postsync + +[Repository {name}-local] +type = Maildir +localfolders = {storage} + +[Repository {name}-remote] +type = IMAP +{secconf} +keepalive = 60 +holdconnectionopen = yes +remotehost = {imaphost} +remoteport = {imapport} +remoteuser = {imapuser} +remotepass = {imappass} + +""" + +offlineIMAPstr = OFFLINEIMAP_BEGIN.format(','.join(accounts), len(accounts)) +for name, account in accounts.items(): + if account["imapstarttls"]: + secconf = "ssl = no" + else: + secconf = "sslcacertfile = /etc/ssl/certs/ca-certificates.crt" + offlineIMAPstr += OFFLINEIMAP_ACCOUNT.format(**account, secconf=secconf) +# TODO Write + +# mbsync +MBSYNC_ACCOUNT = """IMAPAccount {name} +Host {imaphost} +User {imapuser} +Pass "{imappass}" +{secconf} + +IMAPStore {name}-remote +Account {name} + +MaildirStore {name}-local +Subfolders Verbatim +Path {storage}/ +Inbox {storageInbox}/ + +Channel {name} +Master :{name}-remote: +Slave :{name}-local: +Patterns * +Create Both +SyncState * + +""" + +mbsyncStr = "" +for name, account in accounts.items(): + if account["imapstarttls"]: + secconf = "SSLType STARTTLS" + else: + secconf = "SSLType IMAPS" + mbsyncStr += MBSYNC_ACCOUNT.format(**account, secconf=secconf) +msbsyncFilepath = os.path.join(os.path.expanduser('~'), '.mbsyncrc') +with open(msbsyncFilepath, 'w') as f: + f.write(mbsyncStr) + +# msmtp +MSMTP_BEGIN = """defaults +protocol smtp +auth on +tls_trust_file /etc/ssl/certs/ca-certificates.crt + +""" + +MSMTP_ACCOUNT = """account {name} +from {from} +user {smtpuser} +password {smtppass} +host {smtphost} +port {smtpport} +tls on + +""" + +msmtpStr = MSMTP_BEGIN +for name, account in accounts.items(): + msmtpStr += MSMTP_ACCOUNT.format(**account) +msbsyncFilepath = os.path.join(os.path.expanduser('~'), '.msmtprc') +with open(msbsyncFilepath, 'w') as f: + f.write(msmtpStr) diff --git a/scripts/optimize b/scripts/optimize index bbb3543..d127d83 100755 --- a/scripts/optimize +++ b/scripts/optimize @@ -146,20 +146,26 @@ do done <<< "$(find "$dir" -type f -iname "*.png")" -# SVG (requires svgo) -while read image -do - if [ -z "$image" ]; then continue; fi - echo Processing $image +# # SVG (requires scour) +# while read image +# do +# if [ -z "$image" ]; then continue; fi +# echo Processing $image +# +# temp=$(mktemp --suffix .svg) +# scour --quiet "$image" "$temp" --no-line-breaks +# echo "→ Optimize done" +# +# replaceImg "$temp" "$image" +# +# done <<< "$(find "$dir" -type f -iname "*.svg")" - temp=$(mktemp --suffix .svg) - cp "$image" "$temp" - svgo --quiet --config $HOME/.config/optiSvgo.yml "$temp" - echo "→ Optimize done" - - replaceImg "$temp" "$image" - -done <<< "$(find "$dir" -type f -iname "*.svg")" +# NOTE Explicitely disabled since: +# - I only have ~50 MiB of SVG in TOTAL +# - Most conversions are not image losseless +# - Even when they are losseless, they are mostly worthless +# - I might want to keep editor data and/or ids for some of them +# So rather use scour explicitely when needed cleandev diff --git a/vimrc b/vimrc index 733c99c..a8e9d10 100644 --- a/vimrc +++ b/vimrc @@ -52,7 +52,7 @@ else endif Plug 'zchee/deoplete-jedi' -Plug 'python-mode/python-mode', { 'branch': 'develop' } +" Plug 'python-mode/python-mode', { 'branch': 'develop' } Plug 'junegunn/fzf', {'do': './install --bin'} Plug 'junegunn/fzf.vim' Plug 'ervandew/supertab' From 18a7278422c49ecbb717453b2097833d919ba1be Mon Sep 17 00:00:00 2001 From: Geoffrey Frogeye Date: Mon, 13 Aug 2018 17:59:40 +0200 Subject: [PATCH 16/22] mel base --- scripts/mel | 139 +++++++++++++++++++++++++++++++++++++++--------- scripts/melConf | 95 +++++++++++++++++++++++++-------- 2 files changed, 188 insertions(+), 46 deletions(-) diff --git a/scripts/mel b/scripts/mel index 2eecbe4..4922bd1 100755 --- a/scripts/mel +++ b/scripts/mel @@ -4,6 +4,9 @@ Meh mail client """ +# TODO Features +# TODO (only then) Refactor + import notmuch import logging import coloredlogs @@ -12,14 +15,40 @@ import datetime import os import progressbar import time +import argparse +import configparser +import base64 +import shutil colorama.init() coloredlogs.install(level='DEBUG', fmt='%(levelname)s %(message)s') log = logging.getLogger() +log.debug("Loading config") + +# TODO XDG +configPath = os.path.join(os.path.expanduser('~'), '.config', 'mel', 'accounts.conf') + +config = configparser.ConfigParser() +config.read(configPath) + +# Reading config a bit +accounts = dict() +mails = set() +for name in config.sections(): + if not name.islower(): + continue + section = config[name] + mails.add(section["from"]) + if "alternatives" in section: + for alt in section["alternatives"].split(";"): + mails.add(alt) + accounts[name] = section + log.debug("Loading database") -db = notmuch.Database(mode=notmuch.Database.MODE.READ_WRITE) +dbPath = os.path.realpath(os.path.expanduser(config["GENERAL"]["storage"])) +db = notmuch.Database(mode=notmuch.Database.MODE.READ_WRITE, path=dbPath) # TODO Open read-only when needed log.debug("Database loaded") @@ -30,7 +59,10 @@ def get_location(msg): base = db.get_path() assert path.startswith(base) path = path[len(base):] - _, mailbox, folder, state = path.split('/') + pathSplit = path.split('/') + mailbox = pathSplit[1] + state = pathSplit[-1] + folder = tuple(pathSplit[2:-1]) assert state in {'cur', 'tmp', 'new'} return (mailbox, folder, state) @@ -38,10 +70,7 @@ MAILBOX_COLORS = dict() def get_mailbox_color(mailbox): if mailbox not in MAILBOX_COLORS: - colorfile = os.path.join(db.get_path(), mailbox, 'color') - assert os.path.isfile(colorfile) - with open(colorfile, 'r') as f: - colorStr = f.read() + colorStr = config[mailbox]["color"] colorStr = colorStr[1:] if colorStr[0] == '#' else colorStr R = int(colorStr[0:2], 16) G = int(colorStr[2:4], 16) @@ -57,14 +86,50 @@ def format_date(date): else: return date.strftime('%d/%m/%y') +def threadIdToB64(tid): + assert len(tid) == 16 + tidInt = int(tid, 16) + tidBytes = tidInt.to_bytes(8, 'big') + tidB64 = base64.b64encode(tidBytes) + assert len(tidB64) == 12 + return tidB64.decode() + +WIDTH_FIXED = 30 +WIDTH_RATIO_DEST_SUBJECT = 0.3 +destWidth = None +subjectWidth = None +def compute_line_format(): + columns, rows = shutil.get_terminal_size((80, 20)) + remain = columns - WIDTH_FIXED - 1 + global destWidth, subjectWidth + destWidth = int(remain * WIDTH_RATIO_DEST_SUBJECT) + subjectWidth = remain - destWidth + +def clip_text(size, text): + l = len(text) + if l == size: + return text + elif l > size: + return text[:size-1] + '…' + elif l < size: + return text + " " * (size - l) + def print_msg(msg): + if not destWidth: + compute_line_format() + line = "" tags = set(msg.get_tags()) mailbox, folder, state = get_location(msg) line += get_mailbox_color(mailbox) + # ID + line += threadIdToB64(msg.get_thread_id()) + # line += str(int(msg.get_thread_id(), 16)) + # Date + line += " " date = datetime.datetime.fromtimestamp(msg.get_date()) line += format_date(date) @@ -83,16 +148,23 @@ def print_msg(msg): else: line += none - tags2col1('spam', 'draft', '??', '💥', '📝', ' ') - tags2col1('attachment', 'encrypted', '🔐', '📎', '🔑', ' ') - tags2col1('unread', 'flagged', '🏁', '🏳 ', '🏴', ' ') + tags2col1('spam', 'draft', '?', 'S', 'D', ' ') + tags2col1('attachment', 'encrypted', 'E', 'A', 'E', ' ') + tags2col1('unread', 'flagged', '!', 'U', 'F', ' ') tags2col1('sent', 'replied', '?', '↑', '↪', ' ') - # TODO To: / From: + if 'sent' in tags: + dest = msg.get_header("to") + else: + dest = msg.get_header("from") + line += " " + line += clip_text(destWidth, dest) # Subject line += " " - line += msg.get_header("subject") + subject = msg.get_header("subject") + line += clip_text(subjectWidth, subject) + line += colorama.Style.RESET_ALL print(line) @@ -102,26 +174,33 @@ def retag_msg(msg): mailbox, folder, state = get_location(msg) # Search-friendly folder name - if folder.startswith('INBOX.'): - folder = folder[6:] - folder = folder.upper() + slugFolderList = list() + for f, fold in [(f, folder[f]) for f in range(len(folder))]: + if f == 0 and len(folder) > 1 and fold == "INBOX": + continue + slugFolderList.append(fold.upper()) + slugFolder = tuple(slugFolderList) - msg.remove_all_tags() - if folder.startswith('JUNK') or folder.startswith('SPAM'): - msg.add_tag('spam') - if folder.startswith('DRAFT'): - msg.add_tag('draft') - if folder.startswith('INBOX'): - msg.add_tag('inbox') + tags = set(msg.get_tags()) + def tag_if(tag, condition): + if condition and tag not in tags: + msg.add_tag(tag) + elif not condition and tag in tags: + msg.remove_tag(tag) + expeditor = extract_email(msg.get_header('from')) - # TODO 'sent' tag + tag_if('inbox', slugFolder[0] == 'INBOX') + tag_if('spam', slugFolder[0] == 'JUNK' or slugFolder[0] == 'SPAM') + tag_if('deleted', slugFolder[0] == 'TRASH') + tag_if('draft', slugFolder[0] == 'DRAFTS') + tag_if('sent', expeditor in mails) # Save msg.thaw() msg.tags_to_maildir_flags() -def applyMsgs(queryStr, function, useProgressbar=False): +def applyMsgs(queryStr, function, *args, useProgressbar=False, **kwargs): query = notmuch.Query(db, queryStr) query.set_sort(notmuch.Query.SORT.OLDEST_FIRST) msgs = query.search_messages() @@ -131,11 +210,23 @@ def applyMsgs(queryStr, function, useProgressbar=False): else: iterator = msgs for msg in iterator: - function(msg) + function(msg, *args, **kwargs) +def extract_email(field): + try: + sta = field.index('<') + sto = field.index('>') + return field[sta+1:sto] + except ValueError: + return field + +# applyMsgs('*', print_msg) applyMsgs('tag:inbox', print_msg) # applyMsgs('tag:spam', print_msg) # applyMsgs('tag:unread', print_msg) +# applyMsgs('tag:unprocessed', print_msg) # applyMsgs('from:geoffrey@frogeye.fr', print_msg) +# applyMsgs('tag:unprocessed', retag_msg, useProgressbar=True) # applyMsgs('*', retag_msg, useProgressbar=True) + diff --git a/scripts/melConf b/scripts/melConf index 3e850da..a0c2829 100755 --- a/scripts/melConf +++ b/scripts/melConf @@ -9,16 +9,18 @@ import os import sys # TODO Find config file from XDG -# TODO Alias adresses # TODO Signature file # TODO Write ~/.mail/[mailbox]/color file if required by sth? -# Certificate file +# TODO Fix IMAPS with mbsync -configPath = os.path.join(os.path.expanduser('~'), '.config', 'mel.conf') +configPath = os.path.join(os.path.expanduser('~'), '.config', 'mel', 'accounts.conf') config = configparser.ConfigParser() config.read(configPath) +storageFull = os.path.realpath(os.path.expanduser(config["GENERAL"]["storage"])) +config["GENERAL"]["storage"] = storageFull + SERVER_DEFAULTS = { "imap": {"port": 143, "starttls": True}, "smtp": {"port": 587, "starttls": True}, @@ -27,6 +29,7 @@ SERVER_ITEMS = {"host", "port", "user", "pass", "starttls"} # Reading sections accounts = dict() +mails = set() for name in config.sections(): if not name.islower(): @@ -56,13 +59,23 @@ for name in config.sections(): continue data[key] = section[key] - data["name"] = name - data["storage"] = os.path.join(config['GLOBAL']['storage'], name) + mails.add(section["from"]) + if "alternatives" in section: + for alt in section["alternatives"].split(";"): + mails.add(alt) + + data["account"] = name + data["storage"] = os.path.join(config['GENERAL']['storage'], name) data["storageInbox"] = os.path.join(data["storage"], "INBOX") - storageFull = os.path.expanduser(data["storage"]) - os.makedirs(storageFull, exist_ok=True) accounts[name] = data +general = dict() +section = config["GENERAL"] +for key in section.keys(): + general[key] = section[key] +general["main"] = accounts[general["main"]] + + # OfflineIMAP OFFLINEIMAP_BEGIN = """[general] @@ -82,19 +95,19 @@ footer = "\\n" """ -OFFLINEIMAP_ACCOUNT = """[Account {name}] -localrepository = {name}-local -remoterepository = {name}-remote +OFFLINEIMAP_ACCOUNT = """[Account {account}] +localrepository = {account}-local +remoterepository = {account}-remote autorefresh = 0.5 quick = 10 utf8foldernames = yes postsynchook = ~/.mutt/postsync -[Repository {name}-local] +[Repository {account}-local] type = Maildir localfolders = {storage} -[Repository {name}-remote] +[Repository {account}-remote] type = IMAP {secconf} keepalive = 60 @@ -116,23 +129,24 @@ for name, account in accounts.items(): # TODO Write # mbsync -MBSYNC_ACCOUNT = """IMAPAccount {name} +MBSYNC_ACCOUNT = """IMAPAccount {account} Host {imaphost} +Port {imapport} User {imapuser} -Pass "{imappass}" +Pass "{imappassEscaped}" {secconf} -IMAPStore {name}-remote -Account {name} +IMAPStore {account}-remote +Account {account} -MaildirStore {name}-local +MaildirStore {account}-local Subfolders Verbatim Path {storage}/ Inbox {storageInbox}/ -Channel {name} -Master :{name}-remote: -Slave :{name}-local: +Channel {account} +Master :{account}-remote: +Slave :{account}-local: Patterns * Create Both SyncState * @@ -145,7 +159,10 @@ for name, account in accounts.items(): secconf = "SSLType STARTTLS" else: secconf = "SSLType IMAPS" - mbsyncStr += MBSYNC_ACCOUNT.format(**account, secconf=secconf) + if "certificate" in account: + secconf += "\nCertificateFile {certificate}".format(**account) + imappassEscaped = account["imappass"].replace("\\", "\\\\") + mbsyncStr += MBSYNC_ACCOUNT.format(**account, secconf=secconf, imappassEscaped=imappassEscaped) msbsyncFilepath = os.path.join(os.path.expanduser('~'), '.mbsyncrc') with open(msbsyncFilepath, 'w') as f: f.write(mbsyncStr) @@ -158,7 +175,7 @@ tls_trust_file /etc/ssl/certs/ca-certificates.crt """ -MSMTP_ACCOUNT = """account {name} +MSMTP_ACCOUNT = """account {account} from {from} user {smtpuser} password {smtppass} @@ -174,3 +191,37 @@ for name, account in accounts.items(): msbsyncFilepath = os.path.join(os.path.expanduser('~'), '.msmtprc') with open(msbsyncFilepath, 'w') as f: f.write(msmtpStr) + + +# notmuch +NOTMUCH_BEGIN = """[database] +path={storage} + +[user] +name={main[name]} +primary_email={main[from]} +other_email={other_email} + +[new] +tags=unprocessed; +ignore= + +[search] +exclude_tags=deleted;spam; + +[maildir] +synchronize_flags=true + +[crypto] +gpg_path=gpg + +""" + +other_email = mails.copy() +other_email.remove(general["main"]["from"]) +other_email = ";".join(other_email) +notmuchStr = NOTMUCH_BEGIN.format(**general, other_email=other_email) +msbsyncFilepath = os.path.join(os.path.expanduser('~'), '.notmuchrc') +with open(msbsyncFilepath, 'w') as f: + f.write(notmuchStr) + From 47cdc830a00ffd2c33b373bf41888d91ac138a29 Mon Sep 17 00:00:00 2001 From: Geoffrey Frogeye Date: Tue, 14 Aug 2018 10:08:59 +0200 Subject: [PATCH 17/22] mel2 --- scripts/mel | 193 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 149 insertions(+), 44 deletions(-) diff --git a/scripts/mel b/scripts/mel index 4922bd1..7a4dae5 100755 --- a/scripts/mel +++ b/scripts/mel @@ -19,39 +19,25 @@ import argparse import configparser import base64 import shutil +import argparse +import xdg.BaseDirectory +import sys +import subprocess -colorama.init() -coloredlogs.install(level='DEBUG', fmt='%(levelname)s %(message)s') -log = logging.getLogger() +ACCOUNTS = dict() +ALIASES = set() -log.debug("Loading config") +def generate_aliases(): + for name in config.sections(): + if not name.islower(): + continue + section = config[name] + ALIASES.add(section["from"]) + if "alternatives" in section: + for alt in section["alternatives"].split(";"): + ALIASES.add(alt) + ACCOUNTS[name] = section -# TODO XDG -configPath = os.path.join(os.path.expanduser('~'), '.config', 'mel', 'accounts.conf') - -config = configparser.ConfigParser() -config.read(configPath) - -# Reading config a bit -accounts = dict() -mails = set() -for name in config.sections(): - if not name.islower(): - continue - section = config[name] - mails.add(section["from"]) - if "alternatives" in section: - for alt in section["alternatives"].split(";"): - mails.add(alt) - accounts[name] = section - -log.debug("Loading database") - -dbPath = os.path.realpath(os.path.expanduser(config["GENERAL"]["storage"])) -db = notmuch.Database(mode=notmuch.Database.MODE.READ_WRITE, path=dbPath) -# TODO Open read-only when needed - -log.debug("Database loaded") def get_location(msg): path = msg.get_filename() @@ -61,6 +47,7 @@ def get_location(msg): path = path[len(base):] pathSplit = path.split('/') mailbox = pathSplit[1] + assert mailbox in ACCOUNTS state = pathSplit[-1] folder = tuple(pathSplit[2:-1]) assert state in {'cur', 'tmp', 'new'} @@ -193,25 +180,14 @@ def retag_msg(msg): tag_if('spam', slugFolder[0] == 'JUNK' or slugFolder[0] == 'SPAM') tag_if('deleted', slugFolder[0] == 'TRASH') tag_if('draft', slugFolder[0] == 'DRAFTS') - tag_if('sent', expeditor in mails) + tag_if('sent', expeditor in ALIASES) + # TODO remove unprocessed # Save msg.thaw() msg.tags_to_maildir_flags() -def applyMsgs(queryStr, function, *args, useProgressbar=False, **kwargs): - query = notmuch.Query(db, queryStr) - query.set_sort(notmuch.Query.SORT.OLDEST_FIRST) - msgs = query.search_messages() - if useProgressbar: - nbMsgs = query.count_messages() - iterator = progressbar.progressbar(msgs, max_value=nbMsgs) - else: - iterator = msgs - for msg in iterator: - function(msg, *args, **kwargs) - def extract_email(field): try: sta = field.index('<') @@ -220,8 +196,25 @@ def extract_email(field): except ValueError: return field +def applyMsgs(queryStr, action, *args, showProgress=False, **kwargs): + log.info("Querying {}".format(queryStr)) + query = notmuch.Query(db, queryStr) + query.set_sort(notmuch.Query.SORT.OLDEST_FIRST) + + elements = query.search_messages() + + if showProgress: + nbMsgs = query.count_messages() + iterator = progressbar.progressbar(elements, max_value=nbMsgs) + else: + iterator = elements + + log.info("Executing {}".format(action)) + for msg in iterator: + action(msg, *args, **kwargs) + # applyMsgs('*', print_msg) -applyMsgs('tag:inbox', print_msg) +# applyMsgs('tag:inbox', print_msg) # applyMsgs('tag:spam', print_msg) # applyMsgs('tag:unread', print_msg) # applyMsgs('tag:unprocessed', print_msg) @@ -230,3 +223,115 @@ applyMsgs('tag:inbox', print_msg) # applyMsgs('tag:unprocessed', retag_msg, useProgressbar=True) # applyMsgs('*', retag_msg, useProgressbar=True) +if __name__ == "__main__": + # Main arguments + parser = argparse.ArgumentParser(description="Meh mail client") + selectedVerbosityLevels = ["DEBUG", "INFO", "WARNING", "ERROR", "FATAL"] + parser.add_argument('-v', '--verbosity', choices=selectedVerbosityLevels, default='WARNING', help="Verbosity of log messages") + # parser.add_argument('-n', '--dry-run', action='store_true', help="Don't do anything") # DEBUG + defaultConfigFile = os.path.join(xdg.BaseDirectory.xdg_config_home, 'mel', 'accounts.conf') + parser.add_argument('-c', '--config', default=defaultConfigFile, help="Accounts config file") + + parser.set_defaults(dbmode=notmuch.Database.MODE.READ_ONLY) + parser.set_defaults(showProgress=False) + parser.set_defaults(useThreads=False) + parser.set_defaults(actionBefore=None) + parser.set_defaults(actionAfter=None) + parser.set_defaults(action=None) + + subparsers = parser.add_subparsers(help="Action to execute", required=True) + + + ## List messages + + + # inbox (default) + def func_inbox(args): + queryStr = 'tag:unread' if args.only_unread else 'tag:inbox' + applyMsgs(queryStr, print_msg) + + parserInbox = subparsers.add_parser("inbox", help="Show unread, unsorted and flagged messages") + parserInbox.add_argument('-u', '--only-unread', action='store_true', help="Show unread messages only") + # TODO Make this more relevant + parserInbox.set_defaults(func=func_inbox) + + + # list folder [--recurse] + ## List actions + # delete msg... + # spam msg... + # flag msg... + # ↑un* equivalents + # move dest msg... + ## Read message + # read msg [--html] [--plain] [--browser] + # attach msg [id] [--save] (list if no id, xdg-open else) + ## Redaction + # new account + # reply msg [--all] + ## Folder management + # mkdir folder + # rmdir folder (prevent if folder isn't empty (mail/subfolder)) + # (yeah that should do) + ## Meta + # setup (interactive thing maybe) + + + # fetch (mbsync, notmuch new, retag, notify; called by greater gods) + def func_fetch(args): + # Fetch mails + log.info("Fetching mails") + mbsyncConfigPath = os.path.expanduser("~/.mbsyncrc") # TODO Better + cmd = ["mbsync", "--config", mbsyncConfigPath, "--all"] + subprocess.run(cmd) + + # Index new mails + log.info("Indexing mails") + log.error("TODO Can't `notmuch new` when database is already open!") + notmuchConfigPath = os.path.expanduser("~/.notmuchrc") # TODO Better + cmd = ["notmuch", "--config", notmuchConfigPath, "new"] + log.debug(" ".join(cmd)) + subprocess.run(cmd) + + # Tag new mails + applyMsgs('tag:unprocessed', retag_msg, showProgress=True) + + # Notify + log.info("Notifying new mails") + # TODO Maybe before retag, notify unprocessed && unread + + parserFetch = subparsers.add_parser("fetch", help="Fetch mail, tag them, and run notifications") + parserFetch.set_defaults(dbmode=notmuch.Database.MODE.READ_WRITE) + parserFetch.set_defaults(func=func_fetch) + + + ## Debug + # process (all or unprocessed) + + args = parser.parse_args() + print(args) + + # Installing logs + colorama.init() + coloredlogs.install(level=args.verbosity, fmt='%(levelname)s %(message)s') + log = logging.getLogger() + + log.info("Loading config {}".format(args.config)) + if not os.path.isfile(args.config): + log.fatal("Config file not found: {}".format(args.config)) + sys.exit(1) + # TODO Create it, maybe? + config = configparser.ConfigParser() + config.read(args.config) + + generate_aliases() + + if args.dbmode is not None: + log.info("Loading database") + dbPath = os.path.realpath(os.path.expanduser(config["GENERAL"]["storage"])) + db = notmuch.Database(mode=args.dbmode, path=dbPath) + + if args.func: + log.info("Executing function {}".format(args.func)) + args.func(args) + From f910bd6add7d462420b050982401cb16c6d653a8 Mon Sep 17 00:00:00 2001 From: Geoffrey Frogeye Date: Tue, 14 Aug 2018 17:23:57 +0200 Subject: [PATCH 18/22] mel 3 --- config/dunst/dunstrc | 2 +- scripts/mel | 306 ++++++++++++++++++++++++++++++++++--------- scripts/melConf | 2 +- 3 files changed, 248 insertions(+), 62 deletions(-) diff --git a/config/dunst/dunstrc b/config/dunst/dunstrc index 63e6c2a..17278d9 100644 --- a/config/dunst/dunstrc +++ b/config/dunst/dunstrc @@ -15,7 +15,7 @@ hide_duplicate_count = false history_length = 20 horizontal_padding = 8 - icon_path = /usr/share/icons/gnome/256x256/status/:/usr/share/icons/gnome/256x256/devices/ + icon_path = /usr/share/icons/gnome/256x256/actions/:/usr/share/icons/gnome/256x256/status/:/usr/share/icons/gnome/256x256/devices/ icon_position = left idle_threshold = 120 ignore_newline = no diff --git a/scripts/mel b/scripts/mel index 7a4dae5..71869c2 100755 --- a/scripts/mel +++ b/scripts/mel @@ -2,10 +2,35 @@ """ Meh mail client +A dumb Python scripts that leverages notmuch, mbsync, and msmtp +to become a fully-functional extremly-opinonated mail client. """ # TODO Features + # TODO Lockfiles for write operations on mail files (mbsync, tags→maildir operations) + # TODO OPTI Lockfile per account and process everything in parallel (if implemented, this + # should be optional since while it may speed up the mail fetching process, its multi-threading + # nature would cause a lot of cache flushes and be not very efficient on battery) + # TODO Handle true character width + # TODO IMAP IDLE watches? # TODO (only then) Refactor + # TODO OOP-based + # TODO Merge file with melConf + +# DEBUG Small perf profiler +import time +perf_dict = dict() +perf_last = time.perf_counter() +def perfstep(name): + t = time.perf_counter() + global perf_last + global perf_dict + diff = t - perf_last + if name not in perf_dict: + perf_dict[name] = 0 + perf_dict[name] += diff + perf_last = time.perf_counter() + import notmuch import logging @@ -14,7 +39,6 @@ import colorama import datetime import os import progressbar -import time import argparse import configparser import base64 @@ -23,9 +47,27 @@ import argparse import xdg.BaseDirectory import sys import subprocess +import html +import re + +perfstep("import") ACCOUNTS = dict() ALIASES = set() +db = None +config = None + + +def open_database(write=False): + global db + mode = notmuch.Database.MODE.READ_WRITE if write else notmuch.Database.MODE.READ_ONLY + dbPath = os.path.realpath(os.path.expanduser(config["GENERAL"]["storage"])) + db = notmuch.Database(mode=mode, path=dbPath) + +def close_database(): + global db + db.close() + db = None def generate_aliases(): for name in config.sections(): @@ -73,26 +115,25 @@ def format_date(date): else: return date.strftime('%d/%m/%y') -def threadIdToB64(tid): - assert len(tid) == 16 - tidInt = int(tid, 16) - tidBytes = tidInt.to_bytes(8, 'big') - tidB64 = base64.b64encode(tidBytes) - assert len(tidB64) == 12 - return tidB64.decode() - -WIDTH_FIXED = 30 +WIDTH_FIXED = 31 WIDTH_RATIO_DEST_SUBJECT = 0.3 +ISATTY = sys.stdout.isatty() destWidth = None subjectWidth = None def compute_line_format(): - columns, rows = shutil.get_terminal_size((80, 20)) - remain = columns - WIDTH_FIXED - 1 - global destWidth, subjectWidth - destWidth = int(remain * WIDTH_RATIO_DEST_SUBJECT) - subjectWidth = remain - destWidth + if ISATTY: + columns, rows = shutil.get_terminal_size((80, 20)) + remain = columns - WIDTH_FIXED - 1 + global destWidth, subjectWidth + destWidth = int(remain * WIDTH_RATIO_DEST_SUBJECT) + subjectWidth = remain - destWidth + else: + destWidth = None + subjectWidth = None def clip_text(size, text): + if size is None: + return text l = len(text) if l == size: return text @@ -106,22 +147,28 @@ def print_msg(msg): if not destWidth: compute_line_format() + sep = " " if ISATTY else "\t" line = "" tags = set(msg.get_tags()) mailbox, folder, state = get_location(msg) - line += get_mailbox_color(mailbox) + if ISATTY: + line += get_mailbox_color(mailbox) - # ID - line += threadIdToB64(msg.get_thread_id()) - # line += str(int(msg.get_thread_id(), 16)) + # UID + uid = None + for tag in tags: + if tag.startswith('tuid'): + uid = tag[4:] + assert isUID(uid), uid + line += uid # Date - line += " " + line += sep date = datetime.datetime.fromtimestamp(msg.get_date()) line += format_date(date) # Icons - line += " " + line += sep def tags2col1(tag1, tag2, both, first, second, none): nonlocal line if tag1 in tags: @@ -144,20 +191,20 @@ def print_msg(msg): dest = msg.get_header("to") else: dest = msg.get_header("from") - line += " " + line += sep line += clip_text(destWidth, dest) # Subject - line += " " + line += sep subject = msg.get_header("subject") line += clip_text(subjectWidth, subject) - line += colorama.Style.RESET_ALL + if ISATTY: + line += colorama.Style.RESET_ALL print(line) def retag_msg(msg): - msg.freeze() mailbox, folder, state = get_location(msg) # Search-friendly folder name @@ -181,11 +228,18 @@ def retag_msg(msg): tag_if('deleted', slugFolder[0] == 'TRASH') tag_if('draft', slugFolder[0] == 'DRAFTS') tag_if('sent', expeditor in ALIASES) - # TODO remove unprocessed + tag_if('unprocessed', False) + + # UID + uid = msg.get_header("X-TUID") + assert isUID(uid) + uidtag = 'tuid{}'.format(uid) + # Remove eventual others UID + for tag in tags: + if tag.startswith('tuid') and tag != uidtag: + msg.remove_tag(tag) + msg.add_tag(uidtag) - # Save - msg.thaw() - msg.tags_to_maildir_flags() def extract_email(field): @@ -196,23 +250,37 @@ def extract_email(field): except ValueError: return field -def applyMsgs(queryStr, action, *args, showProgress=False, **kwargs): +msg = None +def applyMsgs(queryStr, action, *args, showProgress=False, write=False, closeDb=True, **kwargs): + if db is None: + open_database(write=write) + log.info("Querying {}".format(queryStr)) query = notmuch.Query(db, queryStr) query.set_sort(notmuch.Query.SORT.OLDEST_FIRST) elements = query.search_messages() + nbMsgs = query.count_messages() - if showProgress: - nbMsgs = query.count_messages() - iterator = progressbar.progressbar(elements, max_value=nbMsgs) - else: - iterator = elements + iterator = progressbar.progressbar(elements, max_value=nbMsgs) if showProgress else elements log.info("Executing {}".format(action)) + global msg for msg in iterator: + if write: + msg.freeze() + action(msg, *args, **kwargs) + if write: + msg.thaw() + msg.tags_to_maildir_flags() + + if closeDb: + close_database() + + return nbMsgs + # applyMsgs('*', print_msg) # applyMsgs('tag:inbox', print_msg) # applyMsgs('tag:spam', print_msg) @@ -223,6 +291,92 @@ def applyMsgs(queryStr, action, *args, showProgress=False, **kwargs): # applyMsgs('tag:unprocessed', retag_msg, useProgressbar=True) # applyMsgs('*', retag_msg, useProgressbar=True) +# def update_polybar_status(): +def update_polybar_status(*args, **kwargs): + log.info("Updating polybar status") + accountsList = sorted(ACCOUNTS.keys()) + print(accountsList) + open_database() + statusArr = [] + for account in accountsList: + queryStr = 'folder:/{}/ and tag:unread'.format(account) + query = notmuch.Query(db, queryStr) + nbMsgs = query.count_messages() + if nbMsgs < 1: + continue + color = config[account]['color'] + statusAccStr = '%{F' + color + '}' + str(nbMsgs) + '%{F-}' + statusArr.append(statusAccStr) + close_database() + statusStr = ('_' + ' '.join(statusArr)) if len(statusArr) else '\n' + statusPath = os.path.expanduser("~/.cache/mutt/status") # TODO Better + with open(statusPath, 'w') as f: + f.write(statusStr) + # statusPath = os.path.expanduser("~/.cache/mel/polybarstatus") # TODO Better + +def notify_msg(msg): + log.info("Sending notification for {}".format(msg)) + subject = msg.get_header("subject") + expd = msg.get_header("from") + account, _, _ = get_location(msg) + + summary = '{} ({})'.format(html.escape(expd), account) + body = html.escape(subject) + cmd = ["notify-send", "-u", "low", "-i", "mail-message-new", summary, body] + print(' '.join(cmd)) + subprocess.run(cmd) + + +def notify_all(*args, **kwargs): + open_database() + nbMsgs = applyMsgs('tag:unread and tag:unprocessed', notify_msg) + if nbMsgs > 0: + log.info("Playing notification sound ({} new message(s))".format(nbMsgs)) + cmd = ["play", "-n", "synth", "sine", "E4", "sine", "A5", "remix", "1-2", "fade", "0.5", "1.2", "0.5", "2"] + subprocess.run(cmd) + close_database() + +def isUID(uid): + return isinstance(uid, str) and len(uid) == 12 and re.match('^[a-zA-Z0-9+/]{12}$', uid) + +# From https://stackoverflow.com/a/312464 +def chunks(l, n): + """Yield successive n-sized chunks from l.""" + for i in range(0, len(l), n): + yield l[i:i + n] + +def apply_msgs_input(argmessages, action, write=False): + if not len(argmessages): + fromStdin = not sys.stdin.isatty() + else: + fromStdin = len(argmessages) == 1 and argmessages == '-' + + messages = list() + if fromStdin: + for line in sys.stdin: + uid = line[:12] + if not isUID(uid): + log.error("Not an UID: {}".format(uid)) + continue + messages.append(uid) + else: + for uids in argmessages: + if len(uids) > 12: + log.warn("Might have forgotten some spaces between the UIDs. Don't worry, I'll split them for you") + for uid in chunks(uids, 12): + if not isUID(uid): + log.error("Not an UID: {}".format(uid)) + continue + messages.append(uid) + + for message in messages: + queryStr = 'tag:tuid{}'.format(message) + nbMsgs = applyMsgs(queryStr, action, write=write) + if nbMsgs < 1: + log.error("Couldn't execute function for message {}".format(message)) + +perfstep("definitions") + if __name__ == "__main__": # Main arguments parser = argparse.ArgumentParser(description="Meh mail client") @@ -232,18 +386,13 @@ if __name__ == "__main__": defaultConfigFile = os.path.join(xdg.BaseDirectory.xdg_config_home, 'mel', 'accounts.conf') parser.add_argument('-c', '--config', default=defaultConfigFile, help="Accounts config file") - parser.set_defaults(dbmode=notmuch.Database.MODE.READ_ONLY) - parser.set_defaults(showProgress=False) - parser.set_defaults(useThreads=False) - parser.set_defaults(actionBefore=None) - parser.set_defaults(actionAfter=None) - parser.set_defaults(action=None) - - subparsers = parser.add_subparsers(help="Action to execute", required=True) - + subparsers = parser.add_subparsers(help="Action to execute") ## List messages + def func_default(args): + applyMsgs('tag:inbox', print_msg) + parser.set_defaults(func=func_default) # inbox (default) def func_inbox(args): @@ -258,10 +407,30 @@ if __name__ == "__main__": # list folder [--recurse] ## List actions + + + # flag msg... + def func_flag(args): + def flag_msg(msg): + msg.add_tag('flagged') + apply_msgs_input(args.message, action=flag_msg, write=True) + parserFlag = subparsers.add_parser("flag", help="Mark messages as flagged") + parserFlag.add_argument('message', nargs='*', help="Messages") + parserFlag.set_defaults(func=func_flag) + + + # unflag msg... + def func_unflag(args): + def unflag_msg(msg): + msg.remove_tag('flagged') + apply_msgs_input(args.message, action=unflag_msg, write=True) + parserUnflag = subparsers.add_parser("unflag", help="Mark messages as not-flagged") + parserUnflag.add_argument('message', nargs='*', help="Messages") + parserUnflag.set_defaults(func=func_unflag) + + # delete msg... # spam msg... - # flag msg... - # ↑un* equivalents # move dest msg... ## Read message # read msg [--html] [--plain] [--browser] @@ -270,6 +439,7 @@ if __name__ == "__main__": # new account # reply msg [--all] ## Folder management + # tree [folder] # mkdir folder # rmdir folder (prevent if folder isn't empty (mail/subfolder)) # (yeah that should do) @@ -293,25 +463,40 @@ if __name__ == "__main__": log.debug(" ".join(cmd)) subprocess.run(cmd) - # Tag new mails - applyMsgs('tag:unprocessed', retag_msg, showProgress=True) - # Notify - log.info("Notifying new mails") - # TODO Maybe before retag, notify unprocessed && unread + notify_all() + update_polybar_status() + + # Tag new mails + applyMsgs('tag:unprocessed', retag_msg, showProgress=True, write=True) parserFetch = subparsers.add_parser("fetch", help="Fetch mail, tag them, and run notifications") - parserFetch.set_defaults(dbmode=notmuch.Database.MODE.READ_WRITE) parserFetch.set_defaults(func=func_fetch) ## Debug - # process (all or unprocessed) + # debug (various) + parserRetag = subparsers.add_parser("debug", help="Who know what this holds...") + parserRetag.set_defaults(verbosity='DEBUG') + parserRetag.set_defaults(func=notify_all) + # retag (all or unprocessed) + def func_retag(args): + applyMsgs('*', retag_msg, showProgress=True, write=True) + parserRetag = subparsers.add_parser("retag", help="Retag all mails (when you changed configuration)") + parserRetag.set_defaults(func=func_retag) + + # all + def func_all(args): + applyMsgs('*', print_msg) + + parserAll = subparsers.add_parser("all", help="Show ALL messages") + parserAll.set_defaults(func=func_all) + + # Init args = parser.parse_args() - print(args) + perfstep("parse_args") - # Installing logs colorama.init() coloredlogs.install(level=args.verbosity, fmt='%(levelname)s %(message)s') log = logging.getLogger() @@ -325,13 +510,14 @@ if __name__ == "__main__": config.read(args.config) generate_aliases() - - if args.dbmode is not None: - log.info("Loading database") - dbPath = os.path.realpath(os.path.expanduser(config["GENERAL"]["storage"])) - db = notmuch.Database(mode=args.dbmode, path=dbPath) + perfstep("config") if args.func: log.info("Executing function {}".format(args.func)) args.func(args) + perfstep("exec") + + # DEBUG + for kv in sorted(perf_dict.items(), key=lambda p: p[1]): + log.debug("{1:.6f} {0}".format(*kv)) diff --git a/scripts/melConf b/scripts/melConf index a0c2829..c46e30e 100755 --- a/scripts/melConf +++ b/scripts/melConf @@ -203,7 +203,7 @@ primary_email={main[from]} other_email={other_email} [new] -tags=unprocessed; +tags=unprocessed;unread; ignore= [search] From 08d0776759dca3476771384a118b9245286f5aee Mon Sep 17 00:00:00 2001 From: Geoffrey Frogeye Date: Tue, 14 Aug 2018 19:25:07 +0200 Subject: [PATCH 19/22] Show mel --- scripts/mel | 111 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 93 insertions(+), 18 deletions(-) diff --git a/scripts/mel b/scripts/mel index 71869c2..96113ca 100755 --- a/scripts/mel +++ b/scripts/mel @@ -49,6 +49,7 @@ import sys import subprocess import html import re +import email.parser perfstep("import") @@ -250,8 +251,8 @@ def extract_email(field): except ValueError: return field -msg = None def applyMsgs(queryStr, action, *args, showProgress=False, write=False, closeDb=True, **kwargs): + # TODO Verify it's open in the correct mode if db is None: open_database(write=write) @@ -265,7 +266,6 @@ def applyMsgs(queryStr, action, *args, showProgress=False, write=False, closeDb= iterator = progressbar.progressbar(elements, max_value=nbMsgs) if showProgress else elements log.info("Executing {}".format(action)) - global msg for msg in iterator: if write: msg.freeze() @@ -281,16 +281,6 @@ def applyMsgs(queryStr, action, *args, showProgress=False, write=False, closeDb= return nbMsgs -# applyMsgs('*', print_msg) -# applyMsgs('tag:inbox', print_msg) -# applyMsgs('tag:spam', print_msg) -# applyMsgs('tag:unread', print_msg) -# applyMsgs('tag:unprocessed', print_msg) -# applyMsgs('from:geoffrey@frogeye.fr', print_msg) - -# applyMsgs('tag:unprocessed', retag_msg, useProgressbar=True) -# applyMsgs('*', retag_msg, useProgressbar=True) - # def update_polybar_status(): def update_polybar_status(*args, **kwargs): log.info("Updating polybar status") @@ -371,9 +361,78 @@ def apply_msgs_input(argmessages, action, write=False): for message in messages: queryStr = 'tag:tuid{}'.format(message) - nbMsgs = applyMsgs(queryStr, action, write=write) + nbMsgs = applyMsgs(queryStr, action, write=write, closeDb=False) if nbMsgs < 1: log.error("Couldn't execute function for message {}".format(message)) + close_database() + +def format_header_value(val): + return val.replace('\n', '').replace('\t', '').strip() + +# From https://stackoverflow.com/a/1094933 +def sizeof_fmt(num, suffix='B'): + for unit in ['','Ki','Mi','Gi','Ti','Pi','Ei','Zi']: + if abs(num) < 1024.0: + return "%3.1f %s%s" % (num, unit, suffix) + num /= 1024.0 + return "%.1f %s%s" % (num, 'Yi', suffix) + +def show_parts_tree(part, lvl=0, nb=1): + indent = lvl * '\t' + typ = part.get_content_type() + if part.is_multipart(): + print(PART_MULTI_FORMAT.format(nb=nb, indent=indent, typ=typ)) + payl = part.get_payload() + size = 1 + for obj in payl: + size += show_parts_tree(obj, lvl=lvl+1, nb=nb+size) + return size + else: + size = len(part.get_payload(decode=True)) + desc = part.get('Content-Description', '') + print(PART_LEAF_FORMAT.format(nb=nb, indent=indent, typ=typ, desc=desc, size=sizeof_fmt(size))) + return 1 + +INTERESTING_HEADERS = ["Date", "From", "Subject", "To", "Cc", "Message-Id"] +HEADER_FORMAT = colorama.Fore.BLUE + colorama.Style.BRIGHT + '{}:' + colorama.Style.NORMAL + ' {}' + colorama.Style.RESET_ALL +PART_MULTI_FORMAT = colorama.Fore.BLUE + '{nb} {indent}+ {typ}' + colorama.Style.RESET_ALL +PART_LEAF_FORMAT = colorama.Fore.BLUE + '{nb} {indent}→ {desc} ({typ}; {size})' + colorama.Style.RESET_ALL +def read_msg(msg): + # Parse + filename = msg.get_filename() + parser = email.parser.BytesParser() + with open(filename, 'rb') as f: + mail = parser.parse(f) + + # Debug + global a + a = mail + + # Defects + if len(mail.defects): + log.warn("Defects found in the mail:") + for defect in mail.defects: + log.warn(mail.defects) + + + # Headers + for key in INTERESTING_HEADERS: + val = mail.get(key) + if val: + val = format_header_value(val) + print(HEADER_FORMAT.format(key, val)) + # TODO Show all headers + # TODO BONUS Highlight failed verifications + + show_parts_tree(mail) + print() + + # Show text/plain + for part in mail.walk(): + if part.get_content_type() == "text/plain": + payl = part.get_payload(decode=True) + print(payl.decode()) + perfstep("definitions") @@ -413,7 +472,7 @@ if __name__ == "__main__": def func_flag(args): def flag_msg(msg): msg.add_tag('flagged') - apply_msgs_input(args.message, action=flag_msg, write=True) + apply_msgs_input(args.message, flag_msg, write=True) parserFlag = subparsers.add_parser("flag", help="Mark messages as flagged") parserFlag.add_argument('message', nargs='*', help="Messages") parserFlag.set_defaults(func=func_flag) @@ -423,7 +482,7 @@ if __name__ == "__main__": def func_unflag(args): def unflag_msg(msg): msg.remove_tag('flagged') - apply_msgs_input(args.message, action=unflag_msg, write=True) + apply_msgs_input(args.message, unflag_msg, write=True) parserUnflag = subparsers.add_parser("unflag", help="Mark messages as not-flagged") parserUnflag.add_argument('message', nargs='*', help="Messages") parserUnflag.set_defaults(func=func_unflag) @@ -433,7 +492,16 @@ if __name__ == "__main__": # spam msg... # move dest msg... ## Read message + + # read msg [--html] [--plain] [--browser] + def func_read(args): + apply_msgs_input(args.message, read_msg) + parserRead = subparsers.add_parser("read", help="Read message") + parserRead.add_argument('message', nargs=1, help="Messages") + parserRead.set_defaults(func=func_read) + + # attach msg [id] [--save] (list if no id, xdg-open else) ## Redaction # new account @@ -476,9 +544,15 @@ if __name__ == "__main__": ## Debug # debug (various) - parserRetag = subparsers.add_parser("debug", help="Who know what this holds...") - parserRetag.set_defaults(verbosity='DEBUG') - parserRetag.set_defaults(func=notify_all) + def func_expose(args): + # And leave the door open + def expose_msg(a): + global msg + msg = a + applyMsgs('tag:tuidyviU45m6flff', expose_msg, closeDb=False) + parserDebug = subparsers.add_parser("debug", help="Who know what this holds...") + parserDebug.set_defaults(verbosity='DEBUG') + parserDebug.set_defaults(func=func_expose) # retag (all or unprocessed) def func_retag(args): @@ -519,5 +593,6 @@ if __name__ == "__main__": perfstep("exec") # DEBUG + sys.exit(0) for kv in sorted(perf_dict.items(), key=lambda p: p[1]): log.debug("{1:.6f} {0}".format(*kv)) From 6ad14b3231bcac13137110154883045d4db70208 Mon Sep 17 00:00:00 2001 From: Geoffrey Frogeye Date: Fri, 17 Aug 2018 15:08:40 +0200 Subject: [PATCH 20/22] Mel pause --- scripts/mel | 58 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 14 deletions(-) diff --git a/scripts/mel b/scripts/mel index 96113ca..037e345 100755 --- a/scripts/mel +++ b/scripts/mel @@ -7,12 +7,14 @@ to become a fully-functional extremly-opinonated mail client. """ # TODO Features + # TODO Implement initial command set # TODO Lockfiles for write operations on mail files (mbsync, tags→maildir operations) # TODO OPTI Lockfile per account and process everything in parallel (if implemented, this # should be optional since while it may speed up the mail fetching process, its multi-threading # nature would cause a lot of cache flushes and be not very efficient on battery) # TODO Handle true character width # TODO IMAP IDLE watches? + # TODO GPG # TODO (only then) Refactor # TODO OOP-based # TODO Merge file with melConf @@ -58,17 +60,49 @@ ALIASES = set() db = None config = None +def notmuch_new(): + close_database() + log.info("Indexing mails") + notmuchConfigPath = os.path.expanduser("~/.notmuchrc") # TODO Better + cmd = ["notmuch", "--config", notmuchConfigPath, "new"] + log.debug(" ".join(cmd)) + subprocess.run(cmd) + +def list_folders(): + storagePath = os.path.realpath(os.path.expanduser(config["GENERAL"]["storage"])) + folders = list() + for account in ACCOUNTS.keys(): + storagePathAccount = os.path.join(storagePath, account) + for root, dirs, files in os.walk(storagePathAccount): + if "cur" not in dirs or "new" not in dirs or "tmp" not in dirs: + continue + assert root.startswith(storagePath) + path = root[len(storagePath):] + pathSplit = path.split('/') + if pathSplit[0] == '': + pathSplit = pathSplit[1:] + folders.append(tuple(pathSplit)) + return folders def open_database(write=False): global db mode = notmuch.Database.MODE.READ_WRITE if write else notmuch.Database.MODE.READ_ONLY + if db: + if db.mode == mode: + return + else: + log.info("Current database not in required mode, closing") + close_database() + log.info("Opening database in mode {}".format(mode)) dbPath = os.path.realpath(os.path.expanduser(config["GENERAL"]["storage"])) db = notmuch.Database(mode=mode, path=dbPath) def close_database(): global db - db.close() - db = None + if db: + log.info("Closing database") + db.close() + db = None def generate_aliases(): for name in config.sections(): @@ -252,9 +286,7 @@ def extract_email(field): return field def applyMsgs(queryStr, action, *args, showProgress=False, write=False, closeDb=True, **kwargs): - # TODO Verify it's open in the correct mode - if db is None: - open_database(write=write) + open_database(write=write) log.info("Querying {}".format(queryStr)) query = notmuch.Query(db, queryStr) @@ -377,6 +409,8 @@ def sizeof_fmt(num, suffix='B'): num /= 1024.0 return "%.1f %s%s" % (num, 'Yi', suffix) +PART_MULTI_FORMAT = colorama.Fore.BLUE + '{nb} {indent}+ {typ}' + colorama.Style.RESET_ALL +PART_LEAF_FORMAT = colorama.Fore.BLUE + '{nb} {indent}→ {desc} ({typ}; {size})' + colorama.Style.RESET_ALL def show_parts_tree(part, lvl=0, nb=1): indent = lvl * '\t' typ = part.get_content_type() @@ -395,8 +429,6 @@ def show_parts_tree(part, lvl=0, nb=1): INTERESTING_HEADERS = ["Date", "From", "Subject", "To", "Cc", "Message-Id"] HEADER_FORMAT = colorama.Fore.BLUE + colorama.Style.BRIGHT + '{}:' + colorama.Style.NORMAL + ' {}' + colorama.Style.RESET_ALL -PART_MULTI_FORMAT = colorama.Fore.BLUE + '{nb} {indent}+ {typ}' + colorama.Style.RESET_ALL -PART_LEAF_FORMAT = colorama.Fore.BLUE + '{nb} {indent}→ {desc} ({typ}; {size})' + colorama.Style.RESET_ALL def read_msg(msg): # Parse filename = msg.get_filename() @@ -524,12 +556,7 @@ if __name__ == "__main__": subprocess.run(cmd) # Index new mails - log.info("Indexing mails") - log.error("TODO Can't `notmuch new` when database is already open!") - notmuchConfigPath = os.path.expanduser("~/.notmuchrc") # TODO Better - cmd = ["notmuch", "--config", notmuchConfigPath, "new"] - log.debug(" ".join(cmd)) - subprocess.run(cmd) + notmuch_new() # Notify notify_all() @@ -550,9 +577,12 @@ if __name__ == "__main__": global msg msg = a applyMsgs('tag:tuidyviU45m6flff', expose_msg, closeDb=False) + def func_debug(args): + from pprint import pprint + pprint(list_folders()) parserDebug = subparsers.add_parser("debug", help="Who know what this holds...") parserDebug.set_defaults(verbosity='DEBUG') - parserDebug.set_defaults(func=func_expose) + parserDebug.set_defaults(func=func_debug) # retag (all or unprocessed) def func_retag(args): From 27a817fff30215c43480142762e86022524dcc60 Mon Sep 17 00:00:00 2001 From: Geoffrey Frogeye Date: Fri, 17 Aug 2018 15:34:09 +0200 Subject: [PATCH 21/22] rofi is bae --- config/i3/autorandrdefaultmenu | 2 +- config/i3/autorandrloadmenu | 2 +- config/i3/autorandrremovemenu | 2 +- config/i3/autorandrsavemenu | 2 +- config/i3/clipmenu | 52 -------------- config/i3/clipmenud | 127 --------------------------------- config/i3/config | 22 +++--- config/i3/passmenu | 30 -------- config/i3/sshmenu | 9 --- config/i3/test | 5 -- 10 files changed, 15 insertions(+), 238 deletions(-) delete mode 100755 config/i3/clipmenu delete mode 100755 config/i3/clipmenud delete mode 100755 config/i3/passmenu delete mode 100755 config/i3/sshmenu delete mode 100644 config/i3/test diff --git a/config/i3/autorandrdefaultmenu b/config/i3/autorandrdefaultmenu index 3f8a36e..d2f2d25 100755 --- a/config/i3/autorandrdefaultmenu +++ b/config/i3/autorandrdefaultmenu @@ -2,7 +2,7 @@ shopt -s nullglob globstar -profile=$(echo -e "$(autorandr 2>&1 | cut -d' ' -f1)" | ~/.config/i3/dmenu_cmd -p "Default profile" "$@") +profile=$(echo -e "$(autorandr 2>&1 | cut -d' ' -f1)" | rofi -dmenu -p "Default profile" "$@") [[ -n $profile ]] || exit diff --git a/config/i3/autorandrloadmenu b/config/i3/autorandrloadmenu index 930b044..81000eb 100755 --- a/config/i3/autorandrloadmenu +++ b/config/i3/autorandrloadmenu @@ -2,7 +2,7 @@ shopt -s nullglob globstar -profile=$(echo -e "common\nhorizontal\nvertical\n$(autorandr 2>&1 | cut -d' ' -f1)" | ~/.config/i3/dmenu_cmd -p "Load profile" "$@") +profile=$(echo -e "common\nhorizontal\nvertical\n$(autorandr 2>&1 | cut -d' ' -f1)" | rofi -dmenu -p "Load profile" "$@") [[ -n $profile ]] || exit diff --git a/config/i3/autorandrremovemenu b/config/i3/autorandrremovemenu index 7b12673..323d72c 100755 --- a/config/i3/autorandrremovemenu +++ b/config/i3/autorandrremovemenu @@ -2,7 +2,7 @@ shopt -s nullglob globstar -profile=$(echo -e "$(autorandr 2>&1 | cut -d' ' -f1)" | ~/.config/i3/dmenu_cmd -p "Remove profile" "$@") +profile=$(echo -e "$(autorandr 2>&1 | cut -d' ' -f1)" | rofi -dmenu -p "Remove profile" "$@") [[ -n $profile ]] || exit diff --git a/config/i3/autorandrsavemenu b/config/i3/autorandrsavemenu index bae7989..e24db48 100755 --- a/config/i3/autorandrsavemenu +++ b/config/i3/autorandrsavemenu @@ -2,7 +2,7 @@ shopt -s nullglob globstar -profile=$(echo -e "$(autorandr 2>&1 | cut -d' ' -f1)" | ~/.config/i3/dmenu_cmd -p "Save profile" "$@") +profile=$(echo -e "$(autorandr 2>&1 | cut -d' ' -f1)" | rofi -dmenu -p "Save profile" "$@") [[ -n $profile ]] || exit diff --git a/config/i3/clipmenu b/config/i3/clipmenu deleted file mode 100755 index 89935a1..0000000 --- a/config/i3/clipmenu +++ /dev/null @@ -1,52 +0,0 @@ -#!/bin/bash - -shopt -s nullglob - -# We use this to make sure the cache files are sorted bytewise -LC_COLLATE=C - -# Some people copy/paste huge swathes of text that could slow down dmenu -line_length_limit=500 - -declare -A selections -ordered_selections=() - -files=("$HOME/.clipmenu/"*) - -# We can't use `for ... in` here because we need to add files to -# ordered_selections from last to first -- that is, newest to oldest. Incoming -# clipboard entries have a ISO datetime prefixed to the front to aid in this. -for (( i=${#files[@]}-1; i>=0; i-- )); do - file=${files[$i]} - - # We look for the first line matching regex /./ here because we want the - # first line that can provide reasonable context to the user. That is, if - # you have 5 leading lines of whitespace, displaying " (6 lines)" is much - # less useful than displaying "foo (6 lines)", where "foo" is the first - # line in the entry with actionable context. - first_line=$(sed -n '/./{p;q}' "$file" | cut -c1-"$line_length_limit") - lines=$(wc -l < "$file") - - if (( lines > 1 )); then - first_line+=" ($lines lines)" - fi - - ordered_selections+=("$first_line") - selections[$first_line]=$file -done - -# It's okay to hardcode `-l 8` here as a sensible default without checking -# whether `-l` is also in "$@", because the way that dmenu works allows a later -# argument to override an earlier one. That is, if the user passes in `-l`, our -# one will be ignored. -chosen_line=$(printf '%s\n' "${ordered_selections[@]}" | uniq | $HOME/.config/i3/dmenu_cmd -l 8 -p "Copy" "$@") - -[[ $chosen_line ]] || exit 1 - -for selection in clipboard primary; do - if type -p xsel >/dev/null 2>&1; then - xsel --logfile /dev/null -i --"$selection" < "${selections[$chosen_line]}" - else - xclip -sel "$selection" < "${selections[$chosen_line]}" - fi -done diff --git a/config/i3/clipmenud b/config/i3/clipmenud deleted file mode 100755 index 98edd08..0000000 --- a/config/i3/clipmenud +++ /dev/null @@ -1,127 +0,0 @@ -#!/bin/bash - -hr_msg() { - printf -- '\n--- %s ---\n\n' "$1" >&2 -} - -debug() { - if (( DEBUG )); then - printf '%s\n' "$@" >&2 - fi -} - -print_debug_info() { - # DEBUG comes from the environment - if ! (( DEBUG )); then - return - fi - - local msg="${1?}" - - hr_msg "$msg" - - hr_msg Environment - env | LC_ALL=C sort >&2 - - cgroup_path=/proc/$$/cgroup - - if [[ -f $cgroup_path ]]; then - hr_msg cgroup - cat "$cgroup_path" >&2 - else - hr_msg 'NO CGROUP' - fi - - hr_msg 'Finished debug info' -} - -print_debug_info 'Initialising' - -cache_dir=$HOME/.clipmenu/ - -# It's ok that this only applies to the final directory. -# shellcheck disable=SC2174 -mkdir -p -m0700 "$cache_dir" - -declare -A last_data -declare -A last_filename - -while sleep "${CLIPMENUD_SLEEP:-0.5}"; do - print_debug_info 'About to run selection' - - for selection in clipboard primary; do - print_debug_info "About to do selection for '$selection'" - - if type -p xsel >/dev/null 2>&1; then - debug 'Using xsel' - data=$(xsel --logfile /dev/null -o --"$selection"; printf x) - else - debug 'Using xclip' - data=$(xclip -o -sel "$selection"; printf x) - fi - - debug "Data before stripping: $data" - - # We add and remove the x so that trailing newlines are not stripped. - # Otherwise, they would be stripped by the very nature of how POSIX - # defines command substitution. - data=${data%x} - - debug "Data after stripping: $data" - - if [[ $data != *[^[:space:]]* ]]; then - debug "Skipping as clipboard is only blank" - continue - fi - - if [[ ${last_data[$selection]} == "$data" ]]; then - debug 'Skipping as last selection is the same as this one' - continue - fi - - # If we were in the middle of doing a selection when the previous poll - # ran, then we may have got a partial clip. - possible_partial=${last_data[$selection]} - if [[ $possible_partial && $data == "$possible_partial"* ]]; then - debug "$possible_partial is a possible partial of $data" - debug "Removing ${last_filename[$selection]}" - rm -- "${last_filename[$selection]}" - fi - - filename="$cache_dir/$(LC_ALL=C date +%F-%T.%N)" - - last_data[$selection]=$data - last_filename[$selection]=$filename - - debug "Writing $data to $filename" - printf '%s' "$data" > "$filename" - - if ! (( NO_OWN_CLIPBOARD )) && [[ $selection != primary ]]; then - # Take ownership of the clipboard, in case the original application - # is unable to serve the clipboard request (due to being suspended, - # etc). - # - # Primary is excluded from the change of ownership as applications - # sometimes act up if clipboard focus is taken away from them -- - # for example, urxvt will unhilight text, which is undesirable. - # - # We can't colocate this with the above copying code because - # https://github.com/cdown/clipmenu/issues/34 requires knowing if - # we would skip first. - if type -p xsel >/dev/null 2>&1; then - xsel --logfile /dev/null -o --"$selection" | xsel -i --"$selection" - else - xclip -o -sel "$selection" | xclip -i -sel "$selection" - fi - fi - - if ! (( NO_TRANSFER_CLIPBOARD )) && [[ $selection != primary ]]; then - # Copy every clipboard content into primary clipboard - if type -p xsel >/dev/null 2>&1; then - xsel --logfile /dev/null -o --"$selection" | xsel -i --primary - else - xclip -o -sel "$selection" | xclip -i -sel primary - fi - fi - done -done diff --git a/config/i3/config b/config/i3/config index df41ee8..65f223b 100644 --- a/config/i3/config +++ b/config/i3/config @@ -19,6 +19,11 @@ hide_edge_borders both #bindsym $mod+y border pixel 2 #bindsym $mod+n border normal +# Compatibility layer for people coming from other backgrounds +bindsym Mod1+Tab exec --no-startup-id rofi -modi window -show window +bindsym Mod1+F2 exec --no-startup-id rofi -modi drun -show drun +bindsym Mod1+F4 kill + # Font for window titles. Will also be used by the bar unless a different font # is used in the bar {} block below. @@ -30,20 +35,16 @@ floating_modifier $mod # kill focused window bindsym $mod+z kill -#bindsym Mod1+F4 kill bindsym button2 kill -# start dmenu (a program launcher) -bindsym $mod+F2 exec --no-startup-id ~/.config/i3/dmenu_run -bindsym Mod1+F2 exec --no-startup-id ~/.config/i3/dmenu_run - -bindsym $mod+c exec --no-startup-id ~/.config/i3/passmenu --type -#bindsym $mod+x exec --no-startup-id ~/.config/i3/clipmenu -bindsym $mod+asterisk exec --no-startup-id ~/.config/i3/sshmenu -bindsym $mod+dollar exec --no-startup-id ~/.config/i3/sshmenu root +bindsym $mod+c exec --no-startup-id rofi-pass --last-used +bindsym $mod+asterisk exec --no-startup-id rofi -modi ssh -show ssh +bindsym $mod+dollar exec --no-startup-id rofi -modi ssh -show ssh -ssh-command '{terminal} -e {ssh-client} {host} -t "sudo -s -E"' +bindsym $mod+Tab exec --no-startup-id rofi -modi window -show window # start program launcher -bindsym $mod+d exec --no-startup-id ~/.config/i3/dmenu_run +bindsym $mod+d exec --no-startup-id rofi -modi run -show run +bindsym $mod+Shift+d exec --no-startup-id rofi -modi drun -show drun # Start Applications bindsym $mod+Return exec urxvtc @@ -364,7 +365,6 @@ exec --no-startup-id numlockx on # Activate Num lock exec --no-startup-id unclutter -root # Hide mouse cursor after some time #exec --no-startup-id dunst # Notifications (handled by systemd) exec --no-startup-id keynav # Keyboard cursor controller -#exec --no-startup-id $HOME/.config/i3/clipmenud # Clipboard manager #exec --no-startup-id mpd # Music Player Daemon (handled by systemd) exec --no-startup-id ~/.config/i3/ashuffle # MPD Auto-refill exec --no-startup-id autorandr --change # Screen configuration and everything that depends on it diff --git a/config/i3/passmenu b/config/i3/passmenu deleted file mode 100755 index 9a4bf22..0000000 --- a/config/i3/passmenu +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env bash - -shopt -s nullglob globstar - -typeit=0 -if [[ $1 == "--type" ]]; then - typeit=1 - shift -fi - -if PASS=$(which gopass 2> /dev/null); then - password_files="$($PASS ls --flat)" -elif PASS=$(which pass 2> /dev/null); then - prefix=${PASSWORD_STORE_DIR-~/.password-store} - password_files=( "$prefix"/**/*.gpg ) - password_files=( "${password_files[@]#"$prefix"/}" ) - password_files=( "${password_files[@]%.gpg}" ) -else - exit -fi - -password=$(printf '%s\n' "${password_files[@]}" | $HOME/.config/i3/dmenu_cmd -p "Passwords" "$@") - -[[ -n $password ]] || exit - -if [[ $typeit -eq 0 ]]; then - $PASS show -c "$password" -else - xdotool - <<<"type --clearmodifiers -- $($PASS show "$password" | head -n 1)" -fi diff --git a/config/i3/sshmenu b/config/i3/sshmenu deleted file mode 100755 index e306c40..0000000 --- a/config/i3/sshmenu +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -if [ "$1" == 'root' ]; then - a=" (root)" - b="-t 'sudo -s'" -fi -machine=$(cat ~/.ssh/config | grep '^Host ' | cut -d ' ' -f 2 | grep -v '*' | sort | $HOME/.config/i3/dmenu_cmd -p "SSH$a to ") -if [ ! -z $machine ]; then - urxvtc -e ssh $machine $b -fi diff --git a/config/i3/test b/config/i3/test deleted file mode 100644 index b3fb195..0000000 --- a/config/i3/test +++ /dev/null @@ -1,5 +0,0 @@ -prefix=${PASSWORD_STORE_DIR-~/.password-store} -password_files=( "$prefix"/**/*.gpg ) -password_files=( "${password_files[@]#"$prefix"/}" ) -password_files=( "${password_files[@]%.gpg}" ) -echo -e "${password_files[@]}" From 9f65e9b248c0be1cb2fdc984ca71d2ffbffc3f03 Mon Sep 17 00:00:00 2001 From: Geoffrey Frogeye Date: Sun, 19 Aug 2018 11:29:59 +0200 Subject: [PATCH 22/22] It's new --- bashrc | 6 +++--- bin/.gitignore | 2 -- config/i3/lock | 7 ++++++- config/nvim/init.vim | 3 +++ config/polybar/bbswitch | 20 +++++++++++++------- config/polybar/config | 11 ++++++++--- config/polybar/keystore | 9 +++++++++ config/rofi/.gitignore | 3 +++ config/rofi/config | 8 ++++++++ scripts/changeColors | 17 +++++++++++++---- scripts/install-prefs | 14 +++++++------- vimrc | 3 ++- 12 files changed, 75 insertions(+), 28 deletions(-) delete mode 100644 bin/.gitignore create mode 100644 config/nvim/init.vim create mode 100755 config/polybar/keystore create mode 100644 config/rofi/.gitignore create mode 100644 config/rofi/config diff --git a/bashrc b/bashrc index 3d3c919..c3bcca7 100644 --- a/bashrc +++ b/bashrc @@ -142,14 +142,14 @@ export PROMPT_COMMAND='echo -ne "\033]0;${USER}@${HOSTNAME}:${PWD}\007"' # CUSTOM SCRIPTS -export PATH="$HOME/.bin/:$HOME/.scripts/:$PATH" +export PATH="$HOME/.local/bin/:$HOME/.scripts/:$PATH" [ -f ~/.gscripts/gprofile ] && source ~/.gscripts/gprofile # UTILITIES # Theme -[ -f ~/.bin/colorSchemeApply ] && source ~/.bin/colorSchemeApply -[ -f ~/.bin/colorSchemeApplyFzf ] && source ~/.bin/colorSchemeApplyFzf +[ -f ~/.local/bin/colorSchemeApply ] && source ~/.local/bin/colorSchemeApply +[ -f ~/.local/bin/colorSchemeApplyFzf ] && source ~/.local/bin/colorSchemeApplyFzf # Bash completion [ -f /etc/bash_completion ] && source /etc/bash_completion diff --git a/bin/.gitignore b/bin/.gitignore deleted file mode 100644 index d6b7ef3..0000000 --- a/bin/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/config/i3/lock b/config/i3/lock index e5d1829..be3bebd 100755 --- a/config/i3/lock +++ b/config/i3/lock @@ -1,4 +1,9 @@ -#!/usr/bin/env sh +#!/usr/bin/env bash + +# Remove SSH and GPG keys from keystores +ssh-add -D +echo RELOADAGENT | gpg-connect-agent + dm-tool lock if [ $? -ne 0 ]; then diff --git a/config/nvim/init.vim b/config/nvim/init.vim new file mode 100644 index 0000000..f182e5b --- /dev/null +++ b/config/nvim/init.vim @@ -0,0 +1,3 @@ +set runtimepath^=~/.vim runtimepath+=~/.vim/after +let &packpath = &runtimepath +source ~/.vimrc diff --git a/config/polybar/bbswitch b/config/polybar/bbswitch index 03507c6..1228ef3 100755 --- a/config/polybar/bbswitch +++ b/config/polybar/bbswitch @@ -1,12 +1,18 @@ #!/usr/bin/env bash -state="$(grep -o '\w\+$' /proc/acpi/bbswitch)" -if [ "$state" == "ON" ] +if [ -f /proc/acpi/bbswitch ] then - echo "" -elif [ "$state" == "OFF" ] -then - echo "" + echo -n  + state="$(grep -o '\w\+$' /proc/acpi/bbswitch)" + if [ "$state" == "ON" ] + then + echo "" + elif [ "$state" == "OFF" ] + then + echo "" + else + echo "?" + fi else - echo "?" + echo fi diff --git a/config/polybar/config b/config/polybar/config index 9106d62..a58ef5d 100644 --- a/config/polybar/config +++ b/config/polybar/config @@ -84,7 +84,7 @@ tray-transparent = false [bar/secondary] inherit = bar/base -modules-right = cpu memory temperature vpncheck ethMore wlanMore filesystem linuxmismatch bbswitch xbacklight volume date +modules-right = cpu memory temperature keystore vpncheck ethMore wlanMore filesystem linuxmismatch bbswitch xbacklight volume date [module/filesystem] @@ -177,9 +177,7 @@ toggle-off-foreground = #55 [module/bbswitch] type = custom/script exec = ~/.config/polybar/bbswitch -exec-if = test -f /proc/acpi/bbswitch interval = 5 -format-prefix =  format-foreground = ${theme.redF} [module/todo] @@ -242,6 +240,13 @@ interval = 1 format-foreground = ${theme.greenF} label =  %gb_free% +[module/keystore] +type = custom/script +exec = ~/.config/polybar/keystore +; exec-if = pgrep openvpn +interval = 5 +format-foreground = ${theme.greenF} + [module/vpncheck] type = custom/script exec = echo  diff --git a/config/polybar/keystore b/config/polybar/keystore new file mode 100755 index 0000000..7899ec9 --- /dev/null +++ b/config/polybar/keystore @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +output="$(ssh-add -l)" +if [ $? -ne 0 ]; then + echo +else + echo -n " " + echo "$output" | wc -l +fi diff --git a/config/rofi/.gitignore b/config/rofi/.gitignore new file mode 100644 index 0000000..c1955d4 --- /dev/null +++ b/config/rofi/.gitignore @@ -0,0 +1,3 @@ +theme.config +theme.rasi + diff --git a/config/rofi/config b/config/rofi/config new file mode 100644 index 0000000..6bc4950 --- /dev/null +++ b/config/rofi/config @@ -0,0 +1,8 @@ +#include "theme.config" +rofi.theme: theme +rofi.cycle: true +rofi.case-sensitive: false +rofi.scroll-method: 0 +rofi.show-match: true +rofi.lazy-grab: false +rofi.matching: fuzzy diff --git a/scripts/changeColors b/scripts/changeColors index 4048a85..8ad9fb0 100755 --- a/scripts/changeColors +++ b/scripts/changeColors @@ -4,12 +4,17 @@ # and apply them into my configuration scheme="$1" +if [ -z "$scheme" ] +then + echo Please specify a scheme + exit 1 +fi # TODO Verify if scheme is known # Shell (allows to use all colors in Vim while still having nice colors in the rest of the terminal) -curl "https://raw.githubusercontent.com/chriskempson/base16-shell/master/scripts/base16-${scheme}.sh" > ~/.bin/colorSchemeApply -chmod +x ~/.bin/colorSchemeApply +curl "https://raw.githubusercontent.com/chriskempson/base16-shell/master/scripts/base16-${scheme}.sh" > ~/.local/bin/colorSchemeApply +chmod +x ~/.local/bin/colorSchemeApply # Xressources (I'm not sure if this is really needed with shell overriding these but i3 load those resources) curl "https://raw.githubusercontent.com/chriskempson/base16-xresources/master/xresources/base16-${scheme}-256.Xresources" > ~/.Xresources.d/theme @@ -18,12 +23,16 @@ curl "https://raw.githubusercontent.com/chriskempson/base16-xresources/master/xr echo -e "let base16colorspace=256\nset termguicolors\ncolorscheme base16-${scheme}" > ~/.vim/colorscheme.vim # FZF -curl "https://raw.githubusercontent.com/nicodebo/base16-fzf/master/bash/base16-${scheme}.config" > ~/.bin/colorSchemeApplyFzf -chmod +x ~/.bin/colorSchemeApplyFzf +curl "https://raw.githubusercontent.com/nicodebo/base16-fzf/master/bash/base16-${scheme}.config" > ~/.local/bin/colorSchemeApplyFzf +chmod +x ~/.local/bin/colorSchemeApplyFzf # qutebrowser curl "https://raw.githubusercontent.com/theova/base16-qutebrowser/4a17eea8a39f722c2cee95fb44d4a87f5eb2518f/themes/base16-${scheme}.config.py" > ~/.config/qutebrowser/theme.py +# rofi +curl "https://raw.githubusercontent.com/0xdec/base16-rofi/master/themes/base16-${scheme}.rasi" > ~/.config/rofi/theme.rasi +curl "https://raw.githubusercontent.com/0xdec/base16-rofi/master/themes/base16-${scheme}.config" > ~/.config/rofi/theme.config + # TODO dunst (template online, but not to my liking) # TODO bar (might change bar in the future, so...) # TODO qutebrowser (need to fiddle with the config thing) diff --git a/scripts/install-prefs b/scripts/install-prefs index 055cc90..ac288ed 100755 --- a/scripts/install-prefs +++ b/scripts/install-prefs @@ -190,8 +190,8 @@ if [ $ARCH == 1 ]; then altInst gopass else inst pass - wget -qO ~/.bin/ https://raw.githubusercontent.com/pepa65/tldr-bash-client/master/tldr - chmod +x ~/.bin/tldr + wget -qO ~/.local/bin/ https://raw.githubusercontent.com/pepa65/tldr-bash-client/master/tldr + chmod +x ~/.local/bin/tldr fi tldr -u if [[ $ARCH == 1 && $ADMIN == 1 ]]; then @@ -254,7 +254,7 @@ if [ $GUI == 1 ]; then cmake .. make -j`nproc` strip bin/polybar - mv bin/polybar ~/.bin/ + mv bin/polybar ~/.local/bin/ rm -rf $TMP fi fi @@ -287,10 +287,10 @@ if [ $GUI == 1 ]; then if [ $ARCH == 1 ]; then altInst sct elif [ $TERMUX != 1 ]; then - if [ ! -f $HOME/.bin/sct ]; then + if [ ! -f $HOME/.local/bin/sct ]; then TMP=$(mktemp /tmp/XXXXXXXXXX.c) wget https://gist.githubusercontent.com/ajnirp/208c03d3aa7f02c743d2/raw/55bf3eed25739173d8be57b5179ed5542cf40ed6/sct.c -O $TMP - cc $TMP --std=c99 -lX11 -lXrandr -o $HOME/.bin/sct + cc $TMP --std=c99 -lX11 -lXrandr -o $HOME/.local/bin/sct rm $TMP fi fi @@ -323,8 +323,8 @@ if [ $EXTRA == 1 ]; then systemdUserUnit vdirsyncer.timer else # translate-shell - curl -L git.io/trans > ~/.bin/trans - chmod +x ~/.bin/trans + curl -L git.io/trans > ~/.local/bin/trans + chmod +x ~/.local/bin/trans fi # Extra GUI diff --git a/vimrc b/vimrc index a8e9d10..f6c1eef 100644 --- a/vimrc +++ b/vimrc @@ -52,7 +52,7 @@ else endif Plug 'zchee/deoplete-jedi' -" Plug 'python-mode/python-mode', { 'branch': 'develop' } +Plug 'python-mode/python-mode', { 'branch': 'develop' } Plug 'junegunn/fzf', {'do': './install --bin'} Plug 'junegunn/fzf.vim' Plug 'ervandew/supertab' @@ -91,6 +91,7 @@ nmap :Autoformat """ PYMODE """ +let g:pymode_virtualenv = 1 let g:pymode_lint_ignore = ["W0401"] let g:pymode_lint_cwindow = 0