Skip to main content

Installing Booksonic Air in the cloud for cheap

If you prefer video format tutorials this is also available as a youtube video here

Before we get too deep into this guide I just want to clarify what I mean with cloud in this instance as the term can be kind of vague.

The cloud we are talking about here is not Google Drive, Dropbox etc, it is simply a Linux machine that you don’t have to host at home, what I am talking about here is a VPS, aka a Virtual Private Server.

Getting a server

Ok, with that out of the way lets get started.
Since I started Booksonic I have been on the lookout for VPS providers that can give you the amount of storage needed for audiobooks without having to pay hundreds of dollars and let me tell you, they are few and far between.

The one I am currently using and can really recommend is called Contabo. They offer multiple tiers with different hardware but since all of them can handle Booksonic without any problems all we really care about is the storage. You can see the specs below or at this page

Once you have selected the one you want to use click order and you will be taken to a configuration page. Under Operation System check the box for Ubuntu 20.04, leave the rest unchecked and click Order now.

Here comes the hard part, the wait. As far as I can tell they setup your server manually the first time, in my case this has usually taken about 2 hours but it can probably vary. In the meantime go grab a coffee and watch a good movie.

Installing dependencies

Once the installation is done you will receive and email that contains something like this. This is your server info.

If you are on a Windows computer open up Powershell from the start menu, if you are on Linux or Mac you open up the terminal instead, the commands should be the same.

In Powershell enter the following command, this will give you access to your new Ubuntu server. Replace IPADDRESS with the address from your email.

ssh [email protected]

It will ask you if your trust the fingerprint, answer yes

You will be asked to enter your password, enter the one that is just called password in your email. Something that can be a bit weird for Windows users is that it won’t show you when you type your password but don’t worry just type it anyway and press enter.

The first thing we want to do is change the default password on your box, you do that by typing the command and follow the instructions


Now that the password is changed to something of your own we can get started with preparing the dependencies and folders. The first thing we need is to install something called docker. We do this by typing the following command

apt install

Next we need to create a few folders where we are going to put our files. We do this by typing

mkdir /booksonic
mkdir /booksonic/config
mkdir /audiobooks
mkdir /podcasts

That’s it, easy huh. Now lets do what you came here for, install Booksonic Air.

Installing Booksonic

docker create --name=booksonic-air -p 80:4040 -v /booksonic/config:/config -v /audiobooks:/audiobooks -v /podcasts:/podcasts --restart unless-stopped linuxserver/booksonic-air

That’s it, now you have installed Booksonic, but you still need to start it. You can start it by running the command

docker start booksonic-air

Configuring Booksonic

I won’t be doing a full configuration guide here but there are a few things you need to change in the server to get it working.

Open your browser and in the address field enter the IP address of your server. Sign in with the admin account. Both the username and password is admin.

The first thing you need to do is change your admin password, you can do so from settings -> users

Once you have changed your password you will be signed out and have to sign in again with your new password

Head back to settings, change the folder to /audiobooks
Then hit save

Securing your server

Yeah, I know this is a bit boring but it is really really important, right now your server is not using a firewall, that is a really really bad thing. So lets enable the firewall.

ufw allow 22
ufw allow 80
ufw enable

What did we just do? We just created two rules for ports that should be open and then we enabled the firewall. Port 22 is used by SSH, the tool you are using to control your server, if you had not allowed this you would have lost the connection as soon as the firewall was turned on. Port 80 is the standard port on the web, it is allowed so you can connect to the Booksonic server.

And now at last we are done!

But, how do I upload my audiobooks to the server?

Ah, good question. You do it over sftp, you can use any sftp client you want but if you don’t already have one I can recommend FileZilla.

The Host is the server address, Username is root, Password is the one you set before and Port is 22.

If you are using FileZilla, on the right side, set “Remote site:” to /audiobooks and press enter, then you can drag and drop your audiobook library in the window.

That’s it folks! Should you need any more help or think that something important is missing from the guide, feel free to reach out to me at [email protected], in the comments below or at the Booksonic Subreddit

Obs, if you click the contabo links in this guide and rent a server I will receive a small commission at no extra cost to you, this has not influenced my decision to recommend them in any way, they are what I use personally and what the Booksonic demo is hosted on.


How to install Booksonic Air on Windows 10

Booksonic Air is the new and improved Booksonic server, this new version is based on Airsonic meaning a lot of stability issues has been fixed. It also sports an improved webplayer with variable playback speed, something that has been requested ever since the first server was released.

So how do you install it?

While my reccomendation is to install Booksonic Air with Docker installing it directly on Windows is possible as well.

Still want to run Booksonic on Windows? Ok, first you need to download a file called Install-BooksonicOnWindows.ps1

Go to your start menu and search for powershell, rightclick on that and select run as admin. Enter this command


If you get anything other then Bypass back write down what you get and run the following command, if you get Bypass skip to the step “Start internet explorer…”

Set-ExecutionPolicy Bypass

Once you are done with this guide, come back to this powershell window and run the following command, replace the text MYPOLICY with the text you wrote down earlier

Set-ExecutionPolicy MYPOLICY

Start internet explorer and then close it again, this is an important step due to a function in powershell not working unless internet explorer has been started on the system at least once. I am working on an updated install script where this will hopefully not be needed.

Run the ps1 file with powershell

A ps1 file is a script file that you can run in powershell, similar to how bash works on a Linux machine. Once you have downloaded it all you have to do is right click on the file and select “Run with powershell”.

uac promt to elevate powershell script
You need to select YES here

You will get a popup asking if you want to allow powershell to make changes to your device, select yes on this. If your computer don’t have java installed, the script will download and start the installer for an open source version of java (adopt openJDK) and all you have to do is click next next next etc. Once java is installed Booksonic will continue to install itself.

Booksonic is now installed

When you see this popup it means Booksonic has been installed and is now starting up. Once you click ok a new tab will open in your browser and navigate to your Booksonic server. If you click ok a bit to quickly and the server has not started completely yet you might need to wait a minute or two and then reload the tab.

For instructions on how to use the server please see step 3 and down from the Legacy guide available here, I am currently working on a new and improved guide for this. I am also working on guides for how to install Booksonic on Linux and in the cloud using a cheap VPS from Contabo.

If you are interested in running Booksonic in the cloud I recommend renting a Contabo VPS and installing booksonic there. It starts as low as 3.99 euro for 300GB storage and 4GB ram. If you got a huge library you can get as much as 1.4TB storage for 12.99 euro
Obs, if you click the contabo links and rent a server I will receive a small commision at no extra cost to you, this has not influenced my decision to recommend them in any way, they are what I use personally and what the Booksonic demo is hosted on.


How I upgraded the Lenovo 4 10 Plus to Android Q/10 and passed SafetyNet

Back in June last year I bought a Lenovo 4 10 Plus and upgraded it to what was then the latest version of Android (Pie/9) and made it pass safety net using magisk and a few modules. Today I have upgraded that tablet to Android Q/10 and once again passed safetynet, this is how I did it.

First of all, this guide is written more for my own documentation and requires that you have some idea about this kind of thing already, if you don’t completely understand what I am saying, DO NOT follow my guide, instead head over to this XDA thread and make sure you understand completely what is going on. Doing things wrong here can end up bricking your device and render it useless, continue at your own risk. Just as last time, I have only verified this on the L version of the tablet.

Also note that this guide is for upgrading a tablet that already has a version of TWRP installed, if you have a fully stock tablet check out the beginning of my post for upgrading to Pie/9 where I install TWRP on a stock tablet first and then come back here once you have TWRP installed.

ok, with that out of the way, lets get started.

Just as last time I begin with downloading everything I need and put it on the external SD card, it is very important to not put it on the internal SD card as we will wipe that clean!

Here is the list of what to download and which versions I used

Once again I went with a third party build of lineage that I found on XDA, this time it was a build created by Senior recognized developer codeworkx. You can find the thread for the Lineage OS 17.1 build here and I used the version that was released on 20200731.

A new TWRP version was available for my tablet from here so I decided to install that as well, the version I installed was twrp-3.4.0-0-X704F.img

Just like last time I went with GAPPS from the open gapps project, make sure you select Arm64, Android 10 and Micro. The version I installed was from 20200802 but you should probably use the latest one.

I downloaded the latest version of Magisk (v20.4) and Magiskt Manager (v7.5.1) from the Magisk GitHub release page. Don’t get it anywhere else as there are some shall we say not so legit versions floating around that you really don’t want to install.

Magisk hide props conf
Next I downloaded the latest version of the magisk hide props conf module from here, in my case it was version 5.3.0-v95

Ok now that everything was downloaded I double checked that I had saved it on the external SD card.

Next I rebooted the tablet  into recovery, selected install, changed to image and flashed the new version of TWRP that I downloaded.
The tablet then rebooted into the OS, once it did I rebooted it to TWRP again

If you want a backup of your system now is the time to take it, again make sure it is saved on the external SD card. I did not take a backup, don’t be like me.

Now it was time to clean out the old Android, hit wipe but don´t use the slide, instead hit format data and type yes. Once done, hit the back button and go to advanced wipe. Check Dalvik/ART Cache, System and Cache then swipe to wipe them. Go back to the main menu and reboot into recovery. If it asks if I want to install a TWRP app I always say no.

Once back in TWRP I began by flashing the ROM, then GAPPS and lastly Magisk, then I rebooted into system. Once again say no if it asks for installing a TWRP app.

Now I went through the setup of the tablet, I encountered a bug where the tablet would just keep looping between enter pin code and enabling the google assistant. I tried rebooting the tablet but that did not help, if this happens to you as well you can get around it by going all the way back to the start of the setup, select another language and then continue with the installation, this will let you pass the setup, then you can just change the language back to english US. Some people online seem to have been able to just switch to another version of english while some have needed to select an entierly different language, in my case I used swedish. If you use swedish as well, the setting you want to search for when changing back to english is språk or sprak, both of them will work.

Ok so now that I was back in the tablet I installed magisk manager and then headed to the magisk manager settings and made sure Magisk Hide is enabled, I also enabled that Magisk manager itself should be hidden.

Next I headed to Google Play and installed the app “Termux”, in my case it was version 0.95 but the version shouldn’t matter here.

Now I went back to magisk manager and downloaded/installed the module “Busybox for Android NDK” in my case it was version 1.31.1. Choose not to reboot your device.
Then I installed the magisk hide props conf module that I downloaded before, this time you should reboot the device

When the tablet is back up, I opened up Termux, typed su and hit enter. I allowed it root permissions and then typed props followed by enter. Follow the menu that was started like this

Edit device fingerprint -> pick a certified fingerprint -> Lenovo -> Lenovo Tab 4 10 Plus TB-X704F -> yes for reboot.

When my device was back up I ran the SafetyNet check inside Magisk Manager and saw that everything was green but when I tried to search for Netflix on Google Play it still didn’t show up. To fix this I headed in to the tablets settings, then apps, show system apps. Then clear all data for Google Play Services and Google Play. Now I rebooted the device one final time and then it was done.

The tablet is now running Android Q/10 and passes SafetyNet. One last thing to keep in mind is that SafetyNet is always updated so this might break in the future and then Magisk will probably update to fix it again etc etc. If always passing SafetyNet is really important to you I recommend looking for a new tablet with official updates.


Building Powershell GUIs that won’t freeze when you use them

A few days ago I wrote about how important I think it is that you build a GUI for your scripts if they are to be used by anoyone else.

If you have ever built a GUI using Powershell however, you know there is a problem with it that I didn’t mention, that is, whenever your script is actually doing anything the GUI will freeze and windows will tell you it isn’t responding. This is not good as most users will then try to close it and try again. We don’t want that.

The reason this happens is because you are running your form (GUI) and logic in the same thread, something you should never do, problem is, Powershells support for multithreading is limited at best.

In the last post I also talked about my module EasyGUI and mentioned that there are features I wouldnt go in to in that post, one of those features is an easy way to use threads.

There are two ways to use threads in EasyGUI, I go through both of them below as well as a tip on how I prefer to use them.

Method 1.

Passing in a scriptblock to the thread, here I am creating it on the fly but you could also store the scriptblock in a variable and pass that to the function instead

New-Thread {
        $SYNC.myLabel.Text = $_

Method 2.

You could also write the code for the thread in a separate script file and then pass that to the New-Thread function like this

New-Thread "D:\Program\Powershell\myThread.ps1"

One thing to be aware of is that the threads will start running emediatly when you create them. Often you might not want that. What I usually do is create the threads inside a scriptblock that I store in a variable, then when I need the tread to run I run that scriptblock. That would look something like this.

$myThread = {
    New-Thread {
            $SYNC.myLabel.Text = $_

#Now it's time to run the thread, simply type the & sign and then the variable containing the scriptblock

$SYNC ? Where did that come from?

Noticed that did you?

This is a variable that’s automatically created when you initialize the EasyGUI module. It’s a hashtable that’s synced between all threads so you can communicate between them. In the examples you see that I am using $SYNC.myLabel. This is created in the GUI like this.

$SYNC.myLabel = New-Label @{
    Location = "15, 15"

or like this

$myLabel = New-Label @{
    Location = "15, 15"
$SYNC.myLabel = $myLabel

If you want to see a complete example of a GUI script with threading implemented there is an example availiable in the Easy GUI repository.


Writing Powershell GUIs the easy way

One of the most important tools for any IT professional or service desk technician that works in a Windows environment is without a doubt Powershell. Not only is it extremely powerful but it is also very fun to work with if you ask me.

One of the great features of Powershell that I feel doesn’t get enough love from the Powershell community is the ability to write scripts with graphical user interfaces. Yeah, yeah, I know, you want to be able to do a quick change in the script and then rerun it inside ISE and anyone that doesn’t know how to use a console shouldn’t be in IT anyway, right? Wrong!

It is my firm belief that a clear and easy to use interface is just as important as the logic itself when you write a script that is going to be used by someone else. For most people, that means the interface needs to be graphical. I base this belief on having worked in an IT department at a large company for several years as well as maintained the Booksonic project since the start in late 2015. If you want to change my mind, feel free to try and do so in the comments below.

Now with that out of the way I will stop trying to convince you about writing user interfaces for your scripts, the fact that you are reading this post probably means that you are already doing it or thinking about doing it. Instead I am going to focus on how to do it in a way so the code is easy to read and maintain even once you start writing more complex UIs.

When I first started writing powershell I noticed that most examples of people writing powershell UIs get really messy real fast.

A typical example you may find online looks something like this

Add-Type -AssemblyName System.Windows.Forms
Add-Type -Name Window -Namespace Console -MemberDefinition '
        public static extern IntPtr GetConsoleWindow();     
        public static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow);

[Console.Window]::ShowWindow([Console.Window]::GetConsoleWindow(), 0)       '

$font = New-Object System.Drawing.Font("Times New Roman",24,[System.Drawing.FontStyle]::Bold)

$label = New-Object System.Windows.Forms.Label
$label.Cursor = [System.Windows.Forms.Cursors]::Hand
$label.Text = "Click me to open" 
$label.BackColor = "Transparent"
$label.ForeColor = "Blue" 
$label.AutoSize = $true
$label.Location.X = 15
$label.Location.Y = 15
$label.Add_Click = { Start-Process -FilePath iexplore  -ArgumentList "" }

$form = New-Object system.Windows.Forms.Form
$form.Width = 600
$form.Height = 200
$form.Text = "Example form"
$form.Font = $font

Now functionally speaking there is nothing wrong with that script and in fact it may not look that bad either but remember, all we have done is created a window and added one label to it. Imagine how cluttered this would become once you started doing some more advanced UIs. A better way to write the above that I have actually never seen anyone use other than me and some of my colleages would be using -Properties like this.

Add-Type -AssemblyName System.Windows.Forms
Add-Type -Name Window -Namespace Console -MemberDefinition '
        public static extern IntPtr GetConsoleWindow();     
        public static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow);

[Console.Window]::ShowWindow([Console.Window]::GetConsoleWindow(), 0)

$label = New-Object System.Windows.Forms.Label -Properties @{
    Cursor = [System.Windows.Forms.Cursors]::Hand
    Text = "Click me to open" 
    BackColor = "Transparent"
    ForeColor = "Blue"
    AutoSize = $true
    Location = "15, 15"
    Add_Click = { Start-Process -FilePath iexplore  -ArgumentList "" }

$form = New-Object system.Windows.Forms.Form -Properties @{
    Size = "600, 200"
    Text = "Example form"
    Font = New-Object System.Drawing.Font("Times New Roman", 24, [System.Drawing.FontStyle]::Bold)

While this takes up about the same space it looks much better right? This way a quick glance at the code gives you a much better understanding of what it is you are looking at.

If you choose to stop reading here and start writing your UIs in this way I beleive you will have a much easier time maintaining that script then you had before. However, I have taken it a step further in my scripts.

When I write a script I use a specific framework that makes the code even quicker to read and also includes some other nice features that I won’t go in to in this post but might make future posts about. One of the most important parts of this framework is a module I call EasyGUI and that you can download at

This module builds on the second example above and simplifies it even more. It also includes some other cool stuff that I also won’t go in to here but again, might make another post about. If we decided to use EasyGUI to write the example above it would look like this

Import-Module EasyGUI


$form = New-Form @{
    Size = "600, 200"
    Text = "Example form"
    Font = New-Font -Font "Times New Roman" -Size 24 -Style $FONTSTYLE.Bold

$label = New-Label @{
    Cursor = $CURSOR.Hand
    Text = "Click me to open" 
    BackColor = "Transparent"
    ForeColor = "Blue"
    AutoSize = $true
    Location = "15, 15"
    Add_Click = { Start-Process -FilePath iexplore  -ArgumentList "" }


Show-Form $form

I dont know about you but I find that to be much more readable then the first example we looked at, especially when you are working with a lot more then just a label.

It should be noted that not every forms object is in EasyGUI yet as I add them when I need them but adding them yourself should be very easy even for someone new to Powershell and if not just post a comment below and let me know what object you would like added.

I would love to hear your thoughts on this, is it something you would ever use or do you think I’m just talking rubbish when I say that UIs are this important or that a simple module like this can make it a lot more quick, fun and easy to code and maintain UIs.


Converting subtitles from wsrt to srt

If you are from Sweden or are watching swedish TV anyway chances are that you have come across subtitle files with the extension wsrt. I know I have, and with that, prepare for one of my unnecessary long rants where I try and explain things, if you are only interested in the converter tool you can find it here.

For those that don’t know SubRip or srt is a popular subtitle format for videos on the internet. wsrt on the other hand is not, in fact as far as I know the only sites that use it are SVTPlay and ÖppetArkiv, both of which are run by Sveriges Television. This leads me to believe that wsrt is probably an in-house format for subtitles that Sveriges Television use. Well, that’s great but what if you want to play them in an external player like say VLC or Emby? Then you are out of luck, it wont work because they don’t support the format.

Now here is the good part, wsrt just as the extension suggests is actually based on srt. In fact wsrt and srt files look almost identical but wsrt has a few extra features. Great, then I can just rename the file to have the srt extension and it will be fine you might be saying now, and well, you are partly right. For some players like for example VLC it will actually work but for some it won’t. The reason for this is a small but annoying change that was made to the time in the wsrt file format. Lets see if you can catch it.

00:00:42.640 –> 00:00:48.720
00:00:42,640 –> 00:00:48,720

Ok so the top one is in the wsrt format and the bottom one is in the srt format. The difference is very small but enough to make a lot of players out there say that no, I don’t know anything about this and if you are lucky just keep playing the video without subtitles or if you are unlucky, BAAM, the player will crash.

So what is the small difference, did you catch it? Ok, ok, I’ll tell you. In srt there is a comma, in wsrt this comma has been replaced by a dot. That’s it, well to be honest there are some other additions made to the wsrt format but they don’t seem to mess things up as long as you get those dots replaced. And replacing the dots, is an easy task, it’s just extremely time consuming. Considering the file I took the example from had about 5500 lines meaning there is probably about 1100 lines where you need to go in and replace the dot with a comma. There is no way that I am going to do that for every wsrt file I want to use, so how do we fix this?

Easy, we do  a quick search and replace and all the dots will become commas. Ohh, wait maybe not, don’t most sentences usually end with a dot? Yeah, silly mistake you might think, there is no way that happened, unfortunately you would be wrong, I am that stupid from time to time. So now that we have established that how will we go about doing it then? It’s actually pretty easy, what we do is we search for all the dots that have a character after them and then we replace that dot with a comma. And we are done.

To make this process a little bit easier in the future I made a small web utility that does this for you, the tool is available for anyone to use on my tools page and the source has been released on GitHub. At the moment the tool won’t do anything about the other improvements to the format, I might go back and add that later but it isn’t really a problem as far as I can tell so probably not.

Update: The tool will now also remove other wsrt specific meta data. I think all of it will get removed but if you should find anything that gets left please leave a comment and let me know so I can fix it.

Update 2: Apparently some players can’t handle files encoded in UTF-8. Should you get some weird characters when using the text try to encode it with windows-1252 instead. I added an option for this to the tool, I also fixed a problem with the file name that occurred on Firefox.


A walk-trough of setting up this blog

So I thought it would be nice to take you on a walk-trough on how this blog has been set up in case you might be might be interested in starting one yourself. In the post I will not go into details about the different configurations of the blog software but more into hosting, domain and how to actually get it online. In each step I will tell you a little about why I have chosen for example a specific service.

So lets get started.

1. Domain

For me, choosing a domain name for the blog was easy, actually I bought it long before this blog was even thought of. Popeen is my nickname both online and offline so it just feels natural to me that it should also be my home on the web. When I first wanted to register it back in 08 it was already taken but I kept my eye on it and when suddenly it became available I rushed to buy it. And here is the part you are probably here for, where did I buy it? I bought it from a company called Loopia and since then I have kept it there. A couple of other domains I had at Loopia I have actually moved over to GoDaddy, not because they are better in some way but because they where cheaper at the time the domain  needed renewal. So for buying a domain go for what is cheapest at the moment, just make that you pick a serious company and that they will allow you to change the nameservers yourself, if you are unsure go with Loopia. When its time for renewal, have a look and see if things have changed or keep using what you choose in the first place if you don’t care for a couple of bucks here and there and are happy with what you got. If you decide to switch just remember that there often is a transfer fee from your new registrar.

2. Hosting

When it comes to hosting there are a ton of alternatives out there and probably just as many opinions. But in my case I have one host that I use for everything and that I love, Inleed. The speed of the host is good and so is the price tag. At the moment prices start at 19SEK/month, that is less then $3. But speed and price in all honor, where Inleed really shines is the support. It’s not often that I have had to contact them about something but when it has happened the response has been blazing fast even at very uncomfortable office hours. An example of this was this new years eve when I contacted them by email in the evening, in three minutes I had a response and in five the problem was solved. They are always quick to help and you feel like they really care about you. I’m sure there are other hosts out there that are just as good but there are also a lot of them that are really bad so why take the chance, so I say go for Inleed. If you go for something other then Inleed make sure that they support PHP & mySQL and that you will have FTP access to the server.

3. DNS

DNS? What? Isn’t that included in my domain you may say, and yes, it usually is, although it actually costs extra for Loopia users if you want to use theirs (this is probably true for some other registrars as well). But the thing is, you don’t want to use their DNS. You want to use a service called CloudFlare. Why would I want to use that you are probably saying now, well there are a lot of reasons for it and it would take up at least a full blog post to mention them all so lets just summarize them with that CloudFlare makes your website faster, more secure and that the base plan is free. If you want to read more about it you should have a look at the CloudFlare overview page. When you have registered your domain at CloudFlare you will get two nameservers back, they look something like this:

Keep in mind that yours will probably not be the same as the ones above. Once you have yours you head over to where you registered your domain and head into the domain settings. Since this is a bit different for every registrar I can’t tell you exactly how it will look but what you want to do is find where to change the nameservers and then set them to the ones you got from CloudFlare, if you cant find the settings ask the support and they will help you.

4. Software

Now we are getting in to my favorite part, the software. First of all you will be needing an FTP Client so that you can upload your files to the server, I recommend the FileZilla Client, it’s really good and it’s free. Secondly you will be needing your blogging software, for this we will use WordPress. If your host comes with a One-Click Setup you could use that but to be sure that you get the latest version I would recommend you install it by yourself. Installing it is really easy and I will be putting together a blog post on how to do it soon and add a link here but for now if you need help you can have a look at the wordpress support site.

5. Themes and Plugins

If you have been reading this post to set up a blog on your own and following along then first of all shame on you, you should always be reading the complete instructions before starting, secondly congratulations on your new blog. While the blog is technically finished it might be a good idea to install a custom theme on it to make it look a little better. You can find a lot of both free and paid themes by using google or you can head to the theme section on the wordpress website

Thats it, now you know how this blog is set up and how to do it yourself should you ever want to set up your own blog.

This article contains affiliate links, this means that if you click on it and buy something, for no extra cost to you I might receive a commision. This has not changed my opinions in any way and I have only linked to services I use myself and like.

The top photo in this post was taken by Torkild Retvedt and is released under the CC-BY-SA Licence. It has been cropped a bit and the original is available on this flickr page.