xspf 0.3 → 0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -7,21 +7,40 @@
7
7
  # association with the Ruby license, the GPL). See the "doc" subdirectory of
8
8
  # the XSPF distribution for the texts of these licenses.
9
9
  # -----------------------------------------------------------------------------
10
- # XSPF for Ruby website : http://www.elpauer.org/xspf
10
+ # XSPF for Ruby website : http://xspf.rubyforge.org
11
11
  # =============================================================================
12
12
  #++
13
13
 
14
14
  require 'rexml/document'
15
15
  require 'xml/xslt'
16
16
 
17
- # :main: XSPF
17
+ # :include: USAGE
18
+ # :main: USAGE
18
19
 
19
20
  module MetaGen #:nodoc:
20
21
 
21
22
  # define the method
22
23
  def self.add_method(klass, meth_name, body, meth_rdoc)
23
- code = "# #{meth_rdoc}\n def #{meth_name.downcase}; begin; #{body}; rescue NoMethodError; return nil; end; end"
24
+ code = <<-CODE
25
+ # #{meth_rdoc}
26
+ def #{meth_name.downcase}
27
+ @#{meth_name}
28
+ end
29
+
30
+ def #{meth_name.downcase}=(value)
31
+ @#{meth_name.downcase} = value
32
+ end
24
33
 
34
+ private
35
+ def parse_#{meth_name.downcase}
36
+ begin
37
+ #{body}
38
+ rescue NoMethodError
39
+ return nil
40
+ end
41
+ end
42
+ CODE
43
+
25
44
  klass.module_eval(code)
26
45
 
27
46
  # hook to write klass + name attrib to a file
@@ -34,10 +53,19 @@ module MetaGen #:nodoc:
34
53
  end
35
54
 
36
55
  # output in different formats
37
- def self.add_output_format(klass, format, xspf, meth_rdoc)
56
+ # FIXME Only works in parse mode, not in generation mode.
57
+ def self.add_output_format(klass, format, meth_rdoc)
38
58
  xslt_path = "'#{File.join( File.dirname(__FILE__), %Q{xspf2#{format}.xsl} )}'"
39
- code = "# #{meth_rdoc}\n def to_#{format}; xslt = XML::XSLT.new; xslt.xml = xspf; xslt.xsl = REXML::Document.new( File.new( #{xslt_path} ) ); xslt.serve; end"
40
-
59
+ code = <<-CODE
60
+ # #{meth_rdoc}
61
+ def to_#{format}
62
+ xslt = XML::XSLT.new
63
+ xslt.xml = self.to_xml
64
+ xslt.xsl = REXML::Document.new( File.new( #{xslt_path} ) )
65
+ xslt.serve
66
+ end
67
+ CODE
68
+
41
69
  klass.module_eval(code)
42
70
 
43
71
  if $META_RDOC
@@ -50,150 +78,89 @@ module MetaGen #:nodoc:
50
78
 
51
79
  end
52
80
 
53
- # XML Shareable Playlist Format (XSPF[http://www.xspf.org]) parser for Ruby
54
- #
55
- # When parsing, if a XSPF attribute or element is not set, the corresponding method will return _nil_.
56
- #
57
- # === Examples
58
- # ==== Parse from file
59
- # require 'xspf'
60
- #
61
- # f = File.new("playlist.xspf")
62
- # x = XSPF.new(f)
63
- # pl = XSPF::Playlist.new(x)
64
- # tl = XSPF::Tracklist.new(pl)
65
- #
66
- # puts "XML version: #{x.version}"
67
- # puts "XML encoding: #{ x.encoding}"
68
- # puts "XSPF version: #{pl.version}"
69
- # puts "Namespace: #{pl.xmlns}"
70
- # puts "Playlist title: #{pl.title}"
71
- # puts "Playlist creator: #{pl.creator}"
72
- # puts "Playlist annotation: #{pl.annotation}"
73
- # puts "Playlist info: #{pl.info}"
74
- # puts "Playlist identifier: #{pl.identifier}"
75
- # puts "Playlist attribution: #{pl.attribution}"
76
- # puts "Tracklist: #{pl.tracklist}"
77
- # tl.tracks.each do |t|
78
- # puts "Track identifier: #{t.identifier}"
79
- # puts "Track title: #{t.title}"
80
- # puts "Track creator: #{t.creator}"
81
- # puts "Track duration: #{t.duration}"
82
- # puts "Track metainformation: link=#{t.meta_rel} content=#{t.meta_content}"
83
- # end
84
- #
85
- # # Convert the XSPF document to SMIL
86
- # x.to_smil
87
- #
88
- # # Convert the XSPF document to HTML
89
- # x.to_html
90
- #
91
- # # Convert the XSPF document in a M3U playlist
92
- # x.to_m3u
93
- #
94
- # ==== Parse from string
95
- # playlist_document = <<-END_OF_PLAYLIST
96
- # <?xml version="1.0" encoding="UTF-8"?>
97
- # <playlist version="0" xmlns="http://xspf.org/ns/0/">
98
- # <title>XSPlF it up!</title>
99
- # <creator>Mayhem &amp; Chaos Coordinator</creator>
100
- # <annotation>Just a few songs to enjoy while you XSPlF it up!</annotation>
101
- # <info>http://mayhem-chaos.net/xspf/xspf_it_up.html</info>
102
- # <identifier>http://mayhem-chaos.net/xspf/xspf_it_up/1.0</identifier>
103
- # <attribution>
104
- # <location>ihttp://mayhem-chaos.net/xspf/xspf_it_up.html</location>
105
- # </attribution>
106
- # <trackList>
107
- # <track>
108
- # <identifier>http://musicbrainz.org/track/bdab6db0-2fd6-4166-a5fa-fbf2ff213793</identifier>
109
- # <title>I Wanna Get High</title>
110
- # <creator>Cypress Hill</creator>
111
- # <duration>174613</duration>
112
- # <meta rel="http://musicbrainz.org/track">http://musicbrainz.org/mm-2.1/track/bdab6db0-2fd6-4166-a5fa-fbf2ff213793</meta>
113
- # </track>
114
- # <track>
115
- # <identifier>bdc846e7-6c26-4193-82a6-8d1b5a4d3429</identifier>
116
- # <title>Smoke Two Joints</title>
117
- # <creator>Sublime</creator>
118
- # <duration>175466</duration>
119
- # <meta rel="http://musicbrainz.org/track">http://musicbrainz.org/mm-2.1/track/bdc846e7-6c26-4193-82a6-8d1b5a4d3429</meta>
120
- # </track>
121
- # <track>
122
- # <identifier>http://musicbrainz.org/track/7d9776f7-d428-40dc-a425-3c6e3dce4d58</identifier>
123
- # <title>Hash Pipe</title>
124
- # <creator>Weezer</creator>
125
- # <duration>186533</duration>
126
- # <meta rel="http://musicbrainz.org/track">http://musicbrainz.org/mm-2.1/track/7d9776f7-d428-40dc-a425-3c6e3dce4d58</meta>
127
- # </track>
128
- # </trackList>
129
- # </playlist>
130
- # END_OF_PLAYLIST
131
- #
132
- # x = XSPF.new(playlist_document)
133
- # pl = XSPF::Playlist.new(x)
134
- # tl = XSPF::Tracklist.new(pl)
135
- #
136
- # puts "XML version: #{x.version}"
137
- # puts "XML encoding: #{ x.encoding}"
138
- # puts "XSPF version: #{pl.version}"
139
- # puts "Namespace: #{pl.xmlns}"
140
- # puts "Playlist title: #{pl.title}"
141
- # puts "Playlist creator: #{pl.creator}"
142
- # puts "Playlist annotation: #{pl.annotation}"
143
- # puts "Playlist info: #{pl.info}"
144
- # puts "Playlist identifier: #{pl.identifier}"
145
- # puts "Playlist attribution: #{pl.attribution}"
146
- # puts "Tracklist: #{pl.tracklist}"
147
- # tl.tracks.each do |t|
148
- # puts "Track identifier: #{t.identifier}"
149
- # puts "Track title: #{t.title}"
150
- # puts "Track creator: #{t.creator}"
151
- # puts "Track duration: #{t.duration}"
152
- # puts "Track metainformation: link=#{t.meta_rel} content=#{t.meta_content}"
153
- # end
154
- #
155
- # # Convert the XSPF document to SMIL
156
- # x.to_smil
157
- #
158
- # # Convert the XSPF document to HTML
159
- # x.to_html
160
- #
161
- # # Convert the XSPF document in a M3U playlist
162
- # x.to_m3u
163
- #
164
-
165
81
  class XSPF
166
82
 
167
83
  attr_reader :xspf
168
84
 
169
85
  #:stopdoc:
170
86
  ATTRIBUTES = %w{ version encoding }
171
- VERSION_RDOC = "Version for the XML document or _nil_ if not defined"
172
- ENCODING_RDOC = "Encoding of the XML document or _nil_ if not defined"
87
+ VERSION_RDOC = 'Version for the XML document or _nil_ if not defined'
88
+ ENCODING_RDOC = 'Encoding of the XML document or _nil_ if not defined'
173
89
 
174
90
  OUTPUT_FORMATS = %w{ m3u html smil soundblox }
175
- M3U_RDOC = "Creates a .m3u playlist from the XSPF document. This method makes use of the official XSPF to M3U XSLT transformation by Lucas Gonze."
176
- HTML_RDOC = "Outputs the playlist as an HTML page. This method makes use of the official XSPF to HTML XSLT transformation by Lucas Gonze."
177
- SMIL_RDOC = "Creates a .smil playlist from the XSPF document. This method makes use of the official XSPF to SMIL XSLT transformation by Lucas Gonze."
178
- SOUNDBLOX_RDOC = "Creates a SoundBlox playlist from the XSPF document. This method makes use of the official XSPF to SoundBlox XSLT tranformation by Lucas Gonze."
91
+ M3U_RDOC = 'Creates a .m3u playlist from the XSPF document. This method makes use of the official XSPF to M3U XSLT transformation by Lucas Gonze.'
92
+ HTML_RDOC = 'Outputs the playlist as an HTML page. This method makes use of the official XSPF to HTML XSLT transformation by Lucas Gonze.'
93
+ SMIL_RDOC = 'Creates a .smil playlist from the XSPF document. This method makes use of the official XSPF to SMIL XSLT transformation by Lucas Gonze.'
94
+ SOUNDBLOX_RDOC = 'Creates a SoundBlox playlist from the XSPF document. This method makes use of the official XSPF to SoundBlox XSLT tranformation by Lucas Gonze.'
179
95
 
180
96
  ATTRIBUTES.each do |attrib|
181
97
  MetaGen.add_method(self, attrib, "@xspf.#{attrib}", eval(attrib.upcase + '_RDOC').to_s )
182
98
  end
183
99
 
184
100
  OUTPUT_FORMATS.each do |format|
185
- MetaGen.add_output_format(self, format, @xspf, eval(format.upcase + '_RDOC').to_s )
101
+ MetaGen.add_output_format(self, format, eval(format.upcase + '_RDOC').to_s )
186
102
  end
187
103
 
188
104
  #:startdoc:
189
105
 
190
- # Creates a XSPF object from a file or string
106
+ # Creates a XSPF object from a file or string (parse mode) or from a hash or nil (generation mode).
107
+ #
108
+ # Possible keys in the hash: :version, :encoding
191
109
  def initialize(source = nil)
192
- @xspf = REXML::Document.new(source)
110
+ if ( source.nil? || source.instance_of?(Hash) ) then
111
+ @version = if source.nil? || !source.has_key?(:version)
112
+ '1.0'
113
+ else
114
+ source[:version]
115
+ end
116
+ @encoding = if source.nil? || !source.has_key?(:encoding)
117
+ 'UTF-8'
118
+ else
119
+ source[:encoding]
120
+ end
121
+ @playlist = nil
122
+ @playlist = if !source.nil? && source.has_key?(:playlist) then
123
+ if source[:playlist].instance_of?(XSPF::Playlist)
124
+ source[:playlist]
125
+ else
126
+ raise(TypeError, 'You must pass a file/string (parsing mode) or a hash/nothing (generator mode) as argument to XSPF#new')
127
+ end
128
+ end
129
+
130
+ elsif ( source.instance_of?(File) || source.instance_of?(String) ) then
131
+ @xspf = REXML::Document.new(source)
132
+ ATTRIBUTES.each do |attrib|
133
+ eval('@' + attrib + '= parse_' + attrib)
134
+ end
135
+
136
+ @playlist = XSPF::Playlist.new(self)
137
+
138
+ else
139
+ raise(TypeError, 'You must pass a file/string (parsing mode) or a hash/nothing (generator mode) as argument to XSPF#new')
140
+ end
193
141
  end
194
142
 
195
- # The <playlist> section of the XSPF document
143
+ # A XSPF::Playlist object
196
144
  def playlist
145
+ @playlist
146
+ end
147
+
148
+ def playlist=(value)
149
+ raise(TypeError, 'The playlist must be an instance of XSPF::Playlist') unless value.instance_of?(XSPF::Playlist)
150
+ @playlist = value
151
+ end
152
+
153
+ # Exports the XSPF object to XML
154
+ def to_xml
155
+ xml = REXML::Document.new
156
+ xml << REXML::XMLDecl.new(@version, @encoding)
157
+ xml << REXML::Document.new(@playlist.to_xml) unless @playlist.nil?
158
+ xml.to_s
159
+ end
160
+
161
+ # The <playlist> section of the XSPF document (outputs XML code). This method is only used while parsing.
162
+ protected
163
+ def playlist_xml
197
164
  @xspf.root
198
165
  end
199
166
 
@@ -207,25 +174,28 @@ class XSPF::Playlist < XSPF
207
174
  ATTRIBUTES = %w{ xmlns version }
208
175
  ELEMENTS = %w{ title creator annotation info location identifier image date license attribution extension }
209
176
  ATTRIBUTE_AND_ELEMENT = %w{ link meta }
210
- attribution_subelements = %w{ location identifier }
211
- extension_subelements = %w{ application content }
212
- XMLNS_RDOC = "The XML namespace. It must be http://xspf.org/ns/0/ for a valid XSPF document."
213
- VERSION_RDOC = "The XSPF version. It may be 0 or 1, although 1 is strongly advised."
214
- TITLE_RDOC = "A human-readable title for the playlist. xspf:playlist elements MAY contain exactly one."
215
- CREATOR_RDOC = "Human-readable name of the entity (author, authors, group, company, etc) that authored the playlist. XSPF::Playlist objects MAY contain exactly one."
216
- ANNOTATION_RDOC = "A human-readable comment on the playlist. This is character data, not HTML, and it may not contain markup. XSPF::Playlist objects elements MAY contain exactly one."
217
- INFO_RDOC = "URL of a web page to find out more about this playlist. Likely to be homepage of the author, and would be used to find out more about the author and to find more playlists by the author. XSPF::Playlist objects MAY contain exactly one."
218
- LOCATION_RDOC = "Source URL for this playlist. XSPF::Playlist objects MAY contain exactly one."
219
- IDENTIFIER_RDOC = "Canonical ID for this playlist. Likely to be a hash or other location-independent name. MUST be a legal URN. XSPF::Playlist objects MAY contain exactly one."
220
- IMAGE_RDOC = "URL of an image to display if XSPF::Playlist#image return nil. XSPF::Playlist objects MAY contain exactly one."
221
- DATE_RDOC = "Creation date (not last-modified date) of the playlist, formatted as a XML schema dateTime. XSPF::Playlist objects MAY contain exactly one."
222
- LICENSE_RDOC = "URL of a resource that describes the license under which this playlist was released. XSPF::Playlist objects MAY contain zero or one license element."
223
- ATTRIBUTION_RDOC = "An ordered list of URIs. The purpose is to satisfy licenses allowing modification but requiring attribution. If you modify such a playlist, move its XSPF::Playlist#location or XSPF::Playlist#identifier element to the top of the items in the XSPF::Playlist#attribution element. XSPF::Playlist objects MAY contain exactly one attribution element. Please note that currently XSPF for Ruby does not parse the contents of XSPF::Playlist#attribution."
224
- EXTENSION_RDOC = "The extension element allows non-XSPF XML to be included in XSPF documents without breaking XSPF validation. The purpose is to allow nested XML, which the meta and link elements do not. XSPF::Playlist objects MAY contain zero or more extension elements but currently XSPF for Ruby returns only the first one."
225
- LINK_REL_RDOC = "The link element allows non-XSPF web resources to be included in XSPF documents without breaking XSPF validation. A valid 'link' element has a 'rel' attribute and a 'content' element, obtained with XSPF::Playlist#link_rel and XSPF::Playlist#link_content respectively. XSPF::Playlist objects MAY contain zero or more link elements, but currently XSPF for Ruby returns only the first one."
226
- LINK_CONTENT_RDOC = "The link element allows non-XSPF web resources to be included in XSPF documents without breaking XSPF validation. A valid 'link' element has a 'rel' attribute and a 'content' element, obtained with XSPF::Playlist#link_rel and XSPF::Playlist#link_content respectively. XSPF::Playlist objects MAY contain zero or more meta elements, but currently XSPF for Ruby returns only the first one."
227
- META_REL_RDOC = "The meta element allows non-XSPF metadata to be included in XSPF documents without breaking XSPF validation. A valid 'meta' element has a 'rel' attribute and a 'content' element, obtained with XSPF::Playlist#meta_rel and XSPF::Playlist#meta_content respectively. XSPF::Playlist objects MAY contain zero or more meta elements, but currently XSPF for Ruby returns only the first one."
228
- META_CONTENT_RDOC = "The meta element allows non-XSPF metadata to be included in XSPF documents without breaking XSPF validation. A valid 'meta' element has a 'rel' attribute and a 'content' element, obtained with XSPF::Playlist#meta_rel and XSPF::Playlist#meta_content respectively. XSPF::Playlist objects MAY contain zero or more meta elements, but currently XSPF for Ruby returns only the first one."
177
+ ATTRIBUTION_CHILD_ELEMENTS = %w{ location identifier }
178
+ EXTENSION_CHILD_ELEMENTS = %w{ application content }
179
+
180
+ XMLNS_RDOC = 'The XML namespace. It must be http://xspf.org/ns/0/ for a valid XSPF document.'
181
+ XMLNS_DEFAULT = 'http://xspf.org/ns/0/'
182
+ VERSION_RDOC = 'The XSPF version. It may be 0 or 1, although 1 is strongly advised.'
183
+ VERSION_DEFAULT = '1'
184
+ TITLE_RDOC = 'A human-readable title for the playlist. xspf:playlist elements MAY contain exactly one.'
185
+ CREATOR_RDOC = 'Human-readable name of the entity (author, authors, group, company, etc) that authored the playlist. XSPF::Playlist objects MAY contain exactly one.'
186
+ ANNOTATION_RDOC = 'A human-readable comment on the playlist. This is character data, not HTML, and it may not contain markup. XSPF::Playlist objects elements MAY contain exactly one.'
187
+ INFO_RDOC = 'URL of a web page to find out more about this playlist. Likely to be homepage of the author, and would be used to find out more about the author and to find more playlists by the author. XSPF::Playlist objects MAY contain exactly one.'
188
+ LOCATION_RDOC = 'Source URL for this playlist. XSPF::Playlist objects MAY contain exactly one.'
189
+ IDENTIFIER_RDOC = 'Canonical ID for this playlist. Likely to be a hash or other location-independent name. MUST be a legal URN. XSPF::Playlist objects MAY contain exactly one.'
190
+ IMAGE_RDOC = 'URL of an image to display if XSPF::Playlist#image return nil. XSPF::Playlist objects MAY contain exactly one.'
191
+ DATE_RDOC = 'Creation date (not last-modified date) of the playlist, formatted as a XML schema dateTime. XSPF::Playlist objects MAY contain exactly one.'
192
+ LICENSE_RDOC = 'URL of a resource that describes the license under which this playlist was released. XSPF::Playlist objects MAY contain zero or one license element.'
193
+ ATTRIBUTION_RDOC = 'An ordered list of URIs. The purpose is to satisfy licenses allowing modification but requiring attribution. If you modify such a playlist, move its XSPF::Playlist#location or XSPF::Playlist#identifier element to the top of the items in the XSPF::Playlist#attribution element. XSPF::Playlist objects MAY contain exactly one attribution element. Please note that currently XSPF for Ruby does not parse the contents of XSPF::Playlist#attribution.'
194
+ EXTENSION_RDOC = 'The extension element allows non-XSPF XML to be included in XSPF documents without breaking XSPF validation. The purpose is to allow nested XML, which the meta and link elements do not. XSPF::Playlist objects MAY contain zero or more extension elements but currently XSPF for Ruby returns only the first one.'
195
+ LINK_REL_RDOC = 'The link element allows non-XSPF web resources to be included in XSPF documents without breaking XSPF validation. A valid _link_ element has a _rel_ attribute and a _content_ element, obtained with XSPF::Playlist#link_rel and XSPF::Playlist#link_content respectively. XSPF::Playlist objects MAY contain zero or more link elements, but currently XSPF for Ruby returns only the first one.'
196
+ LINK_CONTENT_RDOC = 'The link element allows non-XSPF web resources to be included in XSPF documents without breaking XSPF validation. A valid _link_ element has a _rel_ attribute and a _content_ element, obtained with XSPF::Playlist#link_rel and XSPF::Playlist#link_content respectively. XSPF::Playlist objects MAY contain zero or more meta elements, but currently XSPF for Ruby returns only the first one.'
197
+ META_REL_RDOC = 'The meta element allows non-XSPF metadata to be included in XSPF documents without breaking XSPF validation. A valid _meta_ element has a _rel_ attribute and a _content_ element, obtained with XSPF::Playlist#meta_rel and XSPF::Playlist#meta_content respectively. XSPF::Playlist objects MAY contain zero or more meta elements, but currently XSPF for Ruby returns only the first one.'
198
+ META_CONTENT_RDOC = 'The meta element allows non-XSPF metadata to be included in XSPF documents without breaking XSPF validation. A valid _meta_ element has a _rel_ attribute and a _content_ element, obtained with XSPF::Playlist#meta_rel and XSPF::Playlist#meta_content respectively. XSPF::Playlist objects MAY contain zero or more meta elements, but currently XSPF for Ruby returns only the first one.'
229
199
 
230
200
  # FIXME Currently we only return the first "link"
231
201
  # FIXME Currently we only return the first "meta"
@@ -249,14 +219,121 @@ class XSPF::Playlist < XSPF
249
219
 
250
220
  #:startdoc:
251
221
 
252
- # Creates a XSPF::Playlist from a XSPF document
253
- def initialize(source)
254
- @playlist = source.playlist
222
+ # Creates a XSPF::Playlist from a XSPF document (parse mode) or from a hash of values (generation mode)
223
+ #
224
+ # Possible keys in the hash: :xmlns, :version, :title, :creator, :annotation, :info, :location, :identifier, :image, :date, :license, :attribution, :extension, :link_rel, :link_content, :meta_rel, :meta_content
225
+ def initialize(source = nil)
226
+
227
+ if ( source.instance_of?(Hash) || source.nil? ) then
228
+
229
+ ATTRIBUTES.each do |attrib|
230
+ add_instance_variable(source, attrib)
231
+ end
232
+
233
+ ELEMENTS.each do |element|
234
+ add_instance_variable(source, element)
235
+ end
236
+
237
+ ATTRIBUTE_AND_ELEMENT.each do |ae|
238
+ add_instance_variable(source, "#{ae}_content" )
239
+ add_instance_variable(source, "#{ae}_rel" )
240
+ end
241
+
242
+ @tracklist = if ( !source.nil? && source.has_key?(:tracklist) && source[:tracklist].instance_of?(XSPF::Tracklist) )
243
+ source[:tracklist]
244
+ else
245
+ nil
246
+ end
247
+
248
+ elsif source.instance_of?(XSPF) then
249
+
250
+ @playlist = source.playlist_xml
251
+
252
+ ATTRIBUTES.each do |attrib|
253
+ eval('@' + attrib.downcase + '= parse_' + attrib.downcase)
254
+ end
255
+
256
+ ELEMENTS.each do |element|
257
+ eval('@' + element.downcase + '= parse_' + element.downcase)
258
+ end
259
+
260
+ ATTRIBUTE_AND_ELEMENT.each do |ae|
261
+ eval('@' + ae.downcase + '_content = parse_' + ae.downcase + '_content')
262
+ eval('@' + ae.downcase + '_rel = parse_' + ae.downcase + '_rel')
263
+ end
264
+
265
+ @tracklist = XSPF::Tracklist.new(self)
266
+
267
+ else
268
+ raise(TypeError, 'You must pass a XSPF object (parsing mode) or a hash (generator mode) as argument to XSPF::Playlist#new')
269
+ end
270
+
255
271
  end
256
272
 
257
- # The <trackList> section of the XSPF document
273
+ # A XSPF::Tracklist object
258
274
  def tracklist
259
- @playlist.elements['trackList'] || nil
275
+ @tracklist
276
+ end
277
+
278
+ def tracklist=(value)
279
+ raise(TypeError, 'The tracklist must be an instance of XSPF::Tracklist') unless value.instance_of?(XSPF::Tracklist)
280
+ @tracklist = value
281
+ end
282
+
283
+ alias :<< :tracklist=
284
+
285
+ # Exports the XSPF::Playlist to XML (only the <playlist> section)
286
+ def to_xml
287
+
288
+ xml = REXML::Element.new('playlist')
289
+
290
+ ATTRIBUTES.each do |attrib|
291
+ # TODO Sure there is a nicer way to do evaluate this condition...
292
+ unless eval('@' + attrib.downcase + '.nil?')
293
+ xml.attributes[attrib] = eval('@' + attrib.downcase)
294
+ end
295
+ end
296
+
297
+ ELEMENTS.each do |element|
298
+ # TODO Sure there is a nicer way to do evaluate this condition...
299
+ unless eval('@' + element.downcase + '.nil?')
300
+ el = REXML::Element.new(element)
301
+ el.add_text( eval('@' + element.downcase) )
302
+ xml.add_element(el)
303
+ end
304
+ end
305
+
306
+ ATTRIBUTE_AND_ELEMENT.each do |ae|
307
+ # TODO Sure there is a nicer way to do evaluate this condition...
308
+ unless eval('@' + ae.downcase + '_rel.nil? && @'+ ae.downcase + '_content.nil?')
309
+ el = REXML::Element.new(ae.downcase)
310
+ el.add_attribute('rel', eval('@' + ae.downcase + '_rel') )
311
+ el.add_text( eval('@' + ae.downcase + '_content') )
312
+ xml.add_element(el)
313
+ end
314
+ end
315
+
316
+ xml << REXML::Document.new(@tracklist.to_xml)
317
+
318
+ xml.to_s
319
+
320
+ end
321
+
322
+ # The <trackList> section of the XSPF document (outputs XML code). This method is only used while parsing.
323
+ protected
324
+ def tracklist_xml
325
+ @playlist.elements['trackList']
326
+ end
327
+
328
+ private
329
+ def add_instance_variable(hash, var)
330
+
331
+ if !hash.nil? && hash.has_key?(var.downcase.to_sym)
332
+ eval('@' + var.downcase + ' = \'' + hash[var.downcase.to_sym] + '\'')
333
+ else
334
+ eval('@' + var.downcase + ' = defined?(' + var.upcase + '_DEFAULT) ? ' + var.upcase + '_DEFAULT : nil')
335
+ end
336
+
260
337
  end
261
338
 
262
339
  end
@@ -265,14 +342,32 @@ class XSPF::Tracklist < XSPF::Playlist
265
342
 
266
343
  attr_reader :tracklist
267
344
 
268
- # Creates a XSPF::Tracklist from a XSPF::Playlist
269
- def initialize(playlist)
270
- @tracklist = playlist.tracklist
345
+ # Creates a XSPF::Tracklist from a XSPF::Playlist (parse mode) or without parameters (generation mode)
346
+ def initialize(playlist=nil)
347
+ if (playlist.instance_of?(Hash) || playlist.nil?) then
348
+ @tracklist = ''
349
+ @tracks = []
350
+ else
351
+ @tracklist = playlist.tracklist_xml
352
+ @tracks = @tracklist.elements.collect { |track| XSPF::Track.new(track) }
353
+ end
271
354
  end
272
355
 
273
- # An array XSPF::Track objects, one for each track returned by XSPF::Playlist#tracklist
356
+ # Returns an array XSPF::Track objects
274
357
  def tracks
275
- @tracklist.elements.collect { |track| XSPF::Track.new(track) }
358
+ @tracks
359
+ end
360
+
361
+ # Adds a new XSPF::Track to the XSPF::Tracklist
362
+ def <<(track)
363
+ @tracks << track
364
+ end
365
+
366
+ # Exports the XSPF::Tracklist to XML (only the <trackList> section)
367
+ def to_xml
368
+ xml = REXML::Element.new('trackList')
369
+ @tracks.each { |t| xml << REXML::Document.new(t.to_xml) }
370
+ xml.to_s
276
371
  end
277
372
 
278
373
  end
@@ -284,21 +379,22 @@ class XSPF::Track
284
379
  #:stopdoc:
285
380
  ELEMENTS = %w{ location identifier title creator annotation info image album trackNum duration extension }
286
381
  ATTRIBUTE_AND_ELEMENT = %w{ link meta }
287
- LOCATION_RDOC = "URL of resource to be rendered. Probably an audio resource, but MAY be any type of resource with a well-known duration, such as video, a SMIL document, or an XSPF document. The duration of the resource defined in this element defines the duration of rendering. XSPF::Track objects MAY contain zero or more location elements, but a user-agent MUST NOT render more than one of the named resources. Currently, XSPF for Ruby returns only the first location."
288
- IDENTIFIER_RDOC = "Canonical ID for this resource. Likely to be a hash or other location-independent name, such as a MusicBrainz identifier or isbn URN (if there existed isbn numbers for audio). MUST be a legal URN. XSPF::Track objects elements MAY contain zero or more identifier elements, but currently XSPF for Ruby returns only the first one."
289
- TITLE_RDOC = "Human-readable name of the track that authored the resource which defines the duration of track rendering. This value is primarily for fuzzy lookups, though a user-agent may display it. XSPF::Track objects MAY contain exactly one."
290
- CREATOR_RDOC = "Human-readable name of the entity (author, authors, group, company, etc) that authored the resource which defines the duration of track rendering. This value is primarily for fuzzy lookups, though a user-agent may display it. XSPF::Track objects MAY contain exactly one."
291
- ANNOTATION_RDOC = "A human-readable comment on the track. This is character data, not HTML, and it may not contain markup. XSPF::Track objects MAY contain exactly one."
292
- INFO_RDOC = "URL of a place where this resource can be bought or more info can be found."
293
- IMAGE_RDOC = "URL of an image to display for the duration of the track. XSPF::Track objects MAY contain exactly one."
294
- ALBUM_RDOC = "Human-readable name of the collection from which the resource which defines the duration of track rendering comes. For a song originally published as a part of a CD or LP, this would be the title of the original release. This value is primarily for fuzzy lookups, though a user-agent may display it. XSPF::Track objects MAY contain exactly one."
295
- TRACKNUM_RDOC = "Integer with value greater than zero giving the ordinal position of the media on the XSPF::Track#album. This value is primarily for fuzzy lookups, though a user-agent may display it. XSPF::Track objects MAY contain exactly one. It MUST be a valid XML Schema nonNegativeInteger."
296
- DURATION_RDOC = "The time to render a resource, in milliseconds. It MUST be a valid XML Schema nonNegativeInteger. This value is only a hint -- different XSPF generators will generate slightly different values. A user-agent MUST NOT use this value to determine the rendering duration, since the data will likely be low quality. XSPF::Track objects MAY contain exactly one duration element."
297
- EXTENSION_RDOC = "The extension element allows non-XSPF XML to be included in XSPF documents without breaking XSPF validation. The purpose is to allow nested XML, which the meta and link elements do not. XSPF::Track objects MAY contain zero or more extension elements, but currently XSPF for Ruby returns only the first one."
298
- LINK_REL_RDOC = "The link element allows non-XSPF web resources to be included in XSPF documents without breaking XSPF validation. A valid 'link' element has a 'rel' attribute and a 'content' element, obtained with XSPF::Track#link_rel and XSPF::Track#link_content respectively. XSPF::Track objects MAY contain zero or more link elements, but currently XSPF for Ruby returns only the first one."
299
- LINK_CONTENT_RDOC = "The link element allows non-XSPF web resources to be included in XSPF documents without breaking XSPF validation. A valid 'link' element has a 'rel' attribute and a 'content' element, obtained with XSPF::Track#link_rel and XSPF::Track#link_content respectively. XSPF::Track objects MAY contain zero or more meta elements, but currently XSPF for Ruby returns only the first one."
300
- META_REL_RDOC = "The meta element allows non-XSPF metadata to be included in XSPF documents without breaking XSPF validation. A valid 'meta' element has a 'rel' attribute and a 'content' element, obtained with XSPF::Track#meta_rel and XSPF::Track#meta_content respectively. XSPF::Track objects MAY contain zero or more meta elements, but currently XSPF for Ruby returns only the first one."
301
- META_CONTENT_RDOC = "The meta element allows non-XSPF metadata to be included in XSPF documents without breaking XSPF validation. A valid 'meta' element has a 'rel' attribute and a 'content' element, obtained with XSPF::Track#meta_rel and XSPF::Track#meta_content respectively. XSPF::Track objects MAY contain zero or more meta elements, but currently XSPF for Ruby returns only the first one."
382
+
383
+ LOCATION_RDOC = 'URL of resource to be rendered. Probably an audio resource, but MAY be any type of resource with a well-known duration, such as video, a SMIL document, or an XSPF document. The duration of the resource defined in this element defines the duration of rendering. XSPF::Track objects MAY contain zero or more location elements, but a user-agent MUST NOT render more than one of the named resources. Currently, XSPF for Ruby returns only the first location.'
384
+ IDENTIFIER_RDOC = 'Canonical ID for this resource. Likely to be a hash or other location-independent name, such as a MusicBrainz identifier or isbn URN (if there existed isbn numbers for audio). MUST be a legal URN. XSPF::Track objects elements MAY contain zero or more identifier elements, but currently XSPF for Ruby returns only the first one.'
385
+ TITLE_RDOC = 'Human-readable name of the track that authored the resource which defines the duration of track rendering. This value is primarily for fuzzy lookups, though a user-agent may display it. XSPF::Track objects MAY contain exactly one.'
386
+ CREATOR_RDOC = 'Human-readable name of the entity (author, authors, group, company, etc) that authored the resource which defines the duration of track rendering. This value is primarily for fuzzy lookups, though a user-agent may display it. XSPF::Track objects MAY contain exactly one.'
387
+ ANNOTATION_RDOC = 'A human-readable comment on the track. This is character data, not HTML, and it may not contain markup. XSPF::Track objects MAY contain exactly one.'
388
+ INFO_RDOC = 'URL of a place where this resource can be bought or more info can be found.'
389
+ IMAGE_RDOC = 'URL of an image to display for the duration of the track. XSPF::Track objects MAY contain exactly one.'
390
+ ALBUM_RDOC = 'Human-readable name of the collection from which the resource which defines the duration of track rendering comes. For a song originally published as a part of a CD or LP, this would be the title of the original release. This value is primarily for fuzzy lookups, though a user-agent may display it. XSPF::Track objects MAY contain exactly one.'
391
+ TRACKNUM_RDOC = 'Integer with value greater than zero giving the ordinal position of the media on the XSPF::Track#album. This value is primarily for fuzzy lookups, though a user-agent may display it. XSPF::Track objects MAY contain exactly one. It MUST be a valid XML Schema nonNegativeInteger.'
392
+ DURATION_RDOC = 'The time to render a resource, in milliseconds. It MUST be a valid XML Schema nonNegativeInteger. This value is only a hint -- different XSPF generators will generate slightly different values. A user-agent MUST NOT use this value to determine the rendering duration, since the data will likely be low quality. XSPF::Track objects MAY contain exactly one duration element.'
393
+ EXTENSION_RDOC = 'The extension element allows non-XSPF XML to be included in XSPF documents without breaking XSPF validation. The purpose is to allow nested XML, which the meta and link elements do not. XSPF::Track objects MAY contain zero or more extension elements, but currently XSPF for Ruby returns only the first one.'
394
+ LINK_REL_RDOC = 'The link element allows non-XSPF web resources to be included in XSPF documents without breaking XSPF validation. A valid _link_ element has a _rel_ attribute and a _content_ element, obtained with XSPF::Track#link_rel and XSPF::Track#link_content respectively. XSPF::Track objects MAY contain zero or more link elements, but currently XSPF for Ruby returns only the first one.'
395
+ LINK_CONTENT_RDOC = 'The link element allows non-XSPF web resources to be included in XSPF documents without breaking XSPF validation. A valid _link_ element has a _rel_ attribute and a _content_ element, obtained with XSPF::Track#link_rel and XSPF::Track#link_content respectively. XSPF::Track objects MAY contain zero or more meta elements, but currently XSPF for Ruby returns only the first one.'
396
+ META_REL_RDOC = 'The meta element allows non-XSPF metadata to be included in XSPF documents without breaking XSPF validation. A valid _meta_ element has a _rel_ attribute and a _content_ element, obtained with XSPF::Track#meta_rel and XSPF::Track#meta_content respectively. XSPF::Track objects MAY contain zero or more meta elements, but currently XSPF for Ruby returns only the first one.'
397
+ META_CONTENT_RDOC = 'The meta element allows non-XSPF metadata to be included in XSPF documents without breaking XSPF validation. A valid _meta_ element has a _rel_ attribute and a _content_ element, obtained with XSPF::Track#meta_rel and XSPF::Track#meta_content respectively. XSPF::Track objects MAY contain zero or more meta elements, but currently XSPF for Ruby returns only the first one.'
302
398
 
303
399
  ELEMENTS.each do |element|
304
400
  MetaGen.add_method( self, element, "@track.elements['#{element}'].text", eval(element.upcase + '_RDOC').to_s )
@@ -311,9 +407,74 @@ class XSPF::Track
311
407
 
312
408
  # :startdoc:
313
409
 
314
- # Creates a XSPF::Track object from a <track> section of the XSPF document
410
+ # Creates a XSPF::Track object from a <track> section of the XSPF document or from a hash of values
411
+ #
412
+ # Possible keys in the hash in generation mode: :location, :identifier, :title, :creator, :annotation, :info, :image, :album, :tracknum, :duration, :extension, :link_rel, :link_content, :meta_rel, :meta_content)
315
413
  def initialize(tr)
316
- @track = tr
414
+
415
+ if tr.instance_of?(Hash)
416
+
417
+ ELEMENTS.each do |element|
418
+ add_instance_variable(tr, element.downcase)
419
+ end
420
+
421
+ ATTRIBUTE_AND_ELEMENT.each do |ae|
422
+ add_instance_variable(tr, "#{ae.downcase}_content" )
423
+ add_instance_variable(tr, "#{ae.downcase}_rel" )
424
+ end
425
+
426
+ else
427
+ @track = tr
428
+
429
+ ELEMENTS.each do |element|
430
+ eval('@' + element.downcase + '= parse_' + element.downcase)
431
+ end
432
+
433
+ ATTRIBUTE_AND_ELEMENT.each do |ae|
434
+ eval('@' + ae.downcase + '_content = parse_' + ae.downcase + '_content')
435
+ eval('@' + ae.downcase + '_rel = parse_' + ae.downcase + '_rel')
436
+ end
437
+ end
438
+
439
+ end
440
+
441
+ # Exports the XSPF::Track to XML (only the <track> section)
442
+ def to_xml
443
+
444
+ xml = REXML::Element.new('track')
445
+
446
+ ELEMENTS.each do |element|
447
+ # TODO Sure there is a nicer way to do evaluate this condition...
448
+ unless eval('@' + element.downcase + '.nil?')
449
+ el = REXML::Element.new(element)
450
+ el.add_text( eval('@' + element.downcase) )
451
+ xml.add_element(el)
452
+ end
453
+ end
454
+
455
+ ATTRIBUTE_AND_ELEMENT.each do |ae|
456
+ # TODO Sure there is a nicer way to do evaluate this condition...
457
+ unless eval('@' + ae.downcase + '_rel.nil? && @'+ ae.downcase + '_content.nil?')
458
+ el = REXML::Element.new(ae.downcase)
459
+ el.add_attribute('rel', eval('@' + ae.downcase + '_rel') )
460
+ el.add_text( eval('@' + ae.downcase + '_content') )
461
+ xml.add_element(el)
462
+ end
463
+ end
464
+
465
+ xml.to_s
466
+
467
+ end
468
+
469
+ private
470
+ def add_instance_variable(hash, var)
471
+
472
+ if hash.has_key?(var.downcase.to_sym)
473
+ eval('@' + var.downcase + ' = \'' + hash[var.downcase.to_sym] + '\'')
474
+ else
475
+ eval('@' + var.downcase + ' = defined?(' + var.upcase + '_DEFAULT) ? ' + var.upcase + '_DEFAULT : nil')
476
+ end
477
+
317
478
  end
318
479
 
319
480
  end