[UserScript] Natively Recap

I ported the code I used to generate my yearly recap to a user script so you can use it too if you want to try it out.

It opens a page like this:

Caveats

  • Absolutely nothing this uses is supported by Natively and may break at any time.
  • I’ve only tested in Firefox and Chromium
  • I’ve only tested on my own profile, which is using Japanese content only.

Installation & Usage

  1. Install TamperMonkey in your browser
  2. Install the user script from the greasy fork link
  3. It will add a new link to the Natively header for “Recap”. Click this link to generate the page
  4. (Optional) Click “Show/Hide Natively UI” to hide the Natively header/footer to make screenshots easier.

How to take a screenshot larger than your screen (firefox)

If you use Firefox, the browser’s inbuilt screenshot tool can take screenshots including things that are scrolled away: right click on a page and “take screenshot”. You can use the “screenshot whole page” or you can draw a rectangle while scrolling. If you do this, I suggest using the “hide Natively UI” button, or you might get the Natively header halfway down the page depending on how far your scroll.

Troubleshooting

If this doesn’t work for you, open your browser console and type JSON.stringify(window.RECAP_STATE) and post it in here, along with what’s different to what you expect. Natively is not consistent in labelling item types (e.g. my own library has items of type Light novel and light_novel), so I imagine most issues are going to be around that.

20 Likes

Actually, one second, I’ve hardcoded my own URL in by mistake, fix incoming lol

EDIT: This is now fixed, make sure you update to 1.0.1

I pushed a second update to fix a sorting bug for items finished on the same day, and to let you choose how many columns.

5 Likes

SCREAMMMM THIS IS AMAZING

Thank you @araigoshi くん!! :star_struck::two_hearts:

8 Likes

Made one last update before I go to bed:

1.0.3

  • Add support for paginated profiles (the natively API is structurally different if you have enough items to cause pagination). If your profile is paginated, you won’t have a series length field as the paginated API does not return series info.
  • Include items of unknown types (they won’t have a colour code though)
5 Likes

I guess it should be RECAP_STATE
image
Trying to do this in Chromium, but it stops almost immediately at “Loading books page 1 of 1”.

2 Likes

It turns out the problem was caused because your profile URL and username are different (as they are for everyone with kana/kanji in usernames).

I’ve published v1.0.4 which fixes the user script for this case.

5 Likes

And I pushed v1.0.5 which lets you customise various display options (colours, shortening names, show/hide info)

Note the colour preview only works for simple hex colours, but you can put any CSS colour in the textbox.

6 Likes

This is super cool, @araigoshi, I definitely want to check it out. I’m away from my computer at the moment, but hopefully I can post my books soon enough!

1 Like

Where exactly am I supposed to be seeing the link? the script is installed (and it shows up as enabled on the page on tampermonkey) but I don’t see anything. JSON.stringify(window.RECAP_STATE) returns undefined.

1 Like

image

It should be in the header, but if window.RECAP_STATE is undefined, I’m guessing that it hasn’t loaded at all for you. Can you let me know what browser you’re using?

1 Like

I’m using chrome

1 Like

Ahh, I see, I might have broken the fresh install, give me 5 minutes.

1 Like

Ok, I’ve uploaded 1.0.6 which fixes the bug introduced for first-time use on 1.0.5

Let me know if it works for you (you may need to force Tampermonkey to check for updates as it normally only does once a day). If not let me know if you see any errors in the console log.

3 Likes

it works! thank you for the immediate reply and your work on the script :slightly_smiling_face:

2 Likes

I made a quick hacky patch to show in progress volumes as well:

@@ -287,12 +287,13 @@
         return function (item) {
             const itemFinishedUnixDate = item.dateFinishedData?.timestampSeconds;
             if (!itemFinishedUnixDate) {
-                return false;
+                return startDate <= new Date(item.dateStartedData?.timestampSeconds * 1000);
             }
 
             const itemFinishedDate = new Date(itemFinishedUnixDate * 1000);
 
             return startDate <= itemFinishedDate && itemFinishedDate <= endDate;
         }
     }
 
@@ -322,7 +323,7 @@
             'name': adjustedName,
             'started': transformItemDate(item, 'dateStartedData'),
             'finished': transformItemDate(item, 'dateFinishedData'),
-            'finishedUnix': item.dateFinishedData?.timestampSeconds || 0,
+            'finishedUnix': item.dateFinishedData?.timestampSeconds || state.endDate.getTime(),
             'type': simpleType,
         }
     }
@@ -347,7 +348,7 @@
 
         const listsResult = await fetch(`/item-list-api?user=${state.user}&library_type=${libraryType}`);
         const resultBody = await listsResult.json();
-        const listFilter = resultBody.find(listFilter => listFilter.correlatedStatus === 'finished');
+        const listFilter = resultBody.find(listFilter => listFilter.correlatedStatus === 'finished' || listFilter.correlatedStatus === "in_progress");
         if (listFilter) {
             state.finishedFilter[libraryType] = listFilter.id;
             return listFilter.id;
@@ -361,6 +362,7 @@
         const dateFilter = isItemInRange(state.startDate, state.endDate);
         const items = nativelyData.results
             .filter(item => item.item != null)
+            .filter(item => item.status !== "stopped")
             .filter(dateFilter);
         return items.map(item => generateItemSummary(item, nativelyData.seriesCache));
     }
2 Likes

I think this might miss some volumes as .find() only returns the first filter and the search API only takes a single filter?

Hm possibly. You also used .find so I just followed suit without asking myself too many questions  ̄\_ (ツ)_/ ̄

Honestly, I don’t understand how the API filters work, nor where you even found documentation for the API in the first place. If I search for it, all I can find are product requests for a public API with comments implying one does not exist yet.

2 Likes

Oh I just looked in the network tools at what the Natively UI does (hence the caveat that this is not supported by Natively and can break if/when they change things)

4 Likes

Thank you for posting this!

Is there a way to include Natively levels as well? That would be a way to easily visualize progress.

Also, I’m not sure if this is actually doable but I’d like to be able to manually change the color assigned to specific books, mainly within the children’s books category, so that I can separate picture books from more novel style books.

3 Likes

Natively levels should be simple enough, I’ll probably add them this evening. Probably will have them hidden by default and you’ll have to go into the settings to turn them on, just to avoid the default view getting more cluttered.

Per item overrides are not something I’m against adding, but I have Christmas wrapping and a few other things to get to, so I probably won’t have time until the other side of the holidays. You can adjust the CSS in the dev tools to achieve the effect if you’re technically inclined

8 Likes