mega-claude-connector 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mega_claude_connector-0.1.0/PKG-INFO +18 -0
- mega_claude_connector-0.1.0/pyproject.toml +35 -0
- mega_claude_connector-0.1.0/setup.cfg +4 -0
- mega_claude_connector-0.1.0/src/mega_claude_connector.egg-info/PKG-INFO +18 -0
- mega_claude_connector-0.1.0/src/mega_claude_connector.egg-info/SOURCES.txt +12 -0
- mega_claude_connector-0.1.0/src/mega_claude_connector.egg-info/dependency_links.txt +1 -0
- mega_claude_connector-0.1.0/src/mega_claude_connector.egg-info/entry_points.txt +3 -0
- mega_claude_connector-0.1.0/src/mega_claude_connector.egg-info/requires.txt +5 -0
- mega_claude_connector-0.1.0/src/mega_claude_connector.egg-info/top_level.txt +1 -0
- mega_claude_connector-0.1.0/src/mega_connector/__init__.py +0 -0
- mega_claude_connector-0.1.0/src/mega_connector/cli.py +155 -0
- mega_claude_connector-0.1.0/src/mega_connector/config.py +59 -0
- mega_claude_connector-0.1.0/src/mega_connector/mcp_server.py +125 -0
- mega_claude_connector-0.1.0/src/mega_connector/mega_client.py +109 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mega-claude-connector
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Mega.nz MCP server and Claude-powered CLI for cloud storage management
|
|
5
|
+
Author-email: Mrigank Shekhar Chaubey <mriganksc@infinitywards.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/mriganksc/mega-claude-connector
|
|
8
|
+
Keywords: mega,claude,mcp,ai,cloud-storage
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.11
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
Requires-Dist: mega.py>=1.0.8
|
|
15
|
+
Requires-Dist: tenacity==5.1.5
|
|
16
|
+
Requires-Dist: anthropic>=0.105.0
|
|
17
|
+
Requires-Dist: mcp>=1.27.0
|
|
18
|
+
Requires-Dist: click>=8.1.0
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "mega-claude-connector"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Mega.nz MCP server and Claude-powered CLI for cloud storage management"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "Mrigank Shekhar Chaubey", email = "mriganksc@infinitywards.com" }]
|
|
13
|
+
keywords = ["mega", "claude", "mcp", "ai", "cloud-storage"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
]
|
|
19
|
+
dependencies = [
|
|
20
|
+
"mega.py>=1.0.8",
|
|
21
|
+
"tenacity==5.1.5",
|
|
22
|
+
"anthropic>=0.105.0",
|
|
23
|
+
"mcp>=1.27.0",
|
|
24
|
+
"click>=8.1.0",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.urls]
|
|
28
|
+
Homepage = "https://github.com/mriganksc/mega-claude-connector"
|
|
29
|
+
|
|
30
|
+
[project.scripts]
|
|
31
|
+
mega-claude = "mega_connector.cli:chat"
|
|
32
|
+
mega-claude-mcp = "mega_connector.mcp_server:main_sync"
|
|
33
|
+
|
|
34
|
+
[tool.setuptools.packages.find]
|
|
35
|
+
where = ["src"]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mega-claude-connector
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Mega.nz MCP server and Claude-powered CLI for cloud storage management
|
|
5
|
+
Author-email: Mrigank Shekhar Chaubey <mriganksc@infinitywards.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/mriganksc/mega-claude-connector
|
|
8
|
+
Keywords: mega,claude,mcp,ai,cloud-storage
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.11
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
Requires-Dist: mega.py>=1.0.8
|
|
15
|
+
Requires-Dist: tenacity==5.1.5
|
|
16
|
+
Requires-Dist: anthropic>=0.105.0
|
|
17
|
+
Requires-Dist: mcp>=1.27.0
|
|
18
|
+
Requires-Dist: click>=8.1.0
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
pyproject.toml
|
|
2
|
+
src/mega_claude_connector.egg-info/PKG-INFO
|
|
3
|
+
src/mega_claude_connector.egg-info/SOURCES.txt
|
|
4
|
+
src/mega_claude_connector.egg-info/dependency_links.txt
|
|
5
|
+
src/mega_claude_connector.egg-info/entry_points.txt
|
|
6
|
+
src/mega_claude_connector.egg-info/requires.txt
|
|
7
|
+
src/mega_claude_connector.egg-info/top_level.txt
|
|
8
|
+
src/mega_connector/__init__.py
|
|
9
|
+
src/mega_connector/cli.py
|
|
10
|
+
src/mega_connector/config.py
|
|
11
|
+
src/mega_connector/mcp_server.py
|
|
12
|
+
src/mega_connector/mega_client.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
mega_connector
|
|
File without changes
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import click
|
|
3
|
+
import anthropic
|
|
4
|
+
from . import mega_client as mc
|
|
5
|
+
from .config import get_credentials
|
|
6
|
+
|
|
7
|
+
TOOLS = [
|
|
8
|
+
{
|
|
9
|
+
"name": "mega_storage_info",
|
|
10
|
+
"description": "Get Mega.nz storage usage and quota",
|
|
11
|
+
"input_schema": {"type": "object", "properties": {}},
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"name": "mega_list_files",
|
|
15
|
+
"description": "List files and folders. Optionally filter by folder path.",
|
|
16
|
+
"input_schema": {
|
|
17
|
+
"type": "object",
|
|
18
|
+
"properties": {
|
|
19
|
+
"folder_path": {"type": "string", "description": "Remote folder name (omit for root)"},
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"name": "mega_search",
|
|
25
|
+
"description": "Search for files or folders by name",
|
|
26
|
+
"input_schema": {
|
|
27
|
+
"type": "object",
|
|
28
|
+
"properties": {
|
|
29
|
+
"query": {"type": "string"},
|
|
30
|
+
},
|
|
31
|
+
"required": ["query"],
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"name": "mega_upload",
|
|
36
|
+
"description": "Upload a local file to Mega.nz",
|
|
37
|
+
"input_schema": {
|
|
38
|
+
"type": "object",
|
|
39
|
+
"properties": {
|
|
40
|
+
"local_path": {"type": "string"},
|
|
41
|
+
"remote_folder": {"type": "string"},
|
|
42
|
+
},
|
|
43
|
+
"required": ["local_path"],
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"name": "mega_download",
|
|
48
|
+
"description": "Download a file from Mega.nz",
|
|
49
|
+
"input_schema": {
|
|
50
|
+
"type": "object",
|
|
51
|
+
"properties": {
|
|
52
|
+
"file_name": {"type": "string"},
|
|
53
|
+
"local_dest": {"type": "string"},
|
|
54
|
+
},
|
|
55
|
+
"required": ["file_name"],
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"name": "mega_create_folder",
|
|
60
|
+
"description": "Create a new folder on Mega.nz",
|
|
61
|
+
"input_schema": {
|
|
62
|
+
"type": "object",
|
|
63
|
+
"properties": {
|
|
64
|
+
"folder_name": {"type": "string"},
|
|
65
|
+
"parent_folder": {"type": "string"},
|
|
66
|
+
},
|
|
67
|
+
"required": ["folder_name"],
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"name": "mega_delete",
|
|
72
|
+
"description": "Delete a file or folder from Mega.nz",
|
|
73
|
+
"input_schema": {
|
|
74
|
+
"type": "object",
|
|
75
|
+
"properties": {
|
|
76
|
+
"file_name": {"type": "string"},
|
|
77
|
+
},
|
|
78
|
+
"required": ["file_name"],
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
SYSTEM = (
|
|
84
|
+
"You are a helpful assistant for managing Mega.nz cloud storage. "
|
|
85
|
+
"Use the provided tools to list, search, upload, download, create folders, and delete files. "
|
|
86
|
+
"Always confirm destructive actions (delete) before proceeding."
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _run_tool(name: str, args: dict):
|
|
91
|
+
dispatch = {
|
|
92
|
+
"mega_storage_info": lambda: mc.get_storage_info(),
|
|
93
|
+
"mega_list_files": lambda: mc.list_files(args.get("folder_path")),
|
|
94
|
+
"mega_search": lambda: mc.search_files(args["query"]),
|
|
95
|
+
"mega_upload": lambda: mc.upload_file(args["local_path"], args.get("remote_folder")),
|
|
96
|
+
"mega_download": lambda: mc.download_file(args["file_name"], args.get("local_dest", ".")),
|
|
97
|
+
"mega_create_folder": lambda: mc.create_folder(args["folder_name"], args.get("parent_folder")),
|
|
98
|
+
"mega_delete": lambda: mc.delete_node(args["file_name"]),
|
|
99
|
+
}
|
|
100
|
+
try:
|
|
101
|
+
return dispatch[name]()
|
|
102
|
+
except Exception as e:
|
|
103
|
+
return {"error": str(e)}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@click.command()
|
|
107
|
+
@click.option("--model", default="claude-sonnet-4-6", show_default=True)
|
|
108
|
+
def chat(model: str):
|
|
109
|
+
"""Interactive Claude-powered Mega.nz file manager."""
|
|
110
|
+
creds = get_credentials(require_anthropic=True)
|
|
111
|
+
client = anthropic.Anthropic(api_key=creds["anthropic_api_key"])
|
|
112
|
+
messages = []
|
|
113
|
+
|
|
114
|
+
click.echo("Mega.nz Claude Connector — type 'exit' to quit\n")
|
|
115
|
+
|
|
116
|
+
while True:
|
|
117
|
+
user_input = click.prompt("You").strip()
|
|
118
|
+
if user_input.lower() in ("exit", "quit"):
|
|
119
|
+
break
|
|
120
|
+
|
|
121
|
+
messages.append({"role": "user", "content": user_input})
|
|
122
|
+
|
|
123
|
+
while True:
|
|
124
|
+
response = client.messages.create(
|
|
125
|
+
model=model,
|
|
126
|
+
max_tokens=4096,
|
|
127
|
+
system=SYSTEM,
|
|
128
|
+
tools=TOOLS,
|
|
129
|
+
messages=messages,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# Collect assistant content
|
|
133
|
+
messages.append({"role": "assistant", "content": response.content})
|
|
134
|
+
|
|
135
|
+
if response.stop_reason == "tool_use":
|
|
136
|
+
tool_results = []
|
|
137
|
+
for block in response.content:
|
|
138
|
+
if block.type == "tool_use":
|
|
139
|
+
click.echo(f" [tool] {block.name}({json.dumps(block.input)})")
|
|
140
|
+
result = _run_tool(block.name, block.input)
|
|
141
|
+
tool_results.append({
|
|
142
|
+
"type": "tool_result",
|
|
143
|
+
"tool_use_id": block.id,
|
|
144
|
+
"content": json.dumps(result),
|
|
145
|
+
})
|
|
146
|
+
messages.append({"role": "user", "content": tool_results})
|
|
147
|
+
else:
|
|
148
|
+
for block in response.content:
|
|
149
|
+
if hasattr(block, "text"):
|
|
150
|
+
click.echo(f"\nClaude: {block.text}\n")
|
|
151
|
+
break
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
if __name__ == "__main__":
|
|
155
|
+
chat()
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import configparser
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
_CONFIG_DIR = Path.home() / ".config" / "mega-claude-connector"
|
|
6
|
+
_CONFIG_FILE = _CONFIG_DIR / "config"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _load_file_config() -> dict:
|
|
10
|
+
if not _CONFIG_FILE.exists():
|
|
11
|
+
return {}
|
|
12
|
+
parser = configparser.ConfigParser()
|
|
13
|
+
parser.read(_CONFIG_FILE)
|
|
14
|
+
return dict(parser.get("credentials", {})) if parser.has_section("credentials") else {}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _save_config(values: dict):
|
|
18
|
+
_CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
|
19
|
+
parser = configparser.ConfigParser()
|
|
20
|
+
parser["credentials"] = values
|
|
21
|
+
with open(_CONFIG_FILE, "w") as f:
|
|
22
|
+
parser.write(f)
|
|
23
|
+
_CONFIG_FILE.chmod(0o600)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _prompt_and_save(require_anthropic: bool = False) -> dict:
|
|
27
|
+
print("\nFirst-time setup — credentials are stored only in ~/.config/mega-claude-connector/config\n")
|
|
28
|
+
values = {
|
|
29
|
+
"mega_email": input("Mega.nz email: ").strip(),
|
|
30
|
+
"mega_password": input("Mega.nz password: ").strip(),
|
|
31
|
+
}
|
|
32
|
+
if require_anthropic:
|
|
33
|
+
values["anthropic_api_key"] = input("Anthropic API key: ").strip()
|
|
34
|
+
_save_config(values)
|
|
35
|
+
print(f"\nCredentials saved to {_CONFIG_FILE}\n")
|
|
36
|
+
return values
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def get_credentials(require_anthropic: bool = False) -> dict:
|
|
40
|
+
"""Return credentials from env vars → config file → interactive prompt."""
|
|
41
|
+
mega_email = os.environ.get("MEGA_EMAIL")
|
|
42
|
+
mega_password = os.environ.get("MEGA_PASSWORD")
|
|
43
|
+
anthropic_key = os.environ.get("ANTHROPIC_API_KEY")
|
|
44
|
+
|
|
45
|
+
if mega_email and mega_password:
|
|
46
|
+
creds = {"mega_email": mega_email, "mega_password": mega_password}
|
|
47
|
+
if anthropic_key:
|
|
48
|
+
creds["anthropic_api_key"] = anthropic_key
|
|
49
|
+
return creds
|
|
50
|
+
|
|
51
|
+
file_creds = _load_file_config()
|
|
52
|
+
if file_creds.get("mega_email") and file_creds.get("mega_password"):
|
|
53
|
+
if require_anthropic and not file_creds.get("anthropic_api_key"):
|
|
54
|
+
key = input("Anthropic API key (not saved yet): ").strip()
|
|
55
|
+
file_creds["anthropic_api_key"] = key
|
|
56
|
+
_save_config(file_creds)
|
|
57
|
+
return file_creds
|
|
58
|
+
|
|
59
|
+
return _prompt_and_save(require_anthropic=require_anthropic)
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from mcp.server import Server
|
|
3
|
+
from mcp.server.stdio import stdio_server
|
|
4
|
+
from mcp import types
|
|
5
|
+
from . import mega_client as mc
|
|
6
|
+
|
|
7
|
+
app = Server("mega-connector")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@app.list_tools()
|
|
11
|
+
async def list_tools() -> list[types.Tool]:
|
|
12
|
+
return [
|
|
13
|
+
types.Tool(
|
|
14
|
+
name="mega_storage_info",
|
|
15
|
+
description="Get Mega.nz storage usage and quota",
|
|
16
|
+
inputSchema={"type": "object", "properties": {}},
|
|
17
|
+
),
|
|
18
|
+
types.Tool(
|
|
19
|
+
name="mega_list_files",
|
|
20
|
+
description="List files and folders. Optionally filter by folder path.",
|
|
21
|
+
inputSchema={
|
|
22
|
+
"type": "object",
|
|
23
|
+
"properties": {
|
|
24
|
+
"folder_path": {"type": "string", "description": "Remote folder name to list (omit for root)"},
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
),
|
|
28
|
+
types.Tool(
|
|
29
|
+
name="mega_search",
|
|
30
|
+
description="Search for files or folders by name",
|
|
31
|
+
inputSchema={
|
|
32
|
+
"type": "object",
|
|
33
|
+
"properties": {
|
|
34
|
+
"query": {"type": "string", "description": "Search term"},
|
|
35
|
+
},
|
|
36
|
+
"required": ["query"],
|
|
37
|
+
},
|
|
38
|
+
),
|
|
39
|
+
types.Tool(
|
|
40
|
+
name="mega_upload",
|
|
41
|
+
description="Upload a local file to Mega.nz",
|
|
42
|
+
inputSchema={
|
|
43
|
+
"type": "object",
|
|
44
|
+
"properties": {
|
|
45
|
+
"local_path": {"type": "string", "description": "Absolute path to the local file"},
|
|
46
|
+
"remote_folder": {"type": "string", "description": "Destination folder name on Mega (omit for root)"},
|
|
47
|
+
},
|
|
48
|
+
"required": ["local_path"],
|
|
49
|
+
},
|
|
50
|
+
),
|
|
51
|
+
types.Tool(
|
|
52
|
+
name="mega_download",
|
|
53
|
+
description="Download a file from Mega.nz to a local directory",
|
|
54
|
+
inputSchema={
|
|
55
|
+
"type": "object",
|
|
56
|
+
"properties": {
|
|
57
|
+
"file_name": {"type": "string", "description": "Name of the file on Mega"},
|
|
58
|
+
"local_dest": {"type": "string", "description": "Local directory to save to (default: current dir)"},
|
|
59
|
+
},
|
|
60
|
+
"required": ["file_name"],
|
|
61
|
+
},
|
|
62
|
+
),
|
|
63
|
+
types.Tool(
|
|
64
|
+
name="mega_create_folder",
|
|
65
|
+
description="Create a new folder on Mega.nz",
|
|
66
|
+
inputSchema={
|
|
67
|
+
"type": "object",
|
|
68
|
+
"properties": {
|
|
69
|
+
"folder_name": {"type": "string", "description": "Name of the new folder"},
|
|
70
|
+
"parent_folder": {"type": "string", "description": "Parent folder name (omit for root)"},
|
|
71
|
+
},
|
|
72
|
+
"required": ["folder_name"],
|
|
73
|
+
},
|
|
74
|
+
),
|
|
75
|
+
types.Tool(
|
|
76
|
+
name="mega_delete",
|
|
77
|
+
description="Delete a file or folder from Mega.nz",
|
|
78
|
+
inputSchema={
|
|
79
|
+
"type": "object",
|
|
80
|
+
"properties": {
|
|
81
|
+
"file_name": {"type": "string", "description": "Name of the file or folder to delete"},
|
|
82
|
+
},
|
|
83
|
+
"required": ["file_name"],
|
|
84
|
+
},
|
|
85
|
+
),
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@app.call_tool()
|
|
90
|
+
async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
|
|
91
|
+
try:
|
|
92
|
+
if name == "mega_storage_info":
|
|
93
|
+
result = mc.get_storage_info()
|
|
94
|
+
elif name == "mega_list_files":
|
|
95
|
+
result = mc.list_files(arguments.get("folder_path"))
|
|
96
|
+
elif name == "mega_search":
|
|
97
|
+
result = mc.search_files(arguments["query"])
|
|
98
|
+
elif name == "mega_upload":
|
|
99
|
+
result = mc.upload_file(arguments["local_path"], arguments.get("remote_folder"))
|
|
100
|
+
elif name == "mega_download":
|
|
101
|
+
result = mc.download_file(arguments["file_name"], arguments.get("local_dest", "."))
|
|
102
|
+
elif name == "mega_create_folder":
|
|
103
|
+
result = mc.create_folder(arguments["folder_name"], arguments.get("parent_folder"))
|
|
104
|
+
elif name == "mega_delete":
|
|
105
|
+
result = mc.delete_node(arguments["file_name"])
|
|
106
|
+
else:
|
|
107
|
+
result = {"error": f"Unknown tool: {name}"}
|
|
108
|
+
except Exception as e:
|
|
109
|
+
result = {"error": str(e)}
|
|
110
|
+
|
|
111
|
+
return [types.TextContent(type="text", text=json.dumps(result, indent=2))]
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
async def main():
|
|
115
|
+
async with stdio_server() as (read_stream, write_stream):
|
|
116
|
+
await app.run(read_stream, write_stream, app.create_initialization_options())
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def main_sync():
|
|
120
|
+
import asyncio
|
|
121
|
+
asyncio.run(main())
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
if __name__ == "__main__":
|
|
125
|
+
main_sync()
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from mega import Mega
|
|
3
|
+
from .config import get_credentials
|
|
4
|
+
|
|
5
|
+
_mega = Mega()
|
|
6
|
+
_client = None
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _get_client():
|
|
10
|
+
global _client
|
|
11
|
+
if _client is None:
|
|
12
|
+
creds = get_credentials()
|
|
13
|
+
_client = _mega.login(creds["mega_email"], creds["mega_password"])
|
|
14
|
+
return _client
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def get_storage_info() -> dict:
|
|
18
|
+
m = _get_client()
|
|
19
|
+
quota = m.get_quota()
|
|
20
|
+
storage = m.get_storage_space()
|
|
21
|
+
return {
|
|
22
|
+
"used_bytes": storage.get("used", 0),
|
|
23
|
+
"total_bytes": storage.get("total", 0),
|
|
24
|
+
"quota_bytes": quota,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def list_files(folder_path: str | None = None) -> list[dict]:
|
|
29
|
+
m = _get_client()
|
|
30
|
+
if folder_path:
|
|
31
|
+
folder = m.find(folder_path)
|
|
32
|
+
if not folder:
|
|
33
|
+
raise ValueError(f"Folder not found: {folder_path}")
|
|
34
|
+
files = m.get_files_in_node(folder[0])
|
|
35
|
+
else:
|
|
36
|
+
files = m.get_files()
|
|
37
|
+
|
|
38
|
+
result = []
|
|
39
|
+
for file_id, file_data in files.items():
|
|
40
|
+
attrs = file_data.get("a", {})
|
|
41
|
+
result.append({
|
|
42
|
+
"id": file_id,
|
|
43
|
+
"name": attrs.get("n", "[no name]") if attrs else "[no name]",
|
|
44
|
+
"type": "folder" if file_data.get("t") == 1 else "file",
|
|
45
|
+
"size": file_data.get("s", 0),
|
|
46
|
+
})
|
|
47
|
+
return result
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def search_files(query: str) -> list[dict]:
|
|
51
|
+
m = _get_client()
|
|
52
|
+
results = m.find(query)
|
|
53
|
+
if not results:
|
|
54
|
+
return []
|
|
55
|
+
output = []
|
|
56
|
+
for item in results:
|
|
57
|
+
attrs = item.get("a", {})
|
|
58
|
+
output.append({
|
|
59
|
+
"id": item.get("h"),
|
|
60
|
+
"name": attrs.get("n", "[no name]") if attrs else "[no name]",
|
|
61
|
+
"type": "folder" if item.get("t") == 1 else "file",
|
|
62
|
+
"size": item.get("s", 0),
|
|
63
|
+
})
|
|
64
|
+
return output
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def upload_file(local_path: str, remote_folder: str | None = None) -> dict:
|
|
68
|
+
m = _get_client()
|
|
69
|
+
if not os.path.exists(local_path):
|
|
70
|
+
raise FileNotFoundError(f"Local file not found: {local_path}")
|
|
71
|
+
dest = None
|
|
72
|
+
if remote_folder:
|
|
73
|
+
folder = m.find(remote_folder)
|
|
74
|
+
if not folder:
|
|
75
|
+
raise ValueError(f"Remote folder not found: {remote_folder}")
|
|
76
|
+
dest = folder[0]
|
|
77
|
+
file = m.upload(local_path, dest)
|
|
78
|
+
link = m.get_upload_link(file)
|
|
79
|
+
return {"name": os.path.basename(local_path), "link": link}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def download_file(file_name: str, local_dest: str = ".") -> str:
|
|
83
|
+
m = _get_client()
|
|
84
|
+
results = m.find(file_name)
|
|
85
|
+
if not results:
|
|
86
|
+
raise FileNotFoundError(f"File not found on Mega: {file_name}")
|
|
87
|
+
m.download(results[0], local_dest)
|
|
88
|
+
return os.path.join(local_dest, file_name)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def create_folder(folder_name: str, parent_folder: str | None = None) -> dict:
|
|
92
|
+
m = _get_client()
|
|
93
|
+
parent = None
|
|
94
|
+
if parent_folder:
|
|
95
|
+
found = m.find(parent_folder)
|
|
96
|
+
if not found:
|
|
97
|
+
raise ValueError(f"Parent folder not found: {parent_folder}")
|
|
98
|
+
parent = found[0]
|
|
99
|
+
folder = m.create_folder(folder_name, parent)
|
|
100
|
+
return {"name": folder_name, "id": folder.get(folder_name)}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def delete_node(file_name: str) -> bool:
|
|
104
|
+
m = _get_client()
|
|
105
|
+
results = m.find(file_name)
|
|
106
|
+
if not results:
|
|
107
|
+
raise FileNotFoundError(f"Not found: {file_name}")
|
|
108
|
+
m.delete(results[0])
|
|
109
|
+
return True
|