Compare commits

..

91 Commits

Author SHA1 Message Date
JonnyWong16
ec685407bb v1.4.22 2017-08-19 20:50:28 -07:00
JonnyWong16
be9a1dcf06 Temporary fix for incorrect source media info 2017-08-19 20:48:01 -07:00
JonnyWong16
1f7e8b4d9a Fix removing old config backups 2017-08-19 20:29:49 -07:00
JonnyWong16
b3da08ce74 Merge pull request #1084 from mttlmy/dev
Added platform 'Linux' to link to image linux.png
2017-08-07 16:32:12 -07:00
mttlmy
50753db4ff Added platform 'Linux' to link to image linux.png 2017-08-07 19:27:46 -04:00
JonnyWong16
af77e51307 v1.4.21 2017-07-01 16:45:44 -07:00
JonnyWong16
b4e8689e92 Fix qrcode javascript 2017-07-01 16:45:37 -07:00
JonnyWong16
8ec30a77ff Update donation methods 2017-07-01 16:35:12 -07:00
JonnyWong16
29c7853380 v1.4.20 2017-06-24 21:41:24 -07:00
JonnyWong16
cd417aaf44 Support custom port for Mattermost (Slack) notifications 2017-06-24 21:34:53 -07:00
JonnyWong16
428a5cc0ff Udate file sizes when refreshing media info 2017-06-24 21:34:22 -07:00
JonnyWong16
d128d7c8e6 Sort 4k properly in media info table 2017-06-24 21:17:26 -07:00
JonnyWong16
8027199bd5 Merge pull request #1021 from senepa/senepa-patch-1
Fixed math used to calculate human_duration
2017-06-24 21:07:35 -07:00
JonnyWong16
099a887cc7 Add PlexTogether platform image 2017-06-24 21:06:40 -07:00
JonnyWong16
ea41d06023 v1.4.19 2017-05-31 19:55:42 -07:00
JonnyWong16
5147baab05 Temporary fix for current activity resolution on PMS 1.7.x 2017-05-31 19:50:44 -07:00
o
1c50e615cf Fixing math used to calculate human_duration
There are 86400 seconds in a day
2017-04-22 19:27:31 -04:00
JonnyWong16
ed2d34e979 v1.4.18 2017-04-22 15:57:43 -07:00
JonnyWong16
c404016700 Merge pull request #1020 from senepa/dev
A correction and additional Arnold quotes
2017-04-22 15:44:26 -07:00
o
14b0353ba4 A correction and additional Arnold quotes
Correction:
True Lies (1994)
'Can you hurry up. My horse is getting tired.' to 'Make it quick because my horse is getting tired.'
http://www.imdb.com/title/tt0111503/quotes?item=qt0408883

Additions:
Last Action Hero (1993)
'Well, listen to this one: Rubber baby buggy bumpers!'
http://www.imdb.com/title/tt0107362/quotes?item=qt1196411

Kindergarten Cop (1990)
'Take your toy back to the carpet!'
http://www.imdb.com/title/tt0099938/quotes?item=qt0460013

Kindergarten Cop (1990)
'My name is John Kimble... And I love my car.'
http://www.imdb.com/title/tt0099938/quotes?item=qt0460001

Commando (1985)
'I eat Green Berets for breakfast.'
http://www.imdb.com/title/tt0088944/quotes?item=qt0402329

Jingle All the Way (1996)
'Put that cookie down. NOW!'
http://www.imdb.com/title/tt0116705/quotes?item=qt0266218
2017-04-22 18:40:21 -04:00
JonnyWong16
fbe136a350 Fix datatables footer text wrapping 2017-04-22 13:43:45 -07:00
JonnyWong16
4fcfea943e Add badges to readme 2017-04-22 13:35:22 -07:00
JonnyWong16
279d27d081 Update PayPal donation link 2017-04-22 13:32:36 -07:00
JonnyWong16
651125ef2c Merge pull request #999 from Hellowlol/patch-1
Add missing CONFIG for API get_apikey
2017-03-21 08:12:38 -07:00
Hellowlol
a5eb0e7faa Add missing CONFIG 2017-03-21 15:35:03 +01:00
JonnyWong16
e85cdd5609 v1.4.17 2017-03-04 15:55:45 -08:00
JonnyWong16
83f80b9288 Fix month capitalization 2017-03-04 15:49:36 -08:00
JonnyWong16
76fe771d8c Update init scripts 2017-03-04 15:42:03 -08:00
JonnyWong16
c6ad09fe8f Cleanup graphs PRs 2017-03-04 15:33:45 -08:00
JonnyWong16
a3014638c8 Merge pull request #953 from ampsonic/patch-1
Update init.ubuntu.systemd
2017-03-04 15:19:00 -08:00
JonnyWong16
8b15c63b0d Merge pull request #982 from Pbaboe/StartDayofWeek
Start day of the week changeable
2017-03-04 15:02:57 -08:00
JonnyWong16
589adf4df9 Merge pull request #980 from Pbaboe/MonthlyGraphs
Change amount of months in Graphs > Play Totals > Plays by Month
2017-03-04 15:00:24 -08:00
JonnyWong16
b07d85f233 Merge pull request #975 from demonbane/dev
Fix 404s for icon files and invalid paths
2017-03-04 14:53:23 -08:00
JonnyWong16
f0e5855a8e Update bitcoin donation link 2017-03-04 14:47:30 -08:00
JonnyWong16
5997aa5cd9 Fix Plex Web 3.0 urls 2017-03-04 14:47:22 -08:00
Peter Kums
cb2a38addc Start day of the week changeable
Option in general settings to change the start day of the week to Monday (instead of the default of Sunday). This is only relevant for the Graph : Play by day of week.

Feathub issue : http://feathub.com/JonnyWong16/plexpy/+15
2017-02-27 19:38:12 +01:00
Peter Kums
873f857c82 Store months for Play by Months graph
Store the selected amount of months in the Graphs > Play Totals > Plays by Months graph in the config.ini file.
2017-02-27 00:17:56 +01:00
Peter Kums
b9a22461c1 Change months in Plays by Month graph
Add an option on the Graphs > Play Totals > Plays by Month page to change the amount of months the graph shows data for.
2017-02-26 23:53:24 +01:00
Alex Malinovich
85a02771a7 Fix 404s for icon files and invalid paths 2017-02-21 01:44:12 -08:00
ampsonic
6a0b0327c3 Update init.ubuntu.systemd
Updated the instructions to more accurately reflect required steps. Removed outdated information and added information on creating the plexpy user. 

Only the comments of the file changed not actual configuration.
2017-01-28 16:42:50 -08:00
JonnyWong16
3742f33d08 v1.4.16 2016-11-25 18:47:22 -08:00
JonnyWong16
82ac33dd75 Fix websocket for new json response on PMS 1.3.0 2016-11-25 18:42:23 -08:00
JonnyWong16
8c7c0101cd Dynamically update stream and transcoder tooltip percent 2016-11-14 21:10:48 -08:00
JonnyWong16
4d6179dfdd Fix typo 2016-11-14 20:49:01 -08:00
JonnyWong16
ef85fba2e5 v1.4.15 2016-11-11 13:08:13 -08:00
JonnyWong16
960b601384 Remove error cmd from API 2016-11-11 12:59:35 -08:00
JonnyWong16
21d9091b43 Merge pull request #898 from Hellowlol/ap_fix
fix result type, fixup responses from the ui to api
2016-11-11 12:48:18 -08:00
Hellowlol
e881c32797 Update md add exception traceback to the browser 2016-11-05 22:24:37 +01:00
JonnyWong16
57f1af05f5 Redirect to proper HTTP root on state change 2016-11-05 12:13:53 -07:00
JonnyWong16
254f41a2cc Fix line breaks in info page summaries 2016-11-05 11:50:41 -07:00
JonnyWong16
59ce3404c9 Fix Plex.tv authentication with special characters 2016-11-05 11:48:53 -07:00
Hellowlol
11c7342299 fix result type, fixup responses from the ui to api
todo unify json responses from webserve.py
2016-11-04 00:52:01 +01:00
JonnyWong16
8b0959aa69 Fix API SQL command 2016-10-28 20:35:39 -07:00
JonnyWong16
9cd8ed12b9 Add percent to stream and transcoder progress tooltip 2016-10-28 20:34:54 -07:00
JonnyWong16
78b10d7ab5 Use HTTPS for app.plex.tv 2016-10-21 22:45:17 -07:00
JonnyWong16
a322ec2b23 Allow refreshing images if not using sessions 2016-10-16 13:08:36 -07:00
JonnyWong16
1f23654735 Merge pull request #870 from XusBadia/patch-2
Incremented table width to 100%
2016-10-13 21:52:49 -07:00
Xus Badia
e1112b95c7 Changed max-width to accommodate wider displays 2016-10-14 06:11:50 +02:00
JonnyWong16
4e043109bf Temporary fix for info pages with missing metadata
* Will be fixed properly in v2
2016-10-13 20:42:11 -07:00
Xus Badia
58e670443d Incremented table width to 100%
Table width (header and body/back) incremented to 100% but adding max-width so it isn't too wide in wide-screen monitors.
2016-10-13 11:56:02 +02:00
JonnyWong16
86a9230da8 v1.4.14 2016-10-12 21:06:34 -07:00
JonnyWong16
86f84766c1 Allow disable script timeout 2016-10-12 21:00:52 -07:00
JonnyWong16
fdc7078e5c API key readonly instead of disabled 2016-10-12 20:55:32 -07:00
JonnyWong16
c649ebfcc0 Fix typo under Flush Temporary Sessions 2016-10-10 22:47:41 -07:00
JonnyWong16
6aa0d4cd0b Check for metadata before attempting to write session history 2016-10-10 22:46:48 -07:00
JonnyWong16
1a2e205c1f v1.4.13 2016-10-08 23:42:18 -07:00
JonnyWong16
5dd04cb8ab Temporarily set stopped time when connection is lost 2016-10-08 22:56:43 -07:00
JonnyWong16
62d05e5e08 Add supplementary URL option to Pushover 2016-10-08 22:45:16 -07:00
JonnyWong16
1c087ec856 Add backup days option 2016-10-08 22:29:30 -07:00
JonnyWong16
010c12da67 Use human file size in table 2016-10-03 21:36:02 -07:00
JonnyWong16
9bdac38561 Merge pull request #859 from logaritmisk/human-file-size
Human file size for media info table
2016-10-03 21:30:26 -07:00
logaritmisk
790ca9c90a Human file size for media info table. 2016-10-03 22:27:21 +02:00
JonnyWong16
58f72d2d9c Merge pull request #856 from Hellowlol/imgfix
fix for image proxy for api.
2016-10-02 12:09:33 -07:00
Hellowlol
285e6513ed fix for image proxy for api. 2016-10-02 20:38:51 +02:00
JonnyWong16
412bc8cf2d Move watched percent to General settings 2016-09-30 19:38:20 -07:00
JonnyWong16
45cd8b8a00 Cleanup kill script 2016-09-30 00:35:31 -07:00
JonnyWong16
ae2227959e Fix 0b10e68 2016-09-29 23:35:33 -07:00
JonnyWong16
b50c92f919 Rename get_log 2016-09-29 23:31:33 -07:00
JonnyWong16
93a1d9c164 Run script in a new thread with timeout
* Also fixes script output not sent to logger
2016-09-29 23:31:15 -07:00
JonnyWong16
0b10e68c60 Force refresh Plex.tv token in settings
* Removes the old PlexPy device and fetches a new token
2016-09-29 21:21:07 -07:00
JonnyWong16
73ac4076ac Disable manual changing of API key 2016-09-29 18:51:35 -07:00
JonnyWong16
5968b82a0b Add pms_image_proxy to api 2016-09-27 22:20:10 -07:00
JonnyWong16
ce1d2a0fd9 Fix success message show incorrectly when sending test notification
* Due to saving the settings before sending the notification
2016-09-27 21:48:09 -07:00
JonnyWong16
de3f813b46 Add flush temporary sessions button in settings 2016-09-27 21:47:12 -07:00
JonnyWong16
4797b1a3b7 Add new librarys to homepage automatically 2016-09-26 22:20:44 -07:00
JonnyWong16
3e996d284d Disable posters by default for all notification agents 2016-09-25 22:05:02 -07:00
JonnyWong16
420c5a0836 Fix Browser config section 2016-09-25 22:04:25 -07:00
JonnyWong16
c6b953055a Fix admin username not shown in login logs 2016-09-24 19:37:09 -07:00
JonnyWong16
1cd0c112a6 Fix PlexPy log level filter 2016-09-24 19:29:26 -07:00
JonnyWong16
492d28ea37 Update pytz library 2016-09-19 21:48:14 -07:00
JonnyWong16
4eb7e03b67 Update Readme 2016-09-19 19:08:26 -07:00
188 changed files with 1243 additions and 802 deletions

26
API.md
View File

@@ -142,6 +142,10 @@ Returns:
``` ```
### delete_temp_sessions
Flush out all of the temporary sessions in the database.
### delete_user ### delete_user
Delete a user from PlexPy. Also erases all history for the user. Delete a user from PlexPy. Also erases all history for the user.
@@ -772,7 +776,7 @@ Get the metadata for a media item.
``` ```
Required parameters: Required parameters:
rating_key (str): Rating key of the item rating_key (str): Rating key of the item
media_info (bool): True or False wheter to get media info media_info (bool): True or False whether to get media info
Optional parameters: Optional parameters:
None None
@@ -1793,6 +1797,26 @@ Returns:
``` ```
### pms_image_proxy
Gets an image from the PMS and saves it to the image cache directory.
```
Required parameters:
img (str): /library/metadata/153037/thumb/1462175060
or
rating_key (str): 54321
Optional parameters:
width (str): 150
height (str): 255
fallback (str): "poster", "cover", "art"
refresh (bool): True or False whether to refresh the image cache
Returns:
None
```
### refresh_libraries_list ### refresh_libraries_list
Refresh the PlexPy libraries list. Refresh the PlexPy libraries list.

View File

@@ -1,5 +1,98 @@
# Changelog # Changelog
## v1.4.22 (2017-08-19)
* Fix: Cleaning up of old config backups.
* Fix: Temporary fix for incorrect source media info.
## v1.4.21 (2017-07-01)
* New: Updated donation methods.
## v1.4.20 (2017-06-24)
* New: Added platform image for the PlexTogether player.
* Fix: Corrected math used to calculate human duration. (Thanks @senepa)
* Fix: Sorting of 4k in media info tables.
* Fix: Update file sizes when refreshing media info tables.
* Fix: Support a custom port for Mattermost (Slack) notifications.
## v1.4.19 (2017-05-31)
* Fix: Video resolution not showing up for transcoded streams on PMS 1.7.x.
## v1.4.18 (2017-04-22)
* New: Added some new Arnold quotes. (Thanks @senepa)
* Fix: Text wrapping in datatable footers.
* Fix: API command get_apikey. (Thanks @Hellowlol)
## v1.4.17 (2017-03-04)
* New: Configurable month range for the Plays by month graph. (Thanks @Pbaboe)
* New: Option to chanage the week to start on Monday for the the Plays by day of week graph. (Thanks @Pbaboe)
* Fix: Invalid iOS icon file paths. (Thanks @demonbane)
* Fix: Plex Web 3.0 URLs on info pages and notifications.
* Fix: Update bitcoin donation link to Coinbase.
* Fix: Update init scripts. (Thanks @ampsonic)
## v1.4.16 (2016-11-25)
* Fix: Websocket for new json response on PMS 1.3.0.
* Fix: Update stream and transcoder tooltip percent.
* Fix: Typo in the edit user modal.
## v1.4.15 (2016-11-11)
* New: Add stream and transcoder progress percent to the current activity tooltip.
* Fix: Refreshing of images in the cache when authentication is disabled.
* Fix: Plex.tv authentication with special characters in the username or password.
* Fix: Line breaks in the info page summaries.
* Fix: Redirect to the proper http root when restarting.
* Fix: API result type and responses showing incorrectly. (Thanks @Hellowlol)
* Change: Use https URL for app.plex.tv.
* Change: Show API traceback errors in the browser with debugging enabled. (Thanks @Hellowlol)
* Change: Increase table width on mobile devices and max width set to 1750px. (Thanks @XusBadia)
## v1.4.14 (2016-10-12)
* Fix: History logging locking up if media is removed from Plex before PlexPy can save the session.
* Fix: Unable to save API key in the settings.
* Fix: Some typos in the settings. (Thanks @Leafar3456)
* Change: Disable script timeout by setting timeout to 0 seconds.
## v1.4.13 (2016-10-08)
* New: Option to set the number of days to keep PlexPy backups.
* New: Option to add a supplementary url to Pushover notifications.
* New: Option to set a timeout duration for script notifications.
* New: Added flush temporary sessions button to extra settings for emergency use.
* New: Added pms_image_proxy to the API.
* Fix: Insanely long play durations being recorded when connection to the Plex server is lost.
* Fix: Script notification output not being sent to the logger.
* Fix: New libraries not being added to homepage automatically.
* Fix: Success message shown incorrectly when sending a test notification.
* Fix: PlexPy log level filter not working.
* Fix: Admin username not shown in login logs.
* Fix: FeatHub link in readme document.
* Change: Posters disabled by default for all notification agents.
* Change: Disable manual changing of the PlexPy API key.
* Change: Force refresh the Plex.tv token when fetching a new token.
* Change: Script notifications run in a new thread with the timeout setting.
* Change: Watched percent moved to general settings.
* Change: Use human readable file sizes to the media info tables. (Thanks @logaritmisk)
* Change: Update pytz library.
## v1.4.12 (2016-09-18) ## v1.4.12 (2016-09-18)
* Fix: PMS update check not working for MacOSX. * Fix: PMS update check not working for MacOSX.

View File

@@ -35,7 +35,7 @@ In case you read this because you are posting an issue, please take a minute and
## Feature Requests ## Feature Requests
Feature requests are handled on [FeatHub](http://feathub.com/drzoidberg33/plexpy). Feature requests are handled on [FeatHub](http://feathub.com/JonnyWong16/plexpy).
1. Search the existing requests to see if your suggestion has already been submitted. 1. Search the existing requests to see if your suggestion has already been submitted.
2. If a similar request exists, give it a thumbs up (+1), or add additional comments to the request. 2. If a similar request exists, give it a thumbs up (+1), or add additional comments to the request.

View File

@@ -8,7 +8,7 @@ Reporting Issues:
Please use [Gist](http://gist.github.com) or [Pastebin](http://pastebin.com/). Please use [Gist](http://gist.github.com) or [Pastebin](http://pastebin.com/).
Feature Requests: Feature Requests:
* Feature requests are handled on FeatHub: http://feathub.com/drzoidberg33/plexpy * Feature requests are handled on FeatHub: http://feathub.com/JonnyWong16/plexpy
* Do not post them on the GitHub issues tracker. * Do not post them on the GitHub issues tracker.
--> -->

View File

@@ -1,15 +1,13 @@
# PlexPy # PlexPy
[![Join the chat at https://gitter.im/plexpy/general](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/plexpy/general?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![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)
A python based web application for monitoring, analytics and notifications for [Plex Media Server](https://plex.tv). A python based web application for monitoring, analytics and notifications for [Plex Media Server](https://plex.tv).
This project is based on code from [Headphones](https://github.com/rembo10/headphones) and [PlexWatchWeb](https://github.com/ecleese/plexWatchWeb). This project is based on code from [Headphones](https://github.com/rembo10/headphones) and [PlexWatchWeb](https://github.com/ecleese/plexWatchWeb).
* [Plex forum thread](https://forums.plex.tv/discussion/169591/plexpy-another-plex-monitoring-program)
* [Gitter chat](https://gitter.im/plexpy/general)
* [/r/Plex Discord server](https://discord.gg/011TFFWSuNFI02EKr) | [PlexPy Discord server](https://discord.gg/36ggawe)
## Features ## Features
* Responsive web design viewable on desktop, tablet and mobile web browsers. * Responsive web design viewable on desktop, tablet and mobile web browsers.
@@ -74,7 +72,7 @@ This project is based on code from [Headphones](https://github.com/rembo10/headp
## Feature Requests ## Feature Requests
Feature requests are handled on [FeatHub](http://feathub.com/drzoidberg33/plexpy). Feature requests are handled on [FeatHub](http://feathub.com/JonnyWong16/plexpy).
1. Search the existing requests to see if your suggestion has already been submitted. 1. Search the existing requests to see if your suggestion has already been submitted.
2. If a similar request exists, give it a thumbs up (+1), or add additional comments to the request. 2. If a similar request exists, give it a thumbs up (+1), or add additional comments to the request.

View File

@@ -66,67 +66,67 @@
<!-- STARTUP IMAGES --> <!-- STARTUP IMAGES -->
<!-- iPad retina portrait startup image --> <!-- iPad retina portrait startup image -->
<link href="${http_root}images/res/screen/ios/Default-Portrait@2x~ipad.png" <link href="${http_root}images/res/ios/Default-Portrait@2x~ipad.png"
media="(device-width: 768px) and (device-height: 1024px) media="(device-width: 768px) and (device-height: 1024px)
and (-webkit-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 2)
and (orientation: portrait)" and (orientation: portrait)"
rel="apple-touch-startup-image"> rel="apple-touch-startup-image">
<!-- iPad retina landscape startup image --> <!-- iPad retina landscape startup image -->
<link href="${http_root}images/res/screen/ios/Default-Landscape@2x~ipad.png" <link href="${http_root}images/res/ios/Default-Landscape@2x~ipad.png"
media="(device-width: 768px) and (device-height: 1024px) media="(device-width: 768px) and (device-height: 1024px)
and (-webkit-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 2)
and (orientation: landscape)" and (orientation: landscape)"
rel="apple-touch-startup-image"> rel="apple-touch-startup-image">
<!-- iPad non-retina portrait startup image --> <!-- iPad non-retina portrait startup image -->
<link href="${http_root}images/res/screen/ios/Default-Portrait~ipad.png" <link href="${http_root}images/res/ios/Default-Portrait~ipad.png"
media="(device-width: 768px) and (device-height: 1024px) media="(device-width: 768px) and (device-height: 1024px)
and (-webkit-device-pixel-ratio: 1) and (-webkit-device-pixel-ratio: 1)
and (orientation: portrait)" and (orientation: portrait)"
rel="apple-touch-startup-image"> rel="apple-touch-startup-image">
<!-- iPad non-retina landscape startup image --> <!-- iPad non-retina landscape startup image -->
<link href="${http_root}images/res/screen/ios/Default-Landscape~ipad.png" <link href="${http_root}images/res/ios/Default-Landscape~ipad.png"
media="(device-width: 768px) and (device-height: 1024px) media="(device-width: 768px) and (device-height: 1024px)
and (-webkit-device-pixel-ratio: 1) and (-webkit-device-pixel-ratio: 1)
and (orientation: landscape)" and (orientation: landscape)"
rel="apple-touch-startup-image"> rel="apple-touch-startup-image">
<!-- iPhone 6 Plus portrait startup image --> <!-- iPhone 6 Plus portrait startup image -->
<link href="${http_root}images/res/screen/ios/Default-736h.png" <link href="${http_root}images/res/ios/Default-736h.png"
media="(device-width: 414px) and (device-height: 736px) media="(device-width: 414px) and (device-height: 736px)
and (-webkit-device-pixel-ratio: 3) and (-webkit-device-pixel-ratio: 3)
and (orientation: portrait)" and (orientation: portrait)"
rel="apple-touch-startup-image"> rel="apple-touch-startup-image">
<!-- iPhone 6 Plus landscape startup image --> <!-- iPhone 6 Plus landscape startup image -->
<link href="${http_root}images/res/screen/ios/Default-Landscape-736h.png" <link href="${http_root}images/res/ios/Default-Landscape-736h.png"
media="(device-width: 414px) and (device-height: 736px) media="(device-width: 414px) and (device-height: 736px)
and (-webkit-device-pixel-ratio: 3) and (-webkit-device-pixel-ratio: 3)
and (orientation: landscape)" and (orientation: landscape)"
rel="apple-touch-startup-image"> rel="apple-touch-startup-image">
<!-- iPhone 6 startup image --> <!-- iPhone 6 startup image -->
<link href="${http_root}images/res/screen/ios/Default-667h.png" <link href="${http_root}images/res/ios/Default-667h.png"
media="(device-width: 375px) and (device-height: 667px) media="(device-width: 375px) and (device-height: 667px)
and (-webkit-device-pixel-ratio: 2)" and (-webkit-device-pixel-ratio: 2)"
rel="apple-touch-startup-image"> rel="apple-touch-startup-image">
<!-- iPhone 5 startup image --> <!-- iPhone 5 startup image -->
<link href="${http_root}images/res/screen/ios/Default-568h@2x~iphone5.jpg" <link href="${http_root}images/res/ios/Default-568h@2x~iphone5.jpg"
media="(device-width: 320px) and (device-height: 568px) media="(device-width: 320px) and (device-height: 568px)
and (-webkit-device-pixel-ratio: 2)" and (-webkit-device-pixel-ratio: 2)"
rel="apple-touch-startup-image"> rel="apple-touch-startup-image">
<!-- iPhone < 5 retina startup image --> <!-- iPhone < 5 retina startup image -->
<link href="${http_root}images/res/screen/ios/Default@2x~iphone.png" <link href="${http_root}images/res/ios/Default@2x~iphone.png"
media="(device-width: 320px) and (device-height: 480px) media="(device-width: 320px) and (device-height: 480px)
and (-webkit-device-pixel-ratio: 2)" and (-webkit-device-pixel-ratio: 2)"
rel="apple-touch-startup-image"> rel="apple-touch-startup-image">
<!-- iPhone < 5 non-retina startup image --> <!-- iPhone < 5 non-retina startup image -->
<link href="${http_root}images/res/screen/ios/Default~iphone.png" <link href="${http_root}images/res/ios/Default~iphone.png"
media="(device-width: 320px) and (device-height: 480px) media="(device-width: 320px) and (device-height: 480px)
and (-webkit-device-pixel-ratio: 1)" and (-webkit-device-pixel-ratio: 1)"
rel="apple-touch-startup-image"> rel="apple-touch-startup-image">
@@ -223,8 +223,7 @@
<li><a href="${anon_url('https://github.com/%s/plexpy/wiki/Frequently-Asked-Questions-(FAQ)' % plexpy.CONFIG.GIT_USER)}" target="_blank"><i class="fa fa-fw fa-question-circle"></i> FAQ</a></li> <li><a href="${anon_url('https://github.com/%s/plexpy/wiki/Frequently-Asked-Questions-(FAQ)' % plexpy.CONFIG.GIT_USER)}" target="_blank"><i class="fa fa-fw fa-question-circle"></i> FAQ</a></li>
<li><a href="settings?support=true"><i class="fa fa-fw fa-comment"></i> Support</a></li> <li><a href="settings?support=true"><i class="fa fa-fw fa-comment"></i> Support</a></li>
<li role="separator" class="divider"></li> <li role="separator" class="divider"></li>
<li><a href="${anon_url('https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=DG783BMSCU3V4')}" target="_blank"><i class="fa fa-fw fa-paypal"></i> Paypal</a></li> <li><a href="#" data-target="#donate-modal" data-toggle="modal"><i class="fa fa-fw fa-heart"></i> Donate</a></li>
<li><a href="${anon_url('http://swiftpanda16.tip.me/')}" target="_blank"><i class="fa fa-fw fa-btc"></i> Bitcoin</a></li>
<li role="separator" class="divider"></li> <li role="separator" class="divider"></li>
% if plexpy.CONFIG.CHECK_GITHUB: % if plexpy.CONFIG.CHECK_GITHUB:
<li><a href="#" id="nav-update"><i class="fa fa-fw fa-arrow-circle-up"></i> Check for Updates</a></li> <li><a href="#" id="nav-update"><i class="fa fa-fw fa-arrow-circle-up"></i> Check for Updates</a></li>
@@ -305,6 +304,63 @@
</div> </div>
</div> </div>
</div> </div>
% else:
<div id="donate-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="crypto-donate-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">PlexPy 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>
</h4>
<p>
Please select a donation method.
</p>
</div>
</div>
<ul id="donation_type" class="nav nav-pills" role="tablist" style="display: flex; justify-content: center; margin: 10px 0;">
<li class="active"><a href="#paypal-donation" role="tab" data-toggle="tab">PayPal</a></li>
<li><a href="#flattr-donation" role="tab" data-toggle="tab">Flattr</a></li>
<li><a href="#crypto-donation" role="tab" data-toggle="tab" class="crypto-donation" data-coin="Bitcoin" data-address="15YMw9c8uH7QecVPXSSjdZ7s55vnbmdKLq">Bitcoin</a></li>
<li><a href="#crypto-donation" role="tab" data-toggle="tab" class="crypto-donation" data-coin="Ethereum" data-address="0x8512f6545858eB6269A0F39349F1cC86Df6Ffaf6">Ethereum</a></li>
<li><a href="#crypto-donation" role="tab" data-toggle="tab" class="crypto-donation" data-coin="Litecoin" data-address="LZWsygTHuL4KNbg2H5EzXBDdJKNtZoqq2R">Litecoin</a></li>
</ul>
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="paypal-donation" style="text-align: center">
<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">
<img src="images/gold-rect-paypal-34px.png" alt="PayPal">
</a>
</div>
<div role="tabpanel" class="tab-pane" id="flattr-donation" style="text-align: center">
<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">
<img src="images/flattr-badge-large.png" alt="Flattr">
</a>
</div>
<div role="tabpanel" class="tab-pane" id="crypto-donation">
<label>QR Code</label>
<pre id="crypto_qr_code" style="text-align: center"></pre>
<label><span id="crypto_type_label"></span> Address</label>
<pre id="crypto_address" style="text-align: center"></pre>
</div>
</div>
</div>
<div class="modal-footer">
<input type="button" class="btn btn-bright" data-dismiss="modal" value="Close">
</div>
</div>
</div>
</div>
% endif % endif
${next.headerIncludes()} ${next.headerIncludes()}
@@ -317,8 +373,10 @@ ${next.headerIncludes()}
<script src="${http_root}js/bootstrap-hover-dropdown.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/pnotify.custom.min.js"></script>
<script src="${http_root}js/script.js"></script> <script src="${http_root}js/script.js"></script>
<script src="${http_root}js/jquery.qrcode.min.js"></script>
% if _session['user_group'] == 'admin' and plexpy.CONFIG.BROWSER_ENABLED: % if _session['user_group'] == 'admin' and plexpy.CONFIG.BROWSER_ENABLED:
<script src="${http_root}js/ajaxNotifications.js"></script> <script src="${http_root}js/ajaxNotifications.js"></script>
% else:
% endif % endif
<script> <script>
% if _session['user_group'] == 'admin': % if _session['user_group'] == 'admin':
@@ -354,6 +412,16 @@ ${next.headerIncludes()}
$(this).html('<i class="fa fa-spin fa-refresh"></i> Checking'); $(this).html('<i class="fa fa-spin fa-refresh"></i> Checking');
window.location.href = "checkGithub"; window.location.href = "checkGithub";
}); });
$('#donation_type a.crypto-donation').on('shown.bs.tab', function () {
var crypto_type = $(this).data('coin');
var crypto_address = $(this).data('address')
$('#crypto_qr_code').empty().qrcode({
text: crypto_type + ":" + crypto_address
});
$('#crypto_type_label').html(crypto_type);
$('#crypto_address').html(crypto_address);
});
% endif % endif
$('.dropdown-toggle').click(function (e) { $('.dropdown-toggle').click(function (e) {

View File

@@ -2251,7 +2251,8 @@ a .home-platforms-list-cover-face:hover
margin-right: auto; margin-right: auto;
margin-top: 20px; margin-top: 20px;
margin-bottom: 20px; margin-bottom: 20px;
width: 90%; width: 100%;
max-width: 1750px;
overflow: hidden; overflow: hidden;
} }
.table-card-header { .table-card-header {
@@ -2263,7 +2264,8 @@ a .home-platforms-list-cover-face:hover
margin-right: auto; margin-right: auto;
margin-top: 20px; margin-top: 20px;
margin-bottom: -20px; margin-bottom: -20px;
width: 90%; width: 100%;
max-width: 1750px;
overflow: hidden; overflow: hidden;
} }
.table-card-back td { .table-card-back td {
@@ -2489,6 +2491,9 @@ a .home-platforms-list-cover-face:hover
.dataTables_paginate li { .dataTables_paginate li {
margin: 0; margin: 0;
} }
div.dataTables_info {
white-space: normal !important;
}
.tooltip.top .tooltip-arrow { .tooltip.top .tooltip-arrow {
border-top-color: #fff; border-top-color: #fff;
} }
@@ -2831,6 +2836,14 @@ div[id^='media_info_child'] div[id^='media_info_child'] div.dataTables_scrollHea
width: 75px; width: 75px;
height: 34px; height: 34px;
} }
#months-selection label {
margin-bottom: 0;
}
#graph-months {
margin: 0;
width: 75px;
height: 34px;
}
.card-sortable { .card-sortable {
height: 36px; height: 36px;
padding: 0 20px 0 0; padding: 0 20px 0 0;
@@ -3055,3 +3068,13 @@ a:hover .overlay-refresh-image:hover {
padding-top: 10px; padding-top: 10px;
padding-bottom: 10px; padding-bottom: 10px;
} }
#plexpy-log-levels label,
#plex-log-levels label {
margin-bottom: 0;
}
#api_key.form-control[readonly] {
background-color: #555;
}
#api_key.form-control[readonly]:focus {
background-color: #fff;
}

View File

@@ -207,8 +207,8 @@ DOCUMENTATION :: END
% endif % endif
<div class="dashboard-activity-progress"> <div class="dashboard-activity-progress">
<div class="dashboard-activity-progress-bar"> <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']}%</div> <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']}%</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> </div>
</div> </div>
</div> </div>

View File

@@ -45,7 +45,7 @@ DOCUMENTATION :: END
<input type="text" class="form-control" id="friendly_name" name="friendly_name" value="${data['friendly_name']}" size="30"> <input type="text" class="form-control" id="friendly_name" name="friendly_name" value="${data['friendly_name']}" size="30">
</div> </div>
</div> </div>
<p class="help-block">Replace all occurances of the username with this name.</p> <p class="help-block">Replace all occurrences of the username with this name.</p>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="profile_url">Profile Picture URL</label> <label for="profile_url">Profile Picture URL</label>

View File

@@ -42,6 +42,11 @@
<input type="number" name="graph-days" id="graph-days" value="${config['graph_days']}" min="1" /> days <input type="number" name="graph-days" id="graph-days" value="${config['graph_days']}" min="1" /> days
</label> </label>
</div> </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>
</div> </div>
</div> </div>
<div class='table-card-back'> <div class='table-card-back'>
@@ -226,7 +231,7 @@
% endif % endif
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<h4><i class="fa fa-calendar"></i> Plays by Month <small>Last 12 months</small></h4> <h4><i class="fa fa-calendar"></i> Plays by month <small>Last <span class="months">12</span> months</small></h4>
<p class="help-block"> <p class="help-block">
The combined total of tv, movies, and music by month. The combined total of tv, movies, and music by month.
</p> </p>
@@ -317,10 +322,12 @@
// Initial values for graph from config // Initial values for graph from config
var yaxis = "${config['graph_type']}"; var yaxis = "${config['graph_type']}";
var current_range = ${config['graph_days']}; var current_day_range = ${config['graph_days']};
var current_month_range = ${config['graph_months']};
var current_tab = "${'#' + config['graph_tab']}"; var current_tab = "${'#' + config['graph_tab']}";
$('.days').html(current_range); $('.days').html(current_day_range);
$('.months').html(current_month_range);
// Load user ids and names (for the selector) // Load user ids and names (for the selector)
$.ajax({ $.ajax({
@@ -352,6 +359,7 @@
function loadGraphsTab1(time_range, yaxis) { function loadGraphsTab1(time_range, yaxis) {
$('#days-selection').show(); $('#days-selection').show();
$('#months-selection').hide();
setGraphFormat(yaxis); setGraphFormat(yaxis);
@@ -442,6 +450,7 @@
function loadGraphsTab2(time_range, yaxis) { function loadGraphsTab2(time_range, yaxis) {
$('#days-selection').show(); $('#days-selection').show();
$('#months-selection').hide();
setGraphFormat(yaxis); setGraphFormat(yaxis);
@@ -525,15 +534,16 @@
}); });
} }
function loadGraphsTab3(yaxis) { function loadGraphsTab3(time_range, yaxis) {
$('#days-selection').hide(); $('#days-selection').hide();
$('#months-selection').show();
setGraphFormat(yaxis); setGraphFormat(yaxis);
$.ajax({ $.ajax({
url: "get_plays_per_month", url: "get_plays_per_month",
type: 'get', type: 'get',
data: { y_axis: yaxis, user_id: selected_user_id }, data: { time_range: time_range, y_axis: yaxis, user_id: selected_user_id },
dataType: "json", dataType: "json",
success: function(data) { success: function(data) {
if (yaxis === 'duration') { dataSecondsToHours(data); } if (yaxis === 'duration') { dataSecondsToHours(data); }
@@ -547,15 +557,15 @@
} }
// Set initial state // Set initial state
if (current_tab == '#tabs-1') { loadGraphsTab1(current_range, yaxis); } if (current_tab == '#tabs-1') { loadGraphsTab1(current_day_range, yaxis); }
if (current_tab == '#tabs-2') { loadGraphsTab2(current_range, yaxis); } if (current_tab == '#tabs-2') { loadGraphsTab2(current_day_range, yaxis); }
if (current_tab == '#tabs-3') { loadGraphsTab3(yaxis); } if (current_tab == '#tabs-3') { loadGraphsTab3(current_month_range, yaxis); }
// Tab1 opened // Tab1 opened
$('#graph-tabs a[href="#tabs-1"]').on('shown.bs.tab', function (e) { $('#graph-tabs a[href="#tabs-1"]').on('shown.bs.tab', function (e) {
e.preventDefault(); e.preventDefault();
current_tab = $(this).attr('href'); current_tab = $(this).attr('href');
loadGraphsTab1(current_range, yaxis); loadGraphsTab1(current_day_range, yaxis);
$.ajax({ $.ajax({
url: 'set_graph_config', url: 'set_graph_config',
data: { graph_tab: current_tab.replace('#','') }, data: { graph_tab: current_tab.replace('#','') },
@@ -567,7 +577,7 @@
$('#graph-tabs a[href="#tabs-2"]').on('shown.bs.tab', function (e) { $('#graph-tabs a[href="#tabs-2"]').on('shown.bs.tab', function (e) {
e.preventDefault(); e.preventDefault();
current_tab = $(this).attr('href'); current_tab = $(this).attr('href');
loadGraphsTab2(current_range, yaxis); loadGraphsTab2(current_day_range, yaxis);
$.ajax({ $.ajax({
url: 'set_graph_config', url: 'set_graph_config',
data: { graph_tab: current_tab.replace('#','') }, data: { graph_tab: current_tab.replace('#','') },
@@ -579,7 +589,7 @@
$('#graph-tabs a[href="#tabs-3"]').on('shown.bs.tab', function (e) { $('#graph-tabs a[href="#tabs-3"]').on('shown.bs.tab', function (e) {
e.preventDefault(); e.preventDefault();
current_tab = $(this).attr('href'); current_tab = $(this).attr('href');
loadGraphsTab3(yaxis); loadGraphsTab3(current_month_range, yaxis);
$.ajax({ $.ajax({
url: 'set_graph_config', url: 'set_graph_config',
data: { graph_tab: current_tab.replace('#','') }, data: { graph_tab: current_tab.replace('#','') },
@@ -589,17 +599,35 @@
// Date range changed // Date range changed
$('#graph-days').on('change', function() { $('#graph-days').on('change', function() {
current_range = $(this).val(); current_day_range = Math.round($(this).val());
if (current_range < 1) { $(this).val(current_day_range);
if (current_day_range < 1) {
$(this).val(7); $(this).val(7);
current_range = 7; current_day_range = 7;
} }
if (current_tab == '#tabs-1') { loadGraphsTab1(current_range, yaxis); } if (current_tab == '#tabs-1') { loadGraphsTab1(current_day_range, yaxis); }
if (current_tab == '#tabs-2') { loadGraphsTab2(current_range, yaxis); } if (current_tab == '#tabs-2') { loadGraphsTab2(current_day_range, yaxis); }
$('.days').html(current_range); $('.days').html(current_day_range);
$.ajax({ $.ajax({
url: 'set_graph_config', url: 'set_graph_config',
data: { graph_days: current_range}, data: { graph_days: current_day_range},
async: true
});
});
// Month range changed
$('#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;
}
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 async: true
}); });
}); });
@@ -607,17 +635,17 @@
// User changed // User changed
$('#graph-user').on('change', function() { $('#graph-user').on('change', function() {
selected_user_id = $(this).val() || null; selected_user_id = $(this).val() || null;
if (current_tab == '#tabs-1') { loadGraphsTab1(current_range, yaxis); } if (current_tab == '#tabs-1') { loadGraphsTab1(current_day_range, yaxis); }
if (current_tab == '#tabs-2') { loadGraphsTab2(current_range, yaxis); } if (current_tab == '#tabs-2') { loadGraphsTab2(current_day_range, yaxis); }
if (current_tab == '#tabs-3') { loadGraphsTab3(yaxis); } if (current_tab == '#tabs-3') { loadGraphsTab3(current_month_range, yaxis); }
}); });
// Y-axis changed // Y-axis changed
$('#yaxis-selection').on('change', function() { $('#yaxis-selection').on('change', function() {
yaxis = $('input[name=yaxis-options]:checked', '#yaxis-selection').val(); yaxis = $('input[name=yaxis-options]:checked', '#yaxis-selection').val();
if (current_tab == '#tabs-1') { loadGraphsTab1(current_range, yaxis); } if (current_tab == '#tabs-1') { loadGraphsTab1(current_day_range, yaxis); }
if (current_tab == '#tabs-2') { loadGraphsTab2(current_range, yaxis); } if (current_tab == '#tabs-2') { loadGraphsTab2(current_day_range, yaxis); }
if (current_tab == '#tabs-3') { loadGraphsTab3(yaxis); } if (current_tab == '#tabs-3') { loadGraphsTab3(current_month_range, yaxis); }
$.ajax({ $.ajax({
url: 'set_graph_config', url: 'set_graph_config',
data: { graph_type: yaxis}, data: { graph_type: yaxis},

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 KiB

View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@@ -204,8 +204,11 @@
// update the progress bars // update the progress bars
// percent - 3 because of 3px padding-right // percent - 3 because of 3px padding-right
$('#bufferbar-' + key).width(parseInt(s.transcode_progress) - 3 + '%').html(s.transcode_progress + '%'); $('#bufferbar-' + key).width(parseInt(s.transcode_progress) - 3 + '%').html(s.transcode_progress + '%')
$('#bar-' + key).width(parseInt(s.progress_percent) - 3 + '%').html(s.progress_percent + '%'); .attr('data-original-title', 'Transcoder Progress ' + s.transcode_progress + '%');
$('#bar-' + key).width(parseInt(s.progress_percent) - 3 + '%').html(s.progress_percent + '%')
.attr('data-original-title', 'Stream Progress ' + s.progress_percent + '%');
// add temporary class so we know which instances are still active // add temporary class so we know which instances are still active
instance.addClass('updated-temp'); instance.addClass('updated-temp');

View File

@@ -53,6 +53,9 @@ DOCUMENTATION :: END
if re.match(pattern, codec): if re.match(pattern, codec):
return file return file
return codec return codec
def br(text):
return text.replace('\n', '<br /><br />')
%> %>
<%inherit file="base.html"/> <%inherit file="base.html"/>
@@ -112,9 +115,9 @@ DOCUMENTATION :: END
<div class="col-md-9"> <div class="col-md-9">
<div class="summary-content-poster hidden-xs hidden-sm"> <div class="summary-content-poster hidden-xs hidden-sm">
% if data['media_type'] == 'track': % if data['media_type'] == 'track':
<a href="http://app.plex.tv/web/app#!/server/${config['pms_identifier']}/details/%2Flibrary%2Fmetadata%2F${data['parent_rating_key']}" target="_blank" title="View in Plex Web"> <a href="https://app.plex.tv/web/app#!/server/${config['pms_identifier']}/details?key=%2Flibrary%2Fmetadata%2F${data['parent_rating_key']}" target="_blank" title="View in Plex Web">
% else: % else:
<a href="http://app.plex.tv/web/app#!/server/${config['pms_identifier']}/details/%2Flibrary%2Fmetadata%2F${data['rating_key']}" target="_blank" title="View in Plex Web"> <a href="https://app.plex.tv/web/app#!/server/${config['pms_identifier']}/details?key=%2Flibrary%2Fmetadata%2F${data['rating_key']}" target="_blank" title="View in Plex Web">
% endif % endif
% if data['media_type'] == 'episode': % if data['media_type'] == 'episode':
<div class="summary-poster-face-episode" style="background-image: url(pms_image_proxy?img=${data['thumb']}&width=500&height=280&fallback=art);"> <div class="summary-poster-face-episode" style="background-image: url(pms_image_proxy?img=${data['thumb']}&width=500&height=280&fallback=art);">
@@ -250,7 +253,7 @@ DOCUMENTATION :: END
</div> </div>
% endif % endif
<div class="summary-content-summary"> <div class="summary-content-summary">
<p> ${data['summary']} </p> <p> ${data['summary'] | br, n} </p>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -0,0 +1,28 @@
(function(r){r.fn.qrcode=function(h){var s;function u(a){this.mode=s;this.data=a}function o(a,c){this.typeNumber=a;this.errorCorrectLevel=c;this.modules=null;this.moduleCount=0;this.dataCache=null;this.dataList=[]}function q(a,c){if(void 0==a.length)throw Error(a.length+"/"+c);for(var d=0;d<a.length&&0==a[d];)d++;this.num=Array(a.length-d+c);for(var b=0;b<a.length-d;b++)this.num[b]=a[b+d]}function p(a,c){this.totalCount=a;this.dataCount=c}function t(){this.buffer=[];this.length=0}u.prototype={getLength:function(){return this.data.length},
write:function(a){for(var c=0;c<this.data.length;c++)a.put(this.data.charCodeAt(c),8)}};o.prototype={addData:function(a){this.dataList.push(new u(a));this.dataCache=null},isDark:function(a,c){if(0>a||this.moduleCount<=a||0>c||this.moduleCount<=c)throw Error(a+","+c);return this.modules[a][c]},getModuleCount:function(){return this.moduleCount},make:function(){if(1>this.typeNumber){for(var a=1,a=1;40>a;a++){for(var c=p.getRSBlocks(a,this.errorCorrectLevel),d=new t,b=0,e=0;e<c.length;e++)b+=c[e].dataCount;
for(e=0;e<this.dataList.length;e++)c=this.dataList[e],d.put(c.mode,4),d.put(c.getLength(),j.getLengthInBits(c.mode,a)),c.write(d);if(d.getLengthInBits()<=8*b)break}this.typeNumber=a}this.makeImpl(!1,this.getBestMaskPattern())},makeImpl:function(a,c){this.moduleCount=4*this.typeNumber+17;this.modules=Array(this.moduleCount);for(var d=0;d<this.moduleCount;d++){this.modules[d]=Array(this.moduleCount);for(var b=0;b<this.moduleCount;b++)this.modules[d][b]=null}this.setupPositionProbePattern(0,0);this.setupPositionProbePattern(this.moduleCount-
7,0);this.setupPositionProbePattern(0,this.moduleCount-7);this.setupPositionAdjustPattern();this.setupTimingPattern();this.setupTypeInfo(a,c);7<=this.typeNumber&&this.setupTypeNumber(a);null==this.dataCache&&(this.dataCache=o.createData(this.typeNumber,this.errorCorrectLevel,this.dataList));this.mapData(this.dataCache,c)},setupPositionProbePattern:function(a,c){for(var d=-1;7>=d;d++)if(!(-1>=a+d||this.moduleCount<=a+d))for(var b=-1;7>=b;b++)-1>=c+b||this.moduleCount<=c+b||(this.modules[a+d][c+b]=
0<=d&&6>=d&&(0==b||6==b)||0<=b&&6>=b&&(0==d||6==d)||2<=d&&4>=d&&2<=b&&4>=b?!0:!1)},getBestMaskPattern:function(){for(var a=0,c=0,d=0;8>d;d++){this.makeImpl(!0,d);var b=j.getLostPoint(this);if(0==d||a>b)a=b,c=d}return c},createMovieClip:function(a,c,d){a=a.createEmptyMovieClip(c,d);this.make();for(c=0;c<this.modules.length;c++)for(var d=1*c,b=0;b<this.modules[c].length;b++){var e=1*b;this.modules[c][b]&&(a.beginFill(0,100),a.moveTo(e,d),a.lineTo(e+1,d),a.lineTo(e+1,d+1),a.lineTo(e,d+1),a.endFill())}return a},
setupTimingPattern:function(){for(var a=8;a<this.moduleCount-8;a++)null==this.modules[a][6]&&(this.modules[a][6]=0==a%2);for(a=8;a<this.moduleCount-8;a++)null==this.modules[6][a]&&(this.modules[6][a]=0==a%2)},setupPositionAdjustPattern:function(){for(var a=j.getPatternPosition(this.typeNumber),c=0;c<a.length;c++)for(var d=0;d<a.length;d++){var b=a[c],e=a[d];if(null==this.modules[b][e])for(var f=-2;2>=f;f++)for(var i=-2;2>=i;i++)this.modules[b+f][e+i]=-2==f||2==f||-2==i||2==i||0==f&&0==i?!0:!1}},setupTypeNumber:function(a){for(var c=
j.getBCHTypeNumber(this.typeNumber),d=0;18>d;d++){var b=!a&&1==(c>>d&1);this.modules[Math.floor(d/3)][d%3+this.moduleCount-8-3]=b}for(d=0;18>d;d++)b=!a&&1==(c>>d&1),this.modules[d%3+this.moduleCount-8-3][Math.floor(d/3)]=b},setupTypeInfo:function(a,c){for(var d=j.getBCHTypeInfo(this.errorCorrectLevel<<3|c),b=0;15>b;b++){var e=!a&&1==(d>>b&1);6>b?this.modules[b][8]=e:8>b?this.modules[b+1][8]=e:this.modules[this.moduleCount-15+b][8]=e}for(b=0;15>b;b++)e=!a&&1==(d>>b&1),8>b?this.modules[8][this.moduleCount-
b-1]=e:9>b?this.modules[8][15-b-1+1]=e:this.modules[8][15-b-1]=e;this.modules[this.moduleCount-8][8]=!a},mapData:function(a,c){for(var d=-1,b=this.moduleCount-1,e=7,f=0,i=this.moduleCount-1;0<i;i-=2)for(6==i&&i--;;){for(var g=0;2>g;g++)if(null==this.modules[b][i-g]){var n=!1;f<a.length&&(n=1==(a[f]>>>e&1));j.getMask(c,b,i-g)&&(n=!n);this.modules[b][i-g]=n;e--; -1==e&&(f++,e=7)}b+=d;if(0>b||this.moduleCount<=b){b-=d;d=-d;break}}}};o.PAD0=236;o.PAD1=17;o.createData=function(a,c,d){for(var c=p.getRSBlocks(a,
c),b=new t,e=0;e<d.length;e++){var f=d[e];b.put(f.mode,4);b.put(f.getLength(),j.getLengthInBits(f.mode,a));f.write(b)}for(e=a=0;e<c.length;e++)a+=c[e].dataCount;if(b.getLengthInBits()>8*a)throw Error("code length overflow. ("+b.getLengthInBits()+">"+8*a+")");for(b.getLengthInBits()+4<=8*a&&b.put(0,4);0!=b.getLengthInBits()%8;)b.putBit(!1);for(;!(b.getLengthInBits()>=8*a);){b.put(o.PAD0,8);if(b.getLengthInBits()>=8*a)break;b.put(o.PAD1,8)}return o.createBytes(b,c)};o.createBytes=function(a,c){for(var d=
0,b=0,e=0,f=Array(c.length),i=Array(c.length),g=0;g<c.length;g++){var n=c[g].dataCount,h=c[g].totalCount-n,b=Math.max(b,n),e=Math.max(e,h);f[g]=Array(n);for(var k=0;k<f[g].length;k++)f[g][k]=255&a.buffer[k+d];d+=n;k=j.getErrorCorrectPolynomial(h);n=(new q(f[g],k.getLength()-1)).mod(k);i[g]=Array(k.getLength()-1);for(k=0;k<i[g].length;k++)h=k+n.getLength()-i[g].length,i[g][k]=0<=h?n.get(h):0}for(k=g=0;k<c.length;k++)g+=c[k].totalCount;d=Array(g);for(k=n=0;k<b;k++)for(g=0;g<c.length;g++)k<f[g].length&&
(d[n++]=f[g][k]);for(k=0;k<e;k++)for(g=0;g<c.length;g++)k<i[g].length&&(d[n++]=i[g][k]);return d};s=4;for(var j={PATTERN_POSITION_TABLE:[[],[6,18],[6,22],[6,26],[6,30],[6,34],[6,22,38],[6,24,42],[6,26,46],[6,28,50],[6,30,54],[6,32,58],[6,34,62],[6,26,46,66],[6,26,48,70],[6,26,50,74],[6,30,54,78],[6,30,56,82],[6,30,58,86],[6,34,62,90],[6,28,50,72,94],[6,26,50,74,98],[6,30,54,78,102],[6,28,54,80,106],[6,32,58,84,110],[6,30,58,86,114],[6,34,62,90,118],[6,26,50,74,98,122],[6,30,54,78,102,126],[6,26,52,
78,104,130],[6,30,56,82,108,134],[6,34,60,86,112,138],[6,30,58,86,114,142],[6,34,62,90,118,146],[6,30,54,78,102,126,150],[6,24,50,76,102,128,154],[6,28,54,80,106,132,158],[6,32,58,84,110,136,162],[6,26,54,82,110,138,166],[6,30,58,86,114,142,170]],G15:1335,G18:7973,G15_MASK:21522,getBCHTypeInfo:function(a){for(var c=a<<10;0<=j.getBCHDigit(c)-j.getBCHDigit(j.G15);)c^=j.G15<<j.getBCHDigit(c)-j.getBCHDigit(j.G15);return(a<<10|c)^j.G15_MASK},getBCHTypeNumber:function(a){for(var c=a<<12;0<=j.getBCHDigit(c)-
j.getBCHDigit(j.G18);)c^=j.G18<<j.getBCHDigit(c)-j.getBCHDigit(j.G18);return a<<12|c},getBCHDigit:function(a){for(var c=0;0!=a;)c++,a>>>=1;return c},getPatternPosition:function(a){return j.PATTERN_POSITION_TABLE[a-1]},getMask:function(a,c,d){switch(a){case 0:return 0==(c+d)%2;case 1:return 0==c%2;case 2:return 0==d%3;case 3:return 0==(c+d)%3;case 4:return 0==(Math.floor(c/2)+Math.floor(d/3))%2;case 5:return 0==c*d%2+c*d%3;case 6:return 0==(c*d%2+c*d%3)%2;case 7:return 0==(c*d%3+(c+d)%2)%2;default:throw Error("bad maskPattern:"+
a);}},getErrorCorrectPolynomial:function(a){for(var c=new q([1],0),d=0;d<a;d++)c=c.multiply(new q([1,l.gexp(d)],0));return c},getLengthInBits:function(a,c){if(1<=c&&10>c)switch(a){case 1:return 10;case 2:return 9;case s:return 8;case 8:return 8;default:throw Error("mode:"+a);}else if(27>c)switch(a){case 1:return 12;case 2:return 11;case s:return 16;case 8:return 10;default:throw Error("mode:"+a);}else if(41>c)switch(a){case 1:return 14;case 2:return 13;case s:return 16;case 8:return 12;default:throw Error("mode:"+
a);}else throw Error("type:"+c);},getLostPoint:function(a){for(var c=a.getModuleCount(),d=0,b=0;b<c;b++)for(var e=0;e<c;e++){for(var f=0,i=a.isDark(b,e),g=-1;1>=g;g++)if(!(0>b+g||c<=b+g))for(var h=-1;1>=h;h++)0>e+h||c<=e+h||0==g&&0==h||i==a.isDark(b+g,e+h)&&f++;5<f&&(d+=3+f-5)}for(b=0;b<c-1;b++)for(e=0;e<c-1;e++)if(f=0,a.isDark(b,e)&&f++,a.isDark(b+1,e)&&f++,a.isDark(b,e+1)&&f++,a.isDark(b+1,e+1)&&f++,0==f||4==f)d+=3;for(b=0;b<c;b++)for(e=0;e<c-6;e++)a.isDark(b,e)&&!a.isDark(b,e+1)&&a.isDark(b,e+
2)&&a.isDark(b,e+3)&&a.isDark(b,e+4)&&!a.isDark(b,e+5)&&a.isDark(b,e+6)&&(d+=40);for(e=0;e<c;e++)for(b=0;b<c-6;b++)a.isDark(b,e)&&!a.isDark(b+1,e)&&a.isDark(b+2,e)&&a.isDark(b+3,e)&&a.isDark(b+4,e)&&!a.isDark(b+5,e)&&a.isDark(b+6,e)&&(d+=40);for(e=f=0;e<c;e++)for(b=0;b<c;b++)a.isDark(b,e)&&f++;a=Math.abs(100*f/c/c-50)/5;return d+10*a}},l={glog:function(a){if(1>a)throw Error("glog("+a+")");return l.LOG_TABLE[a]},gexp:function(a){for(;0>a;)a+=255;for(;256<=a;)a-=255;return l.EXP_TABLE[a]},EXP_TABLE:Array(256),
LOG_TABLE:Array(256)},m=0;8>m;m++)l.EXP_TABLE[m]=1<<m;for(m=8;256>m;m++)l.EXP_TABLE[m]=l.EXP_TABLE[m-4]^l.EXP_TABLE[m-5]^l.EXP_TABLE[m-6]^l.EXP_TABLE[m-8];for(m=0;255>m;m++)l.LOG_TABLE[l.EXP_TABLE[m]]=m;q.prototype={get:function(a){return this.num[a]},getLength:function(){return this.num.length},multiply:function(a){for(var c=Array(this.getLength()+a.getLength()-1),d=0;d<this.getLength();d++)for(var b=0;b<a.getLength();b++)c[d+b]^=l.gexp(l.glog(this.get(d))+l.glog(a.get(b)));return new q(c,0)},mod:function(a){if(0>
this.getLength()-a.getLength())return this;for(var c=l.glog(this.get(0))-l.glog(a.get(0)),d=Array(this.getLength()),b=0;b<this.getLength();b++)d[b]=this.get(b);for(b=0;b<a.getLength();b++)d[b]^=l.gexp(l.glog(a.get(b))+c);return(new q(d,0)).mod(a)}};p.RS_BLOCK_TABLE=[[1,26,19],[1,26,16],[1,26,13],[1,26,9],[1,44,34],[1,44,28],[1,44,22],[1,44,16],[1,70,55],[1,70,44],[2,35,17],[2,35,13],[1,100,80],[2,50,32],[2,50,24],[4,25,9],[1,134,108],[2,67,43],[2,33,15,2,34,16],[2,33,11,2,34,12],[2,86,68],[4,43,27],
[4,43,19],[4,43,15],[2,98,78],[4,49,31],[2,32,14,4,33,15],[4,39,13,1,40,14],[2,121,97],[2,60,38,2,61,39],[4,40,18,2,41,19],[4,40,14,2,41,15],[2,146,116],[3,58,36,2,59,37],[4,36,16,4,37,17],[4,36,12,4,37,13],[2,86,68,2,87,69],[4,69,43,1,70,44],[6,43,19,2,44,20],[6,43,15,2,44,16],[4,101,81],[1,80,50,4,81,51],[4,50,22,4,51,23],[3,36,12,8,37,13],[2,116,92,2,117,93],[6,58,36,2,59,37],[4,46,20,6,47,21],[7,42,14,4,43,15],[4,133,107],[8,59,37,1,60,38],[8,44,20,4,45,21],[12,33,11,4,34,12],[3,145,115,1,146,
116],[4,64,40,5,65,41],[11,36,16,5,37,17],[11,36,12,5,37,13],[5,109,87,1,110,88],[5,65,41,5,66,42],[5,54,24,7,55,25],[11,36,12],[5,122,98,1,123,99],[7,73,45,3,74,46],[15,43,19,2,44,20],[3,45,15,13,46,16],[1,135,107,5,136,108],[10,74,46,1,75,47],[1,50,22,15,51,23],[2,42,14,17,43,15],[5,150,120,1,151,121],[9,69,43,4,70,44],[17,50,22,1,51,23],[2,42,14,19,43,15],[3,141,113,4,142,114],[3,70,44,11,71,45],[17,47,21,4,48,22],[9,39,13,16,40,14],[3,135,107,5,136,108],[3,67,41,13,68,42],[15,54,24,5,55,25],[15,
43,15,10,44,16],[4,144,116,4,145,117],[17,68,42],[17,50,22,6,51,23],[19,46,16,6,47,17],[2,139,111,7,140,112],[17,74,46],[7,54,24,16,55,25],[34,37,13],[4,151,121,5,152,122],[4,75,47,14,76,48],[11,54,24,14,55,25],[16,45,15,14,46,16],[6,147,117,4,148,118],[6,73,45,14,74,46],[11,54,24,16,55,25],[30,46,16,2,47,17],[8,132,106,4,133,107],[8,75,47,13,76,48],[7,54,24,22,55,25],[22,45,15,13,46,16],[10,142,114,2,143,115],[19,74,46,4,75,47],[28,50,22,6,51,23],[33,46,16,4,47,17],[8,152,122,4,153,123],[22,73,45,
3,74,46],[8,53,23,26,54,24],[12,45,15,28,46,16],[3,147,117,10,148,118],[3,73,45,23,74,46],[4,54,24,31,55,25],[11,45,15,31,46,16],[7,146,116,7,147,117],[21,73,45,7,74,46],[1,53,23,37,54,24],[19,45,15,26,46,16],[5,145,115,10,146,116],[19,75,47,10,76,48],[15,54,24,25,55,25],[23,45,15,25,46,16],[13,145,115,3,146,116],[2,74,46,29,75,47],[42,54,24,1,55,25],[23,45,15,28,46,16],[17,145,115],[10,74,46,23,75,47],[10,54,24,35,55,25],[19,45,15,35,46,16],[17,145,115,1,146,116],[14,74,46,21,75,47],[29,54,24,19,
55,25],[11,45,15,46,46,16],[13,145,115,6,146,116],[14,74,46,23,75,47],[44,54,24,7,55,25],[59,46,16,1,47,17],[12,151,121,7,152,122],[12,75,47,26,76,48],[39,54,24,14,55,25],[22,45,15,41,46,16],[6,151,121,14,152,122],[6,75,47,34,76,48],[46,54,24,10,55,25],[2,45,15,64,46,16],[17,152,122,4,153,123],[29,74,46,14,75,47],[49,54,24,10,55,25],[24,45,15,46,46,16],[4,152,122,18,153,123],[13,74,46,32,75,47],[48,54,24,14,55,25],[42,45,15,32,46,16],[20,147,117,4,148,118],[40,75,47,7,76,48],[43,54,24,22,55,25],[10,
45,15,67,46,16],[19,148,118,6,149,119],[18,75,47,31,76,48],[34,54,24,34,55,25],[20,45,15,61,46,16]];p.getRSBlocks=function(a,c){var d=p.getRsBlockTable(a,c);if(void 0==d)throw Error("bad rs block @ typeNumber:"+a+"/errorCorrectLevel:"+c);for(var b=d.length/3,e=[],f=0;f<b;f++)for(var h=d[3*f+0],g=d[3*f+1],j=d[3*f+2],l=0;l<h;l++)e.push(new p(g,j));return e};p.getRsBlockTable=function(a,c){switch(c){case 1:return p.RS_BLOCK_TABLE[4*(a-1)+0];case 0:return p.RS_BLOCK_TABLE[4*(a-1)+1];case 3:return p.RS_BLOCK_TABLE[4*
(a-1)+2];case 2:return p.RS_BLOCK_TABLE[4*(a-1)+3]}};t.prototype={get:function(a){return 1==(this.buffer[Math.floor(a/8)]>>>7-a%8&1)},put:function(a,c){for(var d=0;d<c;d++)this.putBit(1==(a>>>c-d-1&1))},getLengthInBits:function(){return this.length},putBit:function(a){var c=Math.floor(this.length/8);this.buffer.length<=c&&this.buffer.push(0);a&&(this.buffer[c]|=128>>>this.length%8);this.length++}};"string"===typeof h&&(h={text:h});h=r.extend({},{render:"canvas",width:256,height:256,typeNumber:-1,
correctLevel:2,background:"#ffffff",foreground:"#000000"},h);return this.each(function(){var a;if("canvas"==h.render){a=new o(h.typeNumber,h.correctLevel);a.addData(h.text);a.make();var c=document.createElement("canvas");c.width=h.width;c.height=h.height;for(var d=c.getContext("2d"),b=h.width/a.getModuleCount(),e=h.height/a.getModuleCount(),f=0;f<a.getModuleCount();f++)for(var i=0;i<a.getModuleCount();i++){d.fillStyle=a.isDark(f,i)?h.foreground:h.background;var g=Math.ceil((i+1)*b)-Math.floor(i*b),
j=Math.ceil((f+1)*b)-Math.floor(f*b);d.fillRect(Math.round(i*b),Math.round(f*e),g,j)}}else{a=new o(h.typeNumber,h.correctLevel);a.addData(h.text);a.make();c=r("<table></table>").css("width",h.width+"px").css("height",h.height+"px").css("border","0px").css("border-collapse","collapse").css("background-color",h.background);d=h.width/a.getModuleCount();b=h.height/a.getModuleCount();for(e=0;e<a.getModuleCount();e++){f=r("<tr></tr>").css("height",b+"px").appendTo(c);for(i=0;i<a.getModuleCount();i++)r("<td></td>").css("width",
d+"px").css("background-color",a.isDark(e,i)?h.foreground:h.background).appendTo(f)}}a=c;jQuery(a).appendTo(this)})}})(jQuery);

View File

@@ -65,8 +65,8 @@ function confirmAjaxCall(url, msg, loader_msg, callback) {
url: url, url: url,
type: 'POST', type: 'POST',
complete: function (xhr, status) { complete: function (xhr, status) {
result = $.parseJSON(xhr.responseText); var result = $.parseJSON(xhr.responseText);
msg = result.message; var msg = result.message;
if (result.result == 'success') { if (result.result == 'success') {
showMsg('<i class="fa fa-check"></i> ' + msg, false, true, 5000) showMsg('<i class="fa fa-check"></i> ' + msg, false, true, 5000)
} else { } else {
@@ -80,9 +80,9 @@ function confirmAjaxCall(url, msg, loader_msg, callback) {
}); });
} }
function doAjaxCall(url, elem, reload, form, callback) { function doAjaxCall(url, elem, reload, form, showMsg, callback) {
// Set Message // Set Message
feedback = $("#ajaxMsg"); feedback = (showMsg) ? $("#ajaxMsg") : $();
update = $("#updatebar"); update = $("#updatebar");
if (update.is(":visible")) { if (update.is(":visible")) {
var height = update.height() + 35; var height = update.height() + 35;
@@ -256,6 +256,10 @@ function getPlatformImagePath(platformName) {
return 'images/platforms/wp.png'; return 'images/platforms/wp.png';
} else if (platformName.indexOf("Plex Media Player") > -1) { } else if (platformName.indexOf("Plex Media Player") > -1) {
return 'images/platforms/pmp.png'; return 'images/platforms/pmp.png';
} else if (platformName.indexOf("PlexTogether") > -1) {
return 'images/platforms/plextogether.png';
} else if (platformName.indexOf("Linux") > -1) {
return 'images/platforms/linux.png';
} else { } else {
return 'images/platforms/default.png'; return 'images/platforms/default.png';
} }
@@ -449,3 +453,20 @@ $('*').on('click', '.refresh_pms_image', function (e) {
} }
} }
}); });
// Taken from http://stackoverflow.com/questions/10420352/converting-file-size-in-bytes-to-human-readable#answer-14919494
function humanFileSize(bytes, si) {
var thresh = si ? 1000 : 1024;
if (Math.abs(bytes) < thresh) {
return bytes + ' B';
}
var units = si
? ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
var u = -1;
do {
bytes /= thresh;
++u;
} while (Math.abs(bytes) >= thresh && u < units.length - 1);
return bytes.toFixed(1) + '&nbsp;' + units[u];
}

View File

@@ -4,7 +4,7 @@ var time_format = 'hh:mm a';
$.ajax({ $.ajax({
url: 'get_date_formats', url: 'get_date_formats',
type: 'GET', type: 'GET',
success: function(data) { success: function (data) {
date_format = data.date_format; date_format = data.date_format;
time_format = data.time_format; time_format = data.time_format;
} }
@@ -16,10 +16,10 @@ media_info_table_options = {
"destroy": true, "destroy": true,
"language": { "language": {
"search": "Search: ", "search": "Search: ",
"lengthMenu":"Show _MENU_ entries per page", "lengthMenu": "Show _MENU_ entries per page",
"info":"Showing _START_ to _END_ of _TOTAL_ library items", "info": "Showing _START_ to _END_ of _TOTAL_ library items",
"infoEmpty":"Showing 0 to 0 of 0 entries", "infoEmpty": "Showing 0 to 0 of 0 entries",
"infoFiltered":"<span class='hidden-md hidden-sm hidden-xs'>(filtered from _MAX_ total entries)</span>", "infoFiltered": "<span class='hidden-md hidden-sm hidden-xs'>(filtered from _MAX_ total entries)</span>",
"emptyTable": "No data in table", "emptyTable": "No data in table",
"loadingRecords": '<i class="fa fa-refresh fa-spin"></i> Loading items...</div>' "loadingRecords": '<i class="fa fa-refresh fa-spin"></i> Loading items...</div>'
}, },
@@ -28,7 +28,7 @@ media_info_table_options = {
"processing": false, "processing": false,
"serverSide": true, "serverSide": true,
"pageLength": 25, "pageLength": 25,
"order": [ 1, 'asc'], "order": [1, 'asc'],
"autoWidth": false, "autoWidth": false,
"scrollX": true, "scrollX": true,
"columnDefs": [ "columnDefs": [
@@ -110,7 +110,7 @@ media_info_table_options = {
}, },
"width": "20%", "width": "20%",
"className": "no-wrap", "className": "no-wrap",
}, },
{ {
"targets": [2], "targets": [2],
"data": "container", "data": "container",
@@ -194,7 +194,7 @@ media_info_table_options = {
"data": "file_size", "data": "file_size",
"createdCell": function (td, cellData, rowData, row, col) { "createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') { if (cellData !== null && cellData !== '') {
$(td).html(Math.round(cellData / Math.pow(1024, 2)).toString() + ' MiB'); $(td).html(humanFileSize(cellData));
} else { } else {
if (rowData['section_type'] != 'photo' && get_file_sizes != null) { if (rowData['section_type'] != 'photo' && get_file_sizes != null) {
get_file_sizes = true; get_file_sizes = true;
@@ -280,10 +280,10 @@ media_info_table_options = {
} }
$("#media_info_table-SID-" + section_id + "_info").append('<span class="hidden-md hidden-sm hidden-xs"> with a total file size of ' + $("#media_info_table-SID-" + section_id + "_info").append('<span class="hidden-md hidden-sm hidden-xs"> with a total file size of ' +
Math.round(settings.json.filtered_file_size / Math.pow(1024, 3)).toString() + ' GiB' + humanFileSize(settings.json.filtered_file_size) +
' (filtered from ' + Math.round(settings.json.total_file_size / Math.pow(1024, 3)).toString() + ' GiB)</span>'); ' (filtered from ' + humanFileSize(settings.json.total_file_size) + ')</span>');
}, },
"preDrawCallback": function(settings) { "preDrawCallback": function (settings) {
var msg = "<i class='fa fa-refresh fa-spin'></i>&nbspFetching rows..."; var msg = "<i class='fa fa-refresh fa-spin'></i>&nbspFetching rows...";
showMsg(msg, false, false, 0) showMsg(msg, false, false, 0)
}, },

View File

@@ -28,7 +28,7 @@
<option disabled>&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;</option> <option disabled>&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;</option>
<option value="DEBUG">Debug</option> <option value="DEBUG">Debug</option>
<option value="INFO">Info</option> <option value="INFO">Info</option>
<option value="WARN">Warning</option> <option value="WARNING">Warning</option>
<option value="ERROR">Error</option> <option value="ERROR">Error</option>
</select> </select>
</label> </label>
@@ -198,7 +198,7 @@
var selected_log_level = null; var selected_log_level = null;
function loadPlexPyLogs(selected_log_level) { function loadPlexPyLogs(selected_log_level) {
log_table_options.ajax = { log_table_options.ajax = {
url: "getLog", url: "get_log",
type: 'post', type: 'post',
data: function (d) { data: function (d) {
return { return {

View File

@@ -148,7 +148,7 @@
$('#save-notification-item').click(function () { $('#save-notification-item').click(function () {
// Reload modal to update certain fields // Reload modal to update certain fields
doAjaxCall('set_notification_config', $(this), 'tabs', true, reloadModal); doAjaxCall('set_notification_config', $(this), 'tabs', true, true, reloadModal);
return false; return false;
}); });
@@ -176,7 +176,7 @@
}) })
$('#test_notifier').click(function () { $('#test_notifier').click(function () {
doAjaxCall('set_notification_config', $(this), 'tabs', true, sendTestNotification); doAjaxCall('set_notification_config', $(this), 'tabs', true, false, sendTestNotification);
}); });
function sendTestNotification() { function sendTestNotification() {
@@ -219,7 +219,7 @@
$('#pushbullet_apikey, #pushover_apitoken, #scripts_folder, #join_apikey').on('change', function () { $('#pushbullet_apikey, #pushover_apitoken, #scripts_folder, #join_apikey').on('change', function () {
// Reload modal to update certain fields // Reload modal to update certain fields
doAjaxCall('set_notification_config', $(this), 'tabs', true, reloadModal); doAjaxCall('set_notification_config', $(this), 'tabs', true, false, reloadModal);
return false; return false;
}); });

View File

@@ -117,6 +117,12 @@
</div> </div>
<p class="help-block">Set your preferred time format. <a href="javascript:void(0)" data-target="#dateTimeOptionsModal" data-toggle="modal">Click here</a> to see the parameter list.</p> <p class="help-block">Set your preferred time format. <a href="javascript:void(0)" data-target="#dateTimeOptionsModal" data-toggle="modal">Click here</a> to see the parameter list.</p>
</div> </div>
<div class="checkbox">
<label>
<input type="checkbox" id="week_start_monday" name="week_start_monday" value="1" ${config['week_start_monday']}> Week Starting on Monday
</label>
<p class="help-block">Change the "<em>Play by day of week</em>" graph to start on Monday. Default is start on Sunday.</p>
</div>
<div class="checkbox"> <div class="checkbox">
<label> <label>
<input type="checkbox" id="group_history_tables" name="group_history_tables" value="1" ${config['group_history_tables']}> Group Table and Watch Statistics History <input type="checkbox" id="group_history_tables" name="group_history_tables" value="1" ${config['group_history_tables']}> Group Table and Watch Statistics History
@@ -129,6 +135,16 @@
</label> </label>
<p class="help-block">Include current activity in the history tables. Statistics will not be counted until the stream has ended.</p> <p class="help-block">Include current activity in the history tables. Statistics will not be counted until the stream has ended.</p>
</div> </div>
<div class="form-group">
<label for="notify_watched_percent">Watched Percent</label>
<div class="row">
<div class="col-md-2">
<input type="text" class="form-control" data-parsley-type="integer" id="notify_watched_percent" name="notify_watched_percent" value="${config['notify_watched_percent']}" size="5" data-parsley-range="[50,95]" data-parsley-trigger="change" data-parsley-errors-container="#notify_watched_percent_error" required>
</div>
<div id="notify_watched_percent_error" class="alert alert-danger settings-alert" role="alert"></div>
</div>
<p class="help-block">Set the percentage for a media item to be considered as watched. Minimum 50, Maximum 95.</p>
</div>
<div class="padded-header"> <div class="padded-header">
<h3>Backup</h3> <h3>Backup</h3>
@@ -143,6 +159,19 @@
</div> </div>
<p class="help-block">The interval (in hours) PlexPy will backup the database and configuration file. Minimum 1, maximum 24, default 6.</p> <p class="help-block">The interval (in hours) PlexPy will backup the database and configuration file. Minimum 1, maximum 24, default 6.</p>
</div> </div>
<div class="form-group">
<label for="backup_interval">Backup Days</label>
<div class="row">
<div class="col-md-2">
<input type="text" class="form-control" data-parsley-type="integer" id="backup_days" name="backup_days" value="${config['backup_days']}" size="5" data-parsley-min="1" data-parsley-trigger="change" data-parsley-errors-container="#backup_days_error" required>
</div>
<div id="backup_days_error" class="alert alert-danger settings-alert" role="alert"></div>
</div>
<p class="help-block">
The number of days to keep scheduled backups. Minimum 1, default 3.<br />
Note: Manual backups are not removed automatically.
</p>
</div>
<div class="padded-header"> <div class="padded-header">
<h3>Directories</h3> <h3>Directories</h3>
@@ -518,7 +547,7 @@
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<div class="input-group"> <div class="input-group">
<input class="form-control" type="text" name="api_key" id="api_key" value="${config['api_key']}" size="20"> <input class="form-control" type="text" name="api_key" id="api_key" value="${config['api_key']}" size="20" readonly>
<span class="input-group-btn"> <span class="input-group-btn">
<button class="btn btn-form" type="button" id="generate_api">Generate</button> <button class="btn btn-form" type="button" id="generate_api">Generate</button>
</span> </span>
@@ -736,6 +765,20 @@
</div> </div>
<p class="help-block">Backlink protection via anonymizer service, must end in "?".</p> <p class="help-block">Backlink protection via anonymizer service, must end in "?".</p>
</div> </div>
<div class="form-group">
<label>Flush Temporary Sessions</label>
<p class="help-block">
Attempt to fix history logging by flushing out all of the temporary sessions in the database.<br />
Warning: This will reset all currently active sessions. For emergency use only when history logging is stuck!
</p>
<div class="row">
<div class="col-md-4">
<div class="btn-group">
<button class="btn btn-form" type="button" id="delete_temp_sessions">Flush</button>
</div>
</div>
</div>
</div>
<div class="padded-header"> <div class="padded-header">
<h3>Database Import Tool</h3> <h3>Database Import Tool</h3>
@@ -887,16 +930,6 @@
<h3>Current Activity Notifications</h3> <h3>Current Activity Notifications</h3>
</div> </div>
<div class="form-group">
<label for="notify_watched_percent">Watched Percent</label>
<div class="row">
<div class="col-md-2">
<input type="text" class="form-control" data-parsley-type="integer" id="notify_watched_percent" name="notify_watched_percent" value="${config['notify_watched_percent']}" size="5" data-parsley-range="[50,95]" data-parsley-trigger="change" data-parsley-errors-container="#notify_watched_percent_error" required>
</div>
<div id="notify_watched_percent_error" class="alert alert-danger settings-alert" role="alert"></div>
</div>
<p class="help-block">Set the progress percentage of when a watched notification should be triggered. Minimum 50, Maximum 95.</p>
</div>
<div class="checkbox"> <div class="checkbox">
<label> <label>
<input type="checkbox" name="notify_consecutive" id="notify_consecutive" value="1" ${config['notify_consecutive']}> Allow Consecutive Notifications <input type="checkbox" name="notify_consecutive" id="notify_consecutive" value="1" ${config['notify_consecutive']}> Allow Consecutive Notifications
@@ -1533,11 +1566,11 @@
<div class="modal-body" id="modal-text"> <div class="modal-body" id="modal-text">
<div> <div>
<p class="help-block"> <p class="help-block">
This will attempt to fetch your token for you. This will not work on Internet Explorer 9 or lower. This will attempt to fetch a new Plex.tv token for you. PlexPy does not store your username and password.
PlexPy does not store your username and password. Note: This will not work on Internet Explorer 9 or lower.
</p> </p>
<div class="form-group"> <div class="form-group">
<label for="pms_username">PMS Username</label> <label for="pms_username">Plex.tv Username</label>
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<input type="text" class="form-control" id="pms_username" name="pms_username" size="30"> <input type="text" class="form-control" id="pms_username" name="pms_username" size="30">
@@ -1546,7 +1579,7 @@
<p class="help-block">Username for Plex.tv authentication.</p> <p class="help-block">Username for Plex.tv authentication.</p>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="pms_password">PMS Password</label> <label for="pms_password">Plex.tv Password</label>
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<input type="password" class="form-control" id="pms_password" name="pms_password" size="30"> <input type="password" class="form-control" id="pms_password" name="pms_password" size="30">
@@ -2190,7 +2223,7 @@ $(document).ready(function() {
function saveSettings() { function saveSettings() {
if (configForm.parsley().validate()) { if (configForm.parsley().validate()) {
doAjaxCall('configUpdate', $(this), 'tabs', true, postSaveChecks); doAjaxCall('configUpdate', $(this), 'tabs', true, true, postSaveChecks);
return false; return false;
} else { } else {
showMsg('<i class="fa fa-exclamation-circle"></i> Please verify your settings.', false, true, 5000, true) showMsg('<i class="fa fa-exclamation-circle"></i> Please verify your settings.', false, true, 5000, true)
@@ -2273,6 +2306,12 @@ $(document).ready(function() {
confirmAjaxCall(url, msg); confirmAjaxCall(url, msg);
}); });
$("#delete_temp_sessions").click(function () {
var msg = 'Are you sure you want to flush the temporary sessions?<br /><strong>This will reset all currently active sessions.</strong>';
var url = 'delete_temp_sessions';
confirmAjaxCall(url, msg);
});
$('#api_key').click(function(){ $('#api_key').select() }); $('#api_key').click(function(){ $('#api_key').select() });
$("#generate_api").click(function() { $("#generate_api").click(function() {
$.get('generateAPI', $.get('generateAPI',
@@ -2368,21 +2407,24 @@ $(document).ready(function() {
if ((pms_username !== '') && (pms_password !== '')) { if ((pms_username !== '') && (pms_password !== '')) {
$.ajax({ $.ajax({
type: 'GET', type: 'GET',
url: 'get_pms_token', url: 'get_plexpy_pms_token',
data: { data: {
username: pms_username, username: pms_username,
password: pms_password password: pms_password,
force: true
}, },
cache: false, cache: false,
async: true, async: true,
complete: function(xhr, status) { complete: function(xhr, status) {
var authToken = $.parseJSON(xhr.responseText); var result = $.parseJSON(xhr.responseText);
if (authToken) { var msg = result.message;
$("#pms-token-status").html('<i class="fa fa-check"></i> Authentication successful!'); if (result.result == 'success') {
var authToken = result.token;
$("#pms-token-status").html('<i class="fa fa-check"></i> ' + msg);
$("#pms_token").val(authToken); $("#pms_token").val(authToken);
$('#pms-auth-modal').modal('hide'); $('#pms-auth-modal').modal('hide');
} else { } else {
$("#pms-token-status").html('<i class="fa fa-exclamation-circle"></i> Invalid username or password.'); $("#pms-token-status").html('<i class="fa fa-exclamation-circle"></i> ' + msg);
} }
loadUpdateDistros(); loadUpdateDistros();
} }

View File

@@ -47,7 +47,7 @@
// Redirect to home page after countdown. // Redirect to home page after countdown.
function reloadPage() { function reloadPage() {
window.location.href = "index"; window.location.href = "${new_http_root}index";
} }
</script> </script>
</%def> </%def>

View File

@@ -418,7 +418,7 @@
if ((pms_username !== '') && (pms_password !== '')) { if ((pms_username !== '') && (pms_password !== '')) {
$.ajax({ $.ajax({
type: 'GET', type: 'GET',
url: 'get_pms_token', url: 'get_plexpy_pms_token',
data: { data: {
username: pms_username, username: pms_username,
password: pms_password password: pms_password
@@ -426,15 +426,17 @@
cache: false, cache: false,
async: true, async: true,
complete: function (xhr, status) { complete: function (xhr, status) {
var authToken = $.parseJSON(xhr.responseText); var result = $.parseJSON(xhr.responseText);
if (authToken) { var msg = result.message;
$("#pms-token-status").html('<i class="fa fa-check"></i> Authentication successful!'); if (result.result == 'success') {
var authToken = result.token;
$("#pms-token-status").html('<i class="fa fa-check"></i> ' + msg);
$('#pms-token-status').fadeIn('fast'); $('#pms-token-status').fadeIn('fast');
$("#pms_token").val(authToken); $("#pms_token").val(authToken);
authenticated = true; authenticated = true;
getServerOptions(authToken) getServerOptions(authToken)
} else { } else {
$("#pms-token-status").html('<i class="fa fa-exclamation-circle"></i> Invalid username or password.'); $("#pms-token-status").html('<i class="fa fa-exclamation-circle"></i> ' + msg);
} }
} }
}); });

View File

@@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
# #
# PROVIDE: plexpy # PROVIDE: plexpy
# REQUIRE: sabnzbd # REQUIRE: plexpy
# KEYWORD: shutdown # KEYWORD: shutdown
# #
# Add the following lines to /etc/rc.conf.local or /etc/rc.conf # Add the following lines to /etc/rc.conf.local or /etc/rc.conf
@@ -10,7 +10,7 @@
# plexpy_enable (bool): Set to NO by default. # plexpy_enable (bool): Set to NO by default.
# Set it to YES to enable it. # Set it to YES to enable it.
# plexpy_user: The user account PlexPy daemon runs as what # plexpy_user: The user account PlexPy daemon runs as what
# you want it to be. It uses '_sabnzbd' user by # you want it to be. It uses 'plexpy' user by
# default. Do not sets it as empty or it will run # default. Do not sets it as empty or it will run
# as root. # as root.
# plexpy_dir: Directory where PlexPy lives. # plexpy_dir: Directory where PlexPy lives.
@@ -28,7 +28,7 @@ rcvar=${name}_enable
load_rc_config ${name} load_rc_config ${name}
: ${plexpy_enable:="NO"} : ${plexpy_enable:="NO"}
: ${plexpy_user:="_sabnzbd"} : ${plexpy_user:="plexpy"}
: ${plexpy_dir:="/usr/local/plexpy"} : ${plexpy_dir:="/usr/local/plexpy"}
: ${plexpy_chdir:="${plexpy_dir}"} : ${plexpy_chdir:="${plexpy_dir}"}
: ${plexpy_pid:="${plexpy_dir}/plexpy.pid"} : ${plexpy_pid:="${plexpy_dir}/plexpy.pid"}

View File

@@ -1,66 +0,0 @@
# PlexPy - Stats for Plex Media Server usage
#
# Service Unit file for systemd system manager
#
# INSTALLATION NOTES
#
# 1. Rename this file as you want, ensuring that it ends in .service
# e.g. 'plexpy.service'
#
# 2. Adjust configuration settings as required. More details in the
# "CONFIGURATION NOTES" section shown below.
#
# 3. Copy this file into your systemd service unit directory, which is
# often '/lib/systemd/system'.
#
# 4. Create any files/directories that you specified back in step #2.
# e.g. '/etc/plexpy/plexpy.ini'
# '/home/sabnzbd/.plexpy'
#
# 5. Enable boot-time autostart with the following commands:
# systemctl daemon-reload
# systemctl enable plexpy.service
#
# 6. Start now with the following command:
# systemctl start plexpy.service
#
# 7. If troubleshooting startup-errors, start by checking permissions
# and ownership on the files/directories that you created in step #4.
#
#
# CONFIGURATION NOTES
#
# - The example settings in this file assume that:
# 1. You will run PlexPy as user/group: sabnzbd.sabnzbd
# 2. You will either have PlexPy installed as a subdirectory
# under '~sabnzbd', or that you will have a symlink under
# '~/sabnzbd' pointing to your PlexPy install dir.
# 3. Your PlexPy data directory and configuration file will be
# in separate locations from your PlexPy install dir, to
# simplify updates.
#
# - Option names (e.g. ExecStart=, Type=) appear to be case-sensitive)
#
# - Adjust ExecStart= to point to:
# 1. Your PlexPy executable,
# 2. Your config file (recommended is to put it somewhere in /etc)
# 3. Your datadir (recommended is to NOT put it in your PlexPy exec dir)
#
# - Adjust User= and Group= to the user/group you want PlexPy to run as.
#
# - WantedBy= specifies which target (i.e. runlevel) to start PlexPy for.
# multi-user.target equates to runlevel 3 (multi-user text mode)
# graphical.target equates to runlevel 5 (multi-user X11 graphical mode)
[Unit]
Description=PlexPy - Stats for Plex Media Server usage
[Service]
ExecStart=/home/sabnzbd/plexpy/PlexPy.py --daemon --config /etc/plexpy/plexpy.ini --datadir /home/sabnzbd/.plexpy --nolaunch --quiet
GuessMainPID=no
Type=forking
User=sabnzbd
Group=sabnzbd
[Install]
WantedBy=multi-user.target

View File

@@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
# #
# PROVIDE: plexpy # PROVIDE: plexpy
# REQUIRE: DAEMON sabnzbd # REQUIRE: DAEMON plexpy
# KEYWORD: shutdown # KEYWORD: shutdown
# #
# Add the following lines to /etc/rc.conf.local or /etc/rc.conf # Add the following lines to /etc/rc.conf.local or /etc/rc.conf
@@ -10,7 +10,7 @@
# plexpy_enable (bool): Set to NO by default. # plexpy_enable (bool): Set to NO by default.
# Set it to YES to enable it. # Set it to YES to enable it.
# plexpy_user: The user account PlexPy daemon runs as what # plexpy_user: The user account PlexPy daemon runs as what
# you want it to be. It uses '_sabnzbd' user by # you want it to be. It uses 'plexpy' user by
# default. Do not sets it as empty or it will run # default. Do not sets it as empty or it will run
# as root. # as root.
# plexpy_dir: Directory where PlexPy lives. # plexpy_dir: Directory where PlexPy lives.
@@ -29,7 +29,7 @@ rcvar=${name}_enable
load_rc_config ${name} load_rc_config ${name}
: ${plexpy_enable:="NO"} : ${plexpy_enable:="NO"}
: ${plexpy_user:="_sabnzbd"} : ${plexpy_user:="plexpy"}
: ${plexpy_dir:="/usr/local/share/plexpy"} : ${plexpy_dir:="/usr/local/share/plexpy"}
: ${plexpy_chdir:="${plexpy_dir}"} : ${plexpy_chdir:="${plexpy_dir}"}
: ${plexpy_pid:="${plexpy_dir}/plexpy.pid"} : ${plexpy_pid:="${plexpy_dir}/plexpy.pid"}

View File

@@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
# #
# PROVIDE: plexpy # PROVIDE: plexpy
# REQUIRE: DAEMON sabnzbd # REQUIRE: DAEMON plexpy
# KEYWORD: shutdown # KEYWORD: shutdown
# #
# Add the following lines to /etc/rc.conf.local or /etc/rc.conf # Add the following lines to /etc/rc.conf.local or /etc/rc.conf
@@ -10,7 +10,7 @@
# plexpy_enable (bool): Set to NO by default. # plexpy_enable (bool): Set to NO by default.
# Set it to YES to enable it. # Set it to YES to enable it.
# plexpy_user: The user account PlexPy daemon runs as what # plexpy_user: The user account PlexPy daemon runs as what
# you want it to be. It uses '_sabnzbd' user by # you want it to be. It uses 'plexpy' user by
# default. Do not sets it as empty or it will run # default. Do not sets it as empty or it will run
# as root. # as root.
# plexpy_dir: Directory where PlexPy lives. # plexpy_dir: Directory where PlexPy lives.
@@ -29,7 +29,7 @@ rcvar=${name}_enable
load_rc_config ${name} load_rc_config ${name}
: ${plexpy_enable:="NO"} : ${plexpy_enable:="NO"}
: ${plexpy_user:="_sabnzbd"} : ${plexpy_user:="plexpy"}
: ${plexpy_dir:="/usr/local/share/plexpy"} : ${plexpy_dir:="/usr/local/share/plexpy"}
: ${plexpy_chdir:="${plexpy_dir}"} : ${plexpy_chdir:="${plexpy_dir}"}
: ${plexpy_pid:="${plexpy_dir}/plexpy.pid"} : ${plexpy_pid:="${plexpy_dir}/plexpy.pid"}

View File

@@ -1,67 +0,0 @@
# PlexPy - Stats for Plex Media Server usage
#
# Service Unit file for systemd system manager
#
# INSTALLATION NOTES
#
# 1. Rename this file as you want, ensuring that it ends in .service
# e.g. 'plexpy.service'
#
# 2. Adjust configuration settings as required. More details in the
# "CONFIGURATION NOTES" section shown below.
#
# 3. Copy this file into your systemd service unit directory, which is
# often '/lib/systemd/system'.
#
# 4. Create any files/directories that you specified back in step #2.
# e.g. '/opt/plexpy.ini'
# '/opt/plexpy'
#
# 5. Enable boot-time autostart with the following commands:
# systemctl daemon-reload
# systemctl enable plexpy.service
#
# 6. Start now with the following command:
# systemctl start plexpy.service
#
# 7. If troubleshooting startup-errors, start by checking permissions
# and ownership on the files/directories that you created in step #4.
#
#
# CONFIGURATION NOTES
#
# - The example settings in this file assume that:
# 1. You will run PlexPy as user/group: plex.users
# 2. You will either have PlexPy installed as a subdirectory
# under '/opt', or that you will have a symlink under
# '/opt' pointing to your PlexPy install dir.
# 3. Your PlexPy data directory and configuration file can be
# in separate locations from your PlexPy install dir, to
# simplify updates. However, in the example below they are in the
# PlexPy install dir.
#
# - Option names (e.g. ExecStart=, Type=) appear to be case-sensitive)
#
# - Adjust ExecStart= to point to:
# 1. Your PlexPy executable,
# 2. Your config file (recommended is to put it somewhere in /etc)
# 3. Your datadir (recommended is to NOT put it in your PlexPy exec dir)
#
# - Adjust User= and Group= to the user/group you want PlexPy to run as.
#
# - WantedBy= specifies which target (i.e. runlevel) to start PlexPy for.
# multi-user.target equates to runlevel 3 (multi-user text mode)
# graphical.target equates to runlevel 5 (multi-user X11 graphical mode)
[Unit]
Description=PlexPy - Stats for Plex Media Server usage
[Service]
ExecStart=/opt/plexpy/PlexPy.py --daemon --config /opt/plexpy/config.ini --datadir /opt/plexpy --nolaunch --quiet
GuessMainPID=no
Type=forking
User=plex
Group=users
[Install]
WantedBy=multi-user.target

View File

@@ -19,7 +19,7 @@
</dependency> </dependency>
<method_context> <method_context>
<method_credential user="sabnzbd" group="sabnzbd"/> <method_credential user="plexpy" group="nogroup"/>
</method_context> </method_context>
<exec_method type="method" name="start" exec="python /opt/plexpy/PlexPy.py --daemon --quiet --nolaunch" timeout_seconds="60"/> <exec_method type="method" name="start" exec="python /opt/plexpy/PlexPy.py --daemon --quiet --nolaunch" timeout_seconds="60"/>

View File

@@ -13,31 +13,19 @@
# 3. Copy this file into your systemd service unit directory, which is # 3. Copy this file into your systemd service unit directory, which is
# often '/lib/systemd/system'. # often '/lib/systemd/system'.
# #
# 4. Create any files/directories that you specified back in step #2. # 4. Enable boot-time autostart with the following commands:
# e.g. '/etc/plexpy/plexpy.ini'
# '/home/sabnzbd/.plexpy'
#
# 5. Enable boot-time autostart with the following commands:
# systemctl daemon-reload # systemctl daemon-reload
# systemctl enable plexpy.service # systemctl enable plexpy.service
# #
# 6. Start now with the following command: # 5. Start now with the following command:
# systemctl start plexpy.service # systemctl start plexpy.service
# #
# 7. If troubleshooting startup-errors, start by checking permissions
# and ownership on the files/directories that you created in step #4.
#
#
# CONFIGURATION NOTES # CONFIGURATION NOTES
# #
# - The example settings in this file assume that: # - The example settings in this file assume that you will run PlexPy as user: plexpy
# 1. You will run PlexPy as user/group: sabnzbd.sabnzbd # - To create this user and give it ownership of the plexpy directory:
# 2. You will either have PlexPy installed as a subdirectory # sudo adduser --system --no-create-home plexpy
# under '~sabnzbd', or that you will have a symlink under # sudo chown plexpy:nogroup -R /opt/plexpy
# '~/sabnzbd' pointing to your PlexPy install dir.
# 3. Your PlexPy data directory and configuration file will be
# in separate locations from your PlexPy install dir, to
# simplify updates.
# #
# - Option names (e.g. ExecStart=, Type=) appear to be case-sensitive) # - Option names (e.g. ExecStart=, Type=) appear to be case-sensitive)
# #

View File

@@ -8,9 +8,9 @@ See the datetime section of the Python Library Reference for information
on how to use these modules. on how to use these modules.
''' '''
# The Olson database is updated several times a year. # The IANA (nee Olson) database is updated several times a year.
OLSON_VERSION = '2014j' OLSON_VERSION = '2016f'
VERSION = '2014.10' # Switching to pip compatible version numbering. VERSION = '2016.6.1' # Switching to pip compatible version numbering.
__version__ = VERSION __version__ = VERSION
OLSEN_VERSION = OLSON_VERSION # Old releases had this misspelling OLSEN_VERSION = OLSON_VERSION # Old releases had this misspelling
@@ -25,11 +25,6 @@ __all__ = [
import sys, datetime, os.path, gettext import sys, datetime, os.path, gettext
try:
from pkg_resources import resource_stream
except ImportError:
resource_stream = None
from pytz.exceptions import AmbiguousTimeError from pytz.exceptions import AmbiguousTimeError
from pytz.exceptions import InvalidTimeError from pytz.exceptions import InvalidTimeError
from pytz.exceptions import NonExistentTimeError from pytz.exceptions import NonExistentTimeError
@@ -57,7 +52,7 @@ except NameError: # Python 3.x
... ...
UnicodeEncodeError: ... UnicodeEncodeError: ...
""" """
s.encode('US-ASCII') # Raise an exception if not ASCII s.encode('ASCII') # Raise an exception if not ASCII
return s # But return the original string - not a byte string. return s # But return the original string - not a byte string.
else: # Python 2.x else: # Python 2.x
@@ -73,7 +68,7 @@ else: # Python 2.x
... ...
UnicodeEncodeError: ... UnicodeEncodeError: ...
""" """
return s.encode('US-ASCII') return s.encode('ASCII')
def open_resource(name): def open_resource(name):
@@ -88,11 +83,17 @@ def open_resource(name):
raise ValueError('Bad path segment: %r' % part) raise ValueError('Bad path segment: %r' % part)
filename = os.path.join(os.path.dirname(__file__), filename = os.path.join(os.path.dirname(__file__),
'zoneinfo', *name_parts) 'zoneinfo', *name_parts)
if not os.path.exists(filename) and resource_stream is not None: if not os.path.exists(filename):
# http://bugs.launchpad.net/bugs/383171 - we avoid using this # http://bugs.launchpad.net/bugs/383171 - we avoid using this
# unless absolutely necessary to help when a broken version of # unless absolutely necessary to help when a broken version of
# pkg_resources is installed. # pkg_resources is installed.
return resource_stream(__name__, 'zoneinfo/' + name) try:
from pkg_resources import resource_stream
except ImportError:
resource_stream = None
if resource_stream is not None:
return resource_stream(__name__, 'zoneinfo/' + name)
return open(filename, 'rb') return open(filename, 'rb')
@@ -241,7 +242,7 @@ class UTC(datetime.tzinfo):
return "UTC" return "UTC"
UTC = utc = UTC() # UTC is a singleton UTC = utc = UTC() # UTC is a singleton
def _UTC(): def _UTC():
@@ -329,7 +330,7 @@ class _CountryTimezoneDict(LazyDict):
zone_tab = open_resource('zone.tab') zone_tab = open_resource('zone.tab')
try: try:
for line in zone_tab: for line in zone_tab:
line = line.decode('US-ASCII') line = line.decode('UTF-8')
if line.startswith('#'): if line.startswith('#'):
continue continue
code, coordinates, zone = line.split(None, 4)[:3] code, coordinates, zone = line.split(None, 4)[:3]
@@ -357,7 +358,7 @@ class _CountryNameDict(LazyDict):
zone_tab = open_resource('iso3166.tab') zone_tab = open_resource('iso3166.tab')
try: try:
for line in zone_tab.readlines(): for line in zone_tab.readlines():
line = line.decode('US-ASCII') line = line.decode('UTF-8')
if line.startswith('#'): if line.startswith('#'):
continue continue
code, name = line.split(None, 1) code, name = line.split(None, 1)
@@ -404,9 +405,11 @@ class _FixedOffset(datetime.tzinfo):
def normalize(self, dt, is_dst=False): def normalize(self, dt, is_dst=False):
'''Correct the timezone information on the given datetime''' '''Correct the timezone information on the given datetime'''
if dt.tzinfo is self:
return dt
if dt.tzinfo is None: if dt.tzinfo is None:
raise ValueError('Naive time - no tzinfo set') raise ValueError('Naive time - no tzinfo set')
return dt.replace(tzinfo=self) return dt.astimezone(self)
def FixedOffset(offset, _tzinfos = {}): def FixedOffset(offset, _tzinfos = {}):
@@ -599,6 +602,7 @@ all_timezones = \
'America/Eirunepe', 'America/Eirunepe',
'America/El_Salvador', 'America/El_Salvador',
'America/Ensenada', 'America/Ensenada',
'America/Fort_Nelson',
'America/Fort_Wayne', 'America/Fort_Wayne',
'America/Fortaleza', 'America/Fortaleza',
'America/Glace_Bay', 'America/Glace_Bay',
@@ -731,6 +735,7 @@ all_timezones = \
'Asia/Bahrain', 'Asia/Bahrain',
'Asia/Baku', 'Asia/Baku',
'Asia/Bangkok', 'Asia/Bangkok',
'Asia/Barnaul',
'Asia/Beirut', 'Asia/Beirut',
'Asia/Bishkek', 'Asia/Bishkek',
'Asia/Brunei', 'Asia/Brunei',
@@ -802,6 +807,7 @@ all_timezones = \
'Asia/Thimbu', 'Asia/Thimbu',
'Asia/Thimphu', 'Asia/Thimphu',
'Asia/Tokyo', 'Asia/Tokyo',
'Asia/Tomsk',
'Asia/Ujung_Pandang', 'Asia/Ujung_Pandang',
'Asia/Ulaanbaatar', 'Asia/Ulaanbaatar',
'Asia/Ulan_Bator', 'Asia/Ulan_Bator',
@@ -907,6 +913,7 @@ all_timezones = \
'Etc/Zulu', 'Etc/Zulu',
'Europe/Amsterdam', 'Europe/Amsterdam',
'Europe/Andorra', 'Europe/Andorra',
'Europe/Astrakhan',
'Europe/Athens', 'Europe/Athens',
'Europe/Belfast', 'Europe/Belfast',
'Europe/Belgrade', 'Europe/Belgrade',
@@ -927,6 +934,7 @@ all_timezones = \
'Europe/Jersey', 'Europe/Jersey',
'Europe/Kaliningrad', 'Europe/Kaliningrad',
'Europe/Kiev', 'Europe/Kiev',
'Europe/Kirov',
'Europe/Lisbon', 'Europe/Lisbon',
'Europe/Ljubljana', 'Europe/Ljubljana',
'Europe/London', 'Europe/London',
@@ -954,6 +962,7 @@ all_timezones = \
'Europe/Tallinn', 'Europe/Tallinn',
'Europe/Tirane', 'Europe/Tirane',
'Europe/Tiraspol', 'Europe/Tiraspol',
'Europe/Ulyanovsk',
'Europe/Uzhgorod', 'Europe/Uzhgorod',
'Europe/Vaduz', 'Europe/Vaduz',
'Europe/Vatican', 'Europe/Vatican',
@@ -1177,6 +1186,7 @@ common_timezones = \
'America/Edmonton', 'America/Edmonton',
'America/Eirunepe', 'America/Eirunepe',
'America/El_Salvador', 'America/El_Salvador',
'America/Fort_Nelson',
'America/Fortaleza', 'America/Fortaleza',
'America/Glace_Bay', 'America/Glace_Bay',
'America/Godthab', 'America/Godthab',
@@ -1224,7 +1234,6 @@ common_timezones = \
'America/Moncton', 'America/Moncton',
'America/Monterrey', 'America/Monterrey',
'America/Montevideo', 'America/Montevideo',
'America/Montreal',
'America/Montserrat', 'America/Montserrat',
'America/Nassau', 'America/Nassau',
'America/New_York', 'America/New_York',
@@ -1249,7 +1258,6 @@ common_timezones = \
'America/Regina', 'America/Regina',
'America/Resolute', 'America/Resolute',
'America/Rio_Branco', 'America/Rio_Branco',
'America/Santa_Isabel',
'America/Santarem', 'America/Santarem',
'America/Santiago', 'America/Santiago',
'America/Santo_Domingo', 'America/Santo_Domingo',
@@ -1297,6 +1305,7 @@ common_timezones = \
'Asia/Bahrain', 'Asia/Bahrain',
'Asia/Baku', 'Asia/Baku',
'Asia/Bangkok', 'Asia/Bangkok',
'Asia/Barnaul',
'Asia/Beirut', 'Asia/Beirut',
'Asia/Bishkek', 'Asia/Bishkek',
'Asia/Brunei', 'Asia/Brunei',
@@ -1356,6 +1365,7 @@ common_timezones = \
'Asia/Tehran', 'Asia/Tehran',
'Asia/Thimphu', 'Asia/Thimphu',
'Asia/Tokyo', 'Asia/Tokyo',
'Asia/Tomsk',
'Asia/Ulaanbaatar', 'Asia/Ulaanbaatar',
'Asia/Urumqi', 'Asia/Urumqi',
'Asia/Ust-Nera', 'Asia/Ust-Nera',
@@ -1394,6 +1404,7 @@ common_timezones = \
'Canada/Pacific', 'Canada/Pacific',
'Europe/Amsterdam', 'Europe/Amsterdam',
'Europe/Andorra', 'Europe/Andorra',
'Europe/Astrakhan',
'Europe/Athens', 'Europe/Athens',
'Europe/Belgrade', 'Europe/Belgrade',
'Europe/Berlin', 'Europe/Berlin',
@@ -1413,6 +1424,7 @@ common_timezones = \
'Europe/Jersey', 'Europe/Jersey',
'Europe/Kaliningrad', 'Europe/Kaliningrad',
'Europe/Kiev', 'Europe/Kiev',
'Europe/Kirov',
'Europe/Lisbon', 'Europe/Lisbon',
'Europe/Ljubljana', 'Europe/Ljubljana',
'Europe/London', 'Europe/London',
@@ -1438,6 +1450,7 @@ common_timezones = \
'Europe/Stockholm', 'Europe/Stockholm',
'Europe/Tallinn', 'Europe/Tallinn',
'Europe/Tirane', 'Europe/Tirane',
'Europe/Ulyanovsk',
'Europe/Uzhgorod', 'Europe/Uzhgorod',
'Europe/Vaduz', 'Europe/Vaduz',
'Europe/Vatican', 'Europe/Vatican',

View File

@@ -15,13 +15,13 @@ from pytz.tzinfo import memorized_datetime, memorized_timedelta
def _byte_string(s): def _byte_string(s):
"""Cast a string or byte string to an ASCII byte string.""" """Cast a string or byte string to an ASCII byte string."""
return s.encode('US-ASCII') return s.encode('ASCII')
_NULL = _byte_string('\0') _NULL = _byte_string('\0')
def _std_string(s): def _std_string(s):
"""Cast a string or byte string to an ASCII string.""" """Cast a string or byte string to an ASCII string."""
return str(s.decode('US-ASCII')) return str(s.decode('ASCII'))
def build_tzinfo(zone, fp): def build_tzinfo(zone, fp):
head_fmt = '>4s c 15x 6l' head_fmt = '>4s c 15x 6l'
@@ -66,7 +66,7 @@ def build_tzinfo(zone, fp):
i += 3 i += 3
# Now build the timezone object # Now build the timezone object
if len(transitions) == 0: if len(ttinfo) ==1 or len(transitions) == 0:
ttinfo[0][0], ttinfo[0][2] ttinfo[0][0], ttinfo[0][2]
cls = type(zone, (StaticTzInfo,), dict( cls = type(zone, (StaticTzInfo,), dict(
zone=zone, zone=zone,

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

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