PDASC 0.1.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.
@@ -0,0 +1,383 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>ASCII Art Converter</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ body {
15
+ font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
16
+ background: #0a0a0a;
17
+ color: #e0e0e0;
18
+ display: flex;
19
+ height: 100vh;
20
+ overflow: hidden;
21
+ font-size: 16px;
22
+ }
23
+
24
+ .sidebar {
25
+ width: 320px;
26
+ background: #1a1a1a;
27
+ padding: 40px;
28
+ display: flex;
29
+ flex-direction: column;
30
+ gap: 32px;
31
+ box-shadow: 2px 0 20px rgba(0, 0, 0, 0.5);
32
+ }
33
+
34
+ h1 {
35
+ font-size: 28px;
36
+ color: #e0e0e0;
37
+ margin-bottom: 10px;
38
+ font-weight: 600;
39
+ }
40
+
41
+ .upload-section {
42
+ display: flex;
43
+ flex-direction: column;
44
+ gap: 12px;
45
+ }
46
+
47
+ .upload-btn {
48
+ background: #2a2a2a;
49
+ color: #e0e0e0;
50
+ padding: 16px 24px;
51
+ border: 1px solid #3a3a3a;
52
+ border-radius: 8px;
53
+ cursor: pointer;
54
+ font-size: 16px;
55
+ font-weight: 500;
56
+ transition: all 0.3s;
57
+ text-align: center;
58
+ text-justify: center;
59
+ }
60
+
61
+ .upload-btn:hover {
62
+ background: #333;
63
+ border-color: #4a4a4a;
64
+ }
65
+
66
+ input[type="file"] {
67
+ display: none;
68
+ }
69
+
70
+ .file-name {
71
+ font-size: 14px;
72
+ color: #888;
73
+ font-style: italic;
74
+ min-height: 20px;
75
+ }
76
+
77
+ .control-group {
78
+ display: flex;
79
+ flex-direction: column;
80
+ gap: 12px;
81
+ }
82
+
83
+ label {
84
+ font-size: 16px;
85
+ font-weight: 500;
86
+ color: #e0e0e0;
87
+ display: flex;
88
+ justify-content: space-between;
89
+ align-items: center;
90
+ }
91
+
92
+ .value-display {
93
+ color: #7cb3e9;
94
+ font-weight: 600;
95
+ font-size: 16px;
96
+ }
97
+
98
+ input[type="range"] {
99
+ width: 100%;
100
+ height: 8px;
101
+ background: #2a2a2a;
102
+ outline: none;
103
+ border-radius: 4px;
104
+ -webkit-appearance: none;
105
+ }
106
+
107
+ input[type="range"]::-webkit-slider-thumb {
108
+ -webkit-appearance: none;
109
+ appearance: none;
110
+ width: 20px;
111
+ height: 20px;
112
+ background: #7cb3e9;
113
+ cursor: pointer;
114
+ border-radius: 50%;
115
+ transition: background 0.3s;
116
+ }
117
+
118
+ input[type="range"]::-webkit-slider-thumb:hover {
119
+ background: #5a9bd5;
120
+ }
121
+
122
+ input[type="range"]::-moz-range-thumb {
123
+ width: 20px;
124
+ height: 20px;
125
+ background: #7cb3e9;
126
+ cursor: pointer;
127
+ border-radius: 50%;
128
+ border: none;
129
+ }
130
+
131
+ .checkbox-container {
132
+ display: flex;
133
+ align-items: center;
134
+ gap: 12px;
135
+ cursor: pointer;
136
+ }
137
+
138
+ .checkbox-container span {
139
+ order: 1;
140
+ }
141
+
142
+ input[type="checkbox"] {
143
+ order: 2;
144
+ width: 22px;
145
+ height: 22px;
146
+ cursor: pointer;
147
+ accent-color: #7cb3e9;
148
+ }
149
+
150
+ .main-content {
151
+ flex: 1;
152
+ display: flex;
153
+ align-items: center;
154
+ justify-content: center;
155
+ padding: 50px;
156
+ overflow: hidden;
157
+ background: #0f0f0f;
158
+ position: relative;
159
+ transition: background 0.3s;
160
+ }
161
+
162
+ .main-content.drag-over {
163
+ background: #1a1a1a;
164
+ border: 3px dashed #7cb3e9;
165
+ }
166
+
167
+ #ascii-output {
168
+ line-height: 1;
169
+ font-family: "Courier New", monospace;
170
+ white-space: pre;
171
+ text-align: center;
172
+ transform-origin: center center;
173
+ visibility: hidden;
174
+ }
175
+
176
+ #ascii-output.visible {
177
+ visibility: visible;
178
+ }
179
+
180
+ .placeholder {
181
+ color: #4a4a4a;
182
+ font-size: 20px;
183
+ text-align: center;
184
+ }
185
+ </style>
186
+ </head>
187
+ <body>
188
+ <div class="sidebar">
189
+ <div>
190
+ <h1>ASCII Art Converter</h1>
191
+ </div>
192
+
193
+ <div class="upload-section">
194
+ <label for="file-input" class="upload-btn"> Upload Image </label>
195
+ <input type="file" id="file-input" accept="image/*" />
196
+ <div class="file-name" id="file-name"></div>
197
+ </div>
198
+
199
+ <div class="control-group">
200
+ <label>
201
+ ASCII Characters
202
+ <span class="value-display" id="num-ascii-value">8</span>
203
+ </label>
204
+ <input type="range" id="num-ascii" min="2" max="64" value="8" />
205
+ </div>
206
+
207
+ <div class="control-group">
208
+ <label>
209
+ Block Size
210
+ <span class="value-display" id="block-size-value">8</span>
211
+ </label>
212
+ <input type="range" id="block-size" min="2" max="32" value="8" />
213
+ </div>
214
+
215
+ <div class="control-group">
216
+ <label class="checkbox-container">
217
+ <span>Colored</span>
218
+ <input type="checkbox" id="colored" checked />
219
+ </label>
220
+ </div>
221
+
222
+ <button class="upload-btn" id="download-btn" style="display: none">
223
+ Download ASCII
224
+ </button>
225
+ </div>
226
+
227
+ <div class="main-content" id="drop-zone">
228
+ <div id="ascii-output" class="placeholder">
229
+ Upload an image to get started
230
+ </div>
231
+ </div>
232
+
233
+ <script>
234
+ let uploadedImage = null;
235
+
236
+ const fileInput = document.getElementById("file-input");
237
+ const fileName = document.getElementById("file-name");
238
+ const numAsciiSlider = document.getElementById("num-ascii");
239
+ const numAsciiValue = document.getElementById("num-ascii-value");
240
+ const blockSizeSlider = document.getElementById("block-size");
241
+ const blockSizeValue = document.getElementById("block-size-value");
242
+ const coloredCheckbox = document.getElementById("colored");
243
+ const asciiOutput = document.getElementById("ascii-output");
244
+ const dropZone = document.getElementById("drop-zone");
245
+ const downloadBtn = document.getElementById("download-btn");
246
+
247
+ // Drag and drop handlers
248
+ dropZone.addEventListener("dragover", (e) => {
249
+ e.preventDefault();
250
+ dropZone.classList.add("drag-over");
251
+ });
252
+
253
+ dropZone.addEventListener("dragleave", (e) => {
254
+ e.preventDefault();
255
+ dropZone.classList.remove("drag-over");
256
+ });
257
+
258
+ dropZone.addEventListener("drop", (e) => {
259
+ e.preventDefault();
260
+ dropZone.classList.remove("drag-over");
261
+
262
+ const file = e.dataTransfer.files[0];
263
+ if (file && file.type.startsWith("image/")) {
264
+ fileName.textContent = file.name;
265
+ uploadedImage = file;
266
+ generateAscii();
267
+ }
268
+ });
269
+
270
+ // Update slider value displays
271
+ numAsciiSlider.addEventListener("input", (e) => {
272
+ numAsciiValue.textContent = e.target.value;
273
+ if (uploadedImage) generateAscii();
274
+ });
275
+
276
+ blockSizeSlider.addEventListener("input", (e) => {
277
+ blockSizeValue.textContent = e.target.value;
278
+ if (uploadedImage) generateAscii();
279
+ });
280
+
281
+ coloredCheckbox.addEventListener("change", () => {
282
+ if (uploadedImage) generateAscii();
283
+ });
284
+
285
+ // Handle file upload
286
+ fileInput.addEventListener("change", (e) => {
287
+ const file = e.target.files[0];
288
+ if (file) {
289
+ fileName.textContent = file.name;
290
+ uploadedImage = file;
291
+ generateAscii();
292
+ }
293
+ });
294
+
295
+ // Download ASCII art
296
+ downloadBtn.addEventListener("click", () => {
297
+ const asciiText = asciiOutput.innerText;
298
+ const blob = new Blob([asciiText], { type: "text/plain" });
299
+ const url = URL.createObjectURL(blob);
300
+ const a = document.createElement("a");
301
+ a.href = url;
302
+ a.download = "ascii-art.txt";
303
+ document.body.appendChild(a);
304
+ a.click();
305
+ document.body.removeChild(a);
306
+ URL.revokeObjectURL(url);
307
+ });
308
+
309
+ // Scale ASCII to fit container
310
+ function scaleAsciiToFit() {
311
+ const container = document.querySelector(".main-content");
312
+ const output = asciiOutput;
313
+
314
+ if (output.classList.contains("placeholder")) return;
315
+
316
+ output.classList.remove("visible");
317
+
318
+ output.style.transform = "scale(1)";
319
+ output.style.fontSize = "10px";
320
+ void output.offsetHeight;
321
+
322
+ const containerWidth = container.clientWidth - 100;
323
+ const containerHeight = container.clientHeight - 100;
324
+ const outputWidth = output.scrollWidth;
325
+ const outputHeight = output.scrollHeight;
326
+
327
+ const scaleX = containerWidth / outputWidth;
328
+ const scaleY = containerHeight / outputHeight;
329
+
330
+ const scale = Math.min(scaleX, scaleY, 3);
331
+
332
+ output.style.transform = `scale(${scale})`;
333
+ output.classList.add("visible");
334
+ }
335
+
336
+ // Generate ASCII art
337
+ async function generateAscii() {
338
+ if (!uploadedImage) return;
339
+
340
+ // Hide output immediately
341
+ asciiOutput.classList.remove("visible");
342
+
343
+ const formData = new FormData();
344
+ formData.append("num_ascii", numAsciiSlider.value);
345
+ formData.append("block_size", blockSizeSlider.value);
346
+ formData.append("colored", coloredCheckbox.checked ? "1" : "0");
347
+ formData.append("image", uploadedImage);
348
+
349
+ try {
350
+ const response = await fetch("/ascii", {
351
+ method: "POST",
352
+ body: formData,
353
+ });
354
+ const html = await response.text();
355
+ asciiOutput.innerHTML = html;
356
+ asciiOutput.classList.remove("placeholder");
357
+
358
+ // Show download button
359
+ downloadBtn.style.display = "block";
360
+
361
+ // Force immediate scale calculation before showing
362
+ requestAnimationFrame(() => {
363
+ requestAnimationFrame(() => {
364
+ scaleAsciiToFit();
365
+ });
366
+ });
367
+ } catch (error) {
368
+ console.error("Error generating ASCII:", error);
369
+ asciiOutput.innerHTML =
370
+ '<span style="color: #ff6b6b;">Error generating ASCII art</span>';
371
+ asciiOutput.classList.add("visible");
372
+ }
373
+ }
374
+
375
+ // Rescale on window resize
376
+ window.addEventListener("resize", () => {
377
+ if (uploadedImage && !asciiOutput.classList.contains("placeholder")) {
378
+ scaleAsciiToFit();
379
+ }
380
+ });
381
+ </script>
382
+ </body>
383
+ </html>
@@ -0,0 +1 @@
1
+ from .app import create_video_server
@@ -0,0 +1,47 @@
1
+ from flask import Flask, render_template, send_file
2
+ from pathlib import Path
3
+
4
+
5
+ def create_video_server(video_path, title="ASCII Video Player", autoplay=False):
6
+ """
7
+ Create a Flask app that serves a video file.
8
+
9
+ Args:
10
+ video_path: Path to video file (str or Path)
11
+ title: Page title (default: "ASCII Video Player")
12
+ autoplay: Whether to autoplay video (default: False)
13
+
14
+ Returns:
15
+ Flask app instance
16
+
17
+ Example:
18
+ app = create_video_server("output.mp4")
19
+ app.run(host='0.0.0.0', port=5000)
20
+ """
21
+ video_path = Path(video_path).resolve()
22
+
23
+ if not video_path.exists():
24
+ raise FileNotFoundError(f"Video file not found: {video_path}")
25
+
26
+ app = Flask(__name__)
27
+
28
+ @app.route('/')
29
+ def index():
30
+ """Serve the video player page"""
31
+ return render_template(
32
+ "index.html",
33
+ title=title,
34
+ autoplay=autoplay
35
+ )
36
+
37
+ @app.route('/video')
38
+ def video():
39
+ """Serve the video file"""
40
+ return send_file(
41
+ video_path,
42
+ mimetype='video/mp4',
43
+ as_attachment=False,
44
+ conditional=True # Enable range requests for seeking
45
+ )
46
+
47
+ return app