diff --git a/flake.nix b/flake.nix index d9c2953..dc759da 100644 --- a/flake.nix +++ b/flake.nix @@ -1,10 +1,16 @@ { inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - home-manager.url = "github:nix-community/home-manager"; - home-manager.inputs.nixpkgs.follows = "nixpkgs"; - hardware.url = "github:nixos/nixos-hardware"; - nix-colors.url = "github:misterio77/nix-colors"; + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + home-manager.url = "github:nix-community/home-manager"; + home-manager.inputs.nixpkgs.follows = "nixpkgs"; + hardware.url = "github:nixos/nixos-hardware"; + nix-colors.url = "github:misterio77/nix-colors"; + + firefox-addons = { + url = "gitlab:rycee/nur-expressions?dir=pkgs/firefox-addons"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; outputs = { self, nixpkgs, home-manager, ... }:@inputs: diff --git a/home/ooks/modules/feature/desktop/hyprland/home.nix b/home/ooks/modules/feature/desktop/hyprland/home.nix deleted file mode 100644 index 4b4f501..0000000 --- a/home/ooks/modules/feature/desktop/hyprland/home.nix +++ /dev/null @@ -1,198 +0,0 @@ - config, lib, pkgs, ... }: - -{ - imports = [ - (import ../../environment/hypr-variable.nix) - ]; - programs = { - bash = { - initExtra = '' - if [ -z $DISPLAY ] && [ "$(tty)" = "/dev/tty1" ]; then - exec Hyprland - fi - ''; - }; - fish = { - loginShellInit = '' - set TTY1 (tty) - [ "$TTY1" = "/dev/tty1" ] && exec Hyprland - ''; - }; - }; - systemd.user.targets.hyprland-session.Unit.Wants = [ "xdg-desktop-autostart.target" ]; - wayland.windowManager.hyprland = { - enable = true; - systemdIntegration = true; - nvidiaPatches = false; - extraConfig = '' - monitor=,preferred,auto,auto - - exec-once = swaybg -i ~/.dotfiles/walls/everforest/megacity.png - - input { - kb_layout = us - kb_variant = - kb_model = - kb_options = - kb_rules = - - follow_mouse = 1 - - touchpad { - natural_scroll = yes - } - - sensitivity = 0 # -1.0 - 1.0, 0 means no modification. - } - - general { - - gaps_in = 5 - gaps_out = 5 - border_size = 2 - col.active_border = 0xffA7C080 - col.inactive_border = rgba(595959aa) - - layout = dwindle - } - - decoration { - # See https://wiki.hyprland.org/Configuring/Variables/ for more - - rounding = 5 - multisample_edges = true - blur = no - blur_size = 3 - blur_passes = 1 - blur_new_optimizations = on - - drop_shadow = no - shadow_range = 4 - shadow_render_power = 3 - col.shadow = rgba(1a1a1aee) - } - - misc { - animate_manual_resizes = false - enable_swallow = true - swallow_regex = ^(kitty)$ - focus_on_activate = true - disable_hyprland_logo = true - } -# Animations -# ------------------------------------------------------------------------------------------------- - - animations { - enabled = yes - bezier = overshot, 0.11, 1, 0.36, 1 - animation = windows, 1, 4, overshot, slide - animation = windowsOut, 1, 5, default, popin 80% - animation = border, 1, 5, default - animation = fade, 1, 8, default - animation = workspaces, 1, 6, overshot, slide - } - -# Layout -# ------------------------------------------------------------------------------------------------- - - } - - dwindle { - pseudotile = yes - preserve_split = yes - } - - master { - new_is_master = true - } - -# Gestures -# ------------------------------------------------------------------------------------------------- - - gestures { - workspace_swipe = off - } - -# Device Config -# ------------------------------------------------------------------------------------------------- - - device:epic-mouse-v1 { - sensitivity = -0.5 - } - -# Window Rules -# ------------------------------------------------------------------------------------------------- - -# Example windowrule v1 -# windowrule = float, ^(kitty)$ -# Example windowrule v2 -# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$ -# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more - -# Main Mod -# ------------------------------------------------------------------------------------------------- - - $mainMod = SUPER - -# Program Binds -# ------------------------------------------------------------------------------------------------- - - bind = $mainMod, return, exec, kitty - bind = $mainMod, Q, killactive, - bind = $mainMod, B, exec, firefox - bind = $mainMod, M, exit, - bind = $mainMod, space, togglefloating, - bind = $mainMod, P, pseudo, # dwindle - bind = $mainMod, J, togglesplit, # dwindle - -# Move focus with mainMod + arrow keys - bind = $mainMod, left, movefocus, l - bind = $mainMod, right, movefocus, r - bind = $mainMod, up, movefocus, u - bind = $mainMod, down, movefocus, d - -# Workspaces -# ------------------------------------------------------------------------------------------------- - - bind = $mainMod, 1, workspace, 1 - bind = $mainMod, 2, workspace, 2 - bind = $mainMod, 3, workspace, 3 - bind = $mainMod, 4, workspace, 4 - bind = $mainMod, 5, workspace, 5 - bind = $mainMod, 6, workspace, 6 - bind = $mainMod, 7, workspace, 7 - bind = $mainMod, 8, workspace, 8 - bind = $mainMod, 9, workspace, 9 - bind = $mainMod, 0, workspace, 10 - binds { - workspace_back_and_forth = 1 - allow_workspace_cycles = 1 - } - bind=$mainMod,tab,workspace,previous - -# Move To Workspaces -# ------------------------------------------------------------------------------------------------- - - bind = $mainMod SHIFT, 1, movetoworkspace, 1 - bind = $mainMod SHIFT, 2, movetoworkspace, 2 - bind = $mainMod SHIFT, 3, movetoworkspace, 3 - bind = $mainMod SHIFT, 4, movetoworkspace, 4 - bind = $mainMod SHIFT, 5, movetoworkspace, 5 - bind = $mainMod SHIFT, 6, movetoworkspace, 6 - bind = $mainMod SHIFT, 7, movetoworkspace, 7 - bind = $mainMod SHIFT, 8, movetoworkspace, 8 - bind = $mainMod SHIFT, 9, movetoworkspace, 9 - bind = $mainMod SHIFT, 0, movetoworkspace, 10 - -# Workspaces Scroll -# ------------------------------------------------------------------------------------------------- - - bind = $mainMod, mouse_down, workspace, e+1 - bind = $mainMod, mouse_up, workspace, e-1 - -# Move/resize windows with mainMod + LMB/RMB and dragging - bindm = $mainMod, mouse:272, movewindow - bindm = $mainMod, mouse:273, resizewindow - ''; - }; - } diff --git a/home/ooks/modules/programs/firefox/default.nix b/home/ooks/modules/programs/firefox/default.nix deleted file mode 100644 index f11e97e..0000000 --- a/home/ooks/modules/programs/firefox/default.nix +++ /dev/null @@ -1,14 +0,0 @@ -{ config, pkgs, ... }: - -{ - programs.firefox = { - enable = true; - extraPolicies = { - DisplayBookmarksToolbar = true; - Preferences = { - "browser.toolbars.bookmarks.visibility" = "never"; - "toolkit.legacyUserProfileCustomizations.stylesheets" = true; - "media.ffmpeg.vaapi.enabled" = true; - }; - }; - }; diff --git a/home/ooks/modules/programs/kitty/default.nix b/home/ooks/modules/programs/kitty/default.nix deleted file mode 100644 index 275c707..0000000 --- a/home/ooks/modules/programs/kitty/default.nix +++ /dev/null @@ -1,11 +0,0 @@ -{ config, pkgs, ... }: - -{ - programs = { - kitty = { - enable = true; - environment = { }; - keybindings = { }; - }; - }; -} diff --git a/home/ooks/modules/programs/mpv/default.nix b/home/ooks/modules/programs/mpv/default.nix deleted file mode 100644 index 880bbd2..0000000 --- a/home/ooks/modules/programs/mpv/default.nix +++ /dev/null @@ -1,11 +0,0 @@ -{ lib, pkgs, user, ... }: - -{ - programs = { - mpv = { - enable = true; - }; - }; - home.file.".config/mpv/mpv.conf".source = ./mpv.conf; - home.file.".config/mpv/scripts/file-browser.lua".source = ./scripts/file-browser.lua; -} diff --git a/home/ooks/modules/programs/mpv/mpv.conf b/home/ooks/modules/programs/mpv/mpv.conf deleted file mode 100644 index 283b698..0000000 --- a/home/ooks/modules/programs/mpv/mpv.conf +++ /dev/null @@ -1,8 +0,0 @@ -# hwdec=auto -# vo=gpu-next #This will break Anime4K -gpu-api=opengl -gpu-context=wayland -hwdec=auto-safe -vo=gpu -profile=gpu-hq -script-opts=ytdl_hook-ytdl_path=yt-dlp diff --git a/home/ooks/modules/programs/mpv/scripts/file-browser.lua b/home/ooks/modules/programs/mpv/scripts/file-browser.lua deleted file mode 100644 index 803a5b6..0000000 --- a/home/ooks/modules/programs/mpv/scripts/file-browser.lua +++ /dev/null @@ -1,2593 +0,0 @@ ---[[ - mpv-file-browser - This script allows users to browse and open files and folders entirely from within mpv. - The script uses nothing outside the mpv API, so should work identically on all platforms. - The browser can move up and down directories, start playing files and folders, or add them to the queue. - For full documentation see: https://github.com/CogentRedTester/mpv-file-browser -]] --- - -local mp = require("mp") -local msg = require("mp.msg") -local utils = require("mp.utils") -local opt = require("mp.options") - -local o = { - --root directories - root = "~/Videos/", - - --characters to use as separators - root_separators = ",;", - - --number of entries to show on the screen at once - num_entries = 20, - - --wrap the cursor around the top and bottom of the list - wrap = false, - - --only show files compatible with mpv - filter_files = true, - - --experimental feature that recurses directories concurrently when - --appending items to the playlist - concurrent_recursion = false, - - --maximum number of recursions that can run concurrently - max_concurrency = 16, - - --enable custom keybinds - custom_keybinds = false, - - --blacklist compatible files, it's recommended to use this rather than to edit the - --compatible list directly. A semicolon separated list of extensions without spaces - extension_blacklist = "", - - --add extra file extensions - extension_whitelist = "", - - --files with these extensions will be added as additional audio tracks for the current file instead of appended to the playlist - audio_extensions = "mka,dts,dtshd,dts-hd,truehd,true-hd", - - --files with these extensions will be added as additional subtitle tracks instead of appended to the playlist - subtitle_extensions = "etf,etf8,utf-8,idx,sub,srt,rt,ssa,ass,mks,vtt,sup,scc,smi,lrc,pgs", - - --filter dot directories like .config - --most useful on linux systems - filter_dot_dirs = false, - filter_dot_files = false, - - --substitude forward slashes for backslashes when appending a local file to the playlist - --potentially useful on windows systems - substitute_backslash = false, - - --this option reverses the behaviour of the alt+ENTER keybind - --when disabled the keybind is required to enable autoload for the file - --when enabled the keybind disables autoload for the file - autoload = false, - - --if autoload is triggered by selecting the currently playing file, then - --the current file will have it's watch-later config saved before being closed - --essentially the current file will not be restarted - autoload_save_current = true, - - --when opening the browser in idle mode prefer the current working directory over the root - --note that the working directory is set as the 'current' directory regardless, so `home` will - --move the browser there even if this option is set to false - default_to_working_directory = false, - - --allows custom icons be set to fix incompatabilities with some fonts - --the `\h` character is a hard space to add padding between the symbol and the text - folder_icon = "🖿", - cursor_icon = "➤", - indent_icon = [[\h\h\h]], - - --enable addons - addons = false, - addon_directory = "~~/script-modules/file-browser-addons", - - --directory to load external modules - currently just user-input-module - module_directory = "~~/script-modules", - - --force file-browser to use a specific text alignment (default: top-left) - --uses ass tag alignment numbers: https://aegi.vmoe.info/docs/3.0/ASS_Tags/#index23h3 - --set to 0 to use the default mpv osd-align options - alignment = 7, - - --style settings - font_bold_header = true, - - font_size_header = 35, - font_size_body = 25, - font_size_wrappers = 16, - - font_name_header = "", - font_name_body = "", - font_name_wrappers = "", - font_name_folder = "", - font_name_cursor = "", - - font_colour_header = "00ccff", - font_colour_body = "ffffff", - font_colour_wrappers = "00ccff", - font_colour_cursor = "00ccff", - - font_colour_multiselect = "fcad88", - font_colour_selected = "fce788", - font_colour_playing = "33ff66", - font_colour_playing_multiselected = "22b547", -} - -opt.read_options(o, "file_browser") -utils.shared_script_property_set("file_browser-open", "no") - --------------------------------------------------------------------------------------------------------- ------------------------------------------Environment Setup---------------------------------------------- --------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------- - ---sets the version for the file-browser API -API_VERSION = "1.3.0" - ---switch the main script to a different environment so that the ---executed lua code cannot access our global variales -if setfenv then - setfenv(1, setmetatable({}, { __index = _G })) -else - _ENV = setmetatable({}, { __index = _G }) -end - ---creates a table for the API functions ---adds one metatable redirect to prevent addon authors from accidentally breaking file-browser -local API = { API_VERSION = API_VERSION } -package.loaded["file-browser"] = setmetatable({}, { __index = API }) - -local parser_API = setmetatable({}, { __index = package.loaded["file-browser"] }) -local parse_state_API = {} - --------------------------------------------------------------------------------------------------------- -------------------------------------------Variable Setup------------------------------------------------ --------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------- - ---the osd_overlay API was not added until v0.31. The expand-path command was not added until 0.30 -local ass = mp.create_osd_overlay("ass-events") -if not ass then - return msg.error("Script requires minimum mpv version 0.31") -end - -package.path = mp.command_native({ "expand-path", o.module_directory }) .. "/?.lua;" .. package.path - -local style = { - global = o.alignment == 0 and "" or ([[{\an%d}]]):format(o.alignment), - - -- full line styles - header = ([[{\r\q2\b%s\fs%d\fn%s\c&H%s&}]]):format( - (o.font_bold_header and "1" or "0"), - o.font_size_header, - o.font_name_header, - o.font_colour_header - ), - body = ([[{\r\q2\fs%d\fn%s\c&H%s&}]]):format(o.font_size_body, o.font_name_body, o.font_colour_body), - footer_header = ([[{\r\q2\fs%d\fn%s\c&H%s&}]]):format( - o.font_size_wrappers, - o.font_name_wrappers, - o.font_colour_wrappers - ), - - --small section styles (for colours) - multiselect = ([[{\c&H%s&}]]):format(o.font_colour_multiselect), - selected = ([[{\c&H%s&}]]):format(o.font_colour_selected), - playing = ([[{\c&H%s&}]]):format(o.font_colour_playing), - playing_selected = ([[{\c&H%s&}]]):format(o.font_colour_playing_multiselected), - - --icon styles - cursor = ([[{\fn%s\c&H%s&}]]):format(o.font_name_cursor, o.font_colour_cursor), - folder = ([[{\fn%s}]]):format(o.font_name_folder), -} - -local state = { - list = {}, - selected = 1, - hidden = true, - flag_update = false, - keybinds = nil, - - parser = nil, - directory = nil, - directory_label = nil, - prev_directory = "", - co = nil, - - multiselect_start = nil, - initial_selection = nil, - selection = {}, -} - ---the parser table actually contains 3 entries for each parser ---a numeric entry which represents the priority of the parsers and has the parser object as the value ---a string entry representing the id of each parser and with the parser object as the value ---and a table entry with the parser itself as the key and a table value in the form { id = %s, index = %d } -local parsers = {} - ---this table contains the parse_state tables for every parse operation indexed with the coroutine used for the parse ---this table has weakly referenced keys, meaning that once the coroutine for a parse is no-longer used by anything that ---field in the table will be removed by the garbage collector -local parse_states = setmetatable({}, { __mode = "k" }) - -local extensions = {} -local sub_extensions = {} -local audio_extensions = {} -local parseable_extensions = {} - -local dvd_device = nil -local current_file = { - directory = nil, - name = nil, - path = nil, -} - -local root = nil - ---default list of compatible file extensions ---adding an item to this list is a valid request on github -local compatible_file_extensions = { - "264", - "265", - "3g2", - "3ga", - "3ga2", - "3gp", - "3gp2", - "3gpp", - "3iv", - "a52", - "aac", - "adt", - "adts", - "ahn", - "aif", - "aifc", - "aiff", - "amr", - "ape", - "asf", - "au", - "avc", - "avi", - "awb", - "ay", - "bmp", - "cue", - "divx", - "dts", - "dtshd", - "dts-hd", - "dv", - "dvr", - "dvr-ms", - "eac3", - "evo", - "evob", - "f4a", - "flac", - "flc", - "fli", - "flic", - "flv", - "gbs", - "gif", - "gxf", - "gym", - "h264", - "h265", - "hdmov", - "hdv", - "hes", - "hevc", - "jpeg", - "jpg", - "kss", - "lpcm", - "m1a", - "m1v", - "m2a", - "m2t", - "m2ts", - "m2v", - "m3u", - "m3u8", - "m4a", - "m4v", - "mk3d", - "mka", - "mkv", - "mlp", - "mod", - "mov", - "mp1", - "mp2", - "mp2v", - "mp3", - "mp4", - "mp4v", - "mp4v", - "mpa", - "mpe", - "mpeg", - "mpeg2", - "mpeg4", - "mpg", - "mpg4", - "mpv", - "mpv2", - "mts", - "mtv", - "mxf", - "nsf", - "nsfe", - "nsv", - "nut", - "oga", - "ogg", - "ogm", - "ogv", - "ogx", - "opus", - "pcm", - "pls", - "png", - "qt", - "ra", - "ram", - "rm", - "rmvb", - "sap", - "snd", - "spc", - "spx", - "svg", - "thd", - "thd+ac3", - "tif", - "tiff", - "tod", - "trp", - "truehd", - "true-hd", - "ts", - "tsa", - "tsv", - "tta", - "tts", - "vfw", - "vgm", - "vgz", - "vob", - "vro", - "wav", - "weba", - "webm", - "webp", - "wm", - "wma", - "wmv", - "wtv", - "wv", - "x264", - "x265", - "xvid", - "y4m", - "yuv", -} - --------------------------------------------------------------------------------------------------------- ---------------------------------------Cache Implementation---------------------------------------------- --------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------- - ---metatable of methods to manage the cache -local __cache = {} - -__cache.cached_values = { - "directory", - "directory_label", - "list", - "selected", - "selection", - "parser", - "empty_text", - "co", -} - ---inserts latest state values onto the cache stack -function __cache:push() - local t = {} - for _, value in ipairs(self.cached_values) do - t[value] = state[value] - end - table.insert(self, t) -end - -function __cache:pop() - table.remove(self) -end - -function __cache:apply() - local t = self[#self] - for _, value in ipairs(self.cached_values) do - state[value] = t[value] - end -end - -function __cache:clear() - for i = 1, #self do - self[i] = nil - end -end - -local cache = setmetatable({}, { __index = __cache }) - --------------------------------------------------------------------------------------------------------- ------------------------------------------Utility Functions---------------------------------------------- ----------------------------------------Part of the addon API-------------------------------------------- --------------------------------------------------------------------------------------------------------- - -API.coroutine = {} -local ABORT_ERROR = { - msg = "browser is no longer waiting for list - aborting parse", -} - ---implements table.pack if on lua 5.1 -if not table.pack then - table.unpack = unpack - function table.pack(...) - local t = { ... } - t.n = select("#", ...) - return t - end -end - ---prints an error message and a stack trace ---accepts an error object and optionally a coroutine ---can be passed directly to xpcall -function API.traceback(errmsg, co) - if co then - msg.warn(debug.traceback(co)) - else - msg.warn(debug.traceback("", 2)) - end - msg.error(errmsg) -end - ---prints an error if a coroutine returns an error ---unlike the next function this one still returns the results of coroutine.resume() -function API.coroutine.resume_catch(...) - local returns = table.pack(coroutine.resume(...)) - if not returns[1] and returns[2] ~= ABORT_ERROR then - API.traceback(returns[2], select(1, ...)) - end - return table.unpack(returns, 1, returns.n) -end - ---resumes a coroutine and prints an error if it was not sucessful -function API.coroutine.resume_err(...) - local success, err = coroutine.resume(...) - if not success and err ~= ABORT_ERROR then - API.traceback(err, select(1, ...)) - end - return success -end - ---in lua 5.1 there is only one return value which will be nil if run from the main thread ---in lua 5.2 main will be true if running from the main thread -function API.coroutine.assert(err) - local co, main = coroutine.running() - assert(not main and co, err or "error - function must be executed from within a coroutine") - return co -end - ---creates a callback fuction to resume the current coroutine -function API.coroutine.callback() - local co = API.coroutine.assert("cannot create a coroutine callback for the main thread") - return function(...) - return API.coroutine.resume_err(co, ...) - end -end - ---puts the current coroutine to sleep for the given number of seconds -function API.coroutine.sleep(n) - mp.add_timeout(n, API.coroutine.callback()) - coroutine.yield() -end - ---runs the given function in a coroutine, passing through any additional arguments ---this is for triggering an event in a coroutine -function API.coroutine.run(fn, ...) - local co = coroutine.create(fn) - API.coroutine.resume_err(co, ...) -end - ---get the full path for the current file -function API.get_full_path(item, dir) - if item.path then - return item.path - end - return (dir or state.directory) .. item.name -end - ---gets the path for a new subdirectory, redirects if the path field is set ---returns the new directory path and a boolean specifying if a redirect happened -function API.get_new_directory(item, directory) - if item.path and item.redirect ~= false then - return item.path, true - end - if directory == "" then - return item.name - end - if string.sub(directory, -1) == "/" then - return directory .. item.name - end - return directory .. "/" .. item.name -end - ---returns the file extension of the given file -function API.get_extension(filename, def) - return string.lower(filename):match("%.([^%./]+)$") or def -end - ---returns the protocol scheme of the given url, or nil if there is none -function API.get_protocol(filename, def) - return string.lower(filename):match("^(%a[%w+-.]*)://") or def -end - ---formats strings for ass handling ---this function is based on a similar function from https://github.com/mpv-player/mpv/blob/master/player/lua/console.lua#L110 -function API.ass_escape(str, replace_newline) - if replace_newline == true then - replace_newline = "\\\239\187\191n" - end - - --escape the invalid single characters - str = string.gsub(str, "[\\{}\n]", { - -- There is no escape for '\' in ASS (I think?) but '\' is used verbatim if - -- it isn't followed by a recognised character, so add a zero-width - -- non-breaking space - ["\\"] = "\\\239\187\191", - ["{"] = "\\{", - ["}"] = "\\}", - -- Precede newlines with a ZWNBSP to prevent ASS's weird collapsing of - -- consecutive newlines - ["\n"] = "\239\187\191\\N", - }) - - -- Turn leading spaces into hard spaces to prevent ASS from stripping them - str = str:gsub("\\N ", "\\N\\h") - str = str:gsub("^ ", "\\h") - - if replace_newline then - str = str:gsub("\\N", replace_newline) - end - return str -end - ---escape lua pattern characters -function API.pattern_escape(str) - return string.gsub(str, "([%^%$%(%)%%%.%[%]%*%+%-])", "%%%1") -end - ---standardises filepaths across systems -function API.fix_path(str, is_directory) - str = string.gsub(str, [[\]], [[/]]) - str = str:gsub([[/./]], [[/]]) - if is_directory and str:sub(-1) ~= "/" then - str = str .. "/" - end - return str -end - ---wrapper for utils.join_path to handle protocols -function API.join_path(working, relative) - return API.get_protocol(relative) and relative or utils.join_path(working, relative) -end - ---sorts the table lexicographically ignoring case and accounting for leading/non-leading zeroes ---the number format functionality was proposed by github user twophyro, and was presumably taken ---from here: http://notebook.kulchenko.com/algorithms/alphanumeric-natural-sorting-for-humans-in-lua -function API.sort(t) - local function padnum(d) - local r = string.match(d, "0*(.+)") - return ("%03d%s"):format(#r, r) - end - - --appends the letter d or f to the start of the comparison to sort directories and folders as well - table.sort(t, function(a, b) - return a.type:sub(1, 1) .. (a.label or a.name):lower():gsub("%d+", padnum) - < b.type:sub(1, 1) .. (b.label or b.name):lower():gsub("%d+", padnum) - end) - return t -end - -function API.valid_dir(dir) - if o.filter_dot_dirs and string.sub(dir, 1, 1) == "." then - return false - end - return true -end - -function API.valid_file(file) - if o.filter_dot_files and (string.sub(file, 1, 1) == ".") then - return false - end - if o.filter_files and not extensions[API.get_extension(file, "")] then - return false - end - return true -end - ---returns whether or not the item can be parsed -function API.parseable_item(item) - return item.type == "dir" or parseable_extensions[API.get_extension(item.name, "")] -end - ---removes items and folders from the list ---this is for addons which can't filter things during their normal processing -function API.filter(t) - local max = #t - local top = 1 - for i = 1, max do - local temp = t[i] - t[i] = nil - - if - (temp.type == "dir" and API.valid_dir(temp.label or temp.name)) - or (temp.type == "file" and API.valid_file(temp.label or temp.name)) - then - t[top] = temp - top = top + 1 - end - end - return t -end - ---returns a string iterator that uses the root separators -function API.iterate_opt(str) - return string.gmatch(str, "([^" .. API.pattern_escape(o.root_separators) .. "]+)") -end - ---sorts a table into an array of selected items in the correct order ---if a predicate function is passed, then the item will only be added to ---the table if the function returns true -function API.sort_keys(t, include_item) - local keys = {} - for k in pairs(t) do - local item = state.list[k] - if not include_item or include_item(item) then - item.index = k - keys[#keys + 1] = item - end - end - - table.sort(keys, function(a, b) - return a.index < b.index - end) - return keys -end - -local invalid_types = { - userdata = true, - thread = true, - ["function"] = true, -} - -local invalid_key_types = { - boolean = true, - table = true, - ["nil"] = true, -} -setmetatable(invalid_key_types, { __index = invalid_types }) - ---recursively removes elements of the table which would cause ---utils.format_json to throw an error -local function json_safe_recursive(t) - if type(t) ~= "table" then - return t - end - - local invalid_ktypes = setmetatable({}, { __index = invalid_key_types }) - local arr_length = #t - if arr_length > 0 then - invalid_ktypes.string = true - setmetatable(t, { type = "ARRAY" }) - else - invalid_ktypes.number = true - setmetatable(t, { type = "MAP" }) - end - - for key, value in pairs(t) do - local ktype = type(key) - local vtype = type(value) - - if invalid_ktypes[ktype] or invalid_types[vtype] then - t[key] = nil - elseif ktype == "number" and key > arr_length then - t[key] = nil - else - t[key] = json_safe_recursive(t[key]) - end - end - return t -end - ---formats a table into a json string but ensures there are no invalid datatypes inside the table first -function API.format_json_safe(t) - --operate on a copy of the table to prevent any data loss in the original table - t = json_safe_recursive(API.copy_table(t)) - local success, result, err = pcall(utils.format_json, t) - if success then - return result, err - else - return nil, result - end -end - ---copies a table without leaving any references to the original ---uses a structured clone algorithm to maintain cyclic references -local function copy_table_recursive(t, references) - if type(t) ~= "table" then - return t - end - if references[t] then - return references[t] - end - - local mt = { - __original = t, - __index = getmetatable(t), - } - local copy = setmetatable({}, mt) - references[t] = copy - - for key, value in pairs(t) do - key = copy_table_recursive(key, references) - copy[key] = copy_table_recursive(value, references) - end - return copy -end - ---a wrapper around copy_table to provide the reference table -function API.copy_table(t) - --this is to handle cyclic table references - return copy_table_recursive(t, {}) -end - --------------------------------------------------------------------------------------------------------- -------------------------------------Parser Object Implementation---------------------------------------- --------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------- - ---parser object for the root ---this object is not added to the parsers table so that scripts cannot get access to ---the root table, which is returned directly by parse() -local root_parser = { - name = "root", - priority = math.huge, - - --if this is being called then all other parsers have failed and we've fallen back to root - can_parse = function() - return true - end, - - --we return the root directory exactly as setup - parse = function(self) - return root, { - sorted = true, - filtered = true, - escaped = true, - parser = self, - directory = "", - } - end, -} - ---parser ofject for native filesystems -local file_parser = { - name = "file", - priority = 110, - - --as the default parser we'll always attempt to use it if all others fail - can_parse = function(_, directory) - return true - end, - - --scans the given directory using the mp.utils.readdir function - parse = function(self, directory) - local new_list = {} - local list1 = utils.readdir(directory, "dirs") - if list1 == nil then - return nil - end - - --sorts folders and formats them into the list of directories - for i = 1, #list1 do - local item = list1[i] - - --filters hidden dot directories for linux - if self.valid_dir(item) then - msg.trace(item .. "/") - table.insert(new_list, { name = item .. "/", type = "dir" }) - end - end - - --appends files to the list of directory items - local list2 = utils.readdir(directory, "files") - for i = 1, #list2 do - local item = list2[i] - - --only adds whitelisted files to the browser - if self.valid_file(item) then - msg.trace(item) - table.insert(new_list, { name = item, type = "file" }) - end - end - return API.sort(new_list), { filtered = true, sorted = true } - end, -} - --------------------------------------------------------------------------------------------------------- ------------------------------------------List Formatting------------------------------------------------ --------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------- - ---appends the entered text to the overlay -local function append(text) - if text == nil then - return - end - ass.data = ass.data .. text -end - ---appends a newline character to the osd -local function newline() - ass.data = ass.data .. "\\N" -end - ---detects whether or not to highlight the given entry as being played -local function highlight_entry(v) - if current_file.name == nil then - return false - end - if API.parseable_item(v) then - return current_file.directory:find(API.get_full_path(v), 1, true) - else - return current_file.path == API.get_full_path(v) - end -end - ---saves the directory and name of the currently playing file -local function update_current_directory(_, filepath) - --if we're in idle mode then we want to open the working directory - if filepath == nil then - current_file.directory = API.fix_path(mp.get_property("working-directory", ""), true) - current_file.name = nil - current_file.path = nil - return - elseif filepath:find("dvd://") == 1 then - filepath = dvd_device .. filepath:match("dvd://(.*)") - end - - local workingDirectory = mp.get_property("working-directory", "") - local exact_path = API.join_path(workingDirectory, filepath) - exact_path = API.fix_path(exact_path, false) - current_file.directory, current_file.name = utils.split_path(exact_path) - current_file.path = exact_path -end - ---refreshes the ass text using the contents of the list -local function update_ass() - if state.hidden then - state.flag_update = true - return - end - - ass.data = style.global - - local dir_name = state.directory_label or state.directory - if dir_name == "" then - dir_name = "ROOT" - end - append(style.header) - append(API.ass_escape(dir_name, style.cursor .. "\\\239\187\191n" .. style.header)) - append("\\N ----------------------------------------------------") - newline() - - if #state.list < 1 then - append(state.empty_text) - ass:update() - return - end - - local start = 1 - local finish = start + o.num_entries - 1 - - --handling cursor positioning - local mid = math.ceil(o.num_entries / 2) + 1 - if state.selected + mid > finish then - local offset = state.selected - finish + mid - - --if we've overshot the end of the list then undo some of the offset - if finish + offset > #state.list then - offset = offset - ((finish + offset) - #state.list) - end - - start = start + offset - finish = finish + offset - end - - --making sure that we don't overstep the boundaries - if start < 1 then - start = 1 - end - local overflow = finish < #state.list - --this is necessary when the number of items in the dir is less than the max - if not overflow then - finish = #state.list - end - - --adding a header to show there are items above in the list - if start > 1 then - append(style.footer_header .. (start - 1) .. " item(s) above\\N\\N") - end - - for i = start, finish do - local v = state.list[i] - local playing_file = highlight_entry(v) - append(style.body) - - --handles custom styles for different entries - if i == state.selected then - append(style.cursor) - append((state.multiselect_start and style.multiselect or "") .. o.cursor_icon) - append("\\h" .. style.body) - else - append(o.indent_icon .. "\\h" .. style.body) - end - - --sets the selection colour scheme - local multiselected = state.selection[i] - if multiselected then - append(style.multiselect) - elseif i == state.selected then - append(style.selected) - end - - --prints the currently-playing icon and style - if playing_file and multiselected then - append(style.playing_selected) - elseif playing_file then - append(style.playing) - end - - --sets the folder icon - if v.type == "dir" then - append(style.folder .. o.folder_icon .. "\\h" .. "{\\fn" .. o.font_name_body .. "}") - end - - --adds the actual name of the item - append(v.ass or API.ass_escape(v.label or v.name, true)) - newline() - end - - if overflow then - append("\\N" .. style.footer_header .. #state.list - finish .. " item(s) remaining") - end - ass:update() -end - --------------------------------------------------------------------------------------------------------- ---------------------------------Scroll/Select Implementation-------------------------------------------- --------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------- - ---disables multiselect -local function disable_select_mode() - state.multiselect_start = nil - state.initial_selection = nil -end - ---enables multiselect -local function enable_select_mode() - state.multiselect_start = state.selected - state.initial_selection = API.copy_table(state.selection) -end - ---calculates what drag behaviour is required for that specific movement -local function drag_select(original_pos, new_pos) - if original_pos == new_pos then - return - end - - local setting = state.selection[state.multiselect_start] - for i = original_pos, new_pos, (new_pos > original_pos and 1 or -1) do - --if we're moving the cursor away from the starting point then set the selection - --otherwise restore the original selection - if i > state.multiselect_start then - if new_pos > original_pos then - state.selection[i] = setting - elseif i ~= new_pos then - state.selection[i] = state.initial_selection[i] - end - elseif i < state.multiselect_start then - if new_pos < original_pos then - state.selection[i] = setting - elseif i ~= new_pos then - state.selection[i] = state.initial_selection[i] - end - end - end -end - ---moves the selector up and down the list by the entered amount -local function scroll(n, wrap) - local num_items = #state.list - if num_items == 0 then - return - end - - local original_pos = state.selected - - if original_pos + n > num_items then - state.selected = wrap and 1 or num_items - elseif original_pos + n < 1 then - state.selected = wrap and num_items or 1 - else - state.selected = original_pos + n - end - - if state.multiselect_start then - drag_select(original_pos, state.selected) - end - update_ass() -end - ---toggles the selection -local function toggle_selection() - if not state.list[state.selected] then - return - end - state.selection[state.selected] = not state.selection[state.selected] or nil - update_ass() -end - ---select all items in the list -local function select_all() - for i, _ in ipairs(state.list) do - state.selection[i] = true - end - update_ass() -end - ---toggles select mode -local function toggle_select_mode() - if state.multiselect_start == nil then - enable_select_mode() - toggle_selection() - else - disable_select_mode() - update_ass() - end -end - --------------------------------------------------------------------------------------------------------- ------------------------------------------Directory Movement--------------------------------------------- --------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------- - ---scans the list for which item to select by default ---chooses the folder that the script just moved out of ---or, otherwise, the item highlighted as currently playing -local function select_prev_directory() - if state.prev_directory:find(state.directory, 1, true) == 1 then - local i = 1 - while state.list[i] and API.parseable_item(state.list[i]) do - if state.prev_directory:find(API.get_full_path(state.list[i]), 1, true) then - state.selected = i - return - end - i = i + 1 - end - end - - for i, item in ipairs(state.list) do - if highlight_entry(item) then - state.selected = i - return - end - end -end - ---parses the given directory or defers to the next parser if nil is returned -local function choose_and_parse(directory, index) - msg.debug("finding parser for", directory) - local parser, list, opts - local parse_state = API.get_parse_state() - while list == nil and not parse_state.already_deferred and index <= #parsers do - parser = parsers[index] - if parser:can_parse(directory, parse_state) then - msg.debug("attempting parser:", parser:get_id()) - list, opts = parser:parse(directory, parse_state) - end - index = index + 1 - end - if not list then - return nil, {} - end - - msg.debug("list returned from:", parser:get_id()) - opts = opts or {} - if list then - opts.id = opts.id or parser:get_id() - end - return list, opts -end - ---sets up the parse_state table and runs the parse operation -local function run_parse(directory, parse_state) - msg.verbose("scanning files in", directory) - parse_state.directory = directory - local co = coroutine.running() - - setmetatable(parse_state, { __index = parse_state_API }) - if directory == "" then - return root_parser:parse() - end - - parse_states[co] = parse_state - local list, opts = choose_and_parse(directory, 1) - - if list == nil then - return msg.debug("no successful parsers found") - end - opts.parser = parsers[opts.id] - - if not opts.filtered then - API.filter(list) - end - if not opts.sorted then - API.sort(list) - end - return list, opts -end - ---returns the contents of the given directory using the given parse state ---if a coroutine has already been used for a parse then create a new coroutine so that ---the every parse operation has a unique thread ID -local function parse_directory(directory, parse_state) - local co = API.coroutine.assert( - "scan_directory must be executed from within a coroutine - aborting scan " .. utils.to_string(parse_state) - ) - if not parse_states[co] then - return run_parse(directory, parse_state) - end - - --if this coroutine is already is use by another parse operation then we create a new - --one and hand execution over to that - local new_co = coroutine.create(function() - API.coroutine.resume_err(co, run_parse(directory, parse_state)) - end) - - --queue the new coroutine on the mpv event queue - mp.add_timeout(0, function() - local success, err = coroutine.resume(new_co) - if not success then - API.traceback(err, new_co) - API.coroutine.resume_err(co) - end - end) - return parse_states[co]:yield() -end - ---sends update requests to the different parsers -local function update_list() - msg.verbose("opening directory: " .. state.directory) - - state.selected = 1 - state.selection = {} - - --loads the current directry from the cache to save loading time - --there will be a way to forcibly reload the current directory at some point - --the cache is in the form of a stack, items are taken off the stack when the dir moves up - if cache[1] and cache[#cache].directory == state.directory then - msg.verbose("found directory in cache") - cache:apply() - state.prev_directory = state.directory - return - end - local directory = state.directory - local list, opts = parse_directory(state.directory, { source = "browser" }) - - --if the running coroutine isn't the one stored in the state variable, then the user - --changed directories while the coroutine was paused, and this operation should be aborted - if coroutine.running() ~= state.co then - msg.verbose(ABORT_ERROR.msg) - msg.debug("expected:", state.directory, "received:", directory) - return - end - - --apply fallbacks if the scan failed - if not list and cache[1] then - --switches settings back to the previously opened directory - --to the user it will be like the directory never changed - msg.warn("could not read directory", state.directory) - cache:apply() - return - elseif not list then - msg.warn("could not read directory", state.directory) - list, opts = root_parser:parse() - end - - state.list = list - state.parser = opts.parser - - --this only matters when displaying the list on the screen, so it doesn't need to be in the scan function - if not opts.escaped then - for i = 1, #list do - list[i].ass = list[i].ass or API.ass_escape(list[i].label or list[i].name, true) - end - end - - --setting custom options from parsers - state.directory_label = opts.directory_label - state.empty_text = opts.empty_text or state.empty_text - - --we assume that directory is only changed when redirecting to a different location - --therefore, the cache should be wiped - if opts.directory then - state.directory = opts.directory - cache:clear() - end - - if opts.selected_index then - state.selected = opts.selected_index or state.selected - if state.selected > #state.list then - state.selected = #state.list - elseif state.selected < 1 then - state.selected = 1 - end - end - - select_prev_directory() - state.prev_directory = state.directory -end - ---rescans the folder and updates the list -local function update(moving_adjacent) - --we can only make assumptions about the directory label when moving from adjacent directories - if not moving_adjacent then - state.directory_label = nil - cache:clear() - end - - state.empty_text = "~" - state.list = {} - disable_select_mode() - update_ass() - state.empty_text = "empty directory" - - --the directory is always handled within a coroutine to allow addons to - --pause execution for asynchronous operations - state.co = coroutine.create(function() - update_list() - update_ass() - end) - API.coroutine.resume_err(state.co) -end - ---the base function for moving to a directory -local function goto_directory(directory) - state.directory = directory - update() -end - ---loads the root list -local function goto_root() - msg.verbose("jumping to root") - goto_directory("") -end - ---switches to the directory of the currently playing file -local function goto_current_dir() - msg.verbose("jumping to current directory") - goto_directory(current_file.directory) -end - ---moves up a directory -local function up_dir() - local dir = state.directory:reverse() - local index = dir:find("[/\\]") - - while index == 1 do - dir = dir:sub(2) - index = dir:find("[/\\]") - end - - if index == nil then - state.directory = "" - else - state.directory = dir:sub(index):reverse() - end - - --we can make some assumptions about the next directory label when moving up or down - if state.directory_label then - state.directory_label = state.directory_label:match("^(.+/)[^/]+/$") - end - - update(true) - cache:pop() -end - ---moves down a directory -local function down_dir() - local current = state.list[state.selected] - if not current or not API.parseable_item(current) then - return - end - - cache:push() - local directory, redirected = API.get_new_directory(current, state.directory) - state.directory = directory - - --we can make some assumptions about the next directory label when moving up or down - if state.directory_label then - state.directory_label = state.directory_label .. (current.label or current.name) - end - update(not redirected) -end - ------------------------------------------------------------------------------------------- -------------------------------------Browser Controls-------------------------------------- ------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------- - ---opens the browser -local function open() - for _, v in ipairs(state.keybinds) do - mp.add_forced_key_binding(v[1], "dynamic/" .. v[2], v[3], v[4]) - end - - utils.shared_script_property_set("file_browser-open", "yes") - state.hidden = false - if state.directory == nil then - local path = mp.get_property("path") - update_current_directory(nil, path) - if path or o.default_to_working_directory then - goto_current_dir() - else - goto_root() - end - return - end - - if state.flag_update then - update_current_directory(nil, mp.get_property("path")) - end - if not state.flag_update then - ass:update() - else - state.flag_update = false - update_ass() - end -end - ---closes the list and sets the hidden flag -local function close() - for _, v in ipairs(state.keybinds) do - mp.remove_key_binding("dynamic/" .. v[2]) - end - - utils.shared_script_property_set("file_browser-open", "no") - state.hidden = true - ass:remove() -end - ---toggles the list -local function toggle() - if state.hidden then - open() - else - close() - end -end - ---run when the escape key is used -local function escape() - --if multiple items are selection cancel the - --selection instead of closing the browser - if next(state.selection) or state.multiselect_start then - state.selection = {} - disable_select_mode() - update_ass() - return - end - close() -end - ---opens a specific directory -local function browse_directory(directory) - if not directory then - return - end - directory = mp.command_native({ "expand-path", directory }, "") - -- directory = join_path( mp.get_property("working-directory", ""), directory ) - - if directory ~= "" then - directory = API.fix_path(directory, true) - end - msg.verbose("recieved directory from script message: " .. directory) - - if directory == "dvd://" then - directory = dvd_device - end - goto_directory(directory) - open() -end - ------------------------------------------------------------------------------------------- ----------------------------------File/Playlist Opening------------------------------------ ------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------- - ---adds a file to the playlist and changes the flag to `append-play` in preparation ---for future items -local function loadfile(file, opts) - if o.substitute_backslash and not API.get_protocol(file) then - file = file:gsub("/", "\\") - end - - if opts.flag == "replace" then - msg.verbose("Playling file", file) - else - msg.verbose("Appending", file, "to the playlist") - end - - if not mp.commandv("loadfile", file, opts.flag) then - msg.warn(file) - end - opts.flag = "append-play" - opts.items_appended = opts.items_appended + 1 -end - ---this function recursively loads directories concurrently in separate coroutines ---results are saved in a tree of tables that allows asynchronous access -local function concurrent_loadlist_parse(directory, load_opts, prev_dirs, item_t) - --prevents infinite recursion from the item.path or opts.directory fields - if prev_dirs[directory] then - return - end - prev_dirs[directory] = true - - local list, list_opts = parse_directory(directory, { source = "loadlist" }) - if list == root then - return - end - - --if we can't parse the directory then append it and hope mpv fares better - if list == nil then - msg.warn("Could not parse", directory, "appending to playlist anyway") - item_t.type = "file" - return - end - - directory = list_opts.directory or directory - if directory == "" then - return - end - - --we must declare these before we start loading sublists otherwise the append thread will - --need to wait until the whole list is loaded (when synchronous IO is used) - item_t._sublist = list or {} - list._directory = directory - - --launches new parse operations for directories, each in a different coroutine - for _, item in ipairs(list) do - if API.parseable_item(item) then - API.coroutine.run( - concurrent_loadlist_wrapper, - API.get_new_directory(item, directory), - load_opts, - prev_dirs, - item - ) - end - end - return true -end - ---a wrapper function that ensures the concurrent_loadlist_parse is run correctly -function concurrent_loadlist_wrapper(directory, opts, prev_dirs, item) - --ensures that only a set number of concurrent parses are operating at any one time. - --the mpv event queue is seemingly limited to 1000 items, but only async mpv actions like - --command_native_async should use that, events like mp.add_timeout (which coroutine.sleep() uses) should - --be handled enturely on the Lua side with a table, which has a significantly larger maximum size. - while opts.concurrency > o.max_concurrency do - API.coroutine.sleep(0.1) - end - opts.concurrency = opts.concurrency + 1 - - local success = concurrent_loadlist_parse(directory, opts, prev_dirs, item) - opts.concurrency = opts.concurrency - 1 - if not success then - item._sublist = {} - end - if coroutine.status(opts.co) == "suspended" then - API.coroutine.resume_err(opts.co) - end -end - ---recursively appends items to the playlist, acts as a consumer to the previous functions producer; ---if the next directory has not been parsed this function will yield until the parse has completed -local function concurrent_loadlist_append(list, load_opts) - local directory = list._directory - - for _, item in ipairs(list) do - if - not sub_extensions[API.get_extension(item.name, "")] - and not audio_extensions[API.get_extension(item.name, "")] - then - while not item._sublist and API.parseable_item(item) do - coroutine.yield() - end - - if API.parseable_item(item) then - concurrent_loadlist_append(item._sublist, load_opts) - else - loadfile(API.get_full_path(item, directory), load_opts) - end - end - end -end - ---recursive function to load directories using the script custom parsers ---returns true if any items were appended to the playlist -local function custom_loadlist_recursive(directory, load_opts, prev_dirs) - --prevents infinite recursion from the item.path or opts.directory fields - if prev_dirs[directory] then - return - end - prev_dirs[directory] = true - - local list, opts = parse_directory(directory, { source = "loadlist" }) - if list == root then - return - end - - --if we can't parse the directory then append it and hope mpv fares better - if list == nil then - msg.warn("Could not parse", directory, "appending to playlist anyway") - loadfile(directory, load_opts.flag) - return true - end - - directory = opts.directory or directory - if directory == "" then - return - end - - for _, item in ipairs(list) do - if - not sub_extensions[API.get_extension(item.name, "")] - and not audio_extensions[API.get_extension(item.name, "")] - then - if API.parseable_item(item) then - custom_loadlist_recursive(API.get_new_directory(item, directory), load_opts, prev_dirs) - else - local path = API.get_full_path(item, directory) - loadfile(path, load_opts) - end - end - end -end - ---a wrapper for the custom_loadlist_recursive function -local function loadlist(item, opts) - local dir = API.get_full_path(item, opts.directory) - local num_items = opts.items_appended - - if o.concurrent_recursion then - item = API.copy_table(item) - opts.co = API.coroutine.assert() - opts.concurrency = 0 - - --we need the current coroutine to suspend before we run the first parse operation, so - --we schedule the coroutine to run on the mpv event queue - mp.add_timeout(0, function() - API.coroutine.run(concurrent_loadlist_wrapper, dir, opts, {}, item) - end) - concurrent_loadlist_append({ item, _directory = opts.directory }, opts) - else - custom_loadlist_recursive(dir, opts, {}) - end - - if opts.items_appended == num_items then - msg.warn(dir, "contained no valid files") - end -end - ---load playlist entries before and after the currently playing file -local function autoload_dir(path, opts) - if o.autoload_save_current and path == current_file.path then - mp.commandv("write-watch-later-config") - end - - --loads the currently selected file, clearing the playlist in the process - loadfile(path, opts) - - local pos = 1 - local file_count = 0 - for _, item in ipairs(state.list) do - if - item.type == "file" - and not sub_extensions[API.get_extension(item.name, "")] - and not audio_extensions[API.get_extension(item.name, "")] - then - local p = API.get_full_path(item) - - if p == path then - pos = file_count - else - loadfile(p, opts) - end - - file_count = file_count + 1 - end - end - mp.commandv("playlist-move", 0, pos + 1) -end - ---runs the loadfile or loadlist command -local function open_item(item, opts) - if API.parseable_item(item) then - return loadlist(item, opts) - end - - local path = API.get_full_path(item, opts.directory) - if sub_extensions[API.get_extension(item.name, "")] then - mp.commandv("sub-add", path, opts.flag == "replace" and "select" or "auto") - elseif audio_extensions[API.get_extension(item.name, "")] then - mp.commandv("audio-add", path, opts.flag == "replace" and "select" or "auto") - else - if opts.autoload then - autoload_dir(path, opts) - else - loadfile(path, opts) - end - end -end - ---handles the open options as a coroutine ---once loadfile has been run we can no-longer guarantee synchronous execution - the state values may change ---therefore, we must ensure that any state values that could be used after a loadfile call are saved beforehand -local function open_file_coroutine(opts) - if not state.list[state.selected] then - return - end - if opts.flag == "replace" then - close() - end - - --we want to set the idle option to yes to ensure that if the first item - --fails to load then the player has a chance to attempt to load further items (for async append operations) - local idle = mp.get_property("idle", "once") - mp.set_property("idle", "yes") - - --handles multi-selection behaviour - if next(state.selection) then - local selection = API.sort_keys(state.selection) - --reset the selection after - state.selection = {} - - disable_select_mode() - update_ass() - - --the currently selected file will be loaded according to the flag - --the flag variable will be switched to append once a file is loaded - for i = 1, #selection do - open_item(selection[i], opts) - end - else - local item = state.list[state.selected] - if opts.flag == "replace" then - down_dir() - end - open_item(item, opts) - end - - if mp.get_property("idle") == "yes" then - mp.set_property("idle", idle) - end -end - ---opens the selelected file(s) -local function open_file(flag, autoload) - API.coroutine.run(open_file_coroutine, { - flag = flag, - autoload = (autoload ~= o.autoload and flag == "replace"), - directory = state.directory, - items_appended = 0, - }) -end - ------------------------------------------------------------------------------------------- -----------------------------------Keybind Implementation---------------------------------- ------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------- - -state.keybinds = { - { - "ENTER", - "play", - function() - open_file("replace", false) - end, - }, - { - "Shift+ENTER", - "play_append", - function() - open_file("append-play", false) - end, - }, - { - "Alt+ENTER", - "play_autoload", - function() - open_file("replace", true) - end, - }, - { "ESC", "close", escape }, - { "RIGHT", "down_dir", down_dir }, - { "LEFT", "up_dir", up_dir }, - { - "DOWN", - "scroll_down", - function() - scroll(1, o.wrap) - end, - { repeatable = true }, - }, - { - "UP", - "scroll_up", - function() - scroll(-1, o.wrap) - end, - { repeatable = true }, - }, - { - "PGDWN", - "page_down", - function() - scroll(o.num_entries) - end, - { repeatable = true }, - }, - { - "PGUP", - "page_up", - function() - scroll(-o.num_entries) - end, - { repeatable = true }, - }, - { - "Shift+PGDWN", - "list_bottom", - function() - scroll(math.huge) - end, - }, - { - "Shift+PGUP", - "list_top", - function() - scroll(-math.huge) - end, - }, - { "HOME", "goto_current", goto_current_dir }, - { "Shift+HOME", "goto_root", goto_root }, - { - "Ctrl+r", - "reload", - function() - cache:clear() - update() - end, - }, - { "s", "select_mode", toggle_select_mode }, - { "S", "select_item", toggle_selection }, - { "Ctrl+a", "select_all", select_all }, -} - ---characters used for custom keybind codes -local CUSTOM_KEYBIND_CODES = "%fFnNpPdDrR" - ---a map of key-keybinds - only saves the latest keybind if multiple have the same key code -local top_level_keys = {} - ---format the item string for either single or multiple items -local function create_item_string(cmd, items, funct) - if not items[1] then - return - end - - local str = funct(items[1]) - for i = 2, #items do - str = str .. (cmd["concat-string"] or " ") .. funct(items[i]) - end - return str -end - ---iterates through the command table and substitutes special ---character codes for the correct strings used for custom functions -local function format_command_table(cmd, items, state) - local copy = {} - for i = 1, #cmd.command do - copy[i] = {} - - for j = 1, #cmd.command[i] do - copy[i][j] = cmd.command[i][j]:gsub("%%[" .. CUSTOM_KEYBIND_CODES .. "]", { - ["%%"] = "%", - ["%f"] = create_item_string(cmd, items, function(item) - return item and API.get_full_path(item, state.directory) or "" - end), - ["%F"] = create_item_string(cmd, items, function(item) - return string.format("%q", item and API.get_full_path(item, state.directory) or "") - end), - ["%n"] = create_item_string(cmd, items, function(item) - return item and (item.label or item.name) or "" - end), - ["%N"] = create_item_string(cmd, items, function(item) - return string.format("%q", item and (item.label or item.name) or "") - end), - ["%p"] = state.directory or "", - ["%P"] = string.format("%q", state.directory or ""), - ["%d"] = (state.directory_label or state.directory):match("([^/]+)/?$") or "", - ["%D"] = string.format("%q", (state.directory_label or state.directory):match("([^/]+)/$") or ""), - ["%r"] = state.parser.keybind_name or state.parser.name or "", - ["%R"] = string.format("%q", state.parser.keybind_name or state.parser.name or ""), - }) - end - end - return copy -end - ---runs all of the commands in the command table ---key.command must be an array of command tables compatible with mp.command_native ---items must be an array of multiple items (when multi-type ~= concat the array will be 1 long) -local function run_custom_command(cmd, items, state) - local custom_cmds = cmd.codes and format_command_table(cmd, items, state) or cmd.command - - for _, cmd in ipairs(custom_cmds) do - msg.debug("running command:", utils.to_string(cmd)) - mp.command_native(cmd) - end -end - ---runs one of the custom commands -local function custom_command(cmd, state, co) - if cmd.parser and cmd.parser ~= (state.parser.keybind_name or state.parser.name) then - return false - end - - --the function terminates here if we are running the command on a single item - if not (cmd.multiselect and next(state.selection)) then - if cmd.filter then - if not state.list[state.selected] then - return false - end - if state.list[state.selected].type ~= cmd.filter then - return false - end - end - - --if the directory is empty, and this command needs to work on an item, then abort and fallback to the next command - if cmd.codes and not state.list[state.selected] then - if cmd.codes["%f"] or cmd.codes["%F"] or cmd.codes["%n"] or cmd.codes["%N"] then - return false - end - end - - run_custom_command(cmd, { state.list[state.selected] }, state) - return true - end - - --runs the command on all multi-selected items - local selection = API.sort_keys(state.selection, function(item) - return not cmd.filter or item.type == cmd.filter - end) - if not next(selection) then - return false - end - - if cmd["multi-type"] == "concat" then - run_custom_command(cmd, selection, state) - elseif cmd["multi-type"] == "repeat" then - for i, _ in ipairs(selection) do - run_custom_command(cmd, { selection[i] }, state) - - if cmd.delay then - mp.add_timeout(cmd.delay, function() - API.coroutine.resume_err(co) - end) - coroutine.yield() - end - end - end - - --we passthrough by default if the command is not run on every selected item - if cmd.passthrough ~= nil then - return - end - - local num_selection = 0 - for _ in pairs(state.selection) do - num_selection = num_selection + 1 - end - return #selection == num_selection -end - ---recursively runs the keybind functions, passing down through the chain ---of keybinds with the same key value -local function run_keybind_recursive(keybind, state, co) - msg.trace("Attempting custom command:", utils.to_string(keybind)) - - --these are for the default keybinds, or from addons which use direct functions - local addon_fn = type(keybind.command) == "function" - local fn = addon_fn and keybind.command or custom_command - - if keybind.passthrough ~= nil then - fn(keybind, addon_fn and API.copy_table(state) or state, co) - if keybind.passthrough == true and keybind.prev_key then - run_keybind_recursive(keybind.prev_key, state, co) - end - else - if fn(keybind, state, co) == false and keybind.prev_key then - run_keybind_recursive(keybind.prev_key, state, co) - end - end -end - ---a wrapper to run a custom keybind as a lua coroutine -local function run_keybind_coroutine(key) - msg.debug("Received custom keybind " .. key.key) - local co = coroutine.create(run_keybind_recursive) - - local state_copy = { - directory = state.directory, - directory_label = state.directory_label, - list = state.list, --the list should remain unchanged once it has been saved to the global state, new directories get new tables - selected = state.selected, - selection = API.copy_table(state.selection), - parser = state.parser, - } - local success, err = coroutine.resume(co, key, state_copy, co) - if not success then - msg.error("error running keybind:", utils.to_string(key)) - API.traceback(err, co) - end -end - ---scans the given command table to identify if they contain any custom keybind codes -local function scan_for_codes(command_table, codes) - if type(command_table) ~= "table" then - return codes - end - for _, value in pairs(command_table) do - local type = type(value) - if type == "table" then - scan_for_codes(value, codes) - elseif type == "string" then - value:gsub("%%[" .. CUSTOM_KEYBIND_CODES .. "]", function(code) - codes[code] = true - end) - end - end - return codes -end - ---inserting the custom keybind into the keybind array for declaration when file-browser is opened ---custom keybinds with matching names will overwrite eachother -local function insert_custom_keybind(keybind) - --we'll always save the keybinds as either an array of command arrays or a function - if type(keybind.command) == "table" and type(keybind.command[1]) ~= "table" then - keybind.command = { keybind.command } - end - - keybind.codes = scan_for_codes(keybind.command, {}) - if not next(keybind.codes) then - keybind.codes = nil - end - keybind.prev_key = top_level_keys[keybind.key] - - table.insert(state.keybinds, { - keybind.key, - keybind.name, - function() - run_keybind_coroutine(keybind) - end, - keybind.flags or {}, - }) - top_level_keys[keybind.key] = keybind -end - ---loading the custom keybinds ---can either load keybinds from the config file, from addons, or from both -local function setup_keybinds() - if not o.custom_keybinds and not o.addons then - return - end - - --this is to make the default keybinds compatible with passthrough from custom keybinds - for _, keybind in ipairs(state.keybinds) do - top_level_keys[keybind[1]] = { key = keybind[1], name = keybind[2], command = keybind[3], flags = keybind[4] } - end - - --this loads keybinds from addons - if o.addons then - for i = #parsers, 1, -1 do - local parser = parsers[i] - if parser.keybinds then - for i, keybind in ipairs(parser.keybinds) do - --if addons use the native array command format, then we need to convert them over to the custom command format - if not keybind.key then - keybind = { key = keybind[1], name = keybind[2], command = keybind[3], flags = keybind[4] } - else - keybind = API.copy_table(keybind) - end - - keybind.name = parsers[parser].id .. "/" .. (keybind.name or tostring(i)) - insert_custom_keybind(keybind) - end - end - end - end - - --loads custom keybinds from file-browser-keybinds.json - if o.custom_keybinds then - local path = mp.command_native({ "expand-path", "~~/script-opts" }) .. "/file-browser-keybinds.json" - local custom_keybinds, err = io.open(path) - if not custom_keybinds then - return error(err) - end - - local json = custom_keybinds:read("*a") - custom_keybinds:close() - - json = utils.parse_json(json) - if not json then - return error("invalid json syntax for " .. path) - end - - for i, keybind in ipairs(json) do - keybind.name = "custom/" .. (keybind.name or tostring(i)) - insert_custom_keybind(keybind) - end - end -end - --------------------------------------------------------------------------------------------------------- --------------------------------------------API Functions------------------------------------------------ --------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------- - ---these functions we'll provide as-is -API.redraw = update_ass -API.rescan = update -API.browse_directory = browse_directory - -function API.clear_cache() - cache:clear() -end - ---a wrapper around scan_directory for addon API -function API.parse_directory(directory, parse_state) - if not parse_state then - parse_state = { source = "addon" } - elseif not parse_state.source then - parse_state.source = "addon" - end - return parse_directory(directory, parse_state) -end - ---register file extensions which can be opened by the browser -function API.register_parseable_extension(ext) - parseable_extensions[string.lower(ext)] = true -end -function API.remove_parseable_extension(ext) - parseable_extensions[string.lower(ext)] = nil -end - ---add a compatible extension to show through the filter, only applies if run during the setup() method -function API.add_default_extension(ext) - table.insert(compatible_file_extensions, ext) -end - ---add item to root at position pos -function API.insert_root_item(item, pos) - msg.verbose("adding item to root", item.label or item.name) - item.ass = item.ass or API.ass_escape(item.label or item.name) - item.type = "dir" - table.insert(root, pos or (#root + 1), item) -end - ---providing getter and setter functions so that addons can't modify things directly -function API.get_script_opts() - return API.copy_table(o) -end -function API.get_opt(key) - return o[key] -end -function API.get_extensions() - return API.copy_table(extensions) -end -function API.get_sub_extensions() - return API.copy_table(sub_extensions) -end -function API.get_audio_extensions() - return API.copy_table(audio_extensions) -end -function API.get_parseable_extensions() - return API.copy_table(parseable_extensions) -end -function API.get_state() - return API.copy_table(state) -end -function API.get_dvd_device() - return dvd_device -end -function API.get_parsers() - return API.copy_table(parsers) -end -function API.get_root() - return API.copy_table(root) -end -function API.get_directory() - return state.directory -end -function API.get_list() - return API.copy_table(state.list) -end -function API.get_current_file() - return API.copy_table(current_file) -end -function API.get_current_parser() - return state.parser:get_id() -end -function API.get_current_parser_keyname() - return state.parser.keybind_name or state.parser.name -end -function API.get_selected_index() - return state.selected -end -function API.get_selected_item() - return API.copy_table(state.list[state.selected]) -end -function API.get_open_status() - return not state.hidden -end -function API.get_parse_state(co) - return parse_states[co or coroutine.running() or ""] -end - -function API.set_empty_text(str) - state.empty_text = str - API.redraw() -end - -function API.set_selected_index(index) - if type(index) ~= "number" then - return false - end - if index < 1 then - index = 1 - end - if index > #state.list then - index = #state.list - end - state.selected = index - API.redraw() - return index -end - -function parser_API:get_index() - return parsers[self].index -end -function parser_API:get_id() - return parsers[self].id -end - ---runs choose_and_parse starting from the next parser -function parser_API:defer(directory) - msg.trace("deferring to other parsers...") - local list, opts = choose_and_parse(directory, self:get_index() + 1) - API.get_parse_state().already_deferred = true - return list, opts -end - ---a wrapper around coroutine.yield that aborts the coroutine if ---the parse request was cancelled by the user ---the coroutine is -function parse_state_API:yield(...) - local co = coroutine.running() - local is_browser = co == state.co - if self.source == "browser" and not is_browser then - msg.error("current coroutine does not match browser's expected coroutine - did you unsafely yield before this?") - error("current coroutine does not match browser's expected coroutine - aborting the parse") - end - - local result = table.pack(coroutine.yield(...)) - if is_browser and co ~= state.co then - msg.verbose("browser no longer waiting for list - aborting parse for", self.directory) - error(ABORT_ERROR) - end - return unpack(result, 1, result.n) -end - ---checks if the current coroutine is the one handling the browser's request -function parse_state_API:is_coroutine_current() - return coroutine.running() == state.co -end - --------------------------------------------------------------------------------------------------------- ------------------------------------------Setup Functions------------------------------------------------ --------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------- - -local API_MAJOR, API_MINOR, API_PATCH = API_VERSION:match("(%d+)%.(%d+)%.(%d+)") - ---checks if the given parser has a valid version number -local function check_api_version(parser) - local version = parser.version or "1.0.0" - - local major, minor = version:match("(%d+)%.(%d+)") - - if not major or not minor then - return msg.error("Invalid version number") - elseif major ~= API_MAJOR then - return msg.error( - "parser", - parser.name, - "has wrong major version number, expected", - ("v%d.x.x"):format(API_MAJOR), - "got", - "v" .. version - ) - elseif minor > API_MINOR then - msg.warn( - "parser", - parser.name, - "has newer minor version number than API, expected", - ("v%d.%d.x"):format(API_MAJOR, API_MINOR), - "got", - "v" .. version - ) - end - return true -end - ---create a unique id for the given parser -local function set_parser_id(parser) - local name = parser.name - if parsers[name] then - local n = 2 - name = parser.name .. "_" .. n - while parsers[name] do - n = n + 1 - name = parser.name .. "_" .. n - end - end - - parsers[name] = parser - parsers[parser] = { id = name } -end - -local function redirect_table(t) - return setmetatable({}, { __index = t }) -end - ---loads an addon in a separate environment -local function load_addon(path) - local name_sqbr = string.format("[%s]", path:match("/([^/]*)%.lua$")) - local addon_environment = redirect_table(_G) - addon_environment._G = addon_environment - - --gives each addon custom debug messages - addon_environment.package = redirect_table(addon_environment.package) - addon_environment.package.loaded = redirect_table(addon_environment.package.loaded) - local msg_module = { - log = function(level, ...) - msg.log(level, name_sqbr, ...) - end, - fatal = function(...) - return msg.fatal(name_sqbr, ...) - end, - error = function(...) - return msg.error(name_sqbr, ...) - end, - warn = function(...) - return msg.warn(name_sqbr, ...) - end, - info = function(...) - return msg.info(name_sqbr, ...) - end, - verbose = function(...) - return msg.verbose(name_sqbr, ...) - end, - debug = function(...) - return msg.debug(name_sqbr, ...) - end, - trace = function(...) - return msg.trace(name_sqbr, ...) - end, - } - addon_environment.print = msg_module.info - - addon_environment.require = function(module) - if module == "mp.msg" then - return msg_module - end - return require(module) - end - - local chunk, err - if setfenv then - --since I stupidly named a function loadfile I need to specify the global one - --I've been using the name too long to want to change it now - chunk, err = _G.loadfile(path) - if not chunk then - return msg.error(err) - end - setfenv(chunk, addon_environment) - else - chunk, err = _G.loadfile(path, "bt", addon_environment) - if not chunk then - return msg.error(err) - end - end - - local success, result = xpcall(chunk, API.traceback) - return success and result or nil -end - ---setup an internal or external parser -local function setup_parser(parser, file) - parser = setmetatable(parser, { __index = parser_API }) - parser.name = parser.name or file:gsub("%-browser%.lua$", ""):gsub("%.lua$", "") - - set_parser_id(parser) - if not check_api_version(parser) then - return msg.error("aborting load of parser", parser:get_id(), "from", file) - end - - msg.verbose("imported parser", parser:get_id(), "from", file) - - --sets missing functions - if not parser.can_parse then - if parser.parse then - parser.can_parse = function() - return true - end - else - parser.can_parse = function() - return false - end - end - end - - if parser.priority == nil then - parser.priority = 0 - end - if type(parser.priority) ~= "number" then - return msg.error("parser", parser:get_id(), "needs a numeric priority") - end - - table.insert(parsers, parser) -end - ---load an external addon -local function setup_addon(file, path) - if file:sub(-4) ~= ".lua" then - return msg.verbose(path, "is not a lua file - aborting addon setup") - end - - local addon_parsers = load_addon(path) - if not addon_parsers or type(addon_parsers) ~= "table" then - return msg.error("addon", path, "did not return a table") - end - - --if the table contains a priority key then we assume it isn't an array of parsers - if not addon_parsers[1] then - addon_parsers = { addon_parsers } - end - - for _, parser in ipairs(addon_parsers) do - setup_parser(parser, file) - end -end - ---loading external addons -local function setup_addons() - local addon_dir = mp.command_native({ "expand-path", o.addon_directory .. "/" }) - local files = utils.readdir(addon_dir) - if not files then - error("could not read addon directory") - end - - for _, file in ipairs(files) do - setup_addon(file, addon_dir .. file) - end - table.sort(parsers, function(a, b) - return a.priority < b.priority - end) - - --we want to store the indexes of the parsers - for i = #parsers, 1, -1 do - parsers[parsers[i]].index = i - end - - --we want to run the setup functions for each addon - for index, parser in ipairs(parsers) do - if parser.setup then - local success = xpcall(function() - parser:setup() - end, API.traceback) - if not success then - msg.error( - "parser", - parser:get_id(), - "threw an error in the setup method - removing from list of parsers" - ) - table.remove(parsers, index) - end - end - end -end - ---sets up the compatible extensions list -local function setup_extensions_list() - --setting up subtitle extensions - for ext in API.iterate_opt(o.subtitle_extensions:lower()) do - sub_extensions[ext] = true - extensions[ext] = true - end - - --setting up audio extensions - for ext in API.iterate_opt(o.audio_extensions:lower()) do - audio_extensions[ext] = true - extensions[ext] = true - end - - --adding file extensions to the set - for _, ext in ipairs(compatible_file_extensions) do - extensions[ext] = true - end - - --adding extra extensions on the whitelist - for str in API.iterate_opt(o.extension_whitelist:lower()) do - extensions[str] = true - end - - --removing extensions that are in the blacklist - for str in API.iterate_opt(o.extension_blacklist:lower()) do - extensions[str] = nil - end -end - ---splits the string into a table on the semicolons -local function setup_root() - root = {} - for str in API.iterate_opt(o.root) do - local path = mp.command_native({ "expand-path", str }) - path = API.fix_path(path, true) - - local temp = { name = path, type = "dir", label = str, ass = API.ass_escape(str, true) } - - root[#root + 1] = temp - end -end - -setup_root() - -setup_parser(file_parser, "file-browser.lua") -if o.addons then - --all of the API functions need to be defined before this point for the addons to be able to access them safely - setup_addons() -end - ---these need to be below the addon setup in case any parsers add custom entries -setup_extensions_list() -setup_keybinds() - ------------------------------------------------------------------------------------------- -------------------------------Other Script Compatability---------------------------------- ------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------- - -local function scan_directory_json(directory, response_str) - if not directory then - msg.error("did not receive a directory string") - return - end - if not response_str then - msg.error("did not receive a response string") - return - end - - directory = mp.command_native({ "expand-path", directory }, "") - if directory ~= "" then - directory = API.fix_path(directory, true) - end - msg.verbose( - ("recieved %q from 'get-directory-contents' script message - returning result to %q"):format( - directory, - response_str - ) - ) - - local list, opts = parse_directory(directory, { source = "script-message" }) - opts.API_VERSION = API_VERSION - - local err - list, err = API.format_json_safe(list) - if not list then - msg.error(err) - end - - opts, err = API.format_json_safe(opts) - if not opts then - msg.error(err) - end - - mp.commandv("script-message", response_str, list or "", opts or "") -end - -pcall(function() - local input = require("user-input-module") - mp.add_key_binding("Alt+o", "browse-directory/get-user-input", function() - input.get_user_input(browse_directory, { request_text = "open directory:" }) - end) -end) - ------------------------------------------------------------------------------------------- ---------------------------------mpv API Callbacks----------------------------------------- ------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------- - ---we don't want to add any overhead when the browser isn't open -mp.observe_property("path", "string", function(_, path) - if not state.hidden then - update_current_directory(_, path) - update_ass() - else - state.flag_update = true - end -end) - ---updates the dvd_device -mp.observe_property("dvd-device", "string", function(_, device) - if not device or device == "" then - device = "/dev/dvd/" - end - dvd_device = API.fix_path(device, true) -end) - ---declares the keybind to open the browser -mp.add_key_binding("MENU", "browse-files", toggle) -mp.add_key_binding("Ctrl+o", "open-browser", open) - ---allows keybinds/other scripts to auto-open specific directories -mp.register_script_message("browse-directory", browse_directory) - ---allows other scripts to request directory contents from file-browser -mp.register_script_message("get-directory-contents", function(directory, response_str) - API.coroutine.run(scan_directory_json, directory, response_str) -end) diff --git a/home/ooks/modules/programs/neofetch/default.nix b/home/ooks/modules/programs/neofetch/default.nix deleted file mode 100644 index a521339..0000000 --- a/home/ooks/modules/programs/neofetch/default.nix +++ /dev/null @@ -1,5 +0,0 @@ -{ config, lib, pkgs, ... }: - -{ - home.packages = [ pkgs.neofetch ]; -} diff --git a/home/ooks/modules/programs/notify/default.nix b/home/ooks/modules/programs/notify/default.nix deleted file mode 100644 index 2f98e3b..0000000 --- a/home/ooks/modules/programs/notify/default.nix +++ /dev/null @@ -1,6 +0,0 @@ -{ config, pkgs, ... }: -{ - services.mako = { - enable = true; - }; -} diff --git a/home/ooks/modules/programs/zathura/default.nix b/home/ooks/modules/programs/zathura/default.nix deleted file mode 100644 index 378b904..0000000 --- a/home/ooks/modules/programs/zathura/default.nix +++ /dev/null @@ -1,76 +0,0 @@ -{ lib, pkgs, user, ... }: - -{ - programs.zathura = { - enable = true; - extraConfig = '' - # Zathura configuration file - # See man `man zathurarc' - - # Open document in fit-width mode by default - set adjust-open "best-fit" - - # One page per row by default - set pages-per-row 1 - - #stop at page boundries - set scroll-page-aware "true" - set scroll-full-overlap 0.01 - set scroll-step 100 - - #zoom settings - set zoom-min 10 - set guioptions "" - - # zathurarc-dark - - set font "JetBrains Mono Nerd Font 15" - set default-fg "#96CDFB" - set default-bg "#1A1823" - - set completion-bg "#1A1823" - set completion-fg "#96cdfb" - set completion-highlight-bg "#302D41" - set completion-highlight-fg "#96cdfb" - set completion-group-bg "#1a1823" - set completion-group-fg "#89DCEB" - - set statusbar-fg "#C9CBFF" - set statusbar-bg "#1A1823" - set statusbar-h-padding 10 - set statusbar-v-padding 10 - - set notification-bg "#1A1823" - set notification-fg "#D9E0EE" - set notification-error-bg "#d9e0ee" - set notification-error-fg "#D9E0EE" - set notification-warning-bg "#FAE3B0" - set notification-warning-fg "#D9E0EE" - set selection-notification "true" - - set inputbar-fg "#C9CBFF" - set inputbar-bg "#1A1823" - - set recolor "true" - set recolor-lightcolor "#D9E0EE" - set recolor-darkcolor "#1A1823" - - set index-fg "#96cdfb" - set index-bg "#1A1823" - set index-active-fg "#96cdfb" - set index-active-bg "#1A1823" - - set render-loading-bg "#1A1823" - set render-loading-fg "#96cdfb" - - set highlight-color "#96cdfb" - set highlight-active-color "#DDB6F2" - - - set render-loading "false" - set scroll-step 50 - - set selection-clipboard clipboard - ''; - }; -} diff --git a/home/ooks/modules/environment/hypr-variable.nix b/home/ooks/opt/desktop/env/hypr-variable.nix similarity index 100% rename from home/ooks/modules/environment/hypr-variable.nix rename to home/ooks/opt/desktop/env/hypr-variable.nix diff --git a/home/ooks/modules/feature/desktop/hyprland/config.nix b/home/ooks/opt/desktop/hyprland/config.nix similarity index 100% rename from home/ooks/modules/feature/desktop/hyprland/config.nix rename to home/ooks/opt/desktop/hyprland/config.nix diff --git a/home/ooks/modules/common/default.nix b/home/ooks/opt/desktop/hyprland/home.nix similarity index 100% rename from home/ooks/modules/common/default.nix rename to home/ooks/opt/desktop/hyprland/home.nix diff --git a/home/ooks/opt/desktop/music/default.nix b/home/ooks/opt/desktop/music/default.nix new file mode 100644 index 0000000..2f59e43 --- /dev/null +++ b/home/ooks/opt/desktop/music/default.nix @@ -0,0 +1,7 @@ +{ pkgs, ... }: +{ + home.packages = with pkgs; [ + alsa-utils + spotify-player + ]; +} diff --git a/home/ooks/modules/feature/desktop/common/fonts.nix b/home/ooks/opt/desktop/standard/fonts.nix similarity index 100% rename from home/ooks/modules/feature/desktop/common/fonts.nix rename to home/ooks/opt/desktop/standard/fonts.nix diff --git a/home/ooks/modules/feature/desktop/common/gtk.nix b/home/ooks/opt/desktop/standard/gtk.nix similarity index 100% rename from home/ooks/modules/feature/desktop/common/gtk.nix rename to home/ooks/opt/desktop/standard/gtk.nix diff --git a/home/ooks/modules/feature/desktop/common/pavucontrol.nix b/home/ooks/opt/desktop/standard/pavucontrol.nix similarity index 100% rename from home/ooks/modules/feature/desktop/common/pavucontrol.nix rename to home/ooks/opt/desktop/standard/pavucontrol.nix diff --git a/home/ooks/modules/feature/desktop/common/qt.nix b/home/ooks/opt/desktop/standard/qt.nix similarity index 100% rename from home/ooks/modules/feature/desktop/common/qt.nix rename to home/ooks/opt/desktop/standard/qt.nix diff --git a/home/ooks/opt/desktop/standard/wayland/default.nix b/home/ooks/opt/desktop/standard/wayland/default.nix new file mode 100644 index 0000000..1e45633 --- /dev/null +++ b/home/ooks/opt/desktop/standard/wayland/default.nix @@ -0,0 +1,38 @@ +{ pkgs, ... }: +{ + imports = [ + ./mako.nix + ./kitty.nix + ./firefox.nix + ./zathura.nix + ./gammastep.nix + ]; + + xdg.mimeApps.enable = true; + home.packages = with pkgs; [ + grim + mkv + gtk3 + pulseaudio + slurp + wf-recorder + wl-clipboard + wl-mirror + wl-mirror-pick + xdg-utils + ydotool + ]; + + home.sessionVariables = { + MOZ_ENABLE_WAYLAND = 1; + QT_QPA_PLATFORM = "wayland"; + LIBSEAT_BACKEND = "logind"; + EDITOR = "nvim"; + BROWSER = "firefox"; + }; + + + + + +} diff --git a/home/ooks/opt/desktop/standard/wayland/firefox.nix b/home/ooks/opt/desktop/standard/wayland/firefox.nix new file mode 100644 index 0000000..c8b4606 --- /dev/null +++ b/home/ooks/opt/desktop/standard/wayland/firefox.nix @@ -0,0 +1,51 @@ +{ pkgs, lib, inputs, ... }: + +let + addons = inputs.firefox-addons.packages.${pkgs.system}; +in +{ + programs.browserpass.enable = true; + programs.firefox = { + enable = true; + profiles.ooks = { + bookmarks = { }; + extensions = with addons; [ + ublock-origin + browserpass + onepassword-password-manager + stylus + surfingkeys + ]; + bookmarks = { }; + settings = { + "browser.disableResetPrompt" = true; + "browser.download.panel.shown" = true; + "browser.download.useDownloadDir" = false; + "browser.newtabpage.activity-stream.showSponsoredTopSites" = false; + "browser.shell.checkDefaultBrowser" = false; + "browser.shell.defaultBrowserCheckCount" = 1; + "browser.startup.homepage" = "https://start.duckduckgo.com"; + "browser.uiCustomization.state" = ''{"placements":{"widget-overflow-fixed-list":[],"nav-bar":["back-button","forward-button","stop-reload-button","home-button","urlbar-container","downloads-button","library-button","ublock0_raymondhill_net-browser-action","_testpilot-containers-browser-action"],"toolbar-menubar":["menubar-items"],"TabsToolbar":["tabbrowser-tabs","new-tab-button","alltabs-button"],"PersonalToolbar":["import-button","personal-bookmarks"]},"seen":["save-to-pocket-button","developer-button","ublock0_raymondhill_net-browser-action","_testpilot-containers-browser-action"],"dirtyAreaCache":["nav-bar","PersonalToolbar","toolbar-menubar","TabsToolbar","widget-overflow-fixed-list"],"currentVersion":18,"newElementCount":4}''; + "dom.security.https_only_mode" = true; + "identity.fxaccounts.enabled" = false; + "privacy.trackingprotection.enabled" = true; + "signon.rememberSignons" = false; + }; + }; + }; + + home = { + sessionVariables = { + # https://github.com/NixOS/nixpkgs/issues/238025 + TZ = "/etc/localtime"; + BROWSER = "firefox"; + }; + }; + + xdg.mimeApps.defaultApplications = { + "text/html" = [ "firefox.desktop" ]; + "text/xml" = [ "firefox.desktop" ]; + "x-scheme-handler/http" = [ "firefox.desktop" ]; + "x-scheme-handler/https" = [ "firefox.desktop" ]; + }; +} diff --git a/home/ooks/opt/desktop/standard/wayland/gammastep.nix b/home/ooks/opt/desktop/standard/wayland/gammastep.nix new file mode 100644 index 0000000..498756e --- /dev/null +++ b/home/ooks/opt/desktop/standard/wayland/gammastep.nix @@ -0,0 +1,13 @@ +{ + services.gammastep = { + enable = true; + provider = "geoclue2"; + temperature = { + day = 6000; + night = 4600; + }; + settings = { + general.adjustment-method = "wayland"; + }; + }; +} diff --git a/home/ooks/opt/desktop/standard/wayland/kitty.nix b/home/ooks/opt/desktop/standard/wayland/kitty.nix new file mode 100644 index 0000000..3528e1f --- /dev/null +++ b/home/ooks/opt/desktop/standard/wayland/kitty.nix @@ -0,0 +1,66 @@ +{ config, pkgs, ... }: + +let + inherit (config.colorscheme) colors; + kitty-xterm = pkgs.writeShellScriptBin "xterm" '' + ${config.programs.kitty.package}/bin/kitty -1 "$@" + ''; +in +{ + home = { + packages = [ kitty-xterm ]; + sessionVariables = { + TERMINAL = "kitty -1"; + }; + }; + + + programs.kitty = { + enable = true; + font = { + name = config.fontProfiles.monospace.family; + size = 12; + }; + settings = { + shell_integration = "no-rc"; # I prefer to do it manually + scrollback_lines = 4000; + scrollback_pager_history_size = 2048; + window_padding_width = 15; + foreground = "#${colors.base05}"; + background = "#${colors.base00}"; + selection_background = "#${colors.base05}"; + selection_foreground = "#${colors.base00}"; + url_color = "#${colors.base04}"; + cursor = "#${colors.base05}"; + active_border_color = "#${colors.base03}"; + inactive_border_color = "#${colors.base01}"; + active_tab_background = "#${colors.base00}"; + active_tab_foreground = "#${colors.base05}"; + inactive_tab_background = "#${colors.base01}"; + inactive_tab_foreground = "#${colors.base04}"; + tab_bar_background = "#${colors.base01}"; + color0 = "#${colors.base00}"; + color1 = "#${colors.base08}"; + color2 = "#${colors.base0B}"; + color3 = "#${colors.base0A}"; + color4 = "#${colors.base0D}"; + color5 = "#${colors.base0E}"; + color6 = "#${colors.base0C}"; + color7 = "#${colors.base05}"; + color8 = "#${colors.base03}"; + color9 = "#${colors.base08}"; + color10 = "#${colors.base0B}"; + color11 = "#${colors.base0A}"; + color12 = "#${colors.base0D}"; + color13 = "#${colors.base0E}"; + color14 = "#${colors.base0C}"; + color15 = "#${colors.base07}"; + color16 = "#${colors.base09}"; + color17 = "#${colors.base0F}"; + color18 = "#${colors.base01}"; + color19 = "#${colors.base02}"; + color20 = "#${colors.base04}"; + color21 = "#${colors.base06}"; + }; + }; +} diff --git a/home/ooks/opt/desktop/standard/wayland/mako.nix b/home/ooks/opt/desktop/standard/wayland/mako.nix new file mode 100644 index 0000000..a0bb1e2 --- /dev/null +++ b/home/ooks/opt/desktop/standard/wayland/mako.nix @@ -0,0 +1,22 @@ +{ config, ... }: +let inherit (config.colorscheme) colors kind; +in { + services.mako = { + enable = true; + iconPath = + if kind == "dark" then + "${config.gtk.iconTheme.package}/share/icons/Papirus-Dark" + else + "${config.gtk.iconTheme.package}/share/icons/Papirus-Light"; + font = "${config.fontProfiles.regular.family} 12"; + padding = "10,20"; + anchor = "top-center"; + width = 400; + height = 150; + borderSize = 2; + defaultTimeout = 12000; + backgroundColor = "#${colors.base00}dd"; + borderColor = "#${colors.base03}dd"; + textColor = "#${colors.base05}dd"; + }; +} diff --git a/home/ooks/opt/desktop/standard/wayland/zathura.nix b/home/ooks/opt/desktop/standard/wayland/zathura.nix new file mode 100644 index 0000000..8348cc1 --- /dev/null +++ b/home/ooks/opt/desktop/standard/wayland/zathura.nix @@ -0,0 +1,32 @@ +{ config, ... }: +let inherit (config.colorscheme) colors; +in { + programs.zathura = { + enable = true; + options = { + selection-clipboard = "clipboard"; + font = "${config.fontProfiles.regular.family} 12"; + recolor = true; + default-bg = "#${colors.base00}"; + default-fg = "#${colors.base01}"; + statusbar-bg = "#${colors.base02}"; + statusbar-fg = "#${colors.base04}"; + inputbar-bg = "#${colors.base00}"; + inputbar-fg = "#${colors.base07}"; + notification-bg = "#${colors.base00}"; + notification-fg = "#${colors.base07}"; + notification-error-bg = "#${colors.base00}"; + notification-error-fg = "#${colors.base08}"; + notification-warning-bg = "#${colors.base00}"; + notification-warning-fg = "#${colors.base08}"; + highlight-color = "#${colors.base0A}"; + highlight-active-color = "#${colors.base0D}"; + completion-bg = "#${colors.base01}"; + completion-fg = "#${colors.base05}"; + completions-highlight-bg = "#${colors.base0D}"; + completions-highlight-fg = "#${colors.base07}"; + recolor-lightcolor = "#${colors.base00}"; + recolor-darkcolor = "#${colors.base06}"; + }; + }; +} diff --git a/home/ooks/opt/nvim/default.nix b/home/ooks/opt/nvim/default.nix new file mode 100644 index 0000000..b4ca427 --- /dev/null +++ b/home/ooks/opt/nvim/default.nix @@ -0,0 +1,14 @@ +{ pkgs, ... }: + +{ + + home.sessionVariables.EDITOR = "nvim"; + + programs = { + neovim = { + enable = true; + viAlias = true; + }; + }; + +} diff --git a/home/ooks/modules/feature/shell/bash.nix b/home/ooks/opt/shell/bash.nix similarity index 100% rename from home/ooks/modules/feature/shell/bash.nix rename to home/ooks/opt/shell/bash.nix diff --git a/home/ooks/modules/feature/shell/bat.nix b/home/ooks/opt/shell/bat.nix similarity index 100% rename from home/ooks/modules/feature/shell/bat.nix rename to home/ooks/opt/shell/bat.nix diff --git a/home/ooks/modules/feature/shell/default.nix b/home/ooks/opt/shell/default.nix similarity index 100% rename from home/ooks/modules/feature/shell/default.nix rename to home/ooks/opt/shell/default.nix diff --git a/home/ooks/modules/feature/shell/fish.nix b/home/ooks/opt/shell/fish.nix similarity index 100% rename from home/ooks/modules/feature/shell/fish.nix rename to home/ooks/opt/shell/fish.nix diff --git a/home/ooks/modules/feature/shell/git.nix b/home/ooks/opt/shell/git.nix similarity index 100% rename from home/ooks/modules/feature/shell/git.nix rename to home/ooks/opt/shell/git.nix diff --git a/home/ooks/modules/feature/shell/joshuto/config/bookmarks.toml b/home/ooks/opt/shell/joshuto/config/bookmarks.toml similarity index 100% rename from home/ooks/modules/feature/shell/joshuto/config/bookmarks.toml rename to home/ooks/opt/shell/joshuto/config/bookmarks.toml diff --git a/home/ooks/modules/feature/shell/joshuto/config/joshuto.toml b/home/ooks/opt/shell/joshuto/config/joshuto.toml similarity index 100% rename from home/ooks/modules/feature/shell/joshuto/config/joshuto.toml rename to home/ooks/opt/shell/joshuto/config/joshuto.toml diff --git a/home/ooks/modules/feature/shell/joshuto/config/keymap.toml b/home/ooks/opt/shell/joshuto/config/keymap.toml similarity index 100% rename from home/ooks/modules/feature/shell/joshuto/config/keymap.toml rename to home/ooks/opt/shell/joshuto/config/keymap.toml diff --git a/home/ooks/modules/feature/shell/joshuto/config/mimetype.toml b/home/ooks/opt/shell/joshuto/config/mimetype.toml similarity index 100% rename from home/ooks/modules/feature/shell/joshuto/config/mimetype.toml rename to home/ooks/opt/shell/joshuto/config/mimetype.toml diff --git a/home/ooks/modules/feature/shell/joshuto/config/preview_file.sh b/home/ooks/opt/shell/joshuto/config/preview_file.sh similarity index 100% rename from home/ooks/modules/feature/shell/joshuto/config/preview_file.sh rename to home/ooks/opt/shell/joshuto/config/preview_file.sh diff --git a/home/ooks/modules/feature/shell/joshuto/config/theme.toml b/home/ooks/opt/shell/joshuto/config/theme.toml similarity index 100% rename from home/ooks/modules/feature/shell/joshuto/config/theme.toml rename to home/ooks/opt/shell/joshuto/config/theme.toml diff --git a/home/ooks/modules/feature/shell/joshuto/default.nix b/home/ooks/opt/shell/joshuto/default.nix similarity index 100% rename from home/ooks/modules/feature/shell/joshuto/default.nix rename to home/ooks/opt/shell/joshuto/default.nix diff --git a/home/ooks/modules/feature/shell/pfetch.nix b/home/ooks/opt/shell/pfetch.nix similarity index 100% rename from home/ooks/modules/feature/shell/pfetch.nix rename to home/ooks/opt/shell/pfetch.nix diff --git a/home/ooks/modules/feature/shell/starship.nix b/home/ooks/opt/shell/starship.nix similarity index 100% rename from home/ooks/modules/feature/shell/starship.nix rename to home/ooks/opt/shell/starship.nix diff --git a/home/ooks/global/default.nix b/home/ooks/standard/default.nix similarity index 94% rename from home/ooks/global/default.nix rename to home/ooks/standard/default.nix index 0b52322..c351dee 100644 --- a/home/ooks/global/default.nix +++ b/home/ooks/standard/default.nix @@ -5,8 +5,9 @@ in { imports = [ inputs.nix-colors.homeManagerModule - ../modules/editors/nvim/ ../modules/common/ + ../modules/feature/shell/ + ../modules/feature/nvim/ ] ++ (builtins.attrValues outputs.homeManagerModules); nixpkgs = {