Skip to content

Commit

Permalink
Improve the video to ascii player
Browse files Browse the repository at this point in the history
  • Loading branch information
yorevs committed Jan 11, 2025
1 parent b96625d commit 447c5c7
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 27 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,4 @@ py-gradle-master/
build/
.venv/
venv
response_audio.mp3
Expand Down
Binary file added assets/images/robot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/videos/robot.mp4
Binary file not shown.
67 changes: 40 additions & 27 deletions src/demo/devel/animated_ascii.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import sys
import threading
import time
from os.path import dirname
from os.path import dirname, expandvars
import os
from pathlib import Path
from threading import Thread
Expand All @@ -30,7 +30,7 @@

DEFAULT_PALETTE = PALETTES[1]

VIDEO_DIR: Path = Path("/Users/hjunior/GIT-Repository/GitHub/askai/assets/videos")
VIDEO_DIR: Path = Path(expandvars("${HOME}/Movies"))
if not VIDEO_DIR.exists():
VIDEO_DIR.mkdir(parents=True, exist_ok=True)

Expand All @@ -39,7 +39,7 @@
DATA_PATH.mkdir(parents=True, exist_ok=True)


def frame_to_ascii(
def image_to_ascii(
frame_path: str,
width: int,
palette: str,
Expand All @@ -55,7 +55,7 @@ def frame_to_ascii(
num_chars = len(palette if not reverse else palette[::-1])
img: Image = Image.open(frame_path).convert("L")
aspect_ratio = img.height / img.width
new_height = int(width * aspect_ratio * 0.55)
new_height = int(width * aspect_ratio * 0.4)
img = img.resize((width, new_height), resample=Resampling.BILINEAR)
pixels = list(img.getdata())
ascii_str = "".join(palette[min(pixel * num_chars // 256, num_chars - 1)] for pixel in pixels)
Expand All @@ -75,7 +75,7 @@ def get_frames(frames_path: Path, width: int = 80, palette: str = DEFAULT_PALETT
ascii_frames: list[str] = []
for frame_file in sorted(os.listdir(frames_path)):
frame_path: str = os.path.join(frames_path, frame_file)
ascii_frame = frame_to_ascii(frame_path, width, palette, reverse)
ascii_frame = image_to_ascii(frame_path, width, palette, reverse)
ascii_frames.append(ascii_frame)

return ascii_frames
Expand All @@ -87,6 +87,7 @@ def extract_audio_and_video_frames(video_path: Path) -> Optional[tuple[Path, Pat
:return: A tuple containing the path to the extracted audio and a list of paths to the video frames, or None if
the extraction fails.
"""
assert video_path.exists(), f"Video path does not exist: {video_path}"
video_name, _ = os.path.splitext(os.path.basename(video_path))
frame_dir: Path = Path(os.path.join(DATA_PATH, video_name, 'frames'))
audio_dir: Path = Path(os.path.join(DATA_PATH, video_name, 'audio'))
Expand Down Expand Up @@ -119,21 +120,25 @@ def play_ascii_frames(ascii_frames: list[str], fps: int) -> None:
:return: None
:raises OSError: If unable to get terminal size.
"""
console = Console()
delay_ms: int = int(1000 / fps)
cols, _ = shutil.get_terminal_size()
for f in ascii_frames:
cols, rows = shutil.get_terminal_size()
cursor.write("%HOM%")
start_time = time.perf_counter() # Record the start time
for line in f.splitlines()[:cols]:
console.print(Text(line, justify="center"), end='')
cursor.write(f"%EL0%%EOL%")
for frame in ascii_frames:
start_time = print_ascii_image(frame)
end_time = time.perf_counter() # Record the end time
render_time: int = int((end_time - start_time) * 1000)
pause.milliseconds(delay_ms - render_time)


def print_ascii_image(image: str):
console = Console()
cursor.write("%HOM%")
cols, _ = shutil.get_terminal_size()
start_time = time.perf_counter() # Record the start time
for line in image.splitlines()[:cols]:
console.print(Text(line, justify="center"), end='')
cursor.write(f"%EL0%%EOL%")
return start_time


def play_video(ascii_frames: list[str], fps: int) -> Thread:
"""Plays a list of ASCII art frames as a video in a separate thread.
:param ascii_frames: List of ASCII art frames to display.
Expand All @@ -146,15 +151,17 @@ def play_video(ascii_frames: list[str], fps: int) -> Thread:
return thread


def play_audio(audio_path: str) -> Thread:
def play_audio(audio_path: str) -> Optional[Thread]:
"""Plays an audio file in a separate thread.
:param audio_path: Path to the audio file to be played.
:return: The thread running the audio playback.
"""
thread = threading.Thread(target=player.play_audio_file, args=(audio_path,))
thread.daemon = True
thread.start()
return thread
if os.path.exists(audio_path):
thread = threading.Thread(target=player.play_audio_file, args=(audio_path,))
thread.daemon = True
thread.start()
return thread
return None


def setup_terminal() -> None:
Expand Down Expand Up @@ -183,19 +190,25 @@ def play(video_name: str) -> None:
"""Plays a video in ASCII format with synchronized audio.
:param video_name: The name of the video file to play.
"""
cols, rows = shutil.get_terminal_size()
print(cols, rows, cols / rows)
exit()
# cols, rows = shutil.get_terminal_size()
# print(cols, rows, cols / rows)
# exit()
video_path: Path = Path(
os.path.join(VIDEO_DIR, video_name)
if not os.path.exists(expandvars(video_name)) else expandvars(video_name)
)
audio_path, frames_path = extract_audio_and_video_frames(video_path)
ascii_video = get_frames(frames_path, 74, PALETTES[1], True)
setup_terminal()
video_path: Path = Path(os.path.join(VIDEO_DIR, video_name))
audio_path, video_path = extract_audio_and_video_frames(video_path)
ascii_video = get_frames(video_path, 150, PALETTES[1], True)
thv = play_video(ascii_video, FPS)
tha = play_audio(audio_path)
thv.join()
tha.join()
if tha is not None:
tha.join()
cleanup()


if __name__ == '__main__':
play("AskAI-Trailer.mp4")
play("${DESKTOP}/robot.mp4")
# asc_img = image_to_ascii(expandvars("${DESKTOP}/robot.png"), 74, DEFAULT_PALETTE, True)
# print_ascii_image(asc_img)
Binary file removed src/demo/devel/response_audio.mp3
Binary file not shown.

0 comments on commit 447c5c7

Please sign in to comment.