rubycue 0.0.1

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.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ *.swp
data/MIT-LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2010 Blake Smith
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
data/README.rdoc ADDED
@@ -0,0 +1,48 @@
1
+ = RubyCue: A library to parse cuesheet song files
2
+
3
+ RubyCue is a Ruby interface for parsing and accessing cuesheet data.
4
+
5
+ = Usage:
6
+
7
+ === Basic usage
8
+
9
+ require 'rubycue'
10
+
11
+ cuesheet = RubyCue::Cuesheet.new(File.read("cuesheet.cue"))
12
+ cuesheet.parse!
13
+
14
+ cuesheet.songs.each do |song|
15
+ puts "#{song[:performer]} - #{song[:title]} at #{song[:index]}"
16
+ end
17
+
18
+ === Calculated Song Durations
19
+
20
+ RubyCue will also calculate song durations. In order to calculate the last song's duration, you must pass the optional track duration (in seconds) for the entire file.
21
+
22
+ cuesheet = RubyCue::Cuesheet.new(File.read("cuesheet.cue"), 7143)
23
+
24
+ === Current Song Position
25
+
26
+ After a cuesheet is instantiated, you can pass a current track position, and the cuesheet will return which song is currently playing at that position:
27
+
28
+ # with seconds
29
+ cuesheet.position(4352)
30
+ => {:index=>#<RubyCue::Index:0x101025240 @seconds=7, @minutes=71, @frames=31>, :track=>32, :performer=>"Netsky", :title=>"Iron Heart", :duration=>#<RubyCue::Index:0x10100f2d8 @seconds=55, @minutes=2, @frames=38>}
31
+
32
+ # with an array [minutes, seconds, frames]
33
+ cuesheet.position([43, 23, 45])
34
+ => {:index=>#<RubyCue::Index:0x101027590 @seconds=27, @minutes=39, @frames=57>, :track=>19, :performer=>"Legion feat. Adam Wright", :title=>"Both Sides", :duration=>#<RubyCue::Index:0x101013130 @seconds=45, @minutes=4, @frames=13>}
35
+
36
+ === Index objects
37
+
38
+ Index objects are a time representation of the position in a track. They are represented with fields [minutes, seconds, frames]. There are 75 frames in each second. You can access the values of the index object like so:
39
+
40
+ index = Index.new([30, 23, 73])
41
+ index.minutes => 30
42
+ index.seconds => 23
43
+ index.frames => 73
44
+ index.to_i => 1823
45
+ index.to_f => 1823.97333333333
46
+
47
+ There's other stuff you can do with them, check out the source for more in-depth usage.
48
+
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ require 'rake'
2
+ require 'spec/rake/spectask'
3
+
4
+ Spec::Rake::SpecTask.new(:spec) do |t|
5
+ t.spec_files = FileList['spec/unit/*_spec.rb']
6
+ end
7
+
8
+ task :default => :spec
9
+
10
+ begin
11
+ require 'jeweler'
12
+ Jeweler::Tasks.new do |gemspec|
13
+ gemspec.name = "rubycue"
14
+ gemspec.summary = "Ruby cuesheet track parser"
15
+ gemspec.description = "Basic ruby parser for song cuesheets"
16
+ gemspec.email = "blakesmith0@gmail.com"
17
+ gemspec.homepage = "http://github.com/blakesmith/rubycue"
18
+ gemspec.authors = ["Blake Smith"]
19
+ end
20
+ rescue LoadError
21
+ puts "Jeweler not available. Install it with: gem install jeweler"
22
+ end
23
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,92 @@
1
+ module RubyCue
2
+ class Cuesheet
3
+ attr_reader :cuesheet, :songs, :track_duration
4
+
5
+ def initialize(cuesheet, track_duration=nil)
6
+ @cuesheet = cuesheet
7
+ @reg = {
8
+ :track => %r(TRACK (\d{1,3}) AUDIO),
9
+ :performer => %r(PERFORMER "(.*)"),
10
+ :title => %r(TITLE "(.*)"),
11
+ :index => %r(INDEX \d{1,3} (\d{1,3}):(\d{1,2}):(\d{1,2}))
12
+ }
13
+ @track_duration = RubyCue::Index.new(track_duration) if track_duration
14
+ end
15
+
16
+ def parse!
17
+ @songs = parse_titles.map{|title| {:title => title}}
18
+ @songs.each_with_index do |song, i|
19
+ song[:performer] = parse_performers[i]
20
+ song[:track] = parse_tracks[i]
21
+ song[:index] = parse_indices[i]
22
+ end
23
+ raise RubyCue::InvalidCuesheet.new("Field amounts are not all present. Cuesheet is malformed!") unless valid?
24
+ calculate_song_durations!
25
+ true
26
+ end
27
+
28
+ def position(value)
29
+ index = Index.new(value)
30
+ return @songs.first if index < @songs.first[:index]
31
+ @songs.each_with_index do |song, i|
32
+ return song if song == @songs.last
33
+ return song if between(song[:index], @songs[i+1][:index], index)
34
+ end
35
+ end
36
+
37
+ def valid?
38
+ @songs.all? do |song|
39
+ [:performer, :track, :index, :title].all? do |key|
40
+ song[key] != nil
41
+ end
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def calculate_song_durations!
48
+ @songs.each_with_index do |song, i|
49
+ if song == @songs.last
50
+ song[:duration] = (@track_duration - song[:index]) if @track_duration
51
+ return
52
+ end
53
+ song[:duration] = @songs[i+1][:index] - song[:index]
54
+ end
55
+ end
56
+
57
+ def between(a, b, position_index)
58
+ (position_index > a) && (position_index < b)
59
+ end
60
+
61
+ def parse_titles
62
+ unless @titles
63
+ @titles = cuesheet_scan(:title).map{|title| title.first}
64
+ @titles.delete_at(0)
65
+ end
66
+ @titles
67
+ end
68
+
69
+ def parse_performers
70
+ unless @performers
71
+ @performers = cuesheet_scan(:performer).map{|performer| performer.first}
72
+ @performers.delete_at(0)
73
+ end
74
+ @performers
75
+ end
76
+
77
+ def parse_tracks
78
+ @tracks ||= cuesheet_scan(:track).map{|track| track.first.to_i}
79
+ end
80
+
81
+ def parse_indices
82
+ @indices ||= cuesheet_scan(:index).map{|index| RubyCue::Index.new([index[0].to_i, index[1].to_i, index[2].to_i])}
83
+ end
84
+
85
+ def cuesheet_scan(field)
86
+ scan = @cuesheet.scan(@reg[field])
87
+ raise InvalidCuesheet.new("No fields were found for #{field.to_s}") if scan.empty?
88
+ scan
89
+ end
90
+
91
+ end
92
+ end
@@ -0,0 +1,3 @@
1
+ module RubyCue
2
+ class InvalidCuesheet < ::RuntimeError; end
3
+ end
@@ -0,0 +1,108 @@
1
+ module RubyCue
2
+ class Index
3
+ SECONDS_PER_MINUTE = 60
4
+ FRAMES_PER_SECOND = 75
5
+ FRAMES_PER_MINUTE = FRAMES_PER_SECOND * 60
6
+
7
+ attr_reader :minutes, :seconds, :frames
8
+
9
+ def initialize(value=nil)
10
+ case value
11
+ when Array
12
+ set_from_array!(value)
13
+ when Integer
14
+ set_from_integer!(value)
15
+ end
16
+ end
17
+
18
+ def to_f
19
+ ((@minutes * SECONDS_PER_MINUTE) + (@seconds) + (@frames.to_f / FRAMES_PER_SECOND)).to_f
20
+ end
21
+
22
+ def to_i
23
+ to_f.floor
24
+ end
25
+
26
+ def to_a
27
+ [@minutes, @seconds, @frames]
28
+ end
29
+
30
+ def to_s
31
+ "#{'%02d' % @minutes}:#{'%02d' % @seconds}:#{'%02d' % @frames}"
32
+ end
33
+
34
+ def +(other)
35
+ self.class.new(carrying_addition(other))
36
+ end
37
+
38
+ def -(other)
39
+ self.class.new(carrying_subtraction(other))
40
+ end
41
+
42
+ def >(other)
43
+ self.to_f > other.to_f
44
+ end
45
+
46
+ def <(other)
47
+ self.to_f < other.to_f
48
+ end
49
+
50
+ def ==(other)
51
+ self.to_a == other.to_a
52
+ end
53
+
54
+ def each
55
+ to_a.each {|value| yield value }
56
+ end
57
+
58
+ private
59
+
60
+ def carrying_addition(other)
61
+ minutes, seconds, frames = *[@minutes + other.minutes,
62
+ @seconds + other.seconds, @frames + other.frames]
63
+
64
+ seconds, frames = *convert_with_rate(frames, seconds, FRAMES_PER_SECOND)
65
+ minutes, seconds = *convert_with_rate(seconds, minutes, SECONDS_PER_MINUTE)
66
+ [minutes, seconds, frames]
67
+ end
68
+
69
+ def carrying_subtraction(other)
70
+ seconds = minutes = 0
71
+
72
+ my_frames = @frames + (@seconds * FRAMES_PER_SECOND) + (@minutes * FRAMES_PER_MINUTE)
73
+ other_frames = other.frames + (other.seconds * FRAMES_PER_SECOND) + (other.minutes * FRAMES_PER_MINUTE)
74
+ frames = my_frames - other_frames
75
+
76
+ seconds, frames = *convert_with_rate(frames, seconds, FRAMES_PER_SECOND)
77
+ minutes, seconds = *convert_with_rate(seconds, minutes, SECONDS_PER_MINUTE)
78
+ [minutes, seconds, frames]
79
+ end
80
+
81
+ def convert_with_rate(from, to, rate, step=1)
82
+ while from >= rate
83
+ to += step
84
+ from -= rate
85
+ end
86
+ [to, from]
87
+ end
88
+
89
+ def set_from_array!(array)
90
+ if array.size != 3 || array.any?{|element| !element.is_a?(Integer)}
91
+ raise ArgumentError.new("Must be initialized with an array in the format of [minutes, seconds,frames], all integers")
92
+ end
93
+ @minutes, @seconds, @frames = *array
94
+ end
95
+
96
+ def set_from_integer!(seconds)
97
+ @minutes = 0
98
+ @frames = 0
99
+ @seconds = seconds
100
+
101
+ while @seconds >= SECONDS_PER_MINUTE
102
+ @minutes += 1
103
+ @seconds -= SECONDS_PER_MINUTE
104
+ end
105
+ end
106
+
107
+ end
108
+ end
data/lib/rubycue.rb ADDED
@@ -0,0 +1,5 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), "rubycue")
2
+
3
+ require 'cuesheet'
4
+ require 'index'
5
+ require 'exceptions'
@@ -0,0 +1,214 @@
1
+ PERFORMER "Netsky"
2
+ TITLE "Essential Mix (2010-10-09)"
3
+ FILE "2010-10-09 - Essential Mix - Netsky.mp3" MP3
4
+ TRACK 01 AUDIO
5
+ PERFORMER "Essential Mix"
6
+ TITLE "Intro"
7
+ INDEX 01 00:00:00
8
+ TRACK 02 AUDIO
9
+ PERFORMER "Shameboy vs. Friction, Camo & Krooked feat. Dynamite MC"
10
+ TITLE "Strobot (Netsky Remix) vs. Stand Up"
11
+ INDEX 01 01:50:07
12
+ TRACK 03 AUDIO
13
+ PERFORMER "Danny Byrd feat. I-Kay"
14
+ TITLE "Ill Behaviour"
15
+ INDEX 01 05:26:67
16
+ TRACK 04 AUDIO
17
+ PERFORMER "Die & Interface feat. William Cartwright"
18
+ TITLE "Bright Lights (Netsky Remix)"
19
+ INDEX 01 07:16:59
20
+ TRACK 05 AUDIO
21
+ PERFORMER "Nu:Tone feat. Natalie Williams"
22
+ TITLE "Shine In"
23
+ INDEX 01 10:33:72
24
+ TRACK 06 AUDIO
25
+ PERFORMER "Netsky"
26
+ TITLE "Porcelain VIP vs. I Refuse (Acapella)"
27
+ INDEX 01 12:56:64
28
+ TRACK 07 AUDIO
29
+ PERFORMER "Netsky"
30
+ TITLE "Do It Do It vs. I Refuse (Acapella)"
31
+ INDEX 01 15:52:31
32
+ TRACK 08 AUDIO
33
+ PERFORMER "Brookes Brothers"
34
+ TITLE "Beautiful"
35
+ INDEX 01 17:31:10
36
+ TRACK 09 AUDIO
37
+ PERFORMER "Muffler"
38
+ TITLE "Uplifter"
39
+ INDEX 01 20:26:47
40
+ TRACK 10 AUDIO
41
+ PERFORMER "Indivision & Livewire feat. Nelver"
42
+ TITLE "Irresistible"
43
+ INDEX 01 22:15:03
44
+ TRACK 11 AUDIO
45
+ PERFORMER "Bcee & S.P.Y."
46
+ TITLE "Is There Anybody Out There?"
47
+ INDEX 01 23:44:12
48
+ TRACK 12 AUDIO
49
+ PERFORMER "Nu:Logic vs. Brooklyn"
50
+ TITLE "Bleeper vs. With All My Heart"
51
+ INDEX 01 25:33:68
52
+ TRACK 13 AUDIO
53
+ PERFORMER "Chords"
54
+ TITLE "Radio"
55
+ INDEX 01 27:23:42
56
+ TRACK 14 AUDIO
57
+ PERFORMER "Danny Byrd feat. Netsky"
58
+ TITLE "Tonight"
59
+ INDEX 01 29:46:15
60
+ TRACK 15 AUDIO
61
+ PERFORMER "Leftfield"
62
+ TITLE "Release The Pressure (Netsky Remix)"
63
+ INDEX 01 31:24:68
64
+ TRACK 16 AUDIO
65
+ PERFORMER "Danny Byrd feat. London Elektricity"
66
+ TITLE "Failsafe"
67
+ INDEX 01 35:04:31
68
+ TRACK 17 AUDIO
69
+ PERFORMER "Netsky"
70
+ TITLE "Everyday"
71
+ INDEX 01 37:16:06
72
+ TRACK 18 AUDIO
73
+ PERFORMER "Zero 7"
74
+ TITLE "In The Waiting Line (D+B Remix)"
75
+ INDEX 01 38:21:66
76
+ TRACK 19 AUDIO
77
+ PERFORMER "Legion feat. Adam Wright"
78
+ TITLE "Both Sides"
79
+ INDEX 01 39:27:57
80
+ TRACK 20 AUDIO
81
+ PERFORMER "New Zealand Shapeshifter"
82
+ TITLE "The Touch (Netsky Remix)"
83
+ INDEX 01 44:12:70
84
+ TRACK 21 AUDIO
85
+ PERFORMER "Swedish House Mafia"
86
+ TITLE "One (Netsky Remix)"
87
+ INDEX 01 46:23:20
88
+ TRACK 22 AUDIO
89
+ PERFORMER "Netsky"
90
+ TITLE "Pirate Bay VIP"
91
+ INDEX 01 48:36:23
92
+ TRACK 23 AUDIO
93
+ PERFORMER "Danny Byrd feat. Cyantific"
94
+ TITLE "Judgement Day VIP"
95
+ INDEX 01 51:09:68
96
+ TRACK 24 AUDIO
97
+ PERFORMER "Netsky vs. Sidney Samson"
98
+ TITLE "Tomorrow's Another Dub VIP vs. Riverside (Breakage Remix)"
99
+ INDEX 01 53:21:46
100
+ TRACK 25 AUDIO
101
+ PERFORMER "The Bloody Beetroots feat. Steve Aoki"
102
+ TITLE "Warp (Dirtyphonics Remix)"
103
+ INDEX 01 55:37:44
104
+ TRACK 26 AUDIO
105
+ PERFORMER "Danny Byrd vs. Burial"
106
+ TITLE "We Can Have It All vs. Archangel (Edit)"
107
+ INDEX 01 56:05:01
108
+ TRACK 27 AUDIO
109
+ PERFORMER "DJ Fresh & Sigma vs. Netsky"
110
+ TITLE "Lassitude VIP vs. Escape"
111
+ INDEX 01 59:03:20
112
+ TRACK 28 AUDIO
113
+ PERFORMER "Commix"
114
+ TITLE "Painted Smile"
115
+ INDEX 01 63:04:52
116
+ TRACK 29 AUDIO
117
+ PERFORMER "Eastcolors"
118
+ TITLE "Go To Nowhere"
119
+ INDEX 01 66:00:20
120
+ TRACK 30 AUDIO
121
+ PERFORMER "Nu:Logic"
122
+ TITLE "Bigfoot"
123
+ INDEX 01 67:49:73
124
+ TRACK 31 AUDIO
125
+ PERFORMER "Random Movement"
126
+ TITLE "Can't Resist (S.P.Y. Remix)"
127
+ INDEX 01 69:17:50
128
+ TRACK 32 AUDIO
129
+ PERFORMER "Netsky"
130
+ TITLE "Iron Heart"
131
+ INDEX 01 71:07:31
132
+ TRACK 33 AUDIO
133
+ PERFORMER "Unicorn Kid vs. DJ Fresh & Sigma"
134
+ TITLE "Wild Life (Nu:Tone Remix) vs. Cylon"
135
+ INDEX 01 74:02:69
136
+ TRACK 34 AUDIO
137
+ PERFORMER "Agent Alvin"
138
+ TITLE "Move"
139
+ INDEX 01 76:35:15
140
+ TRACK 35 AUDIO
141
+ PERFORMER "S.P.Y."
142
+ TITLE "Go With The Flow"
143
+ INDEX 01 78:01:51
144
+ TRACK 36 AUDIO
145
+ PERFORMER "Metrik"
146
+ TITLE "T 1000"
147
+ INDEX 01 79:37:45
148
+ TRACK 37 AUDIO
149
+ PERFORMER "Culture Shock"
150
+ TITLE "Cathedral"
151
+ INDEX 01 81:21:27
152
+ TRACK 38 AUDIO
153
+ PERFORMER "Blokhe4d"
154
+ TITLE "The Way Life Used To Be"
155
+ INDEX 01 83:33:35
156
+ TRACK 39 AUDIO
157
+ PERFORMER "Hamilton"
158
+ TITLE "Soundboy"
159
+ INDEX 01 85:45:13
160
+ TRACK 40 AUDIO
161
+ PERFORMER "Squash"
162
+ TITLE "Divine"
163
+ INDEX 01 87:34:63
164
+ TRACK 41 AUDIO
165
+ PERFORMER "Above & Beyond & Gareth Emery"
166
+ TITLE "On A Good Day (J Majik & Wickaman Remix)"
167
+ INDEX 01 89:24:45
168
+ TRACK 42 AUDIO
169
+ PERFORMER "Metrik"
170
+ TITLE "Arrival (Instrumental)"
171
+ INDEX 01 91:36:16
172
+ TRACK 43 AUDIO
173
+ PERFORMER "Spy"
174
+ TITLE "By Your Side (Logistics Remix)"
175
+ INDEX 01 94:09:53
176
+ TRACK 44 AUDIO
177
+ PERFORMER "Netsky"
178
+ TITLE "Come Back Home"
179
+ INDEX 01 97:05:34
180
+ TRACK 45 AUDIO
181
+ PERFORMER "Axwell & Sebastian Ingrosso"
182
+ TITLE "Together (D+B Remix)"
183
+ INDEX 01 99:16:36
184
+ TRACK 46 AUDIO
185
+ PERFORMER "Netsky"
186
+ TITLE "Rise & Shine"
187
+ INDEX 01 101:28:54
188
+ TRACK 47 AUDIO
189
+ PERFORMER "Apex"
190
+ TITLE "String Theory"
191
+ INDEX 01 102:56:39
192
+ TRACK 48 AUDIO
193
+ PERFORMER "DJ Marky & S.P.Y."
194
+ TITLE "Touch Me"
195
+ INDEX 01 105:52:04
196
+ TRACK 49 AUDIO
197
+ PERFORMER "Subwave"
198
+ TITLE "Road Rage"
199
+ INDEX 01 107:19:56
200
+ TRACK 50 AUDIO
201
+ PERFORMER "Joe Syntax"
202
+ TITLE "Slingshot"
203
+ INDEX 01 109:53:36
204
+ TRACK 51 AUDIO
205
+ PERFORMER "Pendulum"
206
+ TITLE "Witchcraft (Netsky Remix)"
207
+ INDEX 01 110:58:14
208
+ TRACK 52 AUDIO
209
+ PERFORMER "London Elektricity"
210
+ TITLE "The Great Drum & Bass Swindle (Logistics Remix)"
211
+ INDEX 01 112:27:02
212
+ TRACK 53 AUDIO
213
+ PERFORMER "Netsky vs. Genetic Bros"
214
+ TITLE "The Lotus Symphony vs. Uplifting"
@@ -0,0 +1,215 @@
1
+ PERFORMER "Netsky"
2
+ TITLE "Essential Mix (2010-10-09)"
3
+ FILE "2010-10-09 - Essential Mix - Netsky.mp3" MP3
4
+ TRACK 01 AUDIO
5
+ PERFORMER "Essential Mix"
6
+ TITLE "Intro"
7
+ INDEX 01 00:00:00
8
+ TRACK 02 AUDIO
9
+ PERFORMER "Shameboy vs. Friction, Camo & Krooked feat. Dynamite MC"
10
+ TITLE "Strobot (Netsky Remix) vs. Stand Up"
11
+ INDEX 01 01:50:07
12
+ TRACK 03 AUDIO
13
+ PERFORMER "Danny Byrd feat. I-Kay"
14
+ TITLE "Ill Behaviour"
15
+ INDEX 01 05:26:67
16
+ TRACK 04 AUDIO
17
+ PERFORMER "Die & Interface feat. William Cartwright"
18
+ TITLE "Bright Lights (Netsky Remix)"
19
+ INDEX 01 07:16:59
20
+ TRACK 05 AUDIO
21
+ PERFORMER "Nu:Tone feat. Natalie Williams"
22
+ TITLE "Shine In"
23
+ INDEX 01 10:33:72
24
+ TRACK 06 AUDIO
25
+ PERFORMER "Netsky"
26
+ TITLE "Porcelain VIP vs. I Refuse (Acapella)"
27
+ INDEX 01 12:56:64
28
+ TRACK 07 AUDIO
29
+ PERFORMER "Netsky"
30
+ TITLE "Do It Do It vs. I Refuse (Acapella)"
31
+ INDEX 01 15:52:31
32
+ TRACK 08 AUDIO
33
+ PERFORMER "Brookes Brothers"
34
+ TITLE "Beautiful"
35
+ INDEX 01 17:31:10
36
+ TRACK 09 AUDIO
37
+ PERFORMER "Muffler"
38
+ TITLE "Uplifter"
39
+ INDEX 01 20:26:47
40
+ TRACK 10 AUDIO
41
+ PERFORMER "Indivision & Livewire feat. Nelver"
42
+ TITLE "Irresistible"
43
+ INDEX 01 22:15:03
44
+ TRACK 11 AUDIO
45
+ PERFORMER "Bcee & S.P.Y."
46
+ TITLE "Is There Anybody Out There?"
47
+ INDEX 01 23:44:12
48
+ TRACK 12 AUDIO
49
+ PERFORMER "Nu:Logic vs. Brooklyn"
50
+ TITLE "Bleeper vs. With All My Heart"
51
+ INDEX 01 25:33:68
52
+ TRACK 13 AUDIO
53
+ PERFORMER "Chords"
54
+ TITLE "Radio"
55
+ INDEX 01 27:23:42
56
+ TRACK 14 AUDIO
57
+ PERFORMER "Danny Byrd feat. Netsky"
58
+ TITLE "Tonight"
59
+ INDEX 01 29:46:15
60
+ TRACK 15 AUDIO
61
+ PERFORMER "Leftfield"
62
+ TITLE "Release The Pressure (Netsky Remix)"
63
+ INDEX 01 31:24:68
64
+ TRACK 16 AUDIO
65
+ PERFORMER "Danny Byrd feat. London Elektricity"
66
+ TITLE "Failsafe"
67
+ INDEX 01 35:04:31
68
+ TRACK 17 AUDIO
69
+ PERFORMER "Netsky"
70
+ TITLE "Everyday"
71
+ INDEX 01 37:16:06
72
+ TRACK 18 AUDIO
73
+ PERFORMER "Zero 7"
74
+ TITLE "In The Waiting Line (D+B Remix)"
75
+ INDEX 01 38:21:66
76
+ TRACK 19 AUDIO
77
+ PERFORMER "Legion feat. Adam Wright"
78
+ TITLE "Both Sides"
79
+ INDEX 01 39:27:57
80
+ TRACK 20 AUDIO
81
+ PERFORMER "New Zealand Shapeshifter"
82
+ TITLE "The Touch (Netsky Remix)"
83
+ INDEX 01 44:12:70
84
+ TRACK 21 AUDIO
85
+ PERFORMER "Swedish House Mafia"
86
+ TITLE "One (Netsky Remix)"
87
+ INDEX 01 46:23:20
88
+ TRACK 22 AUDIO
89
+ PERFORMER "Netsky"
90
+ TITLE "Pirate Bay VIP"
91
+ INDEX 01 48:36:23
92
+ TRACK 23 AUDIO
93
+ PERFORMER "Danny Byrd feat. Cyantific"
94
+ TITLE "Judgement Day VIP"
95
+ INDEX 01 51:09:68
96
+ TRACK 24 AUDIO
97
+ PERFORMER "Netsky vs. Sidney Samson"
98
+ TITLE "Tomorrow's Another Dub VIP vs. Riverside (Breakage Remix)"
99
+ INDEX 01 53:21:46
100
+ TRACK 25 AUDIO
101
+ PERFORMER "The Bloody Beetroots feat. Steve Aoki"
102
+ TITLE "Warp (Dirtyphonics Remix)"
103
+ INDEX 01 55:37:44
104
+ TRACK 26 AUDIO
105
+ PERFORMER "Danny Byrd vs. Burial"
106
+ TITLE "We Can Have It All vs. Archangel (Edit)"
107
+ INDEX 01 56:05:01
108
+ TRACK 27 AUDIO
109
+ PERFORMER "DJ Fresh & Sigma vs. Netsky"
110
+ TITLE "Lassitude VIP vs. Escape"
111
+ INDEX 01 59:03:20
112
+ TRACK 28 AUDIO
113
+ PERFORMER "Commix"
114
+ TITLE "Painted Smile"
115
+ INDEX 01 63:04:52
116
+ TRACK 29 AUDIO
117
+ PERFORMER "Eastcolors"
118
+ TITLE "Go To Nowhere"
119
+ INDEX 01 66:00:20
120
+ TRACK 30 AUDIO
121
+ PERFORMER "Nu:Logic"
122
+ TITLE "Bigfoot"
123
+ INDEX 01 67:49:73
124
+ TRACK 31 AUDIO
125
+ PERFORMER "Random Movement"
126
+ TITLE "Can't Resist (S.P.Y. Remix)"
127
+ INDEX 01 69:17:50
128
+ TRACK 32 AUDIO
129
+ PERFORMER "Netsky"
130
+ TITLE "Iron Heart"
131
+ INDEX 01 71:07:31
132
+ TRACK 33 AUDIO
133
+ PERFORMER "Unicorn Kid vs. DJ Fresh & Sigma"
134
+ TITLE "Wild Life (Nu:Tone Remix) vs. Cylon"
135
+ INDEX 01 74:02:69
136
+ TRACK 34 AUDIO
137
+ PERFORMER "Agent Alvin"
138
+ TITLE "Move"
139
+ INDEX 01 76:35:15
140
+ TRACK 35 AUDIO
141
+ PERFORMER "S.P.Y."
142
+ TITLE "Go With The Flow"
143
+ INDEX 01 78:01:51
144
+ TRACK 36 AUDIO
145
+ PERFORMER "Metrik"
146
+ TITLE "T 1000"
147
+ INDEX 01 79:37:45
148
+ TRACK 37 AUDIO
149
+ PERFORMER "Culture Shock"
150
+ TITLE "Cathedral"
151
+ INDEX 01 81:21:27
152
+ TRACK 38 AUDIO
153
+ PERFORMER "Blokhe4d"
154
+ TITLE "The Way Life Used To Be"
155
+ INDEX 01 83:33:35
156
+ TRACK 39 AUDIO
157
+ PERFORMER "Hamilton"
158
+ TITLE "Soundboy"
159
+ INDEX 01 85:45:13
160
+ TRACK 40 AUDIO
161
+ PERFORMER "Squash"
162
+ TITLE "Divine"
163
+ INDEX 01 87:34:63
164
+ TRACK 41 AUDIO
165
+ PERFORMER "Above & Beyond & Gareth Emery"
166
+ TITLE "On A Good Day (J Majik & Wickaman Remix)"
167
+ INDEX 01 89:24:45
168
+ TRACK 42 AUDIO
169
+ PERFORMER "Metrik"
170
+ TITLE "Arrival (Instrumental)"
171
+ INDEX 01 91:36:16
172
+ TRACK 43 AUDIO
173
+ PERFORMER "Spy"
174
+ TITLE "By Your Side (Logistics Remix)"
175
+ INDEX 01 94:09:53
176
+ TRACK 44 AUDIO
177
+ PERFORMER "Netsky"
178
+ TITLE "Come Back Home"
179
+ INDEX 01 97:05:34
180
+ TRACK 45 AUDIO
181
+ PERFORMER "Axwell & Sebastian Ingrosso"
182
+ TITLE "Together (D+B Remix)"
183
+ INDEX 01 99:16:36
184
+ TRACK 46 AUDIO
185
+ PERFORMER "Netsky"
186
+ TITLE "Rise & Shine"
187
+ INDEX 01 101:28:54
188
+ TRACK 47 AUDIO
189
+ PERFORMER "Apex"
190
+ TITLE "String Theory"
191
+ INDEX 01 102:56:39
192
+ TRACK 48 AUDIO
193
+ PERFORMER "DJ Marky & S.P.Y."
194
+ TITLE "Touch Me"
195
+ INDEX 01 105:52:04
196
+ TRACK 49 AUDIO
197
+ PERFORMER "Subwave"
198
+ TITLE "Road Rage"
199
+ INDEX 01 107:19:56
200
+ TRACK 50 AUDIO
201
+ PERFORMER "Joe Syntax"
202
+ TITLE "Slingshot"
203
+ INDEX 01 109:53:36
204
+ TRACK 51 AUDIO
205
+ PERFORMER "Pendulum"
206
+ TITLE "Witchcraft (Netsky Remix)"
207
+ INDEX 01 110:58:14
208
+ TRACK 52 AUDIO
209
+ PERFORMER "London Elektricity"
210
+ TITLE "The Great Drum & Bass Swindle (Logistics Remix)"
211
+ INDEX 01 112:27:02
212
+ TRACK 53 AUDIO
213
+ PERFORMER "Netsky vs. Genetic Bros"
214
+ TITLE "The Lotus Symphony vs. Uplifting"
215
+ INDEX 01 115:22:47
@@ -0,0 +1,8 @@
1
+ require 'rubygems'
2
+ require 'rspec'
3
+
4
+ require File.join(File.dirname(__FILE__), "../lib/rubycue")
5
+
6
+ def load_cuesheet(cuename)
7
+ File.read(File.join(File.dirname(__FILE__), "fixtures/#{cuename}.cue"))
8
+ end
@@ -0,0 +1,102 @@
1
+ require File.join(File.dirname(__FILE__), "../spec_helper")
2
+
3
+ describe RubyCue::Cuesheet do
4
+ before do
5
+ @cuesheet_file = load_cuesheet("test")
6
+ @cuesheet = RubyCue::Cuesheet.new(@cuesheet_file)
7
+ @cuesheet.parse!
8
+ end
9
+
10
+ it "stores the cuesheet string" do
11
+ cuesheet = RubyCue::Cuesheet.new(@cuesheet_file)
12
+ cuesheet.cuesheet.should == @cuesheet_file
13
+ end
14
+
15
+ describe "#parse!" do
16
+ context "properly formatted cuesheet" do
17
+ it "returns true if successfully parsed" do
18
+ @cuesheet.parse!.should be_true
19
+ end
20
+
21
+ it "has the right first track" do
22
+ @cuesheet.songs.first[:title].should == "Intro"
23
+ end
24
+
25
+ it "has the right last track" do
26
+ @cuesheet.songs.last[:title].should == "The Lotus Symphony vs. Uplifting"
27
+ end
28
+
29
+ it "has the right first performer" do
30
+ @cuesheet.songs.first[:performer].should == "Essential Mix"
31
+ end
32
+
33
+ it "has the right last performer" do
34
+ @cuesheet.songs.last[:performer].should == "Netsky vs. Genetic Bros"
35
+ end
36
+
37
+ it "has the right first index" do
38
+ @cuesheet.songs.first[:index].should == [0, 0, 0]
39
+ end
40
+
41
+ it "has the right last index" do
42
+ @cuesheet.songs.last[:index].should == [115, 22, 47]
43
+ end
44
+
45
+ it "has the right first track" do
46
+ @cuesheet.songs.first[:track].should == 1
47
+ end
48
+
49
+ it "has the right last track" do
50
+ @cuesheet.songs.last[:track].should == 53
51
+ end
52
+
53
+ it "has the right amonut of tracks" do
54
+ @cuesheet.songs.size.should == 53
55
+ end
56
+
57
+ describe "#calculate_song_duration!" do
58
+ it "properly calculates song duration at the beginning of the track" do
59
+ @cuesheet.songs.first[:duration].to_a.should == [1, 50, 07]
60
+ end
61
+
62
+ it "properly calculates song duration in the middle of the track" do
63
+ @cuesheet.songs[20][:duration].to_a.should == [2, 13, 3]
64
+ end
65
+
66
+ it "properly calculates song duration of the last song given the user inputs the total track length" do
67
+ cuesheet = RubyCue::Cuesheet.new(load_cuesheet('test'), 7185)
68
+ cuesheet.parse!
69
+ cuesheet.songs.last[:duration].to_a.should == [4, 22, 28]
70
+ end
71
+ end
72
+ end
73
+
74
+ context "improperly formatted cuesheet" do
75
+ it "should raise an exception for a bogus formatted cuesheet" do
76
+ cuesheet = RubyCue::Cuesheet.new("Something bogus")
77
+ lambda { cuesheet.parse! }.should raise_error(RubyCue::InvalidCuesheet)
78
+ end
79
+
80
+ it "raises an exception if all our fields don't find the same amount of items" do
81
+ cue = load_cuesheet('malformed')
82
+ cuesheet = RubyCue::Cuesheet.new(cue)
83
+ lambda { cuesheet.parse! }.should raise_error(RubyCue::InvalidCuesheet)
84
+ end
85
+ end
86
+ end
87
+
88
+ describe "#position" do
89
+ it "returns the current song in the cuesheet based on the designated position" do
90
+ @cuesheet.position(1943).should == @cuesheet.songs[14]
91
+ end
92
+
93
+ it "returns the first song if a negative position is passed" do
94
+ @cuesheet.position(-5).should == @cuesheet.songs[0]
95
+ end
96
+
97
+ it "returns the last song if a position greater than the last index is passed" do
98
+ @cuesheet.position(10000000).should == @cuesheet.songs.last
99
+ end
100
+ end
101
+
102
+ end
@@ -0,0 +1,243 @@
1
+ require File.join(File.dirname(__FILE__), "../spec_helper")
2
+
3
+ describe RubyCue::Index do
4
+ describe "init" do
5
+ it "allows empty initialization" do
6
+ lambda { RubyCue::Index.new }.should_not raise_error
7
+ end
8
+
9
+ it "initializes from an array if specified default" do
10
+ index = RubyCue::Index.new([5, 0, 75])
11
+ index.minutes.should == 5
12
+ index.seconds.should == 0
13
+ index.frames.should == 75
14
+ end
15
+
16
+ it "initializes from integer seconds less than a minute" do
17
+ index = RubyCue::Index.new(30)
18
+ index.minutes.should == 0
19
+ index.seconds.should == 30
20
+ index.frames.should == 0
21
+ end
22
+
23
+ it "initializes from integer seconds over a minute" do
24
+ index = RubyCue::Index.new(90)
25
+ index.minutes.should == 1
26
+ index.seconds.should == 30
27
+ index.frames.should == 0
28
+ end
29
+
30
+ it "initializes from integer seconds at a minute" do
31
+ index = RubyCue::Index.new(60)
32
+ index.minutes.should == 1
33
+ index.seconds.should == 0
34
+ index.frames.should == 0
35
+ end
36
+
37
+ it "raises an error if the array size is not size 3" do
38
+ lambda { RubyCue::Index.new([0, 0, 0, 0]) }.should raise_error(ArgumentError)
39
+ end
40
+
41
+ it "raises an error if not all the array elements are integers" do
42
+ lambda { RubyCue::Index.new([0, "moose", 0]) }.should raise_error(ArgumentError)
43
+ end
44
+
45
+ context "conversions" do
46
+ describe "#to_f" do
47
+ it "converts an array value under a minute" do
48
+ index = RubyCue::Index.new([0, 30, 0])
49
+ index.to_f.should == 30.0
50
+ end
51
+
52
+ it "converts an array value over a minute" do
53
+ index = RubyCue::Index.new([1, 30, 0])
54
+ index.to_f.should == 90.0
55
+ end
56
+
57
+ it "converts an array value over a minute with frames" do
58
+ index = RubyCue::Index.new([1, 30, 74])
59
+ index.to_f.should == (90 + (74 / 75.0)).to_f
60
+ end
61
+ end
62
+
63
+ describe "#to_s" do
64
+ it "renders the index as a string with leading zeroes" do
65
+ index = RubyCue::Index.new([1, 30, 74]).to_s.should == "01:30:74"
66
+ end
67
+ end
68
+
69
+ describe "#to_i" do
70
+ it "converts to seconds and rounds down" do
71
+ index = RubyCue::Index.new([0, 30, 74])
72
+ index.to_i.should == 30
73
+ end
74
+ end
75
+
76
+ describe "#to_a" do
77
+ it "converts to an array" do
78
+ index = RubyCue::Index.new([1, 2, 3])
79
+ index.to_a.should == [1, 2, 3]
80
+ end
81
+ end
82
+ end
83
+
84
+ context "computations" do
85
+ describe "#+" do
86
+ it "returns an object of Class Index" do
87
+ index1 = RubyCue::Index.new([0, 30, 0])
88
+ index2 = RubyCue::Index.new([0, 30, 0])
89
+
90
+ (index1 + index2).class.should == RubyCue::Index
91
+ end
92
+
93
+ it "adds two indices under a minute" do
94
+ index1 = RubyCue::Index.new([0, 30, 0])
95
+ index2 = RubyCue::Index.new([0, 30, 0])
96
+
97
+ (index1 + index2).to_a.should == [1, 0, 0]
98
+ end
99
+
100
+ it "adds two indices over a minute" do
101
+ index1 = RubyCue::Index.new([1, 30, 0])
102
+ index2 = RubyCue::Index.new([2, 20, 0])
103
+
104
+ (index1 + index2).to_a.should == [3, 50, 0]
105
+ end
106
+
107
+ it "adds two indices with frames" do
108
+ index1 = RubyCue::Index.new([1, 30, 50])
109
+ index2 = RubyCue::Index.new([2, 20, 50])
110
+
111
+ (index1 + index2).to_a.should == [3, 51, 25]
112
+ end
113
+
114
+ it "adds two indices with only frames" do
115
+ index1 = RubyCue::Index.new([0, 0, 50])
116
+ index2 = RubyCue::Index.new([0, 0, 25])
117
+
118
+ (index1 + index2).to_a.should == [0, 1, 0]
119
+ end
120
+ end
121
+
122
+ describe "#-" do
123
+ it "returns an object of Class Index" do
124
+ index1 = RubyCue::Index.new([0, 30, 0])
125
+ index2 = RubyCue::Index.new([0, 30, 0])
126
+
127
+ (index1 - index2).class.should == RubyCue::Index
128
+ end
129
+
130
+ it "subtracts two indices with only frames" do
131
+ index1 = RubyCue::Index.new([0, 0, 50])
132
+ index2 = RubyCue::Index.new([0, 0, 25])
133
+
134
+ (index1 - index2).to_a.should == [0, 0, 25]
135
+ end
136
+
137
+ it "subtracts two indices with minutes" do
138
+ index1 = RubyCue::Index.new([3, 20, 50])
139
+ index2 = RubyCue::Index.new([2, 40, 25])
140
+
141
+ (index1 - index2).to_a.should == [0, 40, 25]
142
+ end
143
+
144
+ it "subtracts two indices with even minutes" do
145
+ index1 = RubyCue::Index.new([3, 0, 0])
146
+ index2 = RubyCue::Index.new([2, 0, 0])
147
+
148
+ (index1 - index2).to_a.should == [1, 0, 0]
149
+ end
150
+
151
+ end
152
+
153
+ describe "#>" do
154
+ it "returns true for frames" do
155
+ index1 = RubyCue::Index.new([0, 0, 50])
156
+ index2 = RubyCue::Index.new([0, 0, 25])
157
+
158
+ (index1 > index2).should be_true
159
+ end
160
+
161
+ it "returns true for seconds" do
162
+ index1 = RubyCue::Index.new([0, 30, 50])
163
+ index2 = RubyCue::Index.new([0, 29, 25])
164
+
165
+ (index1 > index2).should be_true
166
+ end
167
+
168
+ it "returns true for minutes" do
169
+ index1 = RubyCue::Index.new([2, 30, 50])
170
+ index2 = RubyCue::Index.new([1, 29, 25])
171
+
172
+ (index1 > index2).should be_true
173
+ end
174
+
175
+ it "returns true for the same time" do
176
+ index1 = RubyCue::Index.new([1, 30, 50])
177
+ index2 = RubyCue::Index.new([1, 30, 50])
178
+
179
+ (index1 > index2).should be_false
180
+ end
181
+ end
182
+
183
+ describe "#<" do
184
+ it "returns false for frames" do
185
+ index1 = RubyCue::Index.new([0, 0, 50])
186
+ index2 = RubyCue::Index.new([0, 0, 25])
187
+
188
+ (index1 < index2).should be_false
189
+ end
190
+
191
+ it "returns false for seconds" do
192
+ index1 = RubyCue::Index.new([0, 30, 50])
193
+ index2 = RubyCue::Index.new([0, 29, 25])
194
+
195
+ (index1 < index2).should be_false
196
+ end
197
+
198
+ it "returns false for minutes" do
199
+ index1 = RubyCue::Index.new([2, 30, 50])
200
+ index2 = RubyCue::Index.new([1, 29, 25])
201
+
202
+ (index1 < index2).should be_false
203
+ end
204
+
205
+ it "returns false for the same time" do
206
+ index1 = RubyCue::Index.new([1, 30, 50])
207
+ index2 = RubyCue::Index.new([1, 30, 50])
208
+
209
+ (index1 < index2).should be_false
210
+ end
211
+ end
212
+
213
+ describe "#==" do
214
+ it "returns true if they're the same" do
215
+ index1 = RubyCue::Index.new([1, 30, 50])
216
+ index2 = RubyCue::Index.new([1, 30, 50])
217
+
218
+ (index1 == index2).should be_true
219
+ end
220
+
221
+ it "returns false if they're not the same" do
222
+ index1 = RubyCue::Index.new([1, 30, 50])
223
+ index2 = RubyCue::Index.new([1, 30, 51])
224
+
225
+ (index1 == index2).should be_false
226
+ end
227
+ end
228
+ end
229
+
230
+ describe "#each" do
231
+ it "yields each minute, second, frame value" do
232
+ array = []
233
+ index = RubyCue::Index.new([1, 30, 45])
234
+ index.each do |value|
235
+ array << value
236
+ end
237
+
238
+ array.to_a.should == index.to_a
239
+ end
240
+ end
241
+
242
+ end
243
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rubycue
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Blake Smith
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-10-26 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: Basic ruby parser for song cuesheets
23
+ email: blakesmith0@gmail.com
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files:
29
+ - README.rdoc
30
+ files:
31
+ - .gitignore
32
+ - MIT-LICENSE
33
+ - README.rdoc
34
+ - Rakefile
35
+ - VERSION
36
+ - lib/rubycue.rb
37
+ - lib/rubycue/cuesheet.rb
38
+ - lib/rubycue/exceptions.rb
39
+ - lib/rubycue/index.rb
40
+ - spec/fixtures/malformed.cue
41
+ - spec/fixtures/test.cue
42
+ - spec/spec_helper.rb
43
+ - spec/unit/cuesheet_spec.rb
44
+ - spec/unit/index_spec.rb
45
+ has_rdoc: true
46
+ homepage: http://github.com/blakesmith/rubycue
47
+ licenses: []
48
+
49
+ post_install_message:
50
+ rdoc_options:
51
+ - --charset=UTF-8
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ hash: 3
69
+ segments:
70
+ - 0
71
+ version: "0"
72
+ requirements: []
73
+
74
+ rubyforge_project:
75
+ rubygems_version: 1.3.7
76
+ signing_key:
77
+ specification_version: 3
78
+ summary: Ruby cuesheet track parser
79
+ test_files:
80
+ - spec/spec_helper.rb
81
+ - spec/unit/cuesheet_spec.rb
82
+ - spec/unit/index_spec.rb