feature/share-libs
rb 350 lines 12.3 KB
Raw
1 module Api
2 module V1
3 # Fix this later
4 # rubocop:disable Metrics/ClassLength
5 class AudioFileController < ApplicationController
6 skip_before_action :verify_authenticity_token
7
8 MAX_AUDIO_LENGTH_IN_MINUTES = 8
9
10 def splitfire
11 @audio_files = AudioFile.order(Arel.sql('random()')).limit(5)
12 render_user_files
13 end
14
15 def splitfire_detail
16 return render_error 'We need the id.' if params[:id].nil?
17
18 begin
19 @audio_file_id = Integer(params[:id])
20 rescue ArgumentError
21 return render_error 'id should be integer.'
22 end
23
24 @audio_file = AudioFile.find_by(id: @audio_file_id)
25
26 return render_error 'File not found' unless @audio_file
27
28 render_file
29 end
30
31 def user_files
32 return render_error 'We need the userId.' if params[:userId].nil?
33
34 begin
35 user_id = Integer(params[:userId])
36 rescue ArgumentError
37 return render_error 'userId should be integer.'
38 end
39
40 @audio_files = AudioFile.where(user_id: user_id).all.with_attached_source_file.order(created_at: :desc)
41 render_user_files
42 end
43
44 # Split request
45 def split
46 logger.info 'Split request'
47 return render_error 'You must login.' unless current_user.present?
48
49 provider_id = params[:provider_id]
50 return render_error 'We need the provider id.' if provider_id.nil?
51
52 process_song_provider(provider_id)
53 end
54
55 def karaoke
56 return unless valid_request?
57
58 if @audio_file.karaoke_file.present?
59 render json: {
60 code: 500,
61 message: 'We are processing this file.'
62 }
63 return
64 end
65
66 AudioFileKarokoweStatus.new(
67 audio_file_id: @audio_file.id,
68 status: :processing,
69 processing_progress: 3
70 ).save
71
72 KarokoweJob.perform_later(@audio_file.id)
73
74 render json: {
75 code: 200,
76 message: 'KaroKowe is generating karaoke file for you.'
77 }
78 end
79
80 def guitar_backing_track
81 return unless valid_request?
82
83 if @audio_file.guitar_backing_track_file.present?
84 render json: {
85 code: 500,
86 message: 'We are processing this file.'
87 }
88 return
89 end
90
91 GuitarBackingTrackJob.perform_later(@audio_file.id)
92
93 render json: {
94 code: 200,
95 message: 'We are generating guitar backing track file for you.'
96 }
97 end
98
99 def bass_backing_track
100 return unless valid_request?
101
102 if @audio_file.bass_backing_track_file.present?
103 render json: {
104 code: 500,
105 message: 'We are processing this file.'
106 }
107 return
108 end
109
110 BassBackingTrackJob.perform_later(@audio_file.id)
111
112 render json: {
113 code: 200,
114 message: 'We are generating bass backing track file for you.'
115 }
116 end
117
118 def drum_backing_track
119 return unless valid_request?
120
121 if @audio_file.drum_backing_track.present?
122 render json: {
123 code: 500,
124 message: 'We are processing this file.'
125 }
126 return
127 end
128
129 DrumBackingTrackJob.perform_later(@audio_file.id)
130
131 render json: {
132 code: 200,
133 message: 'We are generating drum backing track file for you.'
134 }
135 end
136
137 private
138
139 def render_error(message)
140 logger.error message
141 render json: { code: 500, message: message, audio_files: [] }, status: :unprocessable_entity
142 end
143
144 def render_file
145 render json: {
146 code: 200,
147 message: 'OK.',
148 audio_file: @audio_file.as_json_packed.merge(render_file_results)
149 }, status: :ok
150 end
151
152 def render_file_results
153 {
154 results: @audio_file.splitfire_results.map do |result|
155 result.as_json(only: :id).merge({
156 filename: result.source_file.filename,
157 source_file: polymorphic_url(result.source_file),
158 length: result.length,
159 onset: result.onset
160 })
161 end
162 }
163 end
164
165 # Render audio_file as json
166 def render_success_split_request(audio_file)
167 render json: { code: 200, message: 'SplitFire is processing this file for you.', audio_file: audio_file },
168 status: :ok
169 end
170
171 def render_user_files
172 audio_files = @audio_files.map do |audio_file|
173 filename = ''
174 source_file = ''
175
176 if !audio_file.source_file.nil? && audio_file.source_file.attached?
177 filename = audio_file.source_file.filename
178 source_file = polymorphic_url(audio_file.source_file) unless audio_file.source_file.nil?
179 end
180
181 # Karaoke file
182 if !audio_file.karaoke_file.nil? && !audio_file.karaoke_file.source_file.nil? && audio_file.karaoke_file.source_file.attached?
183 karaoke_filename = audio_file.karaoke_file.source_file.filename
184 unless audio_file.karaoke_file.source_file.nil?
185 karaoke_source_file = polymorphic_url(audio_file.karaoke_file.source_file)
186 end
187
188 karaoke_file = audio_file.karaoke_file.as_json.merge({
189 filename: karaoke_filename,
190 source_file: karaoke_source_file
191 })
192 end
193
194 # Guitar backing track
195 if audio_file.guitar_backing_track_file.present?
196 if audio_file.guitar_backing_track_file.source_file.attached?
197 filename = audio_file.guitar_backing_track_file.source_file.filename
198 source_file = polymorphic_url(audio_file.guitar_backing_track_file.source_file)
199 end
200
201 guitar_backing_track_file = audio_file.guitar_backing_track_file.as_json.merge({
202 filename: filename,
203 source_file: source_file
204 })
205 end
206
207 # Bass backing track
208 if audio_file.bass_backing_track_file.present?
209 if audio_file.bass_backing_track_file.source_file.attached?
210 filename = audio_file.bass_backing_track_file.source_file.filename
211 source_file = polymorphic_url(audio_file.bass_backing_track_file.source_file)
212 end
213
214 bass_backing_track_file = audio_file.bass_backing_track_file.as_json.merge({
215 filename: filename,
216 source_file: source_file
217 })
218 end
219
220 # Drum backing track
221 if audio_file.drum_backing_track.present?
222 if audio_file.drum_backing_track.source_file.attached?
223 filename = audio_file.drum_backing_track.source_file.filename
224 source_file = polymorphic_url(audio_file.drum_backing_track.source_file)
225 end
226
227 drum_backing_track_file = audio_file.drum_backing_track.as_json.merge({
228 filename: filename,
229 source_file: source_file
230 })
231 end
232
233 audio_file.as_json.merge({
234 user: audio_file.user.as_json,
235 status: audio_file.audio_file_split_status,
236 status_karaoke: audio_file.audio_file_karokowe_status,
237 filename: filename,
238 source_file: source_file,
239 youtube_thumbnail: audio_file.youtube_thumbnail,
240 results: audio_file.splitfire_results.map do |result|
241 result.as_json.merge({
242 filename: result.source_file.filename,
243 source_file: polymorphic_url(result.source_file)
244 })
245 end,
246 karaoke_file: karaoke_file,
247 guitar_backing_track_file: guitar_backing_track_file,
248 bass_backing_track_file: bass_backing_track_file,
249 drum_backing_track_file: drum_backing_track_file
250 })
251 end
252
253 render json: {
254 code: 200,
255 message: 'OK',
256 audio_files: audio_files
257 }
258 end
259
260 # provider_id is the ActiveRecord id of the SongProvider
261 def process_song_provider(provider_id)
262 # Find song provider by provider_id
263 song_provider = SongProvider.find(provider_id)
264 # Make sure the song provider is found and provider_type is youtube
265 return render_error 'Song provider not found.' if song_provider.nil? || song_provider.provider_type != 'youtube'
266
267 # Make sure it's a valid youtube url
268 return render_error 'Invalid youtube url.' unless valid_youtube_link?(song_provider.preview_url)
269
270 # Make sure video length is valid
271 unless valid_audio_length?(song_provider)
272 return render_error "Less than #{MAX_AUDIO_LENGTH_IN_MINUTES} minutes, please."
273 end
274
275 # We are good to go
276 process_youtube_link(song_provider)
277 end
278
279 def process_youtube_link(song_provider)
280 audio_file = AudioFile.new(song_provider_id: song_provider.id, status: :downloading, progress: 0)
281 return render_error 'Cannot create AudioFile record.' unless audio_file.save
282
283 YoutubeToAudioJob.perform_later(song_provider.id)
284 render_success_split_request(song_provider.audio_file)
285 end
286
287 def valid_audio_length?(song_provider)
288 length_string = `yt-dlp --get-duration #{song_provider.preview_url}`
289 length_strings = length_string.split(':')
290 # We should have two elements, minutes and seconds
291 return false unless length_strings.count == 2
292
293 # We only care about the minutes part
294 length_strings.first.to_i <= MAX_AUDIO_LENGTH_IN_MINUTES
295 end
296
297 def valid_url?(url)
298 uri = URI.parse(url)
299 uri.is_a?(URI::HTTP) && !uri.host.nil?
300 rescue URI::InvalidURIError
301 false
302 end
303
304 def valid_youtube_link?(youtube_video_url)
305 uri = URI.parse(youtube_video_url)
306
307 return false unless !uri.host.nil? && (uri.host.include?('youtube.com') || uri.host.include?('youtu.be'))
308
309 # To make things simple, we only recognize youtube.com and youtu.be
310 youtube_video_url =~ %r{^https*://[www.]*youtu[.]{1}*be[.com]{4}*/[watch?v=]*([a-zA-Z0-9_-]*)}
311 end
312
313 def valid_request?
314 audio_id_param = params[:audioId]
315
316 unless !audio_id_param.nil? && !audio_id_param.empty?
317 render json: {
318 code: 500,
319 message: 'We need the audioId.',
320 audio_files: []
321 }
322 return false
323 end
324
325 begin
326 audio_id = Integer(audio_id_param)
327 rescue ArgumentError
328 render json: {
329 code: 500,
330 message: 'audioId should be integer.',
331 audio_files: []
332 }
333 return false
334 end
335
336 @audio_file = AudioFile.find_by(id: audio_id)
337
338 unless @audio_file.present?
339 render json: {
340 code: 500,
341 message: 'I dont know what is happening. Sorry.'
342 }
343 return false
344 end
345
346 true
347 end
348 end
349 end
350 end