<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://nilsding.org/</id>
  <title>Jyrki's Blog</title>
  <subtitle>ENOTTY: not a typewriter</subtitle>
  <updated>2022-12-31T11:06:50Z</updated>
  <icon>https://nilsding.org/favicon.ico</icon>
  <logo>https://nilsding.org/assets/jyrki-2ff215ef66fc8993e09e1e0e5bac38dfc7d0d75e629f671ef7c47a0cbb31b72d.png</logo>
  <link href="https://nilsding.org"/>
  <link href="https://nilsding.org/feed.xml" rel="self"/>
  <generator uri="https://github.com/nilsding/foxpage" version="1.0.0">FoxPage</generator>
  <entry>
    <id>https://nilsding.org/blog/2022/12/31/kdesrc-build-macos</id>
    <title>Notes on using kdesrc-build on macOS</title>
    <updated>2022-12-31T11:06:50Z</updated>
    <link href="https://nilsding.org/blog/2022/12/31/kdesrc-build-macos" rel="alternate"/>
    <category term="kde"/>
    <category term="apple"/>
    <category term="code"/>
    <published>2022-12-31T11:06:50Z</published>
    <summary type="html">&lt;p&gt;Long time no post – whatever, I guess.  I’ll just post about something I just
ran into and might need to do again in the future.  Maybe this will even be
useful to someone else.&lt;/p&gt;

</summary>
    <content type="html">&lt;p&gt;Long time no post – whatever, I guess.  I’ll just post about something I just
ran into and might need to do again in the future.  Maybe this will even be
useful to someone else.&lt;/p&gt;

&lt;h2 id="builds-fail-with-some-strange-type-errors"&gt;Builds fail with some strange type errors&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Environment:&lt;/strong&gt; Both Qt5 and Qt6 are installed via Homebrew, the Qt5 directory
is in the &lt;code&gt;$PATH&lt;/code&gt; environment variable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; some components fail to build with something like this in the logs:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[4/77] Building CXX object src/i18n/CMakeFiles/KF5I18n.dir/common_helpers.cpp.o
FAILED: src/i18n/CMakeFiles/KF5I18n.dir/common_helpers.cpp.o
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++ [some flags omitted] -I/opt/homebrew/include -iframework /opt/homebrew/opt/qt@5/lib -isystem /opt/homebrew/opt/qt@5/lib/QtCore.framework/Headers -isystem /opt/homebrew/opt/qt@5/./mkspecs/macx-clang-arm64 [more flags omitted] -o src/i18n/CMakeFiles/KF5I18n.dir/common_helpers.cpp.o -c /Users/foxy/kde/src/frameworks/ki18n/src/i18n/common_helpers.cpp
In file included from /Users/foxy/kde/src/frameworks/ki18n/src/i18n/common_helpers.cpp:7:
In file included from /Users/foxy/kde/src/frameworks/ki18n/src/i18n/common_helpers_p.h:10:
In file included from /opt/homebrew/opt/qt@5/lib/QtCore.framework/Headers/QString:1:
/opt/homebrew/opt/qt@5/lib/QtCore.framework/Headers/qstring.h:78:7: error: definition of type 'QStringList' conflicts with type alias of the same name
class QStringList;
      ^
/opt/homebrew/include/QtCore/qcontainerfwd.h:28:7: note: 'QStringList' declared here
using QStringList = QList&amp;lt;QString&amp;gt;;
      ^
In file included from /Users/foxy/kde/src/frameworks/ki18n/src/i18n/common_helpers.cpp:7:
In file included from /Users/foxy/kde/src/frameworks/ki18n/src/i18n/common_helpers_p.h:10:
In file included from /opt/homebrew/opt/qt@5/lib/QtCore.framework/Headers/QString:1:
/opt/homebrew/opt/qt@5/lib/QtCore.framework/Headers/qstring.h:81:29: error: redefinition of 'QVector' as different kind of symbol
template &amp;lt;typename T&amp;gt; class QVector;
                            ^
/opt/homebrew/include/QtCore/qcontainerfwd.h:27:22: note: previous definition is here
template&amp;lt;typename T&amp;gt; using QVector = QList&amp;lt;T&amp;gt;;
                     ^
In file included from /Users/foxy/kde/src/frameworks/ki18n/src/i18n/common_helpers.cpp:7:
In file included from /Users/foxy/kde/src/frameworks/ki18n/src/i18n/common_helpers_p.h:10:
In file included from /opt/homebrew/opt/qt@5/lib/QtCore.framework/Headers/QString:1:
/opt/homebrew/opt/qt@5/lib/QtCore.framework/Headers/qstring.h:261:13: error: unknown type name 'QStringData'
    typedef QStringData Data;
            ^
/opt/homebrew/opt/qt@5/lib/QtCore.framework/Headers/qstring.h:967:37: error: unknown type name 'QStringDataPtr'
    Q_DECL_CONSTEXPR inline QString(QStringDataPtr dd) : d(dd.ptr) {}
                                    ^
/opt/homebrew/opt/qt@5/lib/QtCore.framework/Headers/qstring.h:273:72: error: 'QString::Data' (aka 'int') is not a class, namespace, or enumeration
    inline QString(QString &amp;amp;&amp;amp; other) noexcept : d(other.d) { other.d = Data::sharedNull(); }
                                                                       ^
/opt/homebrew/opt/qt@5/lib/QtCore.framework/Headers/qstring.h:277:39: error: member reference base type 'QString::Data' (aka 'int') is not a structure or union
    inline int size() const { return d-&amp;gt;size; }
                                     ~^ ~~~~
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Run &lt;code&gt;brew unlink qt@6&lt;/code&gt;.  This needs to be done every time after
&lt;code&gt;brew upgrade&lt;/code&gt; updates qt@6.&lt;/p&gt;

&lt;p&gt;This removes all the symlinks Homebrew creates for Qt6 inside e.g.
&lt;code&gt;/opt/homebrew/include&lt;/code&gt;.  I only noticed this after having a closer look at the
paths the compiler spit out at me – some Qt6 libraries already got included
which made the Qt5 one upset.&lt;/p&gt;

&lt;h2 id="no-icons-in-the-apps"&gt;No icons in the apps&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; There are no icons in the apps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Build &lt;code&gt;breeze-icons&lt;/code&gt; and create a special symlink for each app
you want to run:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-sh"&gt;# build breeze-icons
kdesrc-build breeze-icons

# create an app-specific symlink of the compiled breeze-icons resource
# to ~/Library/Application\ Support/&amp;lt;app&amp;gt;
#
# e.g. for Konsole:
ln -s ~/kde/usr/share/icons/breeze/breeze-icons.rcc ~/Library/Application\ Support/konsole/icontheme.rcc
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This method has a downside though, when switching to dark mode the icons do not
change to their light/dark variants.  Since I use the light mode everywhere
this doesn’t bother me too much, so I haven’t looked into how to improve that
(yet).&lt;/p&gt;
</content>
    <author>
      <name>Jyrki</name>
    </author>
  </entry>
  <entry>
    <id>https://nilsding.org/blog/2020/01/10/spotify-unavailable-tracks</id>
    <title>How many songs does Spotify hide from me?</title>
    <updated>2020-01-10T15:57:25Z</updated>
    <link href="https://nilsding.org/blog/2020/01/10/spotify-unavailable-tracks" rel="alternate"/>
    <category term="code"/>
    <category term="music"/>
    <published>2020-01-10T15:57:25Z</published>
    <summary type="html">&lt;p&gt;After writing my last post I got curious about how many songs of my Spotify
library are unavailable to me.  Therefore I wrote a small script to figure out
exactly that.&lt;/p&gt;

&lt;p&gt;Thankfully there are libraries for almost everything imaginable available, ready
to be used.&lt;/p&gt;

</summary>
    <content type="html">&lt;p&gt;After writing my last post I got curious about how many songs of my Spotify
library are unavailable to me.  Therefore I wrote a small script to figure out
exactly that.&lt;/p&gt;

&lt;p&gt;Thankfully there are libraries for almost everything imaginable available, ready
to be used.  &lt;!-- more --&gt; I looked up some Spotify-related gems on RubyGems and
found &lt;a href="https://github.com/guilhermesad/rspotify"&gt;RSpotify&lt;/a&gt;, which interfaces with Spotify’s web API quite nicely (&lt;em&gt;and&lt;/em&gt;
it has docs).&lt;/p&gt;

&lt;p&gt;In order to read private playlists and the saved songs library (which has been
renamed by Spotify to “Liked Songs” for apparently no reason other than to
appeal more to Facebookstagram users, or to emphasise on the fact that these
songs are not actually &lt;em&gt;saved on your devices&lt;/em&gt;), I needed to authorise as a user
against its web API.  Unfortunately it requires a web server for the OAuth2
callback to work properly.  Fortunately, there are libraries that handle the
details for it, &lt;a href="https://github.com/omniauth/omniauth"&gt;OmniAuth&lt;/a&gt; in particular.  And RSpotify even provides an
OmniAuth provider for Spotify – perfect for what I need to do.&lt;/p&gt;

&lt;p&gt;Before implementing some code I had to create an application on Spotify to
receive the OAuth2 client ID and client secret.  This can be done by visiting
&lt;a href="https://developer.spotify.com/dashboard/applications"&gt;the Spotify applications dashboard&lt;/a&gt;.  Once created I added a
&lt;em&gt;Redirect URI&lt;/em&gt; of &lt;code&gt;http://localhost:8080/auth/spotify/callback&lt;/code&gt; to it.&lt;/p&gt;

&lt;p&gt;I figured I started with the OAuth callback server.  For that I decided to just
temporarily start up a very bare bones Rack application that has the OmniAuth
middleware mounted and configured, along with a handler for the actual callback
received from Spotify.  The callback handler just stores the received OAuth
token in a global variable which will be used to authenticate with Spotify’s web
API afterwards.&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-ruby"&gt;require "rspotify"
require "rspotify/oauth"
require "omniauth"
require "webrick"
require "rack"

auth_url = "http://localhost:8080/auth/spotify"
puts "==&amp;gt; Please visit #{auth_url}"

$rack = Rack::Server.new(
  Port:   8080,
  server: "webrick",
  app: Rack::Builder.new do
    # OmniAuth requires a Rack session to be available
    use Rack::Session::Cookie, secret: "Some secret"

    use OmniAuth::Builder do
      provider :spotify,
               ENV.fetch("SPOTIFY_CLIENT_ID"), ENV.fetch("SPOTIFY_CLIENT_SECRET"),
               # We want to read our library (i.e. saved songs) and private playlists
               scope: "user-library-read playlist-read-private"
    end

    run(lambda do |env|
      case env["PATH_INFO"]
      when "/auth/spotify/callback"
        $omniauth_auth = env["omniauth.auth"]
        $rack.server.shutdown
        [200, { "Content-Type" =&amp;gt; "text/html" },
         ["Code received.  Check your terminal."]]
      else
        [200, { "Content-Type" =&amp;gt; "text/html" },
         [%(Please visit &amp;lt;a href="#{auth_url}"&amp;gt;#{auth_url}&amp;lt;/a&amp;gt;.)]]
      end
    end)
  end
)
$rack.start

me = RSpotify::User.new($omniauth_auth)

# For debugging purposes:
binding.irb
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Running this allows me to open up &lt;code&gt;http://localhost:8080/auth/spotify&lt;/code&gt; in a web
browser, where I then need to trust my own application to access my own Spotify
account.  Once allowed I could request some information from their APIs, such
as:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-ruby"&gt;irb(main):001:0&amp;gt; me.id  # get the User ID/screen name
=&amp;gt; "nilsding"
irb(main):002:0&amp;gt; me.saved_tracks.first.name  # get the name of the first track
=&amp;gt; "The Dark Side"
irb(main):003:0&amp;gt; me.saved_tracks.first.artists.map(&amp;amp;:name)  # get the artists of the first track
=&amp;gt; ["Muse"]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The Spotify API docs for &lt;a href="https://developer.spotify.com/documentation/web-api/reference/tracks/get-track/"&gt;“Get a track”&lt;/a&gt; mention a field
&lt;code&gt;is_playable&lt;/code&gt; in the full track object, which describes whether a track is
playable in a given market or not.  For that field to appear I need to add
another parameter for the market I’m interested in to the saved tracks request.
This can be done with RSpotify like so:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-ruby"&gt;irb(main):004:0&amp;gt; me.saved_tracks(market: "AT").first.is_playable
Traceback (most recent call last):
        5: from ./spotify-availability.rb:78:in `&amp;lt;main&amp;gt;'
        4: from &amp;lt;internal:prelude&amp;gt;:145:in `irb'
        3: from (irb):20:in `&amp;lt;main&amp;gt;'
        2: from (irb):20:in `rescue in &amp;lt;main&amp;gt;'
        1: from /home/nilsding/.rbenv/versions/2.6.5/lib64/ruby/gems/2.6.0/gems/rspotify-2.7.0/lib/rspotify/user.rb:316:in `saved_tracks'
ArgumentError (unknown keyword: market)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Or maybe not, at least at the time of writing. But it’s &lt;a href="https://github.com/guilhermesad/rspotify/pull/208/files"&gt;nothing that couldn’t
be fixed&lt;/a&gt;, really.  Once that patch is applied, I finally got the
expected response:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-ruby"&gt;irb(main):001:0&amp;gt; me.saved_tracks(market: "AT").first.is_playable
=&amp;gt; true
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Yay!&lt;/p&gt;

&lt;p&gt;Now I can finally find the first saved track in my library that’s unplayable in
any given market by iterating over the first few pages returned by the API:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-ruby"&gt;irb(main):002:0&amp;gt; def me.first_non_playable_track(market)
irb(main):003:1&amp;gt;   (0..500).step(50).flat_map do |offset|
irb(main):004:2*     self.saved_tracks(market: market, limit: 50, offset: offset)
irb(main):005:2&amp;gt;   end.find { |track| !track.is_playable }.tap do |track|
irb(main):006:2*     return [track.artists.map(&amp;amp;:name).join(", "), track.name].join(" - ")
irb(main):007:2&amp;gt;   end
irb(main):008:1&amp;gt; end
=&amp;gt; :first_non_playable_track
irb(main):009:0&amp;gt; me.first_non_playable_track "AT"  # first non-playable track in Austria
=&amp;gt; "Solid Globe - North Pole - Original mix"
irb(main):010:0&amp;gt; me.first_non_playable_track "US"  # first non-playable track in the USA
=&amp;gt; "Rainhard Fendrich - Haben Sie Wien schon bei Nacht geseh'n"
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;While us Austrians don’t get to enjoy some Dutch progressive trance, people in
the US don’t get to enjoy a very fine piece of &lt;a href="https://en.wikipedia.org/wiki/Austropop"&gt;Austropop&lt;/a&gt;.  Seems like a fair
trade-off &lt;img class="emoji" width="16px" height="16px" title="wink_tongue" src="/assets/emoji/wink_tongue-3c83722bb1ed5e314ca2c6f37930c24ec7a5b2bb8942ddeda30786611aaa7a6e.svg" /&gt;&lt;/p&gt;

&lt;p&gt;Anyway, time to move on from trying stuff around in IRB and finally print out some
numbers.  In the end I came up with this:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-ruby"&gt;songs = []
offset = 0
page_size = 50
loop do
  saved_tracks_part = me.saved_tracks(market: "AT", limit: page_size, offset: offset)
  break if saved_tracks_part.empty?

  songs.concat(saved_tracks_part)
  offset += page_size
rescue RestClient::BadGateway
  # Apparently Spotify's API returns a 502 Bad Gateway if there are no more songs available.
  # Fine, just rescue this error and move on ...
  break
end

puts "Found #{songs.count} songs."

# Group songs by playable (true) or not playable (false)
grouped_songs = songs.group_by(&amp;amp;:is_playable)

puts "Playable songs: #{grouped_songs[true].count}"
puts "Unplayable songs: #{grouped_songs[false].count}"
print "That's "
print ((grouped_songs[false].count.to_f / songs.count) * 100).round(3)
puts "% unplayable songs!"
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In case you’re wondering about the &lt;code&gt;rescue&lt;/code&gt; block above… I’m not making this
up.  Perhaps the response actually contains some useful information, but I
couldn’t be bothered to check it myself.&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-ruby"&gt;irb(main):042:0&amp;gt; me.saved_tracks(limit: 1, offset: 390000)
Traceback (most recent call last):
        [traceback omitted]
RestClient::BadGateway (502 Bad Gateway)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Enough code, time for the results:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Found 4718 songs.
Playable songs: 4641
Unplayable songs: 77
That's 1.632% unplayable songs!
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It’s not does not seem much, but the fact that these numbers can (and will)
change even though I have not added or removed any song myself bothers me.  A
lot.  And while the number might not change, some tracks may become available
again whereas others will become unavailable.  Sigh.&lt;/p&gt;

&lt;p&gt;I also ran the same script for some other markets as well. Because why not.
Here are the results:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Country&lt;/th&gt;
      &lt;th&gt;Playable&lt;/th&gt;
      &lt;th&gt;Unplayable&lt;/th&gt;
      &lt;th&gt;Percentage&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Austria&lt;/td&gt;
      &lt;td&gt;4641&lt;/td&gt;
      &lt;td&gt;77&lt;/td&gt;
      &lt;td&gt;1.632%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Germany&lt;/td&gt;
      &lt;td&gt;4635&lt;/td&gt;
      &lt;td&gt;83&lt;/td&gt;
      &lt;td&gt;1.759%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Finland&lt;/td&gt;
      &lt;td&gt;4609&lt;/td&gt;
      &lt;td&gt;109&lt;/td&gt;
      &lt;td&gt;2.310%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Sweden&lt;/td&gt;
      &lt;td&gt;4608&lt;/td&gt;
      &lt;td&gt;110&lt;/td&gt;
      &lt;td&gt;2.331%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Italy&lt;/td&gt;
      &lt;td&gt;4607&lt;/td&gt;
      &lt;td&gt;111&lt;/td&gt;
      &lt;td&gt;2.353%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;United Kingdom&lt;/td&gt;
      &lt;td&gt;4599&lt;/td&gt;
      &lt;td&gt;119&lt;/td&gt;
      &lt;td&gt;2.522%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;France&lt;/td&gt;
      &lt;td&gt;4591&lt;/td&gt;
      &lt;td&gt;127&lt;/td&gt;
      &lt;td&gt;2.692%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;United States&lt;/td&gt;
      &lt;td&gt;4517&lt;/td&gt;
      &lt;td&gt;201&lt;/td&gt;
      &lt;td&gt;4.260%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Canada&lt;/td&gt;
      &lt;td&gt;4491&lt;/td&gt;
      &lt;td&gt;227&lt;/td&gt;
      &lt;td&gt;4.811%&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Well, looks like my music taste would not work well in Canada.  At least when
using Spotify.&lt;/p&gt;

&lt;p&gt;For those interested in making their own stats, the full code of this is up on
my GitHub of course: &lt;a href="https://github.com/nilsding/spotify-availability"&gt;nilsding/spotify-availability&lt;/a&gt;.&lt;/p&gt;

</content>
    <author>
      <name>Jyrki</name>
    </author>
  </entry>
  <entry>
    <id>https://nilsding.org/blog/2020/01/07/goals-for-the-new-year</id>
    <title>(Some of) my goals for the new year</title>
    <updated>2020-01-07T21:43:56Z</updated>
    <link href="https://nilsding.org/blog/2020/01/07/goals-for-the-new-year" rel="alternate"/>
    <category term="self"/>
    <published>2020-01-07T21:43:56Z</published>
    <summary type="html">&lt;p&gt;A new year has started, and I still haven’t blogged anything… until now.  Oh
well, better late than never ¯\_(ツ)_/¯&lt;/p&gt;

&lt;p&gt;New year’s resolutions aren’t usually my thing (because they usually last a few
weeks anyway), but I think some stuff I currently do or use needs to change.&lt;/p&gt;

</summary>
    <content type="html">&lt;p&gt;A new year has started, and I still haven’t blogged anything… until now.  Oh
well, better late than never ¯\_(ツ)_/¯&lt;/p&gt;

&lt;p&gt;New year’s resolutions aren’t usually my thing (because they usually last a few
weeks anyway), but I think some stuff I currently do or use needs to change.
&lt;!--more--&gt; In particular:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Stop smoking/vaping&lt;/strong&gt;: After a little more than two years of vaping, I think
it’s finally time to call it quits, especially after the Austrian government
finally banned smoking in bars and restaurants on November 1st last year.
During the last few months of 2019 I already cut down on how much I vape, but
I still have some liquid left (at the time of writing it’s 20ml) that I slowly
but surely need to get rid of.  I’m sure there will be another habit to pick
up instead (maybe it’s actually writing some posts? &lt;img class="emoji" width="16px" height="16px" title="wink_tongue" src="/assets/emoji/wink_tongue-3c83722bb1ed5e314ca2c6f37930c24ec7a5b2bb8942ddeda30786611aaa7a6e.svg" /&gt;).&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Use fewer streaming services&lt;/strong&gt;: While Spotify is great and everything, it
still does not have all the music I used to listen to a few years back
(especially releases I bought on Bandcamp), and its suggestions kinda suck
nowadays.  And if it suddenly &lt;em&gt;does&lt;/em&gt; feature something I want to listen to,
something else I have listened to a while ago just disappears &lt;em&gt;without any
notice&lt;/em&gt;.  Well, those songs do not really disappear, they just get greyed out
in my playlists without any way to listen to them ever again from the Spotify
player.&lt;/p&gt;

    &lt;p&gt;Same goes for Netflix and Prime Video – the available library here in Austria
sucks if you don’t want to watch their exclusive titles.  And let’s not forget
the other streaming platforms which have their own set of exclusive titles.
Over time it’s just becoming an expensive nightmare with everyone going their
own way.  And similar to Spotify, there is no guarantee that titles will be
there for all eternity.&lt;/p&gt;

    &lt;p&gt;I think it goes without saying that all of these mentioned services do some
extensive tracking on you, which I rather want to avoid.&lt;/p&gt;

    &lt;p&gt;Obviously, the solution to that is to build up my own media library again.
This happens to be a lot easier with music, so it’s most likely the first
thing I’ll be working on.  I already started to collect some CDs, which have
yet to be ripped; that happens once I have decided what hardware I’ll be using
for that.  I have a few spare SATA HDDs laying around, so it would be nice if
I can just use them with some low-power single-board computer (preferably not
a Raspberry Pi).  Eventually, I’d like to stream the songs from my library to
my devices similar to how Spotify works (sans DRM).  Which means that I’ll
have to not only write a desktop client (or maybe just some integration for an
already existing player), but also work around the hell that is mobile app
development.  Fun.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Produce some music&lt;/strong&gt;: Yeah, that will probably be a hard one.  Maybe.  I
don’t know.  But dabbling around in Renoise and with my reface DX is a whole
lot of fun.  Let’s see (or rather hear) if something good comes out of that.
Hopefully I also figure out a way to make JACK behave nicely with
PulseAudio… yes yes, I know, audio on Linux…  at least we’ve arrived in
the Year of the Linux Desktop™, right?&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Release more software&lt;/strong&gt;: Most likely these will be some tools to assist me
with certain services or tasks.  It’s unlikely that the things I’ll work on
are web applications.  We’ll see how that will go.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Lose some weight&lt;/strong&gt;: I would love to see my scale to display a digit starting
with 7 or even 6 instead of an 8.  Probably should work out some more and/or
eat less (junk) food at unholy times.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not a lot of things listed here, most of them should be pretty easy to achieve,
but yeah.  New year’s resolutions and all that.  You all know how well these
turn out.&lt;/p&gt;

&lt;p&gt;Now, what have I achieved in 2019 of which I did not even plan some things to
happen?  Well, here’s a small selection:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;I went to my first furry convention: NordicFuzzCon.  It was a real blast, had
a really great time with all the people I already have known online.  Can’t
wait for this year’s edition which happens in the centre of Malmö, which opens
up more opportunities of fun craziness.&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://soundcloud.com/nilsding/muehlenrave"&gt;I released my first song to an unsuspecting world&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;I stopped using Twitter in favour of Mastodon.  It was no real fun anymore
when they turned the timeline into a “feed” full of ads and other things I’m
not interested in, but the final straw for me was them killing off the
streaming APIs.  Which turned Tweetbot (the iOS Twitter client I was using)
from super-awesome to a more bearable Twitter experience (it still got the
timelines, after all).&lt;/li&gt;
  &lt;li&gt;I switched my job role from software engineering to infrastructure
engineering.&lt;/li&gt;
  &lt;li&gt;I finally deleted my Instagram account, freeing up the last personal part
Facebook had about me.  (I highly doubt that they &lt;em&gt;actually deleted&lt;/em&gt; my user
data).&lt;/li&gt;
  &lt;li&gt;I finally finished the rework of this site.&lt;/li&gt;
  &lt;li&gt;And last but definitely not least, I’m in a relationship with
&lt;a href="https://pounced-on.me/@karina"&gt;Karina&lt;/a&gt;!  I could not be happier about that.  &lt;img class="emoji" width="16px" height="16px" title="wink_kiss" src="/assets/emoji/wink_kiss-59f434b5840962e610f0a711a04a1f71ff41eb9e58e8b9d8b6debaf23d9c04d6.svg" /&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With all the new year’s resolution-y stuff and some retrospective out of the
way, there are a few events I will definitely attend this year.  Feel free to
say “Hi” to me if you spot me!&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;February&lt;/strong&gt;: NordicFuzzCon&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;April&lt;/strong&gt;: Revision&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;July/August&lt;/strong&gt;: Evoke&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In other news, I managed to get a post tagging system working.  It’s kinda
hacky, but pretty neat.  It even has its own pagination going, but that won’t
kick in until I have more than ten posts per tag.  And as of now there’s only
one tag anyway.  &lt;img class="emoji" width="16px" height="16px" title="sunglasses" src="/assets/emoji/sunglasses-5faf5613934c3e24117d0699a315fec59c7fdd2be296ccc3650f2dca01edb037.svg" /&gt;&lt;/p&gt;

</content>
    <author>
      <name>Jyrki</name>
    </author>
  </entry>
  <entry>
    <id>https://nilsding.org/blog/2019/12/24/hello-world</id>
    <title>hello, world</title>
    <updated>2019-12-23T23:00:00Z</updated>
    <link href="https://nilsding.org/blog/2019/12/24/hello-world" rel="alternate"/>
    <category term="self"/>
    <published>2019-12-23T23:00:00Z</published>
    <summary type="html">&lt;p&gt;After a few months of &lt;del&gt;procrastination&lt;/del&gt; hard work, I finally finished the
rework of my personal website.  Yay!&lt;/p&gt;

&lt;p&gt;It is no longer built with Jekyll, but with my own static page generator which I
purposely built for this site alone: &lt;a href="https://github.com/nilsding/foxpage"&gt;FoxPage&lt;/a&gt;.  Seems to work pretty nice so
far.&lt;/p&gt;

</summary>
    <content type="html">&lt;p&gt;After a few months of &lt;del&gt;procrastination&lt;/del&gt; hard work, I finally finished the
rework of my personal website.  Yay!&lt;/p&gt;

&lt;p&gt;It is no longer built with Jekyll, but with my own static page generator which I
purposely built for this site alone: &lt;a href="https://github.com/nilsding/foxpage"&gt;FoxPage&lt;/a&gt;.  Seems to work pretty nice so
far. &lt;!-- more --&gt;&lt;/p&gt;

&lt;p&gt;The new design is inspired by the looks of &lt;a href="https://en.wikipedia.org/wiki/NeXTSTEP"&gt;NeXTStep&lt;/a&gt;, of course.  I wrote all
the SVGs and CSS used by hand… well, I cheated a bit on the latter by using
SCSS.  I’m pretty pleased with how it looks, especially in contrast to many
other personal websites which most of the time just look like some weird startup
to me.  Oh well.&lt;/p&gt;

&lt;p&gt;And yes, I have a blog again.  I hope that throughout the next year that becomes
more of a habit for me.  Don’t know about what I’ll be blogging just yet though.
But I do provide an &lt;a href="/feed.xml"&gt;Atom feed&lt;/a&gt; if you want to get notified on new
posts.&lt;/p&gt;

</content>
    <author>
      <name>Jyrki</name>
    </author>
  </entry>
</feed>
