Developing a Telegram Bot for Image Uploads

Last month, I wrote about moving my wife's website from WordPress to Hugo. Overall, the pros of switching outweigh the cons. She'll have to adapt to a new publishing workflow that involves pushing updates to GitHub and waiting a few minutes for her site to rebuild. At the same time, it'll be much easier for me to add custom functionality to her site because I'm very familiar with Hugo.

My wife may want to get an iPad to replace her MacBook in the future, so I've been thinking of ways for her to handle image uploading on her new Hugo site. At the moment, the image uploading functionality on my site is handled by a custom Python script mapped to a macOS quick action – it sounds complex, but it's very user-friendly. Unfortunately, this kind of setup isn't possible on iPad.

After some hacking around, I settled on developing a Telegram bot to handle image uploads. I chose Telegram because the bot API is fairly easy to figure out, and the service itself is cross-platform. At the moment, the bot is able to take an uploaded image, download it to a temporary location, and upload it to a Google Cloud Storage bucket (where I store blog images).

The next step is to add Dropbox support as well because my current macOS quick action pushes images to Dropbox for backup purposes. After that, I just need to write some code to instruct the bot to send a Hugo shortcode after a successful upload. That'll be especially useful for my wife because she can just copy and paste the shortcode straight into her post.

The Differences Between Audio Interfaces and Headphone Amplifiers

A question that I often get is about the differences between audio interfaces and headphone amplifiers. While audio interfaces and headphone amps share certain traits and similarities, they are two fundamentally different products. In this post, you'll learn about the differences between audio interfaces and headphone amplifiers, and why you may want one over the other.

Continue Reading →

Cloudflare Stream vs. YouTube for Video Hosting and Streaming

I recently hosted a live webinar on how to host an online concert with Zoom and MainStage. After the webinar, I wanted to make the recording available for people to watch in case they missed the live event. My initial thought was to upload the video to YouTube and call it a day. Eventually, I decided to upload it to Cloudflare Stream instead. In this post, I'll go over the two platforms, and share why I decided to use Cloudflare Stream over YouTube.

Continue Reading →

How to Layer Instruments in MainStage

In today's live and studio productions, being able to quickly create complex patches with multiple layers is crucial. With its extensive library of built-in sound libraries, plugins, and performance-oriented features, MainStage allows you to easily create complex layered patches. In this post, you'll learn how to layer instruments in MainStage.

Continue Reading →

What Apple Silicon Means for Musicians

Earlier this week at WWDC 2020, Apple announced its upcoming transition from Intel CPUs to its own "Apple Silicon" for Macs. The new line of Apple Silicon chips are of the ARM-based variety – similar to the A-series chips found in the iPhone and iPad. In this post, I'll share a few thoughts about how the transition to Apple Silicon may affect musicians, especially those using Apple-developed apps like Logic Pro X and MainStage.

Continue Reading →

Adding Schema Markup to a Hugo Site

For tonight's Hugo project, I decided to add @BlogPosting schema markup to my Hugo-generated blog. Schema markup was something I avoided for a while because I assumed it would be too complicated to add to my blog. After some hacking around tonight, I was able to successfully add the markup.

<script type="application/ld+json">
{
	"@context":"https://schema.org",
	"@type": "BlogPosting",
	"image": {{ partial "seo/og_image.html" . }},
	"url": {{ .Permalink }},
	"dateCreated": "{{ .PublishDate.Format "2006-01-02" }}",
	"datePublished": "{{ .PublishDate.Format "2006-01-02" }}",
	"dateModified": "{{ .Page.Lastmod.Format "2006-01-02" }}",
	"inLanguage": "en-US",
	"isFamilyFriendly": "true",
	"copyrightYear": "{{ .PublishDate.Format "2006" }}",
	"copyrightHolder": "Brian Li",
	"accountablePerson": {
		"@type": "Person",
		"name": "Brian Li",
		"url": "https://brianli.com"
	},
	"author": {
		"@type": "Person",
		"name": "Brian Li",
		"url": "https://brianli.com"
	},
	"creator": {
		"@type": "Person",
		"name": "Brian Li",
		"url": "https://brianli.com"
	},
	"publisher": {
		"@type": "Organization",
		"name": "Brian Li LLC",
		"url": "https://brianli.com",
		"logo": {
			"@type": "ImageObject",
			"url": "/assets/BRIAN-LI-FEATURED-IMAGE.png"
		}
	},
	"mainEntityOfPage": "True",
	"articleBody": {{ .Content | safeJS | htmlUnescape | plainify }}
}
</script>

A few interesting things I noticed with Hugo's rendering engine.

  • For certain pairs like "url": {{ .Permalink }}, adding quotes around {{ .Permalink }} messes up the JSON rendering. If you're trying to add schema markup on your Hugo site, you may need to omit the quotes if you see escaped characters. I had to use this syntax for "image" as well.
  • "articleBody" required quite the workaround. {{ .Content | safeJS | htmlUnescape | plainify }} ended up working for me – notice the lack of quotations once again.

Tasty Burger for Father's Day

My parents were gracious enough to take care of J tonight, so Ayaka and I got to sneak out for Father's Day dinner. We couldn't stay out for too long, so we decided to go to Tasty Burger in Boston for some quick burgers and fries. It's been on our list of places to go since our previous visit to Boston, so it seemed like a good idea. I used to come here almost every week during my time at Northeastern University. It was nice to be able to come back, reflect on how much life has changed since college, and enjoy some greasy goodness.

Continue Reading →

Tokyo in Motion

I took this photo in Tokyo during a night out with my friend Drew. I think it captures Tokyo's nightlife vibe pretty well. I wish I could say this was a planned shot, but it wasn't. It was taken by accident, and I didn't even realize it until I browsed through the night's images afterward.

I took this photo by accident.

Switching Back to a System Font Stack

I'm a huge proponent of using system fonts for websites. Using system fonts is great for website performance because it doesn't require the client to download additional fonts to render a page. I think modern system fonts look beautiful as well, especially the San Francisco font that ships with macOS and iOS.

A system font stack on brianli.com.
I love how the system font stack looks in dark mode. Very clean and sleek.

In the past, I used system fonts on this website. At some point, I switched to FF Meta Serif because Chrome, Brave, and other Chromium-based browsers somehow broke system font rendering on macOS. I opened a ticket regarding the spacing issue back in December 2019, and it was resolved recently.

I have to admit that I sort of miss the serif look, and I'm really glad that I stumbled upon FF Meta Serif during my font hunting because it's a great font. With that said, I also love the reduced performance overhead that a system font stack brings to the table. I'm going to give the system font stack another shot, and see how I feel about it over the next few months. If I really miss the serif look, I can always switch back to FF Meta Serif.

Here's the system font stack I'm using from CSS-Tricks.

body {
  font-family: system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
}

Let me know what you think of the new font!

Apparently I Have Shingles

I've been experiencing back pain over the last week or so. At first, I thought it was due to a mosquito or bug bite on my back. Yesterday, I noticed the pain was getting worse, and the redness had spread to the front of my body as well. Today, I went to the doctor again, and he diagnosed me with shingles, which is a reactivation of the same virus that causes chicken pox.

I always thought only old people get shingles, but there's apparently a trend in younger people in the last few years as well. I hope the symptoms don't get too serious, but I'm not too hopeful. It's already gotten a lot worse since yesterday, and I had to lie down for half an hour today because it was very difficult to focus on work with the pain and fatigue. I ended up sleeping for about 15 minutes, and was drenched in sweat when I woke up. I feel better now, so maybe the pain comes in waves. Anyway, the doctor prescribed some antiviral medication for me, so hopefully that'll help me recover faster.

After this is over, I need to review my commitments because stress may be a trigger for shingles. My baseline stress level has been elevated for the past few months from trying to raise a baby during the COVID-19 crisis, so I wouldn't be surprised if stress was the cause of this shingles episode.