reel_uk 1.0.0

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,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ReelUk
4
+ # Internal utility classes: Do not use
5
+ # @api private
6
+ module Internal
7
+ # Reads pages from Reel's WordPress website, built on Jacro's cinema
8
+ # plugin. No token is needed.
9
+ class Website
10
+ include Http
11
+
12
+ HOME = 'https://reel-cinemas.co.uk/'
13
+ AJAX = 'https://reel-cinemas.co.uk/wp-admin/admin-ajax.php'
14
+ # The quick booking menu's request for every showtime of a film
15
+ SHOWTIMES_ACTION = 'display_showtime_byfilmid'
16
+
17
+ # @return [String] the home page, whose cinema menu lists every venue
18
+ def home
19
+ get(HOME)
20
+ end
21
+
22
+ # @param [String] venue_url e.g. 'https://reel-cinemas.co.uk/chorley'
23
+ # @return [String] the venue's page, whose quick booking menu names
24
+ # the venue's Jacro location id
25
+ def venue(venue_url)
26
+ get("#{venue_url}/")
27
+ end
28
+
29
+ # @param [String] venue_url e.g. 'https://reel-cinemas.co.uk/chorley'
30
+ # @return [String] the next three weeks of showtimes, with labels
31
+ def whats_on(venue_url)
32
+ get("#{venue_url}/whatson/")
33
+ end
34
+
35
+ # @param [String] venue_url e.g. 'https://reel-cinemas.co.uk/chorley'
36
+ # @return [String] films not yet showing, with up to three showtimes each
37
+ def coming_soon(venue_url)
38
+ get("#{venue_url}/comingsoon/")
39
+ end
40
+
41
+ # @param [String] film_id Jacro's film id, the same at every venue
42
+ # @param [String] location Jacro's location id for the venue
43
+ # @return [String] options naming every showtime of the film at the
44
+ # venue, however far ahead
45
+ def showtimes(film_id, location)
46
+ response = post(AJAX, 'action' => SHOWTIMES_ACTION, 'select_movie' => film_id, 'movie_loc' => location)
47
+ JSON.parse(response).fetch('html', '').to_s
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,129 @@
1
+ module ReelUk
2
+ # A single screening of a film on the Reel Cinemas website
3
+ class Performance < Cinebase::Performance
4
+ # @!attribute [r] booking_url
5
+ # @return [String] the booking URL for the screening
6
+ # @!attribute [r] cinema_name
7
+ # @return [String] the cinema name
8
+ # @!attribute [r] cinema_id
9
+ # @return [String] the cinema id
10
+ # @!attribute [r] dimension
11
+ # @return [String] 2d or 3d
12
+ # @!attribute [r] film_name
13
+ # @return [String] the film name
14
+
15
+ # @!method initialize(options)
16
+ # @param [Hash] options options hash
17
+ # @option options [String] :booking_url (nil) buying url for the screening
18
+ # @option options [String] :cinema_name name of the cinema
19
+ # @option options [String] :cinema_id website id of the cinema
20
+ # @option options [String] :dimension ('2d') dimension of the screening
21
+ # @option options [Cinebase::Festival] :festival (nil) festival edition
22
+ # @option options [String] :film_name name of the film
23
+ # @option options [Integer] :film_year (nil) year from the film's title
24
+ # @option options [String] :language (nil) audio language, e.g. 'Hindi'
25
+ # @option options [Time] :starting_at listed start time of the performance
26
+
27
+ # Showtime labels and A1Web group headings, and the variants they mean.
28
+ # 'Movie Madness' marks cheap tickets, not a kind of screening.
29
+ VARIANTS = Cinebase::A1web::Performance::VARIANTS.merge(
30
+ 'ParentBaby' => 'baby',
31
+ 'Senior' => 'senior'
32
+ ).freeze
33
+
34
+ # Event screenings on the WordPress website have no label
35
+ EVENT = /\A(?:RBO|NT Live|National Theatre Live|Met Opera|Exhibition on Screen)\b|\bLive On Stage\z|\s-\sThe Play\z/i
36
+ SUBTITLED = /\bSubtitled\b/i
37
+ THREE_D = Cinebase::A1web::Performance::THREE_D
38
+ IMAX = Cinebase::A1web::Performance::IMAX
39
+ LANGUAGE = Cinebase::A1web::Performance::LANGUAGE
40
+ TIME_ZONE = 'Europe/London'.freeze
41
+
42
+ # The old website's own booking links only work in a browser that has
43
+ # just visited the venue's pages, so link to the venue's Admit One
44
+ # ticketing site, which redirects to seat selection.
45
+ A1WEB_PERF_CODE = %r{/booking/(\d+)/}
46
+
47
+ # Performances within an inclusive range of showing dates, from all of
48
+ # the cinema's advertised dates. A venue on the WordPress website takes
49
+ # three requests and one more for each film; a venue on the old A1Web
50
+ # website takes one.
51
+ # @param [String] cinema_id the venue's path on the website, e.g. 'chorley'
52
+ # @param [Date] from first date (defaults to today in London)
53
+ # @param [Date] to last date (defaults to 13 days after from)
54
+ # @return [Array<ReelUk::Performance>]
55
+ def self.at(
56
+ cinema_id,
57
+ from: TZInfo::Timezone.get(TIME_ZONE).now.to_date,
58
+ to: from + 13
59
+ )
60
+ raise ArgumentError, 'to must not precede from' if to < from
61
+
62
+ cinema = Cinema.new(cinema_id)
63
+ performances = showings(cinema).select { |showing| (from..to).cover?(showing.date) }.map do |showing|
64
+ new(attributes(showing, cinema).merge(cinema_id: cinema.id, cinema_name: cinema.name))
65
+ end
66
+ performances.sort_by(&:starting_at)
67
+ end
68
+
69
+ # @!method showing_on
70
+ # The date of the screening
71
+ # @return [Date]
72
+
73
+ # @!method starting_at
74
+ # UTC time of the screening
75
+ # @return [Time]
76
+
77
+ # @!method variant
78
+ # The kinds of screening (arts, baby, senior, subtitled ...)
79
+ # @return <Array[String]>
80
+
81
+ def self.showings(cinema)
82
+ if cinema.url.empty?
83
+ []
84
+ elsif cinema.a1web?
85
+ today = TZInfo::Timezone.get(TIME_ZONE).now.to_date
86
+ html = Cinema.a1web_client.whats_on(cinema.url)
87
+ Cinebase::A1web::Listings.new(html, venue_url: cinema.url, today: today).showings
88
+ else
89
+ ReelUk::Internal::Listings.new(cinema.url).showings
90
+ end
91
+ end
92
+ private_class_method :showings
93
+
94
+ def self.attributes(showing, cinema)
95
+ title = showing.title
96
+ {
97
+ booking_url: booking_url(showing.booking_url, cinema),
98
+ dimension: [title, *showing.tags].any? { |text| text.match?(THREE_D) } ? '3d' : '2d',
99
+ festival: Cinebase::Festival.from_title(title, year: showing.date.year),
100
+ film_name: ReelUk::Internal::TitleSanitizer.new(title).sanitized,
101
+ film_year: Cinebase::TitleSanitizer.film_year(title),
102
+ language: title[LANGUAGE, 1],
103
+ starting_at: showing.starting_at,
104
+ variant: variant(showing)
105
+ }
106
+ end
107
+ private_class_method :attributes
108
+
109
+ def self.booking_url(url, cinema)
110
+ code = url[A1WEB_PERF_CODE, 1]
111
+ if cinema.a1web? && code
112
+ "https://reel#{cinema.id.delete('-')}.admit-one.co.uk/tickets/?perfCode=#{code}"
113
+ else
114
+ url
115
+ end
116
+ end
117
+ private_class_method :booking_url
118
+
119
+ def self.variant(showing)
120
+ title = showing.title
121
+ variant = showing.tags.filter_map { |tag| VARIANTS[tag] }
122
+ variant << 'subtitled' if title.match?(SUBTITLED)
123
+ variant << 'arts' if title.match?(EVENT)
124
+ variant << 'imax' if title.match?(IMAX)
125
+ variant.uniq
126
+ end
127
+ private_class_method :variant
128
+ end
129
+ end
@@ -0,0 +1,6 @@
1
+ # Ruby interface for https://reel-cinemas.co.uk
2
+ # @version 1.0.0
3
+ module ReelUk
4
+ # Gem version
5
+ VERSION = '1.0.0'.freeze
6
+ end
data/lib/reel_uk.rb ADDED
@@ -0,0 +1,23 @@
1
+ require 'cgi/escape'
2
+ require 'json'
3
+
4
+ require 'cinebase'
5
+ require 'cinebase/a1web'
6
+
7
+ require_relative './reel_uk/version'
8
+
9
+ require_relative './reel_uk/internal/http'
10
+ require_relative './reel_uk/internal/website'
11
+ require_relative './reel_uk/internal/title_sanitizer'
12
+
13
+ require_relative './reel_uk/internal/parser/coming_soon'
14
+ require_relative './reel_uk/internal/parser/showtimes'
15
+ require_relative './reel_uk/internal/parser/whats_on'
16
+ require_relative './reel_uk/internal/listings'
17
+
18
+ require_relative './reel_uk/cinema'
19
+ require_relative './reel_uk/performance'
20
+
21
+ # reel_uk gem
22
+ module ReelUk
23
+ end
metadata ADDED
@@ -0,0 +1,154 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: reel_uk
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Andy Croll
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: bundler
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '2.0'
19
+ type: :development
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '2.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: minitest-mock
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: minitest-reporters
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: rake
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ - !ruby/object:Gem::Dependency
69
+ name: simplecov
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "<"
73
+ - !ruby/object:Gem::Version
74
+ version: '2'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "<"
80
+ - !ruby/object:Gem::Version
81
+ version: '2'
82
+ - !ruby/object:Gem::Dependency
83
+ name: webmock
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ - !ruby/object:Gem::Dependency
97
+ name: cinebase
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '5.7'
103
+ type: :runtime
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '5.7'
110
+ description: Pull movie & cinema information from the Reel Cinemas website
111
+ email:
112
+ - andy@goodscary.com
113
+ executables: []
114
+ extensions: []
115
+ extra_rdoc_files: []
116
+ files:
117
+ - CHANGELOG.md
118
+ - COMM-LICENSE
119
+ - LICENSE
120
+ - README.md
121
+ - lib/reel_uk.rb
122
+ - lib/reel_uk/cinema.rb
123
+ - lib/reel_uk/internal/http.rb
124
+ - lib/reel_uk/internal/listings.rb
125
+ - lib/reel_uk/internal/parser/coming_soon.rb
126
+ - lib/reel_uk/internal/parser/showtimes.rb
127
+ - lib/reel_uk/internal/parser/whats_on.rb
128
+ - lib/reel_uk/internal/title_sanitizer.rb
129
+ - lib/reel_uk/internal/website.rb
130
+ - lib/reel_uk/performance.rb
131
+ - lib/reel_uk/version.rb
132
+ homepage: https://github.com/andycroll/reel_uk
133
+ licenses:
134
+ - AGPL
135
+ - MIT
136
+ metadata: {}
137
+ rdoc_options: []
138
+ require_paths:
139
+ - lib
140
+ required_ruby_version: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '3.4'
145
+ required_rubygems_version: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - ">="
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ requirements: []
151
+ rubygems_version: 4.0.20
152
+ specification_version: 4
153
+ summary: It's a scraper, but a nice one
154
+ test_files: []