henchman-sync 0.3.3 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|