BROKENXAPI 2.0.2__tar.gz → 2.0.2b0__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.
- {brokenxapi-2.0.2 → brokenxapi-2.0.2b0}/BROKENXAPI.egg-info/PKG-INFO +1 -1
- {brokenxapi-2.0.2 → brokenxapi-2.0.2b0}/BROKENXAPI.egg-info/SOURCES.txt +2 -2
- brokenxapi-2.0.2b0/MANIFEST.in +2 -0
- {brokenxapi-2.0.2 → brokenxapi-2.0.2b0}/PKG-INFO +1 -1
- brokenxapi-2.0.2b0/brokenxapi/__init__.py +4 -0
- brokenxapi-2.0.2b0/brokenxapi/__version__.py +2 -0
- brokenxapi-2.0.2b0/brokenxapi/_client.pyc +0 -0
- brokenxapi-2.0.2b0/brokenxapi/cli.py +129 -0
- brokenxapi-2.0.2/brokenxapi/__init__.py +0 -4
- brokenxapi-2.0.2/brokenxapi/__version__.py +0 -2
- brokenxapi-2.0.2/brokenxapi/auth.py +0 -11
- brokenxapi-2.0.2/brokenxapi/cli.py +0 -51
- brokenxapi-2.0.2/brokenxapi/client.py +0 -46
- {brokenxapi-2.0.2 → brokenxapi-2.0.2b0}/BROKENXAPI.egg-info/dependency_links.txt +0 -0
- {brokenxapi-2.0.2 → brokenxapi-2.0.2b0}/BROKENXAPI.egg-info/entry_points.txt +0 -0
- {brokenxapi-2.0.2 → brokenxapi-2.0.2b0}/BROKENXAPI.egg-info/not-zip-safe +0 -0
- {brokenxapi-2.0.2 → brokenxapi-2.0.2b0}/BROKENXAPI.egg-info/requires.txt +0 -0
- {brokenxapi-2.0.2 → brokenxapi-2.0.2b0}/BROKENXAPI.egg-info/top_level.txt +0 -0
- {brokenxapi-2.0.2 → brokenxapi-2.0.2b0}/LICENSE +0 -0
- {brokenxapi-2.0.2 → brokenxapi-2.0.2b0}/README.md +0 -0
- {brokenxapi-2.0.2 → brokenxapi-2.0.2b0}/brokenxapi/exceptions.py +0 -0
- {brokenxapi-2.0.2 → brokenxapi-2.0.2b0}/brokenxapi/models.py +0 -0
- {brokenxapi-2.0.2 → brokenxapi-2.0.2b0}/pyproject.toml +0 -0
- {brokenxapi-2.0.2 → brokenxapi-2.0.2b0}/setup.cfg +0 -0
- {brokenxapi-2.0.2 → brokenxapi-2.0.2b0}/setup.py +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
LICENSE
|
|
2
|
+
MANIFEST.in
|
|
2
3
|
README.md
|
|
3
4
|
pyproject.toml
|
|
4
5
|
setup.py
|
|
@@ -11,8 +12,7 @@ BROKENXAPI.egg-info/requires.txt
|
|
|
11
12
|
BROKENXAPI.egg-info/top_level.txt
|
|
12
13
|
brokenxapi/__init__.py
|
|
13
14
|
brokenxapi/__version__.py
|
|
14
|
-
brokenxapi/
|
|
15
|
+
brokenxapi/_client.pyc
|
|
15
16
|
brokenxapi/cli.py
|
|
16
|
-
brokenxapi/client.py
|
|
17
17
|
brokenxapi/exceptions.py
|
|
18
18
|
brokenxapi/models.py
|
|
Binary file
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import asyncio
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
import stat
|
|
7
|
+
import importlib.metadata
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from brokenxapi import BrokenXAPI
|
|
11
|
+
from brokenxapi.exceptions import BrokenXAPIError
|
|
12
|
+
|
|
13
|
+
CONFIG_DIR = Path.home() / ".brokenx"
|
|
14
|
+
CONFIG_FILE = CONFIG_DIR / "config.json"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# ---------------- CONFIG HELPERS ----------------
|
|
18
|
+
|
|
19
|
+
def save_api_key(api_key: str):
|
|
20
|
+
CONFIG_DIR.mkdir(exist_ok=True)
|
|
21
|
+
CONFIG_FILE.write_text(json.dumps({"api_key": api_key}))
|
|
22
|
+
CONFIG_FILE.chmod(stat.S_IRUSR | stat.S_IWUSR) # 600
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def load_api_key():
|
|
26
|
+
if not CONFIG_FILE.exists():
|
|
27
|
+
return None
|
|
28
|
+
try:
|
|
29
|
+
data = json.loads(CONFIG_FILE.read_text())
|
|
30
|
+
return data.get("api_key")
|
|
31
|
+
except Exception:
|
|
32
|
+
return None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def require_api_key():
|
|
36
|
+
key = load_api_key()
|
|
37
|
+
if not key:
|
|
38
|
+
print(
|
|
39
|
+
"❌ Not authenticated.\n"
|
|
40
|
+
"Run: brokenx auth <YOUR_API_KEY>",
|
|
41
|
+
file=sys.stderr,
|
|
42
|
+
)
|
|
43
|
+
sys.exit(1)
|
|
44
|
+
return key
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# ---------------- CLI CORE ----------------
|
|
48
|
+
|
|
49
|
+
async def run_cli():
|
|
50
|
+
parser = argparse.ArgumentParser(
|
|
51
|
+
prog="brokenx",
|
|
52
|
+
description="BROKENXAPI Command Line Interface",
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
parser.add_argument(
|
|
56
|
+
"-v", "--version",
|
|
57
|
+
action="store_true",
|
|
58
|
+
help="Show BROKENXAPI version",
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
sub = parser.add_subparsers(dest="command")
|
|
62
|
+
|
|
63
|
+
# ---------- AUTH ----------
|
|
64
|
+
auth_cmd = sub.add_parser("auth", help="Authenticate with API key")
|
|
65
|
+
auth_cmd.add_argument("api_key", help="Your BROKENXAPI key")
|
|
66
|
+
|
|
67
|
+
# ---------- SEARCH ----------
|
|
68
|
+
search_cmd = sub.add_parser("search", help="Search YouTube")
|
|
69
|
+
search_cmd.add_argument("query", help="Search query")
|
|
70
|
+
|
|
71
|
+
# ---------- DOWNLOAD ----------
|
|
72
|
+
download_cmd = sub.add_parser("download", help="Download media")
|
|
73
|
+
download_cmd.add_argument("video_id", help="YouTube video ID")
|
|
74
|
+
download_cmd.add_argument(
|
|
75
|
+
"-v", "--video",
|
|
76
|
+
action="store_true",
|
|
77
|
+
help="Download video instead of audio",
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
args = parser.parse_args()
|
|
81
|
+
|
|
82
|
+
# ---------------- VERSION ----------------
|
|
83
|
+
if args.version:
|
|
84
|
+
print(importlib.metadata.version("BROKENXAPI"))
|
|
85
|
+
return
|
|
86
|
+
|
|
87
|
+
# ---------------- AUTH ----------------
|
|
88
|
+
if args.command == "auth":
|
|
89
|
+
try:
|
|
90
|
+
# 🔍 Verify key by doing a lightweight call
|
|
91
|
+
async with BrokenXAPI(api_key=args.api_key) as api:
|
|
92
|
+
await api.search("test") # harmless validation
|
|
93
|
+
save_api_key(args.api_key)
|
|
94
|
+
print("✅ Authentication successful. API key saved.")
|
|
95
|
+
except BrokenXAPIError as e:
|
|
96
|
+
print(f"❌ Authentication failed: {e}", file=sys.stderr)
|
|
97
|
+
sys.exit(1)
|
|
98
|
+
return
|
|
99
|
+
|
|
100
|
+
# ---------------- OTHER COMMANDS ----------------
|
|
101
|
+
api_key = require_api_key()
|
|
102
|
+
|
|
103
|
+
async with BrokenXAPI(api_key=api_key) as api:
|
|
104
|
+
if args.command == "search":
|
|
105
|
+
result = await api.search(args.query)
|
|
106
|
+
print(json.dumps(result, indent=2))
|
|
107
|
+
return
|
|
108
|
+
|
|
109
|
+
if args.command == "download":
|
|
110
|
+
media_type = "video" if args.video else "audio"
|
|
111
|
+
result = await api.download(args.video_id, media_type)
|
|
112
|
+
print(json.dumps(result, indent=2))
|
|
113
|
+
return
|
|
114
|
+
|
|
115
|
+
parser.print_help()
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def main():
|
|
119
|
+
try:
|
|
120
|
+
asyncio.run(run_cli())
|
|
121
|
+
except KeyboardInterrupt:
|
|
122
|
+
pass
|
|
123
|
+
except BrokenXAPIError as e:
|
|
124
|
+
print(f"❌ {e}", file=sys.stderr)
|
|
125
|
+
sys.exit(1)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
if __name__ == "__main__":
|
|
129
|
+
main()
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import argparse
|
|
2
|
-
import asyncio
|
|
3
|
-
from .client import BrokenXAPI
|
|
4
|
-
from .auth import get_key, save_key
|
|
5
|
-
from .__version__ import __version__
|
|
6
|
-
|
|
7
|
-
def main():
|
|
8
|
-
parser = argparse.ArgumentParser("brokenx")
|
|
9
|
-
parser.add_argument("-v", "--version", action="store_true")
|
|
10
|
-
|
|
11
|
-
sub = parser.add_subparsers(dest="cmd")
|
|
12
|
-
|
|
13
|
-
auth = sub.add_parser("auth")
|
|
14
|
-
auth.add_argument("key")
|
|
15
|
-
|
|
16
|
-
search = sub.add_parser("search")
|
|
17
|
-
search.add_argument("query")
|
|
18
|
-
|
|
19
|
-
dl = sub.add_parser("download")
|
|
20
|
-
dl.add_argument("video_id")
|
|
21
|
-
dl.add_argument("-v", "--video", action="store_true")
|
|
22
|
-
|
|
23
|
-
args = parser.parse_args()
|
|
24
|
-
|
|
25
|
-
if args.version:
|
|
26
|
-
print(__version__)
|
|
27
|
-
return
|
|
28
|
-
|
|
29
|
-
if args.cmd == "auth":
|
|
30
|
-
save_key(args.key)
|
|
31
|
-
print("✅ API key saved")
|
|
32
|
-
return
|
|
33
|
-
|
|
34
|
-
api_key = get_key()
|
|
35
|
-
if not api_key:
|
|
36
|
-
print("❌ Please run: brokenx auth <API_KEY>")
|
|
37
|
-
return
|
|
38
|
-
|
|
39
|
-
asyncio.run(run(args, api_key))
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
async def run(args, api_key):
|
|
43
|
-
async with BrokenXAPI(api_key) as api:
|
|
44
|
-
if args.cmd == "search":
|
|
45
|
-
res = await api.search(args.query)
|
|
46
|
-
print(res)
|
|
47
|
-
|
|
48
|
-
elif args.cmd == "download":
|
|
49
|
-
media = "video" if args.video else "audio"
|
|
50
|
-
res = await api.download(args.video_id, media)
|
|
51
|
-
print(res["telegram_url"])
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import aiohttp
|
|
2
|
-
|
|
3
|
-
class BrokenXAPI:
|
|
4
|
-
def __init__(self, api_key: str, base_url: str = "https://mrbroken-brokenxbots.hf.space"):
|
|
5
|
-
self.api_key = api_key
|
|
6
|
-
self.base_url = base_url.rstrip("/")
|
|
7
|
-
self._session = None
|
|
8
|
-
|
|
9
|
-
async def __aenter__(self):
|
|
10
|
-
self._session = aiohttp.ClientSession(
|
|
11
|
-
headers={
|
|
12
|
-
"Authorization": f"Bearer {self.api_key}"
|
|
13
|
-
}
|
|
14
|
-
)
|
|
15
|
-
return self
|
|
16
|
-
|
|
17
|
-
async def __aexit__(self, exc_type, exc, tb):
|
|
18
|
-
if self._session:
|
|
19
|
-
await self._session.close()
|
|
20
|
-
|
|
21
|
-
async def _get(self, path: str, params: dict | None = None):
|
|
22
|
-
url = f"{self.base_url}{path}"
|
|
23
|
-
async with self._session.get(url, params=params) as r:
|
|
24
|
-
r.raise_for_status()
|
|
25
|
-
return await r.json()
|
|
26
|
-
|
|
27
|
-
# -------- PUBLIC METHODS --------
|
|
28
|
-
|
|
29
|
-
async def search(self, query: str, video: bool = False):
|
|
30
|
-
return await self._get(
|
|
31
|
-
"/search",
|
|
32
|
-
{
|
|
33
|
-
"q": query,
|
|
34
|
-
"video": video
|
|
35
|
-
}
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
async def download(self, video_id: str, media: str = "audio"):
|
|
39
|
-
if media not in ("audio", "video"):
|
|
40
|
-
raise ValueError("media must be 'audio' or 'video'")
|
|
41
|
-
|
|
42
|
-
return await self._get(
|
|
43
|
-
f"/download/{media}",
|
|
44
|
-
{"video_id": video_id}
|
|
45
|
-
)
|
|
46
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|