Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions config/devices.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ devices:
unsupported_characters:
- "™"
- '"'
uppercase_paths: true

- name: Octatrack
sample_rates:
Expand Down
9 changes: 6 additions & 3 deletions lib/wavesync/device.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ class Device
attr_reader :bar_multiple #: Integer?
attr_reader :unsupported_characters #: Array[String]
attr_reader :transliterate_metadata #: bool
attr_reader :uppercase_paths #: bool

#: (name: String, sample_rates: Array[Integer], file_types: Array[String], ?bit_depths: Array[Integer], ?bpm_source: Symbol?, ?bar_multiple: Integer?, ?unsupported_characters: Array[String], ?transliterate_metadata: bool) -> void
def initialize(name:, sample_rates:, file_types:, bit_depths: [], bpm_source: nil, bar_multiple: nil, unsupported_characters: [], transliterate_metadata: false)
#: (name: String, sample_rates: Array[Integer], file_types: Array[String], ?bit_depths: Array[Integer], ?bpm_source: Symbol?, ?bar_multiple: Integer?, ?unsupported_characters: Array[String], ?transliterate_metadata: bool, ?uppercase_paths: bool) -> void
def initialize(name:, sample_rates:, file_types:, bit_depths: [], bpm_source: nil, bar_multiple: nil, unsupported_characters: [], transliterate_metadata: false, uppercase_paths: false)
@name = name
@sample_rates = sample_rates
@bit_depths = bit_depths
Expand All @@ -23,6 +24,7 @@ def initialize(name:, sample_rates:, file_types:, bit_depths: [], bpm_source: ni
@bar_multiple = bar_multiple
@unsupported_characters = unsupported_characters
@transliterate_metadata = transliterate_metadata
@uppercase_paths = uppercase_paths
end

#: () -> String
Expand Down Expand Up @@ -52,7 +54,8 @@ def self.load_from_yaml
bpm_source: attrs['bpm_source']&.to_sym,
bar_multiple: attrs['bar_multiple'],
unsupported_characters: attrs['unsupported_characters'] || [],
transliterate_metadata: attrs['transliterate_metadata'] || false
transliterate_metadata: attrs['transliterate_metadata'] || false,
uppercase_paths: attrs['uppercase_paths'] || false
)
end
end
Expand Down
12 changes: 11 additions & 1 deletion lib/wavesync/path_resolver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ def resolve(source_file_path, audio, target_file_type: nil)
bpm = audio.bpm
target_path = add_bpm_to_filename(target_path, bpm) if @device.bpm_source == :filename && bpm

strip_unsupported_characters(target_path)
target_path = strip_unsupported_characters(target_path)
uppercase_relative_path(target_path)
end

#: (Pathname target_path, Audio audio) -> Array[Pathname]
Expand Down Expand Up @@ -59,6 +60,15 @@ def strip_unsupported_characters(path)
Pathname(path.to_s.delete(@device.unsupported_characters.join))
end

#: (Pathname path) -> Pathname
def uppercase_relative_path(path)
return path unless @device.uppercase_paths

relative = path.relative_path_from(@target_library_path)
uppercased = relative.each_filename.map(&:upcase).join('/')
@target_library_path.join(uppercased)
end

#: (Pathname path) -> Pathname
def remove_bpm_from_filename(path)
ext = path.extname
Expand Down
14 changes: 14 additions & 0 deletions test/wavesync/device_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,20 @@ class DeviceTest < Wavesync::TestCase
assert_includes tp7.unsupported_characters, '"'
end

test 'TP-7 has uppercase_paths enabled' do
tp7 = Device.find_by(name: 'TP-7')
assert_equal true, tp7.uppercase_paths
end

test 'uppercase_paths defaults to false' do
device = Device.new(
name: 'Test',
sample_rates: [44_100],
file_types: ['wav']
)
assert_equal false, device.uppercase_paths
end

test 'Octatrack device attributes are correct' do
octatrack = Device.find_by(name: 'Octatrack')
refute_nil octatrack
Expand Down
2 changes: 1 addition & 1 deletion test/wavesync/file_converter_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def teardown
test 'convert skips when converted file already exists in target location' do
source_aiff = File.join(@source_dir, 'track.aiff')
FileUtils.touch(source_aiff)
FileUtils.touch(File.join(@target_dir, 'track.mp3'))
FileUtils.touch(File.join(@target_dir, 'TRACK.MP3'))

Audio.any_instance.stubs(:duration).returns(0)
audio = stub(bpm: nil, duration: 0)
Expand Down
36 changes: 26 additions & 10 deletions test/wavesync/path_resolver_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,23 @@ def setup
audio = stub(bpm: nil)
target_path = resolver_for('TP-7').resolve(source_file, audio)

assert_equal '/media/device/music/artist/album/song.wav', target_path.to_s
assert_equal '/media/device/music/ARTIST/ALBUM/SONG.WAV', target_path.to_s
end

test 'resolve changes file extension when target_file_type provided' do
source_file = '/home/user/music/artist/album/song.m4a'
audio = stub(bpm: nil)
target_path = resolver_for('TP-7').resolve(source_file, audio, target_file_type: 'wav')

assert_equal '/media/device/music/artist/album/song.wav', target_path.to_s
assert_equal '/media/device/music/ARTIST/ALBUM/SONG.WAV', target_path.to_s
end

test 'resolve handles complex paths with spaces and special characters' do
source_file = '/home/user/music/electronic/aphex twin/selected ambient works/xtal.aiff'
audio = stub(bpm: nil)
target_path = resolver_for('TP-7').resolve(source_file, audio, target_file_type: 'wav')

assert_equal '/media/device/music/electronic/aphex twin/selected ambient works/xtal.wav',
assert_equal '/media/device/music/ELECTRONIC/APHEX TWIN/SELECTED AMBIENT WORKS/XTAL.WAV',
target_path.to_s
end

Expand All @@ -52,7 +52,7 @@ def setup
audio = stub(bpm: nil)
target_path = resolver.resolve(source_file, audio)

assert_equal '/media/device/artist/song.wav', target_path.to_s
assert_equal '/media/device/ARTIST/SONG.WAV', target_path.to_s
end

test 'handles source library with trailing slash' do
Expand All @@ -62,7 +62,7 @@ def setup
audio = stub(bpm: nil)
target_path = resolver.resolve(source_file, audio)

assert_equal '/media/device/artist/song.wav', target_path.to_s
assert_equal '/media/device/ARTIST/SONG.WAV', target_path.to_s
end

test 'adds bpm to filename when device has bpm_source :filename' do
Expand All @@ -78,7 +78,7 @@ def setup
audio = stub(bpm: 140)
target_path = resolver_for('TP-7').resolve(source_file, audio)

assert_equal '/media/device/music/artist/song.wav', target_path.to_s
assert_equal '/media/device/music/ARTIST/SONG.WAV', target_path.to_s
end

test 'does not add bpm to filename when audio has no bpm' do
Expand Down Expand Up @@ -186,15 +186,15 @@ def setup
audio = stub(bpm: nil)
target_path = resolver_for('TP-7').resolve(source_file, audio)

assert_equal '/media/device/music/artist/song.wav', target_path.to_s
assert_equal '/media/device/music/ARTIST/SONG.WAV', target_path.to_s
end

test 'resolve strips double-quote from filename' do
source_file = '/home/user/music/artist/song "remix".wav'
audio = stub(bpm: nil)
target_path = resolver_for('TP-7').resolve(source_file, audio)

assert_equal '/media/device/music/artist/song remix.wav', target_path.to_s
assert_equal '/media/device/music/ARTIST/SONG REMIX.WAV', target_path.to_s
end

test 'resolve strips unsupported characters from filename for Octatrack with bpm' do
Expand All @@ -218,15 +218,31 @@ def setup
audio = stub(bpm: nil)
target_path = resolver_for('TP-7').resolve(source_file, audio)

assert_equal '/media/device/music/artist/song.wav', target_path.to_s
assert_equal '/media/device/music/ARTIST/SONG.WAV', target_path.to_s
end

test 'resolve strips double-quote from directory names' do
source_file = '/home/user/music/artist "label"/song.wav'
audio = stub(bpm: nil)
target_path = resolver_for('TP-7').resolve(source_file, audio)

assert_equal '/media/device/music/artist label/song.wav', target_path.to_s
assert_equal '/media/device/music/ARTIST LABEL/SONG.WAV', target_path.to_s
end

test 'resolve uppercases folder and file names for TP-7' do
source_file = '/home/user/music/Deep House/my track.wav'
audio = stub(bpm: nil)
target_path = resolver_for('TP-7').resolve(source_file, audio)

assert_equal '/media/device/music/DEEP HOUSE/MY TRACK.WAV', target_path.to_s
end

test 'resolve does not uppercase for Octatrack' do
source_file = '/home/user/music/Deep House/my track.wav'
audio = stub(bpm: nil)
target_path = resolver_for('Octatrack').resolve(source_file, audio)

assert_equal '/media/device/music/Deep House/my track.wav', target_path.to_s
end

test 'find_files_to_cleanup does not include the target file itself' do
Expand Down
14 changes: 7 additions & 7 deletions test/wavesync/scanner_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def teardown
source_wav = File.join(@source_dir, 'track.wav')
FileUtils.cp(fixture('44100_16.wav'), source_wav)

target_wav = File.join(@target_dir, 'track.wav')
target_wav = File.join(@target_dir, 'TRACK.WAV')
FileUtils.cp(fixture('44100_16.wav'), target_wav)
cue_points = [{ identifier: 1, sample_offset: 44_100, label: 'Marker' }]
CueChunk.write(target_wav, "#{target_wav}.tmp", cue_points)
Expand All @@ -61,7 +61,7 @@ def teardown
CueChunk.write(source_wav, "#{source_wav}.tmp", cue_points)
FileUtils.mv("#{source_wav}.tmp", source_wav)

target_wav = File.join(@target_dir, 'track.wav')
target_wav = File.join(@target_dir, 'TRACK.WAV')
FileUtils.cp(source_wav, target_wav)

Audio.any_instance.expects(:write_cue_points).never
Expand Down Expand Up @@ -97,7 +97,7 @@ def teardown
source_wav = File.join(File.expand_path(@source_dir), 'track.wav')
FileUtils.cp(fixture('44100_16.wav'), source_wav)

expected_target = Pathname(File.join(File.expand_path(@target_dir), 'track.wav'))
expected_target = Pathname(File.join(File.expand_path(@target_dir), 'TRACK.WAV'))
Audio.any_instance.stubs(:bpm).returns(120)
AcidChunk.stubs(:write_bpm)
Logger.expects(:log_error).with(
Expand All @@ -112,7 +112,7 @@ def teardown
source_wav = File.join(File.expand_path(@source_dir), 'track.wav')
FileUtils.cp(fixture('44100_16.wav'), source_wav)

expected_target = Pathname(File.join(File.expand_path(@target_dir), 'track.wav'))
expected_target = Pathname(File.join(File.expand_path(@target_dir), 'TRACK.WAV'))
Audio.any_instance.stubs(:bpm).returns(nil)
FileUtils.stubs(:install)
Logger.expects(:log_error).with(
Expand All @@ -127,7 +127,7 @@ def teardown
source_aiff = File.join(File.expand_path(@source_dir), 'track.aiff')
FileUtils.cp(fixture('44100_16.aiff'), source_aiff)

expected_target = Pathname(File.join(File.expand_path(@target_dir), 'track.wav'))
expected_target = Pathname(File.join(File.expand_path(@target_dir), 'TRACK.WAV'))
Audio.any_instance.stubs(:transcode).returns(true)
Logger.expects(:log_error).with(
instance_of(RuntimeError),
Expand All @@ -152,15 +152,15 @@ def teardown
Audio.any_instance.expects(:transcode).never
Scanner.new(@source_dir).sync(@target_dir, @device)

target_mp3 = File.join(@target_dir, 'track.mp3')
target_mp3 = File.join(@target_dir, 'TRACK.MP3')
assert File.exist?(target_mp3)
end

test 'safe_copy logs error with source and target when ENOENT is raised' do
source_wav = File.join(File.expand_path(@source_dir), 'track.wav')
FileUtils.cp(fixture('44100_16.wav'), source_wav)

expected_target = Pathname(File.join(File.expand_path(@target_dir), 'track.wav'))
expected_target = Pathname(File.join(File.expand_path(@target_dir), 'TRACK.WAV'))
FileUtils.stubs(:install).raises(Errno::ENOENT)
Logger.expects(:log_error).with(
instance_of(Errno::ENOENT),
Expand Down
Loading