mirror of
https://github.com/moparisthebest/SickRage
synced 2024-10-31 15:35:01 -04:00
714 lines
27 KiB
Python
714 lines
27 KiB
Python
# Author: Nic Wolfe <nic@wolfeden.ca>
|
|
# URL: http://code.google.com/p/sickbeard/
|
|
#
|
|
# This file is part of SickRage.
|
|
#
|
|
# SickRage 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.
|
|
#
|
|
# SickRage 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 SickRage. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import os
|
|
import platform
|
|
import shutil
|
|
import subprocess
|
|
import re
|
|
import urllib
|
|
import zipfile
|
|
import tarfile
|
|
import stat
|
|
import traceback
|
|
import gh_api as github
|
|
import threading
|
|
|
|
import sickbeard
|
|
from sickbeard import helpers
|
|
from sickbeard import version, ui
|
|
from sickbeard import logger
|
|
from sickbeard.exceptions import ex
|
|
from sickbeard import encodingKludge as ek
|
|
|
|
class CheckVersion():
|
|
"""
|
|
Version check class meant to run as a thread object with the SB scheduler.
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.install_type = self.find_install_type()
|
|
|
|
if self.install_type == 'win':
|
|
self.updater = WindowsUpdateManager()
|
|
elif self.install_type == 'git':
|
|
self.updater = GitUpdateManager()
|
|
elif self.install_type == 'source':
|
|
self.updater = SourceUpdateManager()
|
|
else:
|
|
self.updater = None
|
|
|
|
def run(self, force=False):
|
|
if self.check_for_new_version():
|
|
if sickbeard.AUTO_UPDATE:
|
|
logger.log(u"New update found for SickRage, starting auto-updater ...")
|
|
if sickbeard.versionCheckScheduler.action.update():
|
|
logger.log(u"Update was successfull, restarting SickRage ...")
|
|
threading.Timer(2, sickbeard.invoke_restart, [False]).start()
|
|
|
|
def find_install_type(self):
|
|
"""
|
|
Determines how this copy of SB was installed.
|
|
|
|
returns: type of installation. Possible values are:
|
|
'win': any compiled windows build
|
|
'git': running from source using git
|
|
'source': running from source without git
|
|
"""
|
|
|
|
# check if we're a windows build
|
|
if sickbeard.version.SICKBEARD_VERSION.startswith('build '):
|
|
install_type = 'win'
|
|
elif os.path.isdir(ek.ek(os.path.join, sickbeard.PROG_DIR, u'.git')):
|
|
install_type = 'git'
|
|
else:
|
|
install_type = 'source'
|
|
|
|
return install_type
|
|
|
|
def check_for_new_version(self, force=False):
|
|
"""
|
|
Checks the internet for a newer version.
|
|
|
|
returns: bool, True for new version or False for no new version.
|
|
|
|
force: if true the VERSION_NOTIFY setting will be ignored and a check will be forced
|
|
"""
|
|
|
|
if not sickbeard.VERSION_NOTIFY and not sickbeard.AUTO_UPDATE and not force:
|
|
logger.log(u"Version checking is disabled, not checking for the newest version")
|
|
return False
|
|
|
|
if not sickbeard.AUTO_UPDATE:
|
|
logger.log(u"Checking if " + self.install_type + " needs an update")
|
|
if not self.updater.need_update():
|
|
sickbeard.NEWEST_VERSION_STRING = None
|
|
if not sickbeard.AUTO_UPDATE:
|
|
logger.log(u"No update needed")
|
|
|
|
if force:
|
|
ui.notifications.message('No update needed')
|
|
return False
|
|
|
|
self.updater.set_newest_text()
|
|
return True
|
|
|
|
def update(self):
|
|
if self.updater.need_update():
|
|
return self.updater.update()
|
|
|
|
class UpdateManager():
|
|
def get_github_repo_user(self):
|
|
return 'echel0n'
|
|
|
|
def get_github_repo(self):
|
|
return 'SickRage'
|
|
|
|
def get_update_url(self):
|
|
return sickbeard.WEB_ROOT + "/home/update/?pid=" + str(sickbeard.PID)
|
|
|
|
|
|
class WindowsUpdateManager(UpdateManager):
|
|
def __init__(self):
|
|
self.github_repo_user = self.get_github_repo_user()
|
|
self.github_repo = self.get_github_repo()
|
|
self.branch = 'windows_binaries'
|
|
|
|
self._cur_version = None
|
|
self._cur_commit_hash = None
|
|
self._newest_version = None
|
|
|
|
self.gc_url = 'http://code.google.com/p/sickbeard/downloads/list'
|
|
self.version_url = 'https://raw.github.com/' + self.github_repo_user + '/' + self.github_repo + '/' + self.branch + '/updates.txt'
|
|
|
|
def _find_installed_version(self):
|
|
version = ''
|
|
|
|
try:
|
|
version = sickbeard.version.SICKBEARD_VERSION
|
|
return int(version[6:])
|
|
except ValueError:
|
|
logger.log(u"Unknown SickRage Windows binary release: " + version, logger.ERROR)
|
|
return None
|
|
|
|
def _find_newest_version(self, whole_link=False):
|
|
"""
|
|
Checks git for the newest Windows binary build. Returns either the
|
|
build number or the entire build URL depending on whole_link's value.
|
|
|
|
whole_link: If True, returns the entire URL to the release. If False, it returns
|
|
only the build number. default: False
|
|
"""
|
|
|
|
regex = ".*SickRage\-win32\-alpha\-build(\d+)(?:\.\d+)?\.zip"
|
|
|
|
version_url_data = helpers.getURL(self.version_url)
|
|
|
|
if version_url_data is None:
|
|
return None
|
|
else:
|
|
for curLine in version_url_data.splitlines():
|
|
logger.log(u"checking line " + curLine, logger.DEBUG)
|
|
match = re.match(regex, curLine)
|
|
if match:
|
|
logger.log(u"found a match", logger.DEBUG)
|
|
if whole_link:
|
|
return curLine.strip()
|
|
else:
|
|
return int(match.group(1))
|
|
|
|
return None
|
|
|
|
def need_update(self):
|
|
self._cur_version = self._find_installed_version()
|
|
self._newest_version = self._find_newest_version()
|
|
|
|
logger.log(u"newest version: " + repr(self._newest_version), logger.DEBUG)
|
|
|
|
if self._newest_version and self._newest_version > self._cur_version:
|
|
return True
|
|
|
|
return False
|
|
|
|
def set_newest_text(self):
|
|
|
|
sickbeard.NEWEST_VERSION_STRING = None
|
|
|
|
if not self._cur_version:
|
|
newest_text = "Unknown SickRage Windows binary version. Not updating with original version."
|
|
else:
|
|
newest_text = 'There is a <a href="' + self.gc_url + '" onclick="window.open(this.href); return false;">newer version available</a> (build ' + str(
|
|
self._newest_version) + ')'
|
|
newest_text += "— <a href=\"" + self.get_update_url() + "\">Update Now</a>"
|
|
|
|
sickbeard.NEWEST_VERSION_STRING = newest_text
|
|
|
|
def update(self):
|
|
|
|
zip_download_url = self._find_newest_version(True)
|
|
logger.log(u"new_link: " + repr(zip_download_url), logger.DEBUG)
|
|
|
|
if not zip_download_url:
|
|
logger.log(u"Unable to find a new version link on google code, not updating")
|
|
return False
|
|
|
|
try:
|
|
# prepare the update dir
|
|
sb_update_dir = ek.ek(os.path.join, sickbeard.PROG_DIR, u'sb-update')
|
|
|
|
if os.path.isdir(sb_update_dir):
|
|
logger.log(u"Clearing out update folder " + sb_update_dir + " before extracting")
|
|
shutil.rmtree(sb_update_dir)
|
|
|
|
logger.log(u"Creating update folder " + sb_update_dir + " before extracting")
|
|
os.makedirs(sb_update_dir)
|
|
|
|
# retrieve file
|
|
logger.log(u"Downloading update from " + zip_download_url)
|
|
zip_download_path = os.path.join(sb_update_dir, u'sb-update.zip')
|
|
urllib.urlretrieve(zip_download_url, zip_download_path)
|
|
|
|
if not ek.ek(os.path.isfile, zip_download_path):
|
|
logger.log(u"Unable to retrieve new version from " + zip_download_url + ", can't update", logger.ERROR)
|
|
return False
|
|
|
|
if not ek.ek(zipfile.is_zipfile, zip_download_path):
|
|
logger.log(u"Retrieved version from " + zip_download_url + " is corrupt, can't update", logger.ERROR)
|
|
return False
|
|
|
|
# extract to sb-update dir
|
|
logger.log(u"Unzipping from " + str(zip_download_path) + " to " + sb_update_dir)
|
|
update_zip = zipfile.ZipFile(zip_download_path, 'r')
|
|
update_zip.extractall(sb_update_dir)
|
|
update_zip.close()
|
|
|
|
# delete the zip
|
|
logger.log(u"Deleting zip file from " + str(zip_download_path))
|
|
os.remove(zip_download_path)
|
|
|
|
# find update dir name
|
|
update_dir_contents = [x for x in os.listdir(sb_update_dir) if
|
|
os.path.isdir(os.path.join(sb_update_dir, x))]
|
|
|
|
if len(update_dir_contents) != 1:
|
|
logger.log(u"Invalid update data, update failed. Maybe try deleting your sb-update folder?",
|
|
logger.ERROR)
|
|
return False
|
|
|
|
content_dir = os.path.join(sb_update_dir, update_dir_contents[0])
|
|
old_update_path = os.path.join(content_dir, u'updater.exe')
|
|
new_update_path = os.path.join(sickbeard.PROG_DIR, u'updater.exe')
|
|
logger.log(u"Copying new update.exe file from " + old_update_path + " to " + new_update_path)
|
|
shutil.move(old_update_path, new_update_path)
|
|
|
|
except Exception, e:
|
|
logger.log(u"Error while trying to update: " + ex(e), logger.ERROR)
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
class GitUpdateManager(UpdateManager):
|
|
def __init__(self):
|
|
self._git_path = self._find_working_git()
|
|
self.github_repo_user = self.get_github_repo_user()
|
|
self.github_repo = self.get_github_repo()
|
|
self.branch = self._find_git_branch()
|
|
|
|
self._cur_commit_hash = None
|
|
self._newest_commit_hash = None
|
|
self._num_commits_behind = 0
|
|
self._num_commits_ahead = 0
|
|
|
|
def _git_error(self):
|
|
error_message = 'Unable to find your git executable - Shutdown SickRage and EITHER set git_path in your config.ini OR delete your .git folder and run from source to enable updates.'
|
|
sickbeard.NEWEST_VERSION_STRING = error_message
|
|
|
|
def _find_working_git(self):
|
|
test_cmd = 'version'
|
|
|
|
if sickbeard.GIT_PATH:
|
|
main_git = '"' + sickbeard.GIT_PATH + '"'
|
|
else:
|
|
main_git = 'git'
|
|
|
|
logger.log(u"Checking if we can use git commands: " + main_git + ' ' + test_cmd, logger.DEBUG)
|
|
output, err, exit_status = self._run_git(main_git, test_cmd)
|
|
|
|
if exit_status == 0:
|
|
logger.log(u"Using: " + main_git, logger.DEBUG)
|
|
return main_git
|
|
else:
|
|
logger.log(u"Not using: " + main_git, logger.DEBUG)
|
|
|
|
# trying alternatives
|
|
|
|
alternative_git = []
|
|
|
|
# osx people who start SB from launchd have a broken path, so try a hail-mary attempt for them
|
|
if platform.system().lower() == 'darwin':
|
|
alternative_git.append('/usr/local/git/bin/git')
|
|
|
|
if platform.system().lower() == 'windows':
|
|
if main_git != main_git.lower():
|
|
alternative_git.append(main_git.lower())
|
|
|
|
if alternative_git:
|
|
logger.log(u"Trying known alternative git locations", logger.DEBUG)
|
|
|
|
for cur_git in alternative_git:
|
|
logger.log(u"Checking if we can use git commands: " + cur_git + ' ' + test_cmd, logger.DEBUG)
|
|
output, err, exit_status = self._run_git(cur_git, test_cmd)
|
|
|
|
if exit_status == 0:
|
|
logger.log(u"Using: " + cur_git, logger.DEBUG)
|
|
return cur_git
|
|
else:
|
|
logger.log(u"Not using: " + cur_git, logger.DEBUG)
|
|
|
|
# Still haven't found a working git
|
|
error_message = 'Unable to find your git executable - Shutdown SickRage and EITHER set git_path in your config.ini OR delete your .git folder and run from source to enable updates.'
|
|
sickbeard.NEWEST_VERSION_STRING = error_message
|
|
|
|
return None
|
|
|
|
def _run_git(self, git_path, args):
|
|
|
|
output = err = exit_status = None
|
|
|
|
if not git_path:
|
|
logger.log(u"No git specified, can't use git commands", logger.ERROR)
|
|
exit_status = 1
|
|
return (output, err, exit_status)
|
|
|
|
cmd = git_path + ' ' + args
|
|
|
|
try:
|
|
logger.log(u"Executing " + cmd + " with your shell in " + sickbeard.PROG_DIR, logger.DEBUG)
|
|
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
|
shell=True, cwd=sickbeard.PROG_DIR)
|
|
output, err = p.communicate()
|
|
exit_status = p.returncode
|
|
|
|
if output:
|
|
output = output.strip()
|
|
logger.log(u"git output: " + output, logger.DEBUG)
|
|
|
|
except OSError:
|
|
logger.log(u"Command " + cmd + " didn't work")
|
|
exit_status = 1
|
|
|
|
if exit_status == 0:
|
|
logger.log(cmd + u" : returned successful", logger.DEBUG)
|
|
exit_status = 0
|
|
|
|
elif exit_status == 1:
|
|
logger.log(cmd + u" returned : " + output, logger.ERROR)
|
|
exit_status = 1
|
|
|
|
elif exit_status == 128 or 'fatal:' in output or err:
|
|
logger.log(cmd + u" returned : " + output, logger.ERROR)
|
|
exit_status = 128
|
|
|
|
else:
|
|
logger.log(cmd + u" returned : " + output + u", treat as error for now", logger.ERROR)
|
|
exit_status = 1
|
|
|
|
return (output, err, exit_status)
|
|
|
|
def _find_installed_version(self):
|
|
"""
|
|
Attempts to find the currently installed version of SickRage.
|
|
|
|
Uses git show to get commit version.
|
|
|
|
Returns: True for success or False for failure
|
|
"""
|
|
|
|
output, err, exit_status = self._run_git(self._git_path, 'rev-parse HEAD') # @UnusedVariable
|
|
|
|
if exit_status == 0 and output:
|
|
cur_commit_hash = output.strip()
|
|
if not re.match('^[a-z0-9]+$', cur_commit_hash):
|
|
logger.log(u"Output doesn't look like a hash, not using it", logger.ERROR)
|
|
return False
|
|
self._cur_commit_hash = cur_commit_hash
|
|
sickbeard.CUR_COMMIT_HASH = str(cur_commit_hash)
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def _find_git_branch(self):
|
|
branch_info, err, exit_status = self._run_git(self._git_path, 'symbolic-ref -q HEAD') # @UnusedVariable
|
|
if exit_status == 0 and branch_info:
|
|
branch = branch_info.strip().replace('refs/heads/', '', 1)
|
|
if branch:
|
|
sickbeard.version.SICKBEARD_VERSION = branch
|
|
return sickbeard.version.SICKBEARD_VERSION
|
|
|
|
def _check_github_for_update(self):
|
|
"""
|
|
Uses git commands to check if there is a newer version that the provided
|
|
commit hash. If there is a newer version it sets _num_commits_behind.
|
|
"""
|
|
|
|
self._newest_commit_hash = None
|
|
self._num_commits_behind = 0
|
|
self._num_commits_ahead = 0
|
|
|
|
# get all new info from github
|
|
output, err, exit_status = self._run_git(self._git_path, 'fetch origin')
|
|
|
|
if not exit_status == 0:
|
|
logger.log(u"Unable to contact github, can't check for update", logger.ERROR)
|
|
return
|
|
|
|
# get latest commit_hash from remote
|
|
output, err, exit_status = self._run_git(self._git_path, 'rev-parse --verify --quiet "@{upstream}"')
|
|
|
|
if exit_status == 0 and output:
|
|
cur_commit_hash = output.strip()
|
|
|
|
if not re.match('^[a-z0-9]+$', cur_commit_hash):
|
|
logger.log(u"Output doesn't look like a hash, not using it", logger.DEBUG)
|
|
return
|
|
|
|
else:
|
|
self._newest_commit_hash = cur_commit_hash
|
|
else:
|
|
logger.log(u"git didn't return newest commit hash", logger.DEBUG)
|
|
return
|
|
|
|
# get number of commits behind and ahead (option --count not supported git < 1.7.2)
|
|
output, err, exit_status = self._run_git(self._git_path, 'rev-list --left-right "@{upstream}"...HEAD')
|
|
|
|
if exit_status == 0 and output:
|
|
|
|
try:
|
|
self._num_commits_behind = int(output.count("<"))
|
|
self._num_commits_ahead = int(output.count(">"))
|
|
|
|
except:
|
|
logger.log(u"git didn't return numbers for behind and ahead, not using it", logger.DEBUG)
|
|
return
|
|
|
|
logger.log(u"cur_commit = " + str(self._cur_commit_hash) + u", newest_commit = " + str(self._newest_commit_hash)
|
|
+ u", num_commits_behind = " + str(self._num_commits_behind) + u", num_commits_ahead = " + str(
|
|
self._num_commits_ahead), logger.DEBUG)
|
|
|
|
def set_newest_text(self):
|
|
|
|
# if we're up to date then don't set this
|
|
sickbeard.NEWEST_VERSION_STRING = None
|
|
|
|
if self._num_commits_ahead:
|
|
logger.log(u"Local branch is ahead of " + self.branch + ". Automatic update not possible.", logger.ERROR)
|
|
newest_text = "Local branch is ahead of " + self.branch + ". Automatic update not possible."
|
|
|
|
elif self._num_commits_behind > 0:
|
|
|
|
base_url = 'http://github.com/' + self.github_repo_user + '/' + self.github_repo
|
|
if self._newest_commit_hash:
|
|
url = base_url + '/compare/' + self._cur_commit_hash + '...' + self._newest_commit_hash
|
|
else:
|
|
url = base_url + '/commits/'
|
|
|
|
newest_text = 'There is a <a href="' + url + '" onclick="window.open(this.href); return false;">newer version available</a> '
|
|
newest_text += " (you're " + str(self._num_commits_behind) + " commit"
|
|
if self._num_commits_behind > 1:
|
|
newest_text += 's'
|
|
newest_text += ' behind)' + "— <a href=\"" + self.get_update_url() + "\">Update Now</a>"
|
|
|
|
else:
|
|
return
|
|
|
|
sickbeard.NEWEST_VERSION_STRING = newest_text
|
|
|
|
def need_update(self):
|
|
self._find_installed_version()
|
|
|
|
if not self._cur_commit_hash:
|
|
return True
|
|
else:
|
|
try:
|
|
self._check_github_for_update()
|
|
except Exception, e:
|
|
logger.log(u"Unable to contact github, can't check for update: " + repr(e), logger.ERROR)
|
|
return False
|
|
|
|
if self._num_commits_behind > 0:
|
|
return True
|
|
|
|
return False
|
|
|
|
def update(self):
|
|
"""
|
|
Calls git pull origin <branch> in order to update SickRage. Returns a bool depending
|
|
on the call's success.
|
|
"""
|
|
|
|
output, err, exit_status = self._run_git(self._git_path, 'pull origin ' + self.branch) # @UnusedVariable
|
|
|
|
if exit_status == 0:
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
class SourceUpdateManager(UpdateManager):
|
|
def __init__(self):
|
|
self.github_repo_user = self.get_github_repo_user()
|
|
self.github_repo = self.get_github_repo()
|
|
self.branch = sickbeard.version.SICKBEARD_VERSION
|
|
|
|
self._cur_commit_hash = None
|
|
self._newest_commit_hash = None
|
|
self._num_commits_behind = 0
|
|
|
|
def _find_installed_version(self):
|
|
|
|
version_file = ek.ek(os.path.join, sickbeard.PROG_DIR, u'version.txt')
|
|
|
|
if not os.path.isfile(version_file):
|
|
self._cur_commit_hash = None
|
|
return
|
|
|
|
try:
|
|
with open(version_file, 'r') as fp:
|
|
self._cur_commit_hash = fp.read().strip(' \n\r')
|
|
except EnvironmentError, e:
|
|
logger.log(u"Unable to open 'version.txt': " + ex(e), logger.DEBUG)
|
|
|
|
if not self._cur_commit_hash:
|
|
self._cur_commit_hash = None
|
|
sickbeard.CUR_COMMIT_HASH = str(self._cur_commit_hash)
|
|
|
|
def need_update(self):
|
|
|
|
self._find_installed_version()
|
|
|
|
try:
|
|
self._check_github_for_update()
|
|
except Exception, e:
|
|
logger.log(u"Unable to contact github, can't check for update: " + repr(e), logger.ERROR)
|
|
return False
|
|
|
|
if not self._cur_commit_hash or self._num_commits_behind > 0:
|
|
return True
|
|
|
|
return False
|
|
|
|
def _check_github_for_update(self):
|
|
"""
|
|
Uses pygithub to ask github if there is a newer version that the provided
|
|
commit hash. If there is a newer version it sets SickRage's version text.
|
|
|
|
commit_hash: hash that we're checking against
|
|
"""
|
|
|
|
self._num_commits_behind = 0
|
|
self._newest_commit_hash = None
|
|
|
|
gh = github.GitHub(self.github_repo_user, self.github_repo, self.branch)
|
|
|
|
# try to get newest commit hash and commits behind directly by comparing branch and current commit
|
|
if self._cur_commit_hash:
|
|
branch_compared = gh.compare(base=self.branch, head=self._cur_commit_hash)
|
|
|
|
if 'base_commit' in branch_compared:
|
|
self._newest_commit_hash = branch_compared['base_commit']['sha']
|
|
|
|
if 'behind_by' in branch_compared:
|
|
self._num_commits_behind = int(branch_compared['behind_by'])
|
|
|
|
# fall back and iterate over last 100 (items per page in gh_api) commits
|
|
if not self._newest_commit_hash:
|
|
|
|
for curCommit in gh.commits():
|
|
if not self._newest_commit_hash:
|
|
self._newest_commit_hash = curCommit['sha']
|
|
if not self._cur_commit_hash:
|
|
break
|
|
|
|
if curCommit['sha'] == self._cur_commit_hash:
|
|
break
|
|
|
|
# when _cur_commit_hash doesn't match anything _num_commits_behind == 100
|
|
self._num_commits_behind += 1
|
|
|
|
logger.log(u"cur_commit = " + str(self._cur_commit_hash) + u", newest_commit = " + str(self._newest_commit_hash)
|
|
+ u", num_commits_behind = " + str(self._num_commits_behind), logger.DEBUG)
|
|
|
|
def set_newest_text(self):
|
|
|
|
# if we're up to date then don't set this
|
|
sickbeard.NEWEST_VERSION_STRING = None
|
|
|
|
if not self._cur_commit_hash:
|
|
logger.log(u"Unknown current version number, don't know if we should update or not", logger.DEBUG)
|
|
|
|
newest_text = "Unknown current version number: If you've never used the SickRage upgrade system before then current version is not set."
|
|
newest_text += "— <a href=\"" + self.get_update_url() + "\">Update Now</a>"
|
|
|
|
elif self._num_commits_behind > 0:
|
|
base_url = 'http://github.com/' + self.github_repo_user + '/' + self.github_repo
|
|
if self._newest_commit_hash:
|
|
url = base_url + '/compare/' + self._cur_commit_hash + '...' + self._newest_commit_hash
|
|
else:
|
|
url = base_url + '/commits/'
|
|
|
|
newest_text = 'There is a <a href="' + url + '" onclick="window.open(this.href); return false;">newer version available</a>'
|
|
newest_text += " (you're " + str(self._num_commits_behind) + " commit"
|
|
if self._num_commits_behind > 1:
|
|
newest_text += "s"
|
|
newest_text += " behind)" + "— <a href=\"" + self.get_update_url() + "\">Update Now</a>"
|
|
else:
|
|
return
|
|
|
|
sickbeard.NEWEST_VERSION_STRING = newest_text
|
|
|
|
def update(self):
|
|
"""
|
|
Downloads the latest source tarball from github and installs it over the existing version.
|
|
"""
|
|
base_url = 'https://github.com/' + self.github_repo_user + '/' + self.github_repo
|
|
tar_download_url = base_url + '/tarball/' + self.branch
|
|
version_path = ek.ek(os.path.join, sickbeard.PROG_DIR, u'version.txt')
|
|
|
|
try:
|
|
# prepare the update dir
|
|
sb_update_dir = ek.ek(os.path.join, sickbeard.PROG_DIR, u'sb-update')
|
|
|
|
if os.path.isdir(sb_update_dir):
|
|
logger.log(u"Clearing out update folder " + sb_update_dir + " before extracting")
|
|
shutil.rmtree(sb_update_dir)
|
|
|
|
logger.log(u"Creating update folder " + sb_update_dir + " before extracting")
|
|
os.makedirs(sb_update_dir)
|
|
|
|
# retrieve file
|
|
logger.log(u"Downloading update from " + repr(tar_download_url))
|
|
tar_download_path = os.path.join(sb_update_dir, u'sb-update.tar')
|
|
urllib.urlretrieve(tar_download_url, tar_download_path)
|
|
|
|
if not ek.ek(os.path.isfile, tar_download_path):
|
|
logger.log(u"Unable to retrieve new version from " + tar_download_url + ", can't update", logger.ERROR)
|
|
return False
|
|
|
|
if not ek.ek(tarfile.is_tarfile, tar_download_path):
|
|
logger.log(u"Retrieved version from " + tar_download_url + " is corrupt, can't update", logger.ERROR)
|
|
return False
|
|
|
|
# extract to sb-update dir
|
|
logger.log(u"Extracting file " + tar_download_path)
|
|
tar = tarfile.open(tar_download_path)
|
|
tar.extractall(sb_update_dir)
|
|
tar.close()
|
|
|
|
# delete .tar.gz
|
|
logger.log(u"Deleting file " + tar_download_path)
|
|
os.remove(tar_download_path)
|
|
|
|
# find update dir name
|
|
update_dir_contents = [x for x in os.listdir(sb_update_dir) if
|
|
os.path.isdir(os.path.join(sb_update_dir, x))]
|
|
if len(update_dir_contents) != 1:
|
|
logger.log(u"Invalid update data, update failed: " + str(update_dir_contents), logger.ERROR)
|
|
return False
|
|
content_dir = os.path.join(sb_update_dir, update_dir_contents[0])
|
|
|
|
# walk temp folder and move files to main folder
|
|
logger.log(u"Moving files from " + content_dir + " to " + sickbeard.PROG_DIR)
|
|
for dirname, dirnames, filenames in os.walk(content_dir): # @UnusedVariable
|
|
dirname = dirname[len(content_dir) + 1:]
|
|
for curfile in filenames:
|
|
old_path = os.path.join(content_dir, dirname, curfile)
|
|
new_path = os.path.join(sickbeard.PROG_DIR, dirname, curfile)
|
|
|
|
#Avoid DLL access problem on WIN32/64
|
|
#These files needing to be updated manually
|
|
#or find a way to kill the access from memory
|
|
if curfile in ('unrar.dll', 'unrar64.dll'):
|
|
try:
|
|
os.chmod(new_path, stat.S_IWRITE)
|
|
os.remove(new_path)
|
|
os.renames(old_path, new_path)
|
|
except Exception, e:
|
|
logger.log(u"Unable to update " + new_path + ': ' + ex(e), logger.DEBUG)
|
|
os.remove(old_path) # Trash the updated file without moving in new path
|
|
continue
|
|
|
|
if os.path.isfile(new_path):
|
|
os.remove(new_path)
|
|
os.renames(old_path, new_path)
|
|
|
|
# update version.txt with commit hash
|
|
try:
|
|
with open(version_path, 'w') as ver_file:
|
|
ver_file.write(self._newest_commit_hash)
|
|
except EnvironmentError, e:
|
|
logger.log(u"Unable to write version file, update not complete: " + ex(e), logger.ERROR)
|
|
return False
|
|
|
|
except Exception, e:
|
|
logger.log(u"Error while trying to update: " + ex(e), logger.ERROR)
|
|
logger.log(u"Traceback: " + traceback.format_exc(), logger.DEBUG)
|
|
return False
|
|
|
|
return True
|