As CODE: main.py

import os
import sys
import time
from typing import List

import matplotlib
matplotlib.use('Agg') # For non-interactive use.
import matplotlib.animation as animation
import matplotlib.lines as mlines
import matplotlib.pyplot as plt
from lrcx import LRCX
from moviepy.editor import VideoFileClip

# Read and parse the lyrics file.
def read_lyrics(file_path: str) -> List[dict]:
lrc = LRCX(file_path, with_meta=True).to_list()
return lrc

# Create the video from the given lyrics list.
def create_video(lyrics: List[dict], output_path: str):
lines = []
for line in lyrics:
if 'x' in line:
lines.append(line['x'])

# Define the dimensions of the figure and the aspect ratio.
fig = plt.figure(figsize=(1, 1))
plt.axis('off')

def update(frame: int):
if frame == 0:
current_line = 0
while lines[current_line] == 0:
current_line += 1
text = lyrics[current_line]['text']
plot_text(text, "g",

else:
current_line += 1
while lines[current_line] == 0:
current_line += 1
text = lyrics[current_line]['text']
plot_text(text, "b", "g")
plt.draw()

if current_line + 1 < len(lyrics):
next_line = lyrics[current_line + 1]['text']
plot_text(next_line, "g", "b")
plt.draw()

# Remove the last line from the display.
lines.pop(0)

if current_line > 0:
lines.

# Set the font properties.
plt.rcParams["font.family"] = "Arial"
plt.rcParams["font.size"] = 60

def plot_text(text: str, color1: str, color2: str):
nonlocal current_line
text_props = dict(color=color1, weight='bold')
next_text_props = dict(color=color2, weight='bold')

ax.text(0, 1 + current_line * 1.5, text, **text_props)
ax.text(0, 1 + (current_line + 1) *

_ = fig.canvas.manager.window.destroy()

# Create the video using FFmpeg.
from moviepy.editor import VideoFileClip
clip = VideoFileClip(output_path, audio=False)
clip.write_videofile("lyrics_video.mp4", fps=frame_rate, codec="mjpeg")

if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('-f', '--frame-rate', type=int, default=30)
parser.add_argument('input', type=argparse.FileType('r'), nargs='?

args = parser.parse_args()

# Read the lyrics file and store it in a list of dictionaries.
with open(args.input, "r") as f:
lyrics = []
for line in f:
if 'x' in line:
lyrics.append({'x': int(line.split('x')[0].strip()), 'text': line.split('x')[1].strip()})
else:
lyrics.append({'x': 0, 'text': line.strip()})

# Get the number of lines in the input file.
num_lines = len(lyrics)

# Calculate the
# Calculate the
gap_width = 2 # Horizontal space between two lines
line_spacing = 1.5 # Vertical space between two lines
x_offset = gap_width / 2 # Horizontal offset for the first line

fig, ax = plt.subplots()
fig.set_size_inches(8, num_lines * line_spacing)

# Play the lyrics animation and save it as a video.
def animate(i):
global current_line
current_line = i % num_lines
plt.cla()
plot_texts(x_offset, 0)

animation = FuncAnimation(fig, animate, frames=num_lines * 5, interval=int((1 / frame_rate) * 1000), repeat=True)
plt.show()
animation.save()

def plot_texts(x_offset, current_line):
global ax
text_props = dict(color="white", weight='bold')
next_text_props = dict(color="blue", weight='bold')

for line in range(current_line, current_line + 5):
if (line < num_lines):
x = x_offset + line * gap_width
ax.text(x,

num_lines * line_spacing - 2, lyrics[line % num_lines]['text'], fontsize=6, **text_props if line % 2 == 0 else **next_text_props)
ax.axis("off")

# Function to align text at bottom center and adjust line spacing.
def plot_texts(x_offset, current_line):
global ax
text_props = dict(color="white", weight='bold')
next_text_props = dict(color="blue", weight='bold')
for line in range(current_line, current_line + 5):
if (line < num_lines):
y = 1 if line == current_line else 2
x = x_offset + line * gap_width
ax.text(x, y * num_lines * line_spacing - 2, lyrics[line % num_lines]['text'], fontsize=6, **text_props if line % 2 == 0 else **next_text_props)
ax.axis("off")

if __name__ == '__main__':
parser = ArgumentParser()
parser.add_argument('--input', type=str, help='lyrics input file', default='sample.txt')
parser.add_argument('--frame-rate', type=int,

args = parser.parse_args()
with open(args.input, 'r') as file:
lines = file.readlines()
lyrics = [line.strip() for line in lines]

num_lines = len(lyrics)
frame_rate = args.frame_rate
if not frame_rate:
frame_rate = int(round(0.8 * num_lines))

plt.switch_ajax('FBTerminalFont')
animate_lyrics(num_lines, frame_rate)

correct syntax errors