Spaces:
Runtime error
Runtime error
Upload 16 files
Browse files- .env +1 -0
- .gitattributes +1 -0
- .gitignore +0 -0
- Dockerfile +26 -0
- Montserrat-Bold.ttf +0 -0
- agents.py +188 -0
- blog_post.docx +0 -0
- blog_post.md +54 -0
- packages.txt +2 -0
- requirements.txt +22 -0
- service_account.json +13 -0
- streamlit_app.py +61 -0
- token.json +1 -0
- tools.py +627 -0
- trained_agents_data.pkl +3 -0
- utils.py +217 -0
- video.mp4 +3 -0
.env
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
OPENAI_API_KEY = 'sk-proj-i6hkOW8E1LXn4N49xH3eT3BlbkFJUQbMtVJTTSHhcLAlAx14'
|
.gitattributes
CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
+
video.mp4 filter=lfs diff=lfs merge=lfs -text
|
.gitignore
ADDED
Binary file (114 Bytes). View file
|
|
Dockerfile
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Use an official Python runtime as a parent image
|
2 |
+
FROM python:3.10
|
3 |
+
|
4 |
+
# Install system dependencies
|
5 |
+
RUN apt-get update && apt-get install -y \
|
6 |
+
pandoc \
|
7 |
+
libgl1-mesa-glx \
|
8 |
+
&& rm -rf /var/lib/apt/lists/*
|
9 |
+
|
10 |
+
# Create a non-root user and switch to it
|
11 |
+
RUN useradd -m -u 1000 user
|
12 |
+
USER user
|
13 |
+
ENV PATH="/home/user/.local/bin:$PATH"
|
14 |
+
|
15 |
+
# Set the working directory in the container
|
16 |
+
WORKDIR /app
|
17 |
+
|
18 |
+
# Copy the requirements file and install dependencies
|
19 |
+
COPY --chown=user ./requirements.txt requirements.txt
|
20 |
+
RUN pip install --no-cache-dir --upgrade -r requirements.txt
|
21 |
+
|
22 |
+
# Copy the rest of the application files
|
23 |
+
COPY --chown=user . /app
|
24 |
+
|
25 |
+
# Define the command to run the Streamlit application
|
26 |
+
CMD ["streamlit", "run", "app.py", "--server.port", "8501", "--server.enableCORS", "false"]
|
Montserrat-Bold.ttf
ADDED
Binary file (29.6 kB). View file
|
|
agents.py
ADDED
@@ -0,0 +1,188 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from crewai import Agent, Task
|
2 |
+
from tools import scrape_website, generate_images_and_add_to_blog, generate_video
|
3 |
+
# from langchain_groq import ChatGroq
|
4 |
+
from langchain_openai import ChatOpenAI
|
5 |
+
from utils import post_image_and_text
|
6 |
+
|
7 |
+
def get_agents_and_tasks(is_token):
|
8 |
+
|
9 |
+
# llm = ChatGroq(model="llama3-70b-8192", api_key=groq_api_key)
|
10 |
+
llm = ChatOpenAI(model='gpt-4o-mini')
|
11 |
+
|
12 |
+
information_retriever_agent = Agent(
|
13 |
+
role="Web Information Retriever",
|
14 |
+
goal="To retieve all the information from the website and summarize the information.",
|
15 |
+
backstory="You are web information retriever agent. You are expert in scraping websites and summarizing the content.",
|
16 |
+
verbose=True,
|
17 |
+
llm=llm,
|
18 |
+
allow_delegation=False
|
19 |
+
)
|
20 |
+
|
21 |
+
task_scrape = Task(
|
22 |
+
description="Scrape all the informatin from the website: {website}.",
|
23 |
+
expected_output="Scraped information from the website: {website}",
|
24 |
+
agent=information_retriever_agent,
|
25 |
+
tools=[scrape_website]
|
26 |
+
)
|
27 |
+
|
28 |
+
task_summarize = Task(
|
29 |
+
description="provide a neet summary of the company. Do not add up things. ",
|
30 |
+
expected_output="Detailed summary of a company web page. "
|
31 |
+
# "Start with Company name, Mission and Vision, Leadership. "
|
32 |
+
# "Explain it's Products and Services, Market presence, Financial highlights, Recent Developments and Future plans. "
|
33 |
+
# "If there are not mentioned, explain they have mentioned."
|
34 |
+
"Do not add up things.",
|
35 |
+
agent=information_retriever_agent,
|
36 |
+
context=[task_scrape]
|
37 |
+
)
|
38 |
+
|
39 |
+
blog_agent = Agent(
|
40 |
+
role="Blog Writer",
|
41 |
+
goal="Create captivating blog that inspire and educate readers.",
|
42 |
+
backstory="You are a skilled blog writer for a company, creating insightful and engaging blog posts on various topics. You have a passion for sharing knowledge through writing. With years of experience in the industry, you know how to craft compelling narratives and provide valuable insights to your audience.",
|
43 |
+
verbose=True,
|
44 |
+
llm=llm,
|
45 |
+
allow_delegation=False
|
46 |
+
)
|
47 |
+
|
48 |
+
task_create_blog = Task(
|
49 |
+
description=
|
50 |
+
"Write a compelling blog on the topic '{topic}' for the company."
|
51 |
+
"Begin by explaining the topic, followed by an introduction to the company. Do not add up things. "
|
52 |
+
# "In the second half of blog, include a brief mention of the company to highlight its relevance to the topic. "
|
53 |
+
"The blog should cover various aspects relevant to {topic}, ensuring it provides comprehensive insights and value to the readers. "
|
54 |
+
"The blog should contain 2 image, so insert '<-IMAGE->' where image has to be inserted. One image should be after first paragraph. "
|
55 |
+
"The blog should not contain author details.",
|
56 |
+
expected_output="A full engaging and informative blog post about the topic: '{topic}'",
|
57 |
+
# output_file="topic_blog_post.md",
|
58 |
+
agent=blog_agent,
|
59 |
+
context=[task_summarize]
|
60 |
+
)
|
61 |
+
|
62 |
+
task_visual_prompts = Task(
|
63 |
+
description = "Replace <-IMAGE-> with prompts.There should be 2 images in the blog. "
|
64 |
+
"Every prompt should be enclosed in '<image> prompt </image>' tag. "
|
65 |
+
"The image should not contain any form of text, names of persons, company or company logo, etc. "
|
66 |
+
"Prompt is 'What you wish to see in the output image'. "
|
67 |
+
"A descriptive prompt that clearly defines elements, colors, and subjects will lead to better results. "
|
68 |
+
# "To control the weight of a given word use the format (word:weight), where word is the word you'd like to control the weight of and weight is a value between 0 and 1. "
|
69 |
+
"For example: The sky was a crisp (blue:0.3) and (green:0.8) would convey a sky that was blue and green, but more green than blue. The weight applies to all words in the prompt. ",
|
70 |
+
# "Image should not contain names of persons, company or company logo, etc. ",
|
71 |
+
expected_output= "A full blog with prompts enclosed in '<image> prompt </image>' tag.",
|
72 |
+
agent = blog_agent,
|
73 |
+
context = [task_create_blog]
|
74 |
+
)
|
75 |
+
|
76 |
+
task_add_images = Task(
|
77 |
+
description = "Generate images and add to blog using the provided tool. If image generation fails, stop the execution.",
|
78 |
+
expected_output= "A full blog with images.",
|
79 |
+
agent = blog_agent,
|
80 |
+
# output_file="blog_post.md",
|
81 |
+
tools = [generate_images_and_add_to_blog],
|
82 |
+
context = [task_visual_prompts]
|
83 |
+
)
|
84 |
+
|
85 |
+
content_creation_agent = Agent(
|
86 |
+
role="Content Creator",
|
87 |
+
goal="To generate accurate and engaging narration and image prompt pairs for video scripts and subsequently generate videos using these pairs.",
|
88 |
+
backstory="The agent is designed to assist in creating engaging video content by generating narrations and image prompts, and then compiling them into videos.",
|
89 |
+
verbose=True,
|
90 |
+
llm=llm,
|
91 |
+
allow_delegation=False
|
92 |
+
)
|
93 |
+
|
94 |
+
# task_summarize_blog = Task(
|
95 |
+
# description = "Summarize the blog into two to three paragraphs.",
|
96 |
+
# # focusing on the topic: '{topic}'. "
|
97 |
+
# # "Ensure the summary is concise yet comprehensive, capturing the essence of the '{topic}' and its significance. ",
|
98 |
+
# expected_output = "Two to three paragraphs of summary of the blog, mainly focussed on '{topic}'. ",
|
99 |
+
# agent = content_creation_agent,
|
100 |
+
# context = [task_visual_prompts]
|
101 |
+
# )
|
102 |
+
|
103 |
+
# task_video_script = Task(
|
104 |
+
# description = "Write a script for video about the topic: {topic}. Remember to use context as just reference, do not use context as script.",
|
105 |
+
# expected_output = "Two to three paragraphs of script for the video, mainly focussed on '{topic}'. ",
|
106 |
+
# agent = content_creation_agent,
|
107 |
+
# context = [task_visual_prompts]
|
108 |
+
# )
|
109 |
+
|
110 |
+
task_generate_narration_image_pairs = Task(
|
111 |
+
description = "Generate narration and image prompt pairs for video script about the topic: '{topic}'. The number of pairs are limited to two. Total words in narration should be less than 100."
|
112 |
+
"Image should not contain any form of text, names of persons, company or company logo, etc. "
|
113 |
+
"Prompt is 'What you wish to see in the output image'. "
|
114 |
+
"A descriptive prompt that clearly defines elements, colors, and subjects will lead to better results. "
|
115 |
+
"To control the weight of a given word use the format (word:weight), where word is the word you'd like to control the weight of and weight is a value between 0 and 1. "
|
116 |
+
"For example: The sky was a crisp (blue:0.3) and (green:0.8) would convey a sky that was blue and green, but more green than blue. The weight applies to all words in the prompt. ",
|
117 |
+
# "Image should not contain names of persons, company or company logo, etc. ",
|
118 |
+
expected_output="Pairs of sentences. Narrations are enclosed in <narration> narration here </narration> tag. Image prompts are enclosed in <image> image prompt here </image> tag.",
|
119 |
+
agent=content_creation_agent,
|
120 |
+
# context = [task_video_script]
|
121 |
+
)
|
122 |
+
|
123 |
+
task_generate_video = Task(
|
124 |
+
description="Generate video using narration and image prompt pairs. If image generation fails, stop the execution.",
|
125 |
+
expected_output="Path of the video",
|
126 |
+
agent=content_creation_agent,
|
127 |
+
context = [task_generate_narration_image_pairs],
|
128 |
+
tools=[generate_video]
|
129 |
+
)
|
130 |
+
|
131 |
+
LinkedInPosterAgent = Agent(
|
132 |
+
role="LinkedIn Poster",
|
133 |
+
goal="To post articles on LinkedIn",
|
134 |
+
backstory="This agent is responsible for automating the posting of articles on LinkedIn to keep the profile active and engaging.",
|
135 |
+
verbose=True,
|
136 |
+
llm=llm,
|
137 |
+
allow_delegation=False
|
138 |
+
)
|
139 |
+
|
140 |
+
BlogtoArticle = Task(
|
141 |
+
description="Convert the blog into engaging LinkedIn post of 150 words. "
|
142 |
+
"Make the post attractive using emojis and symbols ."
|
143 |
+
"Use one image from blog for LinkedIn post",
|
144 |
+
expected_output="A dictionary containing image_path, post_content. ",
|
145 |
+
agent=LinkedInPosterAgent,
|
146 |
+
context = [task_add_images]
|
147 |
+
)
|
148 |
+
|
149 |
+
PostArticleToLinkedIn = Task(
|
150 |
+
description="""post article on LinkedIn.
|
151 |
+
|
152 |
+
token:
|
153 |
+
|
154 |
+
{token}
|
155 |
+
|
156 |
+
""",
|
157 |
+
expected_output="A confirmation that the article was successfully posted on LinkedIn.",
|
158 |
+
agent=LinkedInPosterAgent,
|
159 |
+
context = [BlogtoArticle],
|
160 |
+
tools=[post_image_and_text]
|
161 |
+
)
|
162 |
+
|
163 |
+
agents = [
|
164 |
+
information_retriever_agent,
|
165 |
+
blog_agent,
|
166 |
+
content_creation_agent,
|
167 |
+
]
|
168 |
+
|
169 |
+
tasks = [
|
170 |
+
task_scrape,
|
171 |
+
task_summarize,
|
172 |
+
task_create_blog,
|
173 |
+
task_visual_prompts,
|
174 |
+
task_add_images,
|
175 |
+
# task_summarize_blog,
|
176 |
+
# task_video_script,
|
177 |
+
task_generate_narration_image_pairs,
|
178 |
+
task_generate_video,
|
179 |
+
|
180 |
+
]
|
181 |
+
|
182 |
+
if is_token:
|
183 |
+
agents.append(LinkedInPosterAgent)
|
184 |
+
tasks.append(BlogtoArticle)
|
185 |
+
tasks.append(PostArticleToLinkedIn)
|
186 |
+
|
187 |
+
return agents, tasks
|
188 |
+
|
blog_post.docx
ADDED
Binary file (762 kB). View file
|
|
blog_post.md
ADDED
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
**Marketing Case Study: Unveiling the Success Behind DIGIOTAI's Digital Transformation**
|
2 |
+
|
3 |
+
In today's rapidly evolving digital landscape, effective marketing strategies are paramount for businesses seeking to establish a competitive edge. One way to understand the intricacies and impact of marketing is through detailed case studies that provide actionable insights. This blog delves into a marketing case study, focusing on DIGIOTAI, a digital transformation (DX) enablement partner that empowers companies globally with advanced digital solutions.
|
4 |
+
|
5 |
+

|
6 |
+
|
7 |
+
**Introduction to DIGIOTAI**
|
8 |
+
|
9 |
+
DIGIOTAI stands as a beacon of innovation, facilitating digital transformation for reputed entities worldwide. With a mission to enable companies to thrive in the digital economy, DIGIOTAI offers a suite of services including IoT & Mobility, Data Science, Blockchain, AI/GenAI, AR/VR/MR, and Cloud Enablement. The company's prowess in delivering customized, integrated solutions has made it a trusted partner for businesses across various industries.
|
10 |
+
|
11 |
+
**Understanding the Marketing Case Study**
|
12 |
+
|
13 |
+
A marketing case study is a comprehensive analysis of how a company or brand implemented a marketing strategy to achieve its business objectives. It covers the challenges faced, the strategies employed, the execution process, and the results obtained. For DIGIOTAI, the marketing case study revolves around their successful campaigns and initiatives that have significantly impacted their market presence and client engagement.
|
14 |
+
|
15 |
+
**Key Aspects of DIGIOTAI's Marketing Strategy**
|
16 |
+
|
17 |
+
1. **Target Audience Identification:**
|
18 |
+
DIGIOTAI's marketing team meticulously identified their target audience, focusing on industries such as Manufacturing, Automotive, Logistics & Supply Chain, Energy & Utilities. This targeted approach ensured that their marketing efforts were directed towards businesses that would benefit the most from their services.
|
19 |
+
|
20 |
+
2. **Content Marketing:**
|
21 |
+
Leveraging rich content such as blogs, case studies, eBooks, and white papers, DIGIOTAI positioned itself as a thought leader in the digital transformation space. This not only educated their audience but also built trust and credibility.
|
22 |
+
|
23 |
+
3. **Use of Advanced Technologies:**
|
24 |
+
The integration of AI and Data Science in their marketing strategies enabled DIGIOTAI to analyze market trends, customer behavior, and campaign performance with precision. This data-driven approach allowed for continuous optimization of their marketing efforts.
|
25 |
+
|
26 |
+
4. **Strategic Partnerships:**
|
27 |
+
Forming alliances with other tech leaders and open-source communities, DIGIOTAI expanded its reach and enhanced its service offerings. These partnerships played a crucial role in amplifying their marketing message and establishing a robust network.
|
28 |
+
|
29 |
+
5. **Customer-Centric Approach:**
|
30 |
+
By focusing on delivering exceptional value and personalized experiences, DIGIOTAI ensured high customer satisfaction and loyalty. Their marketing campaigns highlighted real-world benefits and success stories, making their solutions relatable and attractive to potential clients.
|
31 |
+
|
32 |
+
**Results and Achievements**
|
33 |
+
|
34 |
+
The implementation of these marketing strategies yielded remarkable results for DIGIOTAI. They witnessed a significant increase in their client base, enhanced brand recognition, and improved market penetration. Their tailored solutions and innovative approach resonated well with their target audience, leading to successful digital transformation projects across various sectors.
|
35 |
+
|
36 |
+

|
37 |
+
|
38 |
+
**Conclusion**
|
39 |
+
|
40 |
+
The marketing case study of DIGIOTAI showcases the power of a well-crafted and executed marketing strategy in driving business success. By understanding their audience, leveraging advanced technologies, forming strategic partnerships, and maintaining a customer-centric approach, DIGIOTAI not only achieved its business goals but also set a benchmark in the digital transformation industry.
|
41 |
+
|
42 |
+
This case study serves as an inspiring example for other businesses aiming to navigate the complexities of the digital economy and underscores the importance of strategic marketing in achieving sustainable growth.
|
43 |
+
|
44 |
+
**Explore More with DIGIOTAI**
|
45 |
+
|
46 |
+
To learn more about DIGIOTAI's innovative solutions and how they can help your business thrive in the digital era, visit their website and explore their extensive range of services and resources. Stay updated with the latest trends and insights by subscribing to their newsletter.
|
47 |
+
|
48 |
+
**Join the Digital Transformation Journey with DIGIOTAI**
|
49 |
+
|
50 |
+
Embrace the future of business with DIGIOTAI's cutting-edge digital solutions. Connect with their team today and start your journey towards unparalleled digital excellence.
|
51 |
+
|
52 |
+
---
|
53 |
+
|
54 |
+
This comprehensive blog post aims to provide valuable insights into the marketing strategies employed by DIGIOTAI, offering readers a clear understanding of the methodologies that led to their success.
|
packages.txt
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
pandoc
|
2 |
+
libgl1-mesa-glx
|
requirements.txt
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
pysqlite3-binary
|
2 |
+
embedchain
|
3 |
+
crewai
|
4 |
+
crewai-tools
|
5 |
+
langchain-openai
|
6 |
+
streamlit
|
7 |
+
gtts
|
8 |
+
pydub
|
9 |
+
groq
|
10 |
+
Pillow
|
11 |
+
sendgrid
|
12 |
+
moviepy
|
13 |
+
requests
|
14 |
+
pypandoc
|
15 |
+
opencv-python-headless
|
16 |
+
opencv-python
|
17 |
+
numpy
|
18 |
+
openai
|
19 |
+
google-api-python-client
|
20 |
+
google-auth
|
21 |
+
google-auth-oauthlib
|
22 |
+
google-auth-httplib2
|
service_account.json
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"type": "service_account",
|
3 |
+
"project_id": "lithe-climber-430405-c1",
|
4 |
+
"private_key_id": "62057d717771d86af720216847c6e84c4b59660a",
|
5 |
+
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDNU0o022lo79iL\noyHzX7Q1GSQ+ZBje99sAvn4v1XE91zQVbtS+tzgnd4ixnyMZ1setkwo4FENOCS9+\nvhDt+MxbrVVFp2wNVyvyFi0/a2MZl9NRfBqDH6W+/zDgcTdHKCUDMRV77PBWESrm\n6rQKpWwNkQPFeJEDALESMvkDbhQ0mLfiEyDa8TNdhNDCvtl8QN5HLeQ8q9ZJXNml\nN8zFpSOSugr83tU6SO4neCixSS2g/02Hz4bjf5L5jzVVBF+tvxlly7rWMGhrL7Q5\nm/F/c8l2ArNZgLHlOG1OoND5HPY9aq9V2HCB55Hl17ChE974rGT0QTBvyQFCgLxS\nuFHpE/VpAgMBAAECggEAGl5ectBUxrm6rSArrYY2bfVHHs4bFZNuKDfOm9UFAISh\nNzlvD/kWuJbU0r7Vsq1PIn72OoJ7rEtospW1NeKUWBe4EkRwj3wI9EOd1v2zEefa\nJyZRf1Jypkc+nyxbBYPl0PWeRTaGHP+Ca3pr2cwpxso/Wj5kHScbFnO+YA7kgGZ7\nnIQQlCrLHcvIxA6OswnYrkzLGU1BxAml58iRtswggElLacWRys/8yubrjrN/djNj\nMjB81ymBlsgsiN21W8D/RU4u8NNIRGPyaeNCYeZne11AZmTmjMKeDMxIj/MD6bIT\nQkEOVLagRuImbi2G83/GAFdJdQnEh7EvV52+if/OwwKBgQDqIkj797j0RHVTcPwF\npceaUs338VmhZTC/nBbdSSiL8Av6HtHqvdFclB/IwiUsu5xNI++xAfI+SKGIVNYg\nAsQSwkzyICulorcbAQ+0o1XMBxI3RzzTXDleJ6dCGhZ6X4TGRJOdJ2OMGlHcVbBl\n/CA7IgKrTRHaSbW46rnbiKp56wKBgQDggD6LgW9Kp0aOfooX0My0z3MuAYzohZhP\nV7HyKr9RIf6C5YiOjlvPefTK488PhTMF0S964r4z0UDLVwMgZqOjhHjMuciQXNIe\nvSiCW0eSpfSkx4HvjFota095PO8JdtEu2RSBqXplaKgF5MuRp0iMLz+6R0jMnvtf\n2Qyd1aRE+wKBgD/Ma9rM28cjyFyeUu4vCD7TBXDkgSnraucrX0eZIcVP+dOEBmtS\nofP6INcRoBOaY7LZcfznrNyv6eQ+ScKbPlZmP5VSktIljoN7PI5aW4ym+J27eaWW\nYcN4RqlKdomN8Z4dHaQbEZMhsOC7ML/5fcbfM5799zlmEHB89XwOt3VVAoGAb30A\nkoMq46YCkg0hQwZ8QmarSnOWqHp/0xc31Y2JCR+apyKaGEF2Mqjb+k37rDd3yTHD\nJIGp9025ocGKWfLe3PuSigjRI3AVIRLLJUFzX85umc5CJtZKije6dfjetJJ++4bu\neh3SHL5GgvbGaYTrsEJeoYF5E7T4HPdLHq7ULzUCgYBAtNHW2+xWvVCg08O6fiJP\nPaFlMf+aW+F7x5eWdz+h1r/L3EqK/Wd+BFHq3oj/18Qe9zQQlU1S/FHGMmmTezod\ncxV6bb7uVOG6dY2XSzngB2mF43Zk7VVdro6V2NnnqZ9zBCciSzRI5UX3/R9Kuo/S\nvahSzKojgxdWAgtcAkW6LA==\n-----END PRIVATE KEY-----\n",
|
6 |
+
"client_email": "[email protected]",
|
7 |
+
"client_id": "106218391187943604877",
|
8 |
+
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
9 |
+
"token_uri": "https://oauth2.googleapis.com/token",
|
10 |
+
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
11 |
+
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/digiotai-ai%40lithe-climber-430405-c1.iam.gserviceaccount.com",
|
12 |
+
"universe_domain": "googleapis.com"
|
13 |
+
}
|
streamlit_app.py
ADDED
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from agents import get_agents_and_tasks
|
2 |
+
from crewai import Crew
|
3 |
+
from utils import send_email_with_company_details, post_image_and_text
|
4 |
+
import streamlit as st
|
5 |
+
from dotenv import load_dotenv
|
6 |
+
|
7 |
+
load_dotenv()
|
8 |
+
|
9 |
+
# groq_api_key = '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'
|
10 |
+
|
11 |
+
def generate_bolg_and_video(title, url, token):
|
12 |
+
|
13 |
+
if token is not None:
|
14 |
+
agents, tasks = get_agents_and_tasks(is_token=True)
|
15 |
+
else:
|
16 |
+
agents, tasks = get_agents_and_tasks(is_token=False)
|
17 |
+
|
18 |
+
print(agents, '\n', tasks)
|
19 |
+
|
20 |
+
crew = Crew(
|
21 |
+
agents=agents,
|
22 |
+
tasks= tasks,
|
23 |
+
verbose = 2,
|
24 |
+
)
|
25 |
+
|
26 |
+
pairs = crew.kickoff(inputs={'topic':title, 'website':url, 'token':token})
|
27 |
+
print(1)
|
28 |
+
|
29 |
+
return 'video.mp4'
|
30 |
+
|
31 |
+
st.title("Blog and Video Generator")
|
32 |
+
|
33 |
+
title = st.text_input("Title")
|
34 |
+
url = st.text_input("Website URL")
|
35 |
+
email = st.text_input("Email ID")
|
36 |
+
|
37 |
+
# token = 'AQXZFWxVIyE0IJ3vopgzsuG0t4uSg9lUnVwLOTLOkEOKIU9hhswEYXOpEzEveBFCZdcRP4B3vN8gd_HI920LTH5LFbO9TVkHbtn8P2qE_GcwBq_1LzGw-HwatIY3zU7auWhxCMVYAXsklAJx6FAa_Sx_MUtaVcnA42K1vhYxSS7s0ecQq0Thsdod1KrK2_nA0YjMc1lSnQQy1WDiK0HGN2-2jbDt13NpJTkmZqEWm6G9BRplTkUSeSSqGNuLEGpuY0hd50GcRovkcqpz9ZfvqkeiKhAYPPDTAGDX7HO5VjtHTui3ZCFEXvEbAHzng116xDfNnBE8-fsig7c9HP6c06UmmN6evA'
|
38 |
+
|
39 |
+
token = None
|
40 |
+
if st.checkbox(label='post on linkedIn'):
|
41 |
+
token = st.text_input('Enter you LinkedIn access token')
|
42 |
+
|
43 |
+
|
44 |
+
|
45 |
+
if st.button("Generate"):
|
46 |
+
if title and url and email:
|
47 |
+
|
48 |
+
st.success("Blog and Video will be sent to your email.")
|
49 |
+
video_path = generate_bolg_and_video(title, url, token)
|
50 |
+
# st.write(video_path)
|
51 |
+
|
52 |
+
# with open('blog_post.md', 'r', encoding='latin-1') as f:
|
53 |
+
# blog = f.read()
|
54 |
+
# st.markdown(blog)
|
55 |
+
# st.video(video_path)
|
56 |
+
|
57 |
+
send_email_with_company_details(email, 'DIGIOTAI SOLUTIONS', title)
|
58 |
+
|
59 |
+
|
60 |
+
else:
|
61 |
+
st.error("Please provide all inputs.")
|
token.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"token": "ya29.a0AXooCgtRt0YL19FiMHwf1UzqHNlOF0CEgQxJ-lbjy5UXqg-CsRhIx2HZmalHUZLtkGnfI8KB_eKxttDVz9y05HYhIaqQYawwHVfF2AJ9SeEP7f7C-QmSxEarLEQ4WQ1T-eNJijTFjlZvoRgoRt21FE3K6fsKM89odWntgAaCgYKAaoSARASFQHGX2Mit0KJz3qesS_Bb4IpffHDJg0173", "refresh_token": "1//0gh5ZW3LeAPZHCgYIARAAGBASNwF-L9IrfG7kpoExwP30CVArRQebwQ3aXDxBdNWpuTrjd4BWTs0pLLrDj69_rRDZHDuo6bT297A", "token_uri": "https://oauth2.googleapis.com/token", "client_id": "576447402194-q30hgo3vttv2o98ajrm0b7qqgm9mpe7k.apps.googleusercontent.com", "client_secret": "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", "scopes": ["https://www.googleapis.com/auth/gmail.send"], "universe_domain": "googleapis.com", "account": "", "expiry": "2024-07-25T12:32:35.732138Z"}
|
tools.py
ADDED
@@ -0,0 +1,627 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from langchain.tools import tool
|
2 |
+
from crewai_tools import ScrapeWebsiteTool
|
3 |
+
from gtts import gTTS
|
4 |
+
from pydub import AudioSegment
|
5 |
+
from groq import Groq
|
6 |
+
from PIL import Image, ImageDraw, ImageFont
|
7 |
+
from moviepy.editor import VideoFileClip, AudioFileClip, concatenate_videoclips, ImageClip
|
8 |
+
import requests
|
9 |
+
import os
|
10 |
+
import tempfile
|
11 |
+
import re
|
12 |
+
import base64
|
13 |
+
import pypandoc
|
14 |
+
import cv2
|
15 |
+
import numpy as np
|
16 |
+
import warnings
|
17 |
+
warnings.filterwarnings('ignore')
|
18 |
+
|
19 |
+
from pathlib import Path
|
20 |
+
from openai import OpenAI
|
21 |
+
|
22 |
+
# !sudo apt-get install pandoc
|
23 |
+
|
24 |
+
|
25 |
+
@tool
|
26 |
+
def scrape_website(website_url):
|
27 |
+
"""Scrapes all the information from the given website.
|
28 |
+
Args:
|
29 |
+
website_url: A url of a company website.
|
30 |
+
Returns:
|
31 |
+
Scraped information from the given website.
|
32 |
+
"""
|
33 |
+
scrapper = ScrapeWebsiteTool()
|
34 |
+
data = scrapper.run(website_url=website_url)
|
35 |
+
|
36 |
+
return data
|
37 |
+
|
38 |
+
def convert_md_to_docx(md_file_path, docx_file_path):
|
39 |
+
output = pypandoc.convert_file(md_file_path, 'docx', outputfile=docx_file_path)
|
40 |
+
assert output == "", "Conversion failed"
|
41 |
+
print(f"Converted {md_file_path} to {docx_file_path}")
|
42 |
+
|
43 |
+
# def generate_image(text, num):
|
44 |
+
# engine_id = "stable-diffusion-v1-6"
|
45 |
+
# api_host = os.getenv('API_HOST', 'https://api.stability.ai')
|
46 |
+
# api_key = 'sk-5VTo97D19Ruf2zLinj3pQbVXmLmh2Ps354PGkufTHtqmB2BN'
|
47 |
+
# if api_key is None:
|
48 |
+
# raise Exception("Missing Stability API key.")
|
49 |
+
|
50 |
+
# response = requests.post(
|
51 |
+
# f"{api_host}/v1/generation/{engine_id}/text-to-image",
|
52 |
+
# headers={
|
53 |
+
# "Content-Type": "application/json",
|
54 |
+
# "Accept": "application/json",
|
55 |
+
# "Authorization": f"Bearer {api_key}"
|
56 |
+
# },
|
57 |
+
# json={
|
58 |
+
# "text_prompts": [
|
59 |
+
# {
|
60 |
+
# "text": text
|
61 |
+
# }
|
62 |
+
# ],
|
63 |
+
# "cfg_scale": 7,
|
64 |
+
# "height": 512,
|
65 |
+
# "width": 512,
|
66 |
+
# "samples": 1,
|
67 |
+
# "steps": 10,
|
68 |
+
# },
|
69 |
+
# )
|
70 |
+
|
71 |
+
# print(response.status_code)
|
72 |
+
# if response.status_code != 200:
|
73 |
+
# raise Exception("Non-200 response: " + str(response.text))
|
74 |
+
|
75 |
+
# data = response.json()
|
76 |
+
# # base64_image = None
|
77 |
+
# for image in data["artifacts"]:
|
78 |
+
# with open(f"image_{num}.png", "wb") as f:
|
79 |
+
# f.write(base64.b64decode(image["base64"]))
|
80 |
+
|
81 |
+
# # if base64_image is None:
|
82 |
+
# # raise Exception("No image was generated.")
|
83 |
+
|
84 |
+
# return f'image_{num}.png'
|
85 |
+
|
86 |
+
# def generate_image_core(text, num):
|
87 |
+
# response = requests.post(
|
88 |
+
# f"https://api.stability.ai/v2beta/stable-image/generate/core",
|
89 |
+
# headers={
|
90 |
+
# "authorization": f"sk-6iUj0Jg2eeKDOpRJuDmCDSvPJdUJ6oP6qrQY3sujqR8h4ycF",
|
91 |
+
# "accept": "image/*"
|
92 |
+
# },
|
93 |
+
# files={"none": ''},
|
94 |
+
# data={
|
95 |
+
# "prompt": text,
|
96 |
+
# "output_format": "png",
|
97 |
+
# 'aspect_ratio': "3:2"
|
98 |
+
# },
|
99 |
+
# )
|
100 |
+
|
101 |
+
# print(response.status_code)
|
102 |
+
# if response.status_code == 200:
|
103 |
+
# with open(f"image_{num}.png", 'wb') as file:
|
104 |
+
# file.write(response.content)
|
105 |
+
# else:
|
106 |
+
# raise Exception(str(response.json()))
|
107 |
+
# return f'image_{num}.png'
|
108 |
+
|
109 |
+
# def generate_image_openai(text, num):
|
110 |
+
|
111 |
+
# client = OpenAI(api_key='sk-proj-TVCjX5VGWF5s18k0Z1G1T3BlbkFJZYp0HIC4NnxzqC0ne4YG')
|
112 |
+
|
113 |
+
# try:
|
114 |
+
# print(2)
|
115 |
+
# response = client.images.generate(
|
116 |
+
# model="dall-e-2",
|
117 |
+
# prompt=text,
|
118 |
+
# size="512x512",
|
119 |
+
# quality="standard",
|
120 |
+
# n=1
|
121 |
+
# )
|
122 |
+
# print(3)
|
123 |
+
# image_url = response.data[0].url
|
124 |
+
# print(4)
|
125 |
+
# print(f'image {num} generated')
|
126 |
+
|
127 |
+
# image_response = requests.get(image_url)
|
128 |
+
# print(5)
|
129 |
+
# if image_response.status_code == 200:
|
130 |
+
# with open(os.path.join(f'image_{num}.png'), 'wb') as file:
|
131 |
+
# print(6)
|
132 |
+
# file.write(image_response.content)
|
133 |
+
# print(7)
|
134 |
+
# else:
|
135 |
+
# raise Exception(f"Failed to download image with status code {image_response.status_code} and message: {image_response.text}")
|
136 |
+
|
137 |
+
# except Exception as e:
|
138 |
+
# raise Exception(f"Image generation failed: {e}")
|
139 |
+
|
140 |
+
# return f'image_{num}.png'
|
141 |
+
|
142 |
+
# @tool
|
143 |
+
# def generate_images_and_add_to_blog(blog_content):
|
144 |
+
# """This tool is used to generate images and add them to blog
|
145 |
+
# Args:
|
146 |
+
# blog_content: A complete blog with prompts enclosed in <image> prompt </image> tag.
|
147 |
+
# Returns:
|
148 |
+
# A complete blog"""
|
149 |
+
# print('hi')
|
150 |
+
# image_descriptions = re.findall(r'<image>(.*?)</image>', blog_content)
|
151 |
+
|
152 |
+
# for i, text in enumerate(image_descriptions):
|
153 |
+
|
154 |
+
# try:
|
155 |
+
# print(1)
|
156 |
+
# img_path = generate_image_openai(text, i)
|
157 |
+
# print(8)
|
158 |
+
# # image_tag = f'data:image/png;base64,{base64_img}'
|
159 |
+
# blog_content = blog_content.replace(f'<image>{text}</image>', f'')
|
160 |
+
# print(9)
|
161 |
+
# except Exception as e:
|
162 |
+
# print(e)
|
163 |
+
# raise Exception(f"Image generation failed: {e}")
|
164 |
+
|
165 |
+
# with open('blog_post.md', 'w') as f:
|
166 |
+
# f.write(blog_content)
|
167 |
+
|
168 |
+
# convert_md_to_docx('blog_post.md', 'blog_post.docx')
|
169 |
+
|
170 |
+
# return blog_content
|
171 |
+
|
172 |
+
def generate_image_openai(text, num):
|
173 |
+
|
174 |
+
temp_output_file = tempfile.NamedTemporaryFile(delete=False, suffix='.png')
|
175 |
+
output_image = temp_output_file.name
|
176 |
+
|
177 |
+
client = OpenAI()
|
178 |
+
|
179 |
+
try:
|
180 |
+
response = client.images.generate(
|
181 |
+
model="dall-e-2",
|
182 |
+
prompt=text,
|
183 |
+
size="512x512",
|
184 |
+
quality="standard",
|
185 |
+
n=1
|
186 |
+
)
|
187 |
+
image_url = response.data[0].url
|
188 |
+
|
189 |
+
print(f'image {num} generated')
|
190 |
+
|
191 |
+
image_response = requests.get(image_url)
|
192 |
+
print('response')
|
193 |
+
if image_response.status_code == 200:
|
194 |
+
with open(output_image, 'wb') as file:
|
195 |
+
file.write(image_response.content)
|
196 |
+
print('write')
|
197 |
+
else:
|
198 |
+
raise Exception(f"Failed to download image with status code {image_response.status_code} and message: {image_response.text}")
|
199 |
+
|
200 |
+
except Exception as e:
|
201 |
+
raise Exception(f"Image generation failed: {e}")
|
202 |
+
|
203 |
+
return output_image
|
204 |
+
|
205 |
+
@tool
|
206 |
+
def generate_images_and_add_to_blog(blog_content):
|
207 |
+
"""This tool is used to generate images and add them to blog
|
208 |
+
Args:
|
209 |
+
blog_content: A complete blog with prompts enclosed in <image> prompt </image> tag.
|
210 |
+
Returns:
|
211 |
+
A complete blog"""
|
212 |
+
print(blog_content)
|
213 |
+
print('*****************************************************')
|
214 |
+
print(type(blog_content))
|
215 |
+
|
216 |
+
blog_content = str(blog_content)
|
217 |
+
|
218 |
+
image_descriptions = re.findall(r'<image>(.*?)</image>', blog_content)
|
219 |
+
|
220 |
+
for i, text in enumerate(image_descriptions):
|
221 |
+
|
222 |
+
try:
|
223 |
+
temp_folder = tempfile.mkdtemp()
|
224 |
+
img_path = generate_image_openai(text, i)
|
225 |
+
# image_tag = f'data:image/png;base64,{base64_img}'
|
226 |
+
print(img_path)
|
227 |
+
blog_content = blog_content.replace(f'<image>{text}</image>', f'')
|
228 |
+
print('blog content')
|
229 |
+
except Exception as e:
|
230 |
+
print(e)
|
231 |
+
raise Exception(f"Image generation failed: {e}")
|
232 |
+
|
233 |
+
try:
|
234 |
+
|
235 |
+
print('blog')
|
236 |
+
with open('blog_post.md', 'w') as f:
|
237 |
+
f.write(blog_content)
|
238 |
+
|
239 |
+
print('convert')
|
240 |
+
|
241 |
+
convert_md_to_docx('blog_post.md', 'blog_post.docx')
|
242 |
+
|
243 |
+
print('converted')
|
244 |
+
|
245 |
+
except error:
|
246 |
+
print(error)
|
247 |
+
|
248 |
+
return blog_content
|
249 |
+
|
250 |
+
def process_script(script):
|
251 |
+
"""Used to process the script into dictionary format"""
|
252 |
+
dict = {}
|
253 |
+
text_for_image_generation = re.findall(r'<image>(.*?)</?image>', script, re.DOTALL)
|
254 |
+
text_for_speech_generation = re.findall(r'<narration>(.*?)</?narration>', script, re.DOTALL)
|
255 |
+
dict['text_for_image_generation'] = text_for_image_generation
|
256 |
+
dict['text_for_speech_generation'] = text_for_speech_generation
|
257 |
+
return dict
|
258 |
+
|
259 |
+
def generate_speech(text, lang='en', speed=1.0, num=0):
|
260 |
+
"""
|
261 |
+
Generates speech for the given script using gTTS and adjusts the speed.
|
262 |
+
"""
|
263 |
+
temp_speech_file = tempfile.NamedTemporaryFile(delete=False, suffix='.mp3')
|
264 |
+
temp_speech_path = temp_speech_file.name
|
265 |
+
|
266 |
+
|
267 |
+
client = OpenAI()
|
268 |
+
|
269 |
+
speech_file_path = temp_speech_path
|
270 |
+
response = client.audio.speech.create(
|
271 |
+
model="tts-1",
|
272 |
+
voice="echo",
|
273 |
+
input= text
|
274 |
+
)
|
275 |
+
|
276 |
+
response.stream_to_file(speech_file_path)
|
277 |
+
|
278 |
+
# tts = gTTS(text=text, lang=lang)
|
279 |
+
# tts.save(temp_speech_path)
|
280 |
+
|
281 |
+
sound = AudioSegment.from_file(temp_speech_path)
|
282 |
+
if speed != 1.0:
|
283 |
+
sound_with_altered_speed = sound._spawn(sound.raw_data, overrides={
|
284 |
+
"frame_rate": int(sound.frame_rate * speed)
|
285 |
+
}).set_frame_rate(sound.frame_rate)
|
286 |
+
sound_with_altered_speed.export(temp_speech_path, format="mp3")
|
287 |
+
else:
|
288 |
+
sound.export(temp_speech_path, format="mp3")
|
289 |
+
|
290 |
+
temp_speech_file.close()
|
291 |
+
return temp_speech_path
|
292 |
+
|
293 |
+
# def image_generator(script):
|
294 |
+
# """Generates images for the given script.
|
295 |
+
# Saves it to a temporary directory and returns the path.
|
296 |
+
# Args:
|
297 |
+
# script: a complete script containing narrations and image descriptions."""
|
298 |
+
|
299 |
+
# # remove_temp_files('/tmp')
|
300 |
+
|
301 |
+
# images_dir = tempfile.mkdtemp()
|
302 |
+
|
303 |
+
# dict = process_script(script)
|
304 |
+
# for i, text in enumerate(dict['text_for_image_generation']):
|
305 |
+
# try:
|
306 |
+
# # core
|
307 |
+
# # response = requests.post(
|
308 |
+
# # f"https://api.stability.ai/v2beta/stable-image/generate/core",
|
309 |
+
# # headers={
|
310 |
+
# # "authorization": f"sk-5VTo97D19Ruf2zLinj3pQbVXmLmh2Ps354PGkufTHtqmB2BN",
|
311 |
+
# # "accept": "image/*"
|
312 |
+
# # },
|
313 |
+
# # files={"none": ''},
|
314 |
+
# # data={
|
315 |
+
# # "prompt": text,
|
316 |
+
# # "output_format": "png",
|
317 |
+
# # 'aspect_ratio': "9:16"
|
318 |
+
# # },
|
319 |
+
# # )
|
320 |
+
|
321 |
+
# # print(response.status_code)
|
322 |
+
# # if response.status_code == 200:
|
323 |
+
# # with open(os.path.join(images_dir, f'image_{i}.png'), 'wb') as file:
|
324 |
+
# # file.write(response.content)
|
325 |
+
# # else:
|
326 |
+
# # raise Exception(str(response.json()))
|
327 |
+
|
328 |
+
# # v1
|
329 |
+
# # engine_id = "stable-diffusion-v1-6"
|
330 |
+
# # api_host = os.getenv('API_HOST', 'https://api.stability.ai')
|
331 |
+
# # api_key = 'sk-Z3EF1ebJ9oJUht6Q9fsh861wOsNhRFkxYXMYHNl7gt7xpBMD'
|
332 |
+
# # if api_key is None:
|
333 |
+
# # raise Exception("Missing Stability API key.")
|
334 |
+
|
335 |
+
# # response = requests.post(
|
336 |
+
# # f"{api_host}/v1/generation/{engine_id}/text-to-image",
|
337 |
+
# # headers={
|
338 |
+
# # "Content-Type": "application/json",
|
339 |
+
# # "Accept": "application/json",
|
340 |
+
# # "Authorization": f"Bearer {api_key}"
|
341 |
+
# # },
|
342 |
+
# # json={
|
343 |
+
# # "text_prompts": [
|
344 |
+
# # {
|
345 |
+
# # "text": text
|
346 |
+
# # }
|
347 |
+
# # ],
|
348 |
+
# # "cfg_scale": 7,
|
349 |
+
# # "height": 512,
|
350 |
+
# # "width": 512,
|
351 |
+
# # "samples": 1,
|
352 |
+
# # "steps": 10,
|
353 |
+
# # },
|
354 |
+
# # )
|
355 |
+
|
356 |
+
# # print(response.status_code)
|
357 |
+
# # if response.status_code != 200:
|
358 |
+
# # raise Exception("Non-200 response: " + str(response.text))
|
359 |
+
|
360 |
+
# # data = response.json()
|
361 |
+
# # # base64_image = None
|
362 |
+
# # for image in data["artifacts"]:
|
363 |
+
# # with open(os.path.join(images_dir, f'image_{i}.png'), "wb") as f:
|
364 |
+
# # f.write(base64.b64decode(image["base64"]))
|
365 |
+
|
366 |
+
# pass
|
367 |
+
|
368 |
+
# except Exception as e:
|
369 |
+
# print(e)
|
370 |
+
# raise Exception(f"Image generation failed: {e}")
|
371 |
+
|
372 |
+
# return images_dir
|
373 |
+
|
374 |
+
def image_generator(script):
|
375 |
+
"""Generates images for the given script.
|
376 |
+
Saves it to a temporary directory and returns the path.
|
377 |
+
Args:
|
378 |
+
script: a complete script containing narrations and image descriptions."""
|
379 |
+
|
380 |
+
# remove_temp_files('/tmp')
|
381 |
+
|
382 |
+
images_dir = tempfile.mkdtemp()
|
383 |
+
|
384 |
+
client = OpenAI()
|
385 |
+
dict = process_script(script)
|
386 |
+
for i, text in enumerate(dict['text_for_image_generation']):
|
387 |
+
try:
|
388 |
+
response = client.images.generate(
|
389 |
+
model="dall-e-2",
|
390 |
+
prompt=text,
|
391 |
+
size="512x512",
|
392 |
+
quality="standard",
|
393 |
+
n=1
|
394 |
+
)
|
395 |
+
image_url = response.data[0].url
|
396 |
+
|
397 |
+
print(f'image {i} generated')
|
398 |
+
# Download the image
|
399 |
+
image_response = requests.get(image_url)
|
400 |
+
if image_response.status_code == 200:
|
401 |
+
with open(os.path.join(images_dir, f'image_{i}.png'), 'wb') as file:
|
402 |
+
file.write(image_response.content)
|
403 |
+
else:
|
404 |
+
raise Exception(f"Failed to download image with status code {image_response.status_code} and message: {image_response.text}")
|
405 |
+
|
406 |
+
except Exception as e:
|
407 |
+
raise Exception(f"Image generation failed: {e}")
|
408 |
+
|
409 |
+
return images_dir
|
410 |
+
|
411 |
+
def speech_generator(script):
|
412 |
+
"""
|
413 |
+
Generates speech files for the given script using gTTS.
|
414 |
+
Saves them to a temporary directory and returns the path.
|
415 |
+
Args:
|
416 |
+
script: a complete script containing narrations and image descriptions.
|
417 |
+
"""
|
418 |
+
speeches_dir = tempfile.mkdtemp()
|
419 |
+
|
420 |
+
dict = process_script(script)
|
421 |
+
for i, text in enumerate(dict['text_for_speech_generation']):
|
422 |
+
speech_path = generate_speech(text, num=i)
|
423 |
+
print(f'speech {i} generated')
|
424 |
+
os.rename(speech_path, os.path.join(speeches_dir, f'speech_{i}.mp3'))
|
425 |
+
|
426 |
+
return speeches_dir, dict['text_for_speech_generation']
|
427 |
+
|
428 |
+
def split_text_into_chunks(text, chunk_size):
|
429 |
+
words = text.split()
|
430 |
+
return [' '.join(words[i:i + chunk_size]) for i in range(0, len(words), chunk_size)]
|
431 |
+
|
432 |
+
def add_text_to_video(input_video, text, duration=1, fontsize=40, fontcolor=(255, 255, 255),
|
433 |
+
outline_thickness=2, outline_color=(0, 0, 0), delay_between_chunks=0.3,
|
434 |
+
font_path='Montserrat-Bold.ttf'):
|
435 |
+
temp_output_file = tempfile.NamedTemporaryFile(delete=False, suffix='.mp4')
|
436 |
+
output_video = temp_output_file.name
|
437 |
+
|
438 |
+
chunks = split_text_into_chunks(text, 3) # Adjust chunk size as needed
|
439 |
+
|
440 |
+
cap = cv2.VideoCapture(input_video)
|
441 |
+
if not cap.isOpened():
|
442 |
+
raise ValueError("Error opening video file.")
|
443 |
+
|
444 |
+
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
|
445 |
+
fps = int(cap.get(cv2.CAP_PROP_FPS))
|
446 |
+
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
|
447 |
+
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
|
448 |
+
out = cv2.VideoWriter(output_video, fourcc, fps, (width, height))
|
449 |
+
|
450 |
+
frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
|
451 |
+
chunk_duration_frames = duration * fps
|
452 |
+
delay_frames = int(delay_between_chunks * fps)
|
453 |
+
|
454 |
+
if not os.path.exists(font_path):
|
455 |
+
raise FileNotFoundError(f"Font file not found: {font_path}")
|
456 |
+
|
457 |
+
try:
|
458 |
+
font = ImageFont.truetype(font_path, fontsize)
|
459 |
+
except Exception as e:
|
460 |
+
raise RuntimeError(f"Error loading font: {e}")
|
461 |
+
|
462 |
+
current_frame = 0
|
463 |
+
|
464 |
+
while cap.isOpened():
|
465 |
+
ret, frame = cap.read()
|
466 |
+
if not ret:
|
467 |
+
break
|
468 |
+
|
469 |
+
frame_pil = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
|
470 |
+
draw = ImageDraw.Draw(frame_pil)
|
471 |
+
|
472 |
+
chunk_index = current_frame // (chunk_duration_frames + delay_frames)
|
473 |
+
|
474 |
+
if current_frame % (chunk_duration_frames + delay_frames) < chunk_duration_frames and chunk_index < len(chunks):
|
475 |
+
chunk = chunks[chunk_index]
|
476 |
+
text_bbox = draw.textbbox((0, 0), chunk, font=font)
|
477 |
+
text_width, text_height = text_bbox[2] - text_bbox[0], text_bbox[3] - text_bbox[1]
|
478 |
+
text_x = (width - text_width) // 2
|
479 |
+
text_y = height - 100 # Position text at the bottom
|
480 |
+
|
481 |
+
if text_width > width:
|
482 |
+
words = chunk.split()
|
483 |
+
half = len(words) // 2
|
484 |
+
line1 = ' '.join(words[:half])
|
485 |
+
line2 = ' '.join(words[half:])
|
486 |
+
|
487 |
+
text_size_line1 = draw.textsize(line1, font=font)
|
488 |
+
text_size_line2 = draw.textsize(line2, font=font)
|
489 |
+
text_x_line1 = (width - text_size_line1[0]) // 2
|
490 |
+
text_x_line2 = (width - text_size_line2[0]) // 2
|
491 |
+
text_y = height - 250 - text_size_line1[1] # Adjust vertical position for two lines
|
492 |
+
|
493 |
+
for dx in range(-outline_thickness, outline_thickness + 1):
|
494 |
+
for dy in range(-outline_thickness, outline_thickness + 1):
|
495 |
+
if dx != 0 or dy != 0:
|
496 |
+
draw.text((text_x_line1 + dx, text_y + dy), line1, font=font, fill=outline_color)
|
497 |
+
draw.text((text_x_line2 + dx, text_y + text_size_line1[1] + dy), line2, font=font, fill=outline_color)
|
498 |
+
|
499 |
+
draw.text((text_x_line1, text_y), line1, font=font, fill=fontcolor)
|
500 |
+
draw.text((text_x_line2, text_y + text_size_line1[1]), line2, font=font, fill=fontcolor)
|
501 |
+
|
502 |
+
else:
|
503 |
+
for dx in range(-outline_thickness, outline_thickness + 1):
|
504 |
+
for dy in range(-outline_thickness, outline_thickness + 1):
|
505 |
+
if dx != 0 or dy != 0:
|
506 |
+
draw.text((text_x + dx, text_y + dy), chunk, font=font, fill=outline_color)
|
507 |
+
|
508 |
+
draw.text((text_x, text_y), chunk, font=font, fill=fontcolor)
|
509 |
+
|
510 |
+
frame = cv2.cvtColor(np.array(frame_pil), cv2.COLOR_RGB2BGR)
|
511 |
+
|
512 |
+
out.write(frame)
|
513 |
+
current_frame += 1
|
514 |
+
|
515 |
+
# Ensure loop breaks after processing all frames
|
516 |
+
if current_frame >= frame_count:
|
517 |
+
break
|
518 |
+
|
519 |
+
cap.release()
|
520 |
+
out.release()
|
521 |
+
cv2.destroyAllWindows()
|
522 |
+
|
523 |
+
return output_video
|
524 |
+
|
525 |
+
def apply_zoom_in_effect(clip, zoom_factor=1.2):
|
526 |
+
width, height = clip.size
|
527 |
+
duration = clip.duration
|
528 |
+
|
529 |
+
def zoom_in_effect(get_frame, t):
|
530 |
+
frame = get_frame(t)
|
531 |
+
zoom = 1 + (zoom_factor - 1) * (t / duration)
|
532 |
+
new_width, new_height = int(width * zoom), int(height * zoom)
|
533 |
+
resized_frame = cv2.resize(frame, (new_width, new_height))
|
534 |
+
|
535 |
+
x_start = (new_width - width) // 2
|
536 |
+
y_start = (new_height - height) // 2
|
537 |
+
cropped_frame = resized_frame[y_start:y_start + height, x_start:x_start + width]
|
538 |
+
|
539 |
+
return cropped_frame
|
540 |
+
|
541 |
+
return clip.fl(zoom_in_effect, apply_to=['mask'])
|
542 |
+
|
543 |
+
def create_video_from_images_and_audio(images_dir, speeches_dir, final_video_filename, all_captions):
|
544 |
+
"""Creates video using images and audios.
|
545 |
+
Args:
|
546 |
+
images_dir: path to images folder
|
547 |
+
speeches_dir: path to speeches folder
|
548 |
+
final_video_filename: the topic name which will be used as final video file name"""
|
549 |
+
print('hi')
|
550 |
+
client = Groq(api_key='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')
|
551 |
+
# images_paths = sorted(os.listdir(images_dir))
|
552 |
+
# audio_paths = sorted(os.listdir(speeches_dir))
|
553 |
+
images_paths = sorted([os.path.join(images_dir, img) for img in os.listdir(images_dir) if img.endswith('.png') or img.endswith('.jpg')])
|
554 |
+
audio_paths = sorted([os.path.join(speeches_dir, speech) for speech in os.listdir(speeches_dir) if speech.endswith('.mp3')])
|
555 |
+
clips = []
|
556 |
+
temp_files = []
|
557 |
+
video_dir = tempfile.mkdtemp()
|
558 |
+
|
559 |
+
for i in range(min(len(images_paths), len(audio_paths))):
|
560 |
+
img_clip = ImageClip(os.path.join(images_dir, images_paths[i]))
|
561 |
+
audioclip = AudioFileClip(os.path.join(speeches_dir, audio_paths[i]))
|
562 |
+
videoclip = img_clip.set_duration(audioclip.duration)
|
563 |
+
zoomed_clip = apply_zoom_in_effect(videoclip, 1.3)
|
564 |
+
|
565 |
+
# with open(os.path.join(speeches_dir, audio_paths[i]), "rb") as file:
|
566 |
+
# transcription = client.audio.transcriptions.create(
|
567 |
+
# file=(audio_paths[i], file.read()),
|
568 |
+
# model="whisper-large-v3",
|
569 |
+
# response_format="verbose_json",
|
570 |
+
# )
|
571 |
+
# caption = transcription.text
|
572 |
+
temp_video_path = tempfile.NamedTemporaryFile(delete=False, suffix='.mp4').name
|
573 |
+
zoomed_clip.write_videofile(temp_video_path, codec='libx264', fps=24)
|
574 |
+
temp_files.append(temp_video_path)
|
575 |
+
|
576 |
+
caption = all_captions[i]
|
577 |
+
final_video_path = add_text_to_video(temp_video_path, caption, duration=1, fontsize=20)
|
578 |
+
temp_files.append(final_video_path)
|
579 |
+
|
580 |
+
final_clip = VideoFileClip(final_video_path)
|
581 |
+
final_clip = final_clip.set_audio(audioclip)
|
582 |
+
|
583 |
+
print(f'create small video {i}')
|
584 |
+
clips.append(final_clip)
|
585 |
+
|
586 |
+
final_clip = concatenate_videoclips(clips)
|
587 |
+
if not final_video_filename.endswith('.mp4'):
|
588 |
+
final_video_filename = final_video_filename + '.mp4'
|
589 |
+
final_clip.write_videofile(os.path.join(video_dir, final_video_filename), codec='libx264', fps=24)
|
590 |
+
|
591 |
+
# Close all video files properly
|
592 |
+
for clip in clips:
|
593 |
+
clip.close()
|
594 |
+
|
595 |
+
# Remove all temporary files
|
596 |
+
for temp_file in temp_files:
|
597 |
+
try:
|
598 |
+
os.remove(temp_file)
|
599 |
+
except Exception as e:
|
600 |
+
print(f"Error removing file {temp_file}: {e}")
|
601 |
+
|
602 |
+
return os.path.join(video_dir, final_video_filename)
|
603 |
+
|
604 |
+
@tool
|
605 |
+
def generate_video(pairs, final_video_filename):
|
606 |
+
""" Generates video using narration and image prompt pairs.
|
607 |
+
|
608 |
+
Args:
|
609 |
+
pairs:A string of arration and image prompt pairs enclosed in <narration> and <image> tags.
|
610 |
+
final_video_filename: the topic name which will be used as final video file name
|
611 |
+
|
612 |
+
Returns:
|
613 |
+
Generated video path"""
|
614 |
+
|
615 |
+
images_dir = image_generator(pairs)
|
616 |
+
print(images_dir)
|
617 |
+
speeches_dir, all_captions = speech_generator(pairs)
|
618 |
+
print(speeches_dir)
|
619 |
+
video_path = create_video_from_images_and_audio(images_dir, speeches_dir, final_video_filename, all_captions)
|
620 |
+
print('video', video_path)
|
621 |
+
|
622 |
+
with open(video_path, 'rb') as f:
|
623 |
+
video = f.read()
|
624 |
+
with open('video.mp4', 'wb') as f:
|
625 |
+
f.write(video)
|
626 |
+
|
627 |
+
return video_path
|
trained_agents_data.pkl
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:926248e52d1fa532c317e37da24ed652ae64110f8219cb5e061668bd3091f048
|
3 |
+
size 5
|
utils.py
ADDED
@@ -0,0 +1,217 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from email.mime.text import MIMEText
|
2 |
+
from google.oauth2 import service_account
|
3 |
+
from googleapiclient.http import MediaFileUpload
|
4 |
+
import base64
|
5 |
+
import os
|
6 |
+
import base64
|
7 |
+
import streamlit as st
|
8 |
+
from google.auth.transport.requests import Request
|
9 |
+
from google.oauth2.credentials import Credentials
|
10 |
+
from google_auth_oauthlib.flow import InstalledAppFlow
|
11 |
+
from googleapiclient.discovery import build
|
12 |
+
from email.mime.multipart import MIMEMultipart
|
13 |
+
import requests
|
14 |
+
import json
|
15 |
+
from langchain.tools import tool
|
16 |
+
|
17 |
+
# Constants
|
18 |
+
SCOPES_DRIVE = ['https://www.googleapis.com/auth/drive']
|
19 |
+
SERVICE_ACCOUNT_FILE = 'service_account.json'
|
20 |
+
PARENT_FOLDER_ID = "1REXfwxk9dcPdpZXJOFZSur3880soVN9y"
|
21 |
+
|
22 |
+
|
23 |
+
# Authenticate and return credentials for Google Drive
|
24 |
+
def authenticate_drive():
|
25 |
+
credentials = service_account.Credentials.from_service_account_file(
|
26 |
+
SERVICE_ACCOUNT_FILE, scopes=SCOPES_DRIVE)
|
27 |
+
return credentials
|
28 |
+
|
29 |
+
# Upload file to Google Drive
|
30 |
+
def upload_file(filepath, filename, parent_folder_id):
|
31 |
+
creds = authenticate_drive()
|
32 |
+
service = build('drive', 'v3', credentials=creds)
|
33 |
+
|
34 |
+
file_metadata = {
|
35 |
+
'name': filename,
|
36 |
+
'parents': [parent_folder_id]
|
37 |
+
}
|
38 |
+
|
39 |
+
media = MediaFileUpload(filepath, resumable=True)
|
40 |
+
file = service.files().create(body=file_metadata, media_body=media, fields='id').execute()
|
41 |
+
print(f'File ID: {file.get("id")}')
|
42 |
+
return file.get('id')
|
43 |
+
|
44 |
+
SCOPES = ["https://www.googleapis.com/auth/gmail.send"]
|
45 |
+
|
46 |
+
def authenticate_gmail():
|
47 |
+
"""Authenticate and return the Gmail service."""
|
48 |
+
creds = None
|
49 |
+
if os.path.exists("token.json"):
|
50 |
+
creds = Credentials.from_authorized_user_file("token.json", SCOPES)
|
51 |
+
if not creds or not creds.valid:
|
52 |
+
if creds and creds.expired and creds.refresh_token:
|
53 |
+
creds.refresh(Request())
|
54 |
+
else:
|
55 |
+
flow = InstalledAppFlow.from_client_secrets_file("credentials.json", SCOPES)
|
56 |
+
creds = flow.run_local_server(port=0)
|
57 |
+
with open("token.json", "w") as token:
|
58 |
+
token.write(creds.to_json())
|
59 |
+
service = build("gmail", "v1", credentials=creds)
|
60 |
+
return service
|
61 |
+
|
62 |
+
def send_email_with_company_details(user_email, company_name, topic):
|
63 |
+
|
64 |
+
|
65 |
+
blog_id = upload_file('blog_post.docx', 'blog post', PARENT_FOLDER_ID)
|
66 |
+
video_id = upload_file('video.mp4', 'video', PARENT_FOLDER_ID)
|
67 |
+
|
68 |
+
# Prepare email content
|
69 |
+
blog_link = f"https://drive.google.com/file/d/{blog_id}/view?usp=sharing"
|
70 |
+
video_link = f"https://drive.google.com/file/d/{video_id}/view?usp=sharing"
|
71 |
+
email_subject = f"Blog and Video for the topic {topic}"
|
72 |
+
email_body = f"""
|
73 |
+
<html>
|
74 |
+
<body>
|
75 |
+
<p>Hello,</p>
|
76 |
+
<p>The requested blog and video has been shared with you by <b>{company_name}</b>.</p>
|
77 |
+
<p>You can view the files using the following links:</p>
|
78 |
+
<b>Blog:</b>
|
79 |
+
<a href="{blog_link}"> {topic}</a>
|
80 |
+
<br>
|
81 |
+
<br>
|
82 |
+
<b>Video:</b>
|
83 |
+
<a href="{video_link}"> {topic}</a>
|
84 |
+
<br>
|
85 |
+
<p>Best regards,<br>{company_name}</p>
|
86 |
+
</body>
|
87 |
+
</html>
|
88 |
+
"""
|
89 |
+
|
90 |
+
try:
|
91 |
+
# Create message container - the correct MIME type is multipart/alternative.
|
92 |
+
msg = MIMEMultipart('alternative')
|
93 |
+
msg['to'] = user_email
|
94 |
+
msg['subject'] = email_subject
|
95 |
+
|
96 |
+
# Attach parts into message container
|
97 |
+
part1 = MIMEText(email_body, 'plain')
|
98 |
+
part2 = MIMEText(email_body, 'html')
|
99 |
+
msg.attach(part1)
|
100 |
+
msg.attach(part2)
|
101 |
+
|
102 |
+
raw_string = base64.urlsafe_b64encode(msg.as_bytes()).decode()
|
103 |
+
|
104 |
+
service = authenticate_gmail()
|
105 |
+
sent_message = service.users().messages().send(userId='me', body={'raw': raw_string}).execute()
|
106 |
+
|
107 |
+
# # Connect to the SMTP server
|
108 |
+
# server = smtplib.SMTP('smtp.gmail.com', 587)
|
109 |
+
# server.starttls() # Secure the connection
|
110 |
+
# server.login(SENDER_EMAIL, PASSWORD)
|
111 |
+
# server.sendmail(SENDER_EMAIL, user_email, msg.as_string())
|
112 |
+
print('Email sent successfully!')
|
113 |
+
except Exception as e:
|
114 |
+
print(f'Error sending email: {str(e)}')
|
115 |
+
|
116 |
+
# Upload blog post and video, then share them and send an email
|
117 |
+
# def main():
|
118 |
+
# blog_filepath = 'blog_post.docx'
|
119 |
+
# video_filepath = 'video.mp4'
|
120 |
+
# user_email = receiver_email
|
121 |
+
# company_name = 'digiotai'
|
122 |
+
|
123 |
+
# # Upload blog post
|
124 |
+
# blog_file_id = upload_file(blog_filepath, 'blog post', PARENT_FOLDER_ID)
|
125 |
+
# # Upload video
|
126 |
+
# video_file_id = upload_file(video_filepath, 'video', PARENT_FOLDER_ID)
|
127 |
+
|
128 |
+
# send_email_with_company_details(blog_file_id, video_file_id, user_email, company_name)
|
129 |
+
|
130 |
+
# main()
|
131 |
+
|
132 |
+
|
133 |
+
def get_urn(token):
|
134 |
+
# access_token = '<your_access_token>'
|
135 |
+
url = 'https://api.linkedin.com/v2/userinfo'
|
136 |
+
|
137 |
+
headers = {
|
138 |
+
'Authorization': f'Bearer {token}'
|
139 |
+
}
|
140 |
+
|
141 |
+
response = requests.get(url, headers=headers)
|
142 |
+
|
143 |
+
if response.status_code == 200:
|
144 |
+
user_info = response.json()
|
145 |
+
print(user_info['sub'])
|
146 |
+
return user_info['sub']
|
147 |
+
else:
|
148 |
+
print(f'Failed to fetch user info: {response.status_code}')
|
149 |
+
print(response.text)
|
150 |
+
|
151 |
+
@tool
|
152 |
+
def post_image_and_text(
|
153 |
+
token: str, title: str, image_path: str, text_content: str
|
154 |
+
):
|
155 |
+
"""
|
156 |
+
Posts an article on LinkedIn with an image.
|
157 |
+
|
158 |
+
:param token: str. LinkedIn OAuth token.
|
159 |
+
:param title: str. Article title.
|
160 |
+
:param text_content: str. Article content.
|
161 |
+
:param image_path: str. Local file path of the image to be used as a thumbnail.
|
162 |
+
"""
|
163 |
+
|
164 |
+
owner = get_urn(token)
|
165 |
+
|
166 |
+
# Initialize the upload to get the upload URL and image URN
|
167 |
+
init_url = "https://api.linkedin.com/rest/images?action=initializeUpload"
|
168 |
+
headers = {
|
169 |
+
"LinkedIn-Version": "202401",
|
170 |
+
"X-RestLi-Protocol-Version": "2.0.0",
|
171 |
+
"Content-Type": "application/json",
|
172 |
+
"Authorization": f"Bearer {token}",
|
173 |
+
}
|
174 |
+
init_data = json.dumps({"initializeUploadRequest": {"owner": f'urn:li:person:{owner}'}})
|
175 |
+
init_response = requests.post(init_url, headers=headers, data=init_data)
|
176 |
+
print(init_response.content)
|
177 |
+
if init_response.status_code != 200:
|
178 |
+
raise Exception(f"Failed to initialize upload: {init_response.text}")
|
179 |
+
|
180 |
+
init_response_data = init_response.json()["value"]
|
181 |
+
upload_url = init_response_data["uploadUrl"]
|
182 |
+
image_urn = init_response_data["image"]
|
183 |
+
|
184 |
+
# Upload the file
|
185 |
+
with open(image_path, "rb") as f:
|
186 |
+
upload_response = requests.post(upload_url, files={"file": f})
|
187 |
+
if upload_response.status_code not in [200, 201]:
|
188 |
+
raise Exception(f"Failed to upload file: {upload_response.text}")
|
189 |
+
|
190 |
+
# Create the post with the uploaded image URN as thumbnail
|
191 |
+
post_url = "https://api.linkedin.com/rest/posts"
|
192 |
+
post_data = json.dumps(
|
193 |
+
{
|
194 |
+
"author": f'urn:li:person:{owner}',
|
195 |
+
"commentary": text_content,
|
196 |
+
"visibility": "PUBLIC",
|
197 |
+
"distribution": {
|
198 |
+
"feedDistribution": "MAIN_FEED",
|
199 |
+
"targetEntities": [],
|
200 |
+
"thirdPartyDistributionChannels": [],
|
201 |
+
},
|
202 |
+
"content": {
|
203 |
+
"media": {
|
204 |
+
"title": title,
|
205 |
+
"id": image_urn,
|
206 |
+
}
|
207 |
+
},
|
208 |
+
"lifecycleState": "PUBLISHED",
|
209 |
+
"isReshareDisabledByAuthor": False,
|
210 |
+
}
|
211 |
+
)
|
212 |
+
post_response = requests.post(post_url, headers=headers, data=post_data)
|
213 |
+
print(post_response.content)
|
214 |
+
if post_response.status_code in [200, 201]:
|
215 |
+
return "Linkedin article has been posted successfully!"
|
216 |
+
else:
|
217 |
+
raise Exception(f"Failed to post article: {post_response.text}")
|
video.mp4
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:e72d993994dccaa7ae8c0236059aa2d457a8ce79f31a42b98adaa19de8d05ada
|
3 |
+
size 2672279
|