Why I made it
This small project combines the lessons I learnt about ASCII animations in this other project I did for an ASCII art animated RickRoll and the use of beautifulsoup in another project for an ASCII-based basic web browser.
How it works
Here are each step it takes to go from a search query input to an ASCII animation:
- It first asks the user for a search query.
- Then, it sends a GET request to yandex searching for the query under the videos section,
- and uses
beautifulsoupto find all the.mp4video preview URLs. - With the urls now in a list, it randomly selects one of them and downloads the video from it.
- It uses
ffmpegto get the first frame of the video and turns it into ASCII art for the user to see. - It then checks how much time it took to do step 5 and adds that to the current time for a 1:1 timing,
- and repeats step 5 but with the updated time from how long it took to get the preceding frame until it reaches 10 seconds, which is the length of Yandex video previews.
Requirements
You need to have ffmpeg installed before running the script.
Simply enter this: sudo apt install ffmpeg
Code
Here is the python code:
import os
import re
import sys
import time
import random
import shutil
import requests
from ascii_magic import AsciiArt
from bs4 import BeautifulSoup
# -------------------- Dependency checks --------------------
def ensure_ffmpeg_installed():
"""
Ensure ffmpeg is available on the system.
Exit cleanly if it is missing.
"""
if shutil.which("ffmpeg") is None:
print("ffmpeg is not installed.")
print("Install it with:")
print(" sudo apt install ffmpeg")
sys.exit(1)
# -------------------- Network / parsing --------------------
def get_preview_from_yandex(url):
"""
Fetch a Yandex video search page and extract preview MP4 URLs.
:param url: Yandex video search URL
:return: One randomly selected preview video URL
"""
response = requests.get(url, timeout=10)
response.raise_for_status()
soup = BeautifulSoup(response.text, "html.parser")
previews = set(
re.findall(r"https://[^\"']+\.mp4", soup.prettify())
)
if not previews:
print("No preview videos found.")
sys.exit(1)
return random.choice(list(previews))
# -------------------- Media handling --------------------
def extract_frame(video_url, timestamp, output_path):
"""
Extract a single frame from a remote video using ffmpeg.
"""
command = (
f'ffmpeg -loglevel error -ss {timestamp:.3f} '
f'-i "{video_url}" -frames:v 1 "{output_path}" -y'
)
if os.system(command) != 0:
print("ffmpeg failed to extract frame.")
sys.exit(1)
def render_ascii(image_path):
"""
Convert an image to ASCII and render it to the terminal.
"""
art = AsciiArt.from_image(image_path)
art.to_terminal(
columns=os.get_terminal_size().columns,
width_ratio=2,
monochrome=False
)
# -------------------- Playback loop --------------------
def play_preview(video_url):
"""
Render frames from the video at a fixed frequency.
"""
currently_at = 0.0 # seconds into the video
while currently_at < 10.0:
start_time = time.perf_counter()
extract_frame(video_url, currently_at, "data.jpg")
try:
render_ascii("data.jpg")
except OSError as e:
print(f"Image rendering failed: {e}")
sys.exit(1)
finally:
if os.path.exists("data.jpg"):
os.remove("data.jpg")
elapsed = time.perf_counter() - start_time
currently_at += elapsed
# -------------------- Main entry --------------------
def main():
ensure_ffmpeg_installed()
search_query = input("Enter Yandex search query: ")
search_url = f"https://yandex.com/video/search?text={search_query}"
video_url = get_preview_from_yandex(search_url)
play_preview(video_url)
if __name__ == "__main__":
main()
Preview
Please tell me everything that comes to mind!
