pinterest-dl 1.0.3 → 2.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.
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'net/http'
5
+ require 'uri'
6
+
7
+ module PinterestDL
8
+ class Search
9
+ S_URL = 'https://www.pinterest.com/resource/BaseSearchResource/get/'
10
+
11
+ def initialize(client: PinterestDL::Client.new, config: PinterestDL.config)
12
+ @client = client
13
+ @config = config
14
+ end
15
+
16
+ def call(query, limit: 25, pages: 5)
17
+ results = []
18
+ bookmarks = nil
19
+ pages_fetched = 0
20
+
21
+ loop do
22
+ page = fetch_page(query, bookmarks: bookmarks)
23
+ pins = page[:pins]
24
+ results.concat(pins)
25
+ bookmarks = page[:bookmarks]
26
+ pages_fetched += 1
27
+
28
+ break if results.size >= limit
29
+ break if bookmarks.nil? || bookmarks.empty? || bookmarks == ['-end-']
30
+ break if pages_fetched >= pages
31
+ break if pins.empty?
32
+ end
33
+
34
+ results.first(limit)
35
+ end
36
+
37
+ private
38
+
39
+ def fetch_page(query, bookmarks: nil)
40
+ session = establish_session(query)
41
+ body = @client.get(
42
+ build_search_url(session[:source_path], query, bookmarks),
43
+ use_cache: false,
44
+ headers: search_headers(session)
45
+ )
46
+
47
+ json = safe_parse(body)
48
+ raise PinterestDL::NotFoundError, "No search results for \"#{query}\"" unless json
49
+
50
+ parse_search_response(json)
51
+ end
52
+
53
+ # Pinterest's search resource requires a session cookie + CSRF token
54
+ # obtained by first loading the human-facing search page.
55
+ def establish_session(query)
56
+ source_path = "/search/pins/?q=#{URI.encode_www_form_component(query)}"
57
+ home_uri = URI("https://www.pinterest.com#{source_path}")
58
+ home_response = @client.raw_get(home_uri.to_s)
59
+ cookies = (home_response.get_fields('set-cookie') || []).map { |c| c.split(';').first }
60
+
61
+ { source_path: source_path, home_uri: home_uri, cookies: cookies, csrf: csrf_token(cookies) }
62
+ end
63
+
64
+ def csrf_token(cookies)
65
+ token_cookie = cookies.find { |c| c.start_with?('csrftoken=') }
66
+ token_cookie ? token_cookie.split('=', 2).last : 'undefined'
67
+ end
68
+
69
+ def build_search_url(source_path, query, bookmarks)
70
+ options = { query: query, scope: 'pins', page_size: 25 }
71
+ options[:bookmarks] = bookmarks if bookmarks && !bookmarks.empty?
72
+ data = { options: options, context: {} }.to_json
73
+
74
+ res_uri = URI(S_URL)
75
+ res_uri.query = URI.encode_www_form('source_url' => source_path, 'data' => data)
76
+ res_uri.to_s
77
+ end
78
+
79
+ def search_headers(session)
80
+ cookie_header = [@config.cookies, session[:cookies].join('; ')].compact.reject(&:empty?).join('; ')
81
+
82
+ {
83
+ 'Accept' => 'application/json, text/javascript, */*, q=0.01',
84
+ 'X-Requested-With' => 'XMLHttpRequest',
85
+ 'X-Pinterest-PWS-Handler' => 'www/search/[scope].js',
86
+ 'X-CSRFToken' => session[:csrf],
87
+ 'Referer' => session[:home_uri].to_s,
88
+ 'Cookie' => cookie_header
89
+ }
90
+ end
91
+
92
+ def parse_search_response(json)
93
+ data_node = json.dig('resource_response', 'data') || {}
94
+ raw_results = data_node['results'] || []
95
+ next_bookmarks = json.dig('resource_response', 'bookmark') ||
96
+ json.dig('resource', 'options', 'bookmarks')
97
+
98
+ { pins: raw_results.filter_map { |r| build_pin(r) }, bookmarks: Array(next_bookmarks) }
99
+ end
100
+
101
+ def build_pin(raw)
102
+ id = raw['id']
103
+ return nil unless id
104
+
105
+ {
106
+ id: id,
107
+ pin_url: "https://www.pinterest.com/pin/#{id}/",
108
+ title: raw['title'] || raw['grid_title'],
109
+ description: raw['description'],
110
+ creator: pin_creator(raw),
111
+ image_url: pin_image_url(raw),
112
+ video_url: first_video_url(raw)
113
+ }
114
+ end
115
+
116
+ def pin_creator(raw)
117
+ raw.dig('pinner', 'username') || raw.dig('pinner', 'full_name')
118
+ end
119
+
120
+ def pin_image_url(raw)
121
+ raw.dig('images', 'orig', 'url') || fallback_image_url(raw)
122
+ end
123
+
124
+ def fallback_image_url(raw)
125
+ images = raw['images']
126
+ return nil unless images
127
+
128
+ images.values.filter_map { |v| v['url'] if v.is_a?(Hash) }.last
129
+ end
130
+
131
+ def first_video_url(raw)
132
+ video_list = raw.dig('videos', 'video_list')
133
+ return nil unless video_list
134
+
135
+ video_list.values.first&.dig('url')
136
+ end
137
+
138
+ def safe_parse(body)
139
+ JSON.parse(body)
140
+ rescue JSON::ParserError, TypeError
141
+ nil
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Monji024
4
+ module PinterestDL
5
+ VERSION = '2.0.1'
6
+ end
metadata CHANGED
@@ -1,28 +1,50 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pinterest-dl
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Monji
8
+ autorequire:
8
9
  bindir: bin
9
10
  cert_chain: []
10
- date: 2026-07-28 00:00:00.000000000 Z
11
+ date: 2026-08-28 00:00:00.000000000 Z
11
12
  dependencies: []
12
- description: Simple Pinterest Downloader Tool:)
13
+ description: A Ruby toolkit for getting direct image and video URLs from Pinterest
14
+ pins, searching pins, fetching board contents, and downloading media to disk — as
15
+ a library or a CLI.
13
16
  email: hoseinmonjiofficial@gmail.com
14
- executables: []
17
+ executables:
18
+ - pinterest-dl
15
19
  extensions: []
16
20
  extra_rdoc_files: []
17
21
  files:
22
+ - CHANGELOG.md
23
+ - LICENSE
18
24
  - README.md
25
+ - bin/pinterest-dl
19
26
  - lib/pinterest-dl.rb
27
+ - lib/pinterest_dl/api.rb
28
+ - lib/pinterest_dl/board.rb
29
+ - lib/pinterest_dl/client.rb
30
+ - lib/pinterest_dl/configuration.rb
31
+ - lib/pinterest_dl/downloader.rb
32
+ - lib/pinterest_dl/errors.rb
33
+ - lib/pinterest_dl/extractors/image.rb
34
+ - lib/pinterest_dl/extractors/video.rb
35
+ - lib/pinterest_dl/progress_bar.rb
36
+ - lib/pinterest_dl/search.rb
37
+ - lib/pinterest_dl/version.rb
20
38
  homepage: https://github.com/monji024/pinterest-dl
21
39
  licenses:
22
- - MIT
40
+ - WTFPL
23
41
  metadata:
24
42
  source_code_uri: https://github.com/monji024/pinterest-dl
25
43
  bug_tracker_uri: https://github.com/monji024/pinterest-dl/issues
44
+ changelog_uri: https://github.com/monji024/pinterest-dl/blob/main/CHANGELOG.md
45
+ rubygems_mfa_required: 'true'
46
+ documentation_uri: https://rubydoc.info/gems/pinterest-dl
47
+ post_install_message:
26
48
  rdoc_options: []
27
49
  require_paths:
28
50
  - lib
@@ -30,14 +52,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
30
52
  requirements:
31
53
  - - ">="
32
54
  - !ruby/object:Gem::Version
33
- version: 2.5.0
55
+ version: 2.7.0
34
56
  required_rubygems_version: !ruby/object:Gem::Requirement
35
57
  requirements:
36
58
  - - ">="
37
59
  - !ruby/object:Gem::Version
38
60
  version: '0'
39
61
  requirements: []
40
- rubygems_version: 3.6.3
62
+ rubygems_version: 3.3.15
63
+ signing_key:
41
64
  specification_version: 4
42
- summary: Extract direct image/video/search URLs from Pinterest!!!
65
+ summary: Extract direct image/video URLs, search, and download from Pinterest
43
66
  test_files: []