henchman-sync 0.3.3 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +0 -4
- data/henchman.gemspec +1 -1
- data/lib/applescript.rb +3 -5
- data/lib/cache.rb +13 -1
- data/lib/clean.rb +9 -10
- data/lib/configure.rb +26 -15
- data/lib/core.rb +15 -6
- data/lib/dbx_assistant.rb +185 -0
- data/lib/henchman.rb +28 -2
- data/lib/henchman/version.rb +1 -1
- data/lib/launchd_handler.rb +2 -2
- data/lib/templates.rb +4 -3
- metadata +6 -6
- data/lib/dropbox.rb +0 -101
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7d27e875da0439ebd8e24c160d77c962202b0f19
|
4
|
+
data.tar.gz: 775dfd05202ac8248fc155901845f224adc004e5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fd12ef4db1308346aa45ffd3f386f02e90714217430effe5908b8196573275a4dda0133b6dcd8e07409332a4cce8308a45530b8a46c50de1efa2f19c4f54e8ae
|
7
|
+
data.tar.gz: 07d8df1684c9e5ec9db629ec9bbffec58c324668bc447ab98d49db7659743bed18eb6670edc0265b14826c70c6402195f8b9a07027fdacd7c1c92f2ff2bf1d38
|
data/README.md
CHANGED
@@ -48,10 +48,6 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
|
48
48
|
|
49
49
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
50
50
|
|
51
|
-
## TODO
|
52
|
-
|
53
|
-
- Move off of the 'dropbox-sdk' gem, since it uses their API V1, which will be depreciated June 28th, 2017 (or just buck up and update the gem myself)
|
54
|
-
|
55
51
|
## Contributing
|
56
52
|
|
57
53
|
Bug reports and pull requests are welcome on GitHub at https://github.com/gremerritt/henchman.
|
data/henchman.gemspec
CHANGED
@@ -28,7 +28,7 @@ Gem::Specification.new do |spec|
|
|
28
28
|
spec.require_paths = ["lib"]
|
29
29
|
|
30
30
|
spec.add_dependency "commander", "~> 4.4"
|
31
|
-
spec.add_dependency "dropbox-sdk", "~>
|
31
|
+
spec.add_dependency "dropbox-sdk-v2", "~> 0.0"
|
32
32
|
spec.add_development_dependency "bundler", "~> 1.12"
|
33
33
|
spec.add_development_dependency "rake", "~> 10.0"
|
34
34
|
spec.add_development_dependency "rspec", "~> 3.0"
|
data/lib/applescript.rb
CHANGED
@@ -133,7 +133,7 @@ module Henchman
|
|
133
133
|
"end repeat"
|
134
134
|
end
|
135
135
|
|
136
|
-
def get_playlist_tracks_script playlist, skip = [], size =
|
136
|
+
def get_playlist_tracks_script playlist, skip = [], size = 5
|
137
137
|
"property counter : 0\n"\
|
138
138
|
"tell application \"iTunes\"\n"\
|
139
139
|
" try\n"\
|
@@ -210,7 +210,7 @@ module Henchman
|
|
210
210
|
next if track.empty?
|
211
211
|
tmp_track = track.split @delimiter
|
212
212
|
tracks.push( {:id => tmp_track[0],
|
213
|
-
:date => DateTime.parse(tmp_track[1]),
|
213
|
+
:date => (tmp_track[1] != 'missing value') ? DateTime.parse(tmp_track[1]) : DateTime.new,
|
214
214
|
:path => tmp_track[2]} )
|
215
215
|
end
|
216
216
|
tracks
|
@@ -229,7 +229,7 @@ module Henchman
|
|
229
229
|
def get_playlist_tracks playlist, skip = []
|
230
230
|
tracks = Array.new
|
231
231
|
tmp_tracks = %x(#{applescript_command(get_playlist_tracks_script playlist, skip)}).chomp
|
232
|
-
tmp_tracks = tmp_tracks.split @delimiter_major
|
232
|
+
tmp_tracks = tmp_tracks.force_encoding("UTF-8").split @delimiter_major
|
233
233
|
tmp_tracks.each do |track|
|
234
234
|
next if track.empty?
|
235
235
|
tmp_track = track.split @delimiter
|
@@ -274,8 +274,6 @@ module Henchman
|
|
274
274
|
|
275
275
|
ret = %x(#{applescript_command(update_track_location_script selection[:id], local_file)}).chomp
|
276
276
|
if ret.empty? || ret == '0'
|
277
|
-
puts "#{DateTime.now.strftime('%m-%d-%Y %H:%M:%S')}|"\
|
278
|
-
"Could not update location of #{selection.reject{|k,v| k == :path || k == :id}.values.join(':')} to #{local_file}"
|
279
277
|
false
|
280
278
|
else
|
281
279
|
true
|
data/lib/cache.rb
CHANGED
@@ -35,7 +35,8 @@ module Henchman
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def get_time_last_downloaded track
|
38
|
-
|
38
|
+
id = track[:id].to_i
|
39
|
+
(@cache[:history].include?(id)) ? @cache[:history][id] : DateTime.new
|
39
40
|
end
|
40
41
|
|
41
42
|
def tag track
|
@@ -60,6 +61,17 @@ module Henchman
|
|
60
61
|
end
|
61
62
|
end
|
62
63
|
|
64
|
+
def clear type, value
|
65
|
+
raise "Invalid type #{type}" if !@cache[:ignore].keys.include? type
|
66
|
+
if @cache[:ignore][type].include? value
|
67
|
+
@cache[:ignore][type].delete value
|
68
|
+
puts "Deleting #{type} #{value} from cache"
|
69
|
+
else
|
70
|
+
puts "#{type} #{value} not found"
|
71
|
+
end
|
72
|
+
flush
|
73
|
+
end
|
74
|
+
|
63
75
|
end
|
64
76
|
|
65
77
|
end
|
data/lib/clean.rb
CHANGED
@@ -7,7 +7,6 @@ module Henchman
|
|
7
7
|
class Clean
|
8
8
|
|
9
9
|
def self.run played_date
|
10
|
-
|
11
10
|
puts "#{DateTime.now.strftime('%m-%d-%Y %H:%M:%S')}|"\
|
12
11
|
"Cleanup Starting"
|
13
12
|
|
@@ -44,20 +43,20 @@ module Henchman
|
|
44
43
|
|
45
44
|
def self.cleanup track
|
46
45
|
filepath = track[:path]
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
46
|
+
begin
|
47
|
+
File.delete filepath
|
48
|
+
@cache.delete track
|
49
|
+
puts "#{DateTime.now.strftime('%m-%d-%Y %H:%M:%S')}|"\
|
50
|
+
"Deleted #{filepath}"
|
51
51
|
|
52
|
-
|
53
|
-
|
54
|
-
begin
|
52
|
+
while File.dirname(filepath) != @config[:root]
|
53
|
+
filepath = File.dirname(filepath)
|
55
54
|
Dir.rmdir(filepath)
|
56
55
|
puts "#{DateTime.now.strftime('%m-%d-%Y %H:%M:%S')}|"\
|
57
56
|
"Deleted #{filepath}"
|
58
|
-
rescue SystemCallError => msg
|
59
|
-
break
|
60
57
|
end
|
58
|
+
rescue SystemCallError => msg
|
59
|
+
# do nothing
|
61
60
|
end
|
62
61
|
end
|
63
62
|
|
data/lib/configure.rb
CHANGED
@@ -34,7 +34,7 @@ module Henchman
|
|
34
34
|
if config[:root].empty? || agree("\nUpdate local music directory? (y/n) ")
|
35
35
|
get_local_root config
|
36
36
|
end
|
37
|
-
|
37
|
+
|
38
38
|
Dir.mkdir(File.dirname(config_file)) if !File.exists?(File.dirname(config_file))
|
39
39
|
File.open(config_file, "w") { |f| f.write( config.to_yaml ) }
|
40
40
|
puts "\nConfiguration complete! Run `henchman start` to start the daemon."
|
@@ -43,10 +43,10 @@ module Henchman
|
|
43
43
|
def self.connect config={}
|
44
44
|
begin
|
45
45
|
config = YAML.load_file(File.expand_path("~/.henchman/config")) if config.empty?
|
46
|
-
client =
|
47
|
-
account_info = client.
|
46
|
+
client = Dropbox::Client.new config[:dropbox][:access_token]
|
47
|
+
account_info = client.get_current_account()
|
48
48
|
puts "Successfully connected to Dropbox: "
|
49
|
-
puts " #{account_info
|
49
|
+
puts " #{account_info.display_name} [#{account_info.email}]"
|
50
50
|
return client
|
51
51
|
rescue StandardError => err
|
52
52
|
puts "Error connecting to Dropbox account (#{err}). Try deleting the "\
|
@@ -91,8 +91,6 @@ module Henchman
|
|
91
91
|
end
|
92
92
|
|
93
93
|
def self.get_dropbox_root config, client
|
94
|
-
# paths = Hash.new
|
95
|
-
# build_dropbox_dirs(paths, client, '/', 0)
|
96
94
|
not_done = true
|
97
95
|
while not_done
|
98
96
|
path = ask("Enter the path to your music directory in Dropbox: (? for help)" )
|
@@ -115,15 +113,28 @@ module Henchman
|
|
115
113
|
end
|
116
114
|
end
|
117
115
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
116
|
+
def self.collect_exts
|
117
|
+
config_file = File.expand_path('~/.henchman/config')
|
118
|
+
config = YAML.load_file(config_file)
|
119
|
+
client = connect config
|
120
|
+
exts = Hash.new
|
121
|
+
collect_exts_rec client, config[:dropbox][:root], exts
|
122
|
+
|
123
|
+
puts ""
|
124
|
+
exts.each_key { |k| puts k }
|
125
|
+
end
|
126
|
+
|
127
|
+
def self.collect_exts_rec client, path, exts
|
128
|
+
metadata = client.metadata(path)
|
129
|
+
metadata['contents'].each_with_index do |c, i|
|
130
|
+
print "\rCollecting#{'.'*(i%4)}#{' '*(3-(i%4))}"
|
131
|
+
if c['is_dir']
|
132
|
+
collect_exts_rec client, c['path'], exts
|
133
|
+
else
|
134
|
+
exts[File.extname(c['path'])] = true
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
127
138
|
|
128
139
|
def self.get_local_root config
|
129
140
|
not_done = true
|
data/lib/core.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require "
|
1
|
+
require "dbx_assistant"
|
2
2
|
require "applescript"
|
3
3
|
require "cache"
|
4
4
|
require "yaml"
|
@@ -7,7 +7,9 @@ module Henchman
|
|
7
7
|
|
8
8
|
class Core
|
9
9
|
|
10
|
-
def self.run
|
10
|
+
def self.run args
|
11
|
+
debug = true if args[0] == 'debug'
|
12
|
+
|
11
13
|
@appleScript = Henchman::AppleScript.new
|
12
14
|
@cache = Henchman::Cache.new
|
13
15
|
@update_cache = false
|
@@ -23,6 +25,10 @@ module Henchman
|
|
23
25
|
@config[:delimiter_major] = Henchman::Templates.config[:delimiter_major]
|
24
26
|
File.open(config_file, "w") { |f| f.write( @config.to_yaml ) }
|
25
27
|
end
|
28
|
+
if !(@config.include? :file_extensions)
|
29
|
+
@config[:file_extensions] = Henchman::Templates.config[:file_extensions]
|
30
|
+
File.open(config_file, "w") { |f| f.write( @config.to_yaml ) }
|
31
|
+
end
|
26
32
|
|
27
33
|
@cache.config @config
|
28
34
|
rescue StandardError => err
|
@@ -33,10 +39,10 @@ module Henchman
|
|
33
39
|
|
34
40
|
@appleScript.setup @config
|
35
41
|
begin
|
36
|
-
@dropbox = Henchman::DropboxAssistant.new @config
|
37
|
-
rescue
|
42
|
+
@dropbox = Henchman::DropboxAssistant.new @config, debug
|
43
|
+
rescue StandardError => msg
|
38
44
|
puts "#{DateTime.now.strftime('%m-%d-%Y %H:%M:%S')}|"\
|
39
|
-
"Error connecting to Dropbox. Try rerunning `henchman configure`"
|
45
|
+
"Error connecting to Dropbox (#{msg}). Try rerunning `henchman configure`"
|
40
46
|
return
|
41
47
|
end
|
42
48
|
|
@@ -144,7 +150,10 @@ module Henchman
|
|
144
150
|
updated = @appleScript.set_track_location track, file_save_path
|
145
151
|
|
146
152
|
# if the update failed, remove that file
|
147
|
-
|
153
|
+
if !updated
|
154
|
+
cleanup file_save_path, track
|
155
|
+
raise "Could not update location of #{track.reject{|k,v| k == :path || k == :id}.values.join(':')} to #{file_save_path}"
|
156
|
+
end
|
148
157
|
end
|
149
158
|
rescue StandardError => err
|
150
159
|
puts "#{DateTime.now.strftime('%m-%d-%Y %H:%M:%S')}|#{err}"
|
@@ -0,0 +1,185 @@
|
|
1
|
+
require 'dropbox'
|
2
|
+
require 'yaml'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Henchman
|
6
|
+
|
7
|
+
class DropboxAssistant
|
8
|
+
|
9
|
+
def initialize config, debug
|
10
|
+
begin
|
11
|
+
@search_limit = 500
|
12
|
+
@debug = debug
|
13
|
+
@config = config
|
14
|
+
@client = Dropbox::Client.new @config[:dropbox][:access_token]
|
15
|
+
|
16
|
+
# stop words from http://nlp.stanford.edu/IR-book/html/htmledition/dropping-common-terms-stop-words-1.html
|
17
|
+
@stop_words = ['a', 'an', 'and', 'are', 'as', 'at', 'be', 'by', 'for', 'from',
|
18
|
+
'has', 'he', 'in', 'is', 'it', 'its', 'of', 'on', 'that', 'the',
|
19
|
+
'to', 'was', 'were', 'will', 'with']
|
20
|
+
true
|
21
|
+
rescue DropboxError => msg
|
22
|
+
puts "#{DateTime.now.strftime('%m-%d-%Y %H:%M:%S')}|"\
|
23
|
+
"Couldn't connect to Dropbox (#{msg}). "\
|
24
|
+
"Run `henchman stop` then `henchman configure` "\
|
25
|
+
"to configure Dropbox connection."
|
26
|
+
false
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def download selection, dropbox_path
|
31
|
+
puts "#{DateTime.now.strftime('%m-%d-%Y %H:%M:%S')}|"\
|
32
|
+
"Downloading #{selection.reject{|k,v| k == :path || k == :id}.values.join(':')} from #{dropbox_path}"
|
33
|
+
begin
|
34
|
+
# download the file
|
35
|
+
content, body = @client.download dropbox_path
|
36
|
+
|
37
|
+
# make sure we have the directory to put it in
|
38
|
+
trgt_dir = File.join @config[:root], selection[:artist], selection[:album]
|
39
|
+
puts trgt_dir if @debug
|
40
|
+
system 'mkdir', '-p', trgt_dir
|
41
|
+
|
42
|
+
# save the file
|
43
|
+
file_save_path = File.join trgt_dir, File.basename(dropbox_path)
|
44
|
+
puts file_save_path if @debug
|
45
|
+
open(file_save_path, 'w') {|f| f.puts body }
|
46
|
+
file_save_path
|
47
|
+
rescue DropboxError => msg
|
48
|
+
puts "#{DateTime.now.strftime('%m-%d-%Y %H:%M:%S')}|"\
|
49
|
+
"Error downloading Dropbox file #{dropbox_path}: #{msg}"
|
50
|
+
false
|
51
|
+
rescue StandardError => msg
|
52
|
+
puts "#{DateTime.now.strftime('%m-%d-%Y %H:%M:%S')}|"\
|
53
|
+
"Error saving Dropbox file #{dropbox_path} to #{trgt_dir}: #{msg}"
|
54
|
+
false
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def search value, filter = nil, dir = @config[:dropbox][:root]
|
59
|
+
puts "Searching for #{value} in #{dir}, filtering by <#{filter}>" if @debug
|
60
|
+
begin
|
61
|
+
results = @client.search dir, value, 0, @search_limit
|
62
|
+
puts "#{results.length} total results found" if @debug
|
63
|
+
if filter == :dir
|
64
|
+
results.reject! { |result| !result.is_a? Dropbox::FolderMetadata }
|
65
|
+
elsif filter == :file
|
66
|
+
results.reject! { |result| !result.is_a? Dropbox::FileMetadata ||
|
67
|
+
!(@config[:file_extensions].include?(File.extname(result.path_lower)[1..-1])) }
|
68
|
+
end
|
69
|
+
puts "Returning #{results.length} results for `search` (after filtering)" if @debug
|
70
|
+
return results
|
71
|
+
rescue DropboxError => msg
|
72
|
+
raise "Error accessing Dropbox Search API|#{value}|#{dir}|#{msg}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def get_results track, artist
|
77
|
+
puts "`get_results` for #{track} by #{artist}" if @debug
|
78
|
+
# Search Dropbox for the file
|
79
|
+
# We're only not filtering to get files because we want to check if we get 1000 results
|
80
|
+
# (i.e. a maxed out playload) back. This is because the filtering happens in OUR client,
|
81
|
+
# not in the Dropbox search. We're doing a simple search on only track name because we
|
82
|
+
# want to minimize the number of network calls, and USUALLY this is good enough
|
83
|
+
results = search track
|
84
|
+
|
85
|
+
# If we get 1000 results back, it means we probably have a REALLY simple song title
|
86
|
+
# and we're not assured to have the target song in our payload, since we maxed it
|
87
|
+
# out. So, we'll do another search call on the artist
|
88
|
+
if results.length == @search_limit
|
89
|
+
puts "Maximum (#{@search_limit}) results returned" if @debug
|
90
|
+
results.clear
|
91
|
+
album_dirs = search artist, :dir
|
92
|
+
album_dirs.each do |album|
|
93
|
+
tmp_rslts = search track, :file, album['path']
|
94
|
+
results.push(*tmp_rslts)
|
95
|
+
end
|
96
|
+
else # Otherwise, filter off all the directories and things without the right extensions
|
97
|
+
puts "Filtering out directories and incorrect file extensions" if @debug
|
98
|
+
results.reject! { |result| !result.is_a? Dropbox::FileMetadata ||
|
99
|
+
!(@config[:file_extensions].include?(File.extname(result.path_lower)[1..-1])) }
|
100
|
+
end
|
101
|
+
puts "Returning #{results.length} results from `get_results`" if @debug
|
102
|
+
return results
|
103
|
+
end
|
104
|
+
|
105
|
+
def search_for selection
|
106
|
+
puts "#{DateTime.now.strftime('%m-%d-%Y %H:%M:%S')}|"\
|
107
|
+
"Searching for #{selection.reject{|k,v| k == :path || k == :id}.values.join(':')}"
|
108
|
+
|
109
|
+
results = get_results selection[:track], selection[:artist]
|
110
|
+
|
111
|
+
# if we still don't have any results, try dropping any brackets and paranthesis
|
112
|
+
if results.empty? && (selection[:track].match(%r( *\[.*\] *)) || selection[:track].match(%r( *\(.*\) *)))
|
113
|
+
puts "No results. Trying without brackets or parenthesis" if @debug
|
114
|
+
track = selection[:track].gsub(%r( *\[.*\] *), " ").gsub(%r( *\(.*\) *), " ")
|
115
|
+
results = get_results track, selection[:artist]
|
116
|
+
end
|
117
|
+
|
118
|
+
# if there were no results, raise err
|
119
|
+
if results.empty?
|
120
|
+
raise "Track not found in Dropbox: #{selection.reject{|k,v| k == :id}.values.join(':')}"
|
121
|
+
else
|
122
|
+
scores = Hash.new 0
|
123
|
+
tokens = Array.new
|
124
|
+
track_tokens = Array.new
|
125
|
+
[:artist, :album].each do |identifier|
|
126
|
+
tokens |= selection[identifier].downcase
|
127
|
+
.gsub(%r( +), " ")
|
128
|
+
.gsub(%r(-+), "-")
|
129
|
+
.strip
|
130
|
+
.split(/[\s-]/)
|
131
|
+
.delete_if{ |t| @stop_words.include? t }
|
132
|
+
end
|
133
|
+
@config[:file_extensions].each do |extension|
|
134
|
+
track_tokens |= selection[:track].downcase
|
135
|
+
.gsub(%r( +), " ")
|
136
|
+
.gsub(%r(-+), "-")
|
137
|
+
.strip
|
138
|
+
.split(/[\s-]/)
|
139
|
+
.map { |t| "#{t}.#{extension}" }
|
140
|
+
end
|
141
|
+
|
142
|
+
if @debug
|
143
|
+
puts ":artist and :album tokens: #{tokens.inspect}"
|
144
|
+
puts ":track tokens: #{track_tokens.inspect}"
|
145
|
+
end
|
146
|
+
|
147
|
+
results.each do |result|
|
148
|
+
dir = "#{File.dirname(result.path_lower)}/"
|
149
|
+
basename = " #{File.basename(result.path_lower)}"
|
150
|
+
tokens.each do |token|
|
151
|
+
if dir =~ %r(.*[\s\/-]#{token}[\s\/-].*)
|
152
|
+
puts "Token #{token} found in #{dir}" if @debug
|
153
|
+
if results.length == 1
|
154
|
+
return result.path_display
|
155
|
+
else
|
156
|
+
scores[result.path_display] += 1
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
track_tokens.each do |token|
|
161
|
+
if basename =~ %r([.]*[\s-]+#{token})
|
162
|
+
puts "Token #{token} found in #{basename}" if @debug
|
163
|
+
scores[result.path_display] += 1
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# if the we only had one track and we're here, that means the path didn't contain
|
169
|
+
# any of the album or artist tokens, so we'll say we couldn't find it
|
170
|
+
if results.length == 1
|
171
|
+
raise "Track not found in Dropbox: #{selection.reject{|k,v| k == :id}.values.join(':')}"
|
172
|
+
end
|
173
|
+
|
174
|
+
# return the path that has the highest score
|
175
|
+
scores = scores.sort_by { |path, score| score }
|
176
|
+
if @debug
|
177
|
+
scores.each { |score| puts score.join ': ' }
|
178
|
+
end
|
179
|
+
scores[-1][0]
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|
data/lib/henchman.rb
CHANGED
@@ -16,7 +16,7 @@ module Henchman
|
|
16
16
|
c.syntax = 'henchman start'
|
17
17
|
c.description = 'Starts the henchman daemon'
|
18
18
|
c.action do |args, options|
|
19
|
-
Henchman::LaunchdHandler.start
|
19
|
+
Henchman::LaunchdHandler.start args
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
@@ -40,7 +40,7 @@ module Henchman
|
|
40
40
|
c.syntax = 'henchman run'
|
41
41
|
c.description = 'Main interface into henchman. Should not be ran manually.'
|
42
42
|
c.action do |args, options|
|
43
|
-
Henchman::Core.run
|
43
|
+
Henchman::Core.run args
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
@@ -55,5 +55,31 @@ module Henchman
|
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
+
command :extensions do |c|
|
59
|
+
c.syntax = 'henchman extensions'
|
60
|
+
c.description = 'Collect file extensions'
|
61
|
+
c.action do |args, options|
|
62
|
+
Henchman.collect_exts
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
command :log do |c|
|
67
|
+
c.syntax = 'henchman log [options]'
|
68
|
+
c.description = 'Tails the henchman stdout log'
|
69
|
+
c.option '--n <number>', Integer, 'Number of lines to tail'
|
70
|
+
c.action do |args, options|
|
71
|
+
options.default :n => 10
|
72
|
+
puts `tail -n #{options.n} #{File.expand_path('~/.henchman/stdout.log')}`
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
command :clear do |c|
|
77
|
+
c.syntax = 'henchman clear <artist/playlist> <title>'
|
78
|
+
c.description = 'Clears the the artist or playlist from the cache'
|
79
|
+
c.action do |args, options|
|
80
|
+
@cache = Henchman::Cache.new
|
81
|
+
@cache.clear args[0].to_sym, args[1..-1].join(' ')
|
82
|
+
end
|
83
|
+
end
|
58
84
|
end
|
59
85
|
end
|
data/lib/henchman/version.rb
CHANGED
data/lib/launchd_handler.rb
CHANGED
@@ -4,7 +4,7 @@ module Henchman
|
|
4
4
|
|
5
5
|
class LaunchdHandler
|
6
6
|
|
7
|
-
def self.start
|
7
|
+
def self.start args
|
8
8
|
if !internet_connection?
|
9
9
|
puts "No internet connection detected - unable to verify correct configuration."
|
10
10
|
return if !agree("Launch henchman anyways? (y/n) ")
|
@@ -22,7 +22,7 @@ module Henchman
|
|
22
22
|
shell_script_path_clean = File.expand_path("~/.henchman/clean.sh")
|
23
23
|
cache_path = File.expand_path("~/.henchman/cache")
|
24
24
|
File.write(plist_path_main, plist_main)
|
25
|
-
File.write(shell_script_path_main, Henchman::Templates.shell_script('run'))
|
25
|
+
File.write(shell_script_path_main, Henchman::Templates.shell_script('run', args))
|
26
26
|
File.write(plist_path_clean, plist_clean)
|
27
27
|
File.write(shell_script_path_clean, Henchman::Templates.shell_script('clean'))
|
28
28
|
File.open(cache_path, "w") { |f| f.write( Henchman::Templates.cache.to_yaml ) }
|
data/lib/templates.rb
CHANGED
@@ -60,9 +60,9 @@ module Henchman
|
|
60
60
|
"</plist>"
|
61
61
|
end
|
62
62
|
|
63
|
-
def self.shell_script command
|
63
|
+
def self.shell_script command, args = []
|
64
64
|
"#!/bin/sh\n"\
|
65
|
-
"#{`which henchman`.chomp} #{command}"
|
65
|
+
"#{`which henchman`.chomp} #{command} #{args.join(' ')}"
|
66
66
|
end
|
67
67
|
|
68
68
|
def self.config
|
@@ -77,7 +77,8 @@ module Henchman
|
|
77
77
|
:poll_track => 3,
|
78
78
|
:reprompt_timeout => 300,
|
79
79
|
:delimiter => '|~|',
|
80
|
-
:delimiter_major => '|?|'
|
80
|
+
:delimiter_major => '|?|',
|
81
|
+
:file_extensions => ['mp3', 'm4a']
|
81
82
|
}
|
82
83
|
end
|
83
84
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: henchman-sync
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Greg Merritt
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-12-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: commander
|
@@ -25,19 +25,19 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '4.4'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name: dropbox-sdk
|
28
|
+
name: dropbox-sdk-v2
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '0.0'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '0.0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: bundler
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -104,7 +104,7 @@ files:
|
|
104
104
|
- lib/clean.rb
|
105
105
|
- lib/configure.rb
|
106
106
|
- lib/core.rb
|
107
|
-
- lib/
|
107
|
+
- lib/dbx_assistant.rb
|
108
108
|
- lib/henchman.rb
|
109
109
|
- lib/henchman/version.rb
|
110
110
|
- lib/launchd_handler.rb
|
data/lib/dropbox.rb
DELETED
@@ -1,101 +0,0 @@
|
|
1
|
-
require 'dropbox_sdk'
|
2
|
-
require 'yaml'
|
3
|
-
require 'json'
|
4
|
-
|
5
|
-
module Henchman
|
6
|
-
|
7
|
-
class DropboxAssistant
|
8
|
-
|
9
|
-
def initialize config
|
10
|
-
begin
|
11
|
-
@config = config
|
12
|
-
@client = DropboxClient.new(@config[:dropbox][:access_token])
|
13
|
-
true
|
14
|
-
rescue DropboxError => msg
|
15
|
-
puts "#{DateTime.now.strftime('%m-%d-%Y %H:%M:%S')}|"\
|
16
|
-
"Couldn't connect to Dropbox (#{msg}). "\
|
17
|
-
"Run `henchman stop` then `henchman configure` "\
|
18
|
-
"to configure Dropbox connection."
|
19
|
-
false
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def download selection, dropbox_path
|
24
|
-
puts "#{DateTime.now.strftime('%m-%d-%Y %H:%M:%S')}|"\
|
25
|
-
"Downloading #{selection.reject{|k,v| k == :path || k == :id}.values.join(':')}"
|
26
|
-
begin
|
27
|
-
# download the file
|
28
|
-
content = @client.get_file(dropbox_path)
|
29
|
-
|
30
|
-
# make sure we have the directory to put it in
|
31
|
-
trgt_dir = File.join @config[:root], selection[:artist], selection[:album]
|
32
|
-
system 'mkdir', '-p', trgt_dir
|
33
|
-
|
34
|
-
# save the file
|
35
|
-
file_save_path = File.join trgt_dir, File.basename(dropbox_path)
|
36
|
-
open(file_save_path, 'w') {|f| f.puts content }
|
37
|
-
file_save_path
|
38
|
-
rescue DropboxError => msg
|
39
|
-
puts "#{DateTime.now.strftime('%m-%d-%Y %H:%M:%S')}|"\
|
40
|
-
"Error downloading Dropbox file #{dropbox_path}: #{msg}"
|
41
|
-
false
|
42
|
-
rescue StandardError => msg
|
43
|
-
puts "#{DateTime.now.strftime('%m-%d-%Y %H:%M:%S')}|"\
|
44
|
-
"Error saving Dropbox file #{dropbox_path} to #{trgt_dir}: #{msg}"
|
45
|
-
false
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def search_for selection
|
50
|
-
puts "#{DateTime.now.strftime('%m-%d-%Y %H:%M:%S')}|"\
|
51
|
-
"Searching for #{selection.reject{|k,v| k == :path || k == :id}.values.join(':')}"
|
52
|
-
|
53
|
-
# search Dropbox for the file
|
54
|
-
begin
|
55
|
-
results = @client.search(@config[:dropbox][:root], selection[:track])
|
56
|
-
rescue DropboxError => msg
|
57
|
-
raise "Error accessing Dropbox Search API on #{selection.reject{|k,v| k == :path || k == :id}.values.join(':')}: #{msg}"
|
58
|
-
end
|
59
|
-
|
60
|
-
# get rid of any results that are directories
|
61
|
-
results.reject! { |result| result['is_dir'] }
|
62
|
-
|
63
|
-
# if we still don't have any results, try dropping any brackets and paranthesis
|
64
|
-
if results.empty? && (selection[:track].match(%r( *\[.*\] *)) || selection[:track].match(%r( *\(.*\) *)))
|
65
|
-
track = selection[:track].gsub(%r( *\[.*\] *), " ").gsub(%r( *\(.*\) *), " ")
|
66
|
-
results = @client.search(@config[:dropbox][:root], track)
|
67
|
-
results.reject! { |result| result['is_dir'] }
|
68
|
-
end
|
69
|
-
|
70
|
-
# if there were no results, raise err
|
71
|
-
if results.empty?
|
72
|
-
raise "Track not found in Dropbox: #{selection.reject{|k,v| k == :id}.values.join(':')}"
|
73
|
-
|
74
|
-
# if there's only one result, return it
|
75
|
-
elsif results.length == 1
|
76
|
-
results[0]['path']
|
77
|
-
|
78
|
-
# if there are multiple results, score them based on artist + album
|
79
|
-
else
|
80
|
-
scores = Hash.new 0
|
81
|
-
results.each do |result|
|
82
|
-
[:artist, :album].each do |identifier|
|
83
|
-
tokens = selection[identifier].downcase
|
84
|
-
.gsub(%r( +), " ")
|
85
|
-
.gsub(%r(-+), "-")
|
86
|
-
.strip
|
87
|
-
.split(/[\s-]/)
|
88
|
-
tokens.each do |token|
|
89
|
-
scores[result['path']] += 1 if result['path'].downcase.include? token
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
# return the path that has the highest score
|
95
|
-
(scores.sort_by { |path, score| score })[-1][0]
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
end
|
100
|
-
|
101
|
-
end
|