wavesync 1.0.0.alpha2 → 1.0.0.alpha4
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 +95 -59
- data/config/devices.yml +20 -0
- data/lib/wavesync/acid_chunk.rb +45 -4
- data/lib/wavesync/analyzer.rb +17 -4
- data/lib/wavesync/audio.rb +153 -90
- data/lib/wavesync/audio_format.rb +9 -2
- data/lib/wavesync/bpm_detector.rb +14 -7
- data/lib/wavesync/cli.rb +3 -0
- data/lib/wavesync/commands/analyze.rb +2 -0
- data/lib/wavesync/commands/clear_cache.rb +75 -0
- data/lib/wavesync/commands/command.rb +4 -1
- data/lib/wavesync/commands/help.rb +6 -0
- data/lib/wavesync/commands/pull.rb +43 -0
- data/lib/wavesync/commands/setlist.rb +66 -0
- data/lib/wavesync/commands/sync.rb +19 -26
- data/lib/wavesync/commands.rb +52 -12
- data/lib/wavesync/config.rb +43 -3
- data/lib/wavesync/cue_chunk.rb +203 -0
- data/lib/wavesync/device.rb +32 -7
- data/lib/wavesync/essentia_bpm_detector.rb +38 -0
- data/lib/wavesync/ffmpeg/probe.rb +74 -0
- data/lib/wavesync/ffmpeg.rb +144 -0
- data/lib/wavesync/file_converter.rb +7 -2
- data/lib/wavesync/libmtp.rb +333 -0
- data/lib/wavesync/logger.rb +84 -0
- data/lib/wavesync/path_resolver.rb +32 -6
- data/lib/wavesync/percival_bpm_detector.rb +31 -0
- data/lib/wavesync/python_venv.rb +25 -0
- data/lib/wavesync/scanner.rb +143 -27
- data/lib/wavesync/{set.rb → setlist.rb} +28 -12
- data/lib/wavesync/setlist_editor.rb +556 -0
- data/lib/wavesync/track_padding.rb +15 -4
- data/lib/wavesync/transport/filesystem.rb +36 -0
- data/lib/wavesync/transport/mtp.rb +285 -0
- data/lib/wavesync/transport.rb +21 -0
- data/lib/wavesync/ui.rb +67 -12
- data/lib/wavesync/version.rb +2 -1
- data/lib/wavesync.rb +7 -2
- metadata +17 -32
- data/lib/wavesync/commands/set.rb +0 -63
- data/lib/wavesync/set_editor.rb +0 -245
data/lib/wavesync/set_editor.rb
DELETED
|
@@ -1,245 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'tty-prompt'
|
|
4
|
-
require 'io/console'
|
|
5
|
-
|
|
6
|
-
module Wavesync
|
|
7
|
-
class SetEditor
|
|
8
|
-
KEY_MAP = {
|
|
9
|
-
'a' => :add,
|
|
10
|
-
'u' => :move_up,
|
|
11
|
-
'd' => :move_down,
|
|
12
|
-
'r' => :remove,
|
|
13
|
-
's' => :save,
|
|
14
|
-
'c' => :quit,
|
|
15
|
-
' ' => :toggle_play,
|
|
16
|
-
"\e[A" => :cursor_up,
|
|
17
|
-
"\e[B" => :cursor_down
|
|
18
|
-
}.freeze
|
|
19
|
-
|
|
20
|
-
def initialize(set, library_path)
|
|
21
|
-
@set = set
|
|
22
|
-
@library_path = library_path
|
|
23
|
-
@prompt = TTY::Prompt.new(interrupt: :exit, active_color: :red)
|
|
24
|
-
@ui = UI.new
|
|
25
|
-
@selected = @set.tracks.empty? ? nil : 0
|
|
26
|
-
@player_pid = nil
|
|
27
|
-
@player_track = nil
|
|
28
|
-
@player_state = :stopped
|
|
29
|
-
@player_offset = 0
|
|
30
|
-
@player_started_at = nil
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def run
|
|
34
|
-
loop do
|
|
35
|
-
check_player
|
|
36
|
-
render
|
|
37
|
-
action = KEY_MAP[read_key]
|
|
38
|
-
next unless action
|
|
39
|
-
|
|
40
|
-
result = handle_action(action)
|
|
41
|
-
break if result == :quit
|
|
42
|
-
end
|
|
43
|
-
ensure
|
|
44
|
-
stop_playback
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
private
|
|
48
|
-
|
|
49
|
-
def read_key
|
|
50
|
-
$stdin.raw do |io|
|
|
51
|
-
char = io.getc
|
|
52
|
-
if char == "\e"
|
|
53
|
-
rest = begin
|
|
54
|
-
io.read_nonblock(3)
|
|
55
|
-
rescue StandardError
|
|
56
|
-
''
|
|
57
|
-
end
|
|
58
|
-
char + rest
|
|
59
|
-
else
|
|
60
|
-
char
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def relative_path(absolute)
|
|
66
|
-
absolute.sub("#{@library_path}/", '')
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def display_name(relative)
|
|
70
|
-
File.basename(relative, '.*').sub(/\A\d+[\s.\-_]+/, '')
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
def render(title = "wavesync set #{@set.name}")
|
|
74
|
-
@ui.clear
|
|
75
|
-
puts @ui.color(title, :primary)
|
|
76
|
-
puts
|
|
77
|
-
|
|
78
|
-
if @set.tracks.empty?
|
|
79
|
-
puts @ui.color(' (no tracks)', :secondary)
|
|
80
|
-
else
|
|
81
|
-
@set.tracks.each_with_index do |track, i|
|
|
82
|
-
render_track(i, relative_path(track), i == @selected, track == @player_track)
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
puts
|
|
87
|
-
puts @ui.color('[↑↓] navigate [space] play/pause [a] add [u] up [d] down [r] remove [s] save [c] cancel',
|
|
88
|
-
:secondary)
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
def render_track(index, relative, selected, playing)
|
|
92
|
-
name = display_name(relative)
|
|
93
|
-
folder = File.dirname(relative)
|
|
94
|
-
icon = if playing
|
|
95
|
-
@player_state == :paused ? '⏸' : '▶'
|
|
96
|
-
else
|
|
97
|
-
' '
|
|
98
|
-
end
|
|
99
|
-
puts "#{@ui.color(icon, :surface)} #{@ui.color("#{index + 1}.", selected ? :highlight : :extra)} #{@ui.color(name, selected ? :highlight : :primary)}"
|
|
100
|
-
puts @ui.color(" #{folder}", selected ? :highlight : :tertiary) unless folder == '.'
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
def handle_action(action)
|
|
104
|
-
case action
|
|
105
|
-
when :cursor_up
|
|
106
|
-
@selected = [@selected - 1, 0].max unless @set.tracks.empty?
|
|
107
|
-
when :cursor_down
|
|
108
|
-
@selected = [@selected + 1, @set.tracks.size - 1].min unless @set.tracks.empty?
|
|
109
|
-
when :toggle_play then toggle_playback
|
|
110
|
-
when :add then add_track
|
|
111
|
-
when :remove then remove_track
|
|
112
|
-
when :move_up then move_track(:up)
|
|
113
|
-
when :move_down then move_track(:down)
|
|
114
|
-
when :save
|
|
115
|
-
@set.save
|
|
116
|
-
path = Set.set_path(@library_path, @set.name)
|
|
117
|
-
puts @ui.color("Saved '#{@set.name}' to #{path}.", :primary)
|
|
118
|
-
:quit
|
|
119
|
-
when :quit
|
|
120
|
-
:quit
|
|
121
|
-
end
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
def toggle_playback
|
|
125
|
-
return if @selected.nil?
|
|
126
|
-
|
|
127
|
-
track = @set.tracks[@selected]
|
|
128
|
-
|
|
129
|
-
if @player_track == track
|
|
130
|
-
case @player_state
|
|
131
|
-
when :playing
|
|
132
|
-
@player_offset += Time.now - @player_started_at
|
|
133
|
-
kill_player
|
|
134
|
-
@player_state = :paused
|
|
135
|
-
when :paused
|
|
136
|
-
start_player(track, @player_offset)
|
|
137
|
-
end
|
|
138
|
-
else
|
|
139
|
-
stop_playback
|
|
140
|
-
start_player(track)
|
|
141
|
-
end
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
def start_player(track, offset = 0)
|
|
145
|
-
ffplay = FFMPEG.ffmpeg_binary.sub('ffmpeg', 'ffplay')
|
|
146
|
-
args = [ffplay, '-nodisp', '-autoexit', '-loglevel', 'quiet', '-probesize', '32', '-analyzeduration', '0']
|
|
147
|
-
args += ['-ss', offset.to_s] if offset.positive?
|
|
148
|
-
args << track
|
|
149
|
-
@player_pid = spawn(*args, out: File::NULL, err: File::NULL)
|
|
150
|
-
@player_track = track
|
|
151
|
-
@player_offset = offset
|
|
152
|
-
@player_started_at = Time.now
|
|
153
|
-
@player_state = :playing
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
def kill_player
|
|
157
|
-
return unless @player_pid
|
|
158
|
-
|
|
159
|
-
Process.kill('TERM', @player_pid)
|
|
160
|
-
@player_pid = nil
|
|
161
|
-
rescue Errno::ESRCH
|
|
162
|
-
@player_pid = nil
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
def stop_playback
|
|
166
|
-
kill_player
|
|
167
|
-
@player_track = nil
|
|
168
|
-
@player_state = :stopped
|
|
169
|
-
@player_offset = 0
|
|
170
|
-
@player_started_at = nil
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
def check_player
|
|
174
|
-
return unless @player_pid
|
|
175
|
-
|
|
176
|
-
result = Process.waitpid(@player_pid, Process::WNOHANG)
|
|
177
|
-
return unless result
|
|
178
|
-
|
|
179
|
-
@player_pid = nil
|
|
180
|
-
@player_track = nil
|
|
181
|
-
@player_state = :stopped
|
|
182
|
-
@player_offset = 0
|
|
183
|
-
advance_and_play
|
|
184
|
-
rescue Errno::ECHILD
|
|
185
|
-
@player_pid = nil
|
|
186
|
-
@player_track = nil
|
|
187
|
-
@player_state = :stopped
|
|
188
|
-
@player_offset = 0
|
|
189
|
-
advance_and_play
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
def advance_and_play
|
|
193
|
-
return if @selected.nil? || @selected >= @set.tracks.size - 1
|
|
194
|
-
|
|
195
|
-
@selected += 1
|
|
196
|
-
start_player(@set.tracks[@selected])
|
|
197
|
-
end
|
|
198
|
-
|
|
199
|
-
def audio_files
|
|
200
|
-
@audio_files ||= Audio.find_all(@library_path)
|
|
201
|
-
end
|
|
202
|
-
|
|
203
|
-
def add_track
|
|
204
|
-
if audio_files.empty?
|
|
205
|
-
puts @ui.color('No audio files found in library.', :highlight)
|
|
206
|
-
sleep 1
|
|
207
|
-
return
|
|
208
|
-
end
|
|
209
|
-
|
|
210
|
-
choices = audio_files.map { |file| { name: relative_path(file), value: file } }
|
|
211
|
-
|
|
212
|
-
render("wavesync set #{@set.name} — add track")
|
|
213
|
-
puts
|
|
214
|
-
picked = @prompt.select('Select a track to add:', choices, cycle: true, filter: true, per_page: 20)
|
|
215
|
-
|
|
216
|
-
insert_at = @selected.nil? ? 0 : @selected + 1
|
|
217
|
-
@set.tracks.insert(insert_at, picked)
|
|
218
|
-
@selected = insert_at
|
|
219
|
-
end
|
|
220
|
-
|
|
221
|
-
def remove_track
|
|
222
|
-
return if @set.tracks.empty? || @selected.nil?
|
|
223
|
-
|
|
224
|
-
stop_playback if @player_track == @set.tracks[@selected]
|
|
225
|
-
@set.remove_track(@selected)
|
|
226
|
-
@selected = if @set.tracks.empty?
|
|
227
|
-
nil
|
|
228
|
-
else
|
|
229
|
-
[@selected, @set.tracks.size - 1].min
|
|
230
|
-
end
|
|
231
|
-
end
|
|
232
|
-
|
|
233
|
-
def move_track(direction)
|
|
234
|
-
return if @set.tracks.size < 2 || @selected.nil?
|
|
235
|
-
|
|
236
|
-
if direction == :up
|
|
237
|
-
@set.move_up(@selected)
|
|
238
|
-
@selected = [@selected - 1, 0].max
|
|
239
|
-
else
|
|
240
|
-
@set.move_down(@selected)
|
|
241
|
-
@selected = [@selected + 1, @set.tracks.size - 1].min
|
|
242
|
-
end
|
|
243
|
-
end
|
|
244
|
-
end
|
|
245
|
-
end
|