Compare commits
35 Commits
v2.5.1-bet
...
v2.5.3
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1698622d63 | ||
![]() |
fa27271647 | ||
![]() |
d837811c68 | ||
![]() |
ad195f0969 | ||
![]() |
4a8748e322 | ||
![]() |
0f016c83ea | ||
![]() |
061ae44da4 | ||
![]() |
a8b90bf100 | ||
![]() |
eb3cd49bc4 | ||
![]() |
416d869288 | ||
![]() |
a116c26c25 | ||
![]() |
cc4ec53dac | ||
![]() |
63164c7ff5 | ||
![]() |
9815c014e8 | ||
![]() |
41843dc573 | ||
![]() |
cc6bd528a5 | ||
![]() |
2625ef5fb9 | ||
![]() |
dbd2d28877 | ||
![]() |
f70f814c70 | ||
![]() |
6710e42134 | ||
![]() |
78c5b45e43 | ||
![]() |
e562ec96fa | ||
![]() |
9b5e01c319 | ||
![]() |
0097532f4a | ||
![]() |
91935c9018 | ||
![]() |
83df807f7e | ||
![]() |
eb3db20340 | ||
![]() |
6dab6194ea | ||
![]() |
356f64cac0 | ||
![]() |
f77f289125 | ||
![]() |
280257477a | ||
![]() |
660141cb16 | ||
![]() |
cd8a899521 | ||
![]() |
cb577c51b8 | ||
![]() |
1c395ab10c |
16
.github/workflows/publish-release.yml
vendored
16
.github/workflows/publish-release.yml
vendored
@@ -53,7 +53,7 @@ jobs:
|
|||||||
uses: joncloud/makensis-action@v1.2
|
uses: joncloud/makensis-action@v1.2
|
||||||
with:
|
with:
|
||||||
script-file: ./package/Tautulli.nsi
|
script-file: ./package/Tautulli.nsi
|
||||||
arguments: /DVERSION=${{ steps.get_version.outputs.VERSION_NSIS }} /DINSTALLER_NAME=..\Tautulli-windows-${{ steps.get_version.outputs.RELEASE_VERSION }}.exe
|
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-more-plugins: true
|
||||||
include-custom-plugins-path: package/nsis-plugins
|
include-custom-plugins-path: package/nsis-plugins
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ jobs:
|
|||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: Tautulli-windows-installer
|
name: Tautulli-windows-installer
|
||||||
path: Tautulli-windows-${{ steps.get_version.outputs.RELEASE_VERSION }}.exe
|
path: Tautulli-windows-${{ steps.get_version.outputs.RELEASE_VERSION }}-x64.exe
|
||||||
|
|
||||||
- name: Post Status to Discord
|
- name: Post Status to Discord
|
||||||
uses: sarisia/actions-status-discord@v1
|
uses: sarisia/actions-status-discord@v1
|
||||||
@@ -117,13 +117,13 @@ jobs:
|
|||||||
|
|
||||||
- name: Create Installer
|
- name: Create Installer
|
||||||
run: |
|
run: |
|
||||||
sudo pkgbuild --install-location /Applications --version ${{ steps.get_version.outputs.VERSION }} --component ./dist/Tautulli.app --scripts ./package/macos-scripts Tautulli-macos-${{ steps.get_version.outputs.RELEASE_VERSION }}.pkg
|
sudo pkgbuild --install-location /Applications --version ${{ steps.get_version.outputs.VERSION }} --component ./dist/Tautulli.app --scripts ./package/macos-scripts Tautulli-macos-${{ steps.get_version.outputs.RELEASE_VERSION }}-x64.pkg
|
||||||
|
|
||||||
- name: Upload Installer
|
- name: Upload Installer
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: Tautulli-macos-installer
|
name: Tautulli-macos-installer
|
||||||
path: Tautulli-macos-${{ steps.get_version.outputs.RELEASE_VERSION }}.pkg
|
path: Tautulli-macos-${{ steps.get_version.outputs.RELEASE_VERSION }}-x64.pkg
|
||||||
|
|
||||||
- name: Post Status to Discord
|
- name: Post Status to Discord
|
||||||
uses: sarisia/actions-status-discord@v1
|
uses: sarisia/actions-status-discord@v1
|
||||||
@@ -188,8 +188,8 @@ jobs:
|
|||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
asset_path: ./Tautulli-windows-${{ steps.get_version.outputs.RELEASE_VERSION }}.exe
|
asset_path: ./Tautulli-windows-${{ steps.get_version.outputs.RELEASE_VERSION }}-x64.exe
|
||||||
asset_name: Tautulli-windows-${{ steps.get_version.outputs.RELEASE_VERSION }}.exe
|
asset_name: Tautulli-windows-${{ steps.get_version.outputs.RELEASE_VERSION }}-x64.exe
|
||||||
asset_content_type: application/vnd.microsoft.portable-executable
|
asset_content_type: application/vnd.microsoft.portable-executable
|
||||||
|
|
||||||
- name: Upload MacOS Installer
|
- name: Upload MacOS Installer
|
||||||
@@ -199,6 +199,6 @@ jobs:
|
|||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
asset_path: ./Tautulli-macos-${{ steps.get_version.outputs.RELEASE_VERSION }}.pkg
|
asset_path: ./Tautulli-macos-${{ steps.get_version.outputs.RELEASE_VERSION }}-x64.pkg
|
||||||
asset_name: Tautulli-macos-${{ steps.get_version.outputs.RELEASE_VERSION }}.pkg
|
asset_name: Tautulli-macos-${{ steps.get_version.outputs.RELEASE_VERSION }}-x64.pkg
|
||||||
asset_content_type: application/vnd.apple.installer+xml
|
asset_content_type: application/vnd.apple.installer+xml
|
||||||
|
46
CHANGELOG.md
46
CHANGELOG.md
@@ -1,34 +1,46 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## v2.5.1-beta (2020-06-26)
|
## v2.5.3 (2020-07-10)
|
||||||
|
|
||||||
|
* History:
|
||||||
|
* Fix: Unable to delete more than 1000 history entries at the same time.
|
||||||
|
* Notifications:
|
||||||
|
* Change: Python script notifications to run using the same Python interpreter as Tautulli.
|
||||||
|
* Newsletters:
|
||||||
|
* Fix: Unable to view newsletters with special characters.
|
||||||
|
* Other:
|
||||||
|
* Fix: Tautulli failing to start after enabling HTTPS when installed using the Windows / macOS installers.
|
||||||
|
* Fix: Startup script not working on macOS.
|
||||||
|
* Fix: Unable to hide dock icon on macOS with the pkg install. Refer to the FAQ regarding the Python rocket dock icon.
|
||||||
|
* Change: Added path to Python interpreter in system startup (daemon) scripts.
|
||||||
|
* Change: Added Python version to Google analytics.
|
||||||
|
|
||||||
|
|
||||||
|
## v2.5.2 (2020-07-01)
|
||||||
|
|
||||||
|
* Announcements:
|
||||||
|
* Tautulli now supports Python 3!
|
||||||
|
* Python 2 is still supported for the time being, but it is recommended to upgrade to Python 3.
|
||||||
* Notifications:
|
* Notifications:
|
||||||
* Fix: Error uploading images to Cloudinary on Python 2.
|
* Fix: Error uploading images to Cloudinary on Python 2.
|
||||||
* Fix: Testing browser notifications alert not disappearing.
|
* Fix: Testing browser notifications alert not disappearing.
|
||||||
* Change: Default recently added notification delay set to 300 seconds.
|
* Change: Default recently added notification delay set to 300 seconds.
|
||||||
* UI:
|
* UI:
|
||||||
* Fix: MacOS menu bar icon causing Tautulli to fail to start.
|
* Fix: MacOS menu bar icon causing Tautulli to fail to start.
|
||||||
* New: Added platform icon for LG devices.
|
* Fix: Unable to login to Tautulli on Python 2.
|
||||||
* Mobile App:
|
|
||||||
* Fix: Improved API security and validation when registering the Android app.
|
|
||||||
* Other:
|
|
||||||
* Fix: Error creating self-signed certificates on Python 3.
|
|
||||||
* Fix: Docker container not respecting the PUID and PGID environment variables.
|
|
||||||
* Fix: Tautulli login session cookie not set on the HTTP root path.
|
|
||||||
* Remove: Ability to login to Tautulli using a Plex username and password has been removed. Login using a Plex.tv account is only supported via OAuth.
|
|
||||||
|
|
||||||
|
|
||||||
## v2.5.0-beta (2020-05-31)
|
|
||||||
|
|
||||||
* Announcements:
|
|
||||||
* Tautulli now supports Python 3!
|
|
||||||
* Python 2 is still supported for the time being, but it is recommended to upgrade to Python 3.
|
|
||||||
* UI:
|
|
||||||
* New: Windows and MacOS setting to enable Tautulli to start automatically when you login.
|
* New: Windows and MacOS setting to enable Tautulli to start automatically when you login.
|
||||||
* New: Added menu bar icon for MacOS.
|
* New: Added menu bar icon for MacOS.
|
||||||
* New: Ability to import a Tautulli database in the settings.
|
* New: Ability to import a Tautulli database in the settings.
|
||||||
* New: Added Tautulli news area on the settings page.
|
* New: Added Tautulli news area on the settings page.
|
||||||
|
* New: Added platform icon for LG devices.
|
||||||
|
* Remove: Ability to login to Tautulli using a Plex username and password has been removed. Login using a Plex.tv account is only supported via OAuth.
|
||||||
|
* Mobile App:
|
||||||
|
* Fix: Improved API security and validation when registering the Android app.
|
||||||
|
* Docker:
|
||||||
|
* Fix: Docker container not respecting the PUID and PGID environment variables.
|
||||||
* Other:
|
* Other:
|
||||||
|
* Fix: Error creating self-signed certificates on Python 3.
|
||||||
|
* Fix: Tautulli login session cookie not set on the HTTP root path.
|
||||||
* New: Windows and MacOS app installers to install Tautulli without needing Python installed.
|
* New: Windows and MacOS app installers to install Tautulli without needing Python installed.
|
||||||
|
|
||||||
|
|
||||||
|
@@ -265,7 +265,10 @@ def main():
|
|||||||
if plexpy.CONFIG.SYS_TRAY_ICON:
|
if plexpy.CONFIG.SYS_TRAY_ICON:
|
||||||
# MacOS menu bar icon must be run on the main thread and is blocking
|
# MacOS menu bar icon must be run on the main thread and is blocking
|
||||||
# Start the rest of Tautulli on a new thread
|
# Start the rest of Tautulli on a new thread
|
||||||
threading.Thread(target=wait).start()
|
thread = threading.Thread(target=wait)
|
||||||
|
thread.daemon = True
|
||||||
|
thread.start()
|
||||||
|
|
||||||
plexpy.MAC_SYS_TRAY_ICON = macos.MacOSSystemTray()
|
plexpy.MAC_SYS_TRAY_ICON = macos.MacOSSystemTray()
|
||||||
plexpy.MAC_SYS_TRAY_ICON.start()
|
plexpy.MAC_SYS_TRAY_ICON.start()
|
||||||
else:
|
else:
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# Display information
|
# Display information
|
||||||
echo "This script will remove *.pyc files. These files are generated by Python, but they can cause conflicts after an upgrade. It's safe to remove them, because they will be regenerated."
|
echo "This script will remove *.pyc files. These files are generated by Python, but they can cause conflicts after an upgrade. It's safe to remove them, because they will be regenerated."
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# Parameter check
|
# Parameter check
|
||||||
if [ -z "$1" ]; then
|
if [ -z "$1" ]; then
|
||||||
|
@@ -230,20 +230,12 @@ ${next.modalIncludes()}
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ul id="donation_type" class="nav nav-pills" role="tablist" style="display: flex; justify-content: center; margin: 10px 0;">
|
<ul id="donation_type" class="nav nav-pills" role="tablist" style="display: flex; justify-content: center; margin: 10px 0;">
|
||||||
<li class="active"><a href="#patreon-donation" role="tab" data-toggle="tab">Patreon</a></li>
|
<li class="active"><a href="#github-donation" role="tab" data-toggle="tab">GitHub</a></li>
|
||||||
<li><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="#paypal-donation" role="tab" data-toggle="tab">PayPal</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
<div role="tabpanel" class="tab-pane active" id="patreon-donation" style="text-align: center">
|
<div role="tabpanel" class="tab-pane active" id="github-donation" style="text-align: center">
|
||||||
<p>
|
|
||||||
Click the button below to continue to Patreon.
|
|
||||||
</p>
|
|
||||||
<a href="${anon_url('https://www.patreon.com/join/tautulli')}" target="_blank">
|
|
||||||
<img src="images/become_a_patron_button.png" alt="Become a Patron" height="40">
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div role="tabpanel" class="tab-pane" id="github-donation" style="text-align: center">
|
|
||||||
<p>
|
<p>
|
||||||
Click the button below to continue to GitHub.
|
Click the button below to continue to GitHub.
|
||||||
</p>
|
</p>
|
||||||
@@ -251,6 +243,14 @@ ${next.modalIncludes()}
|
|||||||
<i class="fa fa-heart fa-sm" style="color: #ea4aaa;"></i> Sponsor
|
<i class="fa fa-heart fa-sm" style="color: #ea4aaa;"></i> Sponsor
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
<div role="tabpanel" class="tab-pane" id="patreon-donation" style="text-align: center">
|
||||||
|
<p>
|
||||||
|
Click the button below to continue to Patreon.
|
||||||
|
</p>
|
||||||
|
<a href="${anon_url('https://www.patreon.com/join/tautulli')}" target="_blank">
|
||||||
|
<img src="images/become_a_patron_button.png" alt="Become a Patron" height="40">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
<div role="tabpanel" class="tab-pane" id="paypal-donation" style="text-align: center">
|
<div role="tabpanel" class="tab-pane" id="paypal-donation" style="text-align: center">
|
||||||
<p>
|
<p>
|
||||||
Click the button below to continue to PayPal.
|
Click the button below to continue to PayPal.
|
||||||
|
@@ -38,7 +38,7 @@
|
|||||||
<th align="left" id="count">Total Movies / TV Shows / Artists</th>
|
<th align="left" id="count">Total Movies / TV Shows / Artists</th>
|
||||||
<th align="left" id="parent_count">Total Seasons / Albums</th>
|
<th align="left" id="parent_count">Total Seasons / Albums</th>
|
||||||
<th align="left" id="child_count">Total Episodes / Tracks</th>
|
<th align="left" id="child_count">Total Episodes / Tracks</th>
|
||||||
<th align="left" id="last_accessed">Last Accessed</th>
|
<th align="left" id="last_accessed">Last Streamed</th>
|
||||||
<th align="left" id="last_played">Last Played</th>
|
<th align="left" id="last_played">Last Played</th>
|
||||||
<th align="left" id="total_plays">Total Plays</th>
|
<th align="left" id="total_plays">Total Plays</th>
|
||||||
<th align="left" id="total_duration">Total Played Duration</th>
|
<th align="left" id="total_duration">Total Played Duration</th>
|
||||||
|
@@ -284,7 +284,7 @@ DOCUMENTATION :: END
|
|||||||
<table class="display user_ip_table" id="user_ip_table-UID-${data['user_id']}" width="100%">
|
<table class="display user_ip_table" id="user_ip_table-UID-${data['user_id']}" width="100%">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th align="left" id="last_seen">Last Seen</th>
|
<th align="left" id="last_seen">Last Streamed</th>
|
||||||
<th align="left" id="ip_address">IP Address</th>
|
<th align="left" id="ip_address">IP Address</th>
|
||||||
<th align="left" id="platform">Last Platform</th>
|
<th align="left" id="platform">Last Platform</th>
|
||||||
<th align="left" id="player">Last Player</th>
|
<th align="left" id="player">Last Player</th>
|
||||||
|
@@ -34,7 +34,7 @@
|
|||||||
<th align="left" id="edit_row">Edit</th>
|
<th align="left" id="edit_row">Edit</th>
|
||||||
<th align="right" id="avatar"></th>
|
<th align="right" id="avatar"></th>
|
||||||
<th align="left" id="friendly_name">User</th>
|
<th align="left" id="friendly_name">User</th>
|
||||||
<th align="left" id="last_seen">Last Seen</th>
|
<th align="left" id="last_seen">Last Streamed</th>
|
||||||
<th align="left" id="last_known_ip">Last Known IP</th>
|
<th align="left" id="last_known_ip">Last Known IP</th>
|
||||||
<th align="left" id="last_platform">Last Platform</th>
|
<th align="left" id="last_platform">Last Platform</th>
|
||||||
<th align="left" id="last_player">Last Player</th>
|
<th align="left" id="last_player">Last Player</th>
|
||||||
|
@@ -38,6 +38,7 @@ load_rc_config ${name}
|
|||||||
status_cmd="${name}_status"
|
status_cmd="${name}_status"
|
||||||
stop_cmd="${name}_stop"
|
stop_cmd="${name}_stop"
|
||||||
|
|
||||||
|
command_interpreter="/usr/local/bin/python3"
|
||||||
command="${tautulli_dir}/Tautulli.py"
|
command="${tautulli_dir}/Tautulli.py"
|
||||||
command_args="--daemon --pidfile ${tautulli_pid} --quiet --nolaunch ${tautulli_flags}"
|
command_args="--daemon --pidfile ${tautulli_pid} --quiet --nolaunch ${tautulli_flags}"
|
||||||
|
|
||||||
|
@@ -31,11 +31,13 @@
|
|||||||
# sudo chown tautulli:tautulli -R /opt/Tautulli
|
# sudo chown tautulli:tautulli -R /opt/Tautulli
|
||||||
#
|
#
|
||||||
# - Adjust ExecStart= to point to:
|
# - Adjust ExecStart= to point to:
|
||||||
# 1. Your Tautulli executable
|
# 1. Your Python interpreter (get the path with "command -v python3")
|
||||||
|
# - Default: /usr/bin/python3
|
||||||
|
# 2. Your Tautulli executable
|
||||||
# - Default: /opt/Tautulli/Tautulli.py
|
# - Default: /opt/Tautulli/Tautulli.py
|
||||||
# 2. Your config file (recommended is to put it somewhere in /etc)
|
# 3. Your config file (recommended is to put it somewhere in /etc)
|
||||||
# - Default: --config /opt/Tautulli/config.ini
|
# - Default: --config /opt/Tautulli/config.ini
|
||||||
# 3. Your datadir (recommended is to NOT put it in your Tautulli exec dir)
|
# 4. Your datadir (recommended is to NOT put it in your Tautulli exec dir)
|
||||||
# - Default: --datadir /opt/Tautulli
|
# - Default: --datadir /opt/Tautulli
|
||||||
#
|
#
|
||||||
# - Adjust User= and Group= to the user/group you want Tautulli to run as.
|
# - Adjust User= and Group= to the user/group you want Tautulli to run as.
|
||||||
@@ -50,7 +52,7 @@ Wants=network-online.target
|
|||||||
After=network-online.target
|
After=network-online.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
ExecStart=/opt/Tautulli/Tautulli.py --config /opt/Tautulli/config.ini --datadir /opt/Tautulli --quiet --daemon --nolaunch
|
ExecStart=/usr/bin/python3 /opt/Tautulli/Tautulli.py --config /opt/Tautulli/config.ini --datadir /opt/Tautulli --quiet --daemon --nolaunch
|
||||||
GuessMainPID=no
|
GuessMainPID=no
|
||||||
Type=forking
|
Type=forking
|
||||||
User=tautulli
|
User=tautulli
|
||||||
|
@@ -16,10 +16,10 @@ analysis = Analysis(
|
|||||||
('../CHANGELOG.md', '.'),
|
('../CHANGELOG.md', '.'),
|
||||||
('../LICENSE', '.'),
|
('../LICENSE', '.'),
|
||||||
('../version.txt', '.'),
|
('../version.txt', '.'),
|
||||||
('../lib/ipwhois/data', 'data')
|
('../lib/ipwhois/data', 'ipwhois/data')
|
||||||
],
|
],
|
||||||
excludes=['FixTk', 'tcl', 'tk', '_tkinter', 'tkinter', 'Tkinter'],
|
excludes=['FixTk', 'tcl', 'tk', '_tkinter', 'tkinter', 'Tkinter'],
|
||||||
hiddenimports=['Foundation', 'AppKit'],
|
hiddenimports=['pkg_resources.py2_warn', 'Foundation', 'AppKit', 'cheroot.ssl', 'cheroot.ssl.builtin'],
|
||||||
cipher=block_cipher
|
cipher=block_cipher
|
||||||
)
|
)
|
||||||
pyz = PYZ(
|
pyz = PYZ(
|
||||||
@@ -47,5 +47,9 @@ app = BUNDLE(
|
|||||||
name='Tautulli.app',
|
name='Tautulli.app',
|
||||||
icon='../data/interfaces/default/images/logo-circle.icns',
|
icon='../data/interfaces/default/images/logo-circle.icns',
|
||||||
bundle_identifier='com.Tautulli.Tautulli',
|
bundle_identifier='com.Tautulli.Tautulli',
|
||||||
version=VERSION
|
version=VERSION,
|
||||||
|
info_plist={
|
||||||
|
'LSBackgroundOnly': True,
|
||||||
|
'LSUIElement': True
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
@@ -16,6 +16,7 @@ analysis = Analysis(
|
|||||||
('..\\lib\\ipwhois\\data', 'data')
|
('..\\lib\\ipwhois\\data', 'data')
|
||||||
],
|
],
|
||||||
excludes=['FixTk', 'tcl', 'tk', '_tkinter', 'tkinter', 'Tkinter'],
|
excludes=['FixTk', 'tcl', 'tk', '_tkinter', 'tkinter', 'Tkinter'],
|
||||||
|
hiddenimports=['cheroot.ssl', 'cheroot.ssl.builtin'],
|
||||||
cipher=block_cipher,
|
cipher=block_cipher,
|
||||||
)
|
)
|
||||||
pyz = PYZ(
|
pyz = PYZ(
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
dialogText=`osascript -e 'set dialogText to button returned of (display dialog "Installation complete. Start Tautulli?" buttons {"Start", "Close"})'`;
|
dialogText=`osascript -e 'set dialogText to button returned of (display dialog "Installation complete. Start Tautulli?" buttons {"Start", "Close"})'`;
|
||||||
if [[ $dialogText == 'Start' ]]
|
if [[ $dialogText == 'Start' ]]
|
||||||
then
|
then
|
||||||
open /Applications/Tautulli.app
|
open /Applications/Tautulli.app
|
||||||
else
|
else
|
||||||
exit 0;
|
exit 0;
|
||||||
fi
|
fi
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
pyinstaller
|
pyinstaller
|
||||||
pyopenssl
|
pyopenssl
|
||||||
pycryptodomex
|
pycryptodomex
|
||||||
pyobjc
|
pyobjc-framework-Cocoa
|
@@ -37,8 +37,7 @@ from apscheduler.triggers.interval import IntervalTrigger
|
|||||||
from UniversalAnalytics import Tracker
|
from UniversalAnalytics import Tracker
|
||||||
import pytz
|
import pytz
|
||||||
|
|
||||||
PYTHON_VERSION = sys.version_info[:3]
|
PYTHON2 = sys.version_info[0] == 2
|
||||||
PYTHON2 = PYTHON_VERSION[0] == 2
|
|
||||||
|
|
||||||
if PYTHON2:
|
if PYTHON2:
|
||||||
import activity_handler
|
import activity_handler
|
||||||
@@ -2211,11 +2210,6 @@ def shutdown(restart=False, update=False, checkout=False, reset=False):
|
|||||||
logger.info("Removing pidfile %s", PIDFILE)
|
logger.info("Removing pidfile %s", PIDFILE)
|
||||||
os.remove(PIDFILE)
|
os.remove(PIDFILE)
|
||||||
|
|
||||||
if WIN_SYS_TRAY_ICON:
|
|
||||||
WIN_SYS_TRAY_ICON.shutdown()
|
|
||||||
elif MAC_SYS_TRAY_ICON:
|
|
||||||
MAC_SYS_TRAY_ICON.shutdown()
|
|
||||||
|
|
||||||
if restart:
|
if restart:
|
||||||
logger.info("Tautulli is restarting...")
|
logger.info("Tautulli is restarting...")
|
||||||
|
|
||||||
@@ -2238,7 +2232,7 @@ def shutdown(restart=False, update=False, checkout=False, reset=False):
|
|||||||
# https://bugs.python.org/issue19066
|
# https://bugs.python.org/issue19066
|
||||||
if NOFORK:
|
if NOFORK:
|
||||||
pass
|
pass
|
||||||
elif common.PLATFORM == 'Windows':
|
elif common.PLATFORM in ('Windows', 'Darwin'):
|
||||||
subprocess.Popen(args, cwd=os.getcwd())
|
subprocess.Popen(args, cwd=os.getcwd())
|
||||||
else:
|
else:
|
||||||
os.execv(exe, args)
|
os.execv(exe, args)
|
||||||
@@ -2248,6 +2242,11 @@ def shutdown(restart=False, update=False, checkout=False, reset=False):
|
|||||||
|
|
||||||
logger.shutdown()
|
logger.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)
|
||||||
|
|
||||||
|
|
||||||
@@ -2264,6 +2263,7 @@ def initialize_tracker():
|
|||||||
'appInstallerId': CONFIG.GIT_BRANCH,
|
'appInstallerId': CONFIG.GIT_BRANCH,
|
||||||
'dimension1': '{} {}'.format(common.PLATFORM, common.PLATFORM_RELEASE), # App Platform
|
'dimension1': '{} {}'.format(common.PLATFORM, common.PLATFORM_RELEASE), # App Platform
|
||||||
'dimension2': common.PLATFORM_LINUX_DISTRO, # Linux Distro
|
'dimension2': common.PLATFORM_LINUX_DISTRO, # Linux Distro
|
||||||
|
'dimension3': common.PYTHON_VERSION,
|
||||||
'userLanguage': SYS_LANGUAGE,
|
'userLanguage': SYS_LANGUAGE,
|
||||||
'documentEncoding': SYS_ENCODING,
|
'documentEncoding': SYS_ENCODING,
|
||||||
'noninteractive': True
|
'noninteractive': True
|
||||||
|
@@ -35,6 +35,7 @@ PLATFORM_RELEASE = platform.release()
|
|||||||
PLATFORM_VERSION = platform.version()
|
PLATFORM_VERSION = platform.version()
|
||||||
PLATFORM_LINUX_DISTRO = ' '.join(x for x in distro.linux_distribution() if x)
|
PLATFORM_LINUX_DISTRO = ' '.join(x for x in distro.linux_distribution() if x)
|
||||||
PLATFORM_DEVICE_NAME = platform.node()
|
PLATFORM_DEVICE_NAME = platform.node()
|
||||||
|
PYTHON_VERSION = platform.python_version()
|
||||||
BRANCH = version.PLEXPY_BRANCH
|
BRANCH = version.PLEXPY_BRANCH
|
||||||
RELEASE = version.PLEXPY_RELEASE_VERSION
|
RELEASE = version.PLEXPY_RELEASE_VERSION
|
||||||
|
|
||||||
|
@@ -75,7 +75,6 @@ _CONFIG_DEFINITIONS = {
|
|||||||
'PMS_UPDATE_CHECK_INTERVAL': (int, 'Advanced', 24),
|
'PMS_UPDATE_CHECK_INTERVAL': (int, 'Advanced', 24),
|
||||||
'PMS_WEB_URL': (str, 'PMS', 'https://app.plex.tv/desktop'),
|
'PMS_WEB_URL': (str, 'PMS', 'https://app.plex.tv/desktop'),
|
||||||
'TIME_FORMAT': (str, 'General', 'HH:mm'),
|
'TIME_FORMAT': (str, 'General', 'HH:mm'),
|
||||||
'ADD_LIVE_TV_LIBRARY': (int, 'Advanced', 1),
|
|
||||||
'ANON_REDIRECT': (str, 'General', 'http://www.nullrefer.com/?'),
|
'ANON_REDIRECT': (str, 'General', 'http://www.nullrefer.com/?'),
|
||||||
'API_ENABLED': (int, 'General', 1),
|
'API_ENABLED': (int, 'General', 1),
|
||||||
'API_KEY': (str, 'General', ''),
|
'API_KEY': (str, 'General', ''),
|
||||||
|
@@ -27,10 +27,10 @@ import time
|
|||||||
import plexpy
|
import plexpy
|
||||||
if plexpy.PYTHON2:
|
if plexpy.PYTHON2:
|
||||||
import logger
|
import logger
|
||||||
from helpers import cast_to_int, bool_true
|
from helpers import cast_to_int, bool_true, chunk
|
||||||
else:
|
else:
|
||||||
from plexpy import logger
|
from plexpy import logger
|
||||||
from plexpy.helpers import cast_to_int, bool_true
|
from plexpy.helpers import cast_to_int, bool_true, chunk
|
||||||
|
|
||||||
|
|
||||||
FILENAME = "tautulli.db"
|
FILENAME = "tautulli.db"
|
||||||
@@ -218,12 +218,16 @@ def delete_rows_from_table(table, row_ids):
|
|||||||
|
|
||||||
if row_ids:
|
if row_ids:
|
||||||
logger.info("Tautulli Database :: Deleting row ids %s from %s database table", row_ids, table)
|
logger.info("Tautulli Database :: Deleting row ids %s from %s database table", row_ids, table)
|
||||||
query = "DELETE FROM " + table + " WHERE id IN (%s) " % ','.join(['?'] * len(row_ids))
|
|
||||||
monitor_db = MonitorDatabase()
|
|
||||||
|
|
||||||
|
# SQlite verions prior to 3.32.0 (2020-05-22) have maximum variable limit of 999
|
||||||
|
# https://sqlite.org/limits.html
|
||||||
|
sqlite_max_variable_number = 999
|
||||||
|
|
||||||
|
monitor_db = MonitorDatabase()
|
||||||
try:
|
try:
|
||||||
monitor_db.action(query, row_ids)
|
for row_ids_group in chunk(row_ids, sqlite_max_variable_number):
|
||||||
return True
|
query = "DELETE FROM " + table + " WHERE id IN (%s) " % ','.join(['?'] * len(row_ids_group))
|
||||||
|
monitor_db.action(query, row_ids_group)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("Tautulli Database :: Failed to delete rows from %s database table: %s" % (table, e))
|
logger.error("Tautulli Database :: Failed to delete rows from %s database table: %s" % (table, e))
|
||||||
return False
|
return False
|
||||||
|
@@ -31,7 +31,7 @@ import datetime
|
|||||||
from functools import wraps
|
from functools import wraps
|
||||||
import hashlib
|
import hashlib
|
||||||
import imghdr
|
import imghdr
|
||||||
from future.moves.itertools import zip_longest
|
from future.moves.itertools import islice, zip_longest
|
||||||
import ipwhois
|
import ipwhois
|
||||||
import ipwhois.exceptions
|
import ipwhois.exceptions
|
||||||
import ipwhois.utils
|
import ipwhois.utils
|
||||||
@@ -1068,6 +1068,11 @@ def grouper(iterable, n, fillvalue=None):
|
|||||||
return zip_longest(fillvalue=fillvalue, *args)
|
return zip_longest(fillvalue=fillvalue, *args)
|
||||||
|
|
||||||
|
|
||||||
|
def chunk(it, size):
|
||||||
|
it = iter(it)
|
||||||
|
return iter(lambda: tuple(islice(it, size)), ())
|
||||||
|
|
||||||
|
|
||||||
def traverse_map(obj, func):
|
def traverse_map(obj, func):
|
||||||
if isinstance(obj, list):
|
if isinstance(obj, list):
|
||||||
new_obj = []
|
new_obj = []
|
||||||
|
@@ -88,6 +88,8 @@ def refresh_libraries():
|
|||||||
if result == 'insert':
|
if result == 'insert':
|
||||||
new_keys.append(section['section_id'])
|
new_keys.append(section['section_id'])
|
||||||
|
|
||||||
|
add_live_tv_library(refresh=True)
|
||||||
|
|
||||||
query = 'UPDATE library_sections SET is_active = 0 WHERE server_id != ? OR ' \
|
query = 'UPDATE library_sections SET is_active = 0 WHERE server_id != ? OR ' \
|
||||||
'section_id NOT IN ({})'.format(', '.join(['?'] * len(section_ids)))
|
'section_id NOT IN ({})'.format(', '.join(['?'] * len(section_ids)))
|
||||||
monitor_db.action(query=query, args=[plexpy.CONFIG.PMS_IDENTIFIER] + section_ids)
|
monitor_db.action(query=query, args=[plexpy.CONFIG.PMS_IDENTIFIER] + section_ids)
|
||||||
@@ -115,28 +117,28 @@ def refresh_libraries():
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def add_live_tv_library():
|
def add_live_tv_library(refresh=False):
|
||||||
if not plexpy.CONFIG.ADD_LIVE_TV_LIBRARY:
|
monitor_db = database.MonitorDatabase()
|
||||||
|
result = monitor_db.select_single('SELECT * FROM library_sections '
|
||||||
|
'WHERE section_id = ? and server_id = ?',
|
||||||
|
[common.LIVE_TV_SECTION_ID, plexpy.CONFIG.PMS_IDENTIFIER])
|
||||||
|
|
||||||
|
if result and not refresh or not result and refresh:
|
||||||
return
|
return
|
||||||
|
|
||||||
logger.info("Tautulli Libraries :: Adding Live TV library to the database.")
|
logger.info("Tautulli Libraries :: Adding Live TV library to the database.")
|
||||||
|
|
||||||
monitor_db = database.MonitorDatabase()
|
|
||||||
|
|
||||||
section_keys = {'server_id': plexpy.CONFIG.PMS_IDENTIFIER,
|
section_keys = {'server_id': plexpy.CONFIG.PMS_IDENTIFIER,
|
||||||
'section_id': common.LIVE_TV_SECTION_ID}
|
'section_id': common.LIVE_TV_SECTION_ID}
|
||||||
section_values = {'server_id': plexpy.CONFIG.PMS_IDENTIFIER,
|
section_values = {'server_id': plexpy.CONFIG.PMS_IDENTIFIER,
|
||||||
'section_id': common.LIVE_TV_SECTION_ID,
|
'section_id': common.LIVE_TV_SECTION_ID,
|
||||||
'section_name': common.LIVE_TV_SECTION_NAME,
|
'section_name': common.LIVE_TV_SECTION_NAME,
|
||||||
'section_type': 'live'
|
'section_type': 'live',
|
||||||
|
'is_active': 1
|
||||||
}
|
}
|
||||||
|
|
||||||
result = monitor_db.upsert('library_sections', key_dict=section_keys, value_dict=section_values)
|
result = monitor_db.upsert('library_sections', key_dict=section_keys, value_dict=section_values)
|
||||||
|
|
||||||
if result == 'insert':
|
|
||||||
plexpy.CONFIG.__setattr__('ADD_LIVE_TV_LIBRARY', 0)
|
|
||||||
plexpy.CONFIG.write()
|
|
||||||
|
|
||||||
|
|
||||||
def has_library_type(section_type):
|
def has_library_type(section_type):
|
||||||
monitor_db = database.MonitorDatabase()
|
monitor_db = database.MonitorDatabase()
|
||||||
|
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from io import open
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from apscheduler.triggers.cron import CronTrigger
|
from apscheduler.triggers.cron import CronTrigger
|
||||||
@@ -214,7 +215,7 @@ def get_newsletter(newsletter_uuid=None, newsletter_id_name=None):
|
|||||||
|
|
||||||
if newsletter_file in os.listdir(newsletter_folder):
|
if newsletter_file in os.listdir(newsletter_folder):
|
||||||
try:
|
try:
|
||||||
with open(newsletter_file_fp, 'r') as n_file:
|
with open(newsletter_file_fp, 'r', encoding='utf-8') as n_file:
|
||||||
newsletter = n_file.read()
|
newsletter = n_file.read()
|
||||||
return newsletter
|
return newsletter
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
|
@@ -1399,8 +1399,7 @@ class EMAIL(Notifier):
|
|||||||
success = True
|
success = True
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("Tautulli Notifiers :: {name} notification failed: {e}".format(
|
logger.error("Tautulli Notifiers :: %s notification failed: %s", self.NAME, e)
|
||||||
name=self.NAME, e=str(e).decode('utf-8')))
|
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
if mailserver:
|
if mailserver:
|
||||||
@@ -2970,7 +2969,7 @@ class SCRIPTS(Notifier):
|
|||||||
'.php': 'php',
|
'.php': 'php',
|
||||||
'.pl': 'perl',
|
'.pl': 'perl',
|
||||||
'.ps1': 'powershell -executionPolicy bypass -file',
|
'.ps1': 'powershell -executionPolicy bypass -file',
|
||||||
'.py': 'python',
|
'.py': 'python' if plexpy.FROZEN else sys.executable,
|
||||||
'.pyw': 'pythonw',
|
'.pyw': 'pythonw',
|
||||||
'.rb': 'ruby',
|
'.rb': 'ruby',
|
||||||
'.sh': ''
|
'.sh': ''
|
||||||
@@ -3008,7 +3007,7 @@ class SCRIPTS(Notifier):
|
|||||||
'TAUTULLI_PUBLIC_URL': plexpy.CONFIG.HTTP_BASE_URL + plexpy.HTTP_ROOT,
|
'TAUTULLI_PUBLIC_URL': plexpy.CONFIG.HTTP_BASE_URL + plexpy.HTTP_ROOT,
|
||||||
'TAUTULLI_APIKEY': plexpy.CONFIG.API_KEY,
|
'TAUTULLI_APIKEY': plexpy.CONFIG.API_KEY,
|
||||||
'TAUTULLI_ENCODING': plexpy.SYS_ENCODING,
|
'TAUTULLI_ENCODING': plexpy.SYS_ENCODING,
|
||||||
'TAUTULLI_PYTHON_VERSION': '.'.join(map(str, plexpy.PYTHON_VERSION))
|
'TAUTULLI_PYTHON_VERSION': common.PYTHON_VERSION
|
||||||
}
|
}
|
||||||
|
|
||||||
if user_id:
|
if user_id:
|
||||||
|
@@ -17,5 +17,5 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
PLEXPY_BRANCH = "beta"
|
PLEXPY_BRANCH = "master"
|
||||||
PLEXPY_RELEASE_VERSION = "v2.5.1-beta"
|
PLEXPY_RELEASE_VERSION = "v2.5.3"
|
@@ -41,6 +41,13 @@ else:
|
|||||||
from plexpy.users import Users, refresh_users
|
from plexpy.users import Users, refresh_users
|
||||||
from plexpy.plextv import PlexTV
|
from plexpy.plextv import PlexTV
|
||||||
|
|
||||||
|
# Monkey patch SameSite support into cookies.
|
||||||
|
# https://stackoverflow.com/a/50813092
|
||||||
|
try:
|
||||||
|
from http.cookies import Morsel
|
||||||
|
except ImportError:
|
||||||
|
from Cookie import Morsel
|
||||||
|
Morsel._reserved[str('samesite')] = str('SameSite')
|
||||||
|
|
||||||
JWT_ALGORITHM = 'HS256'
|
JWT_ALGORITHM = 'HS256'
|
||||||
JWT_COOKIE_NAME = 'tautulli_token_'
|
JWT_COOKIE_NAME = 'tautulli_token_'
|
||||||
|
20
start.sh
20
start.sh
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
if [[ "$TAUTULLI_DOCKER" = "True" ]]; then
|
if [[ "$TAUTULLI_DOCKER" == "True" ]]; then
|
||||||
if [[ -v PUID && -v PGID ]]; then
|
if [[ -n $PUID && -n $PGID ]]; then
|
||||||
getent group "$PGID" 2>&1 > /dev/null || groupadd -g "$PGID" tautulli
|
getent group "$PGID" 2>&1 > /dev/null || groupadd -g "$PGID" tautulli
|
||||||
getent passwd "$PUID" 2>&1 > /dev/null || useradd -r -u "$PUID" -g "$PGID" tautulli
|
getent passwd "$PUID" 2>&1 > /dev/null || useradd -r -u "$PUID" -g "$PGID" tautulli
|
||||||
|
|
||||||
@@ -14,8 +14,20 @@ if [[ "$TAUTULLI_DOCKER" = "True" ]]; then
|
|||||||
echo "Running Tautulli using user $user (uid=$PUID) and group $group (gid=$PGID)"
|
echo "Running Tautulli using user $user (uid=$PUID) and group $group (gid=$PGID)"
|
||||||
su "$user" -g "$group" -c "python /app/Tautulli.py --datadir /config"
|
su "$user" -g "$group" -c "python /app/Tautulli.py --datadir /config"
|
||||||
else
|
else
|
||||||
python Tautulli.py --datadir /config
|
python Tautulli.py --datadir /config
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
python Tautulli.py &> /dev/null &
|
python_versions=("python3" "python3.8" "python3.7" "python3.6" "python" "python2" "python2.7")
|
||||||
|
for cmd in "${python_versions[@]}"; do
|
||||||
|
if command -v "$cmd" >/dev/null; then
|
||||||
|
echo "Starting Tautulli with $cmd."
|
||||||
|
if [[ "$(uname -s)" == "Darwin" ]]; then
|
||||||
|
$cmd Tautulli.py &> /dev/null &
|
||||||
|
else
|
||||||
|
$cmd Tautulli.py --quiet --daemon
|
||||||
|
fi
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo "Unable to start Tautulli. No Python interpreter was found in the following options:" "${python_versions[@]}"
|
||||||
fi
|
fi
|
||||||
|
Reference in New Issue
Block a user