<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Automation on Grizzlebit</title>
  <subtitle>Ray Grasso's Blog</subtitle>
  <updated>2026-05-05T13:59:36.939268+08:00</updated>
  <id>https://www.grizzlebit.com/tags/automation/feed.xml</id>
  <link rel="alternate" type="text/html" href="https://www.grizzlebit.com/tags/automation/"/>
  <link rel="self" type="application/atom+xml" href="https://www.grizzlebit.com/tags/automation/feed.xml"/>
  <rights>Copyright © 2026, Ray Grasso</rights>
  <author>
    <name>Ray Grasso</name>
  </author>
  <icon>https://www.grizzlebit.com/images/icon.png</icon>
  <logo>https://www.grizzlebit.com/images/icon.png</logo>
  
  <entry>
    <id>https://www.grizzlebit.com/posts/2025/04-23-a-raycast-extension-to-search-my-blog/</id>
    <published>2025-04-23T17:13:26+08:00</published>
    <updated>2025-04-23T19:31:51+08:00</updated>
    <author><name>Ray Grasso</name></author>
    
    <title>A Raycast Extension to Search My Blog</title>
    <content type="html" xml:base="https://www.grizzlebit.com/posts/" xml:lang="en"><![CDATA[<div>
  <p>I&rsquo;ve been looking for a way to search through the local copy of my blog using <a href="https://www.raycast.com">Raycast</a>.</p>
<p>I ended up writing a custom extension to do it. ChatGPT helped grease the way—especially in rendering the results.</p>
<p>It uses a brute force grep over the files&rsquo; contents which works fine given the size of the repository.</p>
<p>The two main actions on the extension are opening the post in my editor and copying a Markdown link to the post<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.</p>
<p>Here is a look at the Raycast command:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-tsx" data-lang="tsx"><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="kd">function</span> <span class="nx">Command() {</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="p">[</span><span class="nx">query</span><span class="p">,</span> <span class="nx">setQuery</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="p">&lt;</span><span class="nt">string</span><span class="p">&gt;(</span><span class="s2">&#34;&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="p">[</span><span class="nx">results</span><span class="p">,</span> <span class="nx">setResults</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="p">&lt;</span><span class="nt">SearchResult</span><span class="err">[]</span><span class="p">&gt;([]);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">useEffect</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">query</span><span class="p">.</span><span class="nx">trim</span><span class="p">()</span> <span class="o">===</span> <span class="s2">&#34;&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">setResults</span><span class="p">([]);</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="kr">const</span> <span class="nx">posts</span> <span class="o">=</span> <span class="nx">getAllPosts</span><span class="p">(</span><span class="nx">BASE_PATH</span><span class="p">,</span> <span class="nx">BLOG_SUBDIRS</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="kr">const</span> <span class="nx">matches</span> <span class="o">=</span> <span class="nx">searchPosts</span><span class="p">(</span><span class="nx">posts</span><span class="p">,</span> <span class="nx">query</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="nx">setResults</span><span class="p">(</span><span class="nx">matches</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s2">&#34;Error reading blog posts:&#34;</span><span class="p">,</span> <span class="nx">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span> <span class="p">[</span><span class="nx">query</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">List</span> <span class="na">onSearchTextChange</span><span class="o">=</span><span class="p">{</span><span class="nx">setQuery</span><span class="p">}</span> <span class="na">throttle</span> <span class="na">isShowingDetail</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="nx">results</span><span class="p">.</span><span class="nx">map</span><span class="p">(({</span> <span class="nx">file</span><span class="p">,</span> <span class="nx">snippet</span> <span class="p">})</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">filename</span> <span class="o">=</span> <span class="nx">path</span><span class="p">.</span><span class="nx">basename</span><span class="p">(</span><span class="nx">file</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">relativePath</span> <span class="o">=</span> <span class="nx">path</span><span class="p">.</span><span class="nx">relative</span><span class="p">(</span><span class="nx">BASE_PATH</span><span class="p">,</span> <span class="nx">file</span><span class="p">).</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/\\/g</span><span class="p">,</span> <span class="s2">&#34;/&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// Convert to URL relative from site root based upon Hugo URL config
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="kr">const</span> <span class="nx">relativeUrl</span> <span class="o">=</span> <span class="sb">`/</span><span class="si">${</span><span class="nx">relativePath</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/\.md$/</span><span class="p">,</span> <span class="s2">&#34;&#34;</span><span class="p">).</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/\/\d\d\d\d-/</span><span class="p">,</span> <span class="s2">&#34;/&#34;</span><span class="p">)</span><span class="si">}</span><span class="sb">`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// Grab the title from the front matter
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="kr">const</span> <span class="nx">fileContent</span> <span class="o">=</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">readFileSync</span><span class="p">(</span><span class="nx">file</span><span class="p">,</span> <span class="s2">&#34;utf8&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">titleMatch</span> <span class="o">=</span> <span class="nx">fileContent</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="sr">/^title:\s*(.*)$/m</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">title</span> <span class="o">=</span> <span class="nx">titleMatch</span> <span class="o">?</span> <span class="nx">titleMatch</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/^[&#39;&#34;]|[&#39;&#34;]$/g</span><span class="p">,</span> <span class="s2">&#34;&#34;</span><span class="p">)</span> <span class="o">:</span> <span class="nx">filename</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">markdownLink</span> <span class="o">=</span> <span class="sb">`[</span><span class="si">${</span><span class="nx">title</span><span class="si">}</span><span class="sb">](</span><span class="si">${</span><span class="nx">relativeUrl</span><span class="si">}</span><span class="sb">)`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">List.Item</span>
</span></span><span class="line"><span class="cl">            <span class="na">key</span><span class="o">=</span><span class="p">{</span><span class="nx">file</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="na">title</span><span class="o">=</span><span class="p">{</span><span class="nx">snippet</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/\*\*/g</span><span class="p">,</span> <span class="s2">&#34;&#34;</span><span class="p">)}</span>
</span></span><span class="line"><span class="cl">            <span class="na">detail</span><span class="o">=</span><span class="p">{&lt;</span><span class="nt">List.Item.Detail</span> <span class="na">markdown</span><span class="o">=</span><span class="p">{</span><span class="sb">`**</span><span class="si">${</span><span class="nx">relativeUrl</span><span class="si">}</span><span class="sb">**</span><span class="err">\</span><span class="sb">n</span><span class="err">\</span><span class="sb">n---</span><span class="err">\</span><span class="sb">n</span><span class="err">\</span><span class="sb">n</span><span class="si">${</span><span class="nx">snippet</span><span class="si">}</span><span class="sb">`</span><span class="p">}</span> <span class="p">/&gt;}</span>
</span></span><span class="line"><span class="cl">            <span class="na">actions</span><span class="o">=</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">              <span class="p">&lt;</span><span class="nt">ActionPanel</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="p">&lt;</span><span class="nt">Action.Open</span> <span class="na">title</span><span class="o">=</span><span class="s">&#34;Open in VS Code&#34;</span> <span class="na">target</span><span class="o">=</span><span class="p">{</span><span class="nx">file</span><span class="p">}</span> <span class="na">application</span><span class="o">=</span><span class="s">&#34;/Applications/Visual Studio Code.app&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="p">&lt;</span><span class="nt">Action.CopyToClipboard</span> <span class="na">title</span><span class="o">=</span><span class="s">&#34;Copy Markdown Link&#34;</span> <span class="na">content</span><span class="o">=</span><span class="p">{</span><span class="nx">markdownLink</span><span class="p">}</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">              <span class="p">&lt;/</span><span class="nt">ActionPanel</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">          <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="p">})}</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">List</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>And here is <a href="/files/find-blog-post.tsx">the full file</a>.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Which is handy when cross linking while writing other posts.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>

  <p>
    
    <a href="https://www.grizzlebit.com/posts/2025/04-23-a-raycast-extension-to-search-my-blog/">🔗</a>
     ∙ Tagged in <a href="https://www.grizzlebit.com/tags/automation/">Automation</a>, <a href="https://www.grizzlebit.com/tags/programming/">Programming</a>, <a href="https://www.grizzlebit.com/tags/tools/">Tools</a>.</p>
</div>
]]></content>
    <link rel="alternate" href="https://www.grizzlebit.com/posts/2025/04-23-a-raycast-extension-to-search-my-blog/"></link>
    <link rel="related" href="https://www.grizzlebit.com/posts/2025/04-23-a-raycast-extension-to-search-my-blog/"></link>
    
  </entry>
  
  <entry>
    <id>https://www.grizzlebit.com/posts/2020/06-10-hyper-keys-and-mouse-buttons-with-karabiner/</id>
    <published>2020-06-10T21:34:00+08:00</published>
    <updated>2025-04-23T19:31:51+08:00</updated>
    <author><name>Ray Grasso</name></author>
    
    <title>Hyper Keys and Mouse Buttons With Karabiner</title>
    <content type="html" xml:base="https://www.grizzlebit.com/posts/" xml:lang="en"><![CDATA[<div>
  <p>I&rsquo;ve got a hankering for keyboard shortcuts.</p>
<p>I&rsquo;m all about pressing a key without having to worry about which application I&rsquo;m in and my computer doing something useful.</p>
<p>This noble pursuit has taught me one thing: there&rsquo;s never enough keys™.</p>
<p>Good old Vim has demonstrated the value of a trusty <a href="https://learnvimscriptthehardway.stevelosh.com/chapters/06.html">leader key</a> in the war to get more keys. So, I undertook a holy mission to find the mythical macOS <a href="https://en.wikipedia.org/wiki/Space-cadet_keyboard">hyper key</a>, and along the way found the deep well of keyboard customisation that is <a href="https://karabiner-elements.pqrs.org/">Karabiner-Elements</a>.</p>
<h2 id="hyper-key">Hyper key</h2>
<p>I&rsquo;ve set up Karabiner-Elements so that if I combine the backslash key with other keys, it acts as the hyper key <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.</p>
<p>I use this hyper key as a prefix to bind global shortcuts without having to crush my fingers, and soul, into a ball.</p>
<p>Here&rsquo;s a selection of the shortcuts I keep behind this hyper key prefix:</p>
<ul>
<li><code>\+t</code> brings my <a href="https://www.toggl.com/toggl-desktop/">time tracking app</a> into focus.</li>
<li><code>\+s</code> locks my screen.</li>
<li>A bunch of shortcuts move windows around via <a href="https://manytricks.com/moom/">Moom</a>.</li>
<li>A couple of shortcuts switch my audio output between my headphones and speakers via an <a href="https://github.com/mikegrb/Alfred-Audio-Device">Alfred workflow</a>.</li>
</ul>
<h2 id="mouse-buttons">Mouse buttons</h2>
<p>macOS doesn&rsquo;t natively recognise the extra buttons on <a href="https://www.amazon.com.au/gp/product/B01LXC1QL0">my new mouse</a> which sucks because: there&rsquo;s never enough keys™.</p>
<p>So, I was chuffed to find that Karabiner recognises these extra mouse buttons and can bind them to key sequences.</p>
<p>Here&rsquo;s a look at my bindings:</p>
<figure><img src="/images/karabiner-elements-preferences.png"
    alt="My Karabiner-Elements preference">
</figure>

<p>I miss the sideways scrolling of the Magic Mouse, but I&rsquo;ve set up Karabiner so that if I hold down my scroll wheel button, I can scroll left and right. It works reasonably well and means I don&rsquo;t need to reach for <code>shift</code> while spinning the scroll wheel to side-scroll.</p>
<p>I map button 4 of my mouse to play and pause my music. The media keys on <a href="https://kinesis-ergo.com/shop/advantage2/">my keyboard</a> are a chord away, but usually, it&rsquo;s easier to press a single button instead.</p>
<p>I map button 5 to a shortcut<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> assigned to the <a href="https://chrome.google.com/webstore/detail/meet-mute/dkgoclojlihiolngeagmhkjiglmoeeic">Meet Mute</a> Chrome extension. This shortcut toggles mute on my current running Google Meet meeting, which is killer.</p>
<h2 id="the-config">The config</h2>
<p>So there you go. Maybe you&rsquo;ll find something useful in my <a href="/files/karabiner.json">Karabiner-Elements config file</a> that you can steal.</p>
<p>And may you never run out of keys.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>The hyper key on the Mac is a combination of <code>Ctrl+Command+Option+Shift</code> which is the equivalent of a dragon costume with four people in it, but hey, it does the job.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p><code>Command+shift+d</code> because the extension doesn&rsquo;t recognise the hyper key chord for some reason.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>

  <p>
    
    <a href="https://www.grizzlebit.com/posts/2020/06-10-hyper-keys-and-mouse-buttons-with-karabiner/">🔗</a>
     ∙ Tagged in <a href="https://www.grizzlebit.com/tags/automation/">Automation</a>, <a href="https://www.grizzlebit.com/tags/productivity/">Productivity</a>, <a href="https://www.grizzlebit.com/tags/tools/">Tools</a>.</p>
</div>
]]></content>
    <link rel="alternate" href="https://www.grizzlebit.com/posts/2020/06-10-hyper-keys-and-mouse-buttons-with-karabiner/"></link>
    <link rel="related" href="https://www.grizzlebit.com/posts/2020/06-10-hyper-keys-and-mouse-buttons-with-karabiner/"></link>
    
  </entry>
  
  <entry>
    <id>https://www.grizzlebit.com/posts/2020/03-15-playing-a-random-album-on-spotify/</id>
    <published>2020-03-15T08:36:00+08:00</published>
    <updated>2025-04-23T19:31:51+08:00</updated>
    <author><name>Ray Grasso</name></author>
    
    <title>Playing a Random Album on Spotify</title>
    <content type="html" xml:base="https://www.grizzlebit.com/posts/" xml:lang="en"><![CDATA[<div>
  <p>I still like listening to albums and sometimes want Spotify to play a random album from a playlist of albums I&rsquo;ve created.</p>
<p>I couldn&rsquo;t find anything out there that does this so I wrote myself a script to handle it instead.</p>
<p>Here&rsquo;s a rundown if you want to use it.</p>
<p>First up, you&rsquo;ll need a playlist with at least one track from each of the albums you want to choose from (<a href="https://open.spotify.com/playlist/4BDGcko4BXBkdE0wFUXSz4?si=SBBz-UNPT56cwcjLUC9f-Q">here&rsquo;s mine</a>). Grab the ID of your playlist<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, and your username and add them into the script below.</p>
<p>Then, you&rsquo;ll need to <a href="https://developer.spotify.com/documentation/general/guides/app-settings/">create an app in Spotify</a> and get your client ID and secret, add them to the script below, so you can authorise the script.</p>
<p>Finally, run <code>gem install rspotify</code> in your default ruby<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> and you should be off to the races.</p>
<p>Run the script with Spotify desktop app installed and it&rsquo;ll open up a random album for you to press that sweet, sweet play button on ⏯.</p>
<p>I run the script from an Alfred workflow so I&rsquo;ve got it close at hand.</p>
<p>Enjoy 🎷🎶</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="ch">#!/usr/bin/env ruby</span>
</span></span><span class="line"><span class="cl"><span class="c1">#/ Usage: open-random-album</span>
</span></span><span class="line"><span class="cl"><span class="c1">#/ Open a random album in Spotify.</span>
</span></span><span class="line"><span class="cl"><span class="c1">#/</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;rspotify&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">RandomAlbum</span>
</span></span><span class="line"><span class="cl">  <span class="no">CLIENT_ID</span> <span class="o">=</span> <span class="s2">&#34;YOUR CLIENT ID&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="no">CLIENT_SECRET</span> <span class="o">=</span> <span class="s2">&#34;YOUR CLIENT SECRET&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="no">USERNAME</span> <span class="o">=</span> <span class="s2">&#34;YOUR USERNAME&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="no">PLAYLIST_ID</span> <span class="o">=</span> <span class="s2">&#34;YOUR PLAYLIST&#39;S ID&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">fetch</span>
</span></span><span class="line"><span class="cl">    <span class="no">RSpotify</span><span class="o">.</span><span class="n">authenticate</span><span class="p">(</span><span class="no">CLIENT_ID</span><span class="p">,</span> <span class="no">CLIENT_SECRET</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kp">new</span><span class="o">.</span><span class="n">fetch</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># Grab the albums from a playlist and choose one at random</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">fetch</span>
</span></span><span class="line"><span class="cl">    <span class="n">playlist</span> <span class="o">=</span> <span class="no">RSpotify</span><span class="o">::</span><span class="no">Playlist</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="no">USERNAME</span><span class="p">,</span> <span class="no">PLAYLIST_ID</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">albums_in_playlist</span><span class="p">(</span><span class="n">playlist</span><span class="p">)</span><span class="o">.</span><span class="n">sample</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kp">private</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">tracks_in_playlist</span><span class="p">(</span><span class="n">playlist</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">limit</span> <span class="o">=</span> <span class="mi">100</span>
</span></span><span class="line"><span class="cl">    <span class="n">offset</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="o">[].</span><span class="n">tap</span> <span class="k">do</span> <span class="o">|</span><span class="n">result</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="kp">loop</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">        <span class="n">tracks</span> <span class="o">=</span> <span class="n">playlist</span><span class="o">.</span><span class="n">tracks</span><span class="p">(</span><span class="ss">limit</span><span class="p">:</span> <span class="n">limit</span><span class="p">,</span> <span class="ss">offset</span><span class="p">:</span> <span class="n">offset</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">break</span> <span class="k">if</span> <span class="n">tracks</span><span class="o">.</span><span class="n">empty?</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">result</span><span class="o">.</span><span class="n">concat</span><span class="p">(</span><span class="n">tracks</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">offset</span> <span class="o">+=</span> <span class="n">limit</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">albums_in_playlist</span><span class="p">(</span><span class="n">playlist</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">tracks</span> <span class="o">=</span> <span class="n">tracks_in_playlist</span><span class="p">(</span><span class="n">playlist</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">tracks</span><span class="o">.</span><span class="n">reduce</span><span class="p">({})</span> <span class="k">do</span> <span class="o">|</span><span class="n">acc</span><span class="p">,</span> <span class="n">track</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="n">acc</span><span class="o">[</span><span class="n">track</span><span class="o">.</span><span class="n">album</span><span class="o">.</span><span class="n">id</span><span class="o">]</span> <span class="o">=</span> <span class="n">track</span><span class="o">.</span><span class="n">album</span>
</span></span><span class="line"><span class="cl">      <span class="n">acc</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span><span class="o">.</span><span class="n">values</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">album</span> <span class="o">=</span> <span class="no">RandomAlbum</span><span class="o">.</span><span class="n">fetch</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">puts</span> <span class="s2">&#34;Opening &#39;</span><span class="si">#{</span><span class="n">album</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">&#39; in Spotify&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">system</span> <span class="s2">&#34;open </span><span class="si">#{</span><span class="n">album</span><span class="o">.</span><span class="n">uri</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span></code></pre></div><div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Click on <code>Share -&gt; Copy Spotify URI</code>. The playlist&rsquo;s ID is the string after the last colon.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>If this becomes a pain I guess you could look into bundling the script up with its required gems somehow.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>

  <p>
    
    <a href="https://www.grizzlebit.com/posts/2020/03-15-playing-a-random-album-on-spotify/">🔗</a>
     ∙ Tagged in <a href="https://www.grizzlebit.com/tags/automation/">Automation</a>, <a href="https://www.grizzlebit.com/tags/music/">Music</a>, <a href="https://www.grizzlebit.com/tags/tools/">Tools</a>.</p>
</div>
]]></content>
    <link rel="alternate" href="https://www.grizzlebit.com/posts/2020/03-15-playing-a-random-album-on-spotify/"></link>
    <link rel="related" href="https://www.grizzlebit.com/posts/2020/03-15-playing-a-random-album-on-spotify/"></link>
    
  </entry>
  
  <entry>
    <id>https://www.grizzlebit.com/posts/2019/07-14-finding-open-web-pages-with-alfred/</id>
    <published>2019-07-14T09:40:00+08:00</published>
    <updated>2025-04-23T19:31:51+08:00</updated>
    <author><name>Ray Grasso</name></author>
    
    <title>Finding Open Web Pages with Alfred</title>
    <content type="html" xml:base="https://www.grizzlebit.com/posts/" xml:lang="en"><![CDATA[<div>
  <p>There are a handful of web pages that I use regularly throughout the day. Some are web apps that I keep pinned in Chrome while others come and go as I work.</p>
<p>I tend to close tabs when I&rsquo;m done with them but I still end up with many open tabs. I&rsquo;ve created an <a href="https://www.alfredapp.com/workflows/">Alfred Workflow</a> that opens a page I&rsquo;m looking for so I don&rsquo;t have to pick through my Chrome tabs by hand to find it.</p>
<p><a href="/files/Find-Page.alfredworkflow">The Find Page workflow</a> takes a URL from a predefined list, runs an AppleScript that finds and activates the associated page if it&rsquo;s already open in Chrome, otherwise it opens it in a new tab.</p>
<figure><img src="/images/alfred-find-page-definition.png"
    alt="Find Page Workflow definition">
</figure>

<figure><img src="/images/alfred-find-page-example.png"
    alt="Find Page Workflow example">
</figure>

<p>You can <a href="/files/Find-Page.alfredworkflow">download the workflow</a> and try it yourself.</p>

  <p>
    
    <a href="https://www.grizzlebit.com/posts/2019/07-14-finding-open-web-pages-with-alfred/">🔗</a>
     ∙ Tagged in <a href="https://www.grizzlebit.com/tags/automation/">Automation</a>, <a href="https://www.grizzlebit.com/tags/productivity/">Productivity</a>, <a href="https://www.grizzlebit.com/tags/tools/">Tools</a>.</p>
</div>
]]></content>
    <link rel="alternate" href="https://www.grizzlebit.com/posts/2019/07-14-finding-open-web-pages-with-alfred/"></link>
    <link rel="related" href="https://www.grizzlebit.com/posts/2019/07-14-finding-open-web-pages-with-alfred/"></link>
    
  </entry>
  
</feed>
