pig-monkey.com - linuxhttps://pig-monkey.com/2024-07-05T08:05:13-07:00Wherein the Author Learns to Compact Borg Archives2024-07-05T00:00:00-07:002024-07-05T08:05:13-07:00Pig Monkeytag:pig-monkey.com,2024-07-05:/2024/07/borg-compact/<p>I noticed that my <a href="https://www.borgbackup.org/">Borg</a> directory on <a href="https://www.rsync.net/products/borg.html">The Cloud</a> was 239 GB. This struck me as problematic, as I could see in my local logs that Borg itself reported the deduplicated size of all archives to be 86 GB.</p> <p>A web search revealed <a href="https://borgbackup.readthedocs.io/en/stable/usage/compact.html"><code>borg compact</code></a>, which apparently I have been …</p><p>I noticed that my <a href="https://www.borgbackup.org/">Borg</a> directory on <a href="https://www.rsync.net/products/borg.html">The Cloud</a> was 239 GB. This struck me as problematic, as I could see in my local logs that Borg itself reported the deduplicated size of all archives to be 86 GB.</p> <p>A web search revealed <a href="https://borgbackup.readthedocs.io/en/stable/usage/compact.html"><code>borg compact</code></a>, which apparently I have been meant to run manually <a href="https://borgbackup.readthedocs.io/en/stable/changes.html#version-1-2-0a2-and-earlier-2019-02-24">since 2019</a>. Oops. After compacting, the directory dropped from 239 GB to 81 GB.</p> <p>My borg wrapper script now looks like this:</p> <div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span> <span class="normal"> 2</span> <span class="normal"> 3</span> <span class="normal"> 4</span> <span class="normal"> 5</span> <span class="normal"> 6</span> <span class="normal"> 7</span> <span class="normal"> 8</span> <span class="normal"> 9</span> <span class="normal">10</span> <span class="normal">11</span> <span class="normal">12</span> <span class="normal">13</span> <span class="normal">14</span> <span class="normal">15</span> <span class="normal">16</span> <span class="normal">17</span> <span class="normal">18</span> <span class="normal">19</span> <span class="normal">20</span> <span class="normal">21</span> <span class="normal">22</span> <span class="normal">23</span> <span class="normal">24</span> <span class="normal">25</span> <span class="normal">26</span> <span class="normal">27</span> <span class="normal">28</span> <span class="normal">29</span> <span class="normal">30</span> <span class="normal">31</span> <span class="normal">32</span> <span class="normal">33</span> <span class="normal">34</span> <span class="normal">35</span> <span class="normal">36</span> <span class="normal">37</span> <span class="normal">38</span> <span class="normal">39</span> <span class="normal">40</span> <span class="normal">41</span> <span class="normal">42</span> <span class="normal">43</span></pre></div></td><td class="code"><div><pre><span></span><code><span class="ch">#!/bin/sh</span> <span class="nb">source</span> ~/.keys/borg.sh <span class="nb">export</span> <span class="nv">BORG_REPO</span><span class="o">=</span><span class="s1">&#39;borg-rsync:borg/nous&#39;</span> <span class="nb">export</span> <span class="nv">BORG_REMOTE_PATH</span><span class="o">=</span><span class="s1">&#39;borg1&#39;</span> <span class="c1"># Create backups</span> <span class="nb">echo</span> <span class="s2">&quot;Creating backups...&quot;</span> borg create --verbose --stats --compression<span class="o">=</span>lz4 <span class="se">\</span> --exclude ~/projects/foo/bar/baz <span class="se">\</span> --exclude ~/projects/xyz/bigfatbinaries <span class="se">\</span> ::<span class="s1">&#39;{hostname}-{user}-{utcnow:%Y-%m-%dT%H:%M:%S}&#39;</span> <span class="se">\</span> ~/documents <span class="se">\</span> ~/projects <span class="se">\</span> ~/mail <span class="se">\</span> <span class="c1"># ...etc</span> <span class="c1"># Prune</span> <span class="nb">echo</span> <span class="s2">&quot;Pruning backups...&quot;</span> borg prune --verbose --list --glob-archives <span class="s1">&#39;{hostname}-{user}-*&#39;</span> <span class="se">\</span> --keep-within<span class="o">=</span>1d <span class="se">\</span> --keep-daily<span class="o">=</span><span class="m">14</span> <span class="se">\</span> --keep-weekly<span class="o">=</span><span class="m">8</span> <span class="se">\</span> --keep-monthly<span class="o">=</span><span class="m">12</span> <span class="se">\</span> <span class="c1"># Compact</span> <span class="nb">echo</span> <span class="s2">&quot;Compacting repository...&quot;</span> backitup <span class="se">\</span> -p <span class="m">604800</span> <span class="se">\</span> -l ~/.borg_compact-repo.lastrun <span class="se">\</span> -b <span class="s2">&quot;borg compact --verbose&quot;</span> <span class="se">\</span> <span class="c1"># Check</span> <span class="nb">echo</span> <span class="s2">&quot;Checking repository...&quot;</span> backitup -a <span class="se">\</span> -p <span class="m">172800</span> <span class="se">\</span> -l ~/.borg_check-repo.lastrun <span class="se">\</span> -b <span class="s2">&quot;borg check --verbose --repository-only --max-duration=1200&quot;</span> <span class="se">\</span> <span class="nb">echo</span> <span class="s2">&quot;Checking archives...&quot;</span> backitup -a <span class="se">\</span> -p <span class="m">259200</span> <span class="se">\</span> -l ~/.borg_check-arch.lastrun <span class="se">\</span> -b <span class="s2">&quot;borg check --verbose --archives-only --last 18&quot;</span> <span class="se">\</span> </code></pre></div></td></tr></table></div> <p>Other than the addition of a weekly <code>compact</code>, my setup is the <a href="/2017/07/borg/">same as it ever was</a>.</p>Working with ACSM Files on Linux2024-07-03T00:00:00-07:002024-07-03T20:14:52-07:00Pig Monkeytag:pig-monkey.com,2024-07-03:/2024/07/libgourou/<p>I acquire books from various <a href="https://www.overdrive.com/">OverDrive</a> instances. OverDrive provides an <a href="https://en.wikipedia.org/wiki/Adobe_Content_Server">ACSM</a> file, which is not a book, but instead an XML ticket meant to be exchanged for the actual book file &ndash; similar to requesting a book in meatspace by turning in a catalog card to a librarian. <a href="https://www.adobe.com/solutions/ebook/digital-editions.html">Adobe Digital Editions …</a></p><p>I acquire books from various <a href="https://www.overdrive.com/">OverDrive</a> instances. OverDrive provides an <a href="https://en.wikipedia.org/wiki/Adobe_Content_Server">ACSM</a> file, which is not a book, but instead an XML ticket meant to be exchanged for the actual book file &ndash; similar to requesting a book in meatspace by turning in a catalog card to a librarian. <a href="https://www.adobe.com/solutions/ebook/digital-editions.html">Adobe Digital Editions</a> is used to perform this exchange. As one would expect from Adobe, this software does not support Linux.</p> <p>Back in 2013 I setup a Windows 7 virtual machine with Adobe Digital Editions v2.0.1.78765, which I used exclusively for turning ACSM files into EPUB files. A few months ago I was finally able to retire that VM thanks to the discovery of <a href="https://forge.soutade.fr/soutade/libgourou/">libgourou</a>, which is both a library and a suite of utilities that can be used to work with ACSM files.</p> <p>To use, I first register an anonymous account with Adobe.</p> <div class="highlight"><pre><span></span><code>$ adept_activate -a </code></pre></div> <p>Next I export the private key that the files will be encrypted to.</p> <div class="highlight"><pre><span></span><code><span class="o">$</span><span class="w"> </span><span class="n">acsmdownloader</span><span class="w"> </span><span class="o">--</span><span class="k">export</span><span class="o">-</span><span class="n">private</span><span class="o">-</span><span class="n">key</span><span class="w"></span> </code></pre></div> <p>This key can then be imported into the <a href="https://github.com/noDRM/DeDRM_tools">DeDRM_tools</a> plugin of <a href="https://calibre-ebook.com/">Calibre</a>.</p> <p>Whenever I receive an ACSM file, I can just pass it to the <code>acsmdownloader</code> utility from libgourou.</p> <div class="highlight"><pre><span></span><code><span class="o">$</span><span class="w"> </span><span class="n">acsmdownloader</span><span class="w"> </span><span class="o">-</span><span class="n">f</span><span class="w"> </span><span class="n">foobar</span><span class="o">.</span><span class="n">acsm</span><span class="w"></span> </code></pre></div> <p>This spits out the EPUB, which may be imported into <a href="/2018/11/ebooks/">my standard Calibre library</a>.</p>The Things I Do for Time2024-02-14T00:00:00-08:002024-02-14T18:04:09-08:00Pig Monkeytag:pig-monkey.com,2024-02-14:/2024/02/things-i-do-for-time/<p>I am a believer in the sacred word as defined in <a href="https://en.wikipedia.org/wiki/ISO_8601">ISO 8601</a>, and the later revelations such as <a href="https://datatracker.ietf.org/doc/html/rfc3339">RFC 3339</a>. Numerical dates should be formatted as YYYY-MM-DD. Hours should be written in 24-hour time. I will die on this hill.</p> <p>Since time immemorial, this has been accomplished on Linux …</p><p>I am a believer in the sacred word as defined in <a href="https://en.wikipedia.org/wiki/ISO_8601">ISO 8601</a>, and the later revelations such as <a href="https://datatracker.ietf.org/doc/html/rfc3339">RFC 3339</a>. Numerical dates should be formatted as YYYY-MM-DD. Hours should be written in 24-hour time. I will die on this hill.</p> <p>Since time immemorial, this has been accomplished on Linux systems by setting <code>LC_TIME</code> to the en_DK locale. More specifically, the git history for glibc shows that en_DK was added (with ISO 8601 date formatting) by Ulrich Drepper on 1997-03-05.</p> <p>A few years ago, this stopped working in Firefox. Instead Firefox started to think that numerical dates were supposed to be formatted as DD/MM/YYYY, which is at least as asinine as the typical American MM-DD-YYYY format. I finally got fed up with this and decided to investigate.</p> <p>The best discussion of the issue is in <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1426907">Thunderbird bug 1426907</a>. Here I learned that the problem is caused by Thunderbird (and by extension Firefox) no longer respecting glibc locales. Mozilla software simply takes the name of the system locale, ignores its definition, and looks up formatting in the <a href="https://cldr.unicode.org/">Unicode CLDR</a>. The CLDR has <a href="https://www.localeplanet.com/icu/en-DK/index.html">redefined en_DK</a> to use DD/MM/YYYY<sup class="footnote-ref" id="fnref:endk"><a rel="footnote" href="#fn:endk" title="see footnote">1</a></sup>.</p> <p>The hack to address the problem was also documented in the Thunderbird bug report. The CLDR includes <a href="https://www.localeplanet.com/icu/en-SE/index.html">a definition for en_SE</a> which uses YYYY-MM-DD<sup class="footnote-ref" id="fnref:ense"><a rel="footnote" href="#fn:ense" title="see footnote">2</a></sup> and 24-hour time. (It also separates the time from the date with a comma, which is weird, but Sweden is weird, so I&rsquo;ll allow it.) There is no en_SE locale in glibc. But it can be created by linking to the en_DK locale. This new locale can then be used for <code>LC_TIME</code>.</p> <div class="highlight"><pre><span></span><code>$ sudo ln -s /usr/share/i18n/locales/en_DK /usr/share/i18n/locales/en_SE $ <span class="nb">echo</span> <span class="s1">&#39;en_SE.UTF-8 UTF-8&#39;</span> <span class="p">|</span> sudo tee -a /etc/locale.gen $ sudo locale-gen $ sed -i <span class="s1">&#39;s/^LC_TIME=.*/LC_TIME=en_SE.UTF-8/&#39;</span> /etc/locale.conf </code></pre></div> <p>Now anything that respects glibc locales will effectively use en_DK, albeit under a different name. Anything that uses CLDR will just see that it is supposed to use a locale named en_SE, which still results in sane formatting. Thus one can use <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/date">HTML date input fields</a> without going crazy.</p> <div id="footnotes"> <h2>Notes</h2> <ol> <li id="fn:endk"><a rev="footnote" href="#fnref:endk" class="footnote-return" title="return to article">&crarr;</a> The Unicode specification defines this pattern as "dd/MM/y", which is rather unintuitive, but worth including here for search engines.</li> <li id="fn:ense"><a rev="footnote" href="#fnref:ense" class="footnote-return" title="return to article">&crarr;</a> The Unicode specification defines this pattern as "y-MM-DD".</li> </ol> </div>Redswitch2021-10-17T00:00:00-07:002021-10-17T10:35:49-07:00Pig Monkeytag:pig-monkey.com,2021-10-17:/2021/10/redswitch/<p><a href="http://jonls.dk/redshift/">Redshift</a> is a program that adjusts the color temperature of the screen based on time and location. It can automatically fetch one&rsquo;s location via <a href="https://gitlab.freedesktop.org/geoclue/geoclue/-/wikis/home">GeoClue</a>. I&rsquo;ve used it for years. It works most of the time. But, more often than I&rsquo;d like, it fails to fetch my …</p><p><a href="http://jonls.dk/redshift/">Redshift</a> is a program that adjusts the color temperature of the screen based on time and location. It can automatically fetch one&rsquo;s location via <a href="https://gitlab.freedesktop.org/geoclue/geoclue/-/wikis/home">GeoClue</a>. I&rsquo;ve used it for years. It works most of the time. But, more often than I&rsquo;d like, it fails to fetch my location from GeoClue. When this happens, I find GeoClue impossible to debug. Redshift <a href="https://github.com/jonls/redshift/issues/393">does not cache location information</a>, so when it fails to fetch my location the result is an eye-meltingly bright screen at night. To address this, I wrote a small shell script to avoid GeoClue entirely.</p> <p><a href="https://github.com/pigmonkey/redswitch">Redswitch</a> fetches the current location via the <a href="https://location.services.mozilla.com/">Mozilla Location Service</a> (using GeoClue&rsquo;s API key, which <a href="https://gitlab.freedesktop.org/geoclue/geoclue/-/issues/136">may go away</a>). The result is stored and compared against the previous location to determine if the device has moved. If a change in location is detected, Redshift is killed and relaunched with the new location (this will result in a noticeable flash, but there seems to be no alternative since <a href="https://github.com/jonls/redshift/pull/96">Redshift cannot reload its settings while running</a>). If Redshift is not running, it is launched. If no change in location is detected and Redshift is already running, nothing happens. Because the location information is stored, this can safely be used to launch Redshift when the machine is offline (or when the Mozilla Location Service API is down or rate-limited).</p> <p>My laptop does not experience frequent, drastic changes in location. I find that having the script automatically execute once upon login is adequate for my needs. If you&rsquo;re jetting around the world, you could periodically execute the script via cron or a systemd timer.</p> <p>This solves all my problems with Redshift. I can go back to forgetting about its existence, which is my goal for software of this sort.</p>Searching Books2020-06-11T00:00:00-07:002020-06-11T21:30:50-07:00Pig Monkeytag:pig-monkey.com,2020-06-11:/2020/06/ripgrep-all/<p><a href="https://github.com/phiresky/ripgrep-all/">ripgrep-all</a> is a small wrapper around <a href="https://github.com/BurntSushi/ripgrep">ripgrep</a> that adds support for additional file formats.</p> <p>I discovered it while looking for a program that would allow me to search <a href="/2018/11/ebooks/">my e-book library</a> without needing to open individual books and search their contents via <a href="https://calibre-ebook.com/">Calibre</a>. ripgrep-all accomplishes this by using <a href="https://pandoc.org/">Pandoc</a> to …</p><p><a href="https://github.com/phiresky/ripgrep-all/">ripgrep-all</a> is a small wrapper around <a href="https://github.com/BurntSushi/ripgrep">ripgrep</a> that adds support for additional file formats.</p> <p>I discovered it while looking for a program that would allow me to search <a href="/2018/11/ebooks/">my e-book library</a> without needing to open individual books and search their contents via <a href="https://calibre-ebook.com/">Calibre</a>. ripgrep-all accomplishes this by using <a href="https://pandoc.org/">Pandoc</a> to convert files to plain text and then running ripgrep on the output. One of the numerous formats supported by Pandoc is <a href="https://en.wikipedia.org/wiki/EPUB">EPUB</a>, which is the format I use to store books.</p> <p>Running Pandoc on every book in my library to extract its text can take some time, but ripgrep-all caches the extracted text so that subsequent runs are similar in speed to simply searching plain text &ndash; which is blazing fast thanks to ripgrep&rsquo;s speed. It takes around two seconds to search 1,706 books.</p> <div class="highlight"><pre><span></span><code>$ time<span class="o">(</span>rga -li <span class="s1">&#39;pandemic&#39;</span> ~/library/books/ <span class="p">|</span> wc -l<span class="o">)</span> <span class="m">33</span> real 0m1.225s user 0m2.458s sys 0m1.759s </code></pre></div>I published my script for creating optical backups.2020-04-23T00:00:00-07:002020-04-23T17:45:01-07:00Pig Monkeytag:pig-monkey.com,2020-04-23:/2020/04/optician/<p><a href="https://github.com/pigmonkey/optician">Optician</a> archives a directory, optionally encrypts it, records the integrity of all the things, and burns it to disc. I created it last year after writing about the steps I took to create <a href="/2019/06/optical-financal-backups/">optical backups of financial archives</a>. Since then I&rsquo;ve used it to create my monthly password database …</p><p><a href="https://github.com/pigmonkey/optician">Optician</a> archives a directory, optionally encrypts it, records the integrity of all the things, and burns it to disc. I created it last year after writing about the steps I took to create <a href="/2019/06/optical-financal-backups/">optical backups of financial archives</a>. Since then I&rsquo;ve used it to create my monthly password database backups, yearly e-book library backups, and this year&rsquo;s annual financial backup.</p>Personal Information Management2019-12-14T00:00:00-08:002019-12-14T18:22:36-08:00Pig Monkeytag:pig-monkey.com,2019-12-14:/2019/12/pim-utils/<p><a href="https://pimutils.org/">pimutils</a> is a collection of software for personal information management. The core piece is <a href="https://vdirsyncer.pimutils.org/">vdirsyncer</a>, which synchronizes calendars and contacts between the local filesystem and CalDav and CardDAV servers. Calendars may then be interacted with via <a href="https://lostpackets.de/khal/">khal</a>, and contacts via <a href="https://github.com/scheibler/khard/">khard</a>. There&rsquo;s not much to say about these three …</p><p><a href="https://pimutils.org/">pimutils</a> is a collection of software for personal information management. The core piece is <a href="https://vdirsyncer.pimutils.org/">vdirsyncer</a>, which synchronizes calendars and contacts between the local filesystem and CalDav and CardDAV servers. Calendars may then be interacted with via <a href="https://lostpackets.de/khal/">khal</a>, and contacts via <a href="https://github.com/scheibler/khard/">khard</a>. There&rsquo;s not much to say about these three programs, other than they all just work. Having offline access to my calendars and contacts is critical, as is the ability to synchronize that data across machines.</p> <p>Khard integrates easily with <a href="https://neomutt.org/">mutt</a> to provide autocomplete when composing emails. I find its interface for creating, editing and reading contacts to be intuitive. It can also output a calendar of birthdays, which can then be imported into khal.</p> <p>Khal&rsquo;s interface for adding new calendar events is much simpler and quicker than all the mousing required by GUI calendar programs.</p> <div class="highlight"><pre><span></span><code>$ khal new <span class="m">2019</span>-11-16 <span class="m">21</span>:30 5h Alessandro Cortini at Public Works :: <span class="m">161</span> Erie St </code></pre></div> <p>There are times when a more complex user interface makes calendaring tasks easier. For this Khal offers the <a href="https://lostpackets.de/khal/usage.html#interactive">interactive option</a>, which provides a <a href="https://en.wikipedia.org/wiki/Text-based_user_interface">TUI</a> for creating, editing and reading events.</p> <p>Khal can also <a href="https://lostpackets.de/khal/usage.html#import">import</a> <a href="https://en.wikipedia.org/wiki/ICalendar">iCalendar</a> files, which is a simple way of getting existing events into my world.</p> <div class="highlight"><pre><span></span><code><span class="err">$</span> <span class="n">khal</span> <span class="kn">import</span> <span class="nn">invite.ics</span> </code></pre></div> <p>Vdirsyncer has <a href="https://github.com/pimutils/vdirsyncer/issues/790">maintenance problems</a> that may call its future into question, but the whole point of modular tools that operate on open data formats is that they are replaceable.</p> <p>I have a simple and often used script which calls <code>khal calendar</code> and <code>task list</code> (the latter command being <a href="https://taskwarrior.org/">taskwarrior</a>), answering the question: what am I supposed to be doing right now?</p>Terminal Calculations2019-12-12T00:00:00-08:002019-12-12T18:52:46-08:00Pig Monkeytag:pig-monkey.com,2019-12-12:/2019/12/qalculate/<p><a href="https://qalculate.github.io/">Qalculate!</a> is a well known GTK-based GUI calculator. For years I ignored it because I failed to realize that it included a terminal interface, <code>qalc</code>. Since learning about <code>qalc</code> last year it has become my go-to calculator. It supports <a href="https://qalculate.github.io/features.html">all the same features</a> as the GUI, including <a href="https://en.wikipedia.org/wiki/Reverse_Polish_notation">RPN</a> and unit …</p><p><a href="https://qalculate.github.io/">Qalculate!</a> is a well known GTK-based GUI calculator. For years I ignored it because I failed to realize that it included a terminal interface, <code>qalc</code>. Since learning about <code>qalc</code> last year it has become my go-to calculator. It supports <a href="https://qalculate.github.io/features.html">all the same features</a> as the GUI, including <a href="https://en.wikipedia.org/wiki/Reverse_Polish_notation">RPN</a> and unit conversions. I <a href="/2019/07/gnu-units/">primarily use GNU Units for unit wrangling</a>, but being able to perform unit conversions within my calculator is sometimes useful.</p> <div class="highlight"><pre><span></span><code>$ qalc &gt; 1EUR to USD It has been <span class="m">20</span> day<span class="o">(</span>s<span class="o">)</span> since the exchange rates last were updated Do you wish to update the exchange rates now? y <span class="m">1</span> * <span class="nv">euro</span> <span class="o">=</span> approx. <span class="nv">$1</span>.1137000 &gt; 32oC to oF <span class="m">32</span> * <span class="nv">celsius</span> <span class="o">=</span> <span class="m">89</span>.6 oF </code></pre></div> <p><a href="https://qalculate.github.io/manual/qalculate-mode.html#qalculate-rpn">The RPN mode</a> is not quite as intuitive as a purpose built RPN calculator like <a href="https://github.com/pelzlpj/orpie">Orpie</a>, but it is adequate for my uses. My most frequent use of RPN mode is totaling a long list of numbers without bothering with all those tedious <code>+</code> symbols.</p> <div class="highlight"><pre><span></span><code>&gt; rpn on &gt; stack The RPN stack is empty &gt; 85 85 = 85 &gt; 42 42 = 42 &gt; 198 198 = 198 &gt; 5 5 = 5 &gt; 659 659 = 659 &gt; stack 1: 659 2: 5 3: 198 4: 42 5: 85 &gt; total total([659, 5, 198, 42, 85]) = 989 &gt; stack 1: 989 </code></pre></div> <p>Also provided are some basic <a href="https://qalculate.github.io/manual/qalculate-definitions-functions.html#qalculate-definitions-functions-1-Statistics">statistics functions</a> that can help save time.</p> <div class="highlight"><pre><span></span><code>&gt; mean(2,12,5,3,1) mean([2, 12, 5, 3, 1]) = 4.6 </code></pre></div> <p>And of course there are <a href="https://qalculate.github.io/manual/qalculate-definitions-variables.html">the varaibles and constants you would expect</a></p> <div class="highlight"><pre><span></span><code>&gt; 12+3*8)/2 (12 + (3 * 8)) / 2 = 18 &gt; ans*pi ans * pi = 56.548668 </code></pre></div> <p>I reach for <code>qalc</code> more frequently than alternative calculators like <a href="https://www.gnu.org/software/bc/">bc</a>, <a href="https://github.com/sharkdp/insect">insect</a>, or the Python shell.</p>Mutt is my mail user agent of choice.2019-07-31T00:00:00-07:002019-07-31T21:12:06-07:00Pig Monkeytag:pig-monkey.com,2019-07-31:/2019/07/mutt-ics/<p>More specifically, <a href="https://neomutt.org/">Neomutt</a>. <a href="https://github.com/dmedvinsky/mutt-ics">Mutt ICS</a> is a python script which takes an <a href="https://en.wikipedia.org/wiki/ICalendar">iCalendar file</a> and outputs the contents in a human friendly format. I use it in <a href="https://github.com/pigmonkey/dotfiles/blob/master/config/mutt/mailcap">my mailcap</a> so that I can see details of calendar attachments when reading email. It&rsquo;s a simple script that improves my email …</p><p>More specifically, <a href="https://neomutt.org/">Neomutt</a>. <a href="https://github.com/dmedvinsky/mutt-ics">Mutt ICS</a> is a python script which takes an <a href="https://en.wikipedia.org/wiki/ICalendar">iCalendar file</a> and outputs the contents in a human friendly format. I use it in <a href="https://github.com/pigmonkey/dotfiles/blob/master/config/mutt/mailcap">my mailcap</a> so that I can see details of calendar attachments when reading email. It&rsquo;s a simple script that improves my email workflow.</p>Without an OCR layer, PDF files are of limited use.2019-07-30T00:00:00-07:002019-07-30T19:47:30-07:00Pig Monkeytag:pig-monkey.com,2019-07-30:/2019/07/ocrmypdf/<p><a href="https://ocrmypdf.readthedocs.io/">OCRmyPDF</a> is a tool that applies <a href="https://en.wikipedia.org/wiki/Optical_character_recognition">optical character recognition</a> to PDFs. It uses <a href="https://github.com/tesseract-ocr/tesseract">Tesseract</a> to perform the OCR, and <a href="https://github.com/Flameeyes/unpaper">unpaper</a> to clean, deskew and optimize the input files. It outputs <a href="https://en.wikipedia.org/?title=PDF/A">PDF/A</a> files, optimized for long-term storage. This isn&rsquo;t a tool I use frequently, but it is one I …</p><p><a href="https://ocrmypdf.readthedocs.io/">OCRmyPDF</a> is a tool that applies <a href="https://en.wikipedia.org/wiki/Optical_character_recognition">optical character recognition</a> to PDFs. It uses <a href="https://github.com/tesseract-ocr/tesseract">Tesseract</a> to perform the OCR, and <a href="https://github.com/Flameeyes/unpaper">unpaper</a> to clean, deskew and optimize the input files. It outputs <a href="https://en.wikipedia.org/?title=PDF/A">PDF/A</a> files, optimized for long-term storage. This isn&rsquo;t a tool I use frequently, but it is one I greatly appreciate having when I need it. If you ever find yourself scanning or photographing documents, you want OCRmyPDF.</p>Date Manipulation2019-07-29T00:00:00-07:002019-07-29T19:02:29-07:00Pig Monkeytag:pig-monkey.com,2019-07-29:/2019/07/dateutils/<p><a href="http://www.fresse.org/dateutils/">Dateutils</a> is a collection of tools for the quick manipulation of dates. The tool I use most frequently is <code>datediff</code>. This program answers questions like: &ldquo;How many days has it been since a date?&rdquo; or &ldquo;How many days are left in summer?&rdquo;</p> <div class="highlight"><pre><span></span><code>$ datediff <span class="m">2019</span>-03-21 now <span class="m">131</span> $ datediff now <span class="m">2019 …</span></code></pre></div><p><a href="http://www.fresse.org/dateutils/">Dateutils</a> is a collection of tools for the quick manipulation of dates. The tool I use most frequently is <code>datediff</code>. This program answers questions like: &ldquo;How many days has it been since a date?&rdquo; or &ldquo;How many days are left in summer?&rdquo;</p> <div class="highlight"><pre><span></span><code>$ datediff <span class="m">2019</span>-03-21 now <span class="m">131</span> $ datediff now <span class="m">2019</span>-09-23 <span class="m">55</span> </code></pre></div> <p>My second most frequently used program is <code>dateadd</code>, which is used to add a duration to a date. It can answer questions like: &ldquo;What will the date be in 3 weeks?&rdquo;</p> <div class="highlight"><pre><span></span><code>$ dateadd now +3w <span class="m">2019</span>-08-20T02:02:23 </code></pre></div> <p>The tools are much more powerful than these examples, but hardly a week goes by when I don&rsquo;t use <code>datediff</code> or <code>dateadd</code> for simple tasks like this.</p>Unit Wrangling2019-07-24T00:00:00-07:002019-07-24T21:39:41-07:00Pig Monkeytag:pig-monkey.com,2019-07-24:/2019/07/gnu-units/<p>I use <a href="https://www.gnu.org/software/units/">GNU Units</a> to convert measurements.</p> <p>The program knows about many obscure and antiquated units, but I mostly use it for boring things like converting currencies and between metric and imperial units. It can be used directly from the command line, or via a prompted interactive mode.</p> <div class="highlight"><pre><span></span><code>$ units 57EUR …</code></pre></div><p>I use <a href="https://www.gnu.org/software/units/">GNU Units</a> to convert measurements.</p> <p>The program knows about many obscure and antiquated units, but I mostly use it for boring things like converting currencies and between metric and imperial units. It can be used directly from the command line, or via a prompted interactive mode.</p> <div class="highlight"><pre><span></span><code>$ units 57EUR USD * <span class="m">63</span>.526262 / <span class="m">0</span>.015741521 $ units Currency exchange rates from FloatRates <span class="o">(</span>USD base<span class="o">)</span> on <span class="m">2019</span>-07-24 <span class="m">3460</span> units, <span class="m">109</span> prefixes, <span class="m">109</span> nonlinear units You have: <span class="m">16</span> floz You want: ml * <span class="m">473</span>.17647 / <span class="m">0</span>.0021133764 You have: tempC<span class="o">(</span><span class="m">30</span><span class="o">)</span> You want: tempF <span class="m">86</span> </code></pre></div> <p>GNU Units is picky about its unit definitions, and they are case sensitive. For example, it knows what <code>USD</code> is, but <code>usd</code> is undefined. It supports tab completion of units in interactive mode, which can be helpful. It knows the difference between a US fluid ounce and a British fluid ounce.</p> <div class="highlight"><pre><span></span><code>$ units <span class="s2">&quot;1 usfloz&quot;</span> ml * <span class="m">29</span>.57353 / <span class="m">0</span>.033814023 $ units <span class="s2">&quot;1 brfloz&quot;</span> ml * <span class="m">28</span>.413063 / <span class="m">0</span>.03519508 </code></pre></div> <p>The unit definitions are stored at <code>/usr/share/units/definitions.units</code>. Occasionally I&rsquo;ll need to peruse through this file to find the correct formatting for the unit I&rsquo;m interested in. Sometimes when doing this I&rsquo;ll run into one of the more obscure definitions, such as <code>beespace</code>. Apparently this unit is used in beekeeping when designing hive boxes. It is described in the definition file thusly: &ldquo;Bees will fill any space that is smaller than the bee space and leave open spaces that are larger. The size of the space varies with species.&rdquo;</p> <div class="highlight"><pre><span></span><code>$ units 12inches beespace * <span class="m">48</span> / <span class="m">0</span>.020833333 </code></pre></div> <p>Every so often you need to know how many Earth days are in one Martian year. With GNU Units that information is a few keystrokes away.</p> <div class="highlight"><pre><span></span><code>$ units 1marsyear days * <span class="m">686</span>.97959 / <span class="m">0</span>.0014556473 </code></pre></div> <p>Currency definitions are stored in <code>/var/lib/units/currency.units</code>. They are updated using the <code>units_cur</code> program. In the past I would update currencies whenever I needed them, but recently I <a href="https://github.com/pigmonkey/spark/commit/24ef24c34aad89a0f9beb907de51b4aad16adff6">setup a systemd timer</a> to update these definitions roughly once per day (depending on network connectivity). This provides me with conversion rates that are current enough for my own use, which I can take advantage of even when offline, and does not require me to let a third party know which currencies or quantities I am interested in.</p> <p>Astute readers will have noted that I am big on this offline computing thing.</p>Undertime2019-07-23T00:00:00-07:002019-07-23T19:10:06-07:00Pig Monkeytag:pig-monkey.com,2019-07-23:/2019/07/undertime/<p><a href="https://gitlab.com/anarcat/undertime">Undertime</a> is a simple program that assists in coordinating events across time zones. It prints a table of your system&rsquo;s local time zone, along with other any other specified zones. The output is colorized based on the start and end hour of the working day. If you want to …</p><p><a href="https://gitlab.com/anarcat/undertime">Undertime</a> is a simple program that assists in coordinating events across time zones. It prints a table of your system&rsquo;s local time zone, along with other any other specified zones. The output is colorized based on the start and end hour of the working day. If you want to talk to someone in Paris tomorrow, and you want the conversation to happen at an hour that is reasonable for both parties, Undertime can help.</p> <p><img alt="Undertime Paris Meeting Example" src="/media/images/undertime.png"></p> <p>I often find myself converting between local time and UTC. Usually this happens when working with system logs. If I have a specific date and time I want to translate, I&rsquo;ll use <code>date</code>.</p> <div class="highlight"><pre><span></span><code># Convert a time from PDT to UTC: $ env TZ=&quot;UTC&quot; date -d &quot;2016-03-25T11:33 PDT&quot; # Convert a time from UTC to local: $ date -d &#39;2016-03-24T12:00 UTC&#39; </code></pre></div> <p>If I&rsquo;m not looking to convert an exact time, but just want to answer a more generalized question like &ldquo;Approximately when was 14:00 UTC?&rdquo; without doing the mental math, I find that Undertime is the quickest solution.</p> <div class="highlight"><pre><span></span><code>$ undertime UTC ╔═══════╦═══════╗ ║ PDT ║ UTC ║ ╠═══════╬═══════╣ ║ <span class="m">00</span>:00 ║ <span class="m">07</span>:00 ║ ║ <span class="m">01</span>:00 ║ <span class="m">08</span>:00 ║ ║ <span class="m">02</span>:00 ║ <span class="m">09</span>:00 ║ ║ <span class="m">03</span>:00 ║ <span class="m">10</span>:00 ║ ║ <span class="m">04</span>:00 ║ <span class="m">11</span>:00 ║ ║ <span class="m">05</span>:00 ║ <span class="m">12</span>:00 ║ ║ <span class="m">06</span>:00 ║ <span class="m">13</span>:00 ║ ║ <span class="m">07</span>:00 ║ <span class="m">14</span>:00 ║ ║ <span class="m">08</span>:00 ║ <span class="m">15</span>:00 ║ ║ <span class="m">09</span>:00 ║ <span class="m">16</span>:00 ║ ║ <span class="m">10</span>:00 ║ <span class="m">17</span>:00 ║ ║ <span class="m">11</span>:00 ║ <span class="m">18</span>:00 ║ ║ <span class="m">12</span>:00 ║ <span class="m">19</span>:00 ║ ║ <span class="m">13</span>:00 ║ <span class="m">20</span>:00 ║ ║ <span class="m">14</span>:00 ║ <span class="m">21</span>:00 ║ ║ <span class="m">15</span>:00 ║ <span class="m">22</span>:00 ║ ║ <span class="m">16</span>:00 ║ <span class="m">23</span>:00 ║ ║ <span class="m">17</span>:00 ║ <span class="m">00</span>:00 ║ ║ <span class="m">18</span>:00 ║ <span class="m">01</span>:00 ║ ║ <span class="m">19</span>:00 ║ <span class="m">02</span>:00 ║ ║ <span class="m">19</span>:04 ║ <span class="m">02</span>:04 ║ ║ <span class="m">20</span>:00 ║ <span class="m">03</span>:00 ║ ║ <span class="m">21</span>:00 ║ <span class="m">04</span>:00 ║ ║ <span class="m">22</span>:00 ║ <span class="m">05</span>:00 ║ ║ <span class="m">23</span>:00 ║ <span class="m">06</span>:00 ║ ╚═══════╩═══════╝ Table generated <span class="k">for</span> time: <span class="m">2019</span>-07-23 <span class="m">19</span>:04:00-07:00 </code></pre></div>Music Organization with Beets2019-07-22T00:00:00-07:002019-07-22T17:49:10-07:00Pig Monkeytag:pig-monkey.com,2019-07-22:/2019/07/beets/<p>I organize my music with <a href="http://beets.io/">Beets</a>.</p> <p>Beets <a href="https://beets.readthedocs.io/en/stable/reference/cli.html#import">imports</a> music into my library, warns me if I&rsquo;m <a href="https://beets.readthedocs.io/en/stable/plugins/missing.html">missing</a> tracks, identifies tracks based on their <a href="https://beets.readthedocs.io/en/stable/plugins/chroma.html">accoustic fingerprint</a>, <a href="https://beets.readthedocs.io/en/stable/plugins/scrub.html">scrubs</a> extraneous metadata, fetches and stores <a href="https://beets.readthedocs.io/en/stable/plugins/fetchart.html">album art</a>, cleans <a href="https://beets.readthedocs.io/en/stable/plugins/lastgenre.html">genres</a>, fetches <a href="https://beets.readthedocs.io/en/stable/plugins/lyrics.html">lyrics</a>, and &ndash; most importantly &ndash; <a href="https://beets.readthedocs.io/en/stable/plugins/mbsync.html">fetches metadata</a> from <a href="https://musicbrainz.org/">MusicBrainz</a>. After some basic <a href="https://github.com/pigmonkey/dotfiles/blob/master/config/beets/config.yaml">configuration</a>, all …</p><p>I organize my music with <a href="http://beets.io/">Beets</a>.</p> <p>Beets <a href="https://beets.readthedocs.io/en/stable/reference/cli.html#import">imports</a> music into my library, warns me if I&rsquo;m <a href="https://beets.readthedocs.io/en/stable/plugins/missing.html">missing</a> tracks, identifies tracks based on their <a href="https://beets.readthedocs.io/en/stable/plugins/chroma.html">accoustic fingerprint</a>, <a href="https://beets.readthedocs.io/en/stable/plugins/scrub.html">scrubs</a> extraneous metadata, fetches and stores <a href="https://beets.readthedocs.io/en/stable/plugins/fetchart.html">album art</a>, cleans <a href="https://beets.readthedocs.io/en/stable/plugins/lastgenre.html">genres</a>, fetches <a href="https://beets.readthedocs.io/en/stable/plugins/lyrics.html">lyrics</a>, and &ndash; most importantly &ndash; <a href="https://beets.readthedocs.io/en/stable/plugins/mbsync.html">fetches metadata</a> from <a href="https://musicbrainz.org/">MusicBrainz</a>. After some basic <a href="https://github.com/pigmonkey/dotfiles/blob/master/config/beets/config.yaml">configuration</a>, all of this happens automatically when I import new files into my library.</p> <p>After the files have been imported, beets makes it easy to query my library based on any of the clean, consistent, high quality, crowd-sourced metadata.</p> <div class="highlight"><pre><span></span><code>$ beet stats genre:ambient Tracks: <span class="m">649</span> Total time: <span class="m">2</span>.7 days Approximate total size: <span class="m">22</span>.4 GiB Artists: <span class="m">76</span> Albums: <span class="m">53</span> Album artists: <span class="m">34</span> $ beet ls -a <span class="s1">&#39;added:2019-07-01..&#39;</span> Deathcount <span class="k">in</span> Silicon Valley - Acheron Dlareme - Compass The Higher Intelligence Agency <span class="p">&amp;</span> Biosphere - Polar Sequences JK/47 - Tokyo Empires Matt Morton - Apollo <span class="m">11</span> Soundtrack $ beet ls -ap albumartist:joplin /home/pigmonkey/library/audio/music/Janis Joplin/Full Tilt Boogie /home/pigmonkey/library/audio/music/Janis Joplin/I Got Dem Ol<span class="err">&#39;</span> Kozmic Blues Again Mama! </code></pre></div> <p>As regular readers will have surmised, the files themselves are stored in <a href="https://git-annex.branchable.com/">git-annex</a>.</p>Terminal Countdown2019-07-20T00:00:00-07:002019-07-20T17:20:30-07:00Pig Monkeytag:pig-monkey.com,2019-07-20:/2019/07/termdown/<p><a href="https://github.com/trehn/termdown">Termdown</a> is a program that provides a countdown timer and stopwatch in the terminal. It uses <a href="http://www.figlet.org/">FIGlet</a> for its display. Its most attractive feature, I think, is the ability to <a href="https://github.com/trehn/termdown/issues/44">support arbitrary script execution</a>.</p> <p>I use it most often as a countdown timer. One of my frequent applications is as …</p><p><a href="https://github.com/trehn/termdown">Termdown</a> is a program that provides a countdown timer and stopwatch in the terminal. It uses <a href="http://www.figlet.org/">FIGlet</a> for its display. Its most attractive feature, I think, is the ability to <a href="https://github.com/trehn/termdown/issues/44">support arbitrary script execution</a>.</p> <p>I use it most often as a countdown timer. One of my frequent applications is as a meditation timer. For this I want a 11 minute timer, with an alert at 10.5 minutes, 60 seconds, and 1 second. This gives me a 10 minute session with 30 seconds preparation and 30 seconds to return. Termdown makes this easy.</p> <div class="highlight"><pre><span></span><code>$ termdown --exec-cmd <span class="s2">&quot;case {0} in 630|30) mpv ~/library/audio/sounds/bell.mp3;; 1) mpv ~/library/audio/sounds/ring.mp3;; esac&quot;</span> 11m </code></pre></div>An Offline Lexicon2019-07-19T00:00:00-07:002019-07-19T20:17:00-07:00Pig Monkeytag:pig-monkey.com,2019-07-19:/2019/07/dictd/<p><a href="https://sourceforge.net/projects/dict/">dictd</a> is a dictionary database server and client. It can be used to lookup word definitions over a network. I don&rsquo;t use it for that. I use the program to provide an offline dictionary. Depending on a network connection, web browser and third-party websites just to define a word …</p><p><a href="https://sourceforge.net/projects/dict/">dictd</a> is a dictionary database server and client. It can be used to lookup word definitions over a network. I don&rsquo;t use it for that. I use the program to provide an offline dictionary. Depending on a network connection, web browser and third-party websites just to define a word strikes me as dumb.</p> <p>To make this go, dictionary files must be installed. I use the <a href="http://gcide.gnu.org.ua/">GNU Collaborative International Dictionary of English</a> (GCIDE), <a href="https://wordnet.princeton.edu/">WordNet</a>, and the <a href="https://www.gutenberg.org/ebooks/3202">Moby Thesaurus</a>. The GCIDE is derived from <a href="http://jsomers.net/blog/dictionary">Noah Webster&rsquo;s famous American dictionary</a>. WordNet is a more modern (one might say &ldquo;dry&rdquo;) resource. The Moby Thesaurus is a public domain thesaurus originally built by <a href="https://en.wikipedia.org/wiki/Grady_Ward">Grady Ward</a>. Between these three sources I can have a pretty good grasp on the English language. No network connectivity required.</p> <p>I use a shell alias to always pipe the definitions through <code>less</code>.</p> <div class="highlight"><pre><span></span><code>def () { dict $1 | less } </code></pre></div>