├── CHANGELOG.md ├── QON_bash-note_helper.qml ├── README.md ├── LICENSE └── bash-note /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Bash-note changelog 2 | ========= 3 | 4 | ### 15.01.2017 5 | - Moved many settings out of the script file to call them with command line arguments (compabilty with old versions broken); 6 | - Made an overhaul of command line arguments (compabilty with old versions broken); 7 | - Implemented fast notes tagging; 8 | - Favcions download for "bookmark" notes; 9 | - Dependency check info moved to general "help" information; 10 | - Fixed many bugs. 11 | 12 | ### 30.09.2016 13 | - Non-ascill URLs are now decoded to a proper unicode instead of `%D0%AF`-like; 14 | - Web2note module now doesn't include Firefox reader toolbar html code to note. 15 | 16 | ### 23.08.2016 17 | - New option to scan all files before starting file system watcher; 18 | - New option for bash-note to generate absolute or relative links while processing notes; 19 | - Added entry type for Fast notes merger and Clipnote - bookmark with web page title handling; 20 | - Fast note merger now use headers as entry titles; 21 | - Clipnote has an option to add headers to an entry like Fast note merger does; 22 | - Fixed some broken things in Relink module; 23 | - Other minor enhancements. 24 | 25 | ### 01.08.2016 26 | - Added Relink module that can convert links and cleanup media dirctory of unused files; 27 | - Fast note merger module now process images put to inbox directory and has a sefety check for file extensions; 28 | - There's an option for auto-rotation of such images based on EXIF orientation to correctly display them in QOwnNotes and other apps that don't use EXIF orientation themselve; 29 | - Added limited support for symlinks - directories set on config can now be symlinks; 30 | - Fixed bugs when there is "media" in note root path; 31 | - Other minor enhancements. 32 | 33 | ### 16.07.2016 34 | Initial release. 35 | -------------------------------------------------------------------------------- /QON_bash-note_helper.qml: -------------------------------------------------------------------------------- 1 | import QtQml 2.0 2 | import com.qownnotes.noteapi 1.0 3 | 4 | QtObject { 5 | 6 | // Put absolute path to bash-note script file here 7 | property var bashNote:"/storage/emulated/0/notes/bash-note"; 8 | 9 | property var lastOpenedNote:null; 10 | 11 | function noteOpenedHook(note) { 12 | 13 | if (note.fullNoteFilePath != lastOpenedNote){ 14 | var replaceCommand = "0,/^clipnote_inbox_file_name=/ s|^clipnote_inbox_file_name=.*|clipnote_inbox_file_name='" + note.fullNoteFilePath + "'|"; 15 | var arguments = ["-i",replaceCommand,bashNote]; 16 | script.startDetachedProcess("sed", arguments); 17 | script.log("Bash-note: current note set to " + note.fullNoteFilePath); 18 | } 19 | 20 | lastOpenedNote = note.fullNoteFilePath; 21 | } 22 | 23 | function init() { 24 | script.registerCustomAction("runBashNote", "Scan this folder with Bash-note", "Scan this folder with Bash-note", "system-run-symbolic.svg"); 25 | script.registerCustomAction("setFolder", "Set folder as note root"); 26 | script.registerCustomAction("setFastnotesInbox", "Set note as Fast notes inbox"); 27 | script.registerCustomAction("editBashNote", "Edit Bash-note file", "Edit", "format-text-code.svg"); 28 | } 29 | 30 | function customActionInvoked(identifier) { 31 | 32 | switch (identifier) { 33 | 34 | case "runBashNote": 35 | var folder = script.currentNoteFolderPath(); 36 | var arguments = [bashNote,"-s",folder] 37 | script.startDetachedProcess("bash", arguments); 38 | script.log("Bash-note: Scanning " + folder); 39 | break; 40 | 41 | case "setFolder": 42 | var folder = script.currentNoteFolderPath(); 43 | var replaceCommand = "0,/^note_root_dir=/{s|^note_root_dir=.*|note_root_dir='" + folder + "'|}"; 44 | var arguments = ["-i",replaceCommand,bashNote]; 45 | script.startDetachedProcess("sed", arguments); 46 | script.log("Bash-note: Note root folder set to " + folder); 47 | break; 48 | 49 | case "setFastnotesInbox": 50 | var note = script.currentNote(); 51 | var replaceCommand = "0,/^fast_notes_inbox_file_name=/{s|^fast_notes_inbox_file_name=.*|fast_notes_inbox_file_name='" + note.fullNoteFilePath + "'|}"; 52 | var arguments = ["-i",replaceCommand,bashNote]; 53 | script.startDetachedProcess("sed", arguments); 54 | script.log("Bash-note: Fast notes inbox set to " + note.fullNoteFilePath); 55 | break; 56 | 57 | case "editBashNote": 58 | script.startDetachedProcess("kwrite", bashNote); 59 | break; 60 | } 61 | } 62 | } 63 | 64 | 65 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bash-note 2 | 3 | Bash-note is as shell script for GNU/Linux to help you dealing with plain-text markdown notes. 4 | 5 | Bash-note can: 6 | - Paste selected text blocks to markdown note, images included; 7 | - Convert web pages to markdown notes; 8 | - Merge "fast notes" into single "inbox" note in a smart manner; 9 | - Process/convert markdown links. 10 | 11 | Paired with [QOwnNotes](https://github.com/pbek/QOwnNotes) and the special script for that app, Bash-note knows the note currently opened in QOwnNotes and can paste selected text there without openning QOwnNotes itself. Make sure you've put path to Bash-note file to the QOwnNotes script file. There is more information about QOwnNotes script at the end of this readme. 12 | 13 | Before running the script you should open it and check config section for set options. The most important is `note_root_dir` which is the directory where you store markdown notes. 14 | This directory can be a symlink, anything inside it however should be real files or hardlinks. 15 | 16 | ## Using Bash-note 17 | 18 | You can see all possbile Bash-note command line arguments by running `bash-note -h`. 19 | This also shows if all requered binaries/packages are present in the system. 20 | 21 | Bash-note requeres: xclip, gettext, pandoc, wget, inotify-tools, jhead. 22 | 23 | ### Import settings from previous version 24 | Deficiencies: none 25 | Usage: `bash-note -i FILE` 26 | 27 | Imports Bash-note settings from FILE. This is useful for updating Bash-note: 28 | 1) Rename old version of bash-note to `bash-note.old` (or any other name); 29 | 2) Put a new version file to the directory; 30 | 3) Run `bash-note -i bash-note.old` and all existing settings will be imported to the new script. 31 | 32 | ### Paste selected text to markdown note 33 | Dependencies: xclip, iconv, pandoc, wget 34 | Usage: 35 | ``` 36 | bash-note -c t Add selected text or part of web page to the top of the inbox file set 37 | bash-note -c b Add selected text or part of web page to the bottom of the inbox file 38 | bash-note -c td Add current date, time and selected text to the top of the inbox file 39 | bash-note -c bd Add current date, time and selected text to the bottom of the inbox file 40 | bash-note -c t FILE Add selected text or part of web page to the top of a FILE, 41 | Same for other -c commands 42 | ``` 43 | 44 | 1) Select any part of web page or document, including images; 45 | 2) Run `bash-note -c td` by binded key shortcut or in terminal; 46 | 3) The text block is in your note. 47 | 48 | Bash-note can paste text block selected in any application (browser, text editor, etc.) to the note file set in config section or by command line arguments. When used with browser Clipnote will also paste all images in selection. 49 | 50 | The provided QOwnNotes script will continously change note in config secion to currently opened note. 51 | 52 | The best way to use it is binding `bash-note -c td` to a key shortcut using you DE instruments or third-party app, and then using it as you use clipboard. I use it assigned to `Win+C`. You can also assign other keys, like `Win+V`, to a `bash-note -c td FILE` to put selected text to that predefined FILE. 53 | 54 | If selected text is web URL, Bash-note considers it as "bookmark" and pastes URL target title and icon as a link to that URL. 55 | 56 | ### Convert web pages to markdown notes 57 | Dependencies: iconv, pandoc 58 | Usage: by "file scanner" with `bash-note -s` for single run or `bash-note -w` for continious directory watch. 59 | Processed files: web pages and their "_files" directories saved to any directory inside note root. 60 | 61 | 1) Open web page in browser; 62 | 2) (Optional) Use "Reader view"; 63 | 3) Save web page to any directory inside note root; 64 | 4) Run Bash-note file scanner by `bash-note -s` or `bash-note -w`; 65 | 5) Web page is a markdown note now. 66 | 67 | Bash-note will convert any web page it finds in note root directory to a markdown note. 68 | The note will have the same name as web page and will be stored in the same sub-directory. 69 | 70 | Modern web pages are very cluttered. The easiest way to get a clean note is saving a web page from the "Reader view" of your browser. Then you can save web page to a desired sub-folder. It will be converted by Bash-note's file scanner. 71 | 72 | There are two Firefox extensions that make the usage of this module more efficient: 73 | [Save File to](https://addons.mozilla.org/en/firefox/addon/save-file-to/) by Atte Kemppilä - adds new context menu items to quickly save web page to desired directory. 74 | [Stylish](https://addons.mozilla.org/en/firefox/addon/stylish/?src=search) with [Reader Mode Button Always Visible](https://userstyles.org/styles/123290/reader-mode-button-always-visible) userstyle - forces "Reader view" availability for every page or 75 | [Open in Reader View](https://addons.mozilla.org/en/firefox/addon/reader-view/) by rNeomy - adds context menu items to use "Reader view" even on pages Firefox doesn't allow to. 76 | 77 | With this extensions the experience of using Bash-note with browser is close to the one of web clippers. Reader mode removes all the clutter and "save to" context menu allows to save web page to any directory by 2 clicks. 78 | 79 | ### Merge "fast notes" into single "inbox" note 80 | Dependencies: jhead 81 | Usage: by "file scanner" with `bash-note -s` for single run or `bash-note -w` for continious directory watch. 82 | Processed files: images and files with set extension (default is `.txt`) put to set "inbox" directory. 83 | 84 | 1) Run Bash-note file scanner by `bash-note -s` or `bash-note -w`; 85 | 2) Put file with set extension (default is `txt`) or an image to the set inbox directory; 86 | 3) File's content added to the set inbox note file. 87 | 88 | Bash-note is supposed to help manage small notes made on the run, which I call "fast notes". 89 | This notes should have different extension (default is `.txt`) than the main notes (default is `.md`). 90 | Bash-note will process only "fast notes" put to specific "inbox" directory which can be set in the Bash-note file. 91 | 92 | If "fast note" is web URL, Bash-note considers it as "bookmark" and get URL target title and icon as a link to that URL. 93 | 94 | Any image put to "inbox" note, like the photo of some document or phone number, will be considered as "fast note" too. 95 | 96 | Bash-note will put the content of all the "fast note" to a single note set in the Bash-note file and then delete them. 97 | 98 | "Fast notes" can be tagged by putting words beggining with symbols set in config section of Bash-note to the text. Default is `@@`. Tags can't have spaces. There can be multiple tags. 99 | The "fast note" with a tag will be merged to the file with the same name. 100 | So if you want to note some idea about using bash-note, you can put `@@bash-note` after your idea. All such notes will be merged to a single `bash-note.md`. 101 | 102 | ### Process markdown links 103 | Dependencies: none 104 | Usage: `bash-note -l` 105 | 106 | Bash-note can parse and process links and linked files with various methods. Bash-note will scan all notes in note root directory set in Bash-note file unless the specific directory provided by command line arguments. 107 | 108 | There is an interactive mode, where you can choose how you want to use it, with all necessary information and action confirmations. You can run it with `bash-note -l`. 109 | 110 | Before each run Bash-note scans note root directory parsing notes for links and getting info about every link. The scan can take a while. You can run the parser in test mode for verbose output and benchmarking with `bash-note -l t`. It won't write anything to files in this mode. 111 | 112 | All commands available in interactive mode can be run with command line arguments after `bash-note -l`. The command run that way will ask no confirmation. 113 | 114 | So, if you want to cleanup your media directory from unused images you can: 115 | 1) Run `bash-note -l`; 116 | 2) Type `c` and press Enter; 117 | 3) Confirm action by typing `y` and pressing Enter. 118 | 119 | Or you can make all the following by running `bash-note -l c`. 120 | 121 | Some actions like conversion of links have a verbose test run mode that can be run on the confirmation stage by typing `t` instead of confirming `y`. This test also won't write anything. 122 | 123 | Run `bash-note -h` to see all command line arguments. 124 | 125 | ## Bash-note helper QOwnNotes script 126 | 127 | TODO 128 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /bash-note: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # ______ _ _ 3 | # | ___ \ | | | | 4 | # | |_/ / __ _ ___| |__ _ __ ___ | |_ ___ 5 | # | ___ \/ _` / __| '_ \_____| '_ \ / _ \| __/ _ \ 6 | # | |_/ / (_| \__ \ | | |____| | | | (_) | | ' __/ 7 | # \____/ \__,_|___/_| |_| |_| |_|\___/ \__\___| 8 | # by Maboroshy 9 | # 10 | # THIS SCRIPT COMES WITHOUT ANY WARRANTY 11 | # ANY DAMAGE DONE TO YOUR FILES IS ALL YOUR FAULT 12 | 13 | # Don't change section markers with CAPS below or importing settings won't work. 14 | 15 | ##################################################### 16 | # CONFIG SECTION: Set options for each module here. # 17 | ##################################################### 18 | 19 | ## General configuration ## 20 | ##-----------------------## 21 | 22 | # Directory that stores notes or stores sub-directories with notes. 23 | # For QOwnNotes it is the one set in options and having "media" sub-directory for storing inline images. 24 | note_root_dir='/storage/emulated/0/notes' 25 | 26 | # File extension which is considered as main note extension. Can be "md", "txt" or whatever. 27 | # Anyway script considers that there is markdown syntax inside it. 28 | main_note_extension="md" 29 | 30 | # Type of links Bash-note modules will generate while processing notes: "absolute" or "relative". 31 | # QOwnNotes generates relative links but also handles absolute without issues. 32 | # Most markdown editors won't work with relative links, so use "absolute" if you need portability. 33 | preferred_link_type="relative" 34 | 35 | 36 | ## Notifications ## 37 | ##---------------## 38 | 39 | ### Duration of notifications in msec (1000 = 1 sec). Put 0 to disable notifications. 40 | ### Some events use short notifications that are half of set duration. 41 | notifications_duration=4000 42 | 43 | ### Optional icons for notifications. Defaults are from KDE Plasma 5 Breeze icon set. 44 | icon_success="emblem-success.svg" 45 | icon_warning="emblem-warning.svg" 46 | 47 | 48 | ## Paste selected text blocks to markdown note ## 49 | ##---------------------------------------------## 50 | 51 | # Put inbox note file name here. Can have absolute path, otherwise file will be stored in note root directory. 52 | clipnote_inbox_file_name='/storage/emulated/0/notes/!Inbox.md' 53 | 54 | 55 | ## Convert web pages to markdown notes ## 56 | ##-------------------------------------## 57 | 58 | # Put 1 to enable, anything other do disable. 59 | web2note_enabled=1 60 | 61 | # Web page files directory appendix. Firefox and Chromium save linked files in "NAME_files" directory. 62 | # Don't name other directories like that or bad thing can happen to files there! 63 | web_page_files_dir_app="_files" 64 | 65 | 66 | ## Merge "fast notes" into single "inbox" note ## 67 | ##---------------------------------------------## 68 | 69 | # Put 1 to enable, anything other do disable. 70 | fast_notes_merger_enabled=1 71 | 72 | # Extension of files which are considered as "fast notes" and should be merged. 73 | fast_notes_extension=txt 74 | 75 | # Inbox directory. All "fast notes" and images put there will be added to inbox note. Absolute path. 76 | fast_notes_merger_inbox_dir="$note_root_dir/Inbox" 77 | 78 | # Put inbox note file name here. Can have absolute path, otherwise will be stored in inbox directory. 79 | # Can be the same as for Clipnote module. 80 | fast_notes_inbox_file_name='/storage/emulated/0/notes/!Inbox.md' 81 | 82 | # Entry sorting mode: newer_first or older_first 83 | fast_notes_sorting_mode=newer_first 84 | 85 | # If a word in a fast note begins with a set characters this word is considered as a tag. Tags can't have spaces. 86 | # Fast note with a tag will be added to a note in inbox directory named like the tag. 87 | # Fast notes can have multiple tags. Leave empty to disable tagging. 88 | fast_notes_tag_marker='@@' 89 | 90 | 91 | ########################################################################## 92 | # CODE SECTION: Don't change anything here... unless you really want to. # 93 | ########################################################################## 94 | 95 | IFS=$'\n' 96 | 97 | ### Set argument as note root path for file scanner mode. 98 | [[ $1 = -s ]] || [[ $1 = -w ]] && [[ -n "$2" ]] && note_root_dir="$2" 99 | 100 | ### Get the real path of directories and files if they are symlinks. 101 | ### Also cuts "/" in the end of paths so user can't really make a mistake by putting or not putting "/". 102 | note_root_dir=$(realpath -q "$note_root_dir") 103 | fast_notes_merger_inbox_dir=$(realpath -q "$fast_notes_merger_inbox_dir") 104 | 105 | ### Set the "media" directory name and path according to link type and the name. With symlink fix. 106 | media="media" 107 | if [[ $preferred_link_type = "absolute" ]]; then 108 | media_path="$note_root_dir/$media" 109 | media_path=$(realpath -q "$media_path") 110 | else media_path="$media" 111 | fi 112 | 113 | mkdir -p "$note_root_dir/$media/Favicons" 114 | 115 | ##==================## 116 | ## Shared Functions ## 117 | ##==================## 118 | 119 | ### Show notification: notify done|fast|fail SUMMARY BODY 120 | ### Fast is half duration. 121 | 122 | notify () { 123 | if [[ $notifications_duration != 0 ]]; then 124 | [[ $1 = done ]] && notify-send -i "$icon_success" -t $notifications_duration "$2" "$3" 125 | [[ $1 = fast ]] && notify-send -i "$icon_success" -t $(($notifications_duration/2)) "$2" "$3" 126 | [[ $1 = fail ]] && notify-send -i "$icon_warning" -t $notifications_duration "$2" "$3" 127 | fi 128 | } 129 | 130 | ### Add to inbox file: add_to_inbox ENTRY SORTING_MODE INBOX_FILE 131 | ### Adds text/variable content to a inbox file using set order 132 | add_to_inbox () { 133 | if [[ $2 = newer_first ]]; then 134 | inbox_content="$(printf '%s\n\n' "$1" "$(cat "$3")")" 135 | printf '%s\n' "$inbox_content" > "$3" 136 | else printf '\n\n%s' "$1" >> "$3" 137 | fi 138 | } 139 | 140 | ### Check if TEXT is a link and convert it to markdown: check_for_link TEXT 141 | check_for_link () { 142 | if [[ $1 = http://* ]] || [[ $1 = https://* ]]; then 143 | link="$(printf '%s\n' "$1" | head -n1)" 144 | link_html="$(wget -qO- "$link")" 145 | 146 | ### Get web page title 147 | title="$(printf '%s' "$link_html" | grep -iPo '(?<=)(.*)(?=)' | tr '\n' ' ')" 148 | 149 | ### Download favicon using google service. 150 | favicon_name="$RANDOM$RANDOM.png" 151 | wget -q "https://www.google.com/s2/favicons?domain=$link" \ 152 | -O "$note_root_dir/$media/Favicons/$favicon_name" 153 | 154 | ### Don't use google's "no favicon" icon. 155 | [[ $(md5sum "$note_root_dir/$media/Favicons/$favicon_name") = 3ca64f83fdcf25135d87e08af65e68c9* ]] && 156 | rm "$note_root_dir/$media/Favicons/$favicon_name" 157 | 158 | ### Prepare markdown link. 159 | if [[ -f "$note_root_dir/$media/Favicons/$favicon_name" ]] && [[ -n $title ]]; then 160 | output="![](file://$media_path/Favicons/$favicon_name) [$title]($link)" 161 | elif [[ -f "$note_root_dir/$media/Favicons/$favicon_name" ]] && [[ -z $title ]]; then 162 | output="![](file://$media_path/Favicons/$favicon_name) <$link>" 163 | elif [[ -n $title ]]; then 164 | output="[$title]($link)" 165 | else output="<$link>" 166 | fi 167 | 168 | else output="$1" 169 | fi 170 | 171 | printf '%s\n' "$output" 172 | } 173 | 174 | ### Decode URL like %D0%AF to normal unicode. 175 | urldecode () { : "${*//+/ }"; echo -e "${_//%/\\x}"; } 176 | 177 | ##===============================## 178 | ## Safety checks and preparation ## 179 | ##===============================## 180 | 181 | ### Check if main note directory path contains special characters. If it does - notify and stop the script. 182 | if [[ $note_root_dir = *[#\\\"]* ]] ; then 183 | notify fail "Bash-note stopped: Bad note directory name" \ 184 | "$note_root_dir contains special characters. Please rename note directory or higher level directories." 185 | exit 186 | fi 187 | 188 | ### Check if main note directory is hidden. If it is - notify and stop the script. 189 | if [[ $note_root_dir = */.* ]] ; then 190 | notify fail "Bash-note stopped: Note directory is hidden" \ 191 | "$note_root_dir is a hidden directory. Delete the leading dot for Bash-note to find it." 192 | exit 193 | fi 194 | 195 | ## Paste selected text blocks to markdown note ## 196 | ##---------------------------------------------## 197 | 198 | if [[ $1 = -c ]]; then 199 | 200 | ### Parse arguments 201 | [[ $2 = b ]] && clipnote_sorting_mode=older_first && clipnote_insert_date=0 202 | [[ $2 = t ]] && clipnote_sorting_mode=newer_first && clipnote_insert_date=0 203 | [[ $2 = bd ]] && clipnote_sorting_mode=older_first && clipnote_insert_date=1 204 | [[ $2 = td ]] && clipnote_sorting_mode=newer_first && clipnote_insert_date=1 205 | 206 | ### If there is a second command line argument, take it as inbox file name. 207 | [[ -n $3 ]] && clipnote_inbox_file_name="$3" 208 | 209 | ### Check if inbox file and media directory for images exist, otherwise make them. 210 | cd "$note_root_dir" 211 | [ -f "$clipnote_inbox_file_name" ] || touch "$clipnote_inbox_file_name" 212 | mkdir -p "$note_root_dir/$media/Clipnote" 213 | fi 214 | 215 | ## File scanner modules ## 216 | ##----------------------## 217 | 218 | if [[ -z "$1" || $1 = "-s" ]]; then 219 | 220 | ## Merge "fast notes" into single "inbox" note ## 221 | ##---------------------------------------------## 222 | 223 | ### Check if "fast notes" extension is different from main notes extension. 224 | ### That may save main notes from assembling into a single Devastator Note Prime. 225 | if [[ $fast_notes_merger_enabled = 1 ]] && [[ $main_note_extension = "$fast_notes_extension" ]]; then 226 | notify fail "Bash-note: Fast notes merger module stopped" \ 227 | "Your main note extension - $main_note_extension is the same as fast notes extension." 228 | fast_notes_merger_enabled=0 229 | fi 230 | 231 | ### Check if fast notes merger inbox file and a directory to store images exist, otherwise make them. 232 | if [[ $fast_notes_merger_enabled = 1 ]]; then 233 | cd "$fast_notes_merger_inbox_dir" 234 | [ -f "$fast_notes_inbox_file_name" ] || touch "$fast_notes_inbox_file_name" 235 | mkdir -p "$note_root_dir/$media/ImageNotes" 236 | fi 237 | fi 238 | 239 | ##===========================================## 240 | ## Scripts called by commands line arguments ## 241 | ##===========================================## 242 | 243 | ## Man page and dependency checker## 244 | ##--------------------------------## 245 | 246 | ### Function to check if binary present: check_command BINARY 247 | check_command () { hash "$1" && echo "FOUND" || echo "NOT FOUND" ;} 248 | 249 | if [[ -z $1 ]] || [[ $1 = -h ]] || [[ $1 = --help ]]; then echo \ 250 | "Bash-note, rev. 15.01.2017 251 | 252 | Bash-note is as set of small scripts called modules which are supposed to help you getting 253 | more out of plain text with markdown syntax than your text editor or note application can. 254 | 255 | Bash-note stores its settings inside the script file in config section. 256 | 257 | There are two types of modules: 258 | 1) Modules evoked on specific file types while scanning files. 259 | Set the settings for them in config section and run bash-note file scanner. 260 | 261 | 2) Modules evoked by running bash-note with specific argument. 262 | Run them with one of the command line arguments below. 263 | Using more than one the arguments is not supported. 264 | 265 | Usage: 266 | 267 | bash-note Show this information 268 | 269 | bash-note -i FILE Import bash-note settings from FILE which is bash-note script 270 | 271 | Process files with bash-note with modules enabled in config section 272 | 273 | bash-note -s Make a single scan of files in a directory set in config section 274 | bash-note -s DIRECTORY Make a single scan of files in DIRECTORY 275 | 276 | bash-note -w Scan files in a directory set in config section then continue watching for changes 277 | bash-note -w DIRECTORY Scan files in DIRECTORY then continue watching for changes 278 | 279 | Add clipboard content to a file as markdown syntax 280 | 281 | bash-note -c t Add selected text or part of web page to the top of a file set in config section 282 | bash-note -c b Add selected text or part of web page to the bottom of a file set in config section 283 | bash-note -c td Add current date, time and selected text or part of web page 284 | to the top of a file set in config section 285 | bash-note -c bd Add current date, time and selected text or part of web page 286 | to the bottom of a file set in config section 287 | bash-note -c t FILE Add selected text or part of web page to the top of a FILE, same for other -c commands 288 | 289 | Process links in markdown files 290 | 291 | bash-note -l Process links in markdown files with interactive menu 292 | bash-note -l t Run verbose test of link parser 293 | 294 | bash-note -l l Show list of all links by categories 295 | 296 | bash-note -l c Clean media directory from orphaned media 297 | bash-note -l c DIRECTORY Clean media directory in DIRECTORY, notes will be scanned in DIRECTORY too 298 | 299 | bash-note -l a Convert all relative links to absolute 300 | bash-note -l a FILE Convert all relative links in FILE to absolute 301 | bash-note -l a DIRECTORY Convert all relative links in notes stored in DIRECTORY to absolute 302 | bash-note -l at Run verbose test of conversion. No notes will be modified. 303 | 304 | bash-note -l r Convert all absolute links to relative 305 | bash-note -l r FILE Convert all relative links in FILE to relative 306 | bash-note -l r DIRECTORY Convert all absolute links in notes stored in DIRECTORY to relative 307 | bash-note -l rt Run verbose test of conversion. No notes will be modified. 308 | 309 | Bash-note dependencies: 310 | 311 | DE notifications: 312 | libnotify $(check_command notify-send) 313 | 314 | File scanner: 315 | inotify-tools $(check_command inotifywait) 316 | 317 | Add selected text or part of web page to a file (bash-note -c): 318 | xclip $(check_command xclip) 319 | gettext $(check_command iconv) 320 | pandoc $(check_command pandoc) 321 | wget $(check_command wget) 322 | 323 | Convert saved web page to markdown: 324 | gettext $(check_command iconv) 325 | pandoc $(check_command pandoc) 326 | 327 | Add all fast notes to a single file: 328 | jhead $(check_command jhead) 329 | "; exit; fi 330 | 331 | ## Settings importer: imports config from another bash-note file ## 332 | ##---------------------------------------------------------------## 333 | 334 | if [[ $1 = -i ]] && [[ -n "$2" ]]; then 335 | 336 | ### Get all setting from config section of the old file. 337 | for setting_string in $(sed -n '/CONFIG SECTION: /,/CODE SECTION: /p' "$2" | grep -ve '#' -ve ^$); do 338 | 339 | ### Replace all setting in this file with setting existing in the old file. 340 | ### If this (running) file have some new setting types - they stay. 341 | setting_name=${setting_string%=*} 342 | sed -i "s/^$setting_name.*/$setting_string/" "$(readlink -f "$0")" 343 | done 344 | 345 | [[ $? = 0 ]] && notify done "Bash-note: Settings imported" \ 346 | "Settings from $2 imported to current script." 347 | fi 348 | 349 | ## Paste selected text blocks to markdown note ## 350 | ##---------------------------------------------## 351 | 352 | if [[ $1 = -c ]]; then 353 | 354 | cd "$note_root_dir" 355 | 356 | ### Get clipboard content as html and convert it to markdown. 357 | IFS= 358 | clipnote_html="$(xclip -clip clip -t text/html -o || xclip -clip clip -t UTF8_STRING -o)" 359 | clipnote_url="$(xclip -clip clip -t text/x-moz-url-priv -o)" 360 | clipnote_md="$(printf '%s\n' "$clipnote_html" | iconv -sc -t utf-8 \ 361 | | pandoc -f html -t markdown_strict+pipe_tables-raw_html --wrap=none)" 362 | 363 | IFS=$'\n' 364 | 365 | ### Get inline images links, even when their markdown code contains new lines. 366 | for img_link in $(printf '%s\n' "$clipnote_md" | tr '\n' ' ' \ 367 | | grep -o -e '!\[[^]]*] *([^)]*)' | grep -o -e 'http[^ )]\+'); do 368 | 369 | ### Download that images to media directory. 370 | ### Adds some unique digits to file names to work around possible duplicates. 371 | img_name="${img_link##*/}" 372 | uniq_img_name="$RANDOM-$img_name" 373 | wget -q "$img_link" -O "$note_root_dir/$media/Clipnote/$uniq_img_name" & 374 | 375 | ### Replace links to online images with links to downloaded ones. 376 | clipnote_md="${clipnote_md//"$img_link"/"file://$media_path/Clipnote/$uniq_img_name"}" 377 | done 378 | 379 | ### If it has a http link as the first line then it's a bookmark. 380 | ### Get the web page title and format it all as entry. If there's no link make a simple text entry. 381 | entry="$(check_for_link "$clipnote_md")" 382 | 383 | ### Get the current date and time for a header. Add source link to header if url is available. 384 | if [[ $clipnote_insert_date = 1 ]]; then 385 | now_time=$(date '+%R %a %x') 386 | 387 | if [[ -n $clipnote_url ]]; then 388 | entry=$(printf '%s\n%s\n' "### $now_time [@]($clipnote_url) " "$entry") 389 | else entry=$(printf '%s\n%s\n' "### $now_time " "$entry") 390 | fi 391 | fi 392 | 393 | ### Write entry to inbox note file with function. 394 | add_to_inbox "$entry" "$clipnote_sorting_mode" "$clipnote_inbox_file_name" 395 | 396 | ### Show notification. There is a function for that. 397 | notify fast "Selection saved to ${clipnote_inbox_file_name##*/}." 398 | exit 399 | fi 400 | 401 | ## Process/convert markdown links ## 402 | ##--------------------------------## 403 | 404 | if [[ $1 = -l ]]; then 405 | 406 | ### Get the list of all note files 407 | declare -a note_files_list 408 | 409 | if [[ -f $3 ]]; then 410 | note_files_list="$3" 411 | else 412 | [[ -d $3 ]] && note_root_dir="$3" 413 | note_files_list=( $(find "$note_root_dir" -not -path '*/.*' -name "*.$main_note_extension" -type f) ) 414 | fi 415 | 416 | ### Get the list of all files in media directory 417 | declare -a media_files_list 418 | media_files_list=( $(find "$note_root_dir/$media" -type f) ) 419 | 420 | ### Prepare arrays for all types of links known to Relink. Link can be in more than one list. 421 | declare -A md_links_list 422 | declare -A inline_image_links_list 423 | declare -A reference_style_links_list 424 | declare -A absolute_links_list 425 | declare -A relative_links_list 426 | declare -A no_leading_file_links_list 427 | declare -A web_links_list 428 | declare -A note_links_list 429 | 430 | ### Set regexp for reference-style links 431 | reference_style_brackets='\[[^]]*]: *<[^>]*>' 432 | reference_style_space='\[[^]]*]: *[^ ]* ' 433 | 434 | printf '%s\n\n' "Scanning notes..." 435 | 436 | ### Parse all links in all notes. 437 | for note_file in ${note_files_list[@]}; do 438 | 439 | ### Print note file name in test mode. The bold way. 440 | if [[ $2 = t ]] || [[ $2 = test ]]; then 441 | printf '\n\033[1m%s\033[0m' "FILE $note_file" 442 | fi 443 | 444 | ### Get any markdown link code. Truncating enables multi-line link code but doubles run time. 445 | for md_link in $(cat "$note_file" | tr '\n' ' ' \ 446 | | grep -o -e '!\[[^]]*] *([^)]*)' -e '\[[^]]*] *([^)]*)' -e '\[[^]]*]: *<[^>]*>' -e '\[[^]]*]: *[^ ]* '); do 447 | 448 | ### Set link unique ID and put it to markdown links list 449 | link_id=$((link_id+1)) 450 | md_links_list["$link_id:$note_file"]="$md_link" 451 | file_link_type="#$link_id: " 452 | 453 | ### Get the link part. Use the special way for two types of refernce-style links. 454 | ### If it's an inline image link "!" will stay before the file link. 455 | if [[ $md_link =~ $reference_style_brackets ]]; then 456 | file_link="$(printf '%s\n' "$md_link" | sed -n 's|\[.*]: *<\(.*\)>]*|\1|p')" 457 | file_link_type+='Reference-style, ' 458 | elif [[ $md_link =~ $reference_style_space ]]; then 459 | file_link="$(printf '%s\n' "$md_link" | sed -n 's|\[.*]: *\(.*\)|\1|p')" 460 | file_link_type+='Reference-style, ' 461 | else 462 | file_link="$(printf '%s\n' "$md_link" | sed -n 's|\[.*] *(\(.*\))|\1|p')" 463 | fi 464 | 465 | ### 1. If the link starts with "!' clear "!" and mark as image link. 466 | [[ $file_link = "!"* ]] && \ 467 | file_link="${file_link#\!}" && file_link_type+='Inline image, ' 468 | 469 | ### 2. Clear from possible description in "" and spaces from the both sides 470 | file_link="${file_link%% \"*}" 471 | read -r file_link <<< "$file_link" 472 | 473 | ### 3. Clear leading "file:/", or put to appropriate list of it's not there 474 | if [[ $file_link = file:/* ]]; then 475 | file_link="$(expr "$file_link" : 'file:/*\(.*\)')" 476 | file_link="/$file_link" 477 | file_link_type+='Has leading "file:/", ' 478 | else 479 | no_leading_file_links_list["$link_id:$note_file"]="$file_link" 480 | file_link_type+='Missing "file:/" part, ' 481 | fi 482 | 483 | ### Parse and put the link to appropriate list. 484 | [[ $file_link_type =~ image ]] \ 485 | && inline_image_links_list["$link_id:$note_file"]="$file_link" 486 | [[ $file_link_type =~ Reference ]] \ 487 | && reference_style_links_list["$link_id:$note_file"]="$file_link" 488 | [[ $file_link = http* ]] \ 489 | && web_links_list["$link_id:$note_file"]="$file_link" && file_link_type+='Web link, ' 490 | [[ $file_link = note* ]] \ 491 | && note_links_list["$link_id:$note_file"]="$file_link" && file_link_type+='Note link, ' 492 | 493 | if [[ -f $(urldecode $file_link) ]]; then 494 | absolute_links_list["$link_id:$note_file"]="$file_link" 495 | file_link_type+='Absolute.' 496 | elif [[ -f $note_root_dir$(urldecode $file_link) ]]; then 497 | relative_links_list["$link_id:$note_file"]="$file_link" 498 | file_link_type+='Relative.' 499 | else 500 | file_link_type+="Not leading to a stored file." 501 | fi 502 | 503 | ### Show all the files and their links list in test mode. 504 | [[ $2 = t ]] || [[ $2 = test ]] && \ 505 | printf '\n%s\n%s\n%s\n' "MD $md_link" "LINK $file_link" "INFO $file_link_type" 506 | done 507 | done 508 | 509 | ### If there's no command line argument given show scan summary and interactive menu. 510 | if [[ -z $2 ]]; then 511 | printf '%s\n\n%s\n' \ 512 | "Bash-note scanned ${#note_files_list[@]} notes and found ${#md_links_list[@]} links." \ 513 | "Among these links there are:" 514 | 515 | printf ' %-3s %s\n' \ 516 | "${#inline_image_links_list[@]}" "inline image links" \ 517 | "${#reference_style_links_list[@]}" "reference-style links" \ 518 | "${#web_links_list[@]}" "links to web resourses" \ 519 | "${#note_links_list[@]}" "links to other notes" \ 520 | "${#absolute_links_list[@]}" "links with absolute path" \ 521 | "${#relative_links_list[@]}" "links with relative path" 522 | 523 | printf '\n\n%s\n' 'Bash-note commands to handle links:' 524 | printf ' %-15s %s\n' \ 525 | 'l' 'to show list of all links with above categories' \ 526 | 'c' 'to clean media directory from files that are not linked to' \ 527 | 'a' 'to convert all relative links to absolute' \ 528 | 'r' 'to convert all absolute links to relative' \ 529 | 'any other' 'to stop this script' 530 | 531 | printf '\n%s' 'Type command and press Enter > ' 532 | read relink_command 533 | else 534 | relink_command="$2" 535 | fi 536 | 537 | ### List command: show list of all links by categories 538 | if [[ $relink_command = l ]] || [[ $2 = t ]]; then 539 | printf '\033[1m%s\n\033[0m%s\n\n' \ 540 | "_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _" "" \ 541 | "Inline images links: ${#inline_image_links_list[*]}" "${inline_image_links_list[*]}" \ 542 | "Reference-style links: ${#reference_style_links_list[*]}" "${reference_style_links_list[*]}" \ 543 | "Links to other notes: ${#note_links_list[*]}" "${note_links_list[*]}" \ 544 | "Absolute links: ${#absolute_links_list[*]}" "${absolute_links_list[*]}" \ 545 | "Relative links: ${#relative_links_list[*]}" "${relative_links_list[*]}" \ 546 | "Web resourses links: ${#web_links_list[*]}" "${web_links_list[*]}" 547 | 548 | ### Show scan time in test mode 549 | [[ $2 = t ]] && printf '\n%s\n\n' "The scan took:" && times 550 | fi 551 | 552 | ### Cleanup command: clean media directory from files that are not linked to. 553 | if [[ $relink_command = c ]]; then 554 | 555 | declare -a media_files_to_delete 556 | for media_file in "${media_files_list[@]}"; do 557 | # media_file="${media_file##*$media}" 558 | if [[ ! "${absolute_links_list[*]}" =~ $media_file ]] \ 559 | && [[ ! "${relative_links_list[*]}" =~ ${media_file##*$note_root_dir} ]]; then 560 | media_files_to_delete+=( $media_file ) 561 | fi 562 | done 563 | 564 | ### Stop the script if there's nothing to cleanup. 565 | [[ ${#media_files_to_delete[@]} = 0 ]] \ 566 | && printf '\n%s\n\n' 'No unused files found in "media" directory.' && exit 567 | 568 | ### Confirm 569 | if [[ ! $2 = c ]]; then 570 | printf '%s\n' '' "Files not linked to in any of your notes: ${#media_files_to_delete[@]}" \ 571 | "${media_files_to_delete[@]}" 572 | printf '\n%s' 'Do you want to delete this files?' \ 573 | 'Type "y" to delete or anything else to abort > ' 574 | read delete_confirmed 575 | else 576 | delete_confirmed=y 577 | fi 578 | 579 | ### Delete the file and delete the directory it is stored if it's empty. 580 | if [[ $delete_confirmed = y ]]; then 581 | for file_to_delete in ${media_files_to_delete[@]}; do 582 | rm -f "$file_to_delete" && n_deleted=$(($n_deleted+1)) 583 | rm -d "${file_to_delete%/*}" | true 584 | done 585 | printf '\n%s\n\n' "$n_deleted files deleted." 586 | [[ ${#media_files_to_delete[@]} != $n_deleted ]] \ 587 | && printf '%s\n\n' "$((${#media_files_to_delete[@]} - $n_deleted)) files could not be deleted." 588 | fi 589 | fi 590 | 591 | ### Relative command: convert all absolute links to relative. 592 | if [[ $relink_command = r ]] || [[ $relink_command = rt ]]; then 593 | 594 | ### Confirm 595 | if [[ ! $2 = r ]] && [[ ! $2 = rt ]]; then 596 | printf '\n%s' "Do you want to convert ${#absolute_links_list[@]} links to relative?" \ 597 | 'Type "y" to convert, "t" for dry-run test or anything else to abort > ' 598 | read convert_confirmed 599 | else 600 | [[ $2 = r ]] && convert_confirmed=y 601 | [[ $2 = rt ]] && convert_confirmed=t 602 | fi 603 | 604 | ### Convert links and replace them in note files. 605 | if [[ $convert_confirmed = y ]] || [[ $convert_confirmed = t ]] \ 606 | && (( ${#absolute_links_list[@]} > 0 )); then 607 | for id in ${!absolute_links_list[@]}; do 608 | 609 | ### Get note file name and convert link. 610 | note_file="${id#*:}" 611 | source_link="${absolute_links_list[$id]}" 612 | target_link="${source_link#$note_root_dir}" 613 | 614 | ### Replace the link or make verbose dry-run for test. 615 | if [[ $convert_confirmed = y ]]; then 616 | 617 | ### Make sure link have right leading "file:". 618 | ### (file:/DIR - absolute,real size; file://DIR - relative; file:///DIR - absolute,scaled) 619 | if [[ ${!no_leading_file_links_list[@]} =~ "$id" ]]; then 620 | sed -i "/file:/! s|$source_link|file:/$target_link|" "$note_file" \ 621 | && n_converted=$(($n_converted+1)) 622 | else 623 | sed -i "s|file:/*$source_link|file:/$target_link|" "$note_file" \ 624 | && n_converted=$(($n_converted+1)) 625 | fi 626 | else 627 | printf '\n%s\n%s\n%s\n' "NOTE $note_file" "ABS $source_link" "REL $target_link" 628 | fi 629 | done 630 | 631 | ### Show summary or an empty line for a test run. 632 | if [[ $convert_confirmed = y ]]; then 633 | printf '\n%s\n\n' "$n_converted links converted." 634 | [[ ${#absolute_links_list[@]} != $n_converted ]] \ 635 | && printf '%s\n\n' "Conversion of $((${#relative_links_list[@]} - $n_converted)) links failed." 636 | 637 | else echo 638 | fi 639 | else 640 | printf '\n%s\n\n' "No absolute links found." 641 | fi 642 | fi 643 | 644 | ### Absolute command: convert all relative links to absolute. 645 | if [[ $relink_command = a ]] || [[ $relink_command = at ]]; then 646 | 647 | ### Confirm 648 | if [[ ! $2 = a ]] && [[ ! $2 = at ]]; then 649 | printf '\n%s' "Do you want to convert ${#relative_links_list[@]} links to absolute?" \ 650 | 'Type "y" to convert, "t" for dry-run test or anything else to abort > ' 651 | read convert_confirmed 652 | else 653 | [[ $2 = a ]] && convert_confirmed=y 654 | [[ $2 = at ]] && convert_confirmed=t 655 | fi 656 | 657 | ### Convert links and replace them in note files. 658 | if [[ $convert_confirmed = y ]] || [[ $convert_confirmed = t ]] \ 659 | && (( ${#relative_links_list[@]} > 0 )); then 660 | for id in ${!relative_links_list[@]}; do 661 | 662 | ### Get note file name without unique ID and convert link. 663 | note_file="${id#*:}" 664 | source_link="${relative_links_list[$id]}" 665 | target_link="$note_root_dir$source_link" 666 | 667 | ### Replace the link or make verbose dry-run for test. 668 | if [[ $convert_confirmed = y ]]; then 669 | 670 | ### Make sure link have right leading "file:". 671 | ### (file:/DIR - absolute,real size; file://DIR - relative; file:///DIR - absolute,scaled) 672 | if [[ ${!no_leading_file_links_list[@]} =~ "$id" ]]; then 673 | sed -i "s|$source_link|file://$target_link|" "$note_file" \ 674 | && n_converted=$(($n_converted+1)) 675 | else 676 | sed -i "s|file:/*$source_link|file://$target_link|" "$note_file" \ 677 | && n_converted=$(($n_converted+1)) 678 | fi 679 | else 680 | printf '\n%s\n%s\n%s\n' "NOTE $note_file" "REL $source_link" "ABS $target_link" 681 | fi 682 | done 683 | 684 | ### Show summary or an empty line for a test run. 685 | if [[ $convert_confirmed = y ]]; then 686 | printf '\n%s\n\n' "$n_converted links converted." 687 | [[ ${#relative_links_list[@]} != $n_converted ]] \ 688 | && printf '\n%s\n\n' "Conversion of $((${#relative_links_list[@]} - $n_converted)) links failed." 689 | 690 | else echo 691 | fi 692 | else 693 | printf '\n%s\n\n' "No relative links found." 694 | fi 695 | fi 696 | 697 | exit 698 | fi 699 | 700 | ##==================================================================## 701 | ## File scanner loop: scan files and pulls them through all modules ## 702 | ##==================================================================## 703 | 704 | ## File scanner ## 705 | ##--------------## 706 | 707 | if [[ $1 = -s ]] || [[ $1 = -w ]]; then 708 | 709 | ( 710 | ### Scan all files excluding hidden directories/files and "media" directories to process everything 711 | ### that could have changed before previous bash-note run. 712 | find "$note_root_dir" ! -path '*/.*' ! -path "$note_root_dir/$media/*" -type f 713 | 714 | ### Watch all files excluding hidden directories/files and "media" directories to process any file 715 | ### changed while bash-note is running. 716 | if [[ $1 = -w ]]; then 717 | sleep 1 718 | inotifywait -mrq -e modify -e move --format '%w%f' \ 719 | --exclude "(.*/\..|$note_root_dir/$media/)" "$note_root_dir" 720 | fi 721 | 722 | ) | while read -r file; do 723 | 724 | ### Check if the file is there or skip it. 725 | [[ -f "$file" ]] || continue 726 | 727 | ### Parse all the file data checked by modules. 728 | file_path="${file%/*}" 729 | file_name="${file##*/}" 730 | file_ext="${file_name##*.}" 731 | file_mime="$(file -ib "$file")" 732 | 733 | ### Check if there are special symbols in file name or path which will break bash. Notify if true. 734 | if [[ $file = *[#\\\"]* ]] ; then 735 | notify fail "Bash-note: Bad file name" \ 736 | "$file_name contains special symbols and won't be processed by Bash-note." 737 | continue 738 | fi 739 | 740 | ## Convert web pages to markdown notes ## 741 | ##-------------------------------------## 742 | 743 | if [[ $web2note_enabled = 1 ]] && [[ ! $file_path =~ "$web_page_files_dir_app" ]] \ 744 | && [[ $file_ext != "$main_note_extension" ]] && [[ $file_mime =~ text/html ]]; then 745 | 746 | ### Delay for browser to check that file is downloaded. 747 | sleep 1 748 | 749 | ### Delete Firefox reader mode toolbar html code from the web page. 750 | ### Then convert web page to html-less markdown with main note extension. 751 | sed '/