├── .gitignore ├── demo.mp4 ├── icon.png ├── README.md └── obsidian-plugin-downloader.sh /.gitignore: -------------------------------------------------------------------------------- 1 | src/ 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /demo.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckman212/obsidian-plugin-downloader/HEAD/demo.mp4 -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luckman212/obsidian-plugin-downloader/HEAD/icon.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![icon](icon.png) 2 | 3 | # Obsidian Plugin Downloader 4 | 5 | ### Demo Video 6 | 7 | https://user-images.githubusercontent.com/1992842/129498054-6426ec90-5c12-4907-a5a0-f03988b15914.mp4 8 | 9 | ### What? 10 | 11 | This is a script to search, download and maintain a local repository of Obsidian plugins, which can be used as a reference for your own research and development. 12 | 13 | ### Why? 14 | 15 | As an absolute beginner to TypeScript, and a lover of [Obsidian](https://obsidian.md/) I often want to take a look at how someone has achieved a certain feature, called on an API, etc. A quick way to do that is by searching through the existing codebase of the ever growing library of plugins out there. 16 | 17 | ### Setup 18 | 19 | 1. First, set up your environment to have `jq`, `fzf`, and `gh`. On macOS the simplest way to do that is with Homebrew: `brew install jq fzf gh`. 20 | 2. Copy the `obsidian-plugin-downloader.sh` script to a directory in your `$PATH` 21 | 3. Make sure it's executable (`chmod +x obsidian-plugin-downloader.sh`) 22 | 4. You can adjust the `$OUTDIR` variable to set the destination directory of your choice (default: `~/Downloads/obsidian-plugins`) 23 | 24 | ### Run 25 | 26 | 1. Open a Terminal window (bash, zsh, etc) 27 | 2. Type `obsidian-plugin-downloader.sh` 28 | 3. The list of plugins should be displayed. You can type in the search field at the top to filter the list—both the names and descriptions of the plugins are searchable. 29 | 4. Choose one or more to download. You can move with the arrow keys, use <TAB> to select/deselect, or press ⌃A / ⌃S to select/deselect all. 30 | 5. Make your selections and press <ENTER> 31 | 6. Plugins should be downloaded! 32 | 33 | > The script will automatically check to see if you have the latest version of the plugin, and will download newer versions as needed. 34 | 35 | ### Next... 36 | 37 | It's nice to have a tool like [`ripgrep`](https://github.com/BurntSushi/ripgrep) to search through the code if you are looking for API references etc. For example: 38 | 39 | ``` 40 | rg 'this.app.workspace.onLayoutReady' --glob '*.ts' 41 | ``` 42 | -------------------------------------------------------------------------------- /obsidian-plugin-downloader.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # https://github.com/obsidianmd/obsidian-releases/blob/master/README.md 4 | # https://docs.github.com/en/graphql/overview/explorer 5 | 6 | # prerequisites (brew install jq fzf gh) 7 | # jq https://stedolan.github.io/jq/ 8 | # fzf https://github.com/junegunn/fzf/ 9 | # gh https://cli.github.com/ 10 | 11 | feed='https://raw.githubusercontent.com/obsidianmd/obsidian-releases/master/community-plugins.json' 12 | curlopts=( --location --silent --max-time 5 ) 13 | OUTDIR="$HOME/Downloads/obsidian-plugins" 14 | FZF_HEADER='⌃ctrl+A=select all ⌃ctrl+S=select none' 15 | pfx='https://github.com' 16 | 17 | # man gh-api 18 | read -r -d '' GQL_QUERY <<-'EOQ' 19 | query($name: String!, $owner: String!) { 20 | repository(owner: $owner, name: $name) { 21 | defaultBranchRef { name } 22 | releases(first: 10, orderBy: {field: CREATED_AT, direction: DESC}) { 23 | nodes { tagName, isDraft, isPrerelease } 24 | } 25 | } 26 | } 27 | EOQ 28 | 29 | function _fetchVersion { 30 | local REPO_VER 31 | OWNER=${1%%/*} 32 | REPO=${1##*/} 33 | GQL_JSON=$(gh api graphql --field owner="$OWNER" --field name="$REPO" --raw-field query="$GQL_QUERY") 34 | if [ -n "$GQL_JSON" ] && [ "$GQL_JSON" != "null" ]; then 35 | REPO_VER=$(jq -r '.data.repository.releases.nodes | 36 | map(select( 37 | (.isDraft==false) and 38 | (.isPrerelease==false) and 39 | (.tagName|test("alpha|beta|rc";"i")|not) 40 | ))[0] | 41 | .tagName' <<<"$GQL_JSON" 2>/dev/null) 42 | case $REPO_VER in 43 | null|'') REPO_VER=$(jq -r '.data.repository.releases.nodes[0] | .tagName' <<<"$GQL_JSON" 2>/dev/null) ;; 44 | esac 45 | echo "$REPO_VER" 46 | fi 47 | } 48 | 49 | function _mkdir { 50 | mkdir -p "$OUTDIR" 2>/dev/null 51 | cd "$_" || exit 1 52 | } 53 | 54 | JSON=$(curl "${curlopts[@]}" -X GET $feed 2>/dev/null) 55 | [ -n "$JSON" ] || exit 1 56 | _mkdir 57 | 58 | if command -v gh &>/dev/null; then 59 | use_graphql=true 60 | function _download { 61 | if gh repo clone "${1#$pfx/}" "$2" ${3:+-- --branch "$3"} &>/dev/null; then (( c++ )); fi; 62 | } 63 | else 64 | if ! command -v git &>/dev/null; then 65 | echo 1>&2 "requires gh or git!" 66 | exit 1 67 | fi 68 | echo 1>&2 "% gh not present, falling back to git" 69 | function _download { 70 | if git clone ${3:+--branch "$3"} -- "$1" "$2" &>/dev/null; then (( c++ )); fi; 71 | } 72 | fi 73 | 74 | while IFS=$'\t' read -r URL _ ID ; do 75 | GH_USER_REPO=${URL#$pfx/} 76 | if [[ $use_graphql == true ]]; then 77 | unset CUR_VER LATEST_VER 78 | CUR_VER=$(jq -r '.version' "$ID/manifest.json" 2>/dev/null) 79 | LATEST_VER="$(_fetchVersion "$GH_USER_REPO")" 80 | if [[ "$LATEST_VER" == "$CUR_VER" ]]; then 81 | echo "$GH_USER_REPO: v${CUR_VER} already downloaded ✓" 82 | continue 83 | else 84 | rm -rf "${ID:?}" 85 | fi 86 | fi 87 | echo "$GH_USER_REPO: downloading${CUR_VER:+ v$CUR_VER →}${LATEST_VER:+ v$LATEST_VER}" 88 | if [[ -n "$ID" ]]; then 89 | _download "$URL" "$ID" "$LATEST_VER" 90 | fi 91 | done < <( 92 | jq -r 'sort_by(.name) | .[] | [ .repo, .name, .description, .id ] | @tsv' <<<"$JSON" | 93 | tr -cd '[[:print:]]\n\t' | 94 | awk -v pfx=$pfx 'BEGIN { FS="\t" } { printf "%s/%s\t%s: %s\t%s\n",pfx,$1,$2,$3,$4 }' | 95 | sed $'s/\t/\u00a0\t/g' | 96 | column -s$'\t' -t | 97 | fzf -i ${1:+-q $1} --exact --multi --no-hscroll --no-mouse --no-select-1 \ 98 | --delimiter $'\u00a0' \ 99 | --with-nth 1,2 \ 100 | --header="$FZF_HEADER" \ 101 | --bind='ctrl-a:select-all,ctrl-s:deselect-all' \ 102 | --preview-window='down,3,wrap' \ 103 | --preview='echo {2}' | 104 | sed $'s/\u00a0\ */\t/g' 105 | ) | 106 | awk -F: '{ printf "%-60s %s\n",$1,$2 }' 107 | --------------------------------------------------------------------------------