BROKENXAPI 2.0.1__tar.gz → 2.0.2__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: BROKENXAPI
3
- Version: 2.0.1
3
+ Version: 2.0.2
4
4
  Summary: Official async Python SDK and CLI for BrokenX YouTube API
5
5
  Author-email: Mr Broken <brokenxnetwork@gmail.com>
6
6
  License: MIT License
@@ -11,6 +11,8 @@ BROKENXAPI.egg-info/requires.txt
11
11
  BROKENXAPI.egg-info/top_level.txt
12
12
  brokenxapi/__init__.py
13
13
  brokenxapi/__version__.py
14
+ brokenxapi/auth.py
14
15
  brokenxapi/cli.py
16
+ brokenxapi/client.py
15
17
  brokenxapi/exceptions.py
16
18
  brokenxapi/models.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: BROKENXAPI
3
- Version: 2.0.1
3
+ Version: 2.0.2
4
4
  Summary: Official async Python SDK and CLI for BrokenX YouTube API
5
5
  Author-email: Mr Broken <brokenxnetwork@gmail.com>
6
6
  License: MIT License
@@ -0,0 +1,2 @@
1
+ __version__ = "2.0.2"
2
+
@@ -0,0 +1,11 @@
1
+ from pathlib import Path
2
+
3
+ AUTH_FILE = Path.home() / ".brokenxapi"
4
+
5
+ def save_key(key: str):
6
+ AUTH_FILE.write_text(key.strip())
7
+
8
+ def get_key() -> str | None:
9
+ if AUTH_FILE.exists():
10
+ return AUTH_FILE.read_text().strip()
11
+ return None
@@ -0,0 +1,51 @@
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"])
@@ -0,0 +1,46 @@
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
+
@@ -1,2 +0,0 @@
1
- __version__ = "2.0.1"
2
-
@@ -1,129 +0,0 @@
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()
File without changes
File without changes
File without changes
File without changes
File without changes