viddler-ruby 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +3 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +38 -0
- data/README.md +27 -0
- data/Rakefile +2 -0
- data/autotest/discover.rb +1 -0
- data/lib/viddler-ruby.rb +7 -0
- data/lib/viddler/client.rb +132 -0
- data/lib/viddler/version.rb +3 -0
- data/spec/client_spec.rb +149 -0
- data/spec/spec_helper.rb +6 -0
- data/viddler-ruby.gemspec +26 -0
- metadata +140 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
viddler-ruby (0.0.1)
|
5
|
+
activesupport (> 2.3.0)
|
6
|
+
json
|
7
|
+
rest-client
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: http://rubygems.org/
|
11
|
+
specs:
|
12
|
+
ZenTest (4.4.0)
|
13
|
+
activesupport (2.3.10)
|
14
|
+
diff-lcs (1.1.2)
|
15
|
+
json (1.4.6)
|
16
|
+
mime-types (1.16)
|
17
|
+
rest-client (1.6.1)
|
18
|
+
mime-types (>= 1.16)
|
19
|
+
rspec (2.1.0)
|
20
|
+
rspec-core (~> 2.1.0)
|
21
|
+
rspec-expectations (~> 2.1.0)
|
22
|
+
rspec-mocks (~> 2.1.0)
|
23
|
+
rspec-core (2.1.0)
|
24
|
+
rspec-expectations (2.1.0)
|
25
|
+
diff-lcs (~> 1.1.2)
|
26
|
+
rspec-mocks (2.1.0)
|
27
|
+
|
28
|
+
PLATFORMS
|
29
|
+
ruby
|
30
|
+
|
31
|
+
DEPENDENCIES
|
32
|
+
ZenTest
|
33
|
+
activesupport (> 2.3.0)
|
34
|
+
bundler (>= 1.0.0)
|
35
|
+
json
|
36
|
+
rest-client
|
37
|
+
rspec
|
38
|
+
viddler-ruby!
|
data/README.md
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
viddler-ruby
|
2
|
+
============
|
3
|
+
|
4
|
+
viddler-ruby provides a simple interface to [Viddler](http://viddler.com)'s API. To use, just instantiate an instance of Viddler::Client and call the `#get` and `#post` methods. For example, to get the details of a video:
|
5
|
+
|
6
|
+
viddler = Viddler::Client.new('your api key')
|
7
|
+
video = viddler.get 'viddler.videos.getDetails', :video_id => 'abc123'
|
8
|
+
|
9
|
+
puts video['title'] # => "My video"
|
10
|
+
puts video['id'] # => "abc123"
|
11
|
+
|
12
|
+
For an authenticated client, just pass a username and password:
|
13
|
+
|
14
|
+
viddler = Viddler::Client.new('your api key', 'username', 'password')
|
15
|
+
|
16
|
+
Then, any calls made on `viddler` will be done using the correct session id.
|
17
|
+
|
18
|
+
Uploading
|
19
|
+
---------
|
20
|
+
|
21
|
+
To upload a file, use the upload method:
|
22
|
+
|
23
|
+
viddler.upload(File.open('./myvideo.mov'), {
|
24
|
+
:title => 'My video',
|
25
|
+
:description => 'This video is awesome!',
|
26
|
+
:tags => 'awesome'
|
27
|
+
})
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Autotest.add_discovery { "rspec2" }
|
data/lib/viddler-ruby.rb
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
module Viddler
|
2
|
+
# Base class for accessing the Viddler API.
|
3
|
+
#
|
4
|
+
# For more information about the Viddler API, check out the documentation:
|
5
|
+
# http://developers.viddler.com/documentation/api-v2/
|
6
|
+
#
|
7
|
+
# Examples
|
8
|
+
#
|
9
|
+
# # Initialize a client with just an API key
|
10
|
+
# viddler = Viddler::Client.new 'your api key'
|
11
|
+
#
|
12
|
+
class Client
|
13
|
+
DEFAULT_ENDPOINT = 'http://api.viddler.com/api/v2/'
|
14
|
+
|
15
|
+
attr_accessor :api_key, :sessionid
|
16
|
+
|
17
|
+
# Sets the API key and sessionid if needed for the given arguments
|
18
|
+
#
|
19
|
+
# api_key - The String API Key from Viddler
|
20
|
+
#
|
21
|
+
# Examples
|
22
|
+
#
|
23
|
+
# # Initialize a client with just an API key
|
24
|
+
# viddler = Viddler::Client.new 'your api key'
|
25
|
+
#
|
26
|
+
# Returns an instance of Viddler::Client
|
27
|
+
def initialize(api_key)
|
28
|
+
self.api_key = api_key
|
29
|
+
end
|
30
|
+
|
31
|
+
# Public: Simple method to determine if this is an authenticated client
|
32
|
+
#
|
33
|
+
# Examples
|
34
|
+
#
|
35
|
+
# viddler = Viddler::Client.new 'abc123'
|
36
|
+
# viddler.authenticated?
|
37
|
+
# # => false
|
38
|
+
#
|
39
|
+
# viddler = Viddler::Client.new 'abc123'
|
40
|
+
# viddler.authenticate! 'user', 'pass'
|
41
|
+
# viddler.authenticated?
|
42
|
+
# # => true
|
43
|
+
#
|
44
|
+
# Returns Boolean
|
45
|
+
def authenticated?
|
46
|
+
!sessionid.nil?
|
47
|
+
end
|
48
|
+
|
49
|
+
# Public: Authenticate the client using a username and password. Any
|
50
|
+
# subsequent calls will be made using the session.
|
51
|
+
#
|
52
|
+
# username - The String Viddler username
|
53
|
+
# password - The String Viddler password
|
54
|
+
#
|
55
|
+
# Examples
|
56
|
+
#
|
57
|
+
# viddler.authenticate! 'username', 'password'
|
58
|
+
#
|
59
|
+
# Returns a String sessionid
|
60
|
+
def authenticate!(username, password)
|
61
|
+
auth = get 'viddler.users.auth', :user => username, :password => password
|
62
|
+
self.sessionid = auth['auth']['sessionid']
|
63
|
+
end
|
64
|
+
|
65
|
+
# Public: Make a GET call to the Viddler API.
|
66
|
+
#
|
67
|
+
# method - The String API method you'd like to call.
|
68
|
+
# arguments - The Hash of arguments to the API method (default: {}).
|
69
|
+
#
|
70
|
+
# Examples
|
71
|
+
#
|
72
|
+
# viddler.get 'viddler.videos.getDetails', :video_id => 'abc123'
|
73
|
+
#
|
74
|
+
# Returns a Hash containing the API response.
|
75
|
+
# Raises ApiException if an error is returned from the API.
|
76
|
+
def get(method, arguments={})
|
77
|
+
arguments[:api_key] = api_key
|
78
|
+
arguments[:sessionid] = sessionid if authenticated?
|
79
|
+
JSON.parse RestClient.get(DEFAULT_ENDPOINT + method + '.json', :params => arguments)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Public: Make a POST call to the Viddler API.
|
83
|
+
#
|
84
|
+
# method - The String API method you'd like to call.
|
85
|
+
# arguments - The Hash of arguments to the API method (default: {}).
|
86
|
+
#
|
87
|
+
# Examples
|
88
|
+
#
|
89
|
+
# viddler.post 'viddler.videos.setDetails', :video_id => 'abc123',
|
90
|
+
# :title => 'new title'
|
91
|
+
#
|
92
|
+
# Returns a Hash containing the API response.
|
93
|
+
# Raises ApiException if an error is returned from the API.
|
94
|
+
def post(method, arguments={})
|
95
|
+
arguments[:api_key] = api_key
|
96
|
+
arguments[:sessionid] = sessionid if authenticated?
|
97
|
+
JSON.parse RestClient.post(DEFAULT_ENDPOINT + method + '.json', :params => arguments)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Public: Upload a video to the Viddler API.
|
101
|
+
#
|
102
|
+
# file - The File you are uploading
|
103
|
+
# arguments - The Hash of arguments for the video
|
104
|
+
# :title - The String title of the video
|
105
|
+
# :tags - The String of tags for the video
|
106
|
+
# :description - The String description of the video
|
107
|
+
# :make_public - The Boolean to make the video public on
|
108
|
+
# upload. Please note that if set to false, it
|
109
|
+
# will not make your video private.
|
110
|
+
#
|
111
|
+
# Examples
|
112
|
+
#
|
113
|
+
# viddler.upload File.open('myvideo.avi'), :title => "My Video",
|
114
|
+
# :tags => "viddler, ruby",
|
115
|
+
# :description => "This is cool"
|
116
|
+
#
|
117
|
+
# Returns a Hash containing the API response.
|
118
|
+
# Raises ApiException if an error is returned from the API
|
119
|
+
def upload(file, arguments)
|
120
|
+
# Need to use OrderedHash, because the API needs the file argument last
|
121
|
+
ordered_arguments = ActiveSupport::OrderedHash.new
|
122
|
+
|
123
|
+
arguments.each {|k,v| ordered_arguments[k] = v}
|
124
|
+
|
125
|
+
ordered_arguments[:api_key] = api_key
|
126
|
+
ordered_arguments[:sessionid] = sessionid
|
127
|
+
ordered_arguments[:file] = file
|
128
|
+
|
129
|
+
JSON.parse RestClient.post(DEFAULT_ENDPOINT + 'viddler.videos.upload.json', ordered_arguments)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
data/spec/client_spec.rb
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe Viddler::Client, ".new" do
|
4
|
+
it "sets API key" do
|
5
|
+
Viddler::Client.new('abc123').api_key.should == 'abc123'
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
describe Viddler::Client, "#authenticate!" do
|
10
|
+
before(:each) do
|
11
|
+
@client = Viddler::Client.new('abc123')
|
12
|
+
@client.stub!(:get).and_return({
|
13
|
+
'auth' => {
|
14
|
+
'sessionid' => 'mysess'
|
15
|
+
}
|
16
|
+
})
|
17
|
+
end
|
18
|
+
|
19
|
+
it "calls GET viddler.users.auth with username and password" do
|
20
|
+
@client.should_receive(:get).with('viddler.users.auth', :password => 'pass', :user => 'user')
|
21
|
+
@client.authenticate! 'user', 'pass'
|
22
|
+
end
|
23
|
+
|
24
|
+
it "sets sessionid" do
|
25
|
+
@client.authenticate! 'user', 'pass'
|
26
|
+
@client.sessionid.should == 'mysess'
|
27
|
+
end
|
28
|
+
|
29
|
+
it "returns sessionid" do
|
30
|
+
@client.authenticate!('user', 'pass').should == 'mysess'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe Viddler::Client, "#authenticated?" do
|
35
|
+
before(:each) do
|
36
|
+
@client = Viddler::Client.new('abc123')
|
37
|
+
end
|
38
|
+
|
39
|
+
it "returns true if a sessionid is set" do
|
40
|
+
@client.sessionid = 'mysess'
|
41
|
+
@client.should be_authenticated
|
42
|
+
end
|
43
|
+
|
44
|
+
it "returns false if a sessionid is not set" do
|
45
|
+
@client.should_not be_authenticated
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe Viddler::Client, "#get" do
|
50
|
+
before(:each) do
|
51
|
+
@client = Viddler::Client.new('abc123')
|
52
|
+
RestClient.stub!(:get).and_return('{"response":["hello", "howdy"]}')
|
53
|
+
end
|
54
|
+
|
55
|
+
it "calls RestClient.get with proper params" do
|
56
|
+
RestClient.should_receive(:get).with('http://api.viddler.com/api/v2/viddler.api.getInfo.json', :params => hash_including(:param1 => 'asdf', :param2 => true))
|
57
|
+
@client.get('viddler.api.getInfo', :param1 => 'asdf', :param2 => true)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "returns result of JSON.parse(response)" do
|
61
|
+
JSON.stub!(:parse).and_return('abc')
|
62
|
+
@client.get('method').should == 'abc'
|
63
|
+
end
|
64
|
+
|
65
|
+
it "includes API key" do
|
66
|
+
RestClient.should_receive(:get).with(anything, :params => hash_including(:api_key => 'abc123'))
|
67
|
+
@client.get('viddler.api.getInfo')
|
68
|
+
end
|
69
|
+
|
70
|
+
it "raises ApiException if an error occurs"
|
71
|
+
|
72
|
+
context "with authenticated client" do
|
73
|
+
before(:each) do
|
74
|
+
@client.sessionid = "mysess"
|
75
|
+
end
|
76
|
+
|
77
|
+
it "calls RestClient.get with sessionid" do
|
78
|
+
RestClient.should_receive('get').with(anything, :params => hash_including(:sessionid => 'mysess'))
|
79
|
+
@client.get('viddler.api.getInfo', :something => 'yes')
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe Viddler::Client, "#post" do
|
85
|
+
before(:each) do
|
86
|
+
@client = Viddler::Client.new('abc123')
|
87
|
+
RestClient.stub!(:post).and_return('{"response":["hello","howdy"]}')
|
88
|
+
end
|
89
|
+
|
90
|
+
it "calls RestClient.post with proper params" do
|
91
|
+
RestClient.should_receive(:post).with('http://api.viddler.com/api/v2/viddler.api.getInfo.json', :params => hash_including(:param1 => 'asdf', :param2 => true))
|
92
|
+
@client.post('viddler.api.getInfo', :param1 => 'asdf', :param2 => true)
|
93
|
+
end
|
94
|
+
|
95
|
+
it "returns result of JSON.parse(response)" do
|
96
|
+
JSON.stub!(:parse).and_return('abc')
|
97
|
+
@client.post('method').should == 'abc'
|
98
|
+
end
|
99
|
+
|
100
|
+
it "includes API key" do
|
101
|
+
RestClient.should_receive(:post).with(anything, :params => hash_including(:api_key => 'abc123'))
|
102
|
+
@client.post('viddler.api.getInfo')
|
103
|
+
end
|
104
|
+
|
105
|
+
it "raises ApiException if an error occurs"
|
106
|
+
|
107
|
+
context "with authenticated client" do
|
108
|
+
before(:each) do
|
109
|
+
@client.sessionid = "mysess"
|
110
|
+
end
|
111
|
+
|
112
|
+
it "calls RestClient.post with sessionid" do
|
113
|
+
RestClient.should_receive('post').with(anything, :params => hash_including(:sessionid => 'mysess'))
|
114
|
+
@client.post('viddler.api.getInfo', :something => 'yes')
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe Viddler::Client, "#upload" do
|
120
|
+
before(:each) do
|
121
|
+
@client = Viddler::Client.new('abc123')
|
122
|
+
@file = mock(File)
|
123
|
+
|
124
|
+
@client.sessionid = 'mysess'
|
125
|
+
RestClient.stub!(:post).and_return('{"response":["hello","howdy"]}')
|
126
|
+
end
|
127
|
+
|
128
|
+
it "calls RestClient.post with params and file" do
|
129
|
+
RestClient.should_receive(:post).with('http://api.viddler.com/api/v2/viddler.videos.upload.json', hash_including(:param1 => 'asdf', :param2 => true, :file => @file))
|
130
|
+
@client.upload @file, :param1 => 'asdf', :param2 => true
|
131
|
+
end
|
132
|
+
|
133
|
+
it "includes sessionid" do
|
134
|
+
RestClient.should_receive(:post).with(anything, hash_including(:sessionid => 'mysess'))
|
135
|
+
@client.upload @file, :param1 => 'asdf', :param2 => true
|
136
|
+
end
|
137
|
+
|
138
|
+
it "includes API key" do
|
139
|
+
RestClient.should_receive(:post).with(anything, hash_including(:api_key => 'abc123'))
|
140
|
+
@client.upload @file, :param1 => 'asdf', :param2 => true
|
141
|
+
end
|
142
|
+
|
143
|
+
it "returns result of JSON.parse" do
|
144
|
+
JSON.stub!(:parse).and_return('asdfasdf')
|
145
|
+
@client.upload(@file, :param1 => 'asdf').should == 'asdfasdf'
|
146
|
+
end
|
147
|
+
|
148
|
+
it "raises an ApiException on API error"
|
149
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path("../lib/viddler/version", __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "viddler-ruby"
|
6
|
+
s.version = Viddler::VERSION
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.authors = ["Kyle Slattery"]
|
9
|
+
s.email = ["kyle@viddler.com"]
|
10
|
+
s.homepage = "http://github.com/viddler/viddler-ruby"
|
11
|
+
s.summary = "An API wrapper for Viddler's v2 API"
|
12
|
+
s.description = "An API wrapper for Viddler's v2 API"
|
13
|
+
|
14
|
+
s.required_rubygems_version = ">= 1.3.6"
|
15
|
+
s.rubyforge_project = "viddler-ruby"
|
16
|
+
|
17
|
+
s.add_dependency "rest-client"
|
18
|
+
s.add_dependency "json"
|
19
|
+
s.add_dependency "activesupport", "> 2.3.0"
|
20
|
+
|
21
|
+
s.add_development_dependency "bundler", ">= 1.0.0"
|
22
|
+
|
23
|
+
s.files = `git ls-files`.split("\n")
|
24
|
+
s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
|
25
|
+
s.require_path = 'lib'
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: viddler-ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Kyle Slattery
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-12-13 00:00:00 -05:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: rest-client
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: json
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :runtime
|
48
|
+
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: activesupport
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">"
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 2
|
60
|
+
- 3
|
61
|
+
- 0
|
62
|
+
version: 2.3.0
|
63
|
+
type: :runtime
|
64
|
+
version_requirements: *id003
|
65
|
+
- !ruby/object:Gem::Dependency
|
66
|
+
name: bundler
|
67
|
+
prerelease: false
|
68
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
hash: 23
|
74
|
+
segments:
|
75
|
+
- 1
|
76
|
+
- 0
|
77
|
+
- 0
|
78
|
+
version: 1.0.0
|
79
|
+
type: :development
|
80
|
+
version_requirements: *id004
|
81
|
+
description: An API wrapper for Viddler's v2 API
|
82
|
+
email:
|
83
|
+
- kyle@viddler.com
|
84
|
+
executables: []
|
85
|
+
|
86
|
+
extensions: []
|
87
|
+
|
88
|
+
extra_rdoc_files: []
|
89
|
+
|
90
|
+
files:
|
91
|
+
- .gitignore
|
92
|
+
- Gemfile
|
93
|
+
- Gemfile.lock
|
94
|
+
- README.md
|
95
|
+
- Rakefile
|
96
|
+
- autotest/discover.rb
|
97
|
+
- lib/viddler-ruby.rb
|
98
|
+
- lib/viddler/client.rb
|
99
|
+
- lib/viddler/version.rb
|
100
|
+
- spec/client_spec.rb
|
101
|
+
- spec/spec_helper.rb
|
102
|
+
- viddler-ruby.gemspec
|
103
|
+
has_rdoc: true
|
104
|
+
homepage: http://github.com/viddler/viddler-ruby
|
105
|
+
licenses: []
|
106
|
+
|
107
|
+
post_install_message:
|
108
|
+
rdoc_options: []
|
109
|
+
|
110
|
+
require_paths:
|
111
|
+
- lib
|
112
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
hash: 3
|
118
|
+
segments:
|
119
|
+
- 0
|
120
|
+
version: "0"
|
121
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
122
|
+
none: false
|
123
|
+
requirements:
|
124
|
+
- - ">="
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
hash: 23
|
127
|
+
segments:
|
128
|
+
- 1
|
129
|
+
- 3
|
130
|
+
- 6
|
131
|
+
version: 1.3.6
|
132
|
+
requirements: []
|
133
|
+
|
134
|
+
rubyforge_project: viddler-ruby
|
135
|
+
rubygems_version: 1.3.7
|
136
|
+
signing_key:
|
137
|
+
specification_version: 3
|
138
|
+
summary: An API wrapper for Viddler's v2 API
|
139
|
+
test_files: []
|
140
|
+
|