{"id":37533001,"url":"https://github.com/maxkagamine/dotfiles","last_synced_at":"2026-01-16T08:34:14.947Z","repository":{"id":32338994,"uuid":"132285085","full_name":"maxkagamine/dotfiles","owner":"maxkagamine","description":"I used to be a terminal like you...","archived":false,"fork":false,"pushed_at":"2025-12-26T20:01:38.000Z","size":54751,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-12-28T10:03:31.885Z","etag":null,"topics":["bash","dotfiles","mod-manager","wsl"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/maxkagamine.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2018-05-05T21:15:29.000Z","updated_at":"2025-12-26T20:01:42.000Z","dependencies_parsed_at":"2023-01-14T21:00:00.210Z","dependency_job_id":"76e01314-278e-43f5-b4f3-5733caaafa49","html_url":"https://github.com/maxkagamine/dotfiles","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/maxkagamine/dotfiles","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxkagamine%2Fdotfiles","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxkagamine%2Fdotfiles/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxkagamine%2Fdotfiles/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxkagamine%2Fdotfiles/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/maxkagamine","download_url":"https://codeload.github.com/maxkagamine/dotfiles/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxkagamine%2Fdotfiles/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28478047,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-16T06:30:42.265Z","status":"ssl_error","status_checked_at":"2026-01-16T06:30:16.248Z","response_time":107,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bash","dotfiles","mod-manager","wsl"],"created_at":"2026-01-16T08:34:14.875Z","updated_at":"2026-01-16T08:34:14.934Z","avatar_url":"https://github.com/maxkagamine.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# maxkagamine's dotfiles\n\n\u003cp align=\"center\"\u003e\u003cimg src=\".github/images/screenshot.png\" /\u003e\u003c/p\u003e\n\n**[\u003cimg src=\".github/images/icons/terminal.png\" height=\"19\" valign=\"middle\" /\u003e bash](#-bash)**\n\u0026nbsp;|\u0026nbsp;\n**[\u003cimg src=\".github/images/icons/git.svg\" height=\"16\" valign=\"middle\" /\u003e git](#-git)**\n\u0026nbsp;|\u0026nbsp;\n**[\u003cimg src=\".github/images/icons/windows.svg\" height=\"14\" valign=\"middle\" /\u003e wsl](#-wsl)**\n\u0026nbsp;|\u0026nbsp;\n**[\u003cimg src=\".github/images/icons/unraid.png\" height=\"14\" valign=\"middle\" /\u003e unraid](#-unraid)**\n\u0026nbsp;|\u0026nbsp;\n**[\u003cimg src=\".github/images/icons/mkvtoolnix.png\" height=\"14\" valign=\"middle\" /\u003e mkvtoolnix](#-mkvtoolnix)**\n\u0026nbsp;|\n**[⚙\u0026hairsp;misc utils](#misc-utils)**\n\u0026nbsp;|\u0026nbsp;\n**[\u003cimg src=\".github/images/icons/sweetroll.png\" height=\"14\" valign=\"middle\" /\u003e sweetroll](#-sweetroll)**\n\u0026nbsp;|\u0026nbsp;\n[more\u0026hellip;](#fzf)\n\nBehold, [GNU Stow][stow]: the mod manager for your Linux home directory! Anyone who's used Vortex or MO2 to mod games like [Skyrim][skyrim] will find this familiar: mod (dot) files are organized into separate folders, and the mod manager (stow) combines them into the game (home) directory using symlinks.\n\nAs Stow doesn't ([yet][stow todo]) support pre/post-install hooks, I'm emulating it by placing Makefiles in the mod directories and including them from [the main Makefile](Makefile). This keeps mods self-contained with any needed package install steps etc., and also allows mods to _depend on other mods_, with Make automatically figuring out which mods need to be installed and in what order:\n\n![Dependency graph](.github/images/graph.png)\n\nBest part about this setup is it doesn't require any dotfile-specific frameworks or YAML files. Just run `make`.\n\nNotable mods (**headers link to the relevant file/directory**):\n\n## \u003cimg src=\".github/images/icons/terminal.png\" height=\"30\" align=\"top\" /\u003e [bash](mods/bash)\n\nApplies the convention of loading configuration files from a directory to bashrc: after setting some common aliases and such, sources every file in **~/.config/bashrc.d/**. This way the other mods can put their stuff in e.g. bashrc.d/git.sh rather than one big file as is traditional. (The system-wide /etc/profile already does this with /etc/profile.d/*.)\n\n\u003e (Psst: if you want to change your ls colors, [my file](mods/bash/.config/dircolors) might be an easier starting-off point than `--print-database`. I spent the time formatting it so you don't have to.)\n\nThere's also a suite of [Linq functions](https://github.com/maxkagamine/dotfiles/blob/master/mods/bash/.bashrc#:~:text=Linq) tucked away here which .NET devs will find familiar.\n\n### [~/.local/lib/common.sh](mods/bash/.local/lib/common.sh)\n\nCommon functions for bash scripts:\n\n- **`throw`** — Prints an error prefixed with the name of the script and exits with 1. Simplifies \"throwing exceptions\" in scripts.\n\n- **`parse_args`** — Parses GNU-style options. Supports short and long options, combined/bundled short options, options with values (in any format: `--foo value`, `-f value`, `--foo=value`, or `-fvalue`), repeated options (by using a callback), option aliases, and the `--` to stop option processing.\n\n- **`expand_directories`** — Reads paths and replaces directories with their containing files while passing along file paths as-is. Has various options and can operate either as part of a pipe or in a manner similar to readarray.\n\nFor examples, see [**upscale**](mods/misc-utils/.local/bin/upscale), [**mkv-ls**](mods/mkvtoolnix/.local/bin/mkv-ls) (\u0026 the other mkv scripts, [detailed below ↓](#-mkvtoolnix)), and [**weigh**](/mods/misc-utils/.local/bin/weigh).\n\n## \u003cimg src=\".github/images/icons/git.svg\" align=\"top\" height=\"30\" /\u003e [git](mods/git)\n\n[Git aliases](mods/git/.config/bashrc.d/git.sh) and [aliases](mods/git/.config/git/config) (including my favorite: the alias alias, `git alias`) + the \"gg\" function I use so much ~~[I wrote an article about it][gg-faster-git-commits]~~, and of course what would be a _Max Kagamine_ system without a myriad of ~~[Skyrim references][fus-ro-dah]~~ (brace yourself).\n\nOh and _[empty string is git status](mods/git/.config/bashrc.d/zz_empty_string_is_git_status.sh):_\n\n\u003e \u003ci\u003e\u003cruby\u003eMukashi mukashi \u003crp\u003e(\u003c/rp\u003e\u003crt\u003eOnce upon a time\u003c/rt\u003e\u003crp\u003e)\u003c/rp\u003e...\u003c/i\u003e  \n\u003e “`git status` is too long,” Max thought as he sat at his desk.  \n\u003e So with the magic of `git config --global`, it became `git s`.  \n\u003e ...But that was too long.  \n\u003e So he aliased it to `gs` in his bashrc; just two letters, see?  \n\u003e But these two, too, were too long!  \n\u003e So he dropped the 's', just `g`, now, short as can be.  \n\u003e But even one letter 'twas one letter too long!  \n\u003e And so it became, the shortest `git status` of all:  \n\u003e **Empty string!**\n\nTrue story. This is a trick I learned a long time ago: using `PROMPT_COMMAND` (or a [precmd hook][bash-preexec]) to compare the last history entry to that which was seen the previous time the prompt was shown, as hitting enter at an empty prompt will run the prompt command again, but the last history number will be the same. Side effect is hitting \u003ckbd\u003eCtrl+C\u003c/kbd\u003e (but not \u003ckbd\u003eCtrl+U\u003c/kbd\u003e or \u003ckbd\u003eAlt+Shift+#\u003c/kbd\u003e) at a prompt will trigger git status, too.\n\nAlso, check out [\u003cimg src=\".github/images/icons/git.svg\" height=\"15\" /\u003e**git-branch-fzf**](mods/git/.local/bin/git-branch-fzf), my awesome [fzf]-powered interactive branch switcher with keyboard shortcuts to toggle remote branches, delete branches (including remote and even the current branch!), and to [fetch the latest](mods/git/.local/bin/git-checkout-latest) of a branch **before** switching.\n\n## \u003cimg src=\".github/images/icons/windows.svg\" height=\"22\" /\u003e\u0026hairsp; [wsl](mods/wsl)\n\nFor \"Tamriel,\" my main machine running the [Windows Subsystem for Linux][wsl], because I can't computer without a command line but also can't not Windows.\n\n[This Alt+V keybind to paste Windows paths as Linux paths](mods/wsl/.config/bashrc.d/wsl.sh) should be useful to any WSL users. `unclip` is an alias for `xsel -bo`. I've created [a WSL version of xsel in Rust](https://github.com/maxkagamine/wsl-tools) that lets commands which aren't WSL-aware copy things to the Windows clipboard (faster and less janky / proper Unicode handling compared to PowerShell-based approaches).\n\nI've also found it helpful to [define a command_not_found_handle](mods/bash/.bashrc) so that I can run Windows exe's without the .exe extension. (Note the `complete -I` below it, which overrides bash's default command tab completion to remove the unnecessary \".exe\". Both of these also check in node modules, so I can run npm package commands without having to prefix them with \"npx\".)\n\n\u003cdetails open\u003e\n\u003csummary\u003e\u003ch3\u003eUsing Yubikey for GPG \u0026 SSH in WSL\u003c/h3\u003e\u003c/summary\u003e\n\nIn the past, I was using [wsl2-ssh-pageant](https://github.com/BlackReloaded/wsl2-ssh-pageant) which uses `socat` to replace the gpg-agent socket with one that runs an exe that bridges Gpg4win. The bridge itself worked well (with a [small fix](https://github.com/maxkagamine/dotfiles/commit/fddf1ee8def1667b04f465f5a52e7a6f4c73bc30)), but despite [my best efforts](https://github.com/maxkagamine/dotfiles/commit/2f61b2820019fc591b33a2d6a47dcb6622cf1eee) I could never get the socket shenanigans to work reliably.\n\nIn the end that was too much of a hassle, so I switched to using [usbipd-win](https://github.com/dorssel/usbipd-win) which connects the Yubikey directly to Linux. This has proved to be much simpler. The downside is that connecting a device to WSL means disconnecting it from Windows; if you need the Yubikey to log into a website, you'll have to temporarily detach it from WSL. (I defined [aliases](mods/wsl/.config/bashrc.d/wsl.sh) to make this easy.)\n\n1. Open a terminal as admin\n2. `winget.exe install --exact dorssel.usbipd-win`\n3. Once installed, restart the terminal (as admin again), then run `usbipd.exe list` and note the Yubikey's VID:PID (should say \"Smartcard Reader\")\n4. Bind the device using `usbipd.exe bind --hardware-id \u003cVID:PID\u003e`. You only have to do this once per device.\n5. Attach it to WSL using `usbipd.exe attach -wi \u003cVID:PID\u003e`. The Yubikey should show up now if you run `lsusb` in WSL. (Arch users: `sudo pacman -S usbutils`)\n   - _Note: If in the future you ever get an error saying your kernel doesn't support USBIP and to run `wsl --update`, but you already ran `wsl --update`, try running the winget command to update usbipd-win instead. I've only had this happen once and it was due to a major version upgrade._\n7. Ubuntu users: `sudo apt install scdaemon` (Arch users can skip this, as it's included in the gnupg package.)\n8. If you run `gpg --card-status` now in WSL, it'll give you an error that says \"gpg: selecting card failed: No such device\" unless you run gpg as root. This has to do with the permissions of the usb device in /dev. There are two ways to fix this:\n   - What was working for me until I tried upgrading to Ubuntu 24.04 (\"noble\") was to install `pcscd` in addition to `scdaemon`. This is a separate daemon that handles communication with smartcards. Ubuntu 24.04 however introduced some kind of security policy change that broke it. Incidentally, this was the final straw that got me to finally switch to Arch, so unfortunately I don't have a solution. It might work for you, but I suggest trying the below first; you may not need pcscd at all:\n   - _**Preferred way:**_ Add a udev rule to fix the device permissions:\n     1. `sudo nano /usr/lib/udev/rules.d/yubikey.rules` and add the following:\n        ```\n        SUBSYSTEM==\"usb\", ATTR{idVendor}==\"*\", ATTR{idProduct}==\"*\", MODE=\"666\"\n        ```\n        Some notes:\n        - This will simply make all USB devices accessible to all users, which for WSL purposes is fine.\n        - The _correct way_, it seems, is to [apply the `uaccess` tag as shown here](https://wiki.archlinux.org/title/Udev#Allowing_regular_users_to_use_devices) and let systemd handle permissions instead.\n        - Originally I was assigning my own user and group to the device with `SUBSYSTEM==\"usb\", ATTR{idVendor}==\"1050\", ATTR{idProduct}==\"0406\", MODE=\"666\", OWNER=\"max\", GROUP=\"max\"`, but this stopped working after a system update. Running `udevadm test $(udevadm info --query=path --name=/dev/usb/hiddev0)` revealed an error \"User 'max' is not a system user, ignoring.\"\n          - Removing the `OWNER` and using `GROUP=\"wheel\"` _did_ work (as `wheel` is a \"system group,\" of which my user is a member), so that's another option if you want it to be a bit more restricted but the `uaccess` approach doesn't work for you (set `MODE=\"660\"` in that case).\n        - If you want to dig into the udev syntax, see [Writing udev rules](https://reactivated.net/writing_udev_rules.html) and [udev(7)](https://man7.org/linux/man-pages/man7/udev.7.html).\n        - You can see the device's permissions with `ls -l /dev/bus/usb/$(lsusb | perl -ne '/Bus (\\d+) Device (\\d+).*Yubikey/ \u0026\u0026 printf \"$1/$2\"')`.\n     3. Detach the Yubikey from WSL and re-attach it.\n     4. `gpg --card-status` should work without root now.\n9. Set `SSH_AUTH_SOCK` and `GPG_TTY` in your .bashrc [as shown here](mods/gpg/.config/bashrc.d/gpg.sh).\n10. Add `enable-ssh-support` to [~/.gnupg/gpg-agent.conf](mods/gpg/.gnupg/gpg-agent.conf)\n11. Add `Match host * exec \"gpg-connect-agent updatestartuptty /bye\"` to [~/.ssh/config](mods/gpg/.ssh/config)\n    - Explanation for why `updatestartuptty` is necessary [here](https://stackoverflow.com/a/72427213); running it via ssh config comes from [this answer](https://unix.stackexchange.com/a/587691). Supposedly `GPG_TTY` is enough, but for whatever reason on my machine that only worked for gpg signing and not the ssh agent ¯\\\\\\_(ツ)\\_/¯\n    - Note: Without a GUI pinentry program, some Git features in VSCode (like auto-fetch) won't work until you've unlocked the card in a terminal (e.g. by running `git fetch` yourself).\n      - I ended up pulling out the Windows build of pinentry-qt5 from Gpg4win so that it can be used standalone from WSL with the Linux version of gpg; see [commit 0a5abfc](https://github.com/maxkagamine/dotfiles/commit/0a5abfc234302e36f1e683bed7ed60716cc74681).\n12. Assuming you've added your GPG key as an SSH key in GitHub (`gpg --export-ssh-key \u003ckey id\u003e`), `ssh git@github.com` should work now!\n    - If when trying to export the SSH public key for a newly-generated key you get an error saying it's invalid, make sure you've added an authentication subkey.\n    - If you get an \"error in libcrypto,\" you may need to run [the command shown here](https://security.stackexchange.com/questions/276688/now-that-sshcontrol-has-been-deprecated-how-to-use-gpg-key-for-ssh-authentica#:~:text=Get%20the%20keygrip,ssh%3A%20true%27%20/bye) to tell gpg to use the key for SSH.\n\n\u003c/details\u003e\n\n\u003cdetails open\u003e\n\u003csummary\u003e\u003ch3\u003eSetting up Arch in WSL\u003c/h3\u003e\u003c/summary\u003e\n\n**Update:** Arch is [now officially supported](https://antiz.fr/blog/archlinux-official-wsl-image/), so some or all of the first 5 steps (depending on how they set it up) may be different/unnecessary. Notes:\n\n- The official Arch install disables color \u0026 progress bars in pacman. Uncomment \"Color\" and comment \"NoProgressBar\" in /etc/pacman.conf.\n\n---\n\n1. [Download the ArchWSL zip](https://github.com/yuk7/ArchWSL/releases/latest), extract to %localappdata%\\Arch (or wherever), and run the exe\n2. Set the root password, create your own user, add it to the wheel group, and set its password:\n   ```\n   # passwd\n   # useradd -m -G wheel -s /bin/bash max\n   # passwd max\n   ```\n   Fun fact: I looked up the origin of the \"wheel\" name. It comes from \"big wheel\" which is an older way of saying \"head honcho.\" According to Ngram the phrase had a surgence in popularity in the late 40s through the 50s. So the term \"wheel\" is basically 1960s-programmer slang for \"Administrator.\" The more you know!\n3. `EDITOR=nano visudo` and uncomment the %wheel line towards the bottom to allow admins to sudo.\n   - The reason we're not adding a sudoers.d file here like the ArchWSL setup guide suggests is we want to be able to override this with our own sudoers file, but they're evaluated in lexicographical order and we don't want a file starting with \"w\" overriding us. Sudoers files should also always be edited using visudo.\n4. Exit out of WSL and `Arch.exe config --default-user max`.\n5. Initialize the pacman keyring (this is the last step from the setup guide; everything after this is custom):\n   ```\n   $ sudo pacman-key --init\n   $ sudo pacman-key --populate\n   $ sudo pacman -Sy archlinux-keyring\n   $ sudo pacman -Su\n   ```\n6. Consider changing the default pacman mirror: https://archlinux.org/mirrorlist/ or [rate-mirrors](https://github.com/westandskif/rate-mirrors) (this helped with the timeout issue \u0026 slow download speed I was experiencing).\n7. Fix the Yubikey's device permissions so GPG can access it without root ([explained above](#using-yubikey-for-gpg--ssh-in-wsl)):\n   ```\n   $ sudo pacman -S usbutils # Installs lsusb\n   $ lsusb # Confirm yubikey's vendor \u0026 product numbers\n   $ sudo nano /lib/udev/rules.d/yubikey.rules\n   SUBSYSTEM==\"usb\", ATTR{idVendor}==\"*\", ATTR{idProduct}==\"*\", MODE=\"666\"\n   $ gpg --card-status # Should work now without root after detaching \u0026 re-attaching the Yubikey\n   ```\n8. Import public keys:\n   ```\n   $ curl https://github.com/maxkagamine.gpg | gpg --import -\n   $ curl https://github.com/web-flow.gpg | gpg --import -\n   ```\n   One-liner to set the trust for all keys to \"ultimate\":\n   ```\n   $ gpg --list-keys --fingerprint --keyid-format long | sed -En '/fingerprint/{s/.*=|\\s+//g;s/$/:6:/;p}' | gpg --import-ownertrust\n   ```\n9. Install the bare minimum needed packages:\n   ```\n   $ sudo pacman -S base-devel git\n   ```\n10. Clone using HTTP first, since we need the dotfiles to set up GPG as the SSH agent:\n    ```\n    $ mkdir Projects \u0026\u0026 cd Projects\n    $ git clone https://github.com/maxkagamine/dotfiles.git\n    $ cd dotfiles\n    $ rm ~/.bash_profile ~/.bashrc\n    $ make\n    ```\n11. Check that SSH works now, and then switch the repo to SSH:\n    ```\n    $ . ~/.bashrc  # No .r alias yet\n    $ gpg-connect-agent reloadagent /bye\n    $ ssh git@github.com\n    $ git remote set-url origin git@github.com:maxkagamine/dotfiles.git\n    ```\n12. Hooray!\n\n\u003c/details\u003e\n\n## \u003cimg src=\".github/images/icons/unraid.png\" align=\"top\" height=\"25\" /\u003e\u0026hairsp; [unraid](mods/unraid)\n\nFor \"Sovngarde,\" my NAS. There isn't much in this mod (just a `CDPATH` and a function to list/monitor files on the array that are being accessed), but if you're\nrunning Unraid as well, see **[How to install GNU Stow on Unraid]**. Here's my user script, set to run on array start, if it happens to be useful:\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003ccode\u003ecat /boot/config/plugins/user.scripts/scripts/install_dotfiles/script\u003c/code\u003e\u003c/summary\u003e\n\n```sh\n#!/bin/bash\n#name=Install dotfiles\n#description=\u0026lpar;Re\u0026rpar;clone \u0026 install dotfiles.\n#argumentDescription=Branch\n#argumentDefault=master\n#clearLog=true\nset -eo pipefail\n\nexport PATH=\"/usr/local/bin:$PATH\"\nexport HOME=/root\n\nDOTFILES_DIR=~/dotfiles\nBRANCH=${1:-master}\n\n# Nuke existing dotfiles\nrm -rfv ~/.bashrc ~/.bash_profile \"$DOTFILES_DIR\"\n\n# Clean up symlinks\nfind ~ -xtype l -exec rm -v -- {} +\nfind ~ -depth -type d -empty -exec rmdir -v -- {} \\;\n\n# Clone repo\ngit clone -b \"$BRANCH\" https://github.com/maxkagamine/dotfiles.git \"$DOTFILES_DIR\"\n\n# Install\ncd \"$DOTFILES_DIR\"\nmake\n```\n\n\u003c/details\u003e\n\nAlso take a look at:\n\n- [User script to create backups of the flash drive and appdata on a schedule][backup_flash_and_appdata.sh]\n- [Guide to running nginx on Unraid with a wildcard cert, using the official nginx and certbot Docker images][Nginx \u0026 certbot on Unraid]\n\n## \u003cimg src=\".github/images/icons/mkvtoolnix.png\" align=\"top\" height=\"25\" /\u003e [mkvtoolnix](mods/mkvtoolnix)\n\nTools for batch remuxing MKVs using mkvtoolnix: [**mkv-ls**](mods/mkvtoolnix/.local/bin/mkv-ls) shows tracks in a table similar to the GUI but groups identical track listings for batch processing with [**mkv-batch**](mods/mkvtoolnix/.local/bin/mkv-batch).\n  \nFor example, if I wanted to keep only the Japanese audio and remove the Signs \u0026 Songs tracks from everything except the \"Another Epilogue\" special (which `mkv-ls` shows has different tracks):\n  \n\u003cimg src=\".github/images/mkv-ls.png\" width=\"550\" /\u003e\n\n_(The escaped filenames in gray are for copy/pasting into the `mkv-batch` command; to fit it in the screenshot I used the `!()` glob syntax instead.)_\n\nAdditional tools:\n\n- [**mkv-cat**](mods/mkvtoolnix/.local/bin/mkv-cat) — Concatenates the input MKVs, adding chapters for each file.\n- [**mkv-extract-subs**](mods/mkvtoolnix/.local/bin/mkv-extract-subs) — Batch extracts all subtitles from the given MKVs.\n- [**mkv-extract-fonts**](mods/mkvtoolnix/.local/bin/mkv-extract-fonts) — Batch extracts all unique fonts (by filename) from the given MKVs.\n- [**mkv-rm-cover**](mods/mkvtoolnix/.local/bin/mkv-rm-cover) — Removes all image/jpeg and image/png attachments from the given MKVs.\n- [**mkv-clean**](mods/mkvtoolnix/.local/bin/mkv-clean) — Removes the title and video track name (both used occasionally just to advertise the encoding group), cover image (some groups plaster the series cover on every episode), and common Windows system fonts (which are often unnecessarily included with subs and do nothing but waste space) from the given MKVs.\n- [**ass-ls-fonts**](mods/mkvtoolnix/.local/bin/ass-ls-fonts) — Lists all unique fonts referenced in the subs via either `Style:` or `\\fn`.\n- [**ass-to-txt**](mods/mkvtoolnix/.local/bin/ass-to-txt) — Strips out everything but the dialogue to make diffing subtitles easier.\n\n## ⚙\u0026hairsp;[misc utils](mods/misc-utils/.local/bin)\n\nMiscellaneous utilities:\n\n- [**append-crc**](mods/misc-utils/.local/bin/append-crc) — Adds (or updates) a file's crc32 hash to its filename.\n- [**batch-rename**](mods/misc-utils/.local/bin/batch-rename) — Helper for quickly applying a perl substitution to filenames.\n- [**flatten**](mods/misc-utils/.local/bin/flatten) — Flattens the contents of a directory such that \"foo/bar/file.jpg\" is renamed to \"foo - bar - file.jpg\", with an optional prefix/suffix added to the filenames.\n- [**html-unescape**](mods/misc-utils/.local/bin/html-unescape) — Unescapes HTML entities. Useful for scraping.\n- [**intersect-csvs**](mods/misc-utils/.local/bin/intersect-csvs) — Creates CSVs containing only rows that exist in two or more of the given CSVs. For example, given A.csv, B.csv, and C.csv, creates A+B.csv, A+C.csv, B+C.csv, and A+B+C.csv. I used this to create [a map](https://www.google.com/maps/d/viewer?mid=1kaE2O2LTjoS5Bf2YUCQ6OFJlXuert8U) of arcades in Tokyo that have my favorite games.\n- [**mkanimedir**](mods/misc-utils/.local/bin/mkanimedir) — Turns a MAL link and a bunch of episodes into a nice folder.\n- [**mkmoviedir**](mods/misc-utils/.local/bin/mkmoviedir) — Like mkanimedir but for an IMDb link.\n- [**rate**](mods/misc-utils/.local/bin/rate) — Rate-limits a pipe.\n- [**sanitize-filename**](mods/misc-utils/.local/bin/sanitize-filename) — Replaces characters invalid in Windows file/directory names with either a space or Unicode lookalikes.\n- [**title-case**](mods/misc-utils/.local/bin/title-case) — Converts text to properly-capitalized title case.\n- [**ubo-sort**](mods/misc-utils/.local/bin/ubo-sort) — Organizes uBlock Origin filter lists.\n- [**upscale**](mods/misc-utils/.local/bin/upscale) — Wrapper for several AI image upscalers, with options for automatic batch processing.\n- [**weigh**](mods/misc-utils/.local/bin/weigh) — Shows the total size of files, directories, or stdin (optionally gzipped).\n\n## \u003cimg src=\".github/images/icons/sweetroll.png\" height=\"23\" valign=\"middle\" /\u003e \u0026hairsp;[sweetroll](mods/sweetroll/.local/bin/sweetroll)\n\n_I need to ask you to stop. That... committing... is making people nervous._\n\nIn case you missed it: ~~[**Nuke a git repo with unrelenting force: the FUS RO DAH command**][fus-ro-dah]~~\n\n## [cron](mods/cron/)\n\nContains a [**cron-wrapper**](mods/cron/.local/bin/cron-wrapper) script for cronjobs that prevents multiple instances, handles logging, and triggers a notification on error. Used for [pull-from-seedbox](mods/wsl/.local/bin/pull-from-seedbox) and another script that runs yt-dlp to backup YouTube playlists.\n\n## [fzf](mods/fzf/.config/bashrc.d/fzf.sh)\n\nFancy [keyboard shortcuts][fzf keybindings] (also powers the aforementioned [\u003cimg src=\".github/images/icons/git.svg\" height=\"15\" /\u003e**git-branch-fzf**](mods/git/.local/bin/git-branch-fzf))\n\n## [imagemagick](mods/imagemagick/)\n\nContains a [**convert-to-srgb**](mods/imagemagick/.local/bin/convert-to-srgb) script I created to help deal with color profile issues when working with certain images. For each input image, if it contains a non-sRGB color profile, extracts the profile with exiftool (as imagemagick sometimes fails to recognize embedded ICC profiles) and converts it to sRGB. Used by [ugoira-to-mp4](mods/ffmpeg/.local/bin/ugoira-to-mp4) and [upscale](mods/misc-utils/.local/bin/upscale).\n\n## [nano](mods/nano/.config/nano/nanorc)\n\n\u003cimg src=\".github/images/nano.png\" height=\"375\" /\u003e\n\n## [yt-dlp](mods/yt-dlp/.config)\n\nBecause nothing on the Internet is guaranteed to be there tomorrow.\n\n[stow]: https://www.gnu.org/software/stow/manual/html_node/index.html#Top\n[stow todo]: https://github.com/aspiers/stow/blob/4ef5eca4a9d107b24e712bb4c2c91f47e7e0fb85/TODO\n[skyrim]: https://www.youtube.com/playlist?list=PLYooEAFUfhDfO3m_WQWkHdIB3Zh2kIXKp\n[fzf]: https://github.com/junegunn/fzf\n[gg-faster-git-commits]: https://github.com/maxkagamine/kagamine.dev/blob/master/src/pages/gg-faster-git-commits/en.md\n[fus-ro-dah]: https://github.com/maxkagamine/kagamine.dev/blob/master/src/pages/fus-ro-dah/en.md\n[bash-preexec]: https://github.com/rcaloras/bash-preexec\n[wsl]: https://docs.microsoft.com/en-us/windows/wsl/\n[How to install GNU Stow on Unraid]: https://gist.github.com/maxkagamine/7e3741b883a272230eb451bdd84a8e23\n[backup_flash_and_appdata.sh]: https://gist.github.com/maxkagamine/0fda138ff67e4ad9fcad692fe852a168\n[Nginx \u0026 certbot on Unraid]: https://gist.github.com/maxkagamine/5b6c34db6045d6413db3b333d6d2bae2\n[fzf keybindings]: https://github.com/junegunn/fzf#key-bindings-for-command-line\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaxkagamine%2Fdotfiles","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmaxkagamine%2Fdotfiles","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaxkagamine%2Fdotfiles/lists"}