all repos — site @ 5261a32bfabc512dbbd4d969a0ecba19dac07213

source for my site, found at icyphox.sh

pages/txt/hacky-scripts.txt (view raw)

  1   24 October, 2019
  2
  3Hacky scripts
  4
  5The most fun way to learn to code
  6
  7   As a CS student, I see a lot of people around me doing courses online
  8   to learn to code. Don't get me wrong -- it probably works for some.
  9   Everyone learns differently. But that's only going to get you so far.
 10   Great you know the syntax, you can solve some competitive programming
 11   problems, but that's not quite enough, is it? The actual learning comes
 12   from applying it in solving actual problems -- not made up ones. (inb4
 13   some seething CP bro comes at me)
 14
 15   Now, what's an actual problem? Some might define it as real world
 16   problems that people out there face, and solving it probably requires
 17   building a product. This is what you see in hackathons, generally.
 18
 19   If you ask me, however, I like to define it as problems that you
 20   yourself face. This could be anything. Heck, it might not even be a
 21   "problem". It could just be an itch that you want to scratch. And this
 22   is where hacky scripts come in. Unclear? Let me illustrate with a few
 23   examples.
 24
 25Now playing status in my bar
 26
 27   If you weren't aware already -- I rice my desktop. A lot. And a part of
 28   this cohesive experience I try to create involves a status bar up at
 29   the top of my screen, showing the time, date, volume and battery
 30   statuses etc.
 31
 32   So here's the "problem". I wanted to have my currently playing song
 33   (Spotify), show up on my bar. How did I approach this? A few ideas
 34   popped up in my head:
 35     * Send playerctl's STDOUT into my bar
 36     * Write a Python script to query Spotify's API
 37     * Write a Python/shell script to query Last.fm's API
 38
 39   The first approach bombed instantly. playerctl didn't recognize my
 40   Spotify client and whined about some dbus issues to top it off. I spent
 41   a while in that rabbit hole but eventually gave up.
 42
 43   My next avenue was the Spotify Web API. One look at the [1]docs and I
 44   realize that I'll have to make more than one request to fetch the
 45   artist and track details. Nope, I need this to work fast.
 46
 47   Last resort -- Last.fm's API. Spolier alert, this worked. Also,
 48   arguably the best choice, since it shows the track status regardless of
 49   where the music is being played. Here's the script in its entirety:
 50#!/usr/bin/env bash
 51# now playing
 52# requires the last.fm API key
 53
 54source ~/.lastfm    # `export API_KEY="<key>"`
 55fg="$(xres color15)"
 56light="$(xres color8)"
 57
 58USER="icyphox"
 59URL="http://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks"
 60URL+="&user=$USER&api_key=$API_KEY&format=json&limit=1&nowplaying=true"
 61NOTPLAYING=" "    # I like to have it show nothing
 62RES=$(curl -s $URL)
 63NOWPLAYING=$(jq '.recenttracks.track[0]."@attr".nowplaying' <<< "$RES" | tr -d '
 64"')
 65
 66
 67if [[ "$NOWPLAYING" = "true" ]]
 68then
 69        TRACK=$(jq '.recenttracks.track[0].name' <<< "$RES" | tr -d '"')
 70        ARTIST=$(jq '.recenttracks.track[0].artist."#text"' <<< "$RES" | tr -d '
 71"')
 72        echo -ne "%{F$light}$TRACK %{F$fg}by $ARTIST"
 73else
 74        echo -ne "$NOTPLAYING"
 75fi
 76
 77   The source command is used to fetch the API key which I store at
 78   ~/.lastfm. The fg and light variables can be ignored, they're only for
 79   coloring output on my bar. The rest is fairly trivial and just involves
 80   JSON parsing with [2]jq. That's it! It's so small, but I learnt a ton.
 81   For those curious, here's what it looks like running:
 82
 83   now playing status polybar
 84
 85Update latest post on the index page
 86
 87   This pertains to this very blog that you're reading. I wanted a quick
 88   way to update the "latest post" section in the home page and the
 89   [3]blog listing, with a link to the latest post. This would require
 90   editing the Markdown [4]source of both pages.
 91
 92   This was a very interesting challenge to me, primarily because it
 93   requires in-place editing of the file, not just appending. Sure, I
 94   could've come up with some sed one-liner, but that didn't seem very
 95   fun. Also I hate regexes. Did a lot of research (read: Googling) on
 96   in-place editing of files in Python, sorting lists of files by
 97   modification time etc. and this is what I ended up on, ultimately:
 98#!/usr/bin/env python3
 99
100from markdown2 import markdown_path
101import os
102import fileinput
103import sys
104
105# change our cwd
106os.chdir("bin")
107
108blog = "../pages/blog/"
109
110# get the most recently created file
111def getrecent(path):
112    files = [path + f for f in os.listdir(blog) if f not in ["_index.md", "feed.
113xml"]]
114    files.sort(key=os.path.getmtime, reverse=True)
115    return files[0]
116
117# adding an entry to the markdown table
118def update_index(s):
119    path = "../pages/_index.md"
120    with open(path, "r") as f:
121        md = f.readlines()
122    ruler = md.index("|  --  | --: |\n")
123    md[ruler + 1] = s + "\n"
124
125    with open(path, "w") as f:
126        f.writelines(md)
127
128# editing the md source in-place
129def update_blog(s):
130    path = "../pages/blog/_index.md"
131    s = s + "\n"
132    for l in fileinput.FileInput(path, inplace=1):
133        if "--:" in l:
134            l = l.replace(l, l + s)
135        print(l, end=""),
136
137
138# fetch title and date
139meta = markdown_path(getrecent(blog), extras=["metadata"]).metadata
140fname = os.path.basename(os.path.splitext(getrecent(blog))[0])
141url = "/blog/" + fname
142line = f"| [{meta['title']}]({url}) | `{meta['date']}` |"
143
144update_index(line)
145update_blog(line)
146
147   I'm going to skip explaining this one out, but in essence, it's one
148   massive hack. And in the end, that's my point exactly. It's very hacky,
149   but the sheer amount I learnt by writing this ~50 line script can't be
150   taught anywhere.
151
152   This was partially how [5]vite was born. It was originally intended to
153   be a script to build my site, but grew into a full-blown Python
154   package. I could've just used an off-the-shelf static site generator
155   given that there are [6]so many of them, but I chose to write one
156   myself.
157
158   And that just about sums up what I wanted to say. The best and most fun
159   way to learn to code -- write hacky scripts. You heard it here.
160
161References
162
163   1. https://developer.spotify.com/documentation/web-api/
164   2. https://stedolan.github.io/jq/
165   3. https://icyphox.sh/blog
166   4. https://github.com/icyphox/site/tree/master/pages
167   5. https://github.com/icyphox/vite
168   6. https://staticgen.com/