diff --git a/CHANGELOG.md b/CHANGELOG.md index 30ef75de..0f1af865 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## v1.1.9 (2015-09-14) + +* 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.1.8 (2015-09-09) * Add platform images for Windows devices. Thanks @JonnyWong. diff --git a/PlexPy.py b/PlexPy.py index 008ccec5..0243ce0f 100755 --- a/PlexPy.py +++ b/PlexPy.py @@ -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') diff --git a/data/interfaces/default/base.html b/data/interfaces/default/base.html index f0918641..96aa9c81 100644 --- a/data/interfaces/default/base.html +++ b/data/interfaces/default/base.html @@ -62,16 +62,16 @@ from plexpy import version % else:
  • % endif - % if title=="History": -
  • History
  • - % else: -
  • History
  • - % endif % if title=="Users" or title=="User":
  • Users
  • % else:
  • Users
  • % endif + % if title=="History": +
  • History
  • + % else: +
  • History
  • + % endif % if title=="Graphs":
  • Graphs
  • % else: diff --git a/data/interfaces/default/css/plexpy.css b/data/interfaces/default/css/plexpy.css index d9ecc338..ff44dbea 100644 --- a/data/interfaces/default/css/plexpy.css +++ b/data/interfaces/default/css/plexpy.css @@ -778,36 +778,88 @@ a .dashboard-activity-metadata-user-thumb:hover { font-weight: bold; color: #F9AA03; } -/*.dashboard-activity-instance-overlay { - position: relative; - top: -12px; - text-align: left; - height: 53px; - width: 250px; - border-radius: 0 0 3px 3px; -}*/ .dashboard-recent-media-row { width: 100%; margin:0 auto; text-align: center; + position: relative; + z-index: 0; } .dashboard-recent-media { width: 100%; margin: auto; list-style: none; } -.dashboard-recent-media-instance { -} .dashboard-recent-media li { - margin-right: 27px; + float: left; + position: relative; + left: 0px; + margin-right: 25px; + margin-bottom: 25px; +} +.dashboard-recent-media-poster { position: relative; float: left; - min-height: 340px; +} +.dashboard-recent-media-cover { + position: relative; + margin-top: 75px; + float: left; +} +a:hover .dashboard-recent-media-poster, +a:hover .dashboard-recent-media-cover { + webkit-box-shadow: inset 0 0 0 2px #e9a049; + -moz-box-shadow: inset 0 0 0 2px #e9a049; + box-shadow: inset 0 0 0 2px #e9a049; +} +.dashboard-recent-media-poster-face { + background-position: center; + background-size: cover; + height: 225px; + width: 150px; + position: relative; + -webkit-box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1); + -moz-box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1); + box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1); + z-index: -2; +} +.dashboard-recent-media-cover-face { + background-position: center; + background-size: cover; + height: 150px; + width: 150px; + position: relative; + -webkit-box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1); + -moz-box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1); + box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1); + z-index: -2; +} +.dashboard-recent-media-overlay { + position: absolute; + left: 0; + right: 0; + bottom: 0; + text-align: left; + background: -moz-linear-gradient(top, rgba(0,0,0,0) 30%, rgba(0,0,0,1) 100%); + background: -webkit-gradient(linear, left top, left bottom, color-stop(30%,rgba(0,0,0,0)), color-stop(100%,rgba(0,0,0,1))); + background: -webkit-linear-gradient(top, rgba(0,0,0,0) 30%,rgba(0,0,0,1) 100%); + background: -o-linear-gradient(top, rgba(0,0,0,0) 30%,rgba(0,0,0,1) 100%); + background: -ms-linear-gradient(top, rgba(0,0,0,0) 30%,rgba(0,0,0,1) 100%); + background: linear-gradient(to bottom, rgba(0,0,0,0) 30%,rgba(0,0,0,1) 100%); + height: 100%; + z-index: -1; +} +.dashboard-recent-media-overlay-text { + color: #aaa; + font-size: 12px; + float: left; + position: absolute; + left: 8px; + bottom: 5px; } .dashboard-recent-media-metacontainer { width: 150px; font-size: 13px; - margin-bottom: 20px; clear: both; } .dashboard-recent-media-metacontainer h3 { @@ -825,6 +877,9 @@ a .dashboard-activity-metadata-user-thumb:hover { text-align: left; clear: both; } +.dashboard-recent-media-metacontainer h3.text-muted { + color: #777; +} .dashboard-recent-media-metacontainer .text-muted { padding: 5px 3px 0 3px; text-overflow: ellipsis; @@ -876,10 +931,12 @@ a .dashboard-activity-metadata-user-thumb:hover { .summary-navbar-list { padding: 0 25px; color: #999; + white-space: nowrap; + overflow: hidden; } .summary-navbar-list span { - float: left; - margin-right: 20px; + display: inline-block; + margin-right: 15px; } .summary-navbar-list span a { color: #999; @@ -904,6 +961,10 @@ a .dashboard-activity-metadata-user-thumb:hover { line-height: 40px; float: left; clear: left; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + width: 100%; } .summary-content-title h1 a { color: #F9AA03; @@ -920,6 +981,10 @@ a .dashboard-activity-metadata-user-thumb:hover { line-height: 40px; float: left; clear: left; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + width: 100%; } .summary-content-title h3 { margin-top: 0; @@ -927,7 +992,9 @@ a .dashboard-activity-metadata-user-thumb:hover { color: #999; font-size: 28px; line-height: 40px; - float: right; + position: absolute; + bottom: 0; + right: 0; } .summary-content-title h3 a:hover { text-decoration: underline; @@ -973,6 +1040,18 @@ a .dashboard-activity-metadata-user-thumb:hover { overflow: hidden; z-index: 1; } +.summary-poster-face-track { + background-position: center; + background-size: cover; + height: 250px; + width: 250px; + position: relative; + webkit-box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1); + -moz-box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1); + box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1); + overflow: hidden; + z-index: 1; +} .summary-poster-face-overlay { position: absolute; left: 0; @@ -995,7 +1074,7 @@ a .dashboard-activity-metadata-user-thumb:hover { display: block; width: 100%; height: 100%; - background-image: url(/interfaces/default/images/plex-logo-light.svg); + background-image: url(../images/plex-logo-light.svg); background-size: 100px; background-repeat: no-repeat; background-position: center; @@ -1004,8 +1083,10 @@ a .dashboard-activity-metadata-user-thumb:hover { } a:hover .summary-poster-face .summary-poster-face-overlay, a:hover .summary-poster-face-episode .summary-poster-face-overlay, +a:hover .summary-poster-face-track .summary-poster-face-overlay, a:hover .summary-poster-face .summary-poster-face-overlay span, -a:hover .summary-poster-face-episode .summary-poster-face-overlay span { +a:hover .summary-poster-face-episode .summary-poster-face-overlay span, +a:hover .summary-poster-face-track .summary-poster-face-overlay span { opacity: 1; } .summary-content-padding { @@ -1027,47 +1108,11 @@ a:hover .summary-poster-face-episode .summary-poster-face-overlay span { position: relative; float: left; } -.summary-content-director { +.summary-content-details-tag { float: left; line-height: 24px; } -.summary-content-director strong { - color: #fff; - margin-left: 2px; - margin-right: 10px; -} -.summary-content-studio { - float: left; - line-height: 24px; -} -.summary-content-studio strong { - color: #fff; - margin-left: 2px; - margin-right: 10px; -} -.summary-content-airdate { - float: left; - line-height: 24px; -} -.summary-content-airdate strong { - color: #fff; - margin-left: 2px; - margin-right: 10px; -} -.summary-content-duration { - float: left; - line-height: 24px; -} -.summary-content-duration strong { - color: #fff; - margin-left: 2px; - margin-right: 10px; -} -.summary-content-content-rating { - float: left; - line-height: 24px; -} -.summary-content-content-rating strong { +.summary-content-details-tag strong { color: #fff; margin-left: 2px; margin-right: 10px; @@ -1174,135 +1219,57 @@ a:hover .summary-poster-face-episode .summary-poster-face-overlay span { background-size: contain; height: 16px; } -.show-seasons-wrapper { +.item-children-wrapper { } -.show-seasons-instance { +.item-children-instance { list-style: none; margin: 0; } -.show-seasons-instance li { +.item-children-instance li { float: left; position: relative; left: 0px; margin-right: 25px; margin-bottom: 25px; } -a .show-seasons-card-overlay:hover { +.item-children-instance li.item-children-list-item { + width: 100%; + height: 35px; + margin-right: 0; + margin-bottom: 0; +} +.item-children-poster { + float: left; + position: relative; + left: 0px; +} +a:hover .item-children-poster { webkit-box-shadow: inset 0 0 0 2px #e9a049; -moz-box-shadow: inset 0 0 0 2px #e9a049; box-shadow: inset 0 0 0 2px #e9a049; } -.show-seasons-poster { - float: left; - position: relative; - left: 0px; -} -.show-seasons-card-overlay { - position: absolute; - left: 0; - right: 0; - bottom: 0; - text-align: left; - background: -moz-linear-gradient(top, rgba(0,0,0,0) 30%, rgba(0,0,0,1) 100%); - background: -webkit-gradient(linear, left top, left bottom, color-stop(30%,rgba(0,0,0,0)), color-stop(100%,rgba(0,0,0,1))); - background: -webkit-linear-gradient(top, rgba(0,0,0,0) 30%,rgba(0,0,0,1) 100%); - background: -o-linear-gradient(top, rgba(0,0,0,0) 30%,rgba(0,0,0,1) 100%); - background: -ms-linear-gradient(top, rgba(0,0,0,0) 30%,rgba(0,0,0,1) 100%); - background: linear-gradient(to bottom, rgba(0,0,0,0) 30%,rgba(0,0,0,1) 100%); - height: 225px; -} -.show-seasons-overlay-text { - color: #aaa; - font-size: 12px; - float: left; - position: absolute; - left: 8px; - bottom: 5px; -} -.show-seasons-instance-text-wrapper { - width: 150px; - font-size: 13px; - margin-bottom: 20px; - clear: both; -} -.show-seasons-instance-text-wrapper h3 { - padding: 5px 3px 0 3px; - color: #fff; - text-overflow: ellipsis; - overflow: hidden; - position: relative; - font-size: 13px; - margin: 0; - line-height: 15px; - font-weight: normal; - width: 250px; - white-space: nowrap; - text-align: left; - clear: both; -} -.show-seasons-title a { - text-decoration: none; - font-size: 14px; - font-weight: normal; - color: #fff; - float: left; - text-overflow: ellipsis; - overflow: hidden; - position: relative; - white-space: nowrap; - width: 205px; - margin-top: 2px; - margin-left: 0px; - margin-bottom: 20px; -} -.show-seasons a:hover { - color: #F9AA03; -} -.season-episodes-wrapper { -} -.season-episodes-instance { - list-style: none; - margin: 0; -} -.season-episodes-instance li { - float: left; - position: relative; - left: 0px; - margin-right: 25px; - margin-bottom: 25px; -} -a .season-episodes-card-overlay:hover { - -webkit-box-shadow: inset 0 0 0 2px #e9a049; - -moz-box-shadow: inset 0 0 0 2px #e9a049; - box-shadow: inset 0 0 0 2px #e9a049; -} -.season-episodes-poster { - float: left; - position: relative; - left: 0px; -} -.season-episodes-poster-face { +.item-children-poster-face { background-position: center; background-size: cover; - height: 140px; - width: 250px; position: relative; -webkit-box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1); -moz-box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1); box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1); + z-index: -2; } -.season-episodes-poster-face img { - bottom: 0; - overflow: hidden; +.item-children-poster-face.season-poster { + width: 150px; + height: 225px; +} +.item-children-poster-face.episode-poster { width: 250px; height: 140px; } -.season-episodes-poster-face img:hover { - -webkit-box-shadow: 0 0 0 2px #F9AA03; - -moz-box-shadow: 0 0 0 2px #F9AA03; - box-shadow: 0 0 0 2px #F9AA03; +.item-children-poster-face.album-poster { + width: 150px; + height: 150px; } -.season-episodes-card-overlay { +.item-children-card-overlay { position: absolute; left: 0; right: 0; @@ -1314,14 +1281,10 @@ a .season-episodes-card-overlay:hover { background: -o-linear-gradient(top, rgba(0,0,0,0) 30%,rgba(0,0,0,1) 100%); background: -ms-linear-gradient(top, rgba(0,0,0,0) 30%,rgba(0,0,0,1) 100%); background: linear-gradient(to bottom, rgba(0,0,0,0) 30%,rgba(0,0,0,1) 100%); - height: 140px; + height: 100%; + z-index: -1; } -.season-episodes-card-overlay-index { - color: #fff; - font-size: 11px; - text-shadow: 0 1px 5px rgba(0,0,0,0.2); -} -.season-episodes-overlay-text { +.item-children-overlay-text { color: #aaa; font-size: 12px; float: left; @@ -1329,7 +1292,22 @@ a .season-episodes-card-overlay:hover { left: 8px; bottom: 5px; } -.season-episodes-instance-text-wrapper h3 { +.item-children-instance-text-wrapper { + font-size: 13px; + margin-bottom: 20px; + clear: both; +} +.item-children-instance-text-wrapper.season-item { + width: 150px; +} +.item-children-instance-text-wrapper.episode-item { + width: 250px; +} +.item-children-instance-text-wrapper.album-item { + width: 150px; +} +.item-children-instance-text-wrapper h3 { + width: 100%; padding: 5px 3px 0 3px; color: #fff; text-overflow: ellipsis; @@ -1339,36 +1317,52 @@ a .season-episodes-card-overlay:hover { margin: 0; line-height: 15px; font-weight: normal; - width: 250px; white-space: nowrap; text-align: left; clear: both; } -.season-episodes-title a { - text-decoration: none; - font-size: 14px; - font-weight: normal; - color: #fff; +.item-children-list-item-odd { + border-top: 0px solid #343434; + border-bottom: 0px solid #343434; + background-color: rgba(255,255,255,0.010); + height: 100%; + font-size: 13px; + padding-top: 10px; +} +.item-children-list-item-even { + border-top: 0px solid #343434; + border-bottom: 0px solid #343434; + background-color: rgba(255,255,255,0.035); + height: 100%; + font-size: 13px; + padding-top: 10px; +} +.item-children-list-item-odd:hover, +.item-children-list-item-even:hover { + background-color: rgba(255,255,255,0.075); +} +.item-children-list-item-index { float: left; + color: #777; + text-align: right; + display: inline-block; + width: 35px; + margin-right: 10px; +} +.item-children-list-item-title { + display: inline-block; + width: calc(100% - 110px); text-overflow: ellipsis; overflow: hidden; - position: relative; white-space: nowrap; - width: 205px; - margin-top: 2px; - margin-left: 0px; - margin-bottom: 20px; } -.season-episodes a:hover { - color: #F9AA03; -} -.season-episodes-season { - color: #aaa; - font-size: 12px; - float: left; - position: absolute; - left: 8px; - bottom: 5px; +.item-children-list-item-duration { + float: right; + color: #777; + text-align: right; + display: inline-block; + width: 40px; + margin-right: 20px; } .settings-alert { float: left; diff --git a/data/interfaces/default/current_activity.html b/data/interfaces/default/current_activity.html index da9a0c0d..04632966 100644 --- a/data/interfaces/default/current_activity.html +++ b/data/interfaces/default/current_activity.html @@ -67,7 +67,7 @@ DOCUMENTATION :: END % if data['stream_count'] != '0': % for a in data['sessions']:
    - % if a['type'] == 'movie' or a['type'] == 'episode': + % if a['type'] == 'movie' or a['type'] == 'episode' or a['type'] == 'track': % endif
    @@ -93,6 +93,8 @@ DOCUMENTATION :: END
    % endif % endif + % elif a['type'] == 'photo': +
    % else:
    % endif @@ -160,17 +162,31 @@ DOCUMENTATION :: END % elif a['audio_decision'] == 'transcode': Audio  Transcode (${a['transcode_audio_codec']}) (${a['transcode_audio_channels']}ch) % endif + % elif a['type'] == 'photo': + % if a['video_decision'] == 'direct play': + Stream  Direct Play + % else: + Stream   + Transcoding + (Speed: ${a['transcode_speed']}) + % if a['throttled'] == '1': + (Throttled) + % endif + + % endif % endif
    + % if a['type'] != 'photo':
    ${a['progress']}/${a['duration']}
    + % endif - % if a['type'] == 'movie' or a['type'] == 'episode': + % if a['type'] == 'movie' or a['type'] == 'episode' or a['type'] == 'track':
    % endif
    @@ -198,9 +214,11 @@ DOCUMENTATION :: END % elif a['type'] == 'clip': ${a['title']} % elif a['type'] == 'track': - ${a['grandparent_title']} - ${a['title']} + ${a['grandparent_title']} - ${a['title']} + % elif a['type'] == 'photo': + ${a['parent_title']} % else: - ${a['grandparent_title']} - ${a['title']} + ${a['title']} % endif
    @@ -209,7 +227,9 @@ DOCUMENTATION :: END % elif a['type'] == 'movie': ${a['year']} % elif a['type'] == 'track': - ${a['parent_title']} + ${a['parent_title']} + % elif a['type'] == 'photo': + ${a['title']} % else: ${a['year']} % endif @@ -231,7 +251,7 @@ DOCUMENTATION :: END % endif -% if data['type'] == 'movie' or data['type'] == 'episode': +% if data['type'] == 'show' or data['type'] == 'artist': -% elif data['type'] == 'show': - - -% endif -% if data['type'] == 'season': - +% elif data['type'] == 'season' or data['type'] == 'album': +% elif data['type'] == 'episode' or data['type'] == 'track' or data['type'] == 'movie': + +% endif + +% if data['type'] == 'show' or data['type'] == 'season' or data['type'] == 'artist' or data['type'] == 'album': + % endif diff --git a/data/interfaces/default/info_children_list.html b/data/interfaces/default/info_children_list.html new file mode 100644 index 00000000..53e9935c --- /dev/null +++ b/data/interfaces/default/info_children_list.html @@ -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 + + +% if data != None: +% if data['children_count'] > 0: +
    + +
    +% endif +% endif + diff --git a/data/interfaces/default/info_episode_list.html b/data/interfaces/default/info_episode_list.html deleted file mode 100644 index 11651969..00000000 --- a/data/interfaces/default/info_episode_list.html +++ /dev/null @@ -1,53 +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 - - -% if data != None: -% if data['episode_count'] > 0: -
    - -
    -% endif -% endif - diff --git a/data/interfaces/default/info_season_list.html b/data/interfaces/default/info_season_list.html deleted file mode 100644 index 6da68e0e..00000000 --- a/data/interfaces/default/info_season_list.html +++ /dev/null @@ -1,55 +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_season_list.html -Version: 0.1 -Variable names: data [list] - -data :: Usable parameters - -== Global keys == -season_count Returns the number of seasons in the array. -season_list Returns an array of seasons. - -data['season_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 season. -index Returns the season number. - -DOCUMENTATION :: END - - -% if data != None: -% if data['season_count'] > 0: -
    - -
    -% endif -% endif \ No newline at end of file diff --git a/data/interfaces/default/js/graphs/plays_by_day.js b/data/interfaces/default/js/graphs/plays_by_day.js index c8dad53f..6eabea39 100644 --- a/data/interfaces/default/js/graphs/plays_by_day.js +++ b/data/interfaces/default/js/graphs/plays_by_day.js @@ -35,7 +35,7 @@ var hc_plays_by_day_options = { } } }, - colors: ['#F9AA03', '#FFFFFF'], + colors: ['#F9AA03', '#FFFFFF', '#FF4747'], xAxis: { type: 'datetime', labels: { diff --git a/data/interfaces/default/js/graphs/plays_by_dayofweek.js b/data/interfaces/default/js/graphs/plays_by_dayofweek.js index 75407fcf..a0d66bbe 100644 --- a/data/interfaces/default/js/graphs/plays_by_dayofweek.js +++ b/data/interfaces/default/js/graphs/plays_by_dayofweek.js @@ -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 }, diff --git a/data/interfaces/default/js/graphs/plays_by_hourofday.js b/data/interfaces/default/js/graphs/plays_by_hourofday.js index 82adde20..e2e036e3 100644 --- a/data/interfaces/default/js/graphs/plays_by_hourofday.js +++ b/data/interfaces/default/js/graphs/plays_by_hourofday.js @@ -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 }, diff --git a/data/interfaces/default/js/graphs/plays_by_month.js b/data/interfaces/default/js/graphs/plays_by_month.js index d8cf6ebd..c43cf041 100644 --- a/data/interfaces/default/js/graphs/plays_by_month.js +++ b/data/interfaces/default/js/graphs/plays_by_month.js @@ -23,7 +23,7 @@ var hc_plays_by_month_options = { credits: { enabled: false }, - colors: ['#F9AA03', '#FFFFFF'], + colors: ['#F9AA03', '#FFFFFF', '#FF4747'], xAxis: { labels: { style: { diff --git a/data/interfaces/default/js/graphs/plays_by_platform.js b/data/interfaces/default/js/graphs/plays_by_platform.js index 0e3ca9bd..c84d785e 100644 --- a/data/interfaces/default/js/graphs/plays_by_platform.js +++ b/data/interfaces/default/js/graphs/plays_by_platform.js @@ -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 }, diff --git a/data/interfaces/default/js/graphs/plays_by_source_resolution.js b/data/interfaces/default/js/graphs/plays_by_source_resolution.js index 88b02209..c82f16bc 100644 --- a/data/interfaces/default/js/graphs/plays_by_source_resolution.js +++ b/data/interfaces/default/js/graphs/plays_by_source_resolution.js @@ -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 }, diff --git a/data/interfaces/default/js/graphs/plays_by_stream_resolution.js b/data/interfaces/default/js/graphs/plays_by_stream_resolution.js index 5a7b546c..7ae7f84d 100644 --- a/data/interfaces/default/js/graphs/plays_by_stream_resolution.js +++ b/data/interfaces/default/js/graphs/plays_by_stream_resolution.js @@ -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 }, diff --git a/data/interfaces/default/js/graphs/plays_by_user.js b/data/interfaces/default/js/graphs/plays_by_user.js index c065321d..8700b445 100644 --- a/data/interfaces/default/js/graphs/plays_by_user.js +++ b/data/interfaces/default/js/graphs/plays_by_user.js @@ -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 }, diff --git a/data/interfaces/default/js/tables/history_table.js b/data/interfaces/default/js/tables/history_table.js index 3e4b9d43..1cccbf9b 100644 --- a/data/interfaces/default/js/tables/history_table.js +++ b/data/interfaces/default/js/tables/history_table.js @@ -125,12 +125,12 @@ history_table_options = { } else if (rowData['media_type'] === 'episode') { media_type = ''; thumb_popover = '' + cellData + ' \ - (S' + ('00' + rowData['parent_media_index']).slice(-2) + 'E' + ('00' + rowData['media_index']).slice(-2) + ')' + (S' + rowData['parent_media_index'] + '· E' + rowData['media_index'] + ')' $(td).html('
    ' + media_type + ' ' + thumb_popover + '
    '); } else if (rowData['media_type'] === 'track') { media_type = ''; thumb_popover = '' + cellData + ' (' + rowData['parent_title'] + ')' - $(td).html('
    ' + media_type + ' ' + thumb_popover + '
    '); + $(td).html('
    ' + media_type + ' ' + thumb_popover + '
    '); } else { $(td).html('' + cellData + ''); } diff --git a/data/interfaces/default/js/tables/history_table_modal.js b/data/interfaces/default/js/tables/history_table_modal.js index 8a0afa22..efb109c3 100644 --- a/data/interfaces/default/js/tables/history_table_modal.js +++ b/data/interfaces/default/js/tables/history_table_modal.js @@ -103,12 +103,12 @@ history_table_modal_options = { } else if (rowData['media_type'] === 'episode') { media_type = ''; thumb_popover = '' + cellData + ' \ - (S' + ('00' + rowData['parent_media_index']).slice(-2) + 'E' + ('00' + rowData['media_index']).slice(-2) + ')' + (S' + rowData['parent_media_index'] + '· E' + rowData['media_index'] + ')' $(td).html('
    ' + media_type + ' ' + thumb_popover + '
    '); } else if (rowData['media_type'] === 'track') { media_type = ''; thumb_popover = '' + cellData + ' (' + rowData['parent_title'] + ')' - $(td).html('
    ' + media_type + ' ' + thumb_popover + '
    '); + $(td).html('
    ' + media_type + ' ' + thumb_popover + '
    '); } else { $(td).html('' + cellData + ''); } diff --git a/data/interfaces/default/js/tables/sync_table.js b/data/interfaces/default/js/tables/sync_table.js index 694272b0..3aa118c6 100644 --- a/data/interfaces/default/js/tables/sync_table.js +++ b/data/interfaces/default/js/tables/sync_table.js @@ -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('' + cellData + ''); } else { $(td).html(cellData); diff --git a/data/interfaces/default/js/tables/user_ips.js b/data/interfaces/default/js/tables/user_ips.js index 5968b67a..bb2667d4 100644 --- a/data/interfaces/default/js/tables/user_ips.js +++ b/data/interfaces/default/js/tables/user_ips.js @@ -86,7 +86,7 @@ user_ip_table_options = { } else if (rowData['media_type'] === 'track') { media_type = ''; thumb_popover = '' + cellData + '' - $(td).html('
    ' + media_type + ' ' + thumb_popover + '
    '); + $(td).html('
    ' + media_type + ' ' + thumb_popover + '
    '); } else if (rowData['media_type']) { $(td).html('' + cellData + ''); } else { diff --git a/data/interfaces/default/js/tables/users.js b/data/interfaces/default/js/tables/users.js index 0e9719db..f47daf0f 100644 --- a/data/interfaces/default/js/tables/users.js +++ b/data/interfaces/default/js/tables/users.js @@ -141,7 +141,7 @@ users_list_table_options = { } else if (rowData['media_type'] === 'track') { media_type = ''; thumb_popover = '' + cellData + '' - $(td).html('
    ' + media_type + ' ' + thumb_popover + '
    '); + $(td).html('
    ' + media_type + ' ' + thumb_popover + '
    '); } else if (rowData['media_type']) { $(td).html('' + cellData + ''); } else { diff --git a/data/interfaces/default/recently_added.html b/data/interfaces/default/recently_added.html index 75482622..86736961 100644 --- a/data/interfaces/default/recently_added.html +++ b/data/interfaces/default/recently_added.html @@ -31,35 +31,48 @@ DOCUMENTATION :: END
  • % if item['type'] == 'season' or item['type'] == 'movie': -
  • - % endfor diff --git a/data/interfaces/default/settings.html b/data/interfaces/default/settings.html index a498399d..d13e8700 100644 --- a/data/interfaces/default/settings.html +++ b/data/interfaces/default/settings.html @@ -282,12 +282,13 @@ available_notification_agents = notifiers.available_notification_agents()
    - +
    +

    Token for Plex.tv authentication.

    @@ -1168,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); diff --git a/data/interfaces/default/user_recently_watched.html b/data/interfaces/default/user_recently_watched.html index 2cf7b57c..b87cae83 100644 --- a/data/interfaces/default/user_recently_watched.html +++ b/data/interfaces/default/user_recently_watched.html @@ -32,29 +32,54 @@ DOCUMENTATION :: END
    +
    + % if item['type'] == 'episode': +

    ${item['grandparent_title']}

    +

    ${item['title']}

    +

    S${item['parent_index']} · E${item['index']}

    + % elif item['type'] == 'movie': +

    ${item['title']}

    +

    ${item['year']}

    + % endif +
    +
    +
    + + % elif item['type'] == 'track': + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +

    ${item['grandparent_title']}

    +

    ${item['title']}

    +

    ${item['parent_title']}

    +
    +
    + % endif + % endfor diff --git a/plexpy/datafactory.py b/plexpy/datafactory.py index 549612e4..4a988480 100644 --- a/plexpy/datafactory.py +++ b/plexpy/datafactory.py @@ -153,10 +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, ' \ - 'cast(round(SUM(round((julianday(datetime(session_history.stopped, "unixepoch", "localtime")) - ' \ - 'julianday(datetime(session_history.started, "unixepoch", "localtime"))) * 86400) - ' \ - '(CASE WHEN session_history.paused_counter IS NULL THEN 0 ' \ - 'ELSE session_history.paused_counter END))/60) as integer) as total_duration,' \ + '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 ' \ @@ -193,52 +193,6 @@ class DataFactory(object): '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, ' \ - 'cast(round(SUM(round((julianday(datetime(session_history.stopped, "unixepoch", "localtime")) - ' \ - 'julianday(datetime(session_history.started, "unixepoch", "localtime"))) * 86400) - ' \ - '(CASE WHEN session_history.paused_counter IS NULL THEN 0 ' \ - 'ELSE session_history.paused_counter END))/60) as integer) 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_tv' in stat: popular_tv = [] try: @@ -281,6 +235,52 @@ 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: @@ -330,10 +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, ' \ - 'cast(round(SUM(round((julianday(datetime(session_history.stopped, "unixepoch", "localtime")) - ' \ - 'julianday(datetime(session_history.started, "unixepoch", "localtime"))) * 86400) - ' \ - '(CASE WHEN session_history.paused_counter IS NULL THEN 0 ' \ - 'ELSE session_history.paused_counter END))/60) as integer) as total_duration,' \ + '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 ' \ @@ -382,10 +382,10 @@ class DataFactory(object): try: query = 'SELECT session_history.platform, ' \ 'COUNT(session_history.id) as total_plays, ' \ - 'cast(round(SUM(round((julianday(datetime(session_history.stopped, "unixepoch", "localtime")) - ' \ - 'julianday(datetime(session_history.started, "unixepoch", "localtime"))) * 86400) - ' \ - '(CASE WHEN session_history.paused_counter IS NULL THEN 0 ' \ - 'ELSE session_history.paused_counter END))/60) as integer) as total_duration,' \ + '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") ' \ @@ -524,24 +524,35 @@ class DataFactory(object): try: if user_id: - query = 'SELECT session_history.id, session_history.media_type, session_history.rating_key, title, ' \ - 'grandparent_title, thumb, parent_thumb, grandparent_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, ' \ - 'grandparent_title, thumb, parent_thumb, grandparent_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, ' \ - 'grandparent_title, thumb, parent_thumb, grandparent_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: @@ -549,24 +560,25 @@ class DataFactory(object): return None for row in result: - if row[1] == 'episode' and row[6]: - thumb = row[6] + if row[1] == 'episode' and row[8]: + thumb = row[8] elif row[1] == 'episode': - thumb = row[7] + thumb = row[9] else: - thumb = row[5] + thumb = row[7] recent_output = {'row_id': row[0], 'type': row[1], 'rating_key': row[2], - 'title': row[3], - 'parent_title': row[4], + 'title': row[4], + 'parent_title': row[5], + 'grandparent_title': row[6], 'thumb': thumb, - 'index': row[8], - 'parent_index': row[9], - 'year': row[10], - 'time': row[11], - 'user': row[12] + 'index': row[10], + 'parent_index': row[11], + 'year': row[12], + 'time': row[13], + 'user': row[14] } recently_watched.append(recent_output) diff --git a/plexpy/graphs.py b/plexpy/graphs.py index 8b139d82..7de362a9 100644 --- a/plexpy/graphs.py +++ b/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,6 +305,7 @@ class Graphs(object): categories = [] series_1 = [] series_2 = [] + series_3 = [] for month_item in sorted(month_range): dt = datetime.datetime(*month_item[:6]) @@ -262,25 +314,31 @@ class Graphs(object): 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) diff --git a/plexpy/monitor.py b/plexpy/monitor.py index c6d52936..091fa575 100644 --- a/plexpy/monitor.py +++ b/plexpy/monitor.py @@ -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: diff --git a/plexpy/pmsconnect.py b/plexpy/pmsconnect.py index ab311a87..25b23253 100644 --- a/plexpy/pmsconnect.py +++ b/plexpy/pmsconnect.py @@ -90,31 +90,14 @@ class PmsConnect(object): return request """ - Return list of seasons in requested show. + 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_season_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 - - """ - Return list of episodes in requested season. - - 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, @@ -381,6 +364,39 @@ 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'), @@ -443,9 +459,39 @@ class PmsConnect(object): 'directors': directors } metadata_list = {'metadata': metadata} - elif metadata_type == 'season': + 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'), + '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': 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'), + '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') - show_details = self.get_metadata_details(parent_rating_key) + 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'), @@ -456,10 +502,10 @@ 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': artist_details['metadata']['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'), @@ -477,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'), @@ -490,7 +540,7 @@ class PmsConnect(object): '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'), @@ -547,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 @@ -565,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') @@ -654,6 +710,7 @@ class PmsConnect(object): 'type': 'track', 'indexes': 0 } + elif stream_type == 'video': media_info = session.getElementsByTagName('Media')[0] audio_decision = 'direct play' @@ -886,92 +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 season list. + Return processed and validated children list. Output: array """ - def get_show_children(self, rating_key=''): - season_data = self.get_season_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 = season_data.getElementsByTagName('MediaContainer') + xml_head = children_data.getElementsByTagName('MediaContainer') except: - logger.warn("Unable to parse XML for get_season_list.") + logger.warn("Unable to parse XML for get_children_list.") return [] - season_list = [] + children_list = [] for a in xml_head: if a.getAttribute('size'): if a.getAttribute('size') == '0': - logger.debug(u"No season data.") - season_list = {'season_count': '0', - 'season_list': [] - } - return season_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') - for result in result_data: - season_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') - } - season_list.append(season_output) - - output = {'season_count': helpers.get_xml_attr(xml_head[0], 'size'), - 'title': helpers.get_xml_attr(xml_head[0], 'title2'), - 'season_list': season_list - } - - return output - - """ - Return processed and validated episode list. - - Output: array - """ - def get_season_children(self, rating_key=''): - episode_data = self.get_episode_list(rating_key, output_format='xml') - - try: - xml_head = episode_data.getElementsByTagName('MediaContainer') - except: - logger.warn("Unable to parse XML for get_episode_list.") - return [] - - episode_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 - 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 diff --git a/plexpy/version.py b/plexpy/version.py index c1d759b7..6af938ca 100644 --- a/plexpy/version.py +++ b/plexpy/version.py @@ -1,2 +1,2 @@ PLEXPY_VERSION = "master" -PLEXPY_RELEASE_VERSION = "1.1.8" +PLEXPY_RELEASE_VERSION = "1.1.9" diff --git a/plexpy/webserve.py b/plexpy/webserve.py index 6f9b9d2e..6bb6f9ac 100644 --- a/plexpy/webserve.py +++ b/plexpy/webserve.py @@ -821,28 +821,16 @@ class WebInterface(object): return serve_template(templatename="user_platform_stats.html", data=None, title="Platform Stats") @cherrypy.expose - def get_show_children(self, rating_key='', **kwargs): + def get_item_children(self, rating_key='', **kwargs): pms_connect = pmsconnect.PmsConnect() - result = pms_connect.get_show_children(rating_key) + result = pms_connect.get_item_children(rating_key) if result: - return serve_template(templatename="info_season_list.html", data=result, title="Season 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_season_list.html", data=None, title="Season List") - - @cherrypy.expose - def get_season_children(self, rating_key='', **kwargs): - - pms_connect = pmsconnect.PmsConnect() - result = pms_connect.get_season_children(rating_key) - - if result: - return serve_template(templatename="info_episode_list.html", data=result, title="Episode 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):