Skip to content

Commit dfcb34e

Browse files
authored
Merge pull request #35 from bendsouza2/feature/lambda-handler
Feature/lambda handler
2 parents 3e66a37 + d915d48 commit dfcb34e

File tree

7 files changed

+154
-44
lines changed

7 files changed

+154
-44
lines changed

.github/workflows/python-unittest.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,4 @@ jobs:
3636
pytest -vvv
3737
- name: type check with mypy
3838
run: |
39-
mypy -v .
39+
mypy .

python/Dockerfile

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
FROM public.ecr.aws/lambda/python:3.10-arm64
2+
3+
WORKDIR /var/task
4+
5+
# Install system dependencies
6+
RUN yum update -y && \
7+
yum install -y wget gnupg gcc python3-devel mysql-devel mariadb-devel libsndfile ffmpeg && \
8+
yum clean all
9+
10+
# Install Python dependencies
11+
COPY requirements.txt /var/task/requirements.txt
12+
RUN pip install --no-cache-dir -r /var/task/requirements.txt
13+
14+
# Copy application code
15+
COPY . /var/task
16+
17+
CMD ["lambda_handler.lambda_handler"]

python/db_handler.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import os
2+
from typing import Dict
3+
4+
import MySQLdb
5+
6+
7+
def write_to_db(video_details: Dict[str, str]) -> None:
8+
"""
9+
Writes video metadata to a MySQL database using mysqlclient (MySQLdb).
10+
:param video_details: Dictionary containing the data to write to the DB
11+
"""
12+
connection = None
13+
try:
14+
connection = MySQLdb.connect(
15+
host=os.getenv("DB_HOST"),
16+
user=os.getenv("DB_USER"),
17+
passwd=os.getenv("DB_PASSWORD"),
18+
db=os.getenv("DB_NAME"),
19+
)
20+
cursor = connection.cursor()
21+
22+
sql_query = """
23+
INSERT INTO videos (video_id, word, sentence, translated_sentence, title, description, upload_time, thumbnail_url)
24+
VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
25+
ON DUPLICATE KEY UPDATE
26+
word = VALUES(word),
27+
sentence = VALUES(sentence),
28+
translated_sentence = VALUES(translated_sentence),
29+
title = VALUES(title),
30+
description = VALUES(description),
31+
upload_time = VALUES(upload_time),
32+
thumbnail_url = VALUES(thumbnail_url);
33+
"""
34+
cursor.execute(
35+
sql_query,
36+
(
37+
video_details["video_id"],
38+
video_details["word"],
39+
video_details["sentence"],
40+
video_details["translated_sentence"],
41+
video_details["title"],
42+
video_details["description"],
43+
video_details["upload_time"],
44+
video_details["thumbnail_url"],
45+
),
46+
)
47+
48+
connection.commit()
49+
50+
except MySQLdb.Error as e:
51+
print(f"Error while interacting with the database: {e}")
52+
raise
53+
54+
finally:
55+
if connection is not None:
56+
connection.close()

python/lambda_handler.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import os
2+
import logging
3+
import traceback
4+
5+
import MySQLdb
6+
7+
from python.main import process_video_and_upload
8+
from python.db_handler import write_to_db
9+
10+
logger = logging.getLogger()
11+
logger.setLevel(logging.INFO)
12+
13+
14+
def lambda_handler():
15+
"""
16+
Lambda entry point to process video, upload to YouTube, and write metadata to db.
17+
"""
18+
try:
19+
required_env_vars = ["DB_HOST", "DB_USER", "DB_PASSWORD", "DB_NAME"]
20+
missing_vars = [var for var in required_env_vars if not os.getenv(var)]
21+
if len(missing_vars) > 0:
22+
raise EnvironmentError(f"Missing required environment variables: {', '.join(missing_vars)}")
23+
24+
video_details = process_video_and_upload(db_write_function=write_to_db)
25+
26+
return {
27+
"statusCode": 200,
28+
"body": {
29+
"message": "Video processed and uploaded successfully",
30+
"video_details": video_details,
31+
},
32+
}
33+
34+
except EnvironmentError:
35+
logger.error(f"Environment Error: {traceback.format_exc()}")
36+
return {
37+
"statusCode": 400,
38+
"body": {
39+
"message": "Missing required environment variables",
40+
"error": traceback.format_exc(),
41+
},
42+
}
43+
44+
except MySQLdb.Error:
45+
logger.error(f"MySQL Error: {traceback.format_exc()}")
46+
return {
47+
"statusCode": 500,
48+
"body": {
49+
"message": "Database error occurred while processing video",
50+
"error": traceback.format_exc(),
51+
},
52+
}
53+
54+
except ValueError:
55+
logger.error(f"Value Error: {traceback.format_exc()}")
56+
return {
57+
"statusCode": 400,
58+
"body": {
59+
"message": "Invalid data error occurred during processing",
60+
"error": traceback.format_exc(),
61+
},
62+
}
63+

python/language_verification.py

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
from typing import Dict
55

66
import requests
7-
import spacy
87
import enchant
98

109
from python.constants import URLs
@@ -43,21 +42,6 @@ def lexical_test_real_word(self, word: str) -> bool:
4342
else:
4443
return True
4544

46-
def spacy_real_word(self, model: str, word: str) -> bool:
47-
"""
48-
Test a word is real using Spacy. For more info see https://spacy.io/
49-
:param model: The model to use to help identify the word
50-
:param word: The word to test
51-
:return: True if the word exists for the given language, False if not
52-
"""
53-
54-
if model is None:
55-
model = f"{self.language}_core_news_sm"
56-
57-
language = spacy.load(model)
58-
doc = language(word)
59-
return doc[0].is_alpha and not doc[0].is_stop
60-
6145
def enchant_real_word(self, word: str) -> bool:
6246
"""
6347
Test a word is real using enchant. For more info see https://pyenchant.github.io/pyenchant/install.html

python/main.py

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
from typing import Dict
1+
from typing import Dict, Callable, Optional
22
from datetime import datetime
33

44
from python.word_generator import Audio, ImageGenerator, VideoGenerator
55
from python.yt_uploader import YTConnector
66
from python.constants import Paths, LANGUAGE_TO_LEARN, NATIVE_LANGUAGE, Prompts
77

88

9-
def process_video_and_upload() -> Dict[str, str]:
9+
def process_video_and_upload(db_write_function: Optional[Callable[[Dict[str, str]], None]] = None) -> Dict[str, str]:
1010
"""
11-
Combines the main functionality of the project to generate audio and video for a random word
12-
:return: a dictionary with the ID of the uploaded video, and the word, sentence and translated sentence that the
13-
video is based on
11+
Combines the main functionality to generate audio and video for a random word and upload it to YouTube.
12+
Optionally writes metadata to a database using `db_write_function`.
13+
:param db_write_function: Write video metadata to a RDB
1414
"""
1515
audio_generator = Audio(
1616
word_list_path=Paths.WORD_LIST_PATH,
@@ -19,16 +19,8 @@ def process_video_and_upload() -> Dict[str, str]:
1919
cloud_storage=True,
2020
)
2121

22-
print(audio_generator.word)
23-
print(audio_generator.sentence)
24-
print(audio_generator.translated_sentence)
25-
2622
prompt = Prompts.IMAGE_GENERATOR + audio_generator.sentence
27-
28-
image_generator = ImageGenerator(
29-
prompts=prompt,
30-
cloud_storage=True,
31-
)
23+
image_generator = ImageGenerator(prompts=prompt, cloud_storage=True)
3224

3325
video_generator = VideoGenerator(
3426
word=audio_generator.word,
@@ -43,22 +35,17 @@ def process_video_and_upload() -> Dict[str, str]:
4335
video_filepath = video_generator.generate_video()
4436
video_metadata = video_generator.generate_video_metadata(language_code=LANGUAGE_TO_LEARN)
4537

46-
yt = YTConnector(
47-
credentials_env=True,
48-
cloud_storage=True,
49-
)
38+
yt = YTConnector(credentials_env=True, cloud_storage=True)
5039
upload_details = yt.upload_youtube_short(
5140
video_path=video_filepath,
52-
title=video_metadata["title"], # type: ignore[arg-type]
53-
description=video_metadata["description"], # type: ignore[arg-type]
54-
tags=video_metadata["tags"]
41+
title=str(video_metadata["title"]),
42+
description=str(video_metadata["description"]),
43+
tags=video_metadata["tags"],
5544
)
5645

5746
video_id = upload_details["id"]
58-
thumbnail_url = upload_details["snippet"]["thumbnails"]["default"]["url"]
5947
upload_time = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
6048

61-
print(video_id)
6249
response = {
6350
"video_id": video_id,
6451
"word": audio_generator.word,
@@ -67,6 +54,10 @@ def process_video_and_upload() -> Dict[str, str]:
6754
"title": video_metadata["title"],
6855
"description": video_metadata["description"],
6956
"upload_time": upload_time,
70-
"thumbnail_url": thumbnail_url
57+
"thumbnail_url": upload_details["snippet"]["thumbnails"]["default"]["url"],
7158
}
59+
60+
if db_write_function is True:
61+
db_write_function(response)
62+
7263
return response

requirements.txt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ google-api-core==2.11.1
33
openai==1.7.2
44
black==23.12.1
55
requests~=2.31.0
6-
types-requests==2.32.0.20240914
76
deep-translator==1.11.4
87
gTTS~=2.5.1
98
moviepy~=1.0.3
@@ -15,12 +14,12 @@ fastapi==0.114.0
1514
pydantic~=2.5.3
1615
botocore==1.35.7
1716
pyenchant==3.2.2
18-
spacy==3.7.6
1917
mypy==1.11.2
2018
python-dotenv==1.0.1
2119
google-auth==2.22.0
2220
google-auth-httplib2==0.1.0
2321
google-auth-oauthlib==1.2.1
2422
Django==5.1.3
2523
djangorestframework==3.15.2
26-
pillow==10.2.0
24+
pillow==10.2.0
25+
mysqlclient==2.1.1

0 commit comments

Comments
 (0)