MangaDexSharp - C# nuget package for api.mangadex.org

Group Leader
Joined
Feb 1, 2023
Messages
32
I wrote a partial implementation of the mangadex API for my reverse image search application, however, it was heavily integrated in the rest of the application, so I decided to split it out and re-work it, kinda.

So here it is:
A C# .net standard 2.1 library for accessing the MangaDex api.
Github: https://github.com/calico-crusade/mangadex-sharp
Nuget: https://www.nuget.org/packages/MangaDexSharp

Currently, it only supports the /chapter, /manga, /author, /cover, and /at-home endpoints, but I'll be adding more when I have time.
Supports most endpoints, excluding: report, scanlation group, settings, upload, user. I'm working on adding more.

Here is a basic usage example:

C#:
var api = MangaDex.Create();

//Fetching a manga by manga ID:
var manga = await api.Manga.Get("fc0a7b86-992e-4126-b30f-ca04811979bf");

//Searching for a manga via it's title:
var results = await api.Manga.List(new MangaFilter
{
    Title = "The Unrivaled Mememori-kun"
});

//Get all of the chapters from a manga by manga ID:
var chapters = await api.Manga.Feed("fc0a7b86-992e-4126-b30f-ca04811979bf");

//Fetch a chapter by chapter ID:
var chapter = await api.Chapter.Get("2c98fbe9-a63f-47c2-9862-ecc9199610a2");

//Get all of the pages of a specific chapter by chapter ID:
var pages = await api.Pages.Pages("2c98fbe9-a63f-47c2-9862-ecc9199610a2");

By default, it supports Dependency Injection for use with asp.net core, there is more information on this in the readme in the nuget package and the github page.

Let me know if you have any questions or suggestions.

Edit: Added more endpoints
Edit 2: Adding even more endpoints
Edit 3: Adding EVEN more endpoints
Edit 4: Adding ALL the endpoints. They are all belong to me.
Edit 5: Fixing documentation of Pages endpoint.
 
Last edited:
Group Leader
Joined
Feb 1, 2023
Messages
32
Group Leader
Joined
Feb 1, 2023
Messages
32
Shouldn't this be
C#:
var pages = await api.Pages.Pages("<id>");

I tried Get but that just gave me an error.
Same problem on the GH ReadMe
You're correct. It was originally api.Pages.Get but I changed it as I introduced more of the endpoints. All of the api endpoint groups that only have 1 or 2 routes were grouped into the same "Misc" service within the API as it didn't make sense to have all of them have their own service.
mdsharp-api.png


Do you think it would be better for it to be
C#:
api.Pages.Get("<id>"); //This way is more phonetically correct, but would introduce a breaking change in the API
or
C#:
api.Pages.Pages("<id>"); //This is the current way of doing things and makes sense in the context of the "misc" service.

I can do either depending on preference. I don't really mind either way; regardless, I'll update the docs and the original post with the current method.
 
Group Leader
Joined
Jan 7, 2023
Messages
32
You're correct. It was originally api.Pages.Get but I changed it as I introduced more of the endpoints. All of the api endpoint groups that only have 1 or 2 routes were grouped into the same "Misc" service within the API as it didn't make sense to have all of them have their own service.
mdsharp-api.png


Do you think it would be better for it to be
C#:
api.Pages.Get("<id>"); //This way is more phonetically correct, but would introduce a breaking change in the API
or
C#:
api.Pages.Pages("<id>"); //This is the current way of doing things and makes sense in the context of the "misc" service.

I can do either depending on preference. I don't really mind either way; regardless, I'll update the docs and the original post with the current method.
If it's correctly documented how it is right now should be fine
 
Group Leader
Joined
Feb 1, 2023
Messages
32
I released a decently massive update to the MD#, most of it is just documentation comments to make development easier, but there are a few useful tools in there as well.
image.png


Specifically, the new pagination utility will take most of the guess work out of getting all results from MD's paginated endpoints. These can be setup manually for any endpoint, but I've added some default ones for things like the /manga and /manga/{id}/feed endpoints.

C#:
var api = MangaDex.Create();

//This will fetch every chapter available at this endpoint
var allChapters = api.Chapters.ListAll(new() {
    Manga = "fc0a7b86-992e-4126-b30f-ca04811979bf"
});

await foreach(var chap in allChapters)
{
    Console.WriteLine("Chapter Title: {0}", chap.Attributes.Title);
}

Before this would have required manually iterating through each of the available pages of chapters, but I've handled that logic for you and it takes rate-limits into consideration. You can change how the library handles rate-limits with the optional parameters on the methods.

The biggest change in this update (and the thing that took the most time) was adding all of the documentation comments to ensure the Nuget package has intellisense comments in it. So now when hovering over API methods in your compatible IDE, you will get a description of what the method does and what the parameters mean. This can be useful for some of the less well documented or named methods / parameters. This took about 5 hours to do and resulted in 901 warnings in VS when I first implemented it.

image.png


Here is what the intellisense comments look like:
image.png


Another useful snippet I added was the Updates poll example. I use this code in my CardboardBox bot to send a discord message whenever manga that I'm interested in reading get new chapters. You can find the snippet and an example in the libraries github repo.

Some of the other updates include bug fixes:
  • DateTimes are now formatted properly in filters
  • The ChaptersFilter now defaults to 100 items per request (500 was too many)
  • Included default resolver for Data-Saver URLs
I'm also working on a typescript version of this API, but my motivation to work on it is relatively low as there are other solutions out there. I plan for it to follow a similar design to this one as I like the layout. If you think this might be useful or want to use it, let me know. You can find my progress so far on github.

If you're curious about more details, feel free to reply here, DM me on discord or checkout the commit details.
 
Last edited:
  • Like
Reactions: rdn
Group Leader
Joined
Feb 1, 2023
Messages
32
Not sure if anyone is using this library, but if you updated to the latest version of the nuget package (1.0.15), there is semi-breaking change:
If you use the dependency injection extension method .AddMangaDex(), you will need to also chain .AddJson() and .AddCardboardHttp() as these packages are no-longer auto registered.

Your code would need to now look something like this:

C#:
using CardboardBox.Http;
using CardboardBox.Json;
using MangaDexSharp;
using Microsoft.Extensions.DependencyInjection;

var services = new ServiceCollection();
...
services
    .AddCardboardHttp()
    .AddJson()
    .AddMangaDex()
The reason for this is two fold; It should have never auto-injected the Json and Http packages by default without the users explicit permission, and when using conflicting versions of those packages (the http package is for doing API requests and the Json package is for abstracting out JSON serialization logic) the MangaDexSharp library just does not work.
People who use the MangaDex.Create() method of creating an API instance need make no change, as it is auto registered there as the service collection is made in an isolated scope.
 
Group Leader
Joined
Feb 1, 2023
Messages
32
If you're receiving sporadic 400 Bad Request responses from the API, try updating to the latest version! I believe I've fixed the issue.

I've also added the ability to override the User-Agent sent on all API requests via the library, you can specify it anywhere that you specify tokens or API urls:

C#:
//With MangaDex.Create():
var api = MangaDex.Create(userAgent: "Some-Fancy-User-Agent");

//With DI services:
services
    .AddMangaDex("Some Token", userAgent: "Some-Fancy-User-Agent");

//With an ICredentialsService
public class SomeCredsService : ICredentialsService
{
    ...
    public string UserAgent => "Some-Fancy-User-Agent";
    ...

}

//From configuration file
{
    "Mangadex": {
        "Token": "Some-Token",
        "UserAgent": "Some-Fancy-User-Agent"
    }
}

I've also got a semi-working version of the new custom-list-v2, however, I won't release that as the nuget package until V2 goes live on api.mangadex.org. I may do a beta release if there is interest.

Let me know if you have any questions, comments or concerns.

Some more things:
If you want to override how the library makes any API request, you can now override the IMdApiService in the dependency injection services and it will effect every request the library makes.
 

rdn

Forum Admin
Staff
Developer
Joined
Jan 18, 2018
Messages
275
Could you add a disclaimer that User-Agent spoofing is discouraged? As in, pretending to be a browser and thus hiding the fact that its automated traffic will get people banned, at least a comment that warns about that would certainly help people out
 
Group Leader
Joined
Feb 1, 2023
Messages
32
Could you add a disclaimer that User-Agent spoofing is discouraged? As in, pretending to be a browser and thus hiding the fact that its automated traffic will get people banned, at least a comment that warns about that would certainly help people out

I have added a notice at the top of the main readme on the GitHub
IMG_0919.png

Does that work?
 
  • Love
Reactions: rdn
Yuri Enjoyer
Developer
Joined
Feb 16, 2020
Messages
468
That's fine yes; didn't need to be this big either tbh.

We will overtime block more and more spoofed UAs automatically anyway (to combat malicious bots and scrapers), so it is really more to avoid people's stuff breaking. And there is indeed no reason to spoof your UA to us (if it's not malicious), as we do not filter for it in the first place.

Tbh 2 lines saying "hey, eventually they'll block you if you spoof it so don't cry then" (in a more polite way) is good enough
 
Group Leader
Joined
Feb 1, 2023
Messages
32
Another slightly breaking change in v1.0.18 that was released today.

It was brought to my attention that when the API returned an error code (non 2xx status code), the library would throw an exception and subsequently hide the returned error body. This has been fixed and now the library will return the results back.
There is also a new property on the root objects returned by the library. ErrorOccurred which is just short hand for Result == "error".

Since this changes the default behavior of the application, there is a way to update while opting out of this behaviour. You can set the throwOnError field on any of the configuration methods to true and it will keep throwing exceptions instead.

C#:
var api = MangaDex.Create();
var chapter = await api.Chapter.Get("not-a-chapter-id");
Console.WriteLine("Error Occurred: " + chapter.ErrorOccurred); //"Error Occurred: true"
Console.WriteLine("Error Count: " + chapter.Errors.Length); //1
...
var api = MangaDex.Create(throwOnError: true);
var chapter = await api.Chapter.Get("not-a-chapter-id"); //This throws an exception

You can read more about the solution in the issue or in the commit. This also required a change to the underlying CardboardBox.Http library that handles the XHR requests in C#.
 
Group Leader
Joined
Feb 1, 2023
Messages
32
A super late update, but an update none-the-less!
Personal API Clients token resolution methods are now supported by MD# v1.1.0!

You can fetch access tokens for your personal API clients with 2 different methods; either via Configuration or Manual specification.

Configuration:
You will need to edit your configuration file to include some new options:
JSON:
{
    "MangaDex": {
        "ClientId": "<your-personal-client-id>",
        "ClientSecret": "<your-personal-client-secret>",
        "Username": "<your-mangadex-username>",
        "Password": "<your-mangadex-password>"
    }
}
You can also use environment variables since it supports general IConfiguration options

Once you've specified the configuration options you can request the auth tokens (and refresh them) like so:
C#:
var services = new ServiceCollection()
    //Add your configuration file however you normally do:
    .AddConfiguration(SomeIConfigurationObject)
    .AddMangaDex()
    .BuildServiceProvider();

var client = services.GetRequiredService<IMangaDex>();
var token = await client.Auth.Personal();
var me = await client.User.Me(token.AccessToken);

//Then you can refresh it like so:
var newToken = await client.Auth.Refresh(token.RefreshToken);

Manually:
You can also specify all of your configuration manually in the request methods:

C#:
string clientId = "<your-personal-client-id>",
       clientSecret = "<your-personal-client-secret>",
       username = "<your-mangadex-username>",
       password = "<your-mangadex-password>";

var client = MangaDex.Create();
var token = await client.Auth.Personal(clientId, clientSecret, username, password);
var me = await client.User.Me(token.AccessToken);

//Then you can refresh it like so:
var newToken = await client.Auth.Refresh(token.RefreshToken, clientId, clientSecret);

You can see all of the changes in this commit.
Since MangaDex doesn't support public clients yet, I will implement them once they become available.
If you have any issues, comments or concerns, feel free to reply here or open an issue on the repo!
 
Group Leader
Joined
Feb 1, 2023
Messages
32
Another update for MangaDexSharp (v1.3.1)
It adds:
  • The "Unavailable Chapters" flags to manga and chapter filters (for the DMCA stuff)
  • The /statistics endpoints
  • Better handling for credentials / authentication
  • New NuGet package for utilities (more on this later)
  • Better handling for configuration of the various settings for the API
  • Bug fixes to uploads and dictionary handling
  • Services for handling rate-limits automatically based on the rate-limit headers
  • Better handling for API errors (like 404s and 429s)
  • Event watching built into the library (like global error handling, caching ect)
  • A debugging service for printing out the raw text results on failures for when the API returns non-JSON results
  • Fetching and creating API client details using the library
  • And a lot more!
For the primary purpose behind this post; I've added a handy utility that can be used for uploading / editing chapters and images to the a new NuGet package MangaDexSharp.Utilities. I plan on adding more utilities and stuff to it as I come across things I can automate.

You can use it like so:
C#:
using MangaDexSharp;
using MangaDexSharp.Utilities.Upload;

//Create a service collection and add your services
var provider = new ServiceCollection()
    .AddMangaDex(c => c
        .WithAuthConfig(a => a
            .WithClientId("<client-id>")
            .WithClientSecret("<client-secret>")
            .WithUsername("<username>")
            .WithPassword("<password>"))
        .AddMangaDexUtils())
    ... //Add custom services here
    .BuildServiceProvider();
//Get an instance of the IUploadUtilityService
var upload = provider.GetRequiredService<IUploadUtilityService>();
//Get the manga ID and groups you want to upload to
string mangaId = "f9c33607-9180-4ba6-b85c-e4b5faee7192"; //Official "Test" Manga
string[] groups = ["e11e461b-8c3a-4b5c-8b07-8892c2dcf449"]; //Cardboard test

//Create a session for the manga
await using var session = await upload.New(mangaId, groups);

//Upload some files to the session (by file path)
await session.UploadFile("page-1.jpg");
await session.UploadFile("page-2.jpg");
await session.UploadFile("page-3.png");

//Maybe upload the files by stream instead?
using var io = File.OpenRead("some-weird-gif.gif");
await session.UploadFile(io, "page-4.gif");

//Commit the chapter to MD
var chapter = await session.Commit(new()
{
    Chapter = "69.5",
    Title = "My Super Chapter",
    TranslatedLanguage = "en",
    Volume = "420"
});
//Print out the chapter ID
Console.WriteLine("Chapter ID: {0}", chapter.Id);
You can also edit existing chapters:
C#:
string chapterId = "<chapter-id>";
int chapterVersion = 2;
await using var session = await upload.Edit(chapterId, chapterVersion);
//Continue using it like in the above example
And continue previous upload sessions:
C#:
await using var session = await upload.Continue();
//Continue using it like in the above example
There is also a handy method that can be used to automatically abandon existing sessions
C#:
await upload.CloseExisting();
The upload session will automatically handle batching your uploaded images (as the max in 1 request is 10 images). It will also handle rate-limits, authentication, and a bunch of other stuff for you. You can configure how all of that works using the configuration action that appears at the end of all of the methods on the IUploadUtilityService service.
The service will also handle abandoning the session if an error occurs so you can start again cleanly.
You can check out all of the code for the upload utility in this directory.

If you have any questions, feel free to ask here or open an issue on the GitHub Repo
 

Users who are viewing this thread

Top