How this page works?

I was sick, the weather has been cold and cloudy for the last 2 weeks, overall pretty bad. No photos this time, instead have some notes on how this site works and why it exists in the first place.

The Why

I’ve been long toying with the idea of having a personal blog where I could share some of my thoughts and home projects but hadn’t had the will nor the amount of content to warrant a need for a full blog with hosting and the rest. In the last few months though, things changed. I’ve got a drone. Now I was always somewhat of a hobbyist photographer, most of my better pictures are shared on Facebook and Instagram. With the drone, however, videos are the main attraction. Where to share them though? On Facebook with its 720p resolution and barely 1Mbit bitrate? Instagram with their weird reel thing? YouTube with all the ads? I’d be better off serving them directly from a small VPS.

That’s exactly what I’ve done, and it stayed like that for a while. Good old NGINX Auto index. With the videos quickly accumulating though it started to get very disorganized. Why not organize them into something aesthetically pleasing? Like some sort of gallery. Or blog.

The How

Once I accepted the fact that I need a “blog”, I started to look into all sorts of ready-made, self-hosted blog engines and content management systems. I wanted something light, quick, and easily customizable, which pretty much eliminates everything. What I needed was a static site with some nice JS and CSS as I didn’t want any server-side features like commenting and such. I also didn’t want to bother with tens to hundreds of HTMLs and change all of them when I want to position the header a tiny bit to the right. I need something like a “static site generator”. Turns out, that’s a thing.

The first one I liked was Hugo. It’s quick, and easy to set up, there are some nice templates for it, and very customizable. It’s based on Golang, which I have some experience with from the dark ages, but I wasn’t confident with it. Besides that, I didn’t want to set up a Golang environment again. Enter Hexo, Hugos JS-based little brother. I quickly found a nice starting template too (Element, which I modified to fit my needs: Modified the grids, added a gallery view, fixed some strange sidebar behavior, optimized the dark mode toggle, and made it photo-centric.

The front

With Hexo, the written content is held in a Markdown format, which meets my needs perfectly. There’s a bit of header in every file where I can specify metadata like the title and date of the post, relevant tags, and the gallery in JSON format. Once the template is “finished”, all I have to worry about is the content. It then generates all the HTMLs from the main template, meaning if I need to change a bit on the looks, I don’t have to manually change it on every single page.

The back

Generating the gallery view happens with a Hexo script and a JSON meta tag in the post header. It’s basically a list of URLs with some flags to set if it’s a video, panorama, or simple image.

Since the path of the images is known, their responsivity of them is easy to handle. There are a few bash scripts on the VPS which automatically resize every image to two different sizes, using ImageMagick:

  • a maximum of 500x500 pixels for the previews
  • a maximum of 4000x4000 pixels for the viewer
  • the original picture is also available to the viewer by pressing the HD button

Panoramas are a bit trickier. Since the library, I use for the viewer (A-frame) needs 2:1 aspect ratio images for the equirectangular view, I have to pad them out from the top. That is because the drone can’t look upwards and doesn’t take photos from the top. It could be done when stitching, but they were done before I realized this. So another script comes in that does that, also ImageMagick. These are then also resized to three different sizes:

  • a maximum of 500x500 pixels for the previews
  • a maximum of 8000x8000 pixels for the viewer
  • a maximum of 16384x16384 pixels for the HD viewer - this was a limitation on the Meta Quest hardware since I wanted these to be viewable in VR. That is the maximum resolution it supports from the web. Still doesn’t work properly though, so I might just return the original instead.

Videos are even trickier, with a large oversight from my end. I wanted the videos to maintain their highest possible quality with the lowest possible footprint. That is why I convert all my drone videos into H265/HEVC. It’s a lengthy process, but it’s worth it. Barely noticeable quality degradation with incredible space savings. The problem is DRM. Turns out, that only those can watch the H265 version, who have the H265 license on their machine. That means that $0.99 additional purchase on Windows, Safari on Mac and who knows what on other devices. I didn’t realize this until recently, because I have the license, so it plays properly without any problems. There are also two versions of every video, one is the H265 version I mentioned above and another is the maximum 1920x1080 resolution H264 version. That’s the one anybody can watch. (Note: I tried AV1 too, with similar limitations in the browser.) The conversions happen on my home machine with its beefy AMD processor. There’s also a small script that extracts a thumbnail from every video at around 1 second.

The host

This was the easy part. I already had a VPS from ATW in Hungary, which I use for various services (on which I may or may not elaborate at a later time). The pages are hosted with NGINX, as are the images/videos. The latter is hosted from a subdomain, I’m not really sure why I did it this way, but I like it now, so it stays. The VPS has 2 tiers of storage, a smaller NVME disk, where the pages are hosted, and a large HDD space where the images and videos are hosted. The hosts’ outbound speed is not the best though, combined with the speed of the shared HDD array some buffering of the videos was inevitable. Mitigating that I did what everybody does, and went with Cloudflare between the host and the domain. It does nicely with caching what’s needed, and it’s free, what else do I need?

<
>