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.
- core/__init__.py +10 -0
- core/ascii_converter.py +111 -0
- core/ascii_displayer.py +317 -0
- core/ascii_file_encoding.py +258 -0
- core/audio_player.py +150 -0
- core/generate_color_ramp.py +69 -0
- core/utils.py +26 -0
- core/video_ascii_video.py +220 -0
- core/video_extractor.py +128 -0
- pdasc/__init__.py +0 -0
- pdasc/fonts/CascadiaMono.ttf +0 -0
- pdasc/fonts/font8x8.ttf +0 -0
- pdasc/main.py +296 -0
- pdasc-0.1.0.dist-info/METADATA +16 -0
- pdasc-0.1.0.dist-info/RECORD +26 -0
- pdasc-0.1.0.dist-info/WHEEL +5 -0
- pdasc-0.1.0.dist-info/entry_points.txt +2 -0
- pdasc-0.1.0.dist-info/licenses/LICENSE +21 -0
- pdasc-0.1.0.dist-info/top_level.txt +3 -0
- web/__init__.py +4 -0
- web/image_controller/__init__.py +1 -0
- web/image_controller/app.py +99 -0
- web/image_controller/templates/index.html +383 -0
- web/video_player/__init__.py +1 -0
- web/video_player/app.py +47 -0
- web/video_player/templates/index.html +296 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>ASCII Video Player</title>
|
|
5
|
+
<meta charset="UTF-8" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<style>
|
|
8
|
+
* {
|
|
9
|
+
margin: 0;
|
|
10
|
+
padding: 0;
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
body {
|
|
15
|
+
background: #000;
|
|
16
|
+
display: flex;
|
|
17
|
+
justify-content: center;
|
|
18
|
+
align-items: center;
|
|
19
|
+
min-height: 100vh;
|
|
20
|
+
font-family: "Courier New", monospace;
|
|
21
|
+
overflow: hidden;
|
|
22
|
+
padding: 5px;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.player-container {
|
|
26
|
+
display: flex;
|
|
27
|
+
align-items: center;
|
|
28
|
+
justify-content: center;
|
|
29
|
+
width: 100vw;
|
|
30
|
+
height: 100vh;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.video-controls-wrapper {
|
|
34
|
+
display: flex;
|
|
35
|
+
flex-direction: column;
|
|
36
|
+
align-items: center;
|
|
37
|
+
gap: 0px;
|
|
38
|
+
max-height: 99vh;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.video-wrapper {
|
|
42
|
+
display: flex;
|
|
43
|
+
align-items: center;
|
|
44
|
+
justify-content: center;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
video {
|
|
48
|
+
width: 95vw;
|
|
49
|
+
max-height: calc(99vh - 55px);
|
|
50
|
+
object-fit: contain;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.controls {
|
|
54
|
+
display: flex;
|
|
55
|
+
align-items: center;
|
|
56
|
+
gap: 12px;
|
|
57
|
+
padding: 12px 16px;
|
|
58
|
+
background: #1a1a1a;
|
|
59
|
+
color: #fff;
|
|
60
|
+
flex-wrap: nowrap;
|
|
61
|
+
white-space: nowrap;
|
|
62
|
+
box-sizing: border-box;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
button {
|
|
66
|
+
background: #333;
|
|
67
|
+
color: #fff;
|
|
68
|
+
border: none;
|
|
69
|
+
padding: 8px 16px;
|
|
70
|
+
border-radius: 4px;
|
|
71
|
+
cursor: pointer;
|
|
72
|
+
font-family: "Courier New", monospace;
|
|
73
|
+
font-size: 14px;
|
|
74
|
+
transition: background 0.2s;
|
|
75
|
+
flex-shrink: 0;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
button:hover {
|
|
79
|
+
background: #555;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
button:active {
|
|
83
|
+
background: #666;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.progress-bar {
|
|
87
|
+
flex: 1;
|
|
88
|
+
min-width: 100px;
|
|
89
|
+
height: 8px;
|
|
90
|
+
background: #333;
|
|
91
|
+
border-radius: 4px;
|
|
92
|
+
cursor: pointer;
|
|
93
|
+
position: relative;
|
|
94
|
+
overflow: hidden;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.progress-fill {
|
|
98
|
+
height: 100%;
|
|
99
|
+
background: rgb(216, 18, 18);
|
|
100
|
+
width: 0%;
|
|
101
|
+
transition: width 0.1s linear;
|
|
102
|
+
border-radius: 4px;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.time-display {
|
|
106
|
+
font-size: 14px;
|
|
107
|
+
color: #ffffff;
|
|
108
|
+
min-width: 90px;
|
|
109
|
+
text-align: center;
|
|
110
|
+
flex-shrink: 0;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.volume-control {
|
|
114
|
+
display: flex;
|
|
115
|
+
align-items: center;
|
|
116
|
+
gap: 8px;
|
|
117
|
+
flex-shrink: 0;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
input[type="range"] {
|
|
121
|
+
width: 80px;
|
|
122
|
+
cursor: pointer;
|
|
123
|
+
}
|
|
124
|
+
</style>
|
|
125
|
+
</head>
|
|
126
|
+
<body>
|
|
127
|
+
<div class="player-container">
|
|
128
|
+
<div class="video-controls-wrapper">
|
|
129
|
+
<div class="video-wrapper">
|
|
130
|
+
<video id="video">
|
|
131
|
+
<source src="/video" type="video/mp4" />
|
|
132
|
+
Your browser doesn't support HTML5 video.
|
|
133
|
+
</video>
|
|
134
|
+
</div>
|
|
135
|
+
|
|
136
|
+
<div class="controls">
|
|
137
|
+
<button id="playPauseBtn">Play</button>
|
|
138
|
+
|
|
139
|
+
<div class="progress-bar" id="progressBar">
|
|
140
|
+
<div class="progress-fill" id="progressFill"></div>
|
|
141
|
+
</div>
|
|
142
|
+
|
|
143
|
+
<div class="time-display">
|
|
144
|
+
<span id="currentTime">0:00</span> /
|
|
145
|
+
<span id="duration">0:00</span>
|
|
146
|
+
</div>
|
|
147
|
+
|
|
148
|
+
<div class="volume-control">
|
|
149
|
+
<span>🔊</span>
|
|
150
|
+
<input
|
|
151
|
+
type="range"
|
|
152
|
+
id="volumeSlider"
|
|
153
|
+
min="0"
|
|
154
|
+
max="100"
|
|
155
|
+
value="100"
|
|
156
|
+
/>
|
|
157
|
+
</div>
|
|
158
|
+
|
|
159
|
+
<button id="fullscreenBtn">Fullscreen</button>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
</div>
|
|
163
|
+
|
|
164
|
+
<script>
|
|
165
|
+
const video = document.getElementById("video");
|
|
166
|
+
const playPauseBtn = document.getElementById("playPauseBtn");
|
|
167
|
+
const progressBar = document.getElementById("progressBar");
|
|
168
|
+
const progressFill = document.getElementById("progressFill");
|
|
169
|
+
const currentTimeSpan = document.getElementById("currentTime");
|
|
170
|
+
const durationSpan = document.getElementById("duration");
|
|
171
|
+
const volumeSlider = document.getElementById("volumeSlider");
|
|
172
|
+
const fullscreenBtn = document.getElementById("fullscreenBtn");
|
|
173
|
+
const controls = document.querySelector(".controls");
|
|
174
|
+
|
|
175
|
+
function formatTime(seconds) {
|
|
176
|
+
if (isNaN(seconds)) return "0:00";
|
|
177
|
+
const mins = Math.floor(seconds / 60);
|
|
178
|
+
const secs = Math.floor(seconds % 60);
|
|
179
|
+
return `${mins}:${secs.toString().padStart(2, "0")}`;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function updateControlsWidth() {
|
|
183
|
+
// Get video's natural aspect ratio
|
|
184
|
+
const videoAspect = video.videoWidth / video.videoHeight;
|
|
185
|
+
const containerAspect = video.offsetWidth / video.offsetHeight;
|
|
186
|
+
|
|
187
|
+
let actualWidth;
|
|
188
|
+
if (videoAspect > containerAspect) {
|
|
189
|
+
// Video is wider - uses full width
|
|
190
|
+
actualWidth = video.offsetWidth;
|
|
191
|
+
} else {
|
|
192
|
+
// Video is taller - width is constrained by height
|
|
193
|
+
actualWidth = video.offsetHeight * videoAspect;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
controls.style.width = actualWidth + "px";
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
video.addEventListener("loadedmetadata", () => {
|
|
200
|
+
durationSpan.textContent = formatTime(video.duration);
|
|
201
|
+
setTimeout(updateControlsWidth, 0);
|
|
202
|
+
console.log("Video loaded:", video.videoWidth, "x", video.videoHeight);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
video.addEventListener("error", (e) => {
|
|
206
|
+
console.error("Video error:", e);
|
|
207
|
+
alert("Failed to load video");
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
window.addEventListener("resize", updateControlsWidth);
|
|
211
|
+
|
|
212
|
+
video.addEventListener("timeupdate", () => {
|
|
213
|
+
const percent = (video.currentTime / video.duration) * 100;
|
|
214
|
+
progressFill.style.width = percent + "%";
|
|
215
|
+
currentTimeSpan.textContent = formatTime(video.currentTime);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
video.addEventListener("click", () => {
|
|
219
|
+
if (video.paused) {
|
|
220
|
+
video.play();
|
|
221
|
+
playPauseBtn.textContent = "Pause";
|
|
222
|
+
} else {
|
|
223
|
+
video.pause();
|
|
224
|
+
playPauseBtn.textContent = "Play";
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
playPauseBtn.addEventListener("click", () => {
|
|
229
|
+
if (video.paused) {
|
|
230
|
+
video.play();
|
|
231
|
+
playPauseBtn.textContent = "Pause";
|
|
232
|
+
} else {
|
|
233
|
+
video.pause();
|
|
234
|
+
playPauseBtn.textContent = "Play";
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
progressBar.addEventListener("click", (e) => {
|
|
239
|
+
const rect = progressBar.getBoundingClientRect();
|
|
240
|
+
const percent = (e.clientX - rect.left) / rect.width;
|
|
241
|
+
video.currentTime = percent * video.duration;
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
volumeSlider.addEventListener("input", (e) => {
|
|
245
|
+
video.volume = e.target.value / 100;
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
fullscreenBtn.addEventListener("click", () => {
|
|
249
|
+
if (!document.fullscreenElement) {
|
|
250
|
+
video.requestFullscreen();
|
|
251
|
+
} else {
|
|
252
|
+
document.exitFullscreen();
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
document.addEventListener("keydown", (e) => {
|
|
257
|
+
if (e.target.tagName === "INPUT") return;
|
|
258
|
+
|
|
259
|
+
switch (e.code) {
|
|
260
|
+
case "Space":
|
|
261
|
+
e.preventDefault();
|
|
262
|
+
playPauseBtn.click();
|
|
263
|
+
break;
|
|
264
|
+
case "ArrowLeft":
|
|
265
|
+
e.preventDefault();
|
|
266
|
+
video.currentTime = Math.max(0, video.currentTime - 5);
|
|
267
|
+
break;
|
|
268
|
+
case "ArrowRight":
|
|
269
|
+
e.preventDefault();
|
|
270
|
+
video.currentTime = Math.min(video.duration, video.currentTime + 5);
|
|
271
|
+
break;
|
|
272
|
+
case "KeyF":
|
|
273
|
+
e.preventDefault();
|
|
274
|
+
fullscreenBtn.click();
|
|
275
|
+
break;
|
|
276
|
+
case "KeyM":
|
|
277
|
+
e.preventDefault();
|
|
278
|
+
video.muted = !video.muted;
|
|
279
|
+
break;
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
video.addEventListener("ended", () => {
|
|
284
|
+
playPauseBtn.textContent = "Play";
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
video.addEventListener("play", () => {
|
|
288
|
+
playPauseBtn.textContent = "Pause";
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
video.addEventListener("pause", () => {
|
|
292
|
+
playPauseBtn.textContent = "Play";
|
|
293
|
+
});
|
|
294
|
+
</script>
|
|
295
|
+
</body>
|
|
296
|
+
</html>
|