mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-11 07:46:07 -07:00
Import Plexivity database
This commit is contained in:
parent
6aa786698e
commit
4311d12603
8 changed files with 536 additions and 55 deletions
5
API.md
5
API.md
|
@ -1429,11 +1429,12 @@ Returns:
|
|||
```
|
||||
|
||||
|
||||
### import_plexwatch_database
|
||||
Import a plexwatch database into PlexPy.
|
||||
### import_database
|
||||
Import a PlexWatch or Plexivity database into PlexPy.
|
||||
|
||||
```
|
||||
Required parameters:
|
||||
app (str): "plexwatch" or "plexivity"
|
||||
database_path (str): The full path to the plexwatch database file
|
||||
table_name (str): "processed" or "grouped"
|
||||
|
||||
|
|
|
@ -2,11 +2,18 @@
|
|||
<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">Import PlexWatch Database</h4>
|
||||
<h4 class="modal-title">Import ${app} Database</h4>
|
||||
</div>
|
||||
<div class="modal-body" id="modal-text">
|
||||
<p class="help-block">
|
||||
Please ensure your PlexWatch database is at version 0.3.2 or higher.
|
||||
<%
|
||||
v = ''
|
||||
if app == 'PlexWatch':
|
||||
v = '0.3.2'
|
||||
elif app == 'Plexivity':
|
||||
v = '0.9.8'
|
||||
%>
|
||||
<strong>Please ensure your ${app} database is at version ${v} or higher.</strong>
|
||||
</p>
|
||||
<div class="form-group">
|
||||
<label for="db_location">Database Location</label>
|
||||
|
@ -15,7 +22,7 @@
|
|||
<input type="text" class="form-control" id="db_location" name="db_location" value="" required>
|
||||
</div>
|
||||
</div>
|
||||
<p class="help-block">Enter the path and file name for the PlexWatch database you wish to import.</p>
|
||||
<p class="help-block">Enter the path and file name for the ${app} database you wish to import.</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="table_name">Table Name</label>
|
||||
|
@ -41,7 +48,7 @@
|
|||
</div>
|
||||
<div class="modal-footer">
|
||||
<div>
|
||||
<span id="status-message"></span>
|
||||
<span id="status-message" style="padding-right: 25px;"></span>
|
||||
<input type="button" id="import_db" class="btn btn-bright" value="Import">
|
||||
</div>
|
||||
</div>
|
||||
|
@ -54,8 +61,13 @@
|
|||
var table_name = $("#table_name").val();
|
||||
var import_ignore_interval = $("#import_ignore_interval").val();
|
||||
$.ajax({
|
||||
url: 'get_plexwatch_export_data',
|
||||
data: {database_path: database_path, table_name:table_name, import_ignore_interval:import_ignore_interval},
|
||||
url: 'import_database',
|
||||
data: {
|
||||
app: "${app}",
|
||||
database_path: database_path,
|
||||
table_name: table_name,
|
||||
import_ignore_interval: import_ignore_interval
|
||||
},
|
||||
cache: false,
|
||||
async: true,
|
||||
success: function(data) {
|
|
@ -755,9 +755,13 @@
|
|||
</div>
|
||||
|
||||
<div class="padded-header">
|
||||
<h3>PlexWatch Import Tool</h3>
|
||||
<h3>Database Import Tool</h3>
|
||||
</div>
|
||||
<p class="help-block">Click a button below to import an exisiting database from another app.</p>
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-form toggle-app-import-modal" type="button" data-target="#app-import-modal" data-toggle="modal" data-app="plexwatch">PlexWatch</button>
|
||||
<button class="btn btn-form toggle-app-import-modal" type="button" data-target="#app-import-modal" data-toggle="modal" data-app="plexivity">Plexivity</button>
|
||||
</div>
|
||||
<p class="help-block"><a href="javascript:void(0)" id="toggle-plexwatch-import-modal" data-target="#plexwatch-import-modal" data-toggle="modal">Click here to Import an existing Plexwatch database.</a></p>
|
||||
|
||||
<p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p>
|
||||
</div>
|
||||
|
@ -1532,7 +1536,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="plexwatch-import-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="plexwatch-import-modal"></div>
|
||||
<div id="app-import-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="app-import-modal"></div>
|
||||
<div id="notification-config-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="notification-config-modal"></div>
|
||||
<div id="notification-triggers-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="notification-triggers-modal"></div>
|
||||
<div id="notify-text-sub-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="notify-text-sub-modal">
|
||||
|
@ -2305,14 +2309,15 @@ $(document).ready(function() {
|
|||
}
|
||||
});
|
||||
|
||||
// Load PlexWatch import modal
|
||||
$("#toggle-plexwatch-import-modal").click(function() {
|
||||
// Load database import modal
|
||||
$(".toggle-app-import-modal").click(function() {
|
||||
$.ajax({
|
||||
url: 'plexwatch_import',
|
||||
url: 'import_database_tool',
|
||||
data: { app: $(this).data('app') },
|
||||
cache: false,
|
||||
async: true,
|
||||
complete: function(xhr, status) {
|
||||
$("#plexwatch-import-modal").html(xhr.responseText);
|
||||
$("#app-import-modal").html(xhr.responseText);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -141,11 +141,11 @@
|
|||
</div>
|
||||
|
||||
<div class="wizard-card" data-cardname="card6">
|
||||
<h3>PlexWatch Import</h3>
|
||||
<p class="help-block">If you have an existing PlexWatch database, you can import the data into PlexPy.</p>
|
||||
<h3>PlexWatch/Plexivity Import</h3>
|
||||
<p class="help-block">If you have an existing PlexWatch/Plexivity database, you can import the data into PlexPy.</p>
|
||||
<p class="help-block">
|
||||
When you complete this wizard navigate to the settings menu and to the Extra Settings tab. You will find an import tool there
|
||||
which will convert your plexWatch database into a format that PlexPy can read.
|
||||
which will convert your PlexWatch/Plexivity database into a format that PlexPy can read.
|
||||
</p>
|
||||
<!-- Figure out best way to get friends list refreshed before adding this back
|
||||
You can skip this and do it later if you wish.</p>
|
||||
|
@ -468,25 +468,25 @@
|
|||
});
|
||||
|
||||
// Send database path to import script
|
||||
$("#plexwatch-import").click(function() {
|
||||
var database_path = $("#db_location").val();
|
||||
var table_name = 'processed';
|
||||
var import_ignore_interval = 0;
|
||||
$.ajax({
|
||||
url: 'get_plexwatch_export_data',
|
||||
data: {database_path: database_path, table_name:table_name, import_ignore_interval:import_ignore_interval},
|
||||
cache: false,
|
||||
async: true,
|
||||
success: function(data) {
|
||||
if (data === 'Import has started. Check the PlexPy logs to monitor any problems.') {
|
||||
$("#plexwatch-import-status").html('Started');
|
||||
} else {
|
||||
$("#plexwatch-import-status").html(data);
|
||||
}
|
||||
$("#db_location").val('')
|
||||
}
|
||||
});
|
||||
});
|
||||
//$("#plexwatch-import").click(function() {
|
||||
// var database_path = $("#db_location").val();
|
||||
// var table_name = 'processed';
|
||||
// var import_ignore_interval = 0;
|
||||
// $.ajax({
|
||||
// url: 'get_plexwatch_export_data',
|
||||
// data: {database_path: database_path, table_name:table_name, import_ignore_interval:import_ignore_interval},
|
||||
// cache: false,
|
||||
// async: true,
|
||||
// success: function(data) {
|
||||
// if (data === 'Import has started. Check the PlexPy logs to monitor any problems.') {
|
||||
// $("#plexwatch-import-status").html('Started');
|
||||
// } else {
|
||||
// $("#plexwatch-import-status").html(data);
|
||||
// }
|
||||
// $("#db_location").val('')
|
||||
// }
|
||||
// });
|
||||
//});
|
||||
|
||||
function getServerOptions(token) {
|
||||
/* Set token and returns server options */
|
||||
|
|
436
plexpy/plexivity_import.py
Normal file
436
plexpy/plexivity_import.py
Normal file
|
@ -0,0 +1,436 @@
|
|||
# This file is part of PlexPy.
|
||||
#
|
||||
# PlexPy is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# PlexPy is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with PlexPy. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import arrow
|
||||
import sqlite3
|
||||
from xml.dom import minidom
|
||||
|
||||
import plexpy
|
||||
import activity_pinger
|
||||
import activity_processor
|
||||
import database
|
||||
import helpers
|
||||
import logger
|
||||
import plextv
|
||||
import users
|
||||
|
||||
|
||||
def extract_plexivity_xml(xml=None):
|
||||
output = {}
|
||||
clean_xml = helpers.latinToAscii(xml)
|
||||
try:
|
||||
xml_parse = minidom.parseString(clean_xml)
|
||||
except:
|
||||
logger.warn(u"PlexPy Importer :: Error parsing XML for Plexivity database.")
|
||||
return None
|
||||
|
||||
# I think Plexivity only tracked videos and not music?
|
||||
xml_head = xml_parse.getElementsByTagName('Video')
|
||||
if not xml_head:
|
||||
logger.warn(u"PlexPy Importer :: Error parsing XML for Plexivity database.")
|
||||
return None
|
||||
|
||||
for a in xml_head:
|
||||
rating_key = helpers.get_xml_attr(a, 'ratingKey')
|
||||
added_at = helpers.get_xml_attr(a, 'addedAt')
|
||||
art = helpers.get_xml_attr(a, 'art')
|
||||
duration = helpers.get_xml_attr(a, 'duration')
|
||||
grandparent_rating_key = helpers.get_xml_attr(a, 'grandparentRatingKey')
|
||||
grandparent_thumb = helpers.get_xml_attr(a, 'grandparentThumb')
|
||||
grandparent_title = helpers.get_xml_attr(a, 'grandparentTitle')
|
||||
guid = helpers.get_xml_attr(a, 'guid')
|
||||
section_id = helpers.get_xml_attr(a, 'librarySectionID')
|
||||
media_index = helpers.get_xml_attr(a, 'index')
|
||||
originally_available_at = helpers.get_xml_attr(a, 'originallyAvailableAt')
|
||||
last_viewed_at = helpers.get_xml_attr(a, 'lastViewedAt')
|
||||
parent_rating_key = helpers.get_xml_attr(a, 'parentRatingKey')
|
||||
parent_media_index = helpers.get_xml_attr(a, 'parentIndex')
|
||||
parent_thumb = helpers.get_xml_attr(a, 'parentThumb')
|
||||
parent_title = helpers.get_xml_attr(a, 'parentTitle')
|
||||
rating = helpers.get_xml_attr(a, 'rating')
|
||||
thumb = helpers.get_xml_attr(a, 'thumb')
|
||||
media_type = helpers.get_xml_attr(a, 'type')
|
||||
updated_at = helpers.get_xml_attr(a, 'updatedAt')
|
||||
view_offset = helpers.get_xml_attr(a, 'viewOffset')
|
||||
year = helpers.get_xml_attr(a, 'year')
|
||||
studio = helpers.get_xml_attr(a, 'studio')
|
||||
title = helpers.get_xml_attr(a, 'title')
|
||||
tagline = helpers.get_xml_attr(a, 'tagline')
|
||||
|
||||
directors = []
|
||||
if a.getElementsByTagName('Director'):
|
||||
director_elem = a.getElementsByTagName('Director')
|
||||
for b in director_elem:
|
||||
directors.append(helpers.get_xml_attr(b, 'tag'))
|
||||
|
||||
aspect_ratio = ''
|
||||
audio_channels = None
|
||||
audio_codec = ''
|
||||
bitrate = None
|
||||
container = ''
|
||||
height = None
|
||||
video_codec = ''
|
||||
video_framerate = ''
|
||||
video_resolution = ''
|
||||
width = None
|
||||
|
||||
if a.getElementsByTagName('Media'):
|
||||
media_elem = a.getElementsByTagName('Media')
|
||||
for c in media_elem:
|
||||
aspect_ratio = helpers.get_xml_attr(c, 'aspectRatio')
|
||||
audio_channels = helpers.get_xml_attr(c, 'audioChannels')
|
||||
audio_codec = helpers.get_xml_attr(c, 'audioCodec')
|
||||
bitrate = helpers.get_xml_attr(c, 'bitrate')
|
||||
container = helpers.get_xml_attr(c, 'container')
|
||||
height = helpers.get_xml_attr(c, 'height')
|
||||
video_codec = helpers.get_xml_attr(c, 'videoCodec')
|
||||
video_framerate = helpers.get_xml_attr(c, 'videoFrameRate')
|
||||
video_resolution = helpers.get_xml_attr(c, 'videoResolution')
|
||||
width = helpers.get_xml_attr(c, 'width')
|
||||
|
||||
machine_id = ''
|
||||
platform = ''
|
||||
player = ''
|
||||
|
||||
if a.getElementsByTagName('Player'):
|
||||
player_elem = a.getElementsByTagName('Player')
|
||||
for d in player_elem:
|
||||
ip_address = helpers.get_xml_attr(d, 'address')
|
||||
machine_id = helpers.get_xml_attr(d, 'machineIdentifier')
|
||||
platform = helpers.get_xml_attr(d, 'platform')
|
||||
player = helpers.get_xml_attr(d, 'title')
|
||||
|
||||
transcode_audio_channels = None
|
||||
transcode_audio_codec = ''
|
||||
audio_decision = 'direct play'
|
||||
transcode_container = ''
|
||||
transcode_height = None
|
||||
transcode_protocol = ''
|
||||
transcode_video_codec = ''
|
||||
video_decision = 'direct play'
|
||||
transcode_width = None
|
||||
|
||||
if a.getElementsByTagName('TranscodeSession'):
|
||||
transcode_elem = a.getElementsByTagName('TranscodeSession')
|
||||
for e in transcode_elem:
|
||||
transcode_audio_channels = helpers.get_xml_attr(e, 'audioChannels')
|
||||
transcode_audio_codec = helpers.get_xml_attr(e, 'audioCodec')
|
||||
audio_decision = helpers.get_xml_attr(e, 'audioDecision')
|
||||
transcode_container = helpers.get_xml_attr(e, 'container')
|
||||
transcode_height = helpers.get_xml_attr(e, 'height')
|
||||
transcode_protocol = helpers.get_xml_attr(e, 'protocol')
|
||||
transcode_video_codec = helpers.get_xml_attr(e, 'videoCodec')
|
||||
video_decision = helpers.get_xml_attr(e, 'videoDecision')
|
||||
transcode_width = helpers.get_xml_attr(e, 'width')
|
||||
|
||||
user_id = None
|
||||
|
||||
if a.getElementsByTagName('User'):
|
||||
user_elem = a.getElementsByTagName('User')
|
||||
for f in user_elem:
|
||||
user_id = helpers.get_xml_attr(f, 'id')
|
||||
|
||||
writers = []
|
||||
if a.getElementsByTagName('Writer'):
|
||||
writer_elem = a.getElementsByTagName('Writer')
|
||||
for g in writer_elem:
|
||||
writers.append(helpers.get_xml_attr(g, 'tag'))
|
||||
|
||||
actors = []
|
||||
if a.getElementsByTagName('Role'):
|
||||
actor_elem = a.getElementsByTagName('Role')
|
||||
for h in actor_elem:
|
||||
actors.append(helpers.get_xml_attr(h, 'tag'))
|
||||
|
||||
genres = []
|
||||
if a.getElementsByTagName('Genre'):
|
||||
genre_elem = a.getElementsByTagName('Genre')
|
||||
for i in genre_elem:
|
||||
genres.append(helpers.get_xml_attr(i, 'tag'))
|
||||
|
||||
labels = []
|
||||
if a.getElementsByTagName('Lables'):
|
||||
label_elem = a.getElementsByTagName('Lables')
|
||||
for i in label_elem:
|
||||
labels.append(helpers.get_xml_attr(i, 'tag'))
|
||||
|
||||
output = {'rating_key': rating_key,
|
||||
'added_at': added_at,
|
||||
'art': art,
|
||||
'duration': duration,
|
||||
'grandparent_rating_key': grandparent_rating_key,
|
||||
'grandparent_thumb': grandparent_thumb,
|
||||
'grandparent_title': grandparent_title,
|
||||
'parent_title': parent_title,
|
||||
'title': title,
|
||||
'tagline': tagline,
|
||||
'guid': guid,
|
||||
'section_id': section_id,
|
||||
'media_index': media_index,
|
||||
'originally_available_at': originally_available_at,
|
||||
'last_viewed_at': last_viewed_at,
|
||||
'parent_rating_key': parent_rating_key,
|
||||
'parent_media_index': parent_media_index,
|
||||
'parent_thumb': parent_thumb,
|
||||
'rating': rating,
|
||||
'thumb': thumb,
|
||||
'media_type': media_type,
|
||||
'updated_at': updated_at,
|
||||
'view_offset': view_offset,
|
||||
'year': year,
|
||||
'directors': directors,
|
||||
'aspect_ratio': aspect_ratio,
|
||||
'audio_channels': audio_channels,
|
||||
'audio_codec': audio_codec,
|
||||
'bitrate': bitrate,
|
||||
'container': container,
|
||||
'height': height,
|
||||
'video_codec': video_codec,
|
||||
'video_framerate': video_framerate,
|
||||
'video_resolution': video_resolution,
|
||||
'width': width,
|
||||
'ip_address': ip_address,
|
||||
'machine_id': machine_id,
|
||||
'platform': platform,
|
||||
'player': player,
|
||||
'transcode_audio_channels': transcode_audio_channels,
|
||||
'transcode_audio_codec': transcode_audio_codec,
|
||||
'audio_decision': audio_decision,
|
||||
'transcode_container': transcode_container,
|
||||
'transcode_height': transcode_height,
|
||||
'transcode_protocol': transcode_protocol,
|
||||
'transcode_video_codec': transcode_video_codec,
|
||||
'video_decision': video_decision,
|
||||
'transcode_width': transcode_width,
|
||||
'user_id': user_id,
|
||||
'writers': writers,
|
||||
'actors': actors,
|
||||
'genres': genres,
|
||||
'studio': studio,
|
||||
'labels': labels
|
||||
}
|
||||
|
||||
return output
|
||||
|
||||
def validate_database(database=None, table_name=None):
|
||||
try:
|
||||
connection = sqlite3.connect(database, timeout=20)
|
||||
except sqlite3.OperationalError:
|
||||
logger.error(u"PlexPy Importer :: Invalid database specified.")
|
||||
return 'Invalid database specified.'
|
||||
except ValueError:
|
||||
logger.error(u"PlexPy Importer :: Invalid database specified.")
|
||||
return 'Invalid database specified.'
|
||||
except:
|
||||
logger.error(u"PlexPy Importer :: Uncaught exception.")
|
||||
return 'Uncaught exception.'
|
||||
|
||||
try:
|
||||
connection.execute('SELECT xml from %s' % table_name)
|
||||
connection.close()
|
||||
except sqlite3.OperationalError:
|
||||
logger.error(u"PlexPy Importer :: Invalid database specified.")
|
||||
return 'Invalid database specified.'
|
||||
except:
|
||||
logger.error(u"PlexPy Importer :: Uncaught exception.")
|
||||
return 'Uncaught exception.'
|
||||
|
||||
return 'success'
|
||||
|
||||
def import_from_plexivity(database=None, table_name=None, import_ignore_interval=0):
|
||||
|
||||
try:
|
||||
connection = sqlite3.connect(database, timeout=20)
|
||||
connection.row_factory = sqlite3.Row
|
||||
except sqlite3.OperationalError:
|
||||
logger.error(u"PlexPy Importer :: Invalid filename.")
|
||||
return None
|
||||
except ValueError:
|
||||
logger.error(u"PlexPy Importer :: Invalid filename.")
|
||||
return None
|
||||
|
||||
try:
|
||||
connection.execute('SELECT xml from %s' % table_name)
|
||||
except sqlite3.OperationalError:
|
||||
logger.error(u"PlexPy Importer :: Database specified does not contain the required fields.")
|
||||
return None
|
||||
|
||||
logger.debug(u"PlexPy Importer :: Plexivity data import in progress...")
|
||||
|
||||
logger.debug(u"PlexPy Importer :: Disabling monitoring while import in progress.")
|
||||
plexpy.schedule_job(activity_pinger.check_active_sessions, 'Check for active sessions',
|
||||
hours=0, minutes=0, seconds=0)
|
||||
plexpy.schedule_job(activity_pinger.check_recently_added, 'Check for recently added items',
|
||||
hours=0, minutes=0, seconds=0)
|
||||
plexpy.schedule_job(activity_pinger.check_server_response, 'Check for Plex remote access',
|
||||
hours=0, minutes=0, seconds=0)
|
||||
|
||||
ap = activity_processor.ActivityProcessor()
|
||||
user_data = users.Users()
|
||||
|
||||
# Get the latest friends list so we can pull user id's
|
||||
try:
|
||||
plextv.refresh_users()
|
||||
except:
|
||||
logger.debug(u"PlexPy Importer :: Unable to refresh the users list. Aborting import.")
|
||||
return None
|
||||
|
||||
query = 'SELECT id AS id, ' \
|
||||
'time AS started, ' \
|
||||
'stopped, ' \
|
||||
'null AS user_id, ' \
|
||||
'user, ' \
|
||||
'ip_address, ' \
|
||||
'paused_counter, ' \
|
||||
'platform AS player, ' \
|
||||
'null AS platform, ' \
|
||||
'null as machine_id, ' \
|
||||
'null AS media_type, ' \
|
||||
'null AS view_offset, ' \
|
||||
'xml, ' \
|
||||
'rating as content_rating,' \
|
||||
'summary,' \
|
||||
'title AS full_title,' \
|
||||
'(case when orig_title_ep = "n/a" then orig_title else ' \
|
||||
'orig_title_ep end) as title,' \
|
||||
'(case when orig_title_ep != "n/a" then orig_title else ' \
|
||||
'null end) as grandparent_title ' \
|
||||
'FROM ' + table_name + ' ORDER BY id'
|
||||
|
||||
result = connection.execute(query)
|
||||
|
||||
for row in result:
|
||||
# Extract the xml from the Plexivity db xml field.
|
||||
extracted_xml = extract_plexivity_xml(row['xml'])
|
||||
|
||||
# If we get back None from our xml extractor skip over the record and log error.
|
||||
if not extracted_xml:
|
||||
logger.error(u"PlexPy Importer :: Skipping record with id %s due to malformed xml."
|
||||
% str(row['id']))
|
||||
continue
|
||||
|
||||
# Skip line if we don't have a ratingKey to work with
|
||||
#if not row['rating_key']:
|
||||
# logger.error(u"PlexPy Importer :: Skipping record due to null ratingKey.")
|
||||
# continue
|
||||
|
||||
# If the user_id no longer exists in the friends list, pull it from the xml.
|
||||
if user_data.get_user_id(user=row['user']):
|
||||
user_id = user_data.get_user_id(user=row['user'])
|
||||
else:
|
||||
user_id = extracted_xml['user_id']
|
||||
|
||||
session_history = {'started': arrow.get(row['started']).timestamp,
|
||||
'stopped': arrow.get(row['stopped']).timestamp,
|
||||
'rating_key': extracted_xml['rating_key'],
|
||||
'title': row['title'],
|
||||
'parent_title': extracted_xml['parent_title'],
|
||||
'grandparent_title': row['grandparent_title'],
|
||||
'user_id': user_id,
|
||||
'user': row['user'],
|
||||
'ip_address': row['ip_address'] if row['ip_address'] else extracted_xml['ip_address'],
|
||||
'paused_counter': row['paused_counter'],
|
||||
'player': row['player'],
|
||||
'platform': extracted_xml['platform'],
|
||||
'machine_id': extracted_xml['machine_id'],
|
||||
'parent_rating_key': extracted_xml['parent_rating_key'],
|
||||
'grandparent_rating_key': extracted_xml['grandparent_rating_key'],
|
||||
'media_type': extracted_xml['media_type'],
|
||||
'view_offset': extracted_xml['view_offset'],
|
||||
'video_decision': extracted_xml['video_decision'],
|
||||
'audio_decision': extracted_xml['audio_decision'],
|
||||
'duration': extracted_xml['duration'],
|
||||
'width': extracted_xml['width'],
|
||||
'height': extracted_xml['height'],
|
||||
'container': extracted_xml['container'],
|
||||
'video_codec': extracted_xml['video_codec'],
|
||||
'audio_codec': extracted_xml['audio_codec'],
|
||||
'bitrate': extracted_xml['bitrate'],
|
||||
'video_resolution': extracted_xml['video_resolution'],
|
||||
'video_framerate': extracted_xml['video_framerate'],
|
||||
'aspect_ratio': extracted_xml['aspect_ratio'],
|
||||
'audio_channels': extracted_xml['audio_channels'],
|
||||
'transcode_protocol': extracted_xml['transcode_protocol'],
|
||||
'transcode_container': extracted_xml['transcode_container'],
|
||||
'transcode_video_codec': extracted_xml['transcode_video_codec'],
|
||||
'transcode_audio_codec': extracted_xml['transcode_audio_codec'],
|
||||
'transcode_audio_channels': extracted_xml['transcode_audio_channels'],
|
||||
'transcode_width': extracted_xml['transcode_width'],
|
||||
'transcode_height': extracted_xml['transcode_height']
|
||||
}
|
||||
|
||||
session_history_metadata = {'rating_key': extracted_xml['rating_key'],
|
||||
'parent_rating_key': extracted_xml['parent_rating_key'],
|
||||
'grandparent_rating_key': extracted_xml['grandparent_rating_key'],
|
||||
'title': row['title'],
|
||||
'parent_title': extracted_xml['parent_title'],
|
||||
'grandparent_title': row['grandparent_title'],
|
||||
'media_index': extracted_xml['media_index'],
|
||||
'parent_media_index': extracted_xml['parent_media_index'],
|
||||
'thumb': extracted_xml['thumb'],
|
||||
'parent_thumb': extracted_xml['parent_thumb'],
|
||||
'grandparent_thumb': extracted_xml['grandparent_thumb'],
|
||||
'art': extracted_xml['art'],
|
||||
'media_type': extracted_xml['media_type'],
|
||||
'year': extracted_xml['year'],
|
||||
'originally_available_at': extracted_xml['originally_available_at'],
|
||||
'added_at': extracted_xml['added_at'],
|
||||
'updated_at': extracted_xml['updated_at'],
|
||||
'last_viewed_at': extracted_xml['last_viewed_at'],
|
||||
'content_rating': row['content_rating'],
|
||||
'summary': row['summary'],
|
||||
'tagline': extracted_xml['tagline'],
|
||||
'rating': extracted_xml['rating'],
|
||||
'duration': extracted_xml['duration'],
|
||||
'guid': extracted_xml['guid'],
|
||||
'section_id': extracted_xml['section_id'],
|
||||
'directors': extracted_xml['directors'],
|
||||
'writers': extracted_xml['writers'],
|
||||
'actors': extracted_xml['actors'],
|
||||
'genres': extracted_xml['genres'],
|
||||
'studio': extracted_xml['studio'],
|
||||
'labels': extracted_xml['labels'],
|
||||
'full_title': row['full_title']
|
||||
}
|
||||
|
||||
# On older versions of PMS, "clip" items were still classified as "movie" and had bad ratingKey values
|
||||
# Just make sure that the ratingKey is indeed an integer
|
||||
if session_history_metadata['rating_key'].isdigit():
|
||||
ap.write_session_history(session=session_history,
|
||||
import_metadata=session_history_metadata,
|
||||
is_import=True,
|
||||
import_ignore_interval=import_ignore_interval)
|
||||
else:
|
||||
logger.debug(u"PlexPy Importer :: Item has bad rating_key: %s" % session_history_metadata['rating_key'])
|
||||
|
||||
logger.debug(u"PlexPy Importer :: Plexivity data import complete.")
|
||||
import_users()
|
||||
|
||||
logger.debug(u"PlexPy Importer :: Re-enabling monitoring.")
|
||||
plexpy.initialize_scheduler()
|
||||
|
||||
def import_users():
|
||||
logger.debug(u"PlexPy Importer :: Importing Plexivity Users...")
|
||||
monitor_db = database.MonitorDatabase()
|
||||
|
||||
query = 'INSERT OR IGNORE INTO users (user_id, username) ' \
|
||||
'SELECT user_id, user ' \
|
||||
'FROM session_history WHERE user_id != 1 GROUP BY user_id'
|
||||
|
||||
try:
|
||||
monitor_db.action(query)
|
||||
logger.debug(u"PlexPy Importer :: Users imported.")
|
||||
except:
|
||||
logger.debug(u"PlexPy Importer :: Failed to import users.")
|
|
@ -32,12 +32,12 @@ def extract_plexwatch_xml(xml=None):
|
|||
try:
|
||||
xml_parse = minidom.parseString(clean_xml)
|
||||
except:
|
||||
logger.warn(u"PlexPy Importer :: Error parsing XML for Plexwatch database.")
|
||||
logger.warn(u"PlexPy Importer :: Error parsing XML for PlexWatch database.")
|
||||
return None
|
||||
|
||||
xml_head = xml_parse.getElementsByTagName('opt')
|
||||
if not xml_head:
|
||||
logger.warn(u"PlexPy Importer :: Error parsing XML for Plexwatch database.")
|
||||
logger.warn(u"PlexPy Importer :: Error parsing XML for PlexWatch database.")
|
||||
return None
|
||||
|
||||
for a in xml_head:
|
||||
|
@ -102,6 +102,7 @@ def extract_plexwatch_xml(xml=None):
|
|||
if a.getElementsByTagName('Player'):
|
||||
player_elem = a.getElementsByTagName('Player')
|
||||
for d in player_elem:
|
||||
ip_address = helpers.get_xml_attr(d, 'address')
|
||||
machine_id = helpers.get_xml_attr(d, 'machineIdentifier')
|
||||
platform = helpers.get_xml_attr(d, 'platform')
|
||||
player = helpers.get_xml_attr(d, 'title')
|
||||
|
@ -192,6 +193,7 @@ def extract_plexwatch_xml(xml=None):
|
|||
'video_framerate': video_framerate,
|
||||
'video_resolution': video_resolution,
|
||||
'width': width,
|
||||
'ip_address': ip_address,
|
||||
'machine_id': machine_id,
|
||||
'platform': platform,
|
||||
'player': player,
|
||||
|
@ -332,7 +334,7 @@ def import_from_plexwatch(database=None, table_name=None, import_ignore_interval
|
|||
'grandparent_title': row['grandparent_title'],
|
||||
'user_id': user_id,
|
||||
'user': row['user'],
|
||||
'ip_address': row['ip_address'],
|
||||
'ip_address': row['ip_address'] if row['ip_address'] else extracted_xml['ip_address'],
|
||||
'paused_counter': row['paused_counter'],
|
||||
'player': row['player'],
|
||||
'platform': extracted_xml['platform'],
|
||||
|
|
|
@ -585,7 +585,7 @@ class Users(object):
|
|||
except Exception as e:
|
||||
logger.warn(u"PlexPy Users :: Unable to execute database query for undelete: %s." % e)
|
||||
|
||||
# Keep method for PlexWatch import
|
||||
# Keep method for PlexWatch/Plexivity import
|
||||
def get_user_id(self, user=None):
|
||||
if user:
|
||||
try:
|
||||
|
|
|
@ -40,6 +40,7 @@ import log_reader
|
|||
import logger
|
||||
import notifiers
|
||||
import plextv
|
||||
import plexivity_import
|
||||
import plexwatch_import
|
||||
import pmsconnect
|
||||
import users
|
||||
|
@ -2630,12 +2631,13 @@ class WebInterface(object):
|
|||
|
||||
@cherrypy.expose
|
||||
@requireAuth(member_of("admin"))
|
||||
@addtoapi("import_plexwatch_database")
|
||||
def get_plexwatch_export_data(self, database_path=None, table_name=None, import_ignore_interval=0, **kwargs):
|
||||
""" Import a plexwatch database into PlexPy.
|
||||
@addtoapi()
|
||||
def import_database(self, app=None, database_path=None, table_name=None, import_ignore_interval=0, **kwargs):
|
||||
""" Import a PlexWatch or Plexivity database into PlexPy.
|
||||
|
||||
```
|
||||
Required parameters:
|
||||
app (str): "plexwatch" or "plexivity"
|
||||
database_path (str): The full path to the plexwatch database file
|
||||
table_name (str): "processed" or "grouped"
|
||||
|
||||
|
@ -2646,21 +2648,44 @@ class WebInterface(object):
|
|||
None
|
||||
```
|
||||
"""
|
||||
db_check_msg = plexwatch_import.validate_database(database=database_path,
|
||||
table_name=table_name)
|
||||
if db_check_msg == 'success':
|
||||
threading.Thread(target=plexwatch_import.import_from_plexwatch,
|
||||
kwargs={'database': database_path,
|
||||
'table_name': table_name,
|
||||
'import_ignore_interval': import_ignore_interval}).start()
|
||||
return 'Import has started. Check the PlexPy logs to monitor any problems.'
|
||||
if not app:
|
||||
return 'No app specified for import'
|
||||
|
||||
if app.lower() == 'plexwatch':
|
||||
db_check_msg = plexwatch_import.validate_database(database=database_path,
|
||||
table_name=table_name)
|
||||
if db_check_msg == 'success':
|
||||
threading.Thread(target=plexwatch_import.import_from_plexwatch,
|
||||
kwargs={'database': database_path,
|
||||
'table_name': table_name,
|
||||
'import_ignore_interval': import_ignore_interval}).start()
|
||||
return 'Import has started. Check the PlexPy logs to monitor any problems.'
|
||||
else:
|
||||
return db_check_msg
|
||||
elif app.lower() == 'plexivity':
|
||||
db_check_msg = plexivity_import.validate_database(database=database_path,
|
||||
table_name=table_name)
|
||||
if db_check_msg == 'success':
|
||||
threading.Thread(target=plexivity_import.import_from_plexivity,
|
||||
kwargs={'database': database_path,
|
||||
'table_name': table_name,
|
||||
'import_ignore_interval': import_ignore_interval}).start()
|
||||
return 'Import has started. Check the PlexPy logs to monitor any problems.'
|
||||
else:
|
||||
return db_check_msg
|
||||
else:
|
||||
return db_check_msg
|
||||
return 'App not recognized for import'
|
||||
|
||||
@cherrypy.expose
|
||||
@requireAuth(member_of("admin"))
|
||||
def plexwatch_import(self, **kwargs):
|
||||
return serve_template(templatename="plexwatch_import.html", title="Import PlexWatch Database")
|
||||
def import_database_tool(self, app=None, **kwargs):
|
||||
if app == 'plexwatch':
|
||||
return serve_template(templatename="app_import.html", title="Import PlexWatch Database", app="PlexWatch")
|
||||
elif app == 'plexivity':
|
||||
return serve_template(templatename="app_import.html", title="Import Plexivity Database", app="Plexivity")
|
||||
|
||||
logger.warn(u"No app specified for import.")
|
||||
return
|
||||
|
||||
@cherrypy.expose
|
||||
@requireAuth(member_of("admin"))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue