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/