A booksidian like plugin for Myanimelist (MAL) (but not Media DB plugin)

Booksidian is a wonderfull app that create note for each of the books of your RSS feeds in goodreads. You just have to “sync” it sometimes

Goodreads is not really good for anime and manga. The best site I’ve found is Myanimelist to keep track of my manga reading. The fact is, there is also a RSS feed available on this site !
So technically, it should not be that complicated to duplicate the way Booksidian works with goodreads and apply it to Myanimelist.
You can this way create a shelve “manga” and a shelves “anime”

I know that there is other alternatives :

  • You can import manually each manga using a quickadd script
  • You can impot manually each of your movie/game/book/Manga with the Media DB plugin
  • you can create your own RSS feed with RSS reader plugin and create a note for each manga of your feed by clicking on it manually. This is the closest solution I’ve found. The only things missing is to automate the creation of the note for each manga, and, if it’s already existing, update it’s content.

But doing it manually is quite annoying. And moreover, my readings are automatically update in MyanimeList when I read online which is pretty convenient.

So, if someone who has some skills can do it, I will be so glad !
Hope it can inspire some people :slight_smile:

EDIT : I solved it a bit

I had two approaches to leverage the MyAnimeList RSS feed and the RSS Reader plugin:

  1. Extracting RSS Feeds from RSS-ReaderThe goal here is to:
  • Find the RSS feed
  • Identify each item and create a note for that item
  • Check if a note already exists and if so, overwrite it with the new note
  • Import a note template and reuse metadata present in the RSS feedIn essence, this is feasible as it automates what can be done by simply pressing a button.

It is interesting because the RSS Reader plugin bypasses CORS issues (which I don’t fully understand) that prevent me from processing the RSS feed directly.

However, my script did not succeed mainly because I could not find the variable name in RSS Reader that holds the content of my feeds.

  1. Convert the RSS Feed JSON File into Separate Notes

Since I couldn’t figure out how to name the feed content for retrieval, I decided to directly access the JSON file at the location: .obsidian\plugins\rss-reader. I wanted to transform a JSON file containing all the metadata of all the works from all feeds into several Obsidian notes (one for each work), with each note organized into a folder corresponding to its feed. I planned to use Templater to create these notes and fill in the frontmatter and content based on the JSON data.

This option turned out to be successful! With the help of ChatGPT, I managed to create a Templater script that:

  • Recognizes the different RSS-Reader feeds
  • Creates a note for each item in each feed and sorts them into different folders (here, manga are sorted into “manga” and anime into “anime”, with both subfolders organized under “SOURCES/Myanimelist”)
  • Formats each note using the (very few) metadata from the feedI’m including this script below

I remain convinced that Option 1 is better; I just don’t know how to “communicate” with RSS-Reader. The script could be optimized by creating a macro with QuickAdd (a JS script to fetch the feed and a Templater script to format each item), but I don’t want to overcomplicate things here.Hopefully, this will help some or that experienced coders will be able to develop a more elegant solution.

The script :

<%*
const fs = require('fs');
const path = require('path');

// Function to create a directory if it does not exist
function ensureDirectoryExistence(dirPath) {
    if (!fs.existsSync(dirPath)) {
        fs.mkdirSync(dirPath, { recursive: true });
    }
}

// Function to sanitize a file name by removing invalid characters
function sanitizeFileName(name) {
    return name.replace(/[:\/\?<>\\\*\|"]/g, '').trim();
}

// Function to clean content and remove hyphens
function cleanContent(content) {
    return content.replace(/^[\s]*- /gm, '').trim();
}

// Function to extract the last chapter number from the content
function extractChapter(content) {
    // Search for numbers at the end of the content
    const matches = content.match(/(\d+)(?!.*\d)/);
    return matches ? matches[0] : '';
}

// Function to create a note for a given item (anime or manga)
async function createNoteForItem(item, folderName, tag) {
    const sanitizedTitle = sanitizeFileName(item.title);
    const noteFolder = path.join(app.vault.adapter.basePath, 'SOURCES', 'Myanimelist', folderName);
    const finalPath = path.join(noteFolder, `${sanitizedTitle}.md`);

    // Determine the reading status and clean the content
    const cleanedContent = cleanContent(item.content);
    const completedStatus = cleanedContent.toLowerCase().includes('completed') ? 'Read' : 'To Read';
    const chapter = extractChapter(cleanedContent);
    
    const coverImage = item.cover ? `![Cover Image](${item.cover})` : 'No cover available';
    const rating = item.rating || 'No rating';

    // Note template
    const noteContent = `---
author: ${item.creator || 'Unknown'}
chapters: ${chapter}
cover: ${coverImage}
rating: ${rating}
dateAdded: ${item.pubDate || 'Unknown'}
dateRead: ''
shelves:
  - ${completedStatus}
---

[[NL ${item.title}]]

***
# Metadata
MOC :: 
Source :: 
Project :: 
Area ::
Tags :: ${tag}
Date :: ${tp.date.now("YYYY-MM-DD")}
Note No. ${tp.date.now("YYYYMMDDHHmmss")}
`;

    // Ensure the directory exists before creating the note
    ensureDirectoryExistence(noteFolder);

    // Directly create the note with the appropriate content
    await fs.promises.writeFile(finalPath, noteContent);
}

// Path to the data.json file in the .obsidian/plugins/rss-reader folder
const dataPath = path.join(app.vault.adapter.basePath, '.obsidian', 'plugins', 'rss-reader', 'data.json');

// Read and parse the data.json file
const data = JSON.parse(fs.readFileSync(dataPath, 'utf8'));

// Iterate over RSS feeds (Anime and Manga)
for (const feed of data.items) {
    // Determine the content type (Manga or Anime) based on the "feed" value
    const folderName = feed.title.includes("Manga") ? 'Manga' : 'Anime';
    const tag = folderName === 'Manga' ? '#SOURCE/book📖/manga' : '#SOURCE/book📖/anime';

    for (const item of feed.items) {
        // Create a note for the item in the appropriate folder
        await createNoteForItem(item, folderName, tag);
    }
}
%>