openlp/openlp/core/version.py

214 lines
8.3 KiB
Python
Raw Normal View History

2017-06-17 08:51:01 +00:00
# -*- coding: utf-8 -*-
2019-04-13 13:00:22 +00:00
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
2022-02-01 10:10:57 +00:00
# Copyright (c) 2008-2022 OpenLP Developers #
2019-04-13 13:00:22 +00:00
# ---------------------------------------------------------------------- #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################
2017-06-17 08:51:01 +00:00
"""
2017-09-09 05:19:22 +00:00
The :mod:`openlp.core.version` module downloads the version details for OpenLP.
2017-06-17 08:51:01 +00:00
"""
2016-04-04 19:53:54 +00:00
import logging
import platform
import sys
from collections import OrderedDict
from datetime import date
2016-04-04 19:53:54 +00:00
from PyQt5 import QtCore
2017-10-07 07:05:07 +00:00
from openlp.core.common.applocation import AppLocation
2020-10-20 11:32:48 +00:00
from openlp.core.common.httputils import get_web_page
from openlp.core.common.registry import Registry
from openlp.core.threading import ThreadWorker, run_thread
2016-07-01 21:17:20 +00:00
2018-10-02 04:39:42 +00:00
2016-04-04 19:53:54 +00:00
log = logging.getLogger(__name__)
APPLICATION_VERSION = {}
CONNECTION_TIMEOUT = 30
CONNECTION_RETRIES = 2
LIBRARIES = OrderedDict([
('Python', ('platform', 'python_version')),
('PyQt5', ('PyQt5.Qt', 'PYQT_VERSION_STR')),
('SQLAlchemy', ('sqlalchemy',)),
('Alembic', ('alembic',)),
('BeautifulSoup', ('bs4',)),
('lxml', ('lxml.etree',)),
('Chardet', ('chardet',)),
('PyEnchant', ('enchant',)),
('Mako', ('mako',)),
('VLC', ('openlp.core.ui.media.vlcplayer', 'VERSION')),
])
VERSION_BASE_URL = 'https://get.openlp.org/versions/'
VERSION_STABLE = 'version.txt'
VERSION_DEVELOP = 'dev_version.txt'
VERSION_NIGHTLY = 'nightly_version.txt'
2016-04-04 19:53:54 +00:00
class VersionWorker(ThreadWorker):
2016-04-04 19:53:54 +00:00
"""
2017-09-09 05:19:22 +00:00
A worker class to fetch the version of OpenLP from the website. This is run from within a thread so that it
doesn't affect the loading time of OpenLP.
2016-04-04 19:53:54 +00:00
"""
2020-10-04 06:31:30 +00:00
new_version = QtCore.pyqtSignal(str)
2017-09-09 05:19:22 +00:00
no_internet = QtCore.pyqtSignal()
def __init__(self, last_check_date, current_version):
2016-04-04 19:53:54 +00:00
"""
2017-09-09 05:19:22 +00:00
Constructor for the version check worker.
2016-04-04 19:53:54 +00:00
2017-09-09 05:19:22 +00:00
:param string last_check_date: The last day we checked for a new version of OpenLP
2016-04-04 19:53:54 +00:00
"""
2017-09-09 05:19:22 +00:00
log.debug('VersionWorker - Initialise')
super(VersionWorker, self).__init__(None)
self.last_check_date = last_check_date
self.current_version = current_version
2016-04-04 19:53:54 +00:00
2017-09-09 05:19:22 +00:00
def start(self):
2016-04-04 19:53:54 +00:00
"""
2017-09-09 05:19:22 +00:00
Check the latest version of OpenLP against the version file on the OpenLP site.
**Rules around versions and version files:**
* If a version number has a revision hash (i.e. g9034d140b), then it is a nightly.
2017-09-09 05:19:22 +00:00
* If a version number's minor version is an odd number, it is a development release.
* If a version number's minor version is an even number, it is a stable release.
2016-04-04 19:53:54 +00:00
"""
2017-09-09 05:19:22 +00:00
log.debug('VersionWorker - Start')
download_url = VERSION_BASE_URL
if self.current_version['build']:
download_url += VERSION_NIGHTLY
elif int(self.current_version['version'].split('.')[1]) % 2 != 0:
download_url += VERSION_DEVELOP
else:
download_url += VERSION_STABLE
2017-09-09 05:19:22 +00:00
headers = {
'User-Agent': 'OpenLP/{version} {system}/{release}; '.format(version=self.current_version['full'],
2017-09-09 05:19:22 +00:00
system=platform.system(),
release=platform.release())
}
remote_version = None
retries = 0
while retries < 3:
try:
2020-10-20 11:32:48 +00:00
response = get_web_page(download_url, headers=headers)
if response:
remote_version = response.strip()
2017-09-09 05:19:22 +00:00
log.debug('New version found: %s', remote_version)
break
except OSError:
2017-09-09 05:19:22 +00:00
log.exception('Unable to connect to OpenLP server to download version file')
retries += 1
else:
self.no_internet.emit()
2020-09-28 23:22:08 +00:00
if remote_version and (QtCore.QVersionNumber.fromString(remote_version) >
QtCore.QVersionNumber.fromString(self.current_version['full'])):
2017-09-09 05:19:22 +00:00
self.new_version.emit(remote_version)
self.quit.emit()
def update_check_date():
"""
Save when we last checked for an update
"""
Registry().get('settings').setValue('core/last version test', date.today().strftime('%Y-%m-%d'))
def check_for_update(main_window):
2017-09-09 05:19:22 +00:00
"""
Run a thread to download and check the version of OpenLP
:param MainWindow main_window: The OpenLP main window.
2017-09-09 05:19:22 +00:00
"""
last_check_date = Registry().get('settings').value('core/last version test')
if date.today().strftime('%Y-%m-%d') <= last_check_date:
2017-09-09 05:19:22 +00:00
log.debug('Version check skipped, last checked today')
return
worker = VersionWorker(last_check_date, get_version())
worker.new_version.connect(main_window.on_new_version)
worker.quit.connect(update_check_date)
2017-09-09 05:19:22 +00:00
# TODO: Use this to figure out if there's an Internet connection?
# worker.no_internet.connect(parent.on_no_internet)
2017-12-17 06:54:21 +00:00
run_thread(worker, 'version')
2017-09-09 05:19:22 +00:00
def get_version():
2016-04-04 19:53:54 +00:00
"""
Returns the application version of the running instance of OpenLP::
{'full': '2.9.0.dev2963+97ba02d1f', 'version': '2.9.0', 'build': '97ba02d1f'}
or
{'full': '2.9.0', 'version': '2.9.0', 'build': None}
2016-04-04 19:53:54 +00:00
"""
global APPLICATION_VERSION
if APPLICATION_VERSION:
return APPLICATION_VERSION
file_path = AppLocation.get_directory(AppLocation.VersionDir) / '.version'
try:
full_version = file_path.read_text().rstrip()
except OSError:
log.exception('Error in version file.')
2018-04-06 19:55:08 +00:00
full_version = '0.0.0'
bits = full_version.split('.dev')
2016-04-04 19:53:54 +00:00
APPLICATION_VERSION = {
'full': full_version,
'version': bits[0],
'build': full_version.split('+')[1] if '+' in full_version else None
2016-04-04 19:53:54 +00:00
}
if APPLICATION_VERSION['build']:
log.info('OpenLP version {version} build {build}'.format(version=APPLICATION_VERSION['version'],
build=APPLICATION_VERSION['build']))
2016-04-04 19:53:54 +00:00
else:
log.info('OpenLP version {version}'.format(version=APPLICATION_VERSION['version']))
2016-04-04 19:53:54 +00:00
return APPLICATION_VERSION
def _get_lib_version(library, version_attr='__version__'):
"""
Import a library and return the value of its version attribute.
:param str library: The name of the library to import
:param str version_attr: The name of the attribute containing the version number. Defaults to '__version__'
:returns str: The version number as a string
"""
if library not in sys.modules:
try:
__import__(library)
except ImportError:
return None
if not hasattr(sys.modules[library], version_attr):
return 'OK'
else:
attr = getattr(sys.modules[library], version_attr)
if callable(attr):
return str(attr())
else:
return str(attr)
def get_library_versions():
"""
Read and return the versions of the libraries we use.
:returns OrderedDict: A dictionary of {'library': 'version'}
"""
library_versions = OrderedDict([(library, _get_lib_version(*args)) for library, args in LIBRARIES.items()])
# Just update the dict for display purposes, changing the None to a '-'
for library, version in library_versions.items():
if version is None:
library_versions[library] = '-'
return library_versions