|
Packit Service |
48484a |
#!/usr/bin/env python3
|
|
Packit Service |
48484a |
# -*- coding: utf-8 -*-
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
"""This program fetches the latest version of the Lensfun database from the
|
|
Packit Service |
48484a |
Internet and places it on the local system. This way, the user can update the
|
|
Packit Service |
48484a |
database conveniently. Unfortunately, we have to take into account that the
|
|
Packit Service |
48484a |
Git database may have a too new format for the locally installed Lensfun.
|
|
Packit Service |
48484a |
Then, it may fetch backports of the database from other URLs.
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
This program must be called with root privileges. It stores the new database
|
|
Packit Service |
48484a |
in `/var/lib/lensfun-updates`.
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
The repository of databases resides at a base URL. Below that URL, there is
|
|
Packit Service |
48484a |
the file versions.json. It contains a list with three elements. The first is
|
|
Packit Service |
48484a |
the database timestamp, the second is a list of available version numbers, and
|
|
Packit Service |
48484a |
the third is a list of strings which represents further alternative base URLs
|
|
Packit Service |
48484a |
to look at. So, the file may contain the following::
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
[1386797501, [1, 2, 3], ["http://wilson.bronger.org/"]]
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
All URLs must end with a slash. For every version number, there must be a file
|
|
Packit Service |
48484a |
called version_<versionnumber>.tar.bz2. So in our case, there must be the
|
|
Packit Service |
48484a |
files
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
::
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
version_1.tar.bz2
|
|
Packit Service |
48484a |
version_2.tar.bz2
|
|
Packit Service |
48484a |
version_3.tar.bz2
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
in the same directory as versions.json. These tar balls contain the Lensfun
|
|
Packit Service |
48484a |
database with the given timestamp and version.
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
The timestamps are the number of seconds since the Epoch as an int.
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
Diagnostics:
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
Status code 0 -- successfully installed updates
|
|
Packit Service |
48484a |
1 -- no newer upstream database found for last installed Lensfun
|
|
Packit Service |
48484a |
3 -- no location was responsive; maybe network problems
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
"""
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
import urllib.request, shutil, sys, os, time, calendar, tarfile, json, glob
|
|
Packit Service |
48484a |
import lensfun
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
database_version = lensfun.get_database_version()
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
lensfun_updates_dir = "/var/lib/lensfun-updates"
|
|
Packit Service |
48484a |
if os.name == "posix" and os.geteuid() != 0:
|
|
Packit Service |
48484a |
lensfun_updates_dir = os.path.join(os.path.expanduser("~"),
|
|
Packit Service |
48484a |
".local", "share", "lensfun", "updates")
|
|
Packit Service |
48484a |
print("Info: root privileges needed for updating the system database.")
|
|
Packit Service |
48484a |
print("Info: updating user DB in '%s'" % lensfun_updates_dir)
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
def seconds_since_epoch():
|
|
Packit Service |
48484a |
return calendar.timegm(time.gmtime())
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
def detect_local_timestamp(version):
|
|
Packit Service |
48484a |
if version == database_version:
|
|
Packit Service |
48484a |
return lensfun.get_core_database()[0]
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
def detect_single_timestamp(path):
|
|
Packit Service |
48484a |
try:
|
|
Packit Service |
48484a |
return int(open(os.path.join(path, "version_{}".format(version), "timestamp.txt")).read())
|
|
Packit Service |
48484a |
except (FileNotFoundError, ValueError):
|
|
Packit Service |
48484a |
return 0
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
directory_candidates = {"/usr/share/lensfun", "/usr/local/share/lensfun",
|
|
Packit Service |
48484a |
"/var/lib/lensfun-updates", os.path.expanduser("~/.local/share/lensfun/updates")}
|
|
Packit Service |
48484a |
return max(map(detect_single_timestamp, directory_candidates))
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
class Location:
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
def __init__(self, base_url, version, timestamp):
|
|
Packit Service |
48484a |
self.base_url, self.version, self.timestamp = base_url, version, timestamp
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
def __lt__(self, other):
|
|
Packit Service |
48484a |
return self.timestamp < other.timestamp
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
def extract(self, directory):
|
|
Packit Service |
48484a |
tar = tarfile.open(fileobj=urllib.request.urlopen(self.base_url + "version_{}.tar.bz2".format(self.version)),
|
|
Packit Service |
48484a |
mode="r|*")
|
|
Packit Service |
48484a |
tar.extractall(directory)
|
|
Packit Service |
48484a |
tar.close()
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
def detect_local_database_versions():
|
|
Packit Service |
48484a |
versions = {database_version}
|
|
Packit Service |
48484a |
for directory in glob.glob("/usr/share/lensfun/version_*") + glob.glob("/usr/local/share/lensfun/version_*"):
|
|
Packit Service |
48484a |
if os.path.isdir(directory):
|
|
Packit Service |
48484a |
try:
|
|
Packit Service |
48484a |
versions.add(int(directory.partition("version_")[2]))
|
|
Packit Service |
48484a |
except ValueError:
|
|
Packit Service |
48484a |
pass
|
|
Packit Service |
48484a |
return versions
|
|
Packit Service |
48484a |
local_database_versions = detect_local_database_versions()
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
locations = {version: set() for version in local_database_versions}
|
|
Packit Service |
48484a |
local_timestamps = {version: detect_local_timestamp(version) for version in local_database_versions}
|
|
Packit Service |
48484a |
seen_urls = set()
|
|
Packit Service |
48484a |
at_least_one_responsive_location = False
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
def read_location(base_url):
|
|
Packit Service |
48484a |
global at_least_one_responsive_location
|
|
Packit Service |
48484a |
if base_url not in seen_urls and len(seen_urls) < 50:
|
|
Packit Service |
48484a |
seen_urls.add(base_url)
|
|
Packit Service |
48484a |
print("Reading {} …".format(base_url + "versions.json"))
|
|
Packit Service |
48484a |
try:
|
|
Packit Service |
48484a |
response = urllib.request.urlopen(base_url + "versions.json")
|
|
Packit Service |
48484a |
except (urllib.error.HTTPError, ValueError):
|
|
Packit Service |
48484a |
print(" Error: URL could not be opened.")
|
|
Packit Service |
48484a |
else:
|
|
Packit Service |
48484a |
try:
|
|
Packit Service |
48484a |
timestamp, versions, alternatives = json.loads(response.read().decode("utf-8"))
|
|
Packit Service |
48484a |
except ValueError:
|
|
Packit Service |
48484a |
print(" Error: Invalid data received.")
|
|
Packit Service |
48484a |
else:
|
|
Packit Service |
48484a |
at_least_one_responsive_location = True
|
|
Packit Service |
48484a |
versions = set(versions) & set(locations)
|
|
Packit Service |
48484a |
for version in versions:
|
|
Packit Service |
48484a |
if timestamp > local_timestamps[version]:
|
|
Packit Service |
48484a |
locations[version].add(Location(base_url, version, timestamp))
|
|
Packit Service |
48484a |
for base_url in alternatives:
|
|
Packit Service |
48484a |
read_location(base_url)
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
read_location("http://lensfun.sourceforge.net/db/")
|
|
Packit Service |
48484a |
read_location("http://wilson.bronger.org/lensfun-db/")
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
if not at_least_one_responsive_location:
|
|
Packit Service |
48484a |
print("Fatal: No location was responsive. Network down?")
|
|
Packit Service |
48484a |
sys.exit(3)
|
|
Packit Service |
48484a |
elif not locations[database_version]:
|
|
Packit Service |
48484a |
print("Info: No newer database was found for last installed Lensfun.")
|
|
Packit Service |
48484a |
sys.exit(1)
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
|
|
Packit Service |
48484a |
for version, location_list in locations.items():
|
|
Packit Service |
48484a |
try:
|
|
Packit Service |
48484a |
best_location = max(location_list)
|
|
Packit Service |
48484a |
except ValueError:
|
|
Packit Service |
48484a |
continue
|
|
Packit Service |
48484a |
updates_dir = os.path.join(lensfun_updates_dir, "version_{}".format(version))
|
|
Packit Service |
48484a |
shutil.rmtree(updates_dir, ignore_errors=True)
|
|
Packit Service |
48484a |
os.makedirs(updates_dir)
|
|
Packit Service |
48484a |
best_location.extract(updates_dir)
|
|
Packit Service |
48484a |
open(os.path.join(updates_dir, "timestamp.txt"), "w").write(str(best_location.timestamp))
|
|
Packit Service |
48484a |
print("Successfully updated the database in " + updates_dir + ".")
|