blipfm 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.txt +47 -0
- data/lib/blipfm.rb +304 -0
- metadata +64 -0
data/README.txt
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
= Blip.fm API for Ruby
|
2
|
+
|
3
|
+
Published by Chris Koehler under the Ruby License.
|
4
|
+
For information see http://rubyforge.org/projects/blipfm/
|
5
|
+
|
6
|
+
== DESCRIPTION:
|
7
|
+
|
8
|
+
A ruby library for the Blip.fm API. Developed to facilitate the development of Blip.fm (social media service centered around music) applications in ruby.
|
9
|
+
|
10
|
+
== FEATURES:
|
11
|
+
|
12
|
+
- Ruby implementation of the Blip.fm API: http://api.blip.fm/
|
13
|
+
|
14
|
+
== GETTING STARTED:
|
15
|
+
|
16
|
+
* Add your Blip.fm developer key and secret key to config.yml
|
17
|
+
|
18
|
+
== KNOWN ISSUES:
|
19
|
+
|
20
|
+
- Missing save preferences method
|
21
|
+
|
22
|
+
== INSTALL:
|
23
|
+
|
24
|
+
sudo gem install blipfm
|
25
|
+
|
26
|
+
== LICENSE:
|
27
|
+
|
28
|
+
Copyright (c) 2009 Chris Koehler
|
29
|
+
|
30
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
31
|
+
a copy of this software and associated documentation files (the
|
32
|
+
'Software'), to deal in the Software without restriction, including
|
33
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
34
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
35
|
+
permit persons to whom the Software is furnished to do so, subject to
|
36
|
+
the following conditions:
|
37
|
+
|
38
|
+
The above copyright notice and this permission notice shall be
|
39
|
+
included in all copies or substantial portions of the Software.
|
40
|
+
|
41
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
42
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
43
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
44
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
45
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
46
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
47
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/lib/blipfm.rb
ADDED
@@ -0,0 +1,304 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'uuid'
|
3
|
+
require 'hmac-sha1'
|
4
|
+
require 'base64'
|
5
|
+
require 'cgi'
|
6
|
+
require 'net/http'
|
7
|
+
require 'uri'
|
8
|
+
require 'rexml/document'
|
9
|
+
|
10
|
+
def if_nill(input_string, nill_string = '')
|
11
|
+
if (input_string.nil?)
|
12
|
+
return nill_string
|
13
|
+
else
|
14
|
+
return input_string
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module BlipFm
|
19
|
+
|
20
|
+
CONFIG = YAML.load_file('config.yml')
|
21
|
+
|
22
|
+
def BlipFm.generate_blip_nonce()
|
23
|
+
return UUID.generate().gsub('-', '')
|
24
|
+
end
|
25
|
+
|
26
|
+
def BlipFm.blip_signature(blip_method, blip_timestamp, blip_nonce)
|
27
|
+
blip_data = blip_method + "\n" + blip_timestamp.to_s + "\n" + blip_nonce
|
28
|
+
CGI.escape(Base64.encode64(HMAC::SHA1.digest(CONFIG['blip_secret_key'], blip_data)).strip)
|
29
|
+
end
|
30
|
+
|
31
|
+
def BlipFm.execute_blip_method(blip_method, require_key, require_auth, parameter_hash = nil, require_user_auth = false, blip_user_name = '', blip_user_password = '', request_type = 'GET')
|
32
|
+
# INIT URL
|
33
|
+
blip_url = '/' + blip_method + '.xml?a=1'
|
34
|
+
if (require_key)
|
35
|
+
blip_url += '&apiKey=' + CONFIG['blip_api_key']
|
36
|
+
end
|
37
|
+
|
38
|
+
# SIGN REQUEST
|
39
|
+
if (require_auth)
|
40
|
+
blip_nonce = generate_blip_nonce()
|
41
|
+
blip_timestamp = get_blip_timestamp()
|
42
|
+
blip_url += '&nonce=' + blip_nonce
|
43
|
+
blip_url += '×tamp=' + blip_timestamp
|
44
|
+
blip_url += '&signature=' + blip_signature(request_type, blip_timestamp, blip_nonce)
|
45
|
+
end
|
46
|
+
|
47
|
+
# ADD PARAMETERS TO URL IF GET
|
48
|
+
if ((!parameter_hash.nil?) && (request_type == 'GET'))
|
49
|
+
parameter_hash.each do |blip_parameter, blip_value|
|
50
|
+
blip_url += '&' + blip_parameter + '=' + CGI.escape(blip_value.to_s)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# PROCESS REQUEST
|
55
|
+
Net::HTTP.start(CONFIG['blip_base_url']) {|http|
|
56
|
+
request = nil
|
57
|
+
if (request_type == 'GET')
|
58
|
+
request = Net::HTTP::Get.new(blip_url)
|
59
|
+
else
|
60
|
+
request = Net::HTTP::Post.new(blip_url)
|
61
|
+
request.set_form_data(parameter_hash)
|
62
|
+
end
|
63
|
+
if (require_user_auth)
|
64
|
+
request.basic_auth blip_user_name, blip_user_password
|
65
|
+
end
|
66
|
+
response = http.request(request)
|
67
|
+
response_xml = response.body
|
68
|
+
return response.body
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
def BlipFm.create_blip_array(blip_xml, object_type = 'Blip')
|
73
|
+
blip_array = []
|
74
|
+
blip_collection_xml = REXML::Document.new(blip_xml).elements['BlipApiResponse/result/collection']
|
75
|
+
blip_collection_xml.elements.each(object_type) do |current_blip_xml|
|
76
|
+
case (object_type)
|
77
|
+
when 'Song'
|
78
|
+
blip_array << Song.create_by_xml(current_blip_xml)
|
79
|
+
when 'user', 'User'
|
80
|
+
blip_array << User.create_by_xml(current_blip_xml)
|
81
|
+
else
|
82
|
+
blip_array << Blip.create_by_xml(current_blip_xml)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
return blip_array
|
86
|
+
end
|
87
|
+
|
88
|
+
def BlipFm.delete_blip(user_name, user_password, blip_id)
|
89
|
+
return execute_blip_method('blip/delete', true, true, {'id' => blip_id}, true, user_name, user_password, 'POST')
|
90
|
+
end
|
91
|
+
|
92
|
+
def BlipFm.get_blips(blip_id_array)
|
93
|
+
return create_blip_array(execute_blip_method('blip/getById', false, false, {'id' => blip_id_array.join(',')}))
|
94
|
+
end
|
95
|
+
|
96
|
+
def BlipFm.get_public_stream(last_blip_timestamp = 0, offset_blip_id = 0, blip_limit = 25)
|
97
|
+
return create_blip_array(execute_blip_method('blip/getPublic', true, true, {'ts' => last_blip_timestamp, 'offset' => offset_blip_id, 'limit' => blip_limit}))
|
98
|
+
end
|
99
|
+
|
100
|
+
def BlipFm.get_user_home(blip_user_name, blip_password, last_blip_timestamp = 0, offset_blip_id = 0, blip_limit = 25)
|
101
|
+
return create_blip_array(execute_blip_method('blip/getUserHome', true, true, {'ts' => last_blip_timestamp, 'offset' => offset_blip_id, 'limit' => blip_limit}, true, blip_user_name, blip_password))
|
102
|
+
end
|
103
|
+
|
104
|
+
def BlipFm.get_user_playlist(blip_user_name)
|
105
|
+
return create_blip_array(execute_blip_method('blip/getUserPlaylist', true, true, {'username' => blip_user_name}))
|
106
|
+
end
|
107
|
+
|
108
|
+
def BlipFm.get_user_profile(blip_user_name, offset_blip_id = 0, blip_limit = 25)
|
109
|
+
return create_blip_array(execute_blip_method('blip/getUserProfile', true, true, {'username' => blip_user_name, 'offset' => offset_blip_id, 'limit' => blip_limit}))
|
110
|
+
end
|
111
|
+
|
112
|
+
def BlipFm.get_user_replies(blip_user_name, offset_blip_id = 0, blip_limit = 150)
|
113
|
+
return create_blip_array(execute_blip_method('blip/getUserReplies', true, true, {'username' => blip_user_name, 'offset' => offset_blip_id, 'limit' => blip_limit}))
|
114
|
+
end
|
115
|
+
|
116
|
+
def BlipFm.post_blip(user_name, user_password, song_artist, song_title, song_url, blip_message, song_bitrate, song_genre, blip_type, reblip_id = 0, get_recommendations = 1)
|
117
|
+
return execute_blip_method('blip/post', true, true, {'artist' => song_artist, 'title' => song_title, 'url' => song_url, 'message' => blip_message, 'bitrate' => song_bitrate, 'genre' => song_genre, 'blipType' => blip_type, 'reblipId' => reblip_id, 'getUserRecs' => get_recommendations}, true, user_name, user_password, 'POST')
|
118
|
+
end
|
119
|
+
|
120
|
+
def BlipFm.add_dj(user_name, user_password, dj_user_name)
|
121
|
+
return execute_blip_method('favorite/addDJ', true, true, {'username' => dj_user_name}, true, user_name, user_password, 'POST')
|
122
|
+
end
|
123
|
+
|
124
|
+
def BlipFm.add_blip_to_playlist(user_name, user_password, blip_id)
|
125
|
+
return execute_blip_method('favorite/addToPlaylist', true, true, {'blipId' => blip_id}, true, user_name, user_password, 'POST')
|
126
|
+
end
|
127
|
+
|
128
|
+
def BlipFm.remove_dj(user_name, user_password, dj_user_name)
|
129
|
+
return execute_blip_method('favorite/removeDJ', true, true, {'username' => dj_user_name}, true, user_name, user_password, 'POST')
|
130
|
+
end
|
131
|
+
|
132
|
+
def BlipFm.remove_blip_from_playlist(user_name, user_password, blip_id)
|
133
|
+
return execute_blip_method('favorite/removeFromPlaylist', true, true, {'blipId' => blip_id}, true, user_name, user_password, 'POST')
|
134
|
+
end
|
135
|
+
|
136
|
+
def BlipFm.update_playlist(user_name, user_password, blip_id_array)
|
137
|
+
return execute_blip_method('favorite/updatePlaylist', true, true, {'ids' => blip_id_array.join(',')}, true, user_name, user_password, 'POST')
|
138
|
+
end
|
139
|
+
|
140
|
+
def BlipFm.find_songs(search_term)
|
141
|
+
return create_blip_array(execute_blip_method('search/findSongs', true, true, {'searchTerm' => search_term}), 'Song')
|
142
|
+
end
|
143
|
+
|
144
|
+
def BlipFm.find_users(search_term, offset = 0, limit = 25)
|
145
|
+
return create_blip_array(execute_blip_method('search/findUsers', true, true, {'searchTerm' => search_term, 'offset' => offset, 'limit' => limit}), 'user')
|
146
|
+
end
|
147
|
+
|
148
|
+
def BlipFm.get_users(user_name_array)
|
149
|
+
return create_blip_array(execute_blip_method('user/getByUsername', true, true, {'username' => user_name_array.join(',')}), 'User')
|
150
|
+
end
|
151
|
+
|
152
|
+
def BlipFm.get_user_djs(user_name, offset = 0, limit = 25)
|
153
|
+
return create_blip_array(execute_blip_method('user/getFavoriteDJs', true, true, {'username' => user_name, 'offset' => offset, 'limit' => limit}), 'User')
|
154
|
+
end
|
155
|
+
|
156
|
+
def BlipFm.get_user_listeners(user_name, offset = 0, limit = 25)
|
157
|
+
return create_blip_array(execute_blip_method('user/getListeners', true, true, {'username' => user_name, 'offset' => offset, 'limit' => limit}), 'User')
|
158
|
+
end
|
159
|
+
|
160
|
+
def BlipFm.get_user_preferences(user_name, user_password)
|
161
|
+
return execute_blip_method('user/getPreferences', true, true, {}, true, user_name, user_password)
|
162
|
+
end
|
163
|
+
|
164
|
+
def BlipFm.get_user_stats(user_name)
|
165
|
+
return execute_blip_method('user/getStats', true, true, {'username' => user_name})
|
166
|
+
end
|
167
|
+
|
168
|
+
def BlipFm.give_user_props(user_name, user_password, dj_user_name, blip_id = 0)
|
169
|
+
return execute_blip_method('user/giveProps', true, true, {'username' => dj_user_name, 'blipId' => blip_id}, true, user_name, user_password, 'POST')
|
170
|
+
end
|
171
|
+
|
172
|
+
def BlipFm.create_user(user_name, user_email, user_password)
|
173
|
+
return execute_blip_method('user/signup', true, true, {'username' => user_name, 'emailAddress' => user_email, 'password' => user_password}, false, nil, nil, 'POST')
|
174
|
+
end
|
175
|
+
|
176
|
+
def BlipFm.get_blip_timestamp()
|
177
|
+
time_xml = execute_blip_method('util/time', false, false)
|
178
|
+
return REXML::Document.new(time_xml).root.elements['result'].text
|
179
|
+
end
|
180
|
+
|
181
|
+
# BLIP CLASSES
|
182
|
+
class User
|
183
|
+
attr_reader :id, :display_name, :profile_pic_url, :status, :props_count, :country_code, :user_name, :website_url, :time_zone, :last_blip_time, :create_time, :update_time, :listener_count
|
184
|
+
|
185
|
+
def User.create_by_xml(user_xml)
|
186
|
+
if (!user_xml.elements['id'].nil?)
|
187
|
+
id = user_xml.elements['id'].text
|
188
|
+
display_name = user_xml.elements['urlName'].text
|
189
|
+
profile_pic_url = user_xml.elements['profilePic'].text
|
190
|
+
status = user_xml.elements['status'].text
|
191
|
+
props_count = user_xml.elements['propsCount'].text
|
192
|
+
country_code = user_xml.elements['countryAbbr'].text
|
193
|
+
user_name = user_xml.elements['name'].text
|
194
|
+
website_url = user_xml.elements['website'].text
|
195
|
+
time_zone = user_xml.elements['timeZone'].text
|
196
|
+
last_blip_time = user_xml.elements['lastBlipTime'].text
|
197
|
+
create_time = user_xml.elements['insTime'].text
|
198
|
+
update_time = user_xml.elements['updateTime'].text
|
199
|
+
listener_count = nil #user_xml.elements['listeners'].text
|
200
|
+
|
201
|
+
User.new(id, display_name, profile_pic_url, status, props_count, country_code, user_name, website_url, time_zone, last_blip_time, create_time, update_time, listener_count)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def initialize(id, display_name, profile_pic_url, status, props_count, country_code, user_name, website_url, time_zone, last_blip_time, create_time, update_time, listener_count)
|
206
|
+
@id = if_nill(id).strip
|
207
|
+
@display_name = if_nill(display_name).strip
|
208
|
+
@profile_pic_url = if_nill(profile_pic_url).strip
|
209
|
+
@status = if_nill(status).strip
|
210
|
+
@props_count = if_nill(props_count).strip
|
211
|
+
@country_code = if_nill(country_code).strip
|
212
|
+
@user_name = if_nill(user_name).strip
|
213
|
+
@website_url = if_nill(website_url).strip
|
214
|
+
@time_zone = if_nill(time_zone).strip
|
215
|
+
@last_blip_time = if_nill(last_blip_time).strip
|
216
|
+
@create_time = if_nill(create_time).strip
|
217
|
+
@update_time = if_nill(update_time).strip
|
218
|
+
@listener_count = if_nill(listener_count).strip
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
class Blip
|
223
|
+
attr_reader :id, :media_url, :owner, :artist, :title, :create_time, :message, :unix_time, :to_user_id, :type, :status, :reblip_id, :thumbplay_url, :via, :via_url, :bitrate, :genre, :is_favorite
|
224
|
+
|
225
|
+
def Blip.create_by_xml(blip_xml)
|
226
|
+
owner = User.create_by_xml(blip_xml.elements['owner'])
|
227
|
+
|
228
|
+
if (!blip_xml.elements['id'].nil?)
|
229
|
+
id = blip_xml.elements['id'].text
|
230
|
+
media_url = blip_xml.elements['url'].text
|
231
|
+
artist = blip_xml.elements['artist'].text
|
232
|
+
title = blip_xml.elements['title'].text
|
233
|
+
create_time = blip_xml.elements['insTime'].text
|
234
|
+
message = blip_xml.elements['message'].text
|
235
|
+
unix_time = blip_xml.elements['unixTime'].text
|
236
|
+
to_user_id = blip_xml.elements['toId'].text
|
237
|
+
type = blip_xml.elements['type'].text
|
238
|
+
status = blip_xml.elements['status'].text
|
239
|
+
reblip_id = blip_xml.elements['reblipId'].text
|
240
|
+
thumbplay_url = blip_xml.elements['thumbplayLink'].text
|
241
|
+
via = blip_xml.elements['via'].text
|
242
|
+
via_url = blip_xml.elements['viaUrl'].text
|
243
|
+
bitrate = blip_xml.elements['bitrate'].text
|
244
|
+
genre = blip_xml.elements['genre'].text
|
245
|
+
is_favorite = nil
|
246
|
+
|
247
|
+
Blip.new(id, media_url, owner, artist, title, create_time, message, unix_time, to_user_id, type, status, reblip_id, thumbplay_url, via, via_url, bitrate, genre, is_favorite)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def Blip.get(id)
|
252
|
+
blip_xml = REXML::Document.new(BlipFm::execute_blip_method('blip/getById', false, false, {'id' => id})).elements['BlipApiResponse/result/collection/Blip']
|
253
|
+
create_by_xml(blip_xml)
|
254
|
+
end
|
255
|
+
|
256
|
+
def initialize(id, media_url, owner, artist, title, create_time, message, unix_time, to_user_id, type, status, reblip_id, thumbplay_url, via, via_url, bitrate, genre, is_favorite)
|
257
|
+
@id = if_nill(id).strip
|
258
|
+
@media_url = if_nill(media_url).strip
|
259
|
+
@owner = owner
|
260
|
+
@artist = if_nill(artist).strip
|
261
|
+
@title = if_nill(title).strip
|
262
|
+
@create_time = if_nill(create_time).strip
|
263
|
+
@message = if_nill(message).strip
|
264
|
+
@unix_time = if_nill(unix_time).strip
|
265
|
+
@to_user_id = if_nill(to_user_id).strip
|
266
|
+
@type = if_nill(type).strip
|
267
|
+
@status = if_nill(status).strip
|
268
|
+
@reblip_id = if_nill(reblip_id).strip
|
269
|
+
@thumbplay_url = if_nill(thumbplay_url).strip
|
270
|
+
@via = if_nill(via).strip
|
271
|
+
@via_url = if_nill(via_url).strip
|
272
|
+
@bitrate = if_nill(bitrate).strip
|
273
|
+
@genre = if_nill(genre).strip
|
274
|
+
@is_favorite = if_nill(is_favorite).strip
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
class Song
|
279
|
+
attr_reader :location, :title, :artist, :bitrate, :genre, :duration, :type
|
280
|
+
|
281
|
+
def Song.create_by_xml(song_xml)
|
282
|
+
|
283
|
+
location = song_xml.elements['location'].text
|
284
|
+
title = song_xml.elements['title'].text
|
285
|
+
artist = song_xml.elements['artist'].text
|
286
|
+
bitrate = song_xml.elements['bitrate'].text
|
287
|
+
genre = song_xml.elements['genre'].text
|
288
|
+
duration = song_xml.elements['duration'].text
|
289
|
+
type = song_xml.elements['blipType'].text
|
290
|
+
|
291
|
+
Song.new(location, title, artist, bitrate, genre, duration, type)
|
292
|
+
end
|
293
|
+
|
294
|
+
def initialize(location, title, artist, bitrate, genre, duration, type)
|
295
|
+
@location = if_nill(location).strip
|
296
|
+
@title = if_nill(title).strip
|
297
|
+
@artist = if_nill(artist).strip
|
298
|
+
@bitrate = if_nill(bitrate).strip
|
299
|
+
@genre = if_nill(genre).strip
|
300
|
+
@duration = if_nill(duration).strip
|
301
|
+
@type = if_nill(type).strip
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.4
|
3
|
+
specification_version: 1
|
4
|
+
name: blipfm
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.1.0
|
7
|
+
date: 2009-06-15 00:00:00 -07:00
|
8
|
+
summary: A ruby library for the Blip.fm API. Developed to facilitate the development of Blip.fm (social media service centered around music) applications in ruby.
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: chris@tucsonconsulting.com
|
12
|
+
homepage: http://blipfm.rubyforge.org/
|
13
|
+
rubyforge_project: blipfm
|
14
|
+
description: A ruby library for the Blip.fm API. Developed to facilitate the development of Blip.fm (social media service centered around music) applications in ruby.
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- Chris Koehler
|
31
|
+
files:
|
32
|
+
- README.txt
|
33
|
+
- lib/blipfm.rb
|
34
|
+
test_files: []
|
35
|
+
|
36
|
+
rdoc_options: []
|
37
|
+
|
38
|
+
extra_rdoc_files: []
|
39
|
+
|
40
|
+
executables: []
|
41
|
+
|
42
|
+
extensions: []
|
43
|
+
|
44
|
+
requirements: []
|
45
|
+
|
46
|
+
dependencies:
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: uuid
|
49
|
+
version_requirement:
|
50
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 2.0.1
|
55
|
+
version:
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: ruby-hmac
|
58
|
+
version_requirement:
|
59
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: 0.3.2
|
64
|
+
version:
|