audio_to_youtube 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/audio_to_youtube.rb +326 -0
  3. metadata +44 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 222b2488455a1f11c03853d3772db05f473aab73
4
+ data.tar.gz: 3d62868b920f7e694913b8cec93eff7b6e8a9b83
5
+ SHA512:
6
+ metadata.gz: 80e4dad589ee2596966310100151262afbe3f486c767159581ebbbcb1032b76ad803851c27ecbab33f8ad7718ba6955c16ff65b602722eb2a273d2a694d3ecc0
7
+ data.tar.gz: 96eac8616a78146fb75764e391fb6e8f5045a14d07ca8d156cf16c2d64d37ac07e04e03ac3269f529c41f7b0622cc1d5e2c738e64b23d5fff65ebde65be8db9b
@@ -0,0 +1,326 @@
1
+ class AudioToYoutube
2
+ @outputFile = 'out.mp4'
3
+ @tempFolder = 'temp'
4
+ @tempBg = 'bg.jpg'
5
+ @backgroundBase = 'media/bg-720.png'
6
+ @ffmpegDir = 'setme'
7
+ @imagemagickDir = 'setme'
8
+ private
9
+ def self.exitWithError(message)
10
+ puts ('\nError: ' + message)
11
+ end
12
+
13
+ ################################################################################
14
+ # Generates the background art for the video
15
+ def self.generateBackground(audio, backgroundBase, output, blur, text)
16
+
17
+ blur ||= false
18
+ text ||= ""
19
+
20
+ # extract the album art
21
+ tempArt = "#{Dir.pwd}/art.png"
22
+ cmd = ["ffmpeg"]
23
+ cmd += ['-y']
24
+ cmd += ["-i #{audio}"]
25
+ cmd += ['-acodec none']
26
+ cmd += [tempArt]
27
+ puts 'Extracting album art...'
28
+ begin
29
+ unless Dir.exist?(@tempFolder) ||
30
+ Dir.mkdir(@tempFolder)
31
+ puts p cmd.join(' ')
32
+ end
33
+ rescue
34
+ exitWithError('Couldn\'t find album art in the audio file. ' +
35
+ 'Use the -background option to select an image file.')
36
+ end
37
+
38
+ # now, build the background image
39
+ cmd = ["imagick"]
40
+ cmd += [backgroundBase]
41
+
42
+ # draw the blurred album art background
43
+ if blur
44
+ cmd += ['-gravity', 'center']
45
+ cmd += ['(']
46
+ cmd += [tempArt]
47
+ cmd += ['-resize', '1500x']
48
+ cmd += ['-modulate', '90']
49
+ cmd += ['-blur', '0x20']
50
+ cmd += [')']
51
+ cmd += ['-composite']
52
+ end
53
+
54
+ # set offsets depending on whether text is drawn
55
+ gravity = 'center'
56
+ offset = []
57
+ shadowOffset = []
58
+ unless text.empty?
59
+ gravity = 'west'
60
+ offset = ['-geometry', '+60+0']
61
+ shadowOffset = ['-geometry', '+30+0']
62
+ end
63
+
64
+ # set some settings used twice
65
+ mainArtFront = ['(']
66
+ mainArtFront += [tempArt]
67
+ mainArtFront += ['-resize', 'x720']
68
+
69
+ mainArtBack = [')']
70
+ mainArtBack += ['-gravity', gravity]
71
+ mainArtBack += ['-composite']
72
+
73
+ # draw shadow of the main album art
74
+ cmd += mainArtFront
75
+ cmd += ['-background', 'black']
76
+ cmd += ['-shadow', '50x15+0+0']
77
+ cmd += shadowOffset
78
+ cmd += mainArtBack
79
+
80
+ # draw the main album art
81
+ cmd += mainArtFront
82
+ cmd += offset
83
+ cmd += mainArtBack
84
+
85
+ # add in the track metadata text
86
+ unless text.empty?
87
+ labelText = text[0]
88
+ for i in 1..text.length
89
+ labelText += '\\n\\n' + text[i]
90
+ end
91
+
92
+ # set some settings used twice
93
+ labelFront = ['(']
94
+ labelFront += ['-size', '440x400']
95
+ labelFront += ['-background', 'none']
96
+ labelFront += ['-gravity', 'center']
97
+ labelFront += ['-font', 'Segoe-UI-Semibold']
98
+
99
+ labelMid = ['caption:' + labelText]
100
+
101
+ labelBack = ['-geometry', '+25+0']
102
+ labelBack += [')']
103
+ labelBack += ['-gravity', 'east']
104
+ labelBack += ['-composite']
105
+
106
+ # draw text shadow
107
+ cmd += labelFront.join(' ')
108
+ cmd += ['-fill', '#00000090']
109
+ cmd += labelMid.join(' ')
110
+ cmd += ['-blur', '0x6']
111
+ cmd += labelBack.join(' ')
112
+
113
+ # draw text itself
114
+ cmd += labelFront.join(' ')
115
+ cmd += ['-fill', 'white']
116
+ cmd += labelMid.join(' ')
117
+ cmd += labelBack.join(' ')
118
+
119
+ end
120
+
121
+ # put it all together
122
+ cmd += [output]
123
+ puts 'Generating video background...'
124
+ begin
125
+ puts Open3.capture3(cmd.join(' '))
126
+ rescue
127
+ exitWithError('Something went wrong when generating the background.')
128
+ end
129
+ end
130
+
131
+ ################################################################################
132
+ # Splits text over multiple lines
133
+ def self.splitText(text, maximumLength)
134
+
135
+ maximumLength ||= 20
136
+ words = text.split(' ')
137
+ output = ''
138
+ currentLine = ''
139
+ words.each do |word|
140
+ if currentLine == ''
141
+ currentLine = word
142
+ else
143
+ temp = currentLine + ' ' + word
144
+ # with next word, still under maximum length - add normally
145
+ if len(temp) < maximumLength
146
+ currentLine = temp
147
+ # next word is longer than the maximum length - it gets its own line
148
+ elsif len(word) > maximumLength
149
+ if output != ''
150
+ output += r'\n'
151
+ end
152
+ output += currentLine
153
+ currentLine = ''
154
+ if output != ''
155
+ output += r'\n'
156
+ end
157
+ output += word
158
+ # we've overrun the maximum length - start a new line with the word
159
+ else
160
+ if output != ''
161
+ output += 'r\n'
162
+ end
163
+ output += currentLine
164
+ currentLine = word
165
+ end
166
+ end
167
+ end
168
+ if output != ''
169
+ output += r'\n'
170
+ end
171
+ output += currentLine
172
+ #print(output)
173
+ return output
174
+ end
175
+
176
+ ################################################################################
177
+ # Retrieves the title and artist from the file
178
+ def self.getMetadata(file)
179
+ cmd = ["ffprobe"]
180
+ cmd += ['-i', file]
181
+ print('Extracting metadata...')
182
+ lines = ""
183
+ begin
184
+ output = p cmd.join(' ')
185
+ lines = output.split('\n')
186
+ rescue
187
+ exitWithError('Couldn\'t extract metadata from the file.')
188
+ end
189
+
190
+ title = ""
191
+ artist = ""
192
+
193
+ lines.each do |line|
194
+ match = line.scan(/^\s*(.*?)\s*: (.*?)\s*@/)
195
+ if match.length > 0
196
+ pair = match[0]
197
+
198
+ if len(pair) == 2
199
+ if pair[0] == 'title' and pair[1].length > 0
200
+ if title == "" # or len(title) < len(pair[1]):
201
+ title = pair[1]
202
+ print("Found title: " + title)
203
+ end
204
+ elsif pair[0] == 'artist' and pair[1].length > 0
205
+ if artist == "" # or len(artist) < len(pair[1]):
206
+ artist = pair[1]
207
+ print("Found artist: " + artist)
208
+ end
209
+ end
210
+ end
211
+ end
212
+ end
213
+ return artist, title
214
+ end
215
+ ################################################################################
216
+ # Creates the final video suitable for uploading
217
+ def self.encode(audio, background, output)
218
+ cmd = ["ffmpeg"]
219
+ cmd += ['-y']
220
+ cmd += ['-loop', '1']
221
+ cmd += ['-r', '1']
222
+ cmd += ['-i', background]
223
+ cmd += ['-i', audio]
224
+ cmd += ['-c:v', 'libx264']
225
+ cmd += ['-preset', 'veryfast']
226
+ cmd += ['-tune', 'stillimage']
227
+ cmd += ['-crf', '15']
228
+ cmd += ['-pix_fmt', 'yuv420p']
229
+ cmd += ['-strict', 'experimental']
230
+ cmd += ['-c:a', 'aac']
231
+ cmd += ['-b:a', '256k']
232
+ cmd += ['-shortest']
233
+ cmd += ['-threads', '0']
234
+ cmd += [output]
235
+ print('Encoding video...')
236
+ begin
237
+ system(cmd.join(' '))
238
+ rescue
239
+ exitWithError('Something went wrong when encoding the video.')
240
+ end
241
+ print('\nDone. Saved output as "%s".' % @outputFile)
242
+ end
243
+
244
+ ################################################################################
245
+ # Set library locations
246
+ def self.setLibraryLocations()
247
+
248
+ # set FFmpeg location
249
+ if @ffmpegDir == 'setme' or @ffmpegDir == ''
250
+ @ffmpegDir = '/usr/local/bin'
251
+ end
252
+
253
+ # set ImageMagick location
254
+ if @imagemagickDir == 'setme' or @imagemagickDir == ''
255
+ @imagemagickDir = '/usr/local/bin'
256
+ end
257
+
258
+ # set full paths
259
+ ffprobe = Dir.pwd + " " + @ffmpegDir + ' ffprobe'
260
+ ffmpeg = Dir.pwd + " " + @ffmpegDir + ' ffmpeg'
261
+ imagick = Dir.pwd + " " + @imagemagickDir + ' convert'
262
+
263
+ # ensure libraries are actually installed
264
+ unless (os.path.exists(ffprobe) and os.path.exists(ffmpeg))
265
+ exitWithError("FFmpeg couldn\'t be found at \"#{@ffmpegDir}\". " +
266
+ 'Please check your configuration.')
267
+ end
268
+ unless os.path.exists(imagick)
269
+ exitWithError("ImageMagick couldn\'t be found at \"#{@imagemagickDir}\". " +
270
+ 'Please check your configuration.')
271
+ end
272
+ end
273
+ public
274
+ ################################################################################
275
+ # Program entrypoint.
276
+ def self.generate *args
277
+ audio = nil
278
+ bg = nil
279
+ out = nil
280
+ if args.size < 1 || args.size > 3
281
+ puts 'This method takes either 1 to 3 arguments'
282
+ else
283
+ audio = args[0]
284
+ if args.size == 2
285
+ bg = args[1]
286
+ elsif args.size == 3
287
+ bg = args[1]
288
+ out = args[2]
289
+ end
290
+ end
291
+
292
+ bg ||= @tempBg
293
+ out ||= @outputFile
294
+ unless File.exist?(audio)
295
+ exitWithError("Input file \"#{audio}\" doesn\'t exist or can\'t be read.")
296
+ end
297
+
298
+ # determine arguments
299
+ custom = bg
300
+ doBlur = true
301
+ doText = false
302
+
303
+ background = Dir.pwd + " " + @tempFolder + " " + bg
304
+
305
+ # grab metadata if necessary
306
+ text = ""
307
+ if doText and custom == ""
308
+ metadata = getMetadata(audio)
309
+ metadata.each do |item|
310
+ if item != ""
311
+ #split = splitText(item)
312
+ text.append(item)
313
+ end
314
+ end
315
+ end
316
+
317
+ # generate the background unless a premade one is specified
318
+ if custom == ""
319
+ generateBackground(audio, backgroundBase, background, blur=doBlur, text=text)
320
+ else
321
+ background = custom
322
+ end
323
+ encode(audio, background, out)
324
+ end
325
+
326
+ end
metadata ADDED
@@ -0,0 +1,44 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: audio_to_youtube
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Brian Doan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-08-29 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Convert audio with background image to mp4 for uploading to youtube
14
+ email: quangkhoidoan@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/audio_to_youtube.rb
20
+ homepage: http://rubygems.org/gems/audio_to_youtube
21
+ licenses:
22
+ - MIT
23
+ metadata: {}
24
+ post_install_message:
25
+ rdoc_options: []
26
+ require_paths:
27
+ - lib
28
+ required_ruby_version: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ required_rubygems_version: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - ">="
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ requirements: []
39
+ rubyforge_project:
40
+ rubygems_version: 2.2.2
41
+ signing_key:
42
+ specification_version: 4
43
+ summary: Convert audio with background image to mp4 for uploading to youtube
44
+ test_files: []