line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package WWW::Webjay;
|
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
41746
|
use 5.008004;
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
41
|
|
4
|
1
|
|
|
1
|
|
6
|
use strict;
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
31
|
|
5
|
1
|
|
|
1
|
|
5
|
use warnings;
|
|
1
|
|
|
|
|
6
|
|
|
1
|
|
|
|
|
224
|
|
6
|
1
|
|
|
1
|
|
6
|
use Carp;
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
252
|
|
7
|
1
|
|
|
1
|
|
2978
|
use Getopt::Std;
|
|
1
|
|
|
|
|
51
|
|
|
1
|
|
|
|
|
63
|
|
8
|
1
|
|
|
1
|
|
1045
|
use Data::Dumper;
|
|
1
|
|
|
|
|
18616
|
|
|
1
|
|
|
|
|
84
|
|
9
|
1
|
|
|
1
|
|
1148
|
use HTML::Entities;
|
|
1
|
|
|
|
|
10267
|
|
|
1
|
|
|
|
|
282
|
|
10
|
1
|
|
|
1
|
|
4058
|
use HTTP::Cookies;
|
|
1
|
|
|
|
|
25091
|
|
|
1
|
|
|
|
|
31
|
|
11
|
1
|
|
|
1
|
|
3138
|
use LWP::UserAgent;
|
|
1
|
|
|
|
|
65300
|
|
|
1
|
|
|
|
|
37
|
|
12
|
1
|
|
|
1
|
|
14
|
use URI::Escape;
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
3041
|
|
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
our $VERSION = '0.1_1';
|
15
|
|
|
|
|
|
|
our $VERSION = eval $VERSION;
|
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
# For creation of a new Webjay object.
|
18
|
|
|
|
|
|
|
sub new {
|
19
|
0
|
|
|
0
|
0
|
|
my ($class, %params) = @_;
|
20
|
0
|
|
|
|
|
|
my $self = {};
|
21
|
|
|
|
|
|
|
|
22
|
0
|
0
|
|
|
|
|
croak "Client description not set with client_page param!" unless $params{client_page};
|
23
|
|
|
|
|
|
|
|
24
|
0
|
|
|
|
|
|
$self->{DEBUG} = 0;
|
25
|
0
|
|
|
|
|
|
$self->{COOKIEJAR} = ".webjaycookies";
|
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
# Create the User Agent:
|
28
|
0
|
|
0
|
|
|
|
$self->{UA} = LWP::UserAgent->new || die "Unable to create LWP::UserAgent";
|
29
|
0
|
|
|
|
|
|
$self->{UA}->agent($params{client_page});
|
30
|
0
|
|
|
|
|
|
$self->{UA}->cookie_jar(HTTP::Cookies->new(file => $self->{COOKIEJAR}, autosave => 1));
|
31
|
|
|
|
|
|
|
|
32
|
0
|
|
|
|
|
|
$self->{USERNAME} = $params{username};
|
33
|
0
|
|
|
|
|
|
$self->{PASSWORD} = $params{password};
|
34
|
0
|
|
|
|
|
|
$self->{CLIENTPAGE} = $params{client_page};
|
35
|
|
|
|
|
|
|
|
36
|
0
|
|
|
|
|
|
bless ($self, $class);
|
37
|
0
|
|
|
|
|
|
return $self;
|
38
|
|
|
|
|
|
|
}
|
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
# Set or retrieve the client description page.
|
41
|
|
|
|
|
|
|
sub client_page {
|
42
|
0
|
|
|
0
|
0
|
|
my $self = shift;
|
43
|
0
|
0
|
|
|
|
|
if (@_) { $self->{CLIENTPAGE} = shift }
|
|
0
|
|
|
|
|
|
|
44
|
0
|
|
|
|
|
|
return $self->{CLIENTPAGE};
|
45
|
|
|
|
|
|
|
}
|
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
# Set or retrieve the username.
|
48
|
|
|
|
|
|
|
sub username {
|
49
|
0
|
|
|
0
|
0
|
|
my $self = shift;
|
50
|
0
|
0
|
|
|
|
|
if (@_) { $self->{USERNAME} = shift }
|
|
0
|
|
|
|
|
|
|
51
|
0
|
|
|
|
|
|
return $self->{USERNAME};
|
52
|
|
|
|
|
|
|
}
|
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
# Set the password (cannot retrieve).
|
55
|
|
|
|
|
|
|
sub password {
|
56
|
0
|
|
|
0
|
0
|
|
my $self = shift;
|
57
|
0
|
0
|
|
|
|
|
if (@_) { $self->{PASSWORD} = shift }
|
|
0
|
|
|
|
|
|
|
58
|
0
|
|
|
|
|
|
return undef;
|
59
|
|
|
|
|
|
|
}
|
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
###########################################################
|
62
|
|
|
|
|
|
|
# To Create A Playlist
|
63
|
|
|
|
|
|
|
#
|
64
|
|
|
|
|
|
|
# If the URI of the playlist is not set,
|
65
|
|
|
|
|
|
|
# and there is a title in the GET arguments,
|
66
|
|
|
|
|
|
|
# create a playlist by that title and return the short-name.
|
67
|
|
|
|
|
|
|
#
|
68
|
|
|
|
|
|
|
# Args:
|
69
|
|
|
|
|
|
|
# title text [MAX LENGTH 255] [REQUIRED]
|
70
|
|
|
|
|
|
|
# public public OR private [default: public] [REQUIRED]
|
71
|
|
|
|
|
|
|
# description text [MAX LENGTH 255] [OPTIONAL]
|
72
|
|
|
|
|
|
|
# m3u Newline separated list of URLs of playlist entries [MAX LENGTH 40K] [OPTIONAL]
|
73
|
|
|
|
|
|
|
#
|
74
|
|
|
|
|
|
|
# Returns:
|
75
|
|
|
|
|
|
|
# 201 on success
|
76
|
|
|
|
|
|
|
# 4xx on failure
|
77
|
|
|
|
|
|
|
# 5xx on failure
|
78
|
|
|
|
|
|
|
#
|
79
|
|
|
|
|
|
|
sub create_playlist {
|
80
|
0
|
|
|
0
|
1
|
|
my ($self, %params) = @_;
|
81
|
0
|
|
|
|
|
|
my $title = $params{title};
|
82
|
0
|
|
|
|
|
|
my $public = $params{public};
|
83
|
0
|
|
|
|
|
|
my $description = $params{description};
|
84
|
0
|
|
|
|
|
|
my $m3u = $params{m3u};
|
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
# Return "bad request" status code on missing params
|
87
|
0
|
0
|
|
|
|
|
$public = "public" unless $public;
|
88
|
0
|
0
|
|
|
|
|
confess "The 'title' parameter is REQUIRED." unless ($title);
|
89
|
|
|
|
|
|
|
|
90
|
0
|
|
|
|
|
|
my $req = HTTP::Request->new(POST => 'http://webjay.org/api/new/playlists');
|
91
|
0
|
|
|
|
|
|
$req->content_type('application/x-www-form-urlencoded');
|
92
|
0
|
|
|
|
|
|
$req->authorization_basic($self->username(), $self->password());
|
93
|
0
|
|
|
|
|
|
$req->content('title=' . uri_escape(encode_entities($title)) .
|
94
|
|
|
|
|
|
|
'&public=' . uri_escape($public).
|
95
|
|
|
|
|
|
|
'&description=' . uri_escape(encode_entities($description)).
|
96
|
|
|
|
|
|
|
'&m3u=' . uri_escape($m3u).
|
97
|
|
|
|
|
|
|
'');
|
98
|
|
|
|
|
|
|
|
99
|
0
|
|
|
|
|
|
my $res = $self->{UA}->request($req);
|
100
|
0
|
0
|
0
|
|
|
|
if (($res->code / 100) == 4 || ($res->code / 100) == 5) {
|
101
|
0
|
|
|
|
|
|
carp "HTTP Error: " . $res->code;
|
102
|
|
|
|
|
|
|
}
|
103
|
0
|
|
|
|
|
|
return $res->code;
|
104
|
|
|
|
|
|
|
}
|
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
###########################################################
|
107
|
|
|
|
|
|
|
# To Modify A Playlist
|
108
|
|
|
|
|
|
|
#
|
109
|
|
|
|
|
|
|
# POST to the API URL (for example:
|
110
|
|
|
|
|
|
|
# http://webjay.org/api/by/yourname/short-name
|
111
|
|
|
|
|
|
|
# ) of the playlist that you want to change.
|
112
|
|
|
|
|
|
|
#
|
113
|
|
|
|
|
|
|
# Args:
|
114
|
|
|
|
|
|
|
# shortname Short-name of the playlist
|
115
|
|
|
|
|
|
|
# title text [MAX LENGTH 255]
|
116
|
|
|
|
|
|
|
# public public OR private [MAX LENGTH 255]
|
117
|
|
|
|
|
|
|
# description text [MAX LENGTH 255]
|
118
|
|
|
|
|
|
|
# m3u Newline-separated list of URLs of playlist entries [MAX LENGTH 40K]
|
119
|
|
|
|
|
|
|
#
|
120
|
|
|
|
|
|
|
# Returns:
|
121
|
|
|
|
|
|
|
# 200 on success
|
122
|
|
|
|
|
|
|
# 4xx on failure
|
123
|
|
|
|
|
|
|
# 5xx on failure
|
124
|
|
|
|
|
|
|
#
|
125
|
|
|
|
|
|
|
sub modify_playlist {
|
126
|
0
|
|
|
0
|
1
|
|
my ($self, %params) = @_;
|
127
|
0
|
|
|
|
|
|
my $shortname = $params{shortname};
|
128
|
0
|
|
|
|
|
|
my $title = $params{title};
|
129
|
0
|
|
|
|
|
|
my $public = $params{public};
|
130
|
0
|
|
|
|
|
|
my $description = $params{description};
|
131
|
0
|
|
|
|
|
|
my $m3u = $params{m3u};
|
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
# Return "bad request" status code on missing params
|
134
|
0
|
0
|
|
|
|
|
confess "The 'shortname' parameter is REQUIRED." unless ($shortname);
|
135
|
|
|
|
|
|
|
|
136
|
0
|
|
|
|
|
|
my $req = HTTP::Request->new(POST => 'http://webjay.org/api/by/' .
|
137
|
|
|
|
|
|
|
$self->username() . '/' . $shortname);
|
138
|
0
|
|
|
|
|
|
$req->content_type('application/x-www-form-urlencoded');
|
139
|
0
|
|
|
|
|
|
$req->authorization_basic($self->username(), $self->password());
|
140
|
0
|
|
|
|
|
|
$req->content('title=' . uri_escape(encode_entities($title)) .
|
141
|
|
|
|
|
|
|
'&public=' . uri_escape($public).
|
142
|
|
|
|
|
|
|
'&description=' . uri_escape(encode_entities($description)).
|
143
|
|
|
|
|
|
|
'&m3u=' . uri_escape($m3u).
|
144
|
|
|
|
|
|
|
'');
|
145
|
|
|
|
|
|
|
|
146
|
0
|
|
|
|
|
|
my $res = $self->{UA}->request($req);
|
147
|
0
|
0
|
0
|
|
|
|
if (($res->code / 100) == 4 || ($res->code / 100) == 5) {
|
148
|
0
|
|
|
|
|
|
carp "HTTP Error: " . $res->code;
|
149
|
|
|
|
|
|
|
}
|
150
|
0
|
|
|
|
|
|
return $res->code();
|
151
|
|
|
|
|
|
|
}
|
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
###########################################################
|
154
|
|
|
|
|
|
|
# To Modify Metadata For A Song
|
155
|
|
|
|
|
|
|
#
|
156
|
|
|
|
|
|
|
# Update metadata related to a playlist entry,
|
157
|
|
|
|
|
|
|
# using the URL of the entry as a key.
|
158
|
|
|
|
|
|
|
#
|
159
|
|
|
|
|
|
|
# Metadata for an audio URL applies to all instances
|
160
|
|
|
|
|
|
|
# of that URL within your playlists.
|
161
|
|
|
|
|
|
|
# If you do not set a value for the image, description,
|
162
|
|
|
|
|
|
|
# or site parameter, a blank value will be used.
|
163
|
|
|
|
|
|
|
#
|
164
|
|
|
|
|
|
|
# Args:
|
165
|
|
|
|
|
|
|
# song [URL of an audio resource in one of your playlists] [REQUIRED]
|
166
|
|
|
|
|
|
|
# description text [MAX LENGTH 255] [REQUIRED]
|
167
|
|
|
|
|
|
|
# image URL of a GIF [MAX LENGTH 255] [REQUIRED]
|
168
|
|
|
|
|
|
|
# site URL related to audio resource [MAX LENGTH 255] [REQUIRED]
|
169
|
|
|
|
|
|
|
#
|
170
|
|
|
|
|
|
|
# Returns:
|
171
|
|
|
|
|
|
|
# 200 on success
|
172
|
|
|
|
|
|
|
# 4xx on failure
|
173
|
|
|
|
|
|
|
# 5xx on failure
|
174
|
|
|
|
|
|
|
#
|
175
|
|
|
|
|
|
|
sub modify_metadata {
|
176
|
0
|
|
|
0
|
1
|
|
my ($self, %params) = @_;
|
177
|
0
|
|
|
|
|
|
my $song = $params{song};
|
178
|
0
|
|
|
|
|
|
my $description = $params{description};
|
179
|
0
|
|
|
|
|
|
my $image = $params{image};
|
180
|
0
|
|
|
|
|
|
my $site = $params{site};
|
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
# Return "bad request" status code on missing params
|
183
|
0
|
0
|
|
|
|
|
confess "The 'song' parameter is REQUIRED." unless ($song);
|
184
|
0
|
0
|
|
|
|
|
confess "The 'description' parameter is REQUIRED." unless ($description);
|
185
|
0
|
0
|
|
|
|
|
confess "The 'image' parameter is REQUIRED." unless ($image);
|
186
|
0
|
0
|
|
|
|
|
confess "The 'site' parameter is REQUIRED." unless ($site);
|
187
|
|
|
|
|
|
|
|
188
|
0
|
|
|
|
|
|
my $req = HTTP::Request->new(POST => 'http://webjay.org/api/songmetadata/' .
|
189
|
|
|
|
|
|
|
$self->username() . '/short-name?audio=' . $song);
|
190
|
0
|
|
|
|
|
|
$req->content_type('application/x-www-form-urlencoded');
|
191
|
0
|
|
|
|
|
|
$req->authorization_basic($self->username(), $self->password());
|
192
|
0
|
|
|
|
|
|
$req->content('description=' . uri_escape(encode_entities($description)) .
|
193
|
|
|
|
|
|
|
'&image=' . uri_escape($image).
|
194
|
|
|
|
|
|
|
'&site=' . uri_escape(encode_entities($site)).
|
195
|
|
|
|
|
|
|
'');
|
196
|
|
|
|
|
|
|
|
197
|
0
|
|
|
|
|
|
my $res = $self->{UA}->request($req);
|
198
|
0
|
0
|
0
|
|
|
|
if (($res->code / 100) == 4 || ($res->code / 100) == 5) {
|
199
|
0
|
|
|
|
|
|
carp "HTTP Error: " . $res->code;
|
200
|
|
|
|
|
|
|
}
|
201
|
0
|
|
|
|
|
|
return $res->code();
|
202
|
|
|
|
|
|
|
}
|
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
###########################################################
|
205
|
|
|
|
|
|
|
# To Get A Listing Of Your Playlists
|
206
|
|
|
|
|
|
|
#
|
207
|
|
|
|
|
|
|
# Fetch a listing of user playlists.
|
208
|
|
|
|
|
|
|
#
|
209
|
|
|
|
|
|
|
# Returns a list: playlist1, playlist2, ...
|
210
|
|
|
|
|
|
|
#
|
211
|
|
|
|
|
|
|
sub list_playlists {
|
212
|
0
|
|
|
0
|
1
|
|
my $self = shift;
|
213
|
0
|
|
|
|
|
|
my $req = HTTP::Request->new(GET => 'http://webjay.org/api/shortname/' . $self->username());
|
214
|
0
|
|
|
|
|
|
$req->authorization_basic($self->username(), $self->password());
|
215
|
0
|
|
|
|
|
|
my $res = $self->{UA}->request($req);
|
216
|
|
|
|
|
|
|
|
217
|
0
|
0
|
0
|
|
|
|
if (($res->code / 100) == 4 || ($res->code / 100) == 5) {
|
218
|
0
|
|
|
|
|
|
carp "HTTP Error: " . $res->code;
|
219
|
|
|
|
|
|
|
}
|
220
|
0
|
|
|
|
|
|
return split /\n/, $res->content;
|
221
|
|
|
|
|
|
|
}
|
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
###########################################################
|
224
|
|
|
|
|
|
|
# To Get The Short-Name Of A Playlist
|
225
|
|
|
|
|
|
|
#
|
226
|
|
|
|
|
|
|
# Given a playlist title, fetch the short-name.
|
227
|
|
|
|
|
|
|
#
|
228
|
|
|
|
|
|
|
# Args:
|
229
|
|
|
|
|
|
|
# title entity-encoded string [MAX LENGTH 255]
|
230
|
|
|
|
|
|
|
#
|
231
|
|
|
|
|
|
|
# Returns shortname
|
232
|
|
|
|
|
|
|
#
|
233
|
|
|
|
|
|
|
sub get_shortname {
|
234
|
0
|
|
|
0
|
1
|
|
my ($self, %params) = @_;
|
235
|
0
|
|
|
|
|
|
my $title = $params{title};
|
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
# Return "bad request" status code on missing params
|
238
|
0
|
0
|
|
|
|
|
confess "The 'title' parameter is REQUIRED." unless ($title);
|
239
|
|
|
|
|
|
|
|
240
|
0
|
|
|
|
|
|
my $req = HTTP::Request->new(GET => 'http://webjay.org/api/shortname/' .
|
241
|
|
|
|
|
|
|
$self->username() . '?title=' . $title);
|
242
|
0
|
|
|
|
|
|
$req->authorization_basic($self->username(), $self->password());
|
243
|
0
|
|
|
|
|
|
my $res = $self->{UA}->request($req);
|
244
|
|
|
|
|
|
|
|
245
|
0
|
0
|
0
|
|
|
|
if (($res->code / 100) == 4 || ($res->code / 100) == 5) {
|
246
|
0
|
|
|
|
|
|
carp "HTTP Error: " . $res->code;
|
247
|
|
|
|
|
|
|
}
|
248
|
0
|
|
|
|
|
|
return $res->content;
|
249
|
|
|
|
|
|
|
}
|
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
###########################################################
|
252
|
|
|
|
|
|
|
# To Fetch Playlist Content
|
253
|
|
|
|
|
|
|
#
|
254
|
|
|
|
|
|
|
# Get the actual content of a playlist.
|
255
|
|
|
|
|
|
|
#
|
256
|
|
|
|
|
|
|
# Args:
|
257
|
|
|
|
|
|
|
# shortname Short-name of the playlist.
|
258
|
|
|
|
|
|
|
#
|
259
|
|
|
|
|
|
|
# Returns playlist in xspf
|
260
|
|
|
|
|
|
|
#
|
261
|
|
|
|
|
|
|
sub fetch_playlist {
|
262
|
0
|
|
|
0
|
1
|
|
my ($self, %params) = @_;
|
263
|
0
|
|
|
|
|
|
my $shortname = $params{shortname};
|
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
# Return "bad request" status code on missing params
|
266
|
0
|
0
|
|
|
|
|
confess "The 'shortname' parameter is REQUIRED." unless ($shortname);
|
267
|
|
|
|
|
|
|
|
268
|
0
|
|
|
|
|
|
my $req = HTTP::Request->new(GET => 'http://webjay.org/api/xspf/' .
|
269
|
|
|
|
|
|
|
$self->username() . '/' . $shortname);
|
270
|
0
|
|
|
|
|
|
$req->authorization_basic($self->username(), $self->password());
|
271
|
0
|
|
|
|
|
|
my $res = $self->{UA}->request($req);
|
272
|
|
|
|
|
|
|
|
273
|
0
|
0
|
0
|
|
|
|
if (($res->code / 100) == 4 || ($res->code / 100) == 5) {
|
274
|
0
|
|
|
|
|
|
carp "HTTP Error: " . $res->code;
|
275
|
|
|
|
|
|
|
}
|
276
|
0
|
|
|
|
|
|
return $res->content;
|
277
|
|
|
|
|
|
|
}
|
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
###########################################################
|
280
|
|
|
|
|
|
|
# To Delete A Playlist
|
281
|
|
|
|
|
|
|
#
|
282
|
|
|
|
|
|
|
# To delete a playlist, use the HTTP DELETE method on the playlist URI.
|
283
|
|
|
|
|
|
|
# Please be careful.
|
284
|
|
|
|
|
|
|
# You will not be prompted,
|
285
|
|
|
|
|
|
|
# and once a playlist is deleted it is not recoverable.
|
286
|
|
|
|
|
|
|
#
|
287
|
|
|
|
|
|
|
# Args:
|
288
|
|
|
|
|
|
|
# shortname Short-name of the playlist.
|
289
|
|
|
|
|
|
|
#
|
290
|
|
|
|
|
|
|
# Returns:
|
291
|
|
|
|
|
|
|
# 200 on success
|
292
|
|
|
|
|
|
|
# 4xx on failure
|
293
|
|
|
|
|
|
|
# 5xx on failure
|
294
|
|
|
|
|
|
|
#
|
295
|
|
|
|
|
|
|
sub delete_playlist {
|
296
|
0
|
|
|
0
|
1
|
|
my ($self, %params) = @_;
|
297
|
0
|
|
|
|
|
|
my $shortname = $params{shortname};
|
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
# Return "bad request" status code on missing params
|
300
|
0
|
0
|
|
|
|
|
confess "The 'shortname' parameter is REQUIRED." unless ($shortname);
|
301
|
|
|
|
|
|
|
|
302
|
0
|
|
|
|
|
|
my $req = HTTP::Request->new(DELETE => 'http://webjay.org/api/by/' .
|
303
|
|
|
|
|
|
|
$self->username() . '/' . $shortname);
|
304
|
0
|
|
|
|
|
|
$req->content_type('application/x-www-form-urlencoded');
|
305
|
0
|
|
|
|
|
|
$req->authorization_basic($self->username(), $self->password());
|
306
|
|
|
|
|
|
|
|
307
|
0
|
|
|
|
|
|
my $res = $self->{UA}->request($req);
|
308
|
0
|
0
|
0
|
|
|
|
if (($res->code / 100) == 4 || ($res->code / 100) == 5) {
|
309
|
0
|
|
|
|
|
|
carp "HTTP Error: " . $res->code;
|
310
|
|
|
|
|
|
|
}
|
311
|
0
|
|
|
|
|
|
return $res->code();
|
312
|
|
|
|
|
|
|
}
|
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
1;
|
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
__END__
|