-
Notifications
You must be signed in to change notification settings - Fork 71
/
product_details.py
256 lines (201 loc) · 8.77 KB
/
product_details.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
# -*- coding: utf-8 -*-
from collections import OrderedDict
from operator import itemgetter
from urllib.parse import urlencode
import json
import os
import re
import settings
def filter_major_versions(versions):
"""Filters out some version numbers not meant for human consumption."""
# Preserves json ordering
for version in settings.VERSIONS_TO_FILTER:
if version in versions:
del versions[version]
return versions
def load_json(path):
"""Load the .json at `path` and return data."""
path = os.path.join(settings.JSON_PATH, path)
with open(path, 'r') as f:
return json.load(f)
def load_all_builds(path):
""" Loads the all_builds data, and mixes in the latest beta information with all release locales. """
all_builds = load_json(path)
# We heavily rely on en-US, but if somehow that's no longer a locale, at least don't crash here.
if 'en-US' not in all_builds:
return all_builds
all_data = {}
# Filter just the beta builds
for build, info in all_builds.get('en-US').items():
if 'b' in build:
all_data.update({build: info})
for locale, build in all_builds.items():
# Already has beta information
if locale == 'en-US':
continue
# Merge the beta build information
all_builds[locale].update(all_data)
return all_builds
class ThunderbirdDetails():
""" Loads Thunderbird versioning information from product details JSON files."""
platform_labels = OrderedDict([
# ('winsha1', 'Windows (XP/Vista)'),
('win64', 'Windows 64-bit'),
('msi', 'Windows MSI 64-bit'),
('osx', 'macOS'),
('linux64', 'Linux 64-bit'),
('win', 'Windows 32-bit'),
('linux', 'Linux 32-bit')
])
# Grouped by platform
grouped_platform_labels = OrderedDict({
'Windows': [('win64', '64-bit (.exe)'), ('msi', '64-bit (.msi)'), ('win', '32-bit (.exe)')],
'Linux': [('linux64', '64-bit (binary)'), ('linux', '32-bit (binary)')],
'MacOS': [('osx', '64-bit (.dmg)')]
})
languages = load_json('languages.json')
current_versions = load_json('thunderbird_versions.json')
all_builds = load_all_builds('thunderbird_primary_builds.json')
major_releases = filter_major_versions(load_json('thunderbird_history_major_releases.json'))
minor_releases = load_json('thunderbird_history_stability_releases.json')
dev_releases = load_json('thunderbird_history_development_releases.json')
version_map = {
'daily': 'LATEST_THUNDERBIRD_NIGHTLY_VERSION',
'beta': 'LATEST_THUNDERBIRD_DEVEL_VERSION',
'release': 'LATEST_THUNDERBIRD_VERSION',
}
channel_labels = OrderedDict({
'release': 'Release',
'beta': 'Beta',
'daily': 'Daily'
})
def latest_version(self, channel='release'):
"""Returns the latest release version of Thunderbird by default, or other `channel`."""
version_name = self.version_map.get(channel, 'LATEST_THUNDERBIRD_VERSION')
return self.current_versions[version_name]
def latest_builds(self, locale, channel='release'):
"""Returns builds for the latest version of Thunderbird based on `channel`."""
version = self.latest_version(channel)
all_builds = self.all_builds
if locale in all_builds and version in all_builds[locale]:
builds = all_builds[locale][version]
# Append 64-bit builds
if 'Linux' in builds:
builds['Linux 64-bit'] = builds['Linux']
if 'Windows' in builds:
builds['Windows 64-bit'] = builds['Windows']
return version, builds
def get_filtered_full_builds(self, channel, version):
version = version or self.latest_version(channel)
f_builds = []
builds = self.all_builds
for locale, build in builds.items():
if locale not in self.languages or not build.get(version):
continue
build_info = {
'locale': locale,
'name_en': self.languages[locale]['English'],
'name_native': self.languages[locale]['native'],
'platforms': {},
}
for platform, label in self.platform_labels.items():
build_info['platforms'][platform] = {
'download_url': self.get_download_url(channel, version,
platform, locale,
True),
}
f_builds.append(build_info)
return sorted(f_builds, key=itemgetter('name_en'))
def get_download_url(self, channel, version, platform, locale, force_direct=True):
"""Retrieve the download url for a given channel, version, platform and locale."""
_version = version
_locale = 'ja-JP-mac' if platform == 'osx' and locale == 'ja' else locale
_platform = 'win' if platform == 'winsha1' else platform
product_url = 'thunderbird-%s-SSL'
if channel == 'daily':
_version = 'nightly-latest'
if platform == 'msi':
_platform = 'win64'
# Daily's bouncer link doesn't support `-msi-SSL`, so we'll just make it a win64 build for now.
if channel != 'daily':
product_url = 'thunderbird-%s-msi-SSL'
# Check if direct download link has been requested
# (bypassing the transition page)
if not force_direct:
# Currently we don't have the transition page for Thunderbird, so
# return a direct link instead
pass
# build a direct download link for 'beta' and 'release' channels.
return '?'.join([settings.BOUNCER_URL,
urlencode([
('product', product_url % _version),
('os', _platform),
# Order matters, lang must be last for bouncer.
('lang', _locale),
])])
def platforms(self, channel='release'):
return self.platform_labels.items()
def list_releases(self, channel='beta'):
releases = {}
for release in self.major_releases:
major_version = float(re.findall(r'^\d+\.\d+', release)[0])
# The version numbering scheme of Thunderbird has changed over the years,
# so there is some trickiness on major versions below 5.
# When updating this sorting, be careful old versions aren't broken.
if major_version < 5:
major_pattern = release + '.'
else:
major_pattern = release.split('.')[0] + '.'
releases[major_version] = {
'major': release,
'minor': sorted([x[0] for x in self.minor_releases.items()
if x[0].startswith(major_pattern)],
key=lambda x: [int(y) for y in x.split('.')])
}
return sorted(releases.items(), reverse=True)
def beta_version_to_canonical(self, version):
last = ''
for x in range(1, 10):
v = re.sub(r'beta', 'b{0}'.format(x), version)
date = self.dev_releases.get(v, '')
if date:
last = v
return last
def get_release_date(self, version):
date = ''
if 'b' in version:
version = self.beta_version_to_canonical(version)
date = self.dev_releases.get(version, '')
if not date:
date = self.major_releases.get(version, '')
if not date:
date = self.minor_releases.get(version, '')
return date
class ThunderbirdMobileDetails():
"""Shim for Thunderbird Mobile."""
platform_labels = OrderedDict([
('gplay', 'Google Play Store'),
('fdroid', 'F-Droid'),
('apk', 'Binary')
])
# Grouped by platform
grouped_platform_labels = OrderedDict({
'Android': [('gplay', 'Google Play Store'), ('fdroid', 'F-Droid'), ('apk', 'Binary (.apk)')],
})
version_map = {
'release': 'LATEST_THUNDERBIRD_VERSION',
}
channel_labels = OrderedDict({
'mobile': 'Mobile',
})
def get_download_url(self, channel, version, platform, locale, force_direct=True):
"""Retrieve the download url for a given channel, version, platform and locale."""
# Nice and simple
if platform == 'gplay':
return settings.URL_MAPPINGS.get('download.android.gplay')
elif platform == 'fdroid':
return settings.URL_MAPPINGS.get('download.android.fdroid')
else:
return settings.URL_MAPPINGS.get('download.android.binary')
thunderbird_desktop = ThunderbirdDetails()
thunderbird_mobile = ThunderbirdMobileDetails()