feed2gram 1.1.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 186b11421ce39cc03f041f010e695fbd295edb0631235d80545e42928fe6edca
4
- data.tar.gz: 688601d45092f6f1c57c9871af2b337c7df93f441ccefc6b787e01169a8bd236
3
+ metadata.gz: 6be6dddfdc2c228de285536b0fbefd7a5f1d34d864fd625e21c996307cc439f1
4
+ data.tar.gz: 35509d9f7bfacf70bce7ce37fdba2ddae9bb783171a561aad80b27b5ba9b0546
5
5
  SHA512:
6
- metadata.gz: 4682d5d13f8443ebb0cdc853394fb9e8a0c8677a0e4850daaa0df8feb54d9fcc73cc64eb5c41368dd14c958e8a5be25fe50e2c8a006602d66751161c411c9e13
7
- data.tar.gz: 7ca4d2096fa0b0b60df85c0f902f312ab27083a2ec9b7b0590951315370b3dd74fee02c92781ebdc7347945a5dc7142ac9f1204cbfe468125bd523c4b5509964
6
+ metadata.gz: cf84920a3113a0bbe9c73790171e8216846aa4f7d66edeabd4ae9f58b71857a65c81630f1cc5eab094b1fe246ae5069a6086e2a798d39f9a925e3c4c2e3951cb
7
+ data.tar.gz: 3176d62ca5a41b2de36cf695771579de800fb3346ae83aa31d51d0716b780afb29dc1a49a268d2c32caf14d351120fcf7fad533e828ce38566089d446bcec63f
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## [1.2.1]
2
+
3
+ * Add `SECONDS_PER_UPLOAD_CHECK` and `MAX_UPLOAD_STATUS_CHECKS` env vars
4
+
5
+ ## [1.2.0]
6
+
7
+ * Add support for the `cover_url` property for reel posts by way of a
8
+ `data-cover-url` attribute on the `<img>` tag of single-video posts..
9
+
1
10
  ## [1.1.0]
2
11
 
3
12
  * Add support for videos and stories, including:
data/README.md CHANGED
@@ -96,6 +96,14 @@ Usage: feed2gram [options]
96
96
  --populate-cache Populate the cache file with any posts found in the feed WITHOUT posting them to Instagram
97
97
  ```
98
98
 
99
+ ## Environment variables
100
+
101
+ These environment variables can be set to augment the gem's behavior:
102
+
103
+ * `SECONDS_PER_UPLOAD_CHECK` - when uploading video, feed2gram must wait until
104
+ the [status code](https://tiagogrosso.github.io/instagram-graph-api-lib/enums/CONTAINER_STATUS_CODE.html) on the media indicates it is published. This variable determines how many seconds to wait between each check (defaults to 30 seconds). Shortening this value can lead to hitting one's hourly rate limit
105
+ * `MAX_UPLOAD_STATUS_CHECKS` - how many status checks to perform before giving up on a piece of media and calling the post failed. Unfortunately, Facebook's servers can take anywhere from 15 seconds to 15 hours to download and process even trivially small videos, so GLHF
106
+
99
107
  ## Formatting your Atom feed's HTML
100
108
 
101
109
  feed2gram uses the first `<figure>` element to generate each Instagram post. That `<figure>` can contain one or more `<img>` tags and one `<figcaption>` tag, which will be used as the post's image(s) and caption, respectively.
@@ -108,6 +116,7 @@ Some things to keep in mind:
108
116
  * If one `<img>` tag is present, a single photo post will be created. If there are more, a [carousel post](https://developers.facebook.com/docs/instagram-api/guides/content-publishing/#carousel-posts) will be created
109
117
  * Because Facebook's servers actually _download your image_ as opposed to receiving them as uploads via the API, every `<img>` tag's `src` attribute must be set to a publicly-reachable, fully-qualified URL
110
118
  * To post videos, stories, or reels, set the `data-media-type` attribute on the `<img>` tag to `video` or `image` (a media type of `image` will be assumed by default if left unspecified). Note that while `image` and `video` media may be interspersed throughout a carousel
119
+ * For video (reel) posts containing a single video, you can set `data-cover-url` on the `<img>` tag to a publicly-available URL and the Instagram API will use it as a custom thumbnail for the reel
111
120
  * For carousel posts, the aspect ratio of the first image determines the aspect ratio of the rest, so be mindful of how you order the images based on how you want them to appear in the app
112
121
  * Only one caption will be published, regardless of whether it's a single photo post or a carousel
113
122
  * The caption limit is 2200 characters, so feed2gram will truncate it if necessary
data/example/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "rackup"
4
+ gem "sinatra"
5
+
6
+ gem "feed2gram", path: ".."
@@ -0,0 +1,59 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ feed2gram (1.2.1)
5
+ nokogiri (~> 1.15)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ base64 (0.2.0)
11
+ mustermann (3.0.0)
12
+ ruby2_keywords (~> 0.0.1)
13
+ nokogiri (1.16.2-aarch64-linux)
14
+ racc (~> 1.4)
15
+ nokogiri (1.16.2-arm-linux)
16
+ racc (~> 1.4)
17
+ nokogiri (1.16.2-arm64-darwin)
18
+ racc (~> 1.4)
19
+ nokogiri (1.16.2-x86-linux)
20
+ racc (~> 1.4)
21
+ nokogiri (1.16.2-x86_64-darwin)
22
+ racc (~> 1.4)
23
+ nokogiri (1.16.2-x86_64-linux)
24
+ racc (~> 1.4)
25
+ racc (1.7.3)
26
+ rack (3.0.9)
27
+ rack-protection (4.0.0)
28
+ base64 (>= 0.1.0)
29
+ rack (>= 3.0.0, < 4)
30
+ rack-session (2.0.0)
31
+ rack (>= 3.0.0)
32
+ rackup (2.1.0)
33
+ rack (>= 3)
34
+ webrick (~> 1.8)
35
+ ruby2_keywords (0.0.5)
36
+ sinatra (4.0.0)
37
+ mustermann (~> 3.0)
38
+ rack (>= 3.0.0, < 4)
39
+ rack-protection (= 4.0.0)
40
+ rack-session (>= 2.0.0, < 3)
41
+ tilt (~> 2.0)
42
+ tilt (2.3.0)
43
+ webrick (1.8.1)
44
+
45
+ PLATFORMS
46
+ aarch64-linux
47
+ arm-linux
48
+ arm64-darwin
49
+ x86-linux
50
+ x86_64-darwin
51
+ x86_64-linux
52
+
53
+ DEPENDENCIES
54
+ feed2gram!
55
+ rackup
56
+ sinatra
57
+
58
+ BUNDLED WITH
59
+ 2.5.4
@@ -0,0 +1,38 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-us">
3
+ <id>https://gram.betterwithbecky.com/syndications/grams</id>
4
+ <title>Beckygram</title>
5
+ <updated>2024-02-09T12:41:16Z</updated>
6
+ <author>
7
+ <name>Becky Searls</name>
8
+ <email>becky@betterwithbecky.com</email>
9
+ </author>
10
+ <link href="https://gram.betterwithbecky.com/" rel="alternate" type="text/html" title="HTML"/>
11
+ <link href="https://gram.betterwithbecky.com/syndications/grams" rel="self" type="application/atom+xml" title="Grams"/>
12
+ <category term="Fitness"/>
13
+ <generator uri="https://rubyonrails.org/" version="0.119.0">
14
+ Ruby on Rails </generator>
15
+ <icon>https://gram.betterwithbecky.com/favicon.ico</icon>
16
+ <logo>https://static-cdn.betterwithbecky.com/assets/logo-fa728624cb9c92f3e052ee6b58a653d8e64fda32.png</logo>
17
+ <rights>Copyright Build with Becky LLC. All rights reserved.</rights>
18
+ <subtitle>What you're getting when you get Better with Becky.</subtitle>
19
+ <entry>
20
+ <id>https://gram.betterwithbecky.com/posts/1/whatever</id>
21
+ <title type="text">Test Title</title>
22
+ <link href="https://gram.betterwithbecky.com/posts/1/whatever" rel="alternate" type="text/html"/>
23
+ <author>
24
+ <name>Becky Searls</name>
25
+ <email>becky@example.com</email>
26
+ </author>
27
+ <published>2024-02-04T14:59:00Z</published>
28
+ <updated>2024-02-09T12:41:16Z</updated>
29
+ <content type="html"><![CDATA[
30
+ <figure data-post-type="post">
31
+ <img data-media-type="video" data-cover-url="https://upload.wikimedia.org/wikipedia/commons/7/7c/Aspect_ratio_16_9_example.jpg" src="https://static.videezy.com/system/resources/previews/000/032/359/original/MM008645___BOUNCING_FRUIT_009___1080p___phantom.mp4"/>"
32
+ <figcaption>
33
+ Nothing to see here. Just a test post.
34
+ </figcaption>
35
+ </figure>
36
+ ]]></content>
37
+ </entry>
38
+ </feed>
data/example/server.rb ADDED
@@ -0,0 +1,9 @@
1
+ require "sinatra"
2
+
3
+ feed_file = ARGV[0] || "sample.xml"
4
+ raise "Usage: script/server <path/to/xml/file>" unless File.exist?(feed_file)
5
+ puts "Hosting '#{feed_file}' on port 4567"
6
+
7
+ get "/" do
8
+ send_file feed_file
9
+ end
@@ -2,7 +2,7 @@ require "nokogiri"
2
2
  require "open-uri"
3
3
 
4
4
  module Feed2Gram
5
- Media = Struct.new(:media_type, :url, keyword_init: true) do
5
+ Media = Struct.new(:media_type, :url, :cover_url, keyword_init: true) do
6
6
  def video?
7
7
  media_type == "VIDEO"
8
8
  end
@@ -17,7 +17,8 @@ module Feed2Gram
17
17
  medias = html.xpath("//figure[1]/img").map { |img|
18
18
  Media.new(
19
19
  media_type: (img["data-media-type"] || "image").upcase,
20
- url: img["src"]
20
+ url: img["src"],
21
+ cover_url: img["data-cover-url"]
21
22
  )
22
23
  }
23
24
 
@@ -33,6 +33,7 @@ module Feed2Gram
33
33
  :media_type => post.media_type,
34
34
  :caption => post.caption,
35
35
  :access_token => config.access_token,
36
+ :cover_url => media.cover_url,
36
37
  media.video? ? :video_url : :image_url => media.url
37
38
  }.compact)[:id]
38
39
 
@@ -80,12 +81,14 @@ module Feed2Gram
80
81
  Result.new(post: post, status: :posted)
81
82
  end
82
83
 
84
+ SECONDS_PER_UPLOAD_CHECK = ENV.fetch("SECONDS_PER_UPLOAD_CHECK") { 30 }
85
+ MAX_UPLOAD_STATUS_CHECKS = ENV.fetch("MAX_UPLOAD_STATUS_CHECKS") { 100 }
83
86
  # Good ol' loop-and-sleep. Haven't loop do'd in a while
84
87
  def wait_for_media_to_upload!(url, container_id, config, options)
85
88
  wait_attempts = 0
86
89
  loop do
87
- if wait_attempts > 90
88
- warn "Giving up waiting for media to upload after waiting 120 seconds: #{url}"
90
+ if wait_attempts > MAX_UPLOAD_STATUS_CHECKS
91
+ warn "Giving up waiting for media to upload after waiting #{SECONDS_PER_UPLOAD_CHECK * MAX_UPLOAD_STATUS_CHECKS} seconds: #{url}"
89
92
  break
90
93
  end
91
94
 
@@ -93,12 +96,12 @@ module Feed2Gram
93
96
  fields: "status_code",
94
97
  access_token: config.access_token
95
98
  })
96
- puts "Upload status #{res[:status_code]} after #{wait_attempts + 1} check for #{url}" if options.verbose
99
+ puts "Upload status #{res[:status_code]} after waiting #{wait_attempts * SECONDS_PER_UPLOAD_CHECK} seconds for IG to download #{url}" if options.verbose
97
100
  if res[:status_code] == "FINISHED"
98
101
  break
99
102
  elsif res[:status_code] == "IN_PROGRESS"
100
103
  wait_attempts += 1
101
- sleep 1
104
+ sleep SECONDS_PER_UPLOAD_CHECK
102
105
  else
103
106
  warn "Unexpected status code (#{res[:status_code]}) uploading: #{url}"
104
107
  break
@@ -1,3 +1,3 @@
1
1
  module Feed2Gram
2
- VERSION = "1.1.0"
2
+ VERSION = "1.2.1"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: feed2gram
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Searls
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-11-26 00:00:00.000000000 Z
11
+ date: 2024-02-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -38,6 +38,10 @@ files:
38
38
  - LICENSE.txt
39
39
  - README.md
40
40
  - Rakefile
41
+ - example/Gemfile
42
+ - example/Gemfile.lock
43
+ - example/sample.xml
44
+ - example/server.rb
41
45
  - exe/feed2gram
42
46
  - lib/feed2gram.rb
43
47
  - lib/feed2gram/filters_posts.rb
@@ -73,7 +77,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
73
77
  - !ruby/object:Gem::Version
74
78
  version: '0'
75
79
  requirements: []
76
- rubygems_version: 3.4.21
80
+ rubygems_version: 3.5.3
77
81
  signing_key:
78
82
  specification_version: 4
79
83
  summary: Reads an Atom feed and posts its entries to Instagram