meowbox 1.0.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.
- meowbox-1.0.0/LICENSE +21 -0
- meowbox-1.0.0/PKG-INFO +155 -0
- meowbox-1.0.0/README.md +133 -0
- meowbox-1.0.0/meowbox/__init__.py +34 -0
- meowbox-1.0.0/meowbox/exceptions.py +13 -0
- meowbox-1.0.0/meowbox/meowbox.py +113 -0
- meowbox-1.0.0/meowbox/meowbox_async.py +76 -0
- meowbox-1.0.0/meowbox.egg-info/PKG-INFO +155 -0
- meowbox-1.0.0/meowbox.egg-info/SOURCES.txt +12 -0
- meowbox-1.0.0/meowbox.egg-info/dependency_links.txt +1 -0
- meowbox-1.0.0/meowbox.egg-info/requires.txt +5 -0
- meowbox-1.0.0/meowbox.egg-info/top_level.txt +1 -0
- meowbox-1.0.0/pyproject.toml +36 -0
- meowbox-1.0.0/setup.cfg +4 -0
meowbox-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 @BadmundaXd
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
meowbox-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: meowbox
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: MeowBox — Permanent file hosting uploader (sync & async)
|
|
5
|
+
Author-email: BadmundaXd <munda.bad1322@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Badmunda05/MeowBox
|
|
8
|
+
Project-URL: Source, https://github.com/Badmunda05/MeowBox
|
|
9
|
+
Project-URL: Telegram, https://t.me/BadmundaXd
|
|
10
|
+
Keywords: meowbox,upload,file hosting,async,httpx
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Requires-Python: >=3.7
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: requests
|
|
18
|
+
Requires-Dist: httpx
|
|
19
|
+
Provides-Extra: async
|
|
20
|
+
Requires-Dist: httpx; extra == "async"
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
|
|
23
|
+
# MeowBox Python Library 📦
|
|
24
|
+
|
|
25
|
+
> **Permanent file hosting — files never expire.**
|
|
26
|
+
> By [@BadmundaXd](https://t.me/BadmundaXd) | [GitHub](https://github.com/Badmunda05/MeowBox)
|
|
27
|
+
|
|
28
|
+
Python library for uploading files to [MeowBox](https://files.tgvibes.online) — supports both sync and async.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Install
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install meowbox
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Quick Start
|
|
41
|
+
|
|
42
|
+
### Sync upload
|
|
43
|
+
```python
|
|
44
|
+
from meowbox import upload
|
|
45
|
+
|
|
46
|
+
urls = upload("photo.jpg")
|
|
47
|
+
print(urls[0])
|
|
48
|
+
# https://files.tgvibes.online/AbCdEfGh.jpg
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Async upload (httpx)
|
|
52
|
+
```python
|
|
53
|
+
from meowbox import upload_async
|
|
54
|
+
|
|
55
|
+
urls = await upload_async("photo.jpg")
|
|
56
|
+
print(urls[0])
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Class-based
|
|
60
|
+
```python
|
|
61
|
+
from meowbox import MeowBox
|
|
62
|
+
|
|
63
|
+
mb = MeowBox()
|
|
64
|
+
|
|
65
|
+
# Sync
|
|
66
|
+
urls = mb.upload("photo.jpg")
|
|
67
|
+
|
|
68
|
+
# Async
|
|
69
|
+
urls = await mb.upload_async("photo.jpg")
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Upload multiple files
|
|
73
|
+
```python
|
|
74
|
+
from meowbox import upload
|
|
75
|
+
|
|
76
|
+
urls = upload(["file1.jpg", "file2.mp4", "file3.pdf"])
|
|
77
|
+
for url in urls:
|
|
78
|
+
print(url)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Upload file object (e.g. Telegram Bot)
|
|
82
|
+
```python
|
|
83
|
+
from meowbox import upload_async
|
|
84
|
+
|
|
85
|
+
path = await message.download()
|
|
86
|
+
urls = await upload_async(path)
|
|
87
|
+
await message.reply(urls[0])
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Telegram Bot Example
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
from meowbox import upload_async
|
|
96
|
+
import os
|
|
97
|
+
|
|
98
|
+
async def upload_handler(message):
|
|
99
|
+
if not message.reply_to_message or not message.reply_to_message.media:
|
|
100
|
+
await message.reply("Reply to a file!")
|
|
101
|
+
return
|
|
102
|
+
|
|
103
|
+
path = await message.reply_to_message.download()
|
|
104
|
+
try:
|
|
105
|
+
urls = await upload_async(path)
|
|
106
|
+
await message.reply(f"✅ {urls[0]}")
|
|
107
|
+
except Exception as e:
|
|
108
|
+
await message.reply(f"❌ Upload failed: {e}")
|
|
109
|
+
finally:
|
|
110
|
+
if os.path.exists(path):
|
|
111
|
+
os.remove(path)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Exceptions
|
|
117
|
+
|
|
118
|
+
| Exception | When |
|
|
119
|
+
|-----------|------|
|
|
120
|
+
| `UploadError` | Upload failed (server error) |
|
|
121
|
+
| `RateLimitError` | Too many uploads |
|
|
122
|
+
| `MeowBoxException` | Base exception |
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
from meowbox import upload
|
|
126
|
+
from meowbox.exceptions import UploadError, RateLimitError
|
|
127
|
+
|
|
128
|
+
try:
|
|
129
|
+
urls = upload("file.jpg")
|
|
130
|
+
except RateLimitError as e:
|
|
131
|
+
print(f"Slow down! Retry after {e.retry_after}s")
|
|
132
|
+
except UploadError as e:
|
|
133
|
+
print(f"Upload failed: {e}")
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## API Reference
|
|
139
|
+
|
|
140
|
+
### `upload(f, base_url=...) -> list[str]`
|
|
141
|
+
Sync upload. `f` = path, file object, or list of either.
|
|
142
|
+
|
|
143
|
+
### `upload_async(f, base_url=...) -> list[str]`
|
|
144
|
+
Async upload using httpx.
|
|
145
|
+
|
|
146
|
+
### `MeowBox(base_url=...)`
|
|
147
|
+
Class with `.upload()` and `.upload_async()` methods.
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Links
|
|
152
|
+
|
|
153
|
+
- 🌐 Site: [files.tgvibes.online](https://files.tgvibes.online)
|
|
154
|
+
- 💬 Telegram: [@BadmundaXd](https://t.me/BadmundaXd)
|
|
155
|
+
- 📦 Repo: [github.com/Badmunda05/MeowBox](https://github.com/Badmunda05/MeowBox)
|
meowbox-1.0.0/README.md
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# MeowBox Python Library 📦
|
|
2
|
+
|
|
3
|
+
> **Permanent file hosting — files never expire.**
|
|
4
|
+
> By [@BadmundaXd](https://t.me/BadmundaXd) | [GitHub](https://github.com/Badmunda05/MeowBox)
|
|
5
|
+
|
|
6
|
+
Python library for uploading files to [MeowBox](https://files.tgvibes.online) — supports both sync and async.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install meowbox
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
### Sync upload
|
|
21
|
+
```python
|
|
22
|
+
from meowbox import upload
|
|
23
|
+
|
|
24
|
+
urls = upload("photo.jpg")
|
|
25
|
+
print(urls[0])
|
|
26
|
+
# https://files.tgvibes.online/AbCdEfGh.jpg
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Async upload (httpx)
|
|
30
|
+
```python
|
|
31
|
+
from meowbox import upload_async
|
|
32
|
+
|
|
33
|
+
urls = await upload_async("photo.jpg")
|
|
34
|
+
print(urls[0])
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Class-based
|
|
38
|
+
```python
|
|
39
|
+
from meowbox import MeowBox
|
|
40
|
+
|
|
41
|
+
mb = MeowBox()
|
|
42
|
+
|
|
43
|
+
# Sync
|
|
44
|
+
urls = mb.upload("photo.jpg")
|
|
45
|
+
|
|
46
|
+
# Async
|
|
47
|
+
urls = await mb.upload_async("photo.jpg")
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Upload multiple files
|
|
51
|
+
```python
|
|
52
|
+
from meowbox import upload
|
|
53
|
+
|
|
54
|
+
urls = upload(["file1.jpg", "file2.mp4", "file3.pdf"])
|
|
55
|
+
for url in urls:
|
|
56
|
+
print(url)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Upload file object (e.g. Telegram Bot)
|
|
60
|
+
```python
|
|
61
|
+
from meowbox import upload_async
|
|
62
|
+
|
|
63
|
+
path = await message.download()
|
|
64
|
+
urls = await upload_async(path)
|
|
65
|
+
await message.reply(urls[0])
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Telegram Bot Example
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
from meowbox import upload_async
|
|
74
|
+
import os
|
|
75
|
+
|
|
76
|
+
async def upload_handler(message):
|
|
77
|
+
if not message.reply_to_message or not message.reply_to_message.media:
|
|
78
|
+
await message.reply("Reply to a file!")
|
|
79
|
+
return
|
|
80
|
+
|
|
81
|
+
path = await message.reply_to_message.download()
|
|
82
|
+
try:
|
|
83
|
+
urls = await upload_async(path)
|
|
84
|
+
await message.reply(f"✅ {urls[0]}")
|
|
85
|
+
except Exception as e:
|
|
86
|
+
await message.reply(f"❌ Upload failed: {e}")
|
|
87
|
+
finally:
|
|
88
|
+
if os.path.exists(path):
|
|
89
|
+
os.remove(path)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Exceptions
|
|
95
|
+
|
|
96
|
+
| Exception | When |
|
|
97
|
+
|-----------|------|
|
|
98
|
+
| `UploadError` | Upload failed (server error) |
|
|
99
|
+
| `RateLimitError` | Too many uploads |
|
|
100
|
+
| `MeowBoxException` | Base exception |
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
from meowbox import upload
|
|
104
|
+
from meowbox.exceptions import UploadError, RateLimitError
|
|
105
|
+
|
|
106
|
+
try:
|
|
107
|
+
urls = upload("file.jpg")
|
|
108
|
+
except RateLimitError as e:
|
|
109
|
+
print(f"Slow down! Retry after {e.retry_after}s")
|
|
110
|
+
except UploadError as e:
|
|
111
|
+
print(f"Upload failed: {e}")
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## API Reference
|
|
117
|
+
|
|
118
|
+
### `upload(f, base_url=...) -> list[str]`
|
|
119
|
+
Sync upload. `f` = path, file object, or list of either.
|
|
120
|
+
|
|
121
|
+
### `upload_async(f, base_url=...) -> list[str]`
|
|
122
|
+
Async upload using httpx.
|
|
123
|
+
|
|
124
|
+
### `MeowBox(base_url=...)`
|
|
125
|
+
Class with `.upload()` and `.upload_async()` methods.
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Links
|
|
130
|
+
|
|
131
|
+
- 🌐 Site: [files.tgvibes.online](https://files.tgvibes.online)
|
|
132
|
+
- 💬 Telegram: [@BadmundaXd](https://t.me/BadmundaXd)
|
|
133
|
+
- 📦 Repo: [github.com/Badmunda05/MeowBox](https://github.com/Badmunda05/MeowBox)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
MeowBox — Permanent File Hosting Python Library
|
|
4
|
+
Repo : https://github.com/Badmunda05/MeowBox
|
|
5
|
+
Author : @BadmundaXd
|
|
6
|
+
|
|
7
|
+
Quick start:
|
|
8
|
+
from meowbox import upload
|
|
9
|
+
urls = upload("photo.jpg")
|
|
10
|
+
print(urls[0])
|
|
11
|
+
|
|
12
|
+
Async:
|
|
13
|
+
from meowbox import MeowBox
|
|
14
|
+
mb = MeowBox()
|
|
15
|
+
urls = await mb.upload_async("photo.jpg")
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
__version__ = "1.0.0"
|
|
19
|
+
__author__ = "@BadmundaXd"
|
|
20
|
+
__repo__ = "https://github.com/Badmunda05/MeowBox"
|
|
21
|
+
|
|
22
|
+
from .meowbox import MeowBox, upload
|
|
23
|
+
from .meowbox_async import upload_async
|
|
24
|
+
from .exceptions import MeowBoxException, UploadError, RateLimitError, DeleteError
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"MeowBox",
|
|
28
|
+
"upload",
|
|
29
|
+
"upload_async",
|
|
30
|
+
"MeowBoxException",
|
|
31
|
+
"UploadError",
|
|
32
|
+
"RateLimitError",
|
|
33
|
+
"DeleteError",
|
|
34
|
+
]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
class MeowBoxException(Exception):
|
|
2
|
+
pass
|
|
3
|
+
|
|
4
|
+
class RateLimitError(MeowBoxException):
|
|
5
|
+
def __init__(self, retry_after: int = 60):
|
|
6
|
+
self.retry_after = retry_after
|
|
7
|
+
super().__init__(f"Rate limit hit. Retry after {retry_after} seconds.")
|
|
8
|
+
|
|
9
|
+
class UploadError(MeowBoxException):
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
class DeleteError(MeowBoxException):
|
|
13
|
+
pass
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
MeowBox — Permanent File Hosting
|
|
4
|
+
Repo : https://github.com/Badmunda05/MeowBox
|
|
5
|
+
Author : @BadmundaXd <munda.bad1322@gmail.com>
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
import requests
|
|
10
|
+
from .exceptions import MeowBoxException, UploadError, RateLimitError, DeleteError
|
|
11
|
+
|
|
12
|
+
MEOWBOX_URL = "https://files.tgvibes.online/upload"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# ─────────────────────────────────────────────
|
|
16
|
+
# MeowBox Class (sync)
|
|
17
|
+
# ─────────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
class MeowBox:
|
|
20
|
+
"""
|
|
21
|
+
MeowBox uploader — sync + async support.
|
|
22
|
+
|
|
23
|
+
Usage (sync):
|
|
24
|
+
from meowbox import MeowBox
|
|
25
|
+
mb = MeowBox()
|
|
26
|
+
urls = mb.upload("photo.jpg")
|
|
27
|
+
print(urls[0])
|
|
28
|
+
|
|
29
|
+
Usage (async):
|
|
30
|
+
from meowbox import MeowBox
|
|
31
|
+
mb = MeowBox()
|
|
32
|
+
urls = await mb.upload_async("photo.jpg")
|
|
33
|
+
print(urls[0])
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(self, base_url: str = MEOWBOX_URL):
|
|
37
|
+
self.base_url = base_url
|
|
38
|
+
|
|
39
|
+
def upload(self, f) -> list:
|
|
40
|
+
"""Upload file(s) to MeowBox. Returns list of direct URLs."""
|
|
41
|
+
return upload(f, base_url=self.base_url)
|
|
42
|
+
|
|
43
|
+
async def upload_async(self, f) -> list:
|
|
44
|
+
"""Async upload file(s) to MeowBox. Returns list of direct URLs."""
|
|
45
|
+
from .meowbox_async import upload_async
|
|
46
|
+
return await upload_async(f, base_url=self.base_url)
|
|
47
|
+
|
|
48
|
+
def __repr__(self):
|
|
49
|
+
return f"MeowBox(url={self.base_url!r})"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# ─────────────────────────────────────────────
|
|
53
|
+
# Sync functions
|
|
54
|
+
# ─────────────────────────────────────────────
|
|
55
|
+
|
|
56
|
+
def upload(f, base_url: str = MEOWBOX_URL) -> list:
|
|
57
|
+
"""
|
|
58
|
+
Upload file(s) to MeowBox (sync).
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
f: File path (str), file object, or list of either.
|
|
62
|
+
base_url: MeowBox upload endpoint.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
list of direct URLs (str).
|
|
66
|
+
|
|
67
|
+
Raises:
|
|
68
|
+
UploadError: If upload fails.
|
|
69
|
+
RateLimitError: If rate limited.
|
|
70
|
+
|
|
71
|
+
Example:
|
|
72
|
+
from meowbox import upload
|
|
73
|
+
urls = upload("photo.jpg")
|
|
74
|
+
print(urls[0]) # https://files.tgvibes.online/AbCdEfGh.jpg
|
|
75
|
+
"""
|
|
76
|
+
files_input = [f] if not isinstance(f, (list, tuple)) else list(f)
|
|
77
|
+
urls = []
|
|
78
|
+
|
|
79
|
+
for item in files_input:
|
|
80
|
+
if isinstance(item, str):
|
|
81
|
+
filename = os.path.basename(item)
|
|
82
|
+
fobj = open(item, "rb")
|
|
83
|
+
opened = True
|
|
84
|
+
else:
|
|
85
|
+
filename = getattr(item, "name", "upload")
|
|
86
|
+
fobj = item
|
|
87
|
+
opened = False
|
|
88
|
+
|
|
89
|
+
try:
|
|
90
|
+
resp = requests.post(
|
|
91
|
+
base_url,
|
|
92
|
+
files={"files[]": (filename, fobj)},
|
|
93
|
+
timeout=120,
|
|
94
|
+
)
|
|
95
|
+
finally:
|
|
96
|
+
if opened:
|
|
97
|
+
fobj.close()
|
|
98
|
+
|
|
99
|
+
if resp.status_code == 429:
|
|
100
|
+
raise RateLimitError()
|
|
101
|
+
|
|
102
|
+
try:
|
|
103
|
+
data = resp.json()
|
|
104
|
+
except Exception:
|
|
105
|
+
raise UploadError(f"Invalid response (HTTP {resp.status_code}): {resp.text[:200]}")
|
|
106
|
+
|
|
107
|
+
if not data.get("success"):
|
|
108
|
+
raise UploadError(f"Upload failed: {data.get('description', 'Unknown error')}")
|
|
109
|
+
|
|
110
|
+
for file_info in data.get("files", []):
|
|
111
|
+
urls.append(file_info["url"])
|
|
112
|
+
|
|
113
|
+
return urls
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
MeowBox — Async uploader (httpx)
|
|
4
|
+
Repo : https://github.com/Badmunda05/MeowBox
|
|
5
|
+
Author : @BadmundaXd <munda.bad1322@gmail.com>
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
from .exceptions import UploadError, RateLimitError
|
|
10
|
+
|
|
11
|
+
MEOWBOX_URL = "https://files.tgvibes.online/upload"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
async def upload_async(f, base_url: str = MEOWBOX_URL) -> list:
|
|
15
|
+
"""
|
|
16
|
+
Async upload file(s) to MeowBox using httpx.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
f: File path (str), file object, or list of either.
|
|
20
|
+
base_url: MeowBox upload endpoint.
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
list of direct URLs (str).
|
|
24
|
+
|
|
25
|
+
Raises:
|
|
26
|
+
UploadError: If upload fails.
|
|
27
|
+
RateLimitError: If rate limited.
|
|
28
|
+
|
|
29
|
+
Example:
|
|
30
|
+
from meowbox.meowbox_async import upload_async
|
|
31
|
+
urls = await upload_async("photo.jpg")
|
|
32
|
+
print(urls[0]) # https://files.tgvibes.online/AbCdEfGh.jpg
|
|
33
|
+
"""
|
|
34
|
+
try:
|
|
35
|
+
import httpx
|
|
36
|
+
except ImportError:
|
|
37
|
+
raise ImportError("httpx is required: pip install httpx")
|
|
38
|
+
|
|
39
|
+
files_input = [f] if not isinstance(f, (list, tuple)) else list(f)
|
|
40
|
+
urls = []
|
|
41
|
+
|
|
42
|
+
async with httpx.AsyncClient(timeout=120) as client:
|
|
43
|
+
for item in files_input:
|
|
44
|
+
if isinstance(item, str):
|
|
45
|
+
filename = os.path.basename(item)
|
|
46
|
+
fobj = open(item, "rb")
|
|
47
|
+
opened = True
|
|
48
|
+
else:
|
|
49
|
+
filename = getattr(item, "name", "upload")
|
|
50
|
+
fobj = item
|
|
51
|
+
opened = False
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
resp = await client.post(
|
|
55
|
+
base_url,
|
|
56
|
+
files={"files[]": (filename, fobj)},
|
|
57
|
+
)
|
|
58
|
+
finally:
|
|
59
|
+
if opened:
|
|
60
|
+
fobj.close()
|
|
61
|
+
|
|
62
|
+
if resp.status_code == 429:
|
|
63
|
+
raise RateLimitError()
|
|
64
|
+
|
|
65
|
+
try:
|
|
66
|
+
data = resp.json()
|
|
67
|
+
except Exception:
|
|
68
|
+
raise UploadError(f"Invalid response (HTTP {resp.status_code}): {resp.text[:200]}")
|
|
69
|
+
|
|
70
|
+
if not data.get("success"):
|
|
71
|
+
raise UploadError(f"Upload failed: {data.get('description', 'Unknown error')}")
|
|
72
|
+
|
|
73
|
+
for file_info in data.get("files", []):
|
|
74
|
+
urls.append(file_info["url"])
|
|
75
|
+
|
|
76
|
+
return urls
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: meowbox
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: MeowBox — Permanent file hosting uploader (sync & async)
|
|
5
|
+
Author-email: BadmundaXd <munda.bad1322@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Badmunda05/MeowBox
|
|
8
|
+
Project-URL: Source, https://github.com/Badmunda05/MeowBox
|
|
9
|
+
Project-URL: Telegram, https://t.me/BadmundaXd
|
|
10
|
+
Keywords: meowbox,upload,file hosting,async,httpx
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Requires-Python: >=3.7
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: requests
|
|
18
|
+
Requires-Dist: httpx
|
|
19
|
+
Provides-Extra: async
|
|
20
|
+
Requires-Dist: httpx; extra == "async"
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
|
|
23
|
+
# MeowBox Python Library 📦
|
|
24
|
+
|
|
25
|
+
> **Permanent file hosting — files never expire.**
|
|
26
|
+
> By [@BadmundaXd](https://t.me/BadmundaXd) | [GitHub](https://github.com/Badmunda05/MeowBox)
|
|
27
|
+
|
|
28
|
+
Python library for uploading files to [MeowBox](https://files.tgvibes.online) — supports both sync and async.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Install
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install meowbox
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Quick Start
|
|
41
|
+
|
|
42
|
+
### Sync upload
|
|
43
|
+
```python
|
|
44
|
+
from meowbox import upload
|
|
45
|
+
|
|
46
|
+
urls = upload("photo.jpg")
|
|
47
|
+
print(urls[0])
|
|
48
|
+
# https://files.tgvibes.online/AbCdEfGh.jpg
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Async upload (httpx)
|
|
52
|
+
```python
|
|
53
|
+
from meowbox import upload_async
|
|
54
|
+
|
|
55
|
+
urls = await upload_async("photo.jpg")
|
|
56
|
+
print(urls[0])
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Class-based
|
|
60
|
+
```python
|
|
61
|
+
from meowbox import MeowBox
|
|
62
|
+
|
|
63
|
+
mb = MeowBox()
|
|
64
|
+
|
|
65
|
+
# Sync
|
|
66
|
+
urls = mb.upload("photo.jpg")
|
|
67
|
+
|
|
68
|
+
# Async
|
|
69
|
+
urls = await mb.upload_async("photo.jpg")
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Upload multiple files
|
|
73
|
+
```python
|
|
74
|
+
from meowbox import upload
|
|
75
|
+
|
|
76
|
+
urls = upload(["file1.jpg", "file2.mp4", "file3.pdf"])
|
|
77
|
+
for url in urls:
|
|
78
|
+
print(url)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Upload file object (e.g. Telegram Bot)
|
|
82
|
+
```python
|
|
83
|
+
from meowbox import upload_async
|
|
84
|
+
|
|
85
|
+
path = await message.download()
|
|
86
|
+
urls = await upload_async(path)
|
|
87
|
+
await message.reply(urls[0])
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Telegram Bot Example
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
from meowbox import upload_async
|
|
96
|
+
import os
|
|
97
|
+
|
|
98
|
+
async def upload_handler(message):
|
|
99
|
+
if not message.reply_to_message or not message.reply_to_message.media:
|
|
100
|
+
await message.reply("Reply to a file!")
|
|
101
|
+
return
|
|
102
|
+
|
|
103
|
+
path = await message.reply_to_message.download()
|
|
104
|
+
try:
|
|
105
|
+
urls = await upload_async(path)
|
|
106
|
+
await message.reply(f"✅ {urls[0]}")
|
|
107
|
+
except Exception as e:
|
|
108
|
+
await message.reply(f"❌ Upload failed: {e}")
|
|
109
|
+
finally:
|
|
110
|
+
if os.path.exists(path):
|
|
111
|
+
os.remove(path)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Exceptions
|
|
117
|
+
|
|
118
|
+
| Exception | When |
|
|
119
|
+
|-----------|------|
|
|
120
|
+
| `UploadError` | Upload failed (server error) |
|
|
121
|
+
| `RateLimitError` | Too many uploads |
|
|
122
|
+
| `MeowBoxException` | Base exception |
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
from meowbox import upload
|
|
126
|
+
from meowbox.exceptions import UploadError, RateLimitError
|
|
127
|
+
|
|
128
|
+
try:
|
|
129
|
+
urls = upload("file.jpg")
|
|
130
|
+
except RateLimitError as e:
|
|
131
|
+
print(f"Slow down! Retry after {e.retry_after}s")
|
|
132
|
+
except UploadError as e:
|
|
133
|
+
print(f"Upload failed: {e}")
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## API Reference
|
|
139
|
+
|
|
140
|
+
### `upload(f, base_url=...) -> list[str]`
|
|
141
|
+
Sync upload. `f` = path, file object, or list of either.
|
|
142
|
+
|
|
143
|
+
### `upload_async(f, base_url=...) -> list[str]`
|
|
144
|
+
Async upload using httpx.
|
|
145
|
+
|
|
146
|
+
### `MeowBox(base_url=...)`
|
|
147
|
+
Class with `.upload()` and `.upload_async()` methods.
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Links
|
|
152
|
+
|
|
153
|
+
- 🌐 Site: [files.tgvibes.online](https://files.tgvibes.online)
|
|
154
|
+
- 💬 Telegram: [@BadmundaXd](https://t.me/BadmundaXd)
|
|
155
|
+
- 📦 Repo: [github.com/Badmunda05/MeowBox](https://github.com/Badmunda05/MeowBox)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
meowbox/__init__.py
|
|
5
|
+
meowbox/exceptions.py
|
|
6
|
+
meowbox/meowbox.py
|
|
7
|
+
meowbox/meowbox_async.py
|
|
8
|
+
meowbox.egg-info/PKG-INFO
|
|
9
|
+
meowbox.egg-info/SOURCES.txt
|
|
10
|
+
meowbox.egg-info/dependency_links.txt
|
|
11
|
+
meowbox.egg-info/requires.txt
|
|
12
|
+
meowbox.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
meowbox
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "meowbox"
|
|
7
|
+
dynamic = ["version", "readme"]
|
|
8
|
+
description = "MeowBox — Permanent file hosting uploader (sync & async)"
|
|
9
|
+
requires-python = ">=3.7"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "BadmundaXd", email = "munda.bad1322@gmail.com" }
|
|
14
|
+
]
|
|
15
|
+
keywords = ["meowbox", "upload", "file hosting", "async", "httpx"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
"Programming Language :: Python",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
]
|
|
21
|
+
dependencies = ["requests", "httpx"]
|
|
22
|
+
|
|
23
|
+
[project.optional-dependencies]
|
|
24
|
+
async = ["httpx"]
|
|
25
|
+
|
|
26
|
+
[project.urls]
|
|
27
|
+
Homepage = "https://github.com/Badmunda05/MeowBox"
|
|
28
|
+
Source = "https://github.com/Badmunda05/MeowBox"
|
|
29
|
+
Telegram = "https://t.me/BadmundaXd"
|
|
30
|
+
|
|
31
|
+
[tool.setuptools]
|
|
32
|
+
packages = ["meowbox"]
|
|
33
|
+
|
|
34
|
+
[tool.setuptools.dynamic]
|
|
35
|
+
version = { attr = "meowbox.__version__" }
|
|
36
|
+
readme = { file = ["README.md"], content-type = "text/markdown" }
|
meowbox-1.0.0/setup.cfg
ADDED