Compare commits

..

33 Commits

Author SHA1 Message Date
JonnyWong16
97f80adf0b v2.6.5 2021-01-09 14:24:27 -08:00
JonnyWong16
2fc7b08909 Add misfire grace time to scheduled tasks 2021-01-09 14:15:32 -08:00
JonnyWong16
defceed696 Fix homepage server status when sections are disabled 2021-01-06 17:24:53 -08:00
JonnyWong16
249533ac51 Migrate snap user data 2021-01-05 17:56:53 -08:00
JonnyWong16
12aee8762e Allow snap package to access home directory 2021-01-05 10:19:41 -08:00
JonnyWong16
d9325b7adf Add Crypto link to funding 2020-12-28 14:16:16 -08:00
JonnyWong16
4975cad4fa Keep news open for longer 2020-12-27 14:53:33 -08:00
JonnyWong16
33fc079318 More logger blacklist improvements 2020-12-27 14:52:18 -08:00
JonnyWong16
b3b2752554 Fix regex masking in logger args 2020-12-27 13:15:06 -08:00
JonnyWong16
505cf25ca3 Add back crypto donation 2020-12-25 14:01:04 -08:00
JonnyWong16
9747e3ba98 Add pull request workflow 2020-12-24 18:23:06 -08:00
JonnyWong16
729191722a Fix typo 2020-12-24 17:00:39 -08:00
JonnyWong16
ff2cf73f23 Update .gitignore 2020-12-24 16:43:00 -08:00
JonnyWong16
9c4d97c0f8 Update installer workflow 2020-12-24 16:43:00 -08:00
JonnyWong16
be911e7700 Move nsis plugin folder 2020-12-24 16:42:37 -08:00
JonnyWong16
00629c0983 Use updater to modify Task Scheduler XML 2020-12-24 16:41:28 -08:00
JonnyWong16
52ebc9a908 Move Tautulli to Program Files x64 2020-12-24 16:41:21 -08:00
JonnyWong16
a029d6a931 Restart Tautulli process if Windows installer failed 2020-12-22 19:26:53 -08:00
JonnyWong16
7641e3b081 Add logger function to Windows updater 2020-12-22 19:18:45 -08:00
JonnyWong16
b54210480f Don't shutdown for Windows installer update 2020-12-22 19:09:27 -08:00
JonnyWong16
0d9c1c640e Update Windows updater 2020-12-22 19:09:06 -08:00
JonnyWong16
7f84353c69 Disable console window when running task scheduler 2020-12-22 16:24:21 -08:00
JonnyWong16
c319a4a5cc Don't auto restart when updating Windows install 2020-12-22 16:22:45 -08:00
JonnyWong16
60f13df992 Add auto update for Windows exe install 2020-12-22 15:59:19 -08:00
JonnyWong16
dea51e32a5 Strip branch and version when reading text file in Windows updater 2020-12-22 15:46:11 -08:00
JonnyWong16
7019f5618b Update Windows powershell command to move updater 2020-12-22 15:36:21 -08:00
JonnyWong16
9106c068ac Update Windows installer workflow 2020-12-22 15:26:37 -08:00
JonnyWong16
0b845294fb Add Tautulli Windows exe updater 2020-12-22 15:21:06 -08:00
JonnyWong16
7e850dd88d Fix typo in apscheduler in package requirements file 2020-12-22 12:33:52 -08:00
JonnyWong16
877bf7060e Missing = sign in package requirements file 2020-12-22 12:31:22 -08:00
JonnyWong16
9326d03a57 Update PyInstaller to 4.1
* APScheduler needs to be installed from PyPI due to the way it retrieves it's own version number using pkg_resources.get_distribution
2020-12-22 12:29:43 -08:00
JonnyWong16
4787f42d2e Add args to Windows and MacOS auto startup 2020-12-21 13:39:53 -08:00
JonnyWong16
56a9ccd818 Make Windows registry key unique to allow more than one instance 2020-12-21 13:39:29 -08:00
28 changed files with 519 additions and 63 deletions

2
.github/FUNDING.yml vendored
View File

@@ -1,3 +1,3 @@
github: JonnyWong16
patreon: Tautulli
custom: ["https://bit.ly/2InPp15"]
custom: ["https://bit.ly/2InPp15", "https://bit.ly/2WTq83m"]

View File

@@ -41,6 +41,13 @@ jobs:
echo ::set-output name=VERSION::0.0.0
echo ::set-output name=RELEASE_VERSION::${GITHUB_SHA::7}
fi
if [[ $GITHUB_REF == refs/tags/*-beta ]]; then
echo "beta" > branch.txt
elif [[ $GITHUB_REF == refs/tags/* ]]; then
echo "master" > branch.txt
else
echo ${GITHUB_REF#refs/heads/} > branch.txt
fi
echo $GITHUB_SHA > version.txt
- name: Set Up Python
@@ -64,16 +71,20 @@ jobs:
run: |
pyinstaller -y ./package/Tautulli-${{ matrix.os }}.spec
- name: Move Windows Updater Files
if: matrix.os == 'windows'
run: |
Move-Item dist\updater\* dist\Tautulli\ -Force
- name: Create Windows Installer
uses: joncloud/makensis-action@v1.2
uses: joncloud/makensis-action@v3.4
if: matrix.os == 'windows'
with:
script-file: ./package/Tautulli.nsi
arguments: >
/DVERSION=${{ steps.get_version.outputs.VERSION_NSIS }}
/DINSTALLER_NAME=..\Tautulli-windows-${{ steps.get_version.outputs.RELEASE_VERSION }}-x64.exe
include-more-plugins: true
include-custom-plugins-path: package/nsis-plugins
additional-plugin-paths: package/nsis-plugins
- name: Create MacOS Installer
if: matrix.os == 'macos'

28
.github/workflows/pull-requests.yml vendored Normal file
View File

@@ -0,0 +1,28 @@
name: Pull Requests
on:
pull_request_target:
types: [opened, synchronize, edited, reopened]
jobs:
check-branch:
name: Check Pull Request
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v2
- name: Comment on Pull Request
uses: mshick/add-pr-comment@v1
if: github.base_ref != 'nightly'
with:
message: Pull requests must be made to the `nightly` branch. Thanks.
repo-token: ${{ secrets.GITHUB_TOKEN }}
repo-token-user-login: 'github-actions[bot]'
- name: Fail Workflow
if: github.base_ref != 'nightly'
run: |
echo Base: ${{ github.base_ref }}
echo Head: ${{ github.head_ref }}
exit 1

6
.gitignore vendored
View File

@@ -1,6 +1,7 @@
# Compiled source #
###################
__pycache__
*.pyc
*.py~
*.pyproj
@@ -64,7 +65,6 @@ Thumbs.db
*.bak
*.cache
*.ilk
*.log
[Bb]in
[Dd]ebug*/
*.lib
@@ -86,11 +86,7 @@ _ReSharper*/
/parts/
/stage/
/prime/
*.snap
.snapcraft
__pycache__
*.pyc
*_source.tar.bz2
snap/.snapcraft

View File

@@ -1,5 +1,14 @@
# Changelog
## v2.6.5 (2021-01-09)
* Other:
* Fix: Some IP addresses not being masked in the logs.
* New: Auto-updater for Windows exe installer.
* Change: Allow Snap package to access the user home directory.
* Change: Migrate Snap user data to a persistent location that is retained if Tautulli is reinstalled.
## v2.6.4 (2020-12-20)
* Other:

View File

@@ -31,6 +31,7 @@ import datetime
import locale
import pytz
import signal
import shutil
import time
import threading
import tzlocal
@@ -188,6 +189,15 @@ def main():
else:
plexpy.DATA_DIR = plexpy.PROG_DIR
# Migrate Snap data dir
if plexpy.SNAP:
snap_common = os.environ['SNAP_COMMON']
old_data_dir = os.path.join(snap_common, 'Tautulli')
if os.path.exists(old_data_dir) and os.listdir(old_data_dir):
plexpy.SNAP_MIGRATE = True
logger.info("Migrating Snap user data.")
shutil.move(old_data_dir, plexpy.DATA_DIR)
if args.config:
config_file = args.config
else:

View File

@@ -61,7 +61,7 @@
Update your Docker container or <a href="#" id="updateDismiss">Dismiss</a>
% elif plexpy.INSTALL_TYPE == 'snap':
Update your Snap package or <a href="#" id="updateDismiss">Dismiss</a>
% elif plexpy.INSTALL_TYPE in ('windows', 'macos'):
% elif plexpy.INSTALL_TYPE == 'macos':
<a href="${anon_url('https://github.com/%s/%s/releases/tag/%s' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO, plexpy.LATEST_RELEASE))}" target="_blank" rel="noreferrer">Download</a> and install the latest version or <a href="#" id="updateDismiss">Dismiss</a>
% else:
<a href="update">Update</a> or <a href="#" id="updateDismiss">Dismiss</a>
@@ -237,6 +237,7 @@ ${next.modalIncludes()}
<li class="active"><a href="#github-donation" role="tab" data-toggle="tab">GitHub</a></li>
<li><a href="#patreon-donation" role="tab" data-toggle="tab">Patreon</a></li>
<li><a href="#paypal-donation" role="tab" data-toggle="tab">PayPal</a></li>
<li><a href="#crypto-donation" role="tab" data-toggle="tab">Crypto</a></li>
</ul>
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="github-donation" style="text-align: center">
@@ -263,6 +264,14 @@ ${next.modalIncludes()}
<img src="images/gold-rect-paypal-34px.png" alt="PayPal">
</a>
</div>
<div role="tabpanel" class="tab-pane" id="crypto-donation" style="text-align: center">
<p>
Click the button below to continue to Coinbase.
</p>
<a href="https://blankrefer.com/?https://commerce.coinbase.com/checkout/8a9fa08c-8a38-409e-9220-868124c4ba0c" target="_blank" rel="noreferrer" class="donate-with-crypto">
<span>Donate with Crypto</span>
</a>
</div>
</div>
</div>
<div class="modal-footer">
@@ -341,7 +350,7 @@ ${next.modalIncludes()}
msg += 'Update your Docker container or <a href="#" id="updateDismiss">Dismiss</a>';
} else if (result.install_type === 'snap') {
msg += 'Update your Snap package or <a href="#" id="updateDismiss">Dismiss</a>';
} else if (result.install_type === 'windows' || result.install_type === 'macos') {
} else if (result.install_type === 'macos') {
msg += '<a href="' + result.release_url + '" target="_blank" rel="noreferrer">Download</a> and install the latest version or <a href="#" id="updateDismiss">Dismiss</a>'
} else {
msg += '<a href="update">Update</a> or <a href="#" id="updateDismiss">Dismiss</a>';

View File

@@ -4368,3 +4368,66 @@ a[data-tab-destination] {
.news-body a:hover {
color: #f9be03;
}
a.donate-with-crypto,
a.donate-with-crypto > span {
background: none;
border: none;
border-radius: 0;
box-sizing: border-box;
clear: none;
clip: auto;
cursor: default;
display: block;
float: none;
height: auto;
margin: 0;
max-height: none;
min-height: none;
padding: 0;
opacity: 1;
text-shadow: none;
vertical-align: baseline;
visibility: visible;
width: auto;
}
a.donate-with-crypto {
user-select: none;
user-drag: none;
-webkit-user-drag: none;
text-decoration: none;
background: #1652f0 linear-gradient(#1652f0, #0655ab);
cursor: pointer;
transition: background 0.2s ease-in-out, padding 0.2s;
border-radius: 6px;
display: inline-block;
height: 40px;
padding: 9px 15px 11px 15px;
position: relative;
min-width: 160px;
}
a.donate-with-crypto:hover {
background: #1652f0;
}
a.donate-with-crypto > span {
color: white;
font: normal 500 14px/20px -apple-system, BlinkMacSystemFont, '.SFNSText-Regular', 'San Francisco', 'Roboto', 'Segoe UI', 'Helvetica Neue', 'Lucida Grande', sans-serif;
letter-spacing: 0;
overflow: hidden;
text-align: center;
text-overflow: ellipsis;
text-shadow: 0 1px 0 rgba(0, 0, 0, 0.15);
white-space: nowrap;
}
a.donate-with-crypto::after {
border-radius: 6px;
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.2);
content: '';
display: block;
width: 100%;
height: 100%;
position: absolute;
opacity: 1;
top: 0;
left: 0;
}

View File

@@ -280,22 +280,32 @@
var error_msg = 'There was an error communicating with your Plex Server.' + msg_settings;
% if 'current_activity' in config['home_sections'] or 'recently_added' in config['home_sections']:
var server_status;
server_status = setInterval(function() {
$.getJSON('server_status', function (data) {
if (data.connected === true) {
clearInterval(server_status);
% if 'current_activity' in config['home_sections']:
$('#currentActivity').html('<div id="dashboard-checking-activity" class="text-muted"><i class="fa fa-refresh fa-spin"></i>&nbsp; Checking for activity...</div>');
$('#recentlyAdded').html('<div id="dashboard-checking-recently-added" class="text-muted"><i class="fa fa-refresh fa-spin"></i>&nbsp; Looking for new items...</div>');
activityConnected();
% endif
% if 'recently_added' in config['home_sections']:
$('#recentlyAdded').html('<div id="dashboard-checking-recently-added" class="text-muted"><i class="fa fa-refresh fa-spin"></i>&nbsp; Looking for new items...</div>');
recentlyAddedConnected();
% endif
} else if (data.connected === false) {
clearInterval(server_status);
% if 'current_activity' in config['home_sections']:
$('#currentActivity').html('<div id="dashboard-no-activity" class="text-muted">' + error_msg + '</div>');
% endif
% if 'recently_added' in config['home_sections']:
$('#recentlyAdded').html('<div id="dashboard-no-recently-added" class="text-muted">' + error_msg + '</div>');
% endif
}
});
}, 1000);
% endif
</script>
% if 'current_activity' in config['home_sections']:
<script>

View File

@@ -220,7 +220,7 @@
<p class="help-block">Check for Tautulli updates periodically.</p>
</div>
<div id="git_update_options">
% if not plexpy.SNAP and not plexpy.FROZEN:
% if not plexpy.SNAP and not (plexpy.FROZEN and common.PLATFORM == 'Darwin'):
<div class="checkbox">
<label>
<input type="checkbox" id="plexpy_auto_update" name="plexpy_auto_update" value="1" ${config['plexpy_auto_update']} ${docker_setting}> Update Automatically ${docker_msg | n}
@@ -3105,7 +3105,7 @@ $(document).ready(function() {
if (news_item.subtitle) { content.append(subtitle); }
content.append(body);
var li = $('<li/>').append(header).append(content)
if (index === 0 && Math.abs(now.diff(date, 'days')) < 7) {
if (index === 0 && Math.abs(now.diff(date, 'days')) <= 30) {
li.addClass('open');
content.css('display', 'block');
}

View File

@@ -3,6 +3,7 @@
import sys
sys.modules['FixTk'] = None
excludes = ['FixTk', 'tcl', 'tk', '_tkinter', 'tkinter', 'Tkinter']
block_cipher = None
analysis = Analysis(
@@ -12,13 +13,27 @@ analysis = Analysis(
('..\\data', 'data'),
('..\\CHANGELOG.md', '.'),
('..\\LICENSE', '.'),
('..\\branch.txt', '.'),
('..\\version.txt', '.'),
('..\\lib\\ipwhois\\data', 'data')
('..\\lib\\ipwhois\\data', 'data'),
('TautulliUpdateTask.xml', '.')
],
excludes=['FixTk', 'tcl', 'tk', '_tkinter', 'tkinter', 'Tkinter'],
excludes=excludes,
hiddenimports=['pkg_resources.py2_warn', 'cheroot.ssl', 'cheroot.ssl.builtin'],
cipher=block_cipher,
cipher=block_cipher
)
updater_analysis = Analysis(
['updater-windows.py'],
pathex=['lib'],
excludes=excludes,
cipher=block_cipher
)
MERGE(
(analysis, 'Tautulli', 'Tautulli'),
(updater_analysis, 'updater', 'updater')
)
pyz = PYZ(
analysis.pure,
analysis.zipped_data,
@@ -39,3 +54,24 @@ coll = COLLECT(
analysis.datas,
name='Tautulli'
)
updater_pyz = PYZ(
updater_analysis.pure,
updater_analysis.zipped_data,
cipher=block_cipher
)
updater_exe = EXE(
updater_pyz,
updater_analysis.scripts,
exclude_binaries=True,
name='updater',
console=False,
icon='..\\data\\interfaces\\default\\images\\logo-circle.ico'
)
coll = COLLECT(
updater_exe,
updater_analysis.binaries,
updater_analysis.zipfiles,
updater_analysis.datas,
name='updater'
)

View File

@@ -32,6 +32,7 @@ VIAddVersionKey "FileVersion" "${VERSION}"
######################################################################
Unicode True
SetCompressor ZLIB
Name "${APP_NAME}"
Caption "${APP_NAME}"
@@ -39,7 +40,7 @@ OutFile "${INSTALLER_NAME}"
BrandingText "${APP_NAME}"
XPStyle on
InstallDirRegKey "${REG_ROOT}" "${REG_APP_PATH}" ""
InstallDir "$PROGRAMFILES\${APP_NAME}"
InstallDir "$PROGRAMFILES64\${APP_NAME}"
######################################################################
@@ -76,9 +77,13 @@ InstallDir "$PROGRAMFILES\${APP_NAME}"
!include Sections.nsh
Var /GLOBAL norun
Var /GLOBAL nolaunch
!include "MUI.nsh"
!include "FileFunc.nsh"
!insertmacro GetParameters
!insertmacro GetOptions
!define MUI_ABORTWARNING
!define MUI_UNABORTWARNING
@@ -99,6 +104,7 @@ Var /GLOBAL nolaunch
!insertmacro MUI_PAGE_STARTMENU Application $SM_Folder
!endif
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!define MUI_FINISHPAGE_RUN "$INSTDIR\${MAIN_APP_EXE}"
@@ -119,10 +125,14 @@ Section -MainProgram
Call UninstallPrevious
${INSTALL_TYPE}
SetOverwrite ifnewer
SetOverwrite on
SetOutPath "$INSTDIR"
File /nonfatal /a /r "..\dist\${APP_NAME}\"
nsExec::Exec "$INSTDIR\updater.exe --xml"
nsExec::Exec '$SYSDIR\SCHTASKS /Create /TN TautulliUpdateTask /XML "$INSTDIR\TautulliUpdateTask.xml" /F'
StrCmp $norun 1 +3 0
IfSilent 0 +2
ExecShell "" "$INSTDIR\${MAIN_APP_EXE}" $nolaunch
SectionEnd
@@ -208,11 +218,20 @@ RmDir "$SMPROGRAMS\${APP_NAME}"
DeleteRegKey ${REG_ROOT} "${REG_APP_PATH}"
DeleteRegKey ${REG_ROOT} "${UNINSTALL_PATH}"
nsExec::Exec "$SYSDIR\SCHTASKS /Delete /TN TautulliUpdateTask /F"
SectionEnd
######################################################################
Function .onInit
StrCpy $norun 0
${GetParameters} $CMDLINE
${GetOptions} "$CMDLINE" "/NORUN" $R0
IfErrors +2 0
StrCpy $norun 1
IfSilent 0 +2
StrCpy $nolaunch "--nolaunch"

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,4 +1,5 @@
pyinstaller==3.6
apscheduler==3.6.3
pyinstaller==4.1
pyopenssl==20.0.0
pycryptodomex==3.9.9
pyobjc-framework-Cocoa==6.2.2

View File

@@ -1,4 +1,6 @@
pyinstaller==3.6
apscheduler==3.6.3
psutil==5.8.0
pyinstaller==4.1
pyopenssl==20.0.0
pycryptodomex==3.9.9
pywin32==300

194
package/updater-windows.py Normal file
View File

@@ -0,0 +1,194 @@
# -*- coding: utf-8 -*-
# This file is part of Tautulli.
#
# Tautulli 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.
#
# Tautulli 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 Tautulli. If not, see <http://www.gnu.org/licenses/>.
from logging import handlers
import argparse
import logging
import os
import psutil
import re
import requests
import shutil
import subprocess
import sys
import tempfile
import xml.etree.ElementTree as ET
SCRIPT_PATH = os.path.dirname(os.path.abspath(__file__))
CREATE_NO_WINDOW = 0x08000000
REPO_URL = 'https://api.github.com/repos/Tautulli/Tautulli'
LOGFILE = 'updater.log'
LOGPATH = os.path.join(SCRIPT_PATH, LOGFILE)
MAX_SIZE = 1000000 # 1MB
MAX_FILES = 1
def init_logger():
log = logging.getLogger('updater')
log.setLevel(logging.DEBUG)
file_formatter = logging.Formatter(
'%(asctime)s - %(levelname)-7s :: %(threadName)s : Tautulli Updater :: %(message)s',
'%Y-%m-%d %H:%M:%S')
file_handler = handlers.RotatingFileHandler(
LOGPATH, maxBytes=MAX_SIZE, backupCount=MAX_FILES, encoding='utf-8')
file_handler.setFormatter(file_formatter)
log.addHandler(file_handler)
return log
def read_file(file_path):
try:
with open(file_path, 'r') as f:
return f.read().strip(' \n\r')
except Exception as e:
logger.error('Read file error: %s', e)
raise Exception(1)
def request_json(url):
try:
response = requests.get(url)
response.raise_for_status()
return response.json()
except Exception as e:
logger.error('Request error: %s', e)
raise Exception(2)
def kill_and_get_processes(process_name):
processes = []
for process in psutil.process_iter():
if process.name() == process_name:
processes.append(process.cmdline())
logger.info('Sending SIGTERM to %s (PID=%d)', process.name(), process.pid)
process.terminate()
return processes
def update_tautulli():
logger.info('Starting Tautulli update check')
branch = read_file(os.path.join(SCRIPT_PATH, 'branch.txt'))
logger.info('Branch: %s', branch)
current_version = read_file(os.path.join(SCRIPT_PATH, 'version.txt'))
logger.info('Current version: %s', current_version)
logger.info('Retrieving latest version from GitHub')
commits = request_json('{}/commits/{}'.format(REPO_URL, branch))
latest_version = commits['sha']
logger.info('Latest version: %s', latest_version)
if current_version == latest_version:
logger.info('Tautulli is already up to date')
return 0
logger.info('Comparing version on GitHub')
compare = request_json('{}/compare/{}...{}'.format(REPO_URL, latest_version, current_version))
commits_behind = compare['behind_by']
logger.info('Commits behind: %s', commits_behind)
if commits_behind <= 0:
logger.info('Tautulli is already up to date')
return 0
logger.info('Retrieving releases on GitHub')
releases = request_json('{}/releases'.format(REPO_URL))
if branch == 'master':
release = next((r for r in releases if not r['prerelease']), releases[0])
else:
release = next((r for r in releases), releases[0])
version = release['tag_name']
logger.info('Release: %s', version)
win_exe = 'application/vnd.microsoft.portable-executable'
asset = next((a for a in release['assets'] if a['content_type'] == win_exe), None)
download_url = asset['browser_download_url']
download_file = asset['name']
file_path = os.path.join(tempfile.gettempdir(), download_file)
logger.info('Downloading installer to temporary directory: %s', file_path)
try:
with requests.get(download_url, stream=True) as r:
with open(file_path, 'wb') as f:
shutil.copyfileobj(r.raw, f)
except Exception as e:
logger.error('Failed to download %s: %s', download_file, e)
return 2
logger.info('Stopping Tautulli processes')
try:
processes = kill_and_get_processes('Tautulli.exe')
except Exception as e:
logger.error('Failed to stop Tautulli: %s', e)
return 1
logger.info('Running %s', download_file)
try:
subprocess.call([file_path, '/S', '/NORUN', '/D=' + SCRIPT_PATH], creationflags=CREATE_NO_WINDOW)
status = 0
except Exception as e:
logger.exception('Failed to install Tautulli: %s', e)
status = -1
if status == 0:
logger.info('Tautulli updated to %s', version)
logger.info('Restarting Tautulli processes')
for process in processes:
logger.info('Starting process: %s', process)
subprocess.Popen(process, creationflags=CREATE_NO_WINDOW)
return status
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--xml', action='store_true')
opts = parser.parse_args()
if opts.xml:
xml_path = os.path.join(SCRIPT_PATH, 'TautulliUpdateTask.xml')
tree = ET.parse(xml_path)
task = tree.getroot()
match = re.match(r'{(.*)}', task.tag)
namespace = match.group(1)
namespaces = {'': namespace}
ET.register_namespace('', namespace)
for elem in task.iterfind('./Actions/Exec/Command', namespaces=namespaces):
elem.text = os.path.join(SCRIPT_PATH, 'updater.exe')
for elem in task.iterfind('./Actions/Exec/WorkingDirectory', namespaces=namespaces):
elem.text = SCRIPT_PATH
tree.write(xml_path, encoding='UTF-16')
else:
logger = init_logger()
try:
status_code = update_tautulli()
except Exception as exc:
status_code = exc
logger.debug('Update function returned status code %s', status_code)
sys.exit(status_code)

View File

@@ -99,6 +99,7 @@ PIDFILE = None
NOFORK = False
DOCKER = False
SNAP = False
SNAP_MIGRATE = False
FROZEN = False
SCHED = None
@@ -173,6 +174,18 @@ def initialize(config_file):
if _INITIALIZED:
return False
if SNAP_MIGRATE:
snap_common = os.environ['SNAP_COMMON']
old_data_dir = os.path.join(snap_common, 'Tautulli')
CONFIG.HTTPS_CERT = CONFIG.HTTPS_CERT.replace(old_data_dir, DATA_DIR)
CONFIG.HTTPS_CERT_CHAIN = CONFIG.HTTPS_CERT_CHAIN.replace(old_data_dir, DATA_DIR)
CONFIG.HTTPS_KEY = CONFIG.HTTPS_KEY.replace(old_data_dir, DATA_DIR)
CONFIG.LOG_DIR = CONFIG.LOG_DIR.replace(old_data_dir, DATA_DIR)
CONFIG.BACKUP_DIR = CONFIG.BACKUP_DIR.replace(old_data_dir, DATA_DIR)
CONFIG.CACHE_DIR = CONFIG.CACHE_DIR.replace(old_data_dir, DATA_DIR)
CONFIG.EXPORT_DIR = CONFIG.EXPORT_DIR.replace(old_data_dir, DATA_DIR)
CONFIG.NEWSLETTER_DIR = CONFIG.NEWSLETTER_DIR.replace(old_data_dir, DATA_DIR)
if CONFIG.HTTP_PORT < 21 or CONFIG.HTTP_PORT > 65535:
logger.warn("HTTP_PORT out of bounds: 21 < %s < 65535", CONFIG.HTTP_PORT)
CONFIG.HTTP_PORT = 8181
@@ -501,12 +514,16 @@ def schedule_job(func, name, hours=0, minutes=0, seconds=0, args=None):
SCHED.remove_job(name)
logger.info("Removed background task: %s", name)
elif job.trigger.interval != datetime.timedelta(hours=hours, minutes=minutes):
SCHED.reschedule_job(name, trigger=IntervalTrigger(
hours=hours, minutes=minutes, seconds=seconds, timezone=pytz.UTC), args=args)
SCHED.reschedule_job(
name, trigger=IntervalTrigger(
hours=hours, minutes=minutes, seconds=seconds, timezone=pytz.UTC),
args=args)
logger.info("Re-scheduled background task: %s", name)
elif hours > 0 or minutes > 0 or seconds > 0:
SCHED.add_job(func, id=name, trigger=IntervalTrigger(
hours=hours, minutes=minutes, seconds=seconds, timezone=pytz.UTC), args=args)
SCHED.add_job(
func, id=name, trigger=IntervalTrigger(
hours=hours, minutes=minutes, seconds=seconds, timezone=pytz.UTC),
args=args, misfire_grace_time=None)
logger.info("Scheduled background task: %s", name)
@@ -2278,7 +2295,12 @@ def upgrade():
return
def shutdown(restart=False, update=False, checkout=False, reset=False):
def shutdown(restart=False, update=False, checkout=False, reset=False,
_shutdown=True):
if FROZEN and common.PLATFORM == 'Windows' and update:
restart = False
_shutdown = False
webstart.stop()
# Shutdown the websocket connection
@@ -2351,14 +2373,15 @@ def shutdown(restart=False, update=False, checkout=False, reset=False):
else:
logger.info("Tautulli is shutting down...")
logger.shutdown()
if _shutdown:
logger.shutdown()
if WIN_SYS_TRAY_ICON:
WIN_SYS_TRAY_ICON.shutdown()
elif MAC_SYS_TRAY_ICON:
MAC_SYS_TRAY_ICON.shutdown()
if WIN_SYS_TRAY_ICON:
WIN_SYS_TRAY_ICON.shutdown()
elif MAC_SYS_TRAY_ICON:
MAC_SYS_TRAY_ICON.shutdown()
os._exit(0)
os._exit(0)
def generate_uuid():

View File

@@ -606,7 +606,8 @@ def schedule_callback(id, func=None, remove_job=False, args=None, **kwargs):
ACTIVITY_SCHED.add_job(
func, args=args, id=id, trigger=DateTrigger(
run_date=datetime.datetime.now(pytz.UTC) + datetime.timedelta(**kwargs),
timezone=pytz.UTC))
timezone=pytz.UTC),
misfire_grace_time=None)
def force_stop_stream(session_key, title, user):

View File

@@ -228,7 +228,7 @@ def delete_rows_from_table(table, row_ids):
if row_ids:
logger.info("Tautulli Database :: Deleting row ids %s from %s database table", row_ids, table)
# SQlite verions prior to 3.32.0 (2020-05-22) have maximum variable limit of 999
# SQlite versions prior to 3.32.0 (2020-05-22) have maximum variable limit of 999
# https://sqlite.org/limits.html
sqlite_max_variable_number = 999

View File

@@ -103,11 +103,21 @@ class BlacklistFilter(logging.Filter):
try:
if item in record.msg:
record.msg = record.msg.replace(item, 16 * '*')
if any(item in str(arg) for arg in record.args):
record.args = tuple(arg.replace(item, 16 * '*') if isinstance(arg, str) else arg
for arg in record.args)
args = []
for arg in record.args:
try:
arg_str = str(arg)
if item in arg_str:
arg_str = arg_str.replace(item, 16 * '*')
arg = arg_str
except:
pass
args.append(arg)
record.args = tuple(args)
except:
pass
return True
@@ -131,9 +141,15 @@ class RegexFilter(logging.Filter):
args = []
for arg in record.args:
matches = self.regex.findall(arg) if isinstance(arg, str) else []
for match in matches:
arg = self.replace(arg, match)
try:
arg_str = str(arg)
matches = self.regex.findall(arg_str)
if matches:
for match in matches:
arg_str = self.replace(arg_str, match)
arg = arg_str
except:
pass
args.append(arg)
record.args = tuple(args)
except:

View File

@@ -162,10 +162,11 @@ def set_startup():
plist_file_path = os.path.join(launch_agents, plist_file)
exe = sys.executable
run_args = [arg for arg in plexpy.ARGS if arg != '--nolaunch']
if plexpy.FROZEN:
args = [exe]
args = [exe] + run_args
else:
args = [exe, plexpy.FULL_PATH]
args = [exe, plexpy.FULL_PATH] + run_args
plist_dict = {
'Label': common.PRODUCT,

View File

@@ -82,7 +82,8 @@ def schedule_newsletter_job(newsletter_job_id, name='', func=None, remove_job=Fa
logger.info("Tautulli NewsletterHandler :: Re-scheduled newsletter: %s" % name)
elif not remove_job:
NEWSLETTER_SCHED.add_job(
func, args=args, id=newsletter_job_id, trigger=CronTrigger.from_crontab(cron))
func, args=args, id=newsletter_job_id, trigger=CronTrigger.from_crontab(cron),
misfire_grace_time=None)
logger.info("Tautulli NewsletterHandler :: Scheduled newsletter: %s" % name)

View File

@@ -18,4 +18,4 @@
from __future__ import unicode_literals
PLEXPY_BRANCH = "master"
PLEXPY_RELEASE_VERSION = "v2.6.4"
PLEXPY_RELEASE_VERSION = "v2.6.5"

View File

@@ -278,7 +278,8 @@ def check_github(scheduler=False, notify=False, use_cache=False):
logger.warn('Tautulli is running using Python 2. Unable to run automatic update.')
elif scheduler and plexpy.CONFIG.PLEXPY_AUTO_UPDATE and \
not plexpy.DOCKER and not plexpy.SNAP and not plexpy.FROZEN:
not plexpy.DOCKER and not plexpy.SNAP and \
not (plexpy.FROZEN and common.PLATFORM == 'Darwin'):
logger.info('Running automatic update.')
plexpy.shutdown(restart=True, update=True)
@@ -296,9 +297,15 @@ def update():
if not plexpy.UPDATE_AVAILABLE:
return
if plexpy.INSTALL_TYPE in ('docker', 'snap', 'windows', 'macos'):
if plexpy.INSTALL_TYPE in ('docker', 'snap', 'macos'):
return
elif plexpy.INSTALL_TYPE == 'windows':
logger.info('Calling Windows scheduled task to update Tautulli')
CREATE_NO_WINDOW = 0x08000000
subprocess.Popen(['SCHTASKS', '/Run', '/TN', 'TautulliUpdateTask'],
creationflags=CREATE_NO_WINDOW)
elif plexpy.INSTALL_TYPE == 'git':
output, err = runGit('pull --ff-only {} {}'.format(plexpy.CONFIG.GIT_REMOTE,
plexpy.CONFIG.GIT_BRANCH))

View File

@@ -148,27 +148,18 @@ def set_startup():
startup_reg_path = "Software\\Microsoft\\Windows\\CurrentVersion\\Run"
exe = sys.executable
run_args = [arg for arg in plexpy.ARGS if arg != '--nolaunch']
if plexpy.FROZEN:
args = [exe]
args = [exe] + run_args
else:
args = [exe, plexpy.FULL_PATH]
args = [exe, plexpy.FULL_PATH] + run_args
registry_key_name = '{}_{}'.format(common.PRODUCT, plexpy.CONFIG.PMS_UUID)
cmd = ' '.join(cmd_quote(arg) for arg in args).replace('python.exe', 'pythonw.exe').replace("'", '"')
if plexpy.CONFIG.LAUNCH_STARTUP:
try:
winreg.CreateKey(winreg.HKEY_CURRENT_USER, startup_reg_path)
registry_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, startup_reg_path, 0, winreg.KEY_WRITE)
winreg.SetValueEx(registry_key, common.PRODUCT, 0, winreg.REG_SZ, cmd)
winreg.CloseKey(registry_key)
logger.info("Added Tautulli to Windows system startup registry key.")
return True
except WindowsError as e:
logger.error("Failed to create Windows system startup registry key: %s", e)
return False
else:
# Check if registry value exists
# Rename old Tautulli registry key
try:
registry_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, startup_reg_path, 0, winreg.KEY_ALL_ACCESS)
winreg.QueryValueEx(registry_key, common.PRODUCT)
@@ -180,6 +171,33 @@ def set_startup():
try:
winreg.DeleteValue(registry_key, common.PRODUCT)
winreg.CloseKey(registry_key)
except WindowsError:
pass
try:
winreg.CreateKey(winreg.HKEY_CURRENT_USER, startup_reg_path)
registry_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, startup_reg_path, 0, winreg.KEY_WRITE)
winreg.SetValueEx(registry_key, registry_key_name, 0, winreg.REG_SZ, cmd)
winreg.CloseKey(registry_key)
logger.info("Added Tautulli to Windows system startup registry key.")
return True
except WindowsError as e:
logger.error("Failed to create Windows system startup registry key: %s", e)
return False
else:
# Check if registry value exists
try:
registry_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, startup_reg_path, 0, winreg.KEY_ALL_ACCESS)
winreg.QueryValueEx(registry_key, registry_key_name)
reg_value_exists = True
except WindowsError:
reg_value_exists = False
if reg_value_exists:
try:
winreg.DeleteValue(registry_key, registry_key_name)
winreg.CloseKey(registry_key)
logger.info("Removed Tautulli from Windows system startup registry key.")
return True
except WindowsError as e:

View File

@@ -51,14 +51,15 @@ apps:
tautulli:
command: >
usr/bin/python3 $SNAP/Tautulli.py
--datadir $SNAP_COMMON/Tautulli
--config $SNAP_COMMON/Tautulli/config.ini
--datadir $SNAP_USER_COMMON/Tautulli
--config $SNAP_USER_COMMON/Tautulli/config.ini
--quiet
--nolaunch
daemon: simple
restart-condition: on-abnormal
restart-delay: 5s
plugs:
- home
- network
- network-bind
environment: