Jellyfin Login ✓
This commit is contained in:
@@ -19,10 +19,10 @@ not well documented (read as: not documented at all).
|
|||||||
|
|
||||||
## Major Differences compared to Tautulli
|
## Major Differences compared to Tautulli
|
||||||
|
|
||||||
* No Plex/PMS Support
|
* Dropped Plex/PMS Support
|
||||||
* Removed Google Analytics
|
* Dropped Google Analytics
|
||||||
* Removed Python2 support
|
* Dropped Python2 support
|
||||||
* Removed import feature from varius abondonded projects
|
* Dropped import from varius abondonded projects
|
||||||
|
|
||||||
## Installation & Support
|
## Installation & Support
|
||||||
|
|
||||||
|
@@ -176,8 +176,7 @@ from jellypy import common, helpers
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<input type="hidden" id="jellyfin_identifier" name="jellyfin_identifier" value="${config['jellyfin_identifier']}">
|
<a class="btn btn-dark" id="verify-jellyfin-server" href="#" role="button">Verify</a>
|
||||||
<a class="btn btn-dark" id="verify-plex-server" href="#" role="button">Verify</a>
|
|
||||||
<span style="margin-left: 10px; display: none;" id="jellyfin-verify-status"></span>
|
<span style="margin-left: 10px; display: none;" id="jellyfin-verify-status"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -185,7 +184,7 @@ from jellypy import common, helpers
|
|||||||
<h3>Activity Logging</h3>
|
<h3>Activity Logging</h3>
|
||||||
<div class="wizard-input-section">
|
<div class="wizard-input-section">
|
||||||
<p class="help-block">
|
<p class="help-block">
|
||||||
Tautulli will keep a history of all streaming activity on your Plex server.
|
JellyPy will keep a history of all streaming activity on your Jellyfin server.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="wizard-input-section">
|
<div class="wizard-input-section">
|
||||||
@@ -216,7 +215,7 @@ from jellypy import common, helpers
|
|||||||
<h3>Notifications</h3>
|
<h3>Notifications</h3>
|
||||||
<div class="wizard-input-section">
|
<div class="wizard-input-section">
|
||||||
<p class="help-block">
|
<p class="help-block">
|
||||||
Tautulli can send a wide variety of notifications to alert you of activity on your Plex
|
JellyPy can send a wide variety of notifications to alert you of activity on your Jellyfin
|
||||||
server.
|
server.
|
||||||
</p>
|
</p>
|
||||||
<p class="help-block">
|
<p class="help-block">
|
||||||
@@ -225,20 +224,6 @@ from jellypy import common, helpers
|
|||||||
wizard.
|
wizard.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="wizard-card" data-cardname="card7">
|
|
||||||
<h3>Database Import</h3>
|
|
||||||
<div class="wizard-input-section">
|
|
||||||
<p class="help-block">
|
|
||||||
If you have an existing Tautulli, PlexWatch, or Plexivity database, you can import the data
|
|
||||||
into Tautulli.
|
|
||||||
</p>
|
|
||||||
<p class="help-block">
|
|
||||||
To import a database, navigate to the <strong>Settings</strong> page
|
|
||||||
and to the <strong>Import & Backups</strong> tab after you have completed this setup wizard.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Required fields but hidden -->
|
<!-- Required fields but hidden -->
|
||||||
<div style="display: none;">
|
<div style="display: none;">
|
||||||
@@ -266,7 +251,6 @@ from jellypy import common, helpers
|
|||||||
<input type="text" name="home_stats_cards" id="home_stats_cards" value="first_run_wizard">
|
<input type="text" name="home_stats_cards" id="home_stats_cards" value="first_run_wizard">
|
||||||
<input type="text" name="home_library_cards" id="home_library_cards" value="first_run_wizard">
|
<input type="text" name="home_library_cards" id="home_library_cards" value="first_run_wizard">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
@@ -308,7 +292,7 @@ from jellypy import common, helpers
|
|||||||
return retValue;
|
return retValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
function validatejellyfinip(el) {
|
function validateJellyfinIp(el) {
|
||||||
var valid_jellyfin_ip = el.val();
|
var valid_jellyfin_ip = el.val();
|
||||||
var retValue = {};
|
var retValue = {};
|
||||||
|
|
||||||
@@ -324,7 +308,7 @@ from jellypy import common, helpers
|
|||||||
return retValue;
|
return retValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
function validatejellyfintoken(el) {
|
function validateJellyfinToken(el) {
|
||||||
var valid_jellyfin_token = el.val();
|
var valid_jellyfin_token = el.val();
|
||||||
var retValue = {};
|
var retValue = {};
|
||||||
|
|
||||||
@@ -518,24 +502,27 @@ from jellypy import common, helpers
|
|||||||
var jellyfin_verified = false;
|
var jellyfin_verified = false;
|
||||||
var authenticated = false;
|
var authenticated = false;
|
||||||
|
|
||||||
$("#verify-plex-server").click(function () {
|
$("#verify-jellyfin-server").click(function () {
|
||||||
if (!(jellyfin_verified)) {
|
if (!(jellyfin_verified)) {
|
||||||
var jellyfin_ip = $("#jellyfin_ip").val().trim();
|
var jellyfin_ip = $("#jellyfin_ip").val().trim();
|
||||||
var jellyfin_port = $("#jellyfin_port").val().trim();
|
var jellyfin_port = $("#jellyfin_port").val().trim();
|
||||||
var jellyfin_identifier = $("#jellyfin_identifier").val();
|
|
||||||
var jellyfin_ssl = $("#jellyfin_ssl").val();
|
var jellyfin_ssl = $("#jellyfin_ssl").val();
|
||||||
var jellyfin_is_remote = $("#jellyfin_is_remote").val();
|
var jellyfin_is_remote = $("#jellyfin_is_remote").val();
|
||||||
|
var jellyfin_user = $("#jellyfin_user").val().trim();
|
||||||
|
var jellyfin_password = $("#jellyfin_password").val();
|
||||||
|
|
||||||
if ((jellyfin_ip !== '') || (jellyfin_port !== '')) {
|
if ((jellyfin_ip !== '') || (jellyfin_port !== '')) {
|
||||||
$("#jellyfin-verify-status").html('<i class="fa fa-refresh fa-spin"></i> Verifying server...');
|
$("#jellyfin-verify-status").html('<i class="fa fa-refresh fa-spin"></i> Verifying server...');
|
||||||
$('#jellyfin-verify-status').fadeIn('fast');
|
$('#jellyfin-verify-status').fadeIn('fast');
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: 'get_server_id',
|
url: 'check_login',
|
||||||
data: {
|
data: {
|
||||||
hostname: jellyfin_ip,
|
hostname: jellyfin_ip,
|
||||||
port: jellyfin_port,
|
port: jellyfin_port,
|
||||||
identifier: jellyfin_identifier,
|
|
||||||
ssl: jellyfin_ssl,
|
ssl: jellyfin_ssl,
|
||||||
remote: jellyfin_is_remote
|
remote: jellyfin_is_remote,
|
||||||
|
user: jellyfin_user,
|
||||||
|
password: jellyfin_password
|
||||||
},
|
},
|
||||||
cache: true,
|
cache: true,
|
||||||
async: true,
|
async: true,
|
||||||
@@ -549,12 +536,12 @@ from jellypy import common, helpers
|
|||||||
var identifier = result.identifier;
|
var identifier = result.identifier;
|
||||||
if (identifier) {
|
if (identifier) {
|
||||||
$("#jellyfin_identifier").val(identifier);
|
$("#jellyfin_identifier").val(identifier);
|
||||||
$("#jellyfin-verify-status").html('<i class="fa fa-check"></i> Server found!');
|
$("#jellyfin-verify-status").html('<i class="fa fa-check"></i> Login successfull!');
|
||||||
$('#jellyfin-verify-status').fadeIn('fast');
|
$('#jellyfin-verify-status').fadeIn('fast');
|
||||||
jellyfin_verified = true;
|
jellyfin_verified = true;
|
||||||
$("#jellyfin_valid").val("valid");
|
$("#jellyfin_valid").val("valid");
|
||||||
} else {
|
} else {
|
||||||
$("#jellyfin-verify-status").html('<i class="fa fa-exclamation-circle"></i> This is not a Plex Server!');
|
$("#jellyfin-verify-status").html('<i class="fa fa-exclamation-circle"></i> This is not a Jellyfin Server!');
|
||||||
$('#jellyfin-verify-status').fadeIn('fast');
|
$('#jellyfin-verify-status').fadeIn('fast');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -653,19 +653,6 @@ def is_public_ip(host):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def get_ip(host):
|
|
||||||
ip_address = ''
|
|
||||||
if is_valid_ip(host):
|
|
||||||
return host
|
|
||||||
elif not re.match(r'^[0-9]+(?:\.[0-9]+){3}(?!\d*-[a-z0-9]{6})$', host):
|
|
||||||
try:
|
|
||||||
ip_address = socket.getaddrinfo(host, None)[0][4][0]
|
|
||||||
logger.debug("IP Checker :: Resolved %s to %s." % (host, ip_address))
|
|
||||||
except:
|
|
||||||
logger.error("IP Checker :: Bad IP or hostname provided: %s." % host)
|
|
||||||
return ip_address
|
|
||||||
|
|
||||||
|
|
||||||
def is_valid_ip(address):
|
def is_valid_ip(address):
|
||||||
try:
|
try:
|
||||||
return IP(address)
|
return IP(address)
|
||||||
|
@@ -35,11 +35,13 @@ class Jellyfin(object):
|
|||||||
PRODUCT, RELEASE, PRODUCT, jellypy.CONFIG.JELLYFIN_CLIENT_UUID
|
PRODUCT, RELEASE, PRODUCT, jellypy.CONFIG.JELLYFIN_CLIENT_UUID
|
||||||
)
|
)
|
||||||
self.jf.config.data["http.user_agent"] = PRODUCT
|
self.jf.config.data["http.user_agent"] = PRODUCT
|
||||||
self.jf.config.data["auth.ssl"] = not jellypy.CONFIG.JELLYFIN_SSL
|
self.jf.config.data["auth.ssl"] = jellypy.CONFIG.JELLYFIN_SSL
|
||||||
self.url = url
|
self.url = url
|
||||||
|
self.id = None
|
||||||
|
self.token = token
|
||||||
|
|
||||||
if token:
|
if self.token:
|
||||||
self.login(token=token)
|
self.login()
|
||||||
|
|
||||||
def get_library(self, section_id):
|
def get_library(self, section_id):
|
||||||
return self.jf.library.sectionByID(str(section_id))
|
return self.jf.library.sectionByID(str(section_id))
|
||||||
@@ -50,22 +52,23 @@ class Jellyfin(object):
|
|||||||
def get_item(self, rating_key):
|
def get_item(self, rating_key):
|
||||||
return self.jf.fetchItem(rating_key)
|
return self.jf.fetchItem(rating_key)
|
||||||
|
|
||||||
def login(self, user=None, password=None, token=None) -> bool:
|
def login(self, user=None, password=None) -> bool:
|
||||||
if user and password:
|
if user and password and self.url:
|
||||||
self.jf.auth.connect_to_address(self.url)
|
self.jf.auth.connect_to_address(self.url)
|
||||||
result = self.jf.auth.login(self.url, user, password)
|
result = self.jf.auth.login(self.url, user, password)
|
||||||
|
|
||||||
if "AccessToken" in result:
|
if "AccessToken" in result:
|
||||||
credentials = self.jf.auth.credentials.get_credentials()
|
credentials = self.jf.auth.credentials.get_credentials()
|
||||||
pprint.pprint(credentials)
|
pprint.pprint(credentials)
|
||||||
server = credentials["Servers"][0]
|
self.id = credentials["Servers"][0]["Id"]
|
||||||
server["uuid"] = server["Id"]
|
|
||||||
server["username"] = user
|
|
||||||
|
|
||||||
# jellypy.CONFIG.JELLYFIN_TOKEN =
|
# jellypy.CONFIG.JELLYFIN_TOKEN =
|
||||||
#
|
#
|
||||||
# self._connect_client(server)
|
# self._connect_client(server)
|
||||||
# self.credentials.append(server)
|
# self.credentials.append(server)
|
||||||
# self.save_credentials()
|
# self.save_credentials()
|
||||||
return True
|
return True
|
||||||
return False
|
if self.token and self.url:
|
||||||
|
# TODO: Add token auth
|
||||||
|
pass
|
||||||
|
|
||||||
|
return False
|
||||||
|
@@ -20,6 +20,7 @@ import csv
|
|||||||
import json
|
import json
|
||||||
import linecache
|
import linecache
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
@@ -30,7 +31,6 @@ from urllib.parse import urlencode
|
|||||||
import cherrypy
|
import cherrypy
|
||||||
import mako.exceptions
|
import mako.exceptions
|
||||||
import mako.template
|
import mako.template
|
||||||
import websocket
|
|
||||||
from cherrypy import NotFound
|
from cherrypy import NotFound
|
||||||
from cherrypy.lib.static import serve_file, serve_fileobj, serve_download
|
from cherrypy.lib.static import serve_file, serve_fileobj, serve_download
|
||||||
from mako.lookup import TemplateLookup
|
from mako.lookup import TemplateLookup
|
||||||
@@ -57,6 +57,7 @@ from jellypy import versioncheck
|
|||||||
from jellypy import webstart
|
from jellypy import webstart
|
||||||
from jellypy.api2 import API2
|
from jellypy.api2 import API2
|
||||||
from jellypy.helpers import checked, addtoapi, create_https_certificates, build_datatables_json, sanitize_out
|
from jellypy.helpers import checked, addtoapi, create_https_certificates, build_datatables_json, sanitize_out
|
||||||
|
from jellypy.jellyfin import Jellyfin
|
||||||
from jellypy.password import make_hash
|
from jellypy.password import make_hash
|
||||||
from jellypy.session import get_session_info, get_session_user_id, allow_session_user, allow_session_library
|
from jellypy.session import get_session_info, get_session_user_id, allow_session_user, allow_session_library
|
||||||
from jellypy.webauth import AuthController, requireAuth, member_of, check_auth
|
from jellypy.webauth import AuthController, requireAuth, member_of, check_auth
|
||||||
@@ -4035,14 +4036,16 @@ class WebInterface(object):
|
|||||||
@cherrypy.tools.json_out()
|
@cherrypy.tools.json_out()
|
||||||
@requireAuth(member_of("admin"))
|
@requireAuth(member_of("admin"))
|
||||||
@addtoapi()
|
@addtoapi()
|
||||||
def get_server_id(self, hostname=None, port=None, identifier=None, ssl=0, remote=0, manual=0,
|
def check_login(self, hostname=None, port=None, ssl=0, remote=0, manual=0, user=None, password=None,
|
||||||
get_url=False, test_websocket=False, **kwargs):
|
get_url=False, test_websocket=False, **kwargs):
|
||||||
""" Get the JELLYFIN server identifier.
|
""" Try Jellyfin Login.
|
||||||
|
|
||||||
```
|
```
|
||||||
Required parameters:
|
Required parameters:
|
||||||
hostname (str): 'localhost' or '192.160.0.10'
|
hostname (str): 'localhost' or '192.160.0.10'
|
||||||
port (int): 32400
|
port (int): 8096
|
||||||
|
user (str): Jellyfin user
|
||||||
|
password (str): Jellyfin password
|
||||||
|
|
||||||
Optional parameters:
|
Optional parameters:
|
||||||
ssl (int): 0 or 1
|
ssl (int): 0 or 1
|
||||||
@@ -4054,66 +4057,54 @@ class WebInterface(object):
|
|||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
# Attempt to get the pms_identifier from plex.tv if the server is published
|
# Attempt to get the pms_identifier from plex.tv if the server is published
|
||||||
# Works for all JELLYFIN SSL settings
|
# Works for all Jellyfin SSL settings
|
||||||
if not identifier and hostname and port:
|
result = {"identifier": None}
|
||||||
pass
|
if hostname and port and user and password:
|
||||||
# TODO: Jellyfin
|
path_regex = re.compile("^(https?://)?([^/:]+)(:[0-9]+)?(/.*)?$")
|
||||||
# plex_tv = plextv.PlexTV()
|
|
||||||
# servers = plex_tv.discover()
|
protocol, host, port, path = path_regex.match(hostname + ":" + port).groups()
|
||||||
# ip_address = get_ip(hostname)
|
|
||||||
|
if not protocol:
|
||||||
|
protocol = "http://"
|
||||||
|
|
||||||
|
if protocol == "http://" and not port:
|
||||||
|
port = "8096"
|
||||||
|
|
||||||
|
server = "".join(filter(bool, (protocol, host, port, path)))
|
||||||
|
|
||||||
|
jf = Jellyfin(server)
|
||||||
|
if jf.login(user, password):
|
||||||
|
result = {'identifier': jf.id}
|
||||||
|
|
||||||
|
# if identifier:
|
||||||
|
# if helpers.bool_true(get_url):
|
||||||
|
# server = self.get_server_resources(jellyfin_ip=hostname,
|
||||||
|
# jellyfin_port=port,
|
||||||
|
# jellyfin_ssl=ssl,
|
||||||
|
# jellyfin_is_remote=remote,
|
||||||
|
# jellyfin_url_manual=manual,
|
||||||
|
# jellyfin_identifier=identifier)
|
||||||
|
# result['url'] = server['jellyfin_url']
|
||||||
|
# result['ws'] = None
|
||||||
#
|
#
|
||||||
# for server in servers:
|
# if helpers.bool_true(test_websocket):
|
||||||
# if (server['ip'] == hostname or server['ip'] == ip_address) and server['port'] == port:
|
# # Quick test websocket connection
|
||||||
# identifier = server['clientIdentifier']
|
# ws_url = result['url'].replace('http', 'ws', 1) + '/:/websockets/notifications'
|
||||||
# break
|
# header = ['X-Plex-Token: %s' % jellypy.CONFIG.JELLYFIN_TOKEN]
|
||||||
#
|
#
|
||||||
# # Fallback to checking /identity endpoint if the server is unpublished
|
# logger.debug("Testing websocket connection...")
|
||||||
# # Cannot set SSL settings on the JELLYFIN if unpublished so 'http' is okay
|
# try:
|
||||||
# if not identifier:
|
# test_ws = websocket.create_connection(ws_url, header=header)
|
||||||
# scheme = 'https' if helpers.cast_to_int(ssl) else 'http'
|
# test_ws.close()
|
||||||
# url = '{scheme}://{hostname}:{port}'.format(scheme=scheme, hostname=hostname, port=port)
|
# logger.debug("Websocket connection test successful.")
|
||||||
# uri = '/identity'
|
# result['ws'] = True
|
||||||
#
|
# except (websocket.WebSocketException, IOError, Exception) as e:
|
||||||
# request_handler = http_handler.HTTPHandler(urls=url,
|
# logger.error("Websocket connection test failed: %s" % e)
|
||||||
# ssl_verify=False)
|
# result['ws'] = False
|
||||||
# request = request_handler.make_request(uri=uri,
|
|
||||||
# request_type='GET',
|
|
||||||
# output_format='xml')
|
|
||||||
# if request:
|
|
||||||
# xml_head = request.getElementsByTagName('MediaContainer')[0]
|
|
||||||
# identifier = xml_head.getAttribute('machineIdentifier')
|
|
||||||
|
|
||||||
result = {'identifier': identifier}
|
|
||||||
|
|
||||||
if identifier:
|
|
||||||
if helpers.bool_true(get_url):
|
|
||||||
server = self.get_server_resources(jellyfin_ip=hostname,
|
|
||||||
jellyfin_port=port,
|
|
||||||
jellyfin_ssl=ssl,
|
|
||||||
jellyfin_is_remote=remote,
|
|
||||||
jellyfin_url_manual=manual,
|
|
||||||
jellyfin_identifier=identifier)
|
|
||||||
result['url'] = server['jellyfin_url']
|
|
||||||
result['ws'] = None
|
|
||||||
|
|
||||||
if helpers.bool_true(test_websocket):
|
|
||||||
# Quick test websocket connection
|
|
||||||
ws_url = result['url'].replace('http', 'ws', 1) + '/:/websockets/notifications'
|
|
||||||
header = ['X-Plex-Token: %s' % jellypy.CONFIG.JELLYFIN_TOKEN]
|
|
||||||
|
|
||||||
logger.debug("Testing websocket connection...")
|
|
||||||
try:
|
|
||||||
test_ws = websocket.create_connection(ws_url, header=header)
|
|
||||||
test_ws.close()
|
|
||||||
logger.debug("Websocket connection test successful.")
|
|
||||||
result['ws'] = True
|
|
||||||
except (websocket.WebSocketException, IOError, Exception) as e:
|
|
||||||
logger.error("Websocket connection test failed: %s" % e)
|
|
||||||
result['ws'] = False
|
|
||||||
|
|
||||||
return result
|
return result
|
||||||
else:
|
else:
|
||||||
logger.warn('Unable to retrieve the JELLYFIN identifier.')
|
logger.warn('Unable to retrieve the Jellyfin identifier.')
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
@@ -4480,8 +4471,8 @@ class WebInterface(object):
|
|||||||
|
|
||||||
@addtoapi('jellyfin_image_proxy')
|
@addtoapi('jellyfin_image_proxy')
|
||||||
def real_jellyfin_image_proxy(self, img=None, rating_key=None, width=750, height=1000,
|
def real_jellyfin_image_proxy(self, img=None, rating_key=None, width=750, height=1000,
|
||||||
opacity=100, background='000000', blur=0, img_format='png',
|
opacity=100, background='000000', blur=0, img_format='png',
|
||||||
fallback=None, refresh=False, clip=False, **kwargs):
|
fallback=None, refresh=False, clip=False, **kwargs):
|
||||||
""" Gets an image from the JELLYFIN and saves it to the image cache directory.
|
""" Gets an image from the JELLYFIN and saves it to the image cache directory.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
Reference in New Issue
Block a user