baidu-sdk 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.travis.yml +15 -0
- data/.yardopts +1 -0
- data/Gemfile +17 -0
- data/Guardfile +6 -0
- data/HISTORY.md +2 -0
- data/LICENSE.txt +22 -0
- data/README.md +161 -0
- data/Rakefile +4 -0
- data/baidu-pcs.gemspec +24 -0
- data/lib/baidu/configure.rb +27 -0
- data/lib/baidu/core.rb +19 -0
- data/lib/baidu/errors/error.rb +22 -0
- data/lib/baidu/oauth.rb +11 -0
- data/lib/baidu/oauth/client.rb +66 -0
- data/lib/baidu/oauth/flow/base.rb +44 -0
- data/lib/baidu/oauth/flow/code.rb +69 -0
- data/lib/baidu/oauth/flow/device.rb +75 -0
- data/lib/baidu/pcs.rb +19 -0
- data/lib/baidu/pcs/client.rb +1090 -0
- data/lib/baidu/session.rb +36 -0
- data/lib/baidu/support/cacert.pem +37 -0
- data/lib/baidu/support/request.rb +127 -0
- data/lib/baidu/support/util.rb +67 -0
- data/lib/baidu/version.rb +5 -0
- data/spec/baidu/core_spec.rb +20 -0
- data/spec/baidu/oauth/client_spec.rb +199 -0
- data/spec/baidu/pcs/client_spec.rb +878 -0
- data/spec/baidu/session_spec.rb +27 -0
- data/spec/baidu/support/request_spec.rb +58 -0
- data/spec/baidu/support/util_spec.rb +48 -0
- data/spec/fixtures/add_task.json +1 -0
- data/spec/fixtures/cancel_task.json +3 -0
- data/spec/fixtures/copy.json +7 -0
- data/spec/fixtures/delete.json +3 -0
- data/spec/fixtures/diff.json +17 -0
- data/spec/fixtures/empty.json +1 -0
- data/spec/fixtures/get_token_code.json +8 -0
- data/spec/fixtures/get_token_device.json +8 -0
- data/spec/fixtures/list.json +11 -0
- data/spec/fixtures/list_task_0.json +1 -0
- data/spec/fixtures/list_task_1.json +1 -0
- data/spec/fixtures/listrecycle.json +23 -0
- data/spec/fixtures/logo.png +0 -0
- data/spec/fixtures/meta.json +11 -0
- data/spec/fixtures/mkdir.json +7 -0
- data/spec/fixtures/move.json +8 -0
- data/spec/fixtures/query_task_0.json +24 -0
- data/spec/fixtures/query_task_1.json +22 -0
- data/spec/fixtures/quota.json +5 -0
- data/spec/fixtures/rapidupload.json +10 -0
- data/spec/fixtures/refresh_token.json +8 -0
- data/spec/fixtures/restore.json +1 -0
- data/spec/fixtures/search.json +11 -0
- data/spec/fixtures/stream_list.json +16 -0
- data/spec/fixtures/streaming.m3u8 +5 -0
- data/spec/fixtures/upload.json +9 -0
- data/spec/fixtures/upload_block.json +4 -0
- data/spec/fixtures/user_and_device_code.json +8 -0
- data/spec/spec_helper.rb +66 -0
- metadata +169 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 989e658f22320158d197a3dcad2a2bb02fd2bae0
|
4
|
+
data.tar.gz: 386a4d15d1237e93b6081d90c678328b6728774f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d1a83fcbeb4bd8eba7ebf195f2f9a62742be6271187288fbd346ed7e6dae040c343658760cc2230da600dd019ec681c848b4482307ca684e7a80667ae6c04d1e
|
7
|
+
data.tar.gz: cd3a8c489b63b9796988fadf59187825fbdf0d6eff4c4ba20f62863de57c622e636f8efa684e513ff978af847c320902505374a2de892479887e34aacffb4f39
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--no-private
|
data/Gemfile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
group :development do
|
4
|
+
gem 'guard-rspec'
|
5
|
+
gem 'terminal-notifier-guard' if /darwin/ =~ RUBY_PLATFORM
|
6
|
+
end
|
7
|
+
|
8
|
+
group :test, :development do
|
9
|
+
gem 'rake'
|
10
|
+
gem 'rspec'
|
11
|
+
gem 'webmock'
|
12
|
+
end
|
13
|
+
|
14
|
+
gem 'rubysl', platforms: :rbx
|
15
|
+
|
16
|
+
# Specify your gem's dependencies in baidu-pcs.gemspec
|
17
|
+
gemspec
|
data/Guardfile
ADDED
data/HISTORY.md
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Lonre Wang
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
# Baidu SDK for Ruby [![Build Status](https://travis-ci.org/lonre/baidu-sdk-ruby.png?branch=master)](https://travis-ci.org/lonre/baidu-sdk-ruby)
|
2
|
+
|
3
|
+
Unofficial Baidu REST API SDK for Ruby.
|
4
|
+
非官方百度开放接口 Ruby 开发包。
|
5
|
+
|
6
|
+
## 安装
|
7
|
+
|
8
|
+
如果你通过 Bundler 来管理你的应用,添加如下一行到应用的 Gemfile 中:
|
9
|
+
|
10
|
+
gem 'baidu-sdk'
|
11
|
+
|
12
|
+
然后在终端执行:
|
13
|
+
|
14
|
+
$ bundle
|
15
|
+
|
16
|
+
或者直接安装:
|
17
|
+
|
18
|
+
$ gem install baidu-sdk
|
19
|
+
|
20
|
+
`baidu-sdk` 当前支持 Ruby(MRI) 版本:1.9.3, 2.0.0
|
21
|
+
|
22
|
+
## 使用简介
|
23
|
+
[baidu-sdk api doc](http://rubydoc.info/gems/baidu-sdk/frames)
|
24
|
+
|
25
|
+
本项目根据百度开放接口划分成多个独立模块,如:`Baidu::OAuth`, `Baidu::PCS`等等。
|
26
|
+
|
27
|
+
`Hash` 必须使用 `symbol` key,无论是返回值或作为参数传递给方法。
|
28
|
+
|
29
|
+
涉及到调用 Baidu REST API 并返回 Hash 时,除非特别说明,Hash 是在原始 API 返回的 JSON 结果之上 `parse(JSON.parse(resp.body, symbolize_names: true))` 而来,Hash 和 JSON 拥有相同的结构,返回的具体结构可以参考各个方法的 API Doc。如:
|
30
|
+
```ruby
|
31
|
+
# 返回的原始 JSON
|
32
|
+
# {"quota":15000000000,"used":5221166,"request_id":4043312634}
|
33
|
+
result = pcs_client.quota
|
34
|
+
result[:quota] # => 15000000000
|
35
|
+
result[:used] # => 5221166
|
36
|
+
```
|
37
|
+
|
38
|
+
## OAuth
|
39
|
+
首先确保已经在百度开发者中心创建了应用。如果还没有创建应用,请通过此链接 [百度开发者中心](http://developer.baidu.com/console) 创建一个新的应用,获取应用的 `API Key` 和 `Secret Key`。
|
40
|
+
|
41
|
+
1. 使用全局配置方式:
|
42
|
+
```ruby
|
43
|
+
Baidu.config do |c|
|
44
|
+
c.client_id = 'an_api_key'
|
45
|
+
c.client_secret = 'a_securet_key'
|
46
|
+
end
|
47
|
+
```
|
48
|
+
此全局配置为可选方式,还可以在新建 `Baidu::OAuth::Client` 实例的时候指定 `API Key` 和 `Secret Key`。
|
49
|
+
|
50
|
+
2. 创建 Baidu::OAuth::Client 实例
|
51
|
+
```ruby
|
52
|
+
require 'baidu/oauth'
|
53
|
+
client = Baidu::OAuth::Client.new
|
54
|
+
# 或者
|
55
|
+
client = Baidu::OAuth::Client.new('a_client_id', 'a_client_secret')
|
56
|
+
```
|
57
|
+
后者创建的实例受全局配置影响
|
58
|
+
3. Baidu OAuth 有四种(当前实现两种)获取 Access Token 的方式,包括 `Authorization Code`, `Implicit Grant`, `Client Credentials`, `Device`以及一个刷新方式`Refresh Token`
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
client.code_flow.authorize_url # 使用 Authorization Code 授权流程
|
62
|
+
client.code_flow.get_token # 使用 Authorization Code 授权流程
|
63
|
+
client.device_flow.user_and_device_code # 使用 Device 授权流程
|
64
|
+
client.device_flow.get_token # 使用 Device 授权流程
|
65
|
+
client.refresh # Access Token刷新方式
|
66
|
+
```
|
67
|
+
4. `Baidu::Session` 包含授权信息
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
session = client.code_flow.get_token
|
71
|
+
session = client.device_flow.get_token
|
72
|
+
...
|
73
|
+
```
|
74
|
+
|
75
|
+
### Authorization Code 授权流程示例:
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
# 1. Edit and save this snippet as code.rb
|
79
|
+
# 2. Run by: ruby coce.rb
|
80
|
+
# 3. Visit: http://localhost:4567/auth
|
81
|
+
|
82
|
+
require 'sinatra'
|
83
|
+
require 'baidu/oauth'
|
84
|
+
|
85
|
+
Baidu.config do |c|
|
86
|
+
c.client_id = ENV['BAIDU_CLIENT_ID'] # Change to your client_id
|
87
|
+
c.client_secret = ENV['BAIDU_CLIENT_SECRET'] # Change to your client_secret
|
88
|
+
end
|
89
|
+
|
90
|
+
# 使用时需要到 http://developer.baidu.com/console 将此 callback 添加到应用安全设置中
|
91
|
+
callback_uri = 'http://localhost:4567/auth/callback'
|
92
|
+
|
93
|
+
get '/' do
|
94
|
+
'Hello world!'
|
95
|
+
end
|
96
|
+
|
97
|
+
get '/auth' do
|
98
|
+
client = Baidu::OAuth::Client.new
|
99
|
+
redirect client.code_flow.authorize_url(callback_uri, confirm_login: true)
|
100
|
+
end
|
101
|
+
|
102
|
+
get '/auth/callback' do
|
103
|
+
begin
|
104
|
+
client = Baidu::OAuth::Client.new
|
105
|
+
session = client.code_flow.get_token params[:code], callback_uri
|
106
|
+
"auth success: #{session.access_token}"
|
107
|
+
rescue => e
|
108
|
+
e.to_s
|
109
|
+
end
|
110
|
+
end
|
111
|
+
```
|
112
|
+
|
113
|
+
## PCS
|
114
|
+
要使用 PCS 必须到 [百度开发者中心](http://developer.baidu.com/console) 开启指定应用的 PCS API 权限,参考 [开通PCS API权限](http://developer.baidu.com/wiki/index.php?title=docs/pcs/guide/api_approve),并获取所设置的文件目录。
|
115
|
+
|
116
|
+
文件 API 已经完全实现。API 中涉及到的文件或目录路径,都是相对于开通 PCS API 权限时所设置的文件目录。比如:上传文件时需要指定文件保存路径,假设指定为 `/mydata/hi.png`,那么在 PCS 中实际的保存路径为 `/apps/文件目录/mydata/hi.png`。
|
117
|
+
|
118
|
+
1. 全局配置
|
119
|
+
```ruby
|
120
|
+
Baidu.config do |c|
|
121
|
+
# ...
|
122
|
+
c.pcs_dir_name = 'notes' # 文件目录名称
|
123
|
+
# ...
|
124
|
+
end
|
125
|
+
```
|
126
|
+
全局配置非强制性的,可以在创建 `Baidu::PCS::Client` 实例时设置
|
127
|
+
|
128
|
+
2. 创建 Baidu::PCS::Client 实例
|
129
|
+
```ruby
|
130
|
+
require 'baidu/pcs'
|
131
|
+
client = Baidu::PCS::Client.new('token_or_session')
|
132
|
+
# 或者
|
133
|
+
client = Baidu::PCS::Client.new('token_or_session', 'notes')
|
134
|
+
```
|
135
|
+
|
136
|
+
3. API 调用
|
137
|
+
```ruby
|
138
|
+
File.open('/opt/ubuntu-12.04.3-server-amd64.iso', 'r') do |f|
|
139
|
+
client.upload(f, block_upload: true)
|
140
|
+
end
|
141
|
+
```
|
142
|
+
|
143
|
+
## 异常处理
|
144
|
+
API 的异常被封装为 `Baidu::Errors::Error`,包含三个子类:
|
145
|
+
|
146
|
+
1. `Baidu::Errors::AuthError` # Access Token 过期或未授权
|
147
|
+
2. `Baidu::Errors::ClientError` # API 返回 `400+` 错误
|
148
|
+
3. `Baidu::Errors::ServerError` # API 返回 `500+` 错误
|
149
|
+
|
150
|
+
`Baidu::Errors::Error => e`,异常的具体信息可以通过如下方式获取到:
|
151
|
+
```ruby
|
152
|
+
begin
|
153
|
+
client.list('apitest/movies')
|
154
|
+
rescue Baidu::Errors::Error => e
|
155
|
+
puts e.code # 错误代码,详情参考百度开发者中心文档
|
156
|
+
puts e.msg # 错误描述
|
157
|
+
end
|
158
|
+
```
|
159
|
+
|
160
|
+
## Copyright
|
161
|
+
MIT License. Copyright (c) 2013 Lonre Wang.
|
data/Rakefile
ADDED
data/baidu-pcs.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'baidu/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = "baidu-sdk"
|
9
|
+
spec.version = Baidu::VERSION
|
10
|
+
spec.authors = ["Lonre Wang"]
|
11
|
+
spec.email = ["me@wanglong.me"]
|
12
|
+
spec.description = %q{Unofficial Baidu REST api sdk for ruby, including OAuth, PCS, etc.}
|
13
|
+
spec.summary = %q{Unofficial Baidu REST API SDK for Ruby.}
|
14
|
+
spec.homepage = "https://github.com/lonre/baidu-sdk-ruby"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files`.split($/)
|
18
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_runtime_dependency "multipart-post", "~> 1.2"
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
24
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Baidu
|
2
|
+
module Configure
|
3
|
+
# 申请创建应用后获取的 API Key
|
4
|
+
# @return [String]
|
5
|
+
attr_accessor :client_id
|
6
|
+
|
7
|
+
# 申请创建应用后获取的 Secret Key
|
8
|
+
# @return [String]
|
9
|
+
attr_accessor :client_secret
|
10
|
+
|
11
|
+
# 用来控制是否在终端输出调试信息,主要涉及网络请求部分(Net::HTTP)
|
12
|
+
# @note 请勿在生产环境中打开
|
13
|
+
# @return [Boolean] 默认为 false
|
14
|
+
attr_accessor :debug
|
15
|
+
|
16
|
+
# 通过 block 进行 API 配置
|
17
|
+
#
|
18
|
+
# @yield [c]
|
19
|
+
# @yieldparam c [Configure]
|
20
|
+
# @return [Configure]
|
21
|
+
def config
|
22
|
+
self.debug = false # 默认 false
|
23
|
+
yield self
|
24
|
+
self
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/baidu/core.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'net/https'
|
5
|
+
require 'uri'
|
6
|
+
require 'baidu/version'
|
7
|
+
require 'baidu/session'
|
8
|
+
require 'baidu/errors/error'
|
9
|
+
require 'baidu/support/util'
|
10
|
+
require 'baidu/support/request'
|
11
|
+
require 'baidu/configure'
|
12
|
+
|
13
|
+
module Baidu
|
14
|
+
|
15
|
+
class << self
|
16
|
+
include Baidu::Configure
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Baidu
|
2
|
+
module Errors
|
3
|
+
class Error < StandardError
|
4
|
+
attr_reader :msg, :code, :response
|
5
|
+
|
6
|
+
def initialize(msg, code=nil, response=nil)
|
7
|
+
@msg, @code, @response = msg, code, response
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
arr = %w[Error]
|
12
|
+
arr << "code: #{code}" unless code.nil?
|
13
|
+
arr << "msg: #{msg}" unless msg.nil?
|
14
|
+
arr.join(' ')
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class AuthError < Error; end
|
19
|
+
class ClientError < Error; end
|
20
|
+
class ServerError < Error; end
|
21
|
+
end
|
22
|
+
end
|
data/lib/baidu/oauth.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'baidu/core'
|
2
|
+
require 'baidu/oauth/client'
|
3
|
+
|
4
|
+
module Baidu
|
5
|
+
module OAuth
|
6
|
+
SITE = 'https://openapi.baidu.com'
|
7
|
+
AUTHORIZATION_ENDPOINT = '/oauth/2.0/authorize'
|
8
|
+
TOKEN_ENDPOINT = '/oauth/2.0/token'
|
9
|
+
DEVICE_ENDPOINT = '/oauth/2.0/device/code'
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'baidu/oauth/flow/code'
|
4
|
+
require 'baidu/oauth/flow/device'
|
5
|
+
|
6
|
+
module Baidu
|
7
|
+
module OAuth
|
8
|
+
class Client
|
9
|
+
include Baidu::Support::Request
|
10
|
+
|
11
|
+
# 申请创建应用后获取的 API Key
|
12
|
+
# @return [String]
|
13
|
+
attr_accessor :client_id
|
14
|
+
# 申请创建应用后获取的 Secret Key
|
15
|
+
# @return [String]
|
16
|
+
attr_accessor :client_secret
|
17
|
+
# @private
|
18
|
+
attr_reader :site
|
19
|
+
|
20
|
+
# 创建一个 OAuth API 实例
|
21
|
+
#
|
22
|
+
# @example 如果不使用全局配置,则可以在创建新实例时指定 +client_id+ 和 +client_secret+
|
23
|
+
# client = Baidu::OAuth::Client.new('a_client_id', 'a_client_secret')
|
24
|
+
def initialize(client_id=Baidu.client_id, client_secret=Baidu.client_secret)
|
25
|
+
@client_id = client_id
|
26
|
+
@client_secret = client_secret
|
27
|
+
@site = Baidu::OAuth::SITE
|
28
|
+
end
|
29
|
+
|
30
|
+
# @!method code_flow
|
31
|
+
# 采用 Authorization Code 获取 Access Token 的授权验证流程
|
32
|
+
# @return [Flow::Code]
|
33
|
+
# @!method device_flow
|
34
|
+
# 采用 Device Code 获取 Access Token 的授权验证流程
|
35
|
+
# @return [Flow::Device]
|
36
|
+
# @see http://developer.baidu.com/wiki/index.php?title=docs/oauth/authorization Authorization Code
|
37
|
+
# @see http://developer.baidu.com/wiki/index.php?title=docs/oauth/device Device
|
38
|
+
[:code, :device].each do |flow|
|
39
|
+
define_method("#{flow}_flow".to_sym) do
|
40
|
+
Baidu::OAuth::Flow.const_get(flow.capitalize).new self
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# 刷新 Access Token
|
45
|
+
#
|
46
|
+
# @param [String] token 用于刷新 Access Token 用的 Refresh Token
|
47
|
+
# @option params [String] :scope 以空格分隔的权限列表,若不传递此参数,代表请求的数据访问操作权限与上次获取 Access Token 时一致。
|
48
|
+
# 通过 Refresh Token 刷新 Access Token 时所要求的scope权限范围必须小于等于上次获取 Access Token 时授予的权限范围。
|
49
|
+
# 关于权限的具体信息请参考“{http://developer.baidu.com/wiki/index.php?title=docs/oauth/list 权限列表}”
|
50
|
+
# @return [Baidu::Session]
|
51
|
+
# @see http://developer.baidu.com/wiki/index.php?title=docs/oauth/list 权限列表
|
52
|
+
# @see http://developer.baidu.com/wiki/index.php?title=docs/oauth/overview Access Token 生命周期
|
53
|
+
def refresh(token, params={})
|
54
|
+
body = {
|
55
|
+
grant_type: 'refresh_token',
|
56
|
+
refresh_token: token,
|
57
|
+
client_id: self.client_id,
|
58
|
+
client_secret: self.client_secret
|
59
|
+
}.update params
|
60
|
+
rest = post Baidu::OAuth::TOKEN_ENDPOINT, nil, body
|
61
|
+
return nil if rest.nil?
|
62
|
+
Baidu::Session.from rest
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|