tempspace-cli 1.2.0__py3-none-any.whl

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.

Potentially problematic release.


This version of tempspace-cli might be problematic. Click here for more details.

cli/__init__.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = "1.0.0"
cli/tempspace.py ADDED
@@ -0,0 +1,206 @@
1
+ import argparse
2
+ import requests
3
+ import os
4
+ import sys
5
+ import hashlib
6
+ import multiprocessing
7
+ from requests_toolbelt.multipart.encoder import MultipartEncoder, MultipartEncoderMonitor
8
+ import math
9
+ from rich.console import Console
10
+ from rich.panel import Panel
11
+ from rich.table import Table
12
+ from rich import box
13
+ import qrcode
14
+ from rich.prompt import Prompt, Confirm
15
+ from rich.progress import Progress, BarColumn, TextColumn, TransferSpeedColumn, TimeRemainingColumn
16
+ from rich.console import Group
17
+ from rich.live import Live
18
+
19
+ # Default configuration
20
+ DEFAULT_SERVER_URL = "https://tempspace.fly.dev/"
21
+ CHUNK_SIZE = 1024 * 1024 # 1MB
22
+
23
+ def parse_time(time_str: str) -> int:
24
+ """
25
+ Parse a time string (e.g., '7d', '24h', '360') into an integer number of hours.
26
+ Returns the number of hours as an integer, or None if parsing fails.
27
+ """
28
+ time_str = time_str.lower().strip()
29
+ if time_str.endswith('d'):
30
+ try:
31
+ days = int(time_str[:-1])
32
+ return days * 24
33
+ except ValueError:
34
+ return None
35
+ elif time_str.endswith('h'):
36
+ try:
37
+ return int(time_str[:-1])
38
+ except ValueError:
39
+ return None
40
+ else:
41
+ try:
42
+ return int(time_str)
43
+ except ValueError:
44
+ return None
45
+
46
+ def format_size(size_bytes: int) -> str:
47
+ """Converts a size in bytes to a human-readable format."""
48
+ if size_bytes == 0:
49
+ return "0B"
50
+ size_name = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
51
+ i = int(math.floor(math.log(size_bytes, 1024)))
52
+ p = math.pow(1024, i)
53
+ s = round(size_bytes / p, 2)
54
+ return f"{s} {size_name[i]}"
55
+
56
+ def main():
57
+ """Main function to handle argument parsing and file upload."""
58
+ console = Console()
59
+
60
+ # --- Header ---
61
+ console.print(Panel("[bold cyan]Tempspace File Uploader[/bold cyan]", expand=False, border_style="blue"))
62
+
63
+
64
+ parser = argparse.ArgumentParser(
65
+ description="Upload a file to Tempspace.",
66
+ formatter_class=argparse.RawTextHelpFormatter
67
+ )
68
+
69
+ parser.add_argument("filepath", nargs='?', default=None, help="The path to the file you want to upload.")
70
+ parser.add_argument("-t", "--time", type=str, default='24', help="Set the file's expiration time. Examples: '24h', '7d', '360' (hours).\nDefault: '24' (24 hours).")
71
+ parser.add_argument("-p", "--password", type=str, help="Protect the file with a password.")
72
+ parser.add_argument("--one-time", action="store_true", help="The file will be deleted after the first download.")
73
+ parser.add_argument("--url", type=str, default=os.environ.get("TEMPSPACE_URL", DEFAULT_SERVER_URL), help=f"The URL of the Tempspace server.\nCan also be set with the TEMPSPACE_URL environment variable.\nDefault: {DEFAULT_SERVER_URL}")
74
+ parser.add_argument("--qr", action="store_true", help="Display a QR code of the download link.")
75
+ parser.add_argument("--it", action="store_true", help="Enable interactive mode.")
76
+
77
+ args = parser.parse_args()
78
+
79
+ # --- Interactive Mode ---
80
+ if args.it:
81
+ args.filepath = Prompt.ask("Enter the path to the file you want to upload")
82
+ args.time = Prompt.ask("Set the file's expiration time (e.g., '24h', '7d')", default='24')
83
+ args.password = Prompt.ask("Protect the file with a password?", default=None, password=True)
84
+ args.one_time = Confirm.ask("Delete the file after the first download?", default=False)
85
+ args.qr = Confirm.ask("Display a QR code of the download link?", default=False)
86
+
87
+
88
+ # --- Validate Inputs ---
89
+ if not args.filepath or not os.path.isfile(args.filepath):
90
+ console.print(Panel(f"[bold red]Error:[/] File not found at '{args.filepath}'", title="[bold red]Error[/bold red]", border_style="red"))
91
+ sys.exit(1)
92
+
93
+ hours = parse_time(args.time)
94
+ if hours is None:
95
+ console.print(Panel(f"[bold red]Error:[/] Invalid time format '{args.time}'. Use formats like '24h', '7d', or '360'.", title="[bold red]Error[/bold red]", border_style="red"))
96
+ sys.exit(1)
97
+
98
+ # --- Display File Details ---
99
+ table = Table(title="File Details", show_header=False, box=box.ROUNDED, border_style="cyan")
100
+ table.add_column("Field", style="bold")
101
+ table.add_column("Value")
102
+ table.add_row("File Name", os.path.basename(args.filepath))
103
+ table.add_row("File Size", format_size(os.path.getsize(args.filepath)))
104
+ table.add_row("Expiration", f"{hours} hours")
105
+ table.add_row("Password", "[green]Yes[/green]" if args.password else "[red]No[/red]")
106
+ table.add_row("One-Time Download", "[green]Yes[/green]" if args.one_time else "[red]No[/red]")
107
+ console.print(table)
108
+
109
+
110
+ # --- Prepare Upload ---
111
+ upload_url = f"{args.url.rstrip('/')}"
112
+ filename = os.path.basename(args.filepath)
113
+ file_size = os.path.getsize(args.filepath)
114
+
115
+ # --- Chunked Upload ---
116
+ response = None
117
+ try:
118
+ # 1. Initiate Upload
119
+ initiate_response = requests.post(f"{upload_url}/upload/initiate")
120
+ initiate_response.raise_for_status()
121
+ upload_id = initiate_response.json()['upload_id']
122
+
123
+ # 2. Upload Chunks
124
+ progress = Progress(
125
+ TextColumn("[bold blue]{task.description}", justify="right"),
126
+ BarColumn(bar_width=None),
127
+ "[progress.percentage]{task.percentage:>3.1f}%", "•",
128
+ TransferSpeedColumn(), "•",
129
+ TimeRemainingColumn(),
130
+ )
131
+
132
+ with Live(Panel(progress, title="[cyan]Uploading[/cyan]", border_style="cyan", title_align="left")) as live:
133
+ task_id = progress.add_task(filename, total=file_size)
134
+ with open(args.filepath, 'rb') as f:
135
+ chunk_number = 0
136
+ while chunk := f.read(CHUNK_SIZE):
137
+ chunk_number += 1
138
+ chunk_data = {
139
+ 'upload_id': upload_id,
140
+ 'chunk_number': str(chunk_number)
141
+ }
142
+ files = {'file': (f'chunk_{chunk_number}', chunk, 'application/octet-stream')}
143
+
144
+ chunk_response = requests.post(
145
+ f"{upload_url}/upload/chunk",
146
+ data=chunk_data,
147
+ files=files
148
+ )
149
+ chunk_response.raise_for_status()
150
+ progress.update(task_id, advance=len(chunk))
151
+
152
+ # 3. Finalize Upload
153
+ console.print(Panel("[bold green]Finalizing upload...[/bold green]", border_style="green"))
154
+ finalize_data = {
155
+ 'upload_id': upload_id,
156
+ 'filename': filename,
157
+ 'hours': str(hours),
158
+ 'one_time': str(args.one_time).lower(),
159
+ }
160
+ if args.password:
161
+ finalize_data['password'] = args.password
162
+
163
+ response = requests.post(f"{upload_url}/upload/finalize", data=finalize_data)
164
+ response.raise_for_status()
165
+
166
+ except FileNotFoundError:
167
+ console.print(Panel(f"[bold red]Error:[/] The file '{args.filepath}' was not found.", title="[bold red]Error[/bold red]", border_style="red"))
168
+ sys.exit(1)
169
+ except requests.exceptions.RequestException as e:
170
+ error_message = str(e)
171
+ if e.response:
172
+ try:
173
+ error_message = e.response.json().get('detail', e.response.text)
174
+ except:
175
+ error_message = e.response.text
176
+ console.print(Panel(f"[bold red]An error occurred:[/] {error_message}", title="[bold red]Error[/bold red]", border_style="red"))
177
+ sys.exit(1)
178
+ except Exception as e:
179
+ console.print(Panel(f"[bold red]An unexpected error occurred:[/] {e}", title="[bold red]Error[/bold red]", border_style="red"))
180
+ sys.exit(1)
181
+
182
+ # --- Handle Response ---
183
+ if response is not None:
184
+ if response.status_code == 200:
185
+ download_link = response.text.strip()
186
+ success_panel = Panel(f"[bold green]Upload successful![/bold green]\n\nDownload Link: {download_link}",
187
+ title="[bold cyan]Success[/bold cyan]", border_style="green")
188
+ console.print(success_panel)
189
+
190
+ if args.qr:
191
+ qr = qrcode.QRCode()
192
+ qr.add_data(download_link)
193
+ qr.make(fit=True)
194
+ qr.print_ascii()
195
+ else:
196
+ try:
197
+ error_details = response.json()
198
+ error_message = error_details.get('detail', 'No details provided.')
199
+ except requests.exceptions.JSONDecodeError:
200
+ error_message = response.text
201
+ console.print(Panel(f"[bold red]Error:[/] Upload failed with status code {response.status_code}\n[red]Server message:[/] {error_message}", title="[bold red]Error[/bold red]", border_style="red"))
202
+ sys.exit(1)
203
+
204
+ if __name__ == "__main__":
205
+ multiprocessing.freeze_support()
206
+ main()
@@ -0,0 +1,108 @@
1
+ Metadata-Version: 2.4
2
+ Name: tempspace-cli
3
+ Version: 1.2.0
4
+ Summary: A command-line tool for uploading files to Tempspace.
5
+ Author-email: Tempspace <mcbplay1@gmail.com>
6
+ License: MIT License
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Operating System :: OS Independent
9
+ Requires-Python: >=3.7
10
+ Description-Content-Type: text/markdown
11
+ Requires-Dist: requests==2.31.0
12
+ Requires-Dist: requests-toolbelt==1.0.0
13
+ Requires-Dist: rich==13.7.1
14
+ Requires-Dist: qrcode==8.2
15
+
16
+ # 🚀 Tempspace
17
+
18
+ Tempspace is a terminal-style file sharing service that allows you to upload files and share them via a link, with features like password protection, one-time downloads, and automatic expiration.
19
+
20
+ ![Screenshot](https://i.imgur.com/your-screenshot.png)
21
+
22
+ ## ✨ Features
23
+
24
+ - **Multiple Upload Methods:** Use the web interface, cURL, or the official CLI tool.
25
+ - **Security:** Optional password protection, one-time downloads, and rate limiting.
26
+ - **Modern UI:** A terminal-inspired design with QR code generation and upload history.
27
+ - **File Management:** Support for large files, multiple expiry options, and automatic cleanup.
28
+
29
+ ## 🚀 Getting Started
30
+
31
+ ### Method 1: Using the Web Interface
32
+
33
+ The easiest way to use Tempspace is through the web interface.
34
+
35
+ 1. **Open** the service URL (e.g., `http://localhost:8000`) in your browser.
36
+ 2. **Drag and drop** a file or click to browse.
37
+ 3. **Configure** the expiry time and an optional password.
38
+ 4. **Upload** and share the generated link.
39
+
40
+ ### Method 2: Using cURL
41
+
42
+ You can use `cURL` to upload files directly from your terminal.
43
+
44
+ ```bash
45
+ # Basic upload
46
+ curl -F "file=@/path/to/file.txt" -F "hours=6" https://tempspace.fly.dev/upload
47
+
48
+ # With a password
49
+ curl -F "file=@/path/to/secret.txt" -F "password=secret123" https://tempspace.fly.dev/upload
50
+ ```
51
+
52
+ ### Method 3: Using the Official CLI Tool
53
+
54
+ For a more integrated terminal experience, you can install the official CLI tool from PyPI.
55
+
56
+ #### Installation
57
+ ```bash
58
+ pip install tempspace-cli
59
+ ```
60
+
61
+ #### Usage
62
+ ```bash
63
+ # Basic upload
64
+ tempspace /path/to/document.pdf -t 6h
65
+
66
+ # With a password
67
+ tempspace /path/to/secret.txt -p "mypassword"
68
+
69
+ # Point to a self-hosted server
70
+ tempspace /path/to/file.txt --url http://localhost:8000
71
+ ```
72
+
73
+ ## 🔧 Self-Hosting
74
+
75
+ You can run your own instance of Tempspace.
76
+
77
+ ### Installation
78
+ ```bash
79
+ # Clone the repository
80
+ git clone https://github.com/your-repo/tempspace.git
81
+ cd tempspace
82
+
83
+ # Install dependencies
84
+ pip install -r requirements.txt
85
+
86
+ # Configure
87
+ cp .env.example .env
88
+ nano .env # Change the admin password
89
+ ```
90
+
91
+ ### Running the Server
92
+ ```bash
93
+ python main.py
94
+ ```
95
+
96
+ The server will be available at `http://localhost:8000`.
97
+
98
+ ## 🛡️ Admin & Configuration
99
+
100
+ For advanced configuration, such as setting up a reverse proxy, managing admin endpoints, and more, please refer to the project's documentation or open an issue.
101
+
102
+ ## 🤝 Contributing
103
+
104
+ Contributions are welcome! Please fork the repository and submit a pull request.
105
+
106
+ ## 📝 License
107
+
108
+ This project is licensed under the MIT License.
@@ -0,0 +1,7 @@
1
+ cli/__init__.py,sha256=J-j-u0itpEFT6irdmWmixQqYMadNl1X91TxUmoiLHMI,22
2
+ cli/tempspace.py,sha256=iswFGSxqkYR2qvujJjm0wm8UUxBM89r9jKOgbqbbJnc,8821
3
+ tempspace_cli-1.2.0.dist-info/METADATA,sha256=0HY-QH_JkX8YyCNYicgvtwo9wPPSUlngIDQgqrhyfDE,3002
4
+ tempspace_cli-1.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
5
+ tempspace_cli-1.2.0.dist-info/entry_points.txt,sha256=DBBYsRS-fK4MSA3-4lrSPyi6oyHxoJLCII7c793nPtw,49
6
+ tempspace_cli-1.2.0.dist-info/top_level.txt,sha256=2ImG917oaVHlm0nP9oJE-Qrgs-fq_fGWgba2H1f8fpE,4
7
+ tempspace_cli-1.2.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ tempspace = cli.tempspace:main
@@ -0,0 +1 @@
1
+ cli