commit 68e3a68d70162f56363d57d430d0b4d10a10e84c
parent 9ca4c54b86cacc24893f9ddbf930ce54b9363115
Author: hhvn <dev@hhvn.uk>
Date: Thu, 9 May 2024 19:35:18 +0100
Stuff
Diffstat:
36 files changed, 1126 insertions(+), 555 deletions(-)
diff --git a/.config/bspwm/bspd b/.config/bspwm/bspd
@@ -1,21 +0,0 @@
-#!/usr/bin/env python
-
-import sys
-import os
-
-
-while True:
- line = sys.stdin.readline()
- if line == '':
- sys.exit()
- if line[0] != 'W':
- tokens = line.rstrip('\n').split(' ')
- path = os.getenv("PATH").split(':')
-
- for pdir in path:
- script = "%s/bspwm/handler/%s" % (pdir, tokens[0])
- if os.access(script, os.X_OK):
- tokens[0] = script
- if (os.fork() == 0):
- os.execv(script, tokens)
- break
diff --git a/.config/bspwm/bspwmrc b/.config/bspwm/bspwmrc
@@ -6,5 +6,7 @@ bspwm/configure-monitors
bspc dekstop Desktop -r
c_global_settings
+c_normal_win
+c_rules
-bspc subscribe all | $home/.config/bspwm/bspd
+#bspc subscribe all | $home/.config/bspwm/bspd
diff --git a/.config/bspwm/conf b/.config/bspwm/conf
@@ -2,39 +2,46 @@
fn set { bspc config $* }
+c_bg = '#050a10'
+c_sel = '#1b364b'
+c_urg = '#90222b'
+
fn c_global_settings {
+ set remove_unplugged_monitors true
+ set remove_disabled_monitors true
+
set split_ratio 0.5
- set borderless_monacle true
- set gapless_monacle true
-}
+ set borderless_monocle true
+ set gapless_monocle true
-# Monitors & desktops {{{
-c_monpriority = (HDMI2 HDMI1 eDP1)
+ set border_width 2
+ set normal_border_color $c_bg
+ set active_border_color $c_bg
+ set focused_border_color $c_sel
-c_desktops = (
- 'HDMI2 1 2 3 4'
- 'HDMI1 5 6 7 8'
- 'eDP1 9 10'
-)
-#}}}
+ set ignore_ewmh_focus true
+ set honor_size_hints false
+ set pointer_follows_focus true
+ set pointer_modifier super
+ set pointer_action1 resize_corner
+ set pointer_action2 move
+ set pointer_action3 move
+ set click_to_focus none
+}
-# Colours and aesthetics {{{
-c_bg = '#050a10'
-c_sel = '#1b364b'
-c_urg = '#90222b'
+fn rule { bspc rule -a $* }
-fn c_norm_border {
- set $* normal_border_color $c_bg
- set $* active_border_color $c_bg
- set $* focused_border_color $c_sel
+fn c_rules {
+ rule scrcpy state=floating sticky=on
}
-fn c_urgent_border {
- set $* normal_border_color $c_urgent
- set $* active_border_color $c_urgent
- set $* focused_border_color $c_sel
-}
+# Monitors & desktops
+c_monpriority = (HDMI1 HDMI2 eDP1)
-# }}}
+c_desktops = (
+ 'HDMI1 1 2 3 4'
+ 'HDMI2 5 6 7 8'
+ 'eDP1 9 10'
+)
diff --git a/.config/bspwm/keys b/.config/bspwm/keys
@@ -1,6 +1,46 @@
# Copyright (c) 2023 hhvn <dev@hhvn.uk>
# ISC license
+# vim: filetype=sxhkd :
#
# See also: git://hhvn.uk/sxhkd-rc
-#
-# vim: filetype=sxhkd :
+
+super + r
+ pkill -USR1 -x sxhkd; bspc wm -r
+
+super + q
+ bspc node -c
+
+# Tiled / monocle
+super + f
+ bspc desktop -l next
+
+# Like dwm
+super + shift + Return
+ bspc node -s biggest.local
+
+# Window management
+super + [ ,shift + ][h, j, k, l]
+ bspc node -[f,s] [west, south, north, east]
+
+super + [ ,shift + ][comma, period]
+ bspc node -[f,s] [prev, next].local
+
+
+super + alt + [h, j, k, l]
+ bspc node -p [west, south, north, east]
+
+super + alt + Return
+ bspc node -p cancel
+
+# super + f
+# bspc node -t ~fullscreen
+
+super + shift + f
+ bspc node -t ~floating
+
+# Desktop management
+super + [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
+ bspc desktop [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] -f
+
+super + shift + [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
+ bspc node -d [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
diff --git a/.config/hirc/config b/.config/hirc/config
@@ -36,13 +36,14 @@ Formats
/format topic %{b}topic%{b}%{=}${2}\nset by %{nick:${nick}}${nick}%{o} now
/format rpl.notopic %{b}topic%{b}%{=}no topic set
/format rpl.topic %{b}topic%{b}%{=}${3}
-/format rpl.whoreply %{b}%{nick:${6}}${6}!${3}@${4}%{b}%{o} (%{split:2, ,${8}}): ${7} %{split:1, ,${8}
+/format rpl.whoreply %{b}%{nick:${6}}${6}!${3}@${4}%{b}%{o} (%{split:2, ,${8}}): ${7} %{split:1, ,${8}}
Ignore ends of some commands for aesthetics
/ignore -format rpl.endofwhois .*
/ignore -format rpl.endofinfo .*
/ignore -format rpl.endofmotd .*
/ignore -format rpl.listend .*
+/ignore -format ui.ignores.end .*
Window decorations
/format ui.separator.vertical %{c:91}│
@@ -51,7 +52,6 @@ Window decorations
/format ui.separator.horizontal %{c:91}─
irssi muscle memory
-/alias /lastlog /grep
/alias /wc /close
Niceties
@@ -98,6 +98,11 @@ Buffer selection
Scrolling
/bind ^k /scroll 20
/bind ^j /scroll -20
+/bind ^Y /scroll 5
+/bind ^E /scroll -5
+
+Macros
+/bind ^[b /source ~/.config/hirc/macros/clutter
Autoconnexion
/source ~/.config/hirc/networks
diff --git a/.config/ls b/.config/ls
@@ -1,9 +1,11 @@
di=38;5;5 : fi=0 : ln=38;5;3 : ex=38;5;9;01
-*.sh=38;5;9 : *.zsh=38;5;9 : *.rc=38;5;9
-*.html=38;5;15 : *.css=38;5;15 : *.md=38;5;15
-*.conf=38;5;10 : *.vim=38;5;10 : *.yml=38;5;10 : *.hirc=38;5;10
+*.sh=38;5;9 : *.zsh=38;5;9 : *.rc=38;5;9
+*.html=38;5;15;02 : *.css=38;5;15 : *.md=38;5;15 : *.adoc=38;5;15
+*.conf=38;5;10 : *.vim=38;5;10 : *.yml=38;5;10 : *.hirc=38;5;10
+
+*.go=38;5;12
*.c=38;5;12 : *.h=38;5;12 : *.y=38;5;12
*.c.orig=38;5;12;01 : *.c.rej=38;5;12;01
*.h.orig=38;5;12;01 : *.h.rej=38;5;12;01
@@ -24,6 +26,7 @@ di=38;5;5 : fi=0 : ln=38;5;3 : ex=38;5;9;01
*Makefile=38;5;8 : *mkfile=38;5;8
*make.sh=38;5;8 : *.mk=38;5;8
+*go.mod=38;5;8 : *go.sum=38;5;8
*configure=38;5;8 : *autogen.sh=38;5;8 : *configure.sh=38;5;8 : *configure.rc=38;5;8
*.am=38;5;8 : *.in=38;5;8
diff --git a/.config/mpv/mpv.conf b/.config/mpv/mpv.conf
@@ -1,9 +1,16 @@
-ytdl-format="(bestvideo[height<=640]+bestaudio)[ext=webm]/bestvideo[height<=640]+bestaudio/best[height<=640]/bestvideo+bestaudio/best"
+# 1080p
+# ytdl-format="(bestvideo[height<=1080]+bestaudio)[ext=webm]/bestvideo[height<=1080]+bestaudio/best[height<=1080]/bestvideo+bestaudio/best"
+
+# 1440p
+ytdl-format="(bestvideo[height<=1080]+bestaudio)[ext=webm]/bestvideo[height<=1080]+bestaudio/best[height<=1080]/bestvideo+bestaudio/best"
+
+# default to no subtitles
no-sub-ass
no-sub
no-audio-display
volume-max=175
+video-sync=display-resample
profile=low-latency
scale=bilinear
cscale=bilinear
@@ -17,19 +24,17 @@ deband=no
hwdec=auto
vd-lavc-fast
-# vd-lavc-skiploopfilter=<skipvalue>
-# vd-lavc-skipframe=<skipvalue>
-# vd-lavc-framedrop=<skipvalue>
vd-lavc-threads=4
-# uosc
-osc=no
-osd-bar=no
-border=no
-
alang=en,eng,sv,swe,se
slang=en,eng,sv,swe,se
ao=sndio
+# make speed up audio sound better
af=scaletempo2
+
+# uosc
+osc=no
+osd-bar=no
+border=no
diff --git a/.config/mpv/scripts/sponsorblock.lua b/.config/mpv/scripts/sponsorblock.lua
@@ -0,0 +1,566 @@
+-- GPLv3
+
+-- sponsorblock.lua
+--
+-- This script skips sponsored segments of YouTube videos
+-- using data from https://github.com/ajayyy/SponsorBlock
+
+local ON_WINDOWS = package.config:sub(1,1) ~= "/"
+
+local options = {
+ server_address = "https://sponsor.ajay.app",
+
+ python_path = ON_WINDOWS and "python" or "python3",
+
+ -- Categories to fetch
+ categories = "sponsor,intro,outro,interaction,selfpromo",
+
+ -- Categories to skip automatically
+ skip_categories = "sponsor",
+
+ -- If true, sponsored segments will only be skipped once
+ skip_once = true,
+
+ -- Note that sponsored segments may ocasionally be inaccurate if this is turned off
+ -- see https://blog.ajay.app/voting-and-pseudo-randomness-or-sponsorblock-or-youtube-sponsorship-segment-blocker
+ local_database = false,
+
+ -- Update database on first run, does nothing if local_database is false
+ auto_update = false,
+
+ -- How long to wait between local database updates
+ -- Format: "X[d,h,m]", leave blank to update on every mpv run
+ auto_update_interval = "6h",
+
+ -- User ID used to submit sponsored segments, leave blank for random
+ user_id = "",
+
+ -- Name to display on the stats page https://sponsor.ajay.app/stats/ leave blank to keep current name
+ display_name = "",
+
+ -- Tell the server when a skip happens
+ report_views = true,
+
+ -- Auto upvote skipped sponsors
+ auto_upvote = false,
+
+ -- Use sponsor times from server if they're more up to date than our local database
+ server_fallback = true,
+
+ -- Create chapters at sponsor boundaries for OSC display and manual skipping
+ make_chapters = true,
+
+ -- Minimum duration for sponsors (in seconds), segments under that threshold will be ignored
+ min_duration = 1,
+
+ -- Fade audio for smoother transitions
+ audio_fade = false,
+
+ -- Audio fade step, applied once every 100ms until cap is reached
+ audio_fade_step = 10,
+
+ -- Audio fade cap
+ audio_fade_cap = 0,
+
+ -- Fast forward through sponsors instead of skipping
+ fast_forward = false,
+
+ -- Playback speed modifier when fast forwarding, applied once every second until cap is reached
+ fast_forward_increase = .2,
+
+ -- Playback speed cap
+ fast_forward_cap = 2,
+
+ -- Length of the sha256 prefix (3-32) when querying server, 0 to disable
+ sha256_length = 4,
+
+ -- Pattern for video id in local files, ignored if blank
+ -- Recommended value for base youtube-dl is "-([%w-_]+)%.[mw][kpe][v4b]m?$"
+ local_pattern = "",
+
+ -- Legacy option, use skip_categories instead
+ skip = true
+}
+
+mp.options = require "mp.options"
+mp.options.read_options(options, "sponsorblock")
+
+local legacy = mp.command_native_async == nil
+if legacy then
+ options.local_database = false
+end
+
+local utils = require "mp.utils"
+scripts_dir = mp.find_config_file("scripts")
+
+local sponsorblock = utils.join_path(scripts_dir, "sponsorblock_shared/sponsorblock.py")
+local uid_path = utils.join_path(scripts_dir, "sponsorblock_shared/sponsorblock.txt")
+local database_file = options.local_database and utils.join_path(scripts_dir, "sponsorblock_shared/sponsorblock.db") or ""
+local youtube_id = nil
+local ranges = {}
+local init = false
+local segment = {a = 0, b = 0, progress = 0, first = true}
+local retrying = false
+local last_skip = {uuid = "", dir = nil}
+local speed_timer = nil
+local fade_timer = nil
+local fade_dir = nil
+local volume_before = mp.get_property_number("volume")
+local categories = {}
+local all_categories = {"sponsor", "intro", "outro", "interaction", "selfpromo", "preview", "music_offtopic"}
+local chapter_cache = {}
+
+for category in string.gmatch(options.skip_categories, "([^,]+)") do
+ categories[category] = true
+end
+
+function file_exists(name)
+ local f = io.open(name,"r")
+ if f ~= nil then io.close(f) return true else return false end
+end
+
+function t_count(t)
+ local count = 0
+ for _ in pairs(t) do count = count + 1 end
+ return count
+end
+
+function time_sort(a, b)
+ if a.time == b.time then
+ return string.match(a.title, "segment end")
+ end
+ return a.time < b.time
+end
+
+function parse_update_interval()
+ local s = options.auto_update_interval
+ if s == "" then return 0 end -- Interval Disabled
+
+ local num, mod = s:match "^(%d+)([hdm])$"
+
+ if num == nil or mod == nil then
+ mp.osd_message("[sponsorblock] auto_update_interval " .. s .. " is invalid", 5)
+ return nil
+ end
+
+ local time_table = {
+ m = 60,
+ h = 60 * 60,
+ d = 60 * 60 * 24,
+ }
+
+ return num * time_table[mod]
+end
+
+function clean_chapters()
+ local chapters = mp.get_property_native("chapter-list")
+ local new_chapters = {}
+ for _, chapter in pairs(chapters) do
+ if chapter.title ~= "Preview segment start" and chapter.title ~= "Preview segment end" then
+ table.insert(new_chapters, chapter)
+ end
+ end
+ mp.set_property_native("chapter-list", new_chapters)
+end
+
+function create_chapter(chapter_title, chapter_time)
+ local chapters = mp.get_property_native("chapter-list")
+ local duration = mp.get_property_native("duration")
+ table.insert(chapters, {title=chapter_title, time=(duration == nil or duration > chapter_time) and chapter_time or duration - .001})
+ table.sort(chapters, time_sort)
+ mp.set_property_native("chapter-list", chapters)
+end
+
+function process(uuid, t, new_ranges)
+ start_time = tonumber(string.match(t, "[^,]+"))
+ end_time = tonumber(string.sub(string.match(t, ",[^,]+"), 2))
+ for o_uuid, o_t in pairs(ranges) do
+ if (start_time >= o_t.start_time and start_time <= o_t.end_time) or (o_t.start_time >= start_time and o_t.start_time <= end_time) then
+ new_ranges[o_uuid] = o_t
+ return
+ end
+ end
+ category = string.match(t, "[^,]+$")
+ if categories[category] and end_time - start_time >= options.min_duration then
+ new_ranges[uuid] = {
+ start_time = start_time,
+ end_time = end_time,
+ category = category,
+ skipped = false
+ }
+ end
+ if options.make_chapters and not chapter_cache[uuid] then
+ chapter_cache[uuid] = true
+ local category_title = (category:gsub("^%l", string.upper):gsub("_", " "))
+ create_chapter(category_title .. " segment start (" .. string.sub(uuid, 1, 6) .. ")", start_time)
+ create_chapter(category_title .. " segment end (" .. string.sub(uuid, 1, 6) .. ")", end_time)
+ end
+end
+
+function getranges(_, exists, db, more)
+ if type(exists) == "table" and exists["status"] == "1" then
+ if options.server_fallback then
+ mp.add_timeout(0, function() getranges(true, true, "") end)
+ else
+ return mp.osd_message("[sponsorblock] database update failed, gave up")
+ end
+ end
+ if db ~= "" and db ~= database_file then db = database_file end
+ if exists ~= true and not file_exists(db) then
+ if not retrying then
+ mp.osd_message("[sponsorblock] database update failed, retrying...")
+ retrying = true
+ end
+ return update()
+ end
+ if retrying then
+ mp.osd_message("[sponsorblock] database update succeeded")
+ retrying = false
+ end
+ local sponsors
+ local args = {
+ options.python_path,
+ sponsorblock,
+ "ranges",
+ db,
+ options.server_address,
+ youtube_id,
+ options.categories,
+ tostring(options.sha256_length)
+ }
+ if not legacy then
+ sponsors = mp.command_native({name = "subprocess", capture_stdout = true, playback_only = false, args = args})
+ else
+ sponsors = utils.subprocess({args = args})
+ end
+ mp.msg.debug("Got: " .. string.gsub(sponsors.stdout, "[\n\r]", ""))
+ if not string.match(sponsors.stdout, "^%s*(.*%S)") then return end
+ if string.match(sponsors.stdout, "error") then return getranges(true, true) end
+ local new_ranges = {}
+ local r_count = 0
+ if more then r_count = -1 end
+ for t in string.gmatch(sponsors.stdout, "[^:%s]+") do
+ uuid = string.match(t, "([^,]+),[^,]+$")
+ if ranges[uuid] then
+ new_ranges[uuid] = ranges[uuid]
+ else
+ process(uuid, t, new_ranges)
+ end
+ r_count = r_count + 1
+ end
+ local c_count = t_count(ranges)
+ if c_count == 0 or r_count >= c_count then
+ ranges = new_ranges
+ end
+end
+
+function fast_forward()
+ if options.fast_forward and options.fast_forward == true then
+ speed_timer = nil
+ mp.set_property("speed", 1)
+ end
+ local last_speed = mp.get_property_number("speed")
+ local new_speed = math.min(last_speed + options.fast_forward_increase, options.fast_forward_cap)
+ if new_speed <= last_speed then return end
+ mp.set_property("speed", new_speed)
+end
+
+function fade_audio(step)
+ local last_volume = mp.get_property_number("volume")
+ local new_volume = math.max(options.audio_fade_cap, math.min(last_volume + step, volume_before))
+ if new_volume == last_volume then
+ if step >= 0 then fade_dir = nil end
+ if fade_timer ~= nil then fade_timer:kill() end
+ fade_timer = nil
+ return
+ end
+ mp.set_property("volume", new_volume)
+end
+
+function skip_ads(name, pos)
+ if pos == nil then return end
+ local sponsor_ahead = false
+ for uuid, t in pairs(ranges) do
+ if (options.fast_forward == uuid or not options.skip_once or not t.skipped) and t.start_time <= pos and t.end_time > pos then
+ if options.fast_forward == uuid then return end
+ if options.fast_forward == false then
+ mp.osd_message("[sponsorblock] " .. t.category .. " skipped")
+ mp.set_property("time-pos", t.end_time)
+ else
+ mp.osd_message("[sponsorblock] skipping " .. t.category)
+ end
+ t.skipped = true
+ last_skip = {uuid = uuid, dir = nil}
+ if options.report_views or options.auto_upvote then
+ local args = {
+ options.python_path,
+ sponsorblock,
+ "stats",
+ database_file,
+ options.server_address,
+ youtube_id,
+ uuid,
+ options.report_views and "1" or "",
+ uid_path,
+ options.user_id,
+ options.auto_upvote and "1" or ""
+ }
+ if not legacy then
+ mp.command_native_async({name = "subprocess", playback_only = false, args = args}, function () end)
+ else
+ utils.subprocess_detached({args = args})
+ end
+ end
+ if options.fast_forward ~= false then
+ options.fast_forward = uuid
+ if speed_timer ~= nil then speed_timer:kill() end
+ speed_timer = mp.add_periodic_timer(1, fast_forward)
+ end
+ return
+ elseif (not options.skip_once or not t.skipped) and t.start_time <= pos + 1 and t.end_time > pos + 1 then
+ sponsor_ahead = true
+ end
+ end
+ if options.audio_fade then
+ if sponsor_ahead then
+ if fade_dir ~= false then
+ if fade_dir == nil then volume_before = mp.get_property_number("volume") end
+ if fade_timer ~= nil then fade_timer:kill() end
+ fade_dir = false
+ fade_timer = mp.add_periodic_timer(.1, function() fade_audio(-options.audio_fade_step) end)
+ end
+ elseif fade_dir == false then
+ fade_dir = true
+ if fade_timer ~= nil then fade_timer:kill() end
+ fade_timer = mp.add_periodic_timer(.1, function() fade_audio(options.audio_fade_step) end)
+ end
+ end
+ if options.fast_forward and options.fast_forward ~= true then
+ options.fast_forward = true
+ speed_timer:kill()
+ speed_timer = nil
+ mp.set_property("speed", 1)
+ end
+end
+
+function vote(dir)
+ if last_skip.uuid == "" then return mp.osd_message("[sponsorblock] no sponsors skipped, can't submit vote") end
+ local updown = dir == "1" and "up" or "down"
+ if last_skip.dir == dir then return mp.osd_message("[sponsorblock] " .. updown .. "vote already submitted") end
+ last_skip.dir = dir
+ local args = {
+ options.python_path,
+ sponsorblock,
+ "stats",
+ database_file,
+ options.server_address,
+ youtube_id,
+ last_skip.uuid,
+ "",
+ uid_path,
+ options.user_id,
+ dir
+ }
+ if not legacy then
+ mp.command_native_async({name = "subprocess", playback_only = false, args = args}, function () end)
+ else
+ utils.subprocess({args = args})
+ end
+ mp.osd_message("[sponsorblock] " .. updown .. "vote submitted")
+end
+
+function update()
+ mp.command_native_async({name = "subprocess", playback_only = false, args = {
+ options.python_path,
+ sponsorblock,
+ "update",
+ database_file,
+ options.server_address
+ }}, getranges)
+end
+
+function file_loaded()
+ local initialized = init
+ ranges = {}
+ segment = {a = 0, b = 0, progress = 0, first = true}
+ last_skip = {uuid = "", dir = nil}
+ chapter_cache = {}
+ local video_path = mp.get_property("path", "")
+ mp.msg.debug("Path: " .. video_path)
+ local video_referer = string.match(mp.get_property("http-header-fields", ""), "Referer:([^,]+)") or ""
+ mp.msg.debug("Referer: " .. video_referer)
+
+ local urls = {
+ "https?://youtu%.be/([%w-_]+).*",
+ "https?://w?w?w?%.?youtube%.com/v/([%w-_]+).*",
+ "/watch.*[?&]v=([%w-_]+).*",
+ "/embed/([%w-_]+).*"
+ }
+ youtube_id = nil
+ for i,url in ipairs(urls) do
+ youtube_id = youtube_id or string.match(video_path, url) or string.match(video_referer, url)
+ end
+ youtube_id = youtube_id or string.match(video_path, options.local_pattern)
+
+ if not youtube_id or string.len(youtube_id) < 11 or (local_pattern and string.len(youtube_id) ~= 11) then return end
+ youtube_id = string.sub(youtube_id, 1, 11)
+ mp.msg.debug("Found YouTube ID: " .. youtube_id)
+ init = true
+ if not options.local_database then
+ getranges(true, true)
+ else
+ local exists = file_exists(database_file)
+ if exists and options.server_fallback then
+ getranges(true, true)
+ mp.add_timeout(0, function() getranges(true, true, "", true) end)
+ elseif exists then
+ getranges(true, true)
+ elseif options.server_fallback then
+ mp.add_timeout(0, function() getranges(true, true, "") end)
+ end
+ end
+ if initialized then return end
+ if options.skip then
+ mp.observe_property("time-pos", "native", skip_ads)
+ end
+ if options.display_name ~= "" then
+ local args = {
+ options.python_path,
+ sponsorblock,
+ "username",
+ database_file,
+ options.server_address,
+ youtube_id,
+ "",
+ "",
+ uid_path,
+ options.user_id,
+ options.display_name
+ }
+ if not legacy then
+ mp.command_native_async({name = "subprocess", playback_only = false, args = args}, function () end)
+ else
+ utils.subprocess_detached({args = args})
+ end
+ end
+ if not options.local_database or (not options.auto_update and file_exists(database_file)) then return end
+
+ if file_exists(database_file) then
+ local db_info = utils.file_info(database_file)
+ local cur_time = os.time(os.date("*t"))
+ local upd_interval = parse_update_interval()
+ if upd_interval == nil or os.difftime(cur_time, db_info.mtime) < upd_interval then return end
+ end
+
+ update()
+end
+
+function set_segment()
+ if not youtube_id then return end
+ local pos = mp.get_property_number("time-pos")
+ if pos == nil then return end
+ if segment.progress > 1 then
+ segment.progress = segment.progress - 2
+ end
+ if segment.progress == 1 then
+ segment.progress = 0
+ segment.b = pos
+ mp.osd_message("[sponsorblock] segment boundary B set, press again for boundary A", 3)
+ else
+ segment.progress = 1
+ segment.a = pos
+ mp.osd_message("[sponsorblock] segment boundary A set, press again for boundary B", 3)
+ end
+ if options.make_chapters and not segment.first then
+ local start_time = math.min(segment.a, segment.b)
+ local end_time = math.max(segment.a, segment.b)
+ if end_time - start_time ~= 0 and end_time ~= 0 then
+ clean_chapters()
+ create_chapter("Preview segment start", start_time)
+ create_chapter("Preview segment end", end_time)
+ end
+ end
+ segment.first = false
+end
+
+function select_category(selected)
+ for category in string.gmatch(options.categories, "([^,]+)") do
+ mp.remove_key_binding("select_category_"..category)
+ mp.remove_key_binding("kp_select_category_"..category)
+ end
+ submit_segment(selected)
+end
+
+function submit_segment(category)
+ if not youtube_id then return end
+ local start_time = math.min(segment.a, segment.b)
+ local end_time = math.max(segment.a, segment.b)
+ if end_time - start_time == 0 or end_time == 0 then
+ mp.osd_message("[sponsorblock] empty segment, not submitting")
+ elseif segment.progress <= 1 then
+ segment.progress = segment.progress + 2
+ local category_list = ""
+ for category_id, category in pairs(all_categories) do
+ local category_title = (category:gsub("^%l", string.upper):gsub("_", " "))
+ category_list = category_list .. category_id .. ": " .. category_title .. "\n"
+ mp.add_forced_key_binding(tostring(category_id), "select_category_"..category, function() select_category(category) end)
+ mp.add_forced_key_binding("KP"..tostring(category_id), "kp_select_category_"..category, function() select_category(category) end)
+ end
+ mp.osd_message(string.format("[sponsorblock] press a number to select category for segment: %.2d:%.2d:%.2d to %.2d:%.2d:%.2d\n\n" .. category_list .. "\nyou can press Shift+G again for default (Sponsor) or hide this message with g", math.floor(start_time/(60*60)), math.floor(start_time/60%60), math.floor(start_time%60), math.floor(end_time/(60*60)), math.floor(end_time/60%60), math.floor(end_time%60)), 30)
+ else
+ mp.osd_message("[sponsorblock] submitting segment...", 30)
+ local submit
+ local args = {
+ options.python_path,
+ sponsorblock,
+ "submit",
+ database_file,
+ options.server_address,
+ youtube_id,
+ tostring(start_time),
+ tostring(end_time),
+ uid_path,
+ options.user_id,
+ category or "sponsor"
+ }
+ if not legacy then
+ submit = mp.command_native({name = "subprocess", capture_stdout = true, playback_only = false, args = args})
+ else
+ submit = utils.subprocess({args = args})
+ end
+ if string.match(submit.stdout, "success") then
+ segment = {a = 0, b = 0, progress = 0, first = true}
+ mp.osd_message("[sponsorblock] segment submitted")
+ if options.make_chapters then
+ clean_chapters()
+ create_chapter("Submitted segment start", start_time)
+ create_chapter("Submitted segment end", end_time)
+ end
+ elseif string.match(submit.stdout, "error") then
+ mp.osd_message("[sponsorblock] segment submission failed, server may be down. try again", 5)
+ elseif string.match(submit.stdout, "502") then
+ mp.osd_message("[sponsorblock] segment submission failed, server is down. try again", 5)
+ elseif string.match(submit.stdout, "400") then
+ mp.osd_message("[sponsorblock] segment submission failed, impossible inputs", 5)
+ segment = {a = 0, b = 0, progress = 0, first = true}
+ elseif string.match(submit.stdout, "429") then
+ mp.osd_message("[sponsorblock] segment submission failed, rate limited. try again", 5)
+ elseif string.match(submit.stdout, "409") then
+ mp.osd_message("[sponsorblock] segment already submitted", 3)
+ segment = {a = 0, b = 0, progress = 0, first = true}
+ else
+ mp.osd_message("[sponsorblock] segment submission failed", 5)
+ end
+ end
+end
+
+mp.register_event("file-loaded", file_loaded)
+mp.add_key_binding("g", "set_segment", set_segment)
+mp.add_key_binding("G", "submit_segment", submit_segment)
+mp.add_key_binding("$", "upvote_segment", function() return vote("1") end)
+mp.add_key_binding("%", "downvote_segment", function() return vote("0") end)
+-- Bindings below are for backwards compatibility and could be removed at any time
+mp.add_key_binding(nil, "sponsorblock_set_segment", set_segment)
+mp.add_key_binding(nil, "sponsorblock_submit_segment", submit_segment)
+mp.add_key_binding(nil, "sponsorblock_upvote", function() return vote("1") end)
+mp.add_key_binding(nil, "sponsorblock_downvote", function() return vote("0") end)
diff --git a/.config/mpv/scripts/sponsorblock_shared/main.lua b/.config/mpv/scripts/sponsorblock_shared/main.lua
@@ -0,0 +1,3 @@
+-- This is a dummy main.lua
+-- required for mpv 0.33
+-- do not delete
+\ No newline at end of file
diff --git a/.config/mpv/scripts/sponsorblock_shared/sponsorblock.py b/.config/mpv/scripts/sponsorblock_shared/sponsorblock.py
@@ -0,0 +1,124 @@
+# GPLv3
+
+import urllib.request
+import urllib.parse
+import hashlib
+import sqlite3
+import random
+import string
+import json
+import sys
+import os
+
+if sys.argv[1] in ["submit", "stats", "username"]:
+ if not sys.argv[8]:
+ if os.path.isfile(sys.argv[7]):
+ with open(sys.argv[7]) as f:
+ uid = f.read()
+ else:
+ uid = "".join(random.choices(string.ascii_letters + string.digits, k=36))
+ with open(sys.argv[7], "w") as f:
+ f.write(uid)
+ else:
+ uid = sys.argv[8]
+
+opener = urllib.request.build_opener()
+opener.addheaders = [("User-Agent", "mpv_sponsorblock/1.0 (https://github.com/po5/mpv_sponsorblock)")]
+urllib.request.install_opener(opener)
+
+if sys.argv[1] == "ranges" and (not sys.argv[2] or not os.path.isfile(sys.argv[2])):
+ sha = None
+ if 3 <= int(sys.argv[6]) <= 32:
+ sha = hashlib.sha256(sys.argv[4].encode()).hexdigest()[:int(sys.argv[6])]
+ times = []
+ try:
+ response = urllib.request.urlopen(sys.argv[3] + "/api/skipSegments" + ("/" + sha + "?" if sha else "?videoID=" + sys.argv[4] + "&") + urllib.parse.urlencode([("categories", json.dumps(sys.argv[5].split(",")))]))
+ segments = json.load(response)
+ for segment in segments:
+ if sha and sys.argv[4] != segment["videoID"]:
+ continue
+ if sha:
+ for s in segment["segments"]:
+ times.append(str(s["segment"][0]) + "," + str(s["segment"][1]) + "," + s["UUID"] + "," + s["category"])
+ else:
+ times.append(str(segment["segment"][0]) + "," + str(segment["segment"][1]) + "," + segment["UUID"] + "," + segment["category"])
+ print(":".join(times))
+ except (TimeoutError, urllib.error.URLError) as e:
+ print("error")
+ except urllib.error.HTTPError as e:
+ if e.code == 404:
+ print("")
+ else:
+ print("error")
+elif sys.argv[1] == "ranges":
+ conn = sqlite3.connect(sys.argv[2])
+ conn.row_factory = sqlite3.Row
+ c = conn.cursor()
+ times = []
+ for category in sys.argv[5].split(","):
+ c.execute("SELECT startTime, endTime, votes, UUID, category FROM sponsorTimes WHERE videoID = ? AND shadowHidden = 0 AND votes > -1 AND category = ?", (sys.argv[4], category))
+ sponsors = c.fetchall()
+ best = list(sponsors)
+ dealtwith = []
+ similar = []
+ for sponsor_a in sponsors:
+ for sponsor_b in sponsors:
+ if sponsor_a is not sponsor_b and sponsor_a["startTime"] >= sponsor_b["startTime"] and sponsor_a["startTime"] <= sponsor_b["endTime"]:
+ similar.append([sponsor_a, sponsor_b])
+ if sponsor_a in best:
+ best.remove(sponsor_a)
+ if sponsor_b in best:
+ best.remove(sponsor_b)
+ for sponsors_a in similar:
+ if sponsors_a in dealtwith:
+ continue
+ group = set(sponsors_a)
+ for sponsors_b in similar:
+ if sponsors_b[0] in group or sponsors_b[1] in group:
+ group.add(sponsors_b[0])
+ group.add(sponsors_b[1])
+ dealtwith.append(sponsors_b)
+ best.append(max(group, key=lambda x:x["votes"]))
+ for time in best:
+ times.append(str(time["startTime"]) + "," + str(time["endTime"]) + "," + time["UUID"] + "," + time["category"])
+ print(":".join(times))
+elif sys.argv[1] == "update":
+ try:
+ urllib.request.urlretrieve(sys.argv[3] + "/database.db", sys.argv[2] + ".tmp")
+ os.replace(sys.argv[2] + ".tmp", sys.argv[2])
+ except PermissionError:
+ print("database update failed, file currently in use", file=sys.stderr)
+ sys.exit(1)
+ except ConnectionResetError:
+ print("database update failed, connection reset", file=sys.stderr)
+ sys.exit(1)
+ except TimeoutError:
+ print("database update failed, timed out", file=sys.stderr)
+ sys.exit(1)
+ except urllib.error.URLError:
+ print("database update failed", file=sys.stderr)
+ sys.exit(1)
+elif sys.argv[1] == "submit":
+ try:
+ req = urllib.request.Request(sys.argv[3] + "/api/skipSegments", data=json.dumps({"videoID": sys.argv[4], "segments": [{"segment": [float(sys.argv[5]), float(sys.argv[6])], "category": sys.argv[9]}], "userID": uid}).encode(), headers={"Content-Type": "application/json"})
+ response = urllib.request.urlopen(req)
+ print("success")
+ except urllib.error.HTTPError as e:
+ print(e.code)
+ except:
+ print("error")
+elif sys.argv[1] == "stats":
+ try:
+ if sys.argv[6]:
+ urllib.request.urlopen(sys.argv[3] + "/api/viewedVideoSponsorTime?UUID=" + sys.argv[5])
+ if sys.argv[9]:
+ urllib.request.urlopen(sys.argv[3] + "/api/voteOnSponsorTime?UUID=" + sys.argv[5] + "&userID=" + uid + "&type=" + sys.argv[9])
+ except:
+ pass
+elif sys.argv[1] == "username":
+ try:
+ data = urllib.parse.urlencode({"userID": uid, "userName": sys.argv[9]}).encode()
+ req = urllib.request.Request(sys.argv[3] + "/api/setUsername", data=data)
+ urllib.request.urlopen(req)
+ except:
+ pass
diff --git a/.config/mpv/scripts/sponsorblock_shared/sponsorblock.txt b/.config/mpv/scripts/sponsorblock_shared/sponsorblock.txt
@@ -0,0 +1 @@
+tKxWZu9YIdFw0N076PS1f66ZjxP9TpM8Rcnk
+\ No newline at end of file
diff --git a/.config/mpv/watch_later/04DA18843AC93F825AE495D1DFCA4CBF b/.config/mpv/watch_later/04DA18843AC93F825AE495D1DFCA4CBF
@@ -0,0 +1 @@
+start=707.842333
diff --git a/.config/mpv/watch_later/1DAD0641B5D9BC10A47E66A8B9D6A9E9 b/.config/mpv/watch_later/1DAD0641B5D9BC10A47E66A8B9D6A9E9
@@ -0,0 +1 @@
+# redirect entry
diff --git a/.config/mpv/watch_later/20963CD7DBA0991EFF3F99883ACA8F4B b/.config/mpv/watch_later/20963CD7DBA0991EFF3F99883ACA8F4B
@@ -0,0 +1,2 @@
+start=178.006556
+fullscreen=yes
diff --git a/.config/mpv/watch_later/46DB9A533C9815F63F3232F2B01601A4 b/.config/mpv/watch_later/46DB9A533C9815F63F3232F2B01601A4
@@ -0,0 +1,3 @@
+start=949.833000
+pause=no
+fullscreen=yes
diff --git a/.config/neomutt/alias b/.config/neomutt/alias
@@ -0,0 +1 @@
+alias alex Alex Hamilton <alexhamilton1978@hotmail.com>
diff --git a/.config/neomutt/colours b/.config/neomutt/colours
@@ -1,72 +0,0 @@
-# vim: filetype=neomuttrc
-
-set index_format="%4C %Z %{%b %d} %-15.15r %-20.20L (%?l?%4l&%4c?) %-3.3M %s"
-
-# Default index colors:
-color index yellow default '.*'
-color index_author magenta default '.*'
-color index_number magenta default
-color index_subject cyan default '.*'
-
-# For new mail:
-color index_flags color14 default "~N"
-color index_flags color14 default "~O"
-
-# For threads
-#color index_subject default default "~v"
-color index_flags color14 default "~(~N)" # matches threads which contain unread entries
-color index_flags color14 default "~(~O)" # ditto
-
-# Header colors:
-color header blue default ".*"
-color header brightmagenta default "^(From)"
-color header brightcyan default "^(Subject)"
-color header brightwhite default "^(CC|BCC)"
-
-mono bold bold
-mono underline underline
-mono indicator reverse
-mono error bold
-color normal default default
-color indicator brightblack white
-color normal brightyellow default
-color error red default
-color tilde black default
-color message cyan default
-color markers red white
-color attachment white default
-color search brightmagenta default
-color status brightyellow black
-color hdrdefault brightgreen default
-color quoted color3 default
-color quoted1 color4 default
-color quoted2 color5 default
-color quoted3 color6 default
-color quoted4 color7 default
-color quoted5 color7 default
-color quoted6 color7 default
-color quoted7 color7 default
-color signature brightgreen default
-color bold black default
-color underline black default
-color normal default default
-
-color body brightred default "[\-\.+_a-zA-Z0-9]+@[\-\.a-zA-Z0-9]+" # Email addresses
-color body brightblue default "(https?|ftp)://[\-\.,/%~_:?&=\#a-zA-Z0-9]+" # URL
-color body green default "\`[^\`]*\`" # Green text between ` and `
-color body brightblue default "^# \.*" # Headings as bold blue
-color body brightcyan default "^## \.*" # Subheadings as bold cyan
-color body brightgreen default "^### \.*" # Subsubheadings as bold green
-color body yellow default "^(\t| )*(-|\\*) \.*" # List items as yellow
-color body color10 default "(BAD signature)"
-color body color14 default "(Good signature)"
-color body brightyellow default "^gpg: "
-color body red default "([a-z][a-z0-9+-]*://(((([a-z0-9_.!~*'();:&=+$,-]|%[0-9a-f][0-9a-f])*@)?((([a-z0-9]([a-z0-9-]*[a-z0-9])?)\\.)*([a-z]([a-z0-9-]*[a-z0-9])?)\\.?|[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+)(:[0-9]+)?)|([a-z0-9_.!~*'()$,;:@&=+-]|%[0-9a-f][0-9a-f])+)(/([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*(;([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*)*(/([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*(;([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*)*)*)?(\\?([a-z0-9_.!~*'();/?:@&=+$,-]|%[0-9a-f][0-9a-f])*)?(#([a-z0-9_.!~*'();/?:@&=+$,-]|%[0-9a-f][0-9a-f])*)?|(www|ftp)\\.(([a-z0-9]([a-z0-9-]*[a-z0-9])?)\\.)*([a-z]([a-z0-9-]*[a-z0-9])?)\\.?(:[0-9]+)?(/([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*(;([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*)*(/([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*(;([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*)*)*)?(\\?([-a-z0-9_.!~*'();/?:@&=+$,]|%[0-9a-f][0-9a-f])*)?(#([-a-z0-9_.!~*'();/?:@&=+$,]|%[0-9a-f][0-9a-f])*)?)[^].,:;!)? \t\r\n<>\"]"
-
-# Set status bar color
-set status_format=""
-set compose_format=""
-set pager_format=""
-set markers=no
-color status color5 default
-color progress color2 color23
diff --git a/.config/neomutt/neomuttrc b/.config/neomutt/neomuttrc
@@ -1,40 +1,16 @@
-set ssl_starttls = yes
-set ssl_force_tls = yes
set mailcap_path = "~/.config/neomutt/mailcap"
-set smtp_authenticators = "plain"
-
-set abort_key = "<Esc>"
-set abort_nosubject = no
-set abort_unmodified = no
set alias_file = "~/.config/neomutt/alias"
-source folders
-source colours
+set folder = "~/mail/"
+set header_cache = "~/mail/cache/"
+set message_cachedir = "~/mail/cache/"
+
+source ~/mail/neomuttrc
source sidebar
source alias
source gpg
-bind index D purge-message
-bind index,pager M compose-to-sender
-bind index \Ct collapse-thread
-bind index \Ca collapse-all
-
-bind compose F edit-from
-
-set sort=threads
-set collapse_all
-set help=no
-
set signature=~/.config/neomutt/signatures
-macro index S <save-message>=spam<enter><enter>
-macro index A <save-message>=spam-domain<enter><enter>
-macro index R \
- <collapse-all>"T ~O | ~N\n;N" \
- "mark all new as read"
-macro index E <save-message>=sent<enter><enter>
-
-my_hdr X-Mailer: neomutt
-hdr_order Subject From To Date
-
-set new_mail_command = "sh -c 'herbe \"%b mailboxes have new mail\" &'"
+macro index z "<enter-command>unset wait_key\n<pipe-entry>bmf -S\n<enter-command>set wait_key\n<save-message>=spam"
+macro index x "<enter-command>unset wait_key\n<pipe-entry>bmf -N\n<enter-command>set wait_key\n<save-message>="
diff --git a/.config/neomutt/signatures b/.config/neomutt/signatures
@@ -0,0 +1,2 @@
+Regards,
+Hayden
diff --git a/.config/neomutt/users/backtrack@airmail.cc b/.config/neomutt/users/backtrack@airmail.cc
@@ -0,0 +1,7 @@
+# vim: set syntax=neomuttrc :
+set imap_pass = `dpass cock.li`
+set smtp_pass = `dpass cock.li`
+set spoolfile = "+inbox-airmail"
+set smtp_url = "smtp://backtrack@airmail.cc@mail.cock.li:587"
+set from = "backtrack@airmail.cc"
+set realname = "hhvn"
diff --git a/.config/neomutt/users/hayden@haydenvh.com b/.config/neomutt/users/hayden@haydenvh.com
@@ -0,0 +1,8 @@
+# vim: set syntax=neomuttrc :
+set spoolfile = "+haydenvhcom"
+set smtp_url = "smtp://haydenvh.com:15556"
+set from = "hayden@haydenvh.com"
+set realname = "Hayden Hamilton"
+set smtp_authenticators = plain
+
+my_hdr Organization: haydenvh.com
diff --git a/.config/neomutt/users/hhvn@dataswamp.org b/.config/neomutt/users/hhvn@dataswamp.org
@@ -0,0 +1,6 @@
+# vim: set syntax=neomuttrc :
+set smtp_pass = `dpass smtp-hayden@haydenvh.com`
+set spoolfile = "+dataswamporg"
+set smtp_url = "smtp://void@haydenvh.com:587"
+set from = "hhvn@dataswamp.org"
+set realname = "hhvn"
diff --git a/.config/neomutt/users/irc@hlirc.net b/.config/neomutt/users/irc@hlirc.net
@@ -0,0 +1,9 @@
+# vim: set syntax=neomuttrc :
+set smtp_pass = `dpass smtp-hayden@haydenvh.com`
+set spoolfile = "+inbox-hlircnet"
+set smtp_url = "smtp://hayden@haydenvh.com:587"
+set from = "hhvn@hlirc.net"
+set realname = "hhvn"
+#set pgp_sign_as=0xCDCBDA3B70AC90AB
+
+my_hdr Organization: HLIRCnet
diff --git a/.config/nvim/init.vim b/.config/nvim/init.vim
@@ -156,9 +156,6 @@ ino <Down> <Nop>
ino <Left> <Nop>
ino <Right> <Nop>
-"Use o/O
-no A<CR> <Nop>
-
"Registers
set clipboard=unnamed
diff --git a/.config/nvim/modules/filetype.vim b/.config/nvim/modules/filetype.vim
@@ -49,7 +49,6 @@ endfunction
augroup filetypes
autocmd FileType,WinEnter,BufEnter netrw call Configurenetrw()
- autocmd FileType html :normal gg=G
autocmd FileType html :setlocal nowrap
autocmd FileType c :noremap <buffer> <localleader>e $a;<esc>
autocmd FileType c :inoremap <buffer> <localleader><localleader>e <esc>$a;<esc>
diff --git a/.config/qutebrowser/config.py b/.config/qutebrowser/config.py
@@ -1,4 +1,4 @@
-from PyQt5.QtCore import QUrl
+from PyQt6.QtCore import QUrl
from qutebrowser.api import interceptor
from qutebrowser.browser import commands
import subprocess
@@ -21,17 +21,59 @@ c.changelog_after_upgrade = 'never'
# Don't annoy me.
c.new_instance_open_target = 'tab-bg-silent'
+white = '#cccccc'
+normfg = '#cccccc'
+normbg = '#050a10'
+selbg = '#1b364b'
+
# Selected tabs
c.colors.tabs.selected.even.bg = '#1b364b'
c.colors.tabs.selected.odd.bg = '#1b364b'
-c.colors.tabs.selected.even.fg = 'white'
-c.colors.tabs.selected.odd.fg = 'white'
-
-# Unselected tabs
+c.colors.tabs.selected.even.fg = white
+c.colors.tabs.selected.odd.fg = white
c.colors.tabs.even.bg = '#050a10'
c.colors.tabs.odd.bg = '#050a10'
-c.colors.tabs.even.fg = 'white'
-c.colors.tabs.odd.fg = 'white'
+c.colors.tabs.even.fg = white
+c.colors.tabs.odd.fg = white
+
+# Unselected tabs
+c.colors.webpage.darkmode.enabled = False
+c.colors.webpage.darkmode.enabled = False
+
+# Statusbar
+c.colors.statusbar.url.hover.fg = 'aqua'
+c.colors.statusbar.progress.bg = selbg
+c.colors.statusbar.command.private.fg = '#005f87'
+c.colors.statusbar.command.private.bg = selbg
+c.colors.statusbar.passthrough.fg = '#bb5540'
+c.colors.statusbar.passthrough.bg = normbg
+c.colors.statusbar.normal.fg = normfg
+c.colors.statusbar.normal.bg = 'black'
+c.colors.statusbar.command.fg = '#005f87'
+c.colors.statusbar.command.bg = 'black'
+
+# Completion
+# c.colors.completion.category.border.bottom = 'black'
+# c.colors.completion.category.border.top = 'black'
+# c.colors.completion.category.fg = 'white'
+# c.colors.completion.even.bg = '#333333'
+# c.colors.completion.fg = [white, white, 'white']
+# c.colors.completion.odd.bg = '#444444'
+# c.colors.completion.odd.bg = '#444444'
+# c.colors.completion.category.fg = cyan
+# c.colors.completion.category.bg = normbg
+# c.colors.completion.category.border.bottom = normbg
+# c.colors.completion.category.border.top = normbg
+# c.colors.completion.item.selected.bg = '#e8c000'
+# c.colors.completion.item.selected.border.bottom = '#bbbb00'
+# c.colors.completion.item.selected.border.top = '#bbbb00'
+# c.colors.completion.item.selected.fg = 'black'
+# c.colors.completion.item.selected.match.fg = '#ff4444'
+# c.colors.completion.match.fg = '#ff4444'
+# c.completion.scrollbar.width = 0
+# c.completion.scrollbar.padding = 0
+# c.colors.completion.scrollbar.bg = normbg
+# c.colors.completion.scrollbar.fg = normbg
# Tabbed muscle memory
config.bind('<Alt-q>', 'tab-close')
@@ -54,17 +96,23 @@ c.editor.command = ['rc', '-c', 'cd `$nl{dirname \'{file}\'}; st -e vim `$nl{bas
config.bind('R', 'config-source')
config.bind('e', 'config-edit')
+# Shady
+c.aliases = {'tor': 'set content.proxy socks://localhost:9050 ;; set content.javascript.enabled false ;; open -t check.torproject.org',
+ 'i2p': 'set content.proxy http://localhost:4444 ;; set content.javascript.enabled false ;; open -t localhost:7070',
+ 'unproxy': 'set content.proxy none ;; set content.javascript.enabled true',
+ 'jon': 'set content.javascript.enabled true',
+ 'joff': 'set content.javascript.enabled false'}
+
# I need to plumb
-config.bind('d', 'hint all spawn plumb {hint-url}')
-config.bind('D', 'hint all spawn rc -c \'net/download -d {hint-url}\'')
-config.bind('v', 'hint all spawn rc -c \'nohup mpv --pause {hint-url} >/dev/null >[2]/dev/null &\'')
+config.bind('d', 'hint all download')
+c.downloads.location.directory = "~/downloads/"
# Warp the mouse as seen by qtwebengine
config.bind('a', 'hint all hover')
# Alphabet soup
config.bind('A', 'spawn --userscript randomagent')
-c.content.headers.user_agent = '.'
+c.content.headers.user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36'
# Stylesheets
c.content.user_stylesheets = stylesheets.list
@@ -102,15 +150,11 @@ def domainredir(info: interceptor.Request):
# them? Why can't you give me a switch statement? I would love for
# string-capable switch statements to exist in more languages.
if (host == 'twitter.com' or host == 'www.twitter.com' or host == 'mobile.twitter.com'):
- target.setHost('nitter.net');
- elif (host == 'reddit.com' or host == 'www.reddit.com' or host == 'old.reddit.com'):
- target.setHost('teddit.net');
- elif (host == 'instagram.com' or host == 'www.instagram.com'):
- target.setHost('bibliogram.art');
+ target.setHost('nitter.io.lol');
+ elif (host == 'reddit.com' or host == 'www.reddit.com' or host == 'teddit.net'):
+ target.setHost('old.reddit.com');
elif (host == 'en.m.wikipedia.org'):
target.setHost('en.wikipedia.org');
- elif (host == 'npr.org' or host == 'www.npr.org'):
- target.setHost('text.npr.org');
else:
return
@@ -137,3 +181,16 @@ config.bind('ym', 'yank inline [{title}]({url}) -s')
config.bind('yp', 'yank pretty-url -s')
config.bind('yt', 'yank title -s')
config.bind('yy', 'yank -s')
+config.bind('yh', 'hint all yank-primary')
+
+# Keeps getting in the way
+config.unbind('q')
+
+# Bookmarks
+config.bind('m', 'bookmark-add')
+config.bind('b', 'set-cmd-text -s :bookmark-load')
+config.bind('B', 'set-cmd-text -s :bookmark-load -t')
+config.bind('M', 'set-cmd-text -s :bookmark-del')
+
+# Notifications
+c.content.notifications.presenter = 'herbe'
diff --git a/.config/redshift/redshift.conf b/.config/redshift/redshift.conf
@@ -2,9 +2,12 @@
[redshift]
temp-day=4700
-temp-night=3500
+temp-night=1700
-; Pozzy london time - I don't wanna be v&
+dawn-time=6:30-8:00
+dusk-time=20:00-21:30
+
+; London. Yeah whatever.
location-provider=manual
[manual]
lat=51.509865
diff --git a/.config/sxhkd/sxhkdrc b/.config/sxhkd/sxhkdrc
@@ -13,19 +13,13 @@ super + shift + t
pkill -SIGUSR2 herbe
super + ctrl + t
- pkill -SIGKILL herbe; herbe
+ pkill -SIGKILL herbe; herbe; herbe Fixed!
super + alt + t
herbe 'test'
-super + a
- addrbook
-
-super + shift + a
- mailselect
-
super + b
- pgrep qutebrowser && herbe 'qutebrowser already running' || qutebrowser
+ qutebrowser
super + n
st -t neomutt -c neomutt -e sh -c 'neomutt; $SHELL'
@@ -34,7 +28,7 @@ super + c
slock_mod
super + shift + c
- slock
+ slock_mod & doas zzz
super + d
dmenu_run
@@ -43,7 +37,7 @@ super + u
plumb `''{xclip -o}
super + s
- dpass
+ st -c dpass -g 100x45 -b -e dpass
super + shift + s
maim -s | hlpaste 'png'
@@ -52,45 +46,23 @@ super + ctrl + s
maim | hlpaste 'png'
@super + w
- wallp $home/general/images/wallpapers/
-
-super + ctrl + w
- wallblur
+ wallp $home/wallpapers/
-super + shift + g
- chradio
-
-super + shift + o
- addtorrent xclip
-
-# Socket mpv control
-# super + alt + [y,u]
-# rmpv general/[videos,music]/list
-#
-# super + ctrl + [i,p]
-# hmpv [back,forward] /tmp/mpv-socket
-#
-# super + alt + [i,p]
-# hmpv [prev,next] /tmp/mpv-socket
-#
-# super + [ctrl,alt] + o
-# hmpv [toggle,quit] /tmp/mpv-socket
-#
-# super + shift + n
-# st -c mpvrcp -g 150x30 -b -e mpvrcp /tmp/mpv-socket
+super + p
+ if (pgrep -x scrcpy >/dev/null) { xid = `{xdotool search --class scrcpy}; xid = $xid(1); bspc node $xid -g hidden; if (~ `{bspc query -n $xid -T | jq .hidden} false) { bspc node $xid -f } } else { scrcpy --no-audio }
# cmus control
super + ctrl + [i,p]
- cmus-remote --seek [-5,+5]
+ cmus-remote --server 127.0.0.1 --passwd helloworld --seek [-5,+5]
super + alt + [i,p]
- cmus-remote [--prev,--next]
+ cmus-remote --server 127.0.0.1 --passwd helloworld [--prev,--next]
super + [ctrl,alt] + o
- cmus-remote [--pause,--stop]
+ cmus-remote --server 127.0.0.1 --passwd helloworld [--pause,--stop]
-super + m
- st -c cmus -g 150x30 -b -e rc -c 'tmux new-session -As cmus cmus'
+super + shift + m
+ st -c cmus -g 150x30 -b -e rc -c 'tmux new-session -As cmus cmus --listen 0.0.0.0'
super + [minus,equal]
hvol 3%[-,+]
@@ -104,12 +76,6 @@ super + shift [minus,equal]
shift + [XF86AudioLowerVolume, XF86AudioRaiseVolume]
hvol 6%[-,+]
-super + shift + h
- st -e htop
-
-Menu
- xdotool click 2
-
#thinkpad codes
# XF86Sleep
diff --git a/.config/transmission/settings.json b/.config/transmission/settings.json
@@ -1,99 +0,0 @@
-{
- "alt-speed-down": 50,
- "alt-speed-enabled": true,
- "alt-speed-time-begin": 540,
- "alt-speed-time-day": 127,
- "alt-speed-time-enabled": false,
- "alt-speed-time-end": 1020,
- "alt-speed-up": 0,
- "bind-address-ipv4": "0.0.0.0",
- "bind-address-ipv6": "::",
- "blocklist-enabled": true,
- "blocklist-updates-enabled": true,
- "blocklist-url": "https://github.com/sahsu/transmission-blocklist/releases/latest/download/blocklist.gz",
- "cache-size-mb": 4,
- "compact-view": false,
- "details-window-height": 542,
- "details-window-width": 700,
- "dht-enabled": false,
- "download-dir": "/home/hayden/general/music",
- "download-queue-enabled": true,
- "download-queue-size": 5,
- "encryption": 1,
- "idle-seeding-limit": 30,
- "idle-seeding-limit-enabled": false,
- "incomplete-dir": "/home/hayden/.incomplete",
- "incomplete-dir-enabled": true,
- "inhibit-desktop-hibernation": false,
- "lpd-enabled": true,
- "main-window-height": 1031,
- "main-window-is-maximized": 0,
- "main-window-width": 850,
- "main-window-x": 1930,
- "main-window-y": 35,
- "message-level": 2,
- "open-dialog-dir": "/home/hayden",
- "peer-congestion-algorithm": "",
- "peer-id-ttl-hours": 6,
- "peer-limit-global": 200,
- "peer-limit-per-torrent": 50,
- "peer-port": 51413,
- "peer-port-random-high": 65535,
- "peer-port-random-low": 49152,
- "peer-port-random-on-start": false,
- "peer-socket-tos": "default",
- "pex-enabled": true,
- "port-forwarding-enabled": false,
- "preallocation": 1,
- "prefetch-enabled": true,
- "queue-stalled-enabled": true,
- "queue-stalled-minutes": 30,
- "ratio-limit": 30,
- "ratio-limit-enabled": true,
- "recent-download-dir-1": "/home/hayden/general/music",
- "rename-partial-files": true,
- "rpc-authentication-required": false,
- "rpc-bind-address": "0.0.0.0",
- "rpc-enabled": true,
- "rpc-host-whitelist": "",
- "rpc-host-whitelist-enabled": true,
- "rpc-password": "{72dc83c52677798ae1153353e83400d2301771ff2ownXslf",
- "rpc-port": 9091,
- "rpc-url": "/transmission/",
- "rpc-username": "",
- "rpc-whitelist": "127.0.0.1,::1",
- "rpc-whitelist-enabled": true,
- "scrape-paused-torrents-enabled": true,
- "script-torrent-done-enabled": false,
- "script-torrent-done-filename": "/home/hayden",
- "seed-queue-enabled": false,
- "seed-queue-size": 10,
- "show-backup-trackers": false,
- "show-extra-peer-details": false,
- "show-filterbar": false,
- "show-notification-area-icon": false,
- "show-options-window": true,
- "show-statusbar": true,
- "show-toolbar": false,
- "show-tracker-scrapes": true,
- "sort-mode": "sort-by-time-left",
- "sort-reversed": true,
- "speed-limit-down": 750,
- "speed-limit-down-enabled": true,
- "speed-limit-up": 1000000,
- "speed-limit-up-enabled": true,
- "start-added-torrents": true,
- "statusbar-stats": "total-transfer",
- "torrent-added-notification-enabled": true,
- "torrent-complete-notification-enabled": true,
- "torrent-complete-sound-command": "torrentdone",
- "torrent-complete-sound-enabled": true,
- "trash-can-enabled": true,
- "trash-original-torrent-files": false,
- "umask": 18,
- "upload-slots-per-torrent": 14,
- "user-has-given-informed-consent": true,
- "utp-enabled": true,
- "watch-dir": "/home/hayden/Desktop",
- "watch-dir-enabled": false
-}
diff --git a/.rcrc b/.rcrc
@@ -1,31 +1,3 @@
-# Fuck GNU
-if (~ `$nl{grep --version >[2=1]} *GNU*)
- __prompt_gnu_binary = 'a'
-
-fn __prompt_tput {
- # Use \001 and \002 to tell readline
- # wether a character is visible or not
- printf '\001%s\002' `''{tput $*}
-}
-
-fn __prompt_exit_colour {
- if (~ $1 0) {
- __prompt_tput setaf 15
- } else if (~ $1 sig*) {
- __prompt_tput setaf 8
- } else {
- __prompt_tput setaf 10
- }
-}
-
-fn __prompt_cleanup {
- for (f in $__prompt_tmp) {
- rm -f $f
- }
- __prompt_tmp = ()
- tput sgr0
-}
-
fn sigexit {
__prompt_cleanup
for (f in $manconfs) {
@@ -33,117 +5,8 @@ fn sigexit {
}
}
-# Backend prompt function
-fn __prompt {
- __prompt_exitcodes = $status
- __prompt_gitoutput = 0
- __prompt_tmp = (/tmp/$pid.prompt.tmp $__prompt_tmp)
-
- # exit codes
- # rc produces a list if there is a pipeline
- if (~ $__prompt_exitcodes(2) ()) {
- # only one
- __prompt_exit_colour $__prompt_exitcodes(1)
- printf '%s ' $__prompt_exitcodes(1) | sed 's/^sig//'
- } else {
- __prompt_tput setaf 3
- printf '['
- __prompt_print_bar = 0
- for (__prompt_exitcode in $__prompt_exitcodes) {
- if (~ $__prompt_print_bar 1) {
- __prompt_tput setaf 3
- printf '|'
- } else {
- __prompt_print_bar = 1
- }
- __prompt_exit_colour $__prompt_exitcode
- printf '%s' $__prompt_exitcode | sed 's/^sig//'
- }
- __prompt_tput setaf 3
- printf '] '
- }
-
- # username
- __prompt_tput setaf 5
- printf '%s ' `{whoami}
-
- if (!~ $hide_git 1) {
- # branch
- __prompt_tput setaf 3
- __prompt_branch = `{git branch >[2]/dev/null |
- grep '^*' |
- awk '{print $2 "|"}'}
- if (!~ $__prompt_branch ()) {
- printf '%s' $__prompt_branch
- __prompt_gitoutput = 1
- }
-
- # unstaged changes
- git diff >[2]/dev/null > $__prompt_tmp(1)
- __prompt_diffstatus = $bqstatus
- if (head -n 1 < $__prompt_tmp(1) | grep '^.' >/dev/null) {
- __prompt_tput setaf 8
- printf '%s+' `{cat $__prompt_tmp(1) |
- grep '-E'^$__prompt_gnu_binary '^(\+\+\+|---)' |
- sed -E 's~^[^a]*a/|^[^b]*b/~~' |
- grep -v /dev/null |
- uniq |
- wc -l |
- tr -d '\n'}
- __prompt_gitoutput = 1
- } else if (~ $bqstatus 0) {
- __prompt_tput setaf 15
- printf '='
- __prompt_gitoutput = 1
- }
-
- # unpushed commits
- git log '--pretty=format:commit %h%d' >[2]/dev/null > $__prompt_tmp(1)
- if (!~ `$nl{head -n 1 < $__prompt_tmp(1)} */*) {
- __prompt_tput setaf 14
- < $__prompt_tmp(1) awk '
- BEGIN {c = 0; exitcode = 1}
- /^commit/ {
- if ($0 ~ /\//) {
- printf("%d^", c)
- exitcode = 0
- exit(0)
- } else c += 1
- }
- END { exit(exitcode) }' && __prompt_gitoutput = 1
- }
- }
-
- if (~ $__prompt_gitoutput 1) {
- printf ' '
- }
-
- # dir
- __prompt_tput setaf 7
- printf '%s' `{pwd | sed 's~' ^ $home ^ '~\~~'}
-
- # end
- printf '$ '
- __prompt_cleanup
-}
-
-# Frontend prompt function
fn prompt {
- __prompt_content = `''{__prompt}
- __prompt_indent = `$nl{printf '%s' $__prompt_content |
- awk '{gsub(/\001[^\002]*\002/, ""); print}' |
- sed 's/[^ ]/ /g;s/$/ /g'}
- prompt = ($__prompt_content $__prompt_indent)
-
- # missing newline
- # https://www.vidarholen.net/contents/blog/?p=878
- __prompt_tput setaf 14
- printf '%%'
- tr '\0' ' ' < /dev/zero | head -c `{tput cols} | sed 's/.//'
- printf '\r'
-
- # set X11 title
- printf '\033]0;rc (%s)\007' `{pwd | sed 's~' ^ $home ^ '~\~~'}
+ prompt = (`''{builtin prompt $status} '>')
}
# Aliases
@@ -165,15 +28,15 @@ fn v { nvim $* }
fn xi { doas xbps-install $*}
fn xr { doas xbps-remove $*}
fn xq { doas xbps-query $*}
-fn ytdl { youtube-dl --add-metadata -ic $* }
-fn ytdla { youtube-dl --add-metadata -xic $* }
+fn ytdl { yt-dlp --add-metadata -ic $* }
+fn ytdla { yt-dlp --add-metadata -xic $* }
fn tmux { builtin tmux -f $home/.config/tmux/config $* }
fn sloccount {
mkdir -p $home/.local/sloccount
builtin sloccount --datadir $home/.local/sloccount $*
}
fn camerasync {
- rsync -azP /mnt/DCIM/* $home/general/images/byme
+ rsync -azP /mnt/DCIM/* $home/photos
}
fn wtf {
if (flag i) {
@@ -186,20 +49,6 @@ fn w3m {
# Symlink $home/.config/w3m -> $home/.config/.w3m
home = $home/.config { builtin w3m $* }
}
-fn _man {
- conf = '/tmp/'^$pid^'.man.conf' program = $1 {
- sed -E 's~HOME~'^$home^'~g' < $home/.config/man.conf > $conf
- manconfs = ($manconfs $conf)
- shift 1
- builtin $program -C $conf $*
- for (f in $manconfs) {
- rm -f $f >[2]/dev/null
- }
- }
-}
-fn man { _man man $* }
-fn apropos { _man apropos $* }
-fn makewhatis { _man makewhatis $* }
fn gpg { builtin gpg --armor $* }
fn convert {
if (flag i) {
@@ -222,10 +71,35 @@ fn dict {
}
}
+# Evaluate command with rc
+fn doas {
+ opts = () exec = () args = () {
+ while (~ $1 -*) {
+ if (~ $1 *[Cu]) {
+ opts = ($opts $1 $2)
+ shift 2
+ } else {
+ opts = ($opts $1)
+ shift
+ }
+ }
+
+ args=`''{whatis '*' | sed -E 's~^\*=\(?~~;s~\)$~~' | head -c -1}
+
+ builtin doas $opts rc -c $args
+ }
+}
+
+fn feeds {
+ SFEED_URL_FILE=$home/.local/sfeed-read
+ sfeed_curses $home/.local/sfeed/*
+}
+
# Enable coredumps of practically infinite size.
limit coredumpsize 99999999
# .profile
+CC = clang
INPUTRC = $home/.config/readline/inputrc
EDITOR = nvim
PAGER = less
@@ -244,6 +118,8 @@ IPFS_PATH = $home/.local/ipfs
CURLHOME = $home/.config/curl/"
LS_COLORS = `''{<$home/.config/ls tr -d ' ' | tr '\n' ':' | tr -s ':'}
+ANDROID_HOME=$home/.android-sdk
+
history = $XDG_CACHE_HOME/history
# Originally I had $HOME/.scripts placed before the regular $PATH to override
# things, however this can mess with traditional unix shells that don't
@@ -251,4 +127,15 @@ history = $XDG_CACHE_HOME/history
# still run the regular /bin/rm, but sh will go 'ah, let's try execute this
# directory and see what happens' - of course nothing happens and it gives up
# on trying to locate rm.
-path = ($path $HOME/.scripts/ /usr/lib/plan9/bin)
+if (!~ $rcrcdone true) {
+ path = ($path $home/cmd/ $home/mail/.cmd $home/go/bin /usr/lib/plan9/bin)
+}
+
+. /etc/locale.conf
+
+if (~ `{tty} /dev/tty1) {
+ doas net/autoconf
+ startx
+}
+
+rcrcdone = true
diff --git a/.ssh/config b/.ssh/config
@@ -1,58 +0,0 @@
-# Host *
-# ControlMaster auto
-# ControlPath ~/.ssh/session.%h%p%r.sock
-# ControlPersist yes
-# VisualHostKey yes
-# CheckHostIP yes
-# Compression yes
-
-Host neutron neutron.hhvn.uk phony phony.haydenvh.com phony.hhvn.uk dumbterm dumbterm.haydenvh.com dumbterm.hhvn.uk port port.haydenvh.com port.hhvn.uk
- # these are (most of the time) on my LAN - haydenvh.com
- # is too, but port forwarded, so proxy to that
- #ProxyJump void@haydenvh.com
- ForwardX11 yes
-
-Host neutron neutron.haydenvh.com neutron.hhvn.uk
- Hostname 192.168.1.57
- User hayden
-
-Host port port.haydenvh.com port.hhvn.uk
- Hostname 192.168.1.97
- User hayden
-
-Host dumbterm dumbterm.haydenvh.com dumbterm.hhvn.uk
- Hostname 192.168.1.13
- User hayden
-
-Host phony phony.haydenvh.com phony.hhvn.uk
- Hostname 192.168.1.8
- User "user"
-
-Host dataswamp ds dataswamp.org
- Hostname dataswamp.org
- User hhvn
-
-Host local l haydenvh.com hhvn.uk 192.168.1.20
- Hostname hhvn.uk
- User void
-
-Host irc1 irc.haydenvh.com irc.hhvn.uk
- Hostname irc.hhvn.uk
- User hayden
-
-Host matrix matrix.haydenvh.com matrix.hhvn.uk
- Hostname matrix.hhvn.uk
- User hayden
-
-Host irc2 irc.area51.haydenvh.com irc.area51.hhvn.uk
- Hostname irc.area51.hhvn.uk
- User hayden
- Port 15552
-
-Host mx1 mx1.haydenvh.com
- Hostname mx1.hhvn.uk
- User hayden
-
-Host tetris
- Hostname netris.rocketnine.space
- User hhvn
diff --git a/.xinitrc b/.xinitrc
@@ -1,7 +1,8 @@
#!/bin/rc
host = `{hostname}
-wm = `{printf 'dwm\nbspwm\n' | dmenu -i}
+# wm = `{printf 'dwm\nbspwm\n' | dmenu -i}
+wm = bspwm
$host^/displays
@@ -11,19 +12,29 @@ $host^/keyboard &
xmodmap -e 'keysym Control_R = Multi_key' &
st &
+# while (inotifywait -e close /sys/class/drm/card0*) {
+# $host^/displays
+# bspwm/configure-monitors
+# } &
+
switch ($wm) {
case dwm
dwmbar/dwmbar &
sxhkd &
- while (dwm > .dwm-log) {
+ while (dwm > /tmp/dwm-log) {
true
}
pkill dwmbar
case bspwm
sxhkd $home/.config/bspwm/keys &
- bspwm > .bspwm-log
+ # give hbspbar a moment because it needs to connect to bspwm
+ @{
+ sleep 1
+ hbspbar &
+ }
+ bspwm > /tmp/bspwm-log
case *
if (command -v $wm >/dev/null) {
$wm
@@ -32,4 +43,3 @@ case *
}
}
-
diff --git a/LICENSE b/LICENSE
@@ -1,7 +1,7 @@
Any file not explicitly containing a license included in this repository
implicitly falls under the following copyright and licensing terms.
-Copyright (C) 2020-2022 hhvn <dev@hhvn.uk>
+Copyright (C) 2020-2024 hhvn <dev@hhvn.uk>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
diff --git a/cmd/colotable b/cmd/colotable
@@ -1,7 +1,7 @@
#!/bin/sh
o=0
-for i in $(seq 0 256)
+for i in $(seq 0 ${1:-256})
do
[ $o -gt 5 ] && printf "\n" && o=0
o=$(($o+1))
diff --git a/cmd/dpass b/cmd/dpass
diff --git a/cmd/pass b/cmd/pass
@@ -0,0 +1,128 @@
+#!/bin/rc
+# Copyright (c) 2022 hhvn <dev@hhvn.uk>
+
+if (~ $#PASS 0) {
+ PASS = $home/.local/pass
+}
+
+if (~ $DISPLAY ()) {
+ x11 = false
+} else {
+ x11 = true
+}
+
+fn send {
+ if (~ $x11 true) {
+ xclip
+ } else {
+ cat
+ printf '\n'
+ }
+}
+
+fn getpass {
+ if (! ~ $1 ()) {
+ if (~ $x11 true) {
+ p = (-p $1)
+ shift
+ } else {
+ p = $1
+ shift
+ }
+ } else {
+ p = ()
+ }
+
+ if (~ $x11 true) {
+ dmenu $p -P < /dev/null
+ } else {
+ printf '%s: ' $p >/dev/stderr
+
+ stty -echo
+ head -n 1 < /dev/tty
+ stty echo
+ }
+}
+
+fn choose {
+ if (! ~ $1 '-'*) {
+ if (~ $x11 true) {
+ p = (-p $1)
+ shift
+ } else {
+ p = (--prompt=$1)
+ shift
+ }
+ } else {
+ p = ()
+ }
+
+ if (~ $x11 true) {
+ dmenu -i $p $*
+ } else {
+ fzf $p
+ }
+}
+
+dec = `''{getpass 'Master password' | gpg -qd --passphrase-fd 0 $PASS}
+
+if (!~ $#* 0) {
+ opt = $1
+ fromarg = 1
+} else {
+ opt = `$nl{printf '%s' $dec | awk -F: 'BEGIN {printf("GENERATE\nAPPEND\nEDIT\n")}; {print $1}' | choose -l 20}
+}
+
+switch ($opt) {
+case GENERATE
+ newpass = `''{tr -dc 'a-zA-Z0-9~!@$%^&*_+=-' < /dev/urandom | head -c 35}
+ newname = `$nl{choose 'Name' </dev/null}
+ if (~ $newname '') {
+ herbe 'dpass exiting... no pass given'
+ exit 1
+ }
+ dec = `''{printf '%s%s: %s\n' $dec $newname $newpass}
+ encfile = /tmp/$pid.plain
+ touch $encfile
+ chmod 0700 $encfile
+ printf '%s' $dec > $encfile
+ gpg --armor -e --default-recipient-self < $encfile > $PASS
+ shred -u $encfile
+ printf '%s' $newpass | send
+ @{ sleep 25; send </dev/null } &
+case APPEND
+ newname = `$nl{choose 'Name' </dev/null}
+ newpass = `$nl{getpass 'Password'}
+ if (~ $newname '') {
+ herbe 'dpass exiting... no pass given'
+ exit 1
+ }
+ dec = `''{printf '%s%s: %s\n' $dec $newname $newpass}
+ encfile = /tmp/$pid.plain
+ touch $encfile
+ chmod 0700 $encfile
+ printf '%s' $dec > $encfile
+ gpg --armor -e --default-recipient-self < $encfile > $PASS
+ shred -u $encfile
+ printf '%s' $newpass | send
+ @{ sleep 25; send </dev/null } &
+case EDIT
+ encfile = /tmp/$pid.plain
+ touch $encfile
+ chmod 0700 $encfile
+ printf '%s' $dec > $encfile
+ if (~ $EDITOR ()) { EDITOR=vim }
+ $EDITOR $encfile
+ gpg --armor -e --default-recipient-self < $encfile > $PASS
+ shred -u $encfile
+ printf '%s' $newpass | send
+ @{ sleep 25; send </dev/null } &
+case *
+ out = `$nl{printf '%s' $dec | awk -F: -v 'opt=' ^ $opt '$1 == opt { print $2 }' | sed 's/^.//'}
+ if (~ $fromarg 1) {
+ echo $out
+ } else {
+ printf '%s' $out | send
+ @{ sleep 25; send </dev/null } &
+ }
+}