Skip to content

Instantly share code, notes, and snippets.

@jmarsh24
Created March 10, 2021 08:58
Show Gist options
  • Save jmarsh24/f7fe39440b278bf25bb1c0df61ae917e to your computer and use it in GitHub Desktop.
Save jmarsh24/f7fe39440b278bf25bb1c0df61ae917e to your computer and use it in GitHub Desktop.
class Video::MusicRecognition::AcrCloud
class << self
def fetch(youtube_id)
new(youtube_id).update_video
end
end
def initialize(youtube_id)
@youtube_id = youtube_id
@video = Video.find_by(youtube_id: youtube_id)
end
def update_video
@video.update(video_params)
end
private
def file_path
@file_path ||= Audio.import(@youtube_id)
end
def acr_cloud_response
@acr_cloud_reponse ||= Client.send_audio(file_path)
end
def video_params
{
acr_response_code: acr_response_code,
spotify_album_id: spotify_album_id,
spotify_album_name: spotify_album_name,
spotify_artist_id: spotify_artist_id,
spotify_artist_id_1: spotify_artist_id_1,
spotify_artist_id_2: spotify_artist_id_2,
spotify_artist_name: spotify_artist_name,
spotify_artist_name_1: spotify_artist_name_1,
spotify_artist_name_2: spotify_artist_name_2,
spotify_track_id: spotify_track_id,
spotify_track_name: spotify_track_name,
acr_cloud_artist_name: acr_cloud_artist_name,
acr_cloud_artist_name_1: acr_cloud_artist_name_1,
acr_cloud_album_name: acr_cloud_album_name,
acr_cloud_track_name: acr_cloud_track_name,
youtube_song_id: youtube_song_id,
isrc: isrc
}
end
def acr_response_code
return if parsed_acr_cloud_data.dig("status", "code").blank?
@acr_response_code ||= parsed_acr_cloud_data.dig("status", "code")
end
def youtube_song_id
return if parsed_acr_cloud_data.deep_find("youtube").blank?
@youtube_song_id ||= parsed_acr_cloud_data.deep_find("youtube")["vid"]
end
def isrc
return if parsed_acr_cloud_data.deep_find("external_ids").blank?
@isrc ||= parsed_acr_cloud_data.deep_find("external_ids")["isrc"]
end
def spotify_params
return if parsed_acr_cloud_data.deep_find("spotify").blank?
@spotify_params ||= parsed_acr_cloud_data.deep_find("spotify")
end
def spotify_album_id
return if spotify_params.blank?
@spotify_album_id ||= spotify_params.dig("album", "id")
end
def spotify_artist_id
return if spotify_params.blank?
@spotify_artist_id ||= spotify_params.dig("artists", 0, "id")
end
def spotify_artist_id_1
return if spotify_params.blank?
@spotify_artist_id_1 ||= spotify_params.dig("artists", 1, "id")
end
def spotify_artist_id_2
return if spotify_params.blank?
@spotify_artist_id_2 ||= spotify_params.dig("artists", 2, "id")
end
def spotify_track_id
return if spotify_params.blank?
@spotify_track_id ||= spotify_params.dig("track", "id")
end
def spotify_album_name
return if spotify_album_id.blank?
@spotify_album_name ||= RSpotify::Album.find(@spotify_album_id).name
end
def spotify_artist_name
return if spotify_artist_id.blank?
@spotify_artist_name ||= RSpotify::Artist.find(@spotify_artist_id).name
end
def spotify_artist_name_1
return if spotify_artist_id_1.blank?
@spotify_artist_name_1 ||= RSpotify::Artist.find(@spotify_artist_id_1).name
end
def spotify_artist_name_2
return if @spotify_artist_id_2.blank?
@spotify_artist_name_2 ||= RSpotify::Artist.find(@spotify_artist_id_2).name
end
def spotify_track_name
return if @spotify_track_id.blank?
@spotify_track_name ||= RSpotify::Track.find(@spotify_track_id).name
end
def acr_cloud_artists
return if parsed_acr_cloud_data.deep_find("artists").blank?
parsed_acr_cloud_data.deep_find("artists")
end
def acr_cloud_artist_name
return if acr_cloud_artists.blank?
acr_cloud_artists.dig(0, "name")
end
def acr_cloud_artist_name_1
return if acr_cloud_artists.blank?
acr_cloud_artists.dig(1, "name")
end
def acr_cloud_album_name
return if parsed_acr_cloud_data.deep_find("album").blank?
parsed_acr_cloud_data.deep_find("album").dig("name")
end
def acr_cloud_track_name
return if parsed_acr_cloud_data.deep_find("title").blank?
parsed_acr_cloud_data.deep_find("title")
end
def parsed_acr_cloud_data
@parsed_acr_cloud_data ||= JSON.parse(acr_cloud_response).extend Hashie::Extensions::DeepFind
end
end
class AcrMusicMatchWorker
include Sidekiq::Worker
sidekiq_options queue: :default, retry: false
def perform(youtube_id)
Video::MusicRecognition::AcrCloud.fetch(youtube_id)
end
end
class Video::MusicRecognition::AcrCloud::Audio
class << self
def import(youtube_id)
new(youtube_id).import
end
end
def initialize(youtube_id)
@youtube_id = youtube_id
@youtube_video_audio = fetch_by_id(youtube_id)
@video = Video.find_by(youtube_id: youtube_id)
end
def import
transcode
output_file_path
end
private
def fetch_by_id(_youtube_id)
YoutubeDL.download(
"https://www.youtube.com/watch?v=#{@youtube_id}",
{ format: "140", output: "~/environment/data/audio/%(id)s.mp3" }
)
end
def audio_file_path
@youtube_video_audio.filename.to_s
end
def output_file_path
calculate_time
audio_file_path.gsub(/.mp3/, "_#{@time_1}_#{@time_2}.mp3")
end
def calculate_time
@time_1 = @youtube_video_audio.duration / 2
@time_2 = @time_1 + 20
end
def transcode
song = FFMPEG::Movie.new(audio_file_path)
song.transcode(output_file_path, { custom: %W[-ss #{@time_1} -to #{@time_2}] })
end
end
class Video::MusicRecognition::AcrCloud::Client
HTTP_METHOD = "POST".freeze
HTTP_URI = "/v1/identify".freeze
DATA_TYPE = "audio".freeze
SIGNATURE_VERSION = "1".freeze
TIMESTAMP = Time.now.utc.to_i.to_s.freeze
ACCESS_KEY = ENV["ACRCLOUD_ACCESS_KEY"]
ACCESS_SECRET = ENV["ACRCLOUD_SECRET_KEY"]
REQ_URL = "http://identify-eu-west-1.acrcloud.com/v1/identify".freeze
class << self
def send_audio(file_path)
new(file_path).send_audio_file_to_acr_cloud
end
end
def initialize(file_path)
@file_path = file_path
end
def send_audio_file_to_acr_cloud
faraday = Faraday.new do |f|
f.request :multipart
f.request :url_encoded
f.adapter :net_http
end
response = faraday.post(url, body)
response.body
end
private
def body
{
sample: sample_file,
access_key: ACCESS_KEY,
data_type: DATA_TYPE,
signature_version: SIGNATURE_VERSION,
signature: signature,
sample_bytes: sample_bytes,
timestamp: TIMESTAMP
}
end
def sample_file
Faraday::UploadIO.new(@file_path, "audio/mp3")
end
def url
URI.parse(REQ_URL)
end
def sample_bytes
File.size(@file_path)
end
def unsigned_string
"#{HTTP_METHOD}\n#{HTTP_URI}\n#{ACCESS_KEY}\n#{DATA_TYPE}\n#{SIGNATURE_VERSION}\n#{TIMESTAMP}"
end
def digest
OpenSSL::Digest.new("sha1")
end
def signature
Base64.encode64(OpenSSL::HMAC.digest(digest, ACCESS_SECRET, unsigned_string)).strip
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment