rsteamshot 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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