diff --git a/ckb-next/.config/ckb-next/ckb-next.conf b/ckb-next/.config/ckb-next/ckb-next.conf index ad717d84a..7449ca4a8 100644 --- a/ckb-next/.config/ckb-next/ckb-next.conf +++ b/ckb-next/.config/ckb-next/ckb-next.conf @@ -197,7 +197,7 @@ 0E029022AF4C18835CBDCC7EF5001BC3\%7B5B1E2E81-ED4F-4F79-9EB5-F8ACA67D1BF0%7D\0\Lighting\Keys\y=#ffffff 0E029022AF4C18835CBDCC7EF5001BC3\%7B5B1E2E81-ED4F-4F79-9EB5-F8ACA67D1BF0%7D\0\Lighting\Keys\z=#ffffff 0E029022AF4C18835CBDCC7EF5001BC3\%7B5B1E2E81-ED4F-4F79-9EB5-F8ACA67D1BF0%7D\0\Lighting\UseRealNames=true -0E029022AF4C18835CBDCC7EF5001BC3\%7B5B1E2E81-ED4F-4F79-9EB5-F8ACA67D1BF0%7D\0\Modified=d2431ffd +0E029022AF4C18835CBDCC7EF5001BC3\%7B5B1E2E81-ED4F-4F79-9EB5-F8ACA67D1BF0%7D\0\Modified=394adceb 0E029022AF4C18835CBDCC7EF5001BC3\%7B5B1E2E81-ED4F-4F79-9EB5-F8ACA67D1BF0%7D\0\Name=Rainbow 0E029022AF4C18835CBDCC7EF5001BC3\%7B5B1E2E81-ED4F-4F79-9EB5-F8ACA67D1BF0%7D\0\Performance\AngleSnap=false 0E029022AF4C18835CBDCC7EF5001BC3\%7B5B1E2E81-ED4F-4F79-9EB5-F8ACA67D1BF0%7D\0\Performance\DPI\0=@Point(400 400) @@ -425,7 +425,7 @@ 0E029022AF4C18835CBDCC7EF5001BC3\%7B5B1E2E81-ED4F-4F79-9EB5-F8ACA67D1BF0%7D\1\Lighting\Keys\y=#ff0000 0E029022AF4C18835CBDCC7EF5001BC3\%7B5B1E2E81-ED4F-4F79-9EB5-F8ACA67D1BF0%7D\1\Lighting\Keys\z=#ff0000 0E029022AF4C18835CBDCC7EF5001BC3\%7B5B1E2E81-ED4F-4F79-9EB5-F8ACA67D1BF0%7D\1\Lighting\UseRealNames=true -0E029022AF4C18835CBDCC7EF5001BC3\%7B5B1E2E81-ED4F-4F79-9EB5-F8ACA67D1BF0%7D\1\Modified=f508be9d +0E029022AF4C18835CBDCC7EF5001BC3\%7B5B1E2E81-ED4F-4F79-9EB5-F8ACA67D1BF0%7D\1\Modified=9389d46a 0E029022AF4C18835CBDCC7EF5001BC3\%7B5B1E2E81-ED4F-4F79-9EB5-F8ACA67D1BF0%7D\1\Name=Breathing 0E029022AF4C18835CBDCC7EF5001BC3\%7B5B1E2E81-ED4F-4F79-9EB5-F8ACA67D1BF0%7D\1\Performance\AngleSnap=false 0E029022AF4C18835CBDCC7EF5001BC3\%7B5B1E2E81-ED4F-4F79-9EB5-F8ACA67D1BF0%7D\1\Performance\DPI\0=@Point(400 400) @@ -675,7 +675,7 @@ 0E029022AF4C18835CBDCC7EF5001BC3\%7B5B1E2E81-ED4F-4F79-9EB5-F8ACA67D1BF0%7D\2\Lighting\Keys\y=#aa00ff 0E029022AF4C18835CBDCC7EF5001BC3\%7B5B1E2E81-ED4F-4F79-9EB5-F8ACA67D1BF0%7D\2\Lighting\Keys\z=#aa00ff 0E029022AF4C18835CBDCC7EF5001BC3\%7B5B1E2E81-ED4F-4F79-9EB5-F8ACA67D1BF0%7D\2\Lighting\UseRealNames=true -0E029022AF4C18835CBDCC7EF5001BC3\%7B5B1E2E81-ED4F-4F79-9EB5-F8ACA67D1BF0%7D\2\Modified=9cd81fff +0E029022AF4C18835CBDCC7EF5001BC3\%7B5B1E2E81-ED4F-4F79-9EB5-F8ACA67D1BF0%7D\2\Modified=7a6eaf79 0E029022AF4C18835CBDCC7EF5001BC3\%7B5B1E2E81-ED4F-4F79-9EB5-F8ACA67D1BF0%7D\2\Name=Trippy 0E029022AF4C18835CBDCC7EF5001BC3\%7B5B1E2E81-ED4F-4F79-9EB5-F8ACA67D1BF0%7D\2\Performance\AngleSnap=false 0E029022AF4C18835CBDCC7EF5001BC3\%7B5B1E2E81-ED4F-4F79-9EB5-F8ACA67D1BF0%7D\2\Performance\DPI\0=@Point(400 400) @@ -733,7 +733,7 @@ 0E029022AF4C18835CBDCC7EF5001BC3\%7B5B1E2E81-ED4F-4F79-9EB5-F8ACA67D1BF0%7D\CurrentMode={51EB6E3A-27A0-4AD6-A35C-6B67E0329A3D} 0E029022AF4C18835CBDCC7EF5001BC3\%7B5B1E2E81-ED4F-4F79-9EB5-F8ACA67D1BF0%7D\HwModified=7ffd 0E029022AF4C18835CBDCC7EF5001BC3\%7B5B1E2E81-ED4F-4F79-9EB5-F8ACA67D1BF0%7D\ModeCount=3 -0E029022AF4C18835CBDCC7EF5001BC3\%7B5B1E2E81-ED4F-4F79-9EB5-F8ACA67D1BF0%7D\Modified=abda8bf7 +0E029022AF4C18835CBDCC7EF5001BC3\%7B5B1E2E81-ED4F-4F79-9EB5-F8ACA67D1BF0%7D\Modified=9d268d65 0E029022AF4C18835CBDCC7EF5001BC3\%7B5B1E2E81-ED4F-4F79-9EB5-F8ACA67D1BF0%7D\Name=Multi 0E029022AF4C18835CBDCC7EF5001BC3\%7BBA7FC152-2D51-4C26-A7A6-A036CC93D924%7D\0\Binding\KeyMap=K68 GB 0E029022AF4C18835CBDCC7EF5001BC3\%7BBA7FC152-2D51-4C26-A7A6-A036CC93D924%7D\0\Binding\UseRealNames=true @@ -929,7 +929,7 @@ 0E029022AF4C18835CBDCC7EF5001BC3\%7BBA7FC152-2D51-4C26-A7A6-A036CC93D924%7D\0\Lighting\Keys\y=#ffffff 0E029022AF4C18835CBDCC7EF5001BC3\%7BBA7FC152-2D51-4C26-A7A6-A036CC93D924%7D\0\Lighting\Keys\z=#ffffff 0E029022AF4C18835CBDCC7EF5001BC3\%7BBA7FC152-2D51-4C26-A7A6-A036CC93D924%7D\0\Lighting\UseRealNames=true -0E029022AF4C18835CBDCC7EF5001BC3\%7BBA7FC152-2D51-4C26-A7A6-A036CC93D924%7D\0\Modified=4935ed91 +0E029022AF4C18835CBDCC7EF5001BC3\%7BBA7FC152-2D51-4C26-A7A6-A036CC93D924%7D\0\Modified=5efaf172 0E029022AF4C18835CBDCC7EF5001BC3\%7BBA7FC152-2D51-4C26-A7A6-A036CC93D924%7D\0\Name=Rainbow 0E029022AF4C18835CBDCC7EF5001BC3\%7BBA7FC152-2D51-4C26-A7A6-A036CC93D924%7D\0\Performance\AngleSnap=false 0E029022AF4C18835CBDCC7EF5001BC3\%7BBA7FC152-2D51-4C26-A7A6-A036CC93D924%7D\0\Performance\DPI\0=@Point(400 400) @@ -1157,7 +1157,7 @@ 0E029022AF4C18835CBDCC7EF5001BC3\%7BBA7FC152-2D51-4C26-A7A6-A036CC93D924%7D\1\Lighting\Keys\y=#ff0000 0E029022AF4C18835CBDCC7EF5001BC3\%7BBA7FC152-2D51-4C26-A7A6-A036CC93D924%7D\1\Lighting\Keys\z=#ff0000 0E029022AF4C18835CBDCC7EF5001BC3\%7BBA7FC152-2D51-4C26-A7A6-A036CC93D924%7D\1\Lighting\UseRealNames=true -0E029022AF4C18835CBDCC7EF5001BC3\%7BBA7FC152-2D51-4C26-A7A6-A036CC93D924%7D\1\Modified=b00d576a +0E029022AF4C18835CBDCC7EF5001BC3\%7BBA7FC152-2D51-4C26-A7A6-A036CC93D924%7D\1\Modified=79e6ce6f 0E029022AF4C18835CBDCC7EF5001BC3\%7BBA7FC152-2D51-4C26-A7A6-A036CC93D924%7D\1\Name=Breathing 0E029022AF4C18835CBDCC7EF5001BC3\%7BBA7FC152-2D51-4C26-A7A6-A036CC93D924%7D\1\Performance\AngleSnap=false 0E029022AF4C18835CBDCC7EF5001BC3\%7BBA7FC152-2D51-4C26-A7A6-A036CC93D924%7D\1\Performance\DPI\0=@Point(400 400) @@ -1407,7 +1407,7 @@ 0E029022AF4C18835CBDCC7EF5001BC3\%7BBA7FC152-2D51-4C26-A7A6-A036CC93D924%7D\2\Lighting\Keys\y=#aa00ff 0E029022AF4C18835CBDCC7EF5001BC3\%7BBA7FC152-2D51-4C26-A7A6-A036CC93D924%7D\2\Lighting\Keys\z=#aa00ff 0E029022AF4C18835CBDCC7EF5001BC3\%7BBA7FC152-2D51-4C26-A7A6-A036CC93D924%7D\2\Lighting\UseRealNames=true -0E029022AF4C18835CBDCC7EF5001BC3\%7BBA7FC152-2D51-4C26-A7A6-A036CC93D924%7D\2\Modified=a066f62b +0E029022AF4C18835CBDCC7EF5001BC3\%7BBA7FC152-2D51-4C26-A7A6-A036CC93D924%7D\2\Modified=c11d9b15 0E029022AF4C18835CBDCC7EF5001BC3\%7BBA7FC152-2D51-4C26-A7A6-A036CC93D924%7D\2\Name=Trippy 0E029022AF4C18835CBDCC7EF5001BC3\%7BBA7FC152-2D51-4C26-A7A6-A036CC93D924%7D\2\Performance\AngleSnap=false 0E029022AF4C18835CBDCC7EF5001BC3\%7BBA7FC152-2D51-4C26-A7A6-A036CC93D924%7D\2\Performance\DPI\0=@Point(400 400) @@ -1465,7 +1465,7 @@ 0E029022AF4C18835CBDCC7EF5001BC3\%7BBA7FC152-2D51-4C26-A7A6-A036CC93D924%7D\CurrentMode={11C11AE3-3195-4DFC-B8AC-2FEA703414E5} 0E029022AF4C18835CBDCC7EF5001BC3\%7BBA7FC152-2D51-4C26-A7A6-A036CC93D924%7D\HwModified=40b6f054 0E029022AF4C18835CBDCC7EF5001BC3\%7BBA7FC152-2D51-4C26-A7A6-A036CC93D924%7D\ModeCount=3 -0E029022AF4C18835CBDCC7EF5001BC3\%7BBA7FC152-2D51-4C26-A7A6-A036CC93D924%7D\Modified=4a91858f +0E029022AF4C18835CBDCC7EF5001BC3\%7BBA7FC152-2D51-4C26-A7A6-A036CC93D924%7D\Modified=648a05f8 0E029022AF4C18835CBDCC7EF5001BC3\%7BBA7FC152-2D51-4C26-A7A6-A036CC93D924%7D\Name=Demo [Popups] diff --git a/dconf/.config/dconf/user b/dconf/.config/dconf/user index eb090e666..dbe91969e 100644 Binary files a/dconf/.config/dconf/user and b/dconf/.config/dconf/user differ diff --git a/fish/.config/fish/conf.d/02_paths.fish b/fish/.config/fish/conf.d/02_paths.fish index 547e2f8b1..d7ff9b4a5 100644 --- a/fish/.config/fish/conf.d/02_paths.fish +++ b/fish/.config/fish/conf.d/02_paths.fish @@ -18,6 +18,12 @@ if test -d "$HOME/.local/bin/repobar" set PATH "$HOME/.local/bin/repobar:$PATH" end +# Adds `~/.local/bin/rofi` to $PATH +# set PATH so it includes user's private bin if it exists +if test -d "$HOME/.local/bin/rofi" + set PATH "$HOME/.local/bin/rofi:$PATH" +end + # Adds `~/.local/bin/fzf` to $PATH # set PATH so it includes user's private bin if it exists if test -d "$HOME/.local/bin/fzf" diff --git a/i3/.config/i3/config b/i3/.config/i3/config index 0ef582dbd..a7b951c18 100644 --- a/i3/.config/i3/config +++ b/i3/.config/i3/config @@ -126,31 +126,31 @@ $super_b+F2 \ ## Category: Menus; # Description: Start ClipMenu; $super_b+F3 \ - $exe fzf_run -c clipmenu -i ClipMenu -t $TerminalEmulator + $exe clipmenu -p ClipMenu -mesg "Copy from clipboard." ############################### -####### FZFMenu Menu's ####### +####### Rofi Menu's ####### ############################### -## Category: FZFMenu; -# Description: Start FZF-FZFMenu; +## Category: Menus; +# Description: Start Main Menu; $super_b+m \ - $exe fzf_run -c fzf_menu_run -i Menu -t $TerminalEmulator + $exe menu -## Category: FZFMenu; -# Description: Start FZF-MusicMenu; +## Category: Menus; +# Description: Start Music Menu; $super_b+F6 \ - $exe fzf_run -c fzf_music -i MusicMenu -t $TerminalEmulator + $exe music -## Category: FZFMenu; -# Description: Start FZF-PassMenu; +## Category: Menus; +# Description: Start Pass Menu; $super_b+p \ - $exe fzf_run -c 'fzf_pass' -i PassMenu -t $TerminalEmulator + $exe passmenu -## Category: FZFMenu; -# Description: Start FZF-YoutubeMenu; +## Category: Menus; +# Description: Start Youtube Menu; $super_b+s \ - $exe fzf_run -c 'fzf_youtube_subs' -i YoutubeMenu -t $TerminalEmulator + $exe youtube_subs # }}} Menus @@ -163,17 +163,17 @@ $super_b+s \ ## Category: Main Keybinds; # Description: Exit / Quit / Restart; $super_b+$shift+e \ - $exe fzf_run -c fzf_powermenu -i PowerMenu -t $TerminalEmulator + $exe rofi -show powermenu -config ~/.config/rofi/powermenu.rasi ## Category: Main Keybinds; # Description: Exit / Quit / Restart; $super_b+$shift+x \ - $exe fzf_run -c fzf_powermenu -i PowerMenu -t $TerminalEmulator + $exe rofi -show powermenu -config ~/.config/rofi/powermenu.rasi ## Category: Main Keybinds; # Description: Exit / Quit / Restart; $super_b+x \ - $exe fzf_run -c fzf_powermenu -i PowerMenu -t $TerminalEmulator + $exe rofi -show powermenu -config ~/.config/rofi/powermenu.rasi # }}} Power @@ -824,7 +824,7 @@ $exe autostart # {{{ Settings -$exe fzf_youtube_subs -d +$exe youtube_subs -d # setting variables for later use # use xrandr and/or arandr to know the names of your monitors diff --git a/localbin/.local/bin/clipmenu/clipmenu b/localbin/.local/bin/clipmenu/clipmenu index 45d17ee02..131ba1f73 100755 --- a/localbin/.local/bin/clipmenu/clipmenu +++ b/localbin/.local/bin/clipmenu/clipmenu @@ -8,8 +8,8 @@ # Modified On - Sun 31 Oct 00:33:48 BST 2021 #------------------------------------------------------------------------------ -: "${CM_LAUNCHER=fzf}" -: "${CM_HISTLENGTH=8}" +: "${CM_LAUNCHER="rofi"}" +: "${CM_HISTLENGTH=15}" shopt -s nullglob diff --git a/package-list/.config/package-list b/package-list/.config/package-list index 8ef97288f..4749efba4 100644 --- a/package-list/.config/package-list +++ b/package-list/.config/package-list @@ -31,6 +31,7 @@ jetbrains-toolbox lib32-libxft-bgra libart-lgpl libxft-bgra +linux-wifi-hotspot linuxrepos-keyring linuxrepos-mirrorlist minecraft-launcher diff --git a/polybar/.config/polybar/launch.sh b/polybar/.config/polybar/launch.sh index bf2f101f4..f12c2743f 100755 --- a/polybar/.config/polybar/launch.sh +++ b/polybar/.config/polybar/launch.sh @@ -21,6 +21,7 @@ MC=0 PRIMARY=$(xrandr --query | grep " connected" | grep "primary" | cut -d" " -f1) OTHERS=$(xrandr --query | grep " connected" | grep -v "primary" | cut -d" " -f1) +DEFAULT_NETWORK_INTERFACE=$(ip route | grep '^default' | awk '{print $5}' | head -n1) # Launch bar1 and bar2 if type "xrandr" > /dev/null; then @@ -38,8 +39,8 @@ if type "xrandr" > /dev/null; then TOP_BAR_CONFIG=$HOME/.config/polybar/monitor_$MC/config_top.ini BOTTOM_BAR_CONFIG=$HOME/.config/polybar/monitor_$MC/config_bottom.ini - MONITOR=$m polybar --reload -c "$TOP_BAR_CONFIG" $BAR_NAME & - MONITOR=$m polybar --reload -c "$BOTTOM_BAR_CONFIG" $BAR_NAME & + DEFAULT_NETWORK_INTERFACE=$DEFAULT_NETWORK_INTERFACE MONITOR=$m polybar --reload -c "$TOP_BAR_CONFIG" $BAR_NAME & + DEFAULT_NETWORK_INTERFACE=$DEFAULT_NETWORK_INTERFACE MONITOR=$m polybar --reload -c "$BOTTOM_BAR_CONFIG" $BAR_NAME & done else polybar --reload main -c ~/.config/polybar/config_top.ini & diff --git a/polybar/.config/polybar/modules.ini b/polybar/.config/polybar/modules.ini index d047046ca..d9eef5caf 100644 --- a/polybar/.config/polybar/modules.ini +++ b/polybar/.config/polybar/modules.ini @@ -477,7 +477,7 @@ icon-pause =  [module/network] type = internal/network -interface = wlp0s20f3 +interface = ${env:DEFAULT_NETWORK_INTERFACE:enp0s20f0u2} interval = 1.0 accumulate-stats = true @@ -499,7 +499,7 @@ label-disconnected = "Disconnected" [module/network_i] type = internal/network -interface = wlp0s20f3 +interface = ${env:DEFAULT_NETWORK_INTERFACE:enp0s20f0u2} interval = 1.0 accumulate-stats = true diff --git a/polybar/.config/polybar/user_modules.ini b/polybar/.config/polybar/user_modules.ini index 85a2df98b..c08a5a512 100644 --- a/polybar/.config/polybar/user_modules.ini +++ b/polybar/.config/polybar/user_modules.ini @@ -66,7 +66,7 @@ format-background = ${color.cyan} format-foreground = ${color.fg} format-padding = 1 -click-left = fzf_run -c fzf_menu_run -i PolybarMenu -t alacritty +click-left = menu [module/launcher_i] type = custom/text @@ -76,7 +76,7 @@ format-background = ${color.red} format-foreground = ${color.fg} format-padding = 1 -click-left = fzf_run -c fzf_menu_run -i PolybarMenu -t alacritty +click-left = menu ;; _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ @@ -88,7 +88,7 @@ format-background = ${color.mf} format-foreground = ${color.fg} format-padding = 1 -click-left = fzf_run -c fzf_powermenu -i PowerMenu -t alacritty +click-left = rofi -show powermenu -config ~/.config/rofi/powermenu.rasi [module/power_i] type = custom/text @@ -98,7 +98,7 @@ format-background = ${color.red} format-foreground = ${color.fg} format-padding = 1 -click-left = fzf_run -c fzf_powermenu -i PowerMenu -t alacritty +click-left = rofi -show powermenu -config ~/.config/rofi/powermenu.rasi ;; _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ diff --git a/rofi/.config/rofi/bookmarks/bm.bmf b/rofi/.config/rofi/bookmarks/bm.bmf new file mode 100644 index 000000000..e69de29bb diff --git a/rofi/.config/rofi/config.rasi b/rofi/.config/rofi/config.rasi new file mode 100644 index 000000000..ac5e5920c --- /dev/null +++ b/rofi/.config/rofi/config.rasi @@ -0,0 +1,10 @@ +configuration { + modi: "combi,drun,filebrowser,keys,window,run,ssh"; + font: "Hack Nerd Font 12"; + combi-modi: "window,ssh"; +} +@theme "repomenu" +listview { + lines: 20; + columns: 1; +} \ No newline at end of file diff --git a/rofi/.config/rofi/configs/subs.ini b/rofi/.config/rofi/configs/subs.ini new file mode 100644 index 000000000..5fcb89971 --- /dev/null +++ b/rofi/.config/rofi/configs/subs.ini @@ -0,0 +1,39 @@ +# Linux +UCVls1GmFKf6WlTraIb_IaJg # DistroTube +UC2eYFnH61tmytImy1mTYvhA # Luke Smith +UCld68syR8Wi-GY_n4CaoJGA # Brodie Robertson +UCg6gPGh8HU2U01vaFCAsvmQ # Chris Titus Tech +UCCIHOP7e271SIumQgyl6XBQ # OldTechBloke +UCJdmdUp5BrsWsYVQUylCMLg # Erik Dubois +UCdGFLV7h9RGeTUX7wa5rqGw # Manjaro Linux +UCoryWpk4QVYKFCJul9KBdyw # Switched to Linux +UC5UAwBUum7CPN5buc-_N1Fw # The Linux Experiment +UCX_WM2O-X96URC5n66G-hvw # EF Linux +UCZiL6BoryLWxyapUuVYW27g # Average Linux User +UCylGUf9BvQooEFjgdNudoQg # The Linux Cast +UC8ENHE5xdFSwx71u3fDH5Xw # ThePrimeagen +UCRE3NFNtdjR96-H4QG4U1Fg # HexDSL +# UCfp-lNJy4QkIGnaEE6NtDSg # Terminal For Life +UCmw-QGOHbHA5cDAvwwqUTKQ # Zaney + +#Minecraft +UChFur_NwVSbUozOcF_F2kMg # MumboJumbo +UC_GQ4mac4oN3wl1UdbFuTEA # ibxToyCat +UCRlEFn0L2G_DktbyvN0AZ5A # WadZee +UC9mvRrl9o7rG65ABsGVvDBw # Mysticat +UCuMJPFqazQI4SofSFEd-5zA # impulseSV + +#Spirit +UC_8fJz5gAnhRqZ740QXlzmw # Jen McCarty +UCAk3t7WHs2zjsZpopox8Taw # Jonna Jinton + +#Meditations +UCUSzc8BHGJPaYt0DjvscIzw # Woke Nation +UCM7XCXnxtYJkkMN0zf0tsSw # Meditation And Healing + +#Music +UCMvETSFFkMOADyrGBj3gbWA # NerdOut +UCXBcDYUr89ImtOLYaHPiiPg # Marcus Veltri + +# Myself +UCuKJ0LqxI8t2a9ATl1rWBGA # Myself diff --git a/rofi/.config/rofi/passmenu/config b/rofi/.config/rofi/passmenu/config new file mode 100644 index 000000000..699005d6d --- /dev/null +++ b/rofi/.config/rofi/passmenu/config @@ -0,0 +1,124 @@ +# permanently set alternative root dir. Use ":" to separate multiple roots +# which can be switched at runtime with shift+left/right +# root=/path/to/root + +# rofi command. Make sure to have "$@" as last argument +_rofi () { + rofi -i -no-auto-select "$@" +} + +# default command to generate passwords +_pwgen () { + pwgen -y "$@" +} + +# image viewer to display qrcode of selected entry +# qrencode is needed to generate the image and a viewer +# that can read from pipes. Known viewers to work are feh and display +_image_viewer () { + feh - +# display +} + +# It is possible to use wl-copy and wl-paste from wl-clipboard +# Just uncomment the lines with wl-copy and wl-paste +# and comment the xclip lines +# +_clip_in_primary() { + xclip + # wl-copy-p +} + +_clip_in_clipboard() { + xclip -selection clipboard + # wl-copy +} + +_clip_out_primary() { + xclip -o + # wl-paste -p +} + +_clip_out_clipboard() { + xclip --selection clipboard -o + # wl-paste +} + + +# xdotool needs the keyboard layout to be set using setxkbmap +# You can do this in your autostart scripts (e.g. xinitrc) + +# If for some reason, you cannot do this, you can set the command here. +# and set fix_layout to true +fix_layout=false + +layout_cmd () { + setxkbmap us +} + +# fields to be used +URL_field='url' +USERNAME_field='user' +AUTOTYPE_field='autotype' + +# delay to be used for :delay keyword +delay=2 + +# rofi-pass needs to close itself before it can type passwords. Set delay here. +wait=0.2 + +# delay between keypresses when typing (in ms) +xdotool_delay=12 + +## Programs to be used +# Editor +EDITOR='vim -f' + +# Browser +BROWSER='xdg-open' + +## Misc settings + +default_do='menu' # menu, autotype, copyPass, typeUser, typePass, copyUser, copyUrl, viewEntry, typeMenu, actionMenu, copyMenu, openUrl +auto_enter='false' +notify='false' +default_autotype='user :tab pass' + +# color of the help messages +# leave empty for autodetection +help_color="#4872FF" + +# Clipboard settings +# Possible options: primary, clipboard, both +clip=primary + +# Seconds before clearing pass from clipboard +clip_clear=45 + +## Options for generating new password entries + +# open new password entries in editor +edit_new_pass="true" + +# default_user is also used for password files that have no user field. +#default_user="${ROFI_PASS_DEFAULT_USER-$(whoami)}" +#default_user2=mary_ann +#password_length=12 + +# Custom Keybindings +autotype="Alt+1" +type_user="Alt+2" +type_pass="Alt+3" +open_url="Alt+4" +copy_name="Alt+u" +copy_url="Alt+l" +copy_pass="Alt+p" +show="Alt+o" +copy_entry="Alt+2" +type_entry="Alt+1" +copy_menu="Alt+c" +action_menu="Alt+a" +type_menu="Alt+t" +help="Alt+h" +switch="Alt+x" +insert_pass="Alt+n" diff --git a/rofi/.config/rofi/powermenu.rasi b/rofi/.config/rofi/powermenu.rasi new file mode 100644 index 000000000..742875fc6 --- /dev/null +++ b/rofi/.config/rofi/powermenu.rasi @@ -0,0 +1,9 @@ +configuration { + modi: "powermenu:powermenu"; + font: "Hack Nerd Font 12"; +} +@theme "powermenu" +listview { + lines: 6; + columns: 1; +} \ No newline at end of file diff --git a/rofi/.config/rofi/themes/powermenu.rasi b/rofi/.config/rofi/themes/powermenu.rasi new file mode 100644 index 000000000..36ff727b7 --- /dev/null +++ b/rofi/.config/rofi/themes/powermenu.rasi @@ -0,0 +1,183 @@ +* { + background: #283036; + foreground: #e5e9f0; + + active-background: #434c5e; + active-foreground: @foreground; + normal-background: @background; + normal-foreground: @foreground; + urgent-background: #ff5959; + urgent-foreground: @background; + + alternate-active-background: @active-background; + alternate-active-foreground: @active-foreground; + alternate-normal-background: @background; + alternate-normal-foreground: @foreground; + alternate-urgent-background: @background; + alternate-urgent-foreground: @foreground; + + selected-active-background: #81a1c1; + selected-active-foreground: @foreground; + selected-normal-background: #434c5e; + selected-normal-foreground: @foreground; + selected-urgent-background: #ff5959; + selected-urgent-foreground: @foreground; + + background-color: @background; + border-color: #59ff59; + spacing: 2; + + text-font: "Hack Nerd Font 12"; +} + +element-text, element-icon { + background-color: inherit; + text-color: inherit; +} + +#window { + background-color: @background; + location: 0; + width: 30%; + padding: 2.5ch; + font: @text-font; +} + +#mainbox { + border: 0; + padding: 0; +} + +#message { + border: 2px 2px 2px; + border-color: @border-color; + padding: 5px; + margin: 0px 0px 8px 0px; +} + +#textbox { + text-color: @foreground; +} + +#inputbar { + children: [ prompt,textbox-prompt-colon,entry,case-indicator ]; + background-color: @border-color; + padding: 2px; + margin: 0px 0px 10px 0px; +} + +#prompt { + spacing: 0; + margin: 2px 6px 2px 6px; + background-color: @border-color; + text-color: @background; + padding: 4px; +} + +#textbox-prompt-colon { + expand: false; + str: "->"; + text-color: @active-foreground; + padding: 5px; +} + +#entry { + spacing: 0; + text-color: @normal-foreground; + padding: 5px; +} + +#case-indicator { + spacing: 0; + text-color: @normal-foreground; +} + +#listview { + fixed-height: 0; + border: 2px 2px 2px 2px; + border-color: @border-color; + scrollbar: false; + padding: 5px; +} + +#scrollbar { + width: 4px ; + border: 0; + handle-width: 8px ; + padding: 0; +} + +#sidebar { + border: 2px 0px 0px; + border-color: @border-color; +} + +#mode-switcher { + border: 2px 0px 0px ; + border-color: @border-color; +} + +#button { + text-color: @normal-foreground; +} + +#button.selected { + background-color: @selected-normal-background; + text-color: @selected-normal-foreground; +} + +#inputbar { + spacing: 0; + text-color: @normal-foreground; + padding: 2px ; +} + +#element { + border: 0; + padding: 6px ; +} + +#element.normal.normal { + background-color: @normal-background; + text-color: @normal-foreground; +} + +#element.normal.urgent { + background-color: @urgent-background; + text-color: @urgent-foreground; +} + +#element.normal.active { + background-color: @active-background; + text-color: @active-foreground; +} + +#element.selected.normal { + background-color: @selected-normal-background; + text-color: @selected-normal-foreground; +} + +#element.selected.urgent { + background-color: @selected-urgent-background; + text-color: @selected-urgent-foreground; +} + +#element.selected.active { + background-color: @selected-active-background; + text-color: @selected-active-foreground; +} + +#element.alternate.normal { + background-color: @alternate-normal-background; + text-color: @alternate-normal-foreground; +} + +#element.alternate.urgent { + background-color: @alternate-urgent-background; + text-color: @alternate-urgent-foreground; +} + +#element.alternate.active { + background-color: @alternate-active-background; + text-color: @alternate-active-foreground; +} diff --git a/rofi/.config/rofi/themes/repomenu.rasi b/rofi/.config/rofi/themes/repomenu.rasi new file mode 100644 index 000000000..ac4a832d2 --- /dev/null +++ b/rofi/.config/rofi/themes/repomenu.rasi @@ -0,0 +1,183 @@ +* { + background: #283036; + foreground: #e5e9f0; + + active-background: #434c5e; + active-foreground: @foreground; + normal-background: @background; + normal-foreground: @foreground; + urgent-background: #ff5959; + urgent-foreground: @background; + + alternate-active-background: @active-background; + alternate-active-foreground: @active-foreground; + alternate-normal-background: @background; + alternate-normal-foreground: @foreground; + alternate-urgent-background: @background; + alternate-urgent-foreground: @foreground; + + selected-active-background: #81a1c1; + selected-active-foreground: @foreground; + selected-normal-background: #434c5e; + selected-normal-foreground: @foreground; + selected-urgent-background: #ff5959; + selected-urgent-foreground: @foreground; + + background-color: @background; + border-color: #59ff59; + spacing: 2; + + text-font: "Hack Nerd Font 12"; +} + +element-text, element-icon { + background-color: inherit; + text-color: inherit; +} + +#window { + background-color: @background; + location: 0; + width: 50%; + padding: 2.5ch; + font: @text-font; +} + +#mainbox { + border: 0; + padding: 0; +} + +#message { + border: 2px 2px 2px; + border-color: @border-color; + padding: 5px; + margin: 0px 0px 8px 0px; +} + +#textbox { + text-color: @foreground; +} + +#inputbar { + children: [ prompt,textbox-prompt-colon,entry,case-indicator ]; + background-color: @border-color; + padding: 2px; + margin: 0px 0px 10px 0px; +} + +#prompt { + spacing: 0; + margin: 2px 6px 2px 6px; + background-color: @border-color; + text-color: @background; + padding: 4px; +} + +#textbox-prompt-colon { + expand: false; + str: "->"; + text-color: @active-foreground; + padding: 5px; +} + +#entry { + spacing: 0; + text-color: @normal-foreground; + padding: 5px; +} + +#case-indicator { + spacing: 0; + text-color: @normal-foreground; +} + +#listview { + fixed-height: 0; + border: 2px 2px 2px 2px; + border-color: @border-color; + scrollbar: false; + padding: 5px; +} + +#scrollbar { + width: 4px ; + border: 0; + handle-width: 8px ; + padding: 0; +} + +#sidebar { + border: 2px 0px 0px; + border-color: @border-color; +} + +#mode-switcher { + border: 2px 0px 0px ; + border-color: @border-color; +} + +#button { + text-color: @normal-foreground; +} + +#button.selected { + background-color: @selected-normal-background; + text-color: @selected-normal-foreground; +} + +#inputbar { + spacing: 0; + text-color: @normal-foreground; + padding: 2px ; +} + +#element { + border: 0; + padding: 6px ; +} + +#element.normal.normal { + background-color: @normal-background; + text-color: @normal-foreground; +} + +#element.normal.urgent { + background-color: @urgent-background; + text-color: @urgent-foreground; +} + +#element.normal.active { + background-color: @active-background; + text-color: @active-foreground; +} + +#element.selected.normal { + background-color: @selected-normal-background; + text-color: @selected-normal-foreground; +} + +#element.selected.urgent { + background-color: @selected-urgent-background; + text-color: @selected-urgent-foreground; +} + +#element.selected.active { + background-color: @selected-active-background; + text-color: @selected-active-foreground; +} + +#element.alternate.normal { + background-color: @alternate-normal-background; + text-color: @alternate-normal-foreground; +} + +#element.alternate.urgent { + background-color: @alternate-urgent-background; + text-color: @alternate-urgent-foreground; +} + +#element.alternate.active { + background-color: @alternate-active-background; + text-color: @alternate-active-foreground; +} diff --git a/rofi/.config/rofi/themes/wifimanager.rasi b/rofi/.config/rofi/themes/wifimanager.rasi new file mode 100644 index 000000000..ac4a832d2 --- /dev/null +++ b/rofi/.config/rofi/themes/wifimanager.rasi @@ -0,0 +1,183 @@ +* { + background: #283036; + foreground: #e5e9f0; + + active-background: #434c5e; + active-foreground: @foreground; + normal-background: @background; + normal-foreground: @foreground; + urgent-background: #ff5959; + urgent-foreground: @background; + + alternate-active-background: @active-background; + alternate-active-foreground: @active-foreground; + alternate-normal-background: @background; + alternate-normal-foreground: @foreground; + alternate-urgent-background: @background; + alternate-urgent-foreground: @foreground; + + selected-active-background: #81a1c1; + selected-active-foreground: @foreground; + selected-normal-background: #434c5e; + selected-normal-foreground: @foreground; + selected-urgent-background: #ff5959; + selected-urgent-foreground: @foreground; + + background-color: @background; + border-color: #59ff59; + spacing: 2; + + text-font: "Hack Nerd Font 12"; +} + +element-text, element-icon { + background-color: inherit; + text-color: inherit; +} + +#window { + background-color: @background; + location: 0; + width: 50%; + padding: 2.5ch; + font: @text-font; +} + +#mainbox { + border: 0; + padding: 0; +} + +#message { + border: 2px 2px 2px; + border-color: @border-color; + padding: 5px; + margin: 0px 0px 8px 0px; +} + +#textbox { + text-color: @foreground; +} + +#inputbar { + children: [ prompt,textbox-prompt-colon,entry,case-indicator ]; + background-color: @border-color; + padding: 2px; + margin: 0px 0px 10px 0px; +} + +#prompt { + spacing: 0; + margin: 2px 6px 2px 6px; + background-color: @border-color; + text-color: @background; + padding: 4px; +} + +#textbox-prompt-colon { + expand: false; + str: "->"; + text-color: @active-foreground; + padding: 5px; +} + +#entry { + spacing: 0; + text-color: @normal-foreground; + padding: 5px; +} + +#case-indicator { + spacing: 0; + text-color: @normal-foreground; +} + +#listview { + fixed-height: 0; + border: 2px 2px 2px 2px; + border-color: @border-color; + scrollbar: false; + padding: 5px; +} + +#scrollbar { + width: 4px ; + border: 0; + handle-width: 8px ; + padding: 0; +} + +#sidebar { + border: 2px 0px 0px; + border-color: @border-color; +} + +#mode-switcher { + border: 2px 0px 0px ; + border-color: @border-color; +} + +#button { + text-color: @normal-foreground; +} + +#button.selected { + background-color: @selected-normal-background; + text-color: @selected-normal-foreground; +} + +#inputbar { + spacing: 0; + text-color: @normal-foreground; + padding: 2px ; +} + +#element { + border: 0; + padding: 6px ; +} + +#element.normal.normal { + background-color: @normal-background; + text-color: @normal-foreground; +} + +#element.normal.urgent { + background-color: @urgent-background; + text-color: @urgent-foreground; +} + +#element.normal.active { + background-color: @active-background; + text-color: @active-foreground; +} + +#element.selected.normal { + background-color: @selected-normal-background; + text-color: @selected-normal-foreground; +} + +#element.selected.urgent { + background-color: @selected-urgent-background; + text-color: @selected-urgent-foreground; +} + +#element.selected.active { + background-color: @selected-active-background; + text-color: @selected-active-foreground; +} + +#element.alternate.normal { + background-color: @alternate-normal-background; + text-color: @alternate-normal-foreground; +} + +#element.alternate.urgent { + background-color: @alternate-urgent-background; + text-color: @alternate-urgent-foreground; +} + +#element.alternate.active { + background-color: @alternate-active-background; + text-color: @alternate-active-foreground; +} diff --git a/rofi/.config/rofi/wifimanager.rasi b/rofi/.config/rofi/wifimanager.rasi new file mode 100644 index 000000000..11a0982b2 --- /dev/null +++ b/rofi/.config/rofi/wifimanager.rasi @@ -0,0 +1,9 @@ +configuration { + modi: "wifimanager:wifimanager"; + font: "Hack Nerd Font 12"; +} +@theme "wifimanager" +listview { + lines: 6; + columns: 1; +} \ No newline at end of file diff --git a/rofi/.local/bin/rofi/README.md b/rofi/.local/bin/rofi/README.md new file mode 100644 index 000000000..d78fd6c5a --- /dev/null +++ b/rofi/.local/bin/rofi/README.md @@ -0,0 +1,128 @@ +# Bookmark Manager + +## Usage + +```help +Usage: ./bm [modifier(s)] command [option(s)] + + Commands : + ========== + -h Print this help + -H Print help for legacy usage + -v/-V Print the version + -a 'URL' Add the URL to bookmark file + Options for -a + -t "TagList" Tags are sparated by a comma , + -T "Title" Title for this URL (if empty and allowed Title + downloaded) + -A "accel" Accelerator when search for URLs (start with Accel) + Default is FQDN without gTld (and www, and sheme) + -F Force the bookmark to be created (even if duplicate + or invalid) + -p Force the screenshot to be taken + + -l List all URLs (default action, same thing as calling bm without args) + Options for -l + -z Use the alternate print list + -n Sort the results by date + -N Sort the results by date (reverse) + + + -s 'object' Search for bookmarks + Options for -s + -i Incensitive case search + -z Use the alternate print list + -n Sort the results by date + -N Sort the results by date (reverse) + + -o 'object' Search for bookmarks and open it (use the same argument as for -s) + Options for -o + -i Incensitive case search + -O If more than one answer force the first bookmark to be open + -Y If more than one answer force ALL bookmarks to be open + + -x 'object' Search for bookmarks and copy it to clipboard (use the same argument as for -s) + Options for -x + -i Incensitive case search + -X If more than one answer force the first bookmark to be copied + -Y If more than one answer force ALL bookmarks to be copied + + -r 'object' Search for bookmarks and Print the recorded associated picture (use the same argument as for -s) + Options for -r + -i Incensitive case search + -O If more than one answer force the first bookmark to have its picture printed + -Y If more than one answer force ALL bookmarks to have their picture printed + + -d 'URL' or Delete the URL from bookmark file md5sum or 'URL part' + Options for -d + -D Delete first occurence only + -F Force the bookmark to be deleted (even if duplicate) + -p Delete the associated picture (no trash available) + + -g Generate a HTML page with all bookmarks + If used more than once, generate a page per tag + Options for -g + -G "filename" If g==1 then generate then use this filename to generate page + -O Open the file when generated + + -P 'object' Generate all Non existant picture (check done for all URL in bm), if none argument. + If an argument is given (use the same argument as for -s) only the results will have a picture. + Options for -P + -F Force the picture to be taken again (even if already exists). + + -L List all tags + + -C Print the color table (usefull for configuration) + + -S Show statistics about bookmarks/tags (and also configuration) + Options for -S + -p Print the list of orphaned Pictures + + Modifiers : + =========== + -c 'file' Use this configuration file instead of the default one + + -b 'file' Use this bookmark file instead of the default one + + Old Legacy usage still available : + Usage: bm [options] [cmd] + + Objects: + ======== + :string Search in accelerator list + +string Search in tags list + =string Search in MD5 list + /string Search in URL list + string Search in full test + + How to use: + =========== + # add a bookmark with the given url, description, and optional tags + $ bm add [desc] [tag...] [accelerator] + + # open the first bookmark matching + $ bm open + $ bm + + # search the bookmarks via full-text + $ bm search + + # list tags + $ bm tags + + # list bookmarks available + $ bm list + $ bm ls + $ bm + + # display statistics about the bookmarks + $ bm statistics + $ bm stats + + # view bookmark screenshots in your default browser + $ bm view design + $ bm view + + # clear all bookmarks + $ bm clear +``` \ No newline at end of file diff --git a/rofi/.local/bin/rofi/bm b/rofi/.local/bin/rofi/bm new file mode 100755 index 000000000..b9868e399 --- /dev/null +++ b/rofi/.local/bin/rofi/bm @@ -0,0 +1,1099 @@ +#!/usr/bin/env bash +#-*-coding:utf-8 -*- +#Auto updated? +# Yes +#File: +# bm +#Author: +# The-Repo-Club [wayne6324@gmail.com] +#Github: +# https://github.com/The-Repo-Club/ +# +#Created: +# Fri 09 December 2022, 07:19:23 AM [GMT] +#Modified: +# Fri 09 December 2022, 07:40:39 AM [GMT] +# +#Description: +# +# +#Dependencies: +# +# +# shellcheck disable=all + +# default filename for Baker config file (just the filename no path) +readonly _BM_DEFAULT_CONFIG_FILENAME="${_BM_DEFAULT_CONFIG_FILENAME:=bm.conf}" + +config() { + echo "Reading Configuration..." + [[ "${__w:=$HOME}" != "${HOME}" ]] && echo "Working dir changed to ${__w}" + [[ "${__w: -1}" != '/' ]] && __w="${__w//\/\//\/}/" + if [ ${__B:=0} -eq 0 ]; then + # If a config file is given, we take it first, or look for a default config file + export _BM_CONFIG_FILE="${__w}${_BM_DEFAULT_CONFIG_FILENAME}" + if [ "$(uname -s)" = 'Linux' -a -f "$(readlink -m "${1:-}" 2>/dev/null)" ]; then + export _BM_CONFIG_FILE="${1}" + . "${1}" + elif [ -e "${1}" ]; then + export _BM_CONFIG_FILE="${1}" + . "${1}" + elif [ ${__n:=0} -eq 0 -a -f "${__w}${_BM_DEFAULT_CONFIG_FILENAME}" ]; then + . "${__w}${_BM_DEFAULT_CONFIG_FILENAME}" + echo "Default config file loaded ${__w}${_BM_DEFAULT_CONFIG_FILENAME}" + fi + else + export _BM_CONFIG_FILE='none' + fi + # _BM_BOOKMARK_FILE : Where is stored the bookmark file + export _BM_BOOKMARK_FILE="${_BM_BOOKMARK_FILE:=${__w}bm.bmf}" + + # _BM_BOOKMARK_BACKUP_FILE : Where is stored the backup of bookmark file + export _BM_BOOKMARK_BACKUP_FILE="${_BM_BOOKMARK_BACKUP_FILE:=${__w}.bm.bmf.bck}" + + # _BM_HTML_FILE : Location of generated HTML file + export _BM_HTML_FILE="${_BM_HTML_FILE:=${__w}bm.html}" + + # _BM_DELETE_FILE : Where to find the deleted URLs + export _BM_DELETE_FILE="${_BM_DELETE_FILE:=${__w}.bm.trash}" + + # _BM_BOOKMARK_BACKUP : Copy the bookmark file before each modification + export _BM_BOOKMARK_BACKUP=${_BM_BOOKMARK_BACKUP:=false} + + # _BM_BOOKMARK_BACKUP_VERSION : How many copy to keep (0 for 1, 1 for 2, ...) + export _BM_BOOKMARK_BACKUP_VERSION=${_BM_BOOKMARK_BACKUP_VERSION:=0} + + # _BM_DELETE_ALLOWED : Even with the force flag the delete function will not be allowed (true or false) + export _BM_DELETE_ALLOWED=${_BM_DELETE_ALLOWED:=true} + + # _BM_DELETE_TO_FILE : Allow or disallow the deleted URLs to be copied in the Trash (true or false) + export _BM_DELETE_TO_FILE=${_BM_DELETE_TO_FILE:=false} + + # _BM_DELETE_PICTURE : Allow or disallow the picture associated to a URL to be deleted (true or false) + export _BM_DELETE_PICTURE=${_BM_DELETE_PICTURE:=true} + + # _BM_DUPS_DETECT : Look for duplicates URL before insert in bookmark file (ovverided by -F) + export _BM_DUPS_DETECT=${_BM_DUPS_DETECT:=true} + + # _BM_ASK_BEFORE_OPEN : When using bm with a URL (or search term) ask before opening the browser if more than one answer + export _BM_ASK_BEFORE_OPEN=${_BM_ASK_BEFORE_OPEN:=true} + + # _BM_GET_PAGE_TITLE : When adding a URL execute a curl command to get the if none given + export _BM_GET_PAGE_TITLE=${_BM_GET_PAGE_TITLE:=true} + + # _BM_SCREENSHOT_DIRECTORY : Where to store the screenshot taken + export _BM_SCREENSHOT_DIRECTORY="${_BM_SCREENSHOT_DIRECTORY:=${__w}.bm.shots}" + + # _BM_SCREENSHOT_GET : When adding a URL try to take a screenshot in background + export _BM_SCREENSHOT_GET=${_BM_SCREENSHOT_GET:=true} + + # _BM_SCREENSHOT_WAIT : When taking a screenshot wait for the action to finish + export _BM_SCREENSHOT_WAIT=${_BM_SCREENSHOT_WAIT:=false} + + # _BM_OPEN_FIRST : When using open, the first link found is open + # Also work with the copy to clipboard function + export _BM_OPEN_FIRST=${_BM_OPEN_FIRST:=false} + + # _BM_OPEN_ALL : When using open, if more than one result, all are open (with the same function) + # Also work with the copy to clipboard function (overrided by -Y) + export _BM_OPEN_ALL=${_BM_OPEN_ALL:=false} + + # _BM_NO_ARGS_FORCE_HELP : If no args given to script sends to help instead of list all link + export _BM_NO_ARGS_FORCE_HELP=${_BM_NO_ARGS_FORCE_HELP:=false} + + # _BM_CREATE_BOOKMARK_FILE : If bm is called to list and no file exists. bm will create a default one. + export _BM_CREATE_BOOKMARK_FILE=${_BM_CREATE_BOOKMARK_FILE:=true} + + # _BM_CMD_CAPTURE_CHECK : If you don't have tool installed to take screenshot, just put to false. + # It will disable the _BM_GET_SCREENSHOT. + export _BM_CMD_CAPTURE_CHECK=${_BM_CMD_CAPTURE_CHECK:=true} + + # I suppose the uname command is available + # Please note, the PRE & POST capture command will not be analyzes nor verified. + if [ "$(uname -s)" = 'Linux' ]; then + export _BM_CMD_IMAGE_OPEN='feh' + export _BM_CMD_OPEN='xdg-open' + export _BM_CMD_PRE_CAPTURE='' + export _BM_CMD_CAPTURE='cutycapt' + export _BM_CMD_CAPTURE_ARGS='--insecure --url={URL} --out={FILE}' + export _BM_CMD_POST_CAPTURE='' + export _BM_CMD_MD5='md5sum' + export _BM_CMD_COPY='xsel' + else + # To keep commpatibility with the Mac (even i don't have any) + export _BM_CMD_IMAGE_OPEN='open' + export _BM_CMD_OPEN='open' + export _BM_CMD_PRE_CAPTURE='' + export _BM_CMD_CAPTURE='webkit2png' + export _BM_CMD_CAPTURE_ARGS='-C -o {FILE}.png {URL}' + export _BM_CMD_POST_CAPTURE='cp -- "{FILE}-clipped.png" "{FILE}" ; rm -f -- {FILE}-{clipped,full,thumb}.png' + export _BM_CMD_MD5='md5' + export _BM_CMD_COPY='xsel' + fi + + # _BM_PRINT_LINE : This line is used to print datas + export _BM_PRINT_LINE="${_BM_PRINT_LINE:=${BOLD}${GRAY}BM_ACCEL ${RESET}${DIM}-->${RESET} ${GRAY_LIGHT}BM_TITLE ${GRAY_DARK}${BOLD}[BM_TAGS]${RESET}\n${UNDERLINE}${DIM}BM_URL${RESET} - ${GRAY_DARK}Added: BM_DATE${RESET}}" + + # _BM_PRINT_LINE_ALTERNATIVE : This line is used to print datas + export _BM_PRINT_LINE_ALTERNATIVE="${_BM_PRINT_LINE_ALTERNATIVE:=${BOLD}${GRAY}BM_ACCEL ${RESET}${DIM}-->${RESET} ${GRAY_LIGHT}BM_TITLE ${GRAY_DARK}${BOLD}[BM_TAGS]${RESET}\n${UNDERLINE}${DIM}BM_URL${RESET} ${GRAY_DARK}(BM_MD5)${RESET} - ${GRAY_DARK}Added: BM_DATE${RESET} - ${GRAY_DARK}Picture: BM_PICTURE${RESET}}" + + # _BM_PRINT_SCHEME : If set to no, bm will remove scheme from URL printing + export _BM_PRINT_SCHEME=${_BM_PRINT_SCHEME:=true} + + # _BM_SEARCH_IGNORECASE : Ignore case if set when searching (overrided by -i) + export _BM_SEARCH_IGNORECASE=${_BM_SEARCH_IGNORECASE:=false} +} + +# slug creates a friendly URL like 'hello-world' +slug() { + iconv -f utf8 -t ascii//TRANSLIT | tr -cs '[:alnum:]\n' - | tr '[:upper:]' '[:lower:]' | sed 's|^-*||;s|-*$||' +} + +# version +RELEASE='$Format:%h$' +VERSION='2021.04.05' + +# +# Output usage info +# + +usage() { + cat <<EOF + +Usage: ${0} [modifier(s)] command [option(s)] + + Commands : + ========== + -h Print this help + -v/-V Print the version/And the short commit name + -a 'URL' Add the URL to bookmark file + Options for -a + -t "TagList" Tags are sparated by a comma , + -T "Title" Title for this URL (if empty and allowed Title + downloaded) + -A "accel" Accelerator when search for URLs (start with Accel) + Default is an autoincrement index + -F Force the bookmark to be created (even if duplicate + or invalid) + -p Force the screenshot to be taken (config dependant) + + -l List all URLs (default action, same thing as calling bm without args) + Options for -l + -z Use the alternate print list + -n Sort the results by date + -N Sort the results by date (reverse) + + -s 'object' Search for bookmarks + Options for -s + -i Incensitive case search + -z Use the alternate print list + -n Sort the results by date + -N Sort the results by date (reverse) + Objects are + :string Search in accelerator list + +string Search in tags list + =string Search in MD5 list + /string Search in URL list + string Search in full test + + -o 'object' Search for bookmarks and open it (use the same argument as for -s) + Options for -o + -i Incensitive case search + -O If more than one answer force the first bookmark to be open + -Y If more than one answer force ALL bookmarks to be open + + -x 'object' Search for bookmarks and copy it to clipboard (use the same argument as for -s) + Options for -x + -i Incensitive case search + -X If more than one answer force the first bookmark to be copied + -Y If more than one answer force ALL bookmarks to be copied + + -r 'object' Search for bookmarks and Print the recorded associated picture (use the same argument as for -s) + Options for -r + -i Incensitive case search + -O If more than one answer force the first bookmark to have its picture printed + -Y If more than one answer force ALL bookmarks to have their picture printed + + -d 'URL' Delete the URL from bookmark file + md5sum or + 'URL part' Options for -d + -D Delete first occurence only + -F Force the bookmark to be deleted (even if duplicate) + -p Delete the associated picture (no trash available) + + -g Generate a HTML page with all bookmarks + If used more than once, generate a page per tag + Options for -g + -G "filename" If g==1 then generate and use this filename to generate page + -O Open the file when generated + + -P 'object' Generate all Non existant picture (check done for all URL in bm), if none argument. + If an argument is given (use the same argument as for -s) only the results will have a picture. + Options for -P + -F Force the picture to be taken again (even if already exists). + + -q 'object' Connect to the URL to replace the existing Title by the downloaded one. + Options for -q + -i Incensitive case search + -O If more than one answer force the first bookmark to have its title downloaded + -Y If more than one answer force ALL bookmarks to have their title downloaded + + -L List all tags + + -C Print the color table (usefull for configuration) + + -E Open the bm.bmf with your \$EDITOR + + -S Show statistics about bookmarks/tags (and also configuration) + Options for -S + -p Print the list of orphaned Pictures + + Modifiers : + =========== + -c 'file' Use this configuration file instead of the default one + + -b 'file' Use this bookmark file instead of the default one + + -w 'directory' Use this directory to find default configuration file and bookmark file instead of the default one +EOF +} + +getAccelMax() { + while read a; do + ((${a:=0} >= ${_max:=0})) && ((_max = ${a} + 1)) + done <<<"$(awk -F'|' '$0 !~ /^[ ]*#/ && $3 ~ /^[:space:]*:[:space:]*[0-9]+[:space:]*$/ { sub(/:+/, "", $3); print $3; }' "${_BM_BOOKMARK_FILE}")" + echo -n "${_max}" +} + +backupBm() { + if [ "${_BM_BOOKMARK_BACKUP,,}" = 'true' ]; then + while [ ${count:=${_BM_BOOKMARK_BACKUP_VERSION}} -gt 0 ]; do + cp -- "${_BM_BOOKMARK_BACKUP_FILE}.$((count - 1))" "${_BM_BOOKMARK_BACKUP_FILE}.$((count--))" 2>&1 >/dev/null + done + cp -- "${_BM_BOOKMARK_FILE}" "${_BM_BOOKMARK_BACKUP_FILE}.0" + fi +} + +titleDl() { + if command -v hxselect &>/dev/null; then + curl -Lks "${1:-}" 2>&1 | hxselect -ic title 2>/dev/null + else + curl -Lks "${1:-}" 2>&1 | sed '/<title>/I!d;/<\/title>/I!d;s;^[[:space:]]*<title>\([^<]*\)<.*;\1;i' + fi +} + +saveUrl() { + [[ "${__url}" =~ ^[[:space:]]*(f|ht)tps*://.*$ ]] || { + if [ ${__F:=0} -eq 0 ]; then + die "Your URL seems invalid '${__url}'. Use -F to force." + else + echo "Force adding invalid URL" >&2 + fi + } + local _sum="$(${_BM_CMD_MD5} <<<"${__url}")" + if [ "${_BM_DUPS_DETECT,,}" = 'true' ]; then + if [ ! -z "$(awk -F '|' '$0 !~ /^[ ]*#/ && $1 ~ /'"${_sum%% *}"'/' "${_BM_BOOKMARK_FILE}")" ]; then + if [ ${__F:=0} -eq 0 ]; then + die "URL is already in the Bookmark file" + else + echo "Force adding duplicate URL" >&2 + fi + fi + fi + if [ "${_BM_GET_PAGE_TITLE,,}" = 'true' -a -z "${__T:=}" ]; then + # if [ $( command -v hxselect &>/dev/null ) ]; then + # __T="$( curl -Lks "${__url}" 2>&1 | hxselect -ic title )" + # else + # __T="$( curl -Lks "${__url}" 2>&1 | sed '/<title>/I!d;/<\/title>/I!d;s;^[[:space:]]*<title>\([^<]*\)<.*;\1;i' )" + # fi + __T="$(titleDl "${__url}")" + fi + [[ ! -z "${__T:=}" ]] && __T="${__T//\|/\\|}" + backupBm + [[ -z "${__T:=}" ]] && __T="$(slug <<<"${__url}")" # If no title => slug the url + [[ -z "${__t:=}" ]] && __t='default' # Default tag is default + [[ -z "${__A:=}" ]] && __A="$(getAccelMax)" + [[ "${__A:0:1}" != ':' ]] && __A=":${__A}" # If no accel => Add a number + echo "${_sum%% *}|$(date '+%FT%TZ')|${__A}|${__url}|${__T}|${__t}" >>"${_BM_BOOKMARK_FILE}" || die "Insert aborted into '${_BM_BOOKMARK_FILE}' !" + echo "${__url} inserted into '${_BM_BOOKMARK_FILE}'" + [[ ! -f "${_BM_SCREENSHOT_DIRECTORY}/${_sum%% *}.png" || ${__F:=0} -eq 1 ]] && { screenshot_take "${_BM_SCREENSHOT_DIRECTORY}/${_sum%% *}.png" "${__url}" & } + [[ "${_BM_SCREENSHOT_WAIT,,}" = 'true' ]] && wait + search_bookmarks "${__A}" +} + +# +# manage each line to print it +# + +readLines() { + IFS='|' + while read m d a u T t; do + _line="${_BM_PRINT_LINE}" + [[ ${__z:=0} -eq 1 ]] && _line="${_BM_PRINT_LINE_ALTERNATIVE}" + _line="${_line//BM_MD5/$m}" + if [ "${_BM_PRINT_SCHEME,,}" = 'false' ]; then + u="$(sed 's/^[[:space:]]*\(f\|ht\)tps*:\/\///' <<<"${u}")" + fi + _line="${_line//BM_URL/$u}" + _line="${_line//BM_TITLE/$T}" + _line="${_line//BM_TAGS/$t}" + _line="${_line//BM_ACCEL/${a:=No-Accelerator}}" + _line="${_line//BM_DATE/${d}}" + local _pict="$(stat -c '%y' "${_BM_SCREENSHOT_DIRECTORY}/${m}.png" 2>/dev/null)" + [[ ! -z "${_pict}" ]] && _pict="$(date '+%FT%TZ' --date "${_pict}")" + _line="${_line//BM_PICTURE/${_pict:=None}}" + echo -e "${_line}\n" + done +} + +# +# List all bookmarks +# + +list_bookmarks() { + [[ ${__n:=0} -eq 1 ]] && { + awk '$0 !~ /^[ ]*#/' "${_BM_BOOKMARK_FILE}" | sort -t'|' -k2 | readLines + return + } + [[ ${__N:=0} -eq 1 ]] && { + awk '$0 !~ /^[ ]*#/' "${_BM_BOOKMARK_FILE}" | sort -t'|' -k2 -r | readLines + return + } + awk '$0 !~ /^[ ]*#/' "${_BM_BOOKMARK_FILE}" | readLines +} + +# +# Search all bookmarks with <query> +# +search() { + local _s='' + [[ -z "${1:-}" ]] && list_bookmarks && return + case "${1:0:1}" in + :) + _s=3 + _ss=0 + ;; # Accelerator + +) + _s="NF" + _ss=1 + ;; # Tags + =) + _s=1 + _ss=1 + ;; # MD5 part + /) + _s=4 + _ss=1 + ;; # URL part + *) + _s=0 + _ss=0 + ;; # full text search + esac + [[ "${_BM_SEARCH_IGNORECASE,,}" = 'true' || ${__i:=0} -eq 1 ]] && local _ign=1 + awk -F'|' -vIgn=${_ign:=0} ' + BEGIN { + if (Ign == 1) { s=tolower("'"${1:${_ss}}"'"); } else { s="'"${1:${_ss}}"'"; } + } +$0 !~ /^[ ]*#/ && (Ign == 0 && $'"${_s}"' ~ s) || (Ign ==1 && tolower($'"${_s}"') ~ s) {print $0;} +' "${_BM_BOOKMARK_FILE}" + +} +search_bookmarks() { + [[ ${__n:=0} -eq 1 ]] && { + search "${@}" | sort -t'|' -k2 | readLines + return + } + [[ ${__N:=0} -eq 1 ]] && { + search "${@}" | sort -t'|' -k2 -r | readLines + return + } + search "${@}" | readLines +} + +# +# Open first bookmark matching <query> +# + +recorded_picture() { + local _lines="$(search "${@}")" + if [ "${_BM_ASK_BEFORE_OPEN,,}" = 'true' -a ${__O:=0} -eq 0 -a ${__Y:=0} -eq 0 ]; then + if [ $(wc -l <<<"${_lines}") -gt 1 ]; then + read -p"More than one URL found. Open all Pictures ? [Y/N] : " -n1 _answer + [[ "${_answer,,}" != 'y' ]] && die "\nUse -O to force the first URL or refine your search" + fi + fi + local _all='' + echo '' + while read _nl; do + readLines <<<"${_nl}" + if [ "${_BM_OPEN_ALL,,}" = 'true' -o ${__Y:=0} -eq 1 ]; then + _all="${_BM_SCREENSHOT_DIRECTORY}/$(awk -F'|' '{print $1}' <<<"${_nl}").png ${_all}" + else + "${_BM_CMD_IMAGE_OPEN}" "${_BM_SCREENSHOT_DIRECTORY}/$(awk -F'|' '{print $1}' <<<"${_nl}").png" + [[ "${_BM_OPEN_FIRST,,}" = 'true' || ${__O:=0} -eq 1 ]] && exit 0 + fi + done <<<"$(echo -e "${_lines}")" + [[ "${_BM_OPEN_ALL,,}" = 'true' || ${__Y:=0} -eq 1 ]] && "${_BM_CMD_IMAGE_OPEN}" "${_all}" +} + +download_title() { + local _lines="$(search "${@}")" + if [ "${_BM_ASK_BEFORE_OPEN,,}" = 'true' -a ${__O:=0} -eq 0 -a ${__Y:=0} -eq 0 ]; then + if [ $(wc -l <<<"${_lines}") -gt 1 ]; then + read -p"More than one URL found. Download Title for all URLs ? [Y/N] : " -n1 _answer + [[ "${_answer,,}" != 'y' ]] && die "\nUse -O to force the first URL or refine your search" + fi + fi + local _all='' + echo '' + while read _nl; do + readLines <<<"${_nl}" + local u="$(awk -F'|' '{print $4}' <<<"${_nl}")" + if [ "${_BM_OPEN_ALL,,}" = 'true' -o ${__Y:=0} -eq 1 ]; then + echo "${u} --> $(titleDl "${u}")" + else + echo "${u} --> $(titleDl "${u}")" + [[ "${_BM_OPEN_FIRST,,}" = 'true' || ${__O:=0} -eq 1 ]] && exit 0 + fi + done <<<"$(echo -e "${_lines}")" +} + +open_bookmark() { + local _lines="$(search "${@}")" + if [ "${_BM_ASK_BEFORE_OPEN,,}" = 'true' -a ${__O:=0} -eq 0 -a ${__Y:=0} -eq 0 ]; then + if [ $(wc -l <<<"${_lines}") -gt 1 ]; then + read -p"More than one URL found. Open all ? [Y/N] : " -n1 _answer + [[ "${_answer,,}" != 'y' ]] && die "\nUse -O to force the first URL or refine your search" + fi + fi + local _all='' + while read _nl; do + readLines <<<"${_nl}" + if [ "${_BM_OPEN_ALL,,}" = 'true' -o ${__Y:=0} -eq 1 ]; then + _all="$(awk -F'|' '{print $4}' <<<"${_nl}") ${_all}" + else + "${_BM_CMD_OPEN}" "$(awk -F'|' '{print $4}' <<<"${_nl}")" + [[ "${_BM_OPEN_FIRST,,}" = 'true' || ${__O:=0} -eq 1 ]] && exit 0 + fi + done <<<"$(echo -e "${_lines}")" + [[ "${_BM_OPEN_ALL,,}" = 'true' || ${__Y:=0} -eq 1 ]] && "${_BM_CMD_OPEN}" "${_all}" +} + +copy_bookmark() { + local _lines="$(search "${@}")" + if [ "${_BM_ASK_BEFORE_OPEN,,}" = 'true' -a ${__X:=0} -eq 0 -a ${__Y:=0} -eq 0 ]; then + if [ $(wc -l <<<"${_lines}") -gt 1 ]; then + read -p"More than one URL found. Copy all ? [Y/N] : " -n1 _answer + [[ "${_answer,,}" != 'y' ]] && die "\nUse -X to force the first URL or refine your search" + fi + fi + local _all='' + while read _nl; do + readLines <<<"${_nl}" + if [ "${_BM_OPEN_ALL,,}" = 'true' -o ${__Y:=0} -eq 1 ]; then + _all="$(awk -F'|' '{print $4}' <<<"${_nl}") ${_all}" + else + awk -F'|' '{print $4}' <<<"${_nl}" | "${_BM_CMD_COPY}" + awk -F'|' '{print $4}' <<<"${_nl}" | "${_BM_CMD_COPY}" -b + [[ "${_BM_OPEN_FIRST,,}" = 'true' || ${__X:=0} -eq 1 ]] && exit 0 + fi + done <<<"$(echo -e "${_lines}")" + [[ "${_BM_OPEN_ALL,,}" = 'true' || ${__Y:=0} -eq 1 ]] && "${_BM_CMD_COPY}" <<<"${_all}" + [[ "${_BM_OPEN_ALL,,}" = 'true' || ${__Y:=0} -eq 1 ]] && "${_BM_CMD_COPY}" -b <<<"${_all}" +} + +delete_bookmark() { + [[ -z "${@//[ ]/}" ]] && die "You MUST give an argument !" + local _lines="$(search "${@}")" + if [ "${_BM_DELETE_ALLOWED,,}" = 'true' ]; then + while read _nl; do readLines <<<"${_nl}"; done <<<"$(echo -e "${_lines}")" + if [ "${_BM_ASK_BEFORE_OPEN,,}" = 'true' -a ${__D:=0} -eq 0 -a ${__Y:=0} -eq 0 ]; then + if [ $(wc -l <<<"${_lines}") -ge 1 ]; then + read -p"You're about to delete entry(ies). Ready ? [Y/N] : " -n1 _answer + [[ "${_answer,,}" != 'y' ]] && die "\nUse -D to force the backup if not configured." + echo '' + fi + fi + backupBm + local _all='' + while read _nl; do + if [ "${_BM_DELETE_TO_FILE,,}" = 'true' -o ${__D:=0} -eq 1 ]; then + echo "${_nl}" >>"${_BM_DELETE_FILE}" + fi + [[ "${_BM_DELETE_PICTURE,,}" = 'true' || ${__p:=0} -eq 1 ]] && rm -f -- "${_BM_SCREENSHOT_DIRECTORY}/${_nl%%|*}.png" + IFS='|' read m d a u T t <<<"${_nl}" + sed -i -e '/^'"${m}.*${u//\//\\/}.*${t//\//\\/}"'$/d' "${_BM_BOOKMARK_FILE}" + done <<<"$(echo -e "${_lines}")" + else + die "You're not allowed to delete entries. Change the _BM_DELETE_ALLOWED to true !" + fi +} + +edit_bookmark() { + echo -e "You're about to open your '${_BM_BOOKMARK_FILE}' file with your \$EDITOR.\n\n/!\ Press any key to continue, or CTRL+C to abort. /!\ " + read -n1 toto + ${EDITOR:=vi} "${_BM_BOOKMARK_FILE}" +} + +# +# Diplay some statistics about the bookmarks +# +stats() { + echo "===== Configuration =====" + echo "Bookmark file : ${_BM_BOOKMARK_FILE}" + echo "Trash file : ${_BM_DELETE_FILE}$([[ ! -e "${_BM_DELETE_FILE}" ]] && echo " (but doesn't exist)")" + echo "Config file : ${_BM_CONFIG_FILE}$([[ ! -e "${_BM_CONFIG_FILE}" ]] && echo " (but doesn't exist)")" + echo "Screenshot directory : ${_BM_SCREENSHOT_DIRECTORY}" + echo "Backup file(s) : ${_BM_BOOKMARK_BACKUP_FILE}*" + for i in "${_BM_BOOKMARK_BACKUP_FILE}"*; do + echo " ${i}" + done + echo -e "\n===== Statistics =====" + readarray -t lines <"${_BM_BOOKMARK_FILE}" + echo "# of Bookmarks : ${#lines[@]}" + echo "# of Duplicate : $(awk -F'|' '$0 !~ /^[ ]*#/ {print $1}' "${_BM_BOOKMARK_FILE}" | sort | uniq -d | wc -l)" + local tags="$(awk -F'|' '$0 !~ /^[ ]*#/ {print $NF}' "${_BM_BOOKMARK_FILE}" | sed -e 's/\([[:space:]]*,[[:space:]]*\)/\n/g')" + + echo "# of tags : $(sort -u <<<"${tags,,}" | wc -l)" + echo "Top 14 tags used :" + local nli=0 + local pa=0 + ( + sort <<<"${tags,,}" | uniq -c | sort -nr | while read n t; do + ((nli++)) + [[ ${nli} -gt 7 ]] && nli=1 && echo '' + ((pa++)) + [[ ${pa} -gt 14 ]] && break + echo -n "${t// / }:${n} " + done + echo '' + ) | column -t -c 17 + echo "# of Pictures : $(ls -1 "${_BM_SCREENSHOT_DIRECTORY}"/*.png | wc -l) [# of files in ${_BM_SCREENSHOT_DIRECTORY}:$(ls -1 "${_BM_SCREENSHOT_DIRECTORY}"/* | wc -l)]" + echo "All Pictures size : $(du -sh "${_BM_SCREENSHOT_DIRECTORY}" | awk '{print $1}')" + local orphaned="$( + cd "${_BM_SCREENSHOT_DIRECTORY}" && for i in *; do [[ -z "$(sed -e '/'"${i%%.*}"'/!d' "${_BM_BOOKMARK_FILE}")" ]] && ((orph++)); done + echo "${orph:=0}" + )" + local nopics="$( + while read i; do [[ ! -f "${_BM_SCREENSHOT_DIRECTORY}/${i%%|*}.png" ]] && ((nopics++)); done <<<${lines[@]} + echo "${nopics:=0}" + )" + echo "Bookmark Without Pic : ${nopics}" + echo "Orphaned pictures : ${orphaned}" + [[ ${__p:=0} -eq 1 ]] && { + echo -e "\n===== Orphaned =====" + echo "List of orphaned pictures :" + cd "${_BM_SCREENSHOT_DIRECTORY}" && for i in *; do [[ -z "$(sed -e '/'"${i%%.*}"'/!d' "${_BM_BOOKMARK_FILE}")" ]] && echo " - ${_BM_SCREENSHOT_DIRECTORY}/${i} [Trashed URL should be: $(awk -F'|' 'BEGIN{l="Unknown"} ($1 ~ /'"${i%%.*}"'/) {l=$4;} END {print l}' "${_BM_DELETE_FILE}")]"; done + } +} + +# +# Output tags. +# + +list_tags() { + local tags="$(awk -F'|' '$0 !~ /^[ ]*#/ {print $NF}' "${_BM_BOOKMARK_FILE}" | sed -e 's/\([[:space:]]*,[[:space:]]*\)/\n/g')" + local nli=0 + ( + sort <<<"${tags,,}" | uniq -c | sort -nr | while read n t; do + ((nli++)) + [[ ${nli} -gt 7 ]] && nli=1 && echo '' + echo -n "${t// / }:${n} " + done + echo '' + ) | column -t -c 17 +} + +# +# Stylesheet +# + +style() { + cat <<EOF +<style type="text/css"> + * { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + body { + padding: 50px 0 5px 50px; + background: #1f1f1f url(${_BM_SCREENSHOT_DIRECTORY}/black-Linen.png); + font: 12px "Helvetica Neue", Helvetica, Arial, sans-serif; + } + .bm { + position: relative; + float: left; + margin: 15px; + padding: 1px; + opacity: 1; + border: 15px solid black; + border-radius: 5px; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + -webkit-transition: -webkit-box-shadow 600ms; + -moz-transition: -webkit-box-shadow 600ms; + -webkit-box-shadow: 0 0 1px 0 #555, 0 0 10px rgba(0,0,0,.5); + -moz-box-shadow: 0 0 1px 0 #555, 0 0 10px rgba(0,0,0,.5); + overflow: hidden; + } + .bm:hover { + -webkit-box-shadow: 0 0 40px #1ab0ff + , 0 0 3px #06bdff + , 0 1px 1px #4ee2ff + , 0 1px 0 #fff; + -moz-box-shadow: 0 0 40px #1ab0ff + , 0 0 3px #06bdff + , 0 1px 1px #4ee2ff + , 0 1px 0 #fff; + } + .bm:hover img { + opacity: 1; + } + .bm img { + width: 200px; + height: 150px; + opacity: .5; + border-radius: 5px; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + -webkit-transition: opacity 200ms; + -moz-transition: opacity 200ms; + } + .bm p { + margin: 0; + padding: 10px; + width: 100%; + background: rgba(0,0,0,.75); + position: absolute; + left: 0; + color: white; + letter-spacing: 1px; + bottom: -50px; + -webkit-font-smoothing: antialiased; + -webkit-transition: bottom 200ms ease-in; + } + .bm:hover p { + bottom: 0; + } +</style> +EOF +} + +# +# Generate bookmark screenshots +# + +screenshot_take() { + if [ "${_BM_SCREENSHOT_GET,,}" = 'true' -o ${__p:=0} -eq 1 ]; then + [[ ! -d "${_BM_SCREENSHOT_DIRECTORY}" ]] && { mkdir -p "${_BM_SCREENSHOT_DIRECTORY}" || die "Can't create thumbnail directory."; } + [[ ! -z "${_BM_CMD_PRE_CAPTURE}" ]] && { + local _pre="${_BM_CMD_PRE_CAPTURE//\{FILE\}/${1}}" + _pre="${_pre//\{URL\}/${2}}" + ${_pre} + } + local _cmd="${_BM_CMD_CAPTURE//\{FILE\}/${1}}" + _cmd="${_cmd//\{URL\}/${2}}" + local _args="${_BM_CMD_CAPTURE_ARGS//\{FILE\}/${1}}" + _args="${_args//\{URL\}/${2}}" + eval ${_cmd} ${_args} + [[ ! -z "${_BM_CMD_POST_CAPTURE}" ]] && { + local _post="${_BM_CMD_POST_CAPTURE//\{FILE\}/${1}}" + _post="${_post//\{URL\}/${2}}" + ${_post} + } + fi +} + +picturize() { + if [ -z "${1:-}" ]; then + awk -F'|' '$0 !~ /^[ ]*#/ {print $1,$4;}' "${_BM_BOOKMARK_FILE}" | while read m u; do + [[ ! -f "${_BM_SCREENSHOT_DIRECTORY}/${m}.png" || ${__F:=0} -eq 1 ]] && { screenshot_take "${_BM_SCREENSHOT_DIRECTORY}/${m}.png" "${u}" & } + [[ "${_BM_SCREENSHOT_WAIT,,}" = 'true' ]] && wait + done + else + local _lines="$(search "${@}")" + if [ "${_BM_ASK_BEFORE_OPEN,,}" = 'true' -a ${__O:=0} -eq 0 -a ${__Y:=0} -eq 0 ]; then + if [ $(wc -l <<<"${_lines}") -gt 1 ]; then + read -p"More than one URL found. Open all ? [Y/N] : " -n1 _answer + [[ "${_answer,,}" != 'y' ]] && die "\nUse -O to force the first URL or refine your search" + fi + fi + local _all='' + while read _nl; do + readLines <<<"${_nl}" + if [ "${_BM_OPEN_ALL,,}" = 'true' -o ${__Y:=0} -eq 1 ]; then + IFS='|' read m d a u b <<<"${_nl}" + screenshot_take "${_BM_SCREENSHOT_DIRECTORY}/${m}.png" "${u}" & + else + IFS='|' read m d a u b <<<"${_nl}" + screenshot_take "${_BM_SCREENSHOT_DIRECTORY}/${m}.png" "${u}" & + [[ "${_BM_OPEN_FIRST,,}" = 'true' || ${__O:=0} -eq 1 ]] && exit 0 + fi + done <<<"$(echo -e "${_lines}")" + fi +} + +bookmark_generator() { + [[ ! -f "${_BM_SCREENSHOT_DIRECTORY}/black-Linen.png" && -f "/usr/share/bm/black-Linen.png" ]] && cp "/usr/share/bm/black-Linen.png" "${_BM_SCREENSHOT_DIRECTORY}/black-Linen.png" + echo "<!DOCTYPE html><html><head><meta charset="UTF-8"><title>bm v${VERSION} : all your bookmarks" >"${1:-${_BM_HTML_FILE}}" + style >>"${1:-${_BM_HTML_FILE}}" + echo "" >>"${1:-${_BM_HTML_FILE}}" + # search "${2:-}" | while IFS='|' read m a u T t d; do + awk '$0 !~ /^[ ]*#/' "${_BM_BOOKMARK_FILE}" | while IFS='|' read m d a u T t; do + echo "
+ ${u} +

${T}
View image

+
" >>"${1:-${_BM_HTML_FILE}}" + done + echo "" >>"${1:-${_BM_HTML_FILE}}" + [[ ${__O:=0} -eq 1 ]] && "${_BM_CMD_OPEN}" "${1:-${_BM_HTML_FILE}}" +} + +# +# die print message to stderr and exit +# + +die() { + echo -e "${@}" >&2 + exit 1 +} + +# +# checkBinaries check the script is able to run and give hints +# + +checkBinaries() { + if [ ${#} -ne 0 ]; then + local cmdErr="${@}" + else + local cmdErr="sed awk date iconv cat curl ${_BM_CMD_OPEN} ${_BM_CMD_MD5} column" + fi + set ${cmdErr} + while [ ${#} -ne 0 ]; do + if ! command -v "${1:-}" &>/dev/null; then + echo "command not found: ${1}" >&2 + local rc=1 + fi + shift + done + [[ ${rc:=0} -ne 0 ]] && die "At least one command is missing. Please install it before using bm." + # Checking sed + local rc=1 + [[ ! -f "/tmp/sedtest.$$" ]] && + echo -n 'toto' >"/tmp/sedtest.$$" && + sed -i.bak -e 's;^toto$;tata;' "/tmp/sedtest.$$" && + [[ -f "/tmp/sedtest.$$.bak" ]] && + grep -q "tata" "/tmp/sedtest.$$" && + rc=0 && + rm -f "/tmp/sedtest.$$" "/tmp/sedtest.$$.bak" + [[ ${rc} -ne 0 ]] && die "sed seems to not handle -i argument properly, please check" +} + +# +# defineColors generate the variables to use to colorize the output +# + +defineColors() { + export BLACK="\e[30m" + export BLACK_LIGHT="\e[90m" + export GRAY_DARK="${BLACK_LIGHT}" + export RED="\e[31m" + export RED_LIGHT="\e[91m" + export GREEN="\e[32m" + export GREEN_LIGHT="\e[92m" + export YELLOW="\e[33m" + export YELLOW_LIGHT="\e[93m" + export BLUE="\e[34m" + export BLUE_LIGHT="\e[94m" + export MAGENTA="\e[35m" + export MAGENTA_LIGHT="\e[95m" + export CYAN="\e[36m" + export CYAN_LIGHT="\e[96m" + export GRAY="\e[37m" + export GRAY_LIGHT="\e[97m" + export WHITE="${GRAY_LIGHT}" + + export RESET="\e[0m" + export BOLD="\e[1m" + export BOLD_RESET="\e[21m" + export DIM="\e[2m" + export DIM_RESET="\e[22m" + export UNDERLINE="\e[4m" + export UNDERLINE_RESET="\e[24m" + export INVERT="\e[7m" + export INVERT_RESET="\e[27m" + if [ ! -z "${1:-}" ]; then + echo "Use the following colors to fit your needs :" + ( + echo -e "${RESET}${BLACK}BLACK${RESET} - ${BLACK_LIGHT}BLACK_LIGHT${RESET} - ${BOLD}${BLACK}BOLD BLACK${RESET} - ${DIM}${BLACK}DIM BLACK${RESET}" + echo -e "${RESET}${RED}RED${RESET} - ${RED_LIGHT}RED_LIGHT${RESET} - ${BOLD}${RED}BOLD RED${RESET} - ${DIM}${RED}DIM RED${RESET}" + echo -e "${RESET}${GREEN}GREEN${RESET} - ${GREEN_LIGHT}GREEN_LIGHT${RESET} - ${BOLD}${GREEN}BOLD GREEN${RESET} - ${DIM}${GREEN}DIM GREEN${RESET}" + echo -e "${RESET}${YELLOW}YELLOW${RESET} - ${YELLOW_LIGHT}YELLOW_LIGHT${RESET} - ${BOLD}${YELLOW}BOLD YELLOW${RESET} - ${DIM}${YELLOW}DIM YELLOW${RESET}" + echo -e "${RESET}${BLUE}BLUE${RESET} - ${BLUE_LIGHT}BLUE_LIGHT${RESET} - ${BOLD}${BLUE}BOLD BLUE${RESET} - ${DIM}${BLUE}DIM BLUE${RESET}" + echo -e "${RESET}${MAGENTA}MAGENTA${RESET} - ${MAGENTA_LIGHT}MAGENTA_LIGHT${RESET} - ${BOLD}${MAGENTA}BOLD MAGENTA${RESET} - ${DIM}${MAGENTA}DIM MAGENTA${RESET}" + echo -e "${RESET}${CYAN}CYAN${RESET} - ${CYAN_LIGHT}CYAN_LIGHT${RESET} - ${BOLD}${CYAN}BOLD CYAN${RESET} - ${DIM}${CYAN}DIM CYAN${RESET}" + echo -e "${RESET}${GRAY}GRAY${RESET} - ${GRAY_LIGHT}GRAY_LIGHT${RESET} - ${BOLD}${GRAY}BOLD GRAY${RESET} - ${DIM}${GRAY}DIM GRAY${RESET}" + echo -e "${RESET}${GRAY_DARK}GRAY_DARK${RESET} - ${WHITE}WHITE${RESET}" + ) | column -t + echo -e "You could also use the ${UNDERLINE}UNDERLINE, the ${INVERT}INVERT and the ${RESET}RESET variable. INVERT_RESET and UNDERLINE_RESET also available" + exit 0 + fi +} + +############################################################################## +# MAIN +############################################################################## + +##################### TRANSITIONNAL ############################ +_regex='^(help|version|tags|ls|list|search|open|add|view|stats|statistics|clear|colors)$' +if [[ ${1:-} =~ ${_regex} ]]; then + case "$1" in + tags) __L=1 ;; + ls | list) __l=1 ;; + search) + __s=1 + __search="${@:2}" + ;; + open) + __o=1 + __open="${@:2}" + ;; + add) + __a=1 + __url="${2:-}" + __T="${3:-}" + __t="${4:-}" + __A="${5:-}" + ;; + view) + __r=1 + __search="${@:2}" + ;; + stats | statistics) __S=1 ;; + clear) die "This now a deprecated feature. Use -d instead." ;; + colors) __C=1 ;; + help) + usage 1 + exit 0 + ;; + version) + echo -n "${0} v${VERSION}" + exit 0 + ;; + esac +else + # -c config file + # -C colors print ==> exit + # -b bookmark file + # -d delete + # -D deletefile if set override _BM_DELETE_TO_FILE to true + # -w working dir + # -a add url =/= -l, -s, -S, -O, -o, -x, -X, -g, -C + # -A Accelerator + # -T title + # -t tags + # -s search + # -S stats + # -l list all url + # -o open + # -O open first + # -x copy to clipboard + # -X copy first URL + # -h help + # -H more help + # -v version + # -V more version ? + # -g generate html file + # -G use this file + # -g -g generate 1 file per tags + # used : AaBbCcDdE-F-GgHh-i----Ll--NnOoPp-q-rSsTt--Vv-wXxY--z---------- + # available : ---------e-f----I-JjKk--Mm------Q-R-----Uu--W----yZ-0123456789 + while getopts ":a:A:b:c:d:G:o:P:q:r:s:t:T:w:x:BCDEFghHilLnNOpPSvVXYz" option; do + case ${option} in + a) + __a=1 + __url="${OPTARG}" + ;; # Add url to bookmark + A) __A="${OPTARG}" ;; # AcceleratoR + b) __b="${OPTARG}" ;; # Bookmark file + B) __B=1 ;; # Don't load the default config file + c) __c="${OPTARG}" ;; # Config file + C) __C=1 ;; # Print color table + d) + __d=1 + __del="${OPTARG}" + ;; # Config file + D) __D=1 ;; # Print color table + E) __E=1 ;; # Open the bm.bmf file with the $EDITOR + F) __F=1 ;; # Force the add or delete or picture + g) ((__g++)) ;; # generate html file(s) + G) + __G=1 + __file="${OPTARG}" + ;; # Generate this file (only for g=1) + h | H) + usage + exit 0 + ;; # Help + i) __i=1 ;; # Ignore case when searching + l) __l=1 ;; # List all bookmarks + L) __L=1 ;; # List all tags + n) __n=1 ;; # sort by date + N) __N=1 ;; # sort by date (reverse) + o) + __o=1 + __open="${OPTARG}" + ;; # Open + O) __O=1 ;; # Open First + p) __p=1 ;; # Take a screenshot + P) + __P=1 + __search="${OPTARG}" + ;; # Take all screenshot + q) + __q=1 + __search="${OPTARG}" + ;; # Search + r) + __r=1 + __search="${OPTARG}" + ;; # Search + s) + __s=1 + __search="${OPTARG}" + ;; # Search + S) __S=1 ;; # Print Statistics + t) __t="${OPTARG}" ;; # tags for a URL + T) __T="${OPTARG}" ;; # Title for a URL + v | V) + echo -n "${0} v${VERSION}" + [[ "${option}" = 'V' ]] && echo -n " [commit: ${RELEASE}]" + echo '' + exit 0 + ;; + w) __w="${OPTARG}" ;; # Working directory + x) + __x=1 + __copy="${OPTARG}" + ;; # Copy + X) __X=1 ;; # Copy First + Y) __Y=1 ;; # Open/copy All + z) __z=1 ;; # Alternative print listing + :) + echo "Missing argument for '-${OPTARG}'" >&2 + exit 1 + ;; + ?) + echo "Argument unknown '-${OPTARG}'" >&2 + exit 1 + ;; # usage;; + *) + echo "Argument unknown '-${option}'" >&2 + exit 1 + ;; # usage;; + esac + done +fi + +# Parsing args is done, Starting to work, so check if binaries are here +checkBinaries + +# defineColors MUST be called before the config() or _BM_PRINT_LINE will be in trouble. +defineColors +# Before loading config, checking if working dir exist... +if [ ! -z "${__w:=}" -a ! -d "${__w}" ]; then + die "Working directory '${__w}' doesn't exist !" >&2 +fi +# Loading config +config "${__c}" +[[ ! -z "${__b:=}" ]] && export _BM_BOOKMARK_FILE="${__b}" + +# Following config, we maybe not have to check the capture tool +[[ ${_BM_CMD_CAPTURE_CHECK} ]] && checkBinaries "${_BM_CMD_CAPTURE}" + +# If no bookmark file exist, create one if allowed +if [ ! -f "${_BM_BOOKMARK_FILE}" -a "${_BM_CREATE_BOOKMARK_FILE,,}" = 'true' ]; then + # Remember fields : 0=md5, 1=date, 2=accel, 3=url, 4=title, 5=tags + echo "eef521de8df447ad392dbace16cf2edc|$(date '+%FT%TZ')|:bm|https://github.com/The-Repo-Club/repomenu-extra/|Download link for repomenu-extra|default,shell,scripts" >>"${_BM_BOOKMARK_FILE}" +fi + +# Starting to work with args. If none probably list... +[[ ${#} -eq 0 && "${_BM_NO_ARGS_FORCE_HELP,,}" = 'true' ]] && { + usage + exit +} + +# Only one action at a time +if [ $((${__a:=0} + ${__C:=0} + ${__d:=0} + ${__E:=0} + ${__g:=0} + ${__l:=0} + ${__L:=0} + ${__o:=0} + ${__P:=0} + ${__q:=0} + ${__r:=0} + ${__s:=0} + ${__S:=0} + ${__x:=0})) -gt 1 ]; then + echo "You have to choose between -a, -C, -d, -E, -g, -l, -L, -o, -P, -q, -r, -s, -S, -x" >&2 + echo "Use -h for help" >&2 + exit 0 +fi +if [ $((${__O:=0} + ${__Y:=0} + ${__X:=0})) -gt 1 ]; then + echo "You have to choose between -O, -X, -Y" >&2 + echo "Use -h for help" >&2 + exit 0 +fi + +# Executing actions +[[ ${__a} -eq 1 ]] && { + saveUrl + exit +} +[[ ${__C} -eq 1 ]] && { + defineColors 1 + exit +} +[[ ${__d} -eq 1 ]] && { + delete_bookmark "${__del}" + exit +} +[[ ${__g} -ge 1 ]] && { + bookmark_generator "${__file:=}" '' + exit +} +[[ ${__l} -eq 1 ]] && { + search + exit +} +[[ ${__L} -eq 1 ]] && { + list_tags + exit +} +[[ ${__o} -eq 1 ]] && { + open_bookmark "${__open}" + exit +} +[[ ${__P} -eq 1 ]] && { + picturize "${__search}" + exit +} +[[ ${__r} -eq 1 ]] && { + recorded_picture "${__search}" + exit +} +[[ ${__q} -eq 1 ]] && { + download_title "${__search}" + exit +} +[[ ${__s} -eq 1 ]] && { + search_bookmarks "${__search}" + exit +} +[[ ${__S} -eq 1 ]] && { + stats + exit +} +[[ ${__x} -eq 1 ]] && { + copy_bookmark "${__copy}" + exit +} +[[ ${__E} -eq 1 ]] && { + edit_bookmark + exit +} +search + +# $Format:%cn @ %cD$ : $Id$ diff --git a/rofi/.local/bin/rofi/bm_add b/rofi/.local/bin/rofi/bm_add new file mode 100755 index 000000000..cb582a220 --- /dev/null +++ b/rofi/.local/bin/rofi/bm_add @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +#-*-coding:utf-8 -*- +#Auto updated? +# Yes +#File: +# bm_add +#Author: +# The-Repo-Club [wayne6324@gmail.com] +#Github: +# https://github.com/The-Repo-Club/ +# +#Created: +# Fri 09 December 2022, 06:43:52 AM [GMT] +#Modified: +# Fri 09 December 2022, 07:33:31 AM [GMT] +# +#Description: +# +# +#Dependencies: +# bm +# + +result() { + echo -n | rofi -dmenu -p "${1:-Add a bookmark:...}" +} + +url="$(result "URL:")" + +if [ -z "$url" ]; then + exit +fi + +title="$(result "Title:")" +tags="$(result "Tags (comma delimited):")" + +bm -w "$HOME/.config/rofi/bookmarks/" -a "$url" -T "$title" -t "$tags" diff --git a/rofi/.local/bin/rofi/bm_remove b/rofi/.local/bin/rofi/bm_remove new file mode 100755 index 000000000..b49a64b69 --- /dev/null +++ b/rofi/.local/bin/rofi/bm_remove @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +#-*-coding:utf-8 -*- +#Auto updated? +# Yes +#File: +# bm_remove +#Author: +# The-Repo-Club [wayne6324@gmail.com] +#Github: +# https://github.com/The-Repo-Club/ +# +#Created: +# Fri 09 December 2022, 06:43:41 AM [GMT] +#Modified: +# Fri 09 December 2022, 07:33:33 AM [GMT] +# +#Description: +# +# +#Dependencies: +# bm +# +# shellcheck disable=all + +bmFile="$HOME/.config/rofi/bookmarks/bm.bmf" + +if [[ ! -f $bmFile ]]; then + printf "%s\n" "No current bookmark file found."; + exit +fi + +declare -A bmarray; + +while IFS=\| read -r md5 date accel url title tags; +do + bookmark="$title-$url-$tags"; + bmarray["$bookmark"]="$url"; +done < "$bmFile" + +function load() { + while IFS=\| read -r md5 date accel url title tags; + do + bookmark="$title-$url-$tags"; + printf "%s\n" "$bookmark"; + done < "$bmFile" + +} + +choice=$(load | rofi -dmenu i -p "${1:-Remove a bookmark:...}") + +LOOPSETTING="true" +while [ -n "$LOOPSETTING" ]; do + [ -n "$choice" ] || exit + unset LOOPSETTING + case "$choice" in + *) bm -w "$HOME/.config/rofi/bookmarks/" -d "${bmarray[$choice]}" -D;; + esac +done diff --git a/rofi/.local/bin/rofi/bm_viewer b/rofi/.local/bin/rofi/bm_viewer new file mode 100755 index 000000000..3508fd2d1 --- /dev/null +++ b/rofi/.local/bin/rofi/bm_viewer @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +#-*-coding:utf-8 -*- +#Auto updated? +# Yes +#File: +# bm_viewer +#Author: +# The-Repo-Club [wayne6324@gmail.com] +#Github: +# https://github.com/The-Repo-Club/ +# +#Created: +# Fri 09 December 2022, 06:43:30 AM [GMT] +#Modified: +# Fri 09 December 2022, 07:36:51 AM [GMT] +# +#Description: +# +# +#Dependencies: +# bm +# +# shellcheck disable=all + +bmFile="$HOME/.config/rofi/bookmarks/bm.bmf" + +if [[ ! -f $bmFile ]]; then + printf "%s\n" "No current bookmark file found."; + exit +fi + +declare -A bmarray; + +function load() { + printf "Add\nRemove\n" + while IFS=\| read -r md5 date accel url title tags; + do + bookmark="$title-$url-$tags"; + printf "%s\n" "$title"; + done < "$bmFile" +} + +choice=$(load | rofi -dmenu) + +LOOPSETTING="true" +while [ -n "$LOOPSETTING" ]; do + [ -n "$choice" ] || exit + unset LOOPSETTING + case "$choice" in + Add) bm_add ;; + Remove) bm_remove ;; + *) bm -w "$HOME/.config/rofi/bookmarks/" -o "${bmarray[$choice]}";; + esac +done diff --git a/rofi/.local/bin/rofi/menu b/rofi/.local/bin/rofi/menu new file mode 100755 index 000000000..3f977c75b --- /dev/null +++ b/rofi/.local/bin/rofi/menu @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +#-*-coding:utf-8 -*- +#Auto updated? +# Yes +#File: +# menu +#Author: +# The-Repo-Club [wayne6324@gmail.com] +#Github: +# https://github.com/The-Repo-Club/ +# +#Created: +# Wed 10 March 2021, 12:34:47 PM [GMT+1] +#Modified: +# Thu 08 December 2022, 07:37:35 AM [GMT] +# +#Description: +# +# +#Dependencies: +# rofi +# +# shellcheck disable=all + +programs=$(compgen -c | sort -u | tail -n +9) +cmd=$(echo -e "$programs" | rofi -dmenu -p "Programs" -mesg "Select a program.") +exec $cmd \ No newline at end of file diff --git a/rofi/.local/bin/rofi/music b/rofi/.local/bin/rofi/music new file mode 100755 index 000000000..fd516b622 --- /dev/null +++ b/rofi/.local/bin/rofi/music @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +# -*-coding:utf-8 -*- +# Auto updated? +# Yes +#File : +# repomenue_music +#Author: +# The-Repo-Club [wayne6324@gmail.com] +#Github: +# https://github.com/The-Repo-Club/ +# +# Created: +# Wed 10 March 2021, 12:34:47 PM [GMT] +# Modified: +# Wed 07 December 2022, 11:01:08 PM [GMT] +# +# Description: +# +# +# shellcheck disable=all + +MUSICPLAYER="ffplay -nodisp" +PLAYING_MSG=" Currently Playing :: " + +for Song in "$HOME/Music/"*; do + if [ -f "$Song" ]; then + Name=${Song##*/} + case $Name in + *.mp3 | *.flac | *.wav | .ogg) + options+=${Song##*/}$'\n' + ;; + esac + fi +done +CHOICE=$(rofi -dmenu -no-custom -p "Muisc Player" -kb-custom-1 "Alt+Return" -mesg "${PLAYING_MSG}" "$@" < +basecommand="$0" + +# set default settings +_rofi () { + rofi -no-auto-select -i "$@" +} + +_pwgen () { + pwgen -y "$@" +} + +_image_viewer () { + feh - +} + +_clip_in_primary() { + xclip +} + +_clip_in_clipboard() { + xclip -selection clipboard +} + +_clip_out_primary() { + xclip -o +} + +_clip_out_clipboard() { + xclip --selection clipboard -o +} + + +config_dir=${XDG_CONFIG_HOME:-$HOME/.config} +cache_dir=${XDG_CACHE_HOME:-$HOME/.cache} + +# We expect to find these fields in pass(1)'s output +URL_field='url' +USERNAME_field='user' +AUTOTYPE_field='autotype' +OTPmethod_field='otp_method' + +default_autotype="user :tab pass" +delay=2 +wait=0.2 +xdotool_delay=12 +default_do='menu' # menu, copyPass, typeUser, typePass, copyUser, copyUrl, viewEntry, typeMenu, actionMenu, copyMenu, openUrl +auto_enter='false' +notify='false' +help_color="" +clip=primary +clip_clear=45 +default_user="${ROFI_PASS_DEFAULT_USER-$(whoami)}" +default_user2=john_doe +password_length=12 +fix_layout=false + +# default shortcuts +autotype="Alt+1" +type_user="Alt+2" +type_pass="Alt+3" +open_url="Alt+4" +copy_name="Alt+u" +copy_url="Alt+l" +copy_pass="Alt+p" +show="Alt+o" +copy_menu="Alt+c" +action_menu="Alt+a" +type_menu="Alt+t" +help="Alt+h" +switch="Alt+x" +insert_pass="Alt+n" +qrcode="Alt+q" +previous_root="Shift+Left" +next_root="Shift+Right" + +# Safe permissions +umask 077 + +has_qrencode() { + command -v qrencode >/dev/null 2>&1 +} + +listgpg () { + mapfile -d '' pw_list < <(find -L . -name '*.gpg' -print0) + pw_list=("${pw_list[@]#./}") + printf '%s\n' "${pw_list[@]}" | sort -n +} + +# get all password files and output as newline-delimited text +list_passwords() { + cd "${root}" || exit + mapfile -t pw_list < <(listgpg) + printf '%s\n' "${pw_list[@]%.gpg}" | sort -n +} + +doClip () { + case "$clip" in + "primary") _clip_in_primary ;; + "clipboard") _clip_in_clipboard ;; + "both") _clip_in_primary; _clip_out_primary | _clip_in_clipboard;; + esac +} + +checkIfPass () { + printf '%s\n' "${root}: $selected_password" >| "$cache_dir/passmenu/last_used" +} + + +autopass () { + x_repeat_enabled=$(xset q | awk '/auto repeat:/ {print $3}') + xset r off + + rm -f "$cache_dir/passmenu/last_used" + printf '%s\n' "${root}: $selected_password" > "$cache_dir/passmenu/last_used" + for word in ${stuff["$AUTOTYPE_field"]}; do + case "$word" in + ":tab") xdotool key Tab;; + ":space") xdotool key space;; + ":delay") sleep "${delay}";; + ":enter") xdotool key Return;; + ":otp") printf '%s' "$(generateOTP)" | xdotool type --delay ${xdotool_delay} --clearmodifiers --file -;; + "pass") printf '%s' "${password}" | xdotool type --delay ${xdotool_delay} --clearmodifiers --file -;; + "path") printf '%s' "${selected_password}" | rev | cut -d'/' -f1 | rev | xdotool type --clearmodifiers --file -;; + *) printf '%s' "${stuff[${word}]}" | xdotool type --delay ${xdotool_delay} --clearmodifiers --file -;; + esac + done + + if [[ ${auto_enter} == "true" ]]; then + xdotool key Return + fi + + xset r "$x_repeat_enabled" + unset x_repeat_enabled + clearUp +} + +generateQrCode() { + has_qrencode + + if [[ $? -eq "1" ]]; then + printf '%s\n' "qrencode not found" | _rofi -dmenu + exit_code=$? + if [[ $exit_code -eq "1" ]]; then + exit + else + "${basecommand}" + fi + fi + + checkIfPass + pass "$selected_password" | head -n 1 | qrencode -d 300 -v 8 -l H -o - | _image_viewer + if [[ $? -eq "1" ]]; then + printf '%s\n' "" | _rofi -dmenu -mesg "Image viewer not defined or cannot read from pipe" + exit_value=$? + if [[ $exit_value -eq "1" ]]; then + exit + else + "${basecommand}" + fi + fi + clearUp +} + +openURL () { + checkIfPass + $BROWSER "$(PASSWORD_STORE_DIR="${root}" pass "$selected_password" | grep "${URL_field}: " | gawk '{sub(/:/,"")}{print $2}1' | head -1)"; exit; + clearUp +} + +typeUser () { + checkIfPass + + x_repeat_enabled=$(xset q | awk '/auto repeat:/ {print $3}') + xset r off + + printf '%s' "${stuff[${USERNAME_field}]}" | xdotool type --delay ${xdotool_delay} --clearmodifiers --file - + + xset r "$x_repeat_enabled" + unset x_repeat_enabled + + clearUp +} + +typePass () { + checkIfPass + + x_repeat_enabled=$(xset q | awk '/auto repeat:/ {print $3}') + xset r off + + printf '%s' "${password}" | xdotool type --delay ${xdotool_delay} --clearmodifiers --file - + + if [[ $notify == "true" ]]; then + if [[ "${stuff[notify]}" == "false" ]]; then + : + else + notify-send "passmenu" "finished typing password"; + fi + elif [[ $notify == "false" ]]; then + if [[ "${stuff[notify]}" == "true" ]]; then + notify-send "passmenu" "finished typing password"; + else + : + fi + fi + + xset r "$x_repeat_enabled" + unset x_repeat_enabled + clearUp +} + +typeField () { + checkIfPass + local to_type + + x_repeat_enabled=$(xset q | awk '/auto repeat:/ {print $3}') + xset r off + + case $typefield in + "OTP") to_type="$(generateOTP)" ;; + *) to_type="${stuff[${typefield}]}" ;; + esac + + printf '%s' "$to_type" | xdotool type --delay ${xdotool_delay} --clearmodifiers --file - + + xset r "$x_repeat_enabled" + unset x_repeat_enabled + unset to_type + + clearUp +} + +generateOTP () { + checkIfPass + + # First, we check if there is a non-conventional OTP command in the pass file + if PASSWORD_STORE_DIR="${root}" pass "$selected_password" | grep -q "${OTPmethod_field}: "; then + # We execute the commands after otp_method: AS-IS + bash -c "$(PASSWORD_STORE_DIR="${root}" pass "$selected_password" | grep "${OTPmethod_field}: " | cut -d' ' -f2-)" + else + # If there is no method defined, fallback to pass-otp + PASSWORD_STORE_DIR="${root}" pass otp "$selected_password" + fi + + clearUp +} + +copyUser () { + checkIfPass + printf '%s' "${stuff[${USERNAME_field}]}" | doClip + clearUp +} + +copyField () { + checkIfPass + printf '%s' "${stuff[${copyfield}]}" | doClip + clearUp +} + +copyURL () { + checkIfPass + printf '%s' "${stuff[${URL_field}]}" | doClip + clearUp +} + +copyPass () { + checkIfPass + printf '%s' "$password" | doClip + if [[ $notify == "true" ]]; then + notify-send "passmenu" "Copied Password\\nClearing in $clip_clear seconds" + fi + + if [[ $notify == "true" ]]; then + (sleep $clip_clear; printf '%s' "" | _clip_in_primary; printf '%s' "" | _clip_in_clipboard | notify-send "passmenu" "Clipboard cleared") & + elif [[ $notify == "false" ]]; then + (sleep $clip_clear; printf '%s' "" | _clip_in_primary; printf '%s' "" | _clip_in_clipboard) & + fi +} + +viewEntry () { + checkIfPass + showEntry "${selected_password}" +} + +generatePass () { + askmenu_content=( + "Yes" + "No" + ) + + askGenMenu=$(printf '%s\n' "${askmenu_content[@]}" | _rofi -dmenu -p "Generate new Password for ${selected_password}? > ") + askgen_exit=$? + + if [[ $askgen_exit -eq 1 ]]; then + exit + fi + if [[ $askGenMenu == "Yes" ]]; then + true + elif [[ $askGenMenu == "No" ]]; then + actionMenu + fi + + checkIfPass + + symbols_content=( + "0 Cancel" + "1 Yes" + "2 No" + ) + + symbols=$(printf '%s\n' "${symbols_content[@]}" | _rofi -dmenu -p "Use Symbols? > ") + symbols_val=$? + + if [[ $symbols_val -eq 1 ]]; then + exit + fi + if [[ $symbols == "0 Cancel" ]]; then + mainMenu; + elif [[ $symbols == "1 Yes" ]]; then + symbols=""; + elif [[ $symbols == "2 No" ]]; then + symbols="-n"; + fi + + HELP="Enter Number or hit Enter to use default length" + length=$(printf '%s' "" | _rofi -dmenu -mesg "${HELP}" -p "Password length? (Default: ${password_length}) > ") + length_exit=$? + + if [[ $length_exit -eq 1 ]]; then + exit + fi + if [[ $length == "" ]]; then + PASSWORD_STORE_DIR="${root}" pass generate ${symbols} -i "$selected_password" "${password_length}" > /dev/null; + else + PASSWORD_STORE_DIR="${root}" pass generate ${symbols} -i "$selected_password" "${length}" > /dev/null; + fi +} + +# main Menu +mainMenu () { + if [[ $1 == "--bmarks" ]]; then + selected_password="$(list_passwords 2>/dev/null \ + | _rofi -mesg "Bookmarks Mode. ${switch} to switch" \ + -dmenu \ + -kb-custom-10 "${switch}" \ + -select "$entry" \ + -p "passmenu > ")" + + rofi_exit=$? + + if [[ $rofi_exit -eq 1 ]]; then + exit + elif [[ $rofi_exit -eq 19 ]]; then + ${basecommand} + elif [[ $rofi_exit -eq 0 ]]; then + openURL + fi + else + unset selected_password + + args=( -dmenu + -kb-custom-1 "${autotype}" + -kb-custom-2 "${type_user}" + -kb-custom-3 "${type_pass}" + -kb-custom-4 "${open_url}" + -kb-custom-5 "${copy_name}" + -kb-custom-6 "${copy_pass}" + -kb-custom-7 "${show}" + -kb-custom-8 "${copy_url}" + -kb-custom-9 "${type_menu}" + -kb-custom-10 "${previous_root}" + -kb-custom-11 "${next_root}" + -kb-custom-14 "${action_menu}" + -kb-custom-15 "${copy_menu}" + -kb-custom-16 "${help}" + -kb-custom-17 "${switch}" + -kb-custom-18 "${insert_pass}" + -kb-custom-19 "${qrcode}" + ) + args+=( -kb-mode-previous "" # These keyboard shortcut options are needed, because + -kb-mode-next "" # Shift+ are otherwise taken by rofi. + -select "$entry" + -mesg "PW Store: ${root}" + -p "passmenu > " ) + + selected_password="$(list_passwords 2>/dev/null | _rofi "${args[@]}")" + + rofi_exit=$? + if [[ $rofi_exit -eq 1 ]]; then + exit + fi + + # Actions based on exit code, which do not need the entry. + # The exit code for -kb-custom-X is X+9. + case "${rofi_exit}" in + 19) roots_index=$(( (roots_index-1+roots_length) % roots_length)); root=${roots[$roots_index]}; mainMenu; return;; + 20) roots_index=$(( (roots_index+1) % roots_length)); root=${roots[$roots_index]}; mainMenu; return;; + 25) helpMenu; return;; + 26) ${basecommand} --bmarks; return;; + esac + + mapfile -t password_temp < <(PASSWORD_STORE_DIR="${root}" pass show "$selected_password") + password=${password_temp[0]} + + if [[ ${password} == "#FILE="* ]]; then + pass_file="${password#*=}" + mapfile -t password_temp2 < <(PASSWORD_STORE_DIR="${root}" pass show "${pass_file}") + password=${password_temp2[0]} + fi + + fields=$(printf '%s\n' "${password_temp[@]:1}" | awk '$1 ~ /:$/ || /otpauth:\/\// {$1=$1;print}') + declare -A stuff + stuff["pass"]=${password} + + if [[ -n $fields ]]; then + while read -r LINE; do + unset _id _val + case "$LINE" in + "otpauth://"*|"${OTPmethod_field}"*) + _id="OTP" + _val="" + ;; + *) + _id="${LINE%%: *}" + _val="${LINE#* }" + ;; + esac + + if [[ -n "$_id" ]]; then + stuff["${_id}"]=${_val} + fi + done < <(printf '%s\n' "${fields}") + + if test "${stuff['autotype']+autotype}"; then + : + else + stuff["autotype"]="${USERNAME_field} :tab pass" + fi + fi + fi + + if [[ -z "${stuff["${AUTOTYPE_field}"]}" ]]; then + if [[ -n $default_autotype ]]; then + stuff["${AUTOTYPE_field}"]="${default_autotype}" + fi + fi + if [[ -z "${stuff["${USERNAME_field}"]}" ]]; then + if [[ -n $default_user ]]; then + if [[ "$default_user" == ":filename" ]]; then + stuff["${USERNAME_field}"]="$(basename "$selected_password")" + else + stuff["${USERNAME_field}"]="${default_user}" + fi + fi + fi + pass_content="$(for key in "${!stuff[@]}"; do printf '%s\n' "${key}: ${stuff[$key]}"; done)" + + # actions based on keypresses + # The exit code for -kb-custom-X is X+9. + case "${rofi_exit}" in + 0) typeMenu;; + 10) sleep $wait; autopass;; + 11) sleep $wait; typeUser;; + 12) sleep $wait; typePass;; + 13) openURL;; + 14) copyUser;; + 15) copyPass;; + 16) viewEntry;; + 17) copyURL;; + 18) default_do="menu" typeMenu;; + 23) actionMenu;; + 24) copyMenu;; + 27) insertPass;; + 28) generateQrCode;; + esac + clearUp +} + + +clearUp () { + password='' + selected_password='' + unset stuff + unset password + unset selected_password + unset password_temp + unset stuff +} + +helpMenu () { + _rofi -dmenu -mesg "Hint: All hotkeys are configurable in config file" -p "Help > " <<- EOM + ${autotype}: Autotype + ${type_user}: Type Username + ${type_pass}: Type Password + ${qrcode}: Generate and display qrcode + --- + ${copy_name}: Copy Username + ${copy_pass}: Copy Password + ${copy_url}: Copy URL + ${open_url}: Open URL + ${copy_menu}: Copy Custom Field + --- + ${action_menu}: Edit, Move, Delete, Re-generate Submenu + ${show}: Show Password File + ${insert_pass}: Insert new Pass Entry + ${switch}: Switch Pass/Bookmark Mode + --- + ${previous_root}: Switch to previous password store (--root) + ${next_root}: Switch to next password store (--root) +EOM +help_val=$? + +if [[ $help_val -eq 1 ]]; then + exit; +else + unset helptext; mainMenu; +fi +} + + +typeMenu () { + if [[ -n $default_do ]]; then + if [[ $default_do == "menu" ]]; then + checkIfPass + local -a keys=("${!stuff[@]}") + keys=("${keys[@]/$AUTOTYPE_field}") + typefield=$({ printf '%s' "${AUTOTYPE_field}" ; printf '%s\n' "${keys[@]}" | sort; } | _rofi -dmenu -p "Choose Field to type > ") + typefield_exit=$? + if [[ $typefield_exit -eq 1 ]]; then + exit + fi + case "$typefield" in + '') exit;; + 'pass') sleep $wait; typePass;; + "${AUTOTYPE_field}") sleep $wait; autopass;; + *) sleep $wait; typeField + esac + clearUp + elif [[ $default_do == "${AUTOTYPE_field}" ]]; then + sleep $wait; autopass + else + ${default_do} + fi + fi +} + +copyMenu () { + checkIfPass + copyfield=$(printf '%s\n' "${!stuff[@]}" | sort | _rofi -dmenu -p "Choose Field to copy > ") + val=$? + if [[ $val -eq 1 ]]; then + exit; + fi + if [[ $copyfield == "pass" ]]; then + copyPass; + else + copyField + fi + clearUp +} + +actionMenu () { + checkIfPass + action_content=("< Return" + "---" + "1 Move Password File" + "2 Copy Password File" + "3 Delete Password File" + "4 Edit Password File" + "5 Generate New Password" + ) + + action=$(printf '%s\n' "${action_content[@]}" | _rofi -dmenu -p "Choose Action > ") + if [[ ${action} == "1 Move Password File" ]]; then + manageEntry move; + elif [[ ${action} == "3 Delete Password File" ]]; then + manageEntry delete; + elif [[ ${action} == "2 Copy Password File" ]]; then + manageEntry copy; + elif [[ ${action} == "4 Edit Password File" ]]; then + manageEntry edit; + elif [[ ${action} == "5 Generate New Password" ]]; then + generatePass; + elif [[ ${action} == "< Return" ]]; then + mainMenu; + elif [[ ${action} == "" ]]; then + exit + fi +} + +showEntry () { + if [[ -z $pass_content ]]; then + pass_temp=$(PASSWORD_STORE_DIR="${root}" pass show "$selected_password") + password="${pass_temp%%$'\n'*}" + pass_key_value=$(printf '%s\n' "${pass_temp}" | tail -n+2 | grep ': ') + declare -A stuff + + while read -r LINE; do + _id="${LINE%%: *}" + _val="${LINE#* }" + stuff["${_id}"]=${_val} + done < <(printf '%s\n' "${pass_key_value}") + + stuff["pass"]=${password} + + if test "${stuff['autotype']+autotype}"; then + : + else + stuff["autotype"]="${USERNAME_field} :tab pass" + fi + + pass_content="$(for key in "${!stuff[@]}"; do printf '%s\n' "${key}: ${stuff[$key]}"; done)" + fi + + bla_content=("< Return" + "${pass_content}" + ) + + bla=$(printf '%s\n' "${bla_content[@]}" | _rofi -dmenu -mesg "Enter: Copy entry to clipboard" -p "> ") + rofi_exit=$? + + word=$(printf '%s' "$bla" | gawk -F': ' '{print $1}') + + if [[ ${rofi_exit} -eq 1 ]]; then + exit + elif [[ ${rofi_exit} -eq 0 ]]; then + if [[ ${bla} == "< Return" ]]; then + mainMenu + else + if [[ -z $(printf '%s' "${stuff[${word}]}") ]]; then + printf '%s' "$word" | doClip + else + printf '%s' "${stuff[${word}]}" | doClip + fi + if [[ $notify == "true" ]]; then + notify-send "passmenu" "Copied Password\\nClearing in $clip_clear seconds" + fi + if [[ $notify == "true" ]]; then + (sleep $clip_clear; printf '%s' "" | _clip_in_primary; printf '%s' "" | _clip_in_clipboard | notify-send "passmenu" "Clipboard cleared") & + elif [[ $notify == "false" ]]; then + (sleep $clip_clear; printf '%s' "" | _clip_in_primary; printf '%s' "" | _clip_in_clipboard) & + fi + exit + fi + fi + exit + unset stuff + unset password + unset selected_password + unset password_temp + unset stuff + exit +} + +manageEntry () { + if [[ "$1" == "edit" ]]; then + EDITOR=$EDITOR PASSWORD_STORE_DIR="${root}" pass edit "${selected_password}" + mainMenu + elif [[ $1 == "move" ]]; then + cd "${root}" || exit + group_array=(*/) + group=$(printf '%s\n' "${group_array[@]%/}" | _rofi -dmenu -p "Choose Group > ") + if [[ $group == "" ]]; then + exit + fi + PASSWORD_STORE_DIR="${root}" pass mv "$selected_password" "${group}" + mainMenu + elif [[ $1 == "copy" ]]; then + cd "${root}" || exit + group_array=(*/) + group=$(printf '%s\n' "${group_array[@]%/}" | _rofi -dmenu -p "Choose Group > ") + if [[ $group == "" ]]; then + exit + else + new_name="$(listgpg | _rofi -dmenu -format 'f' -mesg "Copying to same Group. Please enter a name for the new entry" -p "> ")" + fi + PASSWORD_STORE_DIR="${root}" pass cp "$selected_password" "${group}/${new_name}" + mainMenu + elif [[ "$1" == "delete" ]]; then + HELP="Selected entry: ${selected_password}" + ask_content=("Yes" + "No" + ) + ask=$(printf '%s\n' "${ask_content[@]}" | _rofi -mesg "${HELP}" -dmenu -p "Are You Sure? > ") + if [[ "$ask" == "Yes" ]]; then + PASSWORD_STORE_DIR="${root}" pass rm --force "${selected_password}" + elif [[ "$ask" == "No" ]]; then + mainMenu + elif [[ -z "$ask" ]]; then + exit + fi + else + mainMenu + fi +} + +edit_pass() { + if [[ $edit_new_pass == "true" ]]; then + PASSWORD_STORE_DIR="${root}" pass edit "${1}" + fi +} + +insertPass () { + url=$(_clip_out_clipboard) + + if [[ "${url:0:4}" == "http" ]]; then + domain_name="$(printf '%s\n' "${url}" | awk -F / '{l=split($3,a,"."); print (a[l-1]=="com"?a[l-2] OFS:X) a[l-1] OFS a[l]}' OFS=".")" + help_content="Domain: ${domain_name} + Type name, make sure it is unique" + else + help_content="Hint: Copy URL to clipboard before calling this menu. + Type name, make sure it is unique" + fi + + cd "${root}" || exit + group_array=(*/) + grouplist=$(printf '%s\n' "${group_array[@]%/}") + name="$(listgpg | _rofi -dmenu -format 'f' -filter "${domain_name}" -mesg "${help_content}" -p "> ")" + val=$? + + if [[ $val -eq 1 ]]; then + exit + fi + + user_content=("${default_user2}" + "${USER}" + "${default_user}" + ) + + user=$(printf '%s\n' "${user_content[@]}" | _rofi -dmenu -mesg "Chose Username or type" -p "> ") + val=$? + + if [[ $val -eq 1 ]]; then + exit + fi + + group_content=("No Group" + "---" + "${grouplist}" + ) + + group=$(printf '%s\n' "${group_content[@]}" | _rofi -dmenu -p "Choose Group > ") + val=$? + + if [[ $val -eq 1 ]]; then + exit + fi + + pw=$(printf '%s' "Generate" | _rofi -dmenu -password -p "Password > " -mesg "Type Password or hit Enter to generate one") + + if [[ $pw == "Generate" ]]; then + pw=$(_pwgen "${password_length}") + fi + + clear + + if [[ "$group" == "No Group" ]]; then + if [[ $url == http* ]]; then + pass_content=("${pw}" + "---" + "${USERNAME_field}: ${user}" + "${URL_field}: ${url}" + ) + printf '%s\n' "${pass_content[@]}" | PASSWORD_STORE_DIR="${root}" pass insert -m "${name}" > /dev/null && edit_pass "${name}" + else + pass_content=("${pw}" + "---" + "${USERNAME_field}: ${user}" + ) + printf '%s\n' "${pass_content[@]}" | PASSWORD_STORE_DIR="${root}" pass insert -m "${name}" > /dev/null && edit_pass "${name}" + fi + else + if [[ $url == http* ]]; then + pass_content=("${pw}" + "---" + "${USERNAME_field}: ${user}" + "${URL_field}: ${url}" + ) + printf '%s\n' "${pass_content[@]}" | PASSWORD_STORE_DIR="${root}" pass insert -m "${group}/${name}" > /dev/null && edit_pass "${group}/${name}" + else + pass_content=("${pw}" + "---" + "${USERNAME_field}: ${user}" + ) + printf '%s\n' "${pass_content[@]}" | PASSWORD_STORE_DIR="${root}" pass insert -m "${group}/${name}" > /dev/null && edit_pass "${group}/${name}" + fi + fi +} + +help_msg () { + cat <<'EOF' + Usage: + passmenu [command] + + Commands: + --insert insert new entry to password store + --root set custom root directories (colon separated) + --last-used highlight last used item + --show-last show details of last used Entry + --bmarks start in bookmarks mode + + passmenu version 1.0.0 +EOF +} + +get_config_file () { + configs=("$ROFI_PASS_CONFIG" + "$config_dir/rofi/passmenu/config" + "/etc/passmenu.conf") + + # return the first config file with a valid path + for config in "${configs[@]}"; do + # '-n' is needed in case ROFI_PASS_CONFIG is not set + if [[ -n "${config}" && -f "${config}" ]]; then + printf "%s" "$config" + return + fi + done +} + +main () { + # load config file + config_file="$(get_config_file)" + [[ -n "$config_file" ]] && source "$config_file" + + # create tmp dir + if [[ ! -d "$cache_dir/passmenu" ]]; then + mkdir -p "$cache_dir/passmenu" + fi + + # fix keyboard layout if enabled in config + if [[ $fix_layout == "true" ]]; then + layout_cmd + fi + + # set help color + if [[ $help_color == "" ]]; then + help_color=$(rofi -dump-xresources | grep 'rofi.color.normal' | gawk -F ',' '/,/{gsub(/ /, "", $2); print $2}') + fi + + # check for BROWSER variable, use xdg-open as fallback + if [[ -z $BROWSER ]]; then + export BROWSER=xdg-open + fi + + # check if alternative root directory was given on commandline + if [[ -r "$cache_dir/passmenu/last_used" ]] && [[ $1 == "--last-used" || $1 == "--show-last" ]]; then + roots=("$(awk -F ': ' '{ print $1 }' "$cache_dir/passmenu/last_used")") + elif [[ -n "$2" && "$1" == "--root" ]]; then + custom_root=true; IFS=: read -r -a roots <<< "$2" + elif [[ -n $root ]]; then + custom_root=true; IFS=: read -r -a roots <<< "${root}" + elif [[ -n ${PASSWORD_STORE_DIR} ]]; then + roots=("${PASSWORD_STORE_DIR}") + else + roots=("$HOME/.password-store") + fi + roots_index=0 + roots_length=${#roots[@]} + export root=${roots[$roots_index]} + export PASSWORD_STORE_DIR="${root}" + case $1 in + --insert) + insertPass + ;; + --root) + mainMenu + ;; + --help) + help_msg + ;; + --last-used) + if [[ -r "$cache_dir/passmenu/last_used" ]]; then + entry="$(awk -F ': ' '{ print $2 }' "$cache_dir/passmenu/last_used")" + fi + mainMenu + ;; + --show-last) + if [[ -r "$cache_dir/passmenu/last_used" ]]; then + selected_password="$(awk -F ': ' '{ print $2 }' "$cache_dir/passmenu/last_used")" viewEntry + else + mainMenu + fi + ;; + --bmarks) + mainMenu --bmarks; + ;; + *) + mainMenu + ;; + esac +} + +main "$@" diff --git a/rofi/.local/bin/rofi/powermenu b/rofi/.local/bin/rofi/powermenu new file mode 100755 index 000000000..2a9d0b87e --- /dev/null +++ b/rofi/.local/bin/rofi/powermenu @@ -0,0 +1,292 @@ +#!/usr/bin/env bash +#-*-coding:utf-8 -*- +#Auto updated? +# Yes +#File: +# powermenu +#Author: +# The-Repo-Club [wayne6324@gmail.com] +#Github: +# https://github.com/The-Repo-Club/ +# +#Created: +# Wed 07 December 2022, 11:01:52 PM [GMT] +#Modified: +# Wed 07 December 2022, 11:02:38 PM [GMT] +# +#Description: +# This script defines just a mode for rofi instead of being a self-contained +# executable that launches rofi by itself. This makes it more flexible than +# running rofi inside this script as now the user can call rofi as one pleases. +# For instance: +# +# rofi -show powermenu -modi powermenu:~/.config/rofi/powermenu +# +#Dependencies: +# +# +# shellcheck disable=all + +if [ -z "${ROFI_OUTSIDE}" ] +then + echo "run this script in rofi". + exit +fi + +set -e +set -u + +# All supported choices +all=(shutdown reboot suspend hibernate logout lockscreen) + +# By default, show all (i.e., just copy the array) +show=("${all[@]}") + +declare -A texts +texts[lockscreen]="lock screen" +texts[switchuser]="switch user" +texts[logout]="log out" +texts[suspend]="suspend" +texts[hibernate]="hibernate" +texts[reboot]="reboot" +texts[shutdown]="shut down" + +declare -A icons +icons[lockscreen]="\uf023" +icons[switchuser]="\uf518" +icons[logout]="\uf842" +icons[suspend]="\uf9b1" +icons[hibernate]="\uf7c9" +icons[reboot]="\ufc07" +icons[shutdown]="\uf011" +icons[cancel]="\u00d7" + +declare -A actions +actions[lockscreen]="multimonitorlock -l -- --time-str=\"%I:%M:%S %p\"" +actions[logout]="pkill -KILL -u ${USER}" +actions[suspend]="systemctl suspend" +actions[hibernate]="systemctl hibernate" +actions[reboot]="systemctl reboot" +actions[shutdown]="systemctl poweroff" + +# By default, ask for confirmation for actions that are irreversible +confirmations=(reboot shutdown logout) + +# By default, no dry run +dryrun=false +showsymbols=true + +getuptime() { + uptime -p >/dev/null 2>&1 + + if [ "$?" -eq 0 ]; then + # Supports most Linux distro + # when the machine is up for less than '0' minutes then + # 'uptime -p' returns ONLY 'up', so we need to set a default value + UP_SET_OR_EMPTY=$(uptime -p | awk -F 'up ' '{print $2}') + UP=${UP_SET_OR_EMPTY:-'less than a minute'} + else + # Supports Mac OS X, Debian 7, etc + UP=$(uptime | sed -E 's/^[^,]*up *//; s/mins/minutes/; s/hrs?/hours/; + s/([[:digit:]]+):0?([[:digit:]]+)/\1 hours, \2 minutes/; + s/^1 hours/1 hour/; s/ 1 hours/ 1 hour/; + s/min,/minutes,/; s/ 0 minutes,/ less than a minute,/; s/ 1 minutes/ 1 minute/; + s/ / /; s/, *[[:digit:]]* users?.*//') + fi + + state="$UP" +} + +function check_valid { + option="$1" + shift 1 + for entry in "${@}" + do + if [ -z "${actions[$entry]+x}" ] + then + echo "Invalid choice in $option: $entry" >&2 + exit 1 + fi + done +} + +# Parse command-line options +parsed=$(getopt --options=h --longoptions=help,dry-run,confirm:,choices:,choose:,symbols,no-symbols --name "$0" -- "$@") +if [ $? -ne 0 ]; then + echo 'Terminating...' >&2 + exit 1 +fi +eval set -- "$parsed" +unset parsed +while true; do + case "$1" in + "-h"|"--help") + echo "rofi-power-menu - a power menu mode for Rofi" + echo + echo "Usage: rofi-power-menu [--choices CHOICES] [--confirm CHOICES]" + echo " [--choose CHOICE] [--dry-run] [--symbols|--no-symbols]" + echo + echo "Use with Rofi in script mode. For instance, to ask for shutdown or reboot:" + echo + echo " rofi -show menu -modi \"menu:rofi-power-menu --choices=shutdown/reboot\"" + echo + echo "Available options:" + echo " --dry-run Don't perform the selected action but print it to stderr." + echo " --choices CHOICES Show only the selected choices in the given order. Use / " + echo " as the separator. Available choices are lockscreen, logout," + echo " suspend, hibernate, reboot and shutdown. By default, all" + echo " available choices are shown." + echo " --confirm CHOICES Require confirmation for the gives choices only. Use / as" + echo " the separator. Available choices are lockscreen, logout," + echo " suspend, hibernate, reboot and shutdown. By default, only" + echo " irreversible actions logout, reboot and shutdown require" + echo " confirmation." + echo " --choose CHOICE Preselect the given choice and only ask for a confirmation" + echo " (if confirmation is set to be requested). It is strongly" + echo " recommended to combine this option with --confirm=CHOICE" + echo " if the choice wouldn't require confirmation by default." + echo " Available choices are lockscreen, logout, suspend," + echo " hibernate, reboot and shutdown." + echo " --[no-]symbols Show Unicode symbols or not. Requires a font with support" + echo " for the symbols. Use, for instance, fonts from the" + echo " Nerdfonts collection. By default, they are shown" + echo " -h,--help Show this help text." + exit 0 + ;; + "--dry-run") + dryrun=true + shift 1 + ;; + "--confirm") + IFS='/' read -ra confirmations <<< "$2" + check_valid "$1" "${confirmations[@]}" + shift 2 + ;; + "--choices") + IFS='/' read -ra show <<< "$2" + check_valid "$1" "${show[@]}" + shift 2 + ;; + "--choose") + # Check that the choice is valid + check_valid "$1" "$2" + selectionID="$2" + shift 2 + ;; + "--symbols") + showsymbols=true + shift 1 + ;; + "--no-symbols") + showsymbols=false + shift 1 + ;; + "--") + shift + break + ;; + *) + echo "Internal error" >&2 + exit 1 + ;; + esac +done + +# Define the messages after parsing the CLI options so that it is possible to +# configure them in the future. + +function write_message { + icon="$1" + text="$2" + if [ "$showsymbols" = "true" ] + then + echo -n "\u200e$icon \u2068$text\u2069" + else + echo -n "$text" + fi +} + +function print_selection { + echo -e "$1" | $(read -r -d '' entry; echo "echo $entry") +} + +declare -A messages +declare -A confirmationMessages +for entry in "${all[@]}" +do + messages[$entry]=$(write_message "${icons[$entry]}" "${texts[$entry]^}") +done +for entry in "${all[@]}" +do + confirmationMessages[$entry]=$(write_message "${icons[$entry]}" "Yes, ${texts[$entry]}") +done +confirmationMessages[cancel]=$(write_message "${icons[cancel]}" "No, cancel") + +if [ $# -gt 0 ] +then + # If arguments given, use those as the selection + selection="${*}" +else + # Otherwise, use the CLI passed choice if given + if [ -n "${selectionID+x}" ] + then + selection="${messages[$selectionID]}" + fi +fi + +# Don't allow custom entries +echo -e "\0no-custom\x1ftrue" + +echo -e "\0markup-rows\x1ftrue" + +if [ -z "${selection+x}" ] +then + getuptime + echo -e "\0prompt\x1fPower Menu" + echo -en "\0message\x1f\tUptime :: ${state^}\n" + for entry in "${show[@]}" + do + echo -e "${messages[$entry]}\0icon\x1f${icons[$entry]}" + done +else + for entry in "${show[@]}" + do + if [ "$selection" = "$(print_selection "${messages[$entry]}")" ] + then + # Check if the selected entry is listed in confirmation requirements + for confirmation in "${confirmations[@]}" + do + if [ "$entry" = "$confirmation" ] + then + # Ask for confirmation + echo -e "\0prompt\x1fAre you sure" + echo -e "${confirmationMessages[$entry]}\0icon\x1f${icons[$entry]}" + echo -e "${confirmationMessages[cancel]}\0icon\x1f${icons[cancel]}" + exit 0 + fi + done + # If not, then no confirmation is required, so mark confirmed + selection=$(print_selection "${confirmationMessages[$entry]}") + fi + if [ "$selection" = "$(print_selection "${confirmationMessages[$entry]}")" ] + then + if [ $dryrun = true ] + then + # Tell what would have been done + echo "Selected: $entry" >&2 + else + # Perform the action + ${actions[$entry]} + fi + exit 0 + fi + if [ "$selection" = "$(print_selection "${confirmationMessages[cancel]}")" ] + then + # Do nothing + exit 0 + fi + done + # The selection didn't match anything, so raise an error + echo "Invalid selection: $selection" >&2 + exit 1 +fi diff --git a/rofi/.local/bin/rofi/wifimanager b/rofi/.local/bin/rofi/wifimanager new file mode 100755 index 000000000..79bdfcdb9 --- /dev/null +++ b/rofi/.local/bin/rofi/wifimanager @@ -0,0 +1,223 @@ +#!/bin/sh +#-*-coding:utf-8 -*- +#Auto updated? +# Yes +#File: +# wifimanager +#Author: +# The-Repo-Club [wayne6324@gmail.com] +#Github: +# https://github.com/The-Repo-Club/ +# +#Created: +# Wed 07 December 2022, 11:01:26 PM [GMT] +#Modified: +# Wed 07 December 2022, 11:01:44 PM [GMT] +# +#Description: +# +# +#Dependencies: +# rofi nmcli(Network Manager) dunst +# +# shellcheck disable=all + +if [ -z "${ROFI_OUTSIDE}" ] +then + echo "run this script in rofi". + exit +fi + +CACHE_DIR="$HOME/.cache/rofi/network" +[ ! -d $CACHE_DIR ] && mkdir -p "$CACHE_DIR" + +station=$(nmcli device | grep "wifi " | awk '{ printf $1 }') # Device Station name + +function network_status () { + state=$(nmcli -fields WIFI g | sed -n 2p | xargs) # Wifi enbaled or disabled + connection=$(nmcli connection show --active | grep "wifi" | awk -F "wifi" '{ print $1 }' | sed -e '1{s/[^ ]\+\s*$//}') +} + +# Don't allow custom entries +echo -e "\0no-custom\x1ftrue" + +function network_options() { + if [[ "$state" == "enabled" ]]; then + if [ -z "$connection" ]; then + echo "-=|> Connect to a Network" + echo "-=|> Connect to a Hidden Network" + elif [ ! -z "$connection" ]; then + echo "-=|> Connect to a Network" + echo "-=|> Connect to a Hidden Network" + echo "-=|> Disconnect Current Network" + fi + echo "-=|> Disable Wifi" + elif [[ "$state" =~ "disabled" ]]; then + echo "-=|> Enable Wifi" + fi +} + +function main_scrn() { # Main window to be displayed on start + rm $CACHE_DIR/* >/dev/null 2>&1 + network_status + network_options + echo -en "\x00prompt\x1fWifi\n" + echo -en "\0message\x1f\tWIFI Status :: ${state^}\t\tConnected to :: ${connection}\n" + echo -en "-=|> Delete existing connections\n" + echo -en "-=|> Refresh\n" +} + +function show_available_networks(){ # Output the list of available Wifi networks + nmcli -f BSSID,SSID device wifi list > "$CACHE_DIR/available_networks" + echo -en "\0message\x1f $(cat "$CACHE_DIR/available_networks" | sed '1!d')\n" + echo "$(cat "$CACHE_DIR/available_networks" | sed '1d')" + echo "-=|> Rescan available networks" + echo "<|=- To Previous Menu" +} + +function saved_connections() { + echo "delete_network" > "$CACHE_DIR/delete_connection" + echo -en "\0message\x1f\t\t\t\t\t\t[Enter] - Delete selected connection\n" + echo -en "UUID\t\t\t\t TYPE\tNAME\0nonselectable\x1ftrue\n" + echo -en "\0active\x1f0\n" + nmcli --fields UUID,TYPE,NAME con show | grep wifi + echo "<|=- To Previous Menu" +} + +function error_status() { + if [[ "$status" =~ "Connection successfully activated " ]]; then # If connected successfully, then notify and exit + notify-send "Connected to " "$SSID" + exit 0 + elif [[ "$status" =~ "Device '$station' successfully activated with " ]]; then # If connected successfully, then notify and exit + notify-send "Network " "$(echo $status | cut -c 6-)" + rm $CACHE_DIR/* + exit 0 + else + if [ -f $CACHE_DIR/network_ssid ]; then + echo -en "\0message\x1f! Connection Failed. This is normal if connecting to a hidden network, Try again.\n" + elif [ -f $CACHE_DIR/network_bssid ]; then + echo -en "\0message\x1f! Connection Failed! Check your password.\n" + fi + echo "$status" # If failed to connect, shows the error code and asks for password + echo "<|=- To Previous Menu" + fi +} + +function get_ssid() { + echo "hidden" > "$CACHE_DIR/get_ssid" + echo -en "\00prompt\x1fEnter SSID\n" + echo -en "\0message\x1f# Type your network SSID (Network Name).\n" + echo "<|=- To Previous Menu" +} + +function get_password() { + echo -en "\00prompt\x1fPassword\n" + echo -en "\0message\x1f# Type your network security passphrase here.\n" + echo "<|=- To Previous Menu" +} + +function restore_view() { + if [ -f $CACHE_DIR/delete_connection ]; then + saved_connections + elif [ -f $CACHE_DIR/available_networks ]; then + show_available_networks + elif [ -f $CACHE_DIR/network_selected ]; then + show_available_networks + elif [ -f $CACHE_DIR/get_ssid ]; then + get_ssid + elif [ -f $CACHE_DIR/network_bssid ] || [ -f $CACHE_DIR/network_ssid ]; then + get_password + else + main_scrn + fi +} + +function show_previous_menu() { + if [ -f $CACHE_DIR/network_bssid ]; then + rm $CACHE_DIR/network_bssid + show_available_networks + elif [ -f $CACHE_DIR/network_ssid ]; then + rm $CACHE_DIR/network_ssid + get_ssid + else + main_scrn + fi +} + +if [[ "${ROFI_RETV}" == "0" ]]; then + main_scrn + +elif [[ "${ROFI_RETV}" == "1" ]]; then + if [[ "$@" == "<|=- To Previous Menu" ]]; then + show_previous_menu + elif [[ "$@" == "-=|> Refresh" ]]; then + main_scrn + elif [[ "$@" == "-=|> Disable Wifi" ]]; then + nmcli radio wifi off + restore_view + elif [[ "$@" == "-=|> Enable Wifi" ]]; then + nmcli radio wifi on + restore_view + elif [[ "$@" == "-=|> Connect to a Network" ]]; then + show_available_networks + elif [[ "$@" == "-=|> Connect to a Hidden Network" ]]; then + get_ssid + elif [[ "$@" == "-=|> Disconnect Current Network" ]]; then # Disconnect any connected network + con_uuid=$(nmcli connection show --active | grep "wifi" | awk -F "wifi" '{print $1}' | awk '{print $NF}' | xargs) + nmcli connection down "$con_uuid" > /dev/null 2>&1 + restore_view + elif [[ "$@" == "-=|> Delete existing connections" ]]; then # Opens the saved networks list window + saved_connections + elif [ -f $CACHE_DIR/delete_connection ]; then + connection_uuid=$(echo "$@" | awk '{print $1}') + nmcli con delete uuid "$connection_uuid" >/dev/null 2>&1 + restore_view + elif [ -f $CACHE_DIR/available_networks ]; then + if [[ "$@" == "-=|> Rescan available networks" ]]; then + nmcli device wifi rescan >/dev/null 2>&1 + restore_view + else + rm $CACHE_DIR/available_networks + echo "$@" > "$CACHE_DIR/network_selected" + BSSID=$(echo "$@" | awk '{print $1}') + SSID=$(echo "$@" | cut -c 20- | xargs) + SECURITY=$(nmcli -f BSSID,SECURITY device wifi | grep "$BSSID" | awk -F " " '{print $2}' | xargs) + if [[ $(nmcli -f NAME con show | grep -o -m 1 "$SSID") == "$SSID" ]]; then + status=$(nmcli connection up "$SSID") + error_status + exit 0 + elif [[ "$SECURITY" =~ "--" ]] || [ -z "$SECURITY" ]; then + nmcli device wifi connect "$BSSID" >/dev/null 2>&1 + notify-send "Successfully connected to " "$SSID" + elif [[ "$SECURITY" =~ "WPA" ]] || [ "$SECURITY" =~ "WEP" ]; then + rm $CACHE_DIR/network_selected + echo "$BSSID" > "$CACHE_DIR/network_bssid" + get_password + fi + fi + fi + +elif [[ "${ROFI_RETV}" == "2" ]]; then + if [ -f $CACHE_DIR/get_ssid ]; then + rm $CACHE_DIR/get_ssid + echo "$@" > "$CACHE_DIR/network_ssid" + get_password + elif [ -f $CACHE_DIR/network_bssid ]; then + PASSWORD="$@" + BSSID=$(cat "$CACHE_DIR/network_bssid") + status=$(nmcli device wifi connect "$BSSID" password "$PASSWORD") + error_status + elif [ -f $CACHE_DIR/network_ssid ]; then + PASSWORD="$@" + SSID=$(cat "$CACHE_DIR/network_ssid") + status=$(nmcli device wifi connect "$SSID" password "$PASSWORD" hidden true) + error_status + else + restore_view + fi + +else + restore_view + echo -en "\0message\x1f# Custom key events are not valid in this mode.\n" + +fi diff --git a/rofi/.local/bin/rofi/youtube_subs b/rofi/.local/bin/rofi/youtube_subs new file mode 100755 index 000000000..cce6ba0e6 --- /dev/null +++ b/rofi/.local/bin/rofi/youtube_subs @@ -0,0 +1,269 @@ +#!/usr/bin/env bash +#-*-coding:utf-8 -*- +#Auto updated? +# Yes +#File: +# youtube_subs +#Author: +# The-Repo-Club [wayne6324@gmail.com] +#Github: +# https://github.com/The-Repo-Club/ +# +#Created: +# Sun 03 January 2021, 05:09:33 PM [GMT] +#Modified: +# Thu 08 December 2022, 07:36:45 AM [GMT] +# +#Description: +# Watch your youtube subscriptions without a youtube account +# via curl, repomenu, browser and basic unix commands. +# +# The $SUBS_FILE is a text file containing usernames or channel IDs +# comments and blank lines are ignored. +# +# +#Dependencies: +# rofi +# + +rofis() { + rofi -dmenu -p "Youtube Subs:" -mesg "Select a video." +} + +# -/-/-/-/- Settings -/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/ +: "${SUBS_FILE:=${HOME}/.config/rofi/configs/subs.ini}" +: "${SUBS_MENU_PROG:=rofis}" +: "${SUBS:=${HOME}/.cache/subs}" +: "${SUBS_LINKS:=$SUBS/links}" +: "${SUBS_CACHE:=$SUBS/cache}" +: "${SUBS_SLEEP_VALUE:=0.05}" # raise this if you experience problems +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +SEP=^^^^^ # shouldn't need to change this +SUBS_OPEN="mpv --volume=50" +# -/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/ + +die() { + printf >&2 '%s\n' "$*" + exit 1 +} + +usage() { + die 'Usage: repomenue_youtube_subs [-c cat_subs] [-g gen_links] [-u update_subs] [-d daemonize]' +} + +# -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* +# Synopsis: $SUBS_FILE [txt] -> $SUBS_LINKS [xml links] +# +# Updates local cache of xml subscription links from the +# subscription file containing either usernames or channel ids. +# -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* +gen_links() { + : >"$SUBS_LINKS" + + count=0 + total=$(sed -e '/^$/d' -e '/^#/d' <"$SUBS_FILE" | wc -l) + + while read -r line; do + + # ignore comments and blank lines + case $line in '' | ' ' | '#'*) continue ;; esac + + # strip off in-line comments and any trailing whitespace + line=${line%%#*} + line=${line%% *} + + count=$((count + 1)) + + case $line in + UC*) + # YT channel IDs always begin with 'UC' and are 24 chars long + printf "[%s/%s] using channel ID '%s' for xml link\n" "$count" "$total" "$line" + + [ ${#line} -eq 24 ] && + printf 'https://youtube.com/feeds/videos.xml?%s\n' \ + "channel_id=$line" >>"$SUBS_LINKS" + ;; + *) + # otherwise we are given a username, we must find out its channel ID + printf "fetching channel ID for %s...\n" "$line" + + curl -sfL --retry 10 "https://youtube.com/user/$line/about" | + while read -r line; do + case $line in + *channel/UC??????????????????????*) + line=${line##*channel/} + line=${line%%\"*} + printf "[%s/%s] using channel ID '%s' for xml link\n" "$count" "$total" "$line" + printf 'https://youtube.com/feeds/videos.xml?channel_id=%s\n' \ + "$line" >>"$SUBS_LINKS" + break + ;; + esac + done & + sleep "${SUBS_SLEEP_VALUE:-0}" + ;; + esac + + done <"$SUBS_FILE" + + count=0 + while [ "$count" -ne "$total" ]; do + count=$(wc -l <"$SUBS_LINKS") + printf "[%s/%s] waiting for jobs to complete...\n" "$count" "$total" + sleep 0.5 + done +} + +# -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* +# Synopsis: $1 [LINK] -> $SUBS_CACHE/$chan_name/concat [CHANNEL INFO] +# +# Takes a channel rss feed link and creates a file +# with a line of its videos dates, titles, and urls. +# -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* +get_vids() { + data=$(curl -sfL --retry 15 "$1") + + # hide the first tag which is the channel + # creation date + data=${data#*\<\/published\>} + + # trim off outer tags + chan=${data%%} + + printf "%s\n" "$data" | + while read -r line; do + case $line in + *'link rel='*) + line=${line#*href=\"} + line=${line%\"/\>} + line=https://${line#*www.} + url=$line + ;; + *''*) + line=${line%+00:*} + line=${line#*} + date=$line + ;; + *''*) + line=${line%} + title=$line + printf '%s\n' \ + "${date}${SEP}${chan}${SEP}${title}${SEP}${url}" \ + >>"$SUBS_CACHE/$chan" + ;; + esac + done +} + +# -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* +# Updates the local cache of subscriptions. ([-u] flag) +# -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* +update_subs() { + [ -f "$SUBS_LINKS" ] || die 'Subs links have not been generated.' + + rm -r "${SUBS_CACHE:-?}" 2>/dev/null || : + mkdir -p "$SUBS_CACHE" + + total=$(wc -l <"$SUBS_LINKS") + + count=0 + while read -r link; do + count=$((count + 1)) + printf 'starting job [%s/%s] for %s\n' "$count" "$total" "$link" + get_vids "$link" & + sleep "${SUBS_SLEEP_VALUE:-0}" + done <"$SUBS_LINKS" + + count=0 + while [ "$count" -ne "$total" ]; do + count=$(printf '%s\n' "$SUBS_CACHE"/* | wc -l) + printf "[%s/%s] waiting for fetch jobs to complete...\n" "$count" "$total" + sleep 0.5 + done + + printf '%s\n\n' 'done!' +} + +# -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* +# Grab current cache of subscriptions, sort by date uploaded +# -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* +cat_subs() { + sort -r "$SUBS_CACHE"/* | + while read -r line; do + chan=${line#*$SEP} + chan=${chan%%$SEP*} + title=${line#*$chan$SEP} + title=${title%%$SEP*} + date=${line%%$SEP*} + date=${date#*-} + date=${date%T*} + printf '[%s %s] %s\n' "$date" "$chan" "$title" + done +} + +# Split the concatenated lines into entities, send to menu program. +# Finally, play the result with mpv. +get_sel() { + if [ -d "$SUBS_CACHE" ]; then + sel=$(cat_subs | $SUBS_MENU_PROG) + else + die 'Subs cache has not been retrieved.' + fi + + [ "$sel" ] || die Interrupted + + chan="${sel#* }" + chan="${chan%%] *}" + title=${sel#*"$chan"\] } + while read -r line; do + case $line in + *"$SEP$title$SEP"*) + url=${line##*$SEP} + if [ "$url" ]; then + printf 'playing: %s\n' "$url" + # Play the selection. + # shellcheck disable=2086 + exec $SUBS_OPEN "$url" + fi + break + ;; + esac + done <"$SUBS_CACHE/$chan" +} + +daemonize() { + # create a cached copy of the subs file to check for changes + # if changes occur, re-generate links automatically + daemon_file=${HOME}/.cache/subs_daemon.cache + if [ ! -f "$daemon_file" ]; then + cp -f "${SUBS_FILE:=${HOME}/.config/repomenu/subs.ini}" "$daemon_file" + fi + + while true; do + if ! cmp "${SUBS_FILE:=${HOME}/.config/repomenu/subs.ini}" "$daemon_file"; then + cp -f "${SUBS_FILE:=${HOME}/.config/repomenu/subs.ini}" "$daemon_file" + fi + gen_links + update_subs + interval=${SUBS_DAEMON_INTERVAL:-$((10 * 60))} + printf 'Sleeping for %s seconds...\n' "$interval" + sleep "$interval" + done +} + +main() { + mkdir -p "$SUBS" + + case ${1#-} in + h) usage ;; + g) gen_links ;; + u) update_subs ;; + c) cat_subs ;; + d) daemonize ;; + *) get_sel ;; + esac +} + +main "$@"