Merge branch 'v2' into nightly

This commit is contained in:
JonnyWong16 2017-12-17 23:21:31 -08:00
commit f07ad960dc
290 changed files with 29577 additions and 11682 deletions

8
.gitignore vendored
View file

@ -19,10 +19,10 @@ cache/*
# HTTPS Cert/Key #
##################
*.crt
*.key
*.csr
*.pem
/*.crt
/*.key
/*.csr
/*.pem
# Mergetool
*.orgin

124
API.md
View file

@ -783,55 +783,53 @@ Optional parameters:
Returns:
json:
{"metadata":
{"actors": [
"Kit Harington",
"Emilia Clarke",
"Isaac Hempstead-Wright",
"Maisie Williams",
"Liam Cunningham",
],
"added_at": "1461572396",
"art": "/library/metadata/1219/art/1462175063",
"content_rating": "TV-MA",
"directors": [
"Jeremy Podeswa"
],
"duration": "2998290",
"genres": [
"Adventure",
"Drama",
"Fantasy"
],
"grandparent_rating_key": "1219",
"grandparent_thumb": "/library/metadata/1219/thumb/1462175063",
"grandparent_title": "Game of Thrones",
"guid": "com.plexapp.agents.thetvdb://121361/6/1?lang=en",
"labels": [],
"last_viewed_at": "1462165717",
"library_name": "TV Shows",
"media_index": "1",
"media_type": "episode",
"originally_available_at": "2016-04-24",
"parent_media_index": "6",
"parent_rating_key": "153036",
"parent_thumb": "/library/metadata/153036/thumb/1462175062",
"parent_title": "",
"rating": "7.8",
"rating_key": "153037",
"section_id": "2",
"studio": "HBO",
"summary": "Jon Snow is dead. Daenerys meets a strong man. Cersei sees her daughter again.",
"tagline": "",
"thumb": "/library/metadata/153037/thumb/1462175060",
"title": "The Red Woman",
"updated_at": "1462175060",
"writers": [
"David Benioff",
"D. B. Weiss"
],
"year": "2016"
}
{"actors": [
"Kit Harington",
"Emilia Clarke",
"Isaac Hempstead-Wright",
"Maisie Williams",
"Liam Cunningham",
],
"added_at": "1461572396",
"art": "/library/metadata/1219/art/1462175063",
"content_rating": "TV-MA",
"directors": [
"Jeremy Podeswa"
],
"duration": "2998290",
"genres": [
"Adventure",
"Drama",
"Fantasy"
],
"grandparent_rating_key": "1219",
"grandparent_thumb": "/library/metadata/1219/thumb/1462175063",
"grandparent_title": "Game of Thrones",
"guid": "com.plexapp.agents.thetvdb://121361/6/1?lang=en",
"labels": [],
"last_viewed_at": "1462165717",
"library_name": "TV Shows",
"media_index": "1",
"media_type": "episode",
"originally_available_at": "2016-04-24",
"parent_media_index": "6",
"parent_rating_key": "153036",
"parent_thumb": "/library/metadata/153036/thumb/1462175062",
"parent_title": "",
"rating": "7.8",
"rating_key": "153037",
"section_id": "2",
"studio": "HBO",
"summary": "Jon Snow is dead. Daenerys meets a strong man. Cersei sees her daughter again.",
"tagline": "",
"thumb": "/library/metadata/153037/thumb/1462175060",
"title": "The Red Woman",
"updated_at": "1462175060",
"writers": [
"David Benioff",
"D. B. Weiss"
],
"year": "2016"
}
```
@ -1176,6 +1174,34 @@ Returns:
```
### get_pms_update
Check for updates to the Plex Media Server.
```
Required parameters:
None
Optional parameters:
None
Returns:
json:
{"update_available": true,
"platform": "Windows",
"release_date": "1473721409",
"version": "1.1.4.2757-24ffd60",
"requirements": "...",
"extra_info": "...",
"changelog_added": "...",
"changelog_fixed": "...",
"label": "Download",
"distro": "english",
"distro_build": "windows-i386",
"download_url": "https://downloads.plex.tv/...",
}
```
### get_recently_added
Get all items that where recelty added to plex.

View file

@ -1,5 +1,77 @@
# Changelog
## v2.0.0-beta (2017-12-18)
* Monitoring:
* New: More detailed stream info including subtitles, bitrates, bandwidth, and quality profiles.
* New: Terminate sessions from the current activity (Plex Pass only).
* Change: Monitoring uses websockets only now.
* Notifications:
* New: Completely new notification system.
* Allow adding multiple of the same notification agent and/or duplicating existing notification agents.
* Each notification agent has it's own notification triggers and notification text.
* Notification agents are stored in the database instead of the config file. Some notification configurations may have been lost in the transfer. Sorry.
* New: Discord notification agent.
* New: GroupMe notification agent.
* New: MQTT notification agent.
* New: More customizable info cards for Discord, Facebook, Hipchat, and Slack.
* New: Script notifications are configured individually per script with separate arguments for each notification action.
* New: Icon and duration options for Plex Home Theater and XBMC notifications.
* New: Notification for Tautulli updates.
* New: Added <show>, <season>, <artist>, and <album> notification exclusion tags.
* <tv> is renamed to <episode>, and <music> is renamed to <track>
* New: Preview notification text in the notifier settings.
* New: Properly group recently added notifications when adding a batch of media.
* The {season_num}, {episode_num}, and {track_num} parameters will be substituted with the range (e.g. 06-10)
* New: Option to group recently added notifications by show/artist or season/album.
* New: More detailed media info (video, audio, subtitle, file, etc.) notification options available.
* New: Added notification text modifiers to change case and slice lists.
* New: Custom notification conditions using parameters to filter notifications.
* New: Button to trigger manual recently added notifications from the info pages.
* New: Lookup TVMaze and TheMovieDatabase links.
* Remove: The shared Imgur client ID has been removed. Please enter your own client ID in the settings to continue uploading posters.
* Change: Notifications with a blank subject or body will no longer be sent at all.
* Change: Line breaks inserted automatically in Email notification text.
* Change: Notifications for season/episodes now use the season poster and album/track now use the album art.
* Change: The {action} parameter is no longer capitalized.
* Change: Notification success or failure added to notification logs.
* API:
* New: Added check for Plex Media Server updates with the Tautulli API.
* New: Added show/artist and episode/track titles to the "get_history" API command.
* New: Added manual trigger for recently added notifications.
* Remove: Defunct API v1.
* Change: The "notify" API command now requires a notifier_id instead of an agent_id. The notifier ID can be found in the settings for each notification agent.
* Change: The returned json for the "get_metadata" API command is no longer nested under the "metadata" key.
* UI:
* New: Updated current activity, watch statistics, and library statistics cards on the home page.
* New: Toggle stats and recently added categories directly on the homepage.
* New: Ability to delete synced items from the Synced Items page.
* New: Updated platform icons to a uniform style.
* Remove: Setting for number of top items for watch statistic cards.
* Change: Separate API and websocket logs.
* Android Tautulli Remote App (beta):
* New: Download the Tautulli Remote app on Google Play!
* Link the app using a QR code in the Tautulli settings.
* New: Push notifications directly to the Tautulli Remote app.
* Other:
* New: Option to update Tautulli automatically when an update is available.
* New: Option to switch the tracking git remote and branch.
* New: Option to change the path to your git environment variable.
* New: Option to use a HTTPS certificate chain.
* New: Option to override the Plex Web URL for click-through links.
* New: Separate watched percentage for movies, episodes, and tracks.
* New: Show changelog after updating Tautulli.
* New: Support for IPv6 geolocation lookup.
* New: Download the Tautulli configuration file or database from the settings.
* New: Log failed Tautulli login attempts.
* Fix: Modal popups not working on mobile Safari.
* Fix: Prevent password managers from autofilling the password in the settings.
* Fix: Unable to search with special characters.
* Remove: Some unused options have been removed from the settings page.
* Change: The database schema has been changed, and reverting back to PlexPy v1 will not work.
* Change: The dev branch has been depreciated. A master/beta/nightly system is used instead.
## v1.4.25 (2017-10-02)
* Fix: Tab instead of spaces preventing startup.

View file

@ -6,20 +6,20 @@
# -*- coding: utf-8 -*-
# This file is part of PlexPy.
# This file is part of Tautulli.
#
# PlexPy is free software: you can redistribute it and/or modify
# Tautulli is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# PlexPy is distributed in the hope that it will be useful,
# Tautulli is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with PlexPy. If not, see <http://www.gnu.org/licenses/>.
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
import os
import sys
@ -43,11 +43,11 @@ signal.signal(signal.SIGTERM, plexpy.sig_handler)
def main():
"""
PlexPy application entry point. Parses arguments, setups encoding and
Tautulli application entry point. Parses arguments, setups encoding and
initializes the application.
"""
# Fixed paths to PlexPy
# Fixed paths to Tautulli
if hasattr(sys, 'frozen'):
plexpy.FULL_PATH = os.path.abspath(sys.executable)
else:
@ -81,9 +81,9 @@ def main():
parser.add_argument(
'-d', '--daemon', action='store_true', help='Run as a daemon')
parser.add_argument(
'-p', '--port', type=int, help='Force PlexPy to run on a specified port')
'-p', '--port', type=int, help='Force Tautulli to run on a specified port')
parser.add_argument(
'--dev', action='store_true', help='Start PlexPy in the development environment')
'--dev', action='store_true', help='Start Tautulli in the development environment')
parser.add_argument(
'--datadir', help='Specify a directory where to store your data files')
parser.add_argument(
@ -93,8 +93,8 @@ def main():
parser.add_argument(
'--pidfile', help='Create a pid file (only relevant when running as a daemon)')
parser.add_argument(
'--nofork', action='store_true', help='Start PlexPy as a service, do not fork when restarting')
'--nofork', action='store_true', help='Start Tautulli as a service, do not fork when restarting')
args = parser.parse_args()
if args.verbose:
@ -108,7 +108,7 @@ def main():
if args.dev:
plexpy.DEV = True
logger.debug(u"PlexPy is running in the dev environment.")
logger.debug(u"Tautulli is running in the dev environment.")
if args.daemon:
if sys.platform == 'win32':
@ -120,7 +120,7 @@ def main():
if args.nofork:
plexpy.NOFORK = True
logger.info("PlexPy is running as a service, it will not fork when restarted.")
logger.info("Tautulli is running as a service, it will not fork when restarted.")
if args.pidfile:
plexpy.PIDFILE = str(args.pidfile)
@ -195,14 +195,11 @@ def main():
plexpy.start()
# Open connection for websocket
if plexpy.CONFIG.MONITORING_USE_WEBSOCKET:
try:
web_socket.start_thread()
except:
logger.warn(u"Websocket :: Unable to open connection.")
# Fallback to polling
plexpy.POLLING_FAILOVER = True
plexpy.initialize_scheduler()
try:
web_socket.start_thread()
except:
logger.warn(u"Websocket :: Unable to open connection.")
plexpy.initialize_scheduler()
# Force the http port if neccessary
if args.port:
@ -230,6 +227,7 @@ def main():
'http_proxy': plexpy.CONFIG.HTTP_PROXY,
'enable_https': plexpy.CONFIG.ENABLE_HTTPS,
'https_cert': plexpy.CONFIG.HTTPS_CERT,
'https_cert_chain': plexpy.CONFIG.HTTPS_CERT_CHAIN,
'https_key': plexpy.CONFIG.HTTPS_KEY,
'http_username': plexpy.CONFIG.HTTP_USERNAME,
'http_password': plexpy.CONFIG.HTTP_PASSWORD,
@ -256,6 +254,8 @@ def main():
plexpy.shutdown()
elif plexpy.SIGNAL == 'restart':
plexpy.shutdown(restart=True)
elif plexpy.SIGNAL == 'checkout':
plexpy.shutdown(restart=True, checkout=True)
else:
plexpy.shutdown(restart=True, update=True)

View file

@ -1,8 +1,8 @@
# PlexPy
# Tautulli
[![Discord](https://img.shields.io/badge/Discord-PlexPy-738bd7.svg?style=flat-square)](https://discord.gg/36ggawe)
[![Gitter](https://img.shields.io/badge/Gitter-PlexPy-ed1965.svg?style=flat-square)](https://gitter.im/plexpy/general)
[![Plex Forums](https://img.shields.io/badge/Plex%20Forums-PlexPy-E5A00D.svg?style=flat-square)](https://forums.plex.tv/discussion/169591/plexpy-another-plex-monitoring-program)
[![Discord](https://img.shields.io/badge/Discord-Tautulli-7289DA.svg?style=flat-square)](https://discord.gg/36ggawe)
[![Reddit](https://img.shields.io/badge/Reddit-Tautulli-FF5700.svg?style=flat-square)](https://www.reddit.com/r/Tautulli/)
[![Plex Forums](https://img.shields.io/badge/Plex%20Forums-Tautulli-E5A00D.svg?style=flat-square)](https://forums.plex.tv/discussion/169591/plexpy-another-plex-monitoring-program)
A python based web application for monitoring, analytics and notifications for [Plex Media Server](https://plex.tv).
@ -27,13 +27,13 @@ This project is based on code from [Headphones](https://github.com/rembo10/headp
## Preview
* [Full preview gallery on Imgur](https://imgur.com/a/RwQPM)
* [Full preview gallery on our website](http://tautulli.com)
![PlexPy Homepage](https://i.imgur.com/0D0uFJg.jpg)
![Tautulli Homepage](http://tautulli.com/images/screenshots/activity.png?v=2)
## Installation and Support
* [Installation Guides](https://github.com/JonnyWong16/plexpy/wiki/Installation) shows you how to install PlexPy.
* [Installation Guides](https://github.com/JonnyWong16/plexpy/wiki/Installation) shows you how to install Tautulli.
* [FAQs](https://github.com/JonnyWong16/plexpy/wiki/Frequently-Asked-Questions-(FAQ)) in the wiki can help you with common problems.
**Support** the project by implementing new features, solving support tickets and provide bug fixes.
@ -49,7 +49,7 @@ This project is based on code from [Headphones](https://github.com/rembo10/headp
- Checking the [Wiki](https://github.com/JonnyWong16/plexpy/wiki) for
[ [Installation] ](https://github.com/JonnyWong16/plexpy/wiki/Installation) and
[ [FAQs] ](https://github.com/JonnyWong16/plexpy/wiki/Frequently-Asked-Questions-(FAQ)).
- For basic questions try asking on [Gitter](https://gitter.im/plexpy/general) or the [Plex Forums](https://forums.plex.tv/discussion/169591/plexpy-another-plex-monitoring-program) first before opening an issue.
- For basic questions try asking on [Discord](https://discord.gg/36ggawe), [Reddit](https://www.reddit.com/r/Tautulli), or the [Plex Forums](https://forums.plex.tv/discussion/169591/plexpy-another-plex-monitoring-program) first before opening an issue.
##### If nothing has worked:

View file

@ -8,129 +8,40 @@
<html lang="en">
<head>
<meta charset="utf-8">
<title>PlexPy - ${title} | ${server_name}</title>
<title>Tautulli - ${title} | ${server_name}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="">
<link href="${http_root}css/bootstrap3/bootstrap.css" rel="stylesheet">
<link href="${http_root}css/pnotify.custom.min.css" rel="stylesheet" />
<link href="${http_root}css/plexpy.css" rel="stylesheet">
<link href="${http_root}css/plexpy.css${cache_param}" rel="stylesheet">
<link href="${http_root}css/opensans.min.css" rel="stylesheet">
<link href="${http_root}css/font-awesome.min.css" rel="stylesheet">
${next.headIncludes()}
<link rel="icon" type="image/x-icon" href="${http_root}images/favicon.ico"/>
<link rel="shortcut icon" href="${http_root}images/favicon.png">
<!-- Allow web app to be run in full-screen mode. -->
<meta name="apple-mobile-web-app-capable" content="yes">
<!-- Configure the status bar. -->
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<!-- Set the viewport. -->
<meta name="viewport" content="initial-scale=1">
<!-- Disable automatic phone number detection. -->
<meta name="format-detection" content="telephone=no">
<!-- Favicons -->
<link rel="icon" type="image/png" sizes="32x32" href="${http_root}images/favicon/favicon-32x32.png?v=2.0.0">
<link rel="icon" type="image/png" sizes="16x16" href="${http_root}images/favicon/favicon-16x16.png?v=2.0.0">
<link rel="shortcut icon" href="${http_root}images/favicon/favicon.ico?v=2.0.0">
<!-- ICONS -->
<!-- IE10 icon -->
<meta name="application-name" content="PlexPy" />
<meta name="msapplication-config" content="${http_root}xml/IEconfig.xml"/>
<!-- Android >M39 icon -->
<link rel="manifest" href="${http_root}json/Android-manifest.json">
<!-- iPad retina icon -->
<link href="${http_root}images/res/ios/icon-76@2x.png" sizes="152x152" rel="apple-touch-icon-precomposed">
<!-- iPad retina icon (iOS < 7) -->
<link href="${http_root}images/res/ios/icon-72@2x.png" sizes="144x144" rel="apple-touch-icon-precomposed">
<!-- iPad non-retina icon -->
<link href="${http_root}images/res/ios/icon-76.png" sizes="76x76" rel="apple-touch-icon-precomposed">
<!-- iPad non-retina icon (iOS < 7) -->
<link href="${http_root}images/res/ios/icon-72.png" sizes="72x72" rel="apple-touch-icon-precomposed">
<!-- iPhone 6 Plus icon -->
<link href="${http_root}images/res/ios/icon-60@2x.png" sizes="120x120" rel="apple-touch-icon-precomposed">
<!-- iPhone retina icon (iOS < 7) -->
<link href="${http_root}images/res/ios/icon@2x.png" sizes="114x114" rel="apple-touch-icon-precomposed">
<!-- iPhone non-retina icon (iOS < 7) -->
<link href="${http_root}images/res/ios/icon.png" sizes="57x57" rel="apple-touch-icon-precomposed">
<!-- iPhone / iPod Touch -->
<link href="${http_root}images/res/ios/icon-60@3x.png" sizes="180x180" rel="apple-touch-icon-precomposed">
<link href="${http_root}images/res/ios/icon-60.png" sizes="60x60" rel="apple-touch-icon-precomposed">
<!-- Spotlight Icon -->
<link href="${http_root}images/res/ios/icon-40.png" sizes="40x40" rel="apple-touch-icon-precomposed">
<link href="${http_root}images/res/ios/icon-40@2x.png" sizes="80x80" rel="apple-touch-icon-precomposed">
<!-- iPhone Spotlight and Settings Icon -->
<link href="${http_root}images/res/ios/icon-small.png" sizes="29x29" rel="apple-touch-icon-precomposed">
<link href="${http_root}images/res/ios/icon-small@2x.png" sizes="58x58" rel="apple-touch-icon-precomposed">
<!-- iPad Spotlight and Settings Icon -->
<link href="${http_root}images/res/ios/icon-50.png" sizes="50x50" rel="apple-touch-icon-precomposed">
<link href="${http_root}images/res/ios/icon-50@2x.png" sizes="100x100" rel="apple-touch-icon-precomposed">
<!-- STARTUP IMAGES -->
<!-- iPad retina portrait startup image -->
<link href="${http_root}images/res/ios/Default-Portrait@2x~ipad.png"
media="(device-width: 768px) and (device-height: 1024px)
and (-webkit-device-pixel-ratio: 2)
and (orientation: portrait)"
rel="apple-touch-startup-image">
<!-- iPad retina landscape startup image -->
<link href="${http_root}images/res/ios/Default-Landscape@2x~ipad.png"
media="(device-width: 768px) and (device-height: 1024px)
and (-webkit-device-pixel-ratio: 2)
and (orientation: landscape)"
rel="apple-touch-startup-image">
<!-- iPad non-retina portrait startup image -->
<link href="${http_root}images/res/ios/Default-Portrait~ipad.png"
media="(device-width: 768px) and (device-height: 1024px)
and (-webkit-device-pixel-ratio: 1)
and (orientation: portrait)"
rel="apple-touch-startup-image">
<!-- iPad non-retina landscape startup image -->
<link href="${http_root}images/res/ios/Default-Landscape~ipad.png"
media="(device-width: 768px) and (device-height: 1024px)
and (-webkit-device-pixel-ratio: 1)
and (orientation: landscape)"
rel="apple-touch-startup-image">
<!-- iPhone 6 Plus portrait startup image -->
<link href="${http_root}images/res/ios/Default-736h.png"
media="(device-width: 414px) and (device-height: 736px)
and (-webkit-device-pixel-ratio: 3)
and (orientation: portrait)"
rel="apple-touch-startup-image">
<!-- iPhone 6 Plus landscape startup image -->
<link href="${http_root}images/res/ios/Default-Landscape-736h.png"
media="(device-width: 414px) and (device-height: 736px)
and (-webkit-device-pixel-ratio: 3)
and (orientation: landscape)"
rel="apple-touch-startup-image">
<!-- iPhone 6 startup image -->
<link href="${http_root}images/res/ios/Default-667h.png"
media="(device-width: 375px) and (device-height: 667px)
and (-webkit-device-pixel-ratio: 2)"
rel="apple-touch-startup-image">
<!-- iPhone 5 startup image -->
<link href="${http_root}images/res/ios/Default-568h@2x~iphone5.jpg"
media="(device-width: 320px) and (device-height: 568px)
and (-webkit-device-pixel-ratio: 2)"
rel="apple-touch-startup-image">
<!-- iPhone < 5 retina startup image -->
<link href="${http_root}images/res/ios/Default@2x~iphone.png"
media="(device-width: 320px) and (device-height: 480px)
and (-webkit-device-pixel-ratio: 2)"
rel="apple-touch-startup-image">
<!-- iPhone < 5 non-retina startup image -->
<link href="${http_root}images/res/ios/Default~iphone.png"
media="(device-width: 320px) and (device-height: 480px)
and (-webkit-device-pixel-ratio: 1)"
rel="apple-touch-startup-image">
<link rel="icon" type="image/png" sizes="192x192" href="${http_root}images/favicon/android-chrome-192x192.png?v=2.0.0">
<link rel="manifest" href="${http_root}json/Android-manifest.json?v=2.0.0">
<meta name="theme-color" content="#1f1f1f">
<!-- Apple -->
<link rel="apple-touch-icon" sizes="180x180" href="${http_root}images/favicon/apple-touch-icon.png?v=2.0.0">
<link rel="mask-icon" href="${http_root}images/favicon/safari-pinned-tab.svg?v=2.0.0" color="#1f1f1f">
<meta name="apple-mobile-web-app-title" content="Tautulli">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="viewport" content="initial-scale=1">
<meta name="format-detection" content="telephone=no">
<!-- IE10 icon -->
<meta name="application-name" content="Tautulli">
<meta name="msapplication-TileColor" content="#1f1f1f">
<meta name="msapplication-TileImage" content="${http_root}images/favicon/mstile-144x144.png?v=2.0.0">
<meta name="msapplication-config" content="${http_root}xml/IEconfig.xml?v=2.0.0" />
</head>
<body class="content">
@ -139,7 +50,7 @@
% if _session['user_group'] == 'admin':
% if plexpy.CONFIG.CHECK_GITHUB and not plexpy.CURRENT_VERSION:
<div id="updatebar" style="display: none;">
You're running an unknown version of PlexPy.<br />
You're running an unknown version of Tautulli.<br />
<a href="update">Update</a> or <a href="#" id="updateDismiss">Close</a>
</div>
% elif plexpy.CONFIG.CHECK_GITHUB and plexpy.CURRENT_VERSION != plexpy.LATEST_VERSION and plexpy.COMMITS_BEHIND > 0 and plexpy.INSTALL_TYPE != 'win':
@ -161,7 +72,7 @@
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="home">
<img alt="PlexPy" src="${http_root}images/logo-plexpy@2x.png" height="40">
<img alt="Tautulli" src="${http_root}images/logo-tautulli-50.png" height="40">
</a>
</div>
<div class="collapse navbar-collapse navbar-right" id="navbar-collapse-1">
@ -245,25 +156,14 @@
</nav>
</div>
<div id="confirm-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="confirm-modal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
<h4 class="modal-title">Confirm</h4>
</div>
<div class="modal-body">
<div id="confirm-message" style="text-align: center; margin-top: 20px; margin-bottom: 20px;">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-dark" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger btn-ok" data-dismiss="modal" id="confirm-button">Confirm</button>
</div>
</div>
</div>
${next.headerIncludes()}
<div class="body-container">
${next.body()}
</div>
${next.modalIncludes()}
% if _session['user_group'] != 'admin':
<div id="admin-login-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="admin-login-modal">
<div class="modal-dialog" role="document">
@ -310,13 +210,13 @@
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
<h4 class="modal-title">PlexPy Donation</h4>
<h4 class="modal-title">Tautulli Donation</h4>
</div>
<div class="modal-body">
<div class="row">
<div class="col-md-12" style="text-align: center;">
<h4>
<strong>Thank you for supporting PlexPy!</strong>
<strong>Thank you for supporting Tautulli!</strong>
</h4>
<p>
Please select a donation method.
@ -336,7 +236,7 @@
<p>
Click the button below to continue to PayPal.
</p>
<a href="${anon_url('https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=6XPPKTDSX9QFL&lc=US&item_name=PlexPy&currency_code=USD&bn=PP%2dDonationsBF%3abtn_donate_LG%2egif%3aNonHosted')}" target="_blank">
<a href="${anon_url('https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=6XPPKTDSX9QFL&lc=US&item_name=Tautulli&currency_code=USD&bn=PP%2dDonationsBF%3abtn_donate_LG%2egif%3aNonHosted')}" target="_blank">
<img src="images/gold-rect-paypal-34px.png" alt="PayPal">
</a>
</div>
@ -344,7 +244,7 @@
<p>
Click the button below to continue to Flattr.
</p>
<a href="${anon_url('https://flattr.com/submit/auto?user_id=JonnyWong16&url=https://github.com/JonnyWong16/plexpy&title=PlexPy&language=en_GB&tags=github&category=software')}" target="_blank">
<a href="${anon_url('https://flattr.com/submit/auto?user_id=JonnyWong16&url=https://github.com/JonnyWong16/plexpy&title=Tautulli&language=en_GB&tags=github&category=software')}" target="_blank">
<img src="images/flattr-badge-large.png" alt="Flattr">
</a>
</div>
@ -364,16 +264,30 @@
</div>
% endif
${next.headerIncludes()}
<div class="body-container">
${next.body()}
<div id="confirm-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="confirm-modal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
<h4 class="modal-title">Confirm</h4>
</div>
<div class="modal-body">
<div id="confirm-message" style="text-align: center; margin-top: 20px; margin-bottom: 20px;">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-dark" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger btn-ok" data-dismiss="modal" id="confirm-button">Confirm</button>
</div>
</div>
</div>
</div>
<script src="${http_root}js/jquery-2.1.4.min.js"></script>
<script src="${http_root}js/bootstrap.min.js"></script>
<script src="${http_root}js/bootstrap-hover-dropdown.min.js"></script>
<script src="${http_root}js/pnotify.custom.min.js"></script>
<script src="${http_root}js/script.js"></script>
<script src="${http_root}js/script.js${cache_param}"></script>
<script src="${http_root}js/jquery.qrcode.min.js"></script>
% if _session['user_group'] == 'admin' and plexpy.CONFIG.BROWSER_ENABLED:
<script src="${http_root}js/ajaxNotifications.js"></script>
@ -392,7 +306,7 @@ ${next.headerIncludes()}
}
$("#nav-shutdown").click(function() {
$("#confirm-message").text("Are you sure you want to shutdown PlexPy?");
$("#confirm-message").text("Are you sure you want to shutdown Tautulli?");
$('#confirm-modal').modal();
$('#confirm-modal').one('click', '#confirm-button', function () {
window.location.href = "shutdown";
@ -400,7 +314,7 @@ ${next.headerIncludes()}
});
$("#nav-restart").click(function() {
$("#confirm-message").text("Are you sure you want to restart PlexPy?");
$("#confirm-message").text("Are you sure you want to restart Tautulli?");
$('#confirm-modal').modal();
$('#confirm-modal').one('click', '#confirm-button', function () {
window.location.href = "restart";
@ -465,6 +379,18 @@ ${next.headerIncludes()}
}
});
}
// Allow stacked bootstrap modals
$(document).on('show.bs.modal', '.modal', function (event) {
var zIndex = 1040 + (10 * $('.modal:visible').length);
$(this).css('z-index', zIndex);
setTimeout(function() {
$('.modal-backdrop').not('.modal-stack').css('z-index', zIndex - 1).addClass('modal-stack');
}, 0);
});
$(document).on('hidden.bs.modal', '.modal', function () {
$('.modal:visible').length && $(document.body).addClass('modal-open');
});
});
% if _session['user_group'] != 'admin':
@ -477,6 +403,7 @@ ${next.javascriptIncludes()}
</body>
</html>
<%def name="modalIncludes()"></%def>
<%def name="javascriptIncludes()"></%def>
<%def name="headIncludes()"></%def>
<%def name="headerIncludes()"></%def>

View file

@ -31,15 +31,15 @@ DOCUMENTATION :: END
% endif
<tr>
<td>Configuration File:</td>
<td>${plexpy.CONFIG_FILE}</td>
<td><a class="no-highlight" href="download_config" data-toggle="tooltip" data-placement="right" title="Download Configuration">${plexpy.CONFIG_FILE}</a></td>
</tr>
<tr>
<td>Database File:</td>
<td>${plexpy.DB_FILE}</td>
<td><a class="no-highlight" href="download_database" data-toggle="tooltip" data-placement="right" title="Download Database">${plexpy.DB_FILE}</a></td>
</tr>
<tr>
<td>Log File:</td>
<td><a class="no-highlight" href="logFile" target="_blank">${os.path.join(plexpy.CONFIG.LOG_DIR, logger.FILENAME)}</a></td>
<td><a class="no-highlight" href="download_log" data-toggle="tooltip" data-placement="right" title="Download Log">${os.path.join(plexpy.CONFIG.LOG_DIR, logger.FILENAME)}</a></td>
</tr>
<tr>
<td>Backup Directory:</td>
@ -77,78 +77,37 @@ DOCUMENTATION :: END
<a id="source-link" class="no-highlight" href="${anon_url('https://github.com/%s/plexpy' % plexpy.CONFIG.GIT_USER)}" target="_blank">GitHub Source</a> |
<a class="no-highlight guidelines-modal-link" href="${anon_url('https://github.com/%s/plexpy/issues' % plexpy.CONFIG.GIT_USER)}" data-id="issue">GitHub Issues</a> |
<a class="no-highlight guidelines-modal-link" href="${anon_url('http://feathub.com/%s/plexpy' % plexpy.CONFIG.GIT_USER)}" data-id="feature request">FeatHub Feature Requests</a> |
<a class="no-highlight" href="${anon_url('https://github.com/%s/plexpy/wiki' % plexpy.CONFIG.GIT_USER)}" target="_blank">PlexPy Wiki</a> |
<a id="faq-source-link" class="no-highlight" href="${anon_url('https://github.com/%s/plexpy/wiki/Frequently-Asked-Questions-(FAQ)' % plexpy.CONFIG.GIT_USER)}" target="_blank">PlexPy FAQ</a>
<a class="no-highlight" href="${anon_url('https://github.com/%s/plexpy/wiki' % plexpy.CONFIG.GIT_USER)}" target="_blank">Tautulli Wiki</a> |
<a id="faq-source-link" class="no-highlight" href="${anon_url('https://github.com/%s/plexpy/wiki/Frequently-Asked-Questions-(FAQ)' % plexpy.CONFIG.GIT_USER)}" target="_blank">Tautulli FAQ</a>
</td>
</tr>
<tr>
<td>Support:</td>
<td>
<a class="no-highlight support-modal-link" href="${anon_url('https://forums.plex.tv/discussion/169591/plexpy-another-plex-monitoring-program')}" target="_blank">Plex Forums</a> |
<a class="no-highlight support-modal-link" href="${anon_url('https://gitter.im/plexpy/general')}" target="_blank">PlexPy Gitter Chat</a> |
<a id="best-support-link" class="no-highlight support-modal-link" href="${anon_url('https://discord.gg/011TFFWSuNFI02EKr')}" target="_blank">/r/Plex Discord Server</a> |
<a class="no-highlight support-modal-link" href="${anon_url('https://discord.gg/36ggawe')}" target="_blank">PlexPy Discord Server</a>
<a class="no-highlight support-modal-link" href="${anon_url('https://discord.gg/36ggawe')}" target="_blank">Tautulli Discord Server</a> |
<a class="no-highlight support-modal-link" href="${anon_url('https://www.reddit.com/r/Tautulli')}" target="_blank">Tautulli Subreddit</a> |
<a class="no-highlight support-modal-link" href="${anon_url('https://forums.plex.tv/discussion/169591/plexpy-another-plex-monitoring-program')}" target="_blank">Plex Forums</a>
</td>
</tr>
</tbody>
</table>
<div id="guidelines-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="guidelines-modal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
<h4 class="modal-title">Guidelines</h4>
</div>
<div class="modal-body">
<div style="text-align: center; margin-top: 20px; margin-bottom: 20px;">
<strong>Please read the <a href="#" target="_blank" id="guidelines-link">guidelines</a> in the README document <br />before submitting a new <span id="guidelines-type"></span>!</strong>
<br /><br />
Your post may be removed for failure to follow the guidelines.
</div>
</div>
<div class="modal-footer">
<a href="#" target="_blank" id="guidelines-continue" class="btn btn-bright">Continue</a>
</div>
</div>
</div>
</div>
<div id="support-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="support-modal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
<h4 class="modal-title">Support</h4>
</div>
<div class="modal-body">
<div style="text-align: center; margin-top: 20px; margin-bottom: 20px;">
<strong>Please read the <a href="#" target="_blank" id="faq-link">FAQ</a> before asking for help!</strong>
</div>
</div>
<div class="modal-footer">
<a href="#" target="_blank" id="support-continue" class="btn btn-bright">Continue</a>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function () {
$("#install_geoip_db, #reinstall_geoip_db").click(function () {
var msg = 'Are you sure you want to install the GeoLite2 database?<br /><br />' +
'The database is used to lookup IP address geolocation info.<br />' +
'The database will be downloaded from <a href="${anon_url("https://dev.maxmind.com/geoip/geoip2/geolite2/")}" target="_blank">MaxMind</a>, <br />' +
'and requires <strong>100MB</strong> of free space to install in your PlexPy directory.<br />'
'and requires <strong>100MB</strong> of free space to install in your Tautulli directory.<br />'
var url = 'install_geoip_db';
confirmAjaxCall(url, msg, 'Installing GeoLite2 database.', getConfigurationTable);
confirmAjaxCall(url, msg, null, 'Installing GeoLite2 database.', getConfigurationTable);
});
$("#uninstall_geoip_db").click(function () {
var msg = 'Are you sure you want to uninstall the GeoLite2 database?<br /><br />' +
'You will not be able to lookup IP address geolocation info.';
var url = 'uninstall_geoip_db';
confirmAjaxCall(url, msg, 'Uninstalling GeoLite2 database.', getConfigurationTable);
confirmAjaxCall(url, msg, null, 'Uninstalling GeoLite2 database.', getConfigurationTable);
});
$('.guidelines-modal-link').on('click', function (e) {
@ -168,5 +127,11 @@ DOCUMENTATION :: END
$('#support-modal').modal('hide');
});
});
$('body').tooltip({
selector: '[data-toggle="tooltip"]',
container: 'body'
});
});
</script>

View file

@ -0,0 +1,904 @@
/*************** SCROLLBAR BASE CSS ***************/
.scroll-wrapper {
overflow: hidden !important;
padding: 0 !important;
position: relative;
}
.scroll-wrapper > .scroll-content {
border: none !important;
box-sizing: content-box !important;
height: auto;
left: 0;
margin: 0;
max-height: none;
max-width: none !important;
overflow: scroll !important;
padding: 0;
position: relative !important;
top: 0;
width: auto !important;
}
.scroll-wrapper > .scroll-content::-webkit-scrollbar {
height: 0;
width: 0;
}
.scroll-wrapper.scroll--rtl {
direction: rtl;
}
.scroll-element {
box-sizing: content-box;
display: none;
}
.scroll-element div {
box-sizing: content-box;
}
.scroll-element .scroll-bar,
.scroll-element .scroll-arrow {
cursor: default;
}
.scroll-element.scroll-x.scroll-scrollx_visible, .scroll-element.scroll-y.scroll-scrolly_visible {
display: block;
}
.scroll-textarea {
border: 1px solid #cccccc;
border-top-color: #999999;
}
.scroll-textarea > .scroll-content {
overflow: hidden !important;
}
.scroll-textarea > .scroll-content > textarea {
border: none !important;
box-sizing: border-box;
height: 100% !important;
margin: 0;
max-height: none !important;
max-width: none !important;
overflow: scroll !important;
outline: none;
padding: 2px;
position: relative !important;
top: 0;
width: 100% !important;
}
.scroll-textarea > .scroll-content > textarea::-webkit-scrollbar {
height: 0;
width: 0;
}
/*************** SIMPLE INNER SCROLLBAR ***************/
.scrollbar-inner > .scroll-element,
.scrollbar-inner > .scroll-element div {
border: none;
margin: 0;
padding: 0;
position: absolute;
z-index: 10;
}
.scrollbar-inner > .scroll-element div {
display: block;
height: 100%;
left: 0;
top: 0;
width: 100%;
}
.scrollbar-inner > .scroll-element.scroll-x {
bottom: 2px;
height: 8px;
left: 0;
width: 100%;
}
.scrollbar-inner > .scroll-element.scroll-y {
height: 100%;
right: 2px;
top: 0;
width: 8px;
}
.scrollbar-inner > .scroll-element .scroll-element_outer {
overflow: hidden;
}
.scrollbar-inner > .scroll-element .scroll-element_outer,
.scrollbar-inner > .scroll-element .scroll-element_track,
.scrollbar-inner > .scroll-element .scroll-bar {
-webkit-border-radius: 8px;
-moz-border-radius: 8px;
border-radius: 8px;
}
.scrollbar-inner > .scroll-element .scroll-element_track,
.scrollbar-inner > .scroll-element .scroll-bar {
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=40)";
filter: alpha(opacity=40);
opacity: 0.4;
}
.scrollbar-inner > .scroll-element .scroll-element_track {
background-color: #e0e0e0;
}
.scrollbar-inner > .scroll-element .scroll-bar {
background-color: #c2c2c2;
}
.scrollbar-inner > .scroll-element:hover .scroll-bar {
background-color: #919191;
}
.scrollbar-inner > .scroll-element.scroll-draggable .scroll-bar {
background-color: #919191;
}
/* update scrollbar offset if both scrolls are visible */
.scrollbar-inner > .scroll-element.scroll-x.scroll-scrolly_visible .scroll-element_track {
left: -12px;
}
.scrollbar-inner > .scroll-element.scroll-y.scroll-scrollx_visible .scroll-element_track {
top: -12px;
}
.scrollbar-inner > .scroll-element.scroll-x.scroll-scrolly_visible .scroll-element_size {
left: -12px;
}
.scrollbar-inner > .scroll-element.scroll-y.scroll-scrollx_visible .scroll-element_size {
top: -12px;
}
/*************** SIMPLE OUTER SCROLLBAR ***************/
.scrollbar-outer > .scroll-element,
.scrollbar-outer > .scroll-element div {
border: none;
margin: 0;
padding: 0;
position: absolute;
z-index: 10;
}
.scrollbar-outer > .scroll-element {
background-color: #ffffff;
}
.scrollbar-outer > .scroll-element div {
display: block;
height: 100%;
left: 0;
top: 0;
width: 100%;
}
.scrollbar-outer > .scroll-element.scroll-x {
bottom: 0;
height: 12px;
left: 0;
width: 100%;
}
.scrollbar-outer > .scroll-element.scroll-y {
height: 100%;
right: 0;
top: 0;
width: 12px;
}
.scrollbar-outer > .scroll-element.scroll-x .scroll-element_outer {
height: 8px;
top: 2px;
}
.scrollbar-outer > .scroll-element.scroll-y .scroll-element_outer {
left: 2px;
width: 8px;
}
.scrollbar-outer > .scroll-element .scroll-element_outer {
overflow: hidden;
}
.scrollbar-outer > .scroll-element .scroll-element_track {
background-color: #eeeeee;
}
.scrollbar-outer > .scroll-element .scroll-element_outer,
.scrollbar-outer > .scroll-element .scroll-element_track,
.scrollbar-outer > .scroll-element .scroll-bar {
-webkit-border-radius: 8px;
-moz-border-radius: 8px;
border-radius: 8px;
}
.scrollbar-outer > .scroll-element .scroll-bar {
background-color: #d9d9d9;
}
.scrollbar-outer > .scroll-element .scroll-bar:hover {
background-color: #c2c2c2;
}
.scrollbar-outer > .scroll-element.scroll-draggable .scroll-bar {
background-color: #919191;
}
/* scrollbar height/width & offset from container borders */
.scrollbar-outer > .scroll-content.scroll-scrolly_visible {
left: -12px;
margin-left: 12px;
}
.scrollbar-outer > .scroll-content.scroll-scrollx_visible {
top: -12px;
margin-top: 12px;
}
.scrollbar-outer > .scroll-element.scroll-x .scroll-bar {
min-width: 10px;
}
.scrollbar-outer > .scroll-element.scroll-y .scroll-bar {
min-height: 10px;
}
/* update scrollbar offset if both scrolls are visible */
.scrollbar-outer > .scroll-element.scroll-x.scroll-scrolly_visible .scroll-element_track {
left: -14px;
}
.scrollbar-outer > .scroll-element.scroll-y.scroll-scrollx_visible .scroll-element_track {
top: -14px;
}
.scrollbar-outer > .scroll-element.scroll-x.scroll-scrolly_visible .scroll-element_size {
left: -14px;
}
.scrollbar-outer > .scroll-element.scroll-y.scroll-scrollx_visible .scroll-element_size {
top: -14px;
}
/*************** SCROLLBAR MAC OS X ***************/
.scrollbar-macosx > .scroll-element,
.scrollbar-macosx > .scroll-element div {
background: none;
border: none;
margin: 0;
padding: 0;
position: absolute;
z-index: 10;
}
.scrollbar-macosx > .scroll-element div {
display: block;
height: 100%;
left: 0;
top: 0;
width: 100%;
}
.scrollbar-macosx > .scroll-element .scroll-element_track {
display: none;
}
.scrollbar-macosx > .scroll-element .scroll-bar {
background-color: #6C6E71;
display: block;
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
filter: alpha(opacity=0);
opacity: 0;
-webkit-border-radius: 7px;
-moz-border-radius: 7px;
border-radius: 7px;
-webkit-transition: opacity 0.2s linear;
-moz-transition: opacity 0.2s linear;
-o-transition: opacity 0.2s linear;
-ms-transition: opacity 0.2s linear;
transition: opacity 0.2s linear;
}
.scrollbar-macosx:hover > .scroll-element .scroll-bar,
.scrollbar-macosx > .scroll-element.scroll-draggable .scroll-bar {
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=70)";
filter: alpha(opacity=70);
opacity: 0.7;
}
.scrollbar-macosx > .scroll-element.scroll-x {
bottom: 0px;
height: 0px;
left: 0;
min-width: 100%;
overflow: visible;
width: 100%;
}
.scrollbar-macosx > .scroll-element.scroll-y {
height: 100%;
min-height: 100%;
right: 0px;
top: 0;
width: 0px;
}
/* scrollbar height/width & offset from container borders */
.scrollbar-macosx > .scroll-element.scroll-x .scroll-bar {
height: 7px;
min-width: 10px;
top: -9px;
}
.scrollbar-macosx > .scroll-element.scroll-y .scroll-bar {
left: -9px;
min-height: 10px;
width: 7px;
}
.scrollbar-macosx > .scroll-element.scroll-x .scroll-element_outer {
left: 2px;
}
.scrollbar-macosx > .scroll-element.scroll-x .scroll-element_size {
left: -4px;
}
.scrollbar-macosx > .scroll-element.scroll-y .scroll-element_outer {
top: 2px;
}
.scrollbar-macosx > .scroll-element.scroll-y .scroll-element_size {
top: -4px;
}
/* update scrollbar offset if both scrolls are visible */
.scrollbar-macosx > .scroll-element.scroll-x.scroll-scrolly_visible .scroll-element_size {
left: -11px;
}
.scrollbar-macosx > .scroll-element.scroll-y.scroll-scrollx_visible .scroll-element_size {
top: -11px;
}
/*************** SCROLLBAR LIGHT ***************/
.scrollbar-light > .scroll-element,
.scrollbar-light > .scroll-element div {
border: none;
margin: 0;
overflow: hidden;
padding: 0;
position: absolute;
z-index: 10;
}
.scrollbar-light > .scroll-element {
background-color: #ffffff;
}
.scrollbar-light > .scroll-element div {
display: block;
height: 100%;
left: 0;
top: 0;
width: 100%;
}
.scrollbar-light > .scroll-element .scroll-element_outer {
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
}
.scrollbar-light > .scroll-element .scroll-element_size {
background: #dbdbdb;
background: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2RiZGJkYiIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNlOGU4ZTgiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+");
background: -moz-linear-gradient(left, #dbdbdb 0%, #e8e8e8 100%);
background: -webkit-gradient(linear, left top, right top, color-stop(0%, #dbdbdb), color-stop(100%, #e8e8e8));
background: -webkit-linear-gradient(left, #dbdbdb 0%, #e8e8e8 100%);
background: -o-linear-gradient(left, #dbdbdb 0%, #e8e8e8 100%);
background: -ms-linear-gradient(left, #dbdbdb 0%, #e8e8e8 100%);
background: linear-gradient(to right, #dbdbdb 0%, #e8e8e8 100%);
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
}
.scrollbar-light > .scroll-element.scroll-x {
bottom: 0;
height: 17px;
left: 0;
min-width: 100%;
width: 100%;
}
.scrollbar-light > .scroll-element.scroll-y {
height: 100%;
min-height: 100%;
right: 0;
top: 0;
width: 17px;
}
.scrollbar-light > .scroll-element .scroll-bar {
background: #fefefe;
background: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZlZmVmZSIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNmNWY1ZjUiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+");
background: -moz-linear-gradient(left, #fefefe 0%, #f5f5f5 100%);
background: -webkit-gradient(linear, left top, right top, color-stop(0%, #fefefe), color-stop(100%, #f5f5f5));
background: -webkit-linear-gradient(left, #fefefe 0%, #f5f5f5 100%);
background: -o-linear-gradient(left, #fefefe 0%, #f5f5f5 100%);
background: -ms-linear-gradient(left, #fefefe 0%, #f5f5f5 100%);
background: linear-gradient(to right, #fefefe 0%, #f5f5f5 100%);
border: 1px solid #dbdbdb;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
}
/* scrollbar height/width & offset from container borders */
.scrollbar-light > .scroll-content.scroll-scrolly_visible {
left: -17px;
margin-left: 17px;
}
.scrollbar-light > .scroll-content.scroll-scrollx_visible {
top: -17px;
margin-top: 17px;
}
.scrollbar-light > .scroll-element.scroll-x .scroll-bar {
height: 10px;
min-width: 10px;
top: 0px;
}
.scrollbar-light > .scroll-element.scroll-y .scroll-bar {
left: 0px;
min-height: 10px;
width: 10px;
}
.scrollbar-light > .scroll-element.scroll-x .scroll-element_outer {
height: 12px;
left: 2px;
top: 2px;
}
.scrollbar-light > .scroll-element.scroll-x .scroll-element_size {
left: -4px;
}
.scrollbar-light > .scroll-element.scroll-y .scroll-element_outer {
left: 2px;
top: 2px;
width: 12px;
}
.scrollbar-light > .scroll-element.scroll-y .scroll-element_size {
top: -4px;
}
/* update scrollbar offset if both scrolls are visible */
.scrollbar-light > .scroll-element.scroll-x.scroll-scrolly_visible .scroll-element_size {
left: -19px;
}
.scrollbar-light > .scroll-element.scroll-y.scroll-scrollx_visible .scroll-element_size {
top: -19px;
}
.scrollbar-light > .scroll-element.scroll-x.scroll-scrolly_visible .scroll-element_track {
left: -19px;
}
.scrollbar-light > .scroll-element.scroll-y.scroll-scrollx_visible .scroll-element_track {
top: -19px;
}
/*************** SCROLLBAR RAIL ***************/
.scrollbar-rail > .scroll-element,
.scrollbar-rail > .scroll-element div {
border: none;
margin: 0;
overflow: hidden;
padding: 0;
position: absolute;
z-index: 10;
}
.scrollbar-rail > .scroll-element {
background-color: #ffffff;
}
.scrollbar-rail > .scroll-element div {
display: block;
height: 100%;
left: 0;
top: 0;
width: 100%;
}
.scrollbar-rail > .scroll-element .scroll-element_size {
background-color: #999;
background-color: rgba(0, 0, 0, 0.3);
}
.scrollbar-rail > .scroll-element .scroll-element_outer:hover .scroll-element_size {
background-color: #666;
background-color: rgba(0, 0, 0, 0.5);
}
.scrollbar-rail > .scroll-element.scroll-x {
bottom: 0;
height: 12px;
left: 0;
min-width: 100%;
padding: 3px 0 2px;
width: 100%;
}
.scrollbar-rail > .scroll-element.scroll-y {
height: 100%;
min-height: 100%;
padding: 0 2px 0 3px;
right: 0;
top: 0;
width: 12px;
}
.scrollbar-rail > .scroll-element .scroll-bar {
background-color: #d0b9a0;
-webkit-border-radius: 2px;
-moz-border-radius: 2px;
border-radius: 2px;
box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.5);
}
.scrollbar-rail > .scroll-element .scroll-element_outer:hover .scroll-bar {
box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.6);
}
/* scrollbar height/width & offset from container borders */
.scrollbar-rail > .scroll-content.scroll-scrolly_visible {
left: -17px;
margin-left: 17px;
}
.scrollbar-rail > .scroll-content.scroll-scrollx_visible {
margin-top: 17px;
top: -17px;
}
.scrollbar-rail > .scroll-element.scroll-x .scroll-bar {
height: 10px;
min-width: 10px;
top: 1px;
}
.scrollbar-rail > .scroll-element.scroll-y .scroll-bar {
left: 1px;
min-height: 10px;
width: 10px;
}
.scrollbar-rail > .scroll-element.scroll-x .scroll-element_outer {
height: 15px;
left: 5px;
}
.scrollbar-rail > .scroll-element.scroll-x .scroll-element_size {
height: 2px;
left: -10px;
top: 5px;
}
.scrollbar-rail > .scroll-element.scroll-y .scroll-element_outer {
top: 5px;
width: 15px;
}
.scrollbar-rail > .scroll-element.scroll-y .scroll-element_size {
left: 5px;
top: -10px;
width: 2px;
}
/* update scrollbar offset if both scrolls are visible */
.scrollbar-rail > .scroll-element.scroll-x.scroll-scrolly_visible .scroll-element_size {
left: -25px;
}
.scrollbar-rail > .scroll-element.scroll-y.scroll-scrollx_visible .scroll-element_size {
top: -25px;
}
.scrollbar-rail > .scroll-element.scroll-x.scroll-scrolly_visible .scroll-element_track {
left: -25px;
}
.scrollbar-rail > .scroll-element.scroll-y.scroll-scrollx_visible .scroll-element_track {
top: -25px;
}
/*************** SCROLLBAR DYNAMIC ***************/
.scrollbar-dynamic > .scroll-element,
.scrollbar-dynamic > .scroll-element div {
background: none;
border: none;
margin: 0;
padding: 0;
position: absolute;
z-index: 10;
}
.scrollbar-dynamic > .scroll-element div {
display: block;
height: 100%;
left: 0;
top: 0;
width: 100%;
}
.scrollbar-dynamic > .scroll-element.scroll-x {
bottom: 2px;
height: 7px;
left: 0;
min-width: 100%;
width: 100%;
}
.scrollbar-dynamic > .scroll-element.scroll-y {
height: 100%;
min-height: 100%;
right: 2px;
top: 0;
width: 7px;
}
.scrollbar-dynamic > .scroll-element .scroll-element_outer {
opacity: 0.3;
-webkit-border-radius: 12px;
-moz-border-radius: 12px;
border-radius: 12px;
}
.scrollbar-dynamic > .scroll-element .scroll-element_size {
background-color: #cccccc;
opacity: 0;
-webkit-border-radius: 12px;
-moz-border-radius: 12px;
border-radius: 12px;
-webkit-transition: opacity 0.2s;
-moz-transition: opacity 0.2s;
-o-transition: opacity 0.2s;
-ms-transition: opacity 0.2s;
transition: opacity 0.2s;
}
.scrollbar-dynamic > .scroll-element .scroll-bar {
background-color: #6c6e71;
-webkit-border-radius: 7px;
-moz-border-radius: 7px;
border-radius: 7px;
}
/* scrollbar height/width & offset from container borders */
.scrollbar-dynamic > .scroll-element.scroll-x .scroll-bar {
bottom: 0;
height: 7px;
min-width: 24px;
top: auto;
}
.scrollbar-dynamic > .scroll-element.scroll-y .scroll-bar {
left: auto;
min-height: 24px;
right: 0;
width: 7px;
}
.scrollbar-dynamic > .scroll-element.scroll-x .scroll-element_outer {
bottom: 0;
top: auto;
left: 2px;
-webkit-transition: height 0.2s;
-moz-transition: height 0.2s;
-o-transition: height 0.2s;
-ms-transition: height 0.2s;
transition: height 0.2s;
}
.scrollbar-dynamic > .scroll-element.scroll-y .scroll-element_outer {
left: auto;
right: 0;
top: 2px;
-webkit-transition: width 0.2s;
-moz-transition: width 0.2s;
-o-transition: width 0.2s;
-ms-transition: width 0.2s;
transition: width 0.2s;
}
.scrollbar-dynamic > .scroll-element.scroll-x .scroll-element_size {
left: -4px;
}
.scrollbar-dynamic > .scroll-element.scroll-y .scroll-element_size {
top: -4px;
}
/* update scrollbar offset if both scrolls are visible */
.scrollbar-dynamic > .scroll-element.scroll-x.scroll-scrolly_visible .scroll-element_size {
left: -11px;
}
.scrollbar-dynamic > .scroll-element.scroll-y.scroll-scrollx_visible .scroll-element_size {
top: -11px;
}
/* hover & drag */
.scrollbar-dynamic > .scroll-element:hover .scroll-element_outer,
.scrollbar-dynamic > .scroll-element.scroll-draggable .scroll-element_outer {
overflow: hidden;
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=70)";
filter: alpha(opacity=70);
opacity: 0.7;
}
.scrollbar-dynamic > .scroll-element:hover .scroll-element_outer .scroll-element_size,
.scrollbar-dynamic > .scroll-element.scroll-draggable .scroll-element_outer .scroll-element_size {
opacity: 1;
}
.scrollbar-dynamic > .scroll-element:hover .scroll-element_outer .scroll-bar,
.scrollbar-dynamic > .scroll-element.scroll-draggable .scroll-element_outer .scroll-bar {
height: 100%;
width: 100%;
-webkit-border-radius: 12px;
-moz-border-radius: 12px;
border-radius: 12px;
}
.scrollbar-dynamic > .scroll-element.scroll-x:hover .scroll-element_outer,
.scrollbar-dynamic > .scroll-element.scroll-x.scroll-draggable .scroll-element_outer {
height: 20px;
min-height: 7px;
}
.scrollbar-dynamic > .scroll-element.scroll-y:hover .scroll-element_outer,
.scrollbar-dynamic > .scroll-element.scroll-y.scroll-draggable .scroll-element_outer {
min-width: 7px;
width: 20px;
}
/*************** SCROLLBAR GOOGLE CHROME ***************/
.scrollbar-chrome > .scroll-element,
.scrollbar-chrome > .scroll-element div {
border: none;
margin: 0;
overflow: hidden;
padding: 0;
position: absolute;
z-index: 10;
}
.scrollbar-chrome > .scroll-element {
background-color: #ffffff;
}
.scrollbar-chrome > .scroll-element div {
display: block;
height: 100%;
left: 0;
top: 0;
width: 100%;
}
.scrollbar-chrome > .scroll-element .scroll-element_track {
background: #f1f1f1;
border: 1px solid #dbdbdb;
}
.scrollbar-chrome > .scroll-element.scroll-x {
bottom: 0;
height: 16px;
left: 0;
min-width: 100%;
width: 100%;
}
.scrollbar-chrome > .scroll-element.scroll-y {
height: 100%;
min-height: 100%;
right: 0;
top: 0;
width: 16px;
}
.scrollbar-chrome > .scroll-element .scroll-bar {
background-color: #d9d9d9;
border: 1px solid #bdbdbd;
cursor: default;
-webkit-border-radius: 2px;
-moz-border-radius: 2px;
border-radius: 2px;
}
.scrollbar-chrome > .scroll-element .scroll-bar:hover {
background-color: #c2c2c2;
border-color: #a9a9a9;
}
.scrollbar-chrome > .scroll-element.scroll-draggable .scroll-bar {
background-color: #919191;
border-color: #7e7e7e;
}
/* scrollbar height/width & offset from container borders */
.scrollbar-chrome > .scroll-content.scroll-scrolly_visible {
left: -16px;
margin-left: 16px;
}
.scrollbar-chrome > .scroll-content.scroll-scrollx_visible {
top: -16px;
margin-top: 16px;
}
.scrollbar-chrome > .scroll-element.scroll-x .scroll-bar {
height: 8px;
min-width: 10px;
top: 3px;
}
.scrollbar-chrome > .scroll-element.scroll-y .scroll-bar {
left: 3px;
min-height: 10px;
width: 8px;
}
.scrollbar-chrome > .scroll-element.scroll-x .scroll-element_outer {
border-left: 1px solid #dbdbdb;
}
.scrollbar-chrome > .scroll-element.scroll-x .scroll-element_track {
height: 14px;
left: -3px;
}
.scrollbar-chrome > .scroll-element.scroll-x .scroll-element_size {
height: 14px;
left: -4px;
}
.scrollbar-chrome > .scroll-element.scroll-y .scroll-element_outer {
border-top: 1px solid #dbdbdb;
}
.scrollbar-chrome > .scroll-element.scroll-y .scroll-element_track {
top: -3px;
width: 14px;
}
.scrollbar-chrome > .scroll-element.scroll-y .scroll-element_size {
top: -4px;
width: 14px;
}
/* update scrollbar offset if both scrolls are visible */
.scrollbar-chrome > .scroll-element.scroll-x.scroll-scrolly_visible .scroll-element_size {
left: -19px;
}
.scrollbar-chrome > .scroll-element.scroll-y.scroll-scrollx_visible .scroll-element_size {
top: -19px;
}
.scrollbar-chrome > .scroll-element.scroll-x.scroll-scrolly_visible .scroll-element_track {
left: -19px;
}
.scrollbar-chrome > .scroll-element.scroll-y.scroll-scrollx_visible .scroll-element_track {
top: -19px;
}

View file

@ -34,7 +34,7 @@ table.display {
margin: 0 auto;
clear: both;
width: 100%;
font-size: 14px;
font-size: 12px;
line-height: 25px;
/* Note Firefox 3.5 and before have a bug with border-collapse
* ( https://bugzilla.mozilla.org/show%5Fbug.cgi?id=155955 )
@ -332,4 +332,86 @@ div.box {
}
td.no-wrap, th.no-wrap {
white-space:nowrap;
}
}
/*
* Custom styles
*/
table.display,
table.display tr.shown + tr table[id^='history_child'],
table.display tr.shown + tr table[id^='media_info_child'],
table.display tr.shown + tr table[id^='media_info_child'] tr.shown + tr table[id^='media_info_child'] {
table-layout: auto;
}
table.display tr.shown + tr div.slider {
display: none;
}
table.display tr.shown + tr > td {
padding-top: 0;
padding-bottom: 0;
padding-left: 0;
}
table.display tr.shown + tr:hover {
background-color: rgba(255,255,255,0);
}
table.display tr.shown + tr:hover a,
table.display tr.shown + tr td:hover a,
table.display tr.shown + tr .pagination > .active > a,
table.display tr.shown + tr .pagination > .active > a:hover {
color: #fff;
}
table.display tr.shown + tr table[id^='history_child'] td:hover a,
table.display tr.shown + tr table[id^='media_info_child'] > tr > td:hover a,
table.display tr.shown + tr table[id^='media_info_child'] tr.shown + tr table[id^='media_info_child'] td:hover a {
color: #cc7b19;
}
table.display tr.shown + tr .pagination > .disabled > a,
table.display tr.shown + tr .pagination > .disabled > a:hover {
color: #444444;
}
table.display tr.shown + tr .pagination > li > a:hover {
color: #e9a049;
}
table.display tr.odd td,
table.display tr.even td {
padding: 5px 10px !important;
}
table[id^='history_child'] {
margin-top: 0;
opacity: .6;
}
table[id^='media_info_child'] {
margin-top: 0;
}
div[id^='history_child'] thead th,
div[id^='media_info_child'] thead th {
line-height: 0;
height: 0 !important;
overflow: hidden;
}
div[id^='history_child'] div.row,
div[id^='media_info_child'] div.row {
margin: 0;
}
div[id^='history_child'] div.col-sm-12,
div[id^='media_info_child'] div.col-sm-12 {
padding: 0;
}
div[id^='history_child'] div.dataTables_scrollBody,
div[id^='media_info_child'] div.dataTables_scrollBody {
overflow: hidden !important;
}
div[id^='media_info_child'] div[id^='media_info_child'] div.dataTables_scrollHead thead th {
line-height: 25px;
height: 35px !important;
overflow: hidden;
}
.current-activity-row {
background-color: rgba(255,255,255,.1) !important;
}
.current-activity-row:hover {
background-color: rgba(255,255,255,0.125) !important;
}

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -1,328 +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: current_activity.html
Version: 0.1
Variable names: data [list]
data :: Usable parameters
data['stream_count'] Returns the current number of active streams
data['sessions'] Returns an array containing session data
data[sessions] :: Usable parameters
== Global keys ==
session_key Returns a unique session id for the active stream
rating_key Returns the unique identifier for the media item.
media_index Returns the index of the media item.
parent_media_index Returns the index of the media item's parent.
media_type Returns the type of session. Either 'track', 'episode' or 'movie'.
thumb Returns the location of the item's thumbnail. Use with pms_image_proxy.
bif_thumb Returns the location of the item's bif thumbnail. Use with pms_image_proxy.
art Returns the location of the item's artwork
progress_percent Returns the current progress of the item. 0 to 100.
user Returns the name of the user owning the session.
user_id Returns the Plex user id if available.
machine_id Returns the machine id of the players being used.
friendly_name Returns the friendlly name of the user owning the session.
user_thumb Returns the profile picture of the user owning the session.
state Returns the state of the current session. Either 'playing', 'paused' or 'buffering'.
title Returns the name of the episode, movie or music track.
year Returns the year of the episode, movie, or clip.
ip_address Returns the ip address of the stream.
player Returns the name of the platform used to play the stream.
platform Returns the type of platform used to play the stream.
throttled Returns true if the transcode session is throttled.
transcode_progress Returns the current transcode progress of the item. 0 to 100.
transcode_speed Returns the current transcode speed of the item.
audio_decision Returns the audio transcode decision. Either 'transcode', 'copy' or 'direct play'.
audio_codec Returns the name of the audio codec.
audio_channels Returns the number of audio channels.
grandparent_title Returns the title of the item's grandparent.
parent_title Returns the title of the item's parent.
video_decision Returns the video transcode decision. Either 'transcode', 'copy' or 'direct play'.
video_codec Returns the name of the video codec.
height Returns the value of the video height.
width Returns the value of the video width.
container Returns the value of the media container.
bitrate Returns the value of the media bitrate.
video_resolution Returns the value of the video resolution.
video_framerate Returns the value of the video framerate.
video_aspect_ratio Returns the value of the video aspect ratio.
transcode_audio_channels Returns the amount of audio channels if there is a transcode session.
transcode_audio_codec Returns the name of the audio codec if there is a transcode session.
transcode_video_codec Returns the name of the video codec if there is a transcode session.
transcode_width Returns the video width if there is a transcode session.
transcode_height Returns the video height if there is a transcode session.
transcode_container Returns the value of media container if there is a transcode session.
transcode_protocol Returns the value of media protocol if there is a transcode session.
indexes Returns true if the media has media indexes and are enabled
DOCUMENTATION :: END
</%doc>
% if data is not None:
% if data['stream_count'] != '0':
% for a in data['sessions']:
<div class="dashboard-instance" id="instance-${a['session_key']}">
% if (a['media_type'] == 'movie' or a['media_type'] == 'episode' or a['media_type'] == 'track') and a['rating_key']:
<a href="info?rating_key=${a['rating_key']}">
% else:
<a href="#">
% endif
<div class="dashboard-activity-poster">
% if not a['art'].startswith('interfaces') or not a['thumb'].startswith('interfaces'):
% if (a['media_type'] == 'movie' and not a['indexes']) or (a['indexes'] and not a['view_offset']):
<div class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img=${a['art']}&width=500&height=280&fallback=art);"></div>
% elif (a['media_type'] == 'episode' and not a['indexes']) or (a['indexes'] and not a['view_offset']):
<div class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img=${a['art']}&width=500&height=280&fallback=art);"></div>
% elif a['indexes']:
<div class="dashboard-activity-poster-face bif" style="background-image: url(pms_image_proxy?img=${a['bif_thumb']}&width=500&height=280&fallback=art); display: none;"></div>
% else:
% if a['media_type'] == 'track':
<div class="dashboard-activity-cover-face-bg" style="background-image: url(pms_image_proxy?img=${a['thumb']}&width=300&height=300&fallback=cover);"></div>
<div class="dashboard-activity-cover-face" style="background-image: url(pms_image_proxy?img=${a['thumb']}&width=300&height=300&fallback=cover);"></div>
% elif a['media_type'] == 'clip':
% if a['art'][:4] == 'http':
<div class="dashboard-activity-poster-face" style="background-image: url(${a['art']});"></div>
% elif a['thumb'][:4] == 'http':
<div class="dashboard-activity-poster-face" style="background-image: url(${a['thumb']});"></div>
% else:
% if a['art']:
<div class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img=${a['art']}&width=500&height=280&fallback=art);"></div>
% else:
<div class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img=${a['thumb']}&width=500&height=280&fallback=art);"></div>
% endif
% endif
% elif a['media_type'] == 'photo':
<div class="dashboard-activity-poster-face bif" style="background-image: url(pms_image_proxy?img=${a['thumb']}&width=500&height=500&fallback=cover);"></div>
% else:
<div class="dashboard-activity-cover-face" style="background-image: url(pms_image_proxy?img=${a['thumb']}&width=300&height=300&fallback=cover);"></div>
% endif
% endif
% else:
<div class="dashboard-activity-poster-face" style="background-image: url(${a['art']});"></div>
% endif
% if _session['user_group'] == 'admin':
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
% endif
<div class="dashboard-activity-button-info">
<button type="button" class="btn btn-activity-info btn-lg" data-target="#stream-${a['session_key']}">
<i class="fa fa-info-circle"></i>
</button>
</div>
<div id="stream-${a['session_key']}" class="dashboard-activity-info-details-overlay">
<div class="dashboard-activity-info-details-content">
<div id="platform-${a['session_key']}" title="${a['platform']}">
<script>
$("#platform-${a['session_key']}").html("<div class='dashboard-activity-info-platform-box' style='background-image: url(" + getPlatformImagePath('${a['platform']}') + ");'>");
</script>
</div>
<div class="dashboard-activity-info-platform">
<strong>${a['player']}</strong><br />
% if a['state'] == 'playing':
State &nbsp;<strong>Playing</strong>
% elif a['state'] == 'paused':
State &nbsp;<strong>Paused</strong>
% elif a['state'] == 'buffering':
State &nbsp;<strong>Buffering</strong>
% endif
</div>
% if a['media_type'] == 'track':
% if a['audio_decision'] == 'direct play':
Stream &nbsp;<strong>Direct Play</strong>
% elif a['audio_decision'] == 'copy':
Stream &nbsp;<strong>Direct Stream</strong>
% else:
Stream &nbsp;<strong>Transcoding
(Speed: ${a['transcode_speed']})
% if a['throttled'] == '1':
(Throttled)
% endif
</strong>
% endif
<br />
% if a['audio_decision'] == 'direct play':
Audio &nbsp;<strong>Direct Play (${a['audio_codec']}) (${a['audio_channels']}ch)</strong>
% elif a['audio_decision'] == 'copy':
Audio &nbsp;<strong>Direct Stream (${a['transcode_audio_codec']}) (${a['transcode_audio_channels']}ch)</strong>
% elif a['audio_decision'] == 'transcode':
Audio &nbsp;<strong>Transcode (${a['transcode_audio_codec']}) (${a['transcode_audio_channels']}ch)</strong>
% endif
% elif a['media_type'] == 'episode' or a['media_type'] == 'movie' or a['media_type'] == 'clip':
% if a['video_decision'] == 'direct play' and a['audio_decision'] == 'direct play':
Stream &nbsp;<strong>Direct Play</strong>
% elif a['video_decision'] == 'copy' and a['audio_decision'] == 'copy':
Stream &nbsp;<strong>Direct Stream</strong>
% else:
Stream &nbsp;<strong>Transcoding
(Speed: ${a['transcode_speed']})
% if a['throttled'] == '1':
(Throttled)
% endif
</strong>
% endif
<br />
% if a['video_decision'] == 'direct play':
Video &nbsp;<strong>Direct Play (${a['video_codec']}) (${a['width']}x${a['height']})</strong>
% elif a['video_decision'] == 'copy':
Video &nbsp;<strong>Direct Stream (${a['transcode_video_codec']}) (${a['width']}x${a['height']})</strong>
% elif a['video_decision'] == 'transcode':
Video &nbsp;<strong>Transcode (${a['transcode_video_codec']}) (${a['transcode_width']}x${a['transcode_height']})</strong>
% endif
<br />
% if a['audio_decision'] == 'direct play':
Audio &nbsp;<strong>Direct Play (${a['audio_codec']}) (${a['audio_channels']}ch)</strong>
% elif a['audio_decision'] == 'copy':
Audio &nbsp;<strong>Direct Stream (${a['transcode_audio_codec']}) (${a['transcode_audio_channels']}ch)</strong>
% elif a['audio_decision'] == 'transcode':
Audio &nbsp;<strong>Transcode (${a['transcode_audio_codec']}) (${a['transcode_audio_channels']}ch)</strong>
% endif
% elif a['media_type'] == 'photo':
% if a['video_decision'] == 'direct play':
Stream &nbsp;<strong>Direct Play</strong>
% elif a['video_decision'] == 'copy':
Stream &nbsp;<strong>Direct Stream</strong>
% else:
Stream &nbsp;<strong>
Transcoding
(Speed: ${a['transcode_speed']})
% if a['throttled'] == '1':
(Throttled)
% endif
</strong>
% endif
% endif
<br>
</div>
</div>
% if a['media_type'] != 'photo':
<div class="dashboard-activity-poster-info-bar">
<div class="dashboard-activity-poster-info-ip-address">
% if a['ip_address']:
<span>IP: ${a['ip_address']}</span>
% else:
<span>IP: N/A</span>
% endif
<br />
ETA:
<span id="stream-eta-${a['session_key']}">
<script>
$("#stream-eta-${a['session_key']}").html(moment().add(parseInt(${a['duration']}) - parseInt(${a['view_offset']}), 'milliseconds').format(time_format));
</script>
</span>
</div>
<div class="dashboard-activity-poster-info-time">
<span class="progress_time">${a['view_offset']}</span>/<span class="progress_time">${a['duration']}</span>
</div>
</div>
% endif
</div>
% if (a['media_type'] == 'movie' or a['media_type'] == 'episode' or a['media_type'] == 'track') and a['rating_key']:
</a>
% else:
</a>
% endif
<div class="dashboard-activity-progress">
<div class="dashboard-activity-progress-bar">
<div class="bufferbar" style="width: ${a['transcode_progress']}%" data-toggle="tooltip" title="Transcoder Progress">${a['transcode_progress']}%</div>
<div class="bar" style="width: ${a['progress_percent']}%" data-toggle="tooltip" title="Stream Progress">${a['progress_percent']}%</div>
</div>
</div>
<div class="dashboard-activity-metadata-wrapper">
% if a['user_id']:
<a href="user?user_id=${a['user_id']}">
<div class="dashboard-activity-metadata-user-thumb" style="background-image: url(${a['user_thumb']});"></div>
</a>
% else:
<div class="dashboard-activity-metadata-user-thumb" style="background-image: url(${a['user_thumb']});"></div>
% endif
<div class="dashboard-activity-metadata-title">
% if a['state'] == 'playing':
<i class="fa fa-play"></i>&nbsp;
% elif a['state'] == 'paused':
<i class="fa fa-pause"></i>&nbsp;
% elif a['state'] == 'buffering':
<i class="fa fa-spinner"></i>&nbsp;
% endif
% if a['rating_key']:
% if a['media_type'] == 'episode':
<a href="info?rating_key=${a['rating_key']}" title="${a['grandparent_title']} - ${a['title']}">${a['grandparent_title']} - ${a['title']}</a>
% elif a['media_type'] == 'movie':
<a href="info?rating_key=${a['rating_key']}" title="${a['title']}">${a['title']}</a>
% elif a['media_type'] == 'clip':
<span title="${a['title']}">${a['title']}</span>
% elif a['media_type'] == 'track':
<a href="info?rating_key=${a['rating_key']}" title="${a['grandparent_title']} - ${a['title']}">${a['grandparent_title']} - ${a['title']}</a>
% elif a['media_type'] == 'photo':
<span title="${a['parent_title']}">${a['parent_title']}</span>
% else:
<span title="${a['title']}">${a['title']}</span>
% endif
% else:
${a['title']}
% endif
</div>
<div class="dashboard-activity-metadata-subtitle">
% if a['rating_key']:
% if a['media_type'] == 'episode':
<span title="S${a['parent_media_index']} &middot; E${a['media_index']}">S${a['parent_media_index']} &middot; E${a['media_index']}</span>
% elif a['media_type'] == 'movie':
<span title="${a['year']}">${a['year']}</span>
% elif a['media_type'] == 'track':
<a href="info?rating_key=${a['parent_rating_key']}" title="${a['parent_title']}">${a['parent_title']}</a>
% elif a['media_type'] == 'photo':
<span title="${a['title']}">${a['title']}</span>
% else:
<span title="${a['year']}">${a['year']}</span>
% endif
% endif
</div>
<div class="dashboard-activity-metadata-user">
% if a['user_id']:
<a href="user?user_id=${a['user_id']}" title="${a['friendly_name']}">${a['friendly_name']}</a>
% else:
${a['friendly_name']}
% endif
</div>
</div>
</div>
% endfor
<script>
// When using bif indexes make the image transition a little smoother.
$('.bif').each(function() {
$(this).hide().fadeIn(1000);
});
// Convert timestamps to readable times
$('.progress_time').each(function(index) {
$(this).html(millisecondsToMinutes($(this).text(), false));
});
// Show/Hide activity info
$('.btn-activity-info').on('click', function(e) {
e.preventDefault();
$($(this).attr('data-target')).toggle();
});
// Add hover class to dashboard-instance
$('.dashboard-activity-poster, .dashboard-activity-progress-bar').hover(function() {
$(this).closest('.dashboard-instance').addClass('hover');
}, function() {
$(this).closest('.dashboard-instance').removeClass('hover');
});
$('.bar, .bufferbar').tooltip({container: 'body', placement: 'right', delay: 50});
</script>
% else:
<div class="text-muted">Nothing is currently being watched.</div><br>
% endif
% else:
<div class="text-muted">There was an error communicating with your Plex Server. Please check your <a
href="settings">settings</a>.
</div><br>
% endif

View file

@ -30,7 +30,7 @@ year Returns the year of the episode, movie, or clip.
ip_address Returns the ip address of the stream.
player Returns the name of the platform used to play the stream.
platform Returns the type of platform used to play the stream.
throttled Returns true if the transcode session is throttled.
transcode_throttled Returns true if the transcode session is throttled.
transcode_progress Returns the current transcode progress of the item. 0 to 100.
transcode_speed Returns the current transcode speed of the item.
audio_decision Returns the audio transcode decision. Either 'transcode', 'copy' or 'direct play'.
@ -59,169 +59,296 @@ indexes Returns true if the media has media indexes and are
DOCUMENTATION :: END
</%doc>
% if data is not None:
% if session is not None:
<%
from collections import defaultdict
from urllib import quote
from plexpy import helpers
data['indexes'] = helpers.cast_to_int(data['indexes'])
import plexpy
%>
<div class="dashboard-instance" id="instance-${data['session_key']}" data-id="${data['session_key']}">
<div class="dashboard-hover-container">
% if (data['media_type'] == 'movie' or data['media_type'] == 'episode' or data['media_type'] == 'track') and data['rating_key']:
<a href="info?rating_key=${data['rating_key']}">
<% data = defaultdict(lambda: 'Unknown', **session) %>
<% sk = data['session_key'] %>
<div class="dashboard-activity-instance" id="activity-instance-${sk}" data-key="${sk}" data-id="${data['session_id']}"
data-rating_key="${data['rating_key']}" data-parent_rating_key="${data['parent_rating_key']}" data-grandparent_rating_key="${data['grandparent_rating_key']}">
<div class="dashboard-activity-container">
<div class="dashboard-activity-background-overlay">
% if data['channel_stream'] == '0':
<div id="background-${sk}" class="dashboard-activity-background" style="background-image: url(pms_image_proxy?img=${data['art']}&width=500&height=280&fallback=art&refresh=true);"></div>
% else:
<a href="#">
% if (data['art'] and data['art'].startswith('http')) or (data['thumb'] and data['thumb'].startswith('http')):
<div id="background-${sk}" class="dashboard-activity-background" style="background-image: url(${data['art']});"></div>
% else:
<!--Hacky solution to escape the image url until I come up with something better-->
<div id="background-${sk}" class="dashboard-activity-background" style="background-image: url(pms_image_proxy?img=${quote(data['art'] or data['thumb'])}&width=500&height=280&fallback=art&refresh=true&clip=true);"></div>
% endif
<div class="dashboard-activity-poster" id="poster-${data['session_key']}">
% if not data['art'].startswith('interfaces') or not data['thumb'].startswith('interfaces'):
% if (data['media_type'] == 'movie' and not data['indexes']) or (data['indexes'] and not data['view_offset']):
<div id="bif-${data['session_key']}" class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img=${data['art']}&width=500&height=280&fallback=art);"></div>
% elif (data['media_type'] == 'episode' and not data['indexes']) or (data['indexes'] and not data['view_offset']):
<div id="bif-${data['session_key']}" class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img=${data['art']}&width=500&height=280&fallback=art);"></div>
% elif data['indexes']:
<div id="bif-${data['session_key']}" class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img=${data['bif_thumb']}&width=500&height=280&fallback=art); display: none;"></div>
% endif
<div class="dashboard-activity-poster-container hidden-xs">
% if data['media_type'] == 'track':
<div id="poster-${sk}-bg" class="dashboard-activity-poster-blur" style="background-image: url(pms_image_proxy?img=${data['parent_thumb']}&width=300&height=300&fallback=cover&refresh=true);"></div>
% endif
% if data['channel_stream'] == '0':
% if data['media_type'] == 'movie':
<a id="poster-url-${sk}" href="info?rating_key=${data['rating_key']}" title="${data['title']}">
<div id="poster-${sk}" class="dashboard-activity-poster" style="background-image: url(pms_image_proxy?img=${data['thumb']}&width=300&height=450&fallback=poster&refresh=true);"></div>
</a>
% elif data['media_type'] == 'episode':
<a id="poster-url-${sk}" href="info?rating_key=${data['grandparent_rating_key']}" title="${data['grandparent_title']}">
<div id="poster-${sk}" class="dashboard-activity-poster" style="background-image: url(pms_image_proxy?img=${data['grandparent_thumb']}&width=300&height=450&fallback=poster&refresh=true);"></div>
</a>
% elif data['media_type'] == 'track':
<a id="poster-url-${sk}" href="info?rating_key=${data['parent_rating_key']}" title="${data['parent_title']}">
<div id="poster-${sk}" class="dashboard-activity-cover" style="background-image: url(pms_image_proxy?img=${data['parent_thumb']}&width=300&height=300&fallback=cover&refresh=true);"></div>
</a>
% elif data['media_type'] in ('photo', 'clip'):
<div id="poster-${sk}" class="dashboard-activity-poster" style="background-image: url(pms_image_proxy?img=${data['parent_thumb']}&width=300&height=450&fallback=poster&refresh=true);"></div>
% else:
% if data['media_type'] == 'track':
<div class="dashboard-activity-cover-face-bg" style="background-image: url(pms_image_proxy?img=${data['thumb']}&width=300&height=300&fallback=cover);"></div>
<div class="dashboard-activity-cover-face" style="background-image: url(pms_image_proxy?img=${data['thumb']}&width=300&height=300&fallback=cover);"></div>
% elif data['media_type'] == 'clip':
% if data['art'].startswith('http'):
<div class="dashboard-activity-poster-face" style="background-image: url(${data['art']});"></div>
% elif data['thumb'].startswith('http'):
<div class="dashboard-activity-poster-face" style="background-image: url(${data['thumb']});"></div>
<div id="poster-${sk}" class="dashboard-activity-poster" style="background-image: url(images/art.png);"></div>
% endif
% else:
% if data['channel_icon'].startswith('http'):
<div id="poster-${sk}" class="dashboard-activity-poster-blur" style="background-image: url(${data['channel_icon']});"></div>
<div id="poster-${sk}" class="dashboard-activity-cover" style="background-image: url(${data['channel_icon']});"></div>
% else:
% if data['art']:
<!--Hacky solution to escape the image url until I come up with something better-->
<div class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img=${quote(data['art'])}&width=500&height=280&fallback=art);"></div>
% else:
<!--Hacky solution to escape the image url until I come up with something better-->
<div class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img=${quote(data['thumb'])}&width=500&height=280&fallback=art);"></div>
<div id="poster-${sk}" class="dashboard-activity-poster-blur" style="background-image: url(pms_image_proxy?img=${data['channel_icon']}&width=300&height=300&fallback=cover&refresh=true);"></div>
<div id="poster-${sk}" class="dashboard-activity-cover" style="background-image: url(pms_image_proxy?img=${data['channel_icon']}&width=300&height=300&fallback=cover&refresh=true);"></div>
% endif
% endif
% elif data['media_type'] == 'photo':
<div id="bif-${data['session_key']}" class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img=${data['thumb']}&width=500&height=500&fallback=cover);"></div>
% else:
<div class="dashboard-activity-cover-face" style="background-image: url(pms_image_proxy?img=${data['thumb']}&width=300&height=300&fallback=cover);"></div>
% endif
% endif
% else:
<div class="dashboard-activity-poster-face" style="background-image: url(${data['art']});"></div>
% endif
% if _session['user_group'] == 'admin':
<span class="overlay-refresh-image left" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
% endif
<div class="dashboard-activity-button-info">
<button type="button" class="btn btn-activity-info btn-lg" data-target="#stream-${data['session_key']}" data-id="${data['session_key']}">
<i class="fa fa-info-circle"></i>
</button>
</div>
<div id="stream-${data['session_key']}" class="dashboard-activity-info-details-overlay">
<div class="dashboard-activity-info-details-content">
<div id="platform-${data['session_key']}" title="${data['platform']}">
<script>
$("#platform-${data['session_key']}").html("<div class='dashboard-activity-info-platform-box' style='background-image: url(" + getPlatformImagePath('${data['platform']}') + ");'>");
</script>
</div>
<div class="dashboard-activity-info-platform">
<strong>${data['player']}</strong><br />
<span id="overlay-play-state-${data['session_key']}">
% if data['state'] == 'playing':
State &nbsp;<strong>Playing</strong>
% elif data['state'] == 'paused':
State &nbsp;<strong>Paused</strong>
% elif data['state'] == 'buffering':
State &nbsp;<strong>Buffering</strong>
% endif
</span>
</div>
<span id="transcode-state-${data['session_key']}">
% if data['video_decision'] == 'transcode' or data['audio_decision'] == 'transcode':
Stream &nbsp;<strong>Transcode (Speed: ${data['transcode_speed']})
% if data['throttled'] == '1':
(Throttled)
% endif
</strong>
% elif data['video_decision'] == 'copy' or data['audio_decision'] == 'copy':
Stream &nbsp;<strong>Direct Stream</strong>
% else:
Stream &nbsp;<strong>Direct Play</strong>
% endif
<br />
% if data['video_decision'] and data['media_type'] != 'photo':
% if data['video_decision'] == 'transcode':
Video &nbsp;<strong>Transcode (${data['transcode_video_codec']}) (${data['transcode_width']}x${data['transcode_height']})</strong>
% elif data['video_decision'] == 'copy':
Video &nbsp;<strong>Direct Stream (${data['transcode_video_codec']}) (${data['width']}x${data['height']})</strong>
% endif
</div>
<div class="dashboard-activity-info-icon">
<%
if not _session['user_group'] == 'admin' or not data['session_id']:
no_terminate = '-no-terminate'
else:
no_terminate = ''
%>
<div id="platform-${sk}" class="dashboard-activity-info-platform${no_terminate} svg-icon platform-${data['platform_name']}" title="${data['platform']}"></div>
% if _session['user_group'] == 'admin' and plexpy.CONFIG.PMS_PLEXPASS and data['session_id']:
<div class="dashboard-activity-terminate-session" id="terminate-button-${sk}" data-key="${sk}" data-id="${data['session_id']}" data-toggle="tooltip" title="Terminate Stream">
<i class="fa fa-times" style="padding-top: 8px;"></i>
</div>
% endif
</div>
<div class="dashboard-activity-info-container">
<div class="dashboard-activity-info-scroller scrollbar-macosx">
<div class="dashboard-activity-info scoller-content">
<ul class="list-unstyled dashboard-activity-info-list">
<li class="dashboard-activity-info-item">
<div class="sub-heading">Product</div>
<div class="sub-value">${data['product']}</div>
</li>
<li class="dashboard-activity-info-item">
<div class="sub-heading">Player</div>
<div class="sub-value">${data['player']}</div>
</li>
<li class="dashboard-activity-info-item">
<div class="sub-heading">Quality</div>
<div class="sub-value" id="stream_quality-${sk}">
% if data['media_type'] != 'photo' and data['quality_profile'] != 'Unknown':
<%
br = helpers.cast_to_int(data['stream_bitrate']) or ''
if br:
if br > 1000:
br = '(' + str(round(br / 1000.0, 1)) + ' Mbps)'
else:
br = '(' + str(br) + ' kbps)'
%>
${data['quality_profile']} ${br}
% else:
Video &nbsp;<strong>Direct Play (${data['video_codec']}) (${data['width']}x${data['height']})</strong>
${data['quality_profile']}
% endif
<br />
% endif
% if data['audio_decision']:
% if data['audio_decision'] == 'transcode':
Audio &nbsp;<strong>Transcode (${data['transcode_audio_codec']}) (${data['transcode_audio_channels']}ch)</strong>
% elif data['audio_decision'] == 'copy':
Audio &nbsp;<strong>Direct Stream (${data['transcode_audio_codec']}) (${data['transcode_audio_channels']}ch)</strong>
% else:
Audio &nbsp;<strong>Direct Play (${data['audio_codec']}) (${data['audio_channels']}ch)</strong>
% endif
% endif
</span>
</div>
</div>
% if data['media_type'] != 'photo':
<div class="dashboard-activity-poster-info-bar">
<div class="dashboard-activity-poster-info-ip-address">
% if data['ip_address']:
<span>IP: ${data['ip_address']}</span>
% else:
<span>IP: N/A</span>
</div>
</li>
% if data['optimized_version'] == '1':
<li class="dashboard-activity-info-item">
<div class="sub-heading">Optimized</div>
<div class="sub-value" id="optimized_version-${sk}">
${data['optimized_version_profile']}
</div>
</li>
% endif
<br />
ETA:
<span id="stream-eta-${data['session_key']}">
<script>
$("#stream-eta-${data['session_key']}").html(moment().add(parseInt("${data['duration']}") - parseInt("${data['view_offset']}"), 'milliseconds').format(time_format));
</script>
</span>
</div>
<div class="dashboard-activity-poster-info-time">
<span class="progress_time" id="stream-view-offset-${data['session_key']}">
<script>
$("#stream-view-offset-${data['session_key']}").html(millisecondsToMinutes(parseInt("${data['view_offset']}"), false));
</script>
</span>/<span class="progress_time" id="stream-duration-${data['session_key']}">
<script>
$("#stream-duration-${data['session_key']}").html(millisecondsToMinutes(parseInt("${data['duration']}"), false));
</script>
</span>
</div>
</ul>
<ul class="list-unstyled dashboard-activity-info-list">
<li class="dashboard-activity-info-item">
% if _session['user_group'] == 'admin':
<div class="sub-heading"><span class="raw-stream-info-modal" data-toggle="modal" data-target="#raw-stream-info-modal" data-key="${sk}">Stream</span></div>\
% else:
<div class="sub-heading">Stream</div>
% endif
<div class="sub-value" id="transcode_decision-${sk}">
% if data['transcode_decision'] == 'transcode':
Transcode
% if data['transcode_throttled'] == '1':
(Throttled)
% else:
(Speed: ${data['transcode_speed']})
% endif
% elif data['transcode_decision'] == 'copy':
Direct Stream
% else:
Direct Play ${'(Synced)' if data['synced_version'] == '1' else ''}
% endif
</div>
</li>
<li class="dashboard-activity-info-item">
<div class="sub-heading">Container</div>
<div class="sub-value" id="transcode_container-${sk}">
% if data.get('stream_container_decision') == 'transcode':
Transcode (${data['container'].upper()} &rarr; ${data['stream_container'].upper()})
% else:
Direct Play (${data['container'].upper()})
% endif
</div>
</li>
% if data['media_type'] in ('movie', 'episode', 'clip', 'photo'):
<li class="dashboard-activity-info-item">
<div class="sub-heading">Video</div>
<div class="sub-value" id="video_decision-${sk}">
% if data['media_type'] in ('movie', 'episode', 'clip'):
% if data.get('stream_video_decision') == 'transcode':
<%
hw_d = hw_e = ''
if data['transcode_hw_requested'] == '1' and data['transcode_hw_full_pipeline'] == '0':
hw_d = ' (HW)'
elif data['transcode_hw_requested'] == '1' and data['transcode_hw_full_pipeline'] == '1':
hw_d = hw_e = ' (HW)'
%>
Transcode (${data['video_codec'].upper()}${hw_d} ${plexpy.common.VIDEO_RESOLUTION_OVERRIDES.get(data['video_resolution'], data['video_resolution'])} &rarr; ${data['stream_video_codec'].upper()}${hw_e} ${plexpy.common.VIDEO_RESOLUTION_OVERRIDES.get(data['stream_video_resolution'], data['stream_video_resolution'])})
% elif data.get('stream_video_decision') == 'copy':
Direct Stream (${data['stream_video_codec'].upper()} ${plexpy.common.VIDEO_RESOLUTION_OVERRIDES.get(data['stream_video_resolution'], data['stream_video_resolution'])})
% else:
Direct Play (${data['video_codec'].upper()} ${plexpy.common.VIDEO_RESOLUTION_OVERRIDES.get(data['video_resolution'], data['video_resolution'])})
% endif
% elif data['media_type'] == 'photo':
Direct Play (${data['width']}x${data['height']})
% endif
</div>
</li>
% endif
% if data['media_type'] in ('movie', 'episode', 'clip', 'track'):
<li class="dashboard-activity-info-item">
<div class="sub-heading">Audio</div>
<div class="sub-value" id="audio_decision-${sk}">
% if data.get('stream_audio_decision') == 'transcode':
Transcode (${plexpy.common.AUDIO_CODEC_OVERRIDES.get(data['audio_codec'], data['audio_codec'].upper())} ${data['audio_channel_layout'].split('(')[0].capitalize()} &rarr; ${plexpy.common.AUDIO_CODEC_OVERRIDES.get(data['stream_audio_codec'], data['stream_audio_codec'].upper())} ${data['stream_audio_channel_layout'].split('(')[0].capitalize()})
% elif data.get('stream_audio_decision') == 'copy':
Direct Stream (${plexpy.common.AUDIO_CODEC_OVERRIDES.get(data['stream_audio_codec'], data['stream_audio_codec'].upper())} ${data['stream_audio_channel_layout'].split('(')[0].capitalize()})
% else:
Direct Play (${plexpy.common.AUDIO_CODEC_OVERRIDES.get(data['audio_codec'], data['audio_codec'].upper())} ${data['audio_channel_layout'].split('(')[0].capitalize()})
% endif
</div>
</li>
% endif
% if data['media_type'] in ('movie', 'episode', 'clip'):
<li class="dashboard-activity-info-item">
<div class="sub-heading">Subtitle</div>
<div class="sub-value" id="subtitle_decision-${sk}">
% if data['subtitles'] == '1':
% if data['stream_subtitle_decision'] == 'transcode':
Transcode (${data['subtitle_codec'].upper()} &rarr; ${data['stream_subtitle_codec'].upper()})
% elif data['stream_subtitle_decision'] == 'copy':
Direct Stream (${data['subtitle_codec'].upper()})
% elif data['stream_subtitle_decision'] == 'burn':
Burn (${data['subtitle_codec'].upper()})
% else:
Direct Play (${data['subtitle_codec'].upper()})
% endif
% else:
None
% endif
</div>
</li>
% endif
</ul>
<ul class="list-unstyled dashboard-activity-info-list">
<li class="dashboard-activity-info-item">
<div class="sub-heading">Location</div>
<div class="sub-value">
% if data['ip_address'] != 'N/A':
${'LAN' if data['local'] == '1' else 'WAN'}: ${data['ip_address']}
<a href="#" class="external_ip-modal" data-toggle="modal" data-target="#ip-info-modal" data-ip="${data['ip_address']}">
<span id="external_ip-${sk}" class="external-ip-tooltip" data-toggle="tooltip" title="Lookup IP" style="display: none;"><i class="fa fa-map-marker"></i></span>
</a>
<script>
isPrivateIP("${data['ip_address']}").then(function () {
$("#external_ip-${sk}").hide();
}, function () {
$("#external_ip-${sk}").show();
});
</script>
% else:
N/A
% endif
</div>
</li>
<li class="dashboard-activity-info-item">
<div class="sub-heading">Bandwidth</div>
<div class="sub-value">
% if data['media_type'] != 'photo' and helpers.cast_to_int(data['bandwidth']):
<%
bw = helpers.cast_to_int(data['bandwidth'])
if bw != "Unknown":
if bw > 1000:
bw = str(round(bw / 1000.0, 1)) + ' Mbps'
else:
bw = str(bw) + ' kbps'
%>
<span id="stream-bandwidth-${sk}">${bw}</span>
<span id="streaming-brain-${sk}" data-toggle="tooltip" title="Streaming Brain Estimate"><i class="fa fa-info-circle"></i></span>
% elif data['synced_version'] == '1' or data['channel_stream'] == '1':
<span id="stream-bandwidth-${sk}">None</span>
% else:
<span id="stream-bandwidth-${sk}">Unknown</span>
% endif
</div>
</li>
</ul>
</div>
</div>
% if data['media_type'] != 'photo':
<div class="dashboard-activity-info-time">
% if data['view_offset']:
ETA:
<span id="stream-eta-${sk}">
<script>
$("#stream-eta-${sk}").html(moment().add(parseInt("${data['stream_duration']}") - parseInt("${data['view_offset']}"), 'milliseconds').format(time_format));
</script>
</span><br /><span class="progress_time_offset" id="stream-view-offset-${sk}" data-last_view_offset="${data['view_offset']}" data-view_offset="${data['view_offset']}" data-stream_duration="${data['stream_duration']}" data-state="${data['state']}">
<script>
$("#stream-view-offset-${sk}").html(millisecondsToMinutes(parseInt("${data['view_offset']}"), false));
</script>
</span> / <span class="progress_time_total" id="stream-duration-${sk}">
<script>
$("#stream-duration-${sk}").html(millisecondsToMinutes(parseInt("${data['stream_duration']}"), false));
</script>
</span>
% else:
ETA: Unknown<br />0:00 / <span class="progress_time_total" id="stream-duration-${sk}">
<script>
$("#stream-duration-${sk}").html(millisecondsToMinutes(parseInt("${data['stream_duration']}"), false));
</script>
</span>
% endif
</div>
% if (data['media_type'] == 'movie' or data['media_type'] == 'episode' or data['media_type'] == 'track') and data['rating_key']:
</a>
% else:
</a>
% endif
% endif
</div>
</div>
<div class="dashboard-activity-progress">
<div class="dashboard-activity-progress-bar">
<div id="bufferbar-${data['session_key']}" class="bufferbar" style="width: ${data['transcode_progress']}%" data-toggle="tooltip" title="Transcoder Progress ${data['transcode_progress']}%">${data['transcode_progress']}%</div>
<div id="bar-${data['session_key']}" class="bar" style="width: ${data['progress_percent']}%" data-toggle="tooltip" title="Stream Progress ${data['progress_percent']}%">${data['progress_percent']}%</div>
<div id="buffer-bar-${sk}" class="buffer-bar" style="width: ${data['transcode_progress']}%" data-toggle="tooltip" title="Transcoder Progress ${data['transcode_progress']}%">${data['transcode_progress']}%</div>
<div id="progress-bar-${sk}" class="progress-bar" style="width: ${data['progress_percent']}%" data-last_view_offset="${data['view_offset']}" data-view_offset="${data['view_offset']}" data-stream_duration="${data['stream_duration']}" data-state="${data['state']}" data-toggle="tooltip" title="Stream Progress ${data['progress_percent']}%">${data['progress_percent']}%</div>
</div>
</div>
</div>
<div class="dashboard-activity-metadata-wrapper">
% if data['user_id']:
<a href="user?user_id=${data['user_id']}">
<a href="user?user_id=${data['user_id']}" title="${data['friendly_name']}">
<div class="dashboard-activity-metadata-user-thumb" style="background-image: url(${data['user_thumb']});"></div>
</a>
% else:
<div class="dashboard-activity-metadata-user-thumb" style="background-image: url(${data['user_thumb']});"></div>
% endif
<div class="dashboard-activity-metadata-title">
<span id="play-state-${data['session_key']}">
<div class="dashboard-activity-metadata-title-container">
<div id="play-state-${sk}" class="dashboard-activity-metadata-play_state-icon" title="${data['state'].capitalize()}">
% if data['state'] == 'playing':
<i class="fa fa-fw fa-play"></i>&nbsp;
% elif data['state'] == 'paused':
@ -229,49 +356,91 @@ DOCUMENTATION :: END
% elif data['state'] == 'buffering':
<i class="fa fa-fw fa-spinner"></i>&nbsp;
% endif
</span>
% if data['rating_key']:
% if data['media_type'] == 'episode':
<a href="info?rating_key=${data['grandparent_rating_key']}" title="${data['grandparent_title']}">${data['grandparent_title']}</a>
- <a href="info?rating_key=${data['rating_key']}" title="${data['title']}">${data['title']}</a>
% elif data['media_type'] == 'movie':
<a href="info?rating_key=${data['rating_key']}" title="${data['title']}">${data['title']}</a>
% elif data['media_type'] == 'clip':
<span title="${data['title']}">${data['title']}</span>
% elif data['media_type'] == 'track':
<a href="info?rating_key=${data['grandparent_rating_key']}" title="${data['grandparent_title']}">${data['grandparent_title']}</a>
- <a href="info?rating_key=${data['rating_key']}" title="${data['title']}">${data['title']}</a>
% elif data['media_type'] == 'photo':
<span title="${data['parent_title']}">${data['parent_title']}</span>
% else:
<span title="${data['title']}">${data['title']}</span>
% endif
% else:
${data['title']}
% endif
</div>
<div class="dashboard-activity-metadata-title">
% if data['channel_stream'] == '0':
% if data['media_type'] == 'movie':
<a href="info?rating_key=${data['rating_key']}" title="${data['title']}">${data['title']}</a>
% elif data['media_type'] == 'episode':
<a href="info?rating_key=${data['grandparent_rating_key']}" title="${data['grandparent_title']}">${data['grandparent_title']}</a>
- <a href="info?rating_key=${data['rating_key']}" title="${data['title']}">${data['title']}</a>
% elif data['media_type'] == 'track':
<a id="metadata-grandparent_title-${sk}" href="info?rating_key=${data['grandparent_rating_key']}" title="${data['grandparent_title']}">${data['grandparent_title']}</a>
- <a id="metadata-title-${sk}" href="info?rating_key=${data['rating_key']}" title="${data['title']}">${data['title']}</a>
% elif data['media_type'] == 'photo':
<span title="${data['parent_title']}">${data['parent_title']}</span>
% elif data['media_type'] == 'clip':
<span title="${data['title']}">${data['title']}</span>
% else:
<span title="${data['title']}">${data['title']}</span>
% endif
% elif data['media_type'] == 'episode' and data['grandparent_title']:
<span title="${data['grandparent_title']}">${data['grandparent_title']}</span>
- <span title="${data['title']}">${data['title']}</span>
% else:
<span title="${data['title']}">${data['title']}</span>
% endif
</div>
</div>
<div class="dashboard-activity-metadata-subtitle">
% if data['rating_key']:
% if data['media_type'] == 'episode':
<a href="info?rating_key=${data['parent_rating_key']}" title="Season ${data['parent_media_index']}" class="text-muted">S${data['parent_media_index']}</a>
&middot; <a href="info?rating_key=${data['rating_key']}" title="Episode ${data['media_index']}" class="text-muted">E${data['media_index']}</a>
% elif data['media_type'] == 'movie':
<span title="${data['year']}">${data['year']}</span>
% elif data['media_type'] == 'track':
<a href="info?rating_key=${data['parent_rating_key']}" title="${data['parent_title']}">${data['parent_title']}</a>
% elif data['media_type'] == 'photo':
<span title="${data['title']}">${data['title']}</span>
<div class="dashboard-activity-metadata-subtitle-container">
% if data['channel_stream'] == '0':
<div id="media-type-${sk}" class="dashboard-activity-metadata-media_type-icon" title="${data['media_type'].capitalize()}">
% if data['media_type'] == 'movie':
<i class="fa fa-fw fa-film"></i>&nbsp;
% elif data['media_type'] == 'episode':
<i class="fa fa-fw fa-television"></i>&nbsp;
% elif data['media_type'] == 'track':
<i class="fa fa-fw fa-music"></i>&nbsp;
% elif data['media_type'] == 'photo':
<i class="fa fa-fw fa-picture-o"></i>&nbsp;
% elif data['media_type'] == 'clip':
<i class="fa fa-fw fa-video-camera"></i>&nbsp;
% endif
</div>
% else:
<span title="${data['year']}">${data['year']}</span>
% endif
% endif
</div>
<div class="dashboard-activity-metadata-user">
% if data['user_id']:
<a href="user?user_id=${data['user_id']}" title="${data['friendly_name']}">${data['friendly_name']}</a>
% else:
${data['friendly_name']}
<div id="media-type-${sk}" title="Channel">
<i class="fa fa-fw fa-cloud"></i>&nbsp;
</div>
% endif
<div class="dashboard-activity-metadata-subtitle">
% if data['channel_stream'] == '0':
% if data['media_type'] == 'movie':
<span title="${data['year']}" class="sub-heading">${data['year']}</span>
% elif data['media_type'] == 'episode':
<a href="info?rating_key=${data['parent_rating_key']}" title="Season ${data['parent_media_index']}" class="sub-heading">S${data['parent_media_index']}</a>
&middot; <a href="info?rating_key=${data['rating_key']}" title="Episode ${data['media_index']}" class="sub-heading">E${data['media_index']}</a>
% elif data['media_type'] == 'track':
<a id="metadata-parent_title-${sk}" href="info?rating_key=${data['parent_rating_key']}" title="${data['parent_title']}" class="sub-heading">${data['parent_title']}</a>
% elif data['media_type'] == 'photo':
<span title="${data['title']}" class="sub-heading">${data['title']}</span>
% else:
<span title="${data['year']}" class="sub-heading">${data['year']}</span>
% endif
% elif data['channel_title']:
<span title="${data['channel_title']}" class="sub-heading">${data['channel_title']}</span>
% if data['media_type'] == 'episode' and data['parent_media_index'] and data['media_index']:
(<span title="Season ${data['parent_media_index']}" class="sub-heading">S${data['parent_media_index']}</span>
&middot; <span title="Episode ${data['media_index']}" class="sub-heading">E${data['media_index']}</span>)
% elif data['media_type'] == 'episode' and data['originally_available_at']:
(<span title="${data['originally_available_at']}" class="sub-heading">${data['originally_available_at']}</span>)
% endif
% else:
<span title="Channel" class="sub-heading">Channel</span>
% if data['media_type'] == 'episode' and data['parent_media_index'] and data['media_index']:
(<span title="Season ${data['parent_media_index']}" class="sub-heading">S${data['parent_media_index']}</span>
&middot; <span title="Episode ${data['media_index']}" class="sub-heading">E${data['media_index']}</span>)
% elif data['media_type'] == 'episode' and data['originally_available_at']:
(<span title="${data['originally_available_at']}" class="sub-heading">${data['originally_available_at']}</span>)
% endif
% endif
</div>
<div class="dashboard-activity-metadata-user">
% if data['user_id']:
<a href="user?user_id=${data['user_id']}" title="${data['friendly_name']}">${data['friendly_name']}</a>
% else:
${data['friendly_name']}
% endif
</div>
</div>
</div>
</div>

View file

@ -45,7 +45,7 @@ DOCUMENTATION :: END
<input type="text" class="form-control" id="custom_thumb_url" name="custom_thumb_url" value="${data['library_thumb']}">
</div>
</div>
<p class="help-block">Change the library's picture in PlexPy. To reset to default, leave this field empty and save.</p>
<p class="help-block">Change the library's picture in Tautulli. To reset to default, leave this field empty and save.</p>
</div>
<div class="checkbox">
<label>
@ -81,24 +81,6 @@ DOCUMENTATION :: END
</div>
</div>
</div>
<div id="confirm-modal-purge" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="confirm-modal-purge">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
<h4 class="modal-title" id="myModalLabel">Confirm Purge</h4>
</div>
<div class="modal-body" style="text-align: center;">
<p>Are you REALLY sure you want to purge all history for this library?</p>
<p>This is permanent and cannot be undone!</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-dark" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger btn-ok" data-dismiss="modal" id="confirm-purge">Purge</button>
</div>
</div>
</div>
</div>
<script>
// Save library options
$("#save_library").on('click', function () {
@ -133,47 +115,19 @@ DOCUMENTATION :: END
});
});
$("#delete-all-history").on('click', function() {
$('#confirm-modal-purge').modal();
$('#confirm-modal-purge').one('click', '#confirm-purge', function () {
$.ajax({
url: 'delete_all_library_history',
data: { section_id: '${data["section_id"]}' },
cache: false,
async: true,
success: function(data) {
location.reload();
}
});
});
$('#delete-all-history').click(function () {
var msg = 'Are you REALLY sure you want to purge all history for the <strong>${data["section_name"]}</strong> library?<br>' +
'This is permanent and cannot be undone!';
var url = 'delete_all_library_history';
confirmAjaxCall(url, msg, { section_id: '${data["section_id"]}' }, null, function () { location.reload(); });
});
$(document).ready(function() {
// Move #confirm-modal to parent container
if (!($('#edit-library-modal').next().is('#confirm-modal-purge'))) {
$('#confirm-modal-purge').appendTo($('#edit-library-modal').parent());
}
$('#edit-library-modal > #confirm-modal-purge').remove();
$('#edit-library-modal').css('z-index', '1050');
$('.modal-backdrop').not('.modal-backdrop-stack').css('z-index', '1049');
$('.modal-backdrop').not('.modal-backdrop-stack').addClass('modal-backdrop-stack');
$('#confirm-modal-purge').on('show.bs.modal', function () {
// Fix position to match parent modal
var currentPadding = parseInt($('body').css('padding-right'));
$(this).children('.modal-dialog').css('left', -currentPadding/2);
$('#edit-library-modal').css('overflow-y', 'hidden');
});
$('#confirm-modal-purge').on('shown.bs.modal', function () {
$(this).css('z-index', '1060');
$('.modal-backdrop').not('.modal-backdrop-stack').css('z-index', '1059');
$('.modal-backdrop').not('.modal-backdrop-stack').addClass('modal-backdrop-stack');
});
$('#confirm-modal-purge').on('hidden.bs.modal', function () {
$('body').addClass('modal-open');
$('#edit-library-modal').css('overflow-y', 'auto');
});
});
</script>
% endif

View file

@ -54,7 +54,7 @@ DOCUMENTATION :: END
<input type="text" class="form-control" id="custom_avatar_url" name="custom_avatar_url" value="${data['user_thumb']}">
</div>
</div>
<p class="help-block">Change the users profile picture in PlexPy. To reset to default, leave this field empty and save.</p>
<p class="help-block">Change the users profile picture in Tautulli. To reset to default, leave this field empty and save.</p>
</div>
<div class="checkbox">
<label>
@ -72,7 +72,7 @@ DOCUMENTATION :: END
<label>
<input type="checkbox" id="allow_guest" name="allow_guest" value="1" ${helpers.checked(data['allow_guest'])}> Allow Guest Access
</label>
<p class="help-block">Uncheck this if you do not want to allow this user to login to PlexPy.</p>
<p class="help-block">Uncheck this if you do not want to allow this user to login to Tautulli.</p>
</div>
% if data['user_id']:
<div class="form-group">
@ -90,28 +90,10 @@ DOCUMENTATION :: END
</div>
</div>
</div>
<div id="confirm-modal-purge" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="confirm-modal-purge">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
<h4 class="modal-title" id="myModalLabel">Confirm Purge</h4>
</div>
<div class="modal-body" style="text-align: center;">
<p>Are you REALLY sure you want to purge all history for this user?</p>
<p>This is permanent and cannot be undone!</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-dark" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger btn-ok" data-dismiss="modal" id="confirm-purge">Purge</button>
</div>
</div>
</div>
</div>
<script>
// Set user options
$("#save_user").on('click', function () {
var friendly_name = $("#friendly_name").val();
var friendly_name = $("input#friendly_name").val();
var custom_thumb = $("#custom_avatar_url").val();
var do_notify = 0;
var keep_history = 0;
@ -144,47 +126,19 @@ DOCUMENTATION :: END
});
});
$("#delete-all-history").on('click', function() {
$('#confirm-modal-purge').modal();
$('#confirm-modal-purge').one('click', '#confirm-purge', function () {
$.ajax({
url: 'delete_all_user_history',
data: { user_id: '${data["user_id"]}' },
cache: false,
async: true,
success: function(data) {
location.reload();
}
});
});
$('#delete-all-history').click(function () {
var msg = 'Are you REALLY sure you want to purge all history for the <strong>${data["username"]}</strong> user?<br>' +
'This is permanent and cannot be undone!';
var url = 'delete_all_user_history';
confirmAjaxCall(url, msg, { user_id: '${data["user_id"]}' }, null, function () { location.reload(); });
});
$(document).ready(function() {
// Move #confirm-modal-purge to parent container
if (!($('#edit-user-modal').next().is('#confirm-modal-purge'))) {
$('#confirm-modal-purge').appendTo($('#edit-user-modal').parent());
}
$('#edit-user-modal > #confirm-modal-purge').remove();
$('#edit-user-modal').css('z-index', '1050');
$('.modal-backdrop').not('.modal-backdrop-stack').css('z-index', '1049');
$('.modal-backdrop').not('.modal-backdrop-stack').addClass('modal-backdrop-stack');
$('#confirm-modal-purge').on('show.bs.modal', function () {
// Fix position to match parent modal
var currentPadding = parseInt($('body').css('padding-right'));
$(this).children('.modal-dialog').css('left', -currentPadding/2);
$('#edit-user-modal').css('overflow-y', 'hidden');
});
$('#confirm-modal-purge').on('shown.bs.modal', function () {
$(this).css('z-index', '1060');
$('.modal-backdrop').not('.modal-backdrop-stack').css('z-index', '1059');
$('.modal-backdrop').not('.modal-backdrop-stack').addClass('modal-backdrop-stack');
});
$('#confirm-modal-purge').on('hidden.bs.modal', function () {
$('body').addClass('modal-open');
$('#edit-user-modal').css('overflow-y', 'auto');
});
});
</script>
% endif

View file

@ -20,7 +20,7 @@
</select>
</label>
</div>
<div class="btn-group" data-toggle="buttons" id="yaxis-selection">
<div class="btn-group" style="margin-right: 2px;" data-toggle="buttons" id="yaxis-selection">
% if config['graph_type'] == 'duration':
<label class="btn btn-dark">
<input type="radio" name="yaxis-options" id="yaxis-count" value="plays" autocomplete="off"> Play Count
@ -37,15 +37,15 @@
</label>
% endif
</div>
<div class="btn-group" id="days-selection">
<label>
<input type="number" name="graph-days" id="graph-days" value="${config['graph_days']}" min="1" /> days
</label>
<div class="input-group pull-right" style="width: 1px;" id="days-selection">
<span class="input-group-addon btn-dark inactive">Last</span>
<input type="number" class="form-control" name="graph-days" id="graph-days" value="${config['graph_days']}" min="1" data-default="7" data-toggle="tooltip" title="Min: 1 day" />
<span class="input-group-addon btn-dark inactive">days</span>
</div>
<div class="btn-group" id="months-selection">
<label>
<input type="number" name="graph-months" id="graph-months" value="${config['graph_months']}" min="1" /> months
</label>
<div class="input-group pull-right" style="width: 1px;" id="months-selection">
<span class="input-group-addon btn-dark inactive">Last</span>
<input type="number" class="form-control" name="graph-months" id="graph-months" value="${config['graph_months']}" min="1" data-default="12" data-toggle="tooltip" title="Min: 1 month" />
<span class="input-group-addon btn-dark inactive">months</span>
</div>
</div>
</div>
@ -245,13 +245,18 @@
</div>
</div>
</div>
<div class="modal fade" id="history-modal" tabindex="-1" role="dialog" aria-labelledby="history-modal">
</div>
</div>
</div>
</div>
</%def>
<%def name="modalIncludes()">
<div class="modal fade" id="history-modal" tabindex="-1" role="dialog" aria-labelledby="history-modal">
</div>
<div class="modal fade" id="info-modal" tabindex="-1" role="dialog" aria-labelledby="info-modal">
</div>
</%def>
<%def name="javascriptIncludes()">
<script src="${http_root}js/moment-with-locale.js"></script>
<script src="${http_root}js/moment-duration-format.js"></script>
@ -287,16 +292,17 @@
$.ajax({
url: "history_table_modal",
type: 'post',
data: {
cache: false,
async: true,
data: {
user_id: selected_user_id,
start_date: dateString,
media_type: media_type,
transcode_decision: transcode_decision
},
complete: function(xhr, status) {
$('#history-modal').modal('show');
$("#history-modal").html(xhr.responseText);
$('#history-modal').modal();
}
});
}
@ -566,11 +572,7 @@
e.preventDefault();
current_tab = $(this).attr('href');
loadGraphsTab1(current_day_range, yaxis);
$.ajax({
url: 'set_graph_config',
data: { graph_tab: current_tab.replace('#','') },
async: true
});
$.post('set_graph_config', { graph_tab: current_tab.replace('#','') });
})
// Tab2 opened
@ -578,11 +580,7 @@
e.preventDefault();
current_tab = $(this).attr('href');
loadGraphsTab2(current_day_range, yaxis);
$.ajax({
url: 'set_graph_config',
data: { graph_tab: current_tab.replace('#','') },
async: true
});
$.post('set_graph_config', { graph_tab: current_tab.replace('#','') });
})
// Tab3 opened
@ -590,46 +588,28 @@
e.preventDefault();
current_tab = $(this).attr('href');
loadGraphsTab3(current_month_range, yaxis);
$.ajax({
url: 'set_graph_config',
data: { graph_tab: current_tab.replace('#','') },
async: true
});
$.post('set_graph_config', { graph_tab: current_tab.replace('#','') });
})
// Date range changed
$('#graph-days').tooltip({ container: 'body', placement: 'top', html: true });
$('#graph-days').on('change', function() {
current_day_range = Math.round($(this).val());
$(this).val(current_day_range);
if (current_day_range < 1) {
$(this).val(7);
current_day_range = 7;
}
forceMinMax($(this));
current_day_range = $(this).val();
if (current_tab == '#tabs-1') { loadGraphsTab1(current_day_range, yaxis); }
if (current_tab == '#tabs-2') { loadGraphsTab2(current_day_range, yaxis); }
$('.days').html(current_day_range);
$.ajax({
url: 'set_graph_config',
data: { graph_days: current_day_range},
async: true
});
$.post('set_graph_config', { graph_days: current_day_range });
});
// Month range changed
$('#graph-months').tooltip({ container: 'body', placement: 'top', html: true });
$('#graph-months').on('change', function() {
current_month_range = Math.round($(this).val());
$(this).val(current_month_range);
if (current_month_range < 1) {
$(this).val(12);
current_month_range = 12;
}
forceMinMax($(this));
current_month_range = $(this).val();
if (current_tab == '#tabs-3') { loadGraphsTab3(current_month_range, yaxis); }
$('.months').html(current_month_range);
$.ajax({
url: 'set_graph_config',
data: { graph_months: current_month_range},
async: true
});
$.post('set_graph_config', { graph_months: current_month_range });
});
// User changed
@ -646,11 +626,7 @@
if (current_tab == '#tabs-1') { loadGraphsTab1(current_day_range, yaxis); }
if (current_tab == '#tabs-2') { loadGraphsTab2(current_day_range, yaxis); }
if (current_tab == '#tabs-3') { loadGraphsTab3(current_month_range, yaxis); }
$.ajax({
url: 'set_graph_config',
data: { graph_type: yaxis},
async: true
});
$.post('set_graph_config', { graph_type: yaxis });
});
function setGraphFormat(type) {

View file

@ -45,6 +45,9 @@
<input type="radio" name="media_type-filter" id="history-music" value="track" autocomplete="off"> Music
</label>
</div>
<div class="btn-group">
<button class="btn btn-dark refresh-history-button" id="refresh-history-list"><i class="fa fa-refresh"></i> Refresh history</button>
</div>
<div class="btn-group colvis-button-bar"></div>
</div>
</div>
@ -53,7 +56,7 @@
<thead>
<tr>
<th align="left" id="delete_row">Delete</th>
<th align="left" id="time">Time</th>
<th align="left" id="date">Date</th>
<th align="left" id="friendly_name">User</th>
<th align="left" id="ip_address">IP Address</th>
<th align="left" id="platform">Platform</th>
@ -69,26 +72,29 @@
<tbody>
</tbody>
</table>
<div class="modal fade" id="info-modal" tabindex="-1" role="dialog" aria-labelledby="info-modal">
</div>
<div class="modal fade" id="ip-info-modal" tabindex="-1" role="dialog" aria-labelledby="ip-info-modal">
</div>
<div class="modal fade" id="confirm-modal-delete" tabindex="-1" role="dialog" aria-labelledby="confirm-modal-delete">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
<h4 class="modal-title" id="myModalLabel">Confirm Delete</h4>
</div>
<div class="modal-body" style="text-align: center;">
<p>Are you REALLY sure you want to delete <strong><span id="deleteCount"></span></strong> history item(s)?</p>
<p>This is permanent and cannot be undone!</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-dark" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger btn-ok" data-dismiss="modal" id="confirm-delete">Delete</button>
</div>
</div>
</div>
</div>
</%def>
<%def name="modalIncludes()">
<div class="modal fade" id="info-modal" tabindex="-1" role="dialog" aria-labelledby="info-modal">
</div>
<div class="modal fade" id="ip-info-modal" tabindex="-1" role="dialog" aria-labelledby="ip-info-modal">
</div>
<div class="modal fade" id="confirm-modal-delete" tabindex="-1" role="dialog" aria-labelledby="confirm-modal-delete">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
<h4 class="modal-title" id="myModalLabel">Confirm Delete</h4>
</div>
<div class="modal-body" style="text-align: center;">
<p>Are you REALLY sure you want to delete <strong><span id="deleteCount"></span></strong> history item(s)?</p>
<p>This is permanent and cannot be undone!</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-dark" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger btn-ok" data-dismiss="modal" id="confirm-delete">Delete</button>
</div>
</div>
</div>
@ -101,7 +107,7 @@
<script src="${http_root}js/dataTables.bootstrap.min.js"></script>
<script src="${http_root}js/dataTables.bootstrap.pagination.js"></script>
<script src="${http_root}js/moment-with-locale.js"></script>
<script src="${http_root}js/tables/history_table.js"></script>
<script src="${http_root}js/tables/history_table.js${cache_param}"></script>
<script>
$(document).ready(function () {
// Load user ids and names (for the selector)
@ -166,17 +172,18 @@
$('#deleteCount').text(history_to_delete.length);
$('#confirm-modal-delete').modal();
$('#confirm-modal-delete').one('click', '#confirm-delete', function () {
for (var i = 0; i < history_to_delete.length; i++) {
history_to_delete.forEach(function(row, idx) {
$.ajax({
url: 'delete_history_rows',
data: { row_id: history_to_delete[i] },
type: 'POST',
data: { row_id: row },
async: true,
success: function (data) {
var msg = "History deleted";
showMsg(msg, false, true, 2000);
}
});
}
});
history_table.draw();
});
}
@ -196,5 +203,9 @@
});
% endif
});
$("#refresh-history-list").click(function() {
history_table.draw();
});
</script>
</%def>

View file

@ -27,9 +27,8 @@
<div class="modal-footer"></div>
</div>
</div>
<div class="modal fade" id="info-modal" tabindex="-1" role="dialog" aria-labelledby="info-modal">
</div>
<script src="${http_root}js/tables/history_table_modal.js"></script>
<script src="${http_root}js/tables/history_table_modal.js${cache_param}"></script>
<script>
$(document).ready(function() {
$('#date-header').html(moment('${data["start_date"]}','YYYY-MM-DD').format('ddd MMM Do YYYY'));
@ -44,39 +43,13 @@
start_date: "${data['start_date']}",
media_type: "${data.get('media_type')}",
transcode_decision: "${data.get('transcode_decision')}"
};
};
}
}
history_table = $('#history_table_modal').DataTable(history_table_modal_options);
clearSearchButton('history_table_modal', history_table);
// Move #info-modal to parent container
if (!($('#history-modal').next().is('#info-modal'))) {
$('#info-modal').appendTo($('#history-modal').parent());
}
$('#history-modal > #info-modal').remove();
$('#history-modal').css('z-index', '1050');
$('.modal-backdrop').not('.modal-backdrop-stack').css('z-index', '1049');
$('.modal-backdrop').not('.modal-backdrop-stack').addClass('modal-backdrop-stack');
$('#info-modal').on('show.bs.modal', function () {
// Fix position to match parent modal
var currentPadding = parseInt($('body').css('padding-right'));
$(this).children('.modal-dialog').css('left', -currentPadding / 2);
$('#history-modal').css('overflow-y', 'hidden');
setTimeout(function () {
$('#info-modal').css('z-index', '1060');
$('.modal-backdrop').not('.modal-backdrop-stack').css('z-index', '1059');
$('.modal-backdrop').not('.modal-backdrop-stack').addClass('modal-backdrop-stack');
}, 0);
});
$('#info-modal').on('hidden.bs.modal', function () {
$('body').addClass('modal-open');
$('#history-modal').css('overflow-y', 'auto');
});
});
</script>
% else:

File diff suppressed because it is too large Load diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

View file

@ -0,0 +1,70 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="275.000000pt" height="275.000000pt" viewBox="0 0 275.000000 275.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.11, written by Peter Selinger 2001-2013
</metadata>
<g transform="translate(0.000000,275.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M2200 2569 c0 -4 -12 -11 -27 -17 -57 -20 -143 -130 -149 -187 -7
-82 -6 -105 6 -140 7 -22 23 -56 36 -75 13 -19 20 -37 16 -40 -5 -3 -11 -13
-15 -23 -4 -11 -12 -15 -19 -11 -7 4 -10 4 -6 -1 10 -11 -20 -65 -37 -65 -8 0
-12 -6 -9 -14 3 -7 -2 -16 -11 -20 -9 -3 -14 -10 -11 -15 4 -5 -5 -18 -19 -30
-15 -11 -26 -23 -27 -28 0 -4 -4 -7 -9 -6 -4 2 -6 -3 -4 -10 3 -6 -6 -24 -20
-39 -14 -16 -23 -28 -20 -28 4 0 0 -7 -7 -15 -30 -33 -38 -45 -38 -55 0 -5 -4
-10 -8 -10 -5 0 -17 -16 -28 -35 -10 -19 -22 -35 -26 -35 -5 0 -8 -5 -8 -10 0
-10 -8 -22 -37 -55 -7 -7 -13 -17 -13 -22 0 -5 -13 -23 -30 -41 -16 -18 -31
-36 -32 -40 -2 -4 -6 -6 -10 -5 -4 2 -4 -5 -1 -14 4 -10 2 -14 -4 -10 -6 4
-13 -3 -17 -16 -3 -12 -16 -34 -30 -48 -14 -15 -22 -29 -19 -33 3 -3 -2 -8
-11 -12 -9 -3 -16 -12 -16 -19 0 -6 -11 -24 -25 -39 -13 -14 -24 -30 -24 -33
0 -4 -5 -9 -12 -12 -6 -2 -9 1 -6 6 3 5 -2 9 -12 8 -10 0 -45 2 -79 5 -34 3
-62 2 -62 -2 0 -4 -7 -8 -15 -8 -21 0 -95 -33 -95 -43 0 -4 -5 -5 -12 -1 -7 5
-8 3 -4 -5 5 -8 0 -14 -13 -18 -11 -3 -18 -9 -15 -14 3 -5 1 -9 -4 -9 -8 0
-43 -60 -48 -82 -1 -5 -3 -10 -4 -13 -1 -3 -4 -9 -5 -15 -1 -5 -4 -13 -5 -17
-1 -5 -1 -17 0 -28 1 -16 -5 -13 -28 15 -17 19 -28 38 -25 43 2 4 1 6 -4 5
-14 -5 -114 112 -107 124 4 6 3 8 -3 5 -5 -3 -44 33 -85 81 -42 48 -90 103
-107 122 -55 61 -72 88 -61 95 34 21 50 139 30 210 -6 22 -12 48 -13 57 -1 9
-5 14 -9 12 -4 -3 -8 -2 -8 3 0 17 -70 84 -110 104 -26 14 -67 24 -110 27 -57
4 -76 1 -121 -20 -30 -14 -67 -38 -83 -54 -33 -34 -70 -84 -61 -84 3 0 -1 -10
-9 -22 -23 -37 -18 -147 8 -205 61 -130 212 -195 343 -149 38 13 41 13 59 -7
10 -12 17 -23 15 -25 -1 -2 8 -13 21 -24 13 -11 22 -25 20 -32 -2 -6 -1 -8 3
-4 7 7 35 -15 35 -27 0 -4 14 -20 30 -35 17 -15 30 -32 30 -38 0 -5 7 -13 16
-16 8 -3 12 -11 8 -17 -4 -7 -3 -9 3 -6 11 7 49 -42 45 -56 -1 -5 1 -6 6 -3 8
5 49 -35 43 -43 -1 -2 4 -8 13 -13 9 -5 13 -14 10 -20 -4 -6 -3 -8 3 -5 11 7
45 -28 36 -37 -4 -3 2 -6 12 -6 10 0 15 -3 12 -6 -4 -4 4 -16 17 -28 13 -12
31 -33 40 -47 15 -24 15 -27 0 -33 -8 -3 -21 -3 -26 1 -7 3 -8 1 -4 -5 4 -7
-2 -16 -16 -22 -13 -6 -43 -24 -68 -40 -25 -16 -56 -35 -70 -42 -14 -7 -28
-17 -31 -22 -4 -5 -12 -5 -19 -1 -9 5 -11 4 -6 -3 4 -7 3 -12 -1 -12 -5 0 -33
-16 -63 -35 -61 -40 -63 -40 -82 -22 -7 8 -24 19 -36 26 -12 7 -20 17 -16 23
4 7 3 8 -4 4 -6 -4 -31 -2 -54 5 -49 14 -128 18 -128 7 0 -5 -7 -8 -16 -8 -28
0 -80 -30 -120 -69 -43 -43 -65 -68 -64 -76 1 -27 -1 -35 -9 -31 -5 3 -8 -32
-7 -82 2 -128 46 -201 155 -256 96 -49 228 -27 309 52 17 16 34 27 37 24 4 -4
5 -2 4 3 -4 15 11 56 20 51 6 -4 11 18 14 62 0 6 4 12 8 12 4 0 5 8 3 18 -2 9
-5 36 -7 59 -4 40 -2 44 32 65 20 12 39 21 44 20 4 -1 7 3 7 9 0 6 7 8 17 5
10 -4 14 -4 11 0 -6 5 54 54 67 54 2 0 24 14 49 31 25 17 51 27 57 23 8 -4 9
-3 5 5 -4 6 0 14 11 18 10 3 30 14 44 24 14 10 29 16 33 12 3 -3 6 -1 6 5 0 7
7 12 15 12 8 0 15 -7 15 -15 0 -8 -5 -15 -12 -15 -6 0 -9 -2 -6 -5 3 -3 0 -24
-6 -47 -7 -23 -10 -59 -6 -83 4 -23 6 -52 5 -63 -1 -12 2 -19 6 -17 3 3 13
-10 20 -28 19 -47 95 -113 152 -131 123 -40 255 4 319 106 22 36 45 44 60 23
5 -6 8 -5 8 2 0 9 7 10 24 3 13 -5 44 -11 67 -15 24 -3 76 -12 114 -19 39 -8
86 -17 106 -20 33 -6 38 -11 44 -43 12 -67 50 -124 113 -176 12 -9 26 -17 30
-17 4 0 19 -6 34 -14 15 -8 56 -14 95 -14 245 0 365 295 190 468 -31 31 -64
53 -92 61 -24 7 -42 15 -40 18 2 4 -20 5 -48 4 -29 -1 -52 -2 -52 -3 0 -1 -10
-3 -23 -6 -61 -11 -182 -105 -182 -141 0 -9 -8 -12 -22 -8 -26 5 -184 33 -238
41 -19 3 -42 8 -50 11 -15 6 -35 10 -70 12 -14 1 -20 8 -20 26 0 29 -39 115
-60 132 -12 10 -13 15 -2 30 24 34 61 117 56 125 -3 4 0 8 6 8 6 0 9 4 6 8 -3
5 -6 34 -7 65 -1 31 -6 57 -11 57 -4 0 -8 7 -8 16 0 9 -9 29 -21 45 -17 24
-18 33 -9 44 7 8 16 12 21 9 5 -3 6 2 3 10 -3 9 -1 16 5 16 6 0 10 6 8 13 -1
6 4 11 11 9 8 -1 11 1 8 6 -7 12 39 74 48 65 4 -4 4 0 0 10 -3 10 -1 17 5 17
6 0 11 7 11 15 0 8 7 15 15 15 8 0 14 3 13 8 -3 10 110 180 199 297 5 6 26 36
47 67 21 32 43 55 47 52 5 -3 6 1 4 8 -3 8 3 23 13 34 11 11 30 36 43 56 13
19 40 58 59 86 l35 52 45 -7 c25 -3 50 -6 55 -6 78 5 153 38 199 87 68 72 96
198 63 279 -9 20 -14 37 -11 37 5 0 -43 55 -76 86 -14 13 -28 24 -33 24 -4 0
-5 5 -1 12 4 7 3 8 -4 4 -6 -4 -24 0 -40 8 -28 15 -192 19 -192 5z"/>
<path d="M2046 2132 c-3 -5 1 -9 9 -9 8 0 12 4 9 9 -3 4 -7 8 -9 8 -2 0 -6 -4
-9 -8z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

View file

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="483.904px" height="409.539px" viewBox="66.712 194.513 483.904 409.539"
enable-background="new 66.712 194.513 483.904 409.539" xml:space="preserve">
<g>
<g>
<path fill="none" d="M41.209,627.586c0-151.979,0-303.963,0-456.118c177.822,0,355.688,0,533.881,0c0,151.922,0,304.021,0,456.118
C397.214,627.586,219.233,627.586,41.209,627.586z M461.314,421.571c-110.652,0-220.389,0-329.873,0c0,18.87,0,36.226,0,54.729
c110.29,0,219.734,0,329.824,0c0,6.858-0.271,12.563,0.058,18.231c0.659,11.344,8.853,15.958,18.112,9.46
c22.087-15.499,43.824-31.503,65.474-47.611c7.678-5.715,7.621-10.63-0.234-16.467c-21.352-15.862-42.87-31.508-64.563-46.902
c-9.542-6.773-17.488-2.864-18.691,8.976C460.813,407.959,461.314,414.046,461.314,421.571z M130.996,574.741
c74.536,0,148.057,0,222.423,0c0,6.464-0.617,11.836,0.192,16.98c0.662,4.21,2.17,10.746,4.826,11.622
c4.329,1.427,10.994,0.66,14.77-1.943c21.297-14.688,42.098-30.1,62.911-45.474c8.887-6.564,8.992-11.503,0.233-18.001
c-20.777-15.419-41.647-30.727-62.83-45.579c-3.569-2.503-10.381-4.832-13.03-3.118c-3.676,2.376-5.784,8.447-6.81,13.308
c-1.126,5.336-0.257,11.091-0.257,17.757c-75.009,0-148.525,0-222.43,0C130.996,538.558,130.996,555.663,130.996,574.741z
M130.83,281.392c49.453,0,97.705,0,146.88,0c0,6.838-0.232,12.506,0.049,18.148c0.585,11.758,9.078,16.246,18.887,9.342
c21.767-15.316,43.232-31.064,64.624-46.902c8.01-5.932,7.995-11.301-0.134-17.342c-21.053-15.65-42.257-31.103-63.625-46.318
c-10.735-7.647-18.836-3.478-19.734,9.846c-0.378,5.611-0.063,11.27-0.063,18.431c-49.546,0-98.1,0-146.885,0
C130.83,245.229,130.83,262.325,130.83,281.392z M188.205,379.701c0,6.939-0.606,12.297,0.19,17.438
c0.649,4.198,2.088,10.688,4.753,11.61c4.316,1.496,11.054,0.904,14.8-1.673c21.612-14.857,42.736-30.435,63.834-46.026
c8.481-6.265,8.471-11.812-0.159-18.227c-21.054-15.647-42.241-31.123-63.625-46.315c-10.656-7.571-18.899-3.211-19.724,10.07
c-0.365,5.875-0.062,11.793-0.062,18.885c-20.45,0-38.938,0-57.202,0c0,18.619,0,35.973,0,54.238
C149.953,379.701,168.131,379.701,188.205,379.701z M66.712,280.18c17.568,0,33.699,0,49.59,0c0-18.261,0-35.579,0-53.046
c-16.925,0-33.07,0-49.59,0C66.712,245.159,66.712,262.481,66.712,280.18z M66.869,325.488c0,18.556,0,35.876,0,53.543
c16.862,0,33,0,49.278,0c0-18.274,0-35.852,0-53.543C99.359,325.488,83.5,325.488,66.869,325.488z M116.673,422.747
c-17.484,0-33.644,0-49.668,0c0,17.835,0,34.781,0,51.779c17.017,0,33.178,0,49.668,0
C116.673,456.924,116.673,440.208,116.673,422.747z M116.497,520.842c-17.01,0-33.43,0-49.552,0c0,18.244,0,35.566,0,53.021
c16.944,0,33.084,0,49.552,0C116.497,555.793,116.497,538.471,116.497,520.842z"/>
<path fill="#FFFFFF" d="M461.314,421.571c0-7.525-0.503-13.612,0.105-19.586c1.203-11.839,9.149-15.749,18.691-8.976
c21.691,15.396,43.211,31.04,64.563,46.902c7.855,5.837,7.912,10.752,0.234,16.467c-21.648,16.108-43.387,32.112-65.474,47.611
c-9.261,6.498-17.453,1.884-18.112-9.46c-0.329-5.67-0.058-11.373-0.058-18.231c-110.09,0-219.534,0-329.824,0
c0-18.503,0-35.857,0-54.729C240.927,421.571,350.662,421.571,461.314,421.571z"/>
<path fill="#FFFFFF" d="M130.996,574.741c0-19.078,0-36.185,0-54.448c73.904,0,147.42,0,222.43,0
c0-6.666-0.869-12.421,0.257-17.757c1.024-4.859,3.134-10.932,6.81-13.308c2.649-1.714,9.461,0.615,13.029,3.118
c21.184,14.854,42.054,30.16,62.831,45.579c8.759,6.498,8.652,11.437-0.233,18.001c-20.813,15.374-41.614,30.786-62.911,45.474
c-3.774,2.604-10.439,3.369-14.77,1.943c-2.656-0.876-4.165-7.412-4.826-11.622c-0.811-5.146-0.192-10.518-0.192-16.98
C279.053,574.741,205.532,574.741,130.996,574.741z"/>
<path fill="#FFFFFF" d="M130.83,281.392c0-19.067,0-36.163,0-54.795c48.785,0,97.339,0,146.885,0
c0-7.161-0.315-12.82,0.063-18.431c0.898-13.323,8.999-17.493,19.734-9.846c21.368,15.215,42.572,30.668,63.625,46.318
c8.129,6.041,8.144,11.411,0.133,17.342c-21.391,15.838-42.855,31.586-64.623,46.902c-9.81,6.904-18.302,2.417-18.887-9.342
c-0.281-5.643-0.049-11.31-0.049-18.148C228.535,281.392,180.283,281.392,130.83,281.392z"/>
<path fill="#FFFFFF" d="M188.205,379.701c-20.074,0-38.252,0-57.192,0c0-18.265,0-35.619,0-54.238c18.264,0,36.752,0,57.202,0
c0-7.092-0.303-13.01,0.062-18.885c0.825-13.281,9.067-17.641,19.724-10.07c21.383,15.193,42.57,30.668,63.625,46.315
c8.63,6.416,8.64,11.962,0.159,18.227c-21.099,15.591-42.222,31.169-63.834,46.026c-3.746,2.577-10.484,3.169-14.8,1.673
c-2.666-0.923-4.104-7.412-4.753-11.61C187.599,391.999,188.205,386.64,188.205,379.701z"/>
<path fill="#FFFFFF" d="M66.712,280.18c0-17.699,0-35.021,0-53.046c16.52,0,32.665,0,49.59,0c0,17.468,0,34.786,0,53.046
C100.411,280.18,84.281,280.18,66.712,280.18z"/>
<path fill="#FFFFFF" d="M66.869,325.488c16.63,0,32.49,0,49.278,0c0,17.692,0,35.27,0,53.543c-16.278,0-32.416,0-49.278,0
C66.869,361.364,66.869,344.044,66.869,325.488z"/>
<path fill="#E5A00D" d="M116.673,422.747c0,17.461,0,34.177,0,51.779c-16.49,0-32.652,0-49.668,0c0-16.998,0-33.944,0-51.779
C83.029,422.747,99.188,422.747,116.673,422.747z"/>
<path fill="#FFFFFF" d="M116.497,520.842c0,17.629,0,34.951,0,53.021c-16.468,0-32.608,0-49.552,0c0-17.455,0-34.777,0-53.021
C83.067,520.842,99.487,520.842,116.497,520.842z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.4 KiB

View file

@ -0,0 +1,5 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<title>artist</title>
<path fill="#fff" d="M9.201 24.681c0-6.699 0-13.358 0-20.035 7.594-1.505 15.157-3.004 22.768-4.513 0 0.172 0 0.319 0 0.465 0 7.067-0.026 14.135 0.021 21.202 0.010 1.498-0.59 2.57-1.716 3.423-1.999 1.512-4.26 2.145-6.751 1.88-0.504-0.054-1.020-0.205-1.481-0.418-1.502-0.695-1.856-2.122-0.908-3.48 0.826-1.184 1.99-1.924 3.302-2.433 1.362-0.528 2.774-0.843 4.252-0.719 0.324 0.027 0.646 0.084 0.994 0.13 0-4.345 0-8.679 0-13.050-6.062 1.204-12.099 2.404-18.16 3.608 0 0.196 0 0.345 0 0.495 0 5.174-0.006 10.349 0.004 15.523 0.003 1.409-0.802 2.302-1.854 3.056-0.889 0.637-1.859 1.114-2.906 1.426-1.524 0.453-3.067 0.627-4.619 0.169-0.952-0.281-1.789-0.736-2.010-1.83-0.136-0.673 0.098-1.269 0.459-1.822 0.772-1.183 1.947-1.853 3.193-2.388 1.662-0.714 3.394-1.043 5.207-0.698 0.048 0.009 0.099 0.005 0.206 0.010z"></path>
</svg>

After

Width:  |  Height:  |  Size: 979 B

View file

@ -0,0 +1,5 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<title>movie</title>
<path fill="#fff" d="M0 1.416c10.695 0 21.315 0 32 0 0 9.719 0 19.417 0 29.168-10.635 0-21.293 0-32 0 0-9.699 0-19.399 0-29.168zM9.202 15.129c4.535 0 9.065 0 13.609 0 0-3.798 0-7.579 0-11.355-4.563 0-9.075 0-13.609 0 0 3.801 0 7.551 0 11.355zM9.215 28.909c4.581 0 9.093 0 13.61 0 0-3.818 0-7.585 0-11.382-4.549 0-9.062 0-13.61 0 0 3.803 0 7.57 0 11.382zM6.813 5.983c0-0.753 0-1.467 0-2.209-1.138 0-2.235 0-3.33 0 0 0.766 0 1.48 0 2.209 1.131 0 2.21 0 3.33 0zM25.231 3.754c0 0.783 0 1.494 0 2.219 1.141 0 2.235 0 3.33 0 0-0.763 0-1.476 0-2.219-1.12 0-2.198 0-3.33 0zM25.233 12.938c0 0.777 0 1.492 0 2.19 1.149 0 2.248 0 3.335 0 0-0.754 0-1.452 0-2.19-1.116 0-2.197 0-3.335 0zM25.227 22.074c0 0.783 0 1.496 0 2.218 1.139 0 2.235 0 3.335 0 0-0.758 0-1.471 0-2.218-1.119 0-2.196 0-3.335 0zM3.472 26.689c0 0.768 0 1.481 0 2.221 1.133 0 2.226 0 3.34 0 0-0.763 0-1.475 0-2.221-1.119 0-2.197 0-3.34 0zM28.579 26.711c-1.112 0-2.225 0-3.353 0 0 0.749 0 1.462 0 2.202 1.137 0 2.233 0 3.353 0 0-0.748 0-1.447 0-2.202zM6.817 15.155c0-0.774 0-1.468 0-2.21-1.133 0-2.229 0-3.338 0 0 0.761 0 1.473 0 2.21 1.127 0 2.207 0 3.338 0zM3.463 19.73c1.165 0 2.242 0 3.346 0 0-0.759 0-1.469 0-2.189-1.143 0-2.239 0-3.346 0 0 0.748 0 1.446 0 2.189zM28.559 19.733c0-0.764 0-1.479 0-2.188-1.144 0-2.241 0-3.34 0 0 0.752 0 1.448 0 2.188 1.114 0 2.195 0 3.34 0zM6.791 24.304c0-0.798 0-1.511 0-2.215-1.145 0-2.225 0-3.312 0 0 0.766 0 1.481 0 2.215 1.129 0 2.211 0 3.312 0zM3.489 8.378c0 0.771 0 1.464 0 2.145 1.138 0 2.219 0 3.319 0 0-0.74 0-1.431 0-2.145-1.129 0-2.211 0-3.319 0zM28.554 10.538c0-0.775 0-1.464 0-2.137-1.146 0-2.242 0-3.319 0 0 0.747 0 1.438 0 2.137 1.131 0 2.21 0 3.319 0z"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -0,0 +1,6 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<title>photo</title>
<path fill="#fff" d="M25.208 2.266c0 1.514 0 2.976 0 4.513 2.281 0 4.505 0 6.792 0 0 7.681 0 15.287 0 22.955-10.643 0-21.3 0-32 0 0-7.626 0-15.236 0-22.913 2.258 0 4.486 0 6.792 0 0-1.546 0-3.029 0-4.555 6.152 0 12.226 0 18.415 0zM16.008 9.209c-5.026-0.004-9.12 4.079-9.123 9.099-0.004 4.961 4.099 9.069 9.074 9.085 5.022 0.016 9.124-4.047 9.159-9.070 0.035-4.985-4.087-9.109-9.109-9.114z"></path>
<path fill="#fff" d="M20.601 18.292c0.003 2.551-2.070 4.626-4.61 4.613-2.558-0.013-4.595-2.069-4.591-4.634 0.003-2.524 2.038-4.557 4.577-4.572 2.562-0.015 4.621 2.030 4.624 4.593z"></path>
</svg>

After

Width:  |  Height:  |  Size: 746 B

View file

@ -0,0 +1,12 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<title>playlist</title>
<path fill="#fff" d="M9.167 10.268c7.653 0 15.208 0 22.82 0 0 1.147 0 2.268 0 3.451-7.583 0-15.172 0-22.82 0 0-1.134 0-2.274 0-3.451z"></path>
<path fill="#fff" d="M9.14 21.73c0-1.152 0-2.257 0-3.424 7.609 0 15.212 0 22.86 0 0 1.132 0 2.252 0 3.424-7.607 0-15.191 0-22.86 0z"></path>
<path fill="#fff" d="M9.217 5.679c0-1.145 0-2.23 0-3.363 7.562 0 15.101 0 22.683 0 0 1.113 0 2.213 0 3.363-7.539 0-15.080 0-22.683 0z"></path>
<path fill="#fff" d="M9.225 29.708c0-1.132 0-2.214 0-3.336 7.558 0 15.086 0 22.668 0 0 1.086 0 2.186 0 3.336-7.526 0-15.071 0-22.668 0z"></path>
<path fill="#fff" d="M0.007 10.279c1.58 0 3.043 0 4.551 0 0 1.153 0 2.272 0 3.444-1.511 0-3.011 0-4.551 0 0-1.157 0-2.292 0-3.444z"></path>
<path fill="#fff" d="M4.527 2.292c0 1.183 0 2.284 0 3.424-1.496 0-2.957 0-4.479 0 0-1.142 0-2.276 0-3.424 1.51 0 2.971 0 4.479 0z"></path>
<path fill="#fff" d="M4.571 18.3c0 1.151 0 2.254 0 3.416-1.515 0-3.019 0-4.571 0 0-1.127 0-2.247 0-3.416 1.513 0 3.001 0 4.571 0z"></path>
<path fill="#fff" d="M4.54 26.352c0 1.137 0 2.218 0 3.354-1.494 0-2.975 0-4.506 0 0-1.094 0-2.192 0-3.354 1.489 0 2.965 0 4.506 0z"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1,6 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<title>show</title>
<path fill="#fff" d="M0 26.565c0-8.38 0-16.706 0-25.089 10.661 0 21.307 0 32 0 0 8.343 0 16.686 0 25.089-10.637 0-21.283 0-32 0zM3.514 4.937c0 5.355 0 10.634 0 15.901 8.375 0 16.691 0 24.994 0 0-5.331 0-10.612 0-15.901-8.356 0-16.656 0-24.994 0z"></path>
<path fill="#fff" d="M6.874 30.524c0-0.553 0-1.056 0-1.602 6.084 0 12.136 0 18.25 0 0 0.509 0 1.029 0 1.602-6.050 0-12.12 0-18.25 0z"></path>
</svg>

After

Width:  |  Height:  |  Size: 555 B

View file

@ -0,0 +1,5 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<title>video</title>
<path fill="#fff" d="M20.662 27.439c-6.971 0-13.797 0-20.662 0 0-7.639 0-15.235 0-22.878 6.846 0 13.672 0 20.588 0 0 2.962 0 5.932 0 9.091 3.87-2.316 7.591-4.542 11.412-6.828 0 6.153 0 12.176 0 18.33-3.769-2.257-7.494-4.488-11.338-6.789 0 3.070 0 6.035 0 9.075z"></path>
</svg>

After

Width:  |  Height:  |  Size: 430 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 879 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Some files were not shown because too many files have changed in this diff Show more