-
Notifications
You must be signed in to change notification settings - Fork 4
/
playlist_exporter.rb
executable file
·183 lines (147 loc) · 4.85 KB
/
playlist_exporter.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
#!/usr/bin/env ruby
require 'rubygems'
require 'thor'
require 'plist'
require 'uri'
require 'fileutils'
class PlaylistExporter < Thor
desc "process", "process playlist"
method_option :verbose, :type => :boolean, :default => false, :aliases => "-v",
:desc => "running in verbose mode will also show each file as it's copied"
method_option :debug, :type => :boolean, :default => false, :aliases => "-d",
:desc => "in debug mode files will not actually be copied"
method_option :force, :type => :boolean, :default => false, :aliases => "-f",
:desc => "normally, copying a file is skipped if a file with the same name and size already exists in the destination. Force mode always copies."
def process
puts "*** Verbose Mode" if options.verbose?
puts "*** Debug Mode" if options.debug?
puts "*** Force Mode" if options.force?
get_exported_file
get_target_directory
read_plist
initialize_catalog
process_tracks
copy_catalog
end
private
def get_exported_file
found = false
until found
@exported_file = ask("Location of Playlist [~/Desktop/usb/playlist.xml]")
@exported_file = "~/Desktop/usb/playlist.xml" if @exported_file == ""
@exported_file = File.expand_path(@exported_file)
if File.exists?(@exported_file)
found = true
else
say "File #{@exported_file} does not exist", :red
end
end
end
def get_target_directory
found = false
until found
@target_directory = ask("Location to which music should be copied [~/Desktop/usb]")
@target_directory = "~/Desktop/usb/" if @target_directory == ""
@target_directory += "/" unless ("/" == @target_directory[-1])
@target_directory = File.expand_path(@target_directory)
if File.exists?(@target_directory)
found = true
else
say "Directory #{@target_directory} does not exist", :red
end
end
end
def read_plist
say "Reading #{@exported_file}", :green
@export = Plist::parse_xml(@exported_file)
@tracks = @export["Tracks"]
end
def initialize_catalog
@catalog = {}
end
def process_tracks
@tracks.each do |id, info|
add_track_to_catalog(info)
end
end
def add_track_to_catalog(info)
name = clean_string(info["Name"])
album = clean_string(info["Album"], 25)
genre = clean_string(info["Genre"], 20)
track_number = info["Track Number"] || 0
begin
location = info["Location"]
if location.nil? || location.empty?
puts "** Error -- Song does not have a location on disk. Stored in Apple Music?"
return
end
file_uri = URI(info["Location"])
original_file = URI.decode(file_uri.path)
original_file =~ /.*\.(.*)/
file_type = $1
@catalog[genre] ||= {}
@catalog[genre][album] ||= []
if options.verbose?
puts " Cataloging : #{name} / #{album} / #{genre} / #{track_number}"
end
target_name = ("%02d-" % track_number) + "#{name}.#{file_type}"
@catalog[genre][album] << {:name => target_name, :file => original_file}
rescue Exception => e
puts "** Error trying to process:\n\t#{name}: #{info}"
puts " Exception details: #{e}"
end
end
def clean_string(s, cutoff_at = nil)
unless s.is_a?(String)
s = 'Blank'
end
if cutoff_at
s = s[0, cutoff_at]
end
s && s.gsub(/\/|\(|\)/, '_')
end
def copy_catalog
say "Beginning Copy", :green
@catalog.each do |genre, albums|
puts "Genre: #{genre}"
genre_path = "#{@target_directory}/#{genre}"
unless options.debug?
FileUtils.mkdir(genre_path) unless File.exists?(genre_path)
end
albums.each do |album, tracks|
puts " Album: #{album}"
album_path = "#{@target_directory}/#{genre}/#{album}"
unless options.debug?
FileUtils.mkdir(album_path) unless File.exists?(album_path)
end
tracks.each do |track|
full_destination = "#{@target_directory}/#{genre}/#{album}/#{track[:name]}"
source = track[:file]
if options.verbose?
puts " Creating : #{track[:name]}"
puts " source : #{track[:file]}"
end
if File.exists?(source)
if options.force?
copy_file(source, full_destination)
else
if File.exists?(full_destination) && (File.size(source) == File.size(full_destination))
puts " *** Destination file already exists: #{track[:name]}"
else
copy_file(source, full_destination)
end
end
else
puts " *** Source does not exist"
end
end
end
end
end
def copy_file(source, full_destination)
unless options.debug?
FileUtils.copy_file(source, full_destination)
end
end
end
PlaylistExporter.start