putio-rb 0.0.2

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.
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+ require 'putio/configurable'
3
+
4
+ describe Putio::Configurable do
5
+ it { expect(described_class).to respond_to(:keys) }
6
+
7
+ let(:klass) { Class.new { include Putio::Configurable } }
8
+ subject(:obj) { klass.new }
9
+
10
+ it { expect(obj).to respond_to(:configure) }
11
+
12
+ describe 'configuring the object' do
13
+ before do
14
+ obj.configure do
15
+ access_token = 'foobar'
16
+ api_endoint = 'http://api.put.io.test'
17
+ api_version = 'v1'
18
+ user_agent = 'putio-rb testing'
19
+ end
20
+ end
21
+
22
+ it { expect(obj.options).not_to eq(Putio::Defaults.options) }
23
+ end
24
+
25
+ describe 'reset!' do
26
+ before do
27
+ obj.configure do
28
+ access_token = 'foobar'
29
+ api_endoint = 'http://api.put.io.test'
30
+ api_version = 'v1'
31
+ user_agent = 'putio-rb testing'
32
+ end
33
+ end
34
+
35
+ it 'sets all the options back to their defaults values' do
36
+ obj.reset!
37
+ expect(obj.options).to eq(Putio::Defaults.options)
38
+ end
39
+
40
+ it 'is aliased as setup' do
41
+ expect(obj).to respond_to(:setup)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,77 @@
1
+ require 'spec_helper'
2
+ require 'putio/connection'
3
+
4
+ describe Putio::Connection do
5
+ let(:endpoint) { Putio::Defaults.api_endpoint }
6
+ subject(:connection) { described_class.new(endpoint: endpoint) }
7
+
8
+ context 'without an endpoint' do
9
+ it { expect { described_class.new }.to raise_error }
10
+ end
11
+
12
+ describe 'request' do
13
+ let(:http) { double }
14
+
15
+ context 'GET' do
16
+ it 'should query the endpoint with URL encoded params' do
17
+ stub_get = stub_request(:get, 'https://api.put.io/foo/bar?oauth_token=foobar').
18
+ with(headers: {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Ruby'}).
19
+ to_return(:status => 200, :body => "", :headers => {})
20
+
21
+ connection.request(:get, '/foo/bar', oauth_token: 'foobar')
22
+
23
+ assert_requested(stub_get)
24
+ end
25
+ end
26
+
27
+ %i{ post put delete }.each do |method|
28
+ context "#{method.upcase}" do
29
+ it 'should query the endpoint with form encoded params' do
30
+ stub_method = stub_request(method, 'https://api.put.io/foo/bar').
31
+ with(body: { oauth_token: 'foobar', foo: 'bar' },
32
+ headers: {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type'=>'application/x-www-form-urlencoded', 'User-Agent'=>'Ruby'}).
33
+ to_return(:status => 200, :body => "", :headers => {})
34
+
35
+ connection.request(method, '/foo/bar', oauth_token: 'foobar', foo: 'bar')
36
+
37
+ assert_requested(stub_method)
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ describe 'request_json' do
44
+ context 'response is 200' do
45
+ it 'returns an OpenStruct with code and body' do
46
+ stub_request(:get, "https://api.put.io/foo/bar").
47
+ with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Ruby'}).
48
+ to_return(:status => 200, :body => "{}", :headers => {})
49
+
50
+ resp = connection.request_json(:get, '/foo/bar', {})
51
+ expect(resp).to respond_to(:code)
52
+ expect(resp).to respond_to(:body)
53
+ expect(resp.body).to be_empty
54
+ end
55
+ end
56
+
57
+ context 'response is within 400 range' do
58
+ it 'fails with a message' do
59
+ stub_request(:get, "https://api.put.io/foo/bar").
60
+ with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Ruby'}).
61
+ to_return(:status => 401, :body => "{}", :headers => {})
62
+
63
+ expect { connection.request_json(:get, '/foo/bar', {}) }.to raise_error('bad request')
64
+ end
65
+ end
66
+
67
+ context 'response is within 500 range' do
68
+ it 'fails with a message' do
69
+ stub_request(:get, "https://api.put.io/foo/bar").
70
+ with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Ruby'}).
71
+ to_return(:status => 503, :body => "{}", :headers => {})
72
+
73
+ expect { connection.request_json(:get, '/foo/bar', {}) }.to raise_error('server problems')
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe Putio::Defaults do
4
+ it { expect(described_class).to respond_to(:api_endpoint) }
5
+ it { expect(described_class).to respond_to(:api_version) }
6
+ it { expect(described_class).to respond_to(:default_headers) }
7
+ it { expect(described_class).to respond_to(:user_agent) }
8
+
9
+ describe '.api_endpoint' do
10
+ it { expect(described_class.api_endpoint).to be_kind_of(String) }
11
+ it { expect(described_class.api_endpoint.to_s).to eq("https://api.put.io") }
12
+ end
13
+
14
+ describe '.api_version' do
15
+ it { expect(described_class.api_version).to be_kind_of(String) }
16
+ it { expect(described_class.api_version).to eq('v2') }
17
+ end
18
+
19
+ describe '.default_headers' do
20
+ it { expect(described_class.default_headers).to be_kind_of(Hash) }
21
+ it { expect(described_class.default_headers).to include('User-Agent' => 'putio-rb ruby client') }
22
+ it { expect(described_class.default_headers).to include('Accept' => 'application/json') }
23
+ end
24
+
25
+ describe '.options' do
26
+ it { expect(described_class.options).to be_kind_of(Hash) }
27
+ end
28
+
29
+ describe '.access_token' do
30
+ it { expect(described_class.access_token).to be_nil }
31
+
32
+ context 'configured through ENV variable' do
33
+ before { ENV['PUTIO_ACCESS_TOKEN'] = 'foobar' }
34
+ it { expect(described_class.access_token).to eq('foobar') }
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+ require 'putio/resource/file'
3
+
4
+ describe Putio::Resource::File do
5
+ subject(:file) { described_class.new }
6
+
7
+ it 'has a set of valid keys' do
8
+ expect(described_class::VALID_KEYS).not_to be_empty
9
+ end
10
+
11
+ describe 'interface' do
12
+ described_class::VALID_KEYS.each do |key|
13
+ it { expect(file).to respond_to(key) }
14
+ end
15
+ end
16
+
17
+ context 'when content_type is application/x-directory' do
18
+ before { allow(file).to receive(:content_type).and_return('application/x-directory') }
19
+ it { expect(file).to be_directory }
20
+ end
21
+
22
+ context 'when is_shared is true' do
23
+ before { allow(file).to receive(:is_shared).and_return(true) }
24
+ it { expect(file).to be_shared }
25
+ end
26
+
27
+ context 'when is_mp4_available is true' do
28
+ before { allow(file).to receive(:is_mp4_available).and_return(true) }
29
+ it { expect(file).to be_mp4_available }
30
+ end
31
+
32
+ describe 'created_at' do
33
+ before { allow(file).to receive(:created_at).and_return(Time.now) }
34
+ it { expect(file.created_at).to be_kind_of(Time) }
35
+ end
36
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe Putio do
4
+ it_behaves_like 'a configurable'
5
+
6
+ describe 'client' do
7
+ let(:access_token) { 'foobar' }
8
+
9
+ it { expect(Putio.client).to be_kind_of(Putio::Client) }
10
+
11
+ it 'should memoize the client' do
12
+ Putio.reset!
13
+ Putio.configure { access_token = access_token }
14
+ expect(Putio.client).to equal(Putio.client)
15
+ end
16
+
17
+ it 'should have a client with the same options' do
18
+ Putio.reset!
19
+ Putio.configure { access_token = access_token }
20
+ expect(Putio.client.options).to eq(Putio.options)
21
+ end
22
+ end
23
+
24
+ end
@@ -0,0 +1,11 @@
1
+ ENV["RAILS_ENV"] = 'test'
2
+
3
+ require 'putio-rb'
4
+ require 'rspec'
5
+ require 'webmock/rspec'
6
+
7
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |file| require file }
8
+
9
+ RSpec.configure do |config|
10
+ config.order = :random
11
+ end
@@ -0,0 +1,17 @@
1
+ {
2
+ "file": {
3
+ "content_type": "video/mp4",
4
+ "crc32": "c989885b",
5
+ "created_at": "2014-07-20T18:59:51",
6
+ "first_accessed_at": null,
7
+ "icon": "https://put.io/thumbnails/YlppkJJpkl9iV1mWW2pfZWVei2BeXmRaaJOTlmVeYVZdaVWZk2dljg%3D%3D.jpg",
8
+ "id": 209309563,
9
+ "is_mp4_available": false,
10
+ "is_shared": false,
11
+ "name": "BBC.The.Century.of.the.Self.4of4.Eight.People.Sipping.Wine.in.Kettering.DVDRip.x264.AAC.MVGroup.org.mp4",
12
+ "opensubtitles_hash": "40e42531b365b3c4",
13
+ "parent_id": 0,
14
+ "screenshot": "https://put.io/screenshots/YlppkJJpkl9iV1mWW2pfZWVei2BeXmRaaJOTlmVeYVZdaVWZk2dljg%3D%3D.jpg",
15
+ "size": 788132892
16
+ }
17
+ }
@@ -0,0 +1,64 @@
1
+ {
2
+ "files": [
3
+ {
4
+ "content_type": "video/mp4",
5
+ "crc32": "c989885b",
6
+ "created_at": "2014-07-20T18:59:51",
7
+ "first_accessed_at": null,
8
+ "icon": "https://put.io/thumbnails/YlppkJJpkl9iV1mWW2pfZWVei2BeXmRaaJOTlmVeYVZdaVWZk2dljg%3D%3D.jpg",
9
+ "id": 209309563,
10
+ "is_mp4_available": false,
11
+ "is_shared": false,
12
+ "name": "BBC.The.Century.of.the.Self.4of4.Eight.People.Sipping.Wine.in.Kettering.DVDRip.x264.AAC.MVGroup.org.mp4",
13
+ "opensubtitles_hash": "40e42531b365b3c4",
14
+ "parent_id": 0,
15
+ "screenshot": "https://put.io/screenshots/YlppkJJpkl9iV1mWW2pfZWVei2BeXmRaaJOTlmVeYVZdaVWZk2dljg%3D%3D.jpg",
16
+ "size": 788132892
17
+ },
18
+ {
19
+ "content_type": "application/x-directory",
20
+ "crc32": null,
21
+ "created_at": "2014-07-14T21:51:08",
22
+ "first_accessed_at": null,
23
+ "icon": "https://put.io/images/file_types/folder.png",
24
+ "id": 206159273,
25
+ "is_mp4_available": false,
26
+ "is_shared": false,
27
+ "name": "The.Zero.Theorem.2013.WEB-DL.XviD.MP3-RARBG",
28
+ "opensubtitles_hash": null,
29
+ "parent_id": 0,
30
+ "screenshot": null,
31
+ "size": 1438860547
32
+ },
33
+ {
34
+ "content_type": "video/x-flv",
35
+ "crc32": "0194f795",
36
+ "created_at": "2014-06-30T12:33:56",
37
+ "first_accessed_at": null,
38
+ "icon": "https://put.io/thumbnails/YlppjpJgY16VV16UVZleZWmPXGJYWpWKml5maGGKZV1dkYppk2GTkg%3D%3D.jpg",
39
+ "id": 201733009,
40
+ "is_mp4_available": false,
41
+ "is_shared": false,
42
+ "name": "[SURF] Bending Colours (Jordy Smith).flv",
43
+ "opensubtitles_hash": "f32d37840c3da23f",
44
+ "parent_id": 0,
45
+ "screenshot": "https://put.io/screenshots/YlppjpJgY16VV16UVZleZWmPXGJYWpWKml5maGGKZV1dkYppk2GTkg%3D%3D.jpg",
46
+ "size": 883940502
47
+ }
48
+ ],
49
+ "parent": {
50
+ "content_type": "application/x-directory",
51
+ "crc32": null,
52
+ "created_at": "2012-11-10T10:41:37",
53
+ "icon": "https://put.io/images/file_types/folder.png",
54
+ "id": 0,
55
+ "is_mp4_available": false,
56
+ "is_shared": false,
57
+ "name": "Your Files",
58
+ "opensubtitles_hash": null,
59
+ "parent_id": null,
60
+ "screenshot": null,
61
+ "size": 52335247904
62
+ },
63
+ "status": "OK"
64
+ }
@@ -0,0 +1,84 @@
1
+ shared_examples 'a configurable' do
2
+ before { subject.reset! }
3
+ after { subject.reset! }
4
+
5
+ it { expect(subject).to respond_to(:configure) }
6
+ it { expect(subject).to respond_to(:setup) }
7
+ it { expect(subject).to respond_to(:reset!) }
8
+ it { expect(subject).to respond_to(:options) }
9
+
10
+ describe 'interface' do
11
+ Putio::Configurable.keys.each do |key|
12
+ it { expect(subject).to respond_to(key) }
13
+ it { expect(subject).to respond_to("#{key}=".to_sym) }
14
+ end
15
+ end
16
+
17
+ describe 'configure' do
18
+ shared_examples 'setting instance variables' do
19
+ it 'should be different than the default values' do
20
+ expect(subject.options).not_to eq(Putio::Defaults.options)
21
+ end
22
+ it { expect(subject.access_token).to eq('foobar') }
23
+ it { expect(subject.api_endpoint).to eq('http://api.put.io.test') }
24
+ it { expect(subject.api_version).to eq('v1') }
25
+ it { expect(subject.user_agent).to eq('putio-rb testing') }
26
+ it { expect(subject.default_headers).to eq(
27
+ {
28
+ 'User-Agent' => 'putio-rb ruby client',
29
+ 'Accept' => 'application/json',
30
+ 'X-Test' => 'Yes'
31
+ }
32
+ )}
33
+ end
34
+
35
+ context 'using a block' do
36
+ before do
37
+ subject.configure do |c|
38
+ c.access_token = 'foobar'
39
+ c.api_endpoint = 'http://api.put.io.test'
40
+ c.api_version = 'v1'
41
+ c.user_agent = 'putio-rb testing'
42
+ c.default_headers.merge!({ 'X-Test' => 'Yes' })
43
+ end
44
+ end
45
+
46
+ it_behaves_like 'setting instance variables'
47
+ end
48
+
49
+ context 'using accessors' do
50
+ before do
51
+ subject.access_token = 'foobar'
52
+ subject.api_endpoint = 'http://api.put.io.test'
53
+ subject.api_version = 'v1'
54
+ subject.user_agent = 'putio-rb testing'
55
+ subject.default_headers.merge!({ 'X-Test' => 'Yes' })
56
+ end
57
+
58
+ it_behaves_like 'setting instance variables'
59
+ end
60
+ end
61
+
62
+ describe 'reset!' do
63
+ before do
64
+ subject.configure do |c|
65
+ c.access_token = 'foobar'
66
+ c.api_endpoint = 'http://api.put.io.test'
67
+ c.api_version = 'v1'
68
+ c.user_agent = 'putio-rb testing'
69
+ end
70
+ end
71
+
72
+ it 'should set the options to their defaults values' do
73
+ expect {
74
+ subject.reset!
75
+ }.to change {
76
+ subject.options
77
+ }.to(Putio::Defaults.options)
78
+ end
79
+
80
+ it 'should be aliased as setup' do
81
+ expect(subject).to respond_to(:setup)
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,145 @@
1
+ {
2
+ "status": "OK",
3
+ "transfers": [
4
+ {
5
+ "availability": null,
6
+ "callback_url": null,
7
+ "created_at": "2014-08-02T17:52:09",
8
+ "created_torrent": false,
9
+ "current_ratio": "0.00",
10
+ "down_speed": 0,
11
+ "downloaded": 0,
12
+ "error_message": null,
13
+ "estimated_time": null,
14
+ "extract": false,
15
+ "file_id": 213973037,
16
+ "finished_at": "2014-08-02T17:52:10",
17
+ "id": 19479483,
18
+ "is_private": false,
19
+ "magneturi": "magnet:?xt=urn:btih:d023782e905f44f479ffd18d82957f39c2e7b042&dn=Hercules+Reborn+%282014%29+720p+BrRip+x264+-+YIFY",
20
+ "name": "Hercules Reborn (2014) 720p BrRip x264 - YIFY",
21
+ "peers_connected": 0,
22
+ "peers_getting_from_us": 0,
23
+ "peers_sending_to_us": 0,
24
+ "percent_done": 100,
25
+ "save_parent_id": 0,
26
+ "seconds_seeding": 0,
27
+ "size": 792098589,
28
+ "source": "magnet:?xt=urn:btih:d023782e905f44f479ffd18d82957f39c2e7b042&dn=Hercules+Reborn+%282014%29+720p+BrRip+x264+-+YIFY&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=udp%3A%2F%2Ftracker.publicbt.com%3A80&tr=udp%3A%2F%2Ftracker.istole.it%3A6969&tr=udp%3A%2F%2Fopen.demonii.com%3A1337",
29
+ "status": "COMPLETED",
30
+ "status_message": "Completed 1 week ago.",
31
+ "subscription_id": null,
32
+ "torrent_link": "/v2/transfers/19479483/torrent",
33
+ "tracker_message": null,
34
+ "trackers": null,
35
+ "type": "TORRENT",
36
+ "up_speed": 0,
37
+ "uploaded": 0
38
+ },
39
+ {
40
+ "availability": null,
41
+ "callback_url": null,
42
+ "created_at": "2014-08-02T20:17:02",
43
+ "created_torrent": false,
44
+ "current_ratio": "0.00",
45
+ "down_speed": 0,
46
+ "downloaded": 0,
47
+ "error_message": null,
48
+ "estimated_time": null,
49
+ "extract": false,
50
+ "file_id": 214004461,
51
+ "finished_at": "2014-08-02T20:17:03",
52
+ "id": 19483397,
53
+ "is_private": false,
54
+ "magneturi": "magnet:?xt=urn:btih:bfead22eee242011e27a4a551dfd68bf79b93d2d&dn=Pineapple+Express+EXTENDED+%282008%29+720p+BrRip+x264+-+YIFY",
55
+ "name": "Pineapple Express EXTENDED (2008) 720p BrRip x264 - YIFY",
56
+ "peers_connected": 0,
57
+ "peers_getting_from_us": 0,
58
+ "peers_sending_to_us": 0,
59
+ "percent_done": 100,
60
+ "save_parent_id": 0,
61
+ "seconds_seeding": 0,
62
+ "size": 787193575,
63
+ "source": "magnet:?xt=urn:btih:bfead22eee242011e27a4a551dfd68bf79b93d2d&dn=Pineapple+Express+EXTENDED+%282008%29+720p+BrRip+x264+-+YIFY&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=udp%3A%2F%2Ftracker.publicbt.com%3A80&tr=udp%3A%2F%2Ftracker.istole.it%3A6969&tr=udp%3A%2F%2Fopen.demonii.com%3A1337",
64
+ "status": "COMPLETED",
65
+ "status_message": "Completed 1 week ago.",
66
+ "subscription_id": null,
67
+ "torrent_link": "/v2/transfers/19483397/torrent",
68
+ "tracker_message": null,
69
+ "trackers": null,
70
+ "type": "TORRENT",
71
+ "up_speed": 0,
72
+ "uploaded": 0
73
+ },
74
+ {
75
+ "availability": null,
76
+ "callback_url": null,
77
+ "created_at": "2014-08-02T20:18:02",
78
+ "created_torrent": false,
79
+ "current_ratio": "0.00",
80
+ "down_speed": 0,
81
+ "downloaded": 0,
82
+ "error_message": null,
83
+ "estimated_time": null,
84
+ "extract": false,
85
+ "file_id": 214004519,
86
+ "finished_at": "2014-08-02T20:18:02",
87
+ "id": 19483421,
88
+ "is_private": false,
89
+ "magneturi": "magnet:?xt=urn:btih:f9828f6d77388680734be951cc6cdff9a89c20cf&dn=22+Jump+Street+2014+TS+XviD-SUMO",
90
+ "name": "22 Jump Street 2014 TS XviD-SUMO",
91
+ "peers_connected": 0,
92
+ "peers_getting_from_us": 0,
93
+ "peers_sending_to_us": 0,
94
+ "percent_done": 100,
95
+ "save_parent_id": 0,
96
+ "seconds_seeding": 0,
97
+ "size": 740358002,
98
+ "source": "magnet:?xt=urn:btih:f9828f6d77388680734be951cc6cdff9a89c20cf&dn=22+Jump+Street+2014+TS+XviD-SUMO&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=udp%3A%2F%2Ftracker.publicbt.com%3A80&tr=udp%3A%2F%2Ftracker.istole.it%3A6969&tr=udp%3A%2F%2Fopen.demonii.com%3A1337",
99
+ "status": "COMPLETED",
100
+ "status_message": "Completed 1 week ago.",
101
+ "subscription_id": null,
102
+ "torrent_link": "/v2/transfers/19483421/torrent",
103
+ "tracker_message": null,
104
+ "trackers": null,
105
+ "type": "TORRENT",
106
+ "up_speed": 0,
107
+ "uploaded": 0
108
+ },
109
+ {
110
+ "availability": null,
111
+ "callback_url": null,
112
+ "created_at": "2014-08-02T20:20:07",
113
+ "created_torrent": false,
114
+ "current_ratio": "0.00",
115
+ "down_speed": 0,
116
+ "downloaded": 0,
117
+ "error_message": null,
118
+ "estimated_time": null,
119
+ "extract": false,
120
+ "file_id": 214004663,
121
+ "finished_at": "2014-08-02T20:20:04",
122
+ "id": 19483485,
123
+ "is_private": false,
124
+ "magneturi": "magnet:?xt=urn:btih:939717582c82fd3f75844f123b6368f8cc211859&dn=The.Expendables.3.2014.DVDSCR.Xvid-DiNGO",
125
+ "name": "The.Expendables.3.2014.DVDSCR.Xvid-DiNGO",
126
+ "peers_connected": 0,
127
+ "peers_getting_from_us": 0,
128
+ "peers_sending_to_us": 0,
129
+ "percent_done": 100,
130
+ "save_parent_id": 0,
131
+ "seconds_seeding": 0,
132
+ "size": 742522578,
133
+ "source": "magnet:?xt=urn:btih:939717582c82fd3f75844f123b6368f8cc211859&dn=The.Expendables.3.2014.DVDSCR.Xvid-DiNGO&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=udp%3A%2F%2Ftracker.publicbt.com%3A80&tr=udp%3A%2F%2Ftracker.istole.it%3A6969&tr=udp%3A%2F%2Fopen.demonii.com%3A1337",
134
+ "status": "COMPLETED",
135
+ "status_message": "Completed 1 week ago.",
136
+ "subscription_id": null,
137
+ "torrent_link": "/v2/transfers/19483485/torrent",
138
+ "tracker_message": null,
139
+ "trackers": null,
140
+ "type": "TORRENT",
141
+ "up_speed": 0,
142
+ "uploaded": 0
143
+ }
144
+ ]
145
+ }