Skip to content

Commit

Permalink
podcast controller, audio summarizer
Browse files Browse the repository at this point in the history
  • Loading branch information
michelson committed Jul 1, 2024
1 parent b13898d commit e3a876f
Show file tree
Hide file tree
Showing 13 changed files with 550 additions and 78 deletions.
4 changes: 2 additions & 2 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,12 @@ gem "omniauth-discord"
gem "omniauth-twitch"
gem "dotenv-rails", groups: [:development, :test]

gem "ruby-openai", "~> 4.2"
gem "ruby-openai", "~> 7.1"
gem "qdrant-ruby", "~> 0.9.2"
# gem "pgvector", "~> 0.2"

# gem "plain-rails", path: "/Users/michelson/Documents/rubyonrails/plain"
gem "plain-rails", github: "chaskiq/plain", branch: "documents" # path: "/Users/michelson/Documents/rubyonrails/plain"
# gem "plain-rails", github: "chaskiq/plain", branch: "documents" # path: "/Users/michelson/Documents/rubyonrails/plain"
# gem "plain-rails", "0.1.2" #, path: "/Users/michelson/Documents/rubyonrails/plain"

# sentry
Expand Down
49 changes: 12 additions & 37 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,6 @@ GIT
specs:
ruby-oembed (0.16.1)

GIT
remote: https://github.com/chaskiq/plain.git
revision: c775ad400d207f0a8d972bbae4da1360919fa9a5
branch: documents
specs:
plain-rails (0.1.2)
front_matter_parser (~> 1.0.1)
langchainrb (~> 0.6.12)
qdrant-ruby (~> 0.9.2)
rails (>= 7.0.6)
redcarpet (~> 2.3.0)
ruby-openai (~> 4.2)

GIT
remote: https://github.com/hotwired/turbo-rails.git
revision: e376852bfb273f69f4ebb54cf516b99fcbaa7acb
Expand Down Expand Up @@ -228,7 +215,6 @@ GEM
rack-session (>= 1, < 3)
aws-sigv4 (1.8.0)
aws-eventstream (~> 1, >= 1.0.2)
baran (0.1.7)
base64 (0.2.0)
bcrypt (3.1.19)
bcrypt_pbkdf (1.1.0)
Expand All @@ -249,7 +235,6 @@ GEM
xpath (~> 3.2)
chunky_png (1.4.0)
coderay (1.1.3)
colorize (0.8.1)
concurrent-ruby (1.2.3)
connection_pool (2.4.1)
crass (1.0.6)
Expand Down Expand Up @@ -284,26 +269,26 @@ GEM
drb (2.2.1)
ed25519 (1.3.0)
erubi (1.12.0)
event_stream_parser (1.0.0)
factory_bot (6.2.1)
activesupport (>= 5.0.0)
factory_bot_rails (6.2.0)
factory_bot (~> 6.2.0)
railties (>= 5.0.0)
faker (3.2.0)
i18n (>= 1.8.11, < 2)
faraday (2.7.10)
faraday-net_http (>= 2.0, < 3.1)
ruby2_keywords (>= 0.0.4)
faraday (2.9.2)
faraday-net_http (>= 2.0, < 3.2)
faraday-multipart (1.0.4)
multipart-post (~> 2)
faraday-net_http (3.0.2)
faraday-net_http (3.1.0)
net-http
ffi (1.15.5)
ffi-compiler (1.0.1)
ffi (>= 1.0.0)
rake
friendly_id (5.5.0)
activerecord (>= 4.0.0)
front_matter_parser (1.0.1)
geocoder (1.8.2)
globalid (1.2.1)
activesupport (>= 6.1)
Expand Down Expand Up @@ -333,8 +318,6 @@ GEM
jsbundling-rails (1.1.2)
railties (>= 6.0.0)
json (2.6.3)
json-schema (4.0.0)
addressable (>= 2.8)
jwt (2.7.1)
kaminari (1.2.2)
activesupport (>= 4.1.0)
Expand All @@ -352,12 +335,6 @@ GEM
activemodel (>= 6.0.0)
activesupport (>= 6.0.0)
redis (>= 4.2, < 6)
langchainrb (0.6.12)
baran (~> 0.1.6)
colorize (~> 0.8.1)
json-schema (~> 4.0.0)
tiktoken_ruby (~> 0.0.5)
zeitwerk (= 2.6.11)
language_server-protocol (3.17.0.3)
lint_roller (1.1.0)
llhttp-ffi (0.4.0)
Expand Down Expand Up @@ -391,8 +368,10 @@ GEM
zeitwerk (~> 2.5)
msgpack (1.7.2)
multi_xml (0.6.0)
multipart-post (2.3.0)
multipart-post (2.4.1)
mutex_m (0.2.0)
net-http (0.4.1)
uri
net-imap (0.4.11)
date
net-protocol
Expand Down Expand Up @@ -509,7 +488,6 @@ GEM
zeitwerk (~> 2.6)
rainbow (3.1.1)
rake (13.2.1)
redcarpet (2.3.0)
redis (5.0.6)
redis-client (>= 0.9.0)
redis-client (0.22.1)
Expand Down Expand Up @@ -540,13 +518,13 @@ GEM
rubocop-performance (1.18.0)
rubocop (>= 1.7.0, < 2.0)
rubocop-ast (>= 0.4.0)
ruby-openai (4.3.2)
ruby-openai (7.1.0)
event_stream_parser (>= 0.3.0, < 2.0.0)
faraday (>= 1)
faraday-multipart (>= 1)
ruby-progressbar (1.13.0)
ruby-vips (2.1.4)
ffi (~> 1.12)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
selenium-webdriver (4.10.0)
rexml (~> 3.2, >= 3.2.5)
Expand Down Expand Up @@ -609,9 +587,6 @@ GEM
activerecord (>= 6.0)
stripe (8.6.0)
thor (1.3.1)
tiktoken_ruby (0.0.5-arm64-darwin)
tiktoken_ruby (0.0.5-x86_64-darwin)
tiktoken_ruby (0.0.5-x86_64-linux)
timeout (0.4.1)
transbank-sdk (3.0.2)
json (~> 2.0)
Expand All @@ -621,6 +596,7 @@ GEM
unf_ext
unf_ext (0.0.8.2)
unicode-display_width (2.4.2)
uri (0.13.0)
version_gem (1.1.3)
warden (1.2.9)
rack (>= 2.0.9)
Expand Down Expand Up @@ -686,7 +662,6 @@ DEPENDENCIES
omniauth-twitch
omniauth-twitter
pg (~> 1.1)
plain-rails!
pry
puma
qdrant-ruby (~> 0.9.2)
Expand All @@ -700,7 +675,7 @@ DEPENDENCIES
rspec-rails!
rspec-support!
ruby-oembed!
ruby-openai (~> 4.2)
ruby-openai (~> 7.1)
rubyzip (~> 2.3)
selenium-webdriver
sentry-rails
Expand Down
32 changes: 32 additions & 0 deletions app/controllers/podcasts_controller.rb
Original file line number Diff line number Diff line change
@@ -1,2 +1,34 @@
class PodcastsController < ApplicationController


def show
@user = User.find_by(username: params[:user_id])
end

def edit
@user = User.find_by(username: params[:user_id])
@info = @user.podcaster_info || @user.build_podcaster_info
end

def update
@user = User.find_by(username: params[:user_id])
@info = @user.podcaster_info || @user.build_podcaster_info
@info.update(podcaster_params)
redirect_to user_podcast_path(@user.username)
end


def create
@user = User.find_by(username: params[:user_id])
@info = @user.podcaster_info || @user.build_podcaster_info
@info.update(podcaster_params)
redirect_to user_podcast_path(@user.username)
end


private

def podcaster_params
params.require(:podcaster_info).permit(:about, :title, :description)
end
end
7 changes: 7 additions & 0 deletions app/models/track.rb
Original file line number Diff line number Diff line change
Expand Up @@ -335,4 +335,11 @@ def self.get_tracks_by_tag(tag)
tag = tag.downcase
includes(:user).where("? = ANY (tags)", tag)
end

def podcast_summarizer
file_path = ActiveStorage::Blob.service.path_for(mp3_audio.key)
summarizer = AudioSummarizer.new(file_path)
transcription = summarizer.summarize
self.update(description: transcription)
end
end
88 changes: 88 additions & 0 deletions app/services/audio_summarizer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# app/services/audio_summarizer.rb
require 'openai'
require 'securerandom'
require 'tmpdir'

class AudioSummarizer
MAX_FILE_SIZE_MB = 20
MAX_FILE_SIZE_BYTES = MAX_FILE_SIZE_MB * 1024 * 1024

def initialize(file_path)
@file_path = file_path
@client = OpenAI::Client.new(access_token: ENV["OPENAI_API_KEY"], log_errors: true)
end

def summarize
chunk_paths = split_audio_by_silence
transcriptions = chunk_paths.map { |chunk_path|
Rails.logger.info("CHUNK PATH #{chunk_path}")
transcribe_chunk(chunk_path)
}
text = transcriptions.join("\n")
sumarize_transcription(text)
ensure
cleanup_temp_files(chunk_paths)
end

def sumarize_transcription(text)
response = @client.chat(
parameters: {
model: "gpt-4o",
messages: [
{ role: "assistant", content: "summarize the podcast transcription for the Rauversion platform"},
{ role: "user", content: text}
],
temperature: 0.7,
})
response.dig("choices", 0, "message", "content")
end

private

def split_audio_by_silence
output_dir = Dir.mktmpdir
chunk_pattern = File.join(output_dir, 'chunk_%03d.wav')

# Split the file by silence detection
`ffmpeg -i "#{@file_path}" -af silencedetect=noise=-30dB:d=0.5 -f segment -segment_time 30 -c:a pcm_s16le "#{chunk_pattern}"`

chunk_paths = Dir.glob("#{output_dir}/chunk_*.wav")
valid_chunks = []

chunk_paths.each do |chunk_path|
if File.size(chunk_path) > MAX_FILE_SIZE_BYTES
# Re-split large chunks further
valid_chunks += split_large_chunk(chunk_path, output_dir)
File.delete(chunk_path) # Delete the original large chunk
else
valid_chunks << chunk_path
end
end

valid_chunks
end

def split_large_chunk(chunk_path, output_dir)
temp_pattern = File.join(output_dir, 'temp_chunk_%03d.wav')

`ffmpeg -i "#{chunk_path}" -f segment -segment_time 15 -c:a pcm_s16le "#{temp_pattern}"`

Dir.glob("#{output_dir}/temp_chunk_*.wav").select do |path|
File.size(path) <= MAX_FILE_SIZE_BYTES
end
end

def transcribe_chunk(chunk_path)
response = @client.audio.transcribe(
parameters: {
file: File.open(chunk_path),
model: "whisper-1"
}
)
response['text']
end

def cleanup_temp_files(files)
files.each { |file| File.delete(file) if File.exist?(file) }
end
end
47 changes: 9 additions & 38 deletions app/services/dalle.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,45 +5,16 @@ class Dalle
BASE_URL = "https://api.openai.com/v1/"

def initialize(token = nil)
token ||= ENV["OPENAI_API_KEY"]

@conn = Faraday.new(url: BASE_URL) do |conn|
conn.request :json
conn.response :json
conn.authorization :Bearer, token
conn.adapter Faraday.default_adapter
end
@client = OpenAI::Client.new(access_token: token || ENV["OPENAI_API_KEY"], log_errors: true)
end

# prompt: A text description of the desired image(s).
# Options should include:
# - :n, an integer to denote the number of images to generate.
# - :size, the size of the generated images.
# - :response_format, the format in which the generated images are returned.
# - :user, a unique identifier representing your end-user
def images(prompt, options = {})
options[:prompt] = prompt
response = @conn.post do |req|
req.url "images/generations"
req.headers["Content-Type"] = "application/json"
req.body = options.to_json
end

handle_response(response)
end

private

def handle_response(response)
if response.success?
body = response.body
if body["data"] && body["data"].first["url"]
{ok: body["data"].first["url"]}
else
{error: nil}
end
else
{error: response.body["error"]}
end
def generate(prompt: nil)
response = @client.images.generate(parameters: {
prompt: prompt,
model: "dall-e-3",
size: "1024x1024",
quality: "hd"
})
puts response.dig("data", 0, "url")
end
end
22 changes: 22 additions & 0 deletions app/views/podcasts/_footer.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<<footer class="border-t border-subtle bg-default py-10 pb-40 sm:py-16 sm:pb-32 lg:hidden">
<div class="mx-auto px-4 sm:px-6 md:max-w-2xl md:px-4">
<section>
<h2 class="flex items-center font-mono text-sm font-medium leading-7 text-default">
<svg aria-hidden="true" viewBox="0 0 10 10" class="h-2.5 w-2.5">
<path d="M0 5a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V5Z" class="fill-violet-300"></path>
<path d="M6 1a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V1Z" class="fill-pink-300"></path>
</svg>
<span class="ml-2.5">About</span>
</h2>
<p class="mt-2 text-base leading-7 text-subtle lg:line-clamp-4">In this show, Eric and Wes dig deep to get to the facts with guests who have been labeled villains by a society quick to judge, without actually getting the full story. Tune in every Thursday to get to the truth with another misunderstood outcast as they share the missing context in their tragic tale.</p>
<button type="button" class="mt-2 hidden text-sm font-bold leading-6 text-pink-500 hover:text-pink-700 active:text-pink-900 lg:inline-block">Show more</button>
</section>
<h2 class="mt-8 flex items-center font-mono text-sm font-medium leading-7 text-default">
<svg aria-hidden="true" viewBox="0 0 11 12" class="h-3 w-auto fill-slate-300">
<path d="M5.019 5a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5Zm3.29 7c1.175 0 2.12-1.046 1.567-2.083A5.5 5.5 0 0 0 5.019 7 5.5 5.5 0 0 0 .162 9.917C-.39 10.954.554 12 1.73 12h6.578Z"></path>
</svg>
<span class="ml-2.5">Hosted by</span>
</h2>
<div class="mt-2 flex gap-6 text-sm font-bold leading-7 text-default">Eric Gordon <span aria-hidden="true" class="text-slate-400">/</span>Wes Mantooth </div>
</div>
</footer>>
Loading

0 comments on commit e3a876f

Please sign in to comment.