From 83779faf461ee478c416dd96d4f6030e223cd4b1 Mon Sep 17 00:00:00 2001 From: Eric Meehan Date: Tue, 25 Mar 2025 22:50:55 -0400 Subject: [PATCH 1/6] Execute application with Gitea Actions --- .env_example | 1 + .gitea/workflows/execute.yaml | 22 ++++++++++++++++++++++ app.py | 19 ++++++++++++++----- 3 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 .gitea/workflows/execute.yaml diff --git a/.env_example b/.env_example index 7447381..25e7d50 100644 --- a/.env_example +++ b/.env_example @@ -3,3 +3,4 @@ OPENAI_BASE_URL = OPENAI_RESPONSES_PROMPT = SEGMENT_DURATION = TMP_AUDIO_PATH = +TMP_VIDEO_PATH = diff --git a/.gitea/workflows/execute.yaml b/.gitea/workflows/execute.yaml new file mode 100644 index 0000000..cd4cfda --- /dev/null +++ b/.gitea/workflows/execute.yaml @@ -0,0 +1,22 @@ +name: Execute video summary bot +on: + workflow_dispatch: + inputs: + video-url: + description: "URL for the video to be analyzed" + required: true +jobs: + Python: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.10" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + - name: Run applicaiton + run: python app.py diff --git a/app.py b/app.py index 4676a2d..8f60520 100644 --- a/app.py +++ b/app.py @@ -1,4 +1,3 @@ -import argparse import os from dotenv import load_dotenv @@ -14,12 +13,24 @@ def main(args): openai_client, transcribe_audio( openai_client, - get_audio_from_video(args.video_file_path) + get_audio_from_video( + get_video_from_url() + ) ) ) -def get_audio_from_video(video_file_path): +def get_video_from_url(): + video_file_url = os.getenv('INPUT_VIDEO_URL') + video_file_path = os.getenv('TMP_VIDEO_PATH' , '/tmp/video_summary_bot_tmp_video.mp4') + request = requests.get(video_file_url) + with open(video_file_path, 'wb') as f: + for chunk in requests.get(video_file_url).iter_content(chunk_size=255): + if chunk: + f.write(chunk) + +def get_audio_from_video(): tmp_audio_path = os.getenv('TMP_AUDIO_PATH', '/tmp/video_summary_bot_tmp_audio.wav') + video_file_path = os.getenv('TMP_VIDEO_PATH') VideoFileClip(video_file_path).audio.write_audiofile(tmp_audio_path) return AudioSegment.from_wav(tmp_audio_path) @@ -44,6 +55,4 @@ def summarize_transcription(openai_client, transcription): if __name__ == '__main__': load_dotenv() - parser = argparse.ArgumentParser(description="Use AI models to summarize videos") - parser.add_argument('--video-file-path', type=str, help="Path to the video to be summarized") main(parser.parse_args()) -- 2.45.2 From 1bc7f93e6ed2818872fb4781a71c640b16702975 Mon Sep 17 00:00:00 2001 From: Eric Meehan Date: Tue, 25 Mar 2025 23:01:02 -0400 Subject: [PATCH 2/6] Environment variables for LocalAI --- .gitea/workflows/execute.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitea/workflows/execute.yaml b/.gitea/workflows/execute.yaml index cd4cfda..c6b608a 100644 --- a/.gitea/workflows/execute.yaml +++ b/.gitea/workflows/execute.yaml @@ -8,6 +8,9 @@ on: jobs: Python: runs-on: ubuntu-latest + env: + OPENAI_API_KEY: "nokey" + OPENAI_BASE_URL: "http://192.168.1.168/v1" steps: - uses: actions/checkout@v4 - name: Set up Python -- 2.45.2 From 9ffe534e25edada523616cc5fcfe9ad75769028c Mon Sep 17 00:00:00 2001 From: Eric Meehan Date: Tue, 25 Mar 2025 23:20:29 -0400 Subject: [PATCH 3/6] Requirements --- app.py | 1 + requirements.txt | 3 +++ 2 files changed, 4 insertions(+) diff --git a/app.py b/app.py index 8f60520..90a4dce 100644 --- a/app.py +++ b/app.py @@ -1,4 +1,5 @@ import os +import requests from dotenv import load_dotenv from moviepy import VideoFileClip diff --git a/requirements.txt b/requirements.txt index 2d5fb82..30f1552 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,5 @@ moviepy +openai pydub +python-dotenv +requests -- 2.45.2 From 42bf3f0e9821249db482b0b4e96eb30d15cc5698 Mon Sep 17 00:00:00 2001 From: Eric Meehan Date: Thu, 27 Mar 2025 15:41:26 -0400 Subject: [PATCH 4/6] Functional --- .env_example | 13 +++++---- .gitignore | 2 ++ app.py | 81 +++++++++++++++++++++++++++++++++------------------- 3 files changed, 61 insertions(+), 35 deletions(-) diff --git a/.env_example b/.env_example index 25e7d50..470d39a 100644 --- a/.env_example +++ b/.env_example @@ -1,6 +1,7 @@ -OPENAI_API_KEY = -OPENAI_BASE_URL = -OPENAI_RESPONSES_PROMPT = -SEGMENT_DURATION = -TMP_AUDIO_PATH = -TMP_VIDEO_PATH = +INPUT_VIDEO_URL= +OPENAI_API_KEY= +OPENAI_BASE_URL= +OPENAI_TRANSCRIPTION_MODEL= +OPENAI_CHAT_SYSTEM_PROMPT= +OPENAI_CHAT_MODEL= +OPENAI_CHAT_N= diff --git a/.gitignore b/.gitignore index 6175add..bd4b8c9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ +__pycache__/* +tmp/* venv/* .env diff --git a/app.py b/app.py index 90a4dce..4d45bf1 100644 --- a/app.py +++ b/app.py @@ -1,15 +1,26 @@ import os import requests -from dotenv import load_dotenv from moviepy import VideoFileClip from openai import OpenAI from pydub import AudioSegment -DEFAULT_PROMPT = "You will be provided a video transcription for which you are to generate a blog post in Markdown format summarizing the video contents." +DEFAULT_PROMPT = "The user will provided a video transcription for which you are to generate a blog post in Markdown format summarizing the video contents. Please only output the blog post content." +VIDEO_URL = os.getenv('INPUT_VIDEO_URL', None) +OUTPUT_PATH = os.getenv('OUTPUT_PATH', 'tmp') +AUDIO_SEGMENT_DURATION = 30000 +OPENAI_API_KEY = os.getenv('OPENAI_API_KEY', None) +OPENAI_BASE_URL = os.getenv('OPENAI_BASE_URL', 'https://api.openai.com/v1') +OPENAI_TRANSCRIPTION_MODEL = os.getenv('OPENAI_TRANSCRIPTION_MODEL', 'whisper-1') +OPENAI_CHAT_SYSTEM_PROMPT = os.getenv('OPENAI_CHAT_SYSTEM_PROMPT', DEFAULT_PROMPT) +OPENAI_CHAT_MODEL = os.getenv('OPENAI_CHAT_MODEL', 'whisper-1') +OPENAI_CHAT_N = int(os.getenv('OPENAI_CHAT_N', '3')) -def main(args): - openai_client = OpenAI() +def main(): + openai_client = OpenAI( + base_url = OPENAI_BASE_URL, + api_key = OPENAI_API_KEY + ) return summarize_transcription( openai_client, transcribe_audio( @@ -21,39 +32,51 @@ def main(args): ) def get_video_from_url(): - video_file_url = os.getenv('INPUT_VIDEO_URL') - video_file_path = os.getenv('TMP_VIDEO_PATH' , '/tmp/video_summary_bot_tmp_video.mp4') - request = requests.get(video_file_url) - with open(video_file_path, 'wb') as f: - for chunk in requests.get(video_file_url).iter_content(chunk_size=255): + filename = VIDEO_URL.split('/')[-1] + with open(f"{OUTPUT_PATH}/{filename}", 'wb') as f: + for chunk in requests.get(VIDEO_URL).iter_content(chunk_size=255): if chunk: f.write(chunk) + return filename -def get_audio_from_video(): - tmp_audio_path = os.getenv('TMP_AUDIO_PATH', '/tmp/video_summary_bot_tmp_audio.wav') - video_file_path = os.getenv('TMP_VIDEO_PATH') - VideoFileClip(video_file_path).audio.write_audiofile(tmp_audio_path) - return AudioSegment.from_wav(tmp_audio_path) +def get_audio_from_video(video_filename): + VideoFileClip(f"{OUTPUT_PATH}/{video_filename}").audio.write_audiofile(f"{OUTPUT_PATH}/{video_filename}.wav") + audio = AudioSegment.from_wav(f"{OUTPUT_PATH}/{video_filename}.wav") + segments = [] + for i in range(0, len(audio), AUDIO_SEGMENT_DURATION): + segment = audio[i:i + AUDIO_SEGMENT_DURATION] + path = f"{OUTPUT_PATH}/audio_segment_{i // AUDIO_SEGMENT_DURATION}.wav" + segments.append(path) + segment.export(path, format='wav') + return segments -def transcribe_audio(openai_client, audio): - segment_duration = int(os.getenv('SEGMENT_DURATION', 30000)), - transcription_model = os.getenv('OPENAI_TRANSCRIPTION_MODEL', 'whisper-1') +def transcribe_audio(openai_client, audio_segments): return ' '.join([ openai_client.audio.transcriptions.create( - model=transcription_model, - file=each - ).text for each in [audio[i:i + segment_duration] for i in range(0, len(audio), segment_duration)] + model=OPENAI_TRANSCRIPTION_MODEL, + file=open(each, 'rb') + ).text for each in audio_segments ]) def summarize_transcription(openai_client, transcription): - prompt = os.getenv('OPENAI_RESPONSES_PROMPT', DEFAULT_PROMPT) - responses_model = os.getenv('OPENAI_RESPONSES_MODEL', 'whisper-1') - return client.responses.create( - model=responses_model, - instructions=prompt, - input=transcription - ) + return openai_client.chat.completions.create( + model=OPENAI_CHAT_MODEL, + n=OPENAI_CHAT_N, + messages = [ + {"role": "developer", "content": OPENAI_CHAT_SYSTEM_PROMPT}, + {"role": "user", "content": transcription} + ] + ).choices + +def setup(): + from dotenv import load_dotenv + load_dotenv() + +def cleanup(): + os.rmdir(OUTPUT_PATH) if __name__ == '__main__': - load_dotenv() - main(parser.parse_args()) + setup() + for each in main(): + print("========") + print(each.message.content) -- 2.45.2 From f1b778755366960257b9412e8f02f7a3b352adb6 Mon Sep 17 00:00:00 2001 From: Eric Meehan Date: Fri, 28 Mar 2025 10:33:04 -0400 Subject: [PATCH 5/6] Use completions endpoint --- app.py | 13 +++++-------- prompt.py | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 8 deletions(-) create mode 100644 prompt.py diff --git a/app.py b/app.py index 4d45bf1..ee5d2ed 100644 --- a/app.py +++ b/app.py @@ -5,7 +5,8 @@ from moviepy import VideoFileClip from openai import OpenAI from pydub import AudioSegment -DEFAULT_PROMPT = "The user will provided a video transcription for which you are to generate a blog post in Markdown format summarizing the video contents. Please only output the blog post content." +from prompt import DEFAULT_PROMPT + VIDEO_URL = os.getenv('INPUT_VIDEO_URL', None) OUTPUT_PATH = os.getenv('OUTPUT_PATH', 'tmp') AUDIO_SEGMENT_DURATION = 30000 @@ -59,13 +60,9 @@ def transcribe_audio(openai_client, audio_segments): ]) def summarize_transcription(openai_client, transcription): - return openai_client.chat.completions.create( + return openai_client.completions.create( model=OPENAI_CHAT_MODEL, - n=OPENAI_CHAT_N, - messages = [ - {"role": "developer", "content": OPENAI_CHAT_SYSTEM_PROMPT}, - {"role": "user", "content": transcription} - ] + prompt=OPENAI_CHAT_SYSTEM_PROMPT.format(transcription) ).choices def setup(): @@ -79,4 +76,4 @@ if __name__ == '__main__': setup() for each in main(): print("========") - print(each.message.content) + print(each.text) diff --git a/prompt.py b/prompt.py new file mode 100644 index 0000000..be9dc5f --- /dev/null +++ b/prompt.py @@ -0,0 +1,19 @@ +DEFAULT_PROMPT = """ +You are a professional blog writer and SEO expert. You will be given the transcript of a live stream for which you are to generate +a blog post. + +Instructions: + - The blog post title should be SEO optimized. + - The blog post should be properly and beautifully formatted using markdown. + - Each blog post should have around 5 sections with 3 sub-sections each. + - Each sub section should have about 3 paragraphs. + - Sub-section headings should be clearly marked. + - Ensure that the content flows logically from one section to another, maintaining coherence and readability. + - In the final section, provide a forward-looking perspective on the topic and a conclusion. + - Make the blog post sound as human and as engaging as possible, add real world examples and make it as informative as possible. + - Please ensure proper and standard markdown formatting always. + +Transcription: {} + +Blog Post: +""" -- 2.45.2 From eb1cfb79b6bc68d52adae7cc311c4145f1fb589b Mon Sep 17 00:00:00 2001 From: Eric Meehan Date: Fri, 28 Mar 2025 11:07:51 -0400 Subject: [PATCH 6/6] Gitea actions edit --- .gitea/workflows/execute.yaml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.gitea/workflows/execute.yaml b/.gitea/workflows/execute.yaml index c6b608a..6cecf12 100644 --- a/.gitea/workflows/execute.yaml +++ b/.gitea/workflows/execute.yaml @@ -1,10 +1,12 @@ -name: Execute video summary bot +name: execute + on: workflow_dispatch: inputs: video-url: description: "URL for the video to be analyzed" required: true + jobs: Python: runs-on: ubuntu-latest @@ -13,13 +15,15 @@ jobs: OPENAI_BASE_URL: "http://192.168.1.168/v1" steps: - uses: actions/checkout@v4 - - name: Set up Python - uses: actions/setup-python@v5 + + - uses: actions/setup-python@v5 with: python-version: "3.10" + - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt + - name: Run applicaiton run: python app.py -- 2.45.2