Skip to main content

Modern sync and async Python client for MEGA public file and folder links.

Project description

mega-public-client

mega-public-client is a modern Python 3.11+ library for downloading files and folders from public MEGA links.

It provides synchronous and asynchronous APIs, local MEGA decryption, resumable chunked downloads, progress callbacks, folder traversal, custom retry handling, and clean typed models.

The library does not depend on abandoned MEGA wrappers and does not use tenacity. Runtime dependencies are intentionally small:

  • httpx
  • pycryptodome

Features

  • Public MEGA file downloads
  • Public MEGA folder downloads
  • Original file names from MEGA metadata
  • Recursive folder paths preserved on disk
  • Sync API via MegaClient
  • Async API via AsyncMegaClient
  • Parallel chunk downloads
  • Resume support through .part files
  • Streaming helpers for small files
  • Progress callbacks
  • Custom sync and async retry engine
  • Exponential backoff with retry filters
  • MEGA AES-CTR file decryption
  • MEGA AES-CBC attribute decryption
  • Modern public URL parser
  • Structured exception hierarchy
  • Dataclasses for public models
  • Type hints and py.typed
  • PyPI-ready pyproject.toml
  • Offline unit tests for parser, crypto, retry, and path behavior

Installation

From PyPI, after publishing:

pip install mega-public-client

From a local checkout:

pip install .

For development, install in editable mode:

pip install -e .

With test dependencies:

pip install -e ".[test]"

Quick Start

Download one public file using the original MEGA file name:

from mega_client import MegaClient

url = "https://mega.nz/file/FILE_ID#FILE_KEY"

with MegaClient() as client:
    result = client.download_file(url, "downloads")

print(result.path)

If the MEGA file is named movie.mp4, it will be saved as:

downloads/movie.mp4

To force a custom output name, pass a full file path:

from mega_client import MegaClient

with MegaClient() as client:
    client.download_file(
        "https://mega.nz/file/FILE_ID#FILE_KEY",
        "downloads/custom-name.mp4",
    )

Download a Folder

Use download_folder() with a public folder URL:

from mega_client import MegaClient

url = "https://mega.nz/folder/FOLDER_ID#FOLDER_KEY"

with MegaClient() as client:
    results = client.download_folder(url, "downloads")

for item in results:
    print(item.path)

The library preserves folder paths returned by MEGA. For example, a public folder containing:

Videos/
  intro.mp4
  lessons/
    part-1.mp4

will be written as:

downloads/Videos/intro.mp4
downloads/Videos/lessons/part-1.mp4

Async Usage

Use AsyncMegaClient with async with:

import asyncio

from mega_client import AsyncMegaClient


async def main() -> None:
    async with AsyncMegaClient() as client:
        result = await client.download_file(
            "https://mega.nz/file/FILE_ID#FILE_KEY",
            "downloads",
        )
        print(result.path)


asyncio.run(main())

Async folder download:

import asyncio

from mega_client import AsyncMegaClient


async def main() -> None:
    async with AsyncMegaClient() as client:
        results = await client.download_folder(
            "https://mega.nz/folder/FOLDER_ID#FOLDER_KEY",
            "downloads",
        )

    for result in results:
        print(result.path)


asyncio.run(main())

Progress Callbacks

Progress callbacks receive two arguments:

  • done: bytes downloaded and decrypted so far
  • total: total bytes when known

Synchronous example:

from mega_client import MegaClient


def progress(done: int, total: int | None) -> None:
    if total:
        percent = done / total * 100
        print(f"{percent:.1f}% ({done}/{total})")
    else:
        print(done)


with MegaClient() as client:
    client.download_file(
        "https://mega.nz/file/FILE_ID#FILE_KEY",
        "downloads",
        progress=progress,
    )

Async progress callbacks may be regular functions or async functions:

import asyncio

from mega_client import AsyncMegaClient


async def progress(done: int, total: int | None) -> None:
    print(done, total)


async def main() -> None:
    async with AsyncMegaClient() as client:
        await client.download_file(
            "https://mega.nz/file/FILE_ID#FILE_KEY",
            "downloads",
            progress=progress,
        )


asyncio.run(main())

Streaming Small Files

For small files, you can download and decrypt directly into memory:

from mega_client import MegaClient

with MegaClient() as client:
    data = client.stream_file("https://mega.nz/file/FILE_ID#FILE_KEY")

print(len(data))

Async:

import asyncio

from mega_client import AsyncMegaClient


async def main() -> None:
    async with AsyncMegaClient() as client:
        data = await client.stream_file("https://mega.nz/file/FILE_ID#FILE_KEY")
        print(len(data))


asyncio.run(main())

For large videos and archives, prefer download_file() so the library writes chunks to disk instead of keeping the whole file in memory.

Resume Support

Downloads are written to a temporary .part file first. If a download is interrupted, call download_file() again with the same destination and the library will resume from the existing .part file when possible.

Resume is enabled by default:

client.download_file(url, "downloads", resume=True)

Disable resume:

client.download_file(url, "downloads", resume=False)

Resume offsets are aligned to AES block boundaries so decryption can continue correctly.

Chunk Size and Parallelism

You can tune chunk size and request concurrency:

from mega_client import MegaClient

with MegaClient() as client:
    client.download_file(
        "https://mega.nz/file/FILE_ID#FILE_KEY",
        "downloads",
        chunk_size=2 * 1024 * 1024,
        parallelism=6,
    )

Defaults:

  • chunk_size: 1 MiB
  • parallelism: 4

Higher parallelism may improve throughput, but it can also trigger server-side rate limits or increase memory and disk pressure.

URL Parsing

The parser supports modern and legacy public MEGA links:

from mega_client import parse_mega_url

link = parse_mega_url("https://mega.nz/file/FILE_ID#FILE_KEY")

print(link.type)
print(link.handle)
print(link.key)

Supported examples:

https://mega.nz/file/<file_id>#<file_key>
https://mega.nz/folder/<folder_id>#<folder_key>
https://mega.nz/#F!<folder_id>!<folder_key>
https://mega.nz/#!<file_id>!<file_key>

Malformed or unsupported URLs raise MegaInvalidURLError.

Metadata

Read file metadata without downloading:

from mega_client import MegaClient

with MegaClient() as client:
    info = client.get_file_info("https://mega.nz/file/FILE_ID#FILE_KEY")

print(info.name)
print(info.size)
print(info.handle)

Read folder metadata:

from mega_client import MegaClient

with MegaClient() as client:
    folder = client.get_folder_info("https://mega.nz/folder/FOLDER_ID#FOLDER_KEY")

for node in folder.nodes:
    print(node.name, node.is_file, node.is_folder)

Exceptions

All package-specific exceptions inherit from MegaClientError.

from mega_client import MegaClient, MegaClientError

try:
    with MegaClient() as client:
        client.download_file(url, "downloads")
except MegaClientError as exc:
    print(f"MEGA download failed: {exc}")

Exception classes:

  • MegaClientError: base class
  • MegaInvalidURLError: invalid or unsupported public URL
  • MegaAPIError: MEGA API returned an error or malformed response
  • MegaCryptoError: key, attribute, or decryption failure
  • MegaDownloadError: download could not complete
  • MegaIntegrityError: completed file failed validation
  • MegaRetryError: retry loop exhausted unexpectedly

Retry Configuration

The library includes its own retry engine for sync and async code.

from mega_client import MegaClient
from mega_client.retry import RetryConfig

retry = RetryConfig(
    attempts=6,
    base_delay=0.5,
    factor=2.0,
    max_delay=15.0,
    jitter=0.2,
)

with MegaClient(retry=retry) as client:
    client.download_file(url, "downloads")

By default, retries are applied to transient network errors, timeouts, and server-side HTTP responses such as 429, 500, 502, 503, and 504.

Custom retry filter:

from mega_client.retry import RetryConfig


def retry_filter(exc: BaseException) -> bool:
    return isinstance(exc, TimeoutError)


retry = RetryConfig(attempts=3, retry_filter=retry_filter)

Logging

The package uses standard Python logging names:

  • mega_client.sync
  • mega_client.async
  • mega_client

Example:

import logging

logging.basicConfig(level=logging.INFO)

You can pass a custom logger:

import logging

from mega_client import MegaClient

logger = logging.getLogger("my_app.mega")

with MegaClient(logger=logger) as client:
    client.download_file(url, "downloads")

Public API

Main imports:

from mega_client import MegaClient
from mega_client import AsyncMegaClient
from mega_client import parse_mega_url

Common models:

from mega_client import FileInfo
from mega_client import FolderInfo
from mega_client import NodeInfo
from mega_client import DownloadResult

DownloadResult fields:

  • path: final local path
  • bytes_written: number of bytes written
  • resumed: whether an existing .part file was used
  • verified: whether final validation passed

FileInfo fields:

  • name: original file name from MEGA metadata
  • size: file size in bytes
  • download_url: temporary MEGA download URL
  • key: decoded MEGA file key material
  • handle: MEGA file handle
  • attributes: decrypted metadata attributes

NodeInfo fields:

  • handle: MEGA node handle
  • parent: parent node handle when present
  • kind: MEGA node type
  • name: decrypted node name
  • size: file size for file nodes
  • key: decrypted node key
  • attributes: decrypted node attributes
  • is_file: convenience property
  • is_folder: convenience property

MEGA Protocol Notes

Public-link operations use MEGA's command endpoint:

https://g.api.mega.co.nz/cs

The client sends JSON arrays because the MEGA command server supports batched commands.

Public file metadata is requested with:

[
  {
    "a": "g",
    "g": 1,
    "p": "<file_handle>"
  }
]

Public folder trees are requested with the folder handle in the request query parameter n and a folder listing command body:

[
  {
    "a": "f",
    "c": 1,
    "r": 1,
    "ca": 1
  }
]

Downloads for nodes inside public folders also require the public folder handle as API context.

MEGA files are encrypted client-side:

  • Public file keys contain eight 32-bit words.
  • The AES key is derived by XORing key words with nonce/MAC words.
  • File content is decrypted with AES-CTR.
  • File and folder attributes are decrypted with AES-CBC and a zero IV.
  • Attribute plaintext begins with the literal MEGA prefix followed by JSON.

Project Layout

mega_client/
  client.py        # synchronous facade
  async_client.py  # asynchronous API and MEGA command calls
  downloader.py    # chunked download and resume logic
  crypto.py        # MEGA key and AES helpers
  parser.py        # public URL parser
  models.py        # dataclasses and enums
  retry.py         # custom retry engine
  exceptions.py    # exception hierarchy
  progress.py      # progress callback helpers
  constants.py     # defaults and protocol constants
  utils.py         # small shared helpers
  types.py         # callback and retry typing aliases

Development

Install locally:

pip install -e ".[test]"

Run tests:

python -m pytest

Run the benchmark helper:

python benchmarks/download_throughput.py "https://mega.nz/file/FILE_ID#FILE_KEY" output.bin

Examples

Example scripts are available in:

examples/download_file.py
examples/download_file_async.py

Limitations

  • Only public MEGA file and folder links are supported.
  • Account login, private cloud operations, uploads, deletes, and sharing management are outside the scope of this package.
  • Integrity validation currently verifies completed file size and correct decryption flow. Full MEGA MAC verification can be added as an additional hardening step.
  • stream_file() loads the entire decrypted file into memory and should only be used for small files.

License

MIT. See LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mega_public_client-0.1.1.tar.gz (15.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mega_public_client-0.1.1-py3-none-any.whl (20.1 kB view details)

Uploaded Python 3

File details

Details for the file mega_public_client-0.1.1.tar.gz.

File metadata

  • Download URL: mega_public_client-0.1.1.tar.gz
  • Upload date:
  • Size: 15.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.0

File hashes

Hashes for mega_public_client-0.1.1.tar.gz
Algorithm Hash digest
SHA256 931fade021589ca1fa8885f477b14597ced0ba32e5b233f79834e5c184751c77
MD5 219e73ba1df35ea79c73d64b249accc7
BLAKE2b-256 7ffa84308ee4e1c5ae9b00c4767c5f988550398b773defcead2bf52ea375dbbe

See more details on using hashes here.

File details

Details for the file mega_public_client-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for mega_public_client-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 f5a95e7c1435a6786e9e8fe5e004241ac25aa35444b33c55e3edb5f15a794797
MD5 6996d40cba1da3dc3e235db448f5fef3
BLAKE2b-256 4bef0e2a71d2534a29b03d636ba3b1f4de14d67e7b1511c8e43680204b0f7474

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page