-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
153 lines (114 loc) · 4.55 KB
/
Copy pathmain.py
File metadata and controls
153 lines (114 loc) · 4.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import math
import os
import time
import fpstimer
import numpy as np
import cv2
os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide"
from pygame import mixer
from moviepy.editor import VideoFileClip
CHARS = " .:-=+*#%@"
# you can edit these for your preference
MAX_FRAME_AREA = 12000
MAX_FPS = 30
# for when chars in cmd are not displayed as "perfect squares"
# for no extra scaling, make it equal to 1
W_SCALE_FACTOR = 1.8
# clear console command
clear_console = 'clear' if os.name == 'posix' else 'CLS'
def play_animation(vid: cv2.VideoCapture, dimensions: tuple, seconds=None):
fps = min(MAX_FPS, vid.get(cv2.CAP_PROP_FPS))
# number of frames to play, plays all if no duration specified
frame_count = vid.get(cv2.CAP_PROP_FRAME_COUNT) if not seconds else seconds*fps
length, height = dimensions
# match console size with dimensions of the animation
os.system(f'mode con: cols={length} lines={height}')
current_frame = 1
timer = fpstimer.FPSTimer(fps)
while True:
try:
ret, frame = vid.read() # gets next frame
if ret and current_frame <= frame_count:
if current_frame == 1:
mixer.music.play()
ascii_frame = render_ascii(frame, dimensions)
print(ascii_frame)
current_frame += 1
else:
break
# stalls each frame loop to help maintain frame rate
timer.sleep()
except KeyboardInterrupt:
mixer.music.pause()
os.system(clear_console)
if input("Animation paused. Continue? (y) ") == "y":
mixer.music.unpause()
continue
else:
break
mixer.music.stop()
os.system(clear_console)
print(f"animation has ended. ({round(current_frame/fps, 2)} secs)")
def load_audio(video_path):
vid_name = os.path.basename(video_path).split(".")[0]
audio_path = f"./audio/{vid_name}.mp3"
mixer.init()
mixer.music.set_volume(0.5)
# downloads audio if video's audio file is not found
if not os.path.isfile(audio_path):
video = VideoFileClip(video_path)
video.audio.write_audiofile(audio_path)
mixer.music.load(audio_path)
# extra calculations done here to speed up rendering
b = math.ceil(255/(len(CHARS)-1))
def render_ascii(frame, dimensions: tuple):
# resize frame >> apply grayscale
frame = cv2.resize(frame, dimensions)
frame = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)
# converts each pixel's grayscale value (0-255) to a character index
# appends each row of ascii characters to a list
ascii_rows = ["".join([CHARS[pixel//b] for pixel in ln]) for ln in frame]
return '\n'.join(ascii_rows)
def scale_frame_size(width, height, target_area):
w_to_h_ratio = (width*W_SCALE_FACTOR)/height
# scale to target area while mainting aspect ratio
scaled_width = math.sqrt((target_area*w_to_h_ratio))
scaled_height = math.sqrt((target_area/w_to_h_ratio))
# tuple of (width, height) as int
return (math.floor(scaled_width), math.floor(scaled_height))
def select_video():
vid_folder = "videos/"
videos = [ file for file in os.listdir(vid_folder) if file.endswith(".mp4")]
if not videos:
print(f"no videos found! in {videos}!")
exit()
elif len(videos) == 1:
return vid_folder + videos[0]
for i, v in enumerate(videos):
print(f"{i+1} - {v}")
while True:
index = input("respond with the # of the file you would like to play: ")
if index.isdigit() and int(index) in range(1, len(videos)+1):
return vid_folder + videos[int(index)-1]
def main():
vid_path = select_video()
load_audio(vid_path)
os.system(clear_console)
vid = cv2.VideoCapture(vid_path)
vid_width = vid.get(cv2.CAP_PROP_FRAME_WIDTH)
vid_height = vid.get(cv2.CAP_PROP_FRAME_HEIGHT)
size = scale_frame_size(vid_width, vid_height, MAX_FRAME_AREA)
fps = vid.get(cv2.CAP_PROP_FPS)
length = int(vid.get(cv2.CAP_PROP_FRAME_COUNT)/fps)
print(f"({vid_path}): length: {length} secs width, height {size} fps: {min(MAX_FPS, int(fps))}")
secs = input(f"how many seconds would you like to play? (or play the full video) ")
try:
secs = float(secs)
print(f"playing {secs} seconds of {vid_path}...")
except ValueError:
secs = None
print(f"no number specified, playing full video of {vid_path}...")
time.sleep(1)
play_animation(vid, size, secs)
if __name__ == "__main__":
main()