cinebase 5.1.0 → 5.2.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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +1 -1
- data/.ruby-version +1 -1
- data/CHANGELOG.md +8 -0
- data/README.md +67 -7
- data/cinebase.gemspec +2 -0
- data/lib/cinebase/festival.rb +27 -0
- data/lib/cinebase/performance.rb +9 -0
- data/lib/cinebase/title_sanitizer.rb +27 -0
- data/lib/cinebase/version.rb +1 -1
- data/lib/cinebase/vista/client.rb +55 -0
- data/lib/cinebase/vista/guest_token.rb +77 -0
- data/lib/cinebase/vista/package_task.rb +116 -0
- data/lib/cinebase/vista.rb +14 -0
- data/lib/cinebase.rb +1 -0
- metadata +34 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6e2e7e8c5976a431db0d55f18a831e89e2dda1590dcac508f959e17d06cbdeb3
|
|
4
|
+
data.tar.gz: 20a8dd91bca32d0b31ccd45e1c6d7f2940c0caaa0e0ab184a44a64f3eecaaaea
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 400b26c276c60dfc217148f80d85a216a641731f56cacdce68f2b940454b850fc1d142f2055371f1ad54108e228b85d324c0458247dd66e4404f750622c03ab8
|
|
7
|
+
data.tar.gz: 978a46e47f5a6e004b0f0d1b79dbdb01d8d6c99db800c499db596bb9cf64b336b189c54614241ae93112a7a902fd5e7fe21b757bbbafdef102b35426a871fbad
|
data/.github/workflows/ci.yml
CHANGED
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
3.4.7
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
All notable changes to this project will be documented in this file.
|
|
3
3
|
This project adheres to [Semantic Versioning](http://semver.org/).
|
|
4
4
|
|
|
5
|
+
## [5.2.0] - 2026-10-22
|
|
6
|
+
### Added
|
|
7
|
+
- `Performance#festival`, `#language` and `#subtitled?`, with optional `festival:` and `language:` options
|
|
8
|
+
- `Cinebase::Festival` value object (`name`, `year`) and `Festival.from_title`
|
|
9
|
+
- `TitleSanitizer::COMMON_REMOVE` and `COMMON_REPLACE`: opt-in rules for dub and subtitle versions, language versions, anniversaries, Q&A add-ons, encores, sing-alongs, festival prefixes, opera and ballet seasons, and `WORLD TOUR` capitals
|
|
10
|
+
- `cinebase/vista`, loaded only on request, for chains on Vista OCAPI: `Vista::Client`, a `Vista::GuestToken` template and `Vista::PackageTask` for platform gems bundling curl-impersonate
|
|
11
|
+
- CI on Ruby 4.0
|
|
12
|
+
|
|
5
13
|
## [5.1.0] - 2025-10-31
|
|
6
14
|
### Changed
|
|
7
15
|
- Converted all test files from minitest spec syntax to vanilla minitest syntax
|
data/README.md
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
# Cinebase
|
|
2
2
|
|
|
3
3
|
[](http://rubygems.org/gems/cinebase)
|
|
4
|
-
[](https://travis-ci.org/andycroll/cinebase)
|
|
5
|
-
[](https://codeclimate.com/github/andycroll/cinebase)
|
|
6
|
-
[](https://codeclimate.com/github/andycroll/cinebase/coverage)
|
|
7
4
|

|
|
8
5
|
|
|
9
6
|
A basic interface for a series of cinema gems.
|
|
@@ -44,9 +41,11 @@ module CinemaChainName
|
|
|
44
41
|
end
|
|
45
42
|
|
|
46
43
|
module CinemaChainName
|
|
47
|
-
class
|
|
44
|
+
class Performance < Cinebase::Performance
|
|
48
45
|
def self.at(cinema_id)
|
|
49
|
-
# parse webpages or apis to get
|
|
46
|
+
# parse webpages or apis to get performances for a cinema, built with
|
|
47
|
+
# booking_url:, cinema_id:, cinema_name:, dimension:, festival:,
|
|
48
|
+
# film_name:, language:, starting_at: and variant:
|
|
50
49
|
end
|
|
51
50
|
end
|
|
52
51
|
end
|
|
@@ -56,18 +55,79 @@ module CinemaChainName
|
|
|
56
55
|
class TitleSanitizer < Cinebase::TitleSanitizer
|
|
57
56
|
private
|
|
58
57
|
|
|
58
|
+
# Chain patterns first, so markers like trailing sing-alongs are at the
|
|
59
|
+
# end of the title by the time the common rules run
|
|
59
60
|
def remove
|
|
60
|
-
#
|
|
61
|
+
[] + COMMON_REMOVE # chain-specific patterns, then the common ones
|
|
61
62
|
end
|
|
62
63
|
|
|
63
64
|
def replace
|
|
64
|
-
# { core title regex => replacement text }
|
|
65
|
+
{}.merge(COMMON_REPLACE) # { core title regex => replacement text }
|
|
65
66
|
end
|
|
66
67
|
end
|
|
67
68
|
end
|
|
68
69
|
end
|
|
69
70
|
```
|
|
70
71
|
|
|
72
|
+
### Festivals
|
|
73
|
+
|
|
74
|
+
A festival is an annual event, so `Cinebase::Festival` holds the name and the
|
|
75
|
+
edition year. Pass it to a performance as `festival:`.
|
|
76
|
+
|
|
77
|
+
```ruby
|
|
78
|
+
Cinebase::Festival.from_title('Cine-Excess 2026: Scum Of The Earth', year: 2026)
|
|
79
|
+
#=> #<data Cinebase::Festival name="Cine-Excess", year=2026>
|
|
80
|
+
|
|
81
|
+
# No year in the title, so the screening's year is the edition
|
|
82
|
+
Cinebase::Festival.from_title('UKJFF - From Cable Street To Brick Lane', year: 2026)
|
|
83
|
+
#=> #<data Cinebase::Festival name="UKJFF", year=2026>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`COMMON_REMOVE` strips the same prefixes from titles.
|
|
87
|
+
|
|
88
|
+
### Vista OCAPI chains
|
|
89
|
+
|
|
90
|
+
Chains whose websites run on Vista share a client, guest token retrieval and
|
|
91
|
+
packaging. These are not loaded by `require 'cinebase'`:
|
|
92
|
+
|
|
93
|
+
```ruby
|
|
94
|
+
require 'cinebase/vista'
|
|
95
|
+
|
|
96
|
+
client = Cinebase::Vista::Client.new(
|
|
97
|
+
base_url: 'https://vwc.odeon.co.uk/WSVistaWebClient/ocapi/v1/',
|
|
98
|
+
region_code: 'UK',
|
|
99
|
+
token: guest_jwt
|
|
100
|
+
)
|
|
101
|
+
client.get('sites')
|
|
102
|
+
|
|
103
|
+
module CinemaChainName
|
|
104
|
+
class GuestToken < Cinebase::Vista::GuestToken
|
|
105
|
+
PAGE_URL = 'https://www.example.co.uk/page-with-the-token/'.freeze
|
|
106
|
+
|
|
107
|
+
private
|
|
108
|
+
|
|
109
|
+
def bundled_curl
|
|
110
|
+
File.expand_path('../../vendor/curl-impersonate/bin/curl-impersonate', __dir__)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def extract_token(html)
|
|
114
|
+
# return the guest JWT from the page
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
`GuestToken#fetch` runs `curl-impersonate` with a Chrome profile, checks the
|
|
121
|
+
token's declared expiry and raises `Cinebase::Vista::AuthenticationError` when
|
|
122
|
+
it cannot get a current token. To ship curl-impersonate inside platform gems,
|
|
123
|
+
add this to the chain gem's Rakefile:
|
|
124
|
+
|
|
125
|
+
```ruby
|
|
126
|
+
require 'cinebase/vista/package_task'
|
|
127
|
+
Cinebase::Vista::PackageTask.new('chain_name.gemspec')
|
|
128
|
+
# rake 'package:native[arm64-darwin]' or rake package:all
|
|
129
|
+
```
|
|
130
|
+
|
|
71
131
|
## Contributing
|
|
72
132
|
|
|
73
133
|
1. [Fork it](https://github.com/andycroll/cinebase/fork)
|
data/cinebase.gemspec
CHANGED
|
@@ -18,10 +18,12 @@ Gem::Specification.new do |spec|
|
|
|
18
18
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
19
19
|
spec.require_paths = ['lib']
|
|
20
20
|
|
|
21
|
+
spec.add_development_dependency 'base64'
|
|
21
22
|
spec.add_development_dependency 'bundler', '>= 2.0'
|
|
22
23
|
spec.add_development_dependency 'minitest-reporters'
|
|
23
24
|
spec.add_development_dependency 'rake', '>= 13.0'
|
|
24
25
|
spec.add_development_dependency 'simplecov', '~> 0.22'
|
|
26
|
+
spec.add_development_dependency 'webmock'
|
|
25
27
|
|
|
26
28
|
spec.add_runtime_dependency 'tzinfo'
|
|
27
29
|
spec.add_runtime_dependency 'tzinfo-data'
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Cinebase
|
|
4
|
+
Festival = Data.define(:name, :year)
|
|
5
|
+
|
|
6
|
+
# An annual festival edition a film is screened in
|
|
7
|
+
class Festival
|
|
8
|
+
# Title prefixes naming a festival: the name, then the year when given
|
|
9
|
+
PREFIXES = [
|
|
10
|
+
/\A(Cine-Excess) (20\d{2}): /,
|
|
11
|
+
/\A(Cinemagic) (\d{4}) -/,
|
|
12
|
+
/\A(UKJFF) -/
|
|
13
|
+
].freeze
|
|
14
|
+
|
|
15
|
+
# @param [String] title raw film title
|
|
16
|
+
# @param [Integer] year the edition when the title gives none, usually the
|
|
17
|
+
# screening's year
|
|
18
|
+
# @return [Cinebase::Festival, nil]
|
|
19
|
+
def self.from_title(title, year:)
|
|
20
|
+
PREFIXES.each do |prefix|
|
|
21
|
+
match = title.match(prefix)
|
|
22
|
+
return new(name: match[1], year: Integer(match[2] || year)) if match
|
|
23
|
+
end
|
|
24
|
+
nil
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
data/lib/cinebase/performance.rb
CHANGED
|
@@ -7,14 +7,18 @@ module Cinebase
|
|
|
7
7
|
attr_reader :dimension
|
|
8
8
|
attr_reader :cinema_id
|
|
9
9
|
attr_reader :cinema_name
|
|
10
|
+
attr_reader :festival
|
|
10
11
|
attr_reader :film_name
|
|
12
|
+
attr_reader :language
|
|
11
13
|
|
|
12
14
|
def initialize(options)
|
|
13
15
|
@booking_url = options.fetch(:booking_url, nil)
|
|
14
16
|
@cinema_name = options.fetch(:cinema_name)
|
|
15
17
|
@cinema_id = options.fetch(:cinema_id)
|
|
16
18
|
@dimension = options.fetch(:dimension, '2d')
|
|
19
|
+
@festival = options.fetch(:festival, nil)
|
|
17
20
|
@film_name = options.fetch(:film_name)
|
|
21
|
+
@language = options.fetch(:language, nil)
|
|
18
22
|
@time = options.fetch(:starting_at)
|
|
19
23
|
@variant = options.fetch(:variant, [])
|
|
20
24
|
end
|
|
@@ -41,5 +45,10 @@ module Cinebase
|
|
|
41
45
|
@variant = @variant.split(' ') if @variant.is_a?(String)
|
|
42
46
|
@variant.map(&:downcase).sort.uniq
|
|
43
47
|
end
|
|
48
|
+
|
|
49
|
+
# English subtitles or open captions
|
|
50
|
+
def subtitled?
|
|
51
|
+
variant.include?('subtitled')
|
|
52
|
+
end
|
|
44
53
|
end
|
|
45
54
|
end
|
|
@@ -27,3 +27,30 @@ module Cinebase
|
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
29
|
end
|
|
30
|
+
|
|
31
|
+
module Cinebase
|
|
32
|
+
# Rules every chain can share; subclasses opt in after their own patterns,
|
|
33
|
+
# e.g. `[...] + COMMON_REMOVE` in #remove
|
|
34
|
+
class TitleSanitizer
|
|
35
|
+
LANGUAGES = %w(Arabic Cantonese French Hindi Kannada Korean Malayalam
|
|
36
|
+
Mandarin Marathi Polish Punjabi Tamil Telugu Turkish).freeze
|
|
37
|
+
|
|
38
|
+
COMMON_REMOVE = [
|
|
39
|
+
*Festival::PREFIXES, # festival prefix
|
|
40
|
+
/\s+[23]d\b/i, # dimension
|
|
41
|
+
/\((#{LANGUAGES.join('|')})\)/i, # language version
|
|
42
|
+
/\((Dubbed|Subbed|English Dub|[^)]*subtitles)\)/i, # dub or subtitle version
|
|
43
|
+
/\(\d+(st|nd|rd|th) Anniversary\)/i, # anniversary re-release
|
|
44
|
+
/\s*\+\s*(In-Person )?Q&A\z/i, # Q&A add-on
|
|
45
|
+
/:\s*Encore\z/i, # encore
|
|
46
|
+
/\bsing-?a-?long\z/i, # singalong
|
|
47
|
+
'amp;' # html ampersands
|
|
48
|
+
].freeze
|
|
49
|
+
|
|
50
|
+
COMMON_REPLACE = {
|
|
51
|
+
/Met Opera 20\d{2}-\d{2}: (.*)/ => 'Met Opera: ',
|
|
52
|
+
/RBO (?:Cinema Season )?20\d{2}-\d{2}: (.*)/ => 'Royal Ballet and Opera: ',
|
|
53
|
+
/WORLD TOUR(.*)/ => 'World Tour'
|
|
54
|
+
}.freeze
|
|
55
|
+
end
|
|
56
|
+
end
|
data/lib/cinebase/version.rb
CHANGED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'net/http'
|
|
5
|
+
|
|
6
|
+
module Cinebase
|
|
7
|
+
module Vista
|
|
8
|
+
# Makes guest-token GET requests to a chain's OCAPI
|
|
9
|
+
class Client
|
|
10
|
+
# @param [String] base_url e.g. 'https://vwc.odeon.co.uk/WSVistaWebClient/ocapi/v1/'
|
|
11
|
+
# @param [String] region_code Connect-Region-Code header, e.g. 'UK' or 'GB'
|
|
12
|
+
# @param [String, nil] token raw guest JWT, without the "Bearer " prefix
|
|
13
|
+
def initialize(base_url:, region_code:, token:)
|
|
14
|
+
@base_url = base_url
|
|
15
|
+
@region_code = region_code
|
|
16
|
+
@token = token
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# @param [String] path relative to the base URL
|
|
20
|
+
# @param [Hash] query parameters; Array values repeat the key
|
|
21
|
+
# @return [Hash] parsed JSON
|
|
22
|
+
# @raise [Cinebase::Vista::AuthenticationError] missing or rejected token
|
|
23
|
+
def get(path, query = {})
|
|
24
|
+
if !@token.is_a?(String) || @token.strip.empty?
|
|
25
|
+
raise AuthenticationError, 'Set a current guest token before fetching data'
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
response = request(path, query)
|
|
29
|
+
case response
|
|
30
|
+
when Net::HTTPSuccess
|
|
31
|
+
JSON.parse(response.body)
|
|
32
|
+
when Net::HTTPUnauthorized
|
|
33
|
+
raise AuthenticationError, 'Vista rejected the guest token; supply a fresh one'
|
|
34
|
+
else
|
|
35
|
+
# Omit response bodies and never redirect credentials to another host.
|
|
36
|
+
raise "Vista HTTP error #{response.code}"
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def request(path, query)
|
|
43
|
+
uri = URI(@base_url + path)
|
|
44
|
+
uri.query = URI.encode_www_form(query) unless query.empty?
|
|
45
|
+
request = Net::HTTP::Get.new(uri)
|
|
46
|
+
request['Authorization'] = "Bearer #{@token}"
|
|
47
|
+
request['Connect-Region-Code'] = @region_code
|
|
48
|
+
request['Accept'] = 'application/json'
|
|
49
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: true, open_timeout: 10, read_timeout: 30) do |http|
|
|
50
|
+
http.request(request)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'open3'
|
|
5
|
+
|
|
6
|
+
module Cinebase
|
|
7
|
+
module Vista
|
|
8
|
+
# Retrieves a chain website's public guest token with curl-impersonate,
|
|
9
|
+
# without running a browser. Subclasses set PAGE_URL and implement
|
|
10
|
+
# #extract_token(html) and #bundled_curl.
|
|
11
|
+
class GuestToken
|
|
12
|
+
# @param [String, nil] curl curl-impersonate executable; defaults to the
|
|
13
|
+
# one bundled in the chain gem, then the one on PATH
|
|
14
|
+
def initialize(curl: nil)
|
|
15
|
+
@curl = curl || default_curl
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# @return [String] the guest JWT
|
|
19
|
+
# @raise [Cinebase::Vista::AuthenticationError]
|
|
20
|
+
def fetch
|
|
21
|
+
token = extract_token(request_page)
|
|
22
|
+
unless token.is_a?(String) && !token.empty?
|
|
23
|
+
raise AuthenticationError, 'Guest token is missing from the page'
|
|
24
|
+
end
|
|
25
|
+
validate_expiry!(token)
|
|
26
|
+
token
|
|
27
|
+
rescue JSON::ParserError, ArgumentError, IndexError, TypeError, NoMethodError
|
|
28
|
+
raise AuthenticationError, 'Guest token page data is missing or invalid', cause: nil
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
# HTTP statuses whose page still carries the token
|
|
34
|
+
def accepted_statuses
|
|
35
|
+
%w[200]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def bundled_curl
|
|
39
|
+
nil
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def default_curl
|
|
43
|
+
bundled_curl && File.executable?(bundled_curl) ? bundled_curl : 'curl-impersonate'
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def curl_command
|
|
47
|
+
[@curl, '--disable', '--impersonate', 'chrome150',
|
|
48
|
+
'--compressed', '--silent', '--show-error',
|
|
49
|
+
'--connect-timeout', '10', '--max-time', '30',
|
|
50
|
+
'--write-out', "\n%{http_code}", self.class::PAGE_URL]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def request_page
|
|
54
|
+
output, _stderr, result = Open3.capture3(*curl_command)
|
|
55
|
+
unless result.success?
|
|
56
|
+
raise AuthenticationError, "Guest token request failed (curl exit #{result.exitstatus})"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
html, _, status = output.rpartition("\n")
|
|
60
|
+
unless accepted_statuses.include?(status)
|
|
61
|
+
raise AuthenticationError, "Guest token page returned HTTP #{status}"
|
|
62
|
+
end
|
|
63
|
+
html
|
|
64
|
+
rescue Errno::ENOENT
|
|
65
|
+
raise AuthenticationError, 'Install curl-impersonate or pass its executable path with curl:'
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def validate_expiry!(token)
|
|
69
|
+
payload = token.split('.').fetch(1).tr('-_', '+/').unpack1('m')
|
|
70
|
+
claims = JSON.parse(payload)
|
|
71
|
+
if Integer(claims.fetch('exp')) <= Time.now.to_i
|
|
72
|
+
raise AuthenticationError, 'Guest token supplied by the page has expired'
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'digest'
|
|
4
|
+
require 'fileutils'
|
|
5
|
+
require 'rake'
|
|
6
|
+
require 'rubygems/package'
|
|
7
|
+
require 'tmpdir'
|
|
8
|
+
require 'zlib'
|
|
9
|
+
|
|
10
|
+
module Cinebase
|
|
11
|
+
module Vista
|
|
12
|
+
# Defines package:native[platform] and package:all, which build platform
|
|
13
|
+
# gems that bundle curl-impersonate for GuestToken. In a chain gem's
|
|
14
|
+
# Rakefile: `Cinebase::Vista::PackageTask.new('odeon_uk.gemspec')`
|
|
15
|
+
class PackageTask
|
|
16
|
+
include Rake::DSL
|
|
17
|
+
|
|
18
|
+
VERSION = '2.2.3'
|
|
19
|
+
|
|
20
|
+
# Platform => [upstream asset target, SHA-256 of the v2.2.3 release asset]
|
|
21
|
+
RELEASES = {
|
|
22
|
+
'x86_64-linux-gnu' => ['x86_64-linux-gnu', 'ca7c8aae49e33260d3a2400db16c7737aaf04692d606a843502056b42990f989'],
|
|
23
|
+
'aarch64-linux-gnu' => ['aarch64-linux-gnu', '5e71795b971f32be8f3d7785895f4dceb232f04414f5a067db266432a5b08657'],
|
|
24
|
+
'arm64-darwin' => ['arm64-macos', '2569f4139460fcb301484d37938de91b1c220efd15487ee22a9db554262062fe'],
|
|
25
|
+
'x86_64-darwin' => ['x86_64-macos', '4686806d59abea93866a917c3025a049ef3d7a3240de742ad66bd1a4d9890dc7']
|
|
26
|
+
}.freeze
|
|
27
|
+
|
|
28
|
+
# @param [String] gemspec path to the chain gem's gemspec
|
|
29
|
+
# @param [String] root the chain gem's root directory
|
|
30
|
+
def initialize(gemspec, root: Dir.pwd)
|
|
31
|
+
@gemspec = gemspec
|
|
32
|
+
@root = root
|
|
33
|
+
define
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def define
|
|
39
|
+
namespace :package do
|
|
40
|
+
desc "Build a gem with bundled curl-impersonate #{VERSION} for the given platform"
|
|
41
|
+
task :native, [:platform] do |_task, args|
|
|
42
|
+
build(args.fetch(:platform))
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
desc 'Build all supported platform gems with bundled curl-impersonate'
|
|
46
|
+
task :all do
|
|
47
|
+
RELEASES.each_key { |platform| build(platform) }
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def build(platform)
|
|
53
|
+
target, digest = RELEASES.fetch(platform)
|
|
54
|
+
asset = "curl-impersonate-v#{VERSION}.#{target}.tar.gz"
|
|
55
|
+
url = "https://github.com/lexiforest/curl-impersonate/releases/download/v#{VERSION}/#{asset}"
|
|
56
|
+
archive = File.join(@root, 'pkg', 'downloads', asset)
|
|
57
|
+
download(url, archive)
|
|
58
|
+
abort "SHA-256 mismatch for #{archive}; delete it and retry" unless Digest::SHA256.file(archive).hexdigest == digest
|
|
59
|
+
|
|
60
|
+
spec = Gem::Specification.load(File.join(@root, @gemspec)).dup
|
|
61
|
+
Dir.mktmpdir('vista-package') do |stage|
|
|
62
|
+
spec.files.each do |file|
|
|
63
|
+
destination = File.join(stage, file)
|
|
64
|
+
FileUtils.mkdir_p(File.dirname(destination))
|
|
65
|
+
FileUtils.cp(File.join(@root, file), destination)
|
|
66
|
+
end
|
|
67
|
+
vendor(archive, File.join(stage, 'vendor', 'curl-impersonate'), url, digest)
|
|
68
|
+
|
|
69
|
+
Dir.chdir(stage) do
|
|
70
|
+
spec.platform = Gem::Platform.new(platform)
|
|
71
|
+
spec.files += Dir.glob('vendor/**/*').select { |file| File.file?(file) }
|
|
72
|
+
Gem::Package.build(spec, false, false, File.join(@root, 'pkg', spec.file_name))
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def download(url, archive)
|
|
78
|
+
FileUtils.mkdir_p(File.dirname(archive))
|
|
79
|
+
sh 'curl', '--fail', '--location', '--retry', '3', '--output', archive, url unless File.exist?(archive)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def vendor(archive, vendor, url, digest)
|
|
83
|
+
Zlib::GzipReader.open(archive) do |gzip|
|
|
84
|
+
Gem::Package::TarReader.new(gzip) do |tar|
|
|
85
|
+
tar.each do |entry|
|
|
86
|
+
next unless entry.file?
|
|
87
|
+
|
|
88
|
+
relative = vendor_path(entry.full_name)
|
|
89
|
+
next unless relative
|
|
90
|
+
|
|
91
|
+
destination = File.join(vendor, relative)
|
|
92
|
+
FileUtils.mkdir_p(File.dirname(destination))
|
|
93
|
+
File.binwrite(destination, entry.read)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
File.chmod(0o755, File.join(vendor, 'bin', 'curl-impersonate'))
|
|
98
|
+
File.write(File.join(vendor, 'SOURCE'), <<~SOURCE)
|
|
99
|
+
curl-impersonate #{VERSION}
|
|
100
|
+
Binary: #{url}
|
|
101
|
+
SHA-256: #{digest}
|
|
102
|
+
Source and build instructions: https://github.com/lexiforest/curl-impersonate/tree/v#{VERSION}
|
|
103
|
+
Redistribution notices: licenses/
|
|
104
|
+
SOURCE
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def vendor_path(name)
|
|
108
|
+
if name == 'curl-impersonate'
|
|
109
|
+
'bin/curl-impersonate'
|
|
110
|
+
elsif name.start_with?('LICENSE') && File.basename(name) == name
|
|
111
|
+
"licenses/#{name}"
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'cinebase'
|
|
4
|
+
|
|
5
|
+
module Cinebase
|
|
6
|
+
# Shared support for chains whose sites run on Vista's OCAPI. Not loaded by
|
|
7
|
+
# `require 'cinebase'`; Vista-based gems `require 'cinebase/vista'`.
|
|
8
|
+
module Vista
|
|
9
|
+
class AuthenticationError < StandardError; end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
require 'cinebase/vista/client'
|
|
14
|
+
require 'cinebase/vista/guest_token'
|
data/lib/cinebase.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: cinebase
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.
|
|
4
|
+
version: 5.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andy Croll
|
|
@@ -9,6 +9,20 @@ bindir: exe
|
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: base64
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :development
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0'
|
|
12
26
|
- !ruby/object:Gem::Dependency
|
|
13
27
|
name: bundler
|
|
14
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -65,6 +79,20 @@ dependencies:
|
|
|
65
79
|
- - "~>"
|
|
66
80
|
- !ruby/object:Gem::Version
|
|
67
81
|
version: '0.22'
|
|
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'
|
|
68
96
|
- !ruby/object:Gem::Dependency
|
|
69
97
|
name: tzinfo
|
|
70
98
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -116,9 +144,14 @@ files:
|
|
|
116
144
|
- cinebase.gemspec
|
|
117
145
|
- lib/cinebase.rb
|
|
118
146
|
- lib/cinebase/cinema.rb
|
|
147
|
+
- lib/cinebase/festival.rb
|
|
119
148
|
- lib/cinebase/performance.rb
|
|
120
149
|
- lib/cinebase/title_sanitizer.rb
|
|
121
150
|
- lib/cinebase/version.rb
|
|
151
|
+
- lib/cinebase/vista.rb
|
|
152
|
+
- lib/cinebase/vista/client.rb
|
|
153
|
+
- lib/cinebase/vista/guest_token.rb
|
|
154
|
+
- lib/cinebase/vista/package_task.rb
|
|
122
155
|
homepage: http://github.com/andycroll/cinebase
|
|
123
156
|
licenses:
|
|
124
157
|
- MIT
|