Fix merge conflicts

This commit is contained in:
Jonathan Wong 2015-09-27 12:10:19 -07:00
commit 0784b0a0db
8 changed files with 1098 additions and 25 deletions

View file

@ -57,6 +57,18 @@ from plexpy import version
</div>
<div class="collapse navbar-collapse navbar-right" id="navbar-collapse-1">
<ul class="nav navbar-nav">
<li>
<form action="search" method="post" class="form" id="search_form">
<div class="input-group">
<span class="input-textbox">
<input type="text" class="form-control" name="search_query" id="search_query" aria-label="Search" placeholder="Search..."/>
</span>
<span class="input-group-btn">
<button class="btn btn-dark btn-inactive" type="submit" id="search_button"><i class="fa fa-search"></i></button>
</span>
</div>
</form>
</li>
% if title=="Home":
<li class="active"><a href="home"><i class="fa fa-lg fa-home"></i></a></li>
% else:
@ -117,6 +129,28 @@ ${next.headerIncludes()}
$('#updatebar').show();
}
</script>
<script>
$('#search_form').submit(function (e) {
if ($('#search_query').hasClass('active') && $('#search_query').val().trim() != '') {
$.ajax({
type: 'post',
url: 'search',
data: { 'query': $('#search_query').val() }
})
} else {
e.preventDefault();
$('#search_button').removeClass('btn-inactive');
$('#search_query').clearQueue().val('').animate({ right: '0', width: '250px' }).addClass('active').focus();
}
})
$('#search_query').on('blur', function (e) {
if ($(this).val().trim() == '') {
$(this).delay(200).animate({ right: '-250px', width: '0' }, function () {
$('#search_button').addClass('btn-inactive');
}).removeClass('active');
}
});
</script>
${next.javascriptIncludes()}
</body>
</html>

View file

@ -1246,11 +1246,39 @@ a:hover .summary-poster-face-track .summary-poster-face-overlay span {
background-size: contain;
height: 16px;
}
#children-list, #search-results-list {
position: relative;
z-index: 0;
}
.item-children-wrapper {
}
.item-children-section-title {
position: relative;
padding: 10px;
background-color: #2c2c2c;
border-bottom: 1px solid #3d3d3d;
border-top: 1px solid #282828;
height: 50px;
line-height: 22px;
padding: 13px 20px;
margin: 20px 0;
}
.item-children-section-title h4 {
position: relative;
margin: 0;
line-height: 22px;
color: #fff;
font-size: 16px;
text-align: center;
text-transform: uppercase;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.item-children-instance {
list-style: none;
margin: 0;
overflow: hidden;
}
.item-children-instance li {
float: left;
@ -1348,6 +1376,9 @@ a:hover .item-children-poster {
text-align: left;
clear: both;
}
.item-children-instance-text-wrapper h3.text-muted {
color: #777;
}
.item-children-list-item-odd {
border-top: 0px solid #343434;
border-bottom: 0px solid #343434;
@ -1391,6 +1422,13 @@ a:hover .item-children-poster {
width: 40px;
margin-right: 20px;
}
#new_title h3 {
color: #F9AA03;
font-size: 14px;
line-height: 1.42857143;
font-weight: bold;
margin: 0;
}
.settings-alert {
float: left;
padding: 0;
@ -2412,4 +2450,37 @@ table[id^='history_child'] thead th {
line-height: 0;
height: 0 !important;
overflow: hidden;
}
#search_form {
width: 350px;
padding: 8px 15px;
}
#search_form span.input-textbox {
overflow: hidden;
width: 250px;
height: 34px;
display: inline-flex;
float: right;
}
#search_form #search_query {
width: 0;
height: 34px;
margin-top: 0;
float: right;
position: relative;
right: -250px;
border-radius: 3px 0 0 3px;
}
#search_form #search_query.active {
width: 250px;
right: 0px;
}
#search_form #search_button.btn-inactive {
background-color: #000;
border: 1px solid rgba(0,0,0,0);
-webkit-transition: background 0.3s;
-moz-transition: background 0.3s;
-ms-transition: background 0.3s;
-o-transition: background 0.3s;
transition: background 0.3s;
}

View file

@ -11,27 +11,40 @@ data :: Usable parameters (if not applicable for media type, blank value will be
== Global keys ==
rating_key Returns the unique identifier for the media item.
type Returns the type of media. Either 'movie', 'episode' or 'show' or 'season'.
type Returns the type of media. Either 'movie', 'show', 'season', 'episode', 'artist', 'album', or 'track'.
art Returns the location of the item's artwork
title Returns the name of the episode, show, season or movie.
title Returns the name of the movie, show, episode, artist, album, or track.
duration Returns the standard runtime of the media.
content_rating Returns the age rating for the media.
summary Returns a brief description of the media plot.
grandparent_title Returns the name of the TV show.
parent_index Returns the season number of the TV show.
index Returns the episode number.
grandparent_title Returns the name of the show, or artist.
parent_index Returns the index number of the season.
index Returns the index number of the episode, or track.
parent_thumb Returns the location of the item's thumbnail. Use with pms_image_proxy.
writers Returns an array of writers.
thumb Returns the location of the item's thumbnail. Use with pms_image_proxy.
parent_title Returns the name of the TV show.
parent_title Returns the name of the show, or artist.
rating Returns the 5 star rating value for the movie. Between 1 and 5.
year Returns the release year of the movie.
year Returns the release year of the movie, or show.
genres Returns an array of genres.
actors Returns an array of actors.
directors Returns an array of directors.
studio Returns the name of the studio.
originally_available_at Returns the air date of the item.
query :: Usable parameters
== Global keys ==
query_string Returns the string used for the search query.
title Returns the name of the movie, episode, or track.
parent_title Returns the name of the album.
grandparent_title Returns the name of the show, or artist.
media_index Returns the index number of the episode, or track.
parent_media_index Returns the index number of the season.
year Returns the release year of the movie, or show.
media_type Returns the type of media. Either 'movie', 'show', 'season', 'episode', 'artist', 'album', or 'track'.
rating_key Returns the unique identifier for the media item.
DOCUMENTATION :: END
</%doc>
@ -261,7 +274,7 @@ DOCUMENTATION :: END
</div>
</div>
<div class='table-card-back'>
<div id="children-list"></div>
<div id="children-list"><i class="fa fa-refresh fa-spin"></i>&nbsp; Loading season list...</div>
</div>
</div>
% elif data['type'] == 'season':
@ -272,7 +285,7 @@ DOCUMENTATION :: END
</div>
</div>
<div class='table-card-back'>
<div id="children-list"></div>
<div id="children-list"><i class="fa fa-refresh fa-spin"></i>&nbsp; Loading episode list...</div>
</div>
</div>
% elif data['type'] == 'artist':
@ -283,7 +296,7 @@ DOCUMENTATION :: END
</div>
</div>
<div class='table-card-back'>
<div id="children-list"></div>
<div id="children-list"><i class="fa fa-refresh fa-spin"></i>&nbsp; Loading album list...</div>
</div>
</div>
% elif data['type'] == 'album':
@ -294,7 +307,7 @@ DOCUMENTATION :: END
</div>
</div>
<div class='table-card-back'>
<div id="children-list"></div>
<div id="children-list"><i class="fa fa-refresh fa-spin"></i>&nbsp; Loading track list...</div>
</div>
</div>
% endif
@ -361,11 +374,122 @@ DOCUMENTATION :: END
% else:
<div class="container-fluid">
<div class="row">
<div class="col-md-10">
<h3>
Error retrieving item data. This media may not be available in the Plex Media Server database
anymore.
</h3>
<div class="summary-container">
<div class="summary-navbar">
<div class="col-md-12">
<div class="summary-navbar-list">
% if query:
% if query['media_type'] == 'movie':
<span>Movies</span>
<span class="hidden-xs hidden-sm"><i class="fa fa-chevron-right"></i></span>
<span>${query['title']}</span>
% elif query['media_type'] == 'show':
<span>TV Shows</span>
<span class="hidden-xs hidden-sm"><i class="fa fa-chevron-right"></i></span>
<span>${query['grandparent_title']}</span>
% elif query['media_type'] == 'season':
<span class="hidden-xs hidden-sm">TV Shows</span>
<span class="hidden-xs hidden-sm"><i class="fa fa-chevron-right"></i></span>
<span class="hidden-xs hidden-sm">${query['grandparent_title']}</span>
<span class="hidden-xs hidden-sm"><i class="fa fa-chevron-right"></i></span>
<span>Season ${query['parent_media_index']}</span>
% elif query['media_type'] == 'episode':
<span class="hidden-xs hidden-sm">TV Shows</span>
<span class="hidden-xs hidden-sm"><i class="fa fa-chevron-right"></i></span>
<span class="hidden-xs hidden-sm">${query['grandparent_title']}</span>
<span class="hidden-xs hidden-sm"><i class="fa fa-chevron-right"></i></span>
<span>Season ${query['parent_media_index']}</span>
<span><i class="fa fa-chevron-right"></i></span>
<span>Episode ${query['media_index']} - ${query['title']}</span>
% elif query['media_type'] == 'artist':
<span>Music</span>
<span><i class="fa fa-chevron-right"></i></span>
<span>${query['grandparent_title']}</span>
% elif query['media_type'] == 'album':
<span class="hidden-xs hidden-sm">Music</span>
<span class="hidden-xs hidden-sm"><i class="fa fa-chevron-right"></i></span>
<span>${query['grandparent_title']}</span>
<span><i class="fa fa-chevron-right"></i></span>
<span>${query['parent_title']}</span>
% elif query['media_type'] == 'track':
<span class="hidden-xs hidden-sm">Music</span>
<span class="hidden-xs hidden-sm"><i class="fa fa-chevron-right"></i></span>
<span class="hidden-xs hidden-sm">${query['grandparent_title']}</span>
<span class="hidden-xs hidden-sm"><i class="fa fa-chevron-right"></i></span>
<span>${query['parent_title']}</span>
<span><i class="fa fa-chevron-right"></i></span>
<span>Track ${query['media_index']} - ${query['title']}</span>
% endif
% endif
</div>
</div>
</div>
<div class="summary-content-title-wrapper">
<div class="col-md-12">
<h4 style="text-align: center; margin-bottom: 20px;">
Error retrieving item metadata. This media item is not available in the Plex Media Server library.
</h4>
% if query:
<h4 style="text-align: center; margin-bottom: 20px;">
If the item has been moved, please select the correct match below to update the PlexPy database.
</h4>
% endif
</div>
</div>
<div class="summary-content-wrapper">
<div class='col-md-12'>
% if query:
<div class='table-card-header'>
<div class="header-bar">
<span>Search Results for <strong>${query['query_string']}</strong></span>
</div>
</div>
<div class='table-card-back'>
<div id="search-results-list"><i class="fa fa-refresh fa-spin"></i>&nbsp; Loading search results...</div>
</div>
<div class="modal fade" id="confirm-modal" tabindex="-1" role="dialog" aria-labelledby="confirm-modal">
<div class="modal-dialog">
<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" id="myModalLabel">Confirm Update</h4>
</div>
<div class="modal-body" style="text-align: center;">
<p>Are you REALLY sure you want to replace
<p><strong>
% if query['media_type'] == 'movie':
${query['title']}<br />${query['year']}
% elif query['media_type'] == 'show':
${query['grandparent_title']}
% elif query['media_type'] == 'season':
${query['grandparent_title']}<br />S${query['parent_media_index']}
% elif query['media_type'] == 'episode':
${query['grandparent_title']}<br />${query['title']}<br />S${query['parent_media_index']} &middot; E${query['media_index']}
% elif query['media_type'] == 'artist':
${query['grandparent_title']}
% elif query['media_type'] == 'album':
${query['grandparent_title']}<br />${query['parent_title']}
% elif query['media_type'] == 'track':
${query['grandparent_title']}<br />${query['title']}<br />${query['parent_title']}
% endif
</strong></p>
<p> with </p>
<p><span id="new_title"></span></p>
% if query['media_type'] != 'movie':
<p>All items for <strong>${query['grandparent_title']}</strong> will also be updated.</p>
% endif
<p>This is permanent and cannot be undone!</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-dark" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger btn-ok" data-dismiss="modal" id="confirm-update">Update</button>
</div>
</div>
</div>
</div>
% endif
</div>
</div>
</div>
</div>
</div>
@ -381,13 +505,6 @@ DOCUMENTATION :: END
<script src="interfaces/default/js/moment-with-locale.js"></script>
% if data:
% if data['type'] == 'movie' or data['type'] == 'show' or data['type'] == 'episode':
<script>
// Convert rating to 5 star rating type
var starRating = Math.round(${data['rating']} / 2);
$('#stars').attr('data-rateit-value', starRating);
</script>
% endif
<script src="interfaces/default/js/tables/history_table.js"></script>
% if data['type'] == 'show' or data['type'] == 'artist':
<script>
@ -488,10 +605,57 @@ DOCUMENTATION :: END
});
</script>
% endif
% if data['rating']:
<script>
// Convert rating to 5 star rating type
var starRating = Math.round(${data['rating']} / 2);
$('#stars').attr('data-rateit-value', starRating);
</script>
% endif
<script>
$("#airdate").html(moment($("#airdate").text()).format('MMM DD, YYYY'));
$("#runtime").html(millisecondsToMinutes($("#runtime").text(), true));
$('div.art-face').animate({ opacity: 0.2 }, { duration: 1000 });
</script>
% elif query:
<script>
$.ajax({
url: 'get_search_results_children',
type: "GET",
async: true,
data: {'query': "${query['query_string']}",
'media_type': "${query['media_type']}",
'season_index': "${query['parent_media_index']}"
},
complete: function(xhr, status) {
$("#search-results-list").html(xhr.responseText); }
});
$(document).on('click', '#search-results-list a', function (e) {
e.preventDefault();
var new_rating_key = $(this).attr("id");
var new_href = $(this).attr("href");
$('#new_title').html($(this).find('.item-children-instance-text-wrapper').html());
$('#confirm-modal').modal();
$('#confirm-modal').one('click', '#confirm-update', function () {
$(this).prop('disabled', true);
var msg = "<i class='fa fa-refresh fa-spin'></i>&nbspUpdating database..."
showMsg(msg, false, false, 0)
$.ajax({
url: 'update_history_rating_key',
data: { old_rating_key: "${query['rating_key']}",
new_rating_key: new_rating_key,
media_type: "${query['media_type']}"
},
async: true,
success: function (data) {
window.location.href = new_href;
}
});
});
});
</script>
% endif
</%def>

View file

@ -0,0 +1,224 @@
<%doc>
USAGE DOCUMENTATION :: PLEASE LEAVE THIS AT THE TOP OF THIS FILE
For Mako templating syntax documentation please visit: http://docs.makotemplates.org/en/latest/
Filename: info_children_list.html
Version: 0.1
Variable names: data [list]
data :: Usable parameters
== Global keys ==
results_count Returns the number of search results.
results_list Returns a dictionary of search result types.
data['results_list'] :: Usable paramaters
== media_type keys ==
movie Returns an array of movie results
show Returns an array of show results
season Returns an array of season results
episode Returns an array of episode results
artist Returns an array of artist results
album Returns an array of album results
track Returns an array of track results
data['results_list'][media_type] :: Usable paramaters
== Global keys ==
rating_key Returns the unique identifier for the media item.
type Returns the type of media. Either 'movie', 'show', 'season', 'episode', 'artist', 'album', or 'track'.
art Returns the location of the item's artwork
title Returns the name of the movie, show, episode, artist, album, or track.
duration Returns the standard runtime of the media.
content_rating Returns the age rating for the media.
summary Returns a brief description of the media plot.
grandparent_title Returns the name of the show, or artist.
parent_index Returns the index number of the season.
index Returns the index number of the episode, or track.
parent_thumb Returns the location of the item's thumbnail. Use with pms_image_proxy.
writers Returns an array of writers.
thumb Returns the location of the item's thumbnail. Use with pms_image_proxy.
parent_title Returns the name of the show, or artist.
rating Returns the 5 star rating value for the movie. Between 1 and 5.
year Returns the release year of the movie, or show.
genres Returns an array of genres.
actors Returns an array of actors.
directors Returns an array of directors.
studio Returns the name of the studio.
originally_available_at Returns the air date of the item.
DOCUMENTATION :: END
</%doc>
% if data != None:
% if data['results_count'] > 0:
% if 'movie' in data['results_list'] and data['results_list']['movie']:
<div class="item-children-wrapper">
<div class="item-children-section-title">
<h4>Movies</h4>
</div>
<ul class="item-children-instance list-unstyled">
% for child in data['results_list']['movie']:
<li>
<a href="info?item_id=${child['rating_key']}" id="${child['rating_key']}">
<div class="item-children-poster">
<div class="item-children-poster-face season-poster" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=450);"></div>
</div>
<div class="item-children-instance-text-wrapper season-item">
<h3 title="${child['title']}">${child['title']}</h3>
<h3 class="text-muted">${child['year']}</h3>
</div>
</a>
</li>
% endfor
</ul>
</div>
% endif
% if 'show' in data['results_list'] and data['results_list']['show']:
<div class="item-children-wrapper">
<div class="item-children-section-title">
<h4>TV Shows</h4>
</div>
<ul class="item-children-instance list-unstyled">
% for child in data['results_list']['show']:
<li>
<a href="info?item_id=${child['rating_key']}" id="${child['rating_key']}">
<div class="item-children-poster">
<div class="item-children-poster-face season-poster" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=450);"></div>
</div>
<div class="item-children-instance-text-wrapper season-item">
<h3 title="${child['title']}">${child['title']}</h3>
<h3 class="text-muted">${child['year']}</h3>
</div>
</a>
</li>
% endfor
</ul>
</div>
% endif
% if 'season' in data['results_list'] and data['results_list']['season']:
<div class="item-children-wrapper">
<div class="item-children-section-title">
<h4>Seasons</h4>
</div>
<ul class="item-children-instance list-unstyled">
% for child in data['results_list']['season']:
<li>
<a href="info?item_id=${child['rating_key']}" id="${child['rating_key']}">
<div class="item-children-poster">
<div class="item-children-poster-face season-poster" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=450);"></div>
</div>
<div class="item-children-instance-text-wrapper season-item">
<h3 title="${child['parent_title']}">${child['parent_title']}</h3>
<h3 class="text-muted">S${child['index']}</h3>
</div>
</a>
</li>
% endfor
</ul>
</div>
% endif
% if 'episode' in data['results_list'] and data['results_list']['episode']:
<div class="item-children-wrapper">
<div class="item-children-section-title">
<h4>Episodes</h4>
</div>
<ul class="item-children-instance list-unstyled">
% for child in data['results_list']['episode']:
<li>
<a href="info?item_id=${child['rating_key']}" id="${child['rating_key']}">
<div class="item-children-poster">
<div class="item-children-poster-face episode-poster" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=450);"></div>
</div>
<div class="item-children-instance-text-wrapper episode-item">
<h3 title="${child['grandparent_title']}">${child['grandparent_title']}</h3>
<h3 title="${child['title']}">${child['title']}</h3>
<h3 class="text-muted">S${child['parent_index']} &middot; E${child['index']}</h3>
</div>
</a>
</li>
% endfor
</ul>
</div>
% endif
% if 'artist' in data['results_list'] and data['results_list']['artist']:
<div class="item-children-wrapper">
<div class="item-children-section-title">
<h4>Artists</h4>
</div>
<ul class="item-children-instance list-unstyled">
% for child in data['results_list']['artist']:
<li>
<a href="info?item_id=${child['rating_key']}" id="${child['rating_key']}">
<div class="item-children-poster">
<div class="item-children-poster-face album-poster" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=300);"></div>
</div>
<div class="item-children-instance-text-wrapper album-item">
<h3 title="${child['title']}">${child['title']}</h3>
</div>
</a>
</li>
% endfor
</ul>
</div>
% endif
% if 'album' in data['results_list'] and data['results_list']['album']:
<div class="item-children-wrapper">
<div class="item-children-section-title">
<h4>Albums</h4>
</div>
<ul class="item-children-instance list-unstyled">
% for child in data['results_list']['album']:
<li>
<a href="info?item_id=${child['rating_key']}" id="${child['rating_key']}">
<div class="item-children-poster">
<div class="item-children-poster-face album-poster" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=300);"></div>
</div>
<div class="item-children-instance-text-wrapper album-item">
<h3 title="${child['parent_title']}">${child['parent_title']}</h3>
<h3 title="${child['title']}">${child['title']}</h3>
</div>
</a>
</li>
% endfor
</ul>
</div>
% endif
% if 'track' in data['results_list'] and data['results_list']['track']:
<div class="item-children-wrapper">
<div class="item-children-section-title">
<h4>Tracks</h4>
</div>
<ul class="item-children-instance list-unstyled">
% for child in data['results_list']['track']:
<li>
<a href="info?item_id=${child['rating_key']}" id="${child['rating_key']}">
<div class="item-children-poster">
<div class="item-children-poster-face album-poster" style="background-image: url(pms_image_proxy?img=${child['parent_thumb']}&width=300&height=300);">
<div class="item-children-card-overlay">
<div class="item-children-overlay-text">
Track ${child['index']}
</div>
</div>
</div>
</div>
<div class="item-children-instance-text-wrapper album-item">
<h3 title="${child['grandparent_title']}">${child['grandparent_title']}</h3>
<h3 title="${child['title']}">${child['title']}</h3>
<h3 title="${child['parent_title']}" class="text-muted">${child['parent_title']}</h3>
</div>
</a>
</li>
% endfor
</ul>
</div>
% endif
% else:
<div class="item-children-wrapper">
No search results found.
</div>
% endif
% endif

View file

@ -0,0 +1,41 @@
<%inherit file="base.html"/>
<%def name="headIncludes()">
</%def>
<%def name="headerIncludes()">
</%def>
<%def name="body()">
<div class='container-fluid'>
<div class='table-card-header'>
<div class="header-bar">
<span><i class="fa fa-search"></i> Search Results
% if query:
for <strong>${query}</strong>
% endif
</span>
</div>
</div>
<div class='table-card-back'>
<div id="search-results-list"><i class="fa fa-refresh fa-spin"></i>&nbsp; Loading search results...</div>
</div>
</div>
</%def>
<%def name="javascriptIncludes()">
<script>
$('#search_button').removeClass('btn-inactive');
$('#search_query').val('${query}').css({ right: '0', width: '250px' }).addClass('active');
$.ajax({
url: 'get_search_results_children',
type: "GET",
async: true,
data: {'query': "${query}"},
complete: function (xhr, status) {
$("#search-results-list").html(xhr.responseText);
}
});
</script>
</%def>

View file

@ -789,4 +789,204 @@ class DataFactory(object):
return 'Deleted all items for user_id %s.' % user_id
else:
return 'Unable to delete items. Input user_id not valid.'
return 'Unable to delete items. Input user_id not valid.'
def get_search_query(self, rating_key=''):
monitor_db = database.MonitorDatabase()
if rating_key:
query = 'SELECT rating_key, parent_rating_key, grandparent_rating_key, title, parent_title, grandparent_title, ' \
'media_index, parent_media_index, year, media_type ' \
'FROM session_history_metadata ' \
'WHERE rating_key = ? ' \
'OR parent_rating_key = ? ' \
'OR grandparent_rating_key = ? ' \
'LIMIT 1'
result = monitor_db.select(query=query, args=[rating_key, rating_key, rating_key])
else:
result = []
query = {}
query_string = None
media_type = None
for item in result:
title = item['title']
parent_title = item['parent_title']
grandparent_title = item['grandparent_title']
media_index = item['media_index']
parent_media_index = item['parent_media_index']
year = item['year']
if str(item['rating_key']) == rating_key:
query_string = item['title']
media_type = item['media_type']
elif str(item['parent_rating_key']) == rating_key:
if item['media_type'] == 'episode':
query_string = item['grandparent_title']
media_type = 'season'
elif item['media_type'] == 'track':
query_string = item['parent_title']
media_type = 'album'
elif str(item['grandparent_rating_key']) == rating_key:
if item['media_type'] == 'episode':
query_string = item['grandparent_title']
media_type = 'show'
elif item['media_type'] == 'track':
query_string = item['grandparent_title']
media_type = 'artist'
if query_string and media_type:
query = {'query_string': query_string.replace('"', ''),
'title': title,
'parent_title': parent_title,
'grandparent_title': grandparent_title,
'media_index': media_index,
'parent_media_index': parent_media_index,
'year': year,
'media_type': media_type,
'rating_key': rating_key
}
else:
return None
return query
def get_rating_keys_list(self, rating_key='', media_type=''):
monitor_db = database.MonitorDatabase()
if media_type == 'movie':
key_list = {0: {'rating_key': int(rating_key)}}
return key_list
if media_type == 'artist' or media_type == 'album' or media_type == 'track':
match_type = 'title'
else:
match_type = 'index'
# Get the grandparent rating key
try:
query = 'SELECT rating_key, parent_rating_key, grandparent_rating_key ' \
'FROM session_history_metadata ' \
'WHERE rating_key = ? ' \
'OR parent_rating_key = ? ' \
'OR grandparent_rating_key = ? ' \
'LIMIT 1'
result = monitor_db.select(query=query, args=[rating_key, rating_key, rating_key])
grandparent_rating_key = result[0]['grandparent_rating_key']
except:
logger.warn("Unable to execute database query.")
return {}
query = 'SELECT rating_key, parent_rating_key, grandparent_rating_key, title, parent_title, grandparent_title, ' \
'media_index, parent_media_index ' \
'FROM session_history_metadata ' \
'WHERE {0} = ? ' \
'GROUP BY {1} '
# get grandparent_rating_keys
grandparents = {}
result = monitor_db.select(query=query.format('grandparent_rating_key', 'grandparent_rating_key'),
args=[grandparent_rating_key])
for item in result:
# get parent_rating_keys
parents = {}
result = monitor_db.select(query=query.format('grandparent_rating_key', 'parent_rating_key'),
args=[item['grandparent_rating_key']])
for item in result:
# get rating_keys
children = {}
result = monitor_db.select(query=query.format('parent_rating_key', 'rating_key'),
args=[item['parent_rating_key']])
for item in result:
key = item['media_index']
children.update({key: {'rating_key': item['rating_key']}})
key = item['parent_media_index'] if match_type == 'index' else item['parent_title']
parents.update({key:
{'rating_key': item['parent_rating_key'],
'children': children}
})
key = 0 if match_type == 'index' else item['grandparent_title']
grandparents.update({key:
{'rating_key': item['grandparent_rating_key'],
'children': parents}
})
key_list = grandparents
return key_list
def update_rating_key(self, old_key_list='', new_key_list='', media_type=''):
monitor_db = database.MonitorDatabase()
# function to map rating keys pairs
def get_pairs(old, new):
pairs = {}
for k, v in old.iteritems():
if k in new:
if v['rating_key'] != new[k]['rating_key']:
pairs.update({v['rating_key']: new[k]['rating_key']})
if 'children' in old[k]:
pairs.update(get_pairs(old[k]['children'], new[k]['children']))
return pairs
# map rating keys pairs
mapping = {}
if old_key_list and new_key_list:
mapping = get_pairs(old_key_list, new_key_list)
if mapping:
logger.info(u"PlexPy DataFactory :: Updating rating keys in the database.")
for old_key, new_key in mapping.iteritems():
# check rating_key (3 tables)
monitor_db.action('UPDATE session_history SET rating_key = ? WHERE rating_key = ?',
[new_key, old_key])
monitor_db.action('UPDATE session_history_media_info SET rating_key = ? WHERE rating_key = ?',
[new_key, old_key])
monitor_db.action('UPDATE session_history_metadata SET rating_key = ? WHERE rating_key = ?',
[new_key, old_key])
# check parent_rating_key (2 tables)
monitor_db.action('UPDATE session_history SET parent_rating_key = ? WHERE parent_rating_key = ?',
[new_key, old_key])
monitor_db.action('UPDATE session_history_metadata SET parent_rating_key = ? WHERE parent_rating_key = ?',
[new_key, old_key])
# check grandparent_rating_key (2 tables)
monitor_db.action('UPDATE session_history SET grandparent_rating_key = ? WHERE grandparent_rating_key = ?',
[new_key, old_key])
monitor_db.action('UPDATE session_history_metadata SET grandparent_rating_key = ? WHERE grandparent_rating_key = ?',
[new_key, old_key])
# check thumb (1 table)
monitor_db.action('UPDATE session_history_metadata SET thumb = replace(thumb, ?, ?) \
WHERE thumb LIKE "/library/metadata/%s/thumb/%%"' % old_key,
[old_key, new_key])
# check parent_thumb (1 table)
monitor_db.action('UPDATE session_history_metadata SET parent_thumb = replace(parent_thumb, ?, ?) \
WHERE parent_thumb LIKE "/library/metadata/%s/thumb/%%"' % old_key,
[old_key, new_key])
# check grandparent_thumb (1 table)
monitor_db.action('UPDATE session_history_metadata SET grandparent_thumb = replace(grandparent_thumb, ?, ?) \
WHERE grandparent_thumb LIKE "/library/metadata/%s/thumb/%%"' % old_key,
[old_key, new_key])
# check art (1 table)
monitor_db.action('UPDATE session_history_metadata SET art = replace(art, ?, ?) \
WHERE art LIKE "/library/metadata/%s/art/%%"' % old_key,
[old_key, new_key])
return 'Updated rating key in database.'
else:
return 'No updated rating key needed in database. No changes were made.'
# for debugging
#return mapping

View file

@ -17,6 +17,7 @@ from plexpy import logger, helpers, users, http_handler
from urlparse import urlparse
import plexpy
import urllib2
class PmsConnect(object):
@ -72,6 +73,23 @@ class PmsConnect(object):
return request
"""
Return metadata for children of the request item.
Parameters required: rating_key { Plex ratingKey }
Optional parameters: output_format { dict, json }
Output: array
"""
def get_metadata_children(self, rating_key='', output_format=''):
uri = '/library/metadata/' + rating_key + '/children'
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
output_format=output_format)
return request
"""
Return list of recently added items.
@ -219,6 +237,22 @@ class PmsConnect(object):
return request
"""
Return search results.
Optional parameters: output_format { dict, json }
Output: array
"""
def get_search(self, query='', track='', output_format=''):
uri = '/search?query=' + urllib2.quote(query.encode('utf8')) + track
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
output_format=output_format)
return request
"""
Return processed and validated list of recently added items.
@ -1340,3 +1374,202 @@ class PmsConnect(object):
else:
logger.error("Image proxy queries but no input received.")
return None
"""
Return processed list of search results.
Output: array
"""
def get_search_results(self, query=''):
search_results = self.get_search(query=query, output_format='xml')
search_results_tracks = self.get_search(query=query, track='&type=10', output_format='xml')
xml_head = []
try:
try:
xml_head += search_results.getElementsByTagName('MediaContainer')
except:
pass
try:
xml_head += search_results_tracks.getElementsByTagName('MediaContainer')
except:
pass
except:
logger.warn("Unable to parse XML for get_search_result_details.")
return []
search_results_count = 0
search_results_list = {'movie': [],
'show': [],
'season': [],
'episode': [],
'artist': [],
'album': [],
'track': []
}
totalSize = 0
for a in xml_head:
if a.getAttribute('size'):
totalSize += int(a.getAttribute('size'))
if totalSize == 0:
logger.debug(u"No search results.")
search_results_list = {'results_count': search_results_count,
'results_list': []
}
return search_results_list
for a in xml_head:
if a.getElementsByTagName('Video'):
result_data = a.getElementsByTagName('Video')
for result in result_data:
rating_key = helpers.get_xml_attr(result, 'ratingKey')
metadata = self.get_metadata_details(rating_key=rating_key)
if metadata['metadata']['type'] == 'movie':
search_results_list['movie'].append(metadata['metadata'])
elif metadata['metadata']['type'] == 'episode':
search_results_list['episode'].append(metadata['metadata'])
search_results_count += 1
if a.getElementsByTagName('Directory'):
result_data = a.getElementsByTagName('Directory')
for result in result_data:
rating_key = helpers.get_xml_attr(result, 'ratingKey')
metadata = self.get_metadata_details(rating_key=rating_key)
if metadata['metadata']['type'] == 'show':
search_results_list['show'].append(metadata['metadata'])
show_seasons = self.get_item_children(rating_key=metadata['metadata']['rating_key'])
if show_seasons['children_count'] != '0':
for season in show_seasons['children_list']:
if season['rating_key']:
rating_key = season['rating_key']
metadata = self.get_metadata_details(rating_key=rating_key)
search_results_list['season'].append(metadata['metadata'])
search_results_count += 1
elif metadata['metadata']['type'] == 'artist':
search_results_list['artist'].append(metadata['metadata'])
elif metadata['metadata']['type'] == 'album':
search_results_list['album'].append(metadata['metadata'])
search_results_count += 1
if a.getElementsByTagName('Track'):
result_data = a.getElementsByTagName('Track')
for result in result_data:
rating_key = helpers.get_xml_attr(result, 'ratingKey')
metadata = self.get_metadata_details(rating_key=rating_key)
search_results_list['track'].append(metadata['metadata'])
search_results_count += 1
output = {'results_count': search_results_count,
'results_list': search_results_list
}
return output
"""
Return processed list of grandparent/parent/child rating keys.
Output: array
"""
def get_rating_keys_list(self, rating_key='', media_type=''):
if media_type == 'movie':
key_list = {0: {'rating_key': int(rating_key)}}
return key_list
if media_type == 'artist' or media_type == 'album' or media_type == 'track':
match_type = 'title'
else:
match_type = 'index'
# get grandparent rating key
if media_type == 'season' or media_type == 'album':
try:
metadata = self.get_metadata_details(rating_key=rating_key)
rating_key = metadata['metadata']['parent_rating_key']
except:
logger.warn("Unable to get parent_rating_key for get_rating_keys_list.")
return {}
elif media_type == 'episode' or media_type == 'track':
try:
metadata = self.get_metadata_details(rating_key=rating_key)
rating_key = metadata['metadata']['grandparent_rating_key']
except:
logger.warn("Unable to get grandparent_rating_key for get_rating_keys_list.")
return {}
# get parent_rating_keys
metadata = self.get_metadata_children(str(rating_key), output_format='xml')
try:
xml_head = metadata.getElementsByTagName('MediaContainer')
except:
logger.warn("Unable to parse XML for get_rating_keys_list.")
return {}
for a in xml_head:
if a.getAttribute('size'):
if a.getAttribute('size') == '0':
return {}
title = helpers.get_xml_attr(a, 'title2')
if a.getElementsByTagName('Directory'):
parents_metadata = a.getElementsByTagName('Directory')
else:
parents_metadata = []
parents = {}
for item in parents_metadata:
parent_rating_key = helpers.get_xml_attr(item, 'ratingKey')
parent_index = helpers.get_xml_attr(item, 'index')
parent_title = helpers.get_xml_attr(item, 'title')
if parent_rating_key:
# get rating_keys
metadata = self.get_metadata_children(str(parent_rating_key), output_format='xml')
try:
xml_head = metadata.getElementsByTagName('MediaContainer')
except:
logger.warn("Unable to parse XML for get_rating_keys_list.")
return {}
for a in xml_head:
if a.getAttribute('size'):
if a.getAttribute('size') == '0':
return {}
if a.getElementsByTagName('Video'):
children_metadata = a.getElementsByTagName('Video')
elif a.getElementsByTagName('Track'):
children_metadata = a.getElementsByTagName('Track')
else:
children_metadata = []
children = {}
for item in children_metadata:
child_rating_key = helpers.get_xml_attr(item, 'ratingKey')
child_index = helpers.get_xml_attr(item, 'index')
child_title = helpers.get_xml_attr(item, 'title')
if child_rating_key:
key = int(child_index)
children.update({key: {'rating_key': int(child_rating_key)}})
key = int(parent_index) if match_type == 'index' else parent_title
parents.update({key:
{'rating_key': int(parent_rating_key),
'children': children}
})
key = 0 if match_type == 'index' else title
key_list = {key:
{'rating_key': int(rating_key),
'children': parents}
}
return key_list

View file

@ -761,6 +761,7 @@ class WebInterface(object):
@cherrypy.expose
def info(self, item_id=None, source=None, **kwargs):
metadata = None
query = None
config = {
"pms_identifier": plexpy.CONFIG.PMS_IDENTIFIER
@ -774,12 +775,15 @@ class WebInterface(object):
result = pms_connect.get_metadata_details(rating_key=item_id)
if result:
metadata = result['metadata']
else:
data_factory = datafactory.DataFactory()
query = data_factory.get_search_query(rating_key=item_id)
if metadata:
return serve_template(templatename="info.html", data=metadata, title="Info", config=config)
else:
logger.warn('Unable to retrieve data.')
return serve_template(templatename="info.html", data=None, title="Info")
return serve_template(templatename="info.html", data=None, query=query, title="Info")
@cherrypy.expose
def get_user_recently_watched(self, user=None, user_id=None, limit='10', **kwargs):
@ -1335,3 +1339,105 @@ class WebInterface(object):
cherrypy.response.headers['Content-type'] = 'application/json'
return json.dumps({'message': 'no data received'})
@cherrypy.expose
def search(self, search_query=''):
query = search_query.replace('"', '')
return serve_template(templatename="search.html", title="Search", query=query)
@cherrypy.expose
def search_results(self, query, **kwargs):
pms_connect = pmsconnect.PmsConnect()
result = pms_connect.get_search_results(query)
if result:
cherrypy.response.headers['Content-type'] = 'application/json'
return json.dumps(result)
else:
logger.warn('Unable to retrieve data.')
@cherrypy.expose
def get_search_results_children(self, query, media_type=None, season_index=None, **kwargs):
pms_connect = pmsconnect.PmsConnect()
result = pms_connect.get_search_results(query)
if media_type:
result['results_list'] = {media_type: result['results_list'][media_type]}
if media_type == 'season' and season_index:
for season in result['results_list']['season']:
if season['index'] == season_index:
result['results_list']['season'] = [season]
break
if result:
return serve_template(templatename="info_search_results_list.html", data=result, title="Search Result List")
else:
logger.warn('Unable to retrieve data.')
return serve_template(templatename="info_search_results_list.html", data=None, title="Search Result List")
@cherrypy.expose
def update_history_rating_key(self, old_rating_key, new_rating_key, media_type, **kwargs):
data_factory = datafactory.DataFactory()
pms_connect = pmsconnect.PmsConnect()
old_key_list = data_factory.get_rating_keys_list(rating_key=old_rating_key, media_type=media_type)
new_key_list = pms_connect.get_rating_keys_list(rating_key=new_rating_key, media_type=media_type)
update_db = data_factory.update_rating_key(old_key_list=old_key_list,
new_key_list=new_key_list,
media_type=media_type)
if update_db:
cherrypy.response.headers['Content-type'] = 'application/json'
return json.dumps({'message': update_db})
else:
cherrypy.response.headers['Content-type'] = 'application/json'
return json.dumps({'message': 'no data received'})
# test code
@cherrypy.expose
def get_new_rating_keys(self, rating_key='', media_type='', **kwargs):
pms_connect = pmsconnect.PmsConnect()
result = pms_connect.get_rating_keys_list(rating_key=rating_key, media_type=media_type)
if result:
cherrypy.response.headers['Content-type'] = 'application/json'
return json.dumps(result)
else:
logger.warn('Unable to retrieve data.')
@cherrypy.expose
def get_old_rating_keys(self, rating_key='', media_type='', **kwargs):
data_factory = datafactory.DataFactory()
result = data_factory.get_rating_keys_list(rating_key=rating_key, media_type=media_type)
if result:
cherrypy.response.headers['Content-type'] = 'application/json'
return json.dumps(result)
else:
logger.warn('Unable to retrieve data.')
@cherrypy.expose
def get_map_rating_keys(self, old_rating_key, new_rating_key, media_type, **kwargs):
data_factory = datafactory.DataFactory()
pms_connect = pmsconnect.PmsConnect()
if new_rating_key:
old_key_list = data_factory.get_rating_keys_list(rating_key=old_rating_key, media_type=media_type)
new_key_list = pms_connect.get_rating_keys_list(rating_key=new_rating_key, media_type=media_type)
result = data_factory.update_rating_key(old_key_list=old_key_list,
new_key_list=new_key_list,
media_type=media_type)
if result:
cherrypy.response.headers['Content-type'] = 'application/json'
return json.dumps(result)
else:
logger.warn('Unable to retrieve data.')