Compare commits

...

2 Commits

Author SHA1 Message Date
ee2bb22dc9 README: Jellyfin Login ✓ 2021-02-06 18:42:24 +01:00
3852275b74 Jellyfin Login ✓ 2021-02-06 18:41:24 +01:00
5 changed files with 86 additions and 118 deletions

View File

@@ -11,7 +11,7 @@ JellyPy only supports Jellyfin. If you are running run Plex, head over to Tautul
Working on getting basic functionality up. It's going to take some time, based on that Jellyfin's API is Working on getting basic functionality up. It's going to take some time, based on that Jellyfin's API is
not well documented (read as: not documented at all). not well documented (read as: not documented at all).
- [ ] Login to Jellyfin - [x] Login to Jellyfin
- [ ] Libraries/Media - [ ] Libraries/Media
- [ ] Activity - [ ] Activity
- [ ] History - [ ] History
@@ -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

View File

@@ -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>&nbsp; Verifying server...'); $("#jellyfin-verify-status").html('<i class="fa fa-refresh fa-spin"></i>&nbsp; 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>&nbsp; Server found!'); $("#jellyfin-verify-status").html('<i class="fa fa-check"></i>&nbsp; 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>&nbsp; This is not a Plex Server!'); $("#jellyfin-verify-status").html('<i class="fa fa-exclamation-circle"></i>&nbsp; This is not a Jellyfin Server!');
$('#jellyfin-verify-status').fadeIn('fast'); $('#jellyfin-verify-status').fadeIn('fast');
} }
} }

View File

@@ -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)

View File

@@ -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
if self.token and self.url:
# TODO: Add token auth
pass
return False return False

View File

@@ -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