1
0
mirror of https://github.com/Tautulli/Tautulli.git synced 2025-03-12 04:35:40 -07:00

Rename Tautulli android app to remote app

This commit is contained in:
JonnyWong16 2021-06-27 12:43:22 -07:00
parent bde2372d90
commit 741f3d836a
No known key found for this signature in database
GPG Key ID: B1F1F9807184697A
7 changed files with 334 additions and 208 deletions

@ -3888,16 +3888,15 @@ a:hover .overlay-refresh-image:hover {
#changelog-modal ul {
padding-left: 20px;
}
.google-play-badge {
display:inline-block;
height:45px;
margin-top: 10px;
.app-badge {
float: left;
display: inline-block;
height: 46px;
margin-right: 10px;
margin-bottom: 5px;
}
.google-play-badge img {
border: 1px solid #3B3B3B;
border-radius: 6px;
box-sizing: border-box;
height: 45px;
.app-badge img {
height: 46px;
vertical-align: middle;
}

Binary file not shown.

Before

(image error) Size: 20 KiB

@ -0,0 +1,113 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
<!ENTITY ns_extend "http://ns.adobe.com/Extensibility/1.0/">
<!ENTITY ns_ai "http://ns.adobe.com/AdobeIllustrator/10.0/">
<!ENTITY ns_graphs "http://ns.adobe.com/Graphs/1.0/">
<!ENTITY ns_vars "http://ns.adobe.com/Variables/1.0/">
<!ENTITY ns_imrep "http://ns.adobe.com/ImageReplacement/1.0/">
<!ENTITY ns_sfw "http://ns.adobe.com/SaveForWeb/1.0/">
<!ENTITY ns_custom "http://ns.adobe.com/GenericCustomNamespace/1.0/">
<!ENTITY ns_adobe_xpath "http://ns.adobe.com/XPath/1.0/">
]>
<svg version="1.1" id="Livello_1" xmlns:x="&ns_extend;" xmlns:i="&ns_ai;" xmlns:graph="&ns_graphs;"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 5435.8 1604"
style="enable-background:new 0 0 5435.8 1604;" xml:space="preserve">
<style type="text/css">
.st0{fill-rule:evenodd;clip-rule:evenodd;}
.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#A6A6A6;}
.st2{fill:#FFFFFF;}
.st3{fill-rule:evenodd;clip-rule:evenodd;fill:#FFFFFF;}
.st4{fill-rule:evenodd;clip-rule:evenodd;fill:url(#SVGID_1_);}
.st5{fill-rule:evenodd;clip-rule:evenodd;fill:url(#SVGID_2_);}
.st6{fill-rule:evenodd;clip-rule:evenodd;fill:url(#SVGID_3_);}
.st7{fill-rule:evenodd;clip-rule:evenodd;fill:url(#SVGID_4_);}
.st8{opacity:0.2;fill-rule:evenodd;clip-rule:evenodd;enable-background:new ;}
.st9{opacity:0.12;fill-rule:evenodd;clip-rule:evenodd;enable-background:new ;}
.st10{opacity:0.25;fill-rule:evenodd;clip-rule:evenodd;fill:#FFFFFF;enable-background:new ;}
</style>
<metadata>
<sfw xmlns="&ns_sfw;">
<slices></slices>
<sliceSourceBounds bottomLeftOrigin="true" height="1604" width="5435.8" x="-2656.9" y="-784"></sliceSourceBounds>
</sfw>
</metadata>
<path class="st0" d="M5234.4,1604h-5033C90.4,1604,0,1513.6,0,1403.5v-1203C0,90,90.4,0,201.4,0h5033c110.9,0,201.4,90,201.4,200.5
v1203C5435.8,1513.6,5345.3,1604,5234.4,1604z"/>
<path class="st1" d="M5234.4,32.1c93.1,0,169.3,75.7,169.3,168.4v1203c0,92.7-75.7,168.4-169.3,168.4h-5033
c-93.1,0-169.3-75.7-169.3-168.4v-1203c0-92.7,75.7-168.4,169.3-168.4C201.4,32.1,5234.4,32.1,5234.4,32.1z M5234.4,0h-5033
C90.4,0,0,90.4,0,200.5v1203C0,1514,90.4,1604,201.4,1604h5033c110.9,0,201.4-90,201.4-200.5v-1203C5435.8,90.4,5345.3,0,5234.4,0z"
/>
<path class="st2" d="M2863.6,530.6c-36.3,0-66.9-12.7-91.1-37.7c-24-24.3-37.4-57.8-36.8-92c0-36.5,12.4-67.4,36.8-91.9
c24.1-25,54.7-37.7,91-37.7c35.9,0,66.5,12.7,91.1,37.7c24.4,25.3,36.8,56.2,36.8,91.9c-0.4,36.6-12.8,67.5-36.8,91.9
C2930.5,518,2899.9,530.6,2863.6,530.6z M1783.5,530.6c-35.5,0-66.3-12.5-91.5-37.2c-25-24.6-37.7-55.7-37.7-92.4
s12.7-67.8,37.7-92.4c24.7-24.7,55.5-37.2,91.5-37.2c17.6,0,34.7,3.5,51.1,10.6c16.1,6.9,29.2,16.3,38.9,27.8l2.4,2.9l-27.1,26.6
l-2.8-3.3c-15.3-18.2-35.8-27.1-62.9-27.1c-24.2,0-45.3,8.6-62.7,25.6c-17.5,17.1-26.4,39.5-26.4,66.6s8.9,49.5,26.4,66.6
c17.4,17,38.5,25.6,62.7,25.6c25.8,0,47.5-8.6,64.4-25.6c10-10,16.2-24,18.4-41.7h-86.9v-37.4h124.2l0.5,3.4
c0.9,6.3,1.8,12.8,1.8,18.8c0,34.5-10.4,62.4-31,83C1851.1,518.2,1820.5,530.6,1783.5,530.6z M3219.6,525.3h-38.3L3064,337.6l1,33.8
v153.8h-38.3V276.7h43.7l1.2,1.9l110.3,176.8l-1-33.7V276.7h38.7V525.3z M2575.8,525.3H2537V314.1h-67.3v-37.4H2643v37.4h-67.3
V525.3z M2438.1,525.3h-38.7V276.7h38.7V525.3z M2220.6,525.3h-38.7V314.1h-67.3v-37.4h173.3v37.4h-67.3V525.3z M2090.1,524.9
h-148.4V276.7h148.4v37.4h-109.6v68.2h98.9v37h-98.9v68.2h109.6V524.9z M2800.9,467.2c17.3,17.3,38.3,26,62.7,26
c25.1,0,45.6-8.5,62.7-26c17-17,25.6-39.3,25.6-66.2s-8.6-49.3-25.5-66.2c-17.3-17.3-38.4-26-62.7-26c-25.1,0-45.6,8.5-62.6,26
c-17,17-25.6,39.3-25.6,66.2S2784,450.3,2800.9,467.2L2800.9,467.2z"/>
<path class="st3" d="M2732.1,872.4c-94.5,0-171.1,71.7-171.1,170.6c0,98,77.1,170.6,171.1,170.6c94.5,0,171.1-72.2,171.1-170.6
C2903.2,944.1,2826.6,872.4,2732.1,872.4z M2732.1,1146c-51.7,0-96.2-42.8-96.2-103.4c0-61.5,44.6-103.4,96.2-103.4
c51.7,0,96.2,41.9,96.2,103.4C2828.4,1103.6,2783.8,1146,2732.1,1146z M2358.8,872.4c-94.5,0-171.1,71.7-171.1,170.6
c0,98,77.1,170.6,171.1,170.6c94.5,0,171.1-72.2,171.1-170.6C2529.9,944.1,2453.2,872.4,2358.8,872.4z M2358.8,1146
c-51.7,0-96.2-42.8-96.2-103.4c0-61.5,44.6-103.4,96.2-103.4c51.7,0,96.2,41.9,96.2,103.4C2455,1103.6,2410.5,1146,2358.8,1146z
M1914.6,924.5v72.2h173.3c-5.3,40.5-18.7,70.4-39.2,90.9c-25.4,25.4-64.6,53-133.7,53c-106.5,0-189.8-86-189.8-192.5
s83.3-192.5,189.8-192.5c57.5,0,99.4,22.7,130.5,51.7l51.2-51.2c-43.2-41.4-100.7-73.1-181.3-73.1c-146.1,0-268.7,119-268.7,264.7
c0,146.1,122.5,264.7,268.7,264.7c78.9,0,138.1-25.8,184.9-74.4c47.7-47.7,62.8-115,62.8-169.3c0-16.9-1.3-32.1-4-45h-244.6
C1914.6,923.6,1914.6,924.5,1914.6,924.5z M3731.5,980.7c-14.3-38.3-57.5-108.7-146.1-108.7c-87.8,0-160.8,69.1-160.8,170.6
c0,95.8,72.2,170.6,169.3,170.6c78,0,123.4-47.7,142.1-75.7l-57.9-38.8c-19.2,28.5-45.9,47.2-83.8,47.2c-38.3,0-65.1-17.4-82.9-51.7
l228.1-94.5C3739.5,999.8,3731.5,980.7,3731.5,980.7z M3498.9,1037.7c-1.8-65.9,51.2-99.4,89.1-99.4c29.9,0,54.8,14.7,63.3,36.1
L3498.9,1037.7z M3313.6,1203h74.9V701.8h-74.9V1203z M3190.6,910.3h-2.7c-16.9-20.1-49-38.3-90-38.3
c-85.1,0-163.5,74.9-163.5,171.1c0,95.8,78,169.8,163.5,169.8c40.5,0,73.1-18.3,90-38.8h2.7v24.5c0,65.1-34.8,100.2-90.9,100.2
c-45.9,0-74.4-33-86-60.6l-65.1,27.2c18.7,45,68.6,100.7,151,100.7c87.8,0,162.2-51.7,162.2-177.8V882.2h-70.8v28.1
C3191.1,910.3,3190.6,910.3,3190.6,910.3z M3104.6,1146c-51.7,0-94.9-43.2-94.9-102.9c0-60.2,43.2-103.8,94.9-103.8
c51.2,0,90.9,44.1,90.9,103.8C3196,1102.8,3155.9,1146,3104.6,1146z M4082.2,701.8h-179.1V1203h74.9v-189.8h104.3
c82.9,0,164.4-60.1,164.4-155.5S4165.5,701.8,4082.2,701.8z M4084.4,943.2h-106.5v-172h106.5c56.1,0,87.8,46.3,87.8,86
C4172.2,896.5,4140.1,943.2,4084.4,943.2z M4546.9,871.5c-54.4,0-110.5,24.1-133.7,76.6l66.4,27.6c14.3-27.6,40.5-37,68.2-37
c38.8,0,78,23.2,78.9,64.6v5.3c-13.4-7.6-42.8-19.2-78-19.2c-71.7,0-144.4,39.2-144.4,112.7c0,67.3,58.8,110.5,124.3,110.5
c50.3,0,78-22.7,95.3-49h2.7v38.8h72.2v-192C4698.8,921,4632.4,871.5,4546.9,871.5z M4537.5,1146c-24.5,0-58.8-12-58.8-42.8
c0-38.8,42.8-53.5,79.3-53.5c33,0,48.6,7.1,68.2,16.9C4620.8,1111.6,4582.8,1145.6,4537.5,1146z M4962.2,882.2l-86,217.4h-2.7
l-89.1-217.4h-80.6l133.7,303.9l-76.2,168.9h78L5045,882.2C5045,882.2,4962.2,882.2,4962.2,882.2z M4288,1203h74.9V701.8H4288V1203z
"/>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="1682.1077" y1="1339.4783" x2="1624.2924" y2="1309.6345" gradientTransform="matrix(11.64 0 0 -22.55 -18705.5957 30554.3691)">
<stop offset="0" style="stop-color:#00A0FF"/>
<stop offset="6.569999e-03" style="stop-color:#00A1FF"/>
<stop offset="0.2601" style="stop-color:#00BEFF"/>
<stop offset="0.5122" style="stop-color:#00D2FF"/>
<stop offset="0.7604" style="stop-color:#00DFFF"/>
<stop offset="1" style="stop-color:#00E3FF"/>
</linearGradient>
<path class="st4" d="M418.4,302.1c-11.6,12.5-18.3,31.6-18.3,56.6v886.7c0,25,6.7,44.1,18.7,56.1l3.1,2.7l496.8-496.8v-11.1
L421.5,299.4C421.5,299.4,418.4,302.1,418.4,302.1z"/>
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="1712.6624" y1="1274.8376" x2="1606.5613" y2="1274.8376" gradientTransform="matrix(9.145 0 0 -7.7 -14305.5381 10618.251)">
<stop offset="0" style="stop-color:#FFE000"/>
<stop offset="0.4087" style="stop-color:#FFBD00"/>
<stop offset="0.7754" style="stop-color:#FFA500"/>
<stop offset="1" style="stop-color:#FF9C00"/>
</linearGradient>
<path class="st5" d="M1084,973.5L918.3,807.8v-11.6L1084,630.5l3.6,2.2l196,111.4c56.1,31.6,56.1,83.8,0,115.8l-196,111.4
C1087.6,971.3,1084,973.5,1084,973.5z"/>
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="1707.4414" y1="1290.0475" x2="1646.682" y2="1211.2225" gradientTransform="matrix(15.02 0 0 -11.5775 -24650.2285 15829.6484)">
<stop offset="0" style="stop-color:#FF3A44"/>
<stop offset="1" style="stop-color:#C31162"/>
</linearGradient>
<path class="st6" d="M1087.6,971.3L918.3,802l-499.9,499.9c18.3,19.6,49,21.8,83.3,2.7L1087.6,971.3"/>
<linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="1660.6357" y1="1365.6676" x2="1687.767" y2="1330.4501" gradientTransform="matrix(15.02 0 0 -11.5715 -24650.2285 15809.9922)">
<stop offset="0" style="stop-color:#32A071"/>
<stop offset="6.850000e-02" style="stop-color:#2DA771"/>
<stop offset="0.4762" style="stop-color:#15CF74"/>
<stop offset="0.8009" style="stop-color:#06E775"/>
<stop offset="1" style="stop-color:#00F076"/>
</linearGradient>
<path class="st7" d="M1087.6,632.7L501.7,299.9c-34.3-19.6-65.1-16.9-83.3,2.7L918.3,802L1087.6,632.7z"/>
<path class="st8" d="M1084,967.7l-581.9,330.6c-32.5,18.7-61.5,17.4-80.2,0.4l-3.1,3.1l3.1,2.7c18.7,16.9,47.7,18.3,80.2-0.4
L1088,971.3C1088,971.3,1084,967.7,1084,967.7z"/>
<path class="st9" d="M1283.6,854.1l-200.1,113.6l3.6,3.6l196-111.4c28.1-16,41.9-37,41.9-57.9C1323.3,821.2,1309,839.4,1283.6,854.1
z"/>
<path class="st10" d="M501.7,305.7l781.9,444.2c25.4,14.3,39.7,33,41.9,52.1c0-20.9-13.8-41.9-41.9-57.9L501.7,299.9
c-56.1-32.1-101.6-5.3-101.6,58.8v5.8C400.1,300.3,445.6,274,501.7,305.7z"/>
</svg>

After

(image error) Size: 9.0 KiB

@ -46,7 +46,7 @@
<div class="form-group">
<label for="friendly_name">OneSignal Device ID</label>
<div class="row">
<div class="col-md-8">
<div class="col-md-12">
<div class="input-group">
<span class="input-group-btn">
<button class="btn btn-form reveal-token" type="button"><i class="fa fa-eye-slash"></i></button>

@ -56,7 +56,7 @@
<li role="presentation"><a id="nav-tabs-newsletter_agents" href="#tabs-newsletter_agents" aria-controls="tabs-newsletter_agents" role="tab" data-toggle="tab">Newsletter Agents</a></li>
<li role="presentation"><a id="nav-tabs-3rd_party_apis" href="#tabs-3rd_party_apis" aria-controls="tabs-3rd_party_apis" role="tab" data-toggle="tab">3rd Party APIs</a></li>
<li role="presentation"><a id="nav-tabs-import_backups" href="#tabs-import_backups" aria-controls="tabs-import_backups" role="tab" data-toggle="tab">Import & Backups</a></li>
<li role="presentation"><a id="nav-tabs-android_app" href="#tabs-android_app" aria-controls="tabs-android_app" role="tab" data-toggle="tab">Tautulli Remote Android App <sup><small>beta</small></sup></a></li>
<li role="presentation"><a id="nav-tabs-remote_app" href="#tabs-remote_app" aria-controls="tabs-remote_app" role="tab" data-toggle="tab">Tautulli Remote App <sup><small>beta</small></sup></a></li>
</ul>
</div>
<div class="col-md-9">
@ -696,7 +696,7 @@
<label>
<input type="checkbox" id="api_enabled" name="api_enabled" value="1" ${config['api_enabled']}> Enable API
</label>
<p class="help-block">Allow remote applications to interface with Tautulli.</p>
<p class="help-block">Allow external applications to interface with Tautulli.</p>
</div>
<div id="apioptions">
<div class="form-group">
@ -1559,20 +1559,28 @@
</div>
<div role="tabpanel" class="tab-pane" id="tabs-android_app">
<div role="tabpanel" class="tab-pane" id="tabs-remote_app">
<div class="padded-header">
<h3>Tautulli Remote Android App</h3>
<h3>Tautulli Remote App</h3>
</div>
<div class="form-group">
<label>Get the App</label>
<p class="help-block">
Get the <a href="${anon_url('https://play.google.com/store/apps/details?id=com.tautulli.tautulli_remote')}" target="_blank" rel="noreferrer">Tautulli Remote</a> app on Google Play<sup><small>TM</small></sup> to access Tautulli from your Android device!<br />
<span class="google-play-badge">
<a href="${anon_url('https://play.google.com/store/apps/details?id=com.tautulli.tautulli_remote')}" target="_blank" rel="noreferrer"><img alt="Get it on Google Play" src="images/en-play-badge.png" /></a>
Get the Tautulli Remote app on
<a href="${anon_url('https://play.google.com/store/apps/details?id=com.tautulli.tautulli_remote')}" target="_blank" rel="noreferrer">Google Play</a>
to access Tautulli from your mobile device!<br />
</p>
<p class="help-block">
<span class="app-badge">
<a href="${anon_url('https://play.google.com/store/apps/details?id=com.tautulli.tautulli_remote')}" target="_blank" rel="noreferrer"><img alt="Get it on Google Play" src="images/google-play-badge.svg" /></a>
</span>
<span class="app-badge">
<small>Google Play and the Google Play logo are trademarks of Google LLC.</small>
</span>
</p>
<br style="clear: both">
</div>
<div class="form-group">
<label>Registered Devices</label>
@ -2040,12 +2048,12 @@ Rating: {rating}/10 --> Rating: /10
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
<h4 class="modal-title">Register Tautulli Android App</h4>
<h4 class="modal-title">Register Tautulli Remote App</h4>
</div>
<div class="modal-body">
<label>Instructions</label>
<p class="help-block">
Scan the QR code below with the Tautulli Android app to automatically register it with the server (make sure the Tautulli Address below is correct)
Scan the QR code below with the Tautulli Remote app to automatically register it with the server (make sure the Tautulli Address below is correct)
or manually enter the connection info and device token into the app settings. This window will automatically close once device registration is successful.
</p>
<p class="help-block">

@ -1,4 +1,4 @@
# This file is part of Tautulli.
# 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
@ -2342,7 +2342,9 @@ def dbcheck():
# Rename notifiers in the database
result = c_db.execute('SELECT agent_label FROM notifiers '
'WHERE agent_label = "XBMC" OR agent_label = "OSX Notify"').fetchone()
'WHERE agent_label = "XBMC" '
'OR agent_label = "OSX Notify" '
'OR agent_name = "androidapp"').fetchone()
if result:
logger.debug("Altering database. Renaming notifiers.")
c_db.execute(
@ -2351,6 +2353,10 @@ def dbcheck():
c_db.execute(
'UPDATE notifiers SET agent_label = "macOS Notification Center" WHERE agent_label = "OSX Notify"'
)
c_db.execute(
'UPDATE notifiers SET agent_name = "remoteapp", agent_label = "Tautulli Remote App" '
'WHERE agent_name = "androidapp"'
)
# Upgrade exports table from earlier versions
try:

@ -98,7 +98,7 @@ AGENT_IDS = {'growl': 0,
'browser': 17,
'join': 18,
'discord': 20,
'androidapp': 21,
'remoteapp': 21,
'groupme': 22,
'mqtt': 23,
'zapier': 24,
@ -110,10 +110,10 @@ DEFAULT_CUSTOM_CONDITIONS = [{'parameter': '', 'operator': '', 'value': ''}]
def available_notification_agents():
agents = [{'label': 'Tautulli Remote Android App',
'name': 'androidapp',
'id': AGENT_IDS['androidapp'],
'class': ANDROIDAPP,
agents = [{'label': 'Tautulli Remote App',
'name': 'remoteapp',
'id': AGENT_IDS['remoteapp'],
'class': TAUTULLIREMOTEAPP,
'action_types': ('all',)
},
{'label': 'Boxcar',
@ -882,188 +882,6 @@ class Notifier(object):
return config_options
class ANDROIDAPP(Notifier):
"""
Tautulli Remote Android app notifications
"""
NAME = 'Tautulli Remote Android App'
_DEFAULT_CONFIG = {'device_id': '',
'priority': 3,
'notification_type': 0
}
def agent_notify(self, subject='', body='', action='', notification_id=None, **kwargs):
# Check mobile device is still registered
device = mobile_app.get_mobile_devices(device_id=self.config['device_id'])
if not device:
logger.warn("Tautulli Notifiers :: Unable to send Android app notification: device not registered.")
return
else:
device = device[0]
pretty_metadata = PrettyMetadata(kwargs.get('parameters'))
plaintext_data = {'notification_id': notification_id,
'subject': subject,
'body': body,
'action': action,
'priority': self.config['priority'],
'notification_type': self.config['notification_type'],
'session_key': pretty_metadata.parameters.get('session_key', ''),
'session_id': pretty_metadata.parameters.get('session_id', ''),
'user_id': pretty_metadata.parameters.get('user_id', ''),
'rating_key': pretty_metadata.parameters.get('rating_key', ''),
'poster_thumb': pretty_metadata.parameters.get('poster_thumb', '')}
#logger.debug("Plaintext data: {}".format(plaintext_data))
if CRYPTODOME:
# Key generation
salt = get_random_bytes(16)
passphrase = device['device_token']
key_length = 32 # AES256
iterations = 1000
key = PBKDF2(passphrase, salt, dkLen=key_length, count=iterations,
prf=lambda p, s: HMAC.new(p, s, SHA1).digest())
#logger.debug("Encryption key (base64): {}".format(base64.b64encode(key)))
# Encrypt using AES GCM
nonce = get_random_bytes(16)
cipher = AES.new(key, AES.MODE_GCM, nonce)
encrypted_data, gcm_tag = cipher.encrypt_and_digest(json.dumps(plaintext_data).encode('utf-8'))
encrypted_data += gcm_tag
#logger.debug("Encrypted data (base64): {}".format(base64.b64encode(encrypted_data)))
#logger.debug("GCM tag (base64): {}".format(base64.b64encode(gcm_tag)))
#logger.debug("Nonce (base64): {}".format(base64.b64encode(nonce)))
#logger.debug("Salt (base64): {}".format(base64.b64encode(salt)))
payload = {'app_id': mobile_app._ONESIGNAL_APP_ID,
'include_player_ids': [device['onesignal_id']],
'contents': {'en': 'Tautulli Notification'},
'data': {'encrypted': True,
'cipher_text': base64.b64encode(encrypted_data),
'nonce': base64.b64encode(nonce),
'salt': base64.b64encode(salt),
'server_id': plexpy.CONFIG.PMS_UUID}
}
else:
logger.warn("Tautulli Notifiers :: PyCryptodome library is missing. "
"Android app notifications will be sent unecrypted. "
"Install the library to encrypt the notifications.")
payload = {'app_id': mobile_app._ONESIGNAL_APP_ID,
'include_player_ids': [device['onesignal_id']],
'contents': {'en': 'Tautulli Notification'},
'data': {'encrypted': False,
'plain_text': plaintext_data,
'server_id': plexpy.CONFIG.PMS_UUID}
}
#logger.debug("OneSignal payload: {}".format(payload))
headers = {'Content-Type': 'application/json'}
return self.make_request("https://onesignal.com/api/v1/notifications", headers=headers, json=payload)
def get_devices(self):
db = database.MonitorDatabase()
try:
query = 'SELECT * FROM mobile_devices WHERE official = 1 ' \
'AND onesignal_id IS NOT NULL AND onesignal_id != ""'
result = db.select(query=query)
except Exception as e:
logger.warn("Tautulli Notifiers :: Unable to retrieve Android app devices list: %s." % e)
return {'': ''}
devices = {}
for device in result:
if device['friendly_name']:
devices[device['device_id']] = device['friendly_name']
else:
devices[device['device_id']] = device['device_name']
return devices
def _return_config_options(self):
config_option = []
if not CRYPTODOME:
config_option.append({
'label': 'Warning',
'description': '<strong>The PyCryptodome library is missing. '
'The content of your notifications will be sent unencrypted!</strong><br>'
'Please install the library to encrypt the notification contents. '
'Instructions can be found in the '
'<a href="' + helpers.anon_url(
'https://github.com/%s/%s/wiki/Frequently-Asked-Questions#notifications-pycryptodome'
% (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO)) + '" target="_blank">FAQ</a>.' ,
'input_type': 'help'
})
else:
config_option.append({
'label': 'Note',
'description': 'The PyCryptodome library was found. '
'The content of your notifications will be sent encrypted!',
'input_type': 'help'
})
config_option[-1]['description'] += '<br><br>Notifications are sent using the ' \
'<a href="' + helpers.anon_url('https://onesignal.com') + '" target="_blank">' \
'OneSignal</a>. Some user data is collected and cannot be encrypted. ' \
'Please read the <a href="' + helpers.anon_url(
'https://onesignal.com/privacy_policy') + '" target="_blank">' \
'OneSignal Privacy Policy</a> for more details.'
devices = self.get_devices()
if not devices:
config_option.append({
'label': 'Device',
'description': 'No mobile devices registered with OneSignal. '
'<a data-tab-destination="android_app" data-toggle="tab" data-dismiss="modal">'
'Get the Android App</a> and register a device.<br>'
'Note: Only devices registered with a valid OneSignal ID will appear in the list.',
'input_type': 'help'
})
else:
config_option.append({
'label': 'Device',
'value': self.config['device_id'],
'name': 'androidapp_device_id',
'description': 'Select your mobile device or '
'<a data-tab-destination="android_app" data-toggle="tab" data-dismiss="modal">'
'register a new device</a> with Tautulli.<br>'
'Note: Only devices registered with a valid OneSignal ID will appear in the list.',
'input_type': 'select',
'select_options': devices
})
config_option.append({
'label': 'Priority',
'value': self.config['priority'],
'name': 'androidapp_priority',
'description': 'Set the notification priority.',
'input_type': 'select',
'select_options': {1: 'Minimum', 2: 'Low', 3: 'Normal', 4: 'High'}
})
config_option.append({
'label': 'Notification Image Type',
'value': self.config['notification_type'],
'name': 'androidapp_notification_type',
'description': 'Set the notification image type.',
'input_type': 'select',
'select_options': {0: 'No notification image',
1: 'Small image (Expandable text)',
2: 'Large image (Non-expandable text)'
}
})
return config_option
class BOXCAR(Notifier):
"""
Boxcar notifications
@ -3399,6 +3217,188 @@ class SLACK(Notifier):
return config_option
class TAUTULLIREMOTEAPP(Notifier):
"""
Tautulli Remote app notifications
"""
NAME = 'Tautulli Remote App'
_DEFAULT_CONFIG = {'device_id': '',
'priority': 3,
'notification_type': 0
}
def agent_notify(self, subject='', body='', action='', notification_id=None, **kwargs):
# Check mobile device is still registered
device = mobile_app.get_mobile_devices(device_id=self.config['device_id'])
if not device:
logger.warn("Tautulli Notifiers :: Unable to send Tautulli Remote app notification: device not registered.")
return
else:
device = device[0]
pretty_metadata = PrettyMetadata(kwargs.get('parameters'))
plaintext_data = {'notification_id': notification_id,
'subject': subject,
'body': body,
'action': action,
'priority': self.config['priority'],
'notification_type': self.config['notification_type'],
'session_key': pretty_metadata.parameters.get('session_key', ''),
'session_id': pretty_metadata.parameters.get('session_id', ''),
'user_id': pretty_metadata.parameters.get('user_id', ''),
'rating_key': pretty_metadata.parameters.get('rating_key', ''),
'poster_thumb': pretty_metadata.parameters.get('poster_thumb', '')}
#logger.debug("Plaintext data: {}".format(plaintext_data))
if CRYPTODOME:
# Key generation
salt = get_random_bytes(16)
passphrase = device['device_token']
key_length = 32 # AES256
iterations = 1000
key = PBKDF2(passphrase, salt, dkLen=key_length, count=iterations,
prf=lambda p, s: HMAC.new(p, s, SHA1).digest())
#logger.debug("Encryption key (base64): {}".format(base64.b64encode(key)))
# Encrypt using AES GCM
nonce = get_random_bytes(16)
cipher = AES.new(key, AES.MODE_GCM, nonce)
encrypted_data, gcm_tag = cipher.encrypt_and_digest(json.dumps(plaintext_data).encode('utf-8'))
encrypted_data += gcm_tag
#logger.debug("Encrypted data (base64): {}".format(base64.b64encode(encrypted_data)))
#logger.debug("GCM tag (base64): {}".format(base64.b64encode(gcm_tag)))
#logger.debug("Nonce (base64): {}".format(base64.b64encode(nonce)))
#logger.debug("Salt (base64): {}".format(base64.b64encode(salt)))
payload = {'app_id': mobile_app._ONESIGNAL_APP_ID,
'include_player_ids': [device['onesignal_id']],
'contents': {'en': 'Tautulli Notification'},
'data': {'encrypted': True,
'cipher_text': base64.b64encode(encrypted_data),
'nonce': base64.b64encode(nonce),
'salt': base64.b64encode(salt),
'server_id': plexpy.CONFIG.PMS_UUID}
}
else:
logger.warn("Tautulli Notifiers :: PyCryptodome library is missing. "
"Tautulli Remote app notifications will be sent unecrypted. "
"Install the library to encrypt the notifications.")
payload = {'app_id': mobile_app._ONESIGNAL_APP_ID,
'include_player_ids': [device['onesignal_id']],
'contents': {'en': 'Tautulli Notification'},
'data': {'encrypted': False,
'plain_text': plaintext_data,
'server_id': plexpy.CONFIG.PMS_UUID}
}
#logger.debug("OneSignal payload: {}".format(payload))
headers = {'Content-Type': 'application/json'}
return self.make_request("https://onesignal.com/api/v1/notifications", headers=headers, json=payload)
def get_devices(self):
db = database.MonitorDatabase()
try:
query = 'SELECT * FROM mobile_devices WHERE official = 1 ' \
'AND onesignal_id IS NOT NULL AND onesignal_id != ""'
result = db.select(query=query)
except Exception as e:
logger.warn("Tautulli Notifiers :: Unable to retrieve Tautulli Remote app devices list: %s." % e)
return {'': ''}
devices = {}
for device in result:
if device['friendly_name']:
devices[device['device_id']] = device['friendly_name']
else:
devices[device['device_id']] = device['device_name']
return devices
def _return_config_options(self):
config_option = []
if not CRYPTODOME:
config_option.append({
'label': 'Warning',
'description': '<strong>The PyCryptodome library is missing. '
'The content of your notifications will be sent unencrypted!</strong><br>'
'Please install the library to encrypt the notification contents. '
'Instructions can be found in the '
'<a href="' + helpers.anon_url(
'https://github.com/%s/%s/wiki/Frequently-Asked-Questions#notifications-pycryptodome'
% (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO)) + '" target="_blank">FAQ</a>.' ,
'input_type': 'help'
})
else:
config_option.append({
'label': 'Note',
'description': 'The PyCryptodome library was found. '
'The content of your notifications will be sent encrypted!',
'input_type': 'help'
})
config_option[-1]['description'] += '<br><br>Notifications are sent using the ' \
'<a href="' + helpers.anon_url('https://onesignal.com') + '" target="_blank">' \
'OneSignal</a>. Some user data is collected and cannot be encrypted. ' \
'Please read the <a href="' + helpers.anon_url(
'https://onesignal.com/privacy_policy') + '" target="_blank">' \
'OneSignal Privacy Policy</a> for more details.'
devices = self.get_devices()
if not devices:
config_option.append({
'label': 'Device',
'description': 'No mobile devices registered with OneSignal. '
'<a data-tab-destination="remote_app" data-toggle="tab" data-dismiss="modal">'
'Get the Tautulli Remote App</a> and register a device.<br>'
'Note: Only devices registered with a valid OneSignal ID will appear in the list.',
'input_type': 'help'
})
else:
config_option.append({
'label': 'Device',
'value': self.config['device_id'],
'name': 'remoteapp_device_id',
'description': 'Select your mobile device or '
'<a data-tab-destination="remote_app" data-toggle="tab" data-dismiss="modal">'
'register a new device</a> with Tautulli.<br>'
'Note: Only devices registered with a valid OneSignal ID will appear in the list.',
'input_type': 'select',
'select_options': devices
})
config_option.append({
'label': 'Priority',
'value': self.config['priority'],
'name': 'remoteapp_priority',
'description': 'Set the notification priority.',
'input_type': 'select',
'select_options': {1: 'Minimum', 2: 'Low', 3: 'Normal', 4: 'High'}
})
config_option.append({
'label': 'Notification Image Type',
'value': self.config['notification_type'],
'name': 'remoteapp_notification_type',
'description': 'Set the notification image type.',
'input_type': 'select',
'select_options': {0: 'No notification image',
1: 'Small image (Expandable text)',
2: 'Large image (Non-expandable text)'
}
})
return config_option
class TELEGRAM(Notifier):
"""
Telegram notifications