rsteamshot 0.1.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.
Files changed (74) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +5 -0
  5. data/CHANGELOG.md +5 -0
  6. data/CODE_OF_CONDUCT.md +74 -0
  7. data/Gemfile +6 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +118 -0
  10. data/Rakefile +14 -0
  11. data/bin/console +14 -0
  12. data/bin/setup +8 -0
  13. data/docs/README_md.html +233 -0
  14. data/docs/Rsteamshot/App/BadAppsFile.html +106 -0
  15. data/docs/Rsteamshot/App.html +466 -0
  16. data/docs/Rsteamshot/Screenshot.html +562 -0
  17. data/docs/Rsteamshot/ScreenshotPage.html +347 -0
  18. data/docs/Rsteamshot/ScreenshotPaginator.html +307 -0
  19. data/docs/Rsteamshot/User.html +309 -0
  20. data/docs/Rsteamshot.html +147 -0
  21. data/docs/created.rid +9 -0
  22. data/docs/css/fonts.css +167 -0
  23. data/docs/css/rdoc.css +590 -0
  24. data/docs/fonts/Lato-Light.ttf +0 -0
  25. data/docs/fonts/Lato-LightItalic.ttf +0 -0
  26. data/docs/fonts/Lato-Regular.ttf +0 -0
  27. data/docs/fonts/Lato-RegularItalic.ttf +0 -0
  28. data/docs/fonts/SourceCodePro-Bold.ttf +0 -0
  29. data/docs/fonts/SourceCodePro-Regular.ttf +0 -0
  30. data/docs/images/add.png +0 -0
  31. data/docs/images/arrow_up.png +0 -0
  32. data/docs/images/brick.png +0 -0
  33. data/docs/images/brick_link.png +0 -0
  34. data/docs/images/bug.png +0 -0
  35. data/docs/images/bullet_black.png +0 -0
  36. data/docs/images/bullet_toggle_minus.png +0 -0
  37. data/docs/images/bullet_toggle_plus.png +0 -0
  38. data/docs/images/date.png +0 -0
  39. data/docs/images/delete.png +0 -0
  40. data/docs/images/find.png +0 -0
  41. data/docs/images/loadingAnimation.gif +0 -0
  42. data/docs/images/macFFBgHack.png +0 -0
  43. data/docs/images/package.png +0 -0
  44. data/docs/images/page_green.png +0 -0
  45. data/docs/images/page_white_text.png +0 -0
  46. data/docs/images/page_white_width.png +0 -0
  47. data/docs/images/plugin.png +0 -0
  48. data/docs/images/ruby.png +0 -0
  49. data/docs/images/tag_blue.png +0 -0
  50. data/docs/images/tag_green.png +0 -0
  51. data/docs/images/transparent.png +0 -0
  52. data/docs/images/wrench.png +0 -0
  53. data/docs/images/wrench_orange.png +0 -0
  54. data/docs/images/zoom.png +0 -0
  55. data/docs/index.html +242 -0
  56. data/docs/js/darkfish.js +161 -0
  57. data/docs/js/jquery.js +4 -0
  58. data/docs/js/navigation.js +142 -0
  59. data/docs/js/navigation.js.gz +0 -0
  60. data/docs/js/search.js +109 -0
  61. data/docs/js/search_index.js +1 -0
  62. data/docs/js/search_index.js.gz +0 -0
  63. data/docs/js/searcher.js +229 -0
  64. data/docs/js/searcher.js.gz +0 -0
  65. data/docs/table_of_contents.html +179 -0
  66. data/lib/rsteamshot/app.rb +213 -0
  67. data/lib/rsteamshot/screenshot.rb +224 -0
  68. data/lib/rsteamshot/screenshot_page.rb +62 -0
  69. data/lib/rsteamshot/screenshot_paginator.rb +77 -0
  70. data/lib/rsteamshot/user.rb +77 -0
  71. data/lib/rsteamshot/version.rb +4 -0
  72. data/lib/rsteamshot.rb +15 -0
  73. data/rsteamshot.gemspec +32 -0
  74. metadata +215 -0
@@ -0,0 +1,224 @@
1
+ module Rsteamshot
2
+ # Public: Represents an image that has been uploaded to Steam of a screenshot taken
3
+ # in a Steam app.
4
+ class Screenshot
5
+ # Public: Returns a String of how the user described this screenshot, or nil.
6
+ attr_reader :title
7
+
8
+ # Public: Returns String URL to the Steam page that shows details about this screenshot.
9
+ attr_reader :details_url
10
+
11
+ # Public: Returns String URL to the full-size image.
12
+ attr_reader :full_size_url
13
+
14
+ # Public: Returns String URL to a medium-size version of the image.
15
+ attr_reader :medium_url
16
+
17
+ # Public: Returns String name of the Steam user who uploaded the screenshot.
18
+ attr_reader :user_name
19
+
20
+ # Public: Returns String URL to the profile of the Steam user who uploaded the screenshot.
21
+ attr_reader :user_url
22
+
23
+ # Public: Returns the DateTime when this screenshot was uploaded.
24
+ attr_reader :date
25
+
26
+ # Public: Returns a String describing how large the screenshot is, e.g., 0.547 MB.
27
+ attr_reader :file_size
28
+
29
+ # Public: Returns Integer pixel width of the screenshot.
30
+ attr_reader :width
31
+
32
+ # Public: Returns Integer pixel height of the screenshot.
33
+ attr_reader :height
34
+
35
+ # Public: Returns Integer count of how many people have voted for this screenshot.
36
+ attr_reader :like_count
37
+
38
+ # Public: Returns Integer count of how many comments people have left on this screenshot.
39
+ attr_reader :comment_count
40
+
41
+ # Public: Initialize a screenshot with the given attributes.
42
+ #
43
+ # attrs - the Hash of attributes for this screenshot
44
+ # :title - how the user described this screenshot
45
+ # :details_url - URL to the Steam page that shows details about this screenshot
46
+ # :full_size_url - URL to the full-size image
47
+ # :medium_url - URL to a medium-size version of the image
48
+ # :user_name - name of the Steam user who uploaded the screenshot
49
+ # :user_url - URL to the profile of the Steam user who uploaded the screenshot
50
+ # :date - the date and time when this screenshot was uploaded
51
+ # :file_size - describes how large the screenshot is, e.g., 0.547 MB
52
+ # :width - pixel width of the screenshot
53
+ # :height - pixel height of the screenshot
54
+ # :like_count - number of likes this screenshot has on Steam
55
+ # :comment_count - number of comments this screenshot has received on Steam
56
+ def initialize(attrs = {})
57
+ attrs.each { |key, value| instance_variable_set("@#{key}", value) }
58
+
59
+ fetch_details unless has_details?
60
+ end
61
+
62
+ # Public: Get a hash representation of this screenshot.
63
+ #
64
+ # Returns a Hash.
65
+ def to_h
66
+ result = { details_url: details_url }
67
+ result[:title] = title if title
68
+ result[:full_size_url] = full_size_url if full_size_url
69
+ result[:medium_url] = medium_url if medium_url
70
+ result[:user_name] = user_name if user_name
71
+ result[:user_url] = user_url if user_url
72
+ result[:date] = date if date
73
+ result[:file_size] = file_size if file_size
74
+ result[:width] = width if width
75
+ result[:height] = height if height
76
+ result[:like_count] = like_count if like_count
77
+ result[:comment_count] = comment_count if comment_count
78
+ result
79
+ end
80
+
81
+ # Public: Get a JSON representation of this screenshot.
82
+ #
83
+ # Returns a String.
84
+ def to_json
85
+ JSON.pretty_generate(to_h)
86
+ end
87
+
88
+ # Public: A detailed representation of this screenshot.
89
+ #
90
+ # Returns a String.
91
+ def inspect
92
+ self.class.name + '<' + JSON.generate(to_h) + '>'
93
+ end
94
+
95
+ private
96
+
97
+ def has_details?
98
+ !full_size_url.nil? && !medium_url.nil?
99
+ end
100
+
101
+ def fetch_details
102
+ return unless details_url
103
+
104
+ Mechanize.new.get(details_url) do |page|
105
+ link = page.at('.actualmediactn a')
106
+ @full_size_url = link['href'] if link
107
+
108
+ @medium_url = medium_url_from(page)
109
+ @like_count = like_count_from(page)
110
+ @comment_count = comment_count_from(page)
111
+
112
+ author = page.at('.creatorsBlock')
113
+ @user_name = user_name_from(author)
114
+ @user_url = user_url_from(author)
115
+
116
+ details_block = details_block_from(page)
117
+ if details_block
118
+ labels_container = details_block.at('.detailsStatsContainerLeft')
119
+ if labels_container
120
+ label_els = labels_container.search('.detailsStatLeft')
121
+ labels = label_els.map { |el| el.text.strip.gsub(/[[:space:]]\z/, '') }
122
+ end
123
+
124
+ values_container = details_block.at('.detailsStatsContainerRight')
125
+ if values_container
126
+ value_els = values_container.search('.detailsStatRight')
127
+ values = value_els.map { |el| el.text.strip.gsub(/[[:space:]]\z/, '') }
128
+ end
129
+
130
+ labelled_values = labels.zip(values).to_h
131
+ @file_size = labelled_values['File Size']
132
+ @date = date_from(labelled_values['Posted'])
133
+ @width, @height = dimensions_from(labelled_values['Size'])
134
+ end
135
+ end
136
+ end
137
+
138
+ def details_block_from(page)
139
+ details_blocks = page.search('.rightDetailsBlock')
140
+ details_blocks.detect do |details_block|
141
+ labels_container = details_block.at('.detailsStatsContainerLeft')
142
+ !labels_container.nil?
143
+ end
144
+ end
145
+
146
+ def medium_url_from(page)
147
+ img = page.at('img#ActualMedia')
148
+ return unless img
149
+
150
+ img['src']
151
+ end
152
+
153
+ def like_count_from(page)
154
+ rate_el = page.at('.rateUpCount')
155
+ return 0 unless rate_el
156
+
157
+ text = rate_el.text.strip.gsub(/[[:space:]]\z/, '')
158
+ if text.length > 0
159
+ text.to_i
160
+ else
161
+ 0
162
+ end
163
+ end
164
+
165
+ def comment_count_from(page)
166
+ header_el = page.at('.commentthread_count_label')
167
+ return 0 unless header_el
168
+
169
+ span = header_el.at('span')
170
+ return 0 unless span
171
+
172
+ text = span.text.strip.gsub(/[[:space:]]\z/, '')
173
+ if text.length > 0
174
+ text.to_i
175
+ else
176
+ 0
177
+ end
178
+ end
179
+
180
+ def user_name_from(author)
181
+ container = author.at('.friendBlockContent')
182
+ return unless container
183
+
184
+ all_text = container.text
185
+ online_status = container.at('.friendSmallText')
186
+ return all_text.strip.gsub(/[[:space:]]\z/, '') unless online_status
187
+
188
+ status_text = online_status.text
189
+ index = all_text.index(status_text)
190
+
191
+ user_name_text = if index
192
+ all_text.slice(0, index)
193
+ else
194
+ all_text
195
+ end
196
+
197
+ user_name_text.strip.gsub(/[[:space:]]\z/, '')
198
+ end
199
+
200
+ def user_url_from(author)
201
+ author_link = author.at('.friendBlockLinkOverlay')
202
+ return unless author_link
203
+
204
+ author_link['href']
205
+ end
206
+
207
+ def date_from(raw_date_str)
208
+ format = if raw_date_str.include?(',')
209
+ '%b %d, %Y @ %l:%M%P'
210
+ else
211
+ '%b %d @ %l:%M%P'
212
+ end
213
+ DateTime.strptime(raw_date_str, format)
214
+ end
215
+
216
+ def dimensions_from(dimension_str)
217
+ if dimension_str
218
+ dimension_str.split(' x ').map { |str| str.to_i }
219
+ else
220
+ []
221
+ end
222
+ end
223
+ end
224
+ end
@@ -0,0 +1,62 @@
1
+ module Rsteamshot
2
+ # Public: Represents a page of screenshots on Steam.
3
+ class ScreenshotPage
4
+ # Public: Returns the Integer number of this page.
5
+ attr_reader :number
6
+
7
+ # Public: Returns the Integer count of how many screenshots to fetch per page.
8
+ attr_reader :per_page
9
+
10
+ # Public: Returns an Array of the Rsteamshot::Screenshots found on this page.
11
+ attr_reader :screenshots
12
+
13
+ # Public: Construct a new ScreenshotPage with the given page number.
14
+ #
15
+ # number - the page number; Integer
16
+ # per_page - how many screenshots are shown on the Steam page
17
+ def initialize(number, per_page)
18
+ @number = number
19
+ @per_page = per_page
20
+ end
21
+
22
+ # Public: Check if the nth screenshot would be on this page on Steam.
23
+ #
24
+ # screenshot_number - the index of the screenshot you want to check
25
+ #
26
+ # Returns a Boolean.
27
+ def includes_screenshot?(screenshot_number)
28
+ range.cover?(screenshot_number)
29
+ end
30
+
31
+ # Public: Fetch the contents of this page from Steam.
32
+ #
33
+ # Returns a Mechanize::Page.
34
+ def fetch(base_url)
35
+ return if @screenshots # already fetched
36
+
37
+ url = with_steam_page_param(base_url)
38
+ Mechanize.new.get(url) do |html|
39
+ @screenshots = yield(html)
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def min_screenshot
46
+ (number - 1) * per_page
47
+ end
48
+
49
+ def max_screenshot
50
+ min_screenshot + per_page
51
+ end
52
+
53
+ def range
54
+ min_screenshot...max_screenshot
55
+ end
56
+
57
+ def with_steam_page_param(base_url)
58
+ joiner = base_url.include?('?') ? '&' : '?'
59
+ "#{base_url}#{joiner}p=#{number}"
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,77 @@
1
+ module Rsteamshot
2
+ # Public: Use to paginate screenshots fetched from Steam in chunks of fewer than
3
+ # 50 per page.
4
+ class ScreenshotPaginator
5
+ # Public: Construct a new ScreenshotPaginator that will process a page of HTML
6
+ # using the given lambda.
7
+ #
8
+ # process_html - a lambda that will take a Mechanize::Page and return a list of
9
+ # Rsteamshot::Screenshot instances found in that page
10
+ # max_per_page - the most screenshots that can be returned in a page, based on how many
11
+ # screenshots can be shown on the Steam page
12
+ # per_page - how many screenshots you want returned; Integer; should be less than or
13
+ # equal to max_per_page
14
+ # steam_per_page - how many screenshots will actually be on the Steam page; Integer; should be
15
+ # less than or equal to max_per_page
16
+ def initialize(process_html, max_per_page:, per_page: 10, steam_per_page:)
17
+ @process_html = process_html
18
+ @screenshot_pages = []
19
+ @max_per_page = max_per_page
20
+ @raw_per_page = per_page
21
+ @steam_per_page = normalize_per_page(steam_per_page)
22
+ end
23
+
24
+ # Public: Returns the Integer count of screenshots per page.
25
+ def per_page
26
+ @per_page = normalize_per_page(@raw_per_page)
27
+ end
28
+
29
+ # Public: Get the specified number of screenshots from the given Steam URL.
30
+ #
31
+ # page - which page of results to fetch; Integer; defaults to 1
32
+ # url - URL to a Steam page with screenshots, should not include a page parameter; String
33
+ #
34
+ # Returns an Array of Rsteamshot::Screenshots.
35
+ def screenshots(page: 1, url:)
36
+ offset = get_offset(page)
37
+ fetch_screenshots(offset, url).drop(offset).take(per_page)
38
+ end
39
+
40
+ private
41
+
42
+ def get_offset(page)
43
+ page = [page.to_i, 1].max
44
+ (page - 1) * per_page
45
+ end
46
+
47
+ def fetch_screenshots(offset, base_url)
48
+ screenshot_page = @screenshot_pages.detect { |page| page.includes_screenshot?(offset) }
49
+ fetch_necessary_screenshots(offset, base_url) unless screenshot_page
50
+ @screenshot_pages.flat_map(&:screenshots)
51
+ end
52
+
53
+ def fetch_necessary_screenshots(offset, base_url)
54
+ screenshot_page = ScreenshotPage.new(next_page_number, @steam_per_page)
55
+ screenshot_page.fetch(base_url) { |html| @process_html.(html) }
56
+ @screenshot_pages << screenshot_page
57
+
58
+ while !screenshot_page.includes_screenshot?(offset)
59
+ screenshot_page = ScreenshotPage.new(screenshot_page.number + 1, @steam_per_page)
60
+ screenshot_page.fetch(base_url) { |html| @process_html.(html) }
61
+ @screenshot_pages << screenshot_page
62
+ end
63
+ end
64
+
65
+ def next_page_number
66
+ last_page = @screenshot_pages.last
67
+ last_page ? last_page.number : 1
68
+ end
69
+
70
+ def normalize_per_page(raw_value)
71
+ value = raw_value.to_i
72
+ value = 1 if value < 1
73
+ value = @max_per_page if value > @max_per_page
74
+ value
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,77 @@
1
+ module Rsteamshot
2
+ # Public: Represents a Steam user. Used to fetch the user's screenshots they have
3
+ # uploaded to Steam.
4
+ class User
5
+ # Public: How to sort screenshots when they are being retrieved.
6
+ VALID_ORDERS = %w[newestfirst score oldestfirst].freeze
7
+
8
+ # Public: How many screenshots are shown on a user's profile per page.
9
+ STEAM_PER_PAGE = 50
10
+
11
+ # Public: Returns a String user name from a Steam user's public profile.
12
+ attr_reader :user_name
13
+
14
+ # Public: Initialize a Steam user with the given user name.
15
+ #
16
+ # user_name - a String
17
+ # per_page - how many screenshots to get in each page; defaults to 10; valid range: 1-50;
18
+ # Integer
19
+ def initialize(user_name, per_page: 10)
20
+ @user_name = user_name
21
+
22
+ process_html = ->(html) do
23
+ links_from(html).map { |link| screenshot_from(link) }
24
+ end
25
+ @paginator = ScreenshotPaginator.new(process_html, max_per_page: STEAM_PER_PAGE,
26
+ per_page: per_page, steam_per_page: STEAM_PER_PAGE)
27
+ end
28
+
29
+ # Public: Fetch a list of the user's newest uploaded screenshots.
30
+ #
31
+ # order - String specifying which screenshots should be retrieved; choose from newestfirst,
32
+ # score, and oldestfirst; defaults to newestfirst
33
+ # page - which page of results to fetch; defaults to 1; Integer
34
+ # app_id - optional Steam app ID as an Integer or String, to get screenshots from this user for
35
+ # the specified app; defaults to including all apps
36
+ #
37
+ # Returns an Array of Rsteamshot::Screenshots.
38
+ def screenshots(order: nil, page: 1, app_id: 0)
39
+ return [] unless user_name
40
+
41
+ url = steam_url(order, app_id)
42
+ @paginator.screenshots(page: page, url: url)
43
+ end
44
+
45
+ private
46
+
47
+ def links_from(html)
48
+ html.search('#image_wall .imageWallRow .profile_media_item')
49
+ end
50
+
51
+ def screenshot_from(link)
52
+ details_url = link['href']
53
+ description = link.at('.imgWallHoverDescription')
54
+ title = description ? description.text.strip : nil
55
+ Screenshot.new(title: title, details_url: details_url)
56
+ end
57
+
58
+ def steam_url(order, app_id = 0)
59
+ params = [
60
+ "appid=#{URI.escape(app_id.to_s)}",
61
+ "sort=#{sort_param(order)}",
62
+ 'browsefilter=myfiles',
63
+ 'view=grid'
64
+ ]
65
+ user_param = URI.escape(user_name)
66
+ "http://steamcommunity.com/id/#{user_param}/screenshots/?" + params.join('&')
67
+ end
68
+
69
+ def sort_param(order)
70
+ if VALID_ORDERS.include?(order)
71
+ order
72
+ else
73
+ VALID_ORDERS.first
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,4 @@
1
+ module Rsteamshot
2
+ # Public: The current version of the Rsteamshot gem.
3
+ VERSION = '0.1.1'
4
+ end
data/lib/rsteamshot.rb ADDED
@@ -0,0 +1,15 @@
1
+ require 'json'
2
+ require 'mechanize'
3
+ require 'open-uri'
4
+ require 'rsteamshot/app'
5
+ require 'rsteamshot/screenshot'
6
+ require 'rsteamshot/screenshot_page'
7
+ require 'rsteamshot/screenshot_paginator'
8
+ require 'rsteamshot/user'
9
+ require 'rsteamshot/version'
10
+ require 'uri'
11
+
12
+ # Public: Contains classes for finding screenshots uploaded by users to Steam. Screenshots
13
+ # are from Steam games (apps).
14
+ module Rsteamshot
15
+ end
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rsteamshot/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'rsteamshot'
8
+ spec.version = Rsteamshot::VERSION
9
+ spec.authors = ['Sarah Vessels']
10
+ spec.email = ['cheshire137@gmail.com']
11
+
12
+ spec.summary = %q{Get Steam screenshots for different games or users.}
13
+ spec.description = %q{Find the latest screenshots a user has uploaded to Steam, as well as the latest screenshots for specific games.}
14
+ spec.homepage = ''
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.bindir = 'exe'
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ['lib']
23
+
24
+ spec.add_development_dependency 'bundler', '~> 1.15'
25
+ spec.add_development_dependency 'rake', '~> 10.0'
26
+ spec.add_development_dependency 'rdoc', '~> 5.1.0'
27
+ spec.add_development_dependency 'rspec', '~> 3.0'
28
+ spec.add_development_dependency 'vcr', '~> 3.0.3'
29
+ spec.add_development_dependency 'webmock', '~> 3.1.0'
30
+
31
+ spec.add_runtime_dependency 'mechanize', '~> 2.7.5'
32
+ end