<?xml version="1.0" encoding="utf-8" standalone="yes"?><?xml-stylesheet href="/atom.xsl" type="text/xsl"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Everythings Better With Brentter</title>
    <link>https://brentter.com/</link>
    <description>Recent content on Everythings Better With Brentter</description>
    <image>
      <title>Everythings Better With Brentter</title>
      <url>https://brentter.com/og.jpg</url>
      <link>https://brentter.com/</link>
    </image>
    <generator>Hugo -- 0.135.0</generator>
    <language>en</language>
    <copyright>Everything&amp;rsquo;s Better With Brentter</copyright>
    <lastBuildDate>Tue, 14 Jan 2025 13:18:37 -0800</lastBuildDate>
    <atom:link href="https://brentter.com/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>The Beautiful Web - Henry.Codes And Talks On Grief</title>
      <link>https://brentter.com/notes/the_beautiful_web/</link>
      <pubDate>Tue, 14 Jan 2025 13:18:37 -0800</pubDate>
      <guid>https://brentter.com/notes/the_beautiful_web/</guid>
      <description>The incredibly well-designed website of designer/developer Henry Desroches and some equally beautiful advice found in his writings.</description><content:encoded><![CDATA[ 
<figure class="entry-cover"><img loading="lazy" src="https://brentter.com/notes/notes-images/henrycodes.webp" alt="The website at henry.codes">
        
</figure>]]>
               <![CDATA[<p>Following blogrolls is something I tend to do when I&rsquo;m bored and have a few moments to spare. It&rsquo;s a habit I started way back when 50% of the web was just animated gifs with fire, MySpace pages and directories. The majority of the time it&rsquo;s pretty easy to figure out right off the bat if there&rsquo;s anything that would resonate with me just based off the site design or layout followed by a quick glance at the most recent blog/discussion topics. In following random links from strangers it&rsquo;s led to some pretty crazy &ldquo;<a href="https://always-hallways.de-ateliers.nl/" target="_blank">destinations</a>
&rdquo; over the years but with that comes the rare occasion when I find something absolutely uniquely beautiful. In this case it&rsquo;s the website, portfolio and blog for <a href="https://henry.codes" target="_blank">Henry Desroches</a>
. He&rsquo;s a self-proclaimed  &ldquo;Front-of-the-front-end developer &amp;&amp; design engineer &amp;&amp; creative technologist &amp;&amp; UX engineer &amp;&amp; web developer from Denver, Colorado&rdquo; and right off the bat you&rsquo;re greeted with a beautiful mix of typography and space. If you keep clicking, you&rsquo;ll find his <a href="https://resume.henry.codes/" target="_blank">previous work</a>
 (impressive AF), a talk he gave in support of the <a href="https://conf.11ty.dev/2024/digital-frontiers-indieweb-cowboys-and-a-place-online-to-call-your-own/" target="_blank">IndieWeb and POSSE</a>
 and then some articles both by a bunch of folks who seem to share the same aptitude for website design that Henry does as well as a few from the man himself.</p>
<p>His entire site is chock full of fun little features from random call-outs to even funny little Editor&rsquo;s Notes in the articles:<br>
<img loading="lazy" src="/notes/notes-images/editorsnote.jpg" alt="Im A Chill Internet Stranger"  />
</p>
<p>He also hits on a topic that I didn&rsquo;t expect from a design blog:</p>
<p><a href="https://henry.codes/writing/what-to-say/" target="_blank">What To Say When You Don&rsquo;t Know What To Say</a>
.</p>
<p>Growing up with ADHD &amp; the anxiety that came with it I tended to react to things like grief by avoiding the even mention of it. Not only did I not know how to express my own feelings as a child but that also meant I felt like I was also unable to comfort others because of a self-imposed cycle of self-doubt. I was always second-guessing myself and was terrified of saying the wrong thing to someone I knew already was hurting.</p>
<p>What do you say to someone you know is in pain? To someone who just lost someone they love? It&rsquo;s not an easy topic and sometimes there is no &ldquo;right&rdquo; answer, but as Henry so eloquently writes in his post, there are some things that should probably be avoided. Having been on the receiving end of at least one of his examples I have to say this post hit me in the feels and I definitely was not expecting that when I first was exploring his site. Instead of taking away from the well-written post that Henry wrote I will instead share with you one thing that will always be a safe way to convey that you care - &ldquo;I&rsquo;m sorry for your loss.&rdquo;</p>
<p>Sometimes the less that is said can mean the most unspoken.</p>
<p>Now go check out his article then for a mood booster click on any of his blogroll or portfolio links as it seems most of his friends are also pretty good at web design.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Here Are My Answers to The 2025 Blog Questions Challenge</title>
      <link>https://brentter.com/blog/my_answers_to_the_blog_questions_challenge/</link>
      <pubDate>Sun, 05 Jan 2025 19:45:25 -0800</pubDate>
      <guid>https://brentter.com/blog/my_answers_to_the_blog_questions_challenge/</guid>
      <description>One of my favorite bloggers Kev Quirk threw out a challenge post to answer questions about blogging so I decided to answer.</description><content:encoded><![CDATA[ 
<figure class="entry-cover"><img loading="lazy" src="https://brentter.com/blog/blogquestionschallenge.png" alt="The Blog Questions Challenge">
        
</figure>]]>
               <![CDATA[<p>So I <a href="/blog/follow-the-best-rss-app-now-in-alpha/">follow</a>
 an ever increasing group of websites/blogs/forums using RSS and tend to separate them through categories (programming, devops, health, osx, etc..) but every now and then I end up liking certain sites more than the others because of the content they&rsquo;re producing, the work they&rsquo;re sharing or even just because I love their website design. The following blogger ends up hitting all three of those variables for me. The guys name is <a href="https://kevquirk.com/" target="_blank">Kev Quirk</a>
 and he constantly puts out interesting stuff in addition to having a very well designed website. So much so that I plan on blatantly ripping off an idea from his site that he&rsquo;s done perfectly which is having a call-out button at the end of each post to reply via email. If you ever end up seeing this, thanks for the idea. Here&rsquo;s the button I&rsquo;m referring to taken from his site:<br>
<img loading="lazy" src="/blogimages/replybyemail.jpg" alt="reply by email button"  />
</p>
<p>So like him, I&rsquo;m not a big fan of meta posts. Most of the time it just reeks of a faked interest to drive up site engagement but if you&rsquo;re familiar with some of Kev&rsquo;s work you&rsquo;d know he&rsquo;s pretty genuine about his intentions and a big believer in community building/indie-web. So he&rsquo;s adopted a similar blog question challenge that he came across on another blogger you should also be following (<a href="https://brandons-journal.com/bear-blog-questions-challenge/" target="_blank">Brandon&rsquo;s Journal</a>
) called the Bear Blog Question Challenge. Also not being in the <a href="https://bearblog.dev/" target="_blank">BearBlog</a>
 network (which btw is a great micro-blogging system if you&rsquo;re in the market for one) I figured I&rsquo;d join him in answering his questions on here. So here we go:</p>
<h2 id="my-answers-to-the-challenge">My Answers To The Challenge:</h2>
<h3 id="why-did-you-start-blogging-in-the-first-place">Why did you start blogging in the first place?</h3>
<p>I was brought up around the time the internet was becoming more and more accessible on the consumer level and was lucky enough to have access to it while still in middle school. I think I was using MySpace and a few sites like Geocities to post random stuff I&rsquo;d come across from alternative uses for AOL CDs to strategy guides for the games I was playing to really just anything (lots of song lyrics and what I thought they meant type posts). I didn&rsquo;t actually start a &lsquo;real&rsquo; blog until probably 2006 after graduating college. I was working in the advertising industry and had a lot of opinions on what was going on, ideas of what would be cool to see, etc.. I had taken on the online handle Brentter (aka my first name and the first three letters of my last name) a year or two earlier so naming it was easy - Everythings Better With Brentter. That quickly turned into not only a personal blog but an advertising industry blog as I was posting sometimes 3-4 times a day with campaigns I&rsquo;d find or even be sent directly by production companies.</p>
<h3 id="what-platform-are-you-using-to-manage-your-blog-and-why-did-you-choose-it">What platform are you using to manage your blog and why did you choose it?</h3>
<p>This site is currently using Hugo and I chose it because I&rsquo;d read nothing but great things about the speed at which huge Hugo sites could load. By the way it&rsquo;s 100% true. It&rsquo;s insanely fast not only to load the pages (because they&rsquo;re flat files, not dynamically generated every time a user visits the page) as well as incredibly fast to compile the site when you&rsquo;re ready to push an update to it. I&rsquo;d been messing around with the go language for the past 2-3 years and that&rsquo;s what Hugo is written in so I thought why not?</p>
<h3 id="have-you-blogged-on-other-platforms-before">Have you blogged on other platforms before?</h3>
<p>Yes. So prior to my first &lsquo;real&rsquo; blog those platforms were mainly HTML/CSS based pages with no real structured CMS system, then with brentter.com it was initially built on some wiki software which for the life of me I can&rsquo;t remember. That lasted about a year then I switched it over to Wordpress, which I had heard was just a new fork of another CMS named b2 (that I liked). Over the years of running that blog I&rsquo;d dabble in little side projects using blogger and tumblr but ultimately stuck with Wordpress until I shut the site down somewhere around 2016/2017.</p>
<h3 id="how-do-you-write-your-posts-for-example-in-a-local-editing-tool-or-in-a-paneldashboard-thats-part-of-your-blog">How do you write your posts? For example, in a local editing tool, or in a panel/dashboard that&rsquo;s part of your blog?</h3>
<p>So unlike my experiences with Wordpress, where I would either draft them in the WP back-end or use a 3rd party application that would help me manage the blog content better (Using the xml-rpc protocol), I write my posts using a great app called Sublime Text. I&rsquo;ve been a fan and paid user of this application since it first came out and it&rsquo;s only gotten better over the years. Hugo relies on pages or posts having what&rsquo;s called front-matter. It&rsquo;s a bit that goes at the top of everything you want to add to your Hugo site which indicates all the meta data as well as instructions on how my settings on the platform should render the page. So I have a couple of pre-made templates that I pull up in Sublime and that let&rsquo;s me pretty quickly get working on something new. It&rsquo;s a pretty easy to use setup. I then use git to push it to GitHub which then in turn triggers it being compiled and sent off to CloudFlare where I host brentter.com.</p>
<h3 id="when-do-you-feel-most-inspired-to-write">When do you feel most inspired to write?</h3>
<p>Usually after reading or coming across something interesting, or at least something I think others would find as interesting as I do. So for instance I&rsquo;m writing this probably 20 minutes after having read the post on Kevs blog. Though in this case it&rsquo;s not because I think anyone cares about how this site is made or my thought process behind it, but rather because I like the idea, enjoy Kevs blog and want to participate. This is one of those few &ldquo;this is for me&rdquo; type posts. I have a bunch of post ideas lined up with a bunch of research that was needed to properly explain the content but those aren&rsquo;t quite ready yet. Those got started right after getting the idea for them. I&rsquo;ve got ADHD so the faster I act on an idea that I like the better chances of it seeing the light of day.</p>
<h3 id="do-you-publish-immediately-after-writing-or-do-you-let-it-simmer-a-bit-as-a-draft">Do you publish immediately after writing, or do you let it simmer a bit as a draft?</h3>
<p>Well this time around for my blog on this domain I tend to just publish immediately after double checking images, grammar, links, etc.. which leads me to another great feature of using Hugo is it has a built-in dev server so as I save changes I can view my updates in real-time before pushing live. On the old site I would tend to save the posts as drafts then give them a once-over before going to bed in case my tone was too strong in a negative direction when talking about other production companies or ad agencies. I didn&rsquo;t want to unnecessarily burn bridges with a wild hot take and by then I&rsquo;d usually have another idea or two to add.</p>
<h3 id="whats-your-favorite-post-on-your-blog">What&rsquo;s your favorite post on your blog?</h3>
<p>So this version of the blog is still very new so I don&rsquo;t have that many posts yet. In fact, I don&rsquo;t really have any of my older style of posts that were long-form and tended to spend significant time arguing whatever my current point of view on a subject was. That being said I&rsquo;d have to go with my post on <a href="/blog/add-a-spotify-recently-played-page-for-hugo/">adding a recently played music</a>
 page to a Hugo blog. I threw it and the code together pretty quickly because it was something I wanted to add on here then quickly re-wrote the entire thing about a week or two later to incorporate it into a much bigger project I have yet to release. That&rsquo;ll come out soon enough but basically it&rsquo;s a homegrown scrobbler to track not only recent music but also bookmarks, starred reps, notes, exercise logs, movies, etc.. Writing that post is what gave me the idea for it and I&rsquo;m pretty happy with the result no matter how basic the approach to it was. It goes back to something a favorite teacher once told me that &ldquo;Success is not about the results from a first attempt, it&rsquo;s about the progress you make after it.&rdquo;</p>
<h3 id="any-future-plans-for-your-blog-maybe-a-redesign-a-move-to-another-platform-or-adding-a-new-feature">Any future plans for your blog? Maybe a redesign, a move to another platform, or adding a new feature?</h3>
<p>So this being my first foray into using Hugo I went with a pretty basic theme that by now has been modified drastically for various features behind the scenes. While I like it, I know I can do better so I have been working on a new design using what I&rsquo;ve learned about how the Hugo framework operates. An obvious new addition will be an email to reply button as mentioned earlier in this post. I also plan on having it incorporate more of my data to expand this from being just a blog to more of a personal info-garden.</p>
<hr>
<p>So that&rsquo;s it. Simple enough, while I don&rsquo;t have anyone in particular in mind to tag (which is know is part of the whole concept of these meta-posts), I hope you go and check out both Kevs and Brandons blogs which are linked above. If you&rsquo;re interested in joining us in this little mini blog challenge you can find more about it on Kevs <a href="https://kevquirk.com/blog/blog-questions-challenge" target="_blank">Blog Questions Challenge</a>
 page.</p>
<p>Cheers,<br>
Brent</p>
]]></content:encoded>
    </item>
    <item>
      <title>Having Fun With The Raindrop.io API To Grab Recent Bookmarks</title>
      <link>https://brentter.com/blog/raindrop_api_fun_to_grab_recent_bookmarks/</link>
      <pubDate>Tue, 31 Dec 2024 18:10:12 -0800</pubDate>
      <guid>https://brentter.com/blog/raindrop_api_fun_to_grab_recent_bookmarks/</guid>
      <description>A quick little python script to convert your most recent bookmarks on the raindrop.io app to a local json file using their API</description><content:encoded><![CDATA[ 
<figure class="entry-cover"><img loading="lazy" src="https://brentter.com/blog/raindrop_api_fun.webp" alt="python script to grab your most recent raindrop.io bookmarks using their api">
        
</figure>]]>
               <![CDATA[<p>I&rsquo;ve been trying to consolidate and filter all my old bookmarks but over the past two decades I&rsquo;ve used many different clients, browsers and other ways to store them so the content is all over the place. A good example is one of the most recent bookmarking apps that I&rsquo;ve been using has been Raindrop.io. It&rsquo;s pretty, has incredible search/filtering options, built-in AI support, is available on all platforms and does the job nicely. The only problem is there&rsquo;s no built-in RSS feed for your entire account - I.e. no recently bookmarked feed. They only provide native RSS feeds for collections or categories that you&rsquo;ve created (assuming they&rsquo;re public and not &ldquo;private&rdquo; collections). That bird just won&rsquo;t fly for me so I wrote a quick little script that&rsquo;ll grab your most recent bookmarks using python. Just scroll to the bottom (or click <a href="#the-download-recently-added-bookmarks-python-script">here</a>
) if you want to skip the whole recipe site experience, otherwise here&rsquo;s a little more about exporting data from the app.</p>
<h2 id="raindrop-and-the-lack-of-a-recently-added-rss-feed">Raindrop And The Lack Of A Recently Added RSS Feed</h2>
<p>The first thing I did was ask in their forum about it and apparently it is something that&rsquo;s been requested numerous times over the years with the developer giving the <a href="https://raindropio.canny.io/feature-requests/p/rss-feeds-by-tagsuntagged" target="_blank">same response</a>
 saying that feature isn&rsquo;t implemented because of privacy concerns. He goes on to say that each collection that you create has both a public and private RSS feed but the private one has a &ldquo;uniq cryptic url&rdquo; (his wording) and if shared will give full read access to that content. It&rsquo;s not opensource so I have no idea how he&rsquo;s set up the service but I&rsquo;d have to assume that changing the method that the developer currently has in place for RSS feeds would be too much of a time-sink to be worthwhile for him. Raindrop has been getting a lot of positive press over the past few years not to mention a massive influx of new customers so why should he spend all that time just for a feature to appease a small subset of users? The developer has mentioned in a few of the RSS-related threads that Raindrop works well with glue-up services like IFTTT but involving another 3rd party service just isn&rsquo;t what I was looking for either. As my mother would say, &ldquo;I&rsquo;m not mad, I&rsquo;m just disappointed.&rdquo; Sadly I discovered this problem after I had already signed up as a paid subscriber and then immediately regretted my decision.</p>
<p>My personal opinion on anything that&rsquo;s online is you can never provide enough RSS feeds or ways to export your own content. If I create something online using your service or application (in this example a bookmarked link with my own tags and comments regarding it), then I should have an easy way to re-purpose and export that content from your site. This goes 20x if I&rsquo;m paying for the service or have purchased the application. This opens up a much larger issue I will eventually talk about regarding a HUGE problem we&rsquo;re seeing today with people not realizing that they don&rsquo;t own anything that&rsquo;s online unless they&rsquo;re the ones in charge of it. You never know when company policy could change, the site could go offline, the company could go bankrupt/be sold, or any other of the million situations where the end result is you no longer have access to your data.</p>
<h2 id="exportbackup-options-and-the-raindropio-api">Export/Backup Options And The Raindrop.io API</h2>
<p>Although Raindrop lacks an RSS feed for recent account activity they do incorporate a few other ways that you can download your data. You&rsquo;ll need to be a paid subscriber to use these two features but they have a  export/backup feature that includes options for it to automatically save regular backups of your account to various cloud services (like Dropbox) as well as an API. I went with the API option and wrote a quick little python wrapper for it. It&rsquo;s pretty basic but should work with authenticating, reading/editing/deleting raindrops and grabbing collections. If you wish it could work with any of the other API functions feel free to submit a ticket on GitHub and I might add it for you. You can find it here - <a href="https://github.com/brentter/Raindrop.io-Python-API-Wrap" target="_blank">Github.com/Brentter/Raindrop.io-Python-API-Wrap</a>
</p>
<p>I also included in that repository a basic python script to download the most recent 25 bookmarks from your account, remove any unnecessary or redundant data that&rsquo;s included and then save it locally as a .json file. Back when I put that all together I was doing something similar for a couple of different services with an additional script that would run every few hours to see if any of them needed updating, trigger those scripts then add anything new to a local database. I like keeping functions as separate as possible so they&rsquo;re easier to change (and scale). This also allowed me to add it to a self-hosted &ldquo;recently saved content&rdquo; page that wasn&rsquo;t reliant on outside sources to use (only to update). For my own projects I&rsquo;m very much a &lsquo;functionality over design&rsquo; kind of guy so I won&rsquo;t be sharing that part of the project anytime soon (it&rsquo;s not pretty) however you can easily get similar results using any locally hosted RSS reader.</p>
<h2 id="the-download-recently-added-bookmarks-python-script">The Download Recently Added Bookmarks Python Script</h2>
<p>Here&rsquo;s the basic download script using my Raindrop.io API wrapper in python, you&rsquo;ll need to add your own token from the app to a .env file before running it (full instructions on the git page posted above):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">raindrop_api</span> <span class="kn">import</span> <span class="n">RaindropAPI</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">requests</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">json</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">argparse</span>
</span></span><span class="line"><span class="cl"><span class="c1"># You can change the number of results it will pull with -l or --limit - Default is 25</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">get_recent_raindrops</span><span class="p">(</span><span class="n">api</span><span class="p">,</span> <span class="n">limit</span><span class="o">=</span><span class="mi">25</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Fetch the most recent raindrops.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">url</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="n">api</span><span class="o">.</span><span class="n">base_url</span><span class="si">}</span><span class="s2">/raindrops/0?perpage=</span><span class="si">{</span><span class="n">limit</span><span class="si">}</span><span class="s2">&amp;sort=-created&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">response</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">headers</span><span class="o">=</span><span class="n">api</span><span class="o">.</span><span class="n">headers</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">response</span><span class="o">.</span><span class="n">json</span><span class="p">()</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">filter_raindrops</span><span class="p">(</span><span class="n">raindrops</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Filter the raindrops to only include specific fields.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">filtered</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">raindrops</span><span class="p">[</span><span class="s1">&#39;items&#39;</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl">        <span class="n">filtered</span><span class="o">.</span><span class="n">append</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">            <span class="s1">&#39;link&#39;</span><span class="p">:</span> <span class="n">item</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;link&#39;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="s1">&#39;title&#39;</span><span class="p">:</span> <span class="n">item</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;title&#39;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="s1">&#39;excerpt&#39;</span><span class="p">:</span> <span class="n">item</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;excerpt&#39;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="s1">&#39;cover&#39;</span><span class="p">:</span> <span class="n">item</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;cover&#39;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="s1">&#39;tags&#39;</span><span class="p">:</span> <span class="n">item</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;tags&#39;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="s1">&#39;created&#39;</span><span class="p">:</span> <span class="n">item</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;created&#39;</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">return</span> <span class="n">filtered</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">main</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">parser</span> <span class="o">=</span> <span class="n">argparse</span><span class="o">.</span><span class="n">ArgumentParser</span><span class="p">(</span><span class="n">description</span><span class="o">=</span><span class="s1">&#39;Fetch and filter recent raindrops.&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">parser</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;-l&#39;</span><span class="p">,</span> <span class="s1">&#39;--limit&#39;</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="nb">int</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="mi">25</span><span class="p">,</span> <span class="n">help</span><span class="o">=</span><span class="s1">&#39;Number of recent raindrops to fetch&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">args</span> <span class="o">=</span> <span class="n">parser</span><span class="o">.</span><span class="n">parse_args</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1"># Initialize the RaindropAPI</span>
</span></span><span class="line"><span class="cl">    <span class="n">api</span> <span class="o">=</span> <span class="n">RaindropAPI</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1"># Get the most recent raindrops</span>
</span></span><span class="line"><span class="cl">    <span class="n">recent_raindrops</span> <span class="o">=</span> <span class="n">get_recent_raindrops</span><span class="p">(</span><span class="n">api</span><span class="p">,</span> <span class="n">limit</span><span class="o">=</span><span class="n">args</span><span class="o">.</span><span class="n">limit</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1"># Filter the raindrops</span>
</span></span><span class="line"><span class="cl">    <span class="n">filtered_raindrops</span> <span class="o">=</span> <span class="n">filter_raindrops</span><span class="p">(</span><span class="n">recent_raindrops</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1"># Print the filtered raindrops in JSON format</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">filtered_raindrops</span><span class="p">,</span> <span class="n">indent</span><span class="o">=</span><span class="mi">4</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1"># Save the filtered raindrops to a file in JSON format</span>
</span></span><span class="line"><span class="cl">    <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s1">&#39;recentraindrops.json&#39;</span><span class="p">,</span> <span class="s1">&#39;w&#39;</span><span class="p">)</span> <span class="k">as</span> <span class="n">file</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">json</span><span class="o">.</span><span class="n">dump</span><span class="p">(</span><span class="n">filtered_raindrops</span><span class="p">,</span> <span class="n">file</span><span class="p">,</span> <span class="n">indent</span><span class="o">=</span><span class="mi">4</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&#34;__main__&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">main</span><span class="p">()</span>
</span></span></code></pre></div><p>So when using their API it can give you a bunch of data fields that I didn&rsquo;t have a use for, hence the quick filtering job. Feel free to use this and the API wrapper for whatever purpose you want. If it&rsquo;s helpful I&rsquo;d love to hear about what you made!</p>
<p>Cheers,<br>
Brent</p>
]]></content:encoded>
    </item>
    <item>
      <title>HTML To Markdown Tools - How To Convert Content For LLMs</title>
      <link>https://brentter.com/notes/my_favorite_website_to_markdown_tools_for_llms/</link>
      <pubDate>Sat, 28 Dec 2024 08:39:05 -0700</pubDate>
      <guid>https://brentter.com/notes/my_favorite_website_to_markdown_tools_for_llms/</guid>
      <description>Some of the best open-source and free tools I use for converting websites and git repos into structured markdown data for improved LLM ingestion</description><content:encoded><![CDATA[ 
<figure class="entry-cover"><img loading="lazy" src="https://brentter.com/notes/my_favorite_website_to_markdown_tools_for_llms.webp" alt="Some free and opensource tools to convert websites to markdown">
        
</figure>]]>
               <![CDATA[<p>A quick set of links to help convert webpages or even git repositories into a single markdown file for easier absorption by LLMs. Now how does that help a LLM better understand data you might ask? Well, by putting the content into a structured format the LLM will know what every piece of content is supposed to be and a better understanding of how one piece of information relates to another. Markdown makes it easy for the models to quickly see what are the main topics or sections based on heading tags (i.e. H1 tags for the main topic, H2 for primary topics, H3 for sub topics, etc..) as well as indicates the format of the information as it&rsquo;s displayed. The easiest way to think about it is structured data provides a &ldquo;roadmap&rdquo; of sorts to the content being ingested. To be able to learn from the data and be able to associate it with data from other sources it tries to parse or &ldquo;chunk&rdquo; it into various associated groupings. This is all an extremely basic description of how the knowledge is managed in these systems but the purpose of this note isn&rsquo;t to go into the &ldquo;Why?&rdquo; so much as it is to share a few tools I&rsquo;ve recently found useful to accomplish this.</p>
<h2 id="combining-git-repositories-into-a-single-file---gitingest">Combining Git Repositories Into A Single File - GitIngest</h2>
<p>Some of the recent AI programming assistants have had mixed results when faced with learning from entire code repositories and one idea that&rsquo;s been gaining traction is to combine the files into a singular source so that it &ldquo;hopefully&rdquo; won&rsquo;t get confused as to how a piece of software or app works. One of the easiest tools I&rsquo;ve come across is an open source project by <a href="https://bsky.app/profile/yasbaltrine.bsky.social" target="_blank">Romain Courtois</a>
 called <a href="https://github.com/cyclotruc/gitingest" target="_blank">GitIngest</a>
. You can use the online app over at <a href="https://gitingest.com/" target="_blank">GitIngest.com</a>
, self-host it via the provided docker containers, run it locally or even use it as a python library in your own projects. It comes with it&rsquo;s own simple front-end, ability to restrict allowed hostnames, ignore file-types, include files only under a certain size, basic token estimation and it even lets you preview the output before downloading the final compiled file. Pretty useful if you ask me.</p>
<h2 id="converting-a-website-into-markdown-content">Converting a Website Into Markdown Content</h2>
<p>So there are a bunch of tools out there that will let you convert single pages and even entire websites into a <a href="https://en.wikipedia.org/wiki/Markdown" target="_blank">standardized Markdown format</a>
 as well as few open-source projects that can help you write your own tools to do the same. Let&rsquo;s break down the basic process behind it:</p>
<ul>
<li>First you&rsquo;re going to need to scrape the webpage.</li>
<li>Next you&rsquo;re going to need to parse out all the information you don&rsquo;t want or need like ads, tracking scripts, headers/footers and if possible most of the navigational UI.</li>
<li>Once you&rsquo;ve done that then you need to convert that content into Markdown.</li>
</ul>
<p>While the basics behind the format are pretty standardized there are a few popular variations you will come across like MultiMarkdown, Markdown Extra, Pandoc and GFM (Github Flavored Markdown) in addition to some sites like Reddit and StackOverflow that use site-specific versions of it for their own functionality.</p>
<h2 id="tools-ive-used-to-convert-websites-to-markdown-for-llm-use">Tools I&rsquo;ve Used To Convert Websites to Markdown For LLM Use</h2>
<p>Here&rsquo;s a couple free tools, most that you can host yourself, that will help. I&rsquo;ve used all of them at some point in the last year and as I&rsquo;ll get into, some work better than others for certain kinds of content/websites.</p>
<p><a href="https://github.com/asadm/markdowndown" target="_blank">MarkdownDown</a>
 - This is a great tool, and the one I&rsquo;ve lately been using the most, to scrape and convert websites into Markdown.  Asad Memon has it hosted for <a href="https://markdowndown.vercel.app/" target="_blank">free to use on Vercel</a>
 as well as the full source code on his Github. It uses <a href="https://pptr.dev/" target="_blank">Puppeteer</a>
 to scrape the page, <a href="https://github.com/mixmark-io/turndown" target="_blank">Turndown</a>
 to convert it to the Markdown format and the <a href="https://github.com/mozilla/readability" target="_blank">Mozilla Readability</a>
 library to clean up the content of any unnecessary un-desired crap. It has the option to add in a Browserless API key so you don&rsquo;t have to scrape it yourself as well as includes the option to spin up a Cloudflare worker who uses the <a href="https://developers.cloudflare.com/browser-rendering/" target="_blank">Browser Rendering API</a>
 (which requires the $5/month paid Cloudflare Worker plan) to do the same. It has a nice front-end that let&rsquo;s you incorporate your own OpenAI key if you want to have it manipulate the content before outputting it to markdown as well as the option to download the images (and re-link them) locally. If you&rsquo;re trying to scrape pages like reddit there is currently a working pull-request by <a href="https://github.com/laggingreflex/web2md/tree/fix/stealth" target="_blank">laggingreflex</a>
 which enables the puppeteer-extra stealth plugin to circumvent blocks.</p>
<p><a href="https://heckyesmarkdown.com" target="_blank">HeckYesMarkdown.com</a>
 - Created by <a href="https://brettterpstra.com/" target="_blank">Brett Terpstra</a>
, heckyesmarkdown will scrape a provided url and output the contents in a variety of customizable formats ranging from multiple types of Markdown to HTML, LaTeX, OpenDocument, Plaintext, RTF and more than a dozen others. It provides a convenient bookmarklet you can put in your browser to use it on the go and displays the results in an easy to copy format with the ability to preview what the rendered markdown will look like in a browser. It also let&rsquo;s you import the result directly into apps like Obsidian (which I love). He also was kind enough to <a href="https://heckyesmarkdown.com/api/" target="_blank">provide an API</a>
 in case you wanted to incorporate the service in your own scripts.</p>
<p><a href="https://urltomarkdown.com/" target="_blank">URLtoMarkdown.com</a>
 - This is a great open-source tool using Turndown, Readability, jsdom and Heroku to convert a URL into markdown and allows you to ignore links and/or include the page title in the result. You can use the tool on their homepage or go to <a href="https://github.com/macsplit/urltomarkdown" target="_blank">their Github</a>
 to host your own. One of the best features of this site is it also provides a web service that will convert a URL to markdown simply by adding it to the end of their Heroku app (that you can host yourself)- i.e. <a href="https://urltomarkdown.herokuapp.com/?url=https://example.com" target="_blank">https://urltomarkdown.herokuapp.com/?url=https://example.com</a>
</p>
<p><a href="https://jina.ai/reader/" target="_blank">Jina.AI Reader API</a>
 - Jina.AI provides a free reader API and web-service that can convert URL&rsquo;s into formats like Markdown, TXT, HTML and even a screenshot. While you don&rsquo;t need an API key to use it, you are rate-limited by them if you don&rsquo;t. Luckily they provide a free API key with 1 million credits on it (yes, 1 million) just by visiting <a href="https://r.jina.ai/https://example.com" target="_blank">their website</a>
 (no registration/login/credit-card required). I hope they don&rsquo;t read this but it&rsquo;s also just as easy to get a new one once you&rsquo;ve used that up just by clearing your cookies and revisiting the same page.  It let&rsquo;s you grab the entire website, select a specific CSS target to include or exclude, remove all the images, gather all the links and/or images at the end, forward custom cookies, use a proxy service and a gaggle of other useful options for hard-to-scrape sources like Reddit or dynamic javascript-based sites. With that same API key you can also use a few of their other API-based products like their AI re-ranker, zero-shot classification tool and more. To use their free web-service just add your desired URL at the end of r.jina.ai like this: <a href="https://r.jina.ai/https://example.com" target="_blank">https://r.jina.ai/https://example.com</a>
</p>
<p><a href="https://www.firecrawl.dev/" target="_blank">Firecrawl.dev</a>
 - Firecrawl is a pretty popular tool that offers both an <a href="https://github.com/mendableai/firecrawl" target="_blank">open-source model</a>
 that you can self-host as well as a fully-featured website API tool that is designed to scrape, crawl and extract entire websites or even libraries and convert them into LLM-ready structured data. You can save the content as Markdown, HTML, screenshots or use their custom Extract LLM service to export it in LLM-specific formats (still in beta). It&rsquo;s very fast, works on most if not all websites I&rsquo;ve thrown at it and if you don&rsquo;t want to self-host it they provide you with 500 credits for free to test it out. Firecrawl is used in a LOT of the big frameworks that you&rsquo;ll find out there and some tend to lean on it pretty hard as their go-to LLM data rendering/scraping/extraction service. If you&rsquo;d rather just pay a small fee to have everything taken care of for you in an easy, ready-made platform this would probably be the one to look into. They have an endless list of features and integrations, incorporate all sorts of different media parsing into their runs and allow you to set up crawl/scrape jobs without needing much technical knowledge. They outline the big differences between their open-source offering and their paid cloud service <a href="https://docs.firecrawl.dev/contributing/open-source-or-cloud" target="_blank">on their docs page here</a>
.</p>
<p><a href="https://github.com/danny0838/webscrapbook" target="_blank">Webscrapbook</a>
 - Webscrapbook is an opensource browser extension available for both Chromium-based browsers (<a href="https://chrome.google.com/webstore/detail/web-scrapbook/oegnpmiddfljlloiklpkeelagaeejfai" target="_blank">Chrome</a>
, <a href="https://microsoftedge.microsoft.com/addons/detail/lodlipoddpmffbngkadebncbbidnambc" target="_blank">Edge</a>
, Opera, Vivaldi, etc..) as well as Firefox-based browsers (<a href="https://addons.mozilla.org/en-US/firefox/addon/webscrapbook/" target="_blank">Firefox</a>
, Tor, Waterfox)  that let&rsquo;s you save any website in a variety of formats as well as manipulate and notate the content before saving it. You can also have it save the content both locally on your own device or remotely so it can be easily shared with others. Mobile friendly, backwards compatible with previous legacy versions of Scrapbook X and offers the ability to take your saved pages and generate a static site index that can be used as it&rsquo;s own static website (which can come in handy when ripping data for purposes other than LLM-use).</p>
<p>If you search on GitHub or even google for &ldquo;website to structured data converter&rdquo; you can see there is an endless supply of both paid and open-source tools available that all have their own pros/cons. Using a combination of the ones above I&rsquo;ve haven&rsquo;t found any source that I wasn&rsquo;t able to easily convert to markdown. Hope that helps.</p>
<p>Cheers,<br>
Brent</p>
]]></content:encoded>
    </item>
    <item>
      <title>How To Find New RSS Feeds To Follow - Free Tools &amp; Resources</title>
      <link>https://brentter.com/notes/how_to_find_good_rss_feeds_to_follow/</link>
      <pubDate>Wed, 18 Dec 2024 10:46:55 -0800</pubDate>
      <guid>https://brentter.com/notes/how_to_find_good_rss_feeds_to_follow/</guid>
      <description>How I find quality RSS feeds that are worth subscribing to and how you can do the same using a few free tools and resources that are easy to use</description><content:encoded><![CDATA[ 
<figure class="entry-cover"><img loading="lazy" src="https://brentter.com/notes/Find_Good_RSS_Feeds.webp" alt="Finding New RSS Feeds To Follow">
        
</figure>]]>
               <![CDATA[<h2 id="how-to-start-using-rss-feeds">How To Start Using RSS Feeds</h2>
<p>So I&rsquo;ve been a big supporter of syndication protocols like <a href="https://aboutfeeds.com/" target="_blank">RSS/Atom</a>
 ever since I first stared blogging 20 years ago. It just makes curating content/information super easy. I&rsquo;m not going to get into all the different options out there for you to manage, filter and manipulate your chosen set of feeds because it&rsquo;s a topic I love to talk about and quite frankly could easily be here for weeks turning this into a 20 part 5k word each posting series so instead I&rsquo;ll just name a few of my favorite free ones. For self-hosting your own RSS aggregator it&rsquo;s really hard to do better than <a href="https://freshrss.org/index.html" target="_blank">FreshRSS</a>
 or <a href="https://miniflux.app/" target="_blank">Miniflux</a>
. Both have huge support communities, are open-source, easy to install and use and can handle hundreds of thousands of feeds with minimal system resources.</p>
<p>For using an actual RSS reader application that you can download on your desktop or mobile device I have to go with my new favorite app <a href="https://follow.is" target="_blank">Follow.is</a>
 followed by <a href="https://netnewswire.com/" target="_blank">Netnewswire</a>
 or <a href="https://ravenreader.app/" target="_blank">Raven</a>
. Again, all are free, have good support communities behind them and universally work well regardless of your environment/use-case. I&rsquo;m a big fan of Follow because it&rsquo;s actively being worked on by a team of dedicated developers and includes a bunch of features that you have to pay for to see in other RSS readers (like AI integration, custom actions and filter options, RSSHub integration, etc..). The question I always get asked though is how do I find RSS feeds that are worth subscribing to? There&rsquo;s just so much content out there that sadly it&rsquo;s pretty difficult to filter through all the noise to find what matches what your interests are.</p>
<p><img loading="lazy" src="/notes/feedle_rock_climbing_search.webp" alt="Feedle rock climbing search"  />
</p>
<h2 id="feedle---rss-feed-and-podcast-search-engine">Feedle - RSS Feed and Podcast Search Engine</h2>
<p><a href="https://feedle.world" target="_blank">Feedle</a>
 - A RSS and podcast search engine that does a pretty good job of helping you filter out and discover content sources that are actually about whatever your search term is. Here&rsquo;s an example, let&rsquo;s say that you&rsquo;re like me and enjoy rock climbing in your spare time so you want to read about it when you&rsquo;re stuck on the train headed to work. <a href="https://feedle.world/search?query=rock-climbing" target="_blank">Here&rsquo;s the search results for rock-climbing</a>
 - It&rsquo;s pretty spot on as far as the quality of the posts. Searching a site like google for the same query will bring you nothing but ads and 100% AI generated BS. I like how Feedle displays the search results too - you not only have the basics of the post image, description and direct link but it also includes any associated mastodon links for the author. Two key features is Feedle allows you to filter out any of the content sources you don&rsquo;t want to see in your results as well it will turn any search query into its very own RSS feed! You can then just subscribe to the search query RSS feed and stay up to date with that topic without the pain of having to curate each source for just posts related to what you&rsquo;re looking for. I can&rsquo;t tell you how helpful that feature is when you&rsquo;re just trying to stay up to date with whatever the latest in a new technology is. If you don&rsquo;t have a specific topic in mind you can also click on their Top Stories link to see what other people have been recently been hyping up online.</p>
<p>Feedle also has a few of their very own curated categories that they provide. Two of my favorites are <a href="https://feedle.world/good-news" target="_blank">Good-News</a>
 which are just positive stories from around the internet and <a href="https://feedle.world/digital-crossroads" target="_blank">Digital Crossroads</a>
 which are a mix of updates and news related to the open web/tech.</p>
<p>There&rsquo;s still a few alternative search engines that can help you find content sources like <a href="https://teclis.com/" target="_blank">Teclis</a>
, which was created by the CEO of Kagi and only includes non-commercial sources. It&rsquo;s not really &ldquo;feed-focused&rdquo; though as it acts like a regular search engine just with a filtered index of sites to pull from. For an expansive list of independent/unique search indexes check out this blog post from 2021 on <a href="https://seirdy.one/posts/2021/03/10/search-engines-with-own-indexes/" target="_blank">Seirdy.com</a>
.  Happy hunting!</p>
]]></content:encoded>
    </item>
    <item>
      <title>How To Generate Your Own Spotify Album Poster With Python</title>
      <link>https://brentter.com/notes/spotify_poster_generator_in_python/</link>
      <pubDate>Tue, 17 Dec 2024 18:11:05 -0800</pubDate>
      <guid>https://brentter.com/notes/spotify_poster_generator_in_python/</guid>
      <description>A great open-source album poster generator that uses the Spotify API to pull the cover image. Lots of options, completely free</description><content:encoded><![CDATA[ 
<figure class="entry-cover"><img loading="lazy" src="https://brentter.com/notes/beatprints.webp" alt="The beatprints github repository by TrueMyst">
        
</figure>]]>
               <![CDATA[<h2 id="beatprints-just-released-a-new-big-update-to-their-music-poster-generator">BeatPrints just released a new big update to their Music Poster Generator!</h2>
<p>So over the years I&rsquo;ve been a pretty faithful <a href="https://www.last.fm/user/brentter" target="_blank">Scrobbler</a>
 of the <a href="/music/">music that I listen to</a>
 so that I can not only keep track of new songs or artists that I come across but also because it makes sharing that content a bit easier with others. The problem I&rsquo;ve run into though is how those links look when embedded on different social platforms. For instance a link to a song that is hosted on YouTube will look one way when posted on mastodon and a different way when posted on twitter. In the past I&rsquo;d just chalk it up to the cost of doing business on multiple social networks but now that is a thing of the past! Let me show you this completely free &amp; open source music poster generator that I found on Github named <a href="https://github.com/TrueMyst/BeatPrints" target="_blank">BeatPrints</a>
. It&rsquo;s written in Python and can be used for an individual song or for the entire album. Here&rsquo;s two examples that I made from earlier today: <a href="https://www.youtube.com/watch?v=aG4EcixbNL4" target="_blank">Vincent Neil Emerson&rsquo;s song Manhattan Island Serenade</a>
 is on the left and is using the dark theme with the extra color flair option selected and <a href="https://www.youtube.com/watch?v=p7diMfJ3Qbs" target="_blank">Sierra Ferrell&rsquo;s cover of Years</a>
 is on the right using the light theme as well as the extra color flair option.</p>
<p><img loading="lazy" src="/notes/beatprints_light_vs_dark.webp" alt="BeatPrints in both dark and light themes"  />
</p>
<p>In addition to letting you choose between the light and dark themes it also allows for custom images, the addition or removal of that colorful bar in the middle (that&rsquo;s the extra flair option) and custom lyrics. Installing it on any platform is a cakewalk and setting up the config file is just copy and pasting in your Spotify API key and deciding where you want it to save your images. After it finds what song and album you want to showcase it will then search on Spotify for the lyrics. If it can&rsquo;t find any it will still give you the option to copy and paste in your own. Now I&rsquo;ve down-scaled and optimized the two images you see above but the original files that the script creates are all high quality .PNG files.</p>
<p>Anyhow, it&rsquo;s just a cool new way to showoff your music library. If you&rsquo;d like something similar but without having to install anything, there&rsquo;s a web-based poster generator that  you can also customize a bit called <a href="https://avictormorais.github.io/posterfy/" target="_blank">Posterfy</a>
.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Update On The Free Follow.is RSS Reader - Lots Added!</title>
      <link>https://brentter.com/blog/follow-rss-updates-with-obsidian-email-newsletters-webhooks-and-more/</link>
      <pubDate>Wed, 30 Oct 2024 08:39:05 -0700</pubDate>
      <guid>https://brentter.com/blog/follow-rss-updates-with-obsidian-email-newsletters-webhooks-and-more/</guid>
      <description>Updates to the Follow RSS Aggregator including Obsidian integration, email newsletters, webhooks and user-generated puzzles for access codes</description><content:encoded><![CDATA[ 
<figure class="entry-cover"><img loading="lazy" src="https://brentter.com/blog/follow-updates.webp" alt="Updates To Follow - Now with webhooks, Obsidian integration, email newsletters and invite-code free">
        
</figure>]]>
               <![CDATA[<h2 id="recent-updates-to-followis">Recent Updates to Follow.is</h2>
<p>Here is a much needed update on the latest <a href="/blog/follow-the-best-rss-app-now-in-alpha/">all-in-one RSS tool Follow</a>
 that I first wrote about last September. Since then a bunch of features have been added and the user-base has been increasing like wildfire. They&rsquo;ve added local Obsidian vault integration, the ability to add and view email newsletters, a custom tool to create new RSS feeds for websites or apps that don&rsquo;t currently have one and the addition of external webhooks to your custom Actions. In addition to that there&rsquo;s been a bit of discord shenanigans involving the sharing of invite codes that I&rsquo;ll talk about later on. Without a doubt though the biggest update is that Follow is no longer invite only! Anyone can now join without requiring an invite code to use the app. Just head on over to the <a href="https://follow.is" target="_blank">follow.is website</a>
 and sign up. The catch is while they&rsquo;re still rolling out new features, you&rsquo;re limited in the number of feeds and lists you can add to your account unless you get a user-generated code that unlocks everything. It&rsquo;s the same user-generated code that previously were used for invites so any user who was invited in the past or has already entered a code can generate a new one that they can share every five days (or even sooner if they complete accomplishments).</p>
<h2 id="create-rss-feeds-for-any-site-or-app">Create RSS Feeds For Any Site or App</h2>
<p><img loading="lazy" src="/blog/follow-img/update-CreateRSSfeed.webp" alt="CreateRSSfeed.png"  />
<br>
Now you can take sites or apps that don&rsquo;t have a native RSS feed integration and Follow will extract from it the necessary data based on CSS attributes to create an updated feed you can then not only follow but share with others.</p>
<h2 id="email-newsletter-integration">Email Newsletter Integration</h2>
<p><img loading="lazy" src="/blog/follow-img/update-email_newsletters.webp" alt="Email Newsletter Follow Integration"  />
<br>
If there are certain email-only newsletters you would like to stay on top of you can now give it your very own Follow email address and it will add them to your feed list. Here&rsquo;s an example using the weekly <a href="https://console.dev/" target="_blank">console.dev Developer Tool &amp; Betas newsletter</a>
 (it comes out every Thursday and is a nice little heads up on new sites and tools out there).</p>
<h2 id="actions-now-with-25-more-action">Actions now with 25% more Action!</h2>
<p><img loading="lazy" src="/blog/follow-img/update-actions-webhooks.webp" alt="New Actions Now With Webhooks"  />
<br>
Previously I showed how you can use their Actions function to automatically translate feeds to the language of your choice, well now they&rsquo;ve added the ability for it to trigger outside webhooks. That gives you the opportunity to integrate it into your own application/system whether it&rsquo;s responding to someone mentioning you or your product or even just adding the user to a list of potential customers via your CRM. It&rsquo;s perfect for using with automation services like n8n, Zapier or IFTTT.  Currently I still have it translating certain social feeds (the developers of Follow are primarily Chinese and I like to keep up with their development notes) as well as summarizing certain blogs that can tend to be a bit verbose in length. I probably will fool around with this feature a little more and maybe even set up a webhook to auto-respond to people who link my website on social media with a &ldquo;Thanks for sharing&rdquo; type message.</p>
<h2 id="save-to-obsidian-integration">Save to Obsidian Integration</h2>
<p><img loading="lazy" src="/blog/follow-img/update-obsidian-follow-integration.webp" alt="Save to Obsidian Integration"  />
<br>
For <a href="https://obsidian.md/" target="_blank">Obsidian</a>
 users like myself you can now enable the option to save content from Follow directly to your Obsidian vault just by setting up your vault location under preferences and clicking the &ldquo;Save to Obsidian&rdquo; button while viewing content.</p>
<h2 id="invitation-code-puzzles">Invitation Code Puzzles</h2>
<p>A curious thing started happening with the Follow app since my first post about it. While still in alpha the discord became flooded with requests for invite keys and as a result, certain users decided to have some fun with it and created puzzles of all sorts that once solved, would reveal a fresh key. That quickly caught on and has become an almost daily trend with users posting new puzzles for others to solve. They even created it&rsquo;s own discord channel specifically for code-puzzles. Here&rsquo;s a few that stood out:</p>
<ul>
<li>A user named Tony has been creating MD5 Hash related puzzles for new codes that seem to keep escalating in difficulty with each attempt.
<ul>
<li><img loading="lazy" src="/blog/follow-img/update-md5hash3-tony.webp" alt="md5hash3-tony.png"  />
</li>
<li>The answer for this next one was (gvcmCSYp_V)<br>
<img loading="lazy" src="/blog/follow-img/update-tonymd5challenge_1.webp" alt="tonymd5challenge_1.png"  />
</li>
<li>Here&rsquo;s one where the winner said it only took 30 seconds to solve using hashcat on his 4090 (answer was f2ZCTi_2LV in case you were curious)<br>
<img loading="lazy" src="/blog/follow-img/update-md5hash.webp" alt="md5hash.png"  />
</li>
</ul>
</li>
<li>A user named Cap_stu posted two different <a href="https://en.wikipedia.org/wiki/Steganography" target="_blank">steganography</a>
 based puzzles, this first one I think took the discord the longest out of all the puzzles to finally solve:<br>
<img loading="lazy" src="/blog/follow-img/update-steganographyexample.webp" alt="steganographyexample.png"  />

<ul>
<li>This one was another from cap_stu after it had been solved because so many people wanted to know how it was done. It gives you an example of the amount of effort people have been putting into creating these.<br>
<img loading="lazy" src="/blog/follow-img/update-Visual_braille_to_invite_code.webp" alt="Visual_braille_to_invite_code.png"  />
</li>
</ul>
</li>
<li>A user named Zvezda posted a txt file containing a string of 10000 characters, all 1s and 0s. To solve it you had to divide it into 100 groups of equal characters then when arranged top to bottom with 0 representing black and 1 representing white a QR code with the invite code would appear.<br>
<img loading="lazy" src="/blog/follow-img/update-message-with-1s-and-0s-Zvezda.png" alt="message-with-1s-and-0s-Zvezda.png"  />
<br>
<img loading="lazy" src="/blog/follow-img/update-howtosolve-zvezda.webp" alt="howtosolve-zvezda.png"  />
</li>
<li>Another involved decoding a string through multiple base* decodings best solved using a tool like cyberchef (string-&gt;decode base45-&gt;decode base58-&gt;decode base85-&gt;decode base92-&gt;answer)<br>
<img loading="lazy" src="/blog/follow-img/update-base45-58-85-92-cyberchef.webp" alt="base45-58-85-92-cyberchef.png"  />
</li>
</ul>
<h2 id="follow-is-no-longer-invite-only">Follow Is No Longer Invite Only!</h2>
<p>Follow no longer requires an invite code to be able to join and use the service however it does require one to unlock all the features. Basically you can check out what it is capable of but are limited to only a small number of feeds and lists. Any invite code from a user unlocks it to its full potential so there are still puzzles being posted regularly in the discord. If you&rsquo;re interested feel free to join <a href="https://discord.com/invite/followapp" target="_blank">their discord</a>
 or visit their website at <a href="https://follow.is" target="_blank">follow.is</a>
. I currently maintain two lists on their service (think of them like OPML feeds) that make keeping up with security and homelab news easy that can be found here:<br>
<a href="https://app.follow.is/share/lists/74697356247078912" target="_blank">Homelab Resources - Host your own anything!</a>
<br>
and<br>
<a href="https://app.follow.is/share/lists/60836830640691200" target="_blank">Security Updates/Blogs</a>
</p>
<p>Give them a follow (pun intended) if you&rsquo;re interested in either topic or just follow me directly here: <a href="https://app.follow.is/share/users/Brentter" target="_blank">brentter@follow</a>
</p>
]]></content:encoded>
    </item>
    <item>
      <title>Venv or Virtualenv? A Short Guide To Virtual Environments</title>
      <link>https://brentter.com/notes/venv-or-virtualenv-whats-the-difference/</link>
      <pubDate>Thu, 24 Oct 2024 05:05:57 -0700</pubDate>
      <guid>https://brentter.com/notes/venv-or-virtualenv-whats-the-difference/</guid>
      <description>A quick tutorial on the difference between Python virtual environments Venv and Virtualenv and why it matters which one you use on your next project</description><content:encoded><![CDATA[ 
<figure class="entry-cover"><img loading="lazy" src="https://brentter.com/notes/Venv-or-virtualenv.jpg" alt="Venv or Virtualenv What&#39;s the Difference">
        
</figure>]]>
               <![CDATA[<h2 id="venv-or-virtualenv-which-should-i-use">Venv or Virtualenv? Which Should I Use?</h2>
<p>Venv and Virtualenv are two of the most commonly used virtual environments for the Python language. Both have been integrated into the Python core since version 3.3 so using them is pretty much a piece of cake. While there is a difference between the two, they actually are made to work together. Which one you&rsquo;ll use all depends on what your needs are. Do you need to use a different version of python for that one project that you don&rsquo;t use anywhere else or do you just want to keep dependencies in sync?</p>
<h2 id="why-use-a-virtual-environment-for-python">Why Use a Virtual Environment for Python?</h2>
<p>So I&rsquo;ve been asked this a few times in the past by folks who read in various guides to always use a virtual environment with python, especially if it&rsquo;s something you&rsquo;ve downloaded off from GitHub. Now that advice doesn&rsquo;t have the same magnitude of outcomes as when you were told to always wear a rubber but it can still cause some major frustration and headaches if not followed. The reason to always use a virtual environment is to keep both the project and your working environment synced up in terms of version control. Many times you&rsquo;ll find code you&rsquo;d like to implement on your own system but it uses deprecated or out of date dependencies and either you have to go through the hassle of downgrading everything, update the code to work with what you have, or start over. There are a couple different virtual environments but honestly the two easiest to implement are Venv and Virtualenv.</p>
<h2 id="venv---the-built-in-python-virtual-environment">Venv - The Built-in Python Virtual Environment</h2>
<p>So venv is a virtual environment that&rsquo;s been built-into Python since version 3.3. So unless you&rsquo;re using a very outdated setup you should already have it installed. It allows you to create an isolated Python environment using the current version of python you have installed. Each environment uses it&rsquo;s own Python binary and an independent set of Python packages. It&rsquo;s pretty fast and easy to use and setup and allows you to keep projects of all different types in-sync and usable. Here&rsquo;s how to use it:</p>
<p>Create your new virtual environment by going to the root directory of your project and issuing the following command (i&rsquo;m assuming you&rsquo;re using some form of linux):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">python3 -m venv .myproject
</span></span></code></pre></div><p>This will create a virtual environment in a new folder named .myproject (rename to whatever you want).</p>
<p>Now it&rsquo;s installed, we need to activate it for it to work. Do that by using:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">source</span> .myproject/bin/activate
</span></span></code></pre></div><p>Your prompt will usually have a new character/notification on it when you&rsquo;re in a virtual environment (at least in the majority of terminal setups) but you can always test it by typing</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">which python
</span></span></code></pre></div><p>It should return with the current file-path with .myproject/bin/python at the end</p>
<p>Now you can install whatever packages you want and it won&rsquo;t affect the rest of your system. Easiest way to do that is with pip. Here are a few examples:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">python3 -m pip install requests
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">#for specific versions of packages:</span>
</span></span><span class="line"><span class="cl">python3 -m pip install <span class="s1">&#39;requests=2.11.2&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">#from a local archive</span>
</span></span><span class="line"><span class="cl">python3 -m pip install requests-2.24.1.tar.gz
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">#if you&#39;re in the directory of the source code for a package you need to install use:</span>
</span></span><span class="line"><span class="cl">python3 -m pip install .
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">#If there&#39;s a requirements.txt file you can install all of those with:</span>
</span></span><span class="line"><span class="cl">python3 -m pip install -r requirements.txt
</span></span></code></pre></div><p>To export a list of all installed packages for that current virtual environment you can use freeze and it will show you both the package and current version:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">python3 -m pip freeze
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">#or to have it write it&#39;s own requirements.txt file:</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">python3 -m pip freeze &gt; requirements.txt 
</span></span></code></pre></div><p>to deactivate it when you&rsquo;re done all you have to type is:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">deactivate
</span></span></code></pre></div><h2 id="virtualenv-python-virtual-environment">Virtualenv Python Virtual Environment</h2>
<p>Virtualenv is similar to venv, also standard in python since version 3.3, but the main difference is it allows you to create a virtual environment using different versions of Python. It works by being installed as a subset of the actual venv command so once installed it&rsquo;s usage is almost identical except in how you create your environment.</p>
<p>First install virtualenv with pip:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">pip install virtualenv
</span></span></code></pre></div><p>Now let&rsquo;s go to the root folder of our project and run:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">python<span class="o">(</span>version<span class="o">)</span> -m venv my-virtual-environment-name-here
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">#so like this:</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">python3.11 -m venv myproject
</span></span></code></pre></div><p>Now just like with venv it will create a virtual environment in your folder under .myproject/bin/</p>
<p>Activate it (to start it) with:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">source</span> .myproject/bin/activate
</span></span></code></pre></div><p>Installing dependencies is the same as it was with venv so for a bunch of examples just scroll up to the previous section.</p>
<p>End it by using deactivate:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">deactivate
</span></span></code></pre></div><h2 id="notes">Notes</h2>
<p>Make sure that regardless of what virtual environment you end up using to always have your .venv (or in our example: .myproject/) folder added to your .gitignore file. Otherwise your repo will download all the python files with it.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Burger Kings Angry Whopper And Facebook Debauchery</title>
      <link>https://brentter.com/blog/burger-kings-angry-whopper-facebook-debauchery/</link>
      <pubDate>Sun, 20 Oct 2024 00:03:29 -0700</pubDate>
      <guid>https://brentter.com/blog/burger-kings-angry-whopper-facebook-debauchery/</guid>
      <description>We revisit Burger Kings launch of their 2009 Angry Whopper campaign as it was nothing short of being one of the most memorable advertising campaigns</description><content:encoded><![CDATA[ 
<figure class="entry-cover"><img loading="lazy" src="https://brentter.com/blog/angrywhopper-img/bk-angriestwhopper.webp" alt="Burger Kings Angry Whopper & Facebook Debauchery">
        
</figure>]]>
               <![CDATA[<h2 id="burger-kings-angry-whopper--facebook-debauchery">Burger King&rsquo;s Angry Whopper &amp; Facebook Debauchery</h2>
<p>Burger King&rsquo;s launch of their new Angry Whopper in 2009 changed social media advertising forever. They&rsquo;d already shown they could pull off a popular viral website idea with The Subservient Chicken but what they did next for The Angry Whopper was nothing short of insanity when you realize it was on behalf of a fortune 100 client. This campaign showed there was nowhere online you could go where advertisers wouldn&rsquo;t be pulling out all the stops to get you to engage.</p>


    
    <div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/muyS-C247Qc?autoplay=0&controls=1&end=0&loop=0&mute=0&start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"
      ></iframe>
    </div>

<h2 id="burger-kings-angry-whopper">Burger King&rsquo;s Angry Whopper</h2>
<p>The original version of the Angry whopper sold back when it was first released in 2009 was just your regular Whopper but with spicy onions, jalapeños, pepper jack cheese and a spicy &ldquo;Angry Sauce.&rdquo; As with everything from that era of Crispin Porter + Bogusky (just <a href="https://www.crispin.com/" target="_blank">Crispin</a>
 now) it had to be pulled off in an extreme way. For 2009 that meant the launch of both a website (which is long dead), angry-gram.com  as well a Facebook campaign that had them temporarily banned from the platform.</p>
<p><img loading="lazy" src="/blog/angrywhopper-img/bk-whoppersacrifice.jpg" alt="The Whopper Sacrifice"  />
</p>
<h2 id="the-facebook-debauchery">The Facebook Debauchery</h2>
<p>This Facebook campaign required you to unfriend 10 people through their app (who would be notified that you unfriended them for a burger) and they would reward you with a coupon for a free Angry Whopper. It was so popular that it scared the higher-ups at Facebook and they forcibly <a href="https://www.joelakaplan.com/whopper-sacrifice" target="_blank">took it down</a>
 after only 10 days for violating it&rsquo;s user agreement. The app alone received 13 million media impressions while it was active and jumped to 32 million after it had just a landing page guiding you to the angry-gram website.</p>
<p><img loading="lazy" src="/blog/angrywhopper-img/bk-whoppersacrifice2.jpg" alt="The Whopper Sacrifice Has Been Sacrificed"  />
<br>
Over 230k people had been unfriended and it was more popular as a taboo, now deleted app than it was when it was actually live. They were the first advertising agency to figure out how to milk that platform for views and that was by showing just how little people cared about who was on their friend-list and how easily you could game the system. Before this they had introduced us to how to create an internet phenomenon with <a href="https://thebrandhopper.com/2024/03/12/case-study-burger-king-subservient-chicken-brand-campaign/" target="_blank">The Subservient Chicken</a>
 with the site gaining almost 400 million hits within a year (average time spent per user on site was 7-8 minutes!). They also created a few other off-color online properties like WhopperFreakout and WhopperVirgins, both sites getting a quarter of a million views each in the first month alone(both sites are also dead so no link). They broke the mold on how to gain consumer engagement online.</p>
<p><img loading="lazy" src="/blog/angrywhopper-img/bk-angry-whopper-googleads2009.jpg" alt="Angry Whopper Google Ad Buys"  />
</p>
<h2 id="the-google-ad-campaign">The Google Ad Campaign</h2>
<p>Creating games and fun commercials wasn&rsquo;t the whole campaign though. They also purchased top ranking positions for various keywords related to not only the Angry Whopper but also numerous other high-priced search phrases and competitor products. They undoubtedly were having to increase their ad buy daily as competitors moved to try and outrank the BK ads. The combined strategy left you very few places on the internet where you wouldn&rsquo;t come across some sort of Burger King related media. This was probably one of the more expensive PPC buys for any brand at the time, let alone a fast-food restaurant.</p>
<h2 id="my-initial-reaction">My Initial Reaction</h2>
<p>When I first wrote about this back in 2009 I said the following towards the end of my little write-up:</p>
<blockquote>
<p>It combines word-of-mouth marketing with social media in a way that encourages users to spread the application across their online communities. It&rsquo;ll be interesting to see the analytics on how well this application does in a month or so.</p>
</blockquote>
<p>A couple of months later I remember talking with a good friend about the metrics/analytics they reported and he put it perfectly, &ldquo;If <a href="https://en.wikipedia.org/wiki/Alex_Bogusky" target="_blank">Bogusky</a>
 comes to you with an idea, sign off on it immediately.&rdquo; Boy was that the truth. By the time of this campaign he&rsquo;d already been inducted to the <a href="https://www.aaf.org/" target="_blank">American Advertising Federations Hall of Achievement</a>
, received an honorary doctorate from the University of Colorado Boulder and was crowned the Creative Director of the Decade by Adweek (In addition to winning just about every award out there in the industry). What was left to do? Easy, he &ldquo;retired&rdquo; from advertising and moved to Boulder, CO to become a consumer advocate in 2010. He&rsquo;d eventually return to CP+B for a short stint in 2018 only to retire yet again in 2020. Nowadays you can occasionally catch him on the &ldquo;<a href="http://www.youtube.com/@JoyOfBike" target="_blank">Joy of Bike</a>
&rdquo; YouTube channel helping connect kids with bikes &amp; teaching us &ldquo;Over 40s&rdquo; how to ride fast. Go subscribe and if you get a chance to hear him speak, I&rsquo;d recommend it as he has a fascinating take on life.</p>


    
    <div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/CNXp2rZfdhg?autoplay=0&controls=1&end=0&loop=0&mute=0&start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"
      ></iframe>
    </div>

<h2 id="a-not-so-spicy-end">A Not So Spicy End</h2>
<p>They launched the Angry Whopper internationally with various TV commercials to back up how uncontrollably spicy it supposedly was.  I remember trying the burger back when they first came out because I&rsquo;m a big fan of spicy food and may have eagerly dropped ten friends to get one for free (I can&rsquo;t recall). I&rsquo;m sorry to report though, this was quite literally a nothing-burger in terms of how spicy it was.  After the initial media blitz and charm wore off and negative reports started coming in via food reviewers they eventually took it off the menu. At least, for a few years that is.</p>
<p><img loading="lazy" src="/blog/angrywhopper-img/bk-angry-gram.png" alt="The Angry-Gram"  />
</p>
<h2 id="attempts-at-bringing-the-angry-whopper-back">Attempts at Bringing The Angry Whopper Back</h2>
<p>Burger King has attempted to bring back the Angry Whopper multiple times throughout the past decade, notably in 2012, 2013, 2016 and <a href="https://www.fastfoodpost.com/burger-king-angry-whopper-return/" target="_blank">2019</a>
  all featuring their own over-the-top commercials. While they never achieved as great of a social &ldquo;buzz&rdquo; as they did with the first campaign in 2009, they did manage to create a <a href="https://www.change.org/p/burger-king-bring-back-the-angry-whopper-to-canada-2" target="_blank">mini-cult following</a>
. While I wouldn&rsquo;t be surprised if that was actually just another brand-sponsored astoturf campaign you have to admire their tenacity. They even tried renaming it to the Angriest Whooper, which also didn&rsquo;t pan so well in terms of taste and consumer reception.</p>
<h2 id="is-the-angry-whopper-ever-coming-back">Is The Angry Whopper Ever Coming Back?</h2>
<p>All signs point to yes. They recently <a href="https://www.thetakeout.com/burger-king-angry-whopper-where-to-buy-columbus-oh-2023-1850867414/" target="_blank">brought it back</a>
 late last 2023 but only in the Columbus, Ohio area and some parts of Canada. In case you were unaware, Columbus is <a href="https://www.npr.org/2009/10/14/113795356/columbus-ohio-test-city-usa" target="_blank">ground zero for fast food</a>
 companies testing new products. A mixture of the large size of the city added with the fact it&rsquo;s right in the center of the Midwest, then you top that off with the prolific number of colleges in the area and you&rsquo;ll understand why.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Dos Equis - The Most Interesting Man In The World Revisited</title>
      <link>https://brentter.com/blog/dos-equis-the-most-interesting-man-revisited/</link>
      <pubDate>Mon, 14 Oct 2024 00:03:29 -0700</pubDate>
      <guid>https://brentter.com/blog/dos-equis-the-most-interesting-man-revisited/</guid>
      <description>We revisit the old Dos Equis - The Most Interesting Man In the World ad campaign with a look back at it&amp;#39;s incredible lifespan of almost a decade.</description><content:encoded><![CDATA[ 
<figure class="entry-cover"><img loading="lazy" src="https://brentter.com/blog/dosequismostinterestingman.webp" alt="Dos Equis Most Interesting Man Campaign And Brentter.com">
        
</figure>]]>
               <![CDATA[<p>The Dos Equis - Most Interesting Man In the World advertising campaign first launched back in late 2006 and was an instant success propelling both the actor who played the title role and the beer brand into international fame. This hilarious international campaign for the brand would continue for almost a decade.</p>


    
    <div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/WGcHnD5qpUI?autoplay=0&controls=1&end=0&loop=0&mute=0&start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="Dos Equis Most Interesting Man In The World"
      ></iframe>
    </div>

<h2 id="who-was-the-dos-equis-most-interesting-man-in-the-world">Who Was the Dos Equis Most Interesting Man In the World?</h2>
<p>In 2006 ad agency Euro RSCG/NYC (now <a href="https://www.havas.com/" target="_blank">Havas Worldwide</a>
) launched an advertising campaign on behalf of the <a href="https://en.wikipedia.org/wiki/Cuauht%C3%A9moc_Moctezuma_Brewery" target="_blank">Cuauhtémoc Moctezuma Brewery</a>
 for their Dos Equis brand named &ldquo;The Most Interesting Man In The World.&rdquo; A quick insert though - the history behind this brewery, which goes all the way back to 1890, is a fascinating read so I&rsquo;ve linked their Wikipedia page and encourage you to check it out. This new campaign featured a handsome, grey-bearded actor named <a href="https://adage.com/article/behind-the-work/story-dos-equis-interesting-man-world/233112" target="_blank">Jonathan Goldsmith</a>
 who played the title role for almost a decade (ending with him boarding a <a href="https://www.youtube.com/watch?v=79cw6K0AjIg" target="_blank">space ship headed to Mars</a>
 in 2016). The entire campaign was based around the epic adventures and pearls of wisdom of the man which the narrator, voiced by Will Lyman from Frontline fame, would describe always followed by Goldsmiths character delivering his now famous line, &ldquo;I don&rsquo;t always drink beer, But when I do, I prefer Dos Equis.&rdquo; It would then feature a nod or toast from him and the new brand slogan &ldquo;Stay thirsty, my friends.&rdquo; Later in the campaign that last line would sometimes be changed to &ldquo;Stay thirsty, mis amigos.&rdquo; It was an wildly popular series leading to over a decade of TV and print ads in addition to hundreds of radio spots all featuring the classic Chuck Norris/Bill Brasky style of setup to further the lore of their new brand spokesman. Here are a few of my personal favorites from the radio series below:</p>
<blockquote>
<p><em>&ldquo;My words carry weight that would break a less interesting mans jaw&rdquo;</em></p>
</blockquote>
<blockquote>
<p><em>&ldquo;I once had an awkward moment, just to see how it feels.&rdquo;</em></p>
</blockquote>
<blockquote>
<p><em>&ldquo;If opportunity knocks, and I am not at home, opportunity waits.&rdquo;</em></p>
</blockquote>
<blockquote>
<p><em>&ldquo;My pillow is cool on BOTH sides.&rdquo;</em></p>
</blockquote>
<blockquote>
<p><em>&ldquo;I once received a standing ovation from a jurors box.&rdquo;</em></p>
</blockquote>
<h2 id="a-little-info-behind-the-initial-campaign">A Little Info Behind the Initial Campaign</h2>
<p>So this campaign was brought to the world by EURO RSCG/NewYork&rsquo;s Creative Director at the time named Jeff Kling along with Steve Miller from <a href="https://www.radicalmedia.com/" target="_blank">Radical Media</a>
 as the director. <a href="https://outsidereditorial.com/" target="_blank">Outside Editorial</a>
 was tasked with the post work for the first batch of ads. It was widely talked about, achieved a few awards over the years and apparently they had brainstormed over a thousand radio spots before narrowing them down to only the few hundred+ that actually made it on air. When they launched the campaign they also had a website aptly titled staythirstymyfriends.com (now offline) where you could &ldquo;test your skill in patience and strength&rdquo; by digitally arm-wrestling Churchill, Stalin, Mao or even the Most Interesting Man himself. It had a bunch of other funny on-brand features like a photo gallery and application for a job as his personal assistant. It was pretty clever, especially for 2006. They would continue on for almost ten years with this campaign until replacing Goldsmith with a much younger French actor named Augustin Legrand. While the campaign had definitely run its course by then, swapping out the actors resulted in another two or so years before we finally heard the last of him. Here was their intro commercial for Legrand, my favorite part was him running through the streets with a supermodel in one hand while cradling a pig wearing a fez hat in the other.</p>

 
     
     <div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
       <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/xO0kvoGKG-E?autoplay=0&controls=1&end=0&loop=0&mute=0&start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"
       ></iframe>
     </div>

<h2 id="the-reaction-from-the-public">The Reaction From The Public</h2>
<p>It&rsquo;s crazy that this was even talked about so widely as it was back then but some people really seemed to feel like the beer brand had done Goldsmith a disservice by not ending the campaign when they sent him off to Mars. At least that&rsquo;s what we&rsquo;d hear on late night television and talk radio shows for weeks following the final commercial. All the drama surrounding the replacement would eventually lead to a series of high-profile interviews where the actor seemed to to just laugh about the whole ordeal and said it was time to move on. Even though he was no longer the official most interesting man in the world that doesn&rsquo;t mean he&rsquo;s no longer acting. From the looks of his <a href="https://www.imdb.com/name/nm0326091" target="_blank">IMDB page</a>
 he still gets cameo roles in popular tv dramas, which honestly isn&rsquo;t that bad of a gig.</p>


    
    <div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/5TGlMqZRe3U?autoplay=0&controls=1&end=0&loop=0&mute=0&start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"
      ></iframe>
    </div>

<h2 id="so-why-where-you-posting-tv-ads-on-brenttercom">So Why Where You Posting TV Ads On Brentter.com?</h2>
<p>So at the time this was one of the few independent advertising blogs on the web and being in the industry myself I would spent countless hours in my spare time reaching out to other agencies and production houses with the promise that if they emailed me whatever upcoming campaigns they were working on I would make a post about it. Better yet, if they included the media content as well as the bio/list of those involved, I&rsquo;d post it in it&rsquo;s entirety. At the time, listing the entire group of people who worked on any given campaign was somewhat rare, as the advertising agency or just their Creative Director would typically take the credit as a whole. My pitch provided anyone that worked on the project, whether from the production agency that filmed and edited the ad to the copywriter from the ad agency that conceptualized it got credit. It was something they could link to their friends or family that would prove they actually worked on the piece. That ended up working pretty well and the Dos Equis - The Most Interesting Man In The World campaign was one of the emails I received with links to download the first few ads.</p>
<h2 id="so-what-happened-to-the-old-blog">So What Happened To The Old Blog?</h2>
<p>In those days I would host all the videos at this fairly new startup that featured unlimited storage for streaming videos. Then one day they finally emailed everyone to let us all know they&rsquo;d been hiding their financial situation and that they were broke. Worse, they had been so late on their hosting bill that all their servers were finally taken offline. All my videos, aka my content, was now offline. That was big learning experience for me in many ways. First, don&rsquo;t trust only one service to host everything that brings visitors (and money) to your site. Second, if there&rsquo;s a free tier, that means you&rsquo;re paying in some other way. If you&rsquo;re not, then they&rsquo;re losing in the worst of ways and you might one day find they&rsquo;ve had their severs repossessed too. Third, always prepare for the worst with backup plans and backed up data.</p>
<h2 id="so-why-revisit-the-dos-equis-campaign-now">So Why Revisit The Dos Equis Campaign Now?</h2>
<p>With the amount of backlinks I had back then there was always going to be a source that was not actively check their own outgoing links for dead-ends (which everyone should do every now and then). That or the owners of the website had abandoned it or quite frankly could just be too lazy to care. Now I first noticed this the other week when I realized some of the old ad campaign pages were getting a few hits from sources that didn&rsquo;t seem like they were bots. The only problem was they were all getting a 404 page because none of those links actually still exist. This post isn&rsquo;t just a cheap seo hit-job though. If I wanted that I&rsquo;d put the whole site back online. Back when I was writing all those articles I really did love the industry and still have a soft spot for creative marketing. In order to send any future visitors to the correct new URLs I set up a 301 redirect to this page and for the other links it will send them to my homepage. Backlinks and 301 redirects, still going strong all these years. If you are interested in learning a little bit more about how <a href="/blog/easy-301-redirects-for-seo/">easy 301 redirects</a>
 are to setup and what they can (or sometimes can&rsquo;t) do for you in terms of boosting your website SEO, I actually wrote a post about it when I noticed all the old traffic. Check it out if you&rsquo;re curious.</p>
<p>Until next time, Stay Thirsty My Friends.<br>
~Brent</p>
]]></content:encoded>
    </item>
    <item>
      <title>Easy 301 Redirects To Help With SEO - The Master Guide</title>
      <link>https://brentter.com/blog/easy-301-redirects-for-seo/</link>
      <pubDate>Sun, 13 Oct 2024 00:03:29 -0700</pubDate>
      <guid>https://brentter.com/blog/easy-301-redirects-for-seo/</guid>
      <description>A guide to implementing easy 301 redirects to send traffic from one link or website to another. Boost your website SEO and do it the right way!</description><content:encoded><![CDATA[ 
<figure class="entry-cover"><img loading="lazy" src="https://brentter.com/blog/easy301-img/easy301_redirects.webp" alt="Easy 301 Redirects For SEO">
        
</figure>]]>
               <![CDATA[<h2 id="what-is-a-301-redirect-and-why-would-i-need-one">What Is A 301 Redirect and Why Would I Need One?</h2>
<p>A 301 redirect is a permanent redirect from one URL to another. 301 is the HTTP status code given that tells browsers (and search engines) that this content is now located over at a new address. In addition to the notice it will reload the browser with the new URL. When a 301 redirect is implemented, it passes most of the link equity (ranking power on search engines) along with it to the new URL. At least most of the time that is the case. Now obviously this is something that comes in handy when moving to a new domain or swapping over your blog posts from  one service to another but another popular usage of it is when websites with quality backlinks go for sale or their domains end up in auction. I talk a little more about that later on in this post.</p>
<h2 id="why-choose-a-301-redirect">Why Choose a 301 Redirect?</h2>
<p>You should use a 301 redirect when you want to permanently move a page or an entire website to a new URL. It&rsquo;s the best choice for SEO because it transfers most of the link equity to the new page. In contrast, a 302 redirect is temporary and doesn&rsquo;t pass as much link equity. Choose a 301 redirect when:</p>
<ul>
<li>You&rsquo;re permanently changing your URL structure.</li>
<li>You&rsquo;re moving to a new domain</li>
<li>You&rsquo;re consolidating multiple pages or websites</li>
</ul>
<p><img loading="lazy" src="/blog/easy301-img/webserver.webp" alt="Easy 301 Redirects To Your Web Server"  />
</p>
<h2 id="easy-301-redirects-with-your-web-server">Easy 301 Redirects With Your Web Server</h2>
<p>So for all the different sources you can use to implement a 301 redirect, I will show you the same three different example scenarios so you can get a good idea of what you need to customize for your own needs.</p>
<h3 id="nginx">Nginx</h3>
<p>For Nginx, you&rsquo;ll need to modify your server block in the Nginx configuration file (usually located at /etc/nginx/nginx.conf or /etc/nginx/sites-available/default).</p>
<p>Scenario 1: Updating a single URL on the same domain</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">server {
</span></span><span class="line"><span class="cl">    ...
</span></span><span class="line"><span class="cl">    rewrite ^/blogone$ /blog/blogone permanent;
</span></span><span class="line"><span class="cl">    ...
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>Scenario 2: Redirecting to a new domain</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">server {
</span></span><span class="line"><span class="cl">    ...
</span></span><span class="line"><span class="cl">    rewrite ^/blogone$ https://example22.com/blogone permanent;
</span></span><span class="line"><span class="cl">    ...
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>Scenario 3: Redirecting multiple pages to another domain&rsquo;s root</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">server {
</span></span><span class="line"><span class="cl">    ...
</span></span><span class="line"><span class="cl">    rewrite ^/blog/(.*)$ https://example22.com permanent;
</span></span><span class="line"><span class="cl">    ...
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>After making any changes always test your nginx config files before reloading nginx:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="n">nginx</span> <span class="o">-</span><span class="n">t</span>
</span></span><span class="line"><span class="cl"><span class="n">sudo</span> <span class="n">systemctl</span> <span class="n">reload</span> <span class="n">nginx</span>
</span></span></code></pre></div><h3 id="apache">Apache</h3>
<p>For Apache we&rsquo;re going to be creating a file name &lsquo;.htaccess&rsquo; and placing it in our websites root directory. Make sure it has the period in front of the name.</p>
<p>Scenario 1: Updating a single URL on the same domain</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">RewriteEngine On
</span></span><span class="line"><span class="cl">RewriteRule ^blogone$ /blog/blogone [R=301,L]
</span></span></code></pre></div><p>Scenario 2: Redirecting to a new domain</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">RewriteEngine On
</span></span><span class="line"><span class="cl">RewriteRule ^blogone$ https://example22.com/blogone [R=301,L]
</span></span></code></pre></div><p>Scenario 3: Redirecting multiple pages to another domain&rsquo;s root</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">RewriteEngine On
</span></span><span class="line"><span class="cl">RewriteRule ^blog/(.*)$ https://example22.com [R=301,L]
</span></span></code></pre></div><p>After making changes you might need to restart apache.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">sudo systemctl restart apache2 
</span></span></code></pre></div><p><img loading="lazy" src="/blog/easy301-img/programming.webp" alt="Easy 301 Redirects Using Programming"  />
</p>
<h2 id="easy-301-redirects-using-programming">Easy 301 Redirects Using Programming</h2>
<h3 id="php">PHP</h3>
<p>PHP redirects are server-side and can be implemented as follows:<br>
Scenario 1: Updating a single URL</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">&lt;?php
</span></span><span class="line"><span class="cl">if ($_SERVER[&#39;REQUEST_URI&#39;] === &#39;/blogone&#39;) {
</span></span><span class="line"><span class="cl">    header(&#34;HTTP/1.1 301 Moved Permanently&#34;);
</span></span><span class="line"><span class="cl">    header(&#34;Location: /blog/blogone&#34;);
</span></span><span class="line"><span class="cl">    exit();
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">?&gt;
</span></span></code></pre></div><p>Scenario 2: Redirecting to a new domain</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">&lt;?php
</span></span><span class="line"><span class="cl">if ($_SERVER[&#39;REQUEST_URI&#39;] === &#39;/blogone&#39;) {
</span></span><span class="line"><span class="cl">    header(&#34;HTTP/1.1 301 Moved Permanently&#34;);
</span></span><span class="line"><span class="cl">    header(&#34;Location: https://example22.com/blogone&#34;);
</span></span><span class="line"><span class="cl">    exit();
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">?&gt;
</span></span></code></pre></div><p>Scenario 3: Redirecting multiple pages to another domain&rsquo;s root</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">&lt;?php
</span></span><span class="line"><span class="cl">if (strpos($_SERVER[&#39;REQUEST_URI&#39;], &#39;/blog/&#39;) === 0) {
</span></span><span class="line"><span class="cl">    header(&#34;HTTP/1.1 301 Moved Permanently&#34;);
</span></span><span class="line"><span class="cl">    header(&#34;Location: https://example22.com&#34;);
</span></span><span class="line"><span class="cl">    exit();
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">?&gt;
</span></span></code></pre></div><h3 id="javascript">Javascript</h3>
<p>So I&rsquo;m including this next one because I&rsquo;ve seen this in practice before but it really isn&rsquo;t going to be recommended unless it&rsquo;s your only choice. This is because sites like Google claim their web-crawlers don&rsquo;t read javascript.</p>
<p>Scenario 1: Updating a single URL on the same domain</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">&lt;script&gt;
</span></span><span class="line"><span class="cl">if (window.location.pathname === &#39;/blogone&#39;) {
</span></span><span class="line"><span class="cl">    window.location.replace(&#39;/blog/blogone&#39;);
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">&lt;/script&gt;
</span></span></code></pre></div><p>Scenario 2: Redirecting to a new domain</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">&lt;script&gt;
</span></span><span class="line"><span class="cl">if (window.location.pathname === &#39;/blogone&#39;) {
</span></span><span class="line"><span class="cl">    window.location.replace(&#39;https://example22.com/blogone&#39;);
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">&lt;/script&gt;
</span></span></code></pre></div><p>Scenario 3: Redirecting multiple pages to another domain&rsquo;s root</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">&lt;script&gt;
</span></span><span class="line"><span class="cl">if (window.location.pathname.startsWith(&#39;/blog/&#39;)) {
</span></span><span class="line"><span class="cl">    window.location.replace(&#39;https://example22.com&#39;);
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">&lt;/script&gt;
</span></span></code></pre></div><h3 id="html-meta-refresh">HTML Meta Refresh</h3>
<p>So this is the old school way from back when you didn&rsquo;t have access to making new files in the root directory or even seeing what the server config looked like. It&rsquo;s not recommended if you have any other options but it will still work to an extent.</p>
<p>Here&rsquo;s a few examples of the redirect, it&rsquo;s pretty basic.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">&lt;meta http-equiv=&#34;refresh&#34; content=&#34;0; URL=&#39;/blog/blogone&#39;&#34; /&gt;
</span></span><span class="line"><span class="cl">&lt;meta http-equiv=&#34;refresh&#34; content=&#34;0; URL=&#39;https://example22.com/blogone&#39;&#34; /&gt;
</span></span><span class="line"><span class="cl">&lt;meta http-equiv=&#34;refresh&#34; content=&#34;0; URL=&#39;https://example22.com&#39;&#34; /&gt;
</span></span></code></pre></div><p><img loading="lazy" src="/blog/easy301-img/blogs.webp" alt="Easy 301 To Your Blog or CMS"  />
</p>
<h2 id="easy-301-redirects-to-your-blogcms">Easy 301 Redirects To Your Blog/CMS</h2>
<p>This is if you&rsquo;d rather have your blog or CMS system handle it for you.</p>
<h3 id="wordpress">WordPress</h3>
<p>Here are three completely free plugins that are all geared toward different types of 301 requirements.</p>
<p><a href="https://wordpress.org/plugins/simple-website-redirect/" target="_blank">Simple Website Redirection</a>
<br>
This plugin was designed for one single purpose - to redirect the entire website (except the WordPress back-end) to somewhere else. You can also have it use a 302 redirect (temporary) to test your settings before switching it to a permanent 301.</p>
<p><a href="https://wordpress.org/plugins/redirection/" target="_blank">Redirection</a>
<br>
This is probably the most powerful of all the redirection plugins that are completely free. It was designed to be able to handle and remember thousands of different redirects and includes the ability to customize them all. From single pages to full directories it also has the ability to use regular expressions so you can create dynamic redirect patterns that it will follow. A cool feature on this one is it keeps track of every redirected visitor (ip/geo/referrer/header/browser) and allows you to also set redirection rules based on who is trying to access that page. Redirect only users with certain countries, browsers, referrers, if even if they have a certain cookie. It also has WP CLI enabled for automating the entire thing!</p>
<h3 id="ghost">Ghost</h3>
<p>You&rsquo;ll first need to go into your admin panel to Settings then click on Labs. Under Redirects click the &ldquo;Download current redirects&rdquo; link and you should get a file named redirects.yaml. It has two types of redirects and they work as list headers, 301 and 302. Once you&rsquo;ve edited the file all you have to do is go back to where you downloaded it and upload your new one. Removing the redirects is as simple as uploading an empty .redirects.yaml file.</p>
<p>Scenario 1: Updating a single URL on the same domain</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">301</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">/old-postname/</span><span class="p">:</span><span class="w"> </span><span class="l">/new-postame/</span><span class="w">
</span></span></span></code></pre></div><p>Scenario 2: Redirecting to a new domain</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">301</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">domain.com/post-slug/</span><span class="p">:</span><span class="w"> </span><span class="l">domain22.com/post-slug/</span><span class="w">
</span></span></span></code></pre></div><p>Scenario 3: Using regex to redirect multiple pages while preserving the post-slug. So instead of writing each one out like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">301</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">/article/article-1</span><span class="p">:</span><span class="w"> </span><span class="l">/article-1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">/article/article-2</span><span class="p">:</span><span class="w"> </span><span class="l">/article-2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">/article/article-3</span><span class="p">:</span><span class="w"> </span><span class="l">/article-3</span><span class="w">
</span></span></span></code></pre></div><p>You could just have this one regex line and it would cover them all.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">301</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">^\/article\/([a-z0-9-]+)\/$</span><span class="p">:</span><span class="w"> </span><span class="l">/$1</span><span class="w">
</span></span></span></code></pre></div><p>So it looks for the article directory and matches with whatever follows. The $1 is the article-1.</p>
<h3 id="hugo">Hugo</h3>
<p>So built-in to the core of hugo is a page method called <a href="https://gohugo.io/content-management/urls/#aliases" target="_blank">Aliases</a>
. To be completely honest I don&rsquo;t think it was meant for any of the use case scenarios we&rsquo;ve been coming up with as solutions for in this post. Luckily, as with everything in Hugo, you can override it by creating  your own version in your layouts folder. So this was taken from [Nick Herrig] (<a href="https://nickherrig.com/posts/hugo-external-redirects/" target="_blank">https://nickherrig.com/posts/hugo-external-redirects/</a>
) who did the leg work for us on this one.</p>
<p>So this is the original <a href="https://github.com/gohugoio/hugo/blob/master/tpl/tplimpl/embedded/templates/alias.html" target="_blank">alias template</a>
 that Hugo uses. We&rsquo;re not going to use this but I wanted you to see how it uses the .Permalink and url variables.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="cp">&lt;!DOCTYPE html&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">html</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;{{ site.Language.LanguageCode }}&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>{{ .Permalink }}<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;canonical&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;{{ .Permalink }}&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;robots&#34;</span> <span class="na">content</span><span class="o">=</span><span class="s">&#34;noindex&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">&#34;utf-8&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">http-equiv</span><span class="o">=</span><span class="s">&#34;refresh&#34;</span> <span class="na">content</span><span class="o">=</span><span class="s">&#34;0; url={{ .Permalink }}&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>So what we are going to do is create our own alias template that&rsquo;s better.<br>
Put this in here: layouts/external-redirect/single.html</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">{{- template &#34;_internal/alias.html&#34; (dict &#34;Permalink&#34; .Params.url) -}}
</span></span></code></pre></div><p>Now whenever you want something redirected just put a file there with this frontmatter in it. Replace the URL parameter for where you&rsquo;d like it to send the user.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;external-redirect&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;https://blog.roboflow.com/security-camera-monitoring/&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">title</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;Making Megadesk Smart&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">date</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;2024-03-17&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nn">---</span><span class="w">
</span></span></span></code></pre></div><p><img loading="lazy" src="/blog/easy301-img/ipfs.webp" alt="Easy 301 Redirects For IPFS Hosts"  />
</p>
<h2 id="easy-301-redirects-for-ipfs-hosts-cloudflare-netlify-etc">Easy 301 Redirects For IPFS Hosts (Cloudflare, Netlify, etc..)</h2>
<p>So most <a href="https://specs.ipfs.tech/http-gateways/web-redirects-file/" target="_blank">IPFS hosts</a>
 have an easy method to setup 301 redirects by putting a single file named <code>_redirects</code> in your root folder. This easy method has been fully embraced by Netlify and many other, lesser known hosts, however depending on your cache settings you might run into problems on Cloudflare (you should be using the cloudflare redirect rule anyways). So if you&rsquo;re on Netlify or a host that offers ipfs hosting, you probably can just use this method.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">/old-url /new-url 301
</span></span><span class="line"><span class="cl">/another-old-url /another-new-url 301
</span></span><span class="line"><span class="cl">/blog/aboutmycats.html  https://mycatsaremylifeandyouknowit.com
</span></span></code></pre></div><p><img loading="lazy" src="/blog/easy301-img/cloudflare.webp" alt="Easy 301 Redirects With Cloudflare"  />
</p>
<h2 id="really-easy-301-redirects-with-cloudflare">Really Easy 301 Redirects With Cloudflare</h2>
<p>So I run this Hugo blog on Cloudflare pages. So far it&rsquo;s been great, although it did take some messing around with the cache system to get it working the way I wanted it to. The beauty of using Cloudflare pages is that it comes with a lot of free utilities like a whole bunch of redirect options. The absolute easiest way to setup a 301 redirect in Cloudflare is with a Page Rule. You&rsquo;re only allowed 3 in the free plan so if you use those up and don&rsquo;t want to pay for more just switch to the Redirect Rules. If you have a whole lot of complicated rules you need to have them redirect there&rsquo;s an option for Bulk Redirect Rules that is also covered under the free tier.</p>
<p>To set up a 301 Redirect via Cloudflare, log-in and select the domain involved. You should see towards the bottom of the left menu a category named Rules. Click on that and then on Page Rules. The incoming URI is the first option, in my case I only want to forward one specific URL so it&rsquo;s pretty simple.</p>
<p><img loading="lazy" src="/blog/easy301-img/cloudflare_page_rule.png" alt="Cloud Flare Page Rule"  />
</p>
<p>And there you go!</p>
<h2 id="buying-expired-domains-to-redirect-for-seo">Buying Expired Domains To Redirect For SEO</h2>
<p>I will keep this part short because there are countless resources already out that do a much better job that I could at explaining the actual strategy behind which domains to be looking at, but this topic is one that&rsquo;s been argued about in the SEO community for 15+ years. Even employees at Google have made statements regarding link equity to known purchased domains won&rsquo;t work only for the truth to come out later in a <a href="https://news.ycombinator.com/item?id=40518016" target="_blank">leaked company document</a>
. Here&rsquo;s the truth, if you&rsquo;re patient, know what to look for and know what you&rsquo;re doing then this method absolutely still works. But most people only understand half of what it really means to find a good match. Or they think they have one but never looked at their dns records or what the pages looked like/ran on over the last year. Just using a redirect on any old domain you buy probably won&rsquo;t do much for your own ranking. It has to be a clean domain that you can match up with your new content. Google keeps track of everything. From the number of recently added links to the history of your site AND the one that&rsquo;s now redirecting to yours. If the content of the link doesn&rsquo;t match up or if the anchor tag doesn&rsquo;t describe the page you now have it going to it then Google won&rsquo;t give you anything for those links. If that domain was recently used for malware or phishing before you purchased it then Google might actually punish you as you&rsquo;re now linked with a toxic source.</p>
<h2 id="being-smart-about-your-301-redirects">Being Smart About Your 301 Redirects</h2>
<p>I&rsquo;ll say it again, do your research. One of those acts of due diligence is to always go and get <a href="/notes/getting-all-known-links-from-wayback-machine/">all known links from the WayBack machine</a>
 to investigate what the actual content looked like, the structure of the site and the tone of it all. Check out the source code on a random page, look at the sitemap if they have one in the archive, etc.. That&rsquo;s just one out of more than a dozen or so points of interest you should be researching. If you&rsquo;re still interested start heading down that google rabbit hole for more info. GoDaddy is one of the leading auction houses along with a few other registrars. Well established aggregators like <a href="https://www.expireddomains.net/" target="_blank">ExpiredDomains.net</a>
 can provide all of the domain info as well as more filters than you&rsquo;ll know what to do with (in fact, how to use those filters should be one of your first searches). If you&rsquo;re having a hard time finding the right domain research resources, here&rsquo;s one that links just about every useful <a href="https://tian.hu/tools" target="_blank">domain related site</a>
 that I can think of.</p>
<p>Hope this helped you get those links moving!<br>
~Brent</p>
]]></content:encoded>
    </item>
    <item>
      <title>Music - The Last 15 Songs I Listened To On Spotify</title>
      <link>https://brentter.com/music/</link>
      <pubDate>Sun, 29 Sep 2024 19:42:48 -0700</pubDate>
      <guid>https://brentter.com/music/</guid>
      <description>An automated page that shows you the last 15 audio tracks that I listened to and scrobbled on last.fm. The main source is Spotify.</description><content:encoded><![CDATA[ ]]>
               <![CDATA[<h2 id="this-is-an-automated-page-using-a-github-workflow--python">This Is An Automated Page Using A Github Workflow &amp; Python:</h2>
<ul>
<li>The data is pulled from a script that checks last.fm every hour for updates</li>
<li>The script is triggered by the workflow and if there&rsquo;s been an update it rebuilds this page.</li>
<li>The only services hooked up to send last.fm data are Spotify and Deezer</li>
<li>That means you&rsquo;re missing out on all my vinyl and anything streamed from youtube or archive.org!</li>
<li>It was just a quick little hack I tried when I was new to Hugo so it&rsquo;s basic.</li>
<li>If you wanted to see more just check-out my <a href="https://www.last.fm/user/brentter" target="_blank">Last.fm User Page</a>
</li>
<li>Want to add one of these to your own hugo site? <a href="https://brentter.com/blog/add-a-spotify-recently-played-page-for-hugo/" target="_blank">Add A Recently Played Music Page to Hugo</a>
</li>
</ul>
<hr>
<p> </p>



<table class="track-list">

<tr class="track-item">
<td class="track-image"><img src="https://lastfm-img.freetls.fastly.net/i/u/174s/2a96cbd8b46e442fc41c2b86b821562f.png" alt="Gonesville"></td>
<td class="track-details">
<h3><a href="https://www.last.fm/music/Bobby+Weir/_/Gonesville" target="_blank">Gonesville</a></h3>
<p><strong>Artist:</strong> Bobby Weir<br>
<strong>Album:</strong> Blue Mountain<br>
<strong>Date Listened To:</strong> 16 Jul 2026</p>
</td>
</tr>
<tr class="track-item">
<td class="track-image"><img src="https://lastfm-img.freetls.fastly.net/i/u/174s/2c636ad6ad9b41ab4d968489244784a2.jpg" alt="A Whiter Shade Of Pale"></td>
<td class="track-details">
<h3><a href="https://www.last.fm/music/Johnny+Blue+Skies/_/A+Whiter+Shade+Of+Pale" target="_blank">A Whiter Shade Of Pale</a></h3>
<p><strong>Artist:</strong> Johnny Blue Skies<br>
<strong>Album:</strong> Mutiny After Midnight (Extended Edition)<br>
<strong>Date Listened To:</strong> 16 Jul 2026</p>
</td>
</tr>
<tr class="track-item">
<td class="track-image"><img src="https://lastfm-img.freetls.fastly.net/i/u/174s/2c636ad6ad9b41ab4d968489244784a2.jpg" alt="Ain't That A Bitch"></td>
<td class="track-details">
<h3><a href="https://www.last.fm/music/Johnny+Blue+Skies/_/Ain%27t+That+A+Bitch" target="_blank">Ain't That A Bitch</a></h3>
<p><strong>Artist:</strong> Johnny Blue Skies<br>
<strong>Album:</strong> Mutiny After Midnight (Extended Edition)<br>
<strong>Date Listened To:</strong> 16 Jul 2026</p>
</td>
</tr>
<tr class="track-item">
<td class="track-image"><img src="https://lastfm-img.freetls.fastly.net/i/u/174s/2c636ad6ad9b41ab4d968489244784a2.jpg" alt="Everyone Is Welcome"></td>
<td class="track-details">
<h3><a href="https://www.last.fm/music/Johnny+Blue+Skies/_/Everyone+Is+Welcome" target="_blank">Everyone Is Welcome</a></h3>
<p><strong>Artist:</strong> Johnny Blue Skies<br>
<strong>Album:</strong> Mutiny After Midnight (Extended Edition)<br>
<strong>Date Listened To:</strong> 16 Jul 2026</p>
</td>
</tr>
<tr class="track-item">
<td class="track-image"><img src="https://lastfm-img.freetls.fastly.net/i/u/174s/2c636ad6ad9b41ab4d968489244784a2.jpg" alt="Party All The Time"></td>
<td class="track-details">
<h3><a href="https://www.last.fm/music/Johnny+Blue+Skies/_/Party+All+The+Time" target="_blank">Party All The Time</a></h3>
<p><strong>Artist:</strong> Johnny Blue Skies<br>
<strong>Album:</strong> Mutiny After Midnight (Extended Edition)<br>
<strong>Date Listened To:</strong> 16 Jul 2026</p>
</td>
</tr>
<tr class="track-item">
<td class="track-image"><img src="https://lastfm-img.freetls.fastly.net/i/u/174s/3aa6281fa40ee244e0da2d75f30ec14e.jpg" alt="Dreadful Wind and Rain"></td>
<td class="track-details">
<h3><a href="https://www.last.fm/music/Jerry+Garcia/_/Dreadful+Wind+and+Rain" target="_blank">Dreadful Wind and Rain</a></h3>
<p><strong>Artist:</strong> Jerry Garcia<br>
<strong>Album:</strong> Shady Grove<br>
<strong>Date Listened To:</strong> 16 Jul 2026</p>
</td>
</tr>
<tr class="track-item">
<td class="track-image"><img src="https://lastfm-img.freetls.fastly.net/i/u/174s/56ac62f4e8cc459cb1ba3255f453131f.jpg" alt="Backwards Down the Number Line"></td>
<td class="track-details">
<h3><a href="https://www.last.fm/music/Phish/_/Backwards+Down+the+Number+Line" target="_blank">Backwards Down the Number Line</a></h3>
<p><strong>Artist:</strong> Phish<br>
<strong>Album:</strong> Joy<br>
<strong>Date Listened To:</strong> 16 Jul 2026</p>
</td>
</tr>
<tr class="track-item">
<td class="track-image"><img src="https://lastfm-img.freetls.fastly.net/i/u/174s/e049e8e7ea2a84242b3547a307650cc8.jpg" alt="Set Your Soul Free (Live, 11/3/2017)"></td>
<td class="track-details">
<h3><a href="https://www.last.fm/music/Trey+Anastasio/_/Set+Your+Soul+Free+(Live,+11%2F3%2F2017)" target="_blank">Set Your Soul Free (Live, 11/3/2017)</a></h3>
<p><strong>Artist:</strong> Trey Anastasio<br>
<strong>Album:</strong> TAB at the Fox Theater (Live)<br>
<strong>Date Listened To:</strong> 16 Jul 2026</p>
</td>
</tr>
<tr class="track-item">
<td class="track-image"><img src="https://lastfm-img.freetls.fastly.net/i/u/174s/d23cb68f0eb64e37e1c4340945442520.jpg" alt="Althea - Live at Hartford Civic Center, October 14, 1983"></td>
<td class="track-details">
<h3><a href="https://www.last.fm/music/Grateful+Dead/_/Althea+-+Live+at+Hartford+Civic+Center,+October+14,+1983" target="_blank">Althea - Live at Hartford Civic Center, October 14, 1983</a></h3>
<p><strong>Artist:</strong> Grateful Dead<br>
<strong>Album:</strong> Dick's Picks Vol. 6: Hartford Civic Center, Hartford, CT 10/14/83 (Live)<br>
<strong>Date Listened To:</strong> 16 Jul 2026</p>
</td>
</tr>
<tr class="track-item">
<td class="track-image"><img src="https://lastfm-img.freetls.fastly.net/i/u/174s/0468f640e46e094da8db4da088b70f4c.jpg" alt="Atlantic City > - Live 2019-11-25"></td>
<td class="track-details">
<h3><a href="https://www.last.fm/music/Joe+Russo%27s+Almost+Dead/_/Atlantic+City+%3E+-+Live+2019-11-25" target="_blank">Atlantic City > - Live 2019-11-25</a></h3>
<p><strong>Artist:</strong> Joe Russo's Almost Dead<br>
<strong>Album:</strong> Brooklyn, NY :: 2019-11-25<br>
<strong>Date Listened To:</strong> 16 Jul 2026</p>
</td>
</tr>
<tr class="track-item">
<td class="track-image"><img src="https://lastfm-img.freetls.fastly.net/i/u/174s/87687e6f41d20a738c599bb54b611d27.jpg" alt="Fire On The Mountain"></td>
<td class="track-details">
<h3><a href="https://www.last.fm/music/Daniel+Donato/_/Fire+On+The+Mountain" target="_blank">Fire On The Mountain</a></h3>
<p><strong>Artist:</strong> Daniel Donato<br>
<strong>Album:</strong> A Young Man's Country<br>
<strong>Date Listened To:</strong> 16 Jul 2026</p>
</td>
</tr>
<tr class="track-item">
<td class="track-image"><img src="https://lastfm-img.freetls.fastly.net/i/u/174s/ea321b2e9eb20085c0388cae4e8269c8.jpg" alt="Black-Throated Wind"></td>
<td class="track-details">
<h3><a href="https://www.last.fm/music/Bobby+Weir/_/Black-Throated+Wind" target="_blank">Black-Throated Wind</a></h3>
<p><strong>Artist:</strong> Bobby Weir<br>
<strong>Album:</strong> Ace<br>
<strong>Date Listened To:</strong> 16 Jul 2026</p>
</td>
</tr>
<tr class="track-item">
<td class="track-image"><img src="https://lastfm-img.freetls.fastly.net/i/u/174s/3aa6281fa40ee244e0da2d75f30ec14e.jpg" alt="Whiskey In the Jar"></td>
<td class="track-details">
<h3><a href="https://www.last.fm/music/Jerry+Garcia/_/Whiskey+In+the+Jar" target="_blank">Whiskey In the Jar</a></h3>
<p><strong>Artist:</strong> Jerry Garcia<br>
<strong>Album:</strong> Shady Grove<br>
<strong>Date Listened To:</strong> 16 Jul 2026</p>
</td>
</tr>
<tr class="track-item">
<td class="track-image"><img src="https://lastfm-img.freetls.fastly.net/i/u/174s/c37936981348b3daa1b6602857a4458c.jpg" alt="Brown-Eyed Woman - Live 2018-10-25"></td>
<td class="track-details">
<h3><a href="https://www.last.fm/music/Joe+Russo%27s+Almost+Dead/_/Brown-Eyed+Woman+-+Live+2018-10-25" target="_blank">Brown-Eyed Woman - Live 2018-10-25</a></h3>
<p><strong>Artist:</strong> Joe Russo's Almost Dead<br>
<strong>Album:</strong> Live Oak, FL :: 2018-10-25<br>
<strong>Date Listened To:</strong> 16 Jul 2026</p>
</td>
</tr>
<tr class="track-item">
<td class="track-image"><img src="https://lastfm-img.freetls.fastly.net/i/u/174s/a953eb3d96f639d0580611862893145d.jpg" alt="New Speedway Boogie - 2013 Remaster"></td>
<td class="track-details">
<h3><a href="https://www.last.fm/music/Grateful+Dead/_/New+Speedway+Boogie+-+2013+Remaster" target="_blank">New Speedway Boogie - 2013 Remaster</a></h3>
<p><strong>Artist:</strong> Grateful Dead<br>
<strong>Album:</strong> Workingman's Dead<br>
<strong>Date Listened To:</strong> 16 Jul 2026</p>
</td>
</tr>
</table >


]]></content:encoded>
    </item>
    <item>
      <title>Add A Recently Played Music Page To Your Hugo Blog</title>
      <link>https://brentter.com/blog/add-a-spotify-recently-played-page-for-hugo/</link>
      <pubDate>Tue, 24 Sep 2024 00:03:29 -0700</pubDate>
      <guid>https://brentter.com/blog/add-a-spotify-recently-played-page-for-hugo/</guid>
      <description>A guide to adding your very own recently listened to music page to your hugo blog using a github workflow and and a python script</description><content:encoded><![CDATA[ 
<figure class="entry-cover"><img loading="lazy" src="https://brentter.com/blog/Add_An_Auto_Updating_Recently_Played_Music_Page_To_Hugo.webp" alt="My profile on last.fm where it pulls my recent music from">
        
</figure>]]>
               <![CDATA[<p>So I love it when people have a list of recently listened to music or recently watched movies or books and wanted to add something similar to this site. The only problem is that Hugo is a static website generator. Every page is created and stored as is, not pulled from a database. That left me with only a few options if I wanted to include this file within my hugo blog (rather than host the page outside of it and just link to it from hugo). What I decided on in the end was to create a github workflow that in combination with a small python script would keep a regular check on my recently listened to list from last.fm via their API. From that list it would then update a music page if it notices new content, running every few hours to check to see if there&rsquo;s been an update. I added all of this to a github repo if you want to skip the long version. You can find that <a href="https://github.com/brentter/HugoLastfmPageGenerator" target="_blank">here</a>
. Otherwise here is how I created <a href="https://brentter.com/music/" target="_blank">Brentter.com/music/</a>
 :</p>
<h2 id="how-do-you-track-what-you-listen-to-what-is-scrobbling">How do you track what you listen to? What is Scrobbling?</h2>
<p><a href="https://www.last.fm/about/trackmymusic" target="_blank">Scrobbling</a>
 is the term for sharing what content you are watching/listening to across other services. There are a bunch of sites like Last.fm which can directly interact with your media apps like Youtube, Spotify and Itunes to record what you are playing. The purpose is to get a better idea of what type of music you have enjoyed over time as well as help introduce you to new music based on your listening habits. You can enable this feature in the settings or options area of most media apps. While there are a bunch of sites that you can use to keep track of this data, I chose last.fm because of how easy it is to use (and free). So you will need a last.fm account for this as well as an API key.</p>
<h2 id="create-a-lastfm-account-and-get-an-api-key">Create a last.fm account and get an API key</h2>
<p>Head on over to <a href="https://www.last.fm" target="_blank">Last.fm</a>
 and create an account. You will then need to head over to <a href="https://www.last.fm/login?next=/api/account/create" target="_blank">here</a>
 to create the API key. Just put anything in for the application description. You can even use localhost as the callback url. The next screen will show you your application API key. Write this down somewhere safe.</p>
<h2 id="enable-scrobbling-in-your-media-apps">Enable scrobbling in your media apps</h2>
<p>This is just going to be for spotify but you can do the same for any other media application that you prefer.  Log into your last.fm account, go to Settings then click Applications. Click on the connect to Spotify button and it will have you verify your credentials with spotify and that you are allowing last.fm to connect to your account.  Once approved last.fm will keep track of what you listen to!</p>
<h2 id="you-will-now-need-to-add-a-new-shortcode">You Will Now Need To Add A New Shortcode</h2>
<p>So in order for the list of recently listened to music to show up properly you&rsquo;ll need to show a little bit of HTML code on the page. Hugo typically will remove any HTML tags on any pages unless you add a shortcode for displaying raw HTML. To do this create new shortcode template in layouts/shortcode/rawhtml.html</p>
<p>Then place the following inside it:</p>
<p><strong>layouts/shortcode/rawhtml.html</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">&lt;!-- raw html --&gt;
</span></span><span class="line"><span class="cl">{{.Inner}}
</span></span></code></pre></div><p>Now on your markdown page you&rsquo;ll be able to use it like this;</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">{{&lt; rawhtml &gt;}} HTML GOES HERE {{&lt; /rawhtml &gt;}}
</span></span></code></pre></div><h2 id="here-is-the-python-script-to-update-the-page">Here is the python script to update the page</h2>
<p>You will need to go into your github repo, click settings then actions and add two new secrets -  LASTFM_API_KEY and LASTFM_USERNAME with the appropriate info.</p>
<p><strong>generate_markdown.py</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">requests</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">xml.etree.ElementTree</span> <span class="k">as</span> <span class="nn">ET</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">datetime</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">os</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">sys</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">json</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Load track data from JSON file instead of fetching it directly from API.</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="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s2">&#34;lastfm_tracks.json&#34;</span><span class="p">,</span> <span class="s2">&#34;r&#34;</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">         <span class="n">response</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">except</span> <span class="ne">FileNotFoundError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Error loading track data file </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>  <span class="c1"># Exit the script with a non-zero status to indicate failure.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Extract track information from loaded JSON data.</span>
</span></span><span class="line"><span class="cl"><span class="n">tracks</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">track</span> <span class="ow">in</span> <span class="n">response</span><span class="p">[</span><span class="s1">&#39;recenttracks&#39;</span><span class="p">][</span><span class="s1">&#39;track&#39;</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl">    <span class="n">artist</span> <span class="o">=</span> <span class="n">track</span><span class="p">[</span><span class="s1">&#39;artist&#39;</span><span class="p">][</span><span class="s1">&#39;#text&#39;</span><span class="p">]</span> <span class="k">if</span> <span class="s1">&#39;artist&#39;</span> <span class="ow">in</span> <span class="n">track</span> <span class="k">else</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">name</span> <span class="o">=</span> <span class="n">track</span><span class="p">[</span><span class="s1">&#39;name&#39;</span><span class="p">]</span> <span class="k">if</span> <span class="s1">&#39;name&#39;</span> <span class="ow">in</span> <span class="n">track</span> <span class="k">else</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">album</span> <span class="o">=</span> <span class="n">track</span><span class="p">[</span><span class="s1">&#39;album&#39;</span><span class="p">][</span><span class="s1">&#39;#text&#39;</span><span class="p">]</span> <span class="k">if</span> <span class="s1">&#39;album&#39;</span> <span class="ow">in</span> <span class="n">track</span> <span class="k">else</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">url</span> <span class="o">=</span> <span class="n">track</span><span class="p">[</span><span class="s1">&#39;url&#39;</span><span class="p">]</span> <span class="k">if</span> <span class="s1">&#39;url&#39;</span> <span class="ow">in</span> <span class="n">track</span> <span class="k">else</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Correctly extract large image URL</span>
</span></span><span class="line"><span class="cl">    <span class="n">image_url</span> <span class="o">=</span> <span class="nb">next</span><span class="p">((</span><span class="n">img</span><span class="p">[</span><span class="s1">&#39;#text&#39;</span><span class="p">]</span> <span class="k">for</span> <span class="n">img</span> <span class="ow">in</span> <span class="n">track</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;image&#39;</span><span class="p">,</span> <span class="p">[])</span> <span class="k">if</span> <span class="n">img</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;size&#39;</span><span class="p">)</span> <span class="o">==</span> <span class="s1">&#39;large&#39;</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="n">date_listened_full</span> <span class="o">=</span> <span class="n">track</span><span class="p">[</span><span class="s1">&#39;date&#39;</span><span class="p">][</span><span class="s1">&#39;#text&#39;</span><span class="p">]</span> <span class="k">if</span> <span class="s1">&#39;date&#39;</span> <span class="ow">in</span> <span class="n">track</span> <span class="k">else</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">date_listened_date_only</span> <span class="o">=</span> <span class="n">date_listened_full</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">&#39;,&#39;</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span> <span class="k">if</span> <span class="n">date_listened_full</span> <span class="k">else</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">tracks</span><span class="o">.</span><span class="n">append</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">         <span class="s1">&#39;artist&#39;</span><span class="p">:</span> <span class="n">artist</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">         <span class="s1">&#39;name&#39;</span><span class="p">:</span> <span class="n">name</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">         <span class="s1">&#39;album&#39;</span><span class="p">:</span> <span class="n">album</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">         <span class="s1">&#39;url&#39;</span><span class="p">:</span> <span class="n">url</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">         <span class="s1">&#39;image_url&#39;</span><span class="p">:</span> <span class="n">image_url</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">         <span class="s1">&#39;date_listened&#39;</span><span class="p">:</span> <span class="n">date_listened_date_only</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></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Get current date and time in ISO 8601 format with timezone offset</span>
</span></span><span class="line"><span class="cl"><span class="n">current_datetime</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span><span class="o">.</span><span class="n">astimezone</span><span class="p">()</span><span class="o">.</span><span class="n">isoformat</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Generate Markdown content with Hugo front matter and embedded HTML for styling</span>
</span></span><span class="line"><span class="cl"><span class="n">markdown_content</span><span class="o">=</span><span class="sa">f</span><span class="s1">&#39;&#39;&#39;---
</span></span></span><span class="line"><span class="cl"><span class="s1">title: &#34;Music&#34;
</span></span></span><span class="line"><span class="cl"><span class="s1">date: &#34;</span><span class="si">{</span><span class="n">current_datetime</span><span class="si">}</span><span class="s1">&#34;
</span></span></span><span class="line"><span class="cl"><span class="s1">#YOUR FRONTPLATE INFO GOES HERE
</span></span></span><span class="line"><span class="cl"><span class="s1">#I included how it changes the date to the current one every time it runs with </span><span class="si">{</span><span class="n">current_datetime</span><span class="si">}</span><span class="s1">
</span></span></span><span class="line"><span class="cl"><span class="s1">#If you prefer to use the lastupdated option instead go for it
</span></span></span><span class="line"><span class="cl"><span class="s1">---
</span></span></span><span class="line"><span class="cl"><span class="s1">
</span></span></span><span class="line"><span class="cl"><span class="s1">## Last Ten Tracks Listened To From Last.fm
</span></span></span><span class="line"><span class="cl"><span class="s1">
</span></span></span><span class="line"><span class="cl"><span class="s1"></span><span class="se">{{{{</span><span class="s1">&lt; rawhtml &gt;</span><span class="se">}}}}</span><span class="s1">
</span></span></span><span class="line"><span class="cl"><span class="s1">
</span></span></span><span class="line"><span class="cl"><span class="s1">&lt;ul class=&#34;track-list&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="s1">&#39;&#39;&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">t</span> <span class="ow">in</span> <span class="n">tracks</span> <span class="p">:</span>
</span></span><span class="line"><span class="cl">   <span class="n">markdown_content</span><span class="o">+=</span> <span class="sa">f</span><span class="s1">&#39;&#39;&#39;
</span></span></span><span class="line"><span class="cl"><span class="s1">&lt;li class=&#34;track-item&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="s1">&lt;div class=&#34;track-image&#34;&gt;&lt;img src=&#34;</span><span class="si">{</span><span class="n">t</span><span class="p">[</span><span class="s1">&#39;image_url&#39;</span><span class="p">]</span><span class="si">}</span><span class="s1">&#34; alt=&#34;</span><span class="si">{</span><span class="n">t</span><span class="p">[</span><span class="s1">&#39;name&#39;</span><span class="p">]</span><span class="si">}</span><span class="s1">&#34;&gt;&lt;/div&gt;
</span></span></span><span class="line"><span class="cl"><span class="s1">&lt;div class=&#34;track-details&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="s1">&lt;h3&gt;&lt;a href=&#34;</span><span class="si">{</span><span class="n">t</span><span class="p">[</span><span class="s1">&#39;url&#39;</span><span class="p">]</span><span class="si">}</span><span class="s1">&#34; target=&#34;_blank&#34;&gt;</span><span class="si">{</span><span class="n">t</span><span class="p">[</span><span class="s1">&#39;name&#39;</span><span class="p">]</span><span class="si">}</span><span class="s1">&lt;/a&gt;&lt;/h3&gt;
</span></span></span><span class="line"><span class="cl"><span class="s1">&lt;p&gt;&lt;strong&gt;Artist:&lt;/strong&gt; </span><span class="si">{</span><span class="n">t</span><span class="p">[</span><span class="s1">&#39;artist&#39;</span><span class="p">]</span><span class="si">}</span><span class="s1">&lt;br&gt;
</span></span></span><span class="line"><span class="cl"><span class="s1">&lt;strong&gt;Album:&lt;/strong&gt; </span><span class="si">{</span><span class="n">t</span><span class="p">[</span><span class="s1">&#39;album&#39;</span><span class="p">]</span><span class="si">}</span><span class="s1">&lt;br&gt;
</span></span></span><span class="line"><span class="cl"><span class="s1">&lt;strong&gt;Date Listened To:&lt;/strong&gt; </span><span class="si">{</span><span class="n">t</span><span class="p">[</span><span class="s1">&#39;date_listened&#39;</span><span class="p">]</span><span class="si">}</span><span class="s1">&lt;/p&gt;
</span></span></span><span class="line"><span class="cl"><span class="s1">&lt;/div&gt;
</span></span></span><span class="line"><span class="cl"><span class="s1">&lt;/li&gt;&#39;&#39;&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">markdown_content</span> <span class="o">+=</span> <span class="s1">&#39;&#39;&#39;
</span></span></span><span class="line"><span class="cl"><span class="s1">&lt;/ul &gt;
</span></span></span><span class="line"><span class="cl"><span class="s1">{{&lt; /rawhtml &gt;}}
</span></span></span><span class="line"><span class="cl"><span class="s1">&#39;&#39;&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Write to a markdown file- Change to whatever folder or filename you want the results placed in. </span>
</span></span><span class="line"><span class="cl"><span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s2">&#34;content/music.md&#34;</span><span class="p">,</span> <span class="s2">&#34;w&#34;</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">     <span class="n">f</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">markdown_content</span><span class="p">)</span>
</span></span></code></pre></div><p>Now what this does is it uses the lastfm_tracks.json file that your workflow will create from the last.fm API and generates a music.md file in your content folder. It currently is set to show the last 15 tracks but if you&rsquo;d like to change that just scroll down and in the workflow change the appropriate line. You will also want to add all your frontplate information where noted as well as double-check where you&rsquo;d like the generated file to go. When pushed live this page would be at yourwebsite.com/music</p>
<h2 id="now-to-the-css-to-make-it-look-decent">Now to the CSS to make it look decent</h2>
<p>Depending on what theme you are using you are going to have to find what css file works for the type of page or post you are placing this in. For me that was post-single.css.  All you have to do is copy that css file from your theme folder to your own <strong>assets/css/common/</strong> folder and at the end add the following:</p>
<p><strong>.css</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="p">.</span><span class="nc">track-item</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">border-radius</span> <span class="p">:</span><span class="mi">8</span><span class="kt">px</span><span class="p">;</span> <span class="k">box-shadow</span> <span class="p">:</span><span class="mi">0</span> <span class="mi">2</span><span class="kt">px</span> <span class="mi">4</span><span class="kt">px</span> <span class="nb">rgba</span><span class="p">(</span><span class="mi">0</span> <span class="p">,</span><span class="mi">0</span> <span class="p">,</span><span class="mi">0</span> <span class="p">,</span><span class="mf">.1</span><span class="p">);</span> <span class="k">margin-bottom</span> <span class="p">:</span><span class="mi">20</span><span class="kt">px</span><span class="p">;</span> <span class="k">overflow</span><span class="p">:</span><span class="kc">hidden</span><span class="p">;</span> <span class="k">display</span><span class="p">:</span><span class="kc">flex</span><span class="p">;</span> <span class="k">align-items</span><span class="p">:</span><span class="kc">center</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></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">track-image</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">width</span><span class="p">:</span><span class="kc">auto</span><span class="p">;</span><span class="k">height</span><span class="p">:</span><span class="kc">auto</span><span class="p">;</span><span class="k">margin-right</span><span class="p">:</span><span class="mf">.5</span><span class="kt">em</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></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">track-image</span> <span class="nt">img</span><span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">max-width</span> <span class="p">:</span><span class="mi">100</span><span class="kt">%</span><span class="p">;</span><span class="k">height</span><span class="p">:</span><span class="kc">auto</span><span class="p">;</span><span class="k">border-radius</span><span class="p">:</span><span class="mf">.5</span><span class="kt">em</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></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">track-details</span><span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">padding</span><span class="p">:</span><span class="mf">.5</span><span class="kt">em</span><span class="p">;</span><span class="k">width</span><span class="p">:</span><span class="kc">auto</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></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">track-details</span> <span class="nt">h3</span><span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">margin-top</span> <span class="p">:</span><span class="mi">0</span><span class="p">;</span><span class="k">margin-bottom</span> <span class="p">:</span><span class="mf">.25</span><span class="kt">em</span><span class="p">;</span><span class="k">font-size</span> <span class="p">:</span>
</span></span><span class="line"><span class="cl"><span class="mf">1.25</span><span class="kt">em</span><span class="p">;}</span> <span class="nt">p</span><span class="p">{</span><span class="k">margin</span> <span class="p">:</span><span class="mf">.25</span><span class="kt">em</span><span class="p">;</span><span class="k">font-size</span> <span class="p">:</span><span class="mf">.9</span><span class="kt">em</span><span class="p">;</span><span class="k">color</span><span class="p">:</span><span class="n">d3d3d3</span><span class="p">;}</span>
</span></span></code></pre></div><h2 id="now-to-keep-this-updated">Now to keep this updated!</h2>
<p>In order to keep this regularly updated we need to create a github workflow. What this will do is grab the latest list of songs recorded by last.fm&rsquo;s API and save it as a json file named lastfm_tracks.json. That&rsquo;s what our python script will use to fill out the music.md page as well as what the workflow will check against to see if there has been an update to our recently listened to list. More information on workflows can be found <a href="https://docs.github.com/en/actions/writing-workflows/quickstart" target="_blank">here</a>
.  In your base directory create two new folders .github and then inside that create a folder named workflows (so /.github/workflows/) In the workflows folder we are going to create a file named update_music_page.yml with the following code:</p>
<p><strong>update_music_page.yml</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl"><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Update Last 15 Tracks</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">on</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">schedule</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">cron</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;0 */4 * * *&#39;</span><span class="w"> </span><span class="c"># Runs every 4 hours (adjust as needed)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">push</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">branches</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">main</span><span class="w"> </span><span class="c"># or your default branch</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">jobs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">update-tracks</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Checkout repository</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/checkout@v4</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Set up Python</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/setup-python@v5</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">python-version</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;3.11.2&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Install dependencies</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">          python -m pip install --upgrade pip
</span></span></span><span class="line"><span class="cl"><span class="sd">          pip install requests jq</span><span class="w">          
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Check for new tracks on Last.fm</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">id</span><span class="p">:</span><span class="w"> </span><span class="l">check_tracks</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">env</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">LASTFM_API_KEY</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.LASTFM_API_KEY }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">LASTFM_USERNAME</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.LASTFM_USERNAME }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">          # Load last fetched tracks from file or set default value if not exists
</span></span></span><span class="line"><span class="cl"><span class="sd">          if [ -f ./lastfm_tracks.json ]; then  # Look in the current directory (root)
</span></span></span><span class="line"><span class="cl"><span class="sd">            LAST_TRACKS=$(cat ./lastfm_tracks.json)
</span></span></span><span class="line"><span class="cl"><span class="sd">            LAST_TIMESTAMP=$(echo &#34;$LAST_TRACKS&#34; | jq &#39;.recenttracks.track[-1].date[&#34;uts&#34;]&#39; | tr -d &#39;&#34;&#39;)
</span></span></span><span class="line"><span class="cl"><span class="sd">          else
</span></span></span><span class="line"><span class="cl"><span class="sd">            LAST_TRACKS=&#34;{}&#34;
</span></span></span><span class="line"><span class="cl"><span class="sd">            LAST_TIMESTAMP=0 # Default value if no previous data exists.
</span></span></span><span class="line"><span class="cl"><span class="sd">          fi
</span></span></span><span class="line"><span class="cl"><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">          # Fetch recent tracks from Last.fm API and filter by timestamp. Change # after limit if you want something other than the latest 15 tracks. 
</span></span></span><span class="line"><span class="cl"><span class="sd">          RESPONSE=$(curl -s &#34;http://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&amp;user=${LASTFM_USERNAME}&amp;limit=15&amp;api_key=${LASTFM_API_KEY}&amp;format=json&#34;)
</span></span></span><span class="line"><span class="cl"><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">          # Print response for debugging purposes
</span></span></span><span class="line"><span class="cl"><span class="sd">          echo &#34;API Response: $RESPONSE&#34;
</span></span></span><span class="line"><span class="cl"><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">          # Check for valid response before processing
</span></span></span><span class="line"><span class="cl"><span class="sd">          if [[ &#34;$RESPONSE&#34; == *&#34;error&#34;* ]]; then
</span></span></span><span class="line"><span class="cl"><span class="sd">            echo &#34;Error fetching data from Last.fm API.&#34;
</span></span></span><span class="line"><span class="cl"><span class="sd">            exit 1  # Exit with error code.
</span></span></span><span class="line"><span class="cl"><span class="sd">          fi
</span></span></span><span class="line"><span class="cl"><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">          NEW_TRACKS=$(echo &#34;$RESPONSE&#34; | jq --arg LAST_TIMESTAMP &#34;$LAST_TIMESTAMP&#34; &#39;.recenttracks.track | map(select(.date.uts != null and (.date.uts | tonumber &gt; ($LAST_TIMESTAMP | tonumber))))&#39;)
</span></span></span><span class="line"><span class="cl"><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">          echo &#34;New Tracks Data: $NEW_TRACKS&#34;
</span></span></span><span class="line"><span class="cl"><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">          NEW_TRACKS_FOUND=false
</span></span></span><span class="line"><span class="cl"><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">          if [ &#34;$(echo &#34;$NEW_TRACKS&#34; | wc -l)&#34; -gt 0 ]; then 
</span></span></span><span class="line"><span class="cl"><span class="sd">            echo &#34;New tracks found.&#34;
</span></span></span><span class="line"><span class="cl"><span class="sd">            NEW_TRACKS_FOUND=true
</span></span></span><span class="line"><span class="cl"><span class="sd">            
</span></span></span><span class="line"><span class="cl"><span class="sd">            # Save the latest track information to a JSON file in root directory.
</span></span></span><span class="line"><span class="cl"><span class="sd">            TRACK_DATA=$(echo &#34;$RESPONSE&#34; | jq &#39;.recenttracks.track&#39;)
</span></span></span><span class="line"><span class="cl"><span class="sd">            
</span></span></span><span class="line"><span class="cl"><span class="sd">            echo &#34;Writing new track data to lastfm_tracks.json...&#34;
</span></span></span><span class="line"><span class="cl"><span class="sd">            
</span></span></span><span class="line"><span class="cl"><span class="sd">            echo &#34;{\&#34;recenttracks\&#34;:{\&#34;track\&#34;:$TRACK_DATA}}&#34; &gt; ./lastfm_tracks.json
</span></span></span><span class="line"><span class="cl"><span class="sd">            
</span></span></span><span class="line"><span class="cl"><span class="sd">            if [ $? -eq 0 ]; then
</span></span></span><span class="line"><span class="cl"><span class="sd">              echo &#34;Successfully wrote to lastfm_tracks.json.&#34;
</span></span></span><span class="line"><span class="cl"><span class="sd">              # Get latest timestamp from new tracks for next run comparison.
</span></span></span><span class="line"><span class="cl"><span class="sd">              LATEST_TIMESTAMP=$(echo &#34;$NEW_TRACKS&#34; | jq &#39;.[-1].date.uts // empty&#39; | tr -d &#39;&#34;&#39;)
</span></span></span><span class="line"><span class="cl"><span class="sd">              echo &#34;Latest timestamp updated to $LATEST_TIMESTAMP.&#34;
</span></span></span><span class="line"><span class="cl"><span class="sd">              
</span></span></span><span class="line"><span class="cl"><span class="sd">              # Set output as environment variable
</span></span></span><span class="line"><span class="cl"><span class="sd">              echo &#34;has_new_tracks=true&#34; &gt;&gt; $GITHUB_ENV
</span></span></span><span class="line"><span class="cl"><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">              # Check for changes before committing
</span></span></span><span class="line"><span class="cl"><span class="sd">              git config --global user.name &#34;${{ github.actor }}&#34;
</span></span></span><span class="line"><span class="cl"><span class="sd">              git config --global user.email &#34;${{ github.actor }}@users.noreply.github.com&#34;
</span></span></span><span class="line"><span class="cl"><span class="sd">              
</span></span></span><span class="line"><span class="cl"><span class="sd">              git add ./lastfm_tracks.json 
</span></span></span><span class="line"><span class="cl"><span class="sd">              
</span></span></span><span class="line"><span class="cl"><span class="sd">              # Only commit if there are changes in lastfm_tracks.json.
</span></span></span><span class="line"><span class="cl"><span class="sd">              git diff-index --quiet HEAD || git commit -m &#34;Update lastfm_tracks.json with new tracks&#34;
</span></span></span><span class="line"><span class="cl"><span class="sd">              
</span></span></span><span class="line"><span class="cl"><span class="sd">           else 
</span></span></span><span class="line"><span class="cl"><span class="sd">             echo &#34;Failed to write to lastfm_tracks.json.&#34;
</span></span></span><span class="line"><span class="cl"><span class="sd">             exit 1  # Exit with error code on failure.
</span></span></span><span class="line"><span class="cl"><span class="sd">           fi
</span></span></span><span class="line"><span class="cl"><span class="sd">            
</span></span></span><span class="line"><span class="cl"><span class="sd">          else
</span></span></span><span class="line"><span class="cl"><span class="sd">            echo &#34;No new tracks found.&#34;
</span></span></span><span class="line"><span class="cl"><span class="sd">            # Set output as environment variable
</span></span></span><span class="line"><span class="cl"><span class="sd">            echo &#34;has_new_tracks=false&#34; &gt;&gt; $GITHUB_ENV
</span></span></span><span class="line"><span class="cl"><span class="sd">          fi</span><span class="w">          
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="c"># Check for changes in git files (including pushes)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Check for new commits or changes in lastfm_tracks.json</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">id</span><span class="p">:</span><span class="w"> </span><span class="l">check_git_changes</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">          git fetch origin main 
</span></span></span><span class="line"><span class="cl"><span class="sd">          
</span></span></span><span class="line"><span class="cl"><span class="sd">          # Check if there are any changes between HEAD and origin/main (the last commit)
</span></span></span><span class="line"><span class="cl"><span class="sd">          if ! git diff --quiet HEAD origin/main; then
</span></span></span><span class="line"><span class="cl"><span class="sd">            echo &#34;has_new_git_changes=true&#34; &gt;&gt; $GITHUB_ENV
</span></span></span><span class="line"><span class="cl"><span class="sd">          else
</span></span></span><span class="line"><span class="cl"><span class="sd">            echo &#34;has_new_git_changes=false&#34; &gt;&gt; $GITHUB_ENV
</span></span></span><span class="line"><span class="cl"><span class="sd">          fi
</span></span></span><span class="line"><span class="cl"><span class="sd">                </span><span class="w">          
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="c"># Conditional execution based on whether new tracks were found or there are new commits/pushes.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Run script to generate markdown file</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">if</span><span class="p">:</span><span class="w"> </span><span class="l">env.has_new_tracks == &#39;true&#39; || env.has_new_git_changes == &#39;true&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">env</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">LASTFM_API_KEY</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.LASTFM_API_KEY }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">LASTFM_USERNAME</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.LASTFM_USERNAME }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">           python generate_music_markdown.py</span><span class="w">           
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Commit changes</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">if</span><span class="p">:</span><span class="w"> </span><span class="l">env.has_new_tracks == &#39;true&#39; || env.has_new_git_changes == &#39;true&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">run </span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">           git config --global user.name &#34;${{ github.actor }}&#34;
</span></span></span><span class="line"><span class="cl"><span class="sd">           git config --global user.email &#34;${{ github.actor }}@users.noreply.github.com&#34;
</span></span></span><span class="line"><span class="cl"><span class="sd">           
</span></span></span><span class="line"><span class="cl"><span class="sd">           git add content/music.md lastfm_tracks.json 
</span></span></span><span class="line"><span class="cl"><span class="sd">           
</span></span></span><span class="line"><span class="cl"><span class="sd">           # Only commit if there are changes in music.md or timestamps.
</span></span></span><span class="line"><span class="cl"><span class="sd">           git diff-index --quiet HEAD || git commit -m &#34;Update music.md and track list&#34;</span><span class="w">           
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Push changes back to repository</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">if</span><span class="p">:</span><span class="w"> </span><span class="l">env.has_new_tracks == &#39;true&#39; || env.has_new_git_changes == &#39;true&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">           git config --global user.name &#34;${{ github.actor }}&#34;
</span></span></span><span class="line"><span class="cl"><span class="sd">           git config --global user.email &#34;${{ github.actor }}@users.noreply.github.com&#34;
</span></span></span><span class="line"><span class="cl"><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">           # Push changes to main branch
</span></span></span><span class="line"><span class="cl"><span class="sd">           git checkout main
</span></span></span><span class="line"><span class="cl"><span class="sd">           git push origin main</span><span class="w">           
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">env</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">GITHUB_TOKEN</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.GITHUB_TOKEN }}EN }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	  </span><span class="nt">branch </span><span class="p">:</span><span class="w"> </span><span class="l">main</span><span class="w">
</span></span></span></code></pre></div><p>This is set to run every 4 hours but you can change that to however frequent you would prefer by changing the line under cron. You should also change the folder and file that it pushes to your git repository unless you want to keep it at contents/music.md.</p>
<h2 id="git-add-commit-and-push-to-your-repo">Git add, commit and push to your repo!</h2>
<p>Hopefully there won&rsquo;t be any errors along the way and it should run the python script, create your new music page and stay dormant until it is ready to update again. What this will not do is auto-push to your hosting service unless your repo is already setup with either another workflow or service like cloudflare where it will automatically update anytime there is a new build. Pushing it to your host of choice though only needs adding another job entry in your yml file though so it should be simple enough. If there are errors you can see them under Actions in your github repo. I currently have it giving updates as it goes through each process so finding where the problem is shouldn&rsquo;t be too hard.</p>
<p>Hopefully this helps some of you, please use it and create something much cooler as this is pretty basic but let me know what you have made so I can check it out!</p>
<p>Enjoy!</p>
<p>~Brent</p>
]]></content:encoded>
    </item>
    <item>
      <title>Add HTML Tags To Hugo Blogs - My Guide To Shortcodes</title>
      <link>https://brentter.com/notes/add-html-tags-to-hugo-pages/</link>
      <pubDate>Sun, 22 Sep 2024 23:49:25 -0700</pubDate>
      <guid>https://brentter.com/notes/add-html-tags-to-hugo-pages/</guid>
      <description>Hugo can render HTML on your blog if you add a shortcode in your layout folder. Here&amp;#39;s a quick guide on what code to add and where to add it!</description><content:encoded><![CDATA[ ]]>
               <![CDATA[<p>So I wanted to add a few features to this site but in order to do so I found the easiest way was to directly embed HTML tags into the page.</p>
<p>The only problem was Hugo will naturally ignore any HTML in posts/pages unless you do the following:</p>
<h2 id="add-a-shortcode-to-your-site">Add a shortcode to your site</h2>
<p>It&rsquo;s simple enough, just add a shortcode template in layouts/shortcodes/rawhtml.html with the following content:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">&lt;!-- raw html --&gt;
</span></span><span class="line"><span class="cl">{{.Inner}}
</span></span></code></pre></div><p>Then in whatever post or page you&rsquo;d like to use html in just enclose it in these tags:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">{{&lt; rawhtml &gt;}}
</span></span><span class="line"><span class="cl">&lt;p&gt;&lt;h2&gt; Brentter.com has all the &lt;strong&gt;BEST&lt;/strong&gt; hugo tips&lt;/h2&gt;&lt;/p&gt;
</span></span><span class="line"><span class="cl">{{&lt; /rawhtml &gt;}}
</span></span></code></pre></div><p>And the output will follow your HTML tags:<br>


<p><h2>Brentter.com has all the <strong>BEST</strong> hugo tips!</h2></p>

</p>
<p>Took me a hot minute to figure that out so hopefully this speeds up your discovery process a bit.</p>
<p>Enjoy!</p>
<p>~brent</p>
]]></content:encoded>
    </item>
    <item>
      <title>My Early Review Of Follow.is - The Best Rss App Now In Alpha</title>
      <link>https://brentter.com/blog/follow-the-best-rss-app-now-in-alpha/</link>
      <pubDate>Wed, 18 Sep 2024 08:39:05 -0700</pubDate>
      <guid>https://brentter.com/blog/follow-the-best-rss-app-now-in-alpha/</guid>
      <description>The new must-try RSS app named Follow.is is here and I&amp;#39;m going to show you why you should download it today and take it for a spin</description><content:encoded><![CDATA[ 
<figure class="entry-cover"><img loading="lazy" src="https://brentter.com/blog/follow-the-best-rss-app-now-in-alpha.webp" alt="Follow is a new RSS Reader and Infomation Browser now in alpha">
        
</figure>]]>
               <![CDATA[<h2 id="let-me-introduce-you-to-follow">Let Me Introduce You To Follow</h2>
<p><a href="https://follow.is" target="_blank">Follow</a>
 is the latest app to try and tackle the world of RSS aggregation and I think they might have done it.<br>
It is being developed by the same group behind <a href="https://rsshub.app/" target="_blank">RSSHub</a>
, the folks who created a unique way of being able to create RSS feeds for websites and platforms that typically didn&rsquo;t have them (or in some cases, didn&rsquo;t want them). It&rsquo;s currently in alpha-phase so an invitation is necessary to join but I was lucky enough to get one early. It&rsquo;s currently available for Linux, OSx, Windows and there&rsquo;s a web-version.  My current solution for RSS aggregation is a self-hosted version of FreshRSS. It&rsquo;s great as far as being easy to setup and light on resources but it&rsquo;s not so easy to get through your unread content to find something you want if it&rsquo;s been a while since you&rsquo;ve gone through your feeds (as in, missed a day or two). It&rsquo;s an RSS feed reader like all the others, nothing special other than how little server resources it requires for an insane amount of feeds. So I&rsquo;m always looking out for something new, and here&rsquo;s why I think Follow has potential.</p>
<h2 id="design-and-layout">Design and Layout</h2>
<p>The feeds are designated by a global category of Notifications, Audio, Video, Pictures, Social Media and Articles. The layout for each is somewhat similar but I&rsquo;ll show you the difference and why it works well. In the Settings area you have a wide variety of options from selecting your own choice of font for not only the UI of the app but also for the content.</p>
<p>It has the option to render the inline style of the original web page (which is a feature I haven&rsquo;t seen before for a RSS reader) and a wide selection of options for what style to render any code in (I tend to stick with nord). Built-in integration with Eagle, Readwise and Instapaper is also provided if you&rsquo;re migrating over from those sites. Speaking of which, if you already are using a RSS reader you can import OPML files so moving your collection over is a one click process. Seeing how some of the main developers speak Chinese, the app has built-in support for multiple languages as well as the option to have content spoken out loud to you in a variety of different style of voices. Now for the content itself.</p>
<h2 id="photos">Photos</h2>
<p><img loading="lazy" src="/blog/follow-img/followphotos.webp" alt="Follows Photo Feed"  />
</p>
<p>First off let&rsquo;s go with one of the sites I am following that is photo-centric, <a href="https://apod.nasa.gov/apod/archivepix.html" target="_blank">The NASA Picture Of The Day</a>
 site. It arranges the photos nicely, loads them quickly and if I click on one it will enlarge it in-screen so I don&rsquo;t have to load the whole page. If I wanted to visit the actual page in my browser I&rsquo;d just click on the title/website name below the image. It makes browsing image sites a breeze.</p>
<h2 id="videos">Videos</h2>
<p><img loading="lazy" src="/blog/follow-img/followvideos.webp" alt="Follows Video Feed"  />
</p>
<p>Here&rsquo;s just a sampling of some of the youtubers I tend to enjoy, as you can see this my HomeLab folder and these are the latest from the grouping (all homelab rockstars in my opinion). If you mouse over any of the videos it will start to play them muted so you can see the basic content and if you click on one of them it will quickly load &amp; start the video full screen in the app. The video can also be manipulated using keyboard shortcuts so you&rsquo;re not constantly having to click on the controls. It&rsquo;s a much more of an enjoyable experience than on the actual Youtube website but if you prefer that then you can also just click the video title/channel name and it will open in your browser. I was impressed with just how fast each video loaded, even from sites that were hosting their videos themselves.</p>
<h2 id="articles">Articles</h2>
<p><img loading="lazy" src="/blog/follow-img/followblogposts.webp" alt="Follows Blog Feed"  />
<br>
So this is usually the meat and potatoes of most RSS readers, a steady collection of blogs/websites separated into category or tags. There&rsquo;s no difference here, you can setup as many folders as your heart desires and check out the latest from a single site, all the sites in a folder or all the sites together. You can easily mark any grouping as read/unread and even reload older content if you want to go back to something from the past that you already looked at. If one of your sites doesn&rsquo;t naturally allow all the content in the rss feed you can click the Readability button in the top right of the app and it will attempt to load the content. So far the only problem I&rsquo;ve come across using that feature was on a few sites where only the text of the post would load.</p>
<h2 id="audio">Audio</h2>
<p><img loading="lazy" src="/blog/follow-img/followpodcasts.webp" alt="Follows Audio Feed"  />
</p>
<p>So when I&rsquo;m out at the gym or walking around town I tend to listen to podcasts and Follow has its own built-in audio player. In the image above you can see an example of one of my favorite podcasts to listen to, <a href="https://darknetdiaries.com/" target="_blank">Darknet Diaries</a>
. The text from the episode page has loaded on the right but if you look in the bottom left corner you can see that the podcast has started to play in it&rsquo;s own little audio player. If you mouse-over the player you have options to speed the audio up, go back or forward 10seconds, visit the podcast site and even an option to download it. Just like with the video player, these can all be controlled via shortcuts.</p>
<h2 id="notifications">Notifications</h2>
<p><img loading="lazy" src="/blog/follow-img/followgithub.webp" alt="Follows Notification Feed"  />
</p>
<p>So this is one of my favorite parts of this app. Using the RSSHub integration, you can create feeds for a lot of important content that usually would be difficult to run in an RSS reader. The example on screen is a feed that tracks <a href="https://github.com/DIYgod" target="_blank">DIYGod</a>
&rsquo;s (one of the main developers of Follow) github activity. As you can see this was a git push to one of his repositories and includes his commit message and the accompanying tag associated with it (which if you click on will take you to the github page).</p>
<p>While that was a last minute addition for this post, I do have a folder following github repo issues for a few projects that makes it easy to track. In addition to that I have a &ldquo;what&rsquo;s trending&rsquo; folder that has feeds for what&rsquo;s currently hot on producthunt.com, github trends for python and go, the latest in HackerNews:Show and a feed for Google&rsquo;s Daily Search Trends US. You can customize it even further if you wanted to (think: keyword or brand tracking). It makes keeping up with what&rsquo;s fresh very easy.</p>
<h2 id="social-media">Social Media</h2>
<p><img loading="lazy" src="/blog/follow-img/followsocialmedia.webp" alt="Follows SocialMedia Feed"  />
</p>
<p>So just like the category says, this is where I list some feeds from social media. Some of these accounts I actually follow and just don&rsquo;t want to have to log in to see whats new and some are just fun to read. It&rsquo;s a mixture of twitter and mastodon and the app tracks them perfectly. It even shows the embedded emojis correctly. Something that you can&rsquo;t see very well in this photo but I will explain how it works next is that you&rsquo;ll notice all of the social feeds on the screen are in English. On twitter these were written in Chinese. How did it get translated? That brings me to the next cool feature, Actions.</p>
<h2 id="actions">Actions</h2>
<p><img loading="lazy" src="/blog/follow-img/followactions.webp" alt="Follows Actions"  />
</p>
<p>So Follow has a feature called Actions where you can setup little automatons within the app itself. The one pictured is my automation where if the content is from the social media section, it auto-translates it to English. Very helpful when you&rsquo;re following a multi-cultural crowd of developers. They&rsquo;re working on adding more features to it but for now you can use it to filter literally anything and then perform actions ranging from having the content summarized by AI, translated, blocked or even rewritten. It has the ability to also add even more actions depending on triggers of that first one. This looks like it&rsquo;d be great for brand management. You can set it to track mentions of your brand or @&rsquo;s on twitter and then have it perform an action as a result.</p>
<h2 id="adding-feeds">Adding Feeds</h2>
<p><img loading="lazy" src="/blog/follow-img/followaddfeed.webp" alt="Adding A Feed in Follow"  />
</p>
<p>So here&rsquo;s how you add a new feed in Follow. You can either just type the website in the search box and it will go off and try to auto-discover the feed (which sometimes works, sometimes doesn&rsquo;t) or you can paste the actual URL to the feed itself. Either way it will bring up a window showing all of the feeds that have been found with a little preview asking if you want to add it and what folder would you like to put it under (also you can change the name of the feed). Where this gets interesting is the integration with web3 and RSSHub. So this app allows you to follow someone&rsquo;s crypto account if they&rsquo;re based on certain platforms (i.e. RSS3). You can track their socials, trades/transactions, collections, nfts, etc.. You can also add feeds using the RSSHub integration which is how I was easily able to add specific github and DockerHub feeds. They have a whole slew of different options and app feeds available. You can find that list on <a href="https://docs.rsshub.app/routes/popular" target="_blank">The RSSHub Routes</a>
 page. They even have an integration for pornhub feeds, if that&rsquo;s what you are into.</p>
<h2 id="rss3crypto-framework">RSS3/Crypto Framework</h2>
<p><img loading="lazy" src="/blog/follow-img/followPowertokens.webp" alt="Follows Power Token"  />
</p>
<p>So this entire app is linked with a web3 blockchain that grants each user 20 free $POWER crypto each day as long as you hit the &ldquo;claim&rdquo; button. You can use these to tip feeds that you like (and the owner can claim that tip), you can use them to claim ownership of your own feed/content, and currently you can use them to generate invite codes for Follow (it requires 100 coins per invite so it&rsquo;ll take 5 days for most users to get their first one). I&rsquo;m not too keen on crypto but in the screenshot you can see my current balance (and that I recently bought an invite for someone) as well as a little more information on the service. It&rsquo;s all on the VSL blockchain.</p>
<h2 id="the-ai-top-news-of-todayyesterdaynight-before-last">The AI Top News Of Today/Yesterday/Night Before Last</h2>
<p><img loading="lazy" src="/blog/follow-img/followstopnews.webp" alt="AI Top News Of The Day"  />
</p>
<p>Here&rsquo;s another feature I&rsquo;ve never seen used before. For any category you can read what the AI thinks the top stories (from your feeds) are of today, yesterday or the day before that. It might need a little work but it&rsquo;s a great feature if you just want to see the highlights of what&rsquo;s going on.</p>
<h2 id="public-profile">Public Profile</h2>
<p><img loading="lazy" src="/blog/follow-img/followprofile.webp" alt="Follows Public Profile Pages"  />
</p>
<p>The last thing I&rsquo;ll show you from the app is that everyone gets their own public profile page. You can decide what doesn&rsquo;t show up on it by making a feed &ldquo;private&rdquo; but it&rsquo;s basically a list of who you follow. Currently you don&rsquo;t get a personalized URL, but that is a feature that they claim is coming soon. This is another great branding idea once they implement that as you can have feeds related to your own work that go beyond just your blog url (product updates, social media feeds of users saying positive things about you, etc..). Here is a link to mine if you want to see what it looks like (I&rsquo;ve only just started using this so there&rsquo;s still a whole lot more sites I still need to add). <a href="https://app.follow.is/share/users/Brentter" target="_blank">Brentter&rsquo;s Profile</a>
</p>
<p>So there you have it. I&rsquo;m obviously a fan, and seeing how it&rsquo;s a free app, I&rsquo;m definitely not a paid one either. While there are still bugs popping up here and there it seems like they&rsquo;re getting worked on pretty quickly by the looks of their very busy github page. Join their <a href="https://discord.com/invite/followapp" target="_blank">discord</a>
 if you&rsquo;re looking for an invite (though I&rsquo;d also follow their developers on twitter too as they tend to post invite codes in both places).</p>
<p>Cheers,<br>
Brent</p>
]]></content:encoded>
    </item>
    <item>
      <title>Wget Tutorial - A Complete Guide To Downloading Websites</title>
      <link>https://brentter.com/notes/wget-quick-tutorial-to-download-everything/</link>
      <pubDate>Fri, 13 Sep 2024 05:05:57 -0700</pubDate>
      <guid>https://brentter.com/notes/wget-quick-tutorial-to-download-everything/</guid>
      <description>A Quick tutorial on using wget to download anything off the internet. Perfect for anyone looking for a easy to use tool that works on any platform</description><content:encoded><![CDATA[ ]]>
               <![CDATA[<p>So let&rsquo;s say you want to download a link from a website, or maybe two links, heck maybe you want the whole site and everything in it. In that case wget is your friend. I&rsquo;m going to assume if you found this page you already can figure out how to download/install wget.</p>
<h2 id="so-lets-start-with-the-basics">So let&rsquo;s start with the basics.</h2>
<p>To download a single file:</p>
<p><code> wget http://example.com/supercool.zip</code></p>
<p>Multiple files? Just add a space between them</p>
<p><code>wget https://example.com/supercool.zip https://example.com/superbad.zip</code></p>
<p>If you have a whole list of files you need to download throw them in a txt file.</p>
<p><code>wget -i urls.txt</code></p>
<p>Use -O if you want to rename the file you&rsquo;re downloading (it&rsquo;ll save the file as homework.zip):</p>
<p><code>wget -O homework.zip https://example.com/xxx.zip</code></p>
<p>If you&rsquo;re getting rate-limited or have a bad connection, you can have it keep retrying (50 times in this case):</p>
<p><code>wget -tries=50 https://example.com/cool.zip</code></p>
<p>Did you spill your chocolate milk on the keyboard mid-download and it aborted? No problem, resume downloads with -c</p>
<p><code>wget -c https://example.com/10000000gig.zip</code></p>
<p>Change the user agent and pretend to be a web browser</p>
<p>✅ <a href="https://gist.github.com/brentter/5e2f57de4617bb4b5ef9bf963bd24eb0" target="_blank">Here&rsquo;s a list of updated user agents to use</a>
 ✅</p>
<p><code>wget --user-agent=&quot;Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:129.0) Gecko/20100101 Firefox/129.0&quot; https://example.com/cia.zip</code></p>
<p>Ok, now let&rsquo;s say you have a directory full of links you want</p>
<p><code>wget -r -nH --cut-dirs=2 -nr -R &quot;index.html*&quot; http://website.com/dir1/dir2/data</code></p>
<ul>
<li>The -r tells it to download recursively</li>
<li>The -nH tells it not to include the domain name.</li>
<li>We don&rsquo;t need those first two directories so &ndash;cut-dirs=2 ignores them</li>
<li>The -nr makes sure it doesn&rsquo;t go back a directory if there&rsquo;s a backlink somewhere on the page</li>
<li>The -R &ldquo;index.html*&rdquo; rejects downloading any index.html files because this is a link directory.<br>
That means all you will download are the links, and they won&rsquo;t be in two levels of empty directories with blank html files and long URLS.</li>
</ul>
<h2 id="now-lets-download-the-entire-website">Now let&rsquo;s download the entire website</h2>
<p><code>wget --mirror -page-requisites -k -P TheFilesGo/Here/ https://www.funtimesbiz.com</code></p>
<ul>
<li>The -mirror tells it to download everything structure wise identical to the site&rsquo;s original content</li>
<li>The -page-requisites tells it to also grab any secondary files like css, js and images</li>
<li>The -k tells it to convert any links to the local directory structure so they&rsquo;re not broken in your local copy</li>
<li>The -P  TheFilesGo/Here tells it to download everything into that directory instead of the local one.</li>
</ul>
<p>Now, let&rsquo;s say you&rsquo;re grabbing a lot of files and you don&rsquo;t want to get rate-limited/banned, you can add <code>-w 3</code> and it will wait three seconds between transfers (adjust as needed). If you also added after that <code>--random-wait</code> it would randomize the time between each transfer between 0.5 and 1.5 seconds of the value chosen with -w.</p>
<h2 id="a-quick-run-down-of-other-useful-wget-options">A quick run down of other useful Wget options:</h2>
<ul>
<li><code>-q</code> is for quiet mode, only major notifications will be shown.</li>
<li><code>-A gif,jpg</code>  would tell it to only download gif and jpg files.</li>
<li><code>-A &quot;resume&quot;</code> would only download files that start with the word resume.</li>
<li><code>-R &quot;cupcakes&quot;</code> would NOT download anything with the word cupcakes.</li>
<li><code>-o logfile.log</code> will put all the info it would have posted to the screen in a logfile.</li>
<li><code>-b</code> will download files in the background, useful when you have other stuff to do in the terminal.</li>
<li><code>--no-check-certificate</code> will skip any certificate check in case the source has an invalid or missing SSL cert.</li>
<li><code>--load-cookies cookies.txt</code> will load the cookies from the text file and send them to the site, great for bypassing having to re-login each time.</li>
<li><code>--save-cookies file.txt</code> will save any non-expired cookies to file before exiting the task.</li>
</ul>
<p>Now you should be ready to scrape that site you&rsquo;ve been after. A fun site to practice on is <a href="https://the-eye.eu/" target="_blank">The-Eye.eu</a>
 as it contains terabytes of directory links/media.</p>
<p>Wget is your friend. DNS is  your enemy.</p>
]]></content:encoded>
    </item>
    <item>
      <title>/Uses - All of My Frequently Used Hardware And Software</title>
      <link>https://brentter.com/uses/</link>
      <pubDate>Thu, 12 Sep 2024 02:43:40 -0700</pubDate>
      <guid>https://brentter.com/uses/</guid>
      <description>This is my /Uses page where I list out all the various hardware and software that I use on any given day. Systems include OSX, iOS and Windodws 10</description><content:encoded><![CDATA[ 
<figure class="entry-cover"><img loading="lazy" src="https://brentter.com/Uses.webp" alt="/Uses - What Apps and Hardware I Use">
        
</figure>]]>
               <![CDATA[<p>Well, since everyone else seems to have a cool /uses page, I figured I might as well put one up myself.<br>
So while I do have a macbook pro that is used very often, I&rsquo;m running Debian on it. So most of my applications that I tend to use are either Linux or Windows based. Sorry not sorry (though if you were to donate a new M3/M4 I&rsquo;d be willing to make the switch).</p>
<h2 id="computers">Computers</h2>
<ul>
<li>
<p>MacBookPro 11,1 (2014?)</p>
<ul>
<li>8GB DDR3</li>
<li>Intel Core i5-4258U x 2</li>
<li>Mesa Intel Iris Graphics 5100 (HSW GT3)</li>
<li>250gb HD</li>
<li>Debian 12 Bookworm w/ Xfce DE (with the <a href="https://github.com/ricotz/plank" target="_blank">Plank dock</a>
 becuase I love it)</li>
</ul>
</li>
<li>
<p>My Gaming/Workstation PC</p>
<ul>
<li>ASUS Prime Z590-P Motherboard</li>
<li>11th Gen Inten Core i7-11700K @ 3.6GHz</li>
<li>NVIDIA GeForce RTX 3080Ti</li>
<li>64GB ddr4 (2x 32GB)</li>
<li>2TB NVMe</li>
<li>27&quot; Asus ROG PG278Q (2560x1400 @ 144hz) - for play</li>
<li>24&quot; Dell S2421NX (1920X1080 @ 60hz) - for work.</li>
<li>Windows 10 Pro</li>
</ul>
</li>
</ul>
<p><img loading="lazy" src="/terminal-icons.png" alt="Terminal Icons Powershell Module"  />
</p>
<h2 id="applications">Applications</h2>
<ul>
<li>Terminal w/ <a href="https://ohmyz.sh/" target="_blank">oh-my-zsh</a>
 running gruvbox theme for linux + WSL</li>
<li>Terminal w/ <a href="https://ohmyposh.dev/" target="_blank">oh-my-posh</a>
 running gruvbox theme &amp; <a href="https://github.com/devblackops/Terminal-Icons" target="_blank">Terminal Icons</a>
 for Powershell</li>
<li><a href="https://alacritty.org/" target="_blank">Alacritty</a>
 with Oh my Zsh and Tmux for the laptop.</li>
<li>Spotify w/ <a href="https://spicetify.app/" target="_blank">Spicetify</a>
 mod - It makes the app so much better.</li>
<li>Steam - Gaming</li>
<li>Discord - Gaming and keeping in touch with friends/community</li>
<li><a href="https://eartrumpet.app/" target="_blank">EarTrumpet</a>
 - Best windows sound manager ever and it&rsquo;s free.</li>
<li>Git CLI - local Gitea for private stuff, github for everything else</li>
<li>VSCode w/ you guessed it, the gruvbox theme.</li>
<li><a href="https://obsidian.md/" target="_blank">Obsidian</a>
 - I use this religiously, with templates and plugins you can do literally anything with this free software.</li>
<li><a href="https://raindrop.io/" target="_blank">Raindrop.io</a>
 - One of the best bookmark apps around&hellip;free as well.</li>
<li><a href="https://follow.is" target="_blank">Follow</a>
 - RSS Feed Reader</li>
<li>Firefox w/ uBlock Origin, <a href="https://github.com/bpc-clone/bypass-paywalls-firefox-clean" target="_blank">Bypass Paywalls Clean</a>
, <a href="https://addons.mozilla.org/en-US/firefox/addon/multi-account-containers/" target="_blank">Multi-Account-Containers</a>
, Raindrop.io and 1password.</li>
<li><a href="https://getsharex.com/" target="_blank">ShareX</a>
 - Best screenshot app out there (and it&rsquo;s free).</li>
<li><a href="https://www.flowlauncher.com/" target="_blank">Flow Launcher</a>
 - Great keyboard shortcut/launcher/task-master for windows (also free)</li>
<li><a href="https://www.qbittorrent.org/" target="_blank">Qbittorent</a>
 - for linux isos of course.</li>
<li>Figma &amp; Affinity Designer 2 for design/mockups</li>
<li><a href="https://www.gimp.org/" target="_blank">Gimp</a>
 for photoshop needs</li>
<li>ProtonVPN - For when I&rsquo;m out and about</li>
<li>ProtonMail - recently switched off of my google workspace plan and have never been happier.</li>
<li>1Password - Been using them since they first came out and they&rsquo;ve only gotten better.</li>
<li>Docker</li>
</ul>
<h2 id="mobile">Mobile</h2>
<p>Iphone 15 Pro</p>
<ul>
<li><a href="https://proton.me/" target="_blank">ProtonMail</a>
</li>
<li><a href="https://1password.com/" target="_blank">1Password</a>
</li>
<li>Firefox</li>
<li><a href="https://www.authy.com/" target="_blank">Authy</a>
 - I regret having used this as it&rsquo;s one of the few apps you can&rsquo;t export your accounts from in case you want to move to a different provider.</li>
<li><a href="https://www.meetcarrot.com/weather/" target="_blank">Carrot</a>
 - It&rsquo;s a weather app with an attitude, also pretty damn accurate.</li>
<li><a href="https://signal.org/" target="_blank">Signal</a>
 - End to end secure communications</li>
<li><a href="https://github.com/Dimillian/IceCubesApp" target="_blank">Ice Cubes</a>
 - Mastodon client</li>
<li><a href="https://netnewswire.com/" target="_blank">NetNewWire</a>
 - To connect to my FreshRSS setup</li>
</ul>
<h2 id="soundmusic">Sound/Music</h2>
<ul>
<li>Sennheiser 599 Open back</li>
<li><a href="https://www.linsoul.com/products/kiwi-ears-quintet" target="_blank">Linsoul Kiwi Ears Quintet IEM</a>
<br>
- <a href="https://www.smsl-audio.com/portal/product/detail/id/884.html" target="_blank">SMSL D10 Pocket Portable Headphone Amp</a>
 (this and the Linsoul Kiwi are my everyday carry)</li>
<li>Spotify, <a href="https://somafm.com/" target="_blank">SomaFM</a>
, <a href="https://relisten.net/" target="_blank">ReListen</a>
 for all the grateful dead on archive.org and then my own instance of <a href="https://www.navidrome.org/" target="_blank">Navidrome</a>
</li>
</ul>
<h2 id="homelab">Homelab</h2>
<ul>
<li>
<p><a href="https://www.bhphotovideo.com/c/product/1217946-REG/hp_p5w24ut_aba_elitedesk_800_g2_small.html" target="_blank">EliteDesk 800 G2 Mini</a>
 - 1TB SSD, 500GB NVMe, 32GB DDR4 Ram</p>
<ul>
<li>Running <a href="https://www.proxmox.com/en/" target="_blank">Proxmox</a>
 which hosts:
<ul>
<li><a href="https://www.navidrome.org/" target="_blank">Navidrome</a>
 - Lightweight multi-user music streaming to any device</li>
<li><a href="https://github.com/Ravencentric/awesome-arr" target="_blank">Radarr, Sonarr, NzbGet, ruTorrent, Overseerr, Plex</a>
 - Media Aquisition</li>
<li><a href="https://github.com/dgtlmoon/changedetection.io" target="_blank">ChangeDetection.io</a>
 - Monitor any webpages to see if any part of them change and get notified.</li>
<li><a href="https://www.activepieces.com/" target="_blank">ActivePieces</a>
 - Free open-source Zapier replacement</li>
<li><a href="https://freshrss.org/index.html" target="_blank">FreshRSS</a>
 - So many feeds!</li>
<li><a href="https://github.com/ajnart/homarr" target="_blank">Homarr</a>
 - Customizable homepage for easy access &amp; stats on all my self-hosted docker containers</li>
<li><a href="https://about.gitea.com/" target="_blank">Gitea</a>
 - local git (But I want to try <a href="https://github.com/charmbracelet/soft-serve" target="_blank">Soft-Serve</a>
)</li>
</ul>
</li>
</ul>
</li>
<li>
<p><a href="https://www.raspberrypi.com/products/raspberry-pi-4-model-b/" target="_blank">Raspberry Pi 4</a>
</p>
<ul>
<li><a href="https://pi-hole.net/" target="_blank">Pi-hole</a>
 - network ad blocking &amp; DNS server.</li>
</ul>
</li>
</ul>
<h2 id="hosting">Hosting</h2>
<ul>
<li><a href="https://www.porkbun.com" target="_blank">Porkbun</a>
 - I use to own a LOT of domains and have used many registrars but Porkbun right now is the best in my opinion, great customer support, they&rsquo;re US based (Oregon!), a working API and constant deals. Plus they haven&rsquo;t had any of the issues that other big registrars have faced regarding locking people out of their accounts, getting social-engineered or straight up hacked. Plus they&rsquo;ve made little <a href="https://porkbun.com/awesome" target="_blank">piggie icons</a>
 for their popular domains.</li>
<li><a href="https://zerossl.com/" target="_blank">ZeroSSL</a>
 - All the same benefits of Let&rsquo;s Encrypt but with no rate limit, a working API and you can verify via email</li>
<li><a href="https://www.hetzner.com/cloud/" target="_blank">Hetzner</a>
 - Not only is their cloud offering on decent equipment, but fast and cheap. Why rely on 3rd party services when you do it yourself?</li>
<li><a href="https://ioflood.com/" target="_blank">IOFlood</a>
 - One of the best bare-metal dedicated server hosts in the US. They only do one thing, bare-metal servers. And they&rsquo;re a no bs, here&rsquo;s the testing/Passmark scores on this rig, honest type of guys. Plus they have servers in various configs. 10Gbps connections out of Phoenix, AZ.</li>
<li><a href="https://www.cloudflare.com/learning/cdn/what-is-a-cdn/" target="_blank">Cloudflare</a>
 - CDN/DDOS protection on the cheap (free depending on your needs)</li>
<li><a href="https://www.backblaze.com" target="_blank">BackBlaze</a>
 - $6 per TB and they free egress if downloading through one of their partners like Cloudflare.</li>
<li><a href="https://www.fastmail.com" target="_blank">Fastmail.com</a>
 and <a href="https://proton.me" target="_blank">Protonmail</a>
 - Both cheap, secure and have great support for teams/small biz. I&rsquo;ve used both.</li>
</ul>
<p>I&rsquo;m skipping a bunch of categories and most likely leaving a bunch of stuff out but if you have any questions, especially about self-hosting or home-networking in general feel free to reach out! I love talking about FOSS and DIY setups.</p>
<p>Cheers,</p>
<p>~Brent</p>
]]></content:encoded>
    </item>
    <item>
      <title>How To Extract All Archived Links From The Wayback Machine</title>
      <link>https://brentter.com/notes/getting-all-known-links-from-wayback-machine/</link>
      <pubDate>Wed, 11 Sep 2024 20:43:42 -0700</pubDate>
      <guid>https://brentter.com/notes/getting-all-known-links-from-wayback-machine/</guid>
      <description>Get all the URL links that the WayBack Machine has for a domain. Bring back to life old archived pages or check up on old designs.</description><content:encoded><![CDATA[ ]]>
               <![CDATA[<h2 id="how-to-get-all-the-wayback-machine-links-for-a-domain">How To Get All The Wayback Machine Links For A Domain</h2>
<p>Ever wanted to see everything the <a href="http://web.archive.org/" target="_blank">WayBack Machine</a>
 has saved for a domain? Ever wanted to get all of it at once? Here are a couple tools including an entire WayBackMachine OSINT framework giving you everything you need to get everything you want from the archive. Here&rsquo;s the easy one, waybackurls.py:</p>
<p>Just give it the url you want it to search for and if you DO want it to include subdomains add True at the end.</p>
<p><code>python3 waybackurls.py example.com true</code>   &lt;&mdash;with subdomains</p>
<p><code>python3 waybackurls.py example.com</code>    &lt;&mdash;without subdomains</p>
<p>It&rsquo;ll save the results in a nice little json file in the same directory.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">requests</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">sys</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">json</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">waybackurls</span><span class="p">(</span><span class="n">host</span><span class="p">,</span> <span class="n">with_subs</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">with_subs</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">url</span> <span class="o">=</span> <span class="s1">&#39;http://web.archive.org/cdx/search/cdx?url=*.</span><span class="si">%s</span><span class="s1">/*&amp;output=json&amp;fl=original&amp;collapse=urlkey&#39;</span> <span class="o">%</span> <span class="n">host</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">url</span> <span class="o">=</span> <span class="s1">&#39;http://web.archive.org/cdx/search/cdx?url=</span><span class="si">%s</span><span class="s1">/*&amp;output=json&amp;fl=original&amp;collapse=urlkey&#39;</span> <span class="o">%</span> <span class="n">host</span>
</span></span><span class="line"><span class="cl">    <span class="n">r</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">results</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">json</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">results</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">argc</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">argc</span> <span class="o">&lt;</span> <span class="mi">2</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;Usage:</span><span class="se">\n\t</span><span class="s1">python3 waybackurls.py &lt;url&gt; &lt;include_subdomains:optional&gt;&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">host</span> <span class="o">=</span> <span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">with_subs</span> <span class="o">=</span> <span class="kc">False</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">argc</span> <span class="o">&gt;</span> <span class="mi">3</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">with_subs</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">urls</span> <span class="o">=</span> <span class="n">waybackurls</span><span class="p">(</span><span class="n">host</span><span class="p">,</span> <span class="n">with_subs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">json_urls</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">urls</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">urls</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">filename</span> <span class="o">=</span> <span class="s1">&#39;</span><span class="si">%s</span><span class="s1">-waybackurls.json&#39;</span> <span class="o">%</span> <span class="n">host</span>
</span></span><span class="line"><span class="cl">        <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="s1">&#39;w&#39;</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">f</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">json_urls</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;[*] Saved results to </span><span class="si">%s</span><span class="s1">&#39;</span> <span class="o">%</span> <span class="n">filename</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;[-] Found nothing&#39;</span><span class="p">)</span>
</span></span></code></pre></div><p>This is the very same wayback script you&rsquo;ll find in Kali Linux and it originated from a github gist back in 2017 by <a href="https://gist.github.com/mhmdiaa/adf6bff70142e5091792841d4b372050" target="_blank">mhmdiaa</a>
</p>
<p>Since then a bunch of variations have sprung up including one written in the <a href="https://github.com/tomnomnom/waybackurls" target="_blank">go language</a>
.</p>
<p>Once you&rsquo;re ready, there&rsquo;s actually a whole python package/cli library that&rsquo;s pretty useful in  interacting with all 3 of the API systems that they have for the WayBack Machine. It&rsquo;s called <a href="https://github.com/akamhy/waybackpy" target="_blank">Waybackpy</a>
.  It&rsquo;s got classes built in to work with the SavePageNow/Save API, their CDX Server API and the Availability API. That one&rsquo;s great because it has built in functions for only grabbing snapshots or the closest saves to a specific date or even the newest vs the oldest recorded archive for a page. You can download that one using pip or conda:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">pip install waybackpy -U
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">conda install -c conda-forge waybackpy
</span></span></code></pre></div><p>Now that you&rsquo;re pretty seasoned in using the WayBack Machine, I think it&rsquo;s time for you to learn just how much information the Wayback Machine actually has inside those archives. It&rsquo;s more than just html, javascript and images.. it&rsquo;s a goldmine of data.</p>
<p><img loading="lazy" src="/notes/Chronos.webp" alt="Chronos"  />
</p>
<h2 id="chronos---an-osint-framework-for-the-wayback-machine">Chronos - An OSINT Framework For The Wayback Machine</h2>
<p>And then, like a WayBack Machine demi-god looking down on us mortals and feeling a bit generous, mhmdiaa, the very same one who wrote the script featured above, gave us <a href="https://github.com/mhmdiaa/chronos" target="_blank">Chronos</a>
. It&rsquo;s an entire OSINT framework for the Wayback Machine Archives. Written in go it offers not only a lot of fun oportunities but also a whole lot of options to modify the output/parameters to whatever your need might be.<br>
A couple highlighted features:<br>
- Extract endpoints and URLs from archived JavaScript code</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl">    <span class="nx">chronos</span> <span class="o">-</span><span class="nx">target</span> <span class="s">&#34;spltjs.com/*&#34;</span> <span class="o">-</span><span class="nx">module</span> <span class="nx">jsluice</span> <span class="o">-</span><span class="nx">output</span> <span class="nx">js_endpoints</span><span class="p">.</span><span class="nx">json</span>
</span></span></code></pre></div><ul>
<li>Calculate archived favicon hashes</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl">    <span class="nx">chronos</span> <span class="o">-</span><span class="nx">target</span> <span class="s">&#34;playboy.com/favicon.ico&#34;</span> <span class="o">-</span><span class="nx">module</span> <span class="nx">favicon</span> <span class="o">-</span><span class="nx">output</span> <span class="nx">favicon_hashes</span><span class="p">.</span><span class="nx">json</span>
</span></span></code></pre></div><ul>
<li>Extract archived page titles</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl">    <span class="nx">chronos</span> <span class="o">-</span><span class="nx">target</span> <span class="s">&#34;github.com&#34;</span> <span class="o">-</span><span class="nx">module</span> <span class="nx">html</span> <span class="o">-</span><span class="nx">module</span><span class="o">-</span><span class="nx">config</span> <span class="s">&#34;html.title=//title&#34;</span> <span class="o">-</span><span class="nx">snapshot</span><span class="o">-</span><span class="nx">interval</span> <span class="nx">y</span> <span class="o">-</span><span class="nx">output</span> <span class="nx">titles</span><span class="p">.</span><span class="nx">json</span>
</span></span></code></pre></div><ul>
<li>Extract paths from archived robots.txt files</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl">    <span class="nx">chronos</span> <span class="o">-</span><span class="nx">target</span> <span class="s">&#34;pornhub.com/robots.txt&#34;</span> <span class="o">-</span><span class="nx">module</span> <span class="nx">regex</span> <span class="o">-</span><span class="nx">module</span><span class="o">-</span><span class="nx">config</span> <span class="err">&#39;</span><span class="nx">regex</span><span class="p">.</span><span class="nx">paths</span><span class="p">=</span><span class="o">/</span><span class="p">[^</span><span class="err">\</span><span class="nx">s</span><span class="p">]</span><span class="o">+</span><span class="err">&#39;</span> <span class="o">-</span><span class="nx">output</span> <span class="nx">robots_paths</span><span class="p">.</span><span class="nx">json</span>
</span></span></code></pre></div><ul>
<li>Extract URLs from archived sitemap.xml files</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl">    <span class="nx">chronos</span> <span class="o">-</span><span class="nx">target</span> <span class="s">&#34;bestbuy.com/sitemap.xml&#34;</span> <span class="o">-</span><span class="nx">module</span> <span class="nx">xml</span> <span class="o">-</span><span class="nx">module</span><span class="o">-</span><span class="nx">config</span> <span class="s">&#34;xml.urls=//urlset/url/loc&#34;</span> <span class="o">-</span><span class="nx">limit</span> <span class="o">-</span><span class="mi">5</span> <span class="o">-</span><span class="nx">output</span> <span class="nx">sitemap_urls</span><span class="p">.</span><span class="nx">json</span>
</span></span></code></pre></div><ul>
<li>Extract endpoints from archived API documentation</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl">    <span class="nx">chronos</span> <span class="o">-</span><span class="nx">target</span> <span class="s">&#34;https://docs.gitlab.com/ee/api/api_resources.html&#34;</span> <span class="o">-</span><span class="nx">module</span> <span class="nx">html</span> <span class="o">-</span><span class="nx">module</span><span class="o">-</span><span class="nx">config</span> <span class="err">&#39;</span><span class="nx">html</span><span class="p">.</span><span class="nx">endpoint</span><span class="p">=</span><span class="c1">//code&#39; -output api_docs_endpoints.json
</span></span></span></code></pre></div><ul>
<li>Find S3 buckets in archived pages</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl">    <span class="nx">chronos</span> <span class="o">-</span><span class="nx">target</span> <span class="s">&#34;github.com&#34;</span> <span class="o">-</span><span class="nx">module</span> <span class="nx">regex</span> <span class="o">-</span><span class="nx">module</span><span class="o">-</span><span class="nx">config</span> <span class="err">&#39;</span><span class="nx">regex</span><span class="p">.</span><span class="nx">s3</span><span class="p">=[</span><span class="nx">a</span><span class="o">-</span><span class="nx">zA</span><span class="o">-</span><span class="nx">Z0</span><span class="o">-</span><span class="mi">9</span><span class="o">-</span><span class="err">\</span><span class="p">.</span><span class="err">\</span><span class="nx">_</span><span class="p">]</span><span class="o">+</span><span class="err">\</span><span class="p">.</span><span class="nf">s3</span><span class="p">(</span><span class="err">?</span><span class="p">:</span><span class="o">-</span><span class="p">[</span><span class="o">-</span><span class="nx">a</span><span class="o">-</span><span class="nx">zA</span><span class="o">-</span><span class="nx">Z0</span><span class="o">-</span><span class="mi">9</span><span class="p">]</span><span class="o">+</span><span class="p">)</span><span class="err">?\</span><span class="p">.</span><span class="nx">amazonaws</span><span class="err">\</span><span class="p">.</span><span class="nx">com</span><span class="err">&#39;</span> <span class="o">-</span><span class="nx">limit</span> <span class="o">-</span><span class="nx">snapshot</span><span class="o">-</span><span class="nx">interval</span> <span class="nx">y</span> <span class="o">-</span><span class="nx">output</span> <span class="nx">s3_buckets</span><span class="p">.</span><span class="nx">json</span>
</span></span></code></pre></div><h2 id="modules-include">Modules include:</h2>
<pre><code>- regex - Extract regex matches
- jsluice - Extract URLs and endpoints from Javascript code using jsluice
- html - Query HTML documents using Xpath expressions
- xml - Query XML documents using XPath expressions
- favicon - Calculate favicon hashes 
- full - Get the full content of snapshots
</code></pre>
<h2 id="command-line-options">Command-Line Options:</h2>
<h3 id="usage-of-chronos">Usage of chronos:</h3>
<ul>
<li>target string
<ul>
<li>Specify the target URL or domain (supports wildcards)</li>
</ul>
</li>
<li>list-modules
<ul>
<li>List available modules</li>
</ul>
</li>
<li>module string
<ul>
<li>Comma-separated list of modules to run</li>
</ul>
</li>
<li>module-config value
<ul>
<li>Module configuration in the format: module.key=value</li>
</ul>
</li>
<li>module-config-file string
<ul>
<li>Path to the module configuration file</li>
</ul>
</li>
<li>match-mime string
<ul>
<li>Comma-separated list of MIME types to match</li>
</ul>
</li>
<li>filter-mime string
<ul>
<li>Comma-separated list of MIME types to filter out</li>
</ul>
</li>
<li>match-status string
<ul>
<li>Comma-separated list of status codes to match (default &ldquo;200&rdquo;)</li>
</ul>
</li>
<li>filter-status string
<ul>
<li>Comma-separated list of status codes to filter out</li>
</ul>
</li>
<li>from string
<ul>
<li>Filter snapshots from a specific date (Format: yyyyMMddhhmmss)</li>
</ul>
</li>
<li>to string
<ul>
<li>Filter snapshots to a specific date (Format: yyyyMMddhhmmss)</li>
</ul>
</li>
<li>limit string
<ul>
<li>Limit the number of snapshots to process (use negative numbers for the newest N snapshots, positive numbers for the oldest N results) (default &ldquo;-50&rdquo;)</li>
</ul>
</li>
<li>snapshot-interval string
<ul>
<li>The interval for getting at most one snapshot (possible values: h, d, m, y)</li>
</ul>
</li>
<li>one-per-url
<ul>
<li>Fetch one snapshot only per URL</li>
</ul>
</li>
<li>threads int
<ul>
<li>Number of concurrent threads to use (default 10)</li>
</ul>
</li>
<li>output string
<ul>
<li>Path to the output file</li>
</ul>
</li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>Why I Am Rebooting This Blog After Almost A Decade Away</title>
      <link>https://brentter.com/blog/time-for-a-reboot/</link>
      <pubDate>Wed, 11 Sep 2024 19:42:48 -0700</pubDate>
      <guid>https://brentter.com/blog/time-for-a-reboot/</guid>
      <description>Restarting this blog after 10 years of no posts, 20 years of owning it. Decided to start completely fresh. Thanks for checking it out.</description><content:encoded><![CDATA[ 
<figure class="entry-cover"><img loading="lazy" src="https://brentter.com/blog/time-for-a-reboot.webp" alt="What brentter.com looked like back in 2011">
        
</figure>]]>
               <![CDATA[<h2 id="starting-back-up-the-brenttercom-blog">Starting Back Up The Brentter.com Blog</h2>
<p>The last blog post on brentter.com was on December 29, 2011. It had been a while since I&rsquo;d left my old day job in the advertising industry and really didn&rsquo;t want to write about it anymore. So instead, I wrote about something I had done earlier that day for one of my clients, Use SQL queries to bulk-modify thousands of wordpress pages. There&rsquo;s no irony in that being the last post seeing how a few years before I had to figure out how to change the video embed tags from my previous video host (which went bankrupt and 2 days later deleted all their videos) to youtube. When that happened all of a sudden 99% of my pages had non-working video embeds. While some were backed up, there were a lot that only lived on that now dead video host. As I re-uploaded what I could over on youtube that meant I also had to change out all of the embed tags. Now I won&rsquo;t lie, my first instinct was to be lazy about it. Back then I was using wordpress/mysql. I did what most wordpress users would do in that situation and I searched for a  plugin to do it for me. But low and behold, wordpress and php back then just weren&rsquo;t a good combination for a long-running, resource intense task like the one that I was in need of. This had to be dealt with more directly. Every post was stored in a mysql table for the blog so all I needed to do was have it search for instances of the video tag and swap it out for the new one. So there it was, the worst day of this site ended the same way the last day of this site would years later, with SQL. At least it wasn&rsquo;t DNS.</p>
<h2 id="so-why-did-you-care-so-much-about-the-site">So Why Did You Care So Much About The Site?</h2>
<p>Believe it or not, this website once was popular. It was consistently ranked as one of the top 50 advertising blogs on the internet (though this was back when very few people actually had blogs) and I was making a decent amount of money every month through sponsors and site traffic. Getting the content was easy back then too, I had emailed just about every advertising, PR and production agency I could find proposing them this great offer: Send me your ad campaigns before you launch them and I&rsquo;ll give you free publicity. To add to that for emails that I&rsquo;d send to creative departments is I would also post whatever attribution/credits you send along with it along with the ad. For production companies I&rsquo;d email their creative directors and/or copywriters. While a PR agency might have seen a blog as not worth the effort, the folks who work in creative jumped at the chance of their names being posted along with their work. Eventually that led to me being asked to write articles on other marketing sites, most of which are now also dead, and scored me free passes to all the AdTech conferences in SF, Chicago and Miami (Thanks to Steve Hall for that, they were truly great memories). I&rsquo;d eventually leave traditional advertising and with that maintaining this advertising blog became more and more of a chore than a fun hobby so one day I just decided to stop.</p>
<h2 id="why-the-change-of-heart">Why The Change Of Heart?</h2>
<p>Even though I wasn&rsquo;t writing in a public forum anymore, my ADHD practically mandates that I write notes for myself otherwise I wouldn&rsquo;t be able to get anything done in my life. While the apps &amp; methods I use to write have changed over the years I still find myself jotting down ideas, bookmarking new finds and cultivating a never-ending stream of content that up until now I kept to myself. This will be a slow change. I&rsquo;m in the process of consolidating all my stored data, including the multiple sets of RSS aggregators I&rsquo;ve set up through the years to keep track of interesting content sources by topic/industry. The logs on those alone are an insane amount of data. Everything needs to be cleaned &amp; organized.  I&rsquo;m still debating how I&rsquo;m going to go about doing that and whether or not it&rsquo;s even worth it for 99% of the old content. Either way, the architecture of this site will most definitely change over the next few months.</p>
<h2 id="some-final-thoughts">Some Final Thoughts</h2>
<p>One thing I really enjoyed about writing all the time on here and other blogs was it gave me a chance to meet new people, even if only for a brief exchange of comments. Tuning in to strangers and seeing what they&rsquo;ve made has led to some incredibly rewarding experiences including some that eventually migrated from being online friends to working on projects together &amp; meeting in real life. While there&rsquo;s always going to be the dark side of the internet and all the strangers who feel anonymous sending messages on it, over-all it was a positive experience. While the internet now is just example after example of <a href="https://pluralistic.net/2023/01/21/potemkin-ai/#hey-guys" target="_blank">enshittification</a>
, I think a return to the old ways of the web is just what we need.</p>
<p>Welcome to my digital garden, I hope you find something interesting while you are here.</p>
<p>~brent</p>
]]></content:encoded>
    </item>
    <item>
      <title>About Brentter.com - Everythings Better With Brentter</title>
      <link>https://brentter.com/about/</link>
      <pubDate>Wed, 11 Sep 2024 10:30:03 +0000</pubDate>
      <guid>https://brentter.com/about/</guid>
      <description>Everythings Better With Brentter is a blog run by Brent Terrazas. This page is all about who he is and where else you can find him on the internet.</description><content:encoded><![CDATA[ 
<figure class="entry-cover"><img loading="lazy" src="https://brentter.com/aboutcover.jpg" alt="Everythings Better With Brentter">
        
</figure>]]>
               <![CDATA[<h2 id="what-is-everythings-better-with-brentter">What is Everythings Better With Brentter?</h2>
<p>This websites name is Everythings Better With Brentter. It&rsquo;s been hosted here on brentter.com for over twenty years by me, Brent Terrazas. I bought brentter.com on November 19th, 2004 and have owned it ever since. Believe it or not this was actually the second domain (out of 100+ since) that I&rsquo;d own. My day job back then was in advertising so it made sense for me to write about the industry as I&rsquo;d been constantly writing online for years at that point. I loved it, and because of that I ended up writing A LOT on here and other advertising industry sites. This website would even become one of the top 20 ranked advertising blogs at one time but somewhere along the way my day job turned into two jobs and then creating content for this site felt like it was becoming a third. It eventually led to me deciding that I needed a change in my life and the best way to do that was to take a break from writing. With that in mind I did what any normal mid-twenty-something kid would do and I disabled comments site-wide then promptly signed up for an adult kickball league with some friends. I loved it.</p>
<p>6 months of no posts and I eventually gave it one last entry. It was about what I had just finished doing for a client. I had been writing up a bunch of SQL queries to bulk-edit a few thousand of his WordPress pages so that he wouldn&rsquo;t have to go through and edit them one by one. Then about a year or so later I decided to just take it offline. The DB was actually still there, just I&rsquo;d removed all the static files and replaced the homepage with some ASCII. This site was running WordPress and at that time the platform was just a security nightmare, especially if you weren&rsquo;t actively monitoring it. I mean, it <strong>still</strong> is a security nightmare. If you&rsquo;ve been keeping up with what&rsquo;s been going on between the co-creator of Wordpress and WP Engine you&rsquo;d also think the <a href="https://tedium.co/2024/09/25/wordpress-wp-engine-open-web-negative-effects/" target="_blank">entire future of the platform</a>
 seems like a nightmare too.</p>
<h2 id="so-youre-going-to-start-posting-advertising-commercials-again">So You&rsquo;re Going To Start Posting Advertising Commercials Again?</h2>
<p>Well, maybe a few, but just some of my old favorites. With all the toxicity that now seems to be everywhere online I think we should all try to remember what it was like in the early days. Back when you were guaranteed at least a dozen AOL cds in the mail each week and AltaVista was the largest search engine. You&rsquo;d go around and find sites that were there to share content with other people without looking for a marketing angle. You&rsquo;d find their favorite blogrolls and web-rings, collections of stories or cool animated gifs - just sharing for the sake of sharing. I like that idea so I&rsquo;m going to follow it.</p>
<h2 id="hows-hugo">How&rsquo;s Hugo?</h2>
<p>So far I&rsquo;ve been enjoying working with Hugo. They really weren&rsquo;t kidding when they talk about just how fast it is. The only downsides have been in the template system and how janky it can be at times and that every big update to Hugo means something important in the blog is going to break. It&rsquo;s a work in progress.</p>
<p>Cheers,</p>
<p>~Brent<br>
Everythings Better With Brentter</p>
<p>(Here&rsquo;s a few spots where I can be found elsewhere on the web):</p>
<style>
table {
    border-color: white;
    border-style: dotted;
    width: 70%;
    text-align: center;
    padding: 10px 10px 10px 40px;
}
 a.author {
    color: #fabd2f;
    text-decoration:none;
}
a.author:hover{
    color:#fabd2f;
}
a.email {
    color: #fb4934;
}



</style>
<address class="author p-author vcard byline hcard h-card" itemprop="author" itemtype="https://schema.org/Person"><table><tr><td>
<img alt='Avatar for Brent' src='/brenttercom.webp' srcset='/brenttercom.webp' class='avatar avatar-100 photo u-photo' height='100px' width='100px'  itemprop="image"> </td><td>
<h3><a href="https://brentter.com/" class="p-name u-url url author metatag" rel="me">Brent Terrazas</a> </h3><span class="p-email u-email" rel="me" href="sendbrentmail@brentter.com"></span>
<p><a rel="me" href="/letschat/">Send Me Mail</a></td></tr></p>
<tr>
<td><a href="https://github.com/brentter" rel="me">GitHub</a></td>
<td><a href="https://www.last.fm/user/brentter" rel="me">Last.FM</a></td>
<td><a href="https://gravatar.com/brentter" rel="me">Gravatar</a></td></tr>
<td><a href="https://indieweb.org/User:Brentter.com" rel="me">IndieWeb</a></td>
<td><a href="https://www.reddit.com/u/brentter/" rel="me">Reddit</a></td>
<td><a href="https://defcon.social/@brentdev" rel="me">Mastodon</a></td></tr>
<tr><td><a href="https://ko-fi.com/brentter" rel="me">Ko-Fi</a></td>
<td><a href="https://app.follow.is/share/users/Brentter" rel="me">Follow.is App</a></td>
<td><a href="https://steamcommunity.com/id/brentter" rel="me">Steam</a></td></tr>
<tr><td><a href="https://keybase.io/brentter" rel="me">Keybase.io</a></td>
<td><a href="https://www.flickr.com/people/brentter/" rel="me">Flickr</a></td>
<td><a href="https://x.com/brentter" rel="me" title="my Twitter/X profile">Twitter/x</a></td></tr>
<tr><td><a href="https://libre.fm/user/brentter" rel="me" title="Libre.fm/brentter">Libre.fm</a></td>
<td><a href="https://bsky.app/profile/brentter.com" rel="me" title="Brentter.com on BlueSky">Bsky.App</a></td></tr></address>
</table>
]]></content:encoded>
    </item>
  </channel>
</rss>
