Changelog
2.0.1 — 2026-11-13
Changes
- Update dependencies
2.0.0 — 2026-10
Breaking changes
Anime.synonymsandManga.synonymsare alistof strings (MAL's comma separated list, split) instead of one string.- Login moved to MAL's official API. MyAnimeList's website login now has a captcha, so
Account(username, password),change_password()andauth_connect()are gone. Log in withAccount(username).authorize(client_id)once (official API; tokens are saved and refreshed automatically), or with the experimentalAccount.login_with_cookie(...)(see the account docs for both).Account.login(client_id, access_token, ...)takes tokens you already have,Account.api(...)calls any API v2 endpoint. MyAnime/MyMangaare rewritten on the API (and about 1,400 lines smaller). The constructor isMyAnime(mal_id, account). Removed, because the API doesn't have them:my_id,my_storage_type,my_storage_value,my_download_episodes,my_fan_sub_groups,my_enable_discussion,my_downloaded_chapters,my_retail_volumes,to_xml(),increase_downloaded()andset_completed_download(). New:in_list.- Invalid values assigned to
my_*attributes raiseValueError(wasRuntimeError);my_priorityis 0–2 andmy_statusonly accepts 1, 2, 3, 4 and 6. increase()simply adds one episode/chapter (and turns Plan to Watch/Read into Watching/Reading).Account.is_authmeans "has an access token".anime.add(account)/manga.add(account)no longer reset an entry that is already in the list.- Dropped the undocumented
map_of_listsof the list objects.
New
Account.authorizealways shows MAL's login screen and refuses (saving nothing) if the browser was logged in as a different account than the one you named.- Experimental:
Account.login_with_cookie()uses theMALSESSIONIDcookie of a browser where you are logged in, when you don't have an API app. It reads private lists and can add, change and delete entries, limited to status, score and progress, through MAL's undocumented website endpoints (see the account docs for the caveats). It takes theMALSESSIONIDvalue, a dict, a wholeCookieheader, or the path of a browsercookies.txtexport, and adds theis_logged_in=1flag cookie MAL checks. Every write is read back from the list and fails loudly if it didn't happen. Account.me(),Account.suggestions()andAccount.anime_ranking().- Public lists of any account still work without logging in (bare entries).
- Account lists are filled from the API in one request per 1,000 entries, including all the data of each entry.
exceptions.SiteUnavailableErrorand automatic retries when MAL is down (see Fixes)- Related entries (
sequels,prequels,adaptations,spin_offs, ...) onAnimeandMangaare now actually filled in; before, they were always empty SearchAccountsProvider/Account.searchworks again (MAL's type isuser, results are keyed by username)
Changes
- Faster lookups: a cold
Anime(...)/Manga(...)load now takes about 1 s instead of 3.5 s. pymal no longer sleeps before the first request of a page (only before a retry), andreviewsandrecommendationsare fetched when you ask for them, not on every load. They are now properties onAnimeandManga(before they were attributes that often didn't exist). - Account lists load every page of the list (before: only the first 300 entries), and a MAL outage is no longer silently an empty list
- Minimum Python is now 3.11 (3.10 dropped)
pillowrequirement raised to>=13.0.0- Replaced legacy
singleton3withsingleton-factory - Published to PyPI as
pymal-ng
Fixes
- Reviews and recommendations work again: both parsers were written for pages MAL has since redesigned and silently returned nothing.
Anime.reviews/Manga.reviewsnow hold the 20 reviews of the first reviews page,recommendationsthe recommendations of the first page.Reviewhasaccount,date,rating,helpful,tagsanddata(watchedandwhen_writtenno longer exist on MAL);Recommendation.recommendedis the recommended title (recommended_animestill works), and recommendations of manga areMangaobjects. - Related entries (
sequels,side_stories,alternative_versions, ...) were missing every relation that MAL puts in its table below the first tiles, and relation names are matched ignoring case (Side Story/Side story). - A manga title could include its English title (
One Punch-ManOne-Punch Man). - Search results keep MAL's ranking (best match first) instead of an arbitrary set order,
id in resultsworks, and an outage during a search is reported at once instead of being retried through the old search. - MAL being down is now handled: requests time out after 15 s, retry on connection errors, timeouts and HTTP 429/5xx, then raise the new
exceptions.SiteUnavailableError(POSTs are never retried after the server may have received them) score,rankandpopularitywere parsed from the wrong place (often0or off by the footnote digit); they now come from the page header stats- Manga
synopsisreturned the whole page text; it now returns only the synopsis - Set operations on account lists were wrong:
list & xalways returned an empty set andlist - xreturned the whole list - Account profile parsing matches MAL's current pages:
user_id, the avatar (image_url/get_image()) and the friends list (account.friends) were all broken - Legacy search fallback could loop forever if MAL ignored the paging parameter
make_countercrashed on counts like1,100or?get_image()now uses the shared retrying/timeout HTTP layer- Corrected return annotations:
status/ratingarestr,start_time/end_timeare timestamps make_timenow handlesNot availableand other unparseable dates safely (returnsinf)tests/account_objects/test_account_mangas.pyfilename typo fixed, so it is collected again
1.0.0 — 2026-07
Breaking changes
- Dropped Python 2 support entirely (was already broken)
- Removed
sixdependency - Removed
html5libdependency
Fixes
- HTML parser: switched from
html5libtolxml— resolves dependency conflicts and speeds up parsing - HTTP client: replaced
requestswithniquests(drop-in replacement with modern TLS/HTTP2 support) - Sidebar parsing (
anime.py): rewrote from brittle index-based access to label-based lookup viaspan.dark_text— fixes all fields broken by MAL's layout update - Title extraction (
anime.py): fixedIndexErrorcaused by MAL wrapping the title in a<strong>tag inside<h1> - Table parsing (
anime.py): fixedAttributeErrorcaused by MAL removing<tbody>from tables - Search API (
searches/search_provider.py): migrated from legacyanime.php?q=...endpoints to MAL's modernprefix.jsonAPI with fallback protection - Season Scraping (
seasons.py,inner_objects/season.py): migrated from deadmalupdater.comthird-party server to native MyAnimeList season (/anime/season/<year>/<season>) and archive listing pages - User Account Lists (
account_animes.py,account_mangas.py): updated to MAL's modern JSON list endpoints (load.json) ReloadedSet: fixedcollections.Set→collections.abc.Set(removed in Python 3.10)HOST_NAME: updatedhttp://→https://(MAL enforces HTTPS)
Infrastructure
- Migrated build system from
setup.pytouv_build(pyproject.toml) - Replaced
nosetestswithpytest - Added offline test suite (
tests/test_fixes.py, 40 tests) - Docs migrated from Sphinx/RST to MkDocs Material
- Deleted obsolete files:
upload_new_version.bat,.travis.yml,MANIFEST.in, legacy HTML fixtures
0.5b4 — 2014
Initial public release.