xspf 0.3 → 0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/AUTHORS +1 -1
- data/ChangeLog +15 -0
- data/README +6 -9
- data/USAGE +350 -0
- data/coverage/index.html +17 -17
- data/coverage/lib-xspf_rb.html +466 -305
- data/doc/created.rid +1 -1
- data/doc/fr_file_index.html +4 -0
- data/doc/fr_method_index.html +88 -44
- data/doc/index.html +1 -1
- data/examples/parse_and_generate_right.rb +25 -0
- data/examples/parse_and_generate_right_too.rb +27 -0
- data/examples/parse_and_generate_wrong.rb +25 -0
- data/examples/{example.rb → parse_from_file.rb} +15 -16
- data/examples/parse_from_string.rb +70 -0
- data/examples/playlist-tiersen.xspf +100 -0
- data/lib/xspf.rb +335 -174
- data/test/tc_dogfood.rb +106 -0
- data/test/tc_generate_output_formats.rb +352 -0
- data/test/tc_generate_playlist.rb +196 -0
- data/test/tc_generate_track.rb +38 -0
- data/test/tc_generate_tracklist.rb +72 -0
- data/test/tc_generate_xspf.rb +90 -0
- data/test/{tc_output_formats.rb → tc_parse_output_formats.rb} +124 -9
- data/test/{tc_playlist.rb → tc_parse_playlist.rb} +9 -1
- data/test/{tc_track.rb → tc_parse_track.rb} +1 -1
- data/test/{tc_tracklist.rb → tc_parse_tracklist.rb} +1 -1
- data/test/{tc_xspf.rb → tc_parse_xspf.rb} +4 -3
- data/test/ts_xspf.rb +12 -5
- data/xspf-expanded.rb +644 -76
- metadata +30 -16
data/lib/xspf.rb
CHANGED
@@ -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://
|
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
|
-
# :
|
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 =
|
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
|
-
|
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 =
|
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 & 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 =
|
172
|
-
ENCODING_RDOC =
|
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 =
|
176
|
-
HTML_RDOC =
|
177
|
-
SMIL_RDOC =
|
178
|
-
SOUNDBLOX_RDOC =
|
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,
|
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
|
-
|
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
|
-
#
|
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
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
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
|
-
|
254
|
-
|
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
|
-
#
|
273
|
+
# A XSPF::Tracklist object
|
258
274
|
def tracklist
|
259
|
-
@
|
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
|
-
|
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
|
-
#
|
356
|
+
# Returns an array XSPF::Track objects
|
274
357
|
def tracks
|
275
|
-
@
|
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
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
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
|
-
|
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
|