├── .gitignore
├── LICENSE
├── README.md
├── create_release.sh
├── install.sh
└── kindlefetch
├── bin
├── downloads
│ ├── lgli_download.sh
│ └── zlib_download.sh
├── filters.sh
├── kindlefetch.sh
├── link_config
├── local_books.sh
├── misc.sh
├── search.sh
├── settings.sh
├── setup.sh
└── update.sh
├── config.xml
├── menu.json
└── run.sh
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | books/
3 | .env
4 | kindlefetch_config
5 | .vscode
6 | .idea
7 | venv/
8 | kindlefetch.zip
9 | .version
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 justrals
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # KindleFetch
4 |
5 |
6 |
7 |
8 | Simple CLI for downloading books directly to your Kindle without a computer.
9 |
10 | ## Prerequisites
11 | **Your Kindle must be jailbroken before proceeding!**
12 | If it's not, follow [this guide](https://kindlemodding.org/) first.
13 |
14 | **Install kterm**:
15 | 1. Download the latest release from [kterm's releases page](https://github.com/bfabiszewski/kterm/releases)
16 | 2. Unzip the archive to the `extensions` directory in your Kindle's root
17 |
18 | **KOReader (Optional but Recommended):**
19 |
20 | 1. **Download** the latest release from the [KOReader Releases Page](https://github.com/koreader/koreader/releases)
21 | 2. **Unsure which version to get?** Check the [Installation Guide](https://github.com/koreader/koreader/wiki/Installation-on-Kindle-devices#err-there-are-four-kindle-packages-to-choose-from-which-do-i-pick)
22 | 3. **Extract** the contents of the downloaded archive to the **root directory** of your Kindle
23 |
24 | ## Installation
25 |
26 | ### Automatic installation (Recomended)
27 |
28 | 1. **Launch KUAL** (Kindle Unified Application Launcher) and open **kterm**
29 |
30 | 2. **Run the installation command** in kterm:
31 | ```bash
32 | curl https://justrals.github.io/KindleFetch/install.sh | sh
33 | ```
34 |
35 | 3. **Complete the setup**:
36 | - Wait for the installation to finish (you should see a success message)
37 | - Type `exit` to close kterm
38 | - You should now see a new "KindleFetch" option in KUAL
39 |
40 | ### Manual Installation
41 |
42 | 1. **Download** the latest release from the [**Releases**](https://github.com/justrals/KindleFetch/releases) tab
43 |
44 | 2. **Unzip** it and move its contents into the `extensions` folder in the root directory of your **Kindle**
45 | - You should now see a new "KindleFetch" option in KUAL
46 |
47 | 3. Update from the main menu if asked
48 |
--------------------------------------------------------------------------------
/create_release.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | rm kindlefetch.zip
3 |
4 | get_version() {
5 | api_response=$(curl -s -H "Accept: application/vnd.github.v3+json" "https://api.github.com/repos/justrals/KindleFetch/commits") || {
6 | echo "Warning: Failed to fetch version from GitHub API" >&2
7 | echo "unknown"
8 | return
9 | }
10 |
11 | latest_sha=$(echo "$api_response" | grep -m1 '"sha":' | cut -d'"' -f4 | cut -c1-7)
12 |
13 | if [ -n "$latest_sha" ]; then
14 | echo "${latest_sha}"
15 | fi
16 | }
17 |
18 | VERSION=$(get_version)
19 | mkdir -p "$INSTALL_DIR/bin"
20 | echo "$VERSION" > "kindlefetch/bin/.version"
21 |
22 | find kindlefetch -name ".DS_Store" -delete
23 |
24 | zip kindlefetch.zip -r kindlefetch
--------------------------------------------------------------------------------
/install.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | # Check if running on a Kindle
6 | if ! { [ -f "/etc/prettyversion.txt" ] || [ -d "/mnt/us" ] || pgrep "lipc-daemon" >/dev/null; }; then
7 | echo "Error: This script must run on a Kindle device." >&2
8 | exit 1
9 | fi
10 |
11 | # Variables
12 | API_URL="https://api.github.com/repos/justrals/KindleFetch/commits"
13 | REPO_URL="https://github.com/justrals/KindleFetch/archive/refs/heads/main.zip"
14 | ZIP_FILE="/mnt/us/repo.zip"
15 | EXTRACTED_DIR="/mnt/us/KindleFetch-main"
16 | INSTALL_DIR="/mnt/us/extensions/kindlefetch"
17 | CONFIG_FILE="$INSTALL_DIR/bin/kindlefetch_config"
18 | VERSION_FILE="$INSTALL_DIR/bin/.version"
19 | TEMP_CONFIG="/mnt/us/kindlefetch_config_backup"
20 |
21 | get_version() {
22 | api_response=$(curl -s -H "Accept: application/vnd.github.v3+json" "$API_URL") || {
23 | echo "Warning: Failed to fetch version from GitHub API" >&2
24 | echo "unknown"
25 | return
26 | }
27 |
28 | latest_sha=$(echo "$api_response" | grep -m1 '"sha":' | cut -d'"' -f4 | cut -c1-7)
29 |
30 | if [ -n "$latest_sha" ]; then
31 | echo "${latest_sha}"
32 | fi
33 | }
34 |
35 | if [ -f "$CONFIG_FILE" ]; then
36 | echo "Backing up existing config..."
37 | cp -f "$CONFIG_FILE" "$TEMP_CONFIG"
38 | fi
39 |
40 | echo "Downloading KindleFetch..."
41 | curl -s -L -o "$ZIP_FILE" "$REPO_URL"
42 | echo "Download complete."
43 |
44 | echo "Extracting files..."
45 | unzip -o "$ZIP_FILE" -d "/mnt/us"
46 | echo "Extraction complete."
47 | rm -f "$ZIP_FILE"
48 |
49 | echo "Removing old installation..."
50 | rm -rf "$INSTALL_DIR"
51 |
52 | echo "Installing KindleFetch..."
53 | mkdir -p "$INSTALL_DIR"
54 | mv -f "$EXTRACTED_DIR/kindlefetch"/* "$INSTALL_DIR/"
55 | echo "Installation successful."
56 |
57 | echo "Creating version file..."
58 | VERSION=$(get_version)
59 | mkdir -p "$INSTALL_DIR/bin"
60 | echo "$VERSION" > "$VERSION_FILE"
61 |
62 | if [ -f "$TEMP_CONFIG" ]; then
63 | echo "Restoring configuration..."
64 | mv -f "$TEMP_CONFIG" "$CONFIG_FILE"
65 | fi
66 |
67 | echo "Cleaning up..."
68 | rm -rf "$EXTRACTED_DIR"
69 |
70 | echo "KindleFetch installation completed successfully. Version: $VERSION"
--------------------------------------------------------------------------------
/kindlefetch/bin/downloads/lgli_download.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | lgli_download() {
4 | local index=$1
5 |
6 | if [ ! -f "/tmp/search_results.json" ]; then
7 | echo "Error: No search results found" >&2
8 | return 1
9 | fi
10 |
11 | local book_info=$(awk -v i="$index" 'BEGIN{RS="\\{"; FS="\\}"} NR==i+1{print $1}' /tmp/search_results.json)
12 | if [ -z "$book_info" ]; then
13 | echo "Error: Invalid book selection" >&2
14 | return 1
15 | fi
16 |
17 | local md5=$(get_json_value "$book_info" "md5")
18 | local title=$(get_json_value "$book_info" "title")
19 | local format=$(get_json_value "$book_info" "format")
20 |
21 | echo "Downloading: $title"
22 |
23 | local clean_title=$(sanitize_filename "$title" | tr -d ' ')
24 | local final_location
25 |
26 | echo -n "Do you want to change filename? [y/N] "
27 | read confirm
28 | if [ "$confirm" = "y" ] || [ "$confirm" = "Y" ]; then
29 | echo -n "Enter your custom filename: "
30 | read custom_filename
31 | if [ -n "$custom_filename" ]; then
32 | local clean_title=$(sanitize_filename "$custom_filename" | tr -d ' ')
33 | else
34 | echo "Invalid filename. Proceeding with original filename."
35 | fi
36 | else
37 | echo "Proceeding with original filename."
38 | fi
39 |
40 | if [ ! -w "$KINDLE_DOCUMENTS" ]; then
41 | echo "Error: No write permission in $KINDLE_DOCUMENTS" >&2
42 | return 1
43 | fi
44 |
45 | if [ "$CREATE_SUBFOLDERS" = "true" ]; then
46 | local book_folder="$KINDLE_DOCUMENTS/$clean_title"
47 | if ! mkdir -p "$book_folder"; then
48 | echo "Error: Failed to create folder '$book_folder'" >&2
49 | return 1
50 | fi
51 | final_location="$book_folder/$clean_title.$format"
52 | else
53 | final_location="$KINDLE_DOCUMENTS/$clean_title.$format"
54 | fi
55 |
56 | if [ -e "$final_location" ] && [ ! -w "$final_location" ]; then
57 | echo "Error: No permission to overwrite $final_location" >&2
58 | return 1
59 | fi
60 |
61 | echo "Fetching download page..."
62 | if ! lgli_content=$(curl -s -L "$LGLI_URL/ads.php?md5=$md5"); then
63 | if ! lgli_content=$(curl -s -L -x "$PROXY_URL" "$LGLI_URL/ads.php?md5=$md5"); then
64 | echo "Error: Failed to fetch book page" >&2
65 | return 1
66 | fi
67 | fi
68 |
69 | if ! download_link=$(echo "$lgli_content" | grep -o -m 1 'href="[^"]*get\.php[^"]*"' | cut -d'"' -f2); then
70 | echo "Error: Failed to parse download link" >&2
71 | return 1
72 | fi
73 |
74 | if [ -z "$download_link" ]; then
75 | echo "Error: No download link found" >&2
76 | return 1
77 | fi
78 |
79 | local download_url="$LGLI_URL/$download_link"
80 | echo "Downloading from: $download_url"
81 |
82 | echo "Progress:"
83 |
84 | for retry in 1 2 3; do
85 | if curl -# -L -o "$final_location" "$download_url"; then
86 | echo "Download successful!"
87 | echo "Saved to: $final_location"
88 | return 0
89 | else
90 | if curl -x "$PROXY_URL" -# -L -o "$final_location" "$download_url"; then
91 | echo "Download successful!"
92 | echo "Saved to: $final_location"
93 | return 0
94 | else
95 | echo "Download attempt $retry failed"
96 | [ $retry -lt 3 ] && sleep 5
97 | fi
98 | fi
99 | done
100 |
101 | echo "Error: Failed to download after 3 attempts" >&2
102 | return 1
103 | }
--------------------------------------------------------------------------------
/kindlefetch/bin/downloads/zlib_download.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | zlib_download() {
4 | local index=$1
5 |
6 | if [ ! -f "/tmp/search_results.json" ]; then
7 | echo "Error: No search results found" >&2
8 | return 1
9 | fi
10 |
11 | local book_info=$(awk -v i="$index" 'BEGIN{RS="\\{"; FS="\\}"} NR==i+1{print $1}' /tmp/search_results.json)
12 | if [ -z "$book_info" ]; then
13 | echo "Error: Invalid book selection" >&2
14 | return 1
15 | fi
16 |
17 | local md5=$(get_json_value "$book_info" "md5")
18 | local title=$(get_json_value "$book_info" "title")
19 | local format=$(get_json_value "$book_info" "format")
20 |
21 | echo "Downloading: $title"
22 |
23 | local clean_title=$(sanitize_filename "$title" | tr -d ' ')
24 | local final_location
25 |
26 | echo -n "Do you want to change filename? [y/N] "
27 | read confirm
28 | if [ "$confirm" = "y" ] || [ "$confirm" = "Y" ]; then
29 | echo -n "Enter your custom filename: "
30 | read custom_filename
31 | if [ -n "$custom_filename" ]; then
32 | local clean_title=$(sanitize_filename "$custom_filename" | tr -d ' ')
33 | else
34 | echo "Invalid filename. Proceeding with original filename."
35 | fi
36 | else
37 | echo "Proceeding with original filename."
38 | fi
39 |
40 | if [ ! -w "$KINDLE_DOCUMENTS" ]; then
41 | echo "Error: No write permission in $KINDLE_DOCUMENTS" >&2
42 | return 1
43 | fi
44 |
45 | if [ "$CREATE_SUBFOLDERS" = "true" ]; then
46 | local book_folder="$KINDLE_DOCUMENTS/$clean_title"
47 | if ! mkdir -p "$book_folder"; then
48 | echo "Error: Failed to create folder '$book_folder'" >&2
49 | return 1
50 | fi
51 | final_location="$book_folder/$clean_title.$format"
52 | else
53 | final_location="$KINDLE_DOCUMENTS/$clean_title.$format"
54 | fi
55 |
56 | if [ -e "$final_location" ] && [ ! -w "$final_location" ]; then
57 | echo "Error: No permission to overwrite $final_location" >&2
58 | return 1
59 | fi
60 |
61 | echo "Fetching download page..."
62 | if ! zlib_content=$(curl -s -L "$ZLIB_URL/md5/$md5"); then
63 | if ! zlib_content=$(curl -s -L -x "$PROXY_URL" "$ZLIB_URL/md5/$md5"); then
64 | echo "Error: Failed to fetch book page" >&2
65 | return 1
66 | fi
67 | fi
68 |
69 | if ! download_link=$(echo "$zlib_content" | grep -o 'href="/dl/[^"]*"' | head -1 | sed 's/href="//;s/"//'); then
70 | echo "Error: Failed to parse download link" >&2
71 | return 1
72 | fi
73 |
74 | if [ -z "$download_link" ]; then
75 | echo "Error: No download link found" >&2
76 | return 1
77 | fi
78 |
79 | local download_url="$ZLIB_URL$download_link"
80 | echo "Downloading from: $download_url"
81 |
82 | echo "Progress:"
83 |
84 | for retry in 1 2 3; do
85 | if curl -# -L -o "$final_location" "$download_url"; then
86 | echo "Download successful!"
87 | echo "Saved to: $final_location"
88 | return 0
89 | else
90 | if curl -x "$PROXY_URL" -# -L -o "$final_location" "$download_url"; then
91 | echo "Download successful!"
92 | echo "Saved to: $final_location"
93 | return 0
94 | else
95 | echo "Download attempt $retry failed"
96 | [ $retry -lt 3 ] && sleep 5
97 | fi
98 | fi
99 | done
100 |
101 | echo "Error: Failed to download after 3 attempts" >&2
102 | return 1
103 | }
--------------------------------------------------------------------------------
/kindlefetch/bin/filters.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | filters_menu() {
4 | CURRENT_FILTERS_FILE="$SCRIPT_DIR/tmp/current_filters"
5 | CURRENT_PARAMS_FILE="$SCRIPT_DIR/tmp/current_filter_params"
6 |
7 | current_tab=1
8 | total_tabs=5
9 |
10 | mkdir -p "$SCRIPT_DIR/tmp" 2>/dev/null
11 |
12 | if [ -f "$CURRENT_FILTERS_FILE" ]; then
13 | . "$CURRENT_FILTERS_FILE" 2>/dev/null || {
14 | content_filter=""
15 | ext_filter=""
16 | lang_filter=""
17 | src_filter=""
18 | sort_filter=""
19 | }
20 | else
21 | content_filter=""
22 | ext_filter=""
23 | lang_filter=""
24 | src_filter=""
25 | sort_filter=""
26 | fi
27 |
28 | while true; do
29 | clear
30 | echo -e "
31 | ______ _ _ _
32 | | ____(_) | |
33 | | |__ _| | |_ ___ _ __ ___
34 | | __| | | | __/ _ \\ '__/ __|
35 | | | | | | || __/ | \\__ \\
36 | |_| |_|_|\\__\\___|_| |___/
37 | "
38 | echo "╭──────────────────────────────────────╮"
39 | echo "│ 1. Content 2. Format 3. Language │"
40 | echo "│ 4. Source 5. Sort │"
41 | echo "│ Enter - Apply & Exit │"
42 | echo "╰──────────────────────────────────────╯"
43 | echo ""
44 |
45 | echo "Active Filters:"
46 | [ -n "$content_filter" ] && echo " content: $content_filter"
47 | [ -n "$ext_filter" ] && echo " ext: $ext_filter"
48 | [ -n "$lang_filter" ] && echo " lang: $lang_filter"
49 | [ -n "$src_filter" ] && echo " src: $src_filter"
50 | [ -n "$sort_filter" ] && echo " sort: $sort_filter"
51 | echo ""
52 |
53 | case $current_tab in
54 | 1) echo "── CONTENT TYPE ────────────────────────"
55 | echo "1) Nonfiction 5) Comic"
56 | echo "2) Fiction 6) Standards"
57 | echo "3) Unknown 7) Other"
58 | echo "4) Magazine 8) Musical Score"
59 | echo ""
60 | echo "0) Clear Filter" ;;
61 | 2) echo "── FILE FORMAT ────────────────────────"
62 | echo "1) PDF 5) CBR"
63 | echo "2) EPUB 6) DJVU"
64 | echo "3) MOBI 7) CBZ"
65 | echo "4) FB2 8) TXT 9) AZW3"
66 | echo ""
67 | echo "0) Clear Filter" ;;
68 | 3) echo "── LANGUAGE ───────────────────────────"
69 | echo "1) English (en) 4) German (de)"
70 | echo "2) Russian (ru) 5) French (fr)"
71 | echo "3) Spanish (es) 6) Custom Input"
72 | echo ""
73 | echo "0) Clear Filter" ;;
74 | 4) echo "── SOURCE ────────────────────────────"
75 | echo "1) Library Genesis (lgli)"
76 | echo "2) Z-Library (zlib)"
77 | echo ""
78 | echo "0) Clear Filter" ;;
79 | 5) echo "── SORT BY ───────────────────────────"
80 | echo "1) Newest 5) Newest Added"
81 | echo "2) Oldest 6) Oldest Added"
82 | echo "3) Largest 7) Random"
83 | echo "4) Smallest 8) Most Relevant"
84 | echo ""
85 | echo "0) Clear Filter" ;;
86 | esac
87 |
88 | echo ""
89 | echo -n "Choose option (or t[1-5] to switch tabs): "
90 | read choice
91 |
92 | case "$choice" in
93 | t1|t2|t3|t4|t5) current_tab=${choice#t} ;;
94 | 0) case $current_tab in
95 | 1) content_filter="" ;;
96 | 2) ext_filter="" ;;
97 | 3) lang_filter="" ;;
98 | 4) src_filter="" ;;
99 | 5) sort_filter="" ;;
100 | esac ;;
101 | [1-9])
102 | case $current_tab in
103 | 1) case $choice in
104 | 1) content_filter="book_nonfiction" ;;
105 | 2) content_filter="book_fiction" ;;
106 | 3) content_filter="book_unknown" ;;
107 | 4) content_filter="magazine" ;;
108 | 5) content_filter="book_comic" ;;
109 | 6) content_filter="standards_document" ;;
110 | 7) content_filter="other" ;;
111 | 8) content_filter="musical_score" ;;
112 | esac ;;
113 | 2) case $choice in
114 | 1) ext_filter="pdf" ;;
115 | 2) ext_filter="epub" ;;
116 | 3) ext_filter="mobi" ;;
117 | 4) ext_filter="fb2" ;;
118 | 5) ext_filter="cbr" ;;
119 | 6) ext_filter="djvu" ;;
120 | 7) ext_filter="cbz" ;;
121 | 8) ext_filter="txt" ;;
122 | 9) ext_filter="azw3" ;;
123 | esac ;;
124 | 3) case $choice in
125 | 1) lang_filter="en" ;;
126 | 2) lang_filter="ru" ;;
127 | 3) lang_filter="es" ;;
128 | 4) lang_filter="de" ;;
129 | 5) lang_filter="fr" ;;
130 | 6) echo -n "Enter language code: "
131 | read lang_code
132 | lang_filter="$lang_code" ;;
133 | esac ;;
134 | 4) case $choice in
135 | 1) src_filter="lgli" ;;
136 | 2) src_filter="zlib" ;;
137 | esac ;;
138 | 5) case $choice in
139 | 1) sort_filter="newest" ;;
140 | 2) sort_filter="oldest" ;;
141 | 3) sort_filter="largest" ;;
142 | 4) sort_filter="smallest" ;;
143 | 5) sort_filter="newest_added" ;;
144 | 6) sort_filter="oldest_added" ;;
145 | 7) sort_filter="random" ;;
146 | 8) sort_filter="" ;;
147 | esac ;;
148 | esac ;;
149 | "")
150 | {
151 | echo "content_filter=\"$content_filter\""
152 | echo "ext_filter=\"$ext_filter\""
153 | echo "lang_filter=\"$lang_filter\""
154 | echo "src_filter=\"$src_filter\""
155 | echo "sort_filter=\"$sort_filter\""
156 | } > "$CURRENT_FILTERS_FILE"
157 |
158 | filter_string=""
159 | [ -n "$content_filter" ] && filter_string="${filter_string}&content=$content_filter"
160 | [ -n "$ext_filter" ] && filter_string="${filter_string}&ext=$ext_filter"
161 | [ -n "$lang_filter" ] && filter_string="${filter_string}&lang=$lang_filter"
162 | [ -n "$src_filter" ] && filter_string="${filter_string}&src=$src_filter"
163 | [ -n "$sort_filter" ] && filter_string="${filter_string}&sort=$sort_filter"
164 | echo "$filter_string" > "$CURRENT_PARAMS_FILE"
165 |
166 | return 0
167 | ;;
168 | *) echo "Invalid option"; sleep 1 ;;
169 | esac
170 | done
171 | }
--------------------------------------------------------------------------------
/kindlefetch/bin/kindlefetch.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # KindleFetch
4 | # Made by justrals
5 | # https://github.com/justrals/KindleFetch
6 |
7 | # Variables
8 | SCRIPT_DIR=$(dirname "$(readlink -f "$0")")
9 | CONFIG_FILE="$SCRIPT_DIR/kindlefetch_config"
10 | LINK_CONFIG_FILE="$SCRIPT_DIR/link_config"
11 | VERSION_FILE="$SCRIPT_DIR/.version"
12 |
13 | UPDATE_AVAILABLE=false
14 |
15 | # Check if running on a Kindle
16 | if ! { [ -f "/etc/prettyversion.txt" ] || [ -d "/mnt/us" ] || pgrep "lipc-daemon" >/dev/null; }; then
17 | echo "Error: This script must run on a Kindle device." >&2
18 | echo "Press any key to exit."
19 | read -n 1 -s
20 | exit 1
21 | fi
22 |
23 | # Script imports
24 | . "$SCRIPT_DIR/downloads/zlib_download.sh"
25 | . "$SCRIPT_DIR/downloads/lgli_download.sh"
26 | . "$SCRIPT_DIR/filters.sh"
27 | . "$SCRIPT_DIR/search.sh"
28 | . "$SCRIPT_DIR/misc.sh"
29 | . "$SCRIPT_DIR/local_books.sh"
30 | . "$SCRIPT_DIR/update.sh"
31 | . "$SCRIPT_DIR/setup.sh"
32 | . "$SCRIPT_DIR/settings.sh"
33 |
34 | main_menu() {
35 | load_config
36 | check_for_updates
37 |
38 | while true; do
39 | clear
40 | echo -e "
41 | _ ___ _ _ ______ _ _
42 | | |/ (_) | | | | ____| | | | |
43 | | ' / _ _ __ __| | | ___| |__ ___| |_ ___| |__
44 | | < | | '_ \ / _\` | |/ _ \ __/ _ \ __/ __| '_ \\
45 | | . \| | | | | (_| | | __/ | | __/ || (__| | | |
46 | |_|\_\_|_| |_|\__,_|_|\___|_| \___|\__\___|_| |_|
47 |
48 | $(load_version) | https://github.com/justrals/KindleFetch
49 | "
50 | if $UPDATE_AVAILABLE; then
51 | echo "Update available! Select option 6 to install."
52 | echo ""
53 | fi
54 | echo "1. Search and download books"
55 | echo "2. Filter search results"
56 | echo "3. List my books"
57 | echo "4. Settings"
58 | echo "5. Exit"
59 | if $UPDATE_AVAILABLE; then
60 | echo ""
61 | echo "6. Install update"
62 | fi
63 | echo ""
64 | echo -n "Choose option: "
65 | read choice
66 |
67 | case "$choice" in
68 | 1)
69 | search_books
70 | ;;
71 | 2)
72 | filters_menu
73 | ;;
74 | 3)
75 | list_local_books
76 | ;;
77 | 4)
78 | settings_menu
79 | ;;
80 | 5)
81 | cleanup
82 | exit 0
83 | ;;
84 | 6)
85 | if $UPDATE_AVAILABLE; then
86 | update
87 | fi
88 | ;;
89 | *)
90 | echo "Invalid option"
91 | sleep 2
92 | ;;
93 | esac
94 | done
95 | }
96 |
97 | trap cleanup EXIT
98 | main_menu
--------------------------------------------------------------------------------
/kindlefetch/bin/link_config:
--------------------------------------------------------------------------------
1 | QU5OQVNfVVJMPSJodHRwczovL2FubmFzLWFyY2hpdmUub3JnIgpMR0xJX1VSTD0iaHR0cHM6Ly9saWJnZW4ubGkiClpMSUJfVVJMPSJodHRwczovL3otbGliLmZtIgpQUk9YWV9VUkw9Imh0dHA6Ly9pZGsuanVzdHJhbHMudGVjaDozMTI4Ig==
--------------------------------------------------------------------------------
/kindlefetch/bin/local_books.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | delete_book() {
4 | index=$1
5 | book_file=$(sed -n "${index}p" /tmp/kindle_books.list 2>/dev/null)
6 |
7 | if [ -z "$book_file" ]; then
8 | echo "Invalid selection"
9 | return 1
10 | fi
11 |
12 | echo -n "Are you sure you want to delete '$book_file'? [y/N] "
13 | read confirm
14 | if [ "$confirm" = "y" ] || [ "$confirm" = "Y" ]; then
15 | if rm -f "$book_file"; then
16 | echo "Book deleted successfully"
17 | else
18 | echo "Failed to delete book"
19 | fi
20 | else
21 | echo "Deletion canceled"
22 | fi
23 | }
24 |
25 | delete_directory() {
26 | index=$1
27 | dir_path=$(sed -n "${index}p" /tmp/kindle_folders.list 2>/dev/null)
28 |
29 | if [ -z "$dir_path" ]; then
30 | echo "Invalid selection"
31 | return 1
32 | fi
33 |
34 | echo -n "Are you sure you want to delete '$dir_path' and all its contents? [y/N] "
35 | read confirm
36 | if [ "$confirm" = "y" ] || [ "$confirm" = "Y" ]; then
37 | if rm -rf "$dir_path"; then
38 | echo "Directory deleted successfully"
39 | else
40 | echo "Failed to delete directory"
41 | fi
42 | else
43 | echo "Deletion canceled"
44 | fi
45 | }
46 |
47 | list_local_books() {
48 | while true; do
49 | local current_dir="${1:-$KINDLE_DOCUMENTS}"
50 | clear
51 | echo -e "
52 | ____ _
53 | | _ \ | |
54 | | |_) | ___ ___ | | _____
55 | | _ < / _ \ / _ \| |/ / __|
56 | | |_) | (_) | (_) | <\__ \\
57 | |____/ \___/ \___/|_|\_\___/
58 | "
59 | echo "Current directory: $current_dir"
60 | echo "--------------------------------"
61 | echo ""
62 |
63 | i=1
64 | > /tmp/kindle_books.list
65 | > /tmp/kindle_folders.list
66 |
67 | if [ ! -d "$current_dir" ]; then
68 | echo "Error: Directory '$current_dir' does not exist."
69 | return 1
70 | fi
71 |
72 | for item in "$current_dir"/*; do
73 | if [ -d "$item" ]; then
74 | foldername=$(basename "$item")
75 | echo "$i. $foldername/"
76 | echo "$item" >> /tmp/kindle_folders.list
77 | i=$((i+1))
78 | fi
79 | done
80 |
81 | for item in "$current_dir"/*; do
82 | if [ -f "$item" ]; then
83 | filename=$(basename "$item")
84 | extension="${filename##*.}"
85 | echo "$i. $filename"
86 | echo "$item" >> /tmp/kindle_books.list
87 | i=$((i+1))
88 | fi
89 | done
90 |
91 | if [ $i -eq 1 ]; then
92 | echo "No books or folders found."
93 | return 1
94 | fi
95 |
96 | echo ""
97 | echo "--------------------------------"
98 | echo "n: Go up to parent directory"
99 | echo "d: Delete directory"
100 | echo "q: Back to main menu"
101 | echo ""
102 |
103 | total_items=$(( $(wc -l < /tmp/kindle_folders.list 2>/dev/null) + $(wc -l < /tmp/kindle_books.list 2>/dev/null) ))
104 |
105 | echo -n "Enter choice: "
106 | read choice
107 |
108 | case "$choice" in
109 | [qQ])
110 | return 0 # Changed from break to return
111 | ;;
112 | [nN])
113 | current_dir=$(dirname "$current_dir")
114 | ;;
115 | [dD])
116 | echo -n "Enter directory number to delete: "
117 | read dir_num
118 | if echo "$dir_num" | grep -qE '^[0-9]+$'; then
119 | if [ "$dir_num" -le $(wc -l < /tmp/kindle_folders.list 2>/dev/null) ]; then
120 | delete_directory "$dir_num"
121 | else
122 | echo "Invalid directory number"
123 | sleep 2
124 | fi
125 | fi
126 | ;;
127 | *)
128 | if echo "$choice" | grep -qE '^[0-9]+$'; then
129 | if [ "$choice" -ge 1 ] && [ "$choice" -le "$total_items" ]; then
130 | if [ "$choice" -le $(wc -l < /tmp/kindle_folders.list 2>/dev/null) ]; then
131 | current_dir=$(sed -n "${choice}p" /tmp/kindle_folders.list)
132 | else
133 | file_index=$((choice - $(wc -l < /tmp/kindle_folders.list 2>/dev/null)))
134 | delete_book "$file_index"
135 | fi
136 | else
137 | echo "Invalid selection (must be between 1 and $total_items)"
138 | sleep 2
139 | fi
140 | else
141 | echo "Invalid input"
142 | sleep 2
143 | fi
144 | ;;
145 | esac
146 |
147 | set -- "$current_dir"
148 | done
149 | }
--------------------------------------------------------------------------------
/kindlefetch/bin/misc.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | load_config() {
4 | eval "$(base64 -d "$LINK_CONFIG_FILE")"
5 | if [ -f "$CONFIG_FILE" ]; then
6 | . "$CONFIG_FILE"
7 | else
8 | first_time_setup
9 | fi
10 | }
11 |
12 | load_version() {
13 | if [ -f "$VERSION_FILE" ]; then
14 | cat "$VERSION_FILE"
15 | else
16 | echo "Version file wasn't found!"
17 | sleep 2
18 | echo "Creating version file"
19 | sleep 2
20 | get_version
21 | fi
22 | }
23 |
24 | sanitize_filename() {
25 | echo "$1" | sed -e 's/[^[:alnum:]\._-]/_/g' -e 's/ /_/g'
26 | }
27 |
28 | get_json_value() {
29 | echo "$1" | grep -o "\"$2\":\"[^\"]*\"" | sed "s/\"$2\":\"\([^\"]*\)\"/\1/" || \
30 | echo "$1" | grep -o "\"$2\":[^,}]*" | sed "s/\"$2\":\([^,}]*\)/\1/"
31 | }
32 |
33 | ensure_config_dir() {
34 | config_dir=$(dirname "$CONFIG_FILE")
35 | if [ ! -d "$config_dir" ]; then
36 | mkdir -p "$config_dir"
37 | fi
38 | }
39 |
40 | cleanup() {
41 | rm -f /tmp/kindle_books.list \
42 | /tmp/kindle_folders.list \
43 | /tmp/search_results.json \
44 | /tmp/last_search_*
45 | }
46 |
47 | get_version() {
48 | api_response=$(curl -s -H "Accept: application/vnd.github.v3+json" "https://api.github.com/repos/justrals/KindleFetch/commits") || {
49 | echo "Warning: Failed to fetch version from GitHub API" >&2
50 | echo "unknown"
51 | return
52 | }
53 |
54 | latest_sha=$(echo "$api_response" | grep -m1 '"sha":' | cut -d'"' -f4 | cut -c1-7)
55 |
56 | echo "$latest_sha" > "$VERSION_FILE"
57 | load_version
58 | }
59 |
60 | check_for_updates() {
61 | local current_sha=$(load_version)
62 |
63 | local latest_sha=$(curl -s -H "Accept: application/vnd.github.v3+json" \
64 | -H "Cache-Control: no-cache" \
65 | "https://api.github.com/repos/justrals/KindleFetch/commits?per_page=1" | \
66 | grep -oE '"sha": "[0-9a-f]+"' | head -1 | cut -d'"' -f4 | cut -c1-7)
67 |
68 | if [ -n "$latest_sha" ] && [ "$current_sha" != "$latest_sha" ]; then
69 | UPDATE_AVAILABLE=true
70 | return 0
71 | else
72 | return 1
73 | fi
74 | }
75 |
76 | save_config() {
77 | echo "KINDLE_DOCUMENTS=\"$KINDLE_DOCUMENTS\"" > "$CONFIG_FILE"
78 | echo "CREATE_SUBFOLDERS=\"$CREATE_SUBFOLDERS\"" >> "$CONFIG_FILE"
79 | echo "DEBUG_MODE=\"$DEBUG_MODE\"" >> "$CONFIG_FILE"
80 | echo "COMPACT_OUTPUT=\"$COMPACT_OUTPUT\"" >> "$CONFIG_FILE"
81 | }
--------------------------------------------------------------------------------
/kindlefetch/bin/search.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | display_books() {
4 | clear
5 | echo -e "
6 | _____ _
7 | / ____| | |
8 | | (___ ___ __ _ _ __ ___| |__
9 | \___ \ / _ \/ _\` | '__/ __| '_ \\
10 | ____) | __/ (_| | | | (__| | | |
11 | |_____/ \___|\__,_|_| \___|_| |_|
12 | "
13 | echo "--------------------------------"
14 | echo ""
15 |
16 | i=$((count-1))
17 | while [ $i -ge 0 ]; do
18 | book_info=$(echo "$1" | awk -v i=$i 'BEGIN{RS="\\{"; FS="\\}"} NR==i+2{print $1}')
19 | title=$(get_json_value "$book_info" "title")
20 | author=$(get_json_value "$book_info" "author")
21 | format=$(get_json_value "$book_info" "format")
22 | description=$(get_json_value "$book_info" "description")
23 |
24 | if ! $COMPACT_OUTPUT; then
25 | printf "%2d. %s\n" $((i+1)) "$title"
26 | [ -n "$description" ] && [ "$description" != "null" ] && echo " $description"
27 | echo ""
28 | else
29 | printf "%2d. %s by %s in %s format\n" $((i+1)) "$title" "$author" "$format"
30 | # [ -n "$description" ] && [ "$description" != "null" ] && echo " $description"
31 | echo ""
32 | fi
33 |
34 | i=$((i-1))
35 | done
36 |
37 | echo "--------------------------------"
38 | echo ""
39 | echo "Page $2 of $5"
40 | echo ""
41 |
42 | if [ "$3" = "true" ]; then
43 | echo -n "p: Previous page | "
44 | fi
45 | if [ "$4" = "true" ]; then
46 | echo -n "n: Next page | "
47 | fi
48 | echo "1-$count: Select book | q: Quit"
49 | echo ""
50 | }
51 |
52 | search_books() {
53 | local query="$1"
54 | local page="${2:-1}"
55 |
56 | if [ -z "$query" ]; then
57 | echo -n "Enter search query: "
58 | read query
59 | [ -z "$query" ] && {
60 | echo "Search query cannot be empty"
61 | return 1
62 | }
63 | fi
64 |
65 | echo "Searching for '$query' (page $page)..."
66 |
67 | local filters=""
68 | if [ -f "$SCRIPT_DIR"/tmp/current_filter_params ]; then
69 | filters=$(cat "$SCRIPT_DIR/tmp/current_filter_params")
70 | fi
71 |
72 | encoded_query=$(echo "$query" | sed 's/ /+/g')
73 | search_url="$ANNAS_URL/search?page=${page}&q=${encoded_query}${filters}"
74 | local html_content=$(curl -s "$search_url") || html_content=$(curl -s -x "$PROXY_URL" "$search_url")
75 |
76 | local last_page=$(echo "$html_content" | grep -o 'page=[0-9]\+"' | sort -nr | head -1 | cut -d= -f2 | tr -d '"')
77 | [ -z "$last_page" ] && last_page=1
78 |
79 | local has_prev="false"
80 | [ "$page" -gt 1 ] && has_prev="true"
81 |
82 | local has_next="false"
83 | [ "$page" -lt "$last_page" ] && has_next="true"
84 |
85 | echo "$query" > /tmp/last_search_query
86 | echo "$page" > /tmp/last_search_page
87 | echo "$last_page" > /tmp/last_search_last_page
88 | echo "$has_next" > /tmp/last_search_has_next
89 | echo "$has_prev" > /tmp/last_search_has_prev
90 |
91 | local books=$(echo "$html_content" | awk '
92 | BEGIN {
93 | RS="