From 3b667cba8c77c75d76f93f0a9a893811819fad23 Mon Sep 17 00:00:00 2001 From: Dillon Date: Fri, 7 Jun 2024 19:13:51 -0500 Subject: [PATCH] added activity photos back --- CHANGELOG.md | 2 + Gemfile | 1 + README.md | 26 +++++ UPGRADING.md | 2 + lib/strava/api/endpoints/activities.rb | 16 +++ .../strava/client/activity_photos.yml | 109 +++++++++++++++--- .../activities/activity_photos_spec.rb | 32 +++++ 7 files changed, 174 insertions(+), 14 deletions(-) create mode 100644 spec/strava/api/client/endpoints/activities/activity_photos_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index bd88376..0363128 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ### 2.1.1 (Next) +* [#84](https://github.com/dblock/strava-ruby-client/pull/84): Adds back activity photos (finished PR #83) - [@simonneutert](https://github.com/simonneutert) +* [#83](https://github.com/dblock/strava-ruby-client/pull/83): Adds back activity photos - [@dillon-co](https://github.com/dillon-co) * Your contribution here. ### 2.1.0 (2024/3/20) diff --git a/Gemfile b/Gemfile index 5797402..e4136ac 100755 --- a/Gemfile +++ b/Gemfile @@ -6,6 +6,7 @@ gemspec group :development, :test do gem 'dotenv' + gem 'faraday-retry' gem 'gpx' gem 'multi_xml' gem 'polylines' diff --git a/README.md b/README.md index a468760..c9dad2c 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ Unlike other clients, including [strava-api-v3](https://github.com/jaredholdcrof - [Activities](#activities) - [Create an Activity](#create-an-activity) - [Get Activity](#get-activity) + - [List Activity Photos](#list-activity-photos) - [List Activity Comments](#list-activity-comments) - [List Activity Kudoers](#list-activity-kudoers) - [List Activity Laps](#list-activity-laps) @@ -149,6 +150,31 @@ google_image_url = "https://maps.googleapis.com/maps/api/staticmap?maptype=roadm See [Strava::Models::Map](lib/strava/models/map.rb) for all available properties. +#### List Activity Photos + +Returns the photos on the given activity. This API is undocumented in Strava's docs. But there is a discussion in the [strava community hub](https://communityhub.strava.com/t5/developer-discussions/download-all-photos-of-my-own-activities/m-p/11262). + +```ruby +photos = client.activity_photos(1982980795) # => Array[Strava::Models::Photo] +# in order to request a certain size (by default it will return the biggest size possible): +# photos = client.activity_photos(1982980795, {size: 1920}) # => Array[Strava::Models::Photo] + +photo = photos.first # => Strava::Models::Photo + +photo.id # => nil +photo.unique_id # => '65889142-538D-4EE5-96F5-3DC3B773B1E3' +photo.urls # => { '0' => 'https://dgtzuqphqg23d.cloudfront.net/eb4DMJ2hJW3k_g9URZEMfaJ8rZfHagrNlZRuEZz0osU-29x64.jpg' } +photo.athlete_id # => 26_462_176 +photo.activity_id # => 1_946_417_534 +photo.activity_name # => 'TCS NYC Marathon 2018' +photo.created_at # => Time +photo.uploaded_at # => Time +photo.sizes # => { '0' => [29, 64] } +photo.default_photo # => false +``` + +See [Strava::Models::Photo](lib/strava/models/photo.rb) for all available properties. + #### List Activity Comments Returns the comments on the given activity. diff --git a/UPGRADING.md b/UPGRADING.md index af5517b..780a80b 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -25,6 +25,8 @@ client = Strava::Api::Client.new(ca_file: OpenSSL::X509::DEFAULT_CERT_FILE, ca_p - Uploading a file using `create_upload` requires you to check its process, using `client.updload('your-unique-upload-id')`. A successful upload does just mean, that the file was accepted. The Processing of the file is an independent process on Strava's side. From now on, `client.updload('your-unique-upload-id')` will raise `Strava::Errors::UploadError`, if Strava failed processing the file, e.g. it being a duplicate. - Exceeded Ratelimits (HTTP Status: 429) do now raise a customized Error `Strava::Errors::RatelimitError`. You can use the `Strava::Api::Ratelimit` object coming with the error, for further inspection of your current ratelimits. - The method `Client#activity_photos` to retrieve an activity's photos has been removed. The Strava API offers no official support for this. See [#76](https://github.com/dblock/strava-ruby-client/issues/76) for details. +- The method `Client#activity_photos` to retrieve an activity's photos has been added back. The Strava API does actually offer undocumented support for this. It's just finicky. See [this discussion](https://communityhub.strava.com/t5/developer-discussions/download-all-photos-of-my-own-activities/m-p/11262) for details. + ### Upgrading to >= 1.0.0 diff --git a/lib/strava/api/endpoints/activities.rb b/lib/strava/api/endpoints/activities.rb index be9ad9c..2f21948 100644 --- a/lib/strava/api/endpoints/activities.rb +++ b/lib/strava/api/endpoints/activities.rb @@ -37,6 +37,22 @@ def activity_comments(id_or_options, options = {}, &block) paginate "activities/#{id}/comments", options, Strava::Models::Comment, &block end + # + # List activity photos. + # + # @option options [String] :id + # Activity id. + # @option options [Integer] :page + # Page number. + # @option options [Integer] :per_page + # Number of items per page. Defaults to 30. + # + def activity_photos(id_or_options, options = {}, &block) + id, options = parse_args(id_or_options, options) + options[:size] = 5000 unless options[:size] # to retrieve full size photos + paginate "activities/#{id}/photos", options, Strava::Models::Photo, &block + end + # # List activity kudoers. # diff --git a/spec/fixtures/strava/client/activity_photos.yml b/spec/fixtures/strava/client/activity_photos.yml index a6f52e4..918251f 100644 --- a/spec/fixtures/strava/client/activity_photos.yml +++ b/spec/fixtures/strava/client/activity_photos.yml @@ -2,7 +2,7 @@ http_interactions: - request: method: get - uri: https://www.strava.com/api/v3/activities/3958491750/photos + uri: https://www.strava.com/api/v3/activities/7287327028/photos?size=5000 body: encoding: US-ASCII string: '' @@ -12,7 +12,7 @@ http_interactions: Accept: - application/json; charset=utf-8 User-Agent: - - Strava Ruby Client/0.5.0 + - Strava Ruby Client/2.1.1 Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 response: @@ -27,20 +27,97 @@ http_interactions: Connection: - keep-alive Date: - - Tue, 02 Aug 2022 09:02:58 GMT + - Mon, 16 Sep 2024 09:55:25 GMT + X-Envoy-Upstream-Service-Time: + - '66' + Server: + - istio-envoy Via: - - 1.1 linkerd, 1.1 linkerd, 1.1 c3b74c81fdcb7942211a6c721efa13fc.cloudfront.net - (CloudFront) + - 1.1 linkerd, 1.1 29051585a13addd312c8ac9d527433c6.cloudfront.net (CloudFront) Etag: - - W/"8d63318cdfdefbd4442f099722a59f7b" + - W/"cbeec23872d5a48d286f8fba6d505930" Vary: - - Origin + - Accept, Origin + Status: + - 200 OK + X-Request-Id: + - 884a63b9-1047-4a4c-bf2e-05db3a2e109b + Cache-Control: + - max-age=0, private, must-revalidate + Referrer-Policy: + - strict-origin-when-cross-origin + X-Frame-Options: + - DENY + X-Xss-Protection: + - 1; mode=block + X-Ratelimit-Limit: + - '600,6000' + X-Ratelimit-Usage: + - '11,11' + X-Download-Options: + - noopen + X-Readratelimit-Limit: + - '300,3000' + X-Readratelimit-Usage: + - '11,11' + X-Content-Type-Options: + - nosniff + X-Permitted-Cross-Domain-Policies: + - none + X-Cache: + - Miss from cloudfront + X-Amz-Cf-Pop: + - FRA2-C1 + X-Amz-Cf-Id: + - xgv_HWH_sACuA0be_EzGGbd1Q8gXbPuOaVpMLouNzD58d5EpeIUWRA== + body: + encoding: UTF-8 + string: "[{\"unique_id\":\"f5ebd6e7-8c87-4478-86ce-ce5cf31cf519\",\"athlete_id\":24776507,\"activity_id\":7287327028,\"activity_name\":\"Die + Rückkehr der Cornichons! \U0001F952\",\"post_id\":null,\"resource_state\":2,\"caption\":\"\",\"type\":1,\"source\":1,\"status\":3,\"uploaded_at\":\"2022-06-10T20:41:21Z\",\"created_at\":\"2022-06-10T20:41:03Z\",\"created_at_local\":\"2022-06-10T22:41:03Z\",\"urls\":{\"5000\":\"https://dgtzuqphqg23d.cloudfront.net/3wt2DyGHKHX6gJSXzpAcVdEI1QE2luP9xoDLD0CX2w4-2048x1536.jpg\"},\"placeholder_image\":null,\"sizes\":{\"5000\":[2048,1536]},\"default_photo\":true,\"cursor\":null},{\"unique_id\":\"67eba19c-c8d1-4cc7-b910-b1b4755eccd1\",\"athlete_id\":24776507,\"activity_id\":7287327028,\"activity_name\":\"Die + Rückkehr der Cornichons! \U0001F952\",\"post_id\":null,\"resource_state\":2,\"caption\":\"\",\"type\":1,\"source\":1,\"status\":3,\"uploaded_at\":\"2022-06-10T20:41:20Z\",\"created_at\":\"2022-06-10T20:41:02Z\",\"created_at_local\":\"2022-06-10T22:41:02Z\",\"urls\":{\"5000\":\"https://dgtzuqphqg23d.cloudfront.net/WS55P6XH57QMUGn9-b7cpBQRc9XeDDsLXREDaCKrjig-2048x1536.jpg\"},\"placeholder_image\":null,\"sizes\":{\"5000\":[2048,1536]},\"default_photo\":false,\"cursor\":null}]" + recorded_at: Mon, 16 Sep 2024 09:55:25 GMT +- request: + method: get + uri: https://www.strava.com/api/v3/activities/3958491750/photos?size=5000 + body: + encoding: US-ASCII + string: '' + headers: + Authorization: + - Bearer access-token + Accept: + - application/json; charset=utf-8 + User-Agent: + - Strava Ruby Client/2.1.1 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Date: + - Mon, 16 Sep 2024 09:55:25 GMT + X-Envoy-Upstream-Service-Time: + - '64' Server: - - nginx/1.21.3 + - istio-envoy + Via: + - 1.1 linkerd, 1.1 41f60102fc29156bc5001d6646f75c02.cloudfront.net (CloudFront) + Etag: + - W/"d465bb01ee133f9d1241c661cd82d157" + Vary: + - Accept, Origin Status: - 200 OK X-Request-Id: - - 94fea007-f7eb-4d91-b96a-357dce973869 + - bad29383-2397-4db2-932c-305d873eea17 Cache-Control: - max-age=0, private, must-revalidate Referrer-Policy: @@ -50,11 +127,15 @@ http_interactions: X-Xss-Protection: - 1; mode=block X-Ratelimit-Limit: - - '600,30000' + - '600,6000' X-Ratelimit-Usage: - - '3,120' + - '12,12' X-Download-Options: - noopen + X-Readratelimit-Limit: + - '300,3000' + X-Readratelimit-Usage: + - '12,12' X-Content-Type-Options: - nosniff X-Permitted-Cross-Domain-Policies: @@ -64,10 +145,10 @@ http_interactions: X-Amz-Cf-Pop: - FRA2-C1 X-Amz-Cf-Id: - - S9fUklRRJ9q4KxM85gTkQhvbysmL7UwkHUDCPGb29WI-Tb_5PrtL2w== + - 4axSzTVcQ3a-X0P71eTCZXFuqvWKSkOPsjboHqYLv4L9UdP_IJwn_g== body: encoding: UTF-8 string: "[{\"unique_id\":\"F775717B-D1C1-443A-AD99-3D9A80FF11C9\",\"athlete_id\":24776507,\"activity_id\":3958491750,\"activity_name\":\"Bitche, - please \U0001F973\",\"post_id\":null,\"resource_state\":2,\"caption\":\"\",\"type\":1,\"source\":1,\"status\":3,\"uploaded_at\":\"2020-08-24T11:50:25Z\",\"created_at\":\"2020-08-24T09:33:05Z\",\"created_at_local\":\"2020-08-24T11:33:05Z\",\"urls\":{\"1800\":\"https://d3nn82uaxijpm6.cloudfront.net/assets/media/placeholder-photo@4x-13b0b44cfa828acc8b95d8dc4b8157d87666aa1ea8ef814c6ec36cd542d2b756.png\"},\"sizes\":{\"1800\":[1372,1000]},\"default_photo\":true,\"location\":[49.049816670000006,7.4272883300000005]}]" - recorded_at: Tue, 02 Aug 2022 09:02:58 GMT + please \U0001F973\",\"post_id\":null,\"resource_state\":2,\"caption\":\"\",\"type\":1,\"source\":1,\"status\":3,\"uploaded_at\":\"2020-08-24T11:50:25Z\",\"created_at\":\"2020-08-24T09:33:05Z\",\"created_at_local\":\"2020-08-24T11:33:05Z\",\"urls\":{\"5000\":\"https://dgtzuqphqg23d.cloudfront.net/BO0H-YeNRZOfFhc0PctUheAKchsY2ll4vsagU58MNKg-2048x1536.jpg\"},\"placeholder_image\":null,\"sizes\":{\"5000\":[2048,1536]},\"default_photo\":true,\"cursor\":null,\"location\":[49.049816670000006,7.4272883300000005]}]" + recorded_at: Mon, 16 Sep 2024 09:55:25 GMT recorded_with: VCR 6.1.0 diff --git a/spec/strava/api/client/endpoints/activities/activity_photos_spec.rb b/spec/strava/api/client/endpoints/activities/activity_photos_spec.rb new file mode 100644 index 0000000..7df8533 --- /dev/null +++ b/spec/strava/api/client/endpoints/activities/activity_photos_spec.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Strava::Api::Client#activity_photos', vcr: { cassette_name: 'client/activity_photos' } do + include_context 'API client' + it 'returns activity photos' do + activity_photos = client.activity_photos(id: 7_287_327_028) + expect(activity_photos).to be_a Enumerable + expect(activity_photos.count).to eq 2 + activity_photo = activity_photos.first + expect(activity_photo.id).to eq nil + expect(activity_photo.unique_id).to eq 'f5ebd6e7-8c87-4478-86ce-ce5cf31cf519' + expect(activity_photo.urls).to eq('5000' => 'https://dgtzuqphqg23d.cloudfront.net/3wt2DyGHKHX6gJSXzpAcVdEI1QE2luP9xoDLD0CX2w4-2048x1536.jpg') + expect(activity_photo.source).to eq 1 + expect(activity_photo.athlete_id).to eq 24_776_507 + expect(activity_photo.activity_id).to eq 7_287_327_028 + expect(activity_photo.activity_name).to eq 'Die Rückkehr der Cornichons! 🥒' + expect(activity_photo.resource_state).to eq 2 + expect(activity_photo.caption).to eq '' + expect(activity_photo.created_at).to be_a Time + expect(activity_photo.created_at_local).to be_a Time + expect(activity_photo.uploaded_at).to be_a Time + expect(activity_photo.sizes).to eq('5000' => [2048, 1536]) + expect(activity_photo.default_photo).to be true + end + it 'returns activity photos by id' do + activity_photos = client.activity_photos(3_958_491_750) + expect(activity_photos).to be_a Enumerable + expect(activity_photos.count).to eq 1 + end +end