├── .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 | 
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 |
--------------------------------------------------------------------------------