CentOS Changes

On 8 Dec I saw the announcement that Red Hat (which had made CentOS an in-house distro a year or two ago) was changing CentOS from being a free clone of Red Hat Enterprise Linux, to focusing solely on CentOS Stream. (The CentOS press release; The Red Hat press release) This would make the progression of features and elements: Fedora -> CentOS Stream -> RHEL. At first, like many others, I felt hurt by this change. It will take place at the end of next year. Usually CentOS follows a support widow similar to that of RHEL, so I was expecting a decade or so for the servers I converted from CentOS 7 to CentOS 8. While I generally run Fedora on many of my computers, I prefer not to have to upgrade every 6 months for my servers. That’s a level of disruption I could do without. That said, after I read this blog post, I had a slightly more nuanced view of things. I still think it should have taken effect with CentOS 9, which I think is due in the next year or so (RH is accelerating releases of new RHELs). But it certainly makes a clearer upstream to downstream path for Red Hat.

For me, personally, I will probably just go to CentOS Stream. It’s going to depend on what the upgrade model looks like. Again, I have no problem with Fedora, I just don’t want to have to upgrade every 6 months. If CentOS Stream just becomes rolling updates, I can swing with that. Particularly if it has the same lifetime as RHEL. I think it’s mostly going to depend on EPEL packages, as I need some of those for some of the servers I run. 

It does give me pause on what to do with my wife. My fourth-most viewed Youtube video is:

In which I investigate whether my wife would do alright switching from Ubuntu to CentOS. I never made the switch because things were going OK with Kubuntu. Recently, however, her computer has been having issues making me think I might need to reinstall. I was thinking of doing CentOS 8, but now I’m not 100% sure.

Then again, there’s another possible option. Today I learned about Rocky Linux.Basically, the founder of CentOS wasn’t happy with this new CentOS direction. So he decided to start over again with Rocky Linux. Hopefully his experience running CentOS means it’s not a rocky start (pun intended). Whether I (and others) go for Rocky is going to really depend on 3 things:

  1. Is EPEL going to support it? Even though EPEL is a repo for CentOS and RHEL packages, it’s run by the Fedora Project. Fedora is *somewhat* independent from Red Hat, but I’m still not sure if they’d make packages that would work well with Rocky if they’re going to focus on making the packages work with CentOS Stream.
    1. Also, EPEL 8 hasn’t had nearly the amount of packages as EPEL 7. So, I’m unsure where that takes things going forward for either CentOS Stream or Rocky.
  2. Are Digital Ocean, Linode, and AWS going to support Rocky? For that matter, are they going to support CentOS Stream? Uncertain at this point. In Red Hat’s announcement they talk about potentially loosening up some of the restrictions on what devs can do with their free license for a RHEL machine. Yes, they offer free dev licenses for RHEL. I’m using it to run a RHEL VM to play around with. The goal is to provide a perfect RHEL for testing against before going to Production. I guess that means there were always some potential differences with CentOS? Otherwise why not just use that?
  3. How many folks are just going to jump ship to Debian/Ubuntu? Sure, if you’re doing that at the enterprise level, it’s not so easy. There may be packages you depend on that are slightly different or the locations of certain config files. At the same time, my experience in the CentOS/RHEL world has always been that there isn’t really an upgrade path. When CentOS/RHEL EOLs you’re expect to migrate. So if you’re migrating anyway, maybe you’ll go to the Debian world? Also, if you’re just a dude with a server in the cloud (AWS, Digital Ocean, Linode) then maybe it’s less important. And if you’re running Docker/Podman containers anyway, you may care even less.

Sam and Stella Birthday Portraits

Somewhat without intention, I’ve ended up alternating portraits for the twins between outdoor portraits and indoor portraits. This year was time for indoor portraits. (Although, to be fair, if it hadn’t been freezing, I might have ended up breaking the tradition) Here’s the setup I used:

It’s a variant on a setup that has been very successful for me with portraits of the kids. I went back to a white background for the first time in a few years. The last time I used it with Scarlett I had an uneven white that looked horrendous to my eye and was a real pain in the butt to try and correct in Photoshop (or was it GIMP?). I never got it quite right and ended up sticking to the black background for a while. But I wanted to do something different. So this time I grabbed my two studio strobes (I believe they’re 100W strobes that someone got me as a present some 8 or so years ago) and threw them into umbrellas to make sure the light would hit the background evenly. With these lights I had to go to somewhere around 80% power in order to get a pure white background according to the spectrograph in camera. Then I posed the twins in front of those lights, so they needed a light for themselves. I used my old Canon 580EX inside of my gigantic octagon (I think it’s somewhere around 3 feet or more in diameter). I wanted to shoot at F8 to try and get the best chance of ensuring the twins would be in focus, so I had to push the 580EX to full power. After this session, I ended up buying a bracket so that next time I can throw my 430EX in there, too.

Once I had my setup perfect, I invited Sam in for his shots. I didn’t try and direct either of the twins. I just let them do whatever came naturally. My only instructions were that every shot couldn’t be a silly one. Here’s my favorite Samuel shot – the one I’m printing for work:

Sam's portraits
Photo Time?

Here are some of the others poses he came up with:

  • Sam's portraits
  • Sam's portraits
  • Sam's portraits

Then it was Stella’s turn. Here’s my favorite Stella shot:

Stella portraits
Yes?

And a few Stella poses:

  • Stella portraits
  • Stella portraits
  • Stella portraits

Then it was time for some twin portraits. The only thing I did was put Sam’s arm on Stella’s shoulder. Everything else they did on their own. There weren’t that many good shots (you try keeping two five year olds from making every pose the “silly” pose), so here are the two that came out great. First, the one I’m printing out for work:

Sam and Stella portrait in black and white
Twin Life
Sam and Stella portrait
Hahahahaha, I have a sister.

Later on for their cake photos, I just aimed my 580EX up at the ceiling with a +1 correction.

Followup On Unity and JetBrains Rider on Fedora

As you recall from the previous blog post, I’d installed Unity and JetBrains on my Fedora 32 computer via Flatpaks. I was going to use them for the Unity Multiplayer course I was taking on Udemy. Unfortunately it was an immediate fail and in lesson one after they have me install a new inputs library and restart Unity, it would always crash upon loading the file. I’m currently installing Unity 2020.1 on my Windows computer where I don’t expect to have that issue. Assuming I don’t, then it’s a big fat nope on using Unity on Fedora via Flatpak (at least for this class). Which, to be fair, is not on their supported OS list – which is only Ubuntu and CentOS 7. (And the latter for movie-making)

Unity and JetBrains Rider on Fedora via Flathub

As I mentioned last year in my 2019 in Programming post, I created a bunch of 2D games in Unity by following along with the Gamedev.tv classes. I would watch the videos on Linux and jump over to my Windows computer for the programming, learning how to use SourceTree and Microsoft Video Studio in the process. But for some reason, going back and forth with the KVM when running Unity would sometimes freeze up the Windows computer. So when I saw someone on Fedora Planet running Unity Hub, I thought I’d see if there was a Flatpak – and there IS! Also, I’ve fallen in love with JetBrain’s Pycharm, so I thought I’d go ahead and use their game dev IDE, Rider. (There’s a Flatpak for that, too!) So, let’s see how well this works!

Unity Hub – need a license

Apparently if you go this route, you have to handle licensing first. Just clicked on manual activation. That led me to login to Unity.com with my already-extant Unity creds. After answering some questions about how much money I make with Unity, they gave me a license file that I attached to Unity hub. Then I went to the General section on the left there to tell it where to install Unity versions. Once that was done, the hub more or less looked like I remembered it on Windows.

Unity Hub without any projects
Unity Hub without any projects

I already knew, from my previous forays, that I would need to add a Unity install, so it was off to the Installs section. This is what the selection looked like:

I don’t know if this is how it is on Windows now, too, since it’s been a long time since I worked in Unity. But, having found myself with a new version of Unity every time I signed in – I’m glad they have LTS versions, now. The CentOS of Unity, if you will. I went and checked the next course I want to do, GameDev.tv’s Unity Multiplayer class (here on Udemy and here on Gamedev.tv), and they want 2020.1. So I’ll install that version. For some reason, it wouldn’t let me download that version – it complained it would take up too much space (even though I have 900ish GB free and it said it would take up 10GB). But it decided it could install the LTS version. So, in the interest of seeing if it could open and run the games I’d previously developed, I just went with the LTS version for now. It quit out and complained about a corrupted download. But I don’t know where it was downloading to, because nothing was in the folder I told it to download to. If it is downloading somewhere else first, like /tmp – maybe that would explain the issues.

Eventually I tell it not to do any runtimes and I keep trying to install a bunch of times. Like a reverse Murphy’s law – as soon as I start posting about the problem on the /r/unity3D subreddit it starts working.

Unity Hub with my Block Destroyer game open
Unity Hub with my Block Destroyer game open

Despite my inability to install Unity 2020 over two days (edit: after literally 10 tries, restarting Unity Hub and restarting my computer, I finally got Unity 2020 to install), at least it ran my code from last year’s class ok, including upgrading it to the LTS version (which came out after I last worked on it). When I hit play it also ran reasonably well – didn’t seem to be at some incredibly low FPS. (Of course, this is a 2D game without lots of resources, but that’s still encouraging). It wanted me to have VS Code running. I think I also saw that on Flathub, but I decided to see if I could somehow get it to work with Rider in Flatpak form.

I launched Rider for the first time and it started off by asking for my preferences. First off was the UI theme:

JetBrains Rider UI Theme Selection
JetBrains Rider UI Theme Selection

And what kind of hacker would I be if I didn’t go with dark? Then it was time for the color schemes:

JetBrains Rider Color Theme Selection
JetBrains Rider Color Theme Selection

If I’d used a bunch more Visual Studio, I’d go with the middle selection. I’m a big fan of the Dracula themes I’ve been using in various editors, but I don’t think that’s the same as their Darcula theme since that mentions Intellij. So I just went with Rider Dark.

JetBrains Rider Keymap Selection
JetBrains Rider Keymap Selection

I didn’t have any particular preference here, so I just went with Visual Studio since I figured that would probably match shortcuts that the GameDev.tv guys would use. I decided not to do a Desktop Entry since, a you can see at the top of the next screenshot, it already seemed to have an icon:

JetBrains Rider Desktop Entry Question
JetBrains Rider Desktop Entry Question

I don’t have the environment installed for C#.

JetBrains Rider Environments
JetBrains Rider Environments

A quick bit of Googling seems to imply that Unity is using Mono for their C#, so I will try and get that installed first. After a bit of searching, I installed the mono-complete package on Fedora.Then it was time to choose Plugins.

JetBrains Rider Plugins
JetBrains Rider Plugins

I didn’t do any featured plugins. Afterwards I chose the free 30 day evaluation (maybe there isn’t a community version like Pycharm?) and decided to open my Block Destroyer project.

JetBrains Rider with Block Destroyer code loaded
JetBrains Rider with Block Destroyer code loaded

It didn’t automatically install the Rider Unity plugin, but I blame that partially on flatpak and partially on Fedora. (Everyone assumes Linux Unity dev is on Ubuntu or Centos 7) In the end I couldn’t quite figure out how to connect the two, but I think it’ll be easy enough to just load the files after I create my project. I did test that editing it in Rider will eventually recompile it in Unity once it realizes that the file has changed on disk. So I’m going to give this a shot for that new GameDev.tv class. I’ll report back on whether it’s worth it or if you should just stick to Windows (or Ubuntu) if you’re doing Unity game development.

PyGame 2.0 is out!

I just found out today that PyGame 2.0 was released yesterday, on the 20th anniversary of the software. One of the first steps I took with Python was a series of games I made from tutorials in Linux Format Magazine. On Github I’ve got my shifter game and my Space Invaders clone. The shifter game is a little wonky since I haven’t touched the code in over a decade. But I was able to make the one shift needed to make the Space Invaders clone work on Python 3. So you can enjoy those and celebrate that PyGame development has picked up steam again.

Fedora 33 is out!

It came out this Tuesday and last night I updated my laptop. The only thing I had to do for the upgrade was remove a python3-test package. Since I’m using virtual environments, for the most part I don’t care which Python packages the system has. So that was a nice, easy upgrade! Good job Fedora packagers and testers! Speaking of Python, it’ll be nice to start upgrading my projects to Python 3.9. (Fedora 33 includes the latest programming language versions as part of its “First” values)

Probably the next upgrade will be Scarlett’s laptop since she has a school-provided Chromebook for school.

What I’ve been up to in Programming: Python

Selenium for Automated Pool Signup

Spent the last week debugging that script. Turns out the key to getting it to run in cron is to add export DISPLAY=:0 && before your command. That’s because Chrome will not launch without a display to send Chrome to.

Python Morsels

The most recent Python Morsels exercise was to figure out if a number was a perfect square. Trey began his problem statement this way: “This week I want you to write a function that might seem simple at first, but there’s a number of ways to solve it.” It definitely took some out of the box thinking for me to figure out how I was going to solve the base case. The math.sqrt() function returns a float so that it can give answers for non-perfect squares. So I kept thinking and I realized that any perfect, non-complex square root must be an integer. So I came up with the conditional to return. (And after all this Pythonic learning, I’ve learned not to evaluate for truth and then return a variable. Just return the evaluation)

import math


def is_perfect_square(number):
    return math.sqrt(number) == int(math.sqrt(number))


if __name__ == "__main__":
    print(is_perfect_square(9))

For the first bonus, Trey just wanted us to return False if the number was negative. The solution was trivial. Anyone who’s done any amount of functional programming could figure it out.

import math


def is_perfect_square(number):
    if number < 0:
        return False
    else:
        return math.sqrt(number) == int(math.sqrt(number))


if __name__ == "__main__":
    print(is_perfect_square(9))
    print(is_perfect_square(-9))

The second bonus was to handle extremely large numbers. Trey mentioned in his intro that the second two bonuses would be hard. I think I can probably reason my way to a solution for bonus 3 (complex numbers). I’m quite a bit rusty on complex numbers, but I think I can get my mind to remember. (Despite having an electrical engineering degree, I’ve rarely had to use it. Ended up becoming a programmer and then a boss in a programming shop) For this one, however, I had no idea why Python couldn’t deal with large numbers. After all isn’t Python the current darling of the scientific community now? So I clicked on the first link related to this hint. The Decimal answer didn’t work. I wasn’t ready to do gmpy2 yet. So I clicked on the second link. Looked like Decimal was probably most likely the way he wanted me to go. That, of course, created some issues with 0.0 not being equal to 0, so I had to change things. I spent about 15-20 minutes trying to figure out if there was a Decimal way to get the integer part separated from the part after the decimal. Tragically, no. I did discover math.modf which will do that for you. However, it was not operating under the same context as the Decimal, so it was rounding too small that to work. (What I was trying to do there is that any perfect square should have nothing to the right of the decimal point.) Eventually, I had the thought that almost the same as the base and bonus 1, just “phrased” slightly differently. If I was previously seeing if they were both the same number, then I could move it over to this side of the equation and ask if sqrt(a)-int(sqrt(a))=0. I finally had a working solution once I raised the context high enough to get enough decimal places for that to work.

import decimal
import math


def is_perfect_square(number):
    if number < 0:
        return False
    else:
        with decimal.localcontext() as c:
            c.prec = 100
            return decimal.Decimal(number).sqrt()-int(decimal.Decimal(number).sqrt()) == 0


if __name__ == "__main__":
    print(is_perfect_square(9))
    print(is_perfect_square(-9))

Bonus 3 (handling complex numbers) was incredibly easy, especially after bonus 2. All I had to do was a quick read of the cmath library and see that a number N that is complex has N.real and N.imag to access the numbers in each part of the complex number. Then I just do what I did above to see if they are integers by making sure nothing’s right of the decimal place. Piece of cake! Also, I already knew about kwargs from other Trey problems plus reading up on it and it FINALLY made sense after all these years of not making sense to me.

import decimal
import cmath


def is_perfect_square(number, **kwargs):
    if kwargs.get("complex"):
        return (cmath.sqrt(number).real - int(cmath.sqrt(number).real) == 0) and (cmath.sqrt(number).imag - int(cmath.sqrt(number).imag) == 0)
    else:
        if number < 0:
            return False
        else:
            with decimal.localcontext() as c:
                c.prec = 100
                return decimal.Decimal(number).sqrt()-int(decimal.Decimal(number).sqrt()) == 0


if __name__ == "__main__":
    print(is_perfect_square(9))
    print(is_perfect_square(-9))

A little dust for the next few days (or weeks)

I just moved this blog to slightly different hosting infrastructure. Because of that I had some funky issues with character encoding yesterday. I believe that’s fixed now, but there may be other things that take me time to notice and fix up. Also, I still have a couple other things to set up so that I can FINALLY have https working for this blog. As part of this infrastructure change, I discovered and fixed at least one long-running issue that was causing problems with that. So if things are offline for a bit over the next few days or weeks – that’s why.

Last Few Days in Programming: Lots of Python

Been quite busy with Python, keeping me away from other pursuits, like video games. (Although the kids have been requesting Spelunky 2 whenever it’s time to hang out with them)

Extra Life Donation Tracker (eldonationtracker)

For my Extra Life Donation Tracker I pushed out a new release, v5.2.2. A user of my program (man, I never get tired of how awesome that is!!) had wholly anonymous donors which was causing an issue I thought I’d handled. But it turns out that the folks that run the Donor Drive API are a little inconsistent in how they handle that in the donor endpoint vs the donations endpoint. So I pushed that fix out and now things should be dandy for game day (about 2 weeks away!!)

Automating some Boring Stuff

In these COVID-19 times I have a problem – the YMCA where I’m a member has instituted signups for swimming. But you have to sign up EXACTLY 48 hours before you want to swim. Since I’m swimming every other day, that means that sign up time is when I’m swimming. For a while I would just wait until after my swim to sign up. But it’s a VERY popular time. So I started taking my phone to the pool to sign up. There are many negatives to this:

  1. It takes ~5 minutes or so with my phone and LTE connection (out of a 45 minute session which is already shorter than I’d normally spend in there)
  2. It uses data
  3. I risk dropping my phone into the pool or into a pool of water around the pool
  4. It means my phone is right there in my gym bag where someone could steal it (although that would give me a great excuse to buy a new one…)

So, while I was swimming today (best source of thoughts other than the shower), I realized I could probably use Selenium to automate this. I’ve never used it before, but I’d heard a lot about it. I knew that Al Sweigart talked about it in his book, Automate the Boring Stuff with Python. I bought a copy of the first edition, but I wanted to make sure I was up to date on the latest stuff so I went to that link I just shared where he has it available to read for free. He’s using the model Corey Doctorow used to use where it’s there for free, but you can also buy it and help him and the publisher. Also, he has a class on Udemy that covers the same topics. Anyway, I spent all morning (literally) digging around in my browser’s inspector mode to get all the data I needed to use it to automate the sign up. I believe I’ve got it working (I’d already signed up for my next swim session, so I had to pretend to sign up for another time, but you can only sign up for one time per day). I set up a cron job and what I’m going to do is let it sign me up and I’ll double-check (safety valve in case it doesn’t work). I’m not ready to share this code at this point – mostly because I’d prefer if it could keep working. However, it was a great experience in debugging and in how web scraping is just as annoying now as when I first learned about it somewhere around 15 years ago with O’Reilly books with titles like “Google Hacking” and “Flickr Hacking”.

raspigaragealert

As I mentioned in Switching up the hardware for the Garage IOT, I recently moved my Raspberry Pi-powered garage alert software from a Raspberry Pi 1B to a Raspberry Pi Zero W. The Raspberry Pi 1B is now in the office providing temperature and humidity data – quantifying just HOW HOT it is in here. This led me to have a renewed interest in the program. So I went ahead and created another config file in order to make it more generally usable to folks who aren’t me. Then I also created documentation. The documentation still needs a bit more work, but it could help others. Also, since it’s Hacktoberfest, someone made a PR for my code!! If this isn’t the first PR someone’s made against my code in a project in which they were co-authors, it’s at least one of the first. So that’s exciting!

Python Morsels

Finally, for this time period there was the most recent Python Morsels exercise. I fell a little behind with some other projects (and Spelunky 2) so my most recent assignment was to “create a ProxyDict class which offers an immutable dictionary-like interface that wraps around a given dictionary.” The first bonus was to add support for len, items, values, and get. The second bonus has to implement iteration and a nice repr string. The final bonus had to support equality.

At first I was a bit lost. I tried a naive solution where I just passed the keys of the dictionary I received in the __init__ method, but I got stuck on figuring out __getitem__. So then I thought I needed to use abstract base classes. I’d seen them in some book I read in the past few months. But I couldn’t remember what they were called. So I clicked on Trey’s first hint, which showed that I was right and reminded me of the term “abstract base class”. This was not a “gimme” for there was not a Dictionary in collections.abc. So after looking at the table in https://docs.python.org/3.8/library/collections.abc.html#module-collections.abc for a while, I thought Collection would give me a lot of what I wanted. But it was still missing a bit, so I looked at Mapping, which was probably the best thing to use because it was immutable and inherited from Collection. Unlike other problems in Python Morsels, this is a very, very esoteric part of Python, but it was interesting to learn how to implement an ABC; particularly the fact that it will let you know which dunder methods you’re missing when you try to create a class. Turns out that by doing this, I got bonuses 1 and 3 (and part of 2) for free! This was my code:

from collections.abc import Mapping


class ProxyDict(Mapping):

    def __init__(self, dictionary):
        self.proxy_dictionary = dictionary
        super().__init__()

    def __getitem__(self, item):
        return self.proxy_dictionary[item]

    def __iter__(self):
        return (key for key in self.proxy_dictionary.keys())

    def __len__(self):
        return len(self.proxy_dictionary)

As for the __repr__ method – I’m a pro at those at this point. I kept thinking there must be some way to cheat and use the one from the dictionary I was proxying, but I didn’t know how. So this was my code:

from collections.abc import Mapping


class ProxyDict(Mapping):

    def __init__(self, dictionary):
        self.proxy_dictionary = dictionary
        super().__init__()

    def __getitem__(self, item):
        return self.proxy_dictionary[item]

    def __iter__(self):
        return (key for key in self.proxy_dictionary.keys())

    def __len__(self):
        return len(self.proxy_dictionary)

    def __repr__(self):
        center_list = []
        for key, value in self.proxy_dictionary.items():
            if isinstance(key, int):
                center_list.append(f"{key}: '{value}'")
            else:
                center_list.append(f"'{key}': '{value}'")
        center = (', '.join(center_list))
        return "ProxyDict({"+center+"})"

I don’t think I have the prettiest syntax for my repr method. I was trying to be elegant and use a list comprehension. That looked like:

center_list = [f"{key}: '{value}'" for key, value in self.proxy_dictionary.items()]
center = (', '.join(center_list))

But without using a lamba or something, I couldn’t figure out how to implement the if/else logic in the list comprehension.

What I learned from Trey’s Solution

First of all, when I said this was an esoteric thing, I wasn’t kidding. There’s actually already a way to do this without any work:

from types import MappingProxyType as ProxyDict

Thanks mostly to Trey’s problem sets I knew that I wanted to use yield or that I probably wanted to do a generator. So I thought my solution was pretty good. But it turns out there are two simpler ways I could have done it. Since I’m proxying a dict, which already has an iter method, I could have done:

def __iter__(self):
   yield from self.proxy_dictionary

or I could have done:

    def __iter__(self):
        return iter(self.proxy_dictionary)

I actually think the first one is more readable. For the repr I kept thinking there must be some easier way to do this. Because the dictionary already has a repr. But I thought that would result in something like ProxyDict(dict(stuff)); apparently not. Because this is Trey’s solution:

def __repr__(self):
        return f"ProxyDict({repr(self.proxy_dictionary)})"

Although, that locks in the class name and causes issues if someone wants to do the same thing with our class. So the better way is:

def __repr__(self):
        return f"{type(self).__name__}({self.proxy_dictionary!r})"

the !r is the same as repr(self.proxy_dictionary).

Well, time to go check out that Pull Request on raspigaragealert!

First 24 Hours with Podcast Republic

It took me a while to get the hang of the many, many options within Podcast Republic so that I could get it to work the way I wanted – automatically downloading episodes on WiFi. Unlike Doggcatcher, but more like other modern pod catchers, you can stream a podcast instead of always having to download it. The option is nice, but it does introduce complexity. As usual, it was annoying at first to have the pod catcher think it needed to download every single episode from the podcasts you subscribed to.

Podcast Republic - recent episodes
Yup, 8783 episodes

Thankfully, unlike Doggcatcher it didn’t try to download them all. So I was able to figure out how to tell Podcast Republic I’d already listened to most of those episodes before it filled up my phone. I also ended up with newer thumbnails for some of the podcasts, like Star Talk:

Unlike Doggcatcher, it does grab the episode thumbnails. I’d heard some podcasts refer to those, but never saw them on Doggcatcher. Here’s what it looks like when a podcast has it:

Podcast Republic - episode thumbnails for Planet Money
Episode thumbnails for Planet Money

What I’m excited about is the ability to make multiple playlists. For example:

Podcast Republic - playlists
Podcast Republic – playlists

Here I’ve created a news playlist and applied it to news podcasts. I like to listen to news when it first comes out so I could jump to that playlist first each morning. Then I could go back to the normal playlist. I’m also happy with how easy it is to organize playlists vs in Doggcatcher. This makes it easier for me to set things up ahead of time and then not have to keep opening my phone to select the next episode I want to listen to. For example, I tend to want to listen to really long podcasts on exercise days because I’m able to hear it all in one go. (In fact, I created a running playlist, too) So I can shuffle those to another part of the default unplayed playlist if I want to get to them later. Here are some of the tags I’ve created. I haven’t done anything with them yet, but I could see it being useful if I’m trying to find a particular podcast:

Podcast Republic tags
Podcast Republic tags

So for now I’m quite glad Dan recommended Podcast Republic as a Doggcatcher replacement. I’ll leave you with two screenshots of the UIs I’m going to be interacting with the most:

Podcast Republic - Playing a Podcast
Podcast Republic – Playing a Podcast
Podcast Republic - what is up next
Podcast Republic – what is up next

Evaluating moving from Doggcatcher to Podcast Republic

I’ve been using Doggcatcher for YEARS – ever since I first got a smartphone something like 8 or so years ago. I started using Doggcatcher on Dan’s recommendation. One of the best features it’s had is the ability to speed up podcasts without chipmunking the voice. (I think that came a year or so after I started using it). Recently I’ve been a bit annoyed at Doggcatcher, particularly with podcasts from the EarWolf network (although there may be other networks with the same behavior). Every time Doggcatcher checks for updates, all the episodes from EarWolf will disappear and redownload. Until it is done, I can’t listen to the episode.
Neil deGrasse Tyson’s podcast is also annoying in that if a new episode comes out before I’ve finished the previous one, it’ll overwrite it so that I now have two copies of the same file. This makes it more stressful than it needs to be when I’m trying to choose the next podcast to listen to. So I started asking folks for recommendations. Dan recommended Podcast Republic to me. I don’t know if it’ll fix things for me because Dan was using it because Doggcatcher wasn’t working well for him for authenticated feeds, but I’m hopeful.

It does have some features that I didn’t know I wanted: being able to sync across devices – would have helped when I changed phones as well as being able to listen on web and sync (not something I’d use a LOT, but might use a bit). So I’m going to try it out and let you know what I think.

Brave on Windows Part 1

This post continues a series on exploring new browsers:

I’ve been using Vivaldi on Windows for about four months now. As I keep saying, my browser needs on Windows aren’t too huge. Mostly I access youtube, the Stardew Valley Farm uploaded, and Google Docs. But I want to keep checking out new browsers on Windows first precisely since they are so important on my Linux computer. I don’t want to mess up a good thing there.

So let’s start off with Brave’s new user tour:

First Page of the Welcome Tour

Interestingly it doesn’t see Vivaldi as a Browser to import from:

Select a browser to import from, but not Vivaldi

Now onto the important part of what makes Brave, Brave:

Brave’s Tracking and Add Blocking

Intelligently they tell you how to turn it off if it’s breaking sites, rather than let users think the browser’s rendering is broken. We’ll see how well it works for the sites I visit – probably just fine on Windows.

Choose your default search engine

Now, this I REALLY like. I guess since everyone else either owns search (Google and Microsoft) or gets paid by the search engine (Mozilla getting Google payments) I never see this. But I think this is the type of transparency that browsers should be providing! Not surprising since one of the Brave founders came from Mozilla.

Brave Rewards?

Rewards is a weird name for this, since I’m not getting paid or any items. But I do like the idea – you earn tokens that equate to money that gets paid out to the websites you want to support. Here’s a little more about it:

About Brave Rewards

I’m not going to sign up now because I don’t really visit enough sites on this computer and I just want to get on with it. Here’s the page I get after that:

Brave default start page

Now, it may look suspicious to you that it claims to have already blocked some trackers when I’ve only gone through their welcome page. I, too, was suspicious at first. But then I remembered when I imported settings from Chrome, it took me to some Adobe page. So new tabs always look like this. I opened a new tab without doing anything else:

New tabs just look like this

Looks like they make money from Cryptocurrencies? However, true to what you’d expect, unlike Vivaldi it doesn’t pre-populate your new tab with a bunch of sponsored sites. In fact, my speed dial still looks exactly the same on Vivaldi. Here’s how the blog looks on Brave:

This blog on Brave

I like the fonts it chooses to render with. It claims to have stopped trackers on my site. I don’t know of any, so I’m going to guess that the “Share This” has some of that embedded as do embedded YouTube videos. Let’s take a quick look at two sites I use that have ads. First Ars Technica:

It claims to have stopped 13 ads

But I guess these things aren’t ads:

And a quick look at reddit:

Reddit on Brave

There’s definitely an ad missing in that square. Supposedly also 13 ads and trackers. Again.

Brave doesn’t seem to have nearly as many widgets as Vivaldi, but that’s not surprising; Vivaldi, like Opera before it is known for being a power user’s browser. I don’t know if this ends up being pro or con for Brave in the long run. It’s a nice clear browser that more or less seems to look and feel like a regular browser – just with supposedly less tracking and ads. To get the same experience as Vivaldi would probably involve lots of potentially dangerous extensions. We’ll see how it handles my day-to-day on Windows.

number of trackers and ads blocked

Switching up the hardware for the Garage IOT

Back in May, I set up my Raspberry Pi B as my garage door monitor. Unfortunately it stopped working, I haven’t investigated yet, but I wouldn’t be surprised if it got hit with the infamous SD card corruption that was a big problem with the early Raspberry Pi boards. (I think I read it’s much less of a problem with the Raspberry Pi 4) So I decided to go ahead and switch it with a Raspberry Pi Zero W, especially since you can get it with headers from Adafruit for only $14. As a bonus, it’s got a better processor (same as the Raspberry Pi 3, I think) and built-in WiFi. It’s also got a smaller footprint, but that doesn’t matter to me for where it’s mounted. So now I’m back to having a Raspberry Pi B without a job to do (assuming the hardware is fine and it just ended up in an unbootable state. I’ve also now got a usb WiFi module for it, so maybe that’ll help me think of something for it to do. I think the Raspberry Pi rover project I got in a Humble Bundle uses a 1st gen Raspberry Pi, but I’d been thinking of using a 4th gen Pi in order to maybe do some more fun stuff with it like maybe some openCV based Computer Vision and/or machine learning.