Skip to main content

DLsite Async

PyPI Status Python Version License

Read the documentation at https://dlsite-async.readthedocs.io/ Tests Codecov

pre-commit Black

Features

Async DLsite API for fetching work metadata. Can be used whether works are served from dlsite.com or comipo.app.

  • Supports most DLsite sites:
    • Comipo (comic/comipo.app)
    • Doujin (All-ages/home, Adult/maniax)
    • Adult comics (books)
    • All-ages games (soft)
    • Galge (pro)
    • Apps (appx)
  • Supports common metadata for most DLsite work types
  • Japanese and English locale support (English metadata may not be available for all works)

Async DLsite Play API

  • Requires valid DLsite account login
  • Supports listing purchased works
  • Supports downloading web-optimized versions of purchased works from DLsite Play
    • Only optimized file versions can be downloaded
    • Images may be resized to smaller resolution and compressed
    • Audio files may be re-encoded and compressed into MP3 format
    • Video files may be resized to smaller resolution and re-encoded
    • Play API returns an m3u8 HLS stream playlist for videos. Downloading the HLS video segments is not supported.
  • Supports de-scrambling downloaded images (from book type works)
    • Image de-scrambling requires installation with dlsite-async[pil]
  • Supports downloading Comic Viewer ebook formats (ebook_fixed, ebook_voicecomic, ebook_webtoon, voicecomic_v2 PlayFile types)
    • For ebook_voicecomic and voicecomic_v2 works, audio for each page is downloaded alongside page images.

Requirements

  • Python 3.11+

Installation

You can install DLsite Async via pip from PyPI:

$ pip install dlsite-async

Certain features require may installing extra dependencies:

$ pip install dlsite-async[pil]

Usage examples

DLsite work lookup

Fetch manga information:

>>> import asyncio
>>> from dlsite_async import DlsiteAPI
>>> async def f():
...     async with DlsiteAPI() as api:
...         return await api.get_work("BJ370220")
...
>>> asyncio.run(f())
Work(
    product_id='BJ370220',
    site_id='comic',
    maker_id='BG01675',
    work_name='衛宮さんちの今 日のごはん (6)\u3000レシピ本付特装版',
    age_category=<AgeCategory.ALL_AGES: 1>,
    circle=None,
    brand=None,
    publisher='KADOKAWA',
    work_image='//img.dlsite.jp/.../BJ370220_img_main.jpg',
    regist_date=datetime.datetime(2021, 10, 28, 0, 0),
    work_type=<WorkType.MANGA: 'MNG'>,
    book_type=<BookType.BOOK: 'comic'>,
    ...
    author=['TAa', '只野まこと', 'TYPE−MOON'],
    ...
    genre=['少年コミック', 'ギャグ', 'コメディ', 'ほのぼの'],
    label='KADOKAWA',
    ...
    page_count=307
)

Fetch English voice/ASMR information:

>>> async def f():
...     async with DlsiteAPI(locale="en_US") as api:
...         return await api.get_work("RJ294126")
...
>>> asyncio.run(f())
Work(
    product_id='RJ294126',
    site_id='maniax',
    maker_id='RG51931',
    work_name='Pure Pussy on Duty',
    age_category=<AgeCategory.R18: 3>,
    circle='aoharu fetishism',
    brand=None,
    publisher=None,
    work_image='//img.dlsite.jp/.../RJ294126_img_main.jpg',
    regist_date=datetime.datetime(2020, 8, 30, 0, 0),
    work_type=<WorkType.VOICE_ASMR: 'SOU'>,
    ...
    illustration=['ぬこぷし'],
    voice_actor=['逢坂成美'],
    ...
    genre=['Healing', 'Dirty Talk', 'Binaural', 'ASMR', ...],
    ...
    file_format=['WAV'],
    file_size='Total 010.63GB',
    ...
)

DLsite Play work lookup

List DLsite Play files in a work:

>>> from dlsite_async import PlayAPI
>>> async def f():
...     async with PlayAPI() as play_api:
...         await play_api.login("username", "password")
...         token = await play_api.download_token("RJ294126")
...         tree = await play_api.ziptree(token)
...         return dict(tree.items())
...
>>> asyncio.run(f())
{
    '純愛おま○こ当番【アップデート版】/readme.txt': PlayFile(
        length=4006,
        type='text',
        files={
            'optimized': {
                'encoding': 'UTF-8',
                'length': 5232,
                'name': '95b8dd8e45a2ff0b1d45717b93a096b7.txt',
                'size': '5.1KB'
            }
        }
    ),
    ...,
    '純愛おま○こ当番【アップデート版】/02_wav/track00_タイトルコール.wav' PlayFile(
        length=5193978,
        type='audio',
        files={
            'optimized': {
                'bit_rate': 74182,
                'duration': 6.768,
                'length': 62758,
                'name': '069498e95c11ad47fc65ee87a1a0ae60.mp3',
                'size': '61.3KB'
            }
        }
    ),
    ...,
    '純愛おま○こ当番【アップデート版】/04_omake/01:高画質画像/junai_r_01.jpg': PlayFile(
        length=1394068,
        type='image',
        files={
            'files': {
                'crypt': False,
                'height': 3000,
                'length': 1394068,
                'name': 'd0b8bc7a93e4f1c4c3d5e3eed6d65393.jpg',
                'size': '1.3MB',
                'width': 2143
            },
            'optimized': {
                'crypt': True,
                'height': 1280,
                'length': 220746,
                'name': 'd0b8bc7a93e4f1c4c3d5e3eed6d65393.jpg',
                'size': '215.6KB',
                'width': 914
            }
        }
    ),
    ...,
    '純愛おま○こ当番【アップデート版】/05_字幕付き音声動画/trackEX_おま○こ当番と体育倉庫でナイショえっち.mp4': PlayFile(
        length=297495132,
        type='video',
        files={
            'files': {
                'frame_rate': '24/1',
                'height': 1080,
                'width': 1920
            },
            'optimized': {
                'duration': '1004.693333',
                'name': 'd5e47f10d9d98944f6d4003497087c53.m3u8',
                'streams': ['v720p', 'v480p', 'v240p']
            }
        }
    ),
    ...,
}

DLsite Play purchase lookup

List purchased works in order of purchase:

>>> import asyncio
>>> from dlsite_async import PlayAPI
>>> async def f():
...     async with PlayAPI() as play_api:
...         await play_api.login(username, password)
...         return sorted(
...             [
...                 (work, purchase_date)
...                 async for work, purchase_date in play_api.purchases()
...             ],
...             key=lambda p: p[1],
...         )
...
>>> asyncio.run(f())
[(Work(...), datetime.datetime(2014, 7, 7, 4, 47, 6, tzinfo=datetime.timezone.utc)),
 ...
 (Work(...), datetime.datetime(2024, 7, 16, 14, 55, 40, tzinfo=datetime.timezone.utc)),]

DLsite Play comic/manga/book download

DLsite Play uses several different web based viewers depending on the type of work and when it was originally added in DLsite. For a explanation of the difference in image qualities for each of these viewer types see the wiki.

The following examples show how to download images from each viewer (with the corresponding PlayFile.type values noted in parentheses). For an example of a generalized comic/manga/book downloader that handles all of these types see dlsite-utils.

Legacy image viewer (image, pdf)

Download web-optimized images from a manga/comic work to the current working directory (Note that using descramble=True requires dlsite_async[pil]):

>>> import os
>>> import asyncio
>>> from dlsite_async import PlayAPI
>>> async def f():
...     async with PlayAPI() as play_api:
...         await play_api.login(username, password)
...         token = await play_api.download_token("BJ277832")
...         tree = await play_api.ziptree(token)
...         for filename, playfile in tree.items():
...             if playfile.type != "image":
...                 continue
...             orig_path, _ = os.path.splitext(filename)
...             _, ext = os.path.splitext(playfile.optimized_name)
...             dest = f"{orig_path}{ext}"
...             await play_api.download_playfile(
...                 token, playfile, dest, mkdir=True, descramble=True
...             )
...
>>> asyncio.run(f())

For playfile.type == "pdf", the PDF entry cannot be directly downloaded. Instead, you need to first load the page array for the PDF entry and then create a PlayFile(type="image") for each page in the PDF which can then be downloaded.

(Note that only images for each page can be downloaded, it is not possible to download a .pdf file for these works)

>>> import os
>>> import asyncio
>>> from dlsite_async import PlayAPI
>>> async def f():
...     async with PlayAPI() as play_api:
...         await play_api.login(username, password)
...         token = await play_api.download_token("BJ277832")
...         tree = await play_api.ziptree(token)
...         for filename, playfile in tree.items():
...             if playfile.type != "pdf":
...                 continue
...             pages = playfile.files.get("page", [])
...             for i, page in enumerate(pages):
...                 page_playfile = PlayFile(playfile.length, "image", page, "")
...                 orig_path, _ = os.path.splitext(filename)
...                 _, ext = os.path.splitext(page_playfile.optimized_name)
...                 dest = os.path.join(orig_path, f"{i:03}{ext}")
...                 await play.download_playfile(
...                     token,
...                     page_playfile,
...                     dest,
...                     mkdir=True,
...                     descramble=True,
...                 )
...
>>> asyncio.run(f())

Ebook viewer (ebook_fixed, ebook_voicecomic, ebook_webtoon, voicecomic_v2)

Download the first page from a Comic Viewer or Webtoon ebook work to the current working directory (as a web-optimized WebP image):

>>> import os
>>> import asyncio
>>> from dlsite_async import EbookSession, PlayAPI
>>> async def f():
...     async with PlayAPI() as play_api:
...         await play_api.login(username, password)
...         token = await play_api.download_token("BJ635840")
...         tree = await play_api.ziptree(token)
...         for filename, playfile in tree.items():
...             if not playfile.is_ebook:
...                 continue
...             ebook_dir, _ = os.path.splitext(filename)
...             async with EbookSession(play_api, tree, playfile) as ebook:
...                 await ebook.download_page(0, ebook_dir, mkdir=True, force=True)
>>> asyncio.run(f())

Download all pages from a Comic Viewer or Webtoon ebook work to the current working directory (with web-optimized images converted to JPEG):

(Note that using convert=jpg|png requires dlsite_async[pil])

>>> import os
>>> import asyncio
>>> from dlsite_async import EbookSession, PlayAPI
>>> async def f():
...     async with PlayAPI() as play_api:
...         await play_api.login(username, password)
...         token = await play_api.download_token("BJ635840")
...         tree = await play_api.ziptree(token)
...         for filename, playfile in tree.items():
...             if not playfile.is_ebook:
...                 continue
...             ebook_dir, _ = os.path.splitext(filename)
...             async with EbookSession(play_api, tree, playfile) as ebook:
...                 for i in range(ebook.page_count):
...                     await ebook.download_page(
...                         i, ebook_dir, mkdir=True, force=True, convert="jpg"
...                     )
>>> asyncio.run(f())

Fixed-layout epub viewer (epub)

Download all page images from a CSR fixed-layout Epub Viewer work to the current working directory:

(Note that using descramble=True requires dlsite_async[pil])

>>> import os
>>> import asyncio
>>> from dlsite_async import EpubFixedSession, PlayAPI
>>> async def f():
...     async with PlayAPI() as play_api:
...         await play_api.login(username, password)
...         token = await play_api.download_token("BJ01755973")
...         tree = await play_api.ziptree(token)
...         for filename, playfile in tree.items():
...             if not playfile.is_epub_fixed:
...                 continue
...             epub_dir, _ = os.path.splitext(filename)
...             async with EpubFixedSession(play_api, tree, playfile) as epub:
...                 for i in range(epub.page_count):
...                     await epub.download_page(
...                         i, epub_dir, mkdir=True, force=True, descramble=True
...                     )
>>> asyncio.run(f())

Reflowable-layout epub viewer (epub_reflowable)

Download CSR-R reflowable-layout Epub Viewer work as .epub file to the current working directory:

>>> import os
>>> import asyncio
>>> from dlsite_async import EpubReflowableSession, PlayAPI
>>> async def f():
...     async with PlayAPI() as play_api:
...         await play_api.login(username, password)
...         token = await play_api.download_token("BJ02421171")
...         tree = await play_api.ziptree(token)
...         for filename, playfile in tree.items():
...             if not playfile.is_epub_reflowable:
...                 continue
...             epub_dir, _ = os.path.splitext(filename)
...             async with EpubReflowableSession(play_api, tree, playfile) as epub:
...                     await epub.download_epub(epub_dir, mkdir=True, force=True)
>>> asyncio.run(f())

Contributing

Contributions are very welcome. To learn more, see the Contributor Guide.

License

Distributed under the terms of the MIT license, DLsite Async is free and open source software.

Issues

If you encounter any problems, please file an issue along with a detailed description.

Credits

This project was generated from @cjolowicz's Hypermodern Python Cookiecutter template.

Release files for dlsite-async 0.11.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for dlsite-async 0.11.0
File Size Uploaded
dlsite_async-0.11.0.tar.gz 37.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for dlsite-async 0.11.0
File Interpreter ABI Platform
dlsite_async-0.11.0-py3-none-any.whl Python 3 none any Details

Total release size: 73.0 kB

Release files / dlsite_async-0.11.0.tar.gz

Download URL dlsite_async-0.11.0.tar.gz
Size 37.6 kB
Tags Source
SHA-256 checksum
How to use checksums
65d1b22a20e242ca655401a3beaf3d4e6c4941edcd99cdc5b99431d081ffa980
BLAKE2b-256 checksum
How to use checksums
494e22073e6898a4ce53875d77d3168b5e9e1bc62a80f7e0310a88fa5524347a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via pdm/2.28.0 CPython/3.12.3 Linux/6.17.0-1022-azure

Release files / dlsite_async-0.11.0-py3-none-any.whl

Download URL dlsite_async-0.11.0-py3-none-any.whl
Size 35.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e5b688d9b5739a0789e84fa38f18a5c574bafe49592baf350e8bca27644f1bf6
BLAKE2b-256 checksum
How to use checksums
319d0636f9642b1ec6a436bab16e1e57f373cda98334a9867eb5636acb077aa5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via pdm/2.28.0 CPython/3.12.3 Linux/6.17.0-1022-azure

Release history Release notifications | RSS feed

This release

0.11.0 This release

2 release files

0.10.3

2 release files

0.10.1

2 release files

0.9.0

2 release files

0.8.5

2 release files

0.8.4

2 release files

0.8.3

2 release files

0.8.2

2 release files

0.8.1

2 release files

0.8.0

2 release files

0.7.1

2 release files

0.7.0

2 release files

0.6.3

2 release files

0.6.2

2 release files

0.6.1

2 release files

0.6.0

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.2

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.0

2 release files

0.0.1

2 release files

0.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page