rubycue 0.0.1

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