Last active
December 12, 2020 22:11
-
-
Save djvs/51c7712d8bba36a1ba23e2df23c3fc77 to your computer and use it in GitHub Desktop.
Script to break down YouTube playlist audio into separate files per-track, based on YouTube description/comment track lists. Depends on commandline ffmpeg
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
require 'shellwords' | |
require 'open3' | |
require 'pry-rails' | |
if ARGV.length < 2 | |
case ARGV[0] | |
when nil, "--help" | |
puts "Usage: youtube-playlist-split.rb AUDIO_FILE PLAYLIST_FILE | |
Will break apart a video or audio file in its current directory, according to track start times specified in PLAYLIST_FILE (in typical YouTube format) | |
Options: | |
--help Print this help | |
--example-playlist Show example playlist | |
" | |
when '--example-playlist' | |
puts "example playlist: | |
1. Artist - Song 12:24 | |
2. My Artist - My Song 42:20 | |
3. Other Artist - Your Song 01:12:20" | |
end | |
exit | |
end | |
source = File.expand_path(ARGV[0]) | |
pl = File.expand_path(ARGV[1]) | |
ext = source.split('.').last | |
dir = source.split('/')[0..-2].join('/') + "/" | |
duration_str = Open3.capture3("ffmpeg -i #{Shellwords.escape(source)}")[1].match(/Duration: [\d:]*/).to_s.gsub("Duration: ","") | |
manifest = {} | |
trackstrs = File.read(pl).split("\n") | |
trackstrs.each do |x| | |
filename = x.gsub(/[\d:]*$/, '').strip | |
manifest[filename.to_sym] = { | |
start_time: x.match(/[\d:]+:[\d:]+/).to_s | |
} | |
end | |
def parsetime(str) | |
str.split(':').map(&:to_i).inject(0) { |a, b| a * 60 + b } | |
end | |
mks = manifest.keys | |
mks.each_with_index do |k,i| | |
next_item = manifest[mks[i+1]] | |
if next_item | |
next_str = next_item[:start_time] | |
else | |
next_str = duration_str | |
end | |
manifest[k][:end_time] = next_str | |
manifest[k][:len] = parsetime(manifest[k][:end_time]) - parsetime(manifest[k][:start_time]) | |
filename = k.to_s.gsub(manifest[k][:start_time], '').strip | |
# binding.pry | |
if !filename.to_s.match(/^[\d]+/) | |
filename = "#{i.to_s.rjust(2, "0")} #{filename}" | |
end | |
filename = "#{Shellwords.escape(dir)}#{Shellwords.escape(filename)}.#{ext}" | |
manifest[k][:filename] = filename | |
end | |
puts "Parsed track list:" | |
manifest.each do |k,v| | |
puts "Filename: #{v[:filename]}" | |
puts "Start time: #{v[:start_time]} End time: #{v[:end_time]} Length: #{v[:len]}" | |
end | |
puts "Proceed? Y/n" | |
answer = $stdin.gets.chomp | |
exit if answer.downcase == "n" | |
manifest.each.with_index do |x,i| | |
# k = x[0] | |
v = x[1] | |
cmd = "ffmpeg -i #{Shellwords.escape(source)} -ss #{Shellwords.escape(v[:start_time])} -t #{Shellwords.escape(v[:len])} -c:a copy #{v[:filename]}" | |
# binding.pry | |
puts "Executing: " + cmd | |
puts `#{cmd}` | |
puts | |
puts "-----------------------------------------------------" | |
puts | |
end | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment