Calibre “read next” query

I read a lot, and a common problem I have is keeping track of the series I’m currently reading. For a while I’ve toyed with the idea of writing a Calibre plugin that would allow me to see books that belong to a series which I’ve started but haven’t caught up with (I rate books as I finish them, but parsing my library at a glance to see what comes next is difficult). Today I found the Multi-Column Search plugin and it turns out to have exactly what I need to solve this problem: a freeform SQLite field.

select id from (
  select *,
    row_number() over(
      partition by series order by series_index asc
    ) row_num
  from (
    select
      b.id, bsl.series, b.series_index, r.rating
    from books b
    inner join books_series_link bsl on b.id = bsl.book
    left join books_ratings_link brl on b.id = brl.book
    left join ratings r on brl.rating = r.id
  ) where rating is null
)
where row_num = 1 and series_index > 1

This script will filter the library to include the next unread (unrated) book in your library from each series that has at least one book that has been rated. It’s a little uglier than I would have liked, since I had to use one long query instead of CTEs, and I would have liked to be able to return them in order (maybe by overall average score), but due to limitations in the plugin this is as good as it’s going to get. Even so, I can tell it’s going to be super useful.

YMD Revisited

In Year, Month, Day, I explained an annoying problem with Javascript dates, and outlined the solution I took to avoid it in Moyase (which only needs to deal with dates in local time, and shouldn’t care if you move your device across timezones). Well, as it turns out, even this solution was not foolproof, and I totally should have seen it coming.

See, Date.toISOString() does indeed return a pretty nicely-formatted string that I can carve up and use for date components…the (obvious, in retrospect) problem is that it gives it to you in UTC time with a timezone offset, which can very well be a totally different calendar day than in local time. I probably would have never noticed this until I found myself on the other side of the world at some point in the future, or happened to be working in the app very early in the morning, so I’m very lucky to have a friend in Australia as an early adopter; she noticed that at certain times in the day, her work would be counted towards the previous day and her calendar wouldn’t display the right dates.

The fix ended up being pretty simple; instead of this:

return date.toISOString().substr(10);

I went with this instead:

return [
    String(date.getFullYear()).padStart(4, '0'),
    String(date.getMonth() + 1).padStart(2, '0'),
    String(date.getDate()).padStart(2, '0'),
].join('-');

With this, the app works correctly no matter the timezone or time of day. Hopefully this is the last time I have to think about this problem.

Sekiro’s straw doll

A youtuber I follow recently uploaded the video below. There’s an “unsolved mystery” regarding the writing on a talisman that features in the model, but a bit of digging around on Japanese internet led me to some interesting discoveries. The following text was originally posted as a comment on the video itself, but I figured I would preserve it on my own blog as well since it involved a decent amount of research.

The “writing” on the talisman has its roots in Daoist magic (符籙 in Chinese, 呪符 in Japanese; English wikipedia has an article on it called “Fulu”), where the symbols are basically formed from kanji components but don’t actually make up real characters at all. These talismans are usually written in seal script, which ranges from “I can read it if I squint” to “how the heck is that supposed to be the same character”, but even so, the writing here is really more like drawing ; while it does contain valid characters like 竜 (dragon) and 王 (king), they aren’t arranged in such a way to suggest that they’re intended to either produce composite kanji nor that they should be interpreted on their own. There are also some other characters which are likely chosen for their appearance alone, like the one that looks like 丑 missing its bottom stroke, or 弗, which can be taken to mean “dollar” but considering the time period I think it’s more likely just the right-hand component of something like 沸. Other parts have no basis in writing at all, like the mirrored squiggles that extend from the bottom of the 田 character, or the cartouche that encloses the bottom two thirds.

Here’s a post (all in Japanese unfortunately) that has some interesting insights into this talisman and also some others that are found elsewhere in the game:

One other thing I’ll point out, is that this guy is definitely a wara-ningyou (lit. straw doll), but he’s not constructed in the typical way of tying together two straight bundles of straw. He’s actually made from a shimenawa, which is a type of rope which is used to cordon off sacred areas and provide protection from evil. The talisman itself (according to that link above) is a warding-against-evil type, though the author points out that depending on how the talisman is affixed, the effect can be reversed, resulting in an invitation for possession by an evil spirit. It’s not obvious which is the case here, but it’s an interesting thing to consider.

Translating Moyase with Deepl

I recently added localization support to Moyase and wrote a Japanese locale file to test it with. Just for fun, I thought I would take the translation strings and run them through a machine translation service to see how it fared. The results are not dramatically bad, but they definitely aren’t good.

Moyase’s localization files are available on Github. The Deepl translation I made for this video is available in my recycle bin.

Year Month Day

5…4…3…2…1…Happy New Year!

It’s midnight, January 1st, 2021. The ceremonial ball has dropped in Times Square and 2020 has just come to a close. As the festivities begin to wind down, your phone buzzes. It’s a text from your friend in Los Angeles.

“My wife just delivered our baby! It’s a boy!”

What is the baby’s birthday?

Continue reading →

Setting up a Kobo without logging in

My trusty Kobo Aura One recently died on me, and I replaced it with a Kobo Libra H2O, which…is honestly kind of a downgrade. I didn’t expect to like the physical buttons, but I’ve come around to them, and the screen is crisper, which makes reading small furigana easier, but the screen is also smaller and the storage size is pitiful (8gb compared to the 32gb I used to have, which is pretty important if you’re going to have a lot of manga on board). In any case…

The main thing I’ve been unhappy about with this new device is the fact that the database handling seems to be really flaky. I had my Aura for four years and never had a problem with it, but this new one has randomly corrupted its own database multiple times over the last few days. Since the only way to fix this is to reset the device and logging in is a hassle (especially since I don’t use any of the online features anyway), I wanted to figure out how to bypass the sign-in process entirely.

There are a few guides on the mobileread forums on how to set this up, but they’re all pretty old — the user database table was only five columns back then and it’s now at 27, which breaks the insert statements. Here’s an updated sqlite script that gets you into the device with minimal fuss, plus a little extra which blocks the analytics events (which seem to be the main culprit).

insert or replace into user
  (UserID, UserKey)
values
  ('-', '-');

drop index if exists analytics_events_timestamp;

create trigger if not exists delete_analytics
after insert on AnalyticsEvents begin
  delete from AnalyticsEvents;
end;

The other steps are the same as ever:

  • Connect the ereader to your PC
  • Tap “Set up via USB” on the home screen
  • Download and run SqliteBrowser
  • Click “File/Open Database”, browse to your ereader, and select .kobo/KoboReader.sqlite
  • In the “Run SQL” tab, paste the above script and execute it by pressing F5 or clicking the play button
  • Click “File/Close Database” and save changes
  • Eject the ereader from your PC and disconnect it

You should be met with a fresh clean dashboard. One nice thing about this method is that the ereader never actually sees the internet, so it can’t download book suggestions. I recommend backing up the sqlite database after you set up your preferences (the calibre plugin Kobo Utilities can perform a backup automatically every time you connect the device, which is something I wish I had done much sooner).

Six Tanuki and Transparency

Last year I started playing Animal Crossing: New Horizons in Japanese as part of my daily practice routine. It was my first experience with the series so all the characters were new to me, though I did know ahead of time that, just like in Pokemon, Ace Attorney, and other many titles with a long history of western localization, all the characters would have completely different names between regions.

Continue reading →

The nested factory pattern

When writing code, I try to structure my project in such a way that it’s impossible to misuse. I often run into cases where I need some class to be responsible for instantiating and managing its own resources, such as a message hub (which needs to maintain a list of subscribers) or a tweening engine (which would need to update and dispose of tween control objects according to a timer). In these cases, I like to ensure that these objects can never be created outside the class which manages them.

A simple solution, of course, is to use internal constructors, but I personally prefer to avoid using the internal access modifier whenever possible, as I find it does a poor job of signalling the intent of the code, and in many cases is just a band-aid fix for a poorly planned access scheme. Furthermore, in cases where the class in question will only be consumed within the same project, internal is essentially no different than public.

My solution to this problem is as follows:

Continue reading →

Haxe still has problems

Following up from my post about using Haxe/OpenFL to save some of my old Flash games, I do feel like I have to talk about some frustrating points that came up during the progress.

I will preface this by saying that the problems Haxe has now are different than the problems it had when I was using it previously. Throughout this process, I only came across a bare few instances where switching targets led to suddenly broken code. The OpenFL runtime never totally crashed on me like it used to do. I didn’t have to fiddle with DCE flags to prevent my code from being erroneously stripped from the build. It’s obvious that Haxe has improved a lot over the past few years, and that’s great. I do plan on continuing to use it to port my other game projects, and I would recommend it to anyone else for the same purpose. I do believe in criticizing the things we love though, so let’s get into it.

Continue reading →

A week of Haxe

It’s been a long time since I used Haxe — my last commit to the Iridescence repository was over six years ago, and I haven’t been back to it ever since. When Flash finally reached its end-of-life and my old Flash games suddenly became unplayable, I realized that this would be a perfect opportunity to get back to it, see what had changed, and do a bit of digital preservation work while I was at it.

Continue reading →