|
import pixeltable as pxt |
|
import os |
|
import openai |
|
import gradio as gr |
|
import getpass |
|
from pixeltable.iterators import FrameIterator |
|
from pixeltable.functions.video import extract_audio |
|
from pixeltable.functions.audio import get_metadata |
|
from pixeltable.functions import openai |
|
|
|
|
|
|
|
if 'OPENAI_API_KEY' not in os.environ: |
|
os.environ['OPENAI_API_KEY'] = getpass.getpass('Enter your OpenAI API key:') |
|
|
|
MAX_VIDEO_SIZE_MB = 35 |
|
CONCURRENCY_LIMIT = 1 |
|
|
|
def process_and_generate_post(video_file, social_media_type, progress=gr.Progress()): |
|
|
|
progress(0, desc="Initializing...") |
|
|
|
|
|
pxt.drop_dir('directory', force=True) |
|
pxt.create_dir('directory') |
|
|
|
t = pxt.create_table( |
|
'directory.video_table', { |
|
"video": pxt.Video, |
|
"sm_type": pxt.String |
|
} |
|
) |
|
|
|
frames_view = pxt.create_view( |
|
"directory.frames", |
|
t, |
|
iterator=FrameIterator.create(video=t.video, fps=1) |
|
) |
|
|
|
|
|
t.add_computed_column(audio=extract_audio(t.video, format='mp3')) |
|
t.add_computed_column(metadata=get_metadata(t.audio)) |
|
t.add_computed_column(transcription=openai.transcriptions(audio=t.audio, model='whisper-1')) |
|
t.add_computed_column(transcription_text=t.transcription.text) |
|
|
|
progress(0.1, desc="Creating UDFs...") |
|
|
|
|
|
@pxt.udf |
|
def prompt(A: str, B: str) -> list[dict]: |
|
system_msg = 'You are an expert in creating social media content and you generate effective post, based on user content. Respect the social media platform guidelines and constraints.' |
|
user_msg = f'A: "{A}" \n B: "{B}"' |
|
return [ |
|
{'role': 'system', 'content': system_msg}, |
|
{'role': 'user', 'content': user_msg} |
|
] |
|
|
|
|
|
t.add_computed_column(message=prompt(t.sm_type, t.transcription_text)) |
|
|
|
"""## Generating Responses with OpenAI's GPT Model""" |
|
|
|
progress(0.2, desc="Calling LLMs") |
|
|
|
|
|
t.add_computed_column(response=openai.chat_completions(messages=t.message, model='gpt-4o-mini-2024-07-18', max_tokens=500)) |
|
|
|
|
|
t.add_computed_column(answer=t.response.choices[0].message.content) |
|
|
|
if not video_file: |
|
return "Please upload a video file.", None |
|
|
|
try: |
|
|
|
|
|
video_size = os.path.getsize(video_file) / (1024 * 1024) |
|
if video_size > MAX_VIDEO_SIZE_MB: |
|
return f"The video file is larger than {MAX_VIDEO_SIZE_MB} MB. Please upload a smaller file.", None |
|
|
|
progress(0.4, desc="Inserting video...") |
|
|
|
|
|
t.insert([{ |
|
"video": video_file, |
|
"sm_type": social_media_type |
|
}]) |
|
|
|
progress(0.6, desc="Generating posts...") |
|
|
|
|
|
social_media_post = t.select(t.answer).tail(1)['answer'][0] |
|
|
|
|
|
audio = t.select(t.audio).tail(1)['audio'][0] |
|
|
|
|
|
thumbnails = frames_view.select(frames_view.frame).tail(6)['frame'] |
|
|
|
progress(0.8, desc="Preparing results...") |
|
|
|
|
|
df_output = t.select(t.transcription_text).tail(1)['transcription_text'][0] |
|
|
|
|
|
return social_media_post, thumbnails, df_output, audio |
|
|
|
except Exception as e: |
|
return f"An error occurred: {str(e)}", None |
|
|
|
|
|
def gradio_interface(): |
|
with gr.Blocks(theme=gr.themes.Base()) as demo: |
|
|
|
gr.Markdown( |
|
""" |
|
<div style="text-align: left; margin-bottom: 20px;"> |
|
<img src="https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/pixeltable-logo-large.png" alt="Pixeltable" style="max-width: 200px; margin-bottom: 20px;" /> |
|
<h1 style="margin-bottom: 0.5em;">πΉ Video to Social Media Post Generator</h1> |
|
<p style="color: #666; margin-bottom: 1em;">Transform your videos into engaging social media content using AI</p> |
|
</div> |
|
""" |
|
) |
|
|
|
with gr.Row(): |
|
|
|
with gr.Column(): |
|
with gr.Accordion("π― Key Features", open=False): |
|
gr.Markdown( |
|
""" |
|
* π₯ **Smart Video Processing** |
|
- Automatic frame extraction |
|
- High-quality audio separation |
|
- Real-time transcription |
|
|
|
* π€ **AI-Powered Content** |
|
- Platform-specific post generation |
|
- Engaging caption creation |
|
- Tone & style optimization |
|
|
|
* π **Complete Analytics** |
|
- Transcription review |
|
- Thumbnail selection |
|
- Audio verification |
|
""" |
|
) |
|
|
|
|
|
with gr.Column(): |
|
with gr.Accordion("π οΈ How It Works", open=False): |
|
gr.Markdown( |
|
""" |
|
1. π€ **Upload Your Video** |
|
- Choose your content |
|
- Select target platform |
|
- Start processing |
|
|
|
2. π **AI Processing** |
|
- Video analysis |
|
- Content extraction |
|
- Post generation |
|
|
|
3. β¨ **Review & Export** |
|
- Preview generated posts |
|
- Select thumbnails |
|
- Copy to clipboard |
|
""" |
|
) |
|
|
|
|
|
with gr.Tabs() as tabs: |
|
|
|
with gr.TabItem("π€ Upload & Generate", id=0): |
|
with gr.Row(): |
|
|
|
with gr.Column(): |
|
video_input = gr.Video( |
|
label="Upload Your Video", |
|
include_audio=True, |
|
max_length=300, |
|
height='400px', |
|
autoplay=False |
|
) |
|
|
|
with gr.Group(): |
|
gr.Markdown("### π― Target Platform") |
|
social_media_type = gr.Radio( |
|
choices=[ |
|
"X (Twitter)", |
|
"Facebook", |
|
"LinkedIn", |
|
"Instagram" |
|
], |
|
value="X (Twitter)", |
|
label="Select where you want to share:", |
|
interactive=True |
|
) |
|
|
|
generate_btn = gr.Button( |
|
"π Generate Post", |
|
scale=1, |
|
size="lg", |
|
variant="primary" |
|
) |
|
|
|
|
|
with gr.Column(): |
|
output = gr.Textbox( |
|
label="β¨ Generated Post", |
|
show_copy_button=True, |
|
lines=4 |
|
) |
|
|
|
gr.Markdown("### πΌοΈ Thumbnail Options") |
|
thumbnail = gr.Gallery( |
|
label="Select your preferred thumbnail", |
|
show_download_button=True, |
|
show_fullscreen_button=True, |
|
height='200px', |
|
object_fit="contain" |
|
) |
|
|
|
|
|
with gr.TabItem("π Review & Export", id=1): |
|
with gr.Row(): |
|
with gr.Column(): |
|
df_output = gr.Textbox( |
|
label="π Video Transcription", |
|
show_copy_button=True, |
|
lines=8 |
|
) |
|
audio = gr.Audio( |
|
label="π΅ Extracted Audio", |
|
show_download_button=True, |
|
type="filepath" |
|
) |
|
|
|
|
|
gr.Markdown("### π Sample Usage") |
|
with gr.Row(): |
|
gr.Examples( |
|
examples=[ |
|
["example1.mp4", "X (Twitter)"], |
|
["example2.mp4", "LinkedIn"], |
|
["example3.mp4", "Instagram"], |
|
["example4.mp4", "Facebook"] |
|
], |
|
inputs=[video_input, social_media_type], |
|
label="Try with sample videos:", |
|
examples_per_page=5 |
|
) |
|
|
|
|
|
gr.HTML( |
|
""" |
|
<div style="text-align: center; margin-top: 2rem; padding-top: 1rem; border-top: 1px solid #ccc;"> |
|
<p style="color: #666; font-size: 0.9em;"> |
|
Powered by <a href="https://github.com/pixeltable/pixeltable" target="_blank" style="color: #F25022; text-decoration: none;">Pixeltable</a> |
|
| <a href="https://docs.pixeltable.com" target="_blank" style="color: #666;">Documentation</a> |
|
| <a href="https://github.com/pixeltable/pixeltable" target="_blank" style="color: #666;">GitHub</a> |
|
</p> |
|
</div> |
|
""" |
|
) |
|
|
|
|
|
generate_btn.click( |
|
fn=process_and_generate_post, |
|
inputs=[video_input, social_media_type], |
|
outputs=[output, thumbnail, df_output, audio], |
|
api_name="generate", |
|
show_progress="full", |
|
trigger_mode='once' |
|
) |
|
|
|
return demo |
|
|
|
|
|
if __name__ == "__main__": |
|
demo = gradio_interface() |
|
demo.launch( |
|
server_name="0.0.0.0", |
|
server_port=7860, |
|
share=False, |
|
show_api=False, |
|
show_error=False, |
|
ssl_verify=True, |
|
quiet=True |
|
) |