Compare commits
217 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e386d3ee21 | ||
![]() |
285946bf94 | ||
![]() |
578ba52215 | ||
![]() |
5126c39c26 | ||
![]() |
5ec9e41244 | ||
![]() |
5eebf6592a | ||
![]() |
7994351644 | ||
![]() |
e445228b8a | ||
![]() |
46e6250329 | ||
![]() |
2edfc1e3da | ||
![]() |
f2024b0854 | ||
![]() |
5f2cf6cb7a | ||
![]() |
73664b6a03 | ||
![]() |
af131ce16d | ||
![]() |
414c4c2ffa | ||
![]() |
d2cdc2cea2 | ||
![]() |
b860f9a5e8 | ||
![]() |
e5535b6167 | ||
![]() |
6359c02c80 | ||
![]() |
1c589fbefa | ||
![]() |
b768ad8a19 | ||
![]() |
60878ed12e | ||
![]() |
328d744efd | ||
![]() |
92a868c3c6 | ||
![]() |
7b9210a5fc | ||
![]() |
fb872596d6 | ||
![]() |
a43efef28a | ||
![]() |
e30e6dfe35 | ||
![]() |
eaadd5e2d6 | ||
![]() |
c608c7c9fc | ||
![]() |
451485d706 | ||
![]() |
849675185d | ||
![]() |
b060a23733 | ||
![]() |
bee9091182 | ||
![]() |
f8e1ba6798 | ||
![]() |
e5ce57fead | ||
![]() |
c9e2d1d200 | ||
![]() |
88b6eae3bf | ||
![]() |
c7d6ee8021 | ||
![]() |
7480508af2 | ||
![]() |
3fcc44aacf | ||
![]() |
fcc4575a86 | ||
![]() |
ebd0276eae | ||
![]() |
35521e127f | ||
![]() |
2c83554631 | ||
![]() |
56b717b1a1 | ||
![]() |
56f601e2a5 | ||
![]() |
3867dd7bdd | ||
![]() |
b7dc28c3fb | ||
![]() |
e6383d52ad | ||
![]() |
0a2ebb8815 | ||
![]() |
e44a0fed22 | ||
![]() |
2d7585d64b | ||
![]() |
df290d995b | ||
![]() |
36ee5234b1 | ||
![]() |
48e601d5ff | ||
![]() |
84aa727387 | ||
![]() |
29204cb6ba | ||
![]() |
6b75a55ce8 | ||
![]() |
527e7be13d | ||
![]() |
2d739f64cf | ||
![]() |
39ae387e0e | ||
![]() |
aaa1f0aa33 | ||
![]() |
423360e820 | ||
![]() |
52fae6df0e | ||
![]() |
58ea4a65e1 | ||
![]() |
82025457ba | ||
![]() |
9d60ed7174 | ||
![]() |
d67f903107 | ||
![]() |
389ce97d03 | ||
![]() |
98f5c33af7 | ||
![]() |
dceab3791f | ||
![]() |
d44bd2f35b | ||
![]() |
82fc314b35 | ||
![]() |
6ff902e653 | ||
![]() |
0245668907 | ||
![]() |
3436175223 | ||
![]() |
8138c27242 | ||
![]() |
5705e677d4 | ||
![]() |
c2417de895 | ||
![]() |
9b03ffbaa1 | ||
![]() |
53c98d0acc | ||
![]() |
883a183208 | ||
![]() |
85a3f15531 | ||
![]() |
28d136075c | ||
![]() |
a682cd31af | ||
![]() |
9fc44f793b | ||
![]() |
16b1b2a781 | ||
![]() |
6e14c08570 | ||
![]() |
b5f1475a90 | ||
![]() |
8efb37c4ec | ||
![]() |
ab78b59f61 | ||
![]() |
e7b1e177d2 | ||
![]() |
e41fa7a08e | ||
![]() |
0f10c21a66 | ||
![]() |
f2b9984cd6 | ||
![]() |
99dd133a9a | ||
![]() |
7bbcc575b0 | ||
![]() |
0fded38bfb | ||
![]() |
88a651645a | ||
![]() |
60380c6a99 | ||
![]() |
298d98cee9 | ||
![]() |
b91a4844e0 | ||
![]() |
f00de8e548 | ||
![]() |
dfbf435c77 | ||
![]() |
dd7390d111 | ||
![]() |
73125153c8 | ||
![]() |
c08d581df3 | ||
![]() |
ef5d9d6604 | ||
![]() |
84ffcc7948 | ||
![]() |
53830a7711 | ||
![]() |
a126703f44 | ||
![]() |
6720d696eb | ||
![]() |
7328fcd0fb | ||
![]() |
1e57e952db | ||
![]() |
d627e0efdb | ||
![]() |
2193d06363 | ||
![]() |
3fc573bd90 | ||
![]() |
9b9088bd5e | ||
![]() |
53fb8b999f | ||
![]() |
58efd299cc | ||
![]() |
8ae2f718f4 | ||
![]() |
ff0ed1abe4 | ||
![]() |
41e1fee4b4 | ||
![]() |
b308e92c9d | ||
![]() |
82808cdc97 | ||
![]() |
6d5e7135ae | ||
![]() |
4a75e372d9 | ||
![]() |
a37ca3f29c | ||
![]() |
9016fc41f9 | ||
![]() |
f56ba300ac | ||
![]() |
0a850fc3af | ||
![]() |
5124ebfe16 | ||
![]() |
4de252d8e4 | ||
![]() |
c5237547cf | ||
![]() |
af85d36762 | ||
![]() |
41c94741e2 | ||
![]() |
8b07b53b43 | ||
![]() |
445dd1250f | ||
![]() |
4df8014102 | ||
![]() |
35320649db | ||
![]() |
c62ec71cfc | ||
![]() |
b685d17969 | ||
![]() |
ba5fd4b9d3 | ||
![]() |
79757da2b1 | ||
![]() |
77f5224e13 | ||
![]() |
31805e39e6 | ||
![]() |
ff6d43c398 | ||
![]() |
81a8d93336 | ||
![]() |
609549f974 | ||
![]() |
7ba6d704cd | ||
![]() |
0ff1b4d5dd | ||
![]() |
7eac521369 | ||
![]() |
1d8a16f3c4 | ||
![]() |
685f8cabdb | ||
![]() |
70141eda22 | ||
![]() |
0204478483 | ||
![]() |
79f317709b | ||
![]() |
0f04a6d25b | ||
![]() |
2d19accdd1 | ||
![]() |
3d5706002d | ||
![]() |
ac3bafcd02 | ||
![]() |
ac9a8b85ec | ||
![]() |
abec036cb2 | ||
![]() |
8bdd40f011 | ||
![]() |
b98c17e738 | ||
![]() |
a71c6e6592 | ||
![]() |
545b7cb581 | ||
![]() |
0848e317ee | ||
![]() |
e976e6cf5c | ||
![]() |
95cd2b4f81 | ||
![]() |
3df73dc287 | ||
![]() |
3a703eb605 | ||
![]() |
e94c00ca48 | ||
![]() |
d6a34b3e6b | ||
![]() |
3cbf05d54a | ||
![]() |
cba8608c23 | ||
![]() |
e34865d0dd | ||
![]() |
b2a7f639bb | ||
![]() |
fcbc921470 | ||
![]() |
5168d76e86 | ||
![]() |
968d213b97 | ||
![]() |
9fc4573c42 | ||
![]() |
9adf5cc39a | ||
![]() |
2ca04f4a8b | ||
![]() |
fd3b2a48f9 | ||
![]() |
01b3ae377b | ||
![]() |
5a1516286c | ||
![]() |
317a9f0b8e | ||
![]() |
c98505038a | ||
![]() |
1ec1edefdd | ||
![]() |
aa351bd965 | ||
![]() |
519ff6b203 | ||
![]() |
5b2beb2b81 | ||
![]() |
13e6a70a30 | ||
![]() |
7e99eb7a2a | ||
![]() |
6efaabb630 | ||
![]() |
2536fdf17b | ||
![]() |
7e8a427107 | ||
![]() |
bbaf428fd8 | ||
![]() |
7dfd063138 | ||
![]() |
5c94b21bd1 | ||
![]() |
58474d9565 | ||
![]() |
6cb1c057cf | ||
![]() |
22cc06dec3 | ||
![]() |
357797df6b | ||
![]() |
4c6f6ca736 | ||
![]() |
c0214f1489 | ||
![]() |
dd27f9bf72 | ||
![]() |
c1c7911d08 | ||
![]() |
755e9107fa | ||
![]() |
3bb6320fc1 | ||
![]() |
b5ad88ae5a | ||
![]() |
bbcf3bf7da | ||
![]() |
51e1949538 | ||
![]() |
6b1a57e650 | ||
![]() |
8e57df53fd |
110
CHANGELOG.md
110
CHANGELOG.md
@@ -1,13 +1,101 @@
|
||||
# Changelog
|
||||
|
||||
## v1.0 (2015-08-11)
|
||||
## v1.1.9 (2015-09-14)
|
||||
|
||||
* First release
|
||||
* Another JonnyWong release. I'm going to stop thanking you now ;)
|
||||
* Add music plays to graphs.
|
||||
* Add info pages for music items.
|
||||
* Add music to user recently watched items.
|
||||
* Add photo views to Activity pane (photos are not logged).
|
||||
* Fix token validation message on Settings page.
|
||||
* Fix some "Mystery" platform names.
|
||||
* Fix paused time be counted for graph data.
|
||||
* Other small bug fixes.
|
||||
|
||||
## v1.0.1 (2015-08-13)
|
||||
|
||||
* Allow SSL certificate check override for certain systems with bad CA stores.
|
||||
* Fix typo on graphs page causing date selection to break on Safari.
|
||||
## v1.1.8 (2015-09-09)
|
||||
|
||||
* Add platform images for Windows devices. Thanks @JonnyWong.
|
||||
* Add click-through to PlexWeb preplay page from info page. Thanks @JonnyWong.
|
||||
* Fix broken delete option on info pages. Thanks @JonnyWong.
|
||||
* Fix tagline bug in PlexWatch db import tool.
|
||||
* Fix home stats text overflow bug. Thanks @JonnyWong.
|
||||
|
||||
|
||||
## v1.1.7 (2015-09-07)
|
||||
|
||||
* Show tagline in info screens for movies. Thanks @JonnyWong.
|
||||
* Add play/pause/buffer icon to activity pane. Thanks @JonnyWong.
|
||||
* Add transcoder info in activity pane info. Thanks @JonnyWong.
|
||||
* Show transcoder progress on activity progress bar. Thanks @JonnyWong.
|
||||
* Fix bug where custom notification strings would be ignored if unicode characters were present.
|
||||
* Fix text overflow issue on home stats cards. Thanks @JonnyWong.
|
||||
* Fix regression with user friendly name change input in edit screen. Thanks @JonnyWong.
|
||||
|
||||
|
||||
## v1.1.6 (2015-09-06)
|
||||
|
||||
* Home stats cards are now expandable to show multiple items. Configurable in settings. Thanks @JonnyWong.
|
||||
* Completely redesigned media info pages. Thanks @JonnyWong.
|
||||
* Redesigned activity pane to match Plex Web more closely. Thanks @JonnyWong.
|
||||
* New Library stats on home page, shows total item counts per library. Thanks @JonnyWong.
|
||||
* New last watched card in home stats. Shows last watched items. Thanks @JonnyWong.
|
||||
* Improved some layout issues on mobile devices. Thanks @JonnyWong.
|
||||
* Fixed issue where some clip/channel items are reported as episodes and causing exceptions.
|
||||
* Many styling improvements and fixes. Thanks @JonnyWong.
|
||||
* Fixed incorrect sort on home stats platform count by duration. Thanks @JonnyWong.
|
||||
* Fix issue where user refresh would continually be called as "Local" user didn't exist in database.
|
||||
* Fixed styling on graph stream modal. Thanks @JonnyWong.
|
||||
* Fixed some issues with users page editing. Thanks @JonnyWong.
|
||||
* Fix error page when clicking through to an item that no longer exists.
|
||||
|
||||
|
||||
## v1.1.5 (2015-08-27)
|
||||
|
||||
* Fix git tag being one release behind.
|
||||
|
||||
|
||||
## v1.1.4 (2015-08-26)
|
||||
|
||||
* User info is now editable from the users table. Thanks @JonnyWong.
|
||||
* Improved delete mode for history pages - able to multi-select now. Thanks @JonnyWong.
|
||||
* Improved image quality on tooltip images.
|
||||
* More styling improvements and fixes on user and info pages. Thanks @JonnyWong.
|
||||
* Added some user submitted systemd init scripts. Thanks @malle-pietje and @artbird309.
|
||||
* Fixed some background operations when saving settings.
|
||||
* Fix max width restricting home stats to 1600px.
|
||||
* Fix stream duration parameter for notifications when paused counter is null.
|
||||
|
||||
|
||||
## v1.1.3 (2015-08-22)
|
||||
|
||||
* Show human readable version info and this cool changelog in Settings -> General.
|
||||
* Add a "delete" mode to the history tables. Toggle it to show a delete button next to each history item.
|
||||
* Two digit season and episode numbers for custom notification messages. Thanks @JonnyWong.
|
||||
* New FreeNAS init script. Thanks @JonnyWong.
|
||||
* Lots of styling improvements! Thanks @JonnyWong.
|
||||
* Graph page remembers last selected options. Thanks @JonnyWong.
|
||||
* New Popular movie homepage stats. Thanks @JonnyWong.
|
||||
* Add option for duration vs play count on home stats. (Settings -> Extra Settings). Thanks @JonnyWong.
|
||||
* Clean up media info pages. Don't show metadata that is missing. Thanks @JonnyWong.
|
||||
* Add clear button to search inputs. Thanks @JonnyWong.
|
||||
* New columns on Users list. Thanks @JonnyWong.
|
||||
* New stream duration option for custom notification messages. Thanks @JonnyWong.
|
||||
* Rad new tooltips on the history pages. Thanks @JonnyWong.
|
||||
* And a lot of small visual changes and fixes. Thanks @JonnyWong.
|
||||
* Fixed IP address modal on user history page.
|
||||
* Fixed "invalid date" showing on monthly plays graph.
|
||||
|
||||
## v1.1.2 (2015-08-16)
|
||||
|
||||
* Fix bug where user refresh would fail under certain circumstances.
|
||||
|
||||
## v1.1.1 (2015-08-15)
|
||||
|
||||
* Added Most watched movie for home stats. Thanks @jroyal.
|
||||
* Added TV show title to recently added text. Thanks @jroyal.
|
||||
* Fix bug with buffer warnings where notification would trigger continuously after first trigger.
|
||||
* Fix bug where custom avatar URL would get reset on every user refresh.
|
||||
|
||||
## v1.1.0 (2015-08-15)
|
||||
|
||||
@@ -24,13 +112,11 @@
|
||||
* Fix behaviour of close button on update popup, will now stay closed for an hour after clicking close.
|
||||
* Fix some styling niggles.
|
||||
|
||||
## v1.1.1 (2015-08-15)
|
||||
## v1.0.1 (2015-08-13)
|
||||
|
||||
* Added Most watched movie for home stats. Thanks @jroyal.
|
||||
* Added TV show title to recently added text. Thanks @jroyal.
|
||||
* Fix bug with buffer warnings where notification would trigger continuously after first trigger.
|
||||
* Fix bug where custom avatar URL would get reset on every user refresh.
|
||||
* Allow SSL certificate check override for certain systems with bad CA stores.
|
||||
* Fix typo on graphs page causing date selection to break on Safari.
|
||||
|
||||
## v1.1.2 (2015-08-16)
|
||||
## v1.0 (2015-08-11)
|
||||
|
||||
* Fix bug where user refresh would fail under certain circumstances.
|
||||
* First release
|
@@ -64,7 +64,7 @@ def main():
|
||||
|
||||
# Set up and gather command line arguments
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Python frontend for PlexWatch.')
|
||||
description='A Python based monitoring and tracking tool for Plex Media Server.')
|
||||
|
||||
parser.add_argument(
|
||||
'-v', '--verbose', action='store_true', help='Increase console logging verbosity')
|
||||
|
65
README.md
65
README.md
@@ -1,12 +1,14 @@
|
||||
#PlexPy
|
||||
|
||||
[](https://gitter.im/drzoidberg33/plexpy?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
A python based web application for monitoring, analytics and notifications for Plex Media Server (www.plex.tv).
|
||||
|
||||
This project is based on code from Headphones (https://github.com/rembo10/headphones) and PlexWatchWeb (https://github.com/ecleese/plexWatchWeb).
|
||||
|
||||
* plexPy forum thread: https://forums.plex.tv/discussion/169591/plexpy-another-plex-monitoring-program
|
||||
|
||||
If you'd like to buy me a beer, hit the donate button below.
|
||||
If you'd like to buy me a beer, hit the donate button below. All donations go to the project maintainer (primarily for the procurement of liquid refreshment).
|
||||
|
||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=G9HZK9BDJLKT6)
|
||||
|
||||
@@ -34,6 +36,14 @@ If you'd like to buy me a beer, hit the donate button below.
|
||||
* stream type (direct, transcoded)
|
||||
* video type & resolution
|
||||
* audio type & channel count.
|
||||
|
||||
* Top statistics on home page with configurable duration and measurement metric:
|
||||
* Most watched TV
|
||||
* Most popular TV
|
||||
* Most watched Movie
|
||||
* Most popular Movie
|
||||
* Most active user
|
||||
* Most active platform
|
||||
|
||||
* Recently added media and how long ago it was added
|
||||
|
||||
@@ -41,42 +51,49 @@ If you'd like to buy me a beer, hit the donate button below.
|
||||
* date
|
||||
* user
|
||||
* platform
|
||||
* ip address (if enabled in plexWatch)
|
||||
* ip address
|
||||
* title
|
||||
* stream information details
|
||||
* start time
|
||||
* paused duration length
|
||||
* stop time
|
||||
* duration length
|
||||
* percentage completed
|
||||
* watched progress
|
||||
* show/hide columns
|
||||
* delete mode - allows deletion of specific history items
|
||||
|
||||
* Full user list with general information and comparison stats
|
||||
|
||||
* Individual user information
|
||||
- username and gravatar (if available)
|
||||
- daily, weekly, monthly, all time stats for play count and duration length
|
||||
- individual platform stats for each user
|
||||
- public ip address history with last seen date and geo tag location
|
||||
- recently watched content
|
||||
- watching history
|
||||
- synced items
|
||||
* username and gravatar (if available)
|
||||
* daily, weekly, monthly, all time stats for play count and duration length
|
||||
* individual platform stats for each user
|
||||
* public ip address history with last seen date and geo tag location
|
||||
* recently watched content
|
||||
* watching history
|
||||
* synced items
|
||||
* assign users custom friendly names within PlexPy
|
||||
* assign users custom avatar URL within PlexPy
|
||||
* disable history logging per user
|
||||
* disable notifications per user
|
||||
* option to purge all history per user.
|
||||
|
||||
* Rich analytics presented using Highcharts graphing
|
||||
- user-selectable time periods of 30, 90 or 365 days
|
||||
- daily watch count and duration
|
||||
- totals by day of week and hours of the day
|
||||
- totals by top 10 platform
|
||||
- totals by top 10 users
|
||||
- detailed breakdown by transcode decision
|
||||
- source and stream resolutions
|
||||
- transcode decision counts by user and platform
|
||||
- total monthly counts
|
||||
* user-selectable time periods of 30, 90 or 365 days
|
||||
* daily watch count and duration
|
||||
* totals by day of week and hours of the day
|
||||
* totals by top 10 platform
|
||||
* totals by top 10 users
|
||||
* detailed breakdown by transcode decision
|
||||
* source and stream resolutions
|
||||
* transcode decision counts by user and platform
|
||||
* total monthly counts
|
||||
|
||||
* Content information pages
|
||||
- movies (includes watching history)
|
||||
- tv shows (includes watching history)
|
||||
- tv seasons
|
||||
- tv episodes (includes watching history)
|
||||
* movies (includes watching history)
|
||||
* tv shows (includes watching history)
|
||||
* tv seasons
|
||||
* tv episodes (includes watching history)
|
||||
|
||||
* Full sync list data on all users syncing items from your library
|
||||
|
||||
@@ -105,4 +122,4 @@ If you **comply with these rules** you can [post your request/issue](http://gith
|
||||
## License
|
||||
This is free software under the GPL v3 open source license. Feel free to do with it what you wish, but any modification must be open sourced. A copy of the license is included.
|
||||
|
||||
This software includes Highsoft software libraries which you may freely distribute for non-commercial use. Commerical users must licence this software, for more information visit https://shop.highsoft.com/faq/non-commercial#non-commercial-redistribution.
|
||||
This software includes Highsoft software libraries which you may freely distribute for non-commercial use. Commerical users must licence this software, for more information visit https://shop.highsoft.com/faq/non-commercial#non-commercial-redistribution.
|
||||
|
@@ -1,4 +1,4 @@
|
||||
<%
|
||||
<%
|
||||
import plexpy
|
||||
from plexpy import version
|
||||
%>
|
||||
@@ -52,7 +52,7 @@ from plexpy import version
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="home">
|
||||
<img alt="PlexPy" src="interfaces/default/images/logo-plexpy@2x.png" height="40px">
|
||||
<img alt="PlexPy" src="interfaces/default/images/logo-plexpy@2x.png" height="40">
|
||||
</a>
|
||||
</div>
|
||||
<div class="collapse navbar-collapse navbar-right" id="navbar-collapse-1">
|
||||
@@ -62,16 +62,16 @@ from plexpy import version
|
||||
% else:
|
||||
<li><a href="home"><i class="fa fa-lg fa-home"></i></a></li>
|
||||
% endif
|
||||
% if title=="History":
|
||||
<li class="active"><a href="history">History</a></li>
|
||||
% else:
|
||||
<li><a href="history">History</a></li>
|
||||
% endif
|
||||
% if title=="Users" or title=="User":
|
||||
<li class="active"><a href="users">Users</a></li>
|
||||
% else:
|
||||
<li><a href="users">Users</a></li>
|
||||
% endif
|
||||
% if title=="History":
|
||||
<li class="active"><a href="history">History</a></li>
|
||||
% else:
|
||||
<li><a href="history">History</a></li>
|
||||
% endif
|
||||
% if title=="Graphs":
|
||||
<li class="active"><a href="graphs">Graphs</a></li>
|
||||
% else:
|
||||
@@ -99,7 +99,9 @@ from plexpy import version
|
||||
</div>
|
||||
|
||||
${next.headerIncludes()}
|
||||
${next.body()}
|
||||
<div class="body-container">
|
||||
${next.body()}
|
||||
</div>
|
||||
|
||||
<script src="interfaces/default/js/jquery-2.1.4.min.js"></script>
|
||||
<script src="interfaces/default/js/bootstrap3/bootstrap.min.js"></script>
|
||||
|
@@ -75,6 +75,7 @@ ul.ColVis_collection {
|
||||
background-color: #444;
|
||||
overflow: hidden;
|
||||
z-index: 2002;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
ul.ColVis_collection li {
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -28,10 +28,15 @@ user Returns the name of the user owning the session.
|
||||
user_id Returns the Plex user id if available.
|
||||
machine_id Returns the machine id of the players being used.
|
||||
friendly_name Returns the friendlly name of the user owning the session.
|
||||
user_thumb Returns the profile picture of the user owning the session.
|
||||
state Returns the state of the current session. Either 'playing', 'paused' or 'buffering'.
|
||||
title Returns the name of the episode, movie or music track.
|
||||
year Returns the year of the episode, movie, or clip.
|
||||
player Returns the name of the platform used to play the stream.
|
||||
platform Returns the type of platform used to play the stream.
|
||||
throttled Returns true if the transcode session is throttled.
|
||||
transcode_progress Returns the current transcode progress of the item. 0 to 100.
|
||||
transcode_speed Returns the current transcode speed of the item.
|
||||
audio_decision Returns the audio transcode decision. Either 'transcode', 'copy' or 'direct play'.
|
||||
audio_codec Returns the name of the audio codec.
|
||||
audio_channels Returns the number of audio channels.
|
||||
@@ -61,159 +66,203 @@ DOCUMENTATION :: END
|
||||
% if data is not None:
|
||||
% if data['stream_count'] != '0':
|
||||
% for a in data['sessions']:
|
||||
<div class="instance" id="instance-${a['session_key']}">
|
||||
<div class="poster">
|
||||
<div class="dashboard-activity-poster-face">
|
||||
<div class="dashboard-instance" id="instance-${a['session_key']}">
|
||||
% if a['type'] == 'movie' or a['type'] == 'episode' or a['type'] == 'track':
|
||||
<a href="info?item_id=${a['rating_key']}">
|
||||
% endif
|
||||
<div class="dashboard-activity-poster">
|
||||
% if a['type'] == 'movie' and not a['indexes']:
|
||||
<img src="pms_image_proxy?img=${a['art']}&width=410&height=230"/>
|
||||
<div class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img=${a['art']}&width=500&height=280);"></div>
|
||||
% elif a['type'] == 'episode' and not a['indexes']:
|
||||
<div class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img=${a['art']}&width=500&height=280);"></div>
|
||||
% elif a['indexes']:
|
||||
<img onload="fadeIn(this)" src="pms_image_proxy?img=${a['bif_thumb']}&width=300&height=169" style="display: none;"/>
|
||||
<div class="dashboard-activity-poster-face bif" style="background-image: url(pms_image_proxy?img=${a['bif_thumb']}&width=500&height=280); display: none;"></div>
|
||||
% else:
|
||||
% if a['type'] == 'track':
|
||||
<div class="dashboard-activity-poster-music-bg" style="background-image: url('pms_image_proxy?img=${a['thumb']}&width=300&height=300');"></div>
|
||||
% endif
|
||||
% if a['type'] == 'clip':
|
||||
<img src="${a['thumb']}"/>
|
||||
% else:
|
||||
<img src="pms_image_proxy?img=${a['thumb']}&width=410&height=230&fallback=cover"/>
|
||||
% endif
|
||||
% endif
|
||||
<div class="dashboard-activity-poster-info-bar">
|
||||
<div class="dashboard-activity-poster-info-text">
|
||||
% if a['type'] == 'track':
|
||||
Track ${a['media_index']}
|
||||
% elif a['type'] == 'episode':
|
||||
Season ${a['parent_media_index']}, Episode ${a['media_index']}
|
||||
% if a['type'] == 'track':
|
||||
<div class="dashboard-activity-cover-face-bg" style="background-image: url(pms_image_proxy?img=${a['thumb']}&width=300&height=300);"></div>
|
||||
<div class="dashboard-activity-cover-face" style="background-image: url(pms_image_proxy?img=${a['thumb']}&width=300&height=300);"></div>
|
||||
% elif a['type'] == 'clip':
|
||||
% if a['art'][:4] == 'http':
|
||||
<div class="dashboard-activity-poster-face" style="background-image: url(${a['art']});"></div>
|
||||
% elif a['thumb'][:4] == 'http':
|
||||
<div class="dashboard-activity-poster-face" style="background-image: url(${a['thumb']});"></div>
|
||||
% else:
|
||||
${a['title']}
|
||||
% if a['art']:
|
||||
<div class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img=${a['art']}&width=500&height=280);"></div>
|
||||
% else:
|
||||
<div class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img=${a['thumb']}&width=500&height=280);"></div>
|
||||
% endif
|
||||
% endif
|
||||
% elif a['type'] == 'photo':
|
||||
<div class="dashboard-activity-poster-face bif" style="background-image: url(pms_image_proxy?img=${a['thumb']}&width=500&height=500);"></div>
|
||||
% else:
|
||||
<div class="dashboard-activity-cover-face" style="background-image: url(pms_image_proxy?img=${a['thumb']}&width=300&height=300&fallback=cover);"></div>
|
||||
% endif
|
||||
% endif
|
||||
<div class="dashboard-activity-button-info">
|
||||
<button type="button" class="btn btn-activity-info btn-lg" data-target="#stream-${a['session_key']}">
|
||||
<i class="fa fa-info-circle"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div id="stream-${a['session_key']}" class="dashboard-activity-info-details-overlay">
|
||||
<div class="dashboard-activity-info-details-content">
|
||||
<div class="dashboard-activity-info-platform" id="platform-${a['session_key']}">
|
||||
<strong>${a['player']}</strong><br />
|
||||
% if a['state'] == 'playing':
|
||||
State <strong>Playing</strong>
|
||||
% elif a['state'] == 'paused':
|
||||
State <strong>Paused</strong>
|
||||
% elif a['state'] == 'buffering':
|
||||
State <strong>Buffering</strong>
|
||||
% endif
|
||||
</div>
|
||||
% if a['type'] == 'track':
|
||||
% if a['audio_decision'] == 'direct play':
|
||||
Stream <strong>Direct Play</strong>
|
||||
% else:
|
||||
Stream <strong>Transcoding
|
||||
(Speed: ${a['transcode_speed']})
|
||||
% if a['throttled'] == '1':
|
||||
(Throttled)
|
||||
% endif
|
||||
</strong>
|
||||
% endif
|
||||
<br />
|
||||
% if a['audio_decision'] == 'direct play':
|
||||
Audio <strong>Direct Play (${a['audio_codec']}) (${a['audio_channels']}ch)</strong>
|
||||
% elif a['audio_decision'] == 'Copy':
|
||||
Audio <strong>Direct Stream (${a['transcode_audio_codec']}) (${a['transcode_audio_channels']}ch)</strong>
|
||||
% elif a['audio_decision'] != 'transcode':
|
||||
Audio <strong>Transcode (${a['transcode_audio_codec']}) (${a['transcode_audio_channels']}ch)</strong>
|
||||
% endif
|
||||
% elif a['type'] == 'episode' or a['type'] == 'movie' or a['type'] == 'clip':
|
||||
% if a['video_decision'] == 'direct play':
|
||||
Stream <strong>Direct Play</strong>
|
||||
% else:
|
||||
Stream <strong>Transcoding
|
||||
(Speed: ${a['transcode_speed']})
|
||||
% if a['throttled'] == '1':
|
||||
(Throttled)
|
||||
% endif
|
||||
</strong>
|
||||
% endif
|
||||
<br />
|
||||
% if a['video_decision'] == 'direct play':
|
||||
Video <strong>Direct Play (${a['video_codec']}) (${a['width']}x${a['height']})</strong>
|
||||
% elif a['video_decision'] == 'copy':
|
||||
Video <strong>Direct Stream (${a['transcode_video_codec']}) (${a['width']}x${a['height']})</strong>
|
||||
% elif a['video_decision'] == 'transcode':
|
||||
Video <strong>Transcode (${a['transcode_video_codec']}) (${a['transcode_width']}x${a['transcode_height']})</strong>
|
||||
% endif
|
||||
<br />
|
||||
% if a['audio_decision'] == 'direct play':
|
||||
Audio <strong>Direct Play (${a['audio_codec']}) (${a['audio_channels']}ch)</strong>
|
||||
% elif a['audio_decision'] == 'copy':
|
||||
Audio <strong>Direct Stream (${a['transcode_audio_codec']}) (${a['transcode_audio_channels']}ch)</strong>
|
||||
% elif a['audio_decision'] == 'transcode':
|
||||
Audio <strong>Transcode (${a['transcode_audio_codec']}) (${a['transcode_audio_channels']}ch)</strong>
|
||||
% endif
|
||||
% elif a['type'] == 'photo':
|
||||
% if a['video_decision'] == 'direct play':
|
||||
Stream <strong>Direct Play</strong>
|
||||
% else:
|
||||
Stream <strong>
|
||||
Transcoding
|
||||
(Speed: ${a['transcode_speed']})
|
||||
% if a['throttled'] == '1':
|
||||
(Throttled)
|
||||
% endif
|
||||
</strong>
|
||||
% endif
|
||||
% endif
|
||||
<br>
|
||||
</div>
|
||||
</div>
|
||||
% if a['type'] != 'photo':
|
||||
<div class="dashboard-activity-poster-info-bar">
|
||||
<div class="dashboard-activity-poster-info-time">
|
||||
<span class="progress_time">${a['progress']}</span>/<span class="progress_time">${a['duration']}</span>
|
||||
</div>
|
||||
</div>
|
||||
% endif
|
||||
</div>
|
||||
<div class='dashboard-activity-metadata-wrapper'>
|
||||
<div class='dashboard-activity-instance-overlay'>
|
||||
<div class='dashboard-activity-metadata-progress-minutes'>
|
||||
<div class='activity-progress'>
|
||||
<div class="bar" style="width: ${a['progress_percent']}%">${a['progress_percent']}%</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dashboard-activity-metadata-platform" data-toggle="tooltip" data-placement="right" title data-title="${a['player']}" id="platform-${a['session_key']}">
|
||||
</div>
|
||||
<div class="dashboard-activity-metadata-user">
|
||||
% if a['user_id']:
|
||||
<a href="user?user_id=${a['user_id']}">${a['friendly_name']}</a> is ${a['state']}
|
||||
% else:
|
||||
<a href="user?user=${a['user']}">${a['friendly_name']}</a> is ${a['state']}
|
||||
% endif
|
||||
</div>
|
||||
<div class="dashboard-activity-metadata-title">
|
||||
% if a['type'] == 'episode':
|
||||
<a href="info?item_id=${a['rating_key']}">${a['grandparent_title']} - ${a['title']}</a>
|
||||
% elif a['type'] == 'movie':
|
||||
<a href="info?item_id=${a['rating_key']}">${a['title']}</a>
|
||||
% elif a['type'] == 'clip':
|
||||
${a['title']}
|
||||
% elif a['type'] == 'track':
|
||||
${a['grandparent_title']} - ${a['title']}
|
||||
% else:
|
||||
${a['grandparent_title']} - ${a['title']}
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
<div id="stream-${a['session_key']}" class="collapse out">
|
||||
<div class='dashboard-activity-info-details-overlay'>
|
||||
<div class='dashboard-activity-info-details-content'>
|
||||
% if a['type'] == 'track':
|
||||
Artist: <strong>${a['grandparent_title']}</strong>
|
||||
<br>
|
||||
Album: <strong>${a['parent_title']}</strong>
|
||||
<br>
|
||||
% endif
|
||||
% if a['state'] == 'playing':
|
||||
State: <strong>Playing</strong>
|
||||
% elif a['state'] == 'paused':
|
||||
State: <strong>Paused</strong>
|
||||
% elif a['state'] == 'buffering':
|
||||
State: <strong>Buffering</strong>
|
||||
% endif
|
||||
<br>
|
||||
% if a['type'] == 'track':
|
||||
% if a['audio_decision'] == 'direct play':
|
||||
Stream: <strong>Direct Play</strong>
|
||||
% else:
|
||||
Stream: <strong>Transcoding</strong>
|
||||
% endif
|
||||
<br/>
|
||||
% if a['audio_decision'] != 'direct play':
|
||||
Audio: <strong>${a['transcode_audio_codec']} (${a['transcode_audio_channels']}ch)</strong>
|
||||
% elif a['audio_decision'] == 'direct play':
|
||||
Audio: <strong>${a['audio_codec']} (${a['audio_channels']}ch)</strong>
|
||||
% endif
|
||||
% elif a['type'] == 'episode' or a['type'] == 'movie' or a['type'] == 'clip':
|
||||
% if a['video_decision'] == 'direct play':
|
||||
Stream: <strong>Direct Play</strong>
|
||||
% else:
|
||||
Stream: <strong>Transcoding</strong>
|
||||
% endif
|
||||
<br/>
|
||||
% if a['video_decision'] != 'direct play':
|
||||
Video: <strong>${a['video_decision']} (${a['transcode_video_codec']})
|
||||
(${a['transcode_width']}x${a['transcode_height']})</strong>
|
||||
% elif a['audio_decision'] == 'direct play':
|
||||
Video: <strong>${a['video_decision']} (${a['video_codec']})
|
||||
(${a['width']}x${a['height']})</strong>
|
||||
% endif
|
||||
<br/>
|
||||
% if a['audio_decision'] != 'direct play':
|
||||
Audio: <strong>${a['audio_decision']} ${a['transcode_audio_codec']} (${a['transcode_audio_channels']}ch)</strong>
|
||||
% elif a['audio_decision'] == 'direct play':
|
||||
Audio: <strong>${a['audio_codec']} (${a['audio_channels']}ch)</strong>
|
||||
% endif
|
||||
% endif
|
||||
<br>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
% if a['type'] == 'movie' or a['type'] == 'episode' or a['type'] == 'track':
|
||||
</a>
|
||||
% endif
|
||||
<div class="dashboard-activity-progress">
|
||||
<div class="dashboard-activity-progress-bar">
|
||||
<div class="bufferbar" style="width: ${a['transcode_progress']}%">${a['transcode_progress']}%</div>
|
||||
<div class="bar" style="width: ${a['progress_percent']}%">${a['progress_percent']}%</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dashboard-activity-button-info">
|
||||
<button type="button" class="btn btn-activity-info btn-sm" data-toggle="collapse" data-target="#stream-${a['session_key']}">
|
||||
<i class='fa fa-info-circle'></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="dashboard-activity-metadata-wrapper">
|
||||
<a href="user?user_id=${a['user_id']}">
|
||||
<div class="dashboard-activity-metadata-user-thumb" style="background-image: url(${a['user_thumb']});"></div>
|
||||
</a>
|
||||
<div class="dashboard-activity-metadata-title">
|
||||
% if a['state'] == 'playing':
|
||||
<i class="fa fa-play"></i>
|
||||
% elif a['state'] == 'paused':
|
||||
<i class="fa fa-pause"></i>
|
||||
% elif a['state'] == 'buffering':
|
||||
<i class="fa fa-spinner"></i>
|
||||
% endif
|
||||
% if a['type'] == 'episode':
|
||||
<a href="info?item_id=${a['rating_key']}">${a['grandparent_title']} - ${a['title']}</a>
|
||||
% elif a['type'] == 'movie':
|
||||
<a href="info?item_id=${a['rating_key']}">${a['title']}</a>
|
||||
% elif a['type'] == 'clip':
|
||||
${a['title']}
|
||||
% elif a['type'] == 'track':
|
||||
<a href="info?item_id=${a['rating_key']}">${a['grandparent_title']} - ${a['title']}</a>
|
||||
% elif a['type'] == 'photo':
|
||||
${a['parent_title']}
|
||||
% else:
|
||||
${a['title']}
|
||||
% endif
|
||||
</div>
|
||||
<div class="dashboard-activity-metadata-subtitle">
|
||||
% if a['type'] == 'episode':
|
||||
S${a['parent_media_index']} · E${a['media_index']}
|
||||
% elif a['type'] == 'movie':
|
||||
${a['year']}
|
||||
% elif a['type'] == 'track':
|
||||
<a href="info?item_id=${a['parent_rating_key']}">${a['parent_title']}</a>
|
||||
% elif a['type'] == 'photo':
|
||||
${a['title']}
|
||||
% else:
|
||||
${a['year']}
|
||||
% endif
|
||||
</div>
|
||||
<div class="dashboard-activity-metadata-user">
|
||||
% if a['user_id']:
|
||||
<a href="user?user_id=${a['user_id']}">${a['friendly_name']}</a>
|
||||
% else:
|
||||
<a href="user?user=${a['user']}">${a['friendly_name']}</a>
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$("#platform-${a['session_key']}").html("<img src='" + getPlatformImagePath('${a['platform']}') + "'>");
|
||||
$("#platform-${a['session_key']}").prepend("<div class='dashboard-activity-info-platform-box' style='background-image: url(" + getPlatformImagePath('${a['platform']}') + ");'>");
|
||||
</script>
|
||||
|
||||
% endfor
|
||||
<script>
|
||||
// When using bif indexes make the image transition a little smoother.
|
||||
function fadeIn(obj) {
|
||||
$(obj).fadeIn(450);
|
||||
}
|
||||
$('.bif').each(function() {
|
||||
$(this).hide().fadeIn(1000);
|
||||
});
|
||||
|
||||
// Convert timestamps to readable times
|
||||
$('.progress_time').each(function(index) {
|
||||
$(this).html(millisecondsToMinutes($(this).text(), false));
|
||||
});
|
||||
|
||||
// Hide the info bar on page load
|
||||
$('.dashboard-activity-poster-info-bar').hide();
|
||||
|
||||
// When mouse over the activity pane, show an info bar with extra info.
|
||||
$('.dashboard-activity-poster-face').mouseenter(function() {
|
||||
$('.dashboard-activity-poster-info-bar', this).slideDown('fast');
|
||||
});
|
||||
$('.dashboard-activity-poster-face').mouseleave(function() {
|
||||
$('.dashboard-activity-poster-info-bar', this).slideUp('fast');
|
||||
});
|
||||
|
||||
// Tooltips
|
||||
$('.dashboard-activity-metadata-platform').each(function() {
|
||||
$(this).tooltip();
|
||||
// Show/Hide activity info
|
||||
$('.btn-activity-info').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
$($(this).attr('data-target')).toggle();
|
||||
});
|
||||
</script>
|
||||
% else:
|
||||
@@ -223,4 +272,4 @@ DOCUMENTATION :: END
|
||||
<div class="text-muted">There was an error communicating with your Plex Server. Please check your <a
|
||||
href="settings">settings</a>.
|
||||
</div><br>
|
||||
% endif
|
||||
% endif
|
||||
|
@@ -58,6 +58,12 @@ DOCUMENTATION :: END
|
||||
</label>
|
||||
<p class="help-block">Uncheck this if you do not want this keep any history on this user's activity.</p>
|
||||
</div>
|
||||
% if data['user_id']:
|
||||
<div class="form-group">
|
||||
<button class="btn btn-danger" id="delete-all-history">Purge</button>
|
||||
<p class="help-block">DANGER ZONE! Click the purge button to remove all history logged for this user. This is permanent!</p>
|
||||
</div>
|
||||
% endif
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
@@ -68,6 +74,24 @@ DOCUMENTATION :: END
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="confirm-modal" class="modal fade" 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 Purge</h4>
|
||||
</div>
|
||||
<div class="modal-body" style="text-align: center;">
|
||||
<p>Are you REALLY sure you want to purge all history for this user?</p>
|
||||
<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-purge">Purge</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
// Set new friendly name
|
||||
$("#save_user_name").click(function() {
|
||||
@@ -112,6 +136,48 @@ DOCUMENTATION :: END
|
||||
});
|
||||
% endif
|
||||
});
|
||||
|
||||
$("#delete-all-history").on('click', function() {
|
||||
$('#confirm-modal').modal();
|
||||
$('#confirm-modal').one('click', '#confirm-purge', function () {
|
||||
$.ajax({
|
||||
url: 'delete_all_user_history',
|
||||
data: {user_id: '${data['user_id']}'},
|
||||
cache: false,
|
||||
async: true,
|
||||
success: function(data) {
|
||||
location.reload();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
$(document).ready(function() {
|
||||
// Move #confirm-modal to parent container
|
||||
if(!($('#edit-user-modal').next().is('#confirm-modal'))) {
|
||||
$('#confirm-modal').appendTo($('#edit-user-modal').parent()); }
|
||||
$('#edit-user-modal > #confirm-modal').remove();
|
||||
|
||||
$('#edit-user-modal').css('z-index', '1050');
|
||||
$('.modal-backdrop').not('.modal-backdrop-stack').css('z-index', '1049');
|
||||
$('.modal-backdrop').not('.modal-backdrop-stack').addClass('modal-backdrop-stack');
|
||||
|
||||
$('#confirm-modal').on('show.bs.modal', function () {
|
||||
// Fix position to match parent modal
|
||||
var currentPadding = parseInt($('body').css('padding-right'));
|
||||
$(this).children('.modal-dialog').css('left', -currentPadding/2);
|
||||
$('#edit-user-modal').css('overflow-y', 'hidden');
|
||||
});
|
||||
$('#confirm-modal').on('shown.bs.modal', function () {
|
||||
$(this).css('z-index', '1060');
|
||||
$('.modal-backdrop').not('.modal-backdrop-stack').css('z-index', '1059');
|
||||
$('.modal-backdrop').not('.modal-backdrop-stack').addClass('modal-backdrop-stack');
|
||||
});
|
||||
$('#confirm-modal').on('hidden.bs.modal', function() {
|
||||
$('body').addClass('modal-open');
|
||||
$('#edit-user-modal').css('overflow-y', 'auto');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
% endif
|
@@ -48,7 +48,7 @@
|
||||
<div class="col-md-12">
|
||||
<h4><i class="fa fa-history"></i> Daily <span class="yaxis-text">Play count</span> <small>Last <span class="days">30</span> days</small></h4>
|
||||
<p class="help-block">
|
||||
The total play count or duration of movies and tv played per day. Click a graph point to open up a list of items played for that specific date.
|
||||
The total play count or duration of tv, movies, and music played per day. Click a graph point to open up a list of items played for that specific date.
|
||||
</p>
|
||||
<div class="graphs-instance">
|
||||
<div class="watch-chart" id="chart_div_plays_by_day">
|
||||
@@ -62,7 +62,7 @@
|
||||
<div class="col-md-6">
|
||||
<h4><i class="fa fa-calendar"></i> <span class="yaxis-text">Play count</span> by day of week <small>Last <span class="days">30</span> days</small></h4>
|
||||
<p class="help-block">
|
||||
The combined total of movies and tv played per day of the week.
|
||||
The combined total of tv, movies, and music played per day of the week.
|
||||
</p>
|
||||
<div class="graphs-instance">
|
||||
<div class="watch-chart" id="chart_div_plays_by_dayofweek" style="float: left;">
|
||||
@@ -75,7 +75,7 @@
|
||||
<div class="col-md-6">
|
||||
<h4><i class="fa fa-clock-o"></i> <span class="yaxis-text">Play count</span> by hour of day <small>Last <span class="days">30</span> days</small></h4>
|
||||
<p class="help-block">
|
||||
The combined total of movies and tv played per hour of the day.
|
||||
The combined total of tv, movies, and music played per hour of the day.
|
||||
</p>
|
||||
<div class="graphs-instance">
|
||||
<div class="watch-chart" id="chart_div_plays_by_hourofday">
|
||||
@@ -90,7 +90,7 @@
|
||||
<div class="col-md-6">
|
||||
<h4><i class="fa fa-television"></i> <span class="yaxis-text">Play count</span> by top 10 platforms <small>Last <span class="days">30</span> days</small></h4>
|
||||
<p class="help-block">
|
||||
The combined total of movies and tv played by top 10 most active platforms.
|
||||
The combined total of tv, movies, and music played by top 10 most active platforms.
|
||||
</p>
|
||||
<div class="graphs-instance">
|
||||
<div class="watch-chart" id="chart_div_plays_by_platform" style="float: left;">
|
||||
@@ -103,7 +103,7 @@
|
||||
<div class="col-md-6">
|
||||
<h4><i class="fa fa-user"></i> <span class="yaxis-text">Play count</span> by top 10 users <small>Last <span class="days">30</span> days</small></h4>
|
||||
<p class="help-block">
|
||||
The combined total of movies and tv played by top 10 most active users.
|
||||
The combined total of tv, movies, and music played by top 10 most active users.
|
||||
</p>
|
||||
<div class="graphs-instance">
|
||||
<div class="watch-chart" id="chart_div_plays_by_user">
|
||||
@@ -121,7 +121,7 @@
|
||||
<div class="col-md-12">
|
||||
<h4><i class="fa fa-video-camera"></i> Daily Stream type breakdown <small>Last <span class="days">30</span> days</small></h4>
|
||||
<p class="help-block">
|
||||
The total play count or duration of movies and tv by the transcode decision. Click a graph point to open up a list of items played for that specific date.
|
||||
The total play count or duration of tv, movies, and music by the transcode decision. Click a graph point to open up a list of items played for that specific date.
|
||||
</p>
|
||||
<div class="graphs-instance">
|
||||
<div class="watch-chart" id="chart_div_plays_by_stream_type">
|
||||
@@ -135,7 +135,7 @@
|
||||
<div class="col-md-6">
|
||||
<h4><i class="fa fa-expand"></i> <span class="yaxis-text">Play count</span> by source resolution <small>Last <span class="days">30</span> days</small></h4>
|
||||
<p class="help-block">
|
||||
The combined total of movies and tv by their original resolution (pre-transcoding).
|
||||
The combined total of tv and movies by their original resolution (pre-transcoding).
|
||||
</p>
|
||||
<div class="graphs-instance">
|
||||
<div class="watch-chart" id="chart_div_plays_by_source_resolution" style="float: left;">
|
||||
@@ -148,7 +148,7 @@
|
||||
<div class="col-md-6">
|
||||
<h4><i class="fa fa-expand"></i> <span class="yaxis-text">Play count</span> by stream resolution <small>Last <span class="days">30</span> days</small></h4>
|
||||
<p class="help-block">
|
||||
The combined total of movies and tv by their streamed resolution (post-transcoding).
|
||||
The combined total of tv and movies by their streamed resolution (post-transcoding).
|
||||
</p>
|
||||
<div class="graphs-instance">
|
||||
<div class="watch-chart" id="chart_div_plays_by_stream_resolution">
|
||||
@@ -163,7 +163,7 @@
|
||||
<div class="col-md-6">
|
||||
<h4><i class="fa fa-television"></i> <span class="yaxis-text">Play count</span> by platform and stream type <small>Last <span class="days">30</span> days</small></h4>
|
||||
<p class="help-block">
|
||||
The combined total of movies and tv by platform and stream type.
|
||||
The combined total of tv, movies, and music by platform and stream type.
|
||||
</p>
|
||||
<div class="graphs-instance">
|
||||
<div class="watch-chart" id="chart_div_plays_by_platform_by_stream_type" style="float: left;">
|
||||
@@ -176,7 +176,7 @@
|
||||
<div class="col-md-6">
|
||||
<h4><i class="fa fa-user"></i> <span class="yaxis-text">Play count</span> by user and stream type <small>Last <span class="days">30</span> days</small></h4>
|
||||
<p class="help-block">
|
||||
The combined total of movies and tv by user and stream type.
|
||||
The combined total of tv, movies, and music by user and stream type.
|
||||
</p>
|
||||
<div class="graphs-instance">
|
||||
<div class="watch-chart" id="chart_div_plays_by_user_by_stream_type" style="float: left;">
|
||||
@@ -194,7 +194,7 @@
|
||||
<div class="col-md-12">
|
||||
<h4><i class="fa fa-calendar"></i> Plays by Month <small>Last 12 months</small></h4>
|
||||
<p class="help-block">
|
||||
The combined total of movies and tv by month.
|
||||
The combined total of tv, movies, and music by month.
|
||||
</p>
|
||||
<div class="graphs-instance">
|
||||
<div class="watch-chart" id="chart_div_plays_by_month">
|
||||
@@ -263,9 +263,36 @@
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
|
||||
var current_range = 30;
|
||||
// Save graph state to cookies
|
||||
$('input[name=yaxis-options]').change(function() {
|
||||
setCookie('graphType', $(this).val(), 365, '/');
|
||||
});
|
||||
$('input[name=date-options]').change(function() {
|
||||
setCookie('graphDate', $(this).val(), 365, '/');
|
||||
});
|
||||
$('a[data-toggle=tab]').click(function() {
|
||||
setCookie('graphTab', $(this).attr('href'), 365, '/');
|
||||
});
|
||||
|
||||
// Initial values for graph if no saved state
|
||||
var yaxis = 'plays';
|
||||
var current_range = 30;
|
||||
var current_tab = '#tabs-1';
|
||||
|
||||
// Read saved graph state from cookies and set initial values
|
||||
if(getCookie('graphType')) {
|
||||
var yaxis = getCookie('graphType');
|
||||
$('input[name=yaxis-options][value=' + yaxis + ']').prop('checked', true).trigger('click');
|
||||
}
|
||||
if(getCookie('graphDate')) {
|
||||
var current_range = getCookie('graphDate');
|
||||
$('input[name=date-options][value=' + current_range + ']').prop('checked', true).trigger('click');
|
||||
$('.days').html(current_range);
|
||||
}
|
||||
if(getCookie('graphTab')) {
|
||||
var current_tab = getCookie('graphTab');
|
||||
$('a[data-toggle=tab][href=' + current_tab + ']').trigger('click');
|
||||
}
|
||||
|
||||
function loadGraphsTab1(time_range, yaxis) {
|
||||
setGraphFormat(yaxis);
|
||||
@@ -432,12 +459,8 @@
|
||||
data: { y_axis: yaxis },
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
var dateArray = [];
|
||||
for (var i = 0; i < data.categories.length; i++) {
|
||||
dateArray.push(moment(data.categories[i], 'YYYY-MM').format('MMM YYYY'));
|
||||
}
|
||||
hc_plays_by_month_options.yAxis.min = 0;
|
||||
hc_plays_by_month_options.xAxis.categories = dateArray;
|
||||
hc_plays_by_month_options.xAxis.categories = data.categories;
|
||||
hc_plays_by_month_options.series = data.series;
|
||||
var hc_plays_by_month = new Highcharts.Chart(hc_plays_by_month_options);
|
||||
}
|
||||
|
@@ -13,23 +13,29 @@
|
||||
<div class="header-bar">
|
||||
<span><i class="fa fa-history"></i> History</span>
|
||||
</div>
|
||||
<div class="colvis-button-bar hidden-xs">
|
||||
<div class="button-bar">
|
||||
<div class="colvis-button-bar hidden-xs"></div>
|
||||
<button class="btn btn-danger btn-edit" data-toggle="button" aria-pressed="false" autocomplete="off" id="row-edit-mode">
|
||||
<i class="fa fa-trash-o"></i> Delete mode
|
||||
</button>
|
||||
<div class="alert alert-danger alert-edit" role="alert" id="row-edit-mode-alert"><i class="fa fa-exclamation-triangle"></i> Select rows to delete. Data is deleted upon exiting delete mode.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='table-card-back'>
|
||||
<table class="display" id="history_table" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th align='left' id="delete_row">Delete</th>
|
||||
<th align='left' id="time">Time</th>
|
||||
<th align='left' id="friendly_name">User</th>
|
||||
<th align='left' id="platform">Platform</th>
|
||||
<th align='left' id="ip_address">IP Address</th>
|
||||
<th align='left' id="platform">Platform</th>
|
||||
<th align='left' id="title">Title</th>
|
||||
<th align='left' id="started">Started</th>
|
||||
<th align='left' id="paused_counter">Paused</th>
|
||||
<th align='left' id="stopped">Stopped</th>
|
||||
<th align='left' id="duration">Duration</th>
|
||||
<th align='left' id="percent_complete">Watched</th>
|
||||
<th align='left' id="percent_complete"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -39,6 +45,24 @@
|
||||
</div>
|
||||
<div class="modal fade" id="ip-info-modal" tabindex="-1" role="dialog" aria-labelledby="ip-info-modal">
|
||||
</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 Delete</h4>
|
||||
</div>
|
||||
<div class="modal-body" style="text-align: center;">
|
||||
<p>Are you REALLY sure you want to delete <strong><span id="deleteCount"></span></strong> history item(s)?</p>
|
||||
<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-delete">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</%def>
|
||||
@@ -60,9 +84,47 @@
|
||||
}
|
||||
}
|
||||
history_table = $('#history_table').DataTable(history_table_options);
|
||||
var colvis = new $.fn.dataTable.ColVis(history_table, { buttonText: 'Select columns', buttonClass: 'btn btn-dark' });
|
||||
var colvis = new $.fn.dataTable.ColVis(history_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark', exclude: [0, 10] });
|
||||
$(colvis.button()).appendTo('div.colvis-button-bar');
|
||||
});
|
||||
|
||||
clearSearchButton('history_table', history_table);
|
||||
|
||||
$('#row-edit-mode').on('click', function() {
|
||||
$('#row-edit-mode-alert').fadeIn(200);
|
||||
|
||||
if ($(this).hasClass('active')) {
|
||||
if (history_to_delete.length > 0) {
|
||||
$('#deleteCount').text(history_to_delete.length);
|
||||
$('#confirm-modal').modal();
|
||||
$('#confirm-modal').one('click', '#confirm-delete', function () {
|
||||
for (var i = 0; i < history_to_delete.length; i++) {
|
||||
$.ajax({
|
||||
url: 'delete_history_rows',
|
||||
data: { row_id: history_to_delete[i] },
|
||||
async: true,
|
||||
success: function (data) {
|
||||
var msg = "History deleted";
|
||||
showMsg(msg, false, true, 2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
history_table.draw();
|
||||
});
|
||||
}
|
||||
|
||||
$('.delete-control').each(function () {
|
||||
$(this).addClass('hidden');
|
||||
$('#row-edit-mode-alert').fadeOut(200);
|
||||
});
|
||||
|
||||
} else {
|
||||
history_to_delete = [];
|
||||
$('.delete-control').each(function() {
|
||||
$(this).find('button.btn-danger').toggleClass('btn-warning').toggleClass('btn-danger');
|
||||
$(this).removeClass('hidden');
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</%def>
|
||||
|
@@ -27,7 +27,8 @@
|
||||
<div class="modal-footer"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="info-modal" tabindex="-1" role="dialog" aria-labelledby="info-modal">
|
||||
</div>
|
||||
<script src="interfaces/default/js/tables/history_table_modal.js"></script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
@@ -43,6 +44,34 @@
|
||||
}
|
||||
|
||||
history_table = $('#history_table').DataTable(history_table_modal_options);
|
||||
|
||||
clearSearchButton('history_table', history_table);
|
||||
|
||||
// Move #info-modal to parent container
|
||||
if (!($('#history-modal').next().is('#info-modal'))) {
|
||||
$('#info-modal').appendTo($('#history-modal').parent());
|
||||
}
|
||||
$('#history-modal > #info-modal').remove();
|
||||
|
||||
$('#history-modal').css('z-index', '1050');
|
||||
$('.modal-backdrop').not('.modal-backdrop-stack').css('z-index', '1049');
|
||||
$('.modal-backdrop').not('.modal-backdrop-stack').addClass('modal-backdrop-stack');
|
||||
|
||||
$('#info-modal').on('show.bs.modal', function () {
|
||||
// Fix position to match parent modal
|
||||
var currentPadding = parseInt($('body').css('padding-right'));
|
||||
$(this).children('.modal-dialog').css('left', -currentPadding / 2);
|
||||
$('#history-modal').css('overflow-y', 'hidden');
|
||||
setTimeout(function () {
|
||||
$('#info-modal').css('z-index', '1060');
|
||||
$('.modal-backdrop').not('.modal-backdrop-stack').css('z-index', '1059');
|
||||
$('.modal-backdrop').not('.modal-backdrop-stack').addClass('modal-backdrop-stack');
|
||||
}, 0);
|
||||
});
|
||||
$('#info-modal').on('hidden.bs.modal', function () {
|
||||
$('body').addClass('modal-open');
|
||||
$('#history-modal').css('overflow-y', 'auto');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
% else:
|
||||
|
@@ -9,173 +9,643 @@ Variable names: data [array]
|
||||
|
||||
data[array_index] :: Usable parameters
|
||||
|
||||
data['stat_id'] Returns the name of the stat. Either 'top_tv', 'popular_tv', 'top_user' or 'top_platform'
|
||||
data['stat_id'] Returns the name of the stat. Either 'top_tv', 'top_movies', 'popular_tv', 'popular_movies', 'top_user' or 'top_platform'
|
||||
data['stat_type'] Returns the type of the stat. Either 'total_plays' or 'total_duration'
|
||||
data['rows'] Returns an array containing stat data
|
||||
|
||||
data[array_index]['rows'] :: Usable parameters
|
||||
|
||||
row_id Return the db row id for a metadata item if one exists
|
||||
|
||||
== Only if 'stat_id' is 'top_tv' or 'popular_tv' or 'top_movies' or 'popular_movies' or 'last_watched' ==
|
||||
thumb Return the thumb for the media item.
|
||||
|
||||
== Only if 'stat_id' is 'top_tv' or 'popular_tv' ==
|
||||
grandparent_thumb Returns location of the item's thumbnail. Use with pms_image_proxy.
|
||||
rating_key Returns the unique identifier for the media item.
|
||||
title Returns the title for the associated stat.
|
||||
|
||||
== Only if 'stat_id' is 'top_tv' or 'top_user' or 'top_platform' ==
|
||||
== Only if 'stat_id' is 'top_tv' or 'top_movies' or 'top_user' or 'top_platform' ==
|
||||
total_plays Returns the count for the associated stat.
|
||||
total_duration Returns the total duration for the associated stat.
|
||||
|
||||
== Only of 'stat_id' is 'popular_tv' ==
|
||||
== Only of 'stat_id' is 'popular_tv' or 'popular_movies' ==
|
||||
users_watched Returns the count for the associated stat.
|
||||
|
||||
== Only if 'stat_id' is 'top_user' ==
|
||||
thumb Returns url of the user's gravatar. Returns '' if none exists.
|
||||
== Only if 'stat_id' is 'top_user' or 'last_watched' ==
|
||||
user_thumb Returns url of the user's gravatar. Returns '' if none exists.
|
||||
user Returns the username for the associated stat.
|
||||
user_id Returns the user id for the associated stat.
|
||||
friendly_name Returns the friendly name of the user for the associated stat.
|
||||
|
||||
== Only if 'stat_id' is 'top_platform' ==
|
||||
== Only if 'stat_id' is 'top_platform' or 'last_watched' ==
|
||||
platform_type Returns the platform name for the associated stat.
|
||||
|
||||
== Only if 'stat_id' is 'last_watched' ==
|
||||
last_watch Returns the time the media item was last watched.
|
||||
|
||||
DOCUMENTATION :: END
|
||||
</%doc>
|
||||
|
||||
<%!
|
||||
from plexpy import helpers
|
||||
|
||||
# Human readable duration
|
||||
def hd(seconds):
|
||||
minutes = helpers.cast_to_float(seconds) / 60
|
||||
if minutes > 60:
|
||||
hours = int(minutes / 60)
|
||||
minutes = int(minutes % 60)
|
||||
if minutes > 0:
|
||||
return "<h3>" + str(hours) + "</h3><p>hrs</p><h3>" + str(minutes) + "</h3><p>mins</p>"
|
||||
else:
|
||||
return "<h3>" + str(hours) + "</h3><p>hrs</p>"
|
||||
else:
|
||||
return "<h3>" + str(int(minutes)) + "</h3><p>mins</p>"
|
||||
%>
|
||||
|
||||
% if data:
|
||||
% if data[0]['rows'] or data[2]['rows']:
|
||||
% if data[0]['rows'] or data[1]['rows'] or data[2]['rows'] or data[3]['rows'] or data[4]['rows'] or data[5]['rows']:
|
||||
<ul class="list-unstyled">
|
||||
% for a in data:
|
||||
% if a['stat_id'] == 'top_tv' and a['rows']:
|
||||
% for top_stat in data:
|
||||
% if top_stat['stat_id'] == 'top_tv' and top_stat['rows']:
|
||||
<div class="home-platforms-instance">
|
||||
<li>
|
||||
<span>
|
||||
<a href="info?item_id=${a['rows'][0]['rating_key']}">
|
||||
% if a['rows'][0]['grandparent_thumb']:
|
||||
<img class="home-platforms-instance-poster"
|
||||
src="pms_image_proxy?img=${a['rows'][0]['grandparent_thumb']}&width=162&height=240&fallback=poster">
|
||||
<div class="home-platforms-instance-info">
|
||||
<div class="home-platforms-instance-name">
|
||||
<h4>Most Watched TV</h4>
|
||||
</div>
|
||||
<div class="home-platforms-instance-playcount">
|
||||
<h4>
|
||||
<a href="info?item_id=${top_stat['rows'][0]['rating_key']}">
|
||||
${top_stat['rows'][0]['title']}
|
||||
</a>
|
||||
</h4>
|
||||
% if top_stat['stat_type'] == 'total_plays':
|
||||
<h3>${top_stat['rows'][0]['total_plays']}</h3>
|
||||
<p> plays</p>
|
||||
% else:
|
||||
<img class="home-platforms-instance-poster" src="interfaces/default/images/poster.png">
|
||||
${top_stat['rows'][0]['total_duration'] | hd}
|
||||
% endif
|
||||
</a>
|
||||
</span>
|
||||
<div class="home-platforms-instance-name">
|
||||
<h4>Most Watched TV</h4>
|
||||
<h5><a href="info?item_id=${a['rows'][0]['rating_key']}">
|
||||
${a['rows'][0]['title']}
|
||||
</a></h5>
|
||||
</div>
|
||||
</div>
|
||||
<div class="user-platforms-instance-playcount">
|
||||
<h3>${a['rows'][0]['total_plays']}</h3>
|
||||
<p> plays</p>
|
||||
</div>
|
||||
</li>
|
||||
</div>
|
||||
% elif a['stat_id'] == 'popular_tv' and a['rows']:
|
||||
<div class="home-platforms-instance">
|
||||
<li>
|
||||
<span>
|
||||
<a href="info?item_id=${a['rows'][0]['rating_key']}">
|
||||
% if a['rows'][0]['grandparent_thumb'] != '':
|
||||
<img class="home-platforms-instance-poster"
|
||||
src="pms_image_proxy?img=${a['rows'][0]['grandparent_thumb']}&width=162&height=240&fallback=poster">
|
||||
% else:
|
||||
<img class="home-platforms-instance-poster" src="interfaces/default/images/poster.png">
|
||||
% endif
|
||||
</a>
|
||||
</span>
|
||||
<div class="home-platforms-instance-name">
|
||||
<h4>Most Popular TV</h4>
|
||||
<h5><a href="info?item_id=${a['rows'][0]['rating_key']}">
|
||||
${a['rows'][0]['title']}
|
||||
</a></h5>
|
||||
</div>
|
||||
<div class="user-platforms-instance-playcount">
|
||||
<h3>${a['rows'][0]['users_watched']}</h3>
|
||||
<p> users</p>
|
||||
</div>
|
||||
</li>
|
||||
</div>
|
||||
% elif a['stat_id'] == 'top_movies' and a['rows']:
|
||||
<div class="home-platforms-instance">
|
||||
<li>
|
||||
<span>
|
||||
<a href="info?item_id=${a['rows'][0]['rating_key']}">
|
||||
% if a['rows'][0]['thumb']:
|
||||
<img class="home-platforms-instance-poster"
|
||||
src="pms_image_proxy?img=${a['rows'][0]['thumb']}&width=162&height=240&fallback=poster">
|
||||
% else:
|
||||
<img class="home-platforms-instance-poster" src="interfaces/default/images/poster.png">
|
||||
% endif
|
||||
</a>
|
||||
</span>
|
||||
<div class="home-platforms-instance-name">
|
||||
<h4>Most Watched Movie</h4>
|
||||
<h5><a href="info?item_id=${a['rows'][0]['rating_key']}">
|
||||
${a['rows'][0]['title']}
|
||||
</a></h5>
|
||||
</div>
|
||||
<div class="user-platforms-instance-playcount">
|
||||
<h3>${a['rows'][0]['total_plays']}</h3>
|
||||
<p> plays</p>
|
||||
</div>
|
||||
</li>
|
||||
</div>
|
||||
% elif a['stat_id'] == 'top_users' and a['rows']:
|
||||
<div class="home-platforms-instance">
|
||||
<li>
|
||||
<span>
|
||||
% if a['rows'][0]['user_id']:
|
||||
<a href="user?user_id=${a['rows'][0]['user_id']}">
|
||||
% else:
|
||||
<a href="user?user=${a['rows'][0]['user']}">
|
||||
<a href="info?item_id=${top_stat['rows'][0]['rating_key']}">
|
||||
% if top_stat['rows'][0]['grandparent_thumb']:
|
||||
<div class="home-platforms-instance-poster">
|
||||
<div class="home-platforms-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][0]['grandparent_thumb']}&width=300&height=450&fallback=poster);"></div>
|
||||
</div>
|
||||
% else:
|
||||
<div class="home-platforms-instance-poster">
|
||||
<div class="home-platforms-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
||||
</div>
|
||||
% endif
|
||||
</a>
|
||||
%if len(top_stat['rows']) > 1:
|
||||
<div class="home-platforms-instance-list-chevron"><i class="fa fa-chevron-down"></i></div>
|
||||
<ul class="list-unstyled">
|
||||
<div class="slider">
|
||||
<div class="home-platforms-instance-list">
|
||||
% for row in top_stat['rows']:
|
||||
%if loop.index > 0:
|
||||
<li>
|
||||
<div class="home-platforms-instance-list-info">
|
||||
<div class="home-platforms-instance-list-name">
|
||||
<h5>
|
||||
<a href="info?item_id=${top_stat['rows'][loop.index]['rating_key']}">
|
||||
${top_stat['rows'][loop.index]['title']}
|
||||
</a>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="home-platforms-instance-list-playcount">
|
||||
% if top_stat['stat_type'] == 'total_plays':
|
||||
<h3>${top_stat['rows'][loop.index]['total_plays']}</h3>
|
||||
<p> plays</p>
|
||||
% else:
|
||||
${top_stat['rows'][loop.index]['total_duration'] | hd}
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
<a href="info?item_id=${top_stat['rows'][loop.index]['rating_key']}">
|
||||
% if top_stat['rows'][loop.index]['grandparent_thumb']:
|
||||
<div class="home-platforms-instance-list-poster">
|
||||
<div class="home-platforms-list-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][loop.index]['grandparent_thumb']}&width=300&height=450&fallback=poster);"></div>
|
||||
</div>
|
||||
% else:
|
||||
<div class="home-platforms-instance-poster2">
|
||||
<div class="home-platforms-list-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
||||
</div>
|
||||
% endif
|
||||
</a>
|
||||
<div class="home-platforms-instance-list-number">
|
||||
<h4>${loop.index + 1}</h4>
|
||||
</div>
|
||||
</li>
|
||||
% endif
|
||||
% if a['rows'][0]['thumb'] != '':
|
||||
<img class="home-platforms-instance-oval" src="${a['rows'][0]['thumb']}"
|
||||
class="poster-face">
|
||||
% endfor
|
||||
</div>
|
||||
</div>
|
||||
</ul>
|
||||
% endif
|
||||
</li>
|
||||
</div>
|
||||
% elif top_stat['stat_id'] == 'popular_tv' and top_stat['rows']:
|
||||
<div class="home-platforms-instance">
|
||||
<li>
|
||||
<div class="home-platforms-instance-info">
|
||||
<div class="home-platforms-instance-name">
|
||||
<h4>Most Popular TV</h4>
|
||||
</div>
|
||||
<div class="home-platforms-instance-playcount">
|
||||
<h4>
|
||||
<a href="info?item_id=${top_stat['rows'][0]['rating_key']}">
|
||||
${top_stat['rows'][0]['title']}
|
||||
</a>
|
||||
</h4>
|
||||
<h3>${top_stat['rows'][0]['users_watched']}</h3>
|
||||
<p> users</p>
|
||||
</div>
|
||||
</div>
|
||||
<a href="info?item_id=${top_stat['rows'][0]['rating_key']}">
|
||||
% if top_stat['rows'][0]['grandparent_thumb'] != '':
|
||||
<div class="home-platforms-instance-poster">
|
||||
<div class="home-platforms-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][0]['grandparent_thumb']}&width=300&height=450&fallback=poster);"></div>
|
||||
</div>
|
||||
% else:
|
||||
<div class="home-platforms-instance-poster">
|
||||
<div class="home-platforms-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
||||
</div>
|
||||
% endif
|
||||
</a>
|
||||
%if len(top_stat['rows']) > 1:
|
||||
<div class="home-platforms-instance-list-chevron"><i class="fa fa-chevron-down"></i></div>
|
||||
<ul class="list-unstyled">
|
||||
<div class="slider">
|
||||
<div class="home-platforms-instance-list">
|
||||
% for row in top_stat['rows']:
|
||||
%if loop.index > 0:
|
||||
<li>
|
||||
<div class="home-platforms-instance-list-info">
|
||||
<div class="home-platforms-instance-list-name">
|
||||
<h5>
|
||||
<a href="info?item_id=${top_stat['rows'][loop.index]['rating_key']}">
|
||||
${top_stat['rows'][loop.index]['title']}
|
||||
</a>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="home-platforms-instance-list-playcount">
|
||||
<h3>${top_stat['rows'][loop.index]['users_watched']}</h3>
|
||||
<p> users</p>
|
||||
</div>
|
||||
</div>
|
||||
<a href="info?item_id=${top_stat['rows'][loop.index]['rating_key']}">
|
||||
% if top_stat['rows'][loop.index]['grandparent_thumb']:
|
||||
<div class="home-platforms-instance-list-poster">
|
||||
<div class="home-platforms-list-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][loop.index]['grandparent_thumb']}&width=300&height=450&fallback=poster);"></div>
|
||||
</div>
|
||||
% else:
|
||||
<div class="home-platforms-instance-poster2">
|
||||
<div class="home-platforms-list-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
||||
</div>
|
||||
% endif
|
||||
</a>
|
||||
<div class="home-platforms-instance-list-number">
|
||||
<h4>${loop.index + 1}</h4>
|
||||
</div>
|
||||
</li>
|
||||
% endif
|
||||
% endfor
|
||||
</div>
|
||||
</div>
|
||||
</ul>
|
||||
% endif
|
||||
</li>
|
||||
</div>
|
||||
% elif top_stat['stat_id'] == 'top_movies' and top_stat['rows']:
|
||||
<div class="home-platforms-instance">
|
||||
<li>
|
||||
<div class="home-platforms-instance-info">
|
||||
<div class="home-platforms-instance-name">
|
||||
<h4>Most Watched Movie</h4>
|
||||
</div>
|
||||
<div class="home-platforms-instance-playcount">
|
||||
<h4>
|
||||
<a href="info?item_id=${top_stat['rows'][0]['rating_key']}">
|
||||
${top_stat['rows'][0]['title']}
|
||||
</a>
|
||||
</h4>
|
||||
% if top_stat['stat_type'] == 'total_plays':
|
||||
<h3>${top_stat['rows'][0]['total_plays']}</h3>
|
||||
<p> plays</p>
|
||||
% else:
|
||||
${top_stat['rows'][0]['total_duration'] | hd}
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
<a href="info?item_id=${top_stat['rows'][0]['rating_key']}">
|
||||
% if top_stat['rows'][0]['thumb']:
|
||||
<div class="home-platforms-instance-poster">
|
||||
<div class="home-platforms-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][0]['thumb']}&width=300&height=450&fallback=poster);"></div>
|
||||
</div>
|
||||
% else:
|
||||
<div class="home-platforms-instance-poster">
|
||||
<div class="home-platforms-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
||||
</div>
|
||||
% endif
|
||||
</a>
|
||||
%if len(top_stat['rows']) > 1:
|
||||
<div class="home-platforms-instance-list-chevron"><i class="fa fa-chevron-down"></i></div>
|
||||
<ul class="list-unstyled">
|
||||
<div class="slider">
|
||||
<div class="home-platforms-instance-list">
|
||||
% for row in top_stat['rows']:
|
||||
%if loop.index > 0:
|
||||
<li>
|
||||
<div class="home-platforms-instance-list-info">
|
||||
<div class="home-platforms-instance-list-name">
|
||||
<h5>
|
||||
<a href="info?item_id=${top_stat['rows'][loop.index]['rating_key']}">
|
||||
${top_stat['rows'][loop.index]['title']}
|
||||
</a>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="home-platforms-instance-list-playcount">
|
||||
% if top_stat['stat_type'] == 'total_plays':
|
||||
<h3>${top_stat['rows'][loop.index]['total_plays']}</h3>
|
||||
<p> plays</p>
|
||||
% else:
|
||||
${top_stat['rows'][loop.index]['total_duration'] | hd}
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
<a href="info?item_id=${top_stat['rows'][loop.index]['rating_key']}">
|
||||
% if top_stat['rows'][loop.index]['thumb']:
|
||||
<div class="home-platforms-instance-list-poster">
|
||||
<div class="home-platforms-list-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][loop.index]['thumb']}&width=300&height=450&fallback=poster);"></div>
|
||||
</div>
|
||||
% else:
|
||||
<div class="home-platforms-instance-poster2">
|
||||
<div class="home-platforms-list-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
||||
</div>
|
||||
% endif
|
||||
</a>
|
||||
<div class="home-platforms-instance-list-number">
|
||||
<h4>${loop.index + 1}</h4>
|
||||
</div>
|
||||
</li>
|
||||
% endif
|
||||
% endfor
|
||||
</div>
|
||||
</div>
|
||||
</ul>
|
||||
% endif
|
||||
</li>
|
||||
</div>
|
||||
% elif top_stat['stat_id'] == 'popular_movies' and top_stat['rows']:
|
||||
<div class="home-platforms-instance">
|
||||
<li>
|
||||
<div class="home-platforms-instance-info">
|
||||
<div class="home-platforms-instance-name">
|
||||
<h4>Most Popular Movie</h4>
|
||||
</div>
|
||||
<div class="home-platforms-instance-playcount">
|
||||
<h4>
|
||||
<a href="info?item_id=${top_stat['rows'][0]['rating_key']}">
|
||||
${top_stat['rows'][0]['title']}
|
||||
</a>
|
||||
</h4>
|
||||
<h3>${top_stat['rows'][0]['users_watched']}</h3>
|
||||
<p> users</p>
|
||||
</div>
|
||||
</div>
|
||||
<a href="info?item_id=${top_stat['rows'][0]['rating_key']}">
|
||||
% if top_stat['rows'][0]['thumb']:
|
||||
<div class="home-platforms-instance-poster">
|
||||
<div class="home-platforms-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][0]['thumb']}&width=300&height=450&fallback=poster);"></div>
|
||||
</div>
|
||||
% else:
|
||||
<div class="home-platforms-instance-poster">
|
||||
<div class="home-platforms-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
||||
</div>
|
||||
% endif
|
||||
</a>
|
||||
%if len(top_stat['rows']) > 1:
|
||||
<div class="home-platforms-instance-list-chevron"><i class="fa fa-chevron-down"></i></div>
|
||||
<ul class="list-unstyled">
|
||||
<div class="slider">
|
||||
<div class="home-platforms-instance-list">
|
||||
% for row in top_stat['rows']:
|
||||
%if loop.index > 0:
|
||||
<li>
|
||||
<div class="home-platforms-instance-list-info">
|
||||
<div class="home-platforms-instance-list-name">
|
||||
<h5>
|
||||
<a href="info?item_id=${top_stat['rows'][loop.index]['rating_key']}">
|
||||
${top_stat['rows'][loop.index]['title']}
|
||||
</a>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="home-platforms-instance-list-playcount">
|
||||
<h3>${top_stat['rows'][loop.index]['users_watched']}</h3>
|
||||
<p> users</p>
|
||||
</div>
|
||||
</div>
|
||||
<a href="info?item_id=${top_stat['rows'][loop.index]['rating_key']}">
|
||||
% if top_stat['rows'][loop.index]['thumb']:
|
||||
<div class="home-platforms-instance-list-poster">
|
||||
<div class="home-platforms-list-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][loop.index]['thumb']}&width=300&height=450&fallback=poster);"></div>
|
||||
</div>
|
||||
% else:
|
||||
<div class="home-platforms-instance-poster2">
|
||||
<div class="home-platforms-list-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
||||
</div>
|
||||
% endif
|
||||
</a>
|
||||
<div class="home-platforms-instance-list-number">
|
||||
<h4>${loop.index + 1}</h4>
|
||||
</div>
|
||||
</li>
|
||||
% endif
|
||||
% endfor
|
||||
</div>
|
||||
</div>
|
||||
</ul>
|
||||
% endif
|
||||
</li>
|
||||
</div>
|
||||
% elif top_stat['stat_id'] == 'top_users' and top_stat['rows']:
|
||||
<div class="home-platforms-instance">
|
||||
<li>
|
||||
<div class="home-platforms-instance-info">
|
||||
<div class="home-platforms-instance-name">
|
||||
<h4>Most Active User</h4>
|
||||
</div>
|
||||
<div class="home-platforms-instance-playcount">
|
||||
<h4>
|
||||
% if top_stat['rows'][0]['user_id']:
|
||||
<a href="user?user_id=${top_stat['rows'][0]['user_id']}">
|
||||
% else:
|
||||
<a href="user?user=${top_stat['rows'][0]['user']}">
|
||||
% endif
|
||||
${top_stat['rows'][0]['friendly_name']}
|
||||
</a>
|
||||
</h4>
|
||||
% if top_stat['stat_type'] == 'total_plays':
|
||||
<h3>${top_stat['rows'][0]['total_plays']}</h3>
|
||||
<p> plays</p>
|
||||
% else:
|
||||
${top_stat['rows'][0]['total_duration'] | hd}
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
% if top_stat['rows'][0]['user_id']:
|
||||
<a href="user?user_id=${top_stat['rows'][0]['user_id']}">
|
||||
% else:
|
||||
<a href="user?user=${top_stat['rows'][0]['user']}">
|
||||
% endif
|
||||
% if top_stat['rows'][0]['user_thumb'] != '':
|
||||
<div class="home-platforms-instance-poster">
|
||||
<div class="home-platforms-instance-oval" style="background-image: url(${top_stat['rows'][0]['user_thumb']});"></div>
|
||||
</div>
|
||||
% else:
|
||||
<div class="home-platforms-instance-poster">
|
||||
<div class="home-platforms-instance-oval" style="background-image: url(interfaces/default/images/gravatar-default.png);"></div>
|
||||
</div>
|
||||
% endif
|
||||
</a>
|
||||
%if len(top_stat['rows']) > 1:
|
||||
<div class="home-platforms-instance-list-chevron"><i class="fa fa-chevron-down"></i></div>
|
||||
<ul class="list-unstyled">
|
||||
<div class="slider">
|
||||
<div class="home-platforms-instance-list">
|
||||
% for row in top_stat['rows']:
|
||||
%if loop.index > 0:
|
||||
<li>
|
||||
<div class="home-platforms-instance-list-info">
|
||||
<div class="home-platforms-instance-list-name">
|
||||
<h5>
|
||||
% if top_stat['rows'][loop.index]['user_id']:
|
||||
<a href="user?user_id=${top_stat['rows'][loop.index]['user_id']}">
|
||||
% else:
|
||||
<a href="user?user=${top_stat['rows'][loop.index]['user']}">
|
||||
% endif
|
||||
${top_stat['rows'][loop.index]['friendly_name']}
|
||||
</a>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="home-platforms-instance-list-playcount">
|
||||
% if top_stat['stat_type'] == 'total_plays':
|
||||
<h3>${top_stat['rows'][loop.index]['total_plays']}</h3>
|
||||
<p> plays</p>
|
||||
% else:
|
||||
${top_stat['rows'][loop.index]['total_duration'] | hd}
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
% if top_stat['rows'][loop.index]['user_id']:
|
||||
<a href="user?user_id=${top_stat['rows'][loop.index]['user_id']}">
|
||||
% else:
|
||||
<a href="user?user=${top_stat['rows'][loop.index]['user']}">
|
||||
% endif
|
||||
% if top_stat['rows'][loop.index]['user_thumb'] != '':
|
||||
<div class="home-platforms-instance-poster">
|
||||
<div class="home-platforms-instance-list-oval" style="background-image: url(${top_stat['rows'][loop.index]['user_thumb']});"></div>
|
||||
</div>
|
||||
% else:
|
||||
<div class="home-platforms-instance-poster">
|
||||
<div class="home-platforms-instance-list-oval" style="background-image: url(interfaces/default/images/gravatar-default.png);"></div>
|
||||
</div>
|
||||
% endif
|
||||
</a>
|
||||
<div class="home-platforms-instance-list-number">
|
||||
<h4>${loop.index + 1}</h4>
|
||||
</div>
|
||||
</li>
|
||||
% endif
|
||||
% endfor
|
||||
</div>
|
||||
</div>
|
||||
</ul>
|
||||
% endif
|
||||
</li>
|
||||
</div>
|
||||
% elif top_stat['stat_id'] == 'top_platforms' and top_stat['rows']:
|
||||
<div class="home-platforms-instance">
|
||||
<li>
|
||||
<div class="home-platforms-instance-info">
|
||||
<div class="home-platforms-instance-name">
|
||||
<h4>Most Active Platform</h4>
|
||||
</div>
|
||||
<div class="home-platforms-instance-playcount">
|
||||
<h4>${top_stat['rows'][0]['platform_type']}</h4>
|
||||
% if top_stat['stat_type'] == 'total_plays':
|
||||
<h3>${top_stat['rows'][0]['total_plays']}</h3>
|
||||
<p> plays</p>
|
||||
% else:
|
||||
${top_stat['rows'][0]['total_duration'] | hd}
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
<div id="platform-stat" class="home-platforms-instance-poster">
|
||||
<script>
|
||||
$("#platform-stat").html("<div class='home-platforms-instance-box' style='background-image: url(" + getPlatformImagePath('${top_stat['rows'][0]['platform_type']}') + ");'>");
|
||||
</script>
|
||||
</div>
|
||||
%if len(top_stat['rows']) > 1:
|
||||
<div class="home-platforms-instance-list-chevron"><i class="fa fa-chevron-down"></i></div>
|
||||
<ul class="list-unstyled">
|
||||
<div class="slider">
|
||||
<div class="home-platforms-instance-list">
|
||||
% for row in top_stat['rows']:
|
||||
%if loop.index > 0:
|
||||
<li>
|
||||
<div class="home-platforms-instance-list-info">
|
||||
<div class="home-platforms-instance-list-name">
|
||||
<h5>
|
||||
${top_stat['rows'][loop.index]['platform_type']}
|
||||
</h5>
|
||||
</div>
|
||||
<div class="home-platforms-instance-list-playcount">
|
||||
% if top_stat['stat_type'] == 'total_plays':
|
||||
<h3>${top_stat['rows'][loop.index]['total_plays']}</h3>
|
||||
<p> plays</p>
|
||||
% else:
|
||||
${top_stat['rows'][loop.index]['total_duration'] | hd}
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="home-platforms-instance-poster" id="home-platforms-instance-poster-${loop.index + 1}">
|
||||
<script>
|
||||
$("#home-platforms-instance-poster-${loop.index + 1}").html("<div class='home-platforms-instance-list-box' style='background-image: url(" + getPlatformImagePath('${top_stat['rows'][loop.index]['platform_type']}') + ");'>");
|
||||
</script>
|
||||
</div>
|
||||
<div class="home-platforms-instance-list-number">
|
||||
<h4>${loop.index + 1}</h4>
|
||||
</div>
|
||||
</li>
|
||||
% endif
|
||||
% endfor
|
||||
</div>
|
||||
</div>
|
||||
</ul>
|
||||
% endif
|
||||
</li>
|
||||
</div>
|
||||
% elif top_stat['stat_id'] == 'last_watched' and top_stat['rows']:
|
||||
<div class="home-platforms-instance">
|
||||
<li>
|
||||
<div class="home-platforms-instance-info">
|
||||
<div class="home-platforms-instance-name">
|
||||
<h4>Last Watched</h4>
|
||||
</div>
|
||||
<div class="home-platforms-instance-last-user">
|
||||
<h4>
|
||||
<a href="info?source=history&item_id=${top_stat['rows'][0]['row_id']}">
|
||||
${top_stat['rows'][0]['title']}
|
||||
</a>
|
||||
</h4>
|
||||
<h5>
|
||||
% if top_stat['rows'][0]['user_id']:
|
||||
<a href="user?user_id=${top_stat['rows'][0]['user_id']}">
|
||||
% else:
|
||||
<img class="home-platforms-instance-oval"
|
||||
src="interfaces/default/images/gravatar-default.png">
|
||||
% endif
|
||||
</a>
|
||||
</span>
|
||||
<div class="home-platforms-instance-name">
|
||||
<h4>Most Active User</h4>
|
||||
<h5>
|
||||
% if a['rows'][0]['user_id']:
|
||||
<a href="user?user_id=${a['rows'][0]['user_id']}">
|
||||
% else:
|
||||
<a href="user?user=${a['rows'][0]['user']}">
|
||||
% endif
|
||||
${a['rows'][0]['friendly_name']}
|
||||
</a>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="user-platforms-instance-playcount">
|
||||
<h3>${a['rows'][0]['total_plays']}</h3>
|
||||
<p> plays</p>
|
||||
<a href="user?user=${top_stat['rows'][0]['user']}">
|
||||
% endif
|
||||
${top_stat['rows'][0]['friendly_name']}
|
||||
</a>
|
||||
</h5>
|
||||
<p>
|
||||
<span id="last-watch-stat">
|
||||
<script>
|
||||
$('#last-watch-stat').text(moment(${top_stat['rows'][0]['last_watch']},"X").format(date_format));
|
||||
</script>
|
||||
</span> - ${top_stat['rows'][0]['platform_type']}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<a href="info?source=history&item_id=${top_stat['rows'][0]['row_id']}">
|
||||
% if top_stat['rows'][0]['thumb']:
|
||||
<div class="home-platforms-instance-poster">
|
||||
<div class="home-platforms-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][0]['thumb']}&width=300&height=450&fallback=poster);"></div>
|
||||
</div>
|
||||
% else:
|
||||
<div class="home-platforms-instance-poster">
|
||||
<div class="home-platforms-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
||||
</div>
|
||||
% endif
|
||||
</a>
|
||||
%if len(top_stat['rows']) > 1:
|
||||
<div class="home-platforms-instance-list-chevron"><i class="fa fa-chevron-down"></i></div>
|
||||
<ul class="list-unstyled">
|
||||
<div class="slider">
|
||||
<div class="home-platforms-instance-list">
|
||||
% for row in top_stat['rows']:
|
||||
%if loop.index > 0:
|
||||
<li>
|
||||
<div class="home-platforms-instance-list-info">
|
||||
<div class="home-platforms-instance-list-name">
|
||||
<h5>
|
||||
<a href="info?source=history&item_id=${top_stat['rows'][loop.index]['row_id']}">
|
||||
${top_stat['rows'][loop.index]['title']}
|
||||
</a>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="home-platforms-instance-list-last-user">
|
||||
<h5>
|
||||
% if top_stat['rows'][loop.index]['user_id']:
|
||||
<a href="user?user_id=${top_stat['rows'][loop.index]['user_id']}">
|
||||
% else:
|
||||
<a href="user?user=${top_stat['rows'][loop.index]['user']}">
|
||||
% endif
|
||||
${top_stat['rows'][loop.index]['friendly_name']}
|
||||
</a>
|
||||
</h5>
|
||||
<p>
|
||||
<span id="home-platforms-instance-list-last-watch-${loop.index + 1}">
|
||||
<script>
|
||||
$('#home-platforms-instance-list-last-watch-${loop.index + 1}').text(moment(${top_stat['rows'][loop.index]['last_watch']},"X").format(date_format));
|
||||
</script>
|
||||
</span> - ${top_stat['rows'][loop.index]['platform_type']}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<a href="info?source=history&item_id=${top_stat['rows'][loop.index]['row_id']}">
|
||||
% if top_stat['rows'][loop.index]['thumb']:
|
||||
<div class="home-platforms-instance-list-poster">
|
||||
<div class="home-platforms-list-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][loop.index]['thumb']}&width=300&height=450&fallback=poster);"></div>
|
||||
</div>
|
||||
% else:
|
||||
<div class="home-platforms-instance-poster2">
|
||||
<div class="home-platforms-list-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
||||
</div>
|
||||
% endif
|
||||
</a>
|
||||
<div class="home-platforms-instance-list-number">
|
||||
<h4>${loop.index + 1}</h4>
|
||||
</div>
|
||||
</li>
|
||||
% endif
|
||||
% endfor
|
||||
</div>
|
||||
</div>
|
||||
</ul>
|
||||
% endif
|
||||
</li>
|
||||
</div>
|
||||
% elif a['stat_id'] == 'top_platforms' and a['rows']:
|
||||
<div class="home-platforms-instance">
|
||||
<li>
|
||||
<div id="platform-stat">
|
||||
<img class="home-platforms-instance-box" src="interfaces/default/images/platforms/default.png">
|
||||
</div>
|
||||
<div class="home-platforms-instance-name">
|
||||
<h4>Most Active Platform</h4>
|
||||
<h5>${a['rows'][0]['platform_type']}</h5>
|
||||
</div>
|
||||
<div class="user-platforms-instance-playcount">
|
||||
<h3>${a['rows'][0]['total_plays']}</h3>
|
||||
<p> plays</p>
|
||||
</div>
|
||||
</li>
|
||||
</div>
|
||||
<script>
|
||||
$("#platform-stat").html("<img class='home-platforms-instance-box' src='" + getPlatformImagePath('${a['rows'][0]['platform_type']}') + "'>");
|
||||
</script>
|
||||
% endif
|
||||
% endfor
|
||||
</ul>
|
||||
<script>
|
||||
var topZIndex = 2;
|
||||
$('.home-platforms-instance-list-chevron').on('click', function() {
|
||||
var instanceBoxChevron = $(this);
|
||||
var instanceBox = $(this).parents('.home-platforms-instance');
|
||||
var instanceBoxSlider = instanceBox.find('.slider');
|
||||
|
||||
topZIndex++;
|
||||
instanceBoxChevron.toggleClass('active');
|
||||
instanceBoxSlider.css('z-index', topZIndex);
|
||||
instanceBoxSlider.stop().slideToggle(500);
|
||||
});
|
||||
</script>
|
||||
% else:
|
||||
<div class="text-muted">No stats for selected period.</div><br>
|
||||
% endif
|
||||
|
26
data/interfaces/default/images/plex-logo-light.svg
Normal file
26
data/interfaces/default/images/plex-logo-light.svg
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="3086px" height="1000px" viewBox="0 0 3086 1000" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
|
||||
<!-- Generator: Sketch 3.2.2 (9983) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>plex-logo-light</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs>
|
||||
<radialGradient cx="89.2670157%" cy="49.76%" fx="89.2670157%" fy="49.76%" r="92.4996161%" id="radialGradient-1">
|
||||
<stop stop-color="#F9BE03" offset="0%"></stop>
|
||||
<stop stop-color="#CC7C19" offset="100%"></stop>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
|
||||
<g id="plex-logo-light" sketch:type="MSArtboardGroup">
|
||||
<g sketch:type="MSLayerGroup">
|
||||
<path d="M3085.99,0 L2795.989,0 L2505.99,500 L2795.989,1000 L3085.737,1000 L2795.989,500.25 L3085.99,0" id="X" fill="#FFFFFF" sketch:type="MSShapeGroup"></path>
|
||||
<path d="M2186,0 L2476.00071,0 L2796,500.25 L2476.00071,1000.5 L2186,1000.5 L2505.99929,500.25 L2186,0" id="chevron" fill="url(#radialGradient-1)" sketch:type="MSShapeGroup"></path>
|
||||
<path d="M2085.947,1000 L1508.874,1000 L1508.874,0 L2085.947,0 L2085.947,173.737 L1721.339,173.737 L1721.339,393.299 L2060.594,393.299 L2060.594,567.03 L1721.339,567.03 L1721.339,824.895 L2085.947,824.895 L2085.947,1000" id="E" fill="#FFFFFF" sketch:type="MSShapeGroup"></path>
|
||||
<path d="M791.276,1000 L791.276,0 L1003.316,0 L1003.316,824.895 L1408.925,824.895 L1408.925,1000 L791.276,1000" id="L" fill="#FFFFFF" sketch:type="MSShapeGroup"></path>
|
||||
<g id="P" fill="#FFFFFF" sketch:type="MSShapeGroup">
|
||||
<path d="M589.947,558.824 C522.679,615.831 427.037,644.325 303.009,644.325 L212.04,644.325 L212.04,1000 L0,1000 L0,643.947829 L0,470.337388 L290,470.697418 C467.563171,468.627777 476.842468,359.878967 476.842468,322.200012 C476.842468,287.221283 476.842468,175.445374 319,173.699997 L0,173.703242 L0,0 L319.424,0 C440.717,0 532.939,26.107 596.101,78.321 C659.253,130.534 690.834,208.392 690.834,311.902 C690.834,419.527 657.202,501.83 589.947,558.824 Z" id="Path"></path>
|
||||
<rect id="Path" x="0" y="110" width="212.2" height="429"></rect>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
@@ -19,9 +19,20 @@
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="padded-header">
|
||||
<h3>Statistics <small>Last ${config['home_stats_length']} days</small></h3>
|
||||
<h3>Watch Statistics <small>Last ${config['home_stats_length']} days</small></h3>
|
||||
</div>
|
||||
<div id="home-stats" class="user-platforms">
|
||||
<div id="home-stats" class="home-platforms">
|
||||
<div class='text-muted'><i class="fa fa-refresh fa-spin"></i> Loading stats...</div>
|
||||
<br>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="padded-header" id="library-statistics-header">
|
||||
<h3>Library Statistics</h3>
|
||||
</div>
|
||||
<div id="library-stats" class="library-platforms">
|
||||
<div class='text-muted'><i class="fa fa-refresh fa-spin"></i> Loading stats...</div>
|
||||
<br>
|
||||
</div>
|
||||
@@ -45,17 +56,18 @@
|
||||
<script src="interfaces/default/js/moment-with-locale.js"></script>
|
||||
<script>
|
||||
|
||||
function getHomeStats(days) {
|
||||
function currentActivityHeader() {
|
||||
$.ajax({
|
||||
url: 'home_stats',
|
||||
url: 'get_current_activity_header',
|
||||
cache: false,
|
||||
async: true,
|
||||
data: {time_range: days},
|
||||
complete: function(xhr, status) {
|
||||
$("#home-stats").html(xhr.responseText);
|
||||
$("#current-activity-header").html(xhr.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
currentActivityHeader();
|
||||
setInterval(currentActivityHeader, 15000);
|
||||
|
||||
function currentActivity() {
|
||||
$.ajax({
|
||||
@@ -70,18 +82,50 @@
|
||||
currentActivity();
|
||||
setInterval(currentActivity, 15000);
|
||||
|
||||
function currentActivityHeader() {
|
||||
function getHomeStats(days, stat_type, stat_count) {
|
||||
$.ajax({
|
||||
url: 'get_current_activity_header',
|
||||
url: 'home_stats',
|
||||
cache: false,
|
||||
async: true,
|
||||
data: {time_range: days, stat_type: stat_type, stat_count: stat_count},
|
||||
complete: function(xhr, status) {
|
||||
$("#current-activity-header").html(xhr.responseText);
|
||||
$("#home-stats").html(xhr.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getLibraryStatsHeader() {
|
||||
$.ajax({
|
||||
"url": "get_servers_info",
|
||||
type: "post",
|
||||
cache: false,
|
||||
async: true,
|
||||
data: { },
|
||||
complete: function (xhr, status) {
|
||||
server_info = $.parseJSON(xhr.responseText);
|
||||
var server_name = 'Server name not found';
|
||||
for (var i in server_info) {
|
||||
if (server_info[i].machine_identifier == '${config['pms_identifier']}') {
|
||||
server_name = server_info[i].name
|
||||
break;
|
||||
}
|
||||
}
|
||||
$('#library-statistics-header h3').append(' <small>' + server_name + '</small>')
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getLibraryStats() {
|
||||
$.ajax({
|
||||
url: 'library_stats',
|
||||
cache: false,
|
||||
async: true,
|
||||
data: { },
|
||||
complete: function(xhr, status) {
|
||||
$("#library-stats").html(xhr.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
currentActivityHeader();
|
||||
setInterval(currentActivityHeader, 15000);
|
||||
|
||||
function recentlyAdded() {
|
||||
var widthVal = $('body').find(".container-fluid").width();
|
||||
@@ -110,7 +154,20 @@
|
||||
});
|
||||
});
|
||||
|
||||
getHomeStats(${config['home_stats_length']});
|
||||
var date_format = 'YYYY-MM-DD';
|
||||
var time_format = 'hh:mm a';
|
||||
$.ajax({
|
||||
url: 'get_date_formats',
|
||||
type: 'GET',
|
||||
success: function(data) {
|
||||
date_format = data.date_format;
|
||||
time_format = data.time_format;
|
||||
}
|
||||
});
|
||||
|
||||
getHomeStats(${config['home_stats_length']}, ${config['home_stats_type']}, ${config['home_stats_count']});
|
||||
getLibraryStatsHeader();
|
||||
getLibraryStats();
|
||||
|
||||
|
||||
</script>
|
||||
|
@@ -30,6 +30,7 @@ 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>
|
||||
@@ -46,111 +47,310 @@ DOCUMENTATION :: END
|
||||
% if data:
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div>
|
||||
<div class="art-face" style="background-image:url(pms_image_proxy?img=${data['art']}&width=1920&height=1080)">
|
||||
<div class="summary-wrapper">
|
||||
<div class="summary-overlay">
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
<div class="summary-content-poster hidden-xs hidden-sm">
|
||||
% if data['type'] == 'episode':
|
||||
<img src="pms_image_proxy?img=${data['parent_thumb']}&width=300&height=450&fallback=poster">
|
||||
% else:
|
||||
<img src="pms_image_proxy?img=${data['thumb']}&width=300&height=450&fallback=poster">
|
||||
% endif
|
||||
</div>
|
||||
<div class="summary-content">
|
||||
<div class="summary-content-title">
|
||||
% if data['type'] == 'movie':
|
||||
<h1>${data['title']} (${data['year']})</h1>
|
||||
% elif data['type'] == 'season':
|
||||
<h1>${data['parent_title']} (${data['title']})</h1>
|
||||
% elif data['type'] == 'episode':
|
||||
<h1>${data['grandparent_title']} (Season ${data['parent_index']}, Episode
|
||||
${data['index']}) "${data['title']}"</h1>
|
||||
% else:
|
||||
<h1>${data['title']}</h1>
|
||||
% endif
|
||||
</div>
|
||||
% if data['type'] == 'movie':
|
||||
<div id="stars" class="rateit hidden-xs hidden-sm" data-rateit-value=""
|
||||
data-rateit-ispreset="true" data-rateit-readonly="true"></div>
|
||||
% endif
|
||||
<div class="summary-content-details-wrapper">
|
||||
<div class="summary-content-director">
|
||||
% if data['type'] == 'episode' or data['type'] == 'movie':
|
||||
% if data['directors']:
|
||||
Directed by <strong> ${data['directors'][0]}</strong>
|
||||
% else:
|
||||
Directed by <strong> unknown</strong>
|
||||
% endif
|
||||
% elif data['type'] == 'show' or data['type'] == 'season':
|
||||
Studio <strong> ${data['studio']}</strong>
|
||||
% endif
|
||||
</div>
|
||||
<div class="summary-content-duration">
|
||||
Runtime <strong> <span id="runtime">${data['duration']}</span> mins</strong>
|
||||
</div>
|
||||
<div class="summary-content-content-rating">
|
||||
Rated <strong> ${data['content_rating']} </strong>
|
||||
</div>
|
||||
</div>
|
||||
<div class="summary-content-summary">
|
||||
<p> ${data['summary']} </p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="art-face" style="background-image:url(pms_image_proxy?img=${data['art']}&width=1920&height=1080)"></div>
|
||||
<div class="summary-container">
|
||||
<div class="summary-navbar">
|
||||
<div class="col-md-12">
|
||||
<div class="summary-navbar-list">
|
||||
% if data['type'] == 'movie':
|
||||
<span>Movies</span>
|
||||
<span><i class="fa fa-chevron-right"></i></span>
|
||||
<span><a href="#">${data['title']}</a></span>
|
||||
% elif data['type'] == 'show':
|
||||
<span>TV Shows</span>
|
||||
<span><i class="fa fa-chevron-right"></i></span>
|
||||
<span><a href="#">${data['title']}</a></span>
|
||||
% elif data['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><a href="info?item_id=${data['parent_rating_key']}">${data['parent_title']}</a></span>
|
||||
<span><i class="fa fa-chevron-right"></i></span>
|
||||
<span><a href="#">Season ${data['index']}</a></span>
|
||||
% elif data['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"><a href="info?item_id=${data['grandparent_rating_key']}">${data['grandparent_title']}</a></span>
|
||||
<span class="hidden-xs hidden-sm"><i class="fa fa-chevron-right"></i></span>
|
||||
<span><a href="info?item_id=${data['parent_rating_key']}">Season ${data['parent_index']}</a></span>
|
||||
<span><i class="fa fa-chevron-right"></i></span>
|
||||
<span><a href="#">Episode ${data['index']} - ${data['title']}</a></span>
|
||||
% elif data['type'] == 'artist':
|
||||
<span>Music</span>
|
||||
<span><i class="fa fa-chevron-right"></i></span>
|
||||
<span><a href="#">${data['title']}</a></span>
|
||||
% elif data['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><a href="info?item_id=${data['parent_rating_key']}">${data['parent_title']}</a></span>
|
||||
<span><i class="fa fa-chevron-right"></i></span>
|
||||
<span><a href="#">${data['title']}</a></span>
|
||||
% elif data['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"><a href="info?item_id=${data['grandparent_rating_key']}">${data['grandparent_title']}</a></span>
|
||||
<span class="hidden-xs hidden-sm"><i class="fa fa-chevron-right"></i></span>
|
||||
<span><a href="info?item_id=${data['parent_rating_key']}">${data['parent_title']}</a></span>
|
||||
<span><i class="fa fa-chevron-right"></i></span>
|
||||
<span><a href="#">Track ${data['index']} - ${data['title']}</a></span>
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="summary-content-title-wrapper">
|
||||
<div class="col-md-9">
|
||||
<div class="summary-content-poster hidden-xs hidden-sm">
|
||||
% if data['type'] == 'track':
|
||||
<a href="http://app.plex.tv/web/app#!/server/${config['pms_identifier']}/details/%2Flibrary%2Fmetadata%2F${data['parent_rating_key']}" target="Plex/Web" title="View in Plex/Web">
|
||||
% else:
|
||||
<a href="http://app.plex.tv/web/app#!/server/${config['pms_identifier']}/details/%2Flibrary%2Fmetadata%2F${data['rating_key']}" target="Plex/Web" title="View in Plex/Web">
|
||||
% endif
|
||||
% if data['type'] == 'episode':
|
||||
<div class="col-md-3">
|
||||
<div class="summary-content-people-wrapper hidden-xs hidden-sm">
|
||||
<div class="summary-content-writers">
|
||||
<strong>Written by</strong>
|
||||
<ul>
|
||||
% for writer in data['writers']:
|
||||
% if loop.index < 5:
|
||||
<li>
|
||||
${writer}
|
||||
</li>
|
||||
% endif
|
||||
% endfor
|
||||
</ul>
|
||||
</div>
|
||||
<div class="summary-poster-face-episode" style="background-image: url(pms_image_proxy?img=${data['thumb']}&width=500&height=280&fallback=poster);">
|
||||
<div class="summary-poster-face-overlay">
|
||||
<span></span>
|
||||
</div>
|
||||
</div>
|
||||
% elif data['type'] == 'movie' or data['type'] == 'show':
|
||||
<div class="col-md-3">
|
||||
<div class="summary-content-people-wrapper hidden-xs hidden-sm">
|
||||
<div class="summary-content-actors">
|
||||
<strong>Genres</strong>
|
||||
<ul>
|
||||
% for genre in data['genres']:
|
||||
% if loop.index < 5:
|
||||
<li>
|
||||
${genre}
|
||||
</li>
|
||||
% endif
|
||||
% endfor
|
||||
</ul>
|
||||
</div>
|
||||
<div class="summary-content-people-wrapper hidden-xs hidden-sm">
|
||||
<div class="summary-content-actors">
|
||||
<strong>Starring</strong>
|
||||
<ul>
|
||||
% for actor in data['actors']:
|
||||
% if loop.index < 5:
|
||||
<li>
|
||||
${actor}
|
||||
</li>
|
||||
% endif
|
||||
% endfor
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
% elif data['type'] == 'artist' or data['type'] == 'album' or data['type'] == 'track':
|
||||
<div class="summary-poster-face-track" style="background-image: url(pms_image_proxy?img=${data['thumb']}&width=500&height=500&fallback=poster);">
|
||||
<div class="summary-poster-face-overlay">
|
||||
<span></span>
|
||||
</div>
|
||||
</div>
|
||||
% else:
|
||||
<div class="summary-poster-face" style="background-image: url(pms_image_proxy?img=${data['thumb']}&width=300&height=450&fallback=poster);">
|
||||
<div class="summary-poster-face-overlay">
|
||||
<span></span>
|
||||
</div>
|
||||
</div>
|
||||
% elif data['type'] == 'season':
|
||||
<div class="col-md-3"></div>
|
||||
% endif
|
||||
</a>
|
||||
</div>
|
||||
<div class="summary-content-title">
|
||||
% if data['type'] == 'movie' or data['type'] == 'show' or data['type'] == 'artist':
|
||||
<h1> </h1><h1>${data['title']}</h1>
|
||||
% elif data['type'] == 'season':
|
||||
<h1> </h1><h1><a href="info?item_id=${data['parent_rating_key']}">${data['parent_title']}</a></h1>
|
||||
<h3 class="hidden-xs">S${data['index']}</h3>
|
||||
% elif data['type'] == 'episode':
|
||||
<h1><a href="info?item_id=${data['grandparent_rating_key']}">${data['grandparent_title']}</a></h1>
|
||||
<h2>${data['title']}</h2>
|
||||
<h3 class="hidden-xs">S${data['parent_index']} · E${data['index']}</h3>
|
||||
% elif data['type'] == 'album':
|
||||
<h1><a href="info?item_id=${data['parent_rating_key']}">${data['parent_title']}</a></h1>
|
||||
<h2>${data['title']}</h2>
|
||||
% elif data['type'] == 'track':
|
||||
<h1><a href="info?item_id=${data['grandparent_rating_key']}">${data['grandparent_title']}</a></h1>
|
||||
<h2><a href="info?item_id=${data['parent_rating_key']}">${data['parent_title']}</a> - ${data['title']}</h2>
|
||||
<h3 class="hidden-xs">T${data['index']}</h3>
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="summary-content-wrapper">
|
||||
<div class="col-md-9">
|
||||
% if data['type'] == 'movie' or data['type'] == 'show' or data['type'] == 'season':
|
||||
<div class="summary-content-padding hidden-xs hidden-sm" style="height: 275px;"></div>
|
||||
% elif data['type'] == 'episode':
|
||||
<div class="summary-content-padding hidden-xs hidden-sm" style="height: 40px;"></div>
|
||||
% elif data['type'] == 'artist' or data['type'] == 'album' or data['type'] == 'track':
|
||||
<div class="summary-content-padding hidden-xs hidden-sm" style="height: 150px;"></div>
|
||||
% else:
|
||||
<div class="summary-content-padding hidden-xs hidden-sm"></div>
|
||||
% endif
|
||||
<div class="summary-content">
|
||||
<div class="summary-content-details-wrapper">
|
||||
% if data['rating']:
|
||||
<div id="stars" class="rateit hidden-xs hidden-sm" data-rateit-value=""
|
||||
data-rateit-ispreset="true" data-rateit-readonly="true"></div>
|
||||
% endif
|
||||
<div class="summary-content-details-tag">
|
||||
% if data['directors']:
|
||||
Directed by <strong> ${data['directors'][0]}</strong>
|
||||
% endif
|
||||
</div>
|
||||
<div class="summary-content-details-tag">
|
||||
% if data['studio']:
|
||||
Studio <strong> ${data['studio']}</strong>
|
||||
% endif
|
||||
</div>
|
||||
<div class="summary-content-details-tag">
|
||||
% if data['type'] == 'movie':
|
||||
Year <strong> ${data['year']}</strong>
|
||||
% elif data['type'] == 'show':
|
||||
Aired <strong> ${data['year']}</strong>
|
||||
% elif data['type'] == 'episode':
|
||||
Aired <strong> <span id="airdate">${data['originally_available_at']}</span></strong>
|
||||
% elif data['type'] == 'album' or data['type'] == 'track':
|
||||
Released <strong> ${data['year']}</strong>
|
||||
% endif
|
||||
</div>
|
||||
<div class="summary-content-details-tag">
|
||||
% if data['duration']:
|
||||
Runtime <strong> <span id="runtime">${data['duration']}</span> mins</strong>
|
||||
% endif
|
||||
</div>
|
||||
<div class="summary-content-details-tag">
|
||||
% if data['content_rating']:
|
||||
Rated <strong> ${data['content_rating']} </strong>
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
% if data['tagline']:
|
||||
<div class="summary-content-summary">
|
||||
<p><strong> ${data['tagline']} </strong></p>
|
||||
</div>
|
||||
% endif
|
||||
<div class="summary-content-summary">
|
||||
<p> ${data['summary']} </p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="summary-content-people-wrapper hidden-xs hidden-sm">
|
||||
% if data['writers']:
|
||||
<div class="summary-content-writers">
|
||||
<strong>Written by</strong>
|
||||
<ul>
|
||||
% for writer in data['writers']:
|
||||
% if loop.index < 5:
|
||||
<li>
|
||||
${writer}
|
||||
</li>
|
||||
% endif
|
||||
% endfor
|
||||
</ul>
|
||||
</div>
|
||||
% endif
|
||||
% if data['actors']:
|
||||
<div class="summary-content-actors">
|
||||
<strong>Starring</strong>
|
||||
<ul>
|
||||
% for actor in data['actors']:
|
||||
% if loop.index < 5:
|
||||
<li>
|
||||
${actor}
|
||||
</li>
|
||||
% endif
|
||||
% endfor
|
||||
</ul>
|
||||
</div>
|
||||
% endif
|
||||
</div>
|
||||
<div class="summary-content-people-wrapper hidden-xs hidden-sm">
|
||||
% if data['genres']:
|
||||
<div class="summary-content-genres">
|
||||
<strong>Genres</strong>
|
||||
<ul>
|
||||
% for genre in data['genres']:
|
||||
% if loop.index < 5:
|
||||
<li>
|
||||
${genre}
|
||||
</li>
|
||||
% endif
|
||||
% endfor
|
||||
</ul>
|
||||
</div>
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
% if data['type'] == 'show':
|
||||
<div class='col-md-12'>
|
||||
<div class='table-card-header'>
|
||||
<div class="header-bar">
|
||||
<span>Season List for <strong>${data['title']}</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class='table-card-back'>
|
||||
<div id="children-list"></div>
|
||||
</div>
|
||||
</div>
|
||||
% elif data['type'] == 'season':
|
||||
<div class='col-md-12'>
|
||||
<div class='table-card-header'>
|
||||
<div class="header-bar">
|
||||
<span>Episode List for <strong>${data['title']}</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class='table-card-back'>
|
||||
<div id="children-list"></div>
|
||||
</div>
|
||||
</div>
|
||||
% elif data['type'] == 'artist':
|
||||
<div class='col-md-12'>
|
||||
<div class='table-card-header'>
|
||||
<div class="header-bar">
|
||||
<span>Album List for <strong>${data['title']}</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class='table-card-back'>
|
||||
<div id="children-list"></div>
|
||||
</div>
|
||||
</div>
|
||||
% elif data['type'] == 'album':
|
||||
<div class='col-md-12'>
|
||||
<div class='table-card-header'>
|
||||
<div class="header-bar">
|
||||
<span>Track List for <strong>${data['title']}</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class='table-card-back'>
|
||||
<div id="children-list"></div>
|
||||
</div>
|
||||
</div>
|
||||
% endif
|
||||
<div class='col-md-12'>
|
||||
<div class='table-card-header'>
|
||||
<div class="header-bar">
|
||||
<span>Watch History for <strong>${data['title']}</strong></span>
|
||||
</div>
|
||||
<div class="button-bar">
|
||||
<div class="colvis-button-bar hidden-xs"></div>
|
||||
<button class="btn btn-danger btn-edit" data-toggle="button" aria-pressed="false" autocomplete="off" id="row-edit-mode">
|
||||
<i class="fa fa-trash-o"></i> Delete mode
|
||||
</button>
|
||||
<div class="alert alert-danger alert-edit" role="alert" id="row-edit-mode-alert"><i class="fa fa-exclamation-triangle"></i> Select rows to delete. Data is deleted upon exiting delete mode.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-card-back">
|
||||
<table class="display" id="history_table" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th align='left' id="delete">Delete</th>
|
||||
<th align='left' id="time">Time</th>
|
||||
<th align='left' id="friendly_name">User</th>
|
||||
<th align='left' id="ip_address">IP Address</th>
|
||||
<th align='left' id="platform">Platform</th>
|
||||
<th align='left' id="title">Title</th>
|
||||
<th align='left' id="started">Started</th>
|
||||
<th align='left' id="paused_counter">Paused</th>
|
||||
<th align='left' id="stopped">Stopped</th>
|
||||
<th align='left' id="duration">Duration</th>
|
||||
<th align='left' id="percent_complete"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div id="info-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="info-modal">
|
||||
</div>
|
||||
<div class="modal fade" id="ip-info-modal" tabindex="-1" role="dialog" aria-labelledby="ip-info-modal">
|
||||
</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 Delete</h4>
|
||||
</div>
|
||||
<div class="modal-body" style="text-align: center;">
|
||||
<p>Are you REALLY sure you want to delete <strong><span id="deleteCount"></span></strong> history item(s)?</p>
|
||||
<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-delete">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -158,66 +358,14 @@ DOCUMENTATION :: END
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
% if data['type'] == 'movie' or data['type'] == 'episode' or data['type'] == 'show':
|
||||
<div class='container-fluid'>
|
||||
<div class='row'>
|
||||
<div class='col-md-12'>
|
||||
<div class='table-card-header'>
|
||||
<div class="header-bar">
|
||||
<span>Watch history for <strong>${data['title']}</strong></span>
|
||||
</div>
|
||||
<div class="colvis-button-bar hidden-xs">
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-card-back">
|
||||
<table class="display" id="history_table" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th align='left' id="time">Time</th>
|
||||
<th align='left' id="friendly_name">User</th>
|
||||
<th align='left' id="platform">Platform</th>
|
||||
<th align='left' id="ip_address">IP Address</th>
|
||||
<th align='left' id="title">Title</th>
|
||||
<th align='left' id="started">Started</th>
|
||||
<th align='left' id="paused_counter">Paused</th>
|
||||
<th align='left' id="stopped">Stopped</th>
|
||||
<th align='left' id="duration">Duration</th>
|
||||
<th align='left' id="percent_complete">Watched</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div id="info-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="info-modal">
|
||||
</div>
|
||||
<div class="modal fade" id="ip-info-modal" tabindex="-1" role="dialog" aria-labelledby="ip-info-modal">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
% elif data['type'] == 'season':
|
||||
<div class='container-fluid'>
|
||||
<div class='row'>
|
||||
<div class='col-md-12'>
|
||||
<div class='table-card-header'>
|
||||
<div class="header-bar">
|
||||
<span>Episode list for <strong>${data['title']}</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class='table-card-back'>
|
||||
<div id="episode-list"></div>
|
||||
</div>
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
% 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>
|
||||
<h3>
|
||||
Error retrieving item data. This media may not be available in the Plex Media Server database
|
||||
anymore.
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -233,68 +381,117 @@ DOCUMENTATION :: END
|
||||
<script src="interfaces/default/js/moment-with-locale.js"></script>
|
||||
|
||||
% if data:
|
||||
% if data['type'] == 'movie':
|
||||
% 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)
|
||||
var starRating = Math.round(${data['rating']} / 2);
|
||||
$('#stars').attr('data-rateit-value', starRating);
|
||||
</script>
|
||||
% endif
|
||||
% if data['type'] == 'movie' or data['type'] == 'episode':
|
||||
<script src="interfaces/default/js/tables/history_table.js"></script>
|
||||
% if data['type'] == 'show' or data['type'] == 'artist':
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
function get_history() {
|
||||
history_table_options.ajax = {
|
||||
"url": "get_history",
|
||||
type: 'post',
|
||||
data: function ( d ) {
|
||||
return { 'json_data': JSON.stringify( d ),
|
||||
'rating_key': ${data['rating_key']}
|
||||
};
|
||||
'grandparent_rating_key': ${data['rating_key']} };
|
||||
}
|
||||
}
|
||||
history_table = $('#history_table').DataTable(history_table_options);
|
||||
history_table.column(4).visible(false);
|
||||
|
||||
var colvis = new $.fn.dataTable.ColVis(history_table, { buttonText: 'Select columns', buttonClass: 'btn btn-dark' });
|
||||
$(colvis.button()).appendTo('div.colvis-button-bar');
|
||||
});
|
||||
}
|
||||
</script>
|
||||
% elif data['type'] == 'show':
|
||||
<script src="interfaces/default/js/tables/history_table.js"></script>
|
||||
% elif data['type'] == 'season' or data['type'] == 'album':
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
function get_history() {
|
||||
history_table_options.ajax = {
|
||||
"url": "get_history",
|
||||
type: 'post',
|
||||
data: function ( d ) {
|
||||
return { 'json_data': JSON.stringify( d ),
|
||||
'grandparent_rating_key': ${data['rating_key']}
|
||||
};
|
||||
'parent_rating_key': ${data['rating_key']} };
|
||||
}
|
||||
}
|
||||
history_table = $('#history_table').DataTable(history_table_options);
|
||||
var colvis = new $.fn.dataTable.ColVis(history_table, { buttonText: 'Select columns', buttonClass: 'btn btn-dark' });
|
||||
$(colvis.button()).appendTo('div.colvis-button-bar');
|
||||
});
|
||||
}
|
||||
</script>
|
||||
% elif data['type'] == 'episode' or data['type'] == 'track' or data['type'] == 'movie':
|
||||
<script>
|
||||
function get_history() {
|
||||
history_table_options.ajax = {
|
||||
"url": "get_history",
|
||||
type: 'post',
|
||||
data: function ( d ) {
|
||||
return { 'json_data': JSON.stringify( d ),
|
||||
'rating_key': ${data['rating_key']} };
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
% endif
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
get_history();
|
||||
history_table = $('#history_table').DataTable(history_table_options);
|
||||
var colvis = new $.fn.dataTable.ColVis(history_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark', exclude: [0, 10] });
|
||||
$(colvis.button()).appendTo('div.colvis-button-bar');
|
||||
|
||||
% if data['type'] == 'season':
|
||||
clearSearchButton('history_table', history_table);
|
||||
|
||||
$('#row-edit-mode').on('click', function() {
|
||||
$('#row-edit-mode-alert').fadeIn(200);
|
||||
|
||||
if ($(this).hasClass('active')) {
|
||||
if (history_to_delete.length > 0) {
|
||||
$('#deleteCount').text(history_to_delete.length);
|
||||
$('#confirm-modal').modal();
|
||||
$('#confirm-modal').one('click', '#confirm-delete', function () {
|
||||
for (var i = 0; i < history_to_delete.length; i++) {
|
||||
$.ajax({
|
||||
url: 'delete_history_rows',
|
||||
data: { row_id: history_to_delete[i] },
|
||||
async: true,
|
||||
success: function (data) {
|
||||
var msg = "History deleted";
|
||||
showMsg(msg, false, true, 2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
history_table.draw();
|
||||
});
|
||||
}
|
||||
|
||||
$('.delete-control').each(function () {
|
||||
$(this).addClass('hidden');
|
||||
$('#row-edit-mode-alert').fadeOut(200);
|
||||
});
|
||||
|
||||
} else {
|
||||
history_to_delete = [];
|
||||
$('.delete-control').each(function() {
|
||||
$(this).find('button.btn-danger').toggleClass('btn-warning').toggleClass('btn-danger');
|
||||
$(this).removeClass('hidden');
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
% if data['type'] == 'show' or data['type'] == 'season' or data['type'] == 'artist' or data['type'] == 'album':
|
||||
<script>
|
||||
$.ajax({
|
||||
url: 'get_children',
|
||||
url: 'get_item_children',
|
||||
type: "GET",
|
||||
async: true,
|
||||
data: { rating_key : ${data['rating_key']} },
|
||||
complete: function(xhr, status) {
|
||||
$("#episode-list").html(xhr.responseText);
|
||||
}
|
||||
$("#children-list").html(xhr.responseText); }
|
||||
});
|
||||
</script>
|
||||
% endif
|
||||
<script>
|
||||
$("#runtime").html(millisecondsToMinutes($("#runtime").html(), true));
|
||||
$("#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>
|
||||
% endif
|
||||
</%def>
|
||||
|
102
data/interfaces/default/info_children_list.html
Normal file
102
data/interfaces/default/info_children_list.html
Normal file
@@ -0,0 +1,102 @@
|
||||
<%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 ==
|
||||
children_type Returns the type of children in the array.
|
||||
children_count Returns the number of episodes in the array.
|
||||
children_list Returns an array of episodes.
|
||||
|
||||
data['children_list'] :: Usable paramaters
|
||||
|
||||
== Global keys ==
|
||||
rating_key Returns the unique identifier for the media item.
|
||||
index Returns the episode number.
|
||||
title Returns the name of the episode.
|
||||
thumb Returns the location of the item's thumbnail. Use with pms_image_proxy.
|
||||
parent_thumb Returns the location of the item's parent thumbnail. Use with pms_image_proxy.
|
||||
|
||||
DOCUMENTATION :: END
|
||||
</%doc>
|
||||
|
||||
% if data != None:
|
||||
% if data['children_count'] > 0:
|
||||
<div class="item-children-wrapper">
|
||||
<ul class="item-children-instance list-unstyled">
|
||||
% for child in data['children_list']:
|
||||
% if child['rating_key']:
|
||||
% if data['children_type'] == 'track':
|
||||
<li class="item-children-list-item">
|
||||
% else:
|
||||
<li>
|
||||
% endif
|
||||
<a href="info?item_id=${child['rating_key']}">
|
||||
%if data['children_type'] == 'season':
|
||||
<div class="item-children-poster">
|
||||
% if child['thumb']:
|
||||
<div class="item-children-poster-face season-poster" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=450);">
|
||||
% else:
|
||||
<div class="item-children-poster-face season-poster" style="background-image: url(pms_image_proxy?img=${child['parent_thumb']}&width=300&height=450);">
|
||||
% endif
|
||||
<div class="item-children-card-overlay">
|
||||
<div class="item-children-overlay-text">
|
||||
Season ${child['index']}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
% elif data['children_type'] == 'episode':
|
||||
<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 class="item-children-card-overlay">
|
||||
<div class="item-children-overlay-text">
|
||||
Episode ${child['index']}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-children-instance-text-wrapper episode-item">
|
||||
<h3>${child['title']}</h3>
|
||||
</div>
|
||||
% elif data['children_type'] == 'album':
|
||||
<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>${child['title']}</h3>
|
||||
</div>
|
||||
% elif data['children_type'] == 'track':
|
||||
% if loop.index % 2 == 0:
|
||||
<div class="item-children-list-item-even">
|
||||
<span class="item-children-list-item-index">${child['index']}</span>
|
||||
<span class="item-children-list-item-title">${child['title']}</span>
|
||||
<span class="item-children-list-item-duration" id="item-children-list-item-duration-${loop.index + 1}">
|
||||
<script>$('#item-children-list-item-duration-${loop.index + 1}').text(moment.utc(${child['duration']}).format("m:ss"));</script>
|
||||
</span>
|
||||
</div>
|
||||
% else:
|
||||
<div class="item-children-list-item-odd">
|
||||
<span class="item-children-list-item-index">${child['index']}</span>
|
||||
<span class="item-children-list-item-title">${child['title']}</span>
|
||||
<span class="item-children-list-item-duration" id="item-children-list-item-duration-${loop.index + 1}">
|
||||
<script>$('#item-children-list-item-duration-${loop.index + 1}').text(moment.utc(${child['duration']}).format("m:ss"));</script>
|
||||
</span>
|
||||
</div>
|
||||
% endif
|
||||
% endif
|
||||
</a>
|
||||
</li>
|
||||
% endif
|
||||
% endfor
|
||||
</ul>
|
||||
</div>
|
||||
% endif
|
||||
% endif
|
||||
|
@@ -1,58 +0,0 @@
|
||||
<%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_episode_list.html
|
||||
Version: 0.1
|
||||
Variable names: data [list]
|
||||
|
||||
data :: Usable parameters
|
||||
|
||||
== Global keys ==
|
||||
episode_count Returns the number of episodes in the array.
|
||||
episode_list Returns an array of episodes.
|
||||
|
||||
data['episode_list'] :: Usable paramaters
|
||||
|
||||
== Global keys ==
|
||||
rating_key Returns the unique identifier for the media item.
|
||||
thumb Returns the location of the item's thumbnail. Use with pms_image_proxy.
|
||||
title Returns the name of the episode.
|
||||
index Returns the episode number.
|
||||
|
||||
DOCUMENTATION :: END
|
||||
</%doc>
|
||||
|
||||
% if data != None:
|
||||
% if data['episode_count'] > 0:
|
||||
<div class="season-episodes-wrapper">
|
||||
<ul class="season-episodes-instance list-unstyled">
|
||||
% for a in data['episode_list']:
|
||||
<li>
|
||||
<div class="season-episodes-poster">
|
||||
<div class="season-episodes-poster-face">
|
||||
<a href="info?item_id=${a['rating_key']}">
|
||||
<img src="pms_image_proxy?img=${a['thumb']}&width=410&height=230" class="season-episodes-poster-face">
|
||||
</a>
|
||||
</div>
|
||||
<div class="season-episodes-card-overlay">
|
||||
<div class="season-episodes-season">
|
||||
Episode ${a['index']}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="season-episodes-instance-text-wrapper">
|
||||
<div class="season-episodes-title">
|
||||
<a href="info?item_id=${a['rating_key']}">
|
||||
"${a['title']}"
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
% endfor
|
||||
</ul>
|
||||
</div>
|
||||
% endif
|
||||
% endif
|
||||
|
@@ -15,7 +15,7 @@
|
||||
<div class="modal-body" id="modal-text">
|
||||
<div class="col-md-6">
|
||||
<h4><strong>Location Details</strong></h4>
|
||||
<ul>
|
||||
<ul class="list-unstyled">
|
||||
<li>Country: <strong><span id="country"></span></strong></li>
|
||||
<li>City: <strong><span id="city"></span></strong></li>
|
||||
<li>Region: <strong><span id="region"></span></strong></li>
|
||||
@@ -26,7 +26,7 @@
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h4><strong>Connection Details</strong></h4>
|
||||
<ul>
|
||||
<ul class="list-unstyled">
|
||||
<li>ISP: <strong><span id="isp"></span></strong></li>
|
||||
<li>Organization: <strong><span id="org"></span></strong></li>
|
||||
<li>AS: <strong><span id="as"></span></strong></li>
|
||||
|
@@ -35,7 +35,7 @@ var hc_plays_by_day_options = {
|
||||
}
|
||||
}
|
||||
},
|
||||
colors: ['#F9AA03', '#FFFFFF'],
|
||||
colors: ['#F9AA03', '#FFFFFF', '#FF4747'],
|
||||
xAxis: {
|
||||
type: 'datetime',
|
||||
labels: {
|
||||
|
@@ -29,7 +29,7 @@ var hc_plays_by_dayofweek_options = {
|
||||
credits: {
|
||||
enabled: false
|
||||
},
|
||||
colors: ['#F9AA03', '#FFFFFF'],
|
||||
colors: ['#F9AA03', '#FFFFFF', '#FF4747'],
|
||||
xAxis: {
|
||||
categories: [{}],
|
||||
labels: {
|
||||
@@ -46,8 +46,26 @@ var hc_plays_by_dayofweek_options = {
|
||||
style: {
|
||||
color: '#aaa'
|
||||
}
|
||||
},
|
||||
stackLabels: {
|
||||
enabled: false,
|
||||
style: {
|
||||
color: '#fff'
|
||||
}
|
||||
}
|
||||
},
|
||||
plotOptions: {
|
||||
column: {
|
||||
stacking: 'normal',
|
||||
borderWidth: '0',
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
style: {
|
||||
color: '#000'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
shared: true
|
||||
},
|
||||
|
@@ -29,7 +29,7 @@ var hc_plays_by_hourofday_options = {
|
||||
credits: {
|
||||
enabled: false
|
||||
},
|
||||
colors: ['#F9AA03', '#FFFFFF'],
|
||||
colors: ['#F9AA03', '#FFFFFF', '#FF4747'],
|
||||
xAxis: {
|
||||
categories: [{}],
|
||||
labels: {
|
||||
@@ -46,8 +46,26 @@ var hc_plays_by_hourofday_options = {
|
||||
style: {
|
||||
color: '#aaa'
|
||||
}
|
||||
},
|
||||
stackLabels: {
|
||||
enabled: false,
|
||||
style: {
|
||||
color: '#fff'
|
||||
}
|
||||
}
|
||||
},
|
||||
plotOptions: {
|
||||
column: {
|
||||
stacking: 'normal',
|
||||
borderWidth: '0',
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
style: {
|
||||
color: '#000'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
shared: true
|
||||
},
|
||||
|
@@ -23,13 +23,9 @@ var hc_plays_by_month_options = {
|
||||
credits: {
|
||||
enabled: false
|
||||
},
|
||||
colors: ['#F9AA03', '#FFFFFF'],
|
||||
colors: ['#F9AA03', '#FFFFFF', '#FF4747'],
|
||||
xAxis: {
|
||||
type: 'datetime',
|
||||
labels: {
|
||||
formatter: function() {
|
||||
return moment(this.value).format("MMM YYYY");
|
||||
},
|
||||
style: {
|
||||
color: '#aaa'
|
||||
}
|
||||
|
@@ -29,7 +29,7 @@ var hc_plays_by_platform_options = {
|
||||
credits: {
|
||||
enabled: false
|
||||
},
|
||||
colors: ['#F9AA03', '#FFFFFF'],
|
||||
colors: ['#F9AA03', '#FFFFFF', '#FF4747'],
|
||||
xAxis: {
|
||||
categories: [{}],
|
||||
labels: {
|
||||
@@ -46,8 +46,26 @@ var hc_plays_by_platform_options = {
|
||||
style: {
|
||||
color: '#aaa'
|
||||
}
|
||||
},
|
||||
stackLabels: {
|
||||
enabled: false,
|
||||
style: {
|
||||
color: '#fff'
|
||||
}
|
||||
}
|
||||
},
|
||||
plotOptions: {
|
||||
column: {
|
||||
stacking: 'normal',
|
||||
borderWidth: '0',
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
style: {
|
||||
color: '#000'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
shared: true
|
||||
},
|
||||
|
@@ -29,7 +29,7 @@ var hc_plays_by_source_resolution_options = {
|
||||
credits: {
|
||||
enabled: false
|
||||
},
|
||||
colors: ['#F9AA03', '#FFFFFF'],
|
||||
colors: ['#F9AA03', '#FFFFFF', '#FF4747'],
|
||||
xAxis: {
|
||||
categories: [{}],
|
||||
labels: {
|
||||
@@ -46,8 +46,26 @@ var hc_plays_by_source_resolution_options = {
|
||||
style: {
|
||||
color: '#aaa'
|
||||
}
|
||||
},
|
||||
stackLabels: {
|
||||
enabled: false,
|
||||
style: {
|
||||
color: '#fff'
|
||||
}
|
||||
}
|
||||
},
|
||||
plotOptions: {
|
||||
column: {
|
||||
stacking: 'normal',
|
||||
borderWidth: '0',
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
style: {
|
||||
color: '#000'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
shared: true
|
||||
},
|
||||
|
@@ -29,7 +29,7 @@ var hc_plays_by_stream_resolution_options = {
|
||||
credits: {
|
||||
enabled: false
|
||||
},
|
||||
colors: ['#F9AA03', '#FFFFFF'],
|
||||
colors: ['#F9AA03', '#FFFFFF', '#FF4747'],
|
||||
xAxis: {
|
||||
categories: [{}],
|
||||
labels: {
|
||||
@@ -46,8 +46,26 @@ var hc_plays_by_stream_resolution_options = {
|
||||
style: {
|
||||
color: '#aaa'
|
||||
}
|
||||
},
|
||||
stackLabels: {
|
||||
enabled: false,
|
||||
style: {
|
||||
color: '#fff'
|
||||
}
|
||||
}
|
||||
},
|
||||
plotOptions: {
|
||||
column: {
|
||||
stacking: 'normal',
|
||||
borderWidth: '0',
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
style: {
|
||||
color: '#000'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
shared: true
|
||||
},
|
||||
|
@@ -29,7 +29,7 @@ var hc_plays_by_user_options = {
|
||||
credits: {
|
||||
enabled: false
|
||||
},
|
||||
colors: ['#F9AA03', '#FFFFFF'],
|
||||
colors: ['#F9AA03', '#FFFFFF', '#FF4747'],
|
||||
xAxis: {
|
||||
categories: [{}],
|
||||
labels: {
|
||||
@@ -46,8 +46,26 @@ var hc_plays_by_user_options = {
|
||||
style: {
|
||||
color: '#aaa'
|
||||
}
|
||||
},
|
||||
stackLabels: {
|
||||
enabled: false,
|
||||
style: {
|
||||
color: '#fff'
|
||||
}
|
||||
}
|
||||
},
|
||||
plotOptions: {
|
||||
column: {
|
||||
stacking: 'normal',
|
||||
borderWidth: '0',
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
style: {
|
||||
color: '#000'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
shared: true
|
||||
},
|
||||
|
@@ -219,7 +219,11 @@ function getPlatformImagePath(platformName) {
|
||||
return 'interfaces/default/images/platforms/playstation.png';
|
||||
} else if (platformName.indexOf("Mystery 5") > -1) {
|
||||
return 'interfaces/default/images/platforms/xbox.png';
|
||||
} else {
|
||||
} else if (platformName.indexOf("Windows") > -1) {
|
||||
return 'interfaces/default/images/platforms/win8.png';
|
||||
} else if (platformName.indexOf("Windows phone") > -1) {
|
||||
return 'interfaces/default/images/platforms/wp.png';
|
||||
} else {
|
||||
return 'interfaces/default/images/platforms/default.png';
|
||||
}
|
||||
}
|
||||
@@ -251,6 +255,9 @@ function humanTime(seconds) {
|
||||
} else if (seconds >= 60) {
|
||||
text = '<h3>' + Math.floor(moment.duration(((seconds % 86400) % 3600), 'seconds').asMinutes()) + '</h3><p> mins</p>';
|
||||
return text;
|
||||
} else {
|
||||
text = '<h3>0</h3><p> mins</p>';
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,3 +355,12 @@ Accordion.prototype.dropdown = function(e) {
|
||||
$el.find('.submenu').not($next).slideUp().parent().removeClass('open');
|
||||
};
|
||||
}
|
||||
|
||||
function clearSearchButton(tableName, table) {
|
||||
$('#' + tableName + '_filter').find('input[type=search]')
|
||||
.wrap('<div class="input-group" role="group" aria-label="Search"></div>')
|
||||
.after('<span class="input-group-btn"><button class="btn btn-form" data-toggle="button" aria-pressed="false" autocomplete="off" id="clear-search-' + tableName + '"><i class="fa fa-remove"></i></button></span>')
|
||||
$('#clear-search-' + tableName).click(function() {
|
||||
table.search('').draw();
|
||||
});
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
var date_format = 'YYYY-MM-DD';
|
||||
var time_format = 'hh:mm a';
|
||||
var history_to_delete = [];
|
||||
|
||||
$.ajax({
|
||||
url: 'get_date_formats',
|
||||
@@ -18,17 +19,29 @@ history_table_options = {
|
||||
"info":"Showing _START_ to _END_ of _TOTAL_ history items",
|
||||
"infoEmpty":"Showing 0 to 0 of 0 entries",
|
||||
"infoFiltered":"(filtered from _MAX_ total entries)",
|
||||
"emptyTable": "No data in table",
|
||||
"emptyTable": "No data in table"
|
||||
},
|
||||
"pagingType": "bootstrap",
|
||||
"stateSave": true,
|
||||
"processing": false,
|
||||
"serverSide": true,
|
||||
"pageLength": 25,
|
||||
"order": [ 0, 'desc'],
|
||||
"order": [ 1, 'desc'],
|
||||
"autoWidth": false,
|
||||
"columnDefs": [
|
||||
{
|
||||
"targets": [0],
|
||||
"data": null,
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
$(td).html('<button class="btn btn-xs btn-warning" data-id="' + rowData['id'] + '"><i class="fa fa-trash-o fa-fw"></i> Delete</button>');
|
||||
},
|
||||
"width": "5%",
|
||||
"className": "delete-control no-wrap hidden",
|
||||
"searchable": false,
|
||||
"orderable": false
|
||||
},
|
||||
{
|
||||
"targets": [1],
|
||||
"data":"date",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (rowData['stopped'] === null) {
|
||||
@@ -38,10 +51,11 @@ history_table_options = {
|
||||
}
|
||||
},
|
||||
"searchable": false,
|
||||
"width": "8%",
|
||||
"className": "no-wrap"
|
||||
},
|
||||
{
|
||||
"targets": [1],
|
||||
"targets": [2],
|
||||
"data":"friendly_name",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData !== '') {
|
||||
@@ -54,21 +68,12 @@ history_table_options = {
|
||||
$(td).html(cellData);
|
||||
}
|
||||
},
|
||||
"width": "8%",
|
||||
"className": "no-wrap hidden-xs"
|
||||
},
|
||||
{
|
||||
"targets": [2],
|
||||
"data":"player",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData !== '') {
|
||||
$(td).html('<a href="#" data-target="#info-modal" data-toggle="modal"><i class="fa fa-lg fa-info-circle"></i></a> '+cellData);
|
||||
}
|
||||
},
|
||||
"className": "modal-control no-wrap hidden-sm hidden-xs"
|
||||
},
|
||||
{
|
||||
"targets": [3],
|
||||
"data":"ip_address",
|
||||
"data": "ip_address",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData) {
|
||||
if (isPrivateIP(cellData)) {
|
||||
@@ -78,35 +83,63 @@ history_table_options = {
|
||||
$(td).html('n/a');
|
||||
}
|
||||
} else {
|
||||
$(td).html('<a href="javascript:void(0)" data-toggle="modal" data-target="#ip-info-modal"><i class="fa fa-map-marker"></i> ' + cellData +'</a>');
|
||||
$(td).html('<a href="javascript:void(0)" data-toggle="modal" data-target="#ip-info-modal"><i class="fa fa-map-marker"></i> ' + cellData + '</a>');
|
||||
}
|
||||
} else {
|
||||
$(td).html('n/a');
|
||||
}
|
||||
},
|
||||
"className": "no-wrap hidden-xs modal-control-ip"
|
||||
"width": "8%",
|
||||
"className": "no-wrap hidden-md hidden-sm hidden-xs modal-control-ip"
|
||||
},
|
||||
{
|
||||
"targets": [4],
|
||||
"data":"player",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData !== '') {
|
||||
var transcode_dec = '';
|
||||
if (rowData['video_decision'] === 'transcode') {
|
||||
transcode_dec = '<span class="transcode-tooltip" data-toggle="tooltip" title="Transcode"><i class="fa fa-server fa-fw"></i></span>';
|
||||
} else if (rowData['video_decision'] === 'copy') {
|
||||
transcode_dec = '<span class="transcode-tooltip" data-toggle="tooltip" title="Direct Stream"><i class="fa fa-video-camera fa-fw"></i></span>';
|
||||
} else if (rowData['video_decision'] === 'direct play' || rowData['video_decision'] === '') {
|
||||
transcode_dec = '<span class="transcode-tooltip" data-toggle="tooltip" title="Direct Play"><i class="fa fa-play-circle fa-fw"></i></span>';
|
||||
}
|
||||
$(td).html('<div><a href="#" data-target="#info-modal" data-toggle="modal"><div style="float: left;">' + transcode_dec + ' ' + cellData + '</div></a></div>');
|
||||
}
|
||||
},
|
||||
"width": "15%",
|
||||
"className": "no-wrap hidden-md hidden-sm hidden-xs modal-control"
|
||||
},
|
||||
{
|
||||
"targets": [5],
|
||||
"data":"full_title",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData !== '') {
|
||||
if (rowData['media_type'] === 'movie' || rowData['media_type'] === 'episode') {
|
||||
var transcode_dec = '';
|
||||
if (rowData['video_decision'] === 'transcode') {
|
||||
transcode_dec = '<i class="fa fa-server"></i> ';
|
||||
}
|
||||
$(td).html('<div><div style="float: left;"><a href="info?source=history&item_id=' + rowData['id'] + '">' + cellData + '</a></div><div style="float: right; text-align: right; padding-right: 5px;">' + transcode_dec + '<i class="fa fa-video-camera"></i></div></div>');
|
||||
var media_type = '';
|
||||
var thumb_popover = '';
|
||||
if (rowData['media_type'] === 'movie') {
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Movie"><i class="fa fa-film fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=poster" data-height="120">' + cellData + ' (' + rowData['year'] + ')</span>'
|
||||
$(td).html('<div class="history-title"><a href="info?source=history&item_id=' + rowData['id'] + '"><div style="float: left;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else if (rowData['media_type'] === 'episode') {
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Episode"><i class="fa fa-television fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=poster" data-height="120">' + cellData + ' \
|
||||
(S' + rowData['parent_media_index'] + '· E' + rowData['media_index'] + ')</span>'
|
||||
$(td).html('<div class="history-title"><a href="info?source=history&item_id=' + rowData['id'] + '"><div style="float: left;" >' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else if (rowData['media_type'] === 'track') {
|
||||
$(td).html('<div><div style="float: left;">' + cellData + '</div><div style="float: right; text-align: right; padding-right: 5px;"><i class="fa fa-music"></i></div></div>');
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Track"><i class="fa fa-music fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=300&fallback=poster" data-height="80">' + cellData + ' (' + rowData['parent_title'] + ')</span>'
|
||||
$(td).html('<div class="history-title"><a href="info?source=history&item_id=' + rowData['id'] + '"><div style="float: left;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else {
|
||||
$(td).html('<a href="info?item_id=' + rowData['id'] + '">' + cellData + '</a>');
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"width": "35%"
|
||||
},
|
||||
{
|
||||
"targets": [5],
|
||||
"targets": [6],
|
||||
"data":"started",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData === null) {
|
||||
@@ -116,10 +149,11 @@ history_table_options = {
|
||||
}
|
||||
},
|
||||
"searchable": false,
|
||||
"width": "5%",
|
||||
"className": "no-wrap hidden-sm hidden-xs"
|
||||
},
|
||||
{
|
||||
"targets": [6],
|
||||
"targets": [7],
|
||||
"data":"paused_counter",
|
||||
"render": function ( data, type, full ) {
|
||||
if (data !== null) {
|
||||
@@ -129,10 +163,11 @@ history_table_options = {
|
||||
}
|
||||
},
|
||||
"searchable": false,
|
||||
"className": "no-wrap hidden-xs"
|
||||
"width": "5%",
|
||||
"className": "no-wrap hidden-md hidden-sm hidden-xs"
|
||||
},
|
||||
{
|
||||
"targets": [7],
|
||||
"targets": [8],
|
||||
"data":"stopped",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData === null) {
|
||||
@@ -142,10 +177,11 @@ history_table_options = {
|
||||
}
|
||||
},
|
||||
"searchable": false,
|
||||
"className": "no-wrap hidden-md hidden-xs"
|
||||
"width": "5%",
|
||||
"className": "no-wrap hidden-sm hidden-xs"
|
||||
},
|
||||
{
|
||||
"targets": [8],
|
||||
"targets": [9],
|
||||
"data":"duration",
|
||||
"render": function ( data, type, full ) {
|
||||
if (data !== null) {
|
||||
@@ -155,38 +191,59 @@ history_table_options = {
|
||||
}
|
||||
},
|
||||
"searchable": false,
|
||||
"width": "5%",
|
||||
"className": "no-wrap hidden-xs"
|
||||
},
|
||||
{
|
||||
"targets": [9],
|
||||
"targets": [10],
|
||||
"data":"percent_complete",
|
||||
"render": function ( data, type, full ) {
|
||||
if (data > 80) {
|
||||
return '<i class="fa fa-lg fa-circle"></i>'
|
||||
return '<span class="watched-tooltip" data-toggle="tooltip" title="Watched"><i class="fa fa-lg fa-circle"></i></span>'
|
||||
} else if (data > 40) {
|
||||
return '<i class="fa fa-lg fa-adjust fa-rotate-180"></i>'
|
||||
return '<span class="watched-tooltip" data-toggle="tooltip" title="Partial"><i class="fa fa-lg fa-adjust fa-rotate-180"></i></span>'
|
||||
} else {
|
||||
return '<i class="fa fa-lg fa-circle-o"></i>'
|
||||
return '<span class="watched-tooltip" data-toggle="tooltip" title="Unwatched"><i class="fa fa-lg fa-circle-o"></i></span>'
|
||||
}
|
||||
},
|
||||
"searchable": false,
|
||||
"orderable": false,
|
||||
"className": "no-wrap hidden-md hidden-xs",
|
||||
"width": "10px"
|
||||
}
|
||||
"className": "no-wrap hidden-md hidden-sm hidden-xs",
|
||||
"width": "1%"
|
||||
},
|
||||
],
|
||||
"drawCallback": function (settings) {
|
||||
// Jump to top of page
|
||||
// $('html,body').scrollTop(0);
|
||||
$('#ajaxMsg').fadeOut();
|
||||
|
||||
// Create the tooltips.
|
||||
$('.info-modal').each(function() {
|
||||
$(this).tooltip();
|
||||
$('.transcode-tooltip').tooltip();
|
||||
$('.media-type-tooltip').tooltip();
|
||||
$('.watched-tooltip').tooltip();
|
||||
$('.thumb-tooltip').popover({
|
||||
html: true,
|
||||
trigger: 'hover',
|
||||
placement: 'right',
|
||||
content: function () {
|
||||
return '<div class="history-thumbnail" style="background-image: url(' + $(this).data('img') + '); height: ' + $(this).data('height') + 'px;" />';
|
||||
}
|
||||
});
|
||||
|
||||
if ($('#row-edit-mode').hasClass('active')) {
|
||||
$('.delete-control').each(function() {
|
||||
$(this).removeClass('hidden');
|
||||
});
|
||||
}
|
||||
},
|
||||
"preDrawCallback": function(settings) {
|
||||
var msg = "<div class='msg'><i class='fa fa-refresh fa-spin'></i> Fetching rows...</div>";
|
||||
showMsg(msg, false, false, 0)
|
||||
},
|
||||
"rowCallback": function (row, rowData) {
|
||||
if ($.inArray(rowData['id'], history_to_delete) !== -1) {
|
||||
$(row).find('button[data-id="' + rowData['id'] + '"]').toggleClass('btn-warning').toggleClass('btn-danger');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,6 +285,19 @@ $('#history_table').on('click', 'td.modal-control-ip', function () {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getUserLocation(rowData['ip_address']);
|
||||
});
|
||||
|
||||
$('#history_table').on('click', 'td.delete-control > button', function () {
|
||||
var tr = $(this).parents('tr');
|
||||
var row = history_table.row( tr );
|
||||
var rowData = row.data();
|
||||
|
||||
var index = $.inArray(rowData['id'], history_to_delete);
|
||||
if (index === -1) {
|
||||
history_to_delete.push(rowData['id']);
|
||||
} else {
|
||||
history_to_delete.splice(index, 1);
|
||||
}
|
||||
$(this).toggleClass('btn-warning').toggleClass('btn-danger');
|
||||
});
|
@@ -74,21 +74,41 @@ history_table_modal_options = {
|
||||
{
|
||||
"targets": [3],
|
||||
"data":"player",
|
||||
"className": "modal-control no-wrap hidden-sm hidden-xs"
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData !== '') {
|
||||
var transcode_dec = '';
|
||||
if (rowData['video_decision'] === 'transcode') {
|
||||
transcode_dec = '<span class="transcode-tooltip" data-toggle="tooltip" title="Transcode"><i class="fa fa-server fa-fw"></i></span>';
|
||||
} else if (rowData['video_decision'] === 'copy') {
|
||||
transcode_dec = '<span class="transcode-tooltip" data-toggle="tooltip" title="Direct Stream"><i class="fa fa-video-camera fa-fw"></i></span>';
|
||||
} else if (rowData['video_decision'] === 'direct play' || rowData['video_decision'] === '') {
|
||||
transcode_dec = '<span class="transcode-tooltip" data-toggle="tooltip" title="Direct Play"><i class="fa fa-play-circle fa-fw"></i></span>';
|
||||
}
|
||||
$(td).html('<div><a href="#" data-target="#info-modal" data-toggle="modal"><div style="float: left;">' + transcode_dec + ' ' + cellData + '</div></a></div>');
|
||||
}
|
||||
},
|
||||
"className": "no-wrap hidden-sm hidden-xs modal-control"
|
||||
},
|
||||
{
|
||||
"targets": [4],
|
||||
"data":"full_title",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData !== '') {
|
||||
if (rowData['media_type'] === 'movie' || rowData['media_type'] === 'episode') {
|
||||
var transcode_dec = '';
|
||||
if (rowData['video_decision'] === 'transcode') {
|
||||
transcode_dec = '<i class="fa fa-server"></i> ';
|
||||
}
|
||||
$(td).html('<div><div style="float: left;"><a href="info?source=history&item_id=' + rowData['id'] + '">' + cellData + '</a></div><div style="float: right; text-align: right; padding-right: 5px;">' + transcode_dec + '<i class="fa fa-video-camera"></i></div></div>');
|
||||
var media_type = '';
|
||||
var thumb_popover = '';
|
||||
if (rowData['media_type'] === 'movie') {
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Movie"><i class="fa fa-film fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=poster" data-height="120">' + cellData + ' (' + rowData['year'] + ')</span>'
|
||||
$(td).html('<div class="history-title"><a href="info?source=history&item_id=' + rowData['id'] + '"><div style="float: left;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else if (rowData['media_type'] === 'episode') {
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Episode"><i class="fa fa-television fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=poster" data-height="120">' + cellData + ' \
|
||||
(S' + rowData['parent_media_index'] + '· E' + rowData['media_index'] + ')</span>'
|
||||
$(td).html('<div class="history-title"><a href="info?source=history&item_id=' + rowData['id'] + '"><div style="float: left;" >' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else if (rowData['media_type'] === 'track') {
|
||||
$(td).html('<div><div style="float: left;">' + cellData + '</div><div style="float: right; text-align: right; padding-right: 5px;"><i class="fa fa-music"></i></div></div>');
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Track"><i class="fa fa-music fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=300&fallback=poster" data-height="80">' + cellData + ' (' + rowData['parent_title'] + ')</span>'
|
||||
$(td).html('<div class="history-title"><a href="info?source=history&item_id=' + rowData['id'] + '"><div style="float: left;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else {
|
||||
$(td).html('<a href="info?item_id=' + rowData['id'] + '">' + cellData + '</a>');
|
||||
}
|
||||
@@ -100,9 +120,40 @@ history_table_modal_options = {
|
||||
// Jump to top of page
|
||||
// $('html,body').scrollTop(0);
|
||||
$('#ajaxMsg').fadeOut();
|
||||
|
||||
// Create the tooltips.
|
||||
$('.transcode-tooltip').tooltip();
|
||||
$('.media-type-tooltip').tooltip();
|
||||
$('.thumb-tooltip').popover({
|
||||
html: true,
|
||||
trigger: 'hover',
|
||||
placement: 'right',
|
||||
content: function () {
|
||||
return '<div class="history-thumbnail" style="background-image: url(' + $(this).data('img') + '); height: ' + $(this).data('height') + 'px;" />';
|
||||
}
|
||||
});
|
||||
},
|
||||
"preDrawCallback": function(settings) {
|
||||
var msg = "<div class='msg'><i class='fa fa-refresh fa-spin'></i> Fetching rows...</div>";
|
||||
showMsg(msg, false, false, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$('#history_table').on('click', 'td.modal-control', function () {
|
||||
var tr = $(this).parents('tr');
|
||||
var row = history_table.row(tr);
|
||||
var rowData = row.data();
|
||||
|
||||
function showStreamDetails() {
|
||||
$.ajax({
|
||||
url: 'get_stream_data',
|
||||
data: { row_id: rowData['id'], user: rowData['friendly_name'] },
|
||||
cache: false,
|
||||
async: true,
|
||||
complete: function (xhr, status) {
|
||||
$("#info-modal").html(xhr.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
showStreamDetails();
|
||||
});
|
@@ -49,7 +49,7 @@ sync_table_options = {
|
||||
"data": "title",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData !== '') {
|
||||
if (rowData['metadata_type'] !== 'track') {
|
||||
if (rowData['metadata_type'] !== '') {
|
||||
$(td).html('<a href="info?item_id=' + rowData['rating_key'] + '">' + cellData + '</a>');
|
||||
} else {
|
||||
$(td).html(cellData);
|
||||
|
@@ -24,13 +24,11 @@ user_ip_table_options = {
|
||||
},
|
||||
"searchable": false,
|
||||
"width": "15%",
|
||||
"className": "no-wrap"
|
||||
"className": "no-wrap hidden-xs"
|
||||
},
|
||||
{
|
||||
"targets": [1],
|
||||
"data":"ip_address",
|
||||
"width": "15%",
|
||||
"className": "modal-control no-wrap",
|
||||
"data": "ip_address",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData) {
|
||||
if (isPrivateIP(cellData)) {
|
||||
@@ -46,31 +44,83 @@ user_ip_table_options = {
|
||||
$(td).html('n/a');
|
||||
}
|
||||
},
|
||||
"width": "15%"
|
||||
"width": "15%",
|
||||
"className": "no-wrap modal-control-ip"
|
||||
},
|
||||
{
|
||||
"targets": [2],
|
||||
"data":"play_count",
|
||||
"width": "10%",
|
||||
"className": "hidden-xs"
|
||||
"data":"platform",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData) {
|
||||
var transcode_dec = '';
|
||||
if (rowData['video_decision'] === 'transcode') {
|
||||
transcode_dec = '<span class="transcode-tooltip" data-toggle="tooltip" title="Transcode"><i class="fa fa-server fa-fw"></i></span> ';
|
||||
} else if (rowData['video_decision'] === 'copy') {
|
||||
transcode_dec = '<span class="transcode-tooltip" data-toggle="tooltip" title="Direct Stream"><i class="fa fa-video-camera fa-fw"></i></span> ';
|
||||
} else if (rowData['video_decision'] === 'direct play' || rowData['video_decision'] === '') {
|
||||
transcode_dec = '<span class="transcode-tooltip" data-toggle="tooltip" title="Direct Play"><i class="fa fa-play-circle fa-fw"></i></span> ';
|
||||
}
|
||||
$(td).html('<div><a href="#" data-target="#info-modal" data-toggle="modal"><div style="float: left;">' + transcode_dec + ' ' + cellData + '</div></a></div>');
|
||||
} else {
|
||||
$(td).html('n/a');
|
||||
}
|
||||
},
|
||||
"width": "15%",
|
||||
"className": "no-wrap hidden-md hidden-sm hidden-xs modal-control"
|
||||
},
|
||||
{
|
||||
"targets": [3],
|
||||
"data":"platform",
|
||||
"width": "15%",
|
||||
"className": "hidden-xs"
|
||||
"data":"last_watched",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData !== '') {
|
||||
var media_type = '';
|
||||
var thumb_popover = ''
|
||||
if (rowData['media_type'] === 'movie') {
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Movie"><i class="fa fa-film fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=80&height=120&fallback=poster" data-height="120">' + cellData + '</span>'
|
||||
$(td).html('<div class="history-title"><a href="info?source=history&item_id=' + rowData['id'] + '"><div style="float: left;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else if (rowData['media_type'] === 'episode') {
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Episode"><i class="fa fa-television fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=80&height=120&fallback=poster" data-height="120">' + cellData + '</span>'
|
||||
$(td).html('<div class="history-title"><a href="info?source=history&item_id=' + rowData['id'] + '"><div style="float: left;" >' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else if (rowData['media_type'] === 'track') {
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Track"><i class="fa fa-music fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=80&height=80&fallback=poster" data-height="80">' + cellData + '</span>'
|
||||
$(td).html('<div class="history-title"><a href="info?source=history&item_id=' + rowData['id'] + '"><div style="float: left;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else if (rowData['media_type']) {
|
||||
$(td).html('<a href="info?item_id=' + rowData['id'] + '">' + cellData + '</a>');
|
||||
} else {
|
||||
$(td).html('n/a');
|
||||
}
|
||||
}
|
||||
},
|
||||
"className": "hidden-sm hidden-xs"
|
||||
},
|
||||
{
|
||||
"targets": [4],
|
||||
"data":"last_watched",
|
||||
"width": "30%",
|
||||
"className": "hidden-sm hidden-xs"
|
||||
}
|
||||
"data":"play_count",
|
||||
"searchable": false,
|
||||
"width": "10%"
|
||||
}
|
||||
],
|
||||
"drawCallback": function (settings) {
|
||||
// Jump to top of page
|
||||
// $('html,body').scrollTop(0);
|
||||
$('#ajaxMsg').fadeOut();
|
||||
|
||||
// Create the tooltips.
|
||||
$('.transcode-tooltip').tooltip();
|
||||
$('.media-type-tooltip').tooltip();
|
||||
$('.watched-tooltip').tooltip();
|
||||
$('.thumb-tooltip').popover({
|
||||
html: true,
|
||||
trigger: 'hover',
|
||||
placement: 'right',
|
||||
content: function () {
|
||||
return '<div style="background-image: url(' + $(this).data('img') + '); width: 80px; height: ' + $(this).data('height') + 'px;" />';
|
||||
}
|
||||
});
|
||||
|
||||
},
|
||||
"preDrawCallback": function(settings) {
|
||||
var msg = "<div class='msg'><i class='fa fa-refresh fa-spin'></i> Fetching rows...</div>";
|
||||
@@ -83,6 +133,25 @@ $('#user_ip_table').on('mouseenter', 'td.modal-control span', function () {
|
||||
});
|
||||
|
||||
$('#user_ip_table').on('click', 'td.modal-control', function () {
|
||||
var tr = $(this).parents('tr');
|
||||
var row = user_ip_table.row(tr);
|
||||
var rowData = row.data();
|
||||
|
||||
function showStreamDetails() {
|
||||
$.ajax({
|
||||
url: 'get_stream_data',
|
||||
data: { row_id: rowData['id'], user: rowData['friendly_name'] },
|
||||
cache: false,
|
||||
async: true,
|
||||
complete: function (xhr, status) {
|
||||
$("#info-modal").html(xhr.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
showStreamDetails();
|
||||
});
|
||||
|
||||
$('#user_ip_table').on('click', 'td.modal-control-ip', function () {
|
||||
var tr = $(this).parents('tr');
|
||||
var row = user_ip_table.row( tr );
|
||||
var rowData = row.data();
|
||||
|
@@ -1,3 +1,5 @@
|
||||
var users_to_purge = [];
|
||||
|
||||
users_list_table_options = {
|
||||
"language": {
|
||||
"search": "Search: ",
|
||||
@@ -11,43 +13,62 @@ users_list_table_options = {
|
||||
"processing": false,
|
||||
"serverSide": true,
|
||||
"pageLength": 10,
|
||||
"order": [ 1, 'asc'],
|
||||
"order": [ 2, 'asc'],
|
||||
"autoWidth": true,
|
||||
"stateSave": true,
|
||||
"pagingType": "bootstrap",
|
||||
"columnDefs": [
|
||||
{
|
||||
"targets": [0],
|
||||
"data": "thumb",
|
||||
"data": null,
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
$(td).html('<div class="edit-user-toggles"><button class="btn btn-xs btn-warning" data-id="' + rowData['user_id'] + '" data-toggle="button"><i class="fa fa-eraser fa-fw"></i> Purge</button>   ' +
|
||||
'<input type="checkbox" id="do_notify-' + rowData['user_id'] + '" name="do_notify" value="1" ' + rowData['do_notify'] + '><label class="edit-tooltip" for="do_notify-' + rowData['user_id'] + '" data-toggle="tooltip" title="Toggle Notifications"><i class="fa fa-bell fa-lg fa-fw"></i></label> ' +
|
||||
'<input type="checkbox" id="keep_history-' + rowData['user_id'] + '" name="keep_history" value="1" ' + rowData['keep_history'] + '><label class="edit-tooltip" for="keep_history-' + rowData['user_id'] + '" data-toggle="tooltip" title="Toggle History"><i class="fa fa-history fa-lg fa-fw"></i></label> ');
|
||||
// Show/hide user currently doesn't work
|
||||
//'<input type="checkbox" id="show_hide-' + rowData['user_id'] + '" name="show_hide" value="1" checked><label class="edit-tooltip" for="show_hide-' + rowData['user_id'] + '" data-toggle="tooltip" title="Show/Hide User"><i class="fa fa-eye fa-lg fa-fw"></i></label>');
|
||||
},
|
||||
"width": "7%",
|
||||
"className": "edit-control no-wrap hidden",
|
||||
"searchable": false,
|
||||
"orderable": false
|
||||
},
|
||||
{
|
||||
"targets": [1],
|
||||
"data": "user_thumb",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData === '') {
|
||||
$(td).html('<img src="interfaces/default/images/gravatar-default-80x80.png" alt="User Logo"/>');
|
||||
$(td).html('<a href="user?user_id=' + rowData['user_id'] + '"><div class="users-poster-face" style="background-image: url(interfaces/default/images/gravatar-default-80x80.png);"></div></a>');
|
||||
} else {
|
||||
$(td).html('<img src="' + cellData + '" alt="User Logo"/>');
|
||||
$(td).html('<a href="user?user_id=' + rowData['user_id'] + '"><div class="users-poster-face" style="background-image: url(' + cellData + ');"></div></a>');
|
||||
}
|
||||
},
|
||||
"orderable": false,
|
||||
"searchable": false,
|
||||
"className": "users-poster-face",
|
||||
"width": "40px"
|
||||
"width": "5%",
|
||||
"className": "users-thumbs"
|
||||
},
|
||||
{
|
||||
"targets": [1],
|
||||
"targets": [2],
|
||||
"data": "friendly_name",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData !== '') {
|
||||
if (rowData['user_id'] > 0) {
|
||||
$(td).html('<a href="user?user_id=' + rowData['user_id'] + '">' + cellData + '</a>');
|
||||
$(td).html('<div class="edit-user-name" data-id="' + rowData['user_id'] + '"><a href="user?user_id=' + rowData['user_id'] + '">' + cellData + '</a>' +
|
||||
'<input type="text" class="hidden" value="' + cellData + '"></div>');
|
||||
} else {
|
||||
$(td).html('<a href="user?user=' + rowData['user'] + '">' + cellData + '</a>');
|
||||
$(td).html('<div class="edit-user-name" data-id="' + rowData['user_id'] + '"><a href="user?user=' + rowData['user'] + '">' + cellData + '</a>' +
|
||||
'<input type="text" class="hidden" value="' + cellData + '"></div>');
|
||||
}
|
||||
} else {
|
||||
$(td).html(cellData);
|
||||
}
|
||||
},
|
||||
"width": "12%",
|
||||
"className": "edit-user-control no-wrap"
|
||||
},
|
||||
{
|
||||
"targets": [2],
|
||||
"targets": [3],
|
||||
"data": "last_seen",
|
||||
"render": function ( data, type, full ) {
|
||||
if (data) {
|
||||
@@ -57,24 +78,85 @@ users_list_table_options = {
|
||||
}
|
||||
},
|
||||
"searchable": false,
|
||||
"className": "hidden-xs",
|
||||
},
|
||||
{
|
||||
"targets": [3],
|
||||
"data": "ip_address",
|
||||
"render": function ( data, type, full ) {
|
||||
if (data) {
|
||||
return data;
|
||||
} else {
|
||||
return "n/a";
|
||||
}
|
||||
},
|
||||
"className": "hidden-xs",
|
||||
"width": "12%",
|
||||
"className": "no-wrap hidden-xs"
|
||||
},
|
||||
{
|
||||
"targets": [4],
|
||||
"data": "ip_address",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData) {
|
||||
if (isPrivateIP(cellData)) {
|
||||
if (cellData != '') {
|
||||
$(td).html(cellData);
|
||||
} else {
|
||||
$(td).html('n/a');
|
||||
}
|
||||
} else {
|
||||
$(td).html('<a href="javascript:void(0)" data-toggle="modal" data-target="#ip-info-modal"><span data-toggle="ip-tooltip" data-placement="left" title="IP Address Info" id="ip-info"><i class="fa fa-map-marker"></i></span> ' + cellData + '</a>');
|
||||
}
|
||||
} else {
|
||||
$(td).html('n/a');
|
||||
}
|
||||
},
|
||||
"width": "12%",
|
||||
"className": "no-wrap hidden-md hidden-sm hidden-xs modal-control-ip"
|
||||
},
|
||||
{
|
||||
"targets": [5],
|
||||
"data":"platform",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData) {
|
||||
var transcode_dec = '';
|
||||
if (rowData['video_decision'] === 'transcode') {
|
||||
transcode_dec = '<span class="transcode-tooltip" data-toggle="tooltip" title="Transcode"><i class="fa fa-server fa-fw"></i></span>';
|
||||
} else if (rowData['video_decision'] === 'copy') {
|
||||
transcode_dec = '<span class="transcode-tooltip" data-toggle="tooltip" title="Direct Stream"><i class="fa fa-video-camera fa-fw"></i></span>';
|
||||
} else if (rowData['video_decision'] === 'direct play' || rowData['video_decision'] === '') {
|
||||
transcode_dec = '<span class="transcode-tooltip" data-toggle="tooltip" title="Direct Play"><i class="fa fa-play-circle fa-fw"></i></span>';
|
||||
}
|
||||
$(td).html('<div><a href="#" data-target="#info-modal" data-toggle="modal"><div style="float: left;">' + transcode_dec + ' ' + cellData + '</div></a></div>');
|
||||
} else {
|
||||
$(td).html('n/a');
|
||||
}
|
||||
},
|
||||
"width": "12%",
|
||||
"className": "no-wrap hidden-md hidden-sm hidden-xs modal-control"
|
||||
},
|
||||
{
|
||||
"targets": [6],
|
||||
"data":"last_watched",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData !== '') {
|
||||
var media_type = '';
|
||||
var thumb_popover = ''
|
||||
if (rowData['media_type'] === 'movie') {
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Movie"><i class="fa fa-film fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=80&height=120&fallback=poster" data-height="120">' + cellData + '</span>'
|
||||
$(td).html('<div class="history-title"><a href="info?source=history&item_id=' + rowData['id'] + '"><div style="float: left;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else if (rowData['media_type'] === 'episode') {
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Episode"><i class="fa fa-television fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=80&height=120&fallback=poster" data-height="120">' + cellData + '</span>'
|
||||
$(td).html('<div class="history-title"><a href="info?source=history&item_id=' + rowData['id'] + '"><div style="float: left;" >' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else if (rowData['media_type'] === 'track') {
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Track"><i class="fa fa-music fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=80&height=80&fallback=poster" data-height="80">' + cellData + '</span>'
|
||||
$(td).html('<div class="history-title"><a href="info?source=history&item_id=' + rowData['id'] + '"><div style="float: left;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else if (rowData['media_type']) {
|
||||
$(td).html('<a href="info?item_id=' + rowData['id'] + '">' + cellData + '</a>');
|
||||
} else {
|
||||
$(td).html('n/a');
|
||||
}
|
||||
}
|
||||
},
|
||||
"width": "30%",
|
||||
"className": "hidden-sm hidden-xs"
|
||||
},
|
||||
{
|
||||
"targets": [7],
|
||||
"data": "plays",
|
||||
"searchable": false
|
||||
"searchable": false,
|
||||
"width": "10%"
|
||||
}
|
||||
|
||||
],
|
||||
@@ -82,9 +164,125 @@ users_list_table_options = {
|
||||
// Jump to top of page
|
||||
//$('html,body').scrollTop(0);
|
||||
$('#ajaxMsg').fadeOut();
|
||||
|
||||
// Create the tooltips.
|
||||
$('.purge-tooltip').tooltip();
|
||||
$('.edit-tooltip').tooltip();
|
||||
$('.transcode-tooltip').tooltip();
|
||||
$('.media-type-tooltip').tooltip();
|
||||
$('.watched-tooltip').tooltip();
|
||||
$('.thumb-tooltip').popover({
|
||||
html: true,
|
||||
trigger: 'hover',
|
||||
placement: 'right',
|
||||
content: function () {
|
||||
return '<div style="background-image: url(' + $(this).data('img') + '); width: 80px; height: ' + $(this).data('height') + 'px;" />';
|
||||
}
|
||||
});
|
||||
|
||||
if ($('#row-edit-mode').hasClass('active')) {
|
||||
$('.edit-control').each(function () {
|
||||
$(this).removeClass('hidden');
|
||||
});
|
||||
}
|
||||
},
|
||||
"preDrawCallback": function(settings) {
|
||||
var msg = "<div class='msg'><i class='fa fa-refresh fa-spin'></i> Fetching rows...</div>";
|
||||
showMsg(msg, false, false, 0)
|
||||
},
|
||||
"rowCallback": function (row, rowData) {
|
||||
if ($.inArray(rowData['user_id'], users_to_purge) !== -1) {
|
||||
$(row).find('button[data-id="' + rowData['user_id'] + '"]').toggleClass('btn-warning').toggleClass('btn-danger');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$('#users_list_table').on('click', 'td.modal-control', function () {
|
||||
var tr = $(this).parents('tr');
|
||||
var row = users_list_table.row(tr);
|
||||
var rowData = row.data();
|
||||
|
||||
function showStreamDetails() {
|
||||
$.ajax({
|
||||
url: 'get_stream_data',
|
||||
data: { row_id: rowData['id'], user: rowData['friendly_name'] },
|
||||
cache: false,
|
||||
async: true,
|
||||
complete: function (xhr, status) {
|
||||
$("#info-modal").html(xhr.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
showStreamDetails();
|
||||
});
|
||||
|
||||
$('#users_list_table').on('click', 'td.modal-control-ip', function () {
|
||||
var tr = $(this).parents('tr');
|
||||
var row = users_list_table.row(tr);
|
||||
var rowData = row.data();
|
||||
|
||||
function getUserLocation(ip_address) {
|
||||
if (isPrivateIP(ip_address)) {
|
||||
return "n/a"
|
||||
} else {
|
||||
$.ajax({
|
||||
url: 'get_ip_address_details',
|
||||
data: { ip_address: ip_address },
|
||||
async: true,
|
||||
complete: function (xhr, status) {
|
||||
$("#ip-info-modal").html(xhr.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getUserLocation(rowData['ip_address']);
|
||||
});
|
||||
|
||||
$('#users_list_table').on('change', 'td.edit-control > .edit-user-toggles > input, td.edit-user-control > .edit-user-name > input', function () {
|
||||
var tr = $(this).parents('tr');
|
||||
var row = users_list_table.row(tr);
|
||||
var rowData = row.data();
|
||||
|
||||
var do_notify = 0;
|
||||
var keep_history = 0;
|
||||
if ($('#do_notify-' + rowData['user_id']).is(':checked')) {
|
||||
do_notify = 1;
|
||||
}
|
||||
if ($('#keep_history-' + rowData['user_id']).is(':checked')) {
|
||||
keep_history = 1;
|
||||
}
|
||||
|
||||
friendly_name = tr.find('td.edit-user-control > .edit-user-name > input').val();
|
||||
|
||||
$.ajax({
|
||||
url: 'edit_user',
|
||||
data: {
|
||||
user_id: rowData['user_id'],
|
||||
friendly_name: friendly_name,
|
||||
do_notify: do_notify,
|
||||
keep_history: keep_history,
|
||||
thumb: rowData['user_thumb']
|
||||
},
|
||||
cache: false,
|
||||
async: true,
|
||||
success: function (data) {
|
||||
var msg = "User updated";
|
||||
showMsg(msg, false, true, 2000);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('#users_list_table').on('click', 'td.edit-control > .edit-user-toggles > button', function () {
|
||||
var tr = $(this).parents('tr');
|
||||
var row = users_list_table.row(tr);
|
||||
var rowData = row.data();
|
||||
|
||||
var index = $.inArray(rowData['user_id'], users_to_purge);
|
||||
if (index === -1) {
|
||||
users_to_purge.push(rowData['user_id']);
|
||||
} else {
|
||||
users_to_purge.splice(index, 1);
|
||||
}
|
||||
$(this).toggleClass('btn-warning').toggleClass('btn-danger');
|
||||
});
|
78
data/interfaces/default/library_stats.html
Normal file
78
data/interfaces/default/library_stats.html
Normal file
@@ -0,0 +1,78 @@
|
||||
<%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: library_stats.html
|
||||
Version: 0.1
|
||||
Variable names: data [array]
|
||||
|
||||
data[array_index] :: Usable parameters
|
||||
|
||||
data['type'] Returns the type of the library. Either 'movie', 'show', 'photo', or 'artist'.
|
||||
data['rows'] Returns an array containing stat data
|
||||
|
||||
data[array_index]['rows'] :: Usable parameters
|
||||
|
||||
title Returns the title of the library.
|
||||
thumb Returns the thumb of the library.
|
||||
count Returns the number of items in the library.
|
||||
count_type Returns the sorting type for the library
|
||||
|
||||
== Only if 'type' is 'show'
|
||||
episode_count Return the number of episodes in the library.
|
||||
episode_count_type Return the sorting type for the episodes.
|
||||
|
||||
== Only if 'type' is 'artist'
|
||||
album_count Return the number of episodes in the library.
|
||||
album_count_type Return the sorting type for the episodes.
|
||||
|
||||
DOCUMENTATION :: END
|
||||
</%doc>
|
||||
|
||||
% if data:
|
||||
<ul class="list-unstyled">
|
||||
% for library in data:
|
||||
<div class="home-platforms-instance">
|
||||
<li>
|
||||
<div class="home-platforms-instance-info">
|
||||
<div class="home-platforms-instance-name">
|
||||
<h4>${library['rows']['title']}</h4>
|
||||
</div>
|
||||
<div class="home-platforms-instance-playcount">
|
||||
<h5>${library['rows']['count_type']}</h5>
|
||||
<h3>${library['rows']['count']}</h3>
|
||||
<p> items</p>
|
||||
</div>
|
||||
% if library['type'] == 'show':
|
||||
<div class="home-platforms-instance-playcount" style="padding-left: 10px;">
|
||||
<h5>${library['rows']['episode_count_type']}</h5>
|
||||
<h3>${library['rows']['episode_count']}</h3>
|
||||
<p> items</p>
|
||||
</div>
|
||||
% endif
|
||||
% if library['type'] == 'artist':
|
||||
<div class="home-platforms-instance-playcount" style="padding-left: 10px;">
|
||||
<h5>${library['rows']['album_count_type']}</h5>
|
||||
<h3>${library['rows']['album_count']}</h3>
|
||||
<p> items</p>
|
||||
</div>
|
||||
% endif
|
||||
</div>
|
||||
% if library['rows']['thumb']:
|
||||
<div class="home-platforms-instance-poster">
|
||||
<div class="home-platforms-library-thumb" style="background-image: url(pms_image_proxy?img=${library['rows']['thumb']}&width=300&height=300&fallback=poster);"></div>
|
||||
</div>
|
||||
% else:
|
||||
<div class="home-platforms-instance-poster">
|
||||
<div class="home-platforms-library-thumb" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
||||
</div>
|
||||
% endif
|
||||
</li>
|
||||
</div>
|
||||
% endfor
|
||||
</ul>
|
||||
% else:
|
||||
<div class="text-muted">Unable to retrieve data from database. Please check your <a href="settings">settings</a>.
|
||||
</div><br>
|
||||
% endif
|
@@ -86,8 +86,9 @@ from plexpy import helpers
|
||||
|
||||
$(document).ready(function() {
|
||||
LoadPlexPyLogs();
|
||||
clearSearchButton('log_table', log_table);
|
||||
});
|
||||
|
||||
|
||||
function LoadPlexPyLogs() {
|
||||
log_table_options.ajax = {
|
||||
"url": "getLog"
|
||||
@@ -105,11 +106,13 @@ from plexpy import helpers
|
||||
$("#plexpy-logs-btn").click(function() {
|
||||
$("#clear-logs").show();
|
||||
LoadPlexPyLogs();
|
||||
clearSearchButton('log_table', log_table);
|
||||
});
|
||||
|
||||
$("#plex-logs-btn").click(function() {
|
||||
$("#clear-logs").hide();
|
||||
LoadPlexLogs();
|
||||
clearSearchButton('plex_log_table', plex_log_table);
|
||||
});
|
||||
|
||||
$("#clear-logs").click(function() {
|
||||
|
@@ -29,34 +29,50 @@ DOCUMENTATION :: END
|
||||
% for item in data:
|
||||
<div class="dashboard-recent-media-instance">
|
||||
<li>
|
||||
<div class="poster">
|
||||
% if item['type'] == 'season' or item['type'] == 'movie':
|
||||
<div class="poster-face">
|
||||
<a href="info?item_id=${item['rating_key']}">
|
||||
<img src="pms_image_proxy?img=${item['thumb']}&width=300&height=450&fallback=poster" class="poster-face">
|
||||
</a>
|
||||
% if item['type'] == 'season' or item['type'] == 'movie':
|
||||
<a href="info?item_id=${item['rating_key']}">
|
||||
<div class="dashboard-recent-media-poster">
|
||||
<div class="dashboard-recent-media-poster-face" style="background-image: url(pms_image_proxy?img=${item['thumb']}&width=300&height=450&fallback=poster);">
|
||||
<div class="dashboard-recent-media-overlay">
|
||||
<div class="dashboard-recent-media-overlay-text" id="added_at-${item['rating_key']}">
|
||||
<script>
|
||||
$('#added_at-${item['rating_key']}').text('Added ' + moment(${item['added_at']}, "X").fromNow())
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
% elif item['type'] == 'album':
|
||||
<div class="cover-face">
|
||||
<img src="pms_image_proxy?img=${item['thumb']}&width=300&height=300&fallback=cover" class="cover-face">
|
||||
<div class="dashboard-recent-media-metacontainer">
|
||||
% if item['type'] == 'season':
|
||||
<h3>${item['parent_title']}</h3>
|
||||
<h3 class="text-muted">${item['title']}</h3>
|
||||
% elif item['type'] == 'movie':
|
||||
<h3>${item['title']}</h3>
|
||||
<h3 class="text-muted">${item['year']}</h3>
|
||||
% endif
|
||||
</div>
|
||||
</a>
|
||||
% elif item['type'] == 'album':
|
||||
<a href="info?item_id=${item['rating_key']}">
|
||||
<div class="dashboard-recent-media-cover">
|
||||
<div class="dashboard-recent-media-cover-face" style="background-image: url(pms_image_proxy?img=${item['thumb']}&width=300&height=300&fallback=cover);">
|
||||
<div class="dashboard-recent-media-overlay">
|
||||
<div class="dashboard-recent-media-overlay-text" id="added_at-${item['rating_key']}">
|
||||
<script>
|
||||
$('#added_at-${item['rating_key']}').text('Added ' + moment(${item['added_at']}, "X").fromNow())
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
% endif
|
||||
</div>
|
||||
<div class="dashboard-recent-media-metacontainer">
|
||||
% if item['type'] == 'season':
|
||||
<h3>${item['parent_title']} - ${item['title']}</h3>
|
||||
% elif item['type'] == 'album':
|
||||
<h3>${item['title']}</h3>
|
||||
% elif item['type'] == 'movie':
|
||||
<h3>${item['title']} (${item['year']})</h3>
|
||||
% endif
|
||||
<div class="text-muted" id="added_at-${item['rating_key']}">${item['added_at']}</div>
|
||||
</div>
|
||||
<div class="dashboard-recent-media-metacontainer">
|
||||
<h3>${item['parent_title']}</h3>
|
||||
<h3 class="text-muted">${item['title']}</h3>
|
||||
</div>
|
||||
</a>
|
||||
% endif
|
||||
</li>
|
||||
</div>
|
||||
<script>
|
||||
$('#added_at-${item['rating_key']}').html('Added ' + moment(${item['added_at']}, "X").fromNow())
|
||||
</script>
|
||||
% endfor
|
||||
</ul>
|
||||
</div>
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<%inherit file="base.html"/>
|
||||
<%!
|
||||
import plexpy
|
||||
from plexpy import notifiers
|
||||
from plexpy import notifiers, common, versioncheck
|
||||
|
||||
available_notification_agents = notifiers.available_notification_agents()
|
||||
%>
|
||||
@@ -48,9 +48,11 @@ available_notification_agents = notifiers.available_notification_agents()
|
||||
<form action="configUpdate" method="post" class="form" id="configUpdate" data-parsley-validate>
|
||||
<div class="tab-content">
|
||||
<div role="tabpanel" class="tab-pane active" id="tabs-1">
|
||||
% if common.VERSION_NUMBER:
|
||||
<div class="padded-header">
|
||||
<h3>Software Updates</h3>
|
||||
<h3>Version ${common.VERSION_NUMBER} <small><a href="#changelog-modal" data-toggle="modal"><i class="fa fa-info-circle"></i> Changelog</a></small></h3>
|
||||
</div>
|
||||
% endif
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="check_github" name="check_github" value="1" ${config['check_github']}> Enable Updates
|
||||
@@ -58,7 +60,7 @@ available_notification_agents = notifiers.available_notification_agents()
|
||||
<p class="help-block">If you have Git installed, allow periodic checks for updates.</p>
|
||||
</div>
|
||||
% if plexpy.CURRENT_VERSION:
|
||||
<p>Current version: ${plexpy.CURRENT_VERSION}</p>
|
||||
<p class="help-block">Git hash: ${plexpy.CURRENT_VERSION}</p>
|
||||
% endif
|
||||
<div class="padded-header">
|
||||
<h3>Display Settings</h3>
|
||||
@@ -81,6 +83,36 @@ available_notification_agents = notifiers.available_notification_agents()
|
||||
</div>
|
||||
<p class="help-block">Set your preferred time format. <a href="javascript:void(0)" data-target="#dateTimeOptionsModal" data-toggle="modal">Click here</a> to see the parameter list.</p>
|
||||
</div>
|
||||
<div class="padded-header">
|
||||
<h3>Homepage Statistics</h3>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="home_stats_length">Time Frame</label>
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<input type="text" class="form-control" data-parsley-type="integer" id="home_stats_length" name="home_stats_length" value="${config['home_stats_length']}" size="3" data-parsley-min="0" data-parsley-trigger="change" data-parsley-errors-container="#home_stats_length_error" required>
|
||||
</div>
|
||||
<div id="home_stats_length_error" class="alert alert-danger settings-alert" role="alert"></div>
|
||||
</div>
|
||||
<p class="help-block">Specify the number of days for the statistics on the home page. Default is 30 days.</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="home_stats_count">Top Lists</label>
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<input type="text" class="form-control" data-parsley-type="integer" id="home_stats_count" name="home_stats_count" value="${config['home_stats_count']}" size="3" data-parsley-range="[0,10]" data-parsley-trigger="change" data-parsley-errors-container="#home_stats_count_error" required>
|
||||
</div>
|
||||
<div id="home_stats_count_error" class="alert alert-danger settings-alert" role="alert"></div>
|
||||
</div>
|
||||
<p class="help-block">Specify the number of items to show in the top lists for the statistics on the home page. Max is 10 items, default is 5 items, 0 to disable.</p>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="home_stats_type" name="home_stats_type" value="1" ${config['home_stats_type']}> Use play duration
|
||||
</label>
|
||||
<p class="help-block">Use play duration instead of play count to generate statistics.</p>
|
||||
</div>
|
||||
<p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p>
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane" id="tabs-2">
|
||||
@@ -101,8 +133,9 @@ available_notification_agents = notifiers.available_notification_agents()
|
||||
<label for="http_port">HTTP Port</label>
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<input type="text" class="form-control" data-parsley-type="integer" id="http_port" name="http_port" value="${config['http_port']}" data-parsley-trigger="change" required>
|
||||
<input type="text" class="form-control" data-parsley-type="integer" id="http_port" name="http_port" value="${config['http_port']}" data-parsley-trigger="change" data-parsley-errors-container="#http_port_error" required>
|
||||
</div>
|
||||
<div id="http_port_error" class="alert alert-danger settings-alert" role="alert"></div>
|
||||
</div>
|
||||
<p class="help-block">Port to bind web server to. Note that ports below 1024 may require root.</p>
|
||||
</div>
|
||||
@@ -202,8 +235,9 @@ available_notification_agents = notifiers.available_notification_agents()
|
||||
<label for="pms_port">Plex Port</label>
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<input data-parsley-type="integer" class="pms-settings form-control" type="text" id="pms_port" name="pms_port" value="${config['pms_port']}" size="30" data-parsley-trigger="change" required>
|
||||
<input data-parsley-type="integer" class="pms-settings form-control" type="text" id="pms_port" name="pms_port" value="${config['pms_port']}" size="30" data-parsley-trigger="change" data-parsley-errors-container="#pms_port_error" required>
|
||||
</div>
|
||||
<div id="pms_port_error" class="alert alert-danger settings-alert" role="alert"></div>
|
||||
</div>
|
||||
<p class="help-block">Port that Plex Media Server is listening on.</p>
|
||||
</div>
|
||||
@@ -219,9 +253,24 @@ available_notification_agents = notifiers.available_notification_agents()
|
||||
</label>
|
||||
<p class="help-block">Force PlexPy to connect to your Plex Server via SSL. Your server needs to have remote access enabled.</p>
|
||||
</div>
|
||||
<div class="padded-header">
|
||||
<h3>Plex Logs</h3>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="pms_logs_folder">Logs Folder</label>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="pms_logs_folder" name="pms_logs_folder" value="${config['pms_logs_folder']}" size="30" data-parsley-trigger="change">
|
||||
</div>
|
||||
</div>
|
||||
<p class="help-block">Set the folder where your Plex Server logs are. This is required if you enable IP logging.<br /><a href="https://support.plex.tv/hc/en-us/articles/200250417-Plex-Media-Server-Log-Files" target="_blank">Click here</a> for help.</p>
|
||||
</div>
|
||||
|
||||
<input type="hidden" id="pms_identifier" name="pms_identifier" value="${config['pms_identifier']}">
|
||||
|
||||
<input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully">
|
||||
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane" id="tabs-5">
|
||||
|
||||
@@ -233,12 +282,13 @@ available_notification_agents = notifiers.available_notification_agents()
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" id="pms_token" name="pms_token" value="${config['pms_token']}" data-parsley-trigger="change" required>
|
||||
<input type="text" class="form-control" id="pms_token" name="pms_token" value="${config['pms_token']}" data-parsley-trigger="change" data-parsley-errors-container="#pms_token_error" required>
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-form" type="button" data-toggle="modal" data-target="#pms-auth-modal">Fetch Token</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="pms_token_error" class="alert alert-danger settings-alert" role="alert"></div>
|
||||
</div>
|
||||
<p class="help-block">Token for Plex.tv authentication.</p>
|
||||
</div>
|
||||
@@ -250,8 +300,9 @@ available_notification_agents = notifiers.available_notification_agents()
|
||||
<label for="refresh_users_interval">User list Refresh Interval</label>
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<input type="text" class="form-control" data-parsley-type="integer" id="refresh_users_interval" name="refresh_users_interval" value="${config['refresh_users_interval']}" size="5" data-parsley-range="[1,24]" data-parsley-trigger="change" required>
|
||||
<input type="text" class="form-control" data-parsley-type="integer" id="refresh_users_interval" name="refresh_users_interval" value="${config['refresh_users_interval']}" size="5" data-parsley-range="[1,24]" data-parsley-trigger="change" data-parsley-errors-container="#refresh_users_interval_error" required>
|
||||
</div>
|
||||
<div id="refresh_users_interval_error" class="alert alert-danger settings-alert" role="alert"></div>
|
||||
</div>
|
||||
<p class="help-block">The interval (in hours) PlexPy will request an updated friends list from Plex.tv. 1 minimum, 24 maximum.</p>
|
||||
</div>
|
||||
@@ -271,36 +322,11 @@ available_notification_agents = notifiers.available_notification_agents()
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="pms_use_bif" name="pms_use_bif" value="1" ${config['pms_use_bif']}> Use BIF thumbs
|
||||
<input type="checkbox" id="pms_use_bif" name="pms_use_bif" value="1" ${config['pms_use_bif']}> Use video preview thumbnails (BIF)
|
||||
</label>
|
||||
<p class="help-block">If you have media indexing enabled on your server, use these on the activity pane.</p>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="home_stats_length">Homepage Statistics Time Frame</label>
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<input type="text" class="form-control" data-parsley-type="integer" id="home_stats_length" name="home_stats_length" value="${config['home_stats_length']}" size="3" data-parsley-min="0" data-parsley-trigger="change" required>
|
||||
</div>
|
||||
</div>
|
||||
<p class="help-block">Specify the number of days for the statistics on the home page. Default is 30 days.</p>
|
||||
</div>
|
||||
|
||||
<div class="padded-header">
|
||||
<h3>Plex Logs</h3>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="pms_logs_folder">Logs Folder</label>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="pms_logs_folder" name="pms_logs_folder" value="${config['pms_logs_folder']}" size="30" data-parsley-trigger="change">
|
||||
</div>
|
||||
</div>
|
||||
<p class="help-block">Set the folder where your Plex Server logs are. This is required if you enable IP logging.<br/><a href="https://support.plex.tv/hc/en-us/articles/200250417-Plex-Media-Server-Log-Files" target="_blank">Click here</a> for help.</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="padded-header">
|
||||
<h3>PlexWatch Import Tool</h3>
|
||||
</div>
|
||||
@@ -317,10 +343,11 @@ available_notification_agents = notifiers.available_notification_agents()
|
||||
<label for="monitoring_interval">Monitoring Interval</label>
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<input type="text" class="form-control" data-parsley-type="integer" id="monitoring_interval" name="monitoring_interval" value="${config['monitoring_interval']}" size="5" data-parsley-min="30" data-parsley-trigger="change" required>
|
||||
<input type="text" class="form-control" data-parsley-type="integer" id="monitoring_interval" name="monitoring_interval" value="${config['monitoring_interval']}" size="5" data-parsley-min="30" data-parsley-trigger="change" data-parsley-errors-container="#monitoring_interval_error" required>
|
||||
</div>
|
||||
<div id="monitoring_interval_error" class="alert alert-danger settings-alert" role="alert"></div>
|
||||
</div>
|
||||
<p class="help-block">The interval (in seconds) PlexPy will ping your Plex Server. Min 30 seconds, Recommended 60 seconds.</p>
|
||||
<p class="help-block">The interval (in seconds) PlexPy will ping your Plex Server. Min 30 seconds, recommended 60 seconds.</p>
|
||||
</div>
|
||||
|
||||
<div class="padded-header">
|
||||
@@ -333,11 +360,12 @@ available_notification_agents = notifiers.available_notification_agents()
|
||||
<p class="help-block">Keep records of all video items played from your Plex Media Server.</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="monitoring_interval">Ignore Interval</label>
|
||||
<label for="logging_ignore_interval">Ignore Interval</label>
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<input type="text" class="form-control" data-parsley-type="integer" id="logging_ignore_interval" name="logging_ignore_interval" value="${config['logging_ignore_interval']}" size="5" data-parsley-min="0" data-parsley-trigger="change" required>
|
||||
<input type="text" class="form-control" data-parsley-type="integer" id="logging_ignore_interval" name="logging_ignore_interval" value="${config['logging_ignore_interval']}" size="5" data-parsley-min="0" data-parsley-trigger="change" data-parsley-errors-container="#logging_ignore_interval_error" required>
|
||||
</div>
|
||||
<div id="logging_ignore_interval_error" class="alert alert-danger settings-alert" role="alert"></div>
|
||||
</div>
|
||||
<p class="help-block">The interval (in seconds) PlexPy will wait for a video item to be active before logging it. 0 to disable.</p>
|
||||
</div>
|
||||
@@ -369,8 +397,9 @@ available_notification_agents = notifiers.available_notification_agents()
|
||||
<label for="buffer_threshold">Buffer Threshold</label>
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<input type="text" class="form-control" data-parsley-type="integer" id="buffer_threshold" name="buffer_threshold" value="${config['buffer_threshold']}" data-parsley-range="[0,50]" data-parsley-trigger="change" required>
|
||||
<input type="text" class="form-control" data-parsley-type="integer" id="buffer_threshold" name="buffer_threshold" value="${config['buffer_threshold']}" data-parsley-range="[0,50]" data-parsley-trigger="change" data-parsley-errors-container="#buffer_threshold_error" required>
|
||||
</div>
|
||||
<div id="buffer_threshold_error" class="alert alert-danger settings-alert" role="alert"></div>
|
||||
</div>
|
||||
<p class="help-block">How many buffer events should we wait before triggering the first warning. Buffer events increment on each monitor ping if play state is buffering. 0 to disable buffer warnings.</p>
|
||||
</div>
|
||||
@@ -378,8 +407,9 @@ available_notification_agents = notifiers.available_notification_agents()
|
||||
<label for="buffer_wait">Buffer Wait</label>
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<input type="text" class="form-control" data-parsley-type="integer" id="buffer_wait" name="buffer_wait" value="${config['buffer_wait']}" data-parsley-min="0" data-parsley-trigger="change" required>
|
||||
<input type="text" class="form-control" data-parsley-type="integer" id="buffer_wait" name="buffer_wait" value="${config['buffer_wait']}" data-parsley-min="0" data-parsley-trigger="change" data-parsley-errors-container="#buffer_wait_error" required>
|
||||
</div>
|
||||
<div id="buffer_wait_error" class="alert alert-danger settings-alert" role="alert"></div>
|
||||
</div>
|
||||
<p class="help-block">The value (in seconds) PlexPy should wait before triggering the next buffer warning. 0 to always trigger.</p>
|
||||
</div>
|
||||
@@ -410,8 +440,9 @@ available_notification_agents = notifiers.available_notification_agents()
|
||||
<label for="notify_watched_percent">Watched Percent</label>
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<input type="text" class="form-control" data-parsley-type="integer" id="notify_watched_percent" name="notify_watched_percent" value="${config['notify_watched_percent']}" size="5" data-parsley-range="[50,95]" data-parsley-trigger="change" required>
|
||||
<input type="text" class="form-control" data-parsley-type="integer" id="notify_watched_percent" name="notify_watched_percent" value="${config['notify_watched_percent']}" size="5" data-parsley-range="[50,95]" data-parsley-trigger="change" data-parsley-errors-container="#notify_watched_percent_error" required>
|
||||
</div>
|
||||
<div id="notify_watched_percent_error" class="alert alert-danger settings-alert" role="alert"></div>
|
||||
</div>
|
||||
<p class="help-block">Set the progress percentage of when a watched notification should be triggered. Minimum 50, Maximum 95.</p>
|
||||
</div>
|
||||
@@ -541,7 +572,7 @@ available_notification_agents = notifiers.available_notification_agents()
|
||||
<h3>Notification Agents</h3>
|
||||
</div>
|
||||
<p class="help-block">
|
||||
Toggle the desired notification options by clicking the bolt icon and configure it by selecting the settings icon to the right.
|
||||
Toggle the desired notification options by clicking the bell icon and configure it by clicking the settings icon to the right.
|
||||
</p>
|
||||
<br/>
|
||||
<ul class="stacked-configs list-unstyled">
|
||||
@@ -549,9 +580,9 @@ available_notification_agents = notifiers.available_notification_agents()
|
||||
<li>
|
||||
<span>
|
||||
% if agent['on_play'] or agent['on_stop'] or agent['on_pause'] or agent['on_resume'] or agent['on_buffer'] or agent['on_watched']:
|
||||
<a href="javascript:void(0)" data-target="#notification-triggers-modal" data-id="${agent['id']}" class="toggle-notification-triggers-modal toggle-left active" data-toggle="modal"><i class="fa fa-lg fa-flash"></i></a>
|
||||
<a href="javascript:void(0)" data-target="#notification-triggers-modal" data-id="${agent['id']}" class="toggle-notification-triggers-modal toggle-left active" data-toggle="modal"><i class="fa fa-lg fa-bell"></i></a>
|
||||
% else:
|
||||
<a href="javascript:void(0)" data-target="#notification-triggers-modal" data-id="${agent['id']}" class="toggle-notification-triggers-modal toggle-left" data-toggle="modal"><i class="fa fa-lg fa-flash"></i></a>
|
||||
<a href="javascript:void(0)" data-target="#notification-triggers-modal" data-id="${agent['id']}" class="toggle-notification-triggers-modal toggle-left" data-toggle="modal"><i class="fa fa-lg fa-bell"></i></a>
|
||||
% endif
|
||||
${agent['name']}
|
||||
% if agent['has_config']:
|
||||
@@ -810,10 +841,18 @@ available_notification_agents = notifiers.available_notification_agents()
|
||||
<td width="150"><strong>{season_num}</strong></td>
|
||||
<td>The season number for the media item if item is episode.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="150"><strong>{season_num00}</strong></td>
|
||||
<td>The two digit season number.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="150"><strong>{episode_num}</strong></td>
|
||||
<td>The episode number for the media item if item is episode.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="150"><strong>{episode_num00}</strong></td>
|
||||
<td>The two digit episode number.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="150"><strong>{rating}</strong></td>
|
||||
<td>The rating (out of 10) for the item.</td>
|
||||
@@ -822,6 +861,10 @@ available_notification_agents = notifiers.available_notification_agents()
|
||||
<td width="150"><strong>{duration}</strong></td>
|
||||
<td>The duration (in minutes) for the item.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="150"><strong>{stream_duration}</strong></td>
|
||||
<td>The stream duration (in minutes) for the item.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="150"><strong>{progress}</strong></td>
|
||||
<td>The last reported offset (in minutes) for the item.</td>
|
||||
@@ -875,6 +918,21 @@ available_notification_agents = notifiers.available_notification_agents()
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="changelog-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="changelog-modal">
|
||||
<div class="modal-dialog" role="document">
|
||||
<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">Changelog</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
${versioncheck.read_changelog()}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
@@ -1023,7 +1081,9 @@ $(document).ready(function() {
|
||||
headers: {'Content-Type': 'application/xml; charset=utf-8',
|
||||
'X-Plex-Device-Name': 'PlexPy',
|
||||
'X-Plex-Product': 'PlexPy',
|
||||
'X-Plex-Version': 'v0.1 dev',
|
||||
'X-Plex-Version': '${common.VERSION_NUMBER}',
|
||||
'X-Plex-Platform': '${common.PLATFORM}',
|
||||
'X-Plex-Platform-Version': '${common.PLATFORM_VERSION}',
|
||||
'X-Plex-Client-Identifier': '${config['pms_uuid']}',
|
||||
'Authorization': 'Basic ' + btoa($("#pms_username").val() + ':' + $("#pms_password").val())
|
||||
},
|
||||
@@ -1109,7 +1169,7 @@ $(document).ready(function() {
|
||||
|
||||
function checkLogsPath() {
|
||||
if ($("#pms_logs_folder").val() == '') {
|
||||
$("#debugLogCheck").html("You must first define your Plex Server Logs folder path under the Extra Settings tab.");
|
||||
$("#debugLogCheck").html("You must first define your Plex Server Logs folder path under the Plex Media Server tab.");
|
||||
$("#ip_logging_enable").attr("disabled", true);
|
||||
} else {
|
||||
$("#ip_logging_enable").attr("disabled", false);
|
||||
@@ -1120,4 +1180,4 @@ $(document).ready(function() {
|
||||
var accordion = new Accordion($('#accordion'), false);
|
||||
});
|
||||
</script>
|
||||
</%def>
|
||||
</%def>
|
||||
|
@@ -54,8 +54,9 @@ DOCUMENTATION :: END
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<h4><strong>Stream Details</strong></h4>
|
||||
% if data['media_type'] != 'track':
|
||||
<h5>Video</h5>
|
||||
<ul>
|
||||
<ul class="list-unstyled">
|
||||
% if data['transcode_video_dec'] != 'direct play':
|
||||
<li>Stream Type: <strong>${data['transcode_video_dec']}</strong></li>
|
||||
<li>Video Resolution: <strong>${data['transcode_height']}p</strong></li>
|
||||
@@ -74,8 +75,9 @@ DOCUMENTATION :: END
|
||||
<li>Video Height: <strong>${data['height']}</strong></li>
|
||||
% endif
|
||||
</ul>
|
||||
% endif
|
||||
<h5>Audio</h5>
|
||||
<ul>
|
||||
<ul class="list-unstyled">
|
||||
% if data['transcode_audio_dec'] != 'direct play':
|
||||
<li>Stream Type: <strong>${data['transcode_audio_dec']}</strong></li>
|
||||
<li>Audio Codec: <strong>${data['transcode_audio_codec']}</strong></li>
|
||||
@@ -89,23 +91,27 @@ DOCUMENTATION :: END
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<h4><strong>Media Source Details</strong></h4>
|
||||
<ul>
|
||||
<ul class="list-unstyled">
|
||||
<li>Container: <strong>${data['container']}</strong></li>
|
||||
% if data['media_type'] != 'track':
|
||||
<li>Resolution: <strong>${data['height']}p</strong></li>
|
||||
% endif
|
||||
<li>Bitrate: <strong>${data['bitrate']} kbps</strong></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
% if data['media_type'] != 'track':
|
||||
<h4><strong>Video Source Details</strong></h4>
|
||||
<ul>
|
||||
<ul class="list-unstyled">
|
||||
<li>Width: <strong>${data['width']}</strong></li>
|
||||
<li>Height: <strong>${data['height']}</strong></li>
|
||||
<li>Aspect Ratio: <strong>${data['aspect_ratio']}</strong></li>
|
||||
<li>Video Frame Rate: <strong>${data['video_framerate']}</strong></li>
|
||||
<li>Video Codec: <strong>${data['video_codec']}</strong></li>
|
||||
</ul>
|
||||
% endif
|
||||
<h4><strong>Audio Source Details</strong></h4>
|
||||
<ul>
|
||||
<ul class="list-unstyled">
|
||||
<li>Audio Codec: <strong>${data['audio_codec']}</strong></li>
|
||||
<li>Audio Channels: <strong>${data['audio_channels']}</strong></li>
|
||||
</ul>
|
||||
|
@@ -57,9 +57,10 @@
|
||||
"url": "get_sync"
|
||||
}
|
||||
sync_table = $('#sync_table').DataTable(sync_table_options);
|
||||
|
||||
var colvis = new $.fn.dataTable.ColVis( sync_table, { buttonText: 'Select columns', buttonClass: 'btn btn-dark' } );
|
||||
var colvis = new $.fn.dataTable.ColVis( sync_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark' } );
|
||||
$( colvis.button() ).appendTo('div.colvis-button-bar');
|
||||
|
||||
clearSearchButton('sync_table', sync_table);
|
||||
});
|
||||
</script>
|
||||
</%def>
|
||||
|
@@ -40,8 +40,7 @@ from plexpy import helpers
|
||||
<div class="col-md-12">
|
||||
<div class="table-card-back">
|
||||
<div class="user-info-wrapper">
|
||||
<div class="user-info-poster-face" id="user-gravatar">
|
||||
<img id="user-profile-thumb" src="${data['thumb']}" height="80px" width="80px">
|
||||
<div class="user-info-poster-face" id="user-gravatar" style="background-image: url(${data['thumb']});">
|
||||
</div>
|
||||
<div class="user-info-username">
|
||||
<span class="set-username">${data['friendly_name']}</span> <span id="edit-user-tooltip" data-target="tooltip" title="Edit user details"><a href="#" data-toggle="modal" data-target="#edit-user-modal" id="toggle-edit-user-modal"><i class="fa fa-pencil"></i></a></span>
|
||||
@@ -103,7 +102,7 @@ from plexpy import helpers
|
||||
<div class="col-md-12">
|
||||
<div class="table-card-header">
|
||||
<div class="header-bar">
|
||||
<span><i class="fa fa-history"></i> Recently watched</span>
|
||||
<span><i class="fa fa-history"></i> Recently Watched</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-card-back">
|
||||
@@ -131,17 +130,15 @@ from plexpy import helpers
|
||||
<table id="user_ip_table" class="display" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th align="left">Last seen</th>
|
||||
<th align="left">Last Seen</th>
|
||||
<th align="left">IP Address</th>
|
||||
<th align="left">Play Count</th>
|
||||
<th align="left">Platform (Last Seen)</th>
|
||||
<th align="left">Last Platform</th>
|
||||
<th align="left">Last Watched</th>
|
||||
<th align="left">Play Count</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
<div id="ip-info-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="ip-info-modal">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -156,30 +153,35 @@ from plexpy import helpers
|
||||
<span class="set-username">${data['friendly_name']}</span>
|
||||
</strong></span>
|
||||
</div>
|
||||
<div class="colvis-button-bar hidden-xs" id="button-bar-history">
|
||||
<div class="button-bar">
|
||||
<div class="colvis-button-bar hidden-xs" id="button-bar-history"></div>
|
||||
<button class="btn btn-danger btn-edit" data-toggle="button" aria-pressed="false" autocomplete="off" id="row-edit-mode">
|
||||
<i class="fa fa-trash-o"></i> Delete mode
|
||||
</button>
|
||||
<div class="alert alert-danger alert-edit" role="alert" id="row-edit-mode-alert"><i class="fa fa-exclamation-triangle"></i> Select rows to delete. Data is deleted upon exiting delete mode.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-card-back">
|
||||
<table class="display" id="history_table" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th align='left' id="delete">Delete</th>
|
||||
<th align='left' id="time">Time</th>
|
||||
<th align='left' id="friendly_name">User</th>
|
||||
<th align='left' id="platform">Platform</th>
|
||||
<th align='left' id="ip_address">IP Address</th>
|
||||
<th align='left' id="platform">Platform</th>
|
||||
<th align='left' id="title">Title</th>
|
||||
<th align='left' id="started">Started</th>
|
||||
<th align='left' id="paused_counter">Paused</th>
|
||||
<th align='left' id="stopped">Stopped</th>
|
||||
<th align='left' id="duration">Duration</th>
|
||||
<th align='left' id="percent_complete">Watched</th>
|
||||
<th align='left' id="percent_complete"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div id="info-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="info-modal"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -222,6 +224,28 @@ from plexpy import helpers
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" id="info-modal" tabindex="-1" role="dialog" aria-labelledby="info-modal">
|
||||
</div>
|
||||
<div class="modal fade" id="ip-info-modal" tabindex="-1" role="dialog" aria-labelledby="ip-info-modal">
|
||||
</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 Delete</h4>
|
||||
</div>
|
||||
<div class="modal-body" style="text-align: center;">
|
||||
<p>Are you REALLY sure you want to delete <strong><span id="deleteCount"></span></strong> history item(s)?</p>
|
||||
<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-delete">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer></footer>
|
||||
</%def>
|
||||
@@ -236,6 +260,33 @@ from plexpy import helpers
|
||||
<script src="interfaces/default/js/tables/user_ips.js"></script>
|
||||
<script src="interfaces/default/js/tables/sync_table.js"></script>
|
||||
<script>
|
||||
function recentlyWatched() {
|
||||
var widthVal = $('body').find("#user-recently-watched").width();
|
||||
var tmp = (widthVal-32) / 180;
|
||||
|
||||
if (tmp > 0) {
|
||||
containerSize = parseInt(tmp);
|
||||
} else {
|
||||
containerSize = 1;
|
||||
}
|
||||
|
||||
% if data['user_id']:
|
||||
var user_id = ${data['user_id']};
|
||||
% else:
|
||||
var user_id = null;
|
||||
% endif
|
||||
|
||||
// Populate recently watched
|
||||
$.ajax({
|
||||
url: 'get_user_recently_watched',
|
||||
async: true,
|
||||
data: { user_id: user_id, user: '${data['username']}', limit: containerSize },
|
||||
complete: function(xhr, status) {
|
||||
$("#user-recently-watched").html(xhr.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
|
||||
% if data['user_id']:
|
||||
@@ -266,16 +317,6 @@ from plexpy import helpers
|
||||
}
|
||||
});
|
||||
|
||||
// Populate recently watched
|
||||
$.ajax({
|
||||
url: 'get_user_recently_watched',
|
||||
async: true,
|
||||
data: { user_id: user_id, user: '${data['username']}' },
|
||||
complete: function(xhr, status) {
|
||||
$("#user-recently-watched").html(xhr.responseText);
|
||||
}
|
||||
});
|
||||
|
||||
$( "#history-tab-btn" ).one( "click", function() {
|
||||
// Build watch history table
|
||||
history_table_options.ajax = {
|
||||
@@ -289,10 +330,12 @@ from plexpy import helpers
|
||||
}
|
||||
}
|
||||
history_table = $('#history_table').DataTable(history_table_options);
|
||||
history_table.column(1).visible(false);
|
||||
history_table.column(2).visible(false);
|
||||
|
||||
var colvis = new $.fn.dataTable.ColVis(history_table, { buttonText: 'Select columns', buttonClass: 'btn btn-dark' });
|
||||
var colvis = new $.fn.dataTable.ColVis(history_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark', exclude: [0, 10] });
|
||||
$(colvis.button()).appendTo('#button-bar-history');
|
||||
|
||||
clearSearchButton('history_table', history_table);
|
||||
});
|
||||
|
||||
$( "#ip-tab-btn" ).one( "click", function() {
|
||||
@@ -308,6 +351,8 @@ from plexpy import helpers
|
||||
}
|
||||
}
|
||||
user_ip_table = $('#user_ip_table').DataTable(user_ip_table_options);
|
||||
|
||||
clearSearchButton('user_ip_table', user_ip_table);
|
||||
});
|
||||
|
||||
$( "#sync-tab-btn" ).one( "click", function() {
|
||||
@@ -320,10 +365,12 @@ from plexpy import helpers
|
||||
}
|
||||
}
|
||||
sync_table = $('#sync_table').DataTable(sync_table_options);
|
||||
history_table.column(1).visible(false);
|
||||
sync_table.column(1).visible(false);
|
||||
|
||||
var colvis_sync = new $.fn.dataTable.ColVis( sync_table, { buttonText: 'Select columns', buttonClass: 'btn btn-dark' } );
|
||||
var colvis_sync = new $.fn.dataTable.ColVis( sync_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark' } );
|
||||
$( colvis_sync.button() ).appendTo('#button-bar-sync');
|
||||
|
||||
clearSearchButton('sync_table', sync_table);
|
||||
});
|
||||
|
||||
// Load edit user modal
|
||||
@@ -339,6 +386,48 @@ from plexpy import helpers
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('#row-edit-mode').on('click', function() {
|
||||
$('#row-edit-mode-alert').fadeIn(200);
|
||||
|
||||
if ($(this).hasClass('active')) {
|
||||
if (history_to_delete.length > 0) {
|
||||
$('#deleteCount').text(history_to_delete.length);
|
||||
$('#confirm-modal').modal();
|
||||
$('#confirm-modal').one('click', '#confirm-delete', function () {
|
||||
for (var i = 0; i < history_to_delete.length; i++) {
|
||||
$.ajax({
|
||||
url: 'delete_history_rows',
|
||||
data: { row_id: history_to_delete[i] },
|
||||
async: true,
|
||||
success: function (data) {
|
||||
var msg = "History deleted";
|
||||
showMsg(msg, false, true, 2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
history_table.draw();
|
||||
});
|
||||
}
|
||||
|
||||
$('.delete-control').each(function () {
|
||||
$(this).addClass('hidden');
|
||||
$('#row-edit-mode-alert').fadeOut(200);
|
||||
});
|
||||
|
||||
} else {
|
||||
history_to_delete = [];
|
||||
$('.delete-control').each(function() {
|
||||
$(this).find('button.btn-danger').toggleClass('btn-warning').toggleClass('btn-danger');
|
||||
$(this).removeClass('hidden');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
recentlyWatched();
|
||||
$(window).resize(function() {
|
||||
recentlyWatched();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</%def>
|
||||
|
@@ -34,7 +34,7 @@ DOCUMENTATION :: END
|
||||
</div>
|
||||
</ul>
|
||||
<script>
|
||||
$("#user-platform-image-${a['result_id']}").html("<img class='user-platforms-instance-poster' src='" + getPlatformImagePath('${a['platform_type']}') + "'>");
|
||||
$("#user-platform-image-${a['result_id']}").html("<div class='user-platforms-instance-box' style='background-image: url(" + getPlatformImagePath('${a['platform_type']}') + ");'>");
|
||||
</script>
|
||||
% endfor
|
||||
% else:
|
||||
|
@@ -18,6 +18,7 @@ time Returns the last watched time of the media.
|
||||
title Returns the name of the movie or episode.
|
||||
|
||||
== Only if 'type' is 'episode ==
|
||||
parent_title Returns the name of the TV Show a season belongs too.
|
||||
parent_index Returns the season number.
|
||||
index Returns the episode number.
|
||||
|
||||
@@ -31,28 +32,54 @@ DOCUMENTATION :: END
|
||||
<div class="dashboard-recent-media-row">
|
||||
<ul class="dashboard-recent-media list-unstyled">
|
||||
% for item in data:
|
||||
<div class="dashboard-recent-media-instance">
|
||||
<li>
|
||||
<div class="poster">
|
||||
<div class="poster-face">
|
||||
<a href="info?source=history&item_id=${item['row_id']}">
|
||||
<img src="pms_image_proxy?img=${item['thumb']}&width=300&height=450&fallback=poster" class="poster-face">
|
||||
</a>
|
||||
<li>
|
||||
% if item['type'] == 'episode' or item['type'] == 'movie':
|
||||
<a href="info?source=history&item_id=${item['row_id']}">
|
||||
<div class="dashboard-recent-media-poster">
|
||||
<div class="dashboard-recent-media-poster-face" style="background-image: url(pms_image_proxy?img=${item['thumb']}&width=300&height=450&fallback=poster);">
|
||||
<div class="dashboard-recent-media-overlay">
|
||||
<div class="dashboard-recent-media-overlay-text" id="time-${item['time']}">
|
||||
<script>
|
||||
$('#time-${item['time']}').text('Watched ' + moment(${item['time']}, "X").fromNow())
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dashboard-recent-media-metacontainer">
|
||||
% if item['type'] == 'episode':
|
||||
<h3>${item['grandparent_title']}</h3>
|
||||
<h3>${item['title']}</h3>
|
||||
<h3 class="text-muted">S${item['parent_index']} · E${item['index']}</h3>
|
||||
% elif item['type'] == 'movie':
|
||||
<h3>${item['title']}</h3>
|
||||
<h3 class="text-muted">${item['year']}</h3>
|
||||
% endif
|
||||
<div class="text-muted" id="time-${item['time']}">
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
% elif item['type'] == 'track':
|
||||
<a href="info?source=history&item_id=${item['row_id']}">
|
||||
<div class="dashboard-recent-media-cover">
|
||||
<div class="dashboard-recent-media-cover-face" style="background-image: url(pms_image_proxy?img=${item['thumb']}&width=300&height=300&fallback=cover);">
|
||||
<div class="dashboard-recent-media-overlay">
|
||||
<div class="dashboard-recent-media-overlay-text" id="time-${item['time']}">
|
||||
<script>
|
||||
$('#time-${item['time']}').text('Watched ' + moment(${item['time']}, "X").fromNow())
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dashboard-recent-media-metacontainer">
|
||||
% if item['type'] == 'episode':
|
||||
<h3>Season ${item['parentIndex']}, Episode ${item['index']}</h3>
|
||||
% elif item['type'] == 'movie':
|
||||
<h3>${item['title']} (${item['year']})</h3>
|
||||
% endif
|
||||
<div class="text-muted" id="time-${item['time']}">${item['time']}</div>
|
||||
<h3>${item['grandparent_title']}</h3>
|
||||
<h3>${item['title']}</h3>
|
||||
<h3 class="text-muted">${item['parent_title']}</h3>
|
||||
</div>
|
||||
</li>
|
||||
</div>
|
||||
<script>
|
||||
$('#time-${item['time']}').html('Watched ' + moment(${item['time']}, "X").fromNow())
|
||||
</script>
|
||||
</a>
|
||||
% endif
|
||||
</li>
|
||||
% endfor
|
||||
</ul>
|
||||
</div>
|
||||
|
@@ -30,8 +30,8 @@ DOCUMENTATION :: END
|
||||
<h4>Last ${a['query_days']} days</h4>
|
||||
% endif
|
||||
<h3>${a['total_plays']}</h3>
|
||||
|
||||
<p>plays</p>
|
||||
<h3><strong>/</strong></h3>
|
||||
<span id="total-time-${a['query_days']}"></span>
|
||||
</div>
|
||||
</li>
|
||||
|
@@ -12,23 +12,53 @@
|
||||
<span><i class="fa fa-group"></i> All Users</span>
|
||||
</div>
|
||||
<div class="button-bar">
|
||||
<button class="btn btn-dark" id="refresh-users-list"><i class="fa fa-refresh"></i> Refresh users</button>
|
||||
<button class="btn btn-dark refresh-users-button" id="refresh-users-list"><i class="fa fa-refresh"></i> Refresh users</button>
|
||||
<button class="btn btn-danger btn-edit" data-toggle="button" aria-pressed="false" autocomplete="off" id="row-edit-mode">
|
||||
<i class="fa fa-pencil"></i> Edit mode
|
||||
</button> 
|
||||
<div class="alert alert-danger alert-edit" role="alert" id="row-edit-mode-alert"><i class="fa fa-exclamation-triangle"></i> Select users to purge. Data is purged upon exiting edit mode.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='table-card-back'>
|
||||
<table id="users_list_table" class="display" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th align="left" id="edit_row">Edit</th>
|
||||
<th align="right" id="avatar"></th>
|
||||
<th align="left" id="friendly_name">User</th>
|
||||
<th align="left" id="last_seen">Last Seen</th>
|
||||
<th align="left" id="last_known_ip">Last Known IP</th>
|
||||
<th align="left" id="last_platform">Last Platform</th>
|
||||
<th align="left" id="last_watched">Last Watched</th>
|
||||
<th align="left" id="total_plays">Total Plays</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="modal fade" id="info-modal" tabindex="-1" role="dialog" aria-labelledby="info-modal">
|
||||
</div>
|
||||
<div class="modal fade" id="ip-info-modal" tabindex="-1" role="dialog" aria-labelledby="ip-info-modal">
|
||||
</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 Purge</h4>
|
||||
</div>
|
||||
<div class="modal-body" style="text-align: center;">
|
||||
<p>Are you REALLY sure you want to purge all history for the following users:</p>
|
||||
<ul id="users-to-delete" class="list-unstyled"></ul>
|
||||
<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-purge">Purge</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -41,15 +71,75 @@
|
||||
<script src="interfaces/default/js/moment-with-locale.js"></script>
|
||||
<script src="interfaces/default/js/tables/users.js"></script>
|
||||
<script>
|
||||
users_list_table_options.ajax = {
|
||||
"url": "get_user_list",
|
||||
type: "post",
|
||||
data: function ( d ) {
|
||||
return { 'json_data': JSON.stringify( d ) };
|
||||
$(document).ready(function () {
|
||||
users_list_table_options.ajax = {
|
||||
"url": "get_user_list",
|
||||
type: "post",
|
||||
data: function ( d ) {
|
||||
return { 'json_data': JSON.stringify( d ) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var users_list_table = $('#users_list_table').DataTable(users_list_table_options);
|
||||
users_list_table = $('#users_list_table').DataTable(users_list_table_options);
|
||||
|
||||
clearSearchButton('users_list_table', users_list_table);
|
||||
|
||||
$('#row-edit-mode').on('click', function () {
|
||||
$('#row-edit-mode-alert').fadeIn(200);
|
||||
$('#users-to-delete').html('');
|
||||
|
||||
if ($(this).hasClass('active')) {
|
||||
if (users_to_purge.length > 0) {
|
||||
$('.edit-control').each(function () {
|
||||
$(this).find('button.btn-danger').toggleClass('btn-warning').toggleClass('btn-danger');
|
||||
});
|
||||
|
||||
for (var i = 0; i < users_to_purge.length; i++) {
|
||||
$('#users-to-delete').append('<li>' + $('div[data-id=' + users_to_purge[i] + '] > input').val() + '</li>');
|
||||
}
|
||||
$('#confirm-modal').modal();
|
||||
$('#confirm-modal').one('click', '#confirm-purge', function () {
|
||||
for (var i = 0; i < users_to_purge.length; i++) {
|
||||
$.ajax({
|
||||
url: 'delete_all_user_history',
|
||||
data: { user_id: users_to_purge[i] },
|
||||
cache: false,
|
||||
async: true,
|
||||
success: function (data) {
|
||||
var msg = "User history purged";
|
||||
showMsg(msg, false, true, 2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
users_list_table.draw();
|
||||
});
|
||||
}
|
||||
|
||||
$('.edit-control').each(function () {
|
||||
$(this).addClass('hidden');
|
||||
$('#row-edit-mode-alert').fadeOut(200);
|
||||
});
|
||||
$('.edit-user-control > .edit-user-name').each(function () {
|
||||
a = $(this).children('a');
|
||||
input = $(this).children('input');
|
||||
a.text(input.val());
|
||||
a.removeClass('hidden');
|
||||
input.addClass('hidden');
|
||||
});
|
||||
|
||||
} else {
|
||||
users_to_purge = [];
|
||||
$('.edit-control').each(function () {
|
||||
$(this).find('button.btn-danger').toggleClass('btn-warning').toggleClass('btn-danger');
|
||||
$(this).removeClass('hidden');
|
||||
});
|
||||
$('.edit-user-control > .edit-user-name').each(function () {
|
||||
$(this).children('a').addClass('hidden');
|
||||
$(this).children('input').removeClass('hidden');
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$("#refresh-users-list").click(function() {
|
||||
$.ajax({
|
||||
@@ -65,4 +155,4 @@
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</%def>
|
||||
</%def>
|
@@ -1,6 +1,6 @@
|
||||
<%
|
||||
import plexpy
|
||||
from plexpy import version
|
||||
from plexpy import common
|
||||
%>
|
||||
|
||||
<!doctype html>
|
||||
@@ -355,7 +355,9 @@ from plexpy import version
|
||||
headers: {'Content-Type': 'application/xml; charset=utf-8',
|
||||
'X-Plex-Device-Name': 'PlexPy',
|
||||
'X-Plex-Product': 'PlexPy',
|
||||
'X-Plex-Version': 'v0.1 dev',
|
||||
'X-Plex-Version': '${common.VERSION_NUMBER}',
|
||||
'X-Plex-Platform': '${common.PLATFORM}',
|
||||
'X-Plex-Platform-Version': '${common.PLATFORM_VERSION}',
|
||||
'X-Plex-Client-Identifier': '${config['pms_uuid']}',
|
||||
'Authorization': 'Basic ' + btoa($("#pms_username").val() + ':' + $("#pms_password").val())
|
||||
},
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# PlexPy - Automatic music downloader for SABnzbd
|
||||
# PlexPy - Stats for Plex Media Server usage
|
||||
#
|
||||
# Service Unit file for systemd system manager
|
||||
#
|
||||
@@ -53,7 +53,7 @@
|
||||
# graphical.target equates to runlevel 5 (multi-user X11 graphical mode)
|
||||
|
||||
[Unit]
|
||||
Description=PlexPy - Automatic music downloader for SABnzbd
|
||||
Description=PlexPy - Stats for Plex Media Server usage
|
||||
|
||||
[Service]
|
||||
ExecStart=/home/sabnzbd/plexpy/PlexPy.py --daemon --config /etc/plexpy/plexpy.ini --datadir /home/sabnzbd/.plexpy --nolaunch --quiet
|
||||
|
80
init-scripts/init.freenas
Normal file
80
init-scripts/init.freenas
Normal file
@@ -0,0 +1,80 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# PROVIDE: plexpy
|
||||
# REQUIRE: DAEMON sabnzbd
|
||||
# KEYWORD: shutdown
|
||||
#
|
||||
# Add the following lines to /etc/rc.conf.local or /etc/rc.conf
|
||||
# to enable this service:
|
||||
#
|
||||
# plexpy_enable (bool): Set to NO by default.
|
||||
# Set it to YES to enable it.
|
||||
# plexpy_user: The user account PlexPy daemon runs as what
|
||||
# you want it to be. It uses '_sabnzbd' user by
|
||||
# default. Do not sets it as empty or it will run
|
||||
# as root.
|
||||
# plexpy_dir: Directory where PlexPy lives.
|
||||
# Default: /usr/local/plexpy
|
||||
# plexpy_chdir: Change to this directory before running PlexPy.
|
||||
# Default is same as plexpy_dir.
|
||||
# plexpy_pid: The name of the pidfile to create.
|
||||
# Default is plexpy.pid in plexpy_dir.
|
||||
PATH="/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin"
|
||||
|
||||
. /etc/rc.subr
|
||||
|
||||
name="plexpy"
|
||||
rcvar=${name}_enable
|
||||
|
||||
load_rc_config ${name}
|
||||
|
||||
: ${plexpy_enable:="NO"}
|
||||
: ${plexpy_user:="_sabnzbd"}
|
||||
: ${plexpy_dir:="/usr/local/share/plexpy"}
|
||||
: ${plexpy_chdir:="${plexpy_dir}"}
|
||||
: ${plexpy_pid:="${plexpy_dir}/plexpy.pid"}
|
||||
|
||||
status_cmd="${name}_status"
|
||||
stop_cmd="${name}_stop"
|
||||
|
||||
command="/usr/sbin/daemon"
|
||||
command_args="-f -p ${plexpy_pid} python2 ${plexpy_dir}/PlexPy.py ${plexpy_flags} --quiet --nolaunch"
|
||||
|
||||
# Ensure user is root when running this script.
|
||||
if [ `id -u` != "0" ]; then
|
||||
echo "Oops, you should be root before running this!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
verify_plexpy_pid() {
|
||||
# Make sure the pid corresponds to the PlexPy process.
|
||||
if [ -f ${plexpy_pid} ]; then
|
||||
pid=`cat ${plexpy_pid} 2>/dev/null`
|
||||
ps -p ${pid} | grep -q "python2 ${plexpy_dir}/PlexPy.py"
|
||||
return $?
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
# Try to stop PlexPy cleanly by calling shutdown over http.
|
||||
plexpy_stop() {
|
||||
echo "Stopping $name."
|
||||
verify_plexpy_pid
|
||||
if [ -n "${pid}" ]; then
|
||||
kill ${pid}
|
||||
wait_for_pids ${pid}
|
||||
echo "Stopped."
|
||||
fi
|
||||
}
|
||||
|
||||
plexpy_status() {
|
||||
verify_plexpy_pid
|
||||
if [ -n "${pid}" ]; then
|
||||
echo "$name is running as ${pid}."
|
||||
else
|
||||
echo "$name is not running."
|
||||
fi
|
||||
}
|
||||
|
||||
run_rc_command "$1"
|
67
init-scripts/init.opensuse.systemd
Normal file
67
init-scripts/init.opensuse.systemd
Normal file
@@ -0,0 +1,67 @@
|
||||
# PlexPy - Stats for Plex Media Server usage
|
||||
#
|
||||
# Service Unit file for systemd system manager
|
||||
#
|
||||
# INSTALLATION NOTES
|
||||
#
|
||||
# 1. Rename this file as you want, ensuring that it ends in .service
|
||||
# e.g. 'plexpy.service'
|
||||
#
|
||||
# 2. Adjust configuration settings as required. More details in the
|
||||
# "CONFIGURATION NOTES" section shown below.
|
||||
#
|
||||
# 3. Copy this file into your systemd service unit directory, which is
|
||||
# often '/lib/systemd/system'.
|
||||
#
|
||||
# 4. Create any files/directories that you specified back in step #2.
|
||||
# e.g. '/opt/plexpy.ini'
|
||||
# '/opt/plexpy'
|
||||
#
|
||||
# 5. Enable boot-time autostart with the following commands:
|
||||
# systemctl daemon-reload
|
||||
# systemctl enable plexpy.service
|
||||
#
|
||||
# 6. Start now with the following command:
|
||||
# systemctl start plexpy.service
|
||||
#
|
||||
# 7. If troubleshooting startup-errors, start by checking permissions
|
||||
# and ownership on the files/directories that you created in step #4.
|
||||
#
|
||||
#
|
||||
# CONFIGURATION NOTES
|
||||
#
|
||||
# - The example settings in this file assume that:
|
||||
# 1. You will run PlexPy as user/group: plex.users
|
||||
# 2. You will either have PlexPy installed as a subdirectory
|
||||
# under '/opt', or that you will have a symlink under
|
||||
# '/opt' pointing to your PlexPy install dir.
|
||||
# 3. Your PlexPy data directory and configuration file can be
|
||||
# in separate locations from your PlexPy install dir, to
|
||||
# simplify updates. However, in the example below they are in the
|
||||
# PlexPy install dir.
|
||||
#
|
||||
# - Option names (e.g. ExecStart=, Type=) appear to be case-sensitive)
|
||||
#
|
||||
# - Adjust ExecStart= to point to:
|
||||
# 1. Your PlexPy executable,
|
||||
# 2. Your config file (recommended is to put it somewhere in /etc)
|
||||
# 3. Your datadir (recommended is to NOT put it in your PlexPy exec dir)
|
||||
#
|
||||
# - Adjust User= and Group= to the user/group you want PlexPy to run as.
|
||||
#
|
||||
# - WantedBy= specifies which target (i.e. runlevel) to start PlexPy for.
|
||||
# multi-user.target equates to runlevel 3 (multi-user text mode)
|
||||
# graphical.target equates to runlevel 5 (multi-user X11 graphical mode)
|
||||
|
||||
[Unit]
|
||||
Description=PlexPy - Stats for Plex Media Server usage
|
||||
|
||||
[Service]
|
||||
ExecStart=/opt/plexpy/PlexPy.py --daemon --config /opt/plexpy/config.ini --datadir /opt/plexpy --nolaunch --quiet
|
||||
GuessMainPID=no
|
||||
Type=forking
|
||||
User=plex
|
||||
Group=users
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
66
init-scripts/init.ubuntu.systemd
Normal file
66
init-scripts/init.ubuntu.systemd
Normal file
@@ -0,0 +1,66 @@
|
||||
# PlexPy - Stats for Plex Media Server usage
|
||||
#
|
||||
# Service Unit file for systemd system manager
|
||||
#
|
||||
# INSTALLATION NOTES
|
||||
#
|
||||
# 1. Rename this file as you want, ensuring that it ends in .service
|
||||
# e.g. 'plexpy.service'
|
||||
#
|
||||
# 2. Adjust configuration settings as required. More details in the
|
||||
# "CONFIGURATION NOTES" section shown below.
|
||||
#
|
||||
# 3. Copy this file into your systemd service unit directory, which is
|
||||
# often '/lib/systemd/system'.
|
||||
#
|
||||
# 4. Create any files/directories that you specified back in step #2.
|
||||
# e.g. '/etc/plexpy/plexpy.ini'
|
||||
# '/home/sabnzbd/.plexpy'
|
||||
#
|
||||
# 5. Enable boot-time autostart with the following commands:
|
||||
# systemctl daemon-reload
|
||||
# systemctl enable plexpy.service
|
||||
#
|
||||
# 6. Start now with the following command:
|
||||
# systemctl start plexpy.service
|
||||
#
|
||||
# 7. If troubleshooting startup-errors, start by checking permissions
|
||||
# and ownership on the files/directories that you created in step #4.
|
||||
#
|
||||
#
|
||||
# CONFIGURATION NOTES
|
||||
#
|
||||
# - The example settings in this file assume that:
|
||||
# 1. You will run PlexPy as user/group: sabnzbd.sabnzbd
|
||||
# 2. You will either have PlexPy installed as a subdirectory
|
||||
# under '~sabnzbd', or that you will have a symlink under
|
||||
# '~/sabnzbd' pointing to your PlexPy install dir.
|
||||
# 3. Your PlexPy data directory and configuration file will be
|
||||
# in separate locations from your PlexPy install dir, to
|
||||
# simplify updates.
|
||||
#
|
||||
# - Option names (e.g. ExecStart=, Type=) appear to be case-sensitive)
|
||||
#
|
||||
# - Adjust ExecStart= to point to:
|
||||
# 1. Your PlexPy executable,
|
||||
# 2. Your config file (recommended is to put it somewhere in /etc)
|
||||
# 3. Your datadir (recommended is to NOT put it in your PlexPy exec dir)
|
||||
#
|
||||
# - Adjust User= and Group= to the user/group you want PlexPy to run as.
|
||||
#
|
||||
# - WantedBy= specifies which target (i.e. runlevel) to start PlexPy for.
|
||||
# multi-user.target equates to runlevel 3 (multi-user text mode)
|
||||
# graphical.target equates to runlevel 5 (multi-user X11 graphical mode)
|
||||
|
||||
[Unit]
|
||||
Description=PlexPy - Stats for Plex Media Server usage
|
||||
|
||||
[Service]
|
||||
ExecStart=/opt/plexpy/PlexPy.py --quiet --daemon --nolaunch --config /opt/plexpy/config.ini --datadir /opt/plexpy
|
||||
GuessMainPID=no
|
||||
Type=forking
|
||||
User=plexpy
|
||||
Group=nogroup
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
@@ -1,4 +1,4 @@
|
||||
# plexpy - Automatic music downloader
|
||||
# plexpy
|
||||
#
|
||||
# This is a session/user job. Install this file into /usr/share/upstart/sessions
|
||||
# if plexpy is installed system wide, and into $XDG_CONFIG_HOME/upstart if
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# This file is part of PlexPy.
|
||||
# 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
|
||||
@@ -377,7 +377,7 @@ def dbcheck():
|
||||
'title TEXT, parent_title TEXT, grandparent_title TEXT, full_title TEXT, media_index INTEGER, '
|
||||
'parent_media_index INTEGER, thumb TEXT, parent_thumb TEXT, grandparent_thumb TEXT, art TEXT, media_type TEXT, '
|
||||
'year INTEGER, originally_available_at TEXT, added_at INTEGER, updated_at INTEGER, last_viewed_at INTEGER, '
|
||||
'content_rating TEXT, summary TEXT, rating TEXT, duration INTEGER DEFAULT 0, guid TEXT, '
|
||||
'content_rating TEXT, summary TEXT, tagline TEXT, rating TEXT, duration INTEGER DEFAULT 0, guid TEXT, '
|
||||
'directors TEXT, writers TEXT, actors TEXT, genres TEXT, studio TEXT)'
|
||||
''
|
||||
)
|
||||
@@ -517,15 +517,24 @@ def dbcheck():
|
||||
'ALTER TABLE sessions ADD COLUMN transcode_height INTEGER'
|
||||
)
|
||||
|
||||
# Upgrade sessions table from earlier versions
|
||||
# Upgrade session_history_metadata table from earlier versions
|
||||
try:
|
||||
c_db.execute('SELECT full_title from session_history_metadata')
|
||||
except sqlite3.OperationalError:
|
||||
logger.debug(u"Altering database. Updating database table sessions.")
|
||||
logger.debug(u"Altering database. Updating database table session_history_metadata.")
|
||||
c_db.execute(
|
||||
'ALTER TABLE session_history_metadata ADD COLUMN full_title TEXT'
|
||||
)
|
||||
|
||||
# Upgrade session_history_metadata table from earlier versions
|
||||
try:
|
||||
c_db.execute('SELECT tagline from session_history_metadata')
|
||||
except sqlite3.OperationalError:
|
||||
logger.debug(u"Altering database. Updating database table session_history_metadata.")
|
||||
c_db.execute(
|
||||
'ALTER TABLE session_history_metadata ADD COLUMN tagline TEXT'
|
||||
)
|
||||
|
||||
# notify_log table :: This is a table which logs notifications sent
|
||||
c_db.execute(
|
||||
'CREATE TABLE IF NOT EXISTS notify_log (id INTEGER PRIMARY KEY AUTOINCREMENT, '
|
||||
@@ -588,6 +597,12 @@ def dbcheck():
|
||||
'ALTER TABLE users ADD COLUMN custom_avatar_url TEXT'
|
||||
)
|
||||
|
||||
# Add "Local" user to database as default unauthenticated user.
|
||||
result = c_db.execute('SELECT id FROM users WHERE username = "Local"')
|
||||
if not result.fetchone():
|
||||
logger.debug(u'User "Local" does not exist. Adding user.')
|
||||
c_db.execute('INSERT INTO users (user_id, username) VALUES (0, "Local")')
|
||||
|
||||
conn_db.commit()
|
||||
c_db.close()
|
||||
|
||||
|
@@ -19,14 +19,17 @@ Created on Aug 1, 2011
|
||||
@author: Michael
|
||||
'''
|
||||
import platform
|
||||
import operator
|
||||
import os
|
||||
import re
|
||||
|
||||
from plexpy import version
|
||||
|
||||
# Identify Our Application
|
||||
USER_AGENT = 'PlexPy/-' + version.PLEXPY_VERSION + ' (' + platform.system() + ' ' + platform.release() + ')'
|
||||
USER_AGENT = 'PlexPy/-' + version.PLEXPY_VERSION + ' v' + version.PLEXPY_RELEASE_VERSION + ' (' + platform.system() + \
|
||||
' ' + platform.release() + ')'
|
||||
|
||||
PLATFORM = platform.system()
|
||||
PLATFORM_VERSION = platform.release()
|
||||
BRANCH = version.PLEXPY_VERSION
|
||||
VERSION_NUMBER = version.PLEXPY_RELEASE_VERSION
|
||||
|
||||
# Notification Types
|
||||
NOTIFY_STARTED = 1
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import plexpy.logger
|
||||
import plexpy.logger
|
||||
import itertools
|
||||
import os
|
||||
import re
|
||||
@@ -83,6 +83,8 @@ _CONFIG_DEFINITIONS = {
|
||||
'GROWL_ON_BUFFER': (int, 'Growl', 0),
|
||||
'GROWL_ON_WATCHED': (int, 'Growl', 0),
|
||||
'HOME_STATS_LENGTH': (int, 'General', 30),
|
||||
'HOME_STATS_TYPE': (int, 'General', 0),
|
||||
'HOME_STATS_COUNT': (int, 'General', 5),
|
||||
'HTTPS_CERT': (str, 'General', ''),
|
||||
'HTTPS_KEY': (str, 'General', ''),
|
||||
'HTTP_HOST': (str, 'General', '0.0.0.0'),
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# This file is part of PlexPy.
|
||||
# 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
|
||||
@@ -36,6 +36,13 @@ class DataFactory(object):
|
||||
'session_history.player',
|
||||
'session_history.ip_address',
|
||||
'session_history_metadata.full_title as full_title',
|
||||
'session_history_metadata.thumb',
|
||||
'session_history_metadata.parent_thumb',
|
||||
'session_history_metadata.grandparent_thumb',
|
||||
'session_history_metadata.media_index',
|
||||
'session_history_metadata.parent_media_index',
|
||||
'session_history_metadata.parent_title',
|
||||
'session_history_metadata.year',
|
||||
'session_history.started',
|
||||
'session_history.paused_counter',
|
||||
'session_history.stopped',
|
||||
@@ -48,6 +55,7 @@ class DataFactory(object):
|
||||
(CASE WHEN session_history_metadata.duration IS NULL THEN 1.0 ELSE \
|
||||
session_history_metadata.duration * 1.0 END) * 100) as percent_complete',
|
||||
'session_history.grandparent_rating_key as grandparent_rating_key',
|
||||
'session_history.parent_rating_key as parent_rating_key',
|
||||
'session_history.rating_key as rating_key',
|
||||
'session_history.user',
|
||||
'session_history_metadata.media_type',
|
||||
@@ -81,18 +89,31 @@ class DataFactory(object):
|
||||
|
||||
rows = []
|
||||
for item in history:
|
||||
if item["media_type"] == 'episode' and item["parent_thumb"]:
|
||||
thumb = item["parent_thumb"]
|
||||
elif item["media_type"] == 'episode':
|
||||
thumb = item["grandparent_thumb"]
|
||||
else:
|
||||
thumb = item["thumb"]
|
||||
|
||||
row = {"id": item['id'],
|
||||
"date": item['date'],
|
||||
"friendly_name": item['friendly_name'],
|
||||
"player": item["player"],
|
||||
"ip_address": item["ip_address"],
|
||||
"full_title": item["full_title"],
|
||||
"thumb": thumb,
|
||||
"media_index": item["media_index"],
|
||||
"parent_media_index": item["parent_media_index"],
|
||||
"parent_title": item["parent_title"],
|
||||
"year": item["year"],
|
||||
"started": item["started"],
|
||||
"paused_counter": item["paused_counter"],
|
||||
"stopped": item["stopped"],
|
||||
"duration": item["duration"],
|
||||
"percent_complete": item["percent_complete"],
|
||||
"grandparent_rating_key": item["grandparent_rating_key"],
|
||||
"parent_rating_key": item["parent_rating_key"],
|
||||
"rating_key": item["rating_key"],
|
||||
"user": item["user"],
|
||||
"media_type": item["media_type"],
|
||||
@@ -110,14 +131,19 @@ class DataFactory(object):
|
||||
|
||||
return dict
|
||||
|
||||
def get_home_stats(self, time_range='30'):
|
||||
def get_home_stats(self, time_range='30', stat_type='0', stat_count='5'):
|
||||
monitor_db = database.MonitorDatabase()
|
||||
|
||||
if not time_range.isdigit():
|
||||
time_range = '30'
|
||||
|
||||
sort_type = 'total_plays' if stat_type == '0' else 'total_duration'
|
||||
|
||||
if not time_range.isdigit():
|
||||
stat_count = '5'
|
||||
|
||||
# This actually determines the output order in the home page
|
||||
stats_queries = ["top_tv", "popular_tv", "top_movies", "top_users", "top_platforms"]
|
||||
stats_queries = ["top_tv", "popular_tv", "top_movies", "popular_movies", "top_users", "top_platforms", "last_watched"]
|
||||
home_stats = []
|
||||
|
||||
for stat in stats_queries:
|
||||
@@ -127,6 +153,10 @@ class DataFactory(object):
|
||||
query = 'SELECT session_history_metadata.id, ' \
|
||||
'session_history_metadata.grandparent_title, ' \
|
||||
'COUNT(session_history_metadata.grandparent_title) as total_plays, ' \
|
||||
'SUM(case when session_history.stopped > 0 ' \
|
||||
'then (session_history.stopped - session_history.started) ' \
|
||||
' - (case when session_history.paused_counter is NULL then 0 else session_history.paused_counter end) ' \
|
||||
'else 0 end) as total_duration, ' \
|
||||
'session_history_metadata.grandparent_rating_key, ' \
|
||||
'MAX(session_history.started) as last_watch,' \
|
||||
'session_history_metadata.grandparent_thumb ' \
|
||||
@@ -136,7 +166,7 @@ class DataFactory(object):
|
||||
'>= datetime("now", "-%s days", "localtime") ' \
|
||||
'AND session_history_metadata.media_type = "episode" ' \
|
||||
'GROUP BY session_history_metadata.grandparent_title ' \
|
||||
'ORDER BY total_plays DESC LIMIT 10' % time_range
|
||||
'ORDER BY %s DESC LIMIT %s' % (time_range, sort_type, stat_count)
|
||||
result = monitor_db.select(query)
|
||||
except:
|
||||
logger.warn("Unable to execute database query.")
|
||||
@@ -145,10 +175,11 @@ class DataFactory(object):
|
||||
for item in result:
|
||||
row = {'title': item[1],
|
||||
'total_plays': item[2],
|
||||
'total_duration': item[3],
|
||||
'users_watched': '',
|
||||
'rating_key': item[3],
|
||||
'last_play': item[4],
|
||||
'grandparent_thumb': item[5],
|
||||
'rating_key': item[4],
|
||||
'last_play': item[5],
|
||||
'grandparent_thumb': item[6],
|
||||
'thumb': '',
|
||||
'user': '',
|
||||
'friendly_name': '',
|
||||
@@ -159,48 +190,9 @@ class DataFactory(object):
|
||||
top_tv.append(row)
|
||||
|
||||
home_stats.append({'stat_id': stat,
|
||||
'stat_type': sort_type,
|
||||
'rows': top_tv})
|
||||
|
||||
elif 'top_movies' in stat:
|
||||
top_movies = []
|
||||
try:
|
||||
query = 'SELECT session_history_metadata.id, ' \
|
||||
'session_history_metadata.full_title, ' \
|
||||
'COUNT(session_history_metadata.full_title) as total_plays, ' \
|
||||
'session_history_metadata.rating_key, ' \
|
||||
'MAX(session_history.started) as last_watch,' \
|
||||
'session_history_metadata.thumb ' \
|
||||
'FROM session_history_metadata ' \
|
||||
'JOIN session_history on session_history_metadata.id = session_history.id ' \
|
||||
'WHERE datetime(session_history.stopped, "unixepoch", "localtime") ' \
|
||||
'>= datetime("now", "-%s days", "localtime") ' \
|
||||
'AND session_history_metadata.media_type = "movie" ' \
|
||||
'GROUP BY session_history_metadata.full_title ' \
|
||||
'ORDER BY total_plays DESC LIMIT 10' % time_range
|
||||
result = monitor_db.select(query)
|
||||
except:
|
||||
logger.warn("Unable to execute database query.")
|
||||
return None
|
||||
|
||||
for item in result:
|
||||
row = {'title': item[1],
|
||||
'total_plays': item[2],
|
||||
'users_watched': '',
|
||||
'rating_key': item[3],
|
||||
'last_play': item[4],
|
||||
'grandparent_thumb': '',
|
||||
'thumb': item[5],
|
||||
'user': '',
|
||||
'friendly_name': '',
|
||||
'platform_type': '',
|
||||
'platform': '',
|
||||
'row_id': item[0]
|
||||
}
|
||||
top_movies.append(row)
|
||||
|
||||
home_stats.append({'stat_id': stat,
|
||||
'rows': top_movies})
|
||||
|
||||
elif 'popular_tv' in stat:
|
||||
popular_tv = []
|
||||
try:
|
||||
@@ -218,7 +210,7 @@ class DataFactory(object):
|
||||
'AND session_history_metadata.media_type = "episode" ' \
|
||||
'GROUP BY session_history_metadata.grandparent_title ' \
|
||||
'ORDER BY users_watched DESC, total_plays DESC ' \
|
||||
'LIMIT 10' % time_range
|
||||
'LIMIT %s' % (time_range, stat_count)
|
||||
result = monitor_db.select(query)
|
||||
except:
|
||||
logger.warn("Unable to execute database query.")
|
||||
@@ -243,6 +235,94 @@ class DataFactory(object):
|
||||
home_stats.append({'stat_id': stat,
|
||||
'rows': popular_tv})
|
||||
|
||||
elif 'top_movies' in stat:
|
||||
top_movies = []
|
||||
try:
|
||||
query = 'SELECT session_history_metadata.id, ' \
|
||||
'session_history_metadata.full_title, ' \
|
||||
'COUNT(session_history_metadata.full_title) as total_plays, ' \
|
||||
'SUM(case when session_history.stopped > 0 ' \
|
||||
'then (session_history.stopped - session_history.started) ' \
|
||||
' - (case when session_history.paused_counter is NULL then 0 else session_history.paused_counter end) ' \
|
||||
'else 0 end) as total_duration, ' \
|
||||
'session_history_metadata.rating_key, ' \
|
||||
'MAX(session_history.started) as last_watch,' \
|
||||
'session_history_metadata.thumb ' \
|
||||
'FROM session_history_metadata ' \
|
||||
'JOIN session_history on session_history_metadata.id = session_history.id ' \
|
||||
'WHERE datetime(session_history.stopped, "unixepoch", "localtime") ' \
|
||||
'>= datetime("now", "-%s days", "localtime") ' \
|
||||
'AND session_history_metadata.media_type = "movie" ' \
|
||||
'GROUP BY session_history_metadata.full_title ' \
|
||||
'ORDER BY %s DESC LIMIT %s' % (time_range, sort_type, stat_count)
|
||||
result = monitor_db.select(query)
|
||||
except:
|
||||
logger.warn("Unable to execute database query.")
|
||||
return None
|
||||
|
||||
for item in result:
|
||||
row = {'title': item[1],
|
||||
'total_plays': item[2],
|
||||
'total_duration': item[3],
|
||||
'users_watched': '',
|
||||
'rating_key': item[4],
|
||||
'last_play': item[5],
|
||||
'grandparent_thumb': '',
|
||||
'thumb': item[6],
|
||||
'user': '',
|
||||
'friendly_name': '',
|
||||
'platform_type': '',
|
||||
'platform': '',
|
||||
'row_id': item[0]
|
||||
}
|
||||
top_movies.append(row)
|
||||
|
||||
home_stats.append({'stat_id': stat,
|
||||
'stat_type': sort_type,
|
||||
'rows': top_movies})
|
||||
|
||||
elif 'popular_movies' in stat:
|
||||
popular_movies = []
|
||||
try:
|
||||
query = 'SELECT session_history_metadata.id, ' \
|
||||
'session_history_metadata.full_title, ' \
|
||||
'COUNT(DISTINCT session_history.user_id) as users_watched, ' \
|
||||
'session_history_metadata.rating_key, ' \
|
||||
'MAX(session_history.started) as last_watch, ' \
|
||||
'COUNT(session_history.id) as total_plays, ' \
|
||||
'session_history_metadata.thumb ' \
|
||||
'FROM session_history_metadata ' \
|
||||
'JOIN session_history ON session_history_metadata.id = session_history.id ' \
|
||||
'WHERE datetime(session_history.stopped, "unixepoch", "localtime") ' \
|
||||
'>= datetime("now", "-%s days", "localtime") ' \
|
||||
'AND session_history_metadata.media_type = "movie" ' \
|
||||
'GROUP BY session_history_metadata.full_title ' \
|
||||
'ORDER BY users_watched DESC, total_plays DESC ' \
|
||||
'LIMIT %s' % (time_range, stat_count)
|
||||
result = monitor_db.select(query)
|
||||
except:
|
||||
logger.warn("Unable to execute database query.")
|
||||
return None
|
||||
|
||||
for item in result:
|
||||
row = {'title': item[1],
|
||||
'users_watched': item[2],
|
||||
'rating_key': item[3],
|
||||
'last_play': item[4],
|
||||
'total_plays': item[5],
|
||||
'grandparent_thumb': '',
|
||||
'thumb': item[6],
|
||||
'user': '',
|
||||
'friendly_name': '',
|
||||
'platform_type': '',
|
||||
'platform': '',
|
||||
'row_id': item[0]
|
||||
}
|
||||
popular_movies.append(row)
|
||||
|
||||
home_stats.append({'stat_id': stat,
|
||||
'rows': popular_movies})
|
||||
|
||||
elif 'top_users' in stat:
|
||||
top_users = []
|
||||
try:
|
||||
@@ -250,6 +330,10 @@ class DataFactory(object):
|
||||
'(case when users.friendly_name is null then session_history.user else ' \
|
||||
'users.friendly_name end) as friendly_name,' \
|
||||
'COUNT(session_history.id) as total_plays, ' \
|
||||
'SUM(case when session_history.stopped > 0 ' \
|
||||
'then (session_history.stopped - session_history.started) ' \
|
||||
' - (case when session_history.paused_counter is NULL then 0 else session_history.paused_counter end) ' \
|
||||
'else 0 end) as total_duration, ' \
|
||||
'MAX(session_history.started) as last_watch, ' \
|
||||
'users.custom_avatar_url as thumb, ' \
|
||||
'users.user_id ' \
|
||||
@@ -259,24 +343,25 @@ class DataFactory(object):
|
||||
'WHERE datetime(session_history.stopped, "unixepoch", "localtime") >= ' \
|
||||
'datetime("now", "-%s days", "localtime") '\
|
||||
'GROUP BY session_history.user_id ' \
|
||||
'ORDER BY total_plays DESC LIMIT 10' % time_range
|
||||
'ORDER BY %s DESC LIMIT %s' % (time_range, sort_type, stat_count)
|
||||
result = monitor_db.select(query)
|
||||
except:
|
||||
logger.warn("Unable to execute database query.")
|
||||
return None
|
||||
|
||||
for item in result:
|
||||
if not item[4] or item[4] == '':
|
||||
if not item[5] or item[5] == '':
|
||||
user_thumb = common.DEFAULT_USER_THUMB
|
||||
else:
|
||||
user_thumb = item[4]
|
||||
user_thumb = item[5]
|
||||
|
||||
row = {'user': item[0],
|
||||
'user_id': item[5],
|
||||
'user_id': item[6],
|
||||
'friendly_name': item[1],
|
||||
'total_plays': item[2],
|
||||
'last_play': item[3],
|
||||
'thumb': user_thumb,
|
||||
'total_duration': item[3],
|
||||
'last_play': item[4],
|
||||
'user_thumb': user_thumb,
|
||||
'grandparent_thumb': '',
|
||||
'users_watched': '',
|
||||
'rating_key': '',
|
||||
@@ -288,6 +373,7 @@ class DataFactory(object):
|
||||
top_users.append(row)
|
||||
|
||||
home_stats.append({'stat_id': stat,
|
||||
'stat_type': sort_type,
|
||||
'rows': top_users})
|
||||
|
||||
elif 'top_platforms' in stat:
|
||||
@@ -296,12 +382,16 @@ class DataFactory(object):
|
||||
try:
|
||||
query = 'SELECT session_history.platform, ' \
|
||||
'COUNT(session_history.id) as total_plays, ' \
|
||||
'SUM(case when session_history.stopped > 0 ' \
|
||||
'then (session_history.stopped - session_history.started) ' \
|
||||
' - (case when session_history.paused_counter is NULL then 0 else session_history.paused_counter end) ' \
|
||||
'else 0 end) as total_duration, ' \
|
||||
'MAX(session_history.started) as last_watch ' \
|
||||
'FROM session_history ' \
|
||||
'WHERE datetime(session_history.stopped, "unixepoch", "localtime") ' \
|
||||
'>= datetime("now", "-%s days", "localtime") ' \
|
||||
'GROUP BY session_history.platform ' \
|
||||
'ORDER BY total_plays DESC' % time_range
|
||||
'ORDER BY %s DESC LIMIT %s' % (time_range, sort_type, stat_count)
|
||||
result = monitor_db.select(query)
|
||||
except:
|
||||
logger.warn("Unable to execute database query.")
|
||||
@@ -310,7 +400,8 @@ class DataFactory(object):
|
||||
for item in result:
|
||||
row = {'platform': item[0],
|
||||
'total_plays': item[1],
|
||||
'last_play': item[2],
|
||||
'total_duration': item[2],
|
||||
'last_play': item[3],
|
||||
'platform_type': item[0],
|
||||
'title': '',
|
||||
'thumb': '',
|
||||
@@ -324,8 +415,62 @@ class DataFactory(object):
|
||||
top_platform.append(row)
|
||||
|
||||
home_stats.append({'stat_id': stat,
|
||||
'stat_type': sort_type,
|
||||
'rows': top_platform})
|
||||
|
||||
elif 'last_watched' in stat:
|
||||
last_watched = []
|
||||
try:
|
||||
query = 'SELECT session_history_metadata.id, ' \
|
||||
'session_history.user, ' \
|
||||
'(case when users.friendly_name is null then session_history.user else ' \
|
||||
'users.friendly_name end) as friendly_name,' \
|
||||
'users.user_id, ' \
|
||||
'users.custom_avatar_url as user_thumb, ' \
|
||||
'session_history_metadata.full_title, ' \
|
||||
'session_history_metadata.rating_key, ' \
|
||||
'session_history_metadata.thumb, ' \
|
||||
'session_history_metadata.grandparent_thumb, ' \
|
||||
'MAX(session_history.started) as last_watch, ' \
|
||||
'session_history.player as platform ' \
|
||||
'FROM session_history_metadata ' \
|
||||
'JOIN session_history ON session_history_metadata.id = session_history.id ' \
|
||||
'LEFT OUTER JOIN users ON session_history.user_id = users.user_id ' \
|
||||
'WHERE datetime(session_history.stopped, "unixepoch", "localtime") ' \
|
||||
'>= datetime("now", "-%s days", "localtime") ' \
|
||||
'AND (session_history_metadata.media_type = "movie" ' \
|
||||
'OR session_history_metadata.media_type = "episode") ' \
|
||||
'GROUP BY session_history_metadata.full_title ' \
|
||||
'ORDER BY last_watch DESC ' \
|
||||
'LIMIT %s' % (time_range, stat_count)
|
||||
result = monitor_db.select(query)
|
||||
except:
|
||||
logger.warn("Unable to execute database query.")
|
||||
return None
|
||||
|
||||
for item in result:
|
||||
if not item[8] or item[8] == '':
|
||||
thumb = item[7]
|
||||
else:
|
||||
thumb = item[8]
|
||||
|
||||
row = {'row_id': item[0],
|
||||
'user': item[1],
|
||||
'friendly_name': item[2],
|
||||
'user_id': item[3],
|
||||
'user_thumb': item[4],
|
||||
'title': item[5],
|
||||
'rating_key': item[6],
|
||||
'thumb': thumb,
|
||||
'grandparent_thumb': item[8],
|
||||
'last_watch': item[9],
|
||||
'platform_type': item[10],
|
||||
}
|
||||
last_watched.append(row)
|
||||
|
||||
home_stats.append({'stat_id': stat,
|
||||
'rows': last_watched})
|
||||
|
||||
return home_stats
|
||||
|
||||
def get_stream_details(self, row_id=None):
|
||||
@@ -343,7 +488,6 @@ class DataFactory(object):
|
||||
else:
|
||||
return None
|
||||
|
||||
print result
|
||||
stream_output = {}
|
||||
|
||||
for item in result:
|
||||
@@ -380,24 +524,35 @@ class DataFactory(object):
|
||||
|
||||
try:
|
||||
if user_id:
|
||||
query = 'SELECT session_history.id, session_history.media_type, session_history.rating_key, title, ' \
|
||||
'thumb, parent_thumb, media_index, parent_media_index, year, started, user ' \
|
||||
query = 'SELECT session_history.id, session_history.media_type, session_history.rating_key, session_history.parent_rating_key, ' \
|
||||
'title, parent_title, grandparent_title, thumb, parent_thumb, grandparent_thumb, media_index, parent_media_index, ' \
|
||||
'year, started, user ' \
|
||||
'FROM session_history_metadata ' \
|
||||
'JOIN session_history ON session_history_metadata.id = session_history.id ' \
|
||||
'WHERE user_id = ? AND session_history.media_type != "track" ORDER BY started DESC LIMIT ?'
|
||||
'WHERE user_id = ? ' \
|
||||
'GROUP BY (CASE WHEN session_history.media_type = "track" THEN session_history.parent_rating_key ' \
|
||||
' ELSE session_history.rating_key END) ' \
|
||||
'ORDER BY started DESC LIMIT ?'
|
||||
result = monitor_db.select(query, args=[user_id, limit])
|
||||
elif user:
|
||||
query = 'SELECT session_history.id, session_history.media_type, session_history.rating_key, title, ' \
|
||||
'thumb, parent_thumb, media_index, parent_media_index, year, started, user ' \
|
||||
query = 'SELECT session_history.id, session_history.media_type, session_history.rating_key, session_history.parent_rating_key, ' \
|
||||
'title, parent_title, grandparent_title, thumb, parent_thumb, grandparent_thumb, media_index, parent_media_index, ' \
|
||||
'year, started, user ' \
|
||||
'FROM session_history_metadata ' \
|
||||
'JOIN session_history ON session_history_metadata.id = session_history.id ' \
|
||||
'WHERE user = ? AND session_history.media_type != "track" ORDER BY started DESC LIMIT ?'
|
||||
'WHERE user = ? ' \
|
||||
'GROUP BY (CASE WHEN session_history.media_type = "track" THEN session_history.parent_rating_key ' \
|
||||
' ELSE session_history.rating_key END) ' \
|
||||
'ORDER BY started DESC LIMIT ?'
|
||||
result = monitor_db.select(query, args=[user, limit])
|
||||
else:
|
||||
query = 'SELECT session_history.id, session_history.media_type, session_history.rating_key, title, ' \
|
||||
'thumb, parent_thumb, media_index, parent_media_index, year, started, user ' \
|
||||
'FROM session_history_metadata WHERE session_history.media_type != "track"' \
|
||||
query = 'SELECT session_history.id, session_history.media_type, session_history.rating_key, session_history.parent_rating_key, ' \
|
||||
'title, parent_title, grandparent_title, thumb, parent_thumb, grandparent_thumb, media_index, parent_media_index, ' \
|
||||
'year, started, user ' \
|
||||
'FROM session_history_metadata ' \
|
||||
'JOIN session_history ON session_history_metadata.id = session_history.id ' \
|
||||
'GROUP BY (CASE WHEN session_history.media_type = "track" THEN session_history.parent_rating_key ' \
|
||||
' ELSE session_history.rating_key END) ' \
|
||||
'ORDER BY started DESC LIMIT ?'
|
||||
result = monitor_db.select(query, args=[limit])
|
||||
except:
|
||||
@@ -405,21 +560,25 @@ class DataFactory(object):
|
||||
return None
|
||||
|
||||
for row in result:
|
||||
if row[1] == 'episode':
|
||||
thumb = row[5]
|
||||
if row[1] == 'episode' and row[8]:
|
||||
thumb = row[8]
|
||||
elif row[1] == 'episode':
|
||||
thumb = row[9]
|
||||
else:
|
||||
thumb = row[4]
|
||||
thumb = row[7]
|
||||
|
||||
recent_output = {'row_id': row[0],
|
||||
'type': row[1],
|
||||
'rating_key': row[2],
|
||||
'title': row[3],
|
||||
'title': row[4],
|
||||
'parent_title': row[5],
|
||||
'grandparent_title': row[6],
|
||||
'thumb': thumb,
|
||||
'index': row[6],
|
||||
'parentIndex': row[7],
|
||||
'year': row[8],
|
||||
'time': row[9],
|
||||
'user': row[10]
|
||||
'index': row[10],
|
||||
'parent_index': row[11],
|
||||
'year': row[12],
|
||||
'time': row[13],
|
||||
'user': row[14]
|
||||
}
|
||||
recently_watched.append(recent_output)
|
||||
|
||||
@@ -431,8 +590,8 @@ class DataFactory(object):
|
||||
if row_id:
|
||||
query = 'SELECT rating_key, parent_rating_key, grandparent_rating_key, title, parent_title, grandparent_title, ' \
|
||||
'full_title, media_index, parent_media_index, thumb, parent_thumb, grandparent_thumb, art, media_type, ' \
|
||||
'year, originally_available_at, added_at, updated_at, last_viewed_at, content_rating, summary, rating, ' \
|
||||
'duration, guid, directors, writers, actors, genres, studio ' \
|
||||
'year, originally_available_at, added_at, updated_at, last_viewed_at, content_rating, summary, tagline, ' \
|
||||
'rating, duration, guid, directors, writers, actors, genres, studio ' \
|
||||
'FROM session_history_metadata ' \
|
||||
'WHERE id = ?'
|
||||
result = monitor_db.select(query=query, args=[row_id])
|
||||
@@ -441,13 +600,15 @@ class DataFactory(object):
|
||||
|
||||
metadata = {}
|
||||
for item in result:
|
||||
directors = item['directors'].split(';')
|
||||
writers = item['writers'].split(';')
|
||||
actors = item['actors'].split(';')
|
||||
genres = item['genres'].split(';')
|
||||
directors = item['directors'].split(';') if item['directors'] else []
|
||||
writers = item['writers'].split(';') if item['writers'] else []
|
||||
actors = item['actors'].split(';') if item['actors'] else []
|
||||
genres = item['genres'].split(';') if item['genres'] else []
|
||||
|
||||
metadata = {'type': item['media_type'],
|
||||
'rating_key': item['rating_key'],
|
||||
'parent_rating_key': item['parent_rating_key'],
|
||||
'grandparent_rating_key': item['grandparent_rating_key'],
|
||||
'grandparent_title': item['grandparent_title'],
|
||||
'parent_index': item['parent_media_index'],
|
||||
'parent_title': item['parent_title'],
|
||||
@@ -456,6 +617,7 @@ class DataFactory(object):
|
||||
'title': item['title'],
|
||||
'content_rating': item['content_rating'],
|
||||
'summary': item['summary'],
|
||||
'tagline': item['tagline'],
|
||||
'rating': item['rating'],
|
||||
'duration': item['duration'],
|
||||
'year': item['year'],
|
||||
@@ -474,4 +636,48 @@ class DataFactory(object):
|
||||
'actors': actors
|
||||
}
|
||||
|
||||
return metadata
|
||||
return metadata
|
||||
|
||||
def delete_session_history_rows(self, row_id=None):
|
||||
monitor_db = database.MonitorDatabase()
|
||||
|
||||
if row_id.isdigit():
|
||||
logger.info(u"PlexPy DataFactory :: Deleting row id %s from the session history database." % row_id)
|
||||
session_history_del = \
|
||||
monitor_db.action('DELETE FROM session_history WHERE id = ?', [row_id])
|
||||
session_history_media_info_del = \
|
||||
monitor_db.action('DELETE FROM session_history_media_info WHERE id = ?', [row_id])
|
||||
session_history_metadata_del = \
|
||||
monitor_db.action('DELETE FROM session_history_metadata WHERE id = ?', [row_id])
|
||||
|
||||
return 'Deleted rows %s.' % row_id
|
||||
else:
|
||||
return 'Unable to delete rows. Input row not valid.'
|
||||
|
||||
def delete_all_user_history(self, user_id=None):
|
||||
monitor_db = database.MonitorDatabase()
|
||||
|
||||
if user_id.isdigit():
|
||||
logger.info(u"PlexPy DataFactory :: Deleting all history for user id %s from database." % user_id)
|
||||
session_history_media_info_del = \
|
||||
monitor_db.action('DELETE FROM '
|
||||
'session_history_media_info '
|
||||
'WHERE session_history_media_info.id IN (SELECT session_history_media_info.id '
|
||||
'FROM session_history_media_info '
|
||||
'JOIN session_history ON session_history_media_info.id = session_history.id '
|
||||
'WHERE session_history.user_id = ?)', [user_id])
|
||||
session_history_metadata_del = \
|
||||
monitor_db.action('DELETE FROM '
|
||||
'session_history_metadata '
|
||||
'WHERE session_history_metadata.id IN (SELECT session_history_metadata.id '
|
||||
'FROM session_history_metadata '
|
||||
'JOIN session_history ON session_history_metadata.id = session_history.id '
|
||||
'WHERE session_history.user_id = ?)', [user_id])
|
||||
session_history_del = \
|
||||
monitor_db.action('DELETE FROM '
|
||||
'session_history '
|
||||
'WHERE session_history.user_id = ?', [user_id])
|
||||
|
||||
return 'Deleted all items for user_id %s.' % user_id
|
||||
else:
|
||||
return 'Unable to delete items. Input user_id not valid.'
|
||||
|
541
plexpy/graphs.py
541
plexpy/graphs.py
@@ -1,4 +1,4 @@
|
||||
# This file is part of PlexPy.
|
||||
# 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
|
||||
@@ -33,7 +33,8 @@ class Graphs(object):
|
||||
if y_axis == 'plays':
|
||||
query = 'SELECT date(started, "unixepoch", "localtime") as date_played, ' \
|
||||
'SUM(case when media_type = "episode" then 1 else 0 end) as tv_count, ' \
|
||||
'SUM(case when media_type = "movie" then 1 else 0 end) as movie_count ' \
|
||||
'SUM(case when media_type = "movie" then 1 else 0 end) as movie_count, ' \
|
||||
'SUM(case when media_type = "track" then 1 else 0 end) as music_count ' \
|
||||
'FROM session_history ' \
|
||||
'WHERE datetime(stopped, "unixepoch", "localtime") >= datetime("now", "-%s days", "localtime") ' \
|
||||
'GROUP BY date_played ' \
|
||||
@@ -42,8 +43,12 @@ class Graphs(object):
|
||||
result = monitor_db.select(query)
|
||||
else:
|
||||
query = 'SELECT date(started, "unixepoch", "localtime") as date_played, ' \
|
||||
'SUM(case when media_type = "episode" and stopped > 0 then (stopped - started) else 0 end) as tv_duration, ' \
|
||||
'SUM(case when media_type = "movie" and stopped > 0 then (stopped - started) else 0 end) as movie_duration ' \
|
||||
'SUM(case when media_type = "episode" and stopped > 0 then (stopped - started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tv_duration, ' \
|
||||
'SUM(case when media_type = "movie" and stopped > 0 then (stopped - started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as movie_duration, ' \
|
||||
'SUM(case when media_type = "track" and stopped > 0 then (stopped - started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as music_duration ' \
|
||||
'FROM session_history ' \
|
||||
'WHERE datetime(stopped, "unixepoch", "localtime") >= datetime("now", "-%s days", "localtime") ' \
|
||||
'GROUP BY date_played ' \
|
||||
@@ -62,31 +67,38 @@ class Graphs(object):
|
||||
categories = []
|
||||
series_1 = []
|
||||
series_2 = []
|
||||
series_3 = []
|
||||
|
||||
for date_item in sorted(date_list):
|
||||
date_string = date_item.strftime('%Y-%m-%d')
|
||||
categories.append(date_string)
|
||||
series_1_value = 0
|
||||
series_2_value = 0
|
||||
series_3_value = 0
|
||||
for item in result:
|
||||
if date_string == item[0]:
|
||||
series_1_value = item[1]
|
||||
series_2_value = item[2]
|
||||
series_3_value = item[3]
|
||||
break
|
||||
else:
|
||||
series_1_value = 0
|
||||
series_2_value = 0
|
||||
series_3_value = 0
|
||||
|
||||
series_1.append(series_1_value)
|
||||
series_2.append(series_2_value)
|
||||
series_3.append(series_3_value)
|
||||
|
||||
series_1_output = {'name': 'TV',
|
||||
'data': series_1}
|
||||
series_2_output = {'name': 'Movies',
|
||||
'data': series_2}
|
||||
series_3_output = {'name': 'Music',
|
||||
'data': series_3}
|
||||
|
||||
output = {'categories': categories,
|
||||
'series': [series_1_output, series_2_output]}
|
||||
'series': [series_1_output, series_2_output, series_3_output]}
|
||||
return output
|
||||
|
||||
def get_total_plays_per_dayofweek(self, time_range='30', y_axis='plays'):
|
||||
@@ -105,16 +117,16 @@ class Graphs(object):
|
||||
'when 4 then "Thursday" ' \
|
||||
'when 5 then "Friday" ' \
|
||||
'else "Saturday" end as dayofweek, ' \
|
||||
'COUNT(id) as total_plays ' \
|
||||
'from session_history ' \
|
||||
'SUM(case when media_type = "episode" then 1 else 0 end) as tv_count, ' \
|
||||
'SUM(case when media_type = "movie" then 1 else 0 end) as movie_count, ' \
|
||||
'SUM(case when media_type = "track" then 1 else 0 end) as music_count ' \
|
||||
'FROM session_history ' \
|
||||
'WHERE datetime(stopped, "unixepoch", "localtime") >= ' \
|
||||
'datetime("now", "-' + time_range + ' days", "localtime") AND ' \
|
||||
'(media_type = "episode" OR media_type = "movie") ' \
|
||||
'datetime("now", "-' + time_range + ' days", "localtime") ' \
|
||||
'GROUP BY dayofweek ' \
|
||||
'ORDER BY daynumber'
|
||||
|
||||
result = monitor_db.select(query)
|
||||
y_axis_label = 'Total plays'
|
||||
else:
|
||||
query = 'SELECT strftime("%w", datetime(started, "unixepoch", "localtime")) as daynumber, ' \
|
||||
'case cast (strftime("%w", datetime(started, "unixepoch", "localtime")) as integer) ' \
|
||||
@@ -125,40 +137,57 @@ class Graphs(object):
|
||||
'when 4 then "Thursday" ' \
|
||||
'when 5 then "Friday" ' \
|
||||
'else "Saturday" end as dayofweek, ' \
|
||||
'SUM(case when media_type != "track" and stopped > 0 then (stopped - started) else 0 end) as duration ' \
|
||||
'from session_history ' \
|
||||
'SUM(case when media_type = "episode" and stopped > 0 then (stopped - started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tv_duration, ' \
|
||||
'SUM(case when media_type = "movie" and stopped > 0 then (stopped - started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as movie_duration, ' \
|
||||
'SUM(case when media_type = "track" and stopped > 0 then (stopped - started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as music_duration ' \
|
||||
'FROM session_history ' \
|
||||
'WHERE datetime(stopped, "unixepoch", "localtime") >= ' \
|
||||
'datetime("now", "-' + time_range + ' days", "localtime") AND ' \
|
||||
'(media_type = "episode" OR media_type = "movie") ' \
|
||||
'datetime("now", "-' + time_range + ' days", "localtime") ' \
|
||||
'GROUP BY dayofweek ' \
|
||||
'ORDER BY daynumber'
|
||||
|
||||
result = monitor_db.select(query)
|
||||
y_axis_label = 'Total duration'
|
||||
|
||||
days_list = ['Sunday', 'Monday', 'Tuesday', 'Wednesday',
|
||||
'Thursday', 'Friday', 'Saturday']
|
||||
|
||||
categories = []
|
||||
series_1 = []
|
||||
series_2 = []
|
||||
series_3 = []
|
||||
|
||||
for day_item in days_list:
|
||||
categories.append(day_item)
|
||||
series_1_value = 0
|
||||
series_2_value = 0
|
||||
series_3_value = 0
|
||||
for item in result:
|
||||
if day_item == item[1]:
|
||||
series_1_value = item[2]
|
||||
series_2_value = item[3]
|
||||
series_3_value = item[4]
|
||||
break
|
||||
else:
|
||||
series_1_value = 0
|
||||
series_2_value = 0
|
||||
series_3_value = 0
|
||||
|
||||
series_1.append(series_1_value)
|
||||
series_2.append(series_2_value)
|
||||
series_3.append(series_3_value)
|
||||
|
||||
series_1_output = {'name': y_axis_label,
|
||||
series_1_output = {'name': 'TV',
|
||||
'data': series_1}
|
||||
series_2_output = {'name': 'Movies',
|
||||
'data': series_2}
|
||||
series_3_output = {'name': 'Music',
|
||||
'data': series_3}
|
||||
|
||||
output = {'categories': categories,
|
||||
'series': [series_1_output]}
|
||||
'series': [series_1_output, series_2_output, series_3_output]}
|
||||
return output
|
||||
|
||||
def get_total_plays_per_hourofday(self, time_range='30', y_axis='plays'):
|
||||
@@ -169,28 +198,31 @@ class Graphs(object):
|
||||
|
||||
if y_axis == 'plays':
|
||||
query = 'select strftime("%H", datetime(started, "unixepoch", "localtime")) as hourofday, ' \
|
||||
'COUNT(id) ' \
|
||||
'SUM(case when media_type = "episode" then 1 else 0 end) as tv_count, ' \
|
||||
'SUM(case when media_type = "movie" then 1 else 0 end) as movie_count, ' \
|
||||
'SUM(case when media_type = "track" then 1 else 0 end) as music_count ' \
|
||||
'FROM session_history ' \
|
||||
'WHERE datetime(stopped, "unixepoch", "localtime") >= ' \
|
||||
'datetime("now", "-' + time_range + ' days", "localtime") AND ' \
|
||||
'(media_type = "episode" OR media_type = "movie") ' \
|
||||
'datetime("now", "-' + time_range + ' days", "localtime") ' \
|
||||
'GROUP BY hourofday ' \
|
||||
'ORDER BY hourofday'
|
||||
|
||||
result = monitor_db.select(query)
|
||||
y_axis_label = 'Total plays'
|
||||
else:
|
||||
query = 'select strftime("%H", datetime(started, "unixepoch", "localtime")) as hourofday, ' \
|
||||
'SUM(case when media_type != "track" and stopped > 0 then (stopped - started) else 0 end) as duration ' \
|
||||
'SUM(case when media_type = "episode" and stopped > 0 then (stopped - started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tv_duration, ' \
|
||||
'SUM(case when media_type = "movie" and stopped > 0 then (stopped - started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as movie_duration, ' \
|
||||
'SUM(case when media_type = "track" and stopped > 0 then (stopped - started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as music_duration ' \
|
||||
'FROM session_history ' \
|
||||
'WHERE datetime(stopped, "unixepoch", "localtime") >= ' \
|
||||
'datetime("now", "-' + time_range + ' days", "localtime") AND ' \
|
||||
'(media_type = "episode" OR media_type = "movie") ' \
|
||||
'datetime("now", "-' + time_range + ' days", "localtime") ' \
|
||||
'GROUP BY hourofday ' \
|
||||
'ORDER BY hourofday'
|
||||
|
||||
result = monitor_db.select(query)
|
||||
y_axis_label = 'Total duration'
|
||||
|
||||
hours_list = ['00','01','02','03','04','05',
|
||||
'06','07','08','09','10','11',
|
||||
@@ -199,24 +231,38 @@ class Graphs(object):
|
||||
|
||||
categories = []
|
||||
series_1 = []
|
||||
series_2 = []
|
||||
series_3 = []
|
||||
|
||||
for hour_item in hours_list:
|
||||
categories.append(hour_item)
|
||||
series_1_value = 0
|
||||
series_2_value = 0
|
||||
series_3_value = 0
|
||||
for item in result:
|
||||
if hour_item == item[0]:
|
||||
series_1_value = item[1]
|
||||
series_2_value = item[2]
|
||||
series_3_value = item[3]
|
||||
break
|
||||
else:
|
||||
series_1_value = 0
|
||||
series_2_value = 0
|
||||
series_3_value = 0
|
||||
|
||||
series_1.append(series_1_value)
|
||||
series_2.append(series_2_value)
|
||||
series_3.append(series_3_value)
|
||||
|
||||
series_1_output = {'name': y_axis_label,
|
||||
series_1_output = {'name': 'TV',
|
||||
'data': series_1}
|
||||
series_2_output = {'name': 'Movies',
|
||||
'data': series_2}
|
||||
series_3_output = {'name': 'Music',
|
||||
'data': series_3}
|
||||
|
||||
output = {'categories': categories,
|
||||
'series': [series_1_output]}
|
||||
'series': [series_1_output, series_2_output, series_3_output]}
|
||||
return output
|
||||
|
||||
def get_total_plays_per_month(self, y_axis='plays'):
|
||||
@@ -226,7 +272,8 @@ class Graphs(object):
|
||||
if y_axis == 'plays':
|
||||
query = 'SELECT strftime("%Y-%m", datetime(started, "unixepoch", "localtime")) as datestring, ' \
|
||||
'SUM(case when media_type = "episode" then 1 else 0 end) as tv_count, ' \
|
||||
'SUM(case when media_type = "movie" then 1 else 0 end) as movie_count ' \
|
||||
'SUM(case when media_type = "movie" then 1 else 0 end) as movie_count, ' \
|
||||
'SUM(case when media_type = "track" then 1 else 0 end) as music_count ' \
|
||||
'FROM session_history ' \
|
||||
'WHERE datetime(started, "unixepoch", "localtime") >= datetime("now", "-12 months", "localtime") ' \
|
||||
'GROUP BY strftime("%Y-%m", datetime(started, "unixepoch", "localtime")) ' \
|
||||
@@ -235,8 +282,12 @@ class Graphs(object):
|
||||
result = monitor_db.select(query)
|
||||
else:
|
||||
query = 'SELECT strftime("%Y-%m", datetime(started, "unixepoch", "localtime")) as datestring, ' \
|
||||
'SUM(case when media_type = "episode" and stopped > 0 then (stopped - started) else 0 end) as tv_duration, ' \
|
||||
'SUM(case when media_type = "movie" and stopped > 0 then (stopped - started) else 0 end) as movie_duration ' \
|
||||
'SUM(case when media_type = "episode" and stopped > 0 then (stopped - started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tv_duration, ' \
|
||||
'SUM(case when media_type = "movie" and stopped > 0 then (stopped - started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as movie_duration, ' \
|
||||
'SUM(case when media_type = "track" and stopped > 0 then (stopped - started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as music_duration ' \
|
||||
'FROM session_history ' \
|
||||
'WHERE datetime(started, "unixepoch", "localtime") >= datetime("now", "-12 months", "localtime") ' \
|
||||
'GROUP BY strftime("%Y-%m", datetime(started, "unixepoch", "localtime")) ' \
|
||||
@@ -254,33 +305,40 @@ class Graphs(object):
|
||||
categories = []
|
||||
series_1 = []
|
||||
series_2 = []
|
||||
series_3 = []
|
||||
|
||||
for month_item in sorted(month_range):
|
||||
dt = datetime.datetime(*month_item[:6])
|
||||
date_string = dt.strftime('%Y-%m')
|
||||
|
||||
categories.append(date_string)
|
||||
categories.append(dt.strftime('%b %Y'))
|
||||
series_1_value = 0
|
||||
series_2_value = 0
|
||||
series_3_value = 0
|
||||
for item in result:
|
||||
if date_string == item[0]:
|
||||
series_1_value = item[1]
|
||||
series_2_value = item[2]
|
||||
series_3_value = item[3]
|
||||
break
|
||||
else:
|
||||
series_1_value = 0
|
||||
series_2_value = 0
|
||||
series_3_value = 0
|
||||
|
||||
series_1.append(series_1_value)
|
||||
series_2.append(series_2_value)
|
||||
series_3.append(series_3_value)
|
||||
|
||||
series_1_output = {'name': 'TV',
|
||||
'data': series_1}
|
||||
series_2_output = {'name': 'Movies',
|
||||
'data': series_2}
|
||||
series_3_output = {'name': 'Music',
|
||||
'data': series_3}
|
||||
|
||||
output = {'categories': categories,
|
||||
'series': [series_1_output, series_2_output]}
|
||||
'series': [series_1_output, series_2_output, series_3_output]}
|
||||
return output
|
||||
|
||||
def get_total_plays_by_top_10_platforms(self, time_range='30', y_axis='plays'):
|
||||
@@ -291,43 +349,64 @@ class Graphs(object):
|
||||
|
||||
if y_axis == 'plays':
|
||||
query = 'SELECT platform, ' \
|
||||
'count(id) as platform_count ' \
|
||||
'SUM(case when media_type = "episode" then 1 else 0 end) as tv_count, ' \
|
||||
'SUM(case when media_type = "movie" then 1 else 0 end) as movie_count, ' \
|
||||
'SUM(case when media_type = "track" then 1 else 0 end) as music_count, ' \
|
||||
'COUNT(id) as total_count ' \
|
||||
'FROM session_history ' \
|
||||
'WHERE (datetime(stopped, "unixepoch", "localtime") >= ' \
|
||||
'datetime("now", "-' + time_range + ' days", "localtime")) AND ' \
|
||||
'(media_type = "episode" OR media_type = "movie") ' \
|
||||
'datetime("now", "-' + time_range + ' days", "localtime")) ' \
|
||||
'GROUP BY platform ' \
|
||||
'ORDER BY platform_count DESC ' \
|
||||
'ORDER BY total_count DESC ' \
|
||||
'LIMIT 10'
|
||||
|
||||
result = monitor_db.select(query)
|
||||
y_axis_label = 'Total plays'
|
||||
else:
|
||||
query = 'SELECT platform, ' \
|
||||
'SUM(case when stopped > 0 then (stopped - started) else 0 end) as duration ' \
|
||||
'SUM(case when media_type = "episode" and stopped > 0 then (stopped - started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tv_duration, ' \
|
||||
'SUM(case when media_type = "movie" and stopped > 0 then (stopped - started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as movie_duration, ' \
|
||||
'SUM(case when media_type = "track" and stopped > 0 then (stopped - started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as music_duration, ' \
|
||||
'SUM(case when stopped > 0 then (stopped - started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as total_duration ' \
|
||||
'FROM session_history ' \
|
||||
'WHERE (datetime(stopped, "unixepoch", "localtime") >= ' \
|
||||
'datetime("now", "-' + time_range + ' days", "localtime")) AND ' \
|
||||
'(media_type = "episode" OR media_type = "movie") ' \
|
||||
'datetime("now", "-' + time_range + ' days", "localtime")) ' \
|
||||
'GROUP BY platform ' \
|
||||
'ORDER BY duration DESC ' \
|
||||
'ORDER BY total_duration DESC ' \
|
||||
'LIMIT 10'
|
||||
|
||||
result = monitor_db.select(query)
|
||||
y_axis_label = 'Total duration'
|
||||
|
||||
categories = []
|
||||
series_1 = []
|
||||
series_2 = []
|
||||
series_3 = []
|
||||
|
||||
for item in result:
|
||||
categories.append(item[0])
|
||||
series_1.append(item[1])
|
||||
series_2.append(item[2])
|
||||
series_3.append(item[3])
|
||||
|
||||
series_1_output = {'name': y_axis_label,
|
||||
# Rename Mystery platform names
|
||||
platform_names = [('Mystery 3', 'Playstation 3'),
|
||||
('Mystery 4', 'Playstation 4'),
|
||||
('Mystery 5', 'Xbox 360')]
|
||||
for old_name, new_name in platform_names:
|
||||
categories = [item.replace(old_name, new_name) for item in categories]
|
||||
|
||||
series_1_output = {'name': 'TV',
|
||||
'data': series_1}
|
||||
series_2_output = {'name': 'Movies',
|
||||
'data': series_2}
|
||||
series_3_output = {'name': 'Music',
|
||||
'data': series_3}
|
||||
|
||||
output = {'categories': categories,
|
||||
'series': [series_1_output]}
|
||||
'series': [series_1_output, series_2_output, series_3_output]}
|
||||
return output
|
||||
|
||||
def get_total_plays_by_top_10_users(self, time_range='30', y_axis='plays'):
|
||||
@@ -340,47 +419,61 @@ class Graphs(object):
|
||||
query = 'SELECT ' \
|
||||
'(case when users.friendly_name is null then session_history.user else ' \
|
||||
'users.friendly_name end) as friendly_name,' \
|
||||
'count(session_history.id) as user_count ' \
|
||||
'SUM(case when media_type = "episode" then 1 else 0 end) as tv_count, ' \
|
||||
'SUM(case when media_type = "movie" then 1 else 0 end) as movie_count, ' \
|
||||
'SUM(case when media_type = "track" then 1 else 0 end) as music_count, ' \
|
||||
'COUNT(session_history.id) as total_count ' \
|
||||
'FROM session_history ' \
|
||||
'JOIN users on session_history.user_id = users.user_id ' \
|
||||
'WHERE (datetime(session_history.stopped, "unixepoch", "localtime") >= ' \
|
||||
'datetime("now", "-' + time_range + ' days", "localtime")) AND ' \
|
||||
'(session_history.media_type = "episode" OR session_history.media_type = "movie") ' \
|
||||
'datetime("now", "-' + time_range + ' days", "localtime")) ' \
|
||||
'GROUP BY session_history.user_id ' \
|
||||
'ORDER BY user_count DESC ' \
|
||||
'ORDER BY total_count DESC ' \
|
||||
'LIMIT 10'
|
||||
|
||||
result = monitor_db.select(query)
|
||||
y_axis_label = 'Total plays'
|
||||
else:
|
||||
query = 'SELECT ' \
|
||||
'(case when users.friendly_name is null then session_history.user else ' \
|
||||
'users.friendly_name end) as friendly_name,' \
|
||||
'SUM(case when stopped > 0 then (stopped - started) else 0 end) as duration ' \
|
||||
'SUM(case when media_type = "episode" and stopped > 0 then (stopped - started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tv_duration, ' \
|
||||
'SUM(case when media_type = "movie" and stopped > 0 then (stopped - started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as movie_duration, ' \
|
||||
'SUM(case when media_type = "track" and stopped > 0 then (stopped - started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as music_duration, ' \
|
||||
'SUM(case when stopped > 0 then (stopped - started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as total_duration ' \
|
||||
'FROM session_history ' \
|
||||
'JOIN users on session_history.user_id = users.user_id ' \
|
||||
'WHERE (datetime(session_history.stopped, "unixepoch", "localtime") >= ' \
|
||||
'datetime("now", "-' + time_range + ' days", "localtime")) AND ' \
|
||||
'(session_history.media_type = "episode" OR session_history.media_type = "movie") ' \
|
||||
'datetime("now", "-' + time_range + ' days", "localtime")) ' \
|
||||
'GROUP BY session_history.user_id ' \
|
||||
'ORDER BY duration DESC ' \
|
||||
'ORDER BY total_duration DESC ' \
|
||||
'LIMIT 10'
|
||||
|
||||
result = monitor_db.select(query)
|
||||
y_axis_label = 'Total duration'
|
||||
|
||||
categories = []
|
||||
series_1 = []
|
||||
series_2 = []
|
||||
series_3 = []
|
||||
|
||||
for item in result:
|
||||
categories.append(item[0])
|
||||
series_1.append(item[1])
|
||||
series_2.append(item[2])
|
||||
series_3.append(item[3])
|
||||
|
||||
series_1_output = {'name': y_axis_label,
|
||||
series_1_output = {'name': 'TV',
|
||||
'data': series_1}
|
||||
series_2_output = {'name': 'Movies',
|
||||
'data': series_2}
|
||||
series_3_output = {'name': 'Music',
|
||||
'data': series_3}
|
||||
|
||||
output = {'categories': categories,
|
||||
'series': [series_1_output]}
|
||||
'series': [series_1_output, series_2_output, series_3_output]}
|
||||
return output
|
||||
|
||||
def get_total_plays_per_stream_type(self, time_range='30', y_axis='plays'):
|
||||
@@ -392,31 +485,43 @@ class Graphs(object):
|
||||
try:
|
||||
if y_axis == 'plays':
|
||||
query = 'SELECT date(session_history.started, "unixepoch", "localtime") as date_played, ' \
|
||||
'SUM(case when session_history_media_info.video_decision = "direct play" then 1 else 0 end) as dp_count, ' \
|
||||
'SUM(case when session_history_media_info.video_decision = "copy" then 1 else 0 end) as ds_count, ' \
|
||||
'SUM(case when session_history_media_info.video_decision = "transcode" then 1 else 0 end) as tc_count ' \
|
||||
'SUM(case when session_history_media_info.video_decision = "direct play" ' \
|
||||
'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "direct play") ' \
|
||||
'then 1 else 0 end) as dp_count, ' \
|
||||
'SUM(case when session_history_media_info.video_decision = "copy" ' \
|
||||
'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "copy") ' \
|
||||
'then 1 else 0 end) as ds_count, ' \
|
||||
'SUM(case when session_history_media_info.video_decision = "transcode" ' \
|
||||
'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "transcode") ' \
|
||||
'then 1 else 0 end) as tc_count ' \
|
||||
'FROM session_history ' \
|
||||
'JOIN session_history_media_info ON session_history.id = session_history_media_info.id ' \
|
||||
'WHERE (datetime(session_history.stopped, "unixepoch", "localtime") >= ' \
|
||||
'datetime("now", "-%s days", "localtime")) AND ' \
|
||||
'(session_history.media_type = "episode" OR session_history.media_type = "movie") ' \
|
||||
'(session_history.media_type = "episode" OR session_history.media_type = "movie" OR session_history.media_type = "track") ' \
|
||||
'GROUP BY date_played ' \
|
||||
'ORDER BY started ASC' % time_range
|
||||
|
||||
result = monitor_db.select(query)
|
||||
else:
|
||||
query = 'SELECT date(session_history.started, "unixepoch", "localtime") as date_played, ' \
|
||||
'SUM(case when session_history_media_info.video_decision = "direct play" AND ' \
|
||||
'session_history.stopped > 0 then (stopped - started) else 0 end) as dp_duration, ' \
|
||||
'SUM(case when session_history_media_info.video_decision = "copy" AND ' \
|
||||
'session_history.stopped > 0 then (stopped - started) else 0 end) as ds_duration, ' \
|
||||
'SUM(case when session_history_media_info.video_decision = "transcode" ' \
|
||||
'AND session_history.stopped > 0 then (stopped - started) else 0 end) as tc_duration ' \
|
||||
'SUM(case when (session_history_media_info.video_decision = "direct play" ' \
|
||||
'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "direct play")) ' \
|
||||
'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as dp_duration, ' \
|
||||
'SUM(case when (session_history_media_info.video_decision = "copy" ' \
|
||||
'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "copy")) ' \
|
||||
'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as ds_duration, ' \
|
||||
'SUM(case when (session_history_media_info.video_decision = "transcode" ' \
|
||||
'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "transcode")) ' \
|
||||
'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tc_duration ' \
|
||||
'FROM session_history ' \
|
||||
'JOIN session_history_media_info ON session_history.id = session_history_media_info.id ' \
|
||||
'WHERE datetime(session_history.stopped, "unixepoch", "localtime") >= ' \
|
||||
'datetime("now", "-%s days", "localtime") AND ' \
|
||||
'(session_history.media_type = "episode" OR session_history.media_type = "movie") ' \
|
||||
'(session_history.media_type = "episode" OR session_history.media_type = "movie" OR session_history.media_type = "track") ' \
|
||||
'GROUP BY date_played ' \
|
||||
'ORDER BY started ASC' % time_range
|
||||
|
||||
@@ -465,7 +570,6 @@ class Graphs(object):
|
||||
|
||||
output = {'categories': categories,
|
||||
'series': [series_1_output, series_2_output, series_3_output]}
|
||||
|
||||
return output
|
||||
|
||||
def get_total_plays_by_source_resolution(self, time_range='30', y_axis='plays'):
|
||||
@@ -475,49 +579,74 @@ class Graphs(object):
|
||||
time_range = '30'
|
||||
|
||||
if y_axis == 'plays':
|
||||
query = 'SELECT ' \
|
||||
'count(session_history.id) as play_count, ' \
|
||||
'session_history_media_info.video_resolution AS resolution ' \
|
||||
query = 'SELECT session_history_media_info.video_resolution AS resolution, ' \
|
||||
'SUM(case when session_history_media_info.video_decision = "direct play" ' \
|
||||
'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "direct play") ' \
|
||||
'then 1 else 0 end) as dp_count, ' \
|
||||
'SUM(case when session_history_media_info.video_decision = "copy" ' \
|
||||
'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "copy") ' \
|
||||
'then 1 else 0 end) as ds_count, ' \
|
||||
'SUM(case when session_history_media_info.video_decision = "transcode" ' \
|
||||
'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "transcode") ' \
|
||||
'then 1 else 0 end) as tc_count, ' \
|
||||
'COUNT(session_history.id) as total_count ' \
|
||||
'FROM session_history ' \
|
||||
'JOIN session_history_media_info on session_history.id = session_history_media_info.id ' \
|
||||
'WHERE (datetime(session_history.stopped, "unixepoch", "localtime") >= ' \
|
||||
'datetime("now", "-' + time_range + ' days", "localtime")) AND ' \
|
||||
'(session_history.media_type = "episode" OR session_history.media_type = "movie") ' \
|
||||
'GROUP BY resolution ' \
|
||||
'ORDER BY play_count DESC ' \
|
||||
'ORDER BY total_count DESC ' \
|
||||
'LIMIT 10'
|
||||
|
||||
result = monitor_db.select(query)
|
||||
y_axis_label = 'Total plays'
|
||||
else:
|
||||
query = 'SELECT ' \
|
||||
'SUM(case when stopped > 0 then (stopped - started) else 0 end) as duration, ' \
|
||||
'session_history_media_info.video_resolution AS resolution ' \
|
||||
query = 'SELECT session_history_media_info.video_resolution AS resolution,' \
|
||||
'SUM(case when (session_history_media_info.video_decision = "direct play" ' \
|
||||
'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "direct play")) ' \
|
||||
'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as dp_duration, ' \
|
||||
'SUM(case when (session_history_media_info.video_decision = "copy" ' \
|
||||
'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "copy")) ' \
|
||||
'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as ds_duration, ' \
|
||||
'SUM(case when (session_history_media_info.video_decision = "transcode" ' \
|
||||
'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "transcode")) ' \
|
||||
'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tc_duration, ' \
|
||||
'SUM(case when stopped > 0 then (stopped - started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as total_duration ' \
|
||||
'FROM session_history ' \
|
||||
'JOIN session_history_media_info on session_history.id = session_history_media_info.id ' \
|
||||
'WHERE (datetime(session_history.stopped, "unixepoch", "localtime") >= ' \
|
||||
'datetime("now", "-' + time_range + ' days", "localtime")) AND ' \
|
||||
'(session_history.media_type = "episode" OR session_history.media_type = "movie") ' \
|
||||
'GROUP BY resolution ' \
|
||||
'ORDER BY duration DESC ' \
|
||||
'ORDER BY total_duration DESC ' \
|
||||
'LIMIT 10'
|
||||
|
||||
result = monitor_db.select(query)
|
||||
y_axis_label = 'Total duration'
|
||||
|
||||
categories = []
|
||||
series_1 = []
|
||||
series_2 = []
|
||||
series_3 = []
|
||||
|
||||
for item in result:
|
||||
categories.append(item[1])
|
||||
series_1.append(item[0])
|
||||
categories.append(item[0])
|
||||
series_1.append(item[1])
|
||||
series_2.append(item[2])
|
||||
series_3.append(item[3])
|
||||
|
||||
series_1_output = {'name': y_axis_label,
|
||||
series_1_output = {'name': 'Direct Play',
|
||||
'data': series_1}
|
||||
series_2_output = {'name': 'Direct Stream',
|
||||
'data': series_2}
|
||||
series_3_output = {'name': 'Transcode',
|
||||
'data': series_3}
|
||||
|
||||
output = {'categories': categories,
|
||||
'series': [series_1_output]}
|
||||
|
||||
'series': [series_1_output, series_2_output, series_3_output]}
|
||||
return output
|
||||
|
||||
def get_total_plays_by_stream_resolution(self, time_range='30', y_axis='plays'):
|
||||
@@ -528,7 +657,6 @@ class Graphs(object):
|
||||
|
||||
if y_axis == 'plays':
|
||||
query = 'SELECT ' \
|
||||
'count(session_history.id) as play_count, ' \
|
||||
'(case when session_history_media_info.video_decision = "transcode" then ' \
|
||||
'(case ' \
|
||||
'when session_history_media_info.transcode_height <= 360 then "sd" ' \
|
||||
@@ -538,21 +666,29 @@ class Graphs(object):
|
||||
'when session_history_media_info.transcode_height <= 1080 then "1080" ' \
|
||||
'when session_history_media_info.transcode_height <= 1440 then "QHD" ' \
|
||||
'when session_history_media_info.transcode_height <= 2160 then "4K" ' \
|
||||
'else "unknown" end) else session_history_media_info.video_resolution end) as resolution ' \
|
||||
'else "unknown" end) else session_history_media_info.video_resolution end) as resolution, ' \
|
||||
'SUM(case when session_history_media_info.video_decision = "direct play" ' \
|
||||
'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "direct play") ' \
|
||||
'then 1 else 0 end) as dp_count, ' \
|
||||
'SUM(case when session_history_media_info.video_decision = "copy" ' \
|
||||
'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "copy") ' \
|
||||
'then 1 else 0 end) as ds_count, ' \
|
||||
'SUM(case when session_history_media_info.video_decision = "transcode" ' \
|
||||
'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "transcode") ' \
|
||||
'then 1 else 0 end) as tc_count, ' \
|
||||
'COUNT(session_history.id) as total_count ' \
|
||||
'FROM session_history ' \
|
||||
'JOIN session_history_media_info on session_history.id = session_history_media_info.id ' \
|
||||
'WHERE (datetime(session_history.stopped, "unixepoch", "localtime") >= ' \
|
||||
'datetime("now", "-' + time_range + ' days", "localtime")) AND ' \
|
||||
'(session_history.media_type = "episode" OR session_history.media_type = "movie") ' \
|
||||
'GROUP BY resolution ' \
|
||||
'ORDER BY play_count DESC ' \
|
||||
'ORDER BY total_count DESC ' \
|
||||
'LIMIT 10'
|
||||
|
||||
result = monitor_db.select(query)
|
||||
y_axis_label = 'Total plays'
|
||||
else:
|
||||
query = 'SELECT ' \
|
||||
'SUM(case when stopped > 0 then (stopped - started) else 0 end) as duration, ' \
|
||||
'(case when session_history_media_info.video_decision = "transcode" then ' \
|
||||
'(case ' \
|
||||
'when session_history_media_info.transcode_height <= 360 then "sd" ' \
|
||||
@@ -562,31 +698,137 @@ class Graphs(object):
|
||||
'when session_history_media_info.transcode_height <= 1080 then "1080" ' \
|
||||
'when session_history_media_info.transcode_height <= 1440 then "QHD" ' \
|
||||
'when session_history_media_info.transcode_height <= 2160 then "4K" ' \
|
||||
'else "unknown" end) else session_history_media_info.video_resolution end) as resolution ' \
|
||||
'else "unknown" end) else session_history_media_info.video_resolution end) as resolution, ' \
|
||||
'SUM(case when (session_history_media_info.video_decision = "direct play" ' \
|
||||
'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "direct play")) ' \
|
||||
'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as dp_duration, ' \
|
||||
'SUM(case when (session_history_media_info.video_decision = "copy" ' \
|
||||
'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "copy")) ' \
|
||||
'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as ds_duration, ' \
|
||||
'SUM(case when (session_history_media_info.video_decision = "transcode" ' \
|
||||
'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "transcode")) ' \
|
||||
'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tc_duration, ' \
|
||||
'SUM(case when stopped > 0 then (stopped - started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as total_duration ' \
|
||||
'FROM session_history ' \
|
||||
'JOIN session_history_media_info on session_history.id = session_history_media_info.id ' \
|
||||
'WHERE (datetime(session_history.stopped, "unixepoch", "localtime") >= ' \
|
||||
'datetime("now", "-' + time_range + ' days", "localtime")) AND ' \
|
||||
'(session_history.media_type = "episode" OR session_history.media_type = "movie") ' \
|
||||
'GROUP BY resolution ' \
|
||||
'ORDER BY duration DESC ' \
|
||||
'ORDER BY total_duration DESC ' \
|
||||
'LIMIT 10'
|
||||
|
||||
result = monitor_db.select(query)
|
||||
y_axis_label = 'Total duration'
|
||||
|
||||
categories = []
|
||||
series_1 = []
|
||||
series_2 = []
|
||||
series_3 = []
|
||||
|
||||
for item in result:
|
||||
categories.append(item[1])
|
||||
series_1.append(item[0])
|
||||
categories.append(item[0])
|
||||
series_1.append(item[1])
|
||||
series_2.append(item[2])
|
||||
series_3.append(item[3])
|
||||
|
||||
series_1_output = {'name': y_axis_label,
|
||||
series_1_output = {'name': 'Direct Play',
|
||||
'data': series_1}
|
||||
series_2_output = {'name': 'Direct Stream',
|
||||
'data': series_2}
|
||||
series_3_output = {'name': 'Transcode',
|
||||
'data': series_3}
|
||||
|
||||
output = {'categories': categories,
|
||||
'series': [series_1_output]}
|
||||
'series': [series_1_output, series_2_output, series_3_output]}
|
||||
return output
|
||||
|
||||
def get_stream_type_by_top_10_platforms(self, time_range='30', y_axis='plays'):
|
||||
monitor_db = database.MonitorDatabase()
|
||||
|
||||
if not time_range.isdigit():
|
||||
time_range = '30'
|
||||
|
||||
if y_axis == 'plays':
|
||||
query = 'SELECT ' \
|
||||
'session_history.platform as platform, ' \
|
||||
'SUM(case when session_history_media_info.video_decision = "direct play" ' \
|
||||
'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "direct play") ' \
|
||||
'then 1 else 0 end) as dp_count, ' \
|
||||
'SUM(case when session_history_media_info.video_decision = "copy" ' \
|
||||
'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "copy") ' \
|
||||
'then 1 else 0 end) as ds_count, ' \
|
||||
'SUM(case when session_history_media_info.video_decision = "transcode" ' \
|
||||
'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "transcode") ' \
|
||||
'then 1 else 0 end) as tc_count, ' \
|
||||
'COUNT(session_history.id) as total_count ' \
|
||||
'FROM session_history ' \
|
||||
'JOIN session_history_media_info ON session_history.id = session_history_media_info.id ' \
|
||||
'WHERE datetime(session_history.started, "unixepoch", "localtime") >= ' \
|
||||
'datetime("now", "-' + time_range + ' days", "localtime") AND ' \
|
||||
'(session_history.media_type = "episode" OR session_history.media_type = "movie" OR session_history.media_type = "track") ' \
|
||||
'GROUP BY platform ' \
|
||||
'ORDER BY total_count DESC LIMIT 10'
|
||||
|
||||
result = monitor_db.select(query)
|
||||
else:
|
||||
query = 'SELECT ' \
|
||||
'session_history.platform as platform, ' \
|
||||
'SUM(case when (session_history_media_info.video_decision = "direct play" ' \
|
||||
'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "direct play")) ' \
|
||||
'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as dp_duration, ' \
|
||||
'SUM(case when (session_history_media_info.video_decision = "copy" ' \
|
||||
'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "copy")) ' \
|
||||
'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as ds_duration, ' \
|
||||
'SUM(case when (session_history_media_info.video_decision = "transcode" ' \
|
||||
'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "transcode")) ' \
|
||||
'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tc_duration, ' \
|
||||
'SUM(case when session_history.stopped > 0 ' \
|
||||
'then (session_history.stopped - session_history.started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as total_duration ' \
|
||||
'FROM session_history ' \
|
||||
'JOIN session_history_media_info ON session_history.id = session_history_media_info.id ' \
|
||||
'WHERE datetime(session_history.started, "unixepoch", "localtime") >= ' \
|
||||
'datetime("now", "-' + time_range + ' days", "localtime") AND ' \
|
||||
'(session_history.media_type = "episode" OR session_history.media_type = "movie" OR session_history.media_type = "track") ' \
|
||||
'GROUP BY platform ' \
|
||||
'ORDER BY total_duration DESC LIMIT 10'
|
||||
|
||||
result = monitor_db.select(query)
|
||||
|
||||
categories = []
|
||||
series_1 = []
|
||||
series_2 = []
|
||||
series_3 = []
|
||||
|
||||
for item in result:
|
||||
categories.append(item[0])
|
||||
series_1.append(item[1])
|
||||
series_2.append(item[2])
|
||||
series_3.append(item[3])
|
||||
|
||||
# Rename Mystery platform names
|
||||
platform_names = [('Mystery 3', 'Playstation 3'),
|
||||
('Mystery 4', 'Playstation 4'),
|
||||
('Mystery 5', 'Xbox 360')]
|
||||
for old_name, new_name in platform_names:
|
||||
categories = [item.replace(old_name, new_name) for item in categories]
|
||||
|
||||
series_1_output = {'name': 'Direct Play',
|
||||
'data': series_1}
|
||||
series_2_output = {'name': 'Direct Stream',
|
||||
'data': series_2}
|
||||
series_3_output = {'name': 'Transcode',
|
||||
'data': series_3}
|
||||
|
||||
output = {'categories': categories,
|
||||
'series': [series_1_output, series_2_output, series_3_output]}
|
||||
|
||||
return output
|
||||
|
||||
@@ -599,15 +841,22 @@ class Graphs(object):
|
||||
if y_axis == 'plays':
|
||||
query = 'SELECT ' \
|
||||
'CASE WHEN users.friendly_name is null then users.username else users.friendly_name end as username, ' \
|
||||
'SUM(case when session_history_media_info.video_decision = "direct play" then 1 else 0 end) as dp_count, ' \
|
||||
'SUM(case when session_history_media_info.video_decision = "copy" then 1 else 0 end) as ds_count, ' \
|
||||
'SUM(case when session_history_media_info.video_decision = "transcode" then 1 else 0 end) as tr_count, ' \
|
||||
'SUM(case when session_history.media_type != "track" then 1 else 0 end) as total_count ' \
|
||||
'SUM(case when session_history_media_info.video_decision = "direct play" ' \
|
||||
'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "direct play") ' \
|
||||
'then 1 else 0 end) as dp_count, ' \
|
||||
'SUM(case when session_history_media_info.video_decision = "copy" ' \
|
||||
'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "copy") ' \
|
||||
'then 1 else 0 end) as ds_count, ' \
|
||||
'SUM(case when session_history_media_info.video_decision = "transcode" ' \
|
||||
'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "transcode") ' \
|
||||
'then 1 else 0 end) as tc_count, ' \
|
||||
'COUNT(session_history.id) as total_count ' \
|
||||
'FROM session_history ' \
|
||||
'JOIN users ON session_history.user_id = users.user_id ' \
|
||||
'JOIN session_history_media_info ON session_history.id = session_history_media_info.id ' \
|
||||
'WHERE datetime(session_history.started, "unixepoch", "localtime") >= ' \
|
||||
'datetime("now", "-' + time_range + ' days", "localtime") ' \
|
||||
'datetime("now", "-' + time_range + ' days", "localtime") AND ' \
|
||||
'(session_history.media_type = "episode" OR session_history.media_type = "movie" OR session_history.media_type = "track") ' \
|
||||
'GROUP BY username ' \
|
||||
'ORDER BY total_count DESC LIMIT 10'
|
||||
|
||||
@@ -615,85 +864,29 @@ class Graphs(object):
|
||||
else:
|
||||
query = 'SELECT ' \
|
||||
'CASE WHEN users.friendly_name is null then users.username else users.friendly_name end as username, ' \
|
||||
'SUM(case when session_history.stopped > 0 AND session_history_media_info.video_decision = "direct play" ' \
|
||||
'then (session_history.stopped - session_history.started) else 0 end) as dp_count, ' \
|
||||
'SUM(case when session_history.stopped > 0 AND session_history_media_info.video_decision = "copy" ' \
|
||||
'then (session_history.stopped - session_history.started) else 0 end) as ds_count, ' \
|
||||
'SUM(case when session_history.stopped > 0 AND session_history_media_info.video_decision = "transcode" ' \
|
||||
'then (session_history.stopped - session_history.started) else 0 end) as tr_count, ' \
|
||||
'SUM(case when session_history.stopped > 0 AND session_history.media_type != "track" ' \
|
||||
'then (session_history.stopped - session_history.started) else 0 end) as total_count ' \
|
||||
'SUM(case when (session_history_media_info.video_decision = "direct play" ' \
|
||||
'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "direct play")) ' \
|
||||
'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as dp_duration, ' \
|
||||
'SUM(case when (session_history_media_info.video_decision = "copy" ' \
|
||||
'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "copy")) ' \
|
||||
'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as ds_duration, ' \
|
||||
'SUM(case when (session_history_media_info.video_decision = "transcode" ' \
|
||||
'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "transcode")) ' \
|
||||
'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tc_duration, ' \
|
||||
'SUM(case when session_history.stopped > 0 ' \
|
||||
'then (session_history.stopped - session_history.started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as total_duration ' \
|
||||
'FROM session_history ' \
|
||||
'JOIN users ON session_history.user_id = users.user_id ' \
|
||||
'JOIN session_history_media_info ON session_history.id = session_history_media_info.id ' \
|
||||
'WHERE datetime(session_history.started, "unixepoch", "localtime") >= ' \
|
||||
'datetime("now", "-' + time_range + ' days", "localtime") ' \
|
||||
'datetime("now", "-' + time_range + ' days", "localtime") AND ' \
|
||||
'(session_history.media_type = "episode" OR session_history.media_type = "movie" OR session_history.media_type = "track") ' \
|
||||
'GROUP BY username ' \
|
||||
'ORDER BY total_count DESC LIMIT 10'
|
||||
|
||||
result = monitor_db.select(query)
|
||||
|
||||
categories = []
|
||||
series_1 = []
|
||||
series_2 = []
|
||||
series_3 = []
|
||||
|
||||
for item in result:
|
||||
categories.append(item[0])
|
||||
series_1.append(item[1])
|
||||
series_2.append(item[2])
|
||||
series_3.append(item[3])
|
||||
|
||||
series_1_output = {'name': 'Direct Play',
|
||||
'data': series_1}
|
||||
series_2_output = {'name': 'Direct Stream',
|
||||
'data': series_2}
|
||||
series_3_output = {'name': 'Transcode',
|
||||
'data': series_3}
|
||||
|
||||
output = {'categories': categories,
|
||||
'series': [series_1_output, series_2_output, series_3_output]}
|
||||
|
||||
return output
|
||||
|
||||
def get_stream_type_by_top_10_platforms(self, time_range='30', y_axis='plays'):
|
||||
monitor_db = database.MonitorDatabase()
|
||||
|
||||
if not time_range.isdigit():
|
||||
time_range = '30'
|
||||
|
||||
if y_axis == 'plays':
|
||||
query = 'SELECT ' \
|
||||
'session_history.platform as platform, ' \
|
||||
'SUM(case when session_history_media_info.video_decision = "direct play" then 1 else 0 end) as dp_count, ' \
|
||||
'SUM(case when session_history_media_info.video_decision = "copy" then 1 else 0 end) as ds_count, ' \
|
||||
'SUM(case when session_history_media_info.video_decision = "transcode" then 1 else 0 end) as tr_count, ' \
|
||||
'SUM(case when session_history.media_type != "track" then 1 else 0 end) as total_count ' \
|
||||
'FROM session_history ' \
|
||||
'JOIN session_history_media_info ON session_history.id = session_history_media_info.id ' \
|
||||
'WHERE datetime(session_history.started, "unixepoch", "localtime") >= ' \
|
||||
'datetime("now", "-' + time_range + ' days", "localtime") ' \
|
||||
'GROUP BY platform ' \
|
||||
'ORDER BY total_count DESC LIMIT 10'
|
||||
|
||||
result = monitor_db.select(query)
|
||||
else:
|
||||
query = 'SELECT ' \
|
||||
'session_history.platform as platform, ' \
|
||||
'SUM(case when session_history.stopped > 0 AND session_history_media_info.video_decision = "direct play" ' \
|
||||
'then (session_history.stopped - session_history.started) else 0 end) as dp_count, ' \
|
||||
'SUM(case when session_history.stopped > 0 AND session_history_media_info.video_decision = "copy" ' \
|
||||
'then (session_history.stopped - session_history.started) else 0 end) as ds_count, ' \
|
||||
'SUM(case when session_history.stopped > 0 AND session_history_media_info.video_decision = "transcode" ' \
|
||||
'then (session_history.stopped - session_history.started) else 0 end) as tr_count, ' \
|
||||
'SUM(case when session_history.stopped > 0 AND session_history.media_type != "track" ' \
|
||||
'then (session_history.stopped - session_history.started) else 0 end) as total_count ' \
|
||||
'FROM session_history ' \
|
||||
'JOIN session_history_media_info ON session_history.id = session_history_media_info.id ' \
|
||||
'WHERE datetime(session_history.started, "unixepoch", "localtime") >= ' \
|
||||
'datetime("now", "-' + time_range + ' days", "localtime") ' \
|
||||
'GROUP BY platform ' \
|
||||
'ORDER BY total_count DESC LIMIT 10'
|
||||
'ORDER BY total_duration DESC LIMIT 10'
|
||||
|
||||
result = monitor_db.select(query)
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# This file is part of PlexPy.
|
||||
# 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
|
||||
@@ -247,10 +247,10 @@ class MonitorProcessing(object):
|
||||
else:
|
||||
stopped = int(time.time())
|
||||
|
||||
if plexpy.CONFIG.VIDEO_LOGGING_ENABLE and \
|
||||
if plexpy.CONFIG.VIDEO_LOGGING_ENABLE and str(session['rating_key']).isdigit() and \
|
||||
(session['media_type'] == 'movie' or session['media_type'] == 'episode'):
|
||||
logging_enabled = True
|
||||
elif plexpy.CONFIG.MUSIC_LOGGING_ENABLE and \
|
||||
elif plexpy.CONFIG.MUSIC_LOGGING_ENABLE and str(session['rating_key']).isdigit() and \
|
||||
session['media_type'] == 'track':
|
||||
logging_enabled = True
|
||||
else:
|
||||
@@ -343,17 +343,17 @@ class MonitorProcessing(object):
|
||||
'grandparent_rating_key, title, parent_title, grandparent_title, full_title, media_index, ' \
|
||||
'parent_media_index, thumb, parent_thumb, grandparent_thumb, art, media_type, year, ' \
|
||||
'originally_available_at, added_at, updated_at, last_viewed_at, content_rating, summary, ' \
|
||||
'rating, duration, guid, directors, writers, actors, genres, studio) VALUES ' \
|
||||
'tagline, rating, duration, guid, directors, writers, actors, genres, studio) VALUES ' \
|
||||
'(last_insert_rowid(), ' \
|
||||
'?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'
|
||||
'?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'
|
||||
|
||||
args = [session['rating_key'], session['parent_rating_key'], session['grandparent_rating_key'],
|
||||
session['title'], session['parent_title'], session['grandparent_title'], full_title,
|
||||
metadata['index'], metadata['parent_index'], metadata['thumb'], metadata['parent_thumb'],
|
||||
metadata['grandparent_thumb'], metadata['art'], session['media_type'], metadata['year'],
|
||||
metadata['originally_available_at'], metadata['added_at'], metadata['updated_at'],
|
||||
metadata['last_viewed_at'], metadata['content_rating'], metadata['summary'], metadata['rating'],
|
||||
metadata['duration'], metadata['guid'], directors, writers, actors, genres, metadata['studio']]
|
||||
metadata['last_viewed_at'], metadata['content_rating'], metadata['summary'], metadata['tagline'],
|
||||
metadata['rating'], metadata['duration'], metadata['guid'], directors, writers, actors, genres, metadata['studio']]
|
||||
|
||||
# logger.debug(u"PlexPy Monitor :: Writing session_history_metadata transaction...")
|
||||
self.db.action(query=query, args=args)
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# This file is part of PlexPy.
|
||||
# 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
|
||||
@@ -162,6 +162,7 @@ def notify(stream_data=None, notify_action=None):
|
||||
else:
|
||||
logger.debug(u"PlexPy Notifier :: Notify called but incomplete data received.")
|
||||
|
||||
|
||||
def get_notify_state(session):
|
||||
monitor_db = database.MonitorDatabase()
|
||||
result = monitor_db.select('SELECT on_play, on_stop, on_pause, on_resume, on_buffer, on_watched, agent_id '
|
||||
@@ -184,6 +185,7 @@ def get_notify_state(session):
|
||||
|
||||
return notify_states
|
||||
|
||||
|
||||
def set_notify_state(session, state, agent_info):
|
||||
|
||||
if session and state and agent_info:
|
||||
@@ -215,6 +217,7 @@ def set_notify_state(session, state, agent_info):
|
||||
else:
|
||||
logger.error('PlexPy Notifier :: Unable to set notify state.')
|
||||
|
||||
|
||||
def build_notify_text(session, state):
|
||||
from plexpy import pmsconnect, helpers
|
||||
import re
|
||||
@@ -300,6 +303,13 @@ def build_notify_text(session, state):
|
||||
|
||||
duration = helpers.convert_milliseconds_to_minutes(item_metadata['duration'])
|
||||
view_offset = helpers.convert_milliseconds_to_minutes(session['view_offset'])
|
||||
stream_duration = 0
|
||||
if state != 'play':
|
||||
if session['paused_counter']:
|
||||
stream_duration = int((time.time() - helpers.cast_to_float(session['started']) -
|
||||
helpers.cast_to_float(session['paused_counter'])) / 60)
|
||||
else:
|
||||
stream_duration = int((time.time() - helpers.cast_to_float(session['started'])) / 60)
|
||||
|
||||
progress_percent = helpers.get_percent(view_offset, duration)
|
||||
|
||||
@@ -317,10 +327,13 @@ def build_notify_text(session, state):
|
||||
'content_rating': item_metadata['content_rating'],
|
||||
'summary': item_metadata['summary'],
|
||||
'season_num': item_metadata['parent_index'],
|
||||
'season_num00': item_metadata['parent_index'].zfill(2),
|
||||
'episode_num': item_metadata['index'],
|
||||
'episode_num00': item_metadata['index'].zfill(2),
|
||||
'album_name': item_metadata['parent_title'],
|
||||
'rating': item_metadata['rating'],
|
||||
'duration': duration,
|
||||
'stream_duration': stream_duration,
|
||||
'progress': view_offset,
|
||||
'progress_percent': progress_percent
|
||||
}
|
||||
@@ -336,14 +349,14 @@ def build_notify_text(session, state):
|
||||
|
||||
if on_start_subject and on_start_body:
|
||||
try:
|
||||
subject_text = on_start_subject.format(**available_params)
|
||||
subject_text = unicode(on_start_subject).format(**available_params)
|
||||
except LookupError, e:
|
||||
logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification subject. Using fallback." % e)
|
||||
except:
|
||||
logger.error(u"PlexPy Notifier :: Unable to parse custom notification subject. Using fallback.")
|
||||
|
||||
try:
|
||||
body_text = on_start_body.format(**available_params)
|
||||
body_text = unicode(on_start_body).format(**available_params)
|
||||
except LookupError, e:
|
||||
logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification body. Using fallback." % e)
|
||||
except:
|
||||
@@ -360,14 +373,14 @@ def build_notify_text(session, state):
|
||||
|
||||
if on_stop_subject and on_stop_body:
|
||||
try:
|
||||
subject_text = on_stop_subject.format(**available_params)
|
||||
subject_text = unicode(on_stop_subject).format(**available_params)
|
||||
except LookupError, e:
|
||||
logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification subject. Using fallback." % e)
|
||||
except:
|
||||
logger.error(u"PlexPy Notifier :: Unable to parse custom notification subject. Using fallback.")
|
||||
|
||||
try:
|
||||
body_text = on_stop_body.format(**available_params)
|
||||
body_text = unicode(on_stop_body).format(**available_params)
|
||||
except LookupError, e:
|
||||
logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification body. Using fallback." % e)
|
||||
except:
|
||||
@@ -384,14 +397,14 @@ def build_notify_text(session, state):
|
||||
|
||||
if on_pause_subject and on_pause_body:
|
||||
try:
|
||||
subject_text = on_pause_subject.format(**available_params)
|
||||
subject_text = unicode(on_pause_subject).format(**available_params)
|
||||
except LookupError, e:
|
||||
logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification subject. Using fallback." % e)
|
||||
except:
|
||||
logger.error(u"PlexPy Notifier :: Unable to parse custom notification subject. Using fallback.")
|
||||
|
||||
try:
|
||||
body_text = on_pause_body.format(**available_params)
|
||||
body_text = unicode(on_pause_body).format(**available_params)
|
||||
except LookupError, e:
|
||||
logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification body. Using fallback." % e)
|
||||
except:
|
||||
@@ -408,14 +421,14 @@ def build_notify_text(session, state):
|
||||
|
||||
if on_resume_subject and on_resume_body:
|
||||
try:
|
||||
subject_text = on_resume_subject.format(**available_params)
|
||||
subject_text = unicode(on_resume_subject).format(**available_params)
|
||||
except LookupError, e:
|
||||
logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification subject. Using fallback." % e)
|
||||
except:
|
||||
logger.error(u"PlexPy Notifier :: Unable to parse custom notification subject. Using fallback.")
|
||||
|
||||
try:
|
||||
body_text = on_resume_body.format(**available_params)
|
||||
body_text = unicode(on_resume_body).format(**available_params)
|
||||
except LookupError, e:
|
||||
logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification body. Using fallback." % e)
|
||||
except:
|
||||
@@ -432,14 +445,14 @@ def build_notify_text(session, state):
|
||||
|
||||
if on_buffer_subject and on_buffer_body:
|
||||
try:
|
||||
subject_text = on_buffer_subject.format(**available_params)
|
||||
subject_text = unicode(on_buffer_subject).format(**available_params)
|
||||
except LookupError, e:
|
||||
logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification subject. Using fallback." % e)
|
||||
except:
|
||||
logger.error(u"PlexPy Notifier :: Unable to parse custom notification subject. Using fallback.")
|
||||
|
||||
try:
|
||||
body_text = on_buffer_body.format(**available_params)
|
||||
body_text = unicode(on_buffer_body).format(**available_params)
|
||||
except LookupError, e:
|
||||
logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification body. Using fallback." % e)
|
||||
except:
|
||||
@@ -456,14 +469,14 @@ def build_notify_text(session, state):
|
||||
|
||||
if on_watched_subject and on_watched_body:
|
||||
try:
|
||||
subject_text = on_watched_subject.format(**available_params)
|
||||
subject_text = unicode(on_watched_subject).format(**available_params)
|
||||
except LookupError, e:
|
||||
logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification subject. Using fallback." % e)
|
||||
except:
|
||||
logger.error(u"PlexPy Notifier :: Unable to parse custom notification subject. Using fallback.")
|
||||
|
||||
try:
|
||||
body_text = on_watched_body.format(**available_params)
|
||||
body_text = unicode(on_watched_body).format(**available_params)
|
||||
except LookupError, e:
|
||||
logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification body. Using fallback." % e)
|
||||
except:
|
||||
@@ -475,6 +488,7 @@ def build_notify_text(session, state):
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def strip_tag(data):
|
||||
import re
|
||||
|
||||
|
@@ -27,9 +27,8 @@ def refresh_users():
|
||||
|
||||
if len(result) > 0:
|
||||
for item in result:
|
||||
control_value_dict = {"username": item['username']}
|
||||
new_value_dict = {"user_id": item['user_id'],
|
||||
"username": item['username'],
|
||||
control_value_dict = {"user_id": item['user_id']}
|
||||
new_value_dict = {"username": item['username'],
|
||||
"thumb": item['thumb'],
|
||||
"email": item['email'],
|
||||
"is_home_user": item['is_home_user'],
|
||||
|
@@ -55,6 +55,7 @@ def extract_plexwatch_xml(xml=None):
|
||||
parent_title = helpers.get_xml_attr(a, 'parentTitle')
|
||||
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'):
|
||||
@@ -153,6 +154,7 @@ def extract_plexwatch_xml(xml=None):
|
||||
'grandparent_title': grandparent_title,
|
||||
'parent_title': parent_title,
|
||||
'title': title,
|
||||
'tagline': tagline,
|
||||
'guid': guid,
|
||||
'media_index': media_index,
|
||||
'originally_available_at': originally_available_at,
|
||||
@@ -356,6 +358,7 @@ def import_from_plexwatch(database=None, table_name=None, import_ignore_interval
|
||||
'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'],
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# This file is part of PlexPy.
|
||||
# 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
|
||||
@@ -90,20 +90,20 @@ class PmsConnect(object):
|
||||
return request
|
||||
|
||||
"""
|
||||
Return list of episodes in requested season.
|
||||
Return list of children in requested library item.
|
||||
|
||||
Parameters required: rating_key { ratingKey of parent }
|
||||
Optional parameters: output_format { dict, json }
|
||||
|
||||
Output: array
|
||||
"""
|
||||
def get_episode_list(self, rating_key='', output_format=''):
|
||||
def get_children_list(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
|
||||
|
||||
"""
|
||||
@@ -154,6 +154,38 @@ class PmsConnect(object):
|
||||
|
||||
return request
|
||||
|
||||
"""
|
||||
Return list of libraries on server.
|
||||
|
||||
Optional parameters: output_format { dict, json }
|
||||
|
||||
Output: array
|
||||
"""
|
||||
def get_libraries_list(self, output_format=''):
|
||||
uri = '/library/sections'
|
||||
request = self.request_handler.make_request(uri=uri,
|
||||
proto=self.protocol,
|
||||
request_type='GET',
|
||||
output_format=output_format)
|
||||
|
||||
return request
|
||||
|
||||
"""
|
||||
Return list of items in library on server.
|
||||
|
||||
Optional parameters: output_format { dict, json }
|
||||
|
||||
Output: array
|
||||
"""
|
||||
def get_library_list(self, section_key='', list_type='all', count='0', sort_type='', output_format=''):
|
||||
uri = '/library/sections/' + section_key + '/' + list_type +'?X-Plex-Container-Start=0&X-Plex-Container-Size=' + count + sort_type
|
||||
request = self.request_handler.make_request(uri=uri,
|
||||
proto=self.protocol,
|
||||
request_type='GET',
|
||||
output_format=output_format)
|
||||
|
||||
return request
|
||||
|
||||
"""
|
||||
Return sync item details.
|
||||
|
||||
@@ -313,6 +345,7 @@ class PmsConnect(object):
|
||||
'title': helpers.get_xml_attr(metadata_main, 'title'),
|
||||
'content_rating': helpers.get_xml_attr(metadata_main, 'contentRating'),
|
||||
'summary': helpers.get_xml_attr(metadata_main, 'summary'),
|
||||
'tagline': helpers.get_xml_attr(metadata_main, 'tagline'),
|
||||
'rating': helpers.get_xml_attr(metadata_main, 'rating'),
|
||||
'duration': helpers.get_xml_attr(metadata_main, 'duration'),
|
||||
'year': helpers.get_xml_attr(metadata_main, 'year'),
|
||||
@@ -331,9 +364,44 @@ class PmsConnect(object):
|
||||
'actors': actors
|
||||
}
|
||||
metadata_list = {'metadata': metadata}
|
||||
elif metadata_type == 'season':
|
||||
parent_rating_key = helpers.get_xml_attr(metadata_main, 'parentRatingKey')
|
||||
show_details = self.get_metadata_details(parent_rating_key)
|
||||
metadata = {'type': metadata_type,
|
||||
'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'),
|
||||
'parent_rating_key': helpers.get_xml_attr(metadata_main, 'parentRatingKey'),
|
||||
'grandparent_title': helpers.get_xml_attr(metadata_main, 'grandparentTitle'),
|
||||
'parent_index': helpers.get_xml_attr(metadata_main, 'parentIndex'),
|
||||
'parent_title': helpers.get_xml_attr(metadata_main, 'parentTitle'),
|
||||
'index': helpers.get_xml_attr(metadata_main, 'index'),
|
||||
'studio': helpers.get_xml_attr(metadata_main, 'studio'),
|
||||
'title': helpers.get_xml_attr(metadata_main, 'title'),
|
||||
'content_rating': helpers.get_xml_attr(metadata_main, 'contentRating'),
|
||||
'summary': show_details['metadata']['summary'],
|
||||
'tagline': helpers.get_xml_attr(metadata_main, 'tagline'),
|
||||
'rating': helpers.get_xml_attr(metadata_main, 'rating'),
|
||||
'duration': show_details['metadata']['duration'],
|
||||
'year': helpers.get_xml_attr(metadata_main, 'year'),
|
||||
'thumb': helpers.get_xml_attr(metadata_main, 'thumb'),
|
||||
'parent_thumb': helpers.get_xml_attr(metadata_main, 'parentThumb'),
|
||||
'grandparent_thumb': helpers.get_xml_attr(metadata_main, 'grandparentThumb'),
|
||||
'art': helpers.get_xml_attr(metadata_main, 'art'),
|
||||
'originally_available_at': helpers.get_xml_attr(metadata_main, 'originallyAvailableAt'),
|
||||
'added_at': helpers.get_xml_attr(metadata_main, 'addedAt'),
|
||||
'updated_at': helpers.get_xml_attr(metadata_main, 'updatedAt'),
|
||||
'last_viewed_at': helpers.get_xml_attr(metadata_main, 'lastViewedAt'),
|
||||
'guid': helpers.get_xml_attr(metadata_main, 'guid'),
|
||||
'genres': genres,
|
||||
'actors': actors,
|
||||
'writers': writers,
|
||||
'directors': directors
|
||||
}
|
||||
metadata_list = {'metadata': metadata}
|
||||
elif metadata_type == 'episode':
|
||||
metadata = {'type': metadata_type,
|
||||
'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'),
|
||||
'parent_rating_key': helpers.get_xml_attr(metadata_main, 'parentRatingKey'),
|
||||
'grandparent_rating_key': helpers.get_xml_attr(metadata_main, 'grandparentRatingKey'),
|
||||
'grandparent_title': helpers.get_xml_attr(metadata_main, 'grandparentTitle'),
|
||||
'parent_index': helpers.get_xml_attr(metadata_main, 'parentIndex'),
|
||||
'parent_title': helpers.get_xml_attr(metadata_main, 'parentTitle'),
|
||||
@@ -342,6 +410,7 @@ class PmsConnect(object):
|
||||
'title': helpers.get_xml_attr(metadata_main, 'title'),
|
||||
'content_rating': helpers.get_xml_attr(metadata_main, 'contentRating'),
|
||||
'summary': helpers.get_xml_attr(metadata_main, 'summary'),
|
||||
'tagline': helpers.get_xml_attr(metadata_main, 'tagline'),
|
||||
'rating': helpers.get_xml_attr(metadata_main, 'rating'),
|
||||
'duration': helpers.get_xml_attr(metadata_main, 'duration'),
|
||||
'year': helpers.get_xml_attr(metadata_main, 'year'),
|
||||
@@ -371,6 +440,7 @@ class PmsConnect(object):
|
||||
'title': helpers.get_xml_attr(metadata_main, 'title'),
|
||||
'content_rating': helpers.get_xml_attr(metadata_main, 'contentRating'),
|
||||
'summary': helpers.get_xml_attr(metadata_main, 'summary'),
|
||||
'tagline': helpers.get_xml_attr(metadata_main, 'tagline'),
|
||||
'rating': helpers.get_xml_attr(metadata_main, 'rating'),
|
||||
'duration': helpers.get_xml_attr(metadata_main, 'duration'),
|
||||
'year': helpers.get_xml_attr(metadata_main, 'year'),
|
||||
@@ -389,9 +459,7 @@ class PmsConnect(object):
|
||||
'directors': directors
|
||||
}
|
||||
metadata_list = {'metadata': metadata}
|
||||
elif metadata_type == 'season':
|
||||
parent_rating_key = helpers.get_xml_attr(metadata_main, 'parentRatingKey')
|
||||
show_details = self.get_metadata_details(parent_rating_key)
|
||||
elif metadata_type == 'artist':
|
||||
metadata = {'type': metadata_type,
|
||||
'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'),
|
||||
'grandparent_title': helpers.get_xml_attr(metadata_main, 'grandparentTitle'),
|
||||
@@ -401,9 +469,43 @@ class PmsConnect(object):
|
||||
'studio': helpers.get_xml_attr(metadata_main, 'studio'),
|
||||
'title': helpers.get_xml_attr(metadata_main, 'title'),
|
||||
'content_rating': helpers.get_xml_attr(metadata_main, 'contentRating'),
|
||||
'summary': show_details['metadata']['summary'],
|
||||
'summary': helpers.get_xml_attr(metadata_main, 'summary'),
|
||||
'tagline': helpers.get_xml_attr(metadata_main, 'tagline'),
|
||||
'rating': helpers.get_xml_attr(metadata_main, 'rating'),
|
||||
'duration': show_details['metadata']['duration'],
|
||||
'duration': helpers.get_xml_attr(metadata_main, 'duration'),
|
||||
'year': helpers.get_xml_attr(metadata_main, 'year'),
|
||||
'thumb': helpers.get_xml_attr(metadata_main, 'thumb'),
|
||||
'parent_thumb': helpers.get_xml_attr(metadata_main, 'parentThumb'),
|
||||
'grandparent_thumb': helpers.get_xml_attr(metadata_main, 'grandparentThumb'),
|
||||
'art': helpers.get_xml_attr(metadata_main, 'art'),
|
||||
'originally_available_at': helpers.get_xml_attr(metadata_main, 'originallyAvailableAt'),
|
||||
'added_at': helpers.get_xml_attr(metadata_main, 'addedAt'),
|
||||
'updated_at': helpers.get_xml_attr(metadata_main, 'updatedAt'),
|
||||
'last_viewed_at': helpers.get_xml_attr(metadata_main, 'lastViewedAt'),
|
||||
'guid': helpers.get_xml_attr(metadata_main, 'guid'),
|
||||
'writers': writers,
|
||||
'directors': directors,
|
||||
'genres': genres,
|
||||
'actors': actors
|
||||
}
|
||||
metadata_list = {'metadata': metadata}
|
||||
elif metadata_type == 'album':
|
||||
parent_rating_key = helpers.get_xml_attr(metadata_main, 'parentRatingKey')
|
||||
artist_details = self.get_metadata_details(parent_rating_key)
|
||||
metadata = {'type': metadata_type,
|
||||
'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'),
|
||||
'parent_rating_key': helpers.get_xml_attr(metadata_main, 'parentRatingKey'),
|
||||
'grandparent_title': helpers.get_xml_attr(metadata_main, 'grandparentTitle'),
|
||||
'parent_index': helpers.get_xml_attr(metadata_main, 'parentIndex'),
|
||||
'parent_title': helpers.get_xml_attr(metadata_main, 'parentTitle'),
|
||||
'index': helpers.get_xml_attr(metadata_main, 'index'),
|
||||
'studio': helpers.get_xml_attr(metadata_main, 'studio'),
|
||||
'title': helpers.get_xml_attr(metadata_main, 'title'),
|
||||
'content_rating': helpers.get_xml_attr(metadata_main, 'contentRating'),
|
||||
'summary': artist_details['metadata']['summary'],
|
||||
'tagline': helpers.get_xml_attr(metadata_main, 'tagline'),
|
||||
'rating': helpers.get_xml_attr(metadata_main, 'rating'),
|
||||
'duration': helpers.get_xml_attr(metadata_main, 'duration'),
|
||||
'year': helpers.get_xml_attr(metadata_main, 'year'),
|
||||
'thumb': helpers.get_xml_attr(metadata_main, 'thumb'),
|
||||
'parent_thumb': helpers.get_xml_attr(metadata_main, 'parentThumb'),
|
||||
@@ -421,8 +523,12 @@ class PmsConnect(object):
|
||||
}
|
||||
metadata_list = {'metadata': metadata}
|
||||
elif metadata_type == 'track':
|
||||
parent_rating_key = helpers.get_xml_attr(metadata_main, 'parentRatingKey')
|
||||
album_details = self.get_metadata_details(parent_rating_key)
|
||||
metadata = {'type': metadata_type,
|
||||
'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'),
|
||||
'parent_rating_key': helpers.get_xml_attr(metadata_main, 'parentRatingKey'),
|
||||
'grandparent_rating_key': helpers.get_xml_attr(metadata_main, 'grandparentRatingKey'),
|
||||
'grandparent_title': helpers.get_xml_attr(metadata_main, 'grandparentTitle'),
|
||||
'parent_index': helpers.get_xml_attr(metadata_main, 'parentIndex'),
|
||||
'parent_title': helpers.get_xml_attr(metadata_main, 'parentTitle'),
|
||||
@@ -431,9 +537,10 @@ class PmsConnect(object):
|
||||
'title': helpers.get_xml_attr(metadata_main, 'title'),
|
||||
'content_rating': helpers.get_xml_attr(metadata_main, 'contentRating'),
|
||||
'summary': helpers.get_xml_attr(metadata_main, 'summary'),
|
||||
'tagline': helpers.get_xml_attr(metadata_main, 'tagline'),
|
||||
'rating': helpers.get_xml_attr(metadata_main, 'rating'),
|
||||
'duration': helpers.get_xml_attr(metadata_main, 'duration'),
|
||||
'year': helpers.get_xml_attr(metadata_main, 'year'),
|
||||
'year': album_details['metadata']['year'],
|
||||
'thumb': helpers.get_xml_attr(metadata_main, 'thumb'),
|
||||
'parent_thumb': helpers.get_xml_attr(metadata_main, 'parentThumb'),
|
||||
'grandparent_thumb': helpers.get_xml_attr(metadata_main, 'grandparentThumb'),
|
||||
@@ -490,6 +597,12 @@ class PmsConnect(object):
|
||||
for session in session_data:
|
||||
session_output = self.get_session_each(session_type, session)
|
||||
session_list.append(session_output)
|
||||
if a.getElementsByTagName('Photo'):
|
||||
session_data = a.getElementsByTagName('Photo')
|
||||
session_type = 'photo'
|
||||
for session in session_data:
|
||||
session_output = self.get_session_each(session_type, session)
|
||||
session_list.append(session_output)
|
||||
|
||||
output = {'stream_count': helpers.get_xml_attr(xml_head[0], 'size'),
|
||||
'sessions': session_list
|
||||
@@ -508,8 +621,8 @@ class PmsConnect(object):
|
||||
def get_session_each(self, stream_type='', session=None):
|
||||
session_output = None
|
||||
user_data = users.Users()
|
||||
if stream_type == 'track':
|
||||
|
||||
if stream_type == 'track':
|
||||
media_info = session.getElementsByTagName('Media')[0]
|
||||
audio_decision = 'direct play'
|
||||
audio_channels = helpers.get_xml_attr(media_info, 'audioChannels')
|
||||
@@ -521,6 +634,9 @@ class PmsConnect(object):
|
||||
|
||||
if session.getElementsByTagName('TranscodeSession'):
|
||||
transcode_session = session.getElementsByTagName('TranscodeSession')[0]
|
||||
throttled = helpers.get_xml_attr(transcode_session, 'throttled')
|
||||
transcode_progress = helpers.get_xml_attr(transcode_session, 'progress')
|
||||
transcode_speed = helpers.get_xml_attr(transcode_session, 'speed')
|
||||
audio_decision = helpers.get_xml_attr(transcode_session, 'audioDecision')
|
||||
transcode_audio_channels = helpers.get_xml_attr(transcode_session, 'audioChannels')
|
||||
transcode_audio_codec = helpers.get_xml_attr(transcode_session, 'audioCodec')
|
||||
@@ -528,6 +644,9 @@ class PmsConnect(object):
|
||||
transcode_protocol = helpers.get_xml_attr(transcode_session, 'protocol')
|
||||
duration = helpers.get_xml_attr(transcode_session, 'duration')
|
||||
else:
|
||||
throttled = '0'
|
||||
transcode_progress = '0'
|
||||
transcode_speed = ''
|
||||
transcode_audio_channels = ''
|
||||
transcode_audio_codec = ''
|
||||
transcode_container = ''
|
||||
@@ -552,6 +671,7 @@ class PmsConnect(object):
|
||||
'user': user_details['username'],
|
||||
'user_id': user_details['user_id'],
|
||||
'friendly_name': user_details['friendly_name'],
|
||||
'user_thumb': user_details['thumb'],
|
||||
'player': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'title'),
|
||||
'platform': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'platform'),
|
||||
'machine_id': machine_id,
|
||||
@@ -562,6 +682,9 @@ class PmsConnect(object):
|
||||
'rating_key': helpers.get_xml_attr(session, 'ratingKey'),
|
||||
'parent_rating_key': helpers.get_xml_attr(session, 'parentRatingKey'),
|
||||
'grandparent_rating_key': helpers.get_xml_attr(session, 'grandparentRatingKey'),
|
||||
'throttled': throttled,
|
||||
'transcode_progress': transcode_progress,
|
||||
'transcode_speed': str(round(helpers.cast_to_float(transcode_speed), 1)),
|
||||
'audio_decision': audio_decision,
|
||||
'audio_channels': audio_channels,
|
||||
'audio_codec': audio_codec,
|
||||
@@ -587,6 +710,7 @@ class PmsConnect(object):
|
||||
'type': 'track',
|
||||
'indexes': 0
|
||||
}
|
||||
|
||||
elif stream_type == 'video':
|
||||
media_info = session.getElementsByTagName('Media')[0]
|
||||
audio_decision = 'direct play'
|
||||
@@ -606,6 +730,9 @@ class PmsConnect(object):
|
||||
|
||||
if session.getElementsByTagName('TranscodeSession'):
|
||||
transcode_session = session.getElementsByTagName('TranscodeSession')[0]
|
||||
throttled = helpers.get_xml_attr(transcode_session, 'throttled')
|
||||
transcode_progress = helpers.get_xml_attr(transcode_session, 'progress')
|
||||
transcode_speed = helpers.get_xml_attr(transcode_session, 'speed')
|
||||
audio_decision = helpers.get_xml_attr(transcode_session, 'audioDecision')
|
||||
transcode_audio_channels = helpers.get_xml_attr(transcode_session, 'audioChannels')
|
||||
transcode_audio_codec = helpers.get_xml_attr(transcode_session, 'audioCodec')
|
||||
@@ -616,6 +743,9 @@ class PmsConnect(object):
|
||||
transcode_container = helpers.get_xml_attr(transcode_session, 'container')
|
||||
transcode_protocol = helpers.get_xml_attr(transcode_session, 'protocol')
|
||||
else:
|
||||
throttled = '0'
|
||||
transcode_progress = '0'
|
||||
transcode_speed = ''
|
||||
transcode_audio_channels = ''
|
||||
transcode_audio_codec = ''
|
||||
transcode_video_codec = ''
|
||||
@@ -661,6 +791,7 @@ class PmsConnect(object):
|
||||
'user': user_details['username'],
|
||||
'user_id': user_details['user_id'],
|
||||
'friendly_name': user_details['friendly_name'],
|
||||
'user_thumb': user_details['thumb'],
|
||||
'player': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'title'),
|
||||
'platform': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'platform'),
|
||||
'machine_id': machine_id,
|
||||
@@ -668,9 +799,13 @@ class PmsConnect(object):
|
||||
'grandparent_title': helpers.get_xml_attr(session, 'grandparentTitle'),
|
||||
'parent_title': helpers.get_xml_attr(session, 'parentTitle'),
|
||||
'title': helpers.get_xml_attr(session, 'title'),
|
||||
'year': helpers.get_xml_attr(session, 'year'),
|
||||
'rating_key': helpers.get_xml_attr(session, 'ratingKey'),
|
||||
'parent_rating_key': helpers.get_xml_attr(session, 'parentRatingKey'),
|
||||
'grandparent_rating_key': helpers.get_xml_attr(session, 'grandparentRatingKey'),
|
||||
'throttled': throttled,
|
||||
'transcode_progress': transcode_progress,
|
||||
'transcode_speed': str(round(helpers.cast_to_float(transcode_speed), 1)),
|
||||
'audio_decision': audio_decision,
|
||||
'audio_channels': audio_channels,
|
||||
'audio_codec': audio_codec,
|
||||
@@ -693,9 +828,13 @@ class PmsConnect(object):
|
||||
'duration': duration,
|
||||
'progress': progress,
|
||||
'progress_percent': str(helpers.get_percent(progress, duration)),
|
||||
'type': helpers.get_xml_attr(session, 'type'),
|
||||
'indexes': use_indexes
|
||||
}
|
||||
if helpers.get_xml_attr(session, 'ratingKey').isdigit():
|
||||
session_output['type'] = helpers.get_xml_attr(session, 'type')
|
||||
else:
|
||||
session_output['type'] = 'clip'
|
||||
|
||||
elif helpers.get_xml_attr(session, 'type') == 'movie':
|
||||
session_output = {'session_key': helpers.get_xml_attr(session, 'sessionKey'),
|
||||
'media_index': helpers.get_xml_attr(session, 'index'),
|
||||
@@ -708,6 +847,7 @@ class PmsConnect(object):
|
||||
'user': user_details['username'],
|
||||
'user_id': user_details['user_id'],
|
||||
'friendly_name': user_details['friendly_name'],
|
||||
'user_thumb': user_details['thumb'],
|
||||
'player': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'title'),
|
||||
'platform': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'platform'),
|
||||
'machine_id': machine_id,
|
||||
@@ -715,9 +855,13 @@ class PmsConnect(object):
|
||||
'grandparent_title': helpers.get_xml_attr(session, 'grandparentTitle'),
|
||||
'parent_title': helpers.get_xml_attr(session, 'parentTitle'),
|
||||
'title': helpers.get_xml_attr(session, 'title'),
|
||||
'year': helpers.get_xml_attr(session, 'year'),
|
||||
'rating_key': helpers.get_xml_attr(session, 'ratingKey'),
|
||||
'parent_rating_key': helpers.get_xml_attr(session, 'parentRatingKey'),
|
||||
'grandparent_rating_key': helpers.get_xml_attr(session, 'grandparentRatingKey'),
|
||||
'throttled': throttled,
|
||||
'transcode_progress': transcode_progress,
|
||||
'transcode_speed': str(round(helpers.cast_to_float(transcode_speed), 1)),
|
||||
'audio_decision': audio_decision,
|
||||
'audio_channels': audio_channels,
|
||||
'audio_codec': audio_codec,
|
||||
@@ -740,9 +884,13 @@ class PmsConnect(object):
|
||||
'duration': duration,
|
||||
'progress': progress,
|
||||
'progress_percent': str(helpers.get_percent(progress, duration)),
|
||||
'type': helpers.get_xml_attr(session, 'type'),
|
||||
'indexes': use_indexes
|
||||
}
|
||||
if helpers.get_xml_attr(session, 'ratingKey').isdigit():
|
||||
session_output['type'] = helpers.get_xml_attr(session, 'type')
|
||||
else:
|
||||
session_output['type'] = 'clip'
|
||||
|
||||
elif helpers.get_xml_attr(session, 'type') == 'clip':
|
||||
session_output = {'session_key': helpers.get_xml_attr(session, 'sessionKey'),
|
||||
'media_index': helpers.get_xml_attr(session, 'index'),
|
||||
@@ -755,6 +903,7 @@ class PmsConnect(object):
|
||||
'user': user_details['username'],
|
||||
'user_id': user_details['user_id'],
|
||||
'friendly_name': user_details['friendly_name'],
|
||||
'user_thumb': user_details['thumb'],
|
||||
'player': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'title'),
|
||||
'platform': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'platform'),
|
||||
'machine_id': machine_id,
|
||||
@@ -762,9 +911,13 @@ class PmsConnect(object):
|
||||
'grandparent_title': helpers.get_xml_attr(session, 'grandparentTitle'),
|
||||
'parent_title': helpers.get_xml_attr(session, 'parentTitle'),
|
||||
'title': helpers.get_xml_attr(session, 'title'),
|
||||
'year': helpers.get_xml_attr(session, 'year'),
|
||||
'rating_key': helpers.get_xml_attr(session, 'ratingKey'),
|
||||
'parent_rating_key': helpers.get_xml_attr(session, 'parentRatingKey'),
|
||||
'grandparent_rating_key': helpers.get_xml_attr(session, 'grandparentRatingKey'),
|
||||
'throttled': throttled,
|
||||
'transcode_progress': transcode_progress,
|
||||
'transcode_speed': str(round(helpers.cast_to_float(transcode_speed), 1)),
|
||||
'audio_decision': audio_decision,
|
||||
'audio_channels': audio_channels,
|
||||
'audio_codec': audio_codec,
|
||||
@@ -790,49 +943,151 @@ class PmsConnect(object):
|
||||
'type': helpers.get_xml_attr(session, 'type'),
|
||||
'indexes': 0
|
||||
}
|
||||
|
||||
elif stream_type == 'photo':
|
||||
media_info = session.getElementsByTagName('Media')[0]
|
||||
video_decision = 'direct play'
|
||||
container = helpers.get_xml_attr(media_info, 'container')
|
||||
aspect_ratio = helpers.get_xml_attr(media_info, 'aspectRatio')
|
||||
width = helpers.get_xml_attr(media_info, 'width')
|
||||
height = helpers.get_xml_attr(media_info, 'height')
|
||||
|
||||
if session.getElementsByTagName('TranscodeSession'):
|
||||
transcode_session = session.getElementsByTagName('TranscodeSession')[0]
|
||||
throttled = helpers.get_xml_attr(transcode_session, 'throttled')
|
||||
transcode_progress = helpers.get_xml_attr(transcode_session, 'progress')
|
||||
transcode_speed = helpers.get_xml_attr(transcode_session, 'speed')
|
||||
video_decision = helpers.get_xml_attr(transcode_session, 'videoDecision')
|
||||
transcode_video_codec = helpers.get_xml_attr(transcode_session, 'videoCodec')
|
||||
transcode_width = helpers.get_xml_attr(transcode_session, 'width')
|
||||
transcode_height = helpers.get_xml_attr(transcode_session, 'height')
|
||||
transcode_container = helpers.get_xml_attr(transcode_session, 'container')
|
||||
transcode_protocol = helpers.get_xml_attr(transcode_session, 'protocol')
|
||||
else:
|
||||
throttled = '0'
|
||||
transcode_progress = '0'
|
||||
transcode_speed = ''
|
||||
transcode_video_codec = ''
|
||||
transcode_width = ''
|
||||
transcode_height = ''
|
||||
transcode_container = ''
|
||||
transcode_protocol = ''
|
||||
|
||||
user_details = user_data.get_user_details(
|
||||
user=helpers.get_xml_attr(session.getElementsByTagName('User')[0], 'title'))
|
||||
|
||||
if helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'machineIdentifier').endswith('_Photo'):
|
||||
machine_id = helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'machineIdentifier')[:-6]
|
||||
else:
|
||||
machine_id = helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'machineIdentifier')
|
||||
|
||||
session_output = {'session_key': helpers.get_xml_attr(session, 'sessionKey'),
|
||||
'media_index': helpers.get_xml_attr(session, 'index'),
|
||||
'parent_media_index': helpers.get_xml_attr(session, 'parentIndex'),
|
||||
'art': helpers.get_xml_attr(session, 'art'),
|
||||
'parent_thumb': helpers.get_xml_attr(session, 'parentThumb'),
|
||||
'grandparent_thumb': helpers.get_xml_attr(session, 'grandparentThumb'),
|
||||
'thumb': helpers.get_xml_attr(session, 'thumb'),
|
||||
'bif_thumb': '',
|
||||
'user': user_details['username'],
|
||||
'user_id': user_details['user_id'],
|
||||
'friendly_name': user_details['friendly_name'],
|
||||
'user_thumb': user_details['thumb'],
|
||||
'player': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'title'),
|
||||
'platform': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'platform'),
|
||||
'machine_id': machine_id,
|
||||
'state': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'state'),
|
||||
'grandparent_title': helpers.get_xml_attr(session, 'grandparentTitle'),
|
||||
'parent_title': helpers.get_xml_attr(session, 'parentTitle'),
|
||||
'title': helpers.get_xml_attr(session, 'title'),
|
||||
'year': helpers.get_xml_attr(session, 'year'),
|
||||
'rating_key': helpers.get_xml_attr(session, 'ratingKey'),
|
||||
'parent_rating_key': helpers.get_xml_attr(session, 'parentRatingKey'),
|
||||
'grandparent_rating_key': helpers.get_xml_attr(session, 'grandparentRatingKey'),
|
||||
'throttled': throttled,
|
||||
'transcode_progress': transcode_progress,
|
||||
'transcode_speed': str(round(helpers.cast_to_float(transcode_speed), 1)),
|
||||
'audio_decision': '',
|
||||
'audio_channels': '',
|
||||
'audio_codec': '',
|
||||
'video_decision': video_decision,
|
||||
'video_codec': '',
|
||||
'height': height,
|
||||
'width': width,
|
||||
'container': container,
|
||||
'bitrate': '',
|
||||
'video_resolution': '',
|
||||
'video_framerate': '',
|
||||
'aspect_ratio': aspect_ratio,
|
||||
'transcode_audio_channels': '',
|
||||
'transcode_audio_codec': '',
|
||||
'transcode_video_codec': transcode_video_codec,
|
||||
'transcode_width': transcode_width,
|
||||
'transcode_height': transcode_height,
|
||||
'transcode_container': transcode_container,
|
||||
'transcode_protocol': transcode_protocol,
|
||||
'duration': '',
|
||||
'progress': '',
|
||||
'progress_percent': '100',
|
||||
'type': 'photo',
|
||||
'indexes': 0
|
||||
}
|
||||
|
||||
else:
|
||||
logger.warn(u"No known stream types found in session list.")
|
||||
|
||||
return session_output
|
||||
|
||||
"""
|
||||
Return processed and validated episode list.
|
||||
Return processed and validated children list.
|
||||
|
||||
Output: array
|
||||
"""
|
||||
def get_season_children(self, rating_key=''):
|
||||
episode_data = self.get_episode_list(rating_key, output_format='xml')
|
||||
def get_item_children(self, rating_key=''):
|
||||
children_data = self.get_children_list(rating_key, output_format='xml')
|
||||
|
||||
try:
|
||||
xml_head = episode_data.getElementsByTagName('MediaContainer')
|
||||
xml_head = children_data.getElementsByTagName('MediaContainer')
|
||||
except:
|
||||
logger.warn("Unable to parse XML for get_episode_list.")
|
||||
logger.warn("Unable to parse XML for get_children_list.")
|
||||
return []
|
||||
|
||||
episode_list = []
|
||||
children_list = []
|
||||
|
||||
for a in xml_head:
|
||||
if a.getAttribute('size'):
|
||||
if a.getAttribute('size') == '0':
|
||||
logger.debug(u"No episode data.")
|
||||
episode_list = {'episode_count': '0',
|
||||
'episode_list': []
|
||||
}
|
||||
return episode_list
|
||||
logger.debug(u"No children data.")
|
||||
children_list = {'children_count': '0',
|
||||
'children_list': []
|
||||
}
|
||||
return parent_list
|
||||
|
||||
result_data = []
|
||||
|
||||
if a.getElementsByTagName('Directory'):
|
||||
result_data = a.getElementsByTagName('Directory')
|
||||
if a.getElementsByTagName('Video'):
|
||||
result_data = a.getElementsByTagName('Video')
|
||||
for result in result_data:
|
||||
episode_output = {'rating_key': helpers.get_xml_attr(result, 'ratingKey'),
|
||||
'index': helpers.get_xml_attr(result, 'index'),
|
||||
'title': helpers.get_xml_attr(result, 'title'),
|
||||
'thumb': helpers.get_xml_attr(result, 'thumb')
|
||||
}
|
||||
episode_list.append(episode_output)
|
||||
if a.getElementsByTagName('Track'):
|
||||
result_data = a.getElementsByTagName('Track')
|
||||
|
||||
output = {'episode_count': helpers.get_xml_attr(xml_head[0], 'size'),
|
||||
if result_data:
|
||||
for result in result_data:
|
||||
children_output = {'rating_key': helpers.get_xml_attr(result, 'ratingKey'),
|
||||
'index': helpers.get_xml_attr(result, 'index'),
|
||||
'title': helpers.get_xml_attr(result, 'title'),
|
||||
'thumb': helpers.get_xml_attr(result, 'thumb'),
|
||||
'parent_thumb': helpers.get_xml_attr(a, 'thumb'),
|
||||
'duration': helpers.get_xml_attr(result, 'duration')
|
||||
}
|
||||
children_list.append(children_output)
|
||||
|
||||
|
||||
output = {'children_count': helpers.get_xml_attr(xml_head[0], 'size'),
|
||||
'children_type': helpers.get_xml_attr(xml_head[0], 'viewGroup'),
|
||||
'title': helpers.get_xml_attr(xml_head[0], 'title2'),
|
||||
'episode_list': episode_list
|
||||
'children_list': children_list
|
||||
}
|
||||
|
||||
return output
|
||||
@@ -914,6 +1169,150 @@ class PmsConnect(object):
|
||||
logger.debug(u"Server preferences queried but no parameter received.")
|
||||
return None
|
||||
|
||||
"""
|
||||
Return processed and validated server libraries list.
|
||||
|
||||
Output: array
|
||||
"""
|
||||
def get_server_children(self):
|
||||
libraries_data = self.get_libraries_list(output_format='xml')
|
||||
|
||||
try:
|
||||
xml_head = libraries_data.getElementsByTagName('MediaContainer')
|
||||
except:
|
||||
logger.warn("Unable to parse XML for get_libraries_list.")
|
||||
return []
|
||||
|
||||
libraries_list = []
|
||||
|
||||
for a in xml_head:
|
||||
if a.getAttribute('size'):
|
||||
if a.getAttribute('size') == '0':
|
||||
logger.debug(u"No libraries data.")
|
||||
libraries_list = {'libraries_count': '0',
|
||||
'libraries_list': []
|
||||
}
|
||||
return libraries_list
|
||||
|
||||
if a.getElementsByTagName('Directory'):
|
||||
result_data = a.getElementsByTagName('Directory')
|
||||
for result in result_data:
|
||||
libraries_output = {'key': helpers.get_xml_attr(result, 'key'),
|
||||
'type': helpers.get_xml_attr(result, 'type'),
|
||||
'title': helpers.get_xml_attr(result, 'title'),
|
||||
'thumb': helpers.get_xml_attr(result, 'thumb')
|
||||
}
|
||||
libraries_list.append(libraries_output)
|
||||
|
||||
output = {'libraries_count': helpers.get_xml_attr(xml_head[0], 'size'),
|
||||
'title': helpers.get_xml_attr(xml_head[0], 'title1'),
|
||||
'libraries_list': libraries_list
|
||||
}
|
||||
|
||||
return output
|
||||
|
||||
"""
|
||||
Return processed and validated server library items list.
|
||||
|
||||
Parameters required: library_type { movie, show, episode, artist }
|
||||
section_key { unique library key }
|
||||
|
||||
Output: array
|
||||
"""
|
||||
def get_library_children(self, library_type='', section_key='', list_type='all', sort_type = ''):
|
||||
|
||||
# Currently only grab the library with 1 items so 'size' is not 0
|
||||
count = '1'
|
||||
|
||||
if library_type == 'movie':
|
||||
sort_type = '&type=1'
|
||||
elif library_type == 'show':
|
||||
sort_type = '&type=2'
|
||||
elif library_type == 'episode':
|
||||
sort_type = '&type=4'
|
||||
elif library_type == 'album':
|
||||
list_type = 'albums'
|
||||
|
||||
library_data = self.get_library_list(section_key, list_type, count, sort_type, output_format='xml')
|
||||
|
||||
try:
|
||||
xml_head = library_data.getElementsByTagName('MediaContainer')
|
||||
except:
|
||||
logger.warn("Unable to parse XML for get_library_children.")
|
||||
return []
|
||||
|
||||
library_list = []
|
||||
|
||||
for a in xml_head:
|
||||
if a.getAttribute('size'):
|
||||
if a.getAttribute('size') == '0':
|
||||
logger.debug(u"No library data.")
|
||||
library_list = {'library_count': '0',
|
||||
'library_list': []
|
||||
}
|
||||
return library_list
|
||||
|
||||
if a.getElementsByTagName('Directory'):
|
||||
result_data = a.getElementsByTagName('Directory')
|
||||
for result in result_data:
|
||||
library_output = {'key': helpers.get_xml_attr(result, 'key'),
|
||||
'type': helpers.get_xml_attr(result, 'type'),
|
||||
'title': helpers.get_xml_attr(result, 'title'),
|
||||
'thumb': helpers.get_xml_attr(result, 'thumb')
|
||||
}
|
||||
library_list.append(library_output)
|
||||
|
||||
output = {'library_count': helpers.get_xml_attr(xml_head[0], 'totalSize'),
|
||||
'count_type': helpers.get_xml_attr(xml_head[0], 'title2'),
|
||||
'library_list': library_list
|
||||
}
|
||||
|
||||
return output
|
||||
|
||||
"""
|
||||
Return processed and validated server statistics.
|
||||
|
||||
Output: array
|
||||
"""
|
||||
def get_library_stats(self):
|
||||
server_libraries = self.get_server_children()
|
||||
|
||||
server_library_stats = []
|
||||
|
||||
if server_libraries['libraries_count'] != '0':
|
||||
libraries_list = server_libraries['libraries_list']
|
||||
|
||||
for library in libraries_list:
|
||||
library_type = library['type']
|
||||
section_key = library['key']
|
||||
library_list = self.get_library_children(library_type, section_key)
|
||||
|
||||
if library_list['library_count'] != '0':
|
||||
library_stats = {'title': library['title'],
|
||||
'thumb': library['thumb'],
|
||||
'count': library_list['library_count'],
|
||||
'count_type': library_list['count_type']
|
||||
}
|
||||
|
||||
if library_type == 'show':
|
||||
episode_list = self.get_library_children(library_type='episode', section_key=section_key)
|
||||
episode_stats = {'episode_count': episode_list['library_count'],
|
||||
'episode_count_type': 'All Episodes'
|
||||
}
|
||||
library_stats.update(episode_stats)
|
||||
|
||||
if library_type == 'artist':
|
||||
album_list = self.get_library_children(library_type='album', section_key=section_key)
|
||||
album_stats = {'album_count': album_list['library_count'],
|
||||
'album_count_type': 'All Albums'
|
||||
}
|
||||
library_stats.update(album_stats)
|
||||
|
||||
server_library_stats.append({'type': library_type,
|
||||
'rows': library_stats})
|
||||
|
||||
return server_library_stats
|
||||
|
||||
"""
|
||||
Return image data as array.
|
||||
Array contains the image content type and image binary
|
||||
|
106
plexpy/users.py
106
plexpy/users.py
@@ -1,4 +1,4 @@
|
||||
# This file is part of PlexPy.
|
||||
# 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
|
||||
@@ -24,23 +24,40 @@ class Users(object):
|
||||
def get_user_list(self, kwargs=None):
|
||||
data_tables = datatables.DataTables()
|
||||
|
||||
columns = ['users.user_id as user_id',
|
||||
'users.custom_avatar_url as thumb',
|
||||
columns = ['session_history.id',
|
||||
'users.user_id as user_id',
|
||||
'users.custom_avatar_url as user_thumb',
|
||||
'(case when users.friendly_name is null then users.username else \
|
||||
users.friendly_name end) as friendly_name',
|
||||
'MAX(session_history.started) as last_seen',
|
||||
'session_history.ip_address as ip_address',
|
||||
'COUNT(session_history.id) as plays',
|
||||
'users.username as user'
|
||||
'session_history.player as platform',
|
||||
'session_history_metadata.full_title as last_watched',
|
||||
'session_history_metadata.thumb',
|
||||
'session_history_metadata.parent_thumb',
|
||||
'session_history_metadata.grandparent_thumb',
|
||||
'session_history_metadata.media_type',
|
||||
'session_history.rating_key as rating_key',
|
||||
'session_history_media_info.video_decision',
|
||||
'users.username as user',
|
||||
'users.do_notify as do_notify',
|
||||
'users.keep_history as keep_history'
|
||||
]
|
||||
try:
|
||||
query = data_tables.ssp_query(table_name='users',
|
||||
columns=columns,
|
||||
custom_where=[],
|
||||
group_by=['users.user_id'],
|
||||
join_types=['LEFT OUTER JOIN'],
|
||||
join_tables=['session_history'],
|
||||
join_evals=[['session_history.user_id', 'users.user_id']],
|
||||
join_types=['LEFT OUTER JOIN',
|
||||
'LEFT OUTER JOIN',
|
||||
'LEFT OUTER JOIN'],
|
||||
join_tables=['session_history',
|
||||
'session_history_metadata',
|
||||
'session_history_media_info'],
|
||||
join_evals=[['session_history.user_id', 'users.user_id'],
|
||||
['session_history.id', 'session_history_metadata.id'],
|
||||
['session_history.id', 'session_history_media_info.id']],
|
||||
kwargs=kwargs)
|
||||
except:
|
||||
logger.warn("Unable to execute database query.")
|
||||
@@ -54,18 +71,34 @@ class Users(object):
|
||||
|
||||
rows = []
|
||||
for item in users:
|
||||
if not item['thumb'] or item['thumb'] == '':
|
||||
if item["media_type"] == 'episode' and item["parent_thumb"]:
|
||||
thumb = item["parent_thumb"]
|
||||
elif item["media_type"] == 'episode':
|
||||
thumb = item["grandparent_thumb"]
|
||||
else:
|
||||
thumb = item["thumb"]
|
||||
|
||||
if not item['user_thumb'] or item['user_thumb'] == '':
|
||||
user_thumb = common.DEFAULT_USER_THUMB
|
||||
else:
|
||||
user_thumb = item['thumb']
|
||||
user_thumb = item['user_thumb']
|
||||
|
||||
row = {"plays": item['plays'],
|
||||
row = {"id": item['id'],
|
||||
"plays": item['plays'],
|
||||
"last_seen": item['last_seen'],
|
||||
"friendly_name": item["friendly_name"],
|
||||
"ip_address": item["ip_address"],
|
||||
"thumb": user_thumb,
|
||||
"friendly_name": item['friendly_name'],
|
||||
"ip_address": item['ip_address'],
|
||||
"platform": item['platform'],
|
||||
"last_watched": item['last_watched'],
|
||||
"thumb": thumb,
|
||||
"media_type": item['media_type'],
|
||||
"rating_key": item['rating_key'],
|
||||
"video_decision": item['video_decision'],
|
||||
"user_thumb": user_thumb,
|
||||
"user": item["user"],
|
||||
"user_id": item['user_id']
|
||||
"user_id": item['user_id'],
|
||||
"do_notify": helpers.checked(item['do_notify']),
|
||||
"keep_history": helpers.checked(item['keep_history'])
|
||||
}
|
||||
|
||||
rows.append(row)
|
||||
@@ -81,13 +114,25 @@ class Users(object):
|
||||
def get_user_unique_ips(self, kwargs=None, custom_where=None):
|
||||
data_tables = datatables.DataTables()
|
||||
|
||||
columns = ['session_history.started as last_seen',
|
||||
# Change custom_where column name due to ambiguous column name after JOIN
|
||||
custom_where[0][0] = 'custom_user_id' if custom_where[0][0] == 'user_id' else custom_where[0][0]
|
||||
|
||||
columns = ['session_history.id',
|
||||
'session_history.started as last_seen',
|
||||
'session_history.ip_address as ip_address',
|
||||
'COUNT(session_history.id) as play_count',
|
||||
'session_history.player as platform',
|
||||
'session_history_metadata.full_title as last_watched',
|
||||
'session_history_metadata.thumb',
|
||||
'session_history_metadata.parent_thumb',
|
||||
'session_history_metadata.grandparent_thumb',
|
||||
'session_history_metadata.media_type',
|
||||
'session_history.rating_key as rating_key',
|
||||
'session_history_media_info.video_decision',
|
||||
'session_history.user as user',
|
||||
'session_history.user_id as user_id'
|
||||
'session_history.user_id as custom_user_id',
|
||||
'(case when users.friendly_name is null then users.username else \
|
||||
users.friendly_name end) as friendly_name'
|
||||
]
|
||||
|
||||
try:
|
||||
@@ -95,9 +140,15 @@ class Users(object):
|
||||
columns=columns,
|
||||
custom_where=custom_where,
|
||||
group_by=['ip_address'],
|
||||
join_types=['JOIN'],
|
||||
join_tables=['session_history_metadata'],
|
||||
join_evals=[['session_history.id', 'session_history_metadata.id']],
|
||||
join_types=['JOIN',
|
||||
'JOIN',
|
||||
'JOIN'],
|
||||
join_tables=['users',
|
||||
'session_history_metadata',
|
||||
'session_history_media_info'],
|
||||
join_evals=[['session_history.user_id', 'users.user_id'],
|
||||
['session_history.id', 'session_history_metadata.id'],
|
||||
['session_history.id', 'session_history_media_info.id']],
|
||||
kwargs=kwargs)
|
||||
except:
|
||||
logger.warn("Unable to execute database query.")
|
||||
@@ -111,11 +162,24 @@ class Users(object):
|
||||
|
||||
rows = []
|
||||
for item in results:
|
||||
row = {"last_seen": item['last_seen'],
|
||||
if item["media_type"] == 'episode' and item["parent_thumb"]:
|
||||
thumb = item["parent_thumb"]
|
||||
elif item["media_type"] == 'episode':
|
||||
thumb = item["grandparent_thumb"]
|
||||
else:
|
||||
thumb = item["thumb"]
|
||||
|
||||
row = {"id": item['id'],
|
||||
"last_seen": item['last_seen'],
|
||||
"ip_address": item['ip_address'],
|
||||
"play_count": item['play_count'],
|
||||
"platform": item['platform'],
|
||||
"last_watched": item['last_watched']
|
||||
"last_watched": item['last_watched'],
|
||||
"thumb": thumb,
|
||||
"media_type": item['media_type'],
|
||||
"rating_key": item['rating_key'],
|
||||
"video_decision": item['video_decision'],
|
||||
"friendly_name": item['friendly_name']
|
||||
}
|
||||
|
||||
rows.append(row)
|
||||
|
@@ -1 +1,2 @@
|
||||
PLEXPY_VERSION = "master"
|
||||
PLEXPY_RELEASE_VERSION = "1.1.9"
|
||||
|
@@ -241,3 +241,37 @@ def update():
|
||||
e
|
||||
)
|
||||
return
|
||||
|
||||
def read_changelog():
|
||||
|
||||
changelog_file = os.path.join(plexpy.PROG_DIR, 'CHANGELOG.md')
|
||||
|
||||
try:
|
||||
logfile = open(changelog_file, "r")
|
||||
except IOError, e:
|
||||
logger.error('PlexPy Version Checker :: Unable to open changelog file. %s' % e)
|
||||
return None
|
||||
|
||||
if logfile:
|
||||
output = ''
|
||||
lines = logfile.readlines()
|
||||
previous_line = ''
|
||||
for line in lines:
|
||||
if line[:2] == '# ':
|
||||
output += '<h3>' + line[2:] + '</h3>'
|
||||
elif line[:3] == '## ':
|
||||
output += '<h4>' + line[3:] + '</h4>'
|
||||
elif line[:2] == '* ' and previous_line.strip() == '':
|
||||
output += '<ul><li>' + line[2:] + '</li>'
|
||||
elif line[:2] == '* ':
|
||||
output += '<li>' + line[2:] + '</li>'
|
||||
elif line.strip() == '' and previous_line[:2] == '* ':
|
||||
output += '</ul></br>'
|
||||
else:
|
||||
output += line + '</br>'
|
||||
|
||||
previous_line = line
|
||||
|
||||
return output
|
||||
else:
|
||||
return '<h4>No changelog data</h4>'
|
@@ -1,4 +1,4 @@
|
||||
# This file is part of PlexPy.
|
||||
# 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
|
||||
@@ -65,7 +65,10 @@ class WebInterface(object):
|
||||
@cherrypy.expose
|
||||
def home(self):
|
||||
config = {
|
||||
"home_stats_length": plexpy.CONFIG.HOME_STATS_LENGTH
|
||||
"home_stats_length": plexpy.CONFIG.HOME_STATS_LENGTH,
|
||||
"home_stats_type": plexpy.CONFIG.HOME_STATS_TYPE,
|
||||
"home_stats_count": plexpy.CONFIG.HOME_STATS_COUNT,
|
||||
"pms_identifier": plexpy.CONFIG.PMS_IDENTIFIER,
|
||||
}
|
||||
return serve_template(templatename="index.html", title="Home", config=config)
|
||||
|
||||
@@ -118,12 +121,19 @@ class WebInterface(object):
|
||||
return json.dumps(formats)
|
||||
|
||||
@cherrypy.expose
|
||||
def home_stats(self, time_range='30', **kwargs):
|
||||
def home_stats(self, time_range='30', stat_type='0', stat_count='5', **kwargs):
|
||||
data_factory = datafactory.DataFactory()
|
||||
stats_data = data_factory.get_home_stats(time_range=time_range)
|
||||
stats_data = data_factory.get_home_stats(time_range=time_range, stat_type=stat_type, stat_count=stat_count)
|
||||
|
||||
return serve_template(templatename="home_stats.html", title="Stats", data=stats_data)
|
||||
|
||||
@cherrypy.expose
|
||||
def library_stats(self, **kwargs):
|
||||
pms_connect = pmsconnect.PmsConnect()
|
||||
stats_data = pms_connect.get_library_stats()
|
||||
|
||||
return serve_template(templatename="library_stats.html", title="Library Stats", data=stats_data)
|
||||
|
||||
@cherrypy.expose
|
||||
def history(self):
|
||||
return serve_template(templatename="history.html", title="History")
|
||||
@@ -451,6 +461,8 @@ class WebInterface(object):
|
||||
"notify_on_watched_subject_text": plexpy.CONFIG.NOTIFY_ON_WATCHED_SUBJECT_TEXT,
|
||||
"notify_on_watched_body_text": plexpy.CONFIG.NOTIFY_ON_WATCHED_BODY_TEXT,
|
||||
"home_stats_length": plexpy.CONFIG.HOME_STATS_LENGTH,
|
||||
"home_stats_type": checked(plexpy.CONFIG.HOME_STATS_TYPE),
|
||||
"home_stats_count": plexpy.CONFIG.HOME_STATS_COUNT,
|
||||
"buffer_threshold": plexpy.CONFIG.BUFFER_THRESHOLD,
|
||||
"buffer_wait": plexpy.CONFIG.BUFFER_WAIT
|
||||
}
|
||||
@@ -473,7 +485,7 @@ class WebInterface(object):
|
||||
"tv_notify_on_start", "movie_notify_on_start", "music_notify_on_start",
|
||||
"tv_notify_on_stop", "movie_notify_on_stop", "music_notify_on_stop",
|
||||
"tv_notify_on_pause", "movie_notify_on_pause", "music_notify_on_pause", "refresh_users_on_startup",
|
||||
"ip_logging_enable", "video_logging_enable", "music_logging_enable", "pms_is_remote"
|
||||
"ip_logging_enable", "video_logging_enable", "music_logging_enable", "pms_is_remote", "home_stats_type"
|
||||
]
|
||||
for checked_config in checked_configs:
|
||||
if checked_config not in kwargs:
|
||||
@@ -490,18 +502,34 @@ class WebInterface(object):
|
||||
kwargs[plain_config] = kwargs[use_config]
|
||||
del kwargs[use_config]
|
||||
|
||||
# Check if we should refresh our data
|
||||
refresh_users = False
|
||||
reschedule = False
|
||||
|
||||
if 'monitoring_interval' in kwargs and 'refresh_users_interval' in kwargs:
|
||||
if (kwargs['monitoring_interval'] != str(plexpy.CONFIG.MONITORING_INTERVAL)) or \
|
||||
(kwargs['refresh_users_interval'] != str(plexpy.CONFIG.REFRESH_USERS_INTERVAL)):
|
||||
reschedule = True
|
||||
|
||||
if 'pms_ip' in kwargs:
|
||||
if kwargs['pms_ip'] != plexpy.CONFIG.PMS_IP:
|
||||
refresh_users = True
|
||||
|
||||
plexpy.CONFIG.process_kwargs(kwargs)
|
||||
|
||||
# Write the config
|
||||
plexpy.CONFIG.write()
|
||||
|
||||
# Reconfigure scheduler
|
||||
plexpy.initialize_scheduler()
|
||||
|
||||
# Get new server URLs for SSL communications.
|
||||
plextv.get_real_pms_url()
|
||||
|
||||
# Refresh users table. Probably shouldn't do this on every config save, will improve this later.
|
||||
threading.Thread(target=plextv.refresh_users).start()
|
||||
# Reconfigure scheduler if intervals changed
|
||||
if reschedule:
|
||||
plexpy.initialize_scheduler()
|
||||
|
||||
# Refresh users table if our server IP changes.
|
||||
if refresh_users:
|
||||
threading.Thread(target=plextv.refresh_users).start()
|
||||
|
||||
raise cherrypy.HTTPRedirect("settings")
|
||||
|
||||
@@ -549,6 +577,9 @@ class WebInterface(object):
|
||||
if 'rating_key' in kwargs:
|
||||
rating_key = kwargs.get('rating_key', "")
|
||||
custom_where = [['rating_key', rating_key]]
|
||||
if 'parent_rating_key' in kwargs:
|
||||
rating_key = kwargs.get('parent_rating_key', "")
|
||||
custom_where = [['parent_rating_key', rating_key]]
|
||||
if 'grandparent_rating_key' in kwargs:
|
||||
rating_key = kwargs.get('grandparent_rating_key', "")
|
||||
custom_where = [['grandparent_rating_key', rating_key]]
|
||||
@@ -729,16 +760,23 @@ class WebInterface(object):
|
||||
|
||||
@cherrypy.expose
|
||||
def info(self, item_id=None, source=None, **kwargs):
|
||||
metadata = None
|
||||
|
||||
config = {
|
||||
"pms_identifier": plexpy.CONFIG.PMS_IDENTIFIER
|
||||
}
|
||||
|
||||
if source == 'history':
|
||||
data_factory = datafactory.DataFactory()
|
||||
result = data_factory.get_metadata_details(row_id=item_id)
|
||||
metadata = data_factory.get_metadata_details(row_id=item_id)
|
||||
else:
|
||||
pms_connect = pmsconnect.PmsConnect()
|
||||
result = pms_connect.get_metadata_details(rating_key=item_id)['metadata']
|
||||
result = pms_connect.get_metadata_details(rating_key=item_id)
|
||||
if result:
|
||||
metadata = result['metadata']
|
||||
|
||||
if result:
|
||||
return serve_template(templatename="info.html", data=result, title="Info")
|
||||
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")
|
||||
@@ -783,16 +821,16 @@ class WebInterface(object):
|
||||
return serve_template(templatename="user_platform_stats.html", data=None, title="Platform Stats")
|
||||
|
||||
@cherrypy.expose
|
||||
def get_children(self, rating_key='', **kwargs):
|
||||
def get_item_children(self, rating_key='', **kwargs):
|
||||
|
||||
pms_connect = pmsconnect.PmsConnect()
|
||||
result = pms_connect.get_season_children(rating_key)
|
||||
result = pms_connect.get_item_children(rating_key)
|
||||
|
||||
if result:
|
||||
return serve_template(templatename="info_episode_list.html", data=result, title="Episode List")
|
||||
return serve_template(templatename="info_children_list.html", data=result, title="Children List")
|
||||
else:
|
||||
logger.warn('Unable to retrieve data.')
|
||||
return serve_template(templatename="info_episode_list.html", data=None, title="Episode List")
|
||||
return serve_template(templatename="info_children_list.html", data=None, title="Children List")
|
||||
|
||||
@cherrypy.expose
|
||||
def get_metadata_json(self, rating_key='', **kwargs):
|
||||
@@ -1256,3 +1294,32 @@ class WebInterface(object):
|
||||
|
||||
return serve_template(templatename="notification_triggers_modal.html", title="Notification Triggers",
|
||||
data=this_agent)
|
||||
|
||||
@cherrypy.expose
|
||||
def delete_history_rows(self, row_id, **kwargs):
|
||||
data_factory = datafactory.DataFactory()
|
||||
|
||||
if row_id:
|
||||
delete_row = data_factory.delete_session_history_rows(row_id=row_id)
|
||||
|
||||
if delete_row:
|
||||
cherrypy.response.headers['Content-type'] = 'application/json'
|
||||
return json.dumps({'message': delete_row})
|
||||
else:
|
||||
cherrypy.response.headers['Content-type'] = 'application/json'
|
||||
return json.dumps({'message': 'no data received'})
|
||||
|
||||
@cherrypy.expose
|
||||
def delete_all_user_history(self, user_id, **kwargs):
|
||||
data_factory = datafactory.DataFactory()
|
||||
|
||||
if user_id:
|
||||
delete_row = data_factory.delete_all_user_history(user_id=user_id)
|
||||
|
||||
if delete_row:
|
||||
cherrypy.response.headers['Content-type'] = 'application/json'
|
||||
return json.dumps({'message': delete_row})
|
||||
else:
|
||||
cherrypy.response.headers['Content-type'] = 'application/json'
|
||||
return json.dumps({'message': 'no data received'})
|
||||
|
||||
|
Reference in New Issue
Block a user