Skip to main content

Python library for the Mega.nz and Transfer.it API

Project description

Async Mega.py

PyPI - Version PyPI - Python Version linting - Ruff GitHub License CI

Python library and CLI app for the Mega.nz and Transfer.it API

API Information

Supports:

  • Login (with credentials or creating a temporary account)
  • Upload
  • Download (files and folders)
  • Delete
  • Search
  • Export (public sharing)
  • Import public files to your account
  • Renaming
  • Moving files
  • Add/remove contacts
  • Get account stats (storage quota, transfer quota, balance (PRO accounts only))

TODO:

  • Support multifactor authentication (MFA) login

Please check src/mega/data_structures.py for details about the objects returned by the API

How To Use

[!TIP] You can run the commands bellow in an interactive python (the asyncio REPL). Try them in real time as is, using async/await keywords!

uv run -p3.12 --with async-mega-py python -m asyncio

from mega.client import MegaNzClient

email = "my_email@email.com"
password = "12345"
async with MegaNzClient() as mega:
    await mega.login(email, password)

Login should always be the first thing you do. Almost all operations require a valid account. You can call login without params to create a temporary account:

from mega.client import MegaNzClient

async with MegaNzClient() as mega:
    await mega.login() # login using a temporary anonymous account

# Also works without using it as a context manager,
mega = MegaNzClient()
await mega.login()
# but you have the responsability to close the session
# mega.close()  

Methods

Check src/mega/client.py and src/mega/data_structures.pyto view the details of all public methods and which objects each one return.

The client deserializes the raw responses from the API and always returns dataclasses for objects (except for get_user which returns a normal dict)

[!TIP] All dataclasses returned by the client have a dump method to convert them to a dict if required

# Get user details
# Includes email, user id, history of all emails, creation timestamp, etc...
await mega.get_user()

# Get account stats
# ex: Balance, storage quota, transfer quota, usage metrics, etc...
await mega.get_account_stats()

# Add/remove contacts
contact = "test@mega.nz"
await mega.add_contact(contact)
await mega.remove_contact(contact)

# Create a folder
await mega.create_folder('new_folder')
await mega.create_folder('new_folder/sub_folder/subsub_folder')

# Rename a file or a folder
folder = await mega.find('new_folder/sub_folder/subsub_folder')
await mega.rename(folder, new_name='my_new_name')

# Delete or destroy folder

await mega.delete(folder.id) # Send this node to the trash bin (still counts towards your quota)
await mega.destroy(folder.id) # This removes it completely from your account

Upload / Downloads

# Upload a file, and get its public link
my_real_file = '/home/user/myfile.doc' # Change this to a real file path!
uploaded_file = await mega.upload(my_real_file) # Upload returns the Node that represents the file you just uploaded
await mega.export(uploaded_file)

# Download a file from your account
output_dir = "my downloads"
await mega.download(uploaded_file, output_dir)

# Download a public file
url = "https://mega.nz/#!hYVmXKqL!r0d0-WRnFwulR_shhuEDwrY1Vo103-am1MyUy8oV6Ps"
public_handle, public_key = mega.parse_file_url(url)  
await mega.download_public_file(public_handle, public_key, output_dir)

# Download a public folder
url = "https://mega.co.nz/#F!utYjgSTQ!OM4U3V5v_W4N5edSo0wolg1D5H0fwSrLD3oLnLuS9pc"
public_handle, public_key = mega.parse_folder_url(url)
success, fails = await mega.download_public_folder(public_handle, public_key, output_dir)
print(f"Download of '{url!s}' finished. Successful downloads {len(success)}, failed {len(fails)}")

# Import a file from URL
url = "https://mega.nz/#!hYVmXKqL!r0d0-WRnFwulR_shhuEDwrY1Vo103-am1MyUy8oV6Ps"
public_handle, public_key = mega.parse_file_url(url)
await mega.import_public_file(public_handle, public_key, dest_node_id=folder.id)

# How do you know if an URL is a file or folder? call the more generic parse_url method
result = mega.parse_url(url)
print (result.is_folder)

[!TIP] You can show a progress bar on the terminal for each download/upload by calling them within the progress_bar context manager (needs optional dependency rich to be installed):

url = "https://mega.co.nz/#F!utYjgSTQ!OM4U3V5v_W4N5edSo0wolg1D5H0fwSrLD3oLnLuS9pc"
public_handle, public_key = mega.parse_folder_url(url)
with mega.progress_bar:  
    success, fails = await mega.download_public_folder(public_handle, public_key, output_dir)

The filesystem object

The filesystem is a read only copy of your account's file structure.

[!IMPORTANT]
mega.py caches your filesystem until you make a request to modify it. ex: create_dir or upload

That means calls to mega.get_filesystem() will not reflect changes made by third parties (ex: MegaNZ's website or the MegaNZ's app)

You can force it to fetch current data by using mega.get_filesystem(force=True)

# Get a read only copy of your filesystem
fs = await mega.get_filesystem()

# save a copy of your filesystem as json
import json
from pathlib import Path

dump = json.dumps(fs.dump(), indent=2, ensure_ascii=False)
Path("my_fs.json").write_text(dump)

What can the filesystem object do?

We are gonna use the example filesystem found at tests/fake_fs.json which has this structure:

"paths": {
    "qCZrYJVK": "/",
    "FeWnQouH": "/backup.sql",
    "coJ3yMOW": "/docker-compose.yml",
    "FzL3QdIj": "/Inbox",
    "2gL2GPaJ": "/index.html",
    "msinsVCj": "/logo.png",
    "MBlNlb2P": "/styles.css",
    "7N9QiWZ9": "/tests",
    "RDJJI2lv": "/tests/logo.png",
    "pHWVIQLd": "/tests/logo.png",
    "oImwb6nN": "/tests/script.js",
    "FIXitv4F": "/tests/scripts",
    "0fPFklV3": "/tests/scripts/notes.txt",
    "l9zkz1GU": "/tests/scripts/script.js",
    "E4LqT4EF": "/tests/scripts/styles.css",
    "i6ry53xV": "/tests/setup.sh",
    "Iri7NRCx": "/tests/utils.py",
    "2Tae8amE": "/Trash Bin",
    "t8HkzBH2": "/Trash Bin/data",
    "8EwHVJna": "/Trash Bin/data/docker-compose.yml"
  }

Read the filesystem from a file dump

from mega.filesystem import UserFileSystem

dump = Path("tests/fake_fs.json").read_text()
fs = UserFileSystem.from_dump(json.loads(dump))

# Search for nodes
query = "tests/script"
for node_id, path in fs.search(query):
    print (node_id)
    print (path)

# or
dict(fs.search(query))

The output will be:

{
    "oImwb6nN": "/tests/script.js",
    "FIXitv4F": "/tests/scripts",
    "0fPFklV3": "/tests/scripts/notes.txt",
    "l9zkz1GU": "/tests/scripts/script.js",
    "E4LqT4EF": "/tests/scripts/styles.css",
}
# Get the path to a node
path = fs.absolute_path("0fPFklV3")
# Should be: /tests/scripts/notes.txt

# Find a node by its *exact* path
result = fs.find("/tests/scripts/notes.txt")
assert result.id == "0fPFklV3"
# Get deleted files and folders (on the trash bin)
list(fs.deleted)

# List all the children of a folder (resursive)
folder = fs.find("/tests")
for node in fs.iterdir(folder.id, recursive=True):
    print(fs.absolute_path(node))

Output will be:

[
    "/tests/logo.png",
    "/tests/logo.png",
    "/tests/script.js",
    "/tests/scripts",
    "/tests/scripts/notes.txt",
    "/tests/scripts/script.js",
    "/tests/scripts/styles.css"
]

[!IMPORTANT]
Mega's filesystem is not POSIX-compliant: multiple nodes may have the same path.

If 2 nodes have the same path, find will throw an error.

fs.find("/tests/logo.png") # This will fail
# You will have to call search and choose which one you actually want
dict(fs.search("/tests/logo.png"))

Transfer.it

[!NOTE] The transfer.it client does not support uploads (yet!)

from mega.transfer_it import TransferItClient

async with TransferItClient() as client:
    transfer_id = client.parse_url(url)
    # This is the same filesystem object as mega's,
    # but it does not have root, inbox or trash_bin nodes
    fs = await client.get_filesystem(transfer_id)
    output_dir = "My downloads"
    success, fails = await client.download_transfer(transfer_id, output_dir)
    logger.info(f"Download of '{url!s}' finished. Successful downloads {len(success)}, failed {len(fails)}")

CLI

You can use async-mega-py as a stand alone CLI app! Just install it with the optional [cli] dependencies. The CLI offers 2 commands: mega-py and async-mega-py. Both are just aliases for the same app.

# Install it with:
uv tool install async-mega-py[cli]

#Run it
mega-py --help
 Usage: mega-py [OPTIONS] COMMAND [ARGS]...  

╭─ Options ──────────────────────────────────────────────────────────────────────╮
│ --help          Show this message and exit.                                    │
╰────────────────────────────────────────────────────────────────────────────────╯
╭─ Commands ─────────────────────────────────────────────────────────────────────╮
│ download   Download a public file or folder by its URL (transfer.it / mega.nz) │
│ dump       Dump a copy of your filesystem to disk                              │
│ stats      Show account stats                                                  │
│ upload     Upload a file to your account                                       │
╰────────────────────────────────────────────

[!TIP] The CLI app does not accept login credentials, but you can still use your account by setting up the MEGA_NZ_EMAIL and MEGA_NZ_PASSWORD enviroment variables

It will also read them from an .env file (if found)

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

async_mega_py-2.0.1.dev0.tar.gz (36.7 kB view details)

Uploaded Source

Built Distribution

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

async_mega_py-2.0.1.dev0-py3-none-any.whl (45.4 kB view details)

Uploaded Python 3

File details

Details for the file async_mega_py-2.0.1.dev0.tar.gz.

File metadata

  • Download URL: async_mega_py-2.0.1.dev0.tar.gz
  • Upload date:
  • Size: 36.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Fedora Linux","version":"43","id":"","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for async_mega_py-2.0.1.dev0.tar.gz
Algorithm Hash digest
SHA256 539bef5584aabc41d9ab84af4cefabd1f1ae7406d0b692767a8086851df44004
MD5 eb8ec024c3597da1ca5f51dce12b67fb
BLAKE2b-256 1fe4a6157c04e1e1eaa09feb773ffde43e28279956d08e79b8849044ff82af5b

See more details on using hashes here.

File details

Details for the file async_mega_py-2.0.1.dev0-py3-none-any.whl.

File metadata

  • Download URL: async_mega_py-2.0.1.dev0-py3-none-any.whl
  • Upload date:
  • Size: 45.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Fedora Linux","version":"43","id":"","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for async_mega_py-2.0.1.dev0-py3-none-any.whl
Algorithm Hash digest
SHA256 9c495e3ddc092d65930cb010acbbc07646434526d12f0712aa35c747b3971685
MD5 31db0cbbd18ca8f81103631153e131df
BLAKE2b-256 3a061d90e54aadae8d1c8f2a1cf33254c44de859f39914e310da0d35b4a899bc

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