Compare commits
338 Commits
v2.1.31-be
...
v2.2.2-bet
Author | SHA1 | Date | |
---|---|---|---|
![]() |
6d5c320701 | ||
![]() |
400a189455 | ||
![]() |
b7d03a4f31 | ||
![]() |
523e6421be | ||
![]() |
e0cd6f7071 | ||
![]() |
38db0b7a70 | ||
![]() |
f39ecd89a7 | ||
![]() |
f7f76d82b6 | ||
![]() |
9097e79e4f | ||
![]() |
88711e7601 | ||
![]() |
d0fa83bb8c | ||
![]() |
1271458f83 | ||
![]() |
2ae09a07e6 | ||
![]() |
33d860384c | ||
![]() |
a4eda99a4a | ||
![]() |
752c7badd2 | ||
![]() |
6399c90642 | ||
![]() |
97089846e9 | ||
![]() |
4de7884e39 | ||
![]() |
440adfb914 | ||
![]() |
5f26d0085d | ||
![]() |
f484604c69 | ||
![]() |
899d2fbf9d | ||
![]() |
6a87dc9c40 | ||
![]() |
104e2929df | ||
![]() |
faac6b11c2 | ||
![]() |
377a23478e | ||
![]() |
c979e78802 | ||
![]() |
38f64c7d85 | ||
![]() |
1091a64863 | ||
![]() |
23de9616f1 | ||
![]() |
198e7767dc | ||
![]() |
aa31bf1a19 | ||
![]() |
f366304c50 | ||
![]() |
ce289995ff | ||
![]() |
ca2b4085c9 | ||
![]() |
1d08069162 | ||
![]() |
bcbfaae630 | ||
![]() |
ae9df92d28 | ||
![]() |
47610323b0 | ||
![]() |
d1f1763919 | ||
![]() |
1326ad8708 | ||
![]() |
6e09e509bd | ||
![]() |
e8d0557852 | ||
![]() |
aac705f465 | ||
![]() |
009971901b | ||
![]() |
1ffd6c0ea1 | ||
![]() |
50ce29cc64 | ||
![]() |
e4ec24be26 | ||
![]() |
04765288d7 | ||
![]() |
8fdd0ba0d9 | ||
![]() |
16ffbd9940 | ||
![]() |
fa61302954 | ||
![]() |
763e5f583a | ||
![]() |
395fc49087 | ||
![]() |
d54794e85f | ||
![]() |
d5917f89f0 | ||
![]() |
1003aa2df5 | ||
![]() |
6205af1a9a | ||
![]() |
d8b1db536c | ||
![]() |
699357ca21 | ||
![]() |
50398049f5 | ||
![]() |
1f83afc2f4 | ||
![]() |
90374bb46f | ||
![]() |
ccdd410eda | ||
![]() |
77bb806a01 | ||
![]() |
a952352e1f | ||
![]() |
b733ce969a | ||
![]() |
f4351df302 | ||
![]() |
76893100fc | ||
![]() |
96e8b808da | ||
![]() |
595bff94b4 | ||
![]() |
5661c00497 | ||
![]() |
a98d7bd4eb | ||
![]() |
097203162d | ||
![]() |
823c9b3159 | ||
![]() |
35965a6a1b | ||
![]() |
8d67cc4c5a | ||
![]() |
42e33a0468 | ||
![]() |
b2529db026 | ||
![]() |
e99c4aec4a | ||
![]() |
2d0a97f259 | ||
![]() |
fabb52763b | ||
![]() |
03dd1a6974 | ||
![]() |
ecee50a5e4 | ||
![]() |
cbab7c4cbf | ||
![]() |
257ea14c59 | ||
![]() |
11299291b0 | ||
![]() |
533b8076e4 | ||
![]() |
b0383b4813 | ||
![]() |
96500f75b0 | ||
![]() |
2a3bd3413f | ||
![]() |
8ec136a0ca | ||
![]() |
2bead0fc29 | ||
![]() |
359776d48a | ||
![]() |
05a16bb199 | ||
![]() |
f457704105 | ||
![]() |
5aa59b93b0 | ||
![]() |
47d51f92c7 | ||
![]() |
068cf17d0a | ||
![]() |
3c1b849a5d | ||
![]() |
8384ec7f15 | ||
![]() |
0e7a3962bb | ||
![]() |
3d1bb9975c | ||
![]() |
eed473be15 | ||
![]() |
e23d9bca22 | ||
![]() |
85a952a5c9 | ||
![]() |
5cc36310ba | ||
![]() |
8690d2ced5 | ||
![]() |
f572943a7b | ||
![]() |
89248ad46a | ||
![]() |
517db71916 | ||
![]() |
ad82de010d | ||
![]() |
02fd9edbbf | ||
![]() |
6c84dd7be8 | ||
![]() |
9edbe6af37 | ||
![]() |
37d09e9bad | ||
![]() |
0d0595e9a0 | ||
![]() |
17477455cb | ||
![]() |
43f594709d | ||
![]() |
8885b3e7e0 | ||
![]() |
c060638539 | ||
![]() |
2144d4d7ed | ||
![]() |
4f2e09d733 | ||
![]() |
9d13c29bf6 | ||
![]() |
60d577f95e | ||
![]() |
1dd1c6f67f | ||
![]() |
bcd0691b33 | ||
![]() |
24a2559ab5 | ||
![]() |
22a6bae4cf | ||
![]() |
2c45de1fe5 | ||
![]() |
0ab93d7a7f | ||
![]() |
c8831efb28 | ||
![]() |
0cbde5a2f5 | ||
![]() |
7de82d87f7 | ||
![]() |
751b97a39c | ||
![]() |
7b58bcc279 | ||
![]() |
820a2e688c | ||
![]() |
10a7f540ad | ||
![]() |
31a6c627af | ||
![]() |
aba4cbf9e4 | ||
![]() |
a5624e86e4 | ||
![]() |
5480d09a0b | ||
![]() |
5fad0a1d97 | ||
![]() |
4a7b5bab54 | ||
![]() |
fe0557dcc1 | ||
![]() |
e4b6d61098 | ||
![]() |
73e01ebaaf | ||
![]() |
d752d46676 | ||
![]() |
3932409fb4 | ||
![]() |
f69d4f1c42 | ||
![]() |
96699fc3b0 | ||
![]() |
97ec8f6828 | ||
![]() |
df851e67f9 | ||
![]() |
5d1bc3cf9b | ||
![]() |
81ab9b006d | ||
![]() |
1699fc09cf | ||
![]() |
f8b6a9f1e8 | ||
![]() |
ba465a0d15 | ||
![]() |
af521b4058 | ||
![]() |
27b512611f | ||
![]() |
b79f165a27 | ||
![]() |
716f76baed | ||
![]() |
18c57a4fc6 | ||
![]() |
c26483b4b8 | ||
![]() |
441e39d776 | ||
![]() |
7ecc075c7e | ||
![]() |
2e42663832 | ||
![]() |
075f9f8cbd | ||
![]() |
f3d42e7b53 | ||
![]() |
fa0b547b32 | ||
![]() |
8f19cdccfd | ||
![]() |
5c207aeee6 | ||
![]() |
d48273ef98 | ||
![]() |
a7803dcad7 | ||
![]() |
4fc9b6fdb4 | ||
![]() |
73f8f83658 | ||
![]() |
66a0f953b3 | ||
![]() |
f189eea32b | ||
![]() |
be29d879a7 | ||
![]() |
e3e906c9e5 | ||
![]() |
6fce31e1b9 | ||
![]() |
db1cb3d658 | ||
![]() |
c0d7c5ddff | ||
![]() |
d0cd2672dd | ||
![]() |
ca55900d40 | ||
![]() |
3d65ffc6d3 | ||
![]() |
f94b796c2b | ||
![]() |
66c1fd6593 | ||
![]() |
13a45facf9 | ||
![]() |
26f10b2c3d | ||
![]() |
9aea4c85b0 | ||
![]() |
ee1b0eeeff | ||
![]() |
c6cfb4a020 | ||
![]() |
91da41ff86 | ||
![]() |
a811edb236 | ||
![]() |
66de806b02 | ||
![]() |
c825176563 | ||
![]() |
7ae87fe0e7 | ||
![]() |
99f826c236 | ||
![]() |
f88d673e87 | ||
![]() |
bc2f73d686 | ||
![]() |
645c2ecef6 | ||
![]() |
4d4a8ca3b2 | ||
![]() |
a98bc45c10 | ||
![]() |
aa5affe366 | ||
![]() |
b9d5e49a71 | ||
![]() |
e90390be67 | ||
![]() |
8ef671c9cb | ||
![]() |
8829516cae | ||
![]() |
d8e8dfbd45 | ||
![]() |
0b6d9a4890 | ||
![]() |
55ffd68023 | ||
![]() |
6380de3e6c | ||
![]() |
d6220a921a | ||
![]() |
10e421b9d4 | ||
![]() |
90056bcce2 | ||
![]() |
4740d0fbf3 | ||
![]() |
13374c9ded | ||
![]() |
2a03be1905 | ||
![]() |
c8f132a750 | ||
![]() |
4d0c4bf4f4 | ||
![]() |
e321c5b197 | ||
![]() |
badbfdc4c1 | ||
![]() |
7d71086a41 | ||
![]() |
0e1764755a | ||
![]() |
c31d3ffd6c | ||
![]() |
0cba3988aa | ||
![]() |
a7a9ed8628 | ||
![]() |
6af96332fa | ||
![]() |
e334a0fc8b | ||
![]() |
54bbbb36a6 | ||
![]() |
b8ef56574a | ||
![]() |
bc491628d4 | ||
![]() |
629800c239 | ||
![]() |
8bf876f88c | ||
![]() |
a81ad6d73e | ||
![]() |
02220209e3 | ||
![]() |
9450a1434d | ||
![]() |
c358693fb2 | ||
![]() |
e7b3d768ce | ||
![]() |
ee91da2ff1 | ||
![]() |
0428df8e3f | ||
![]() |
d4fee1d701 | ||
![]() |
3b44a3afd2 | ||
![]() |
7ee1c51810 | ||
![]() |
f958de2de6 | ||
![]() |
b83eb2e763 | ||
![]() |
41c9369b43 | ||
![]() |
554be92c39 | ||
![]() |
7486a50c33 | ||
![]() |
1e777b1a1b | ||
![]() |
b7bb159630 | ||
![]() |
6e0a0d51b5 | ||
![]() |
55aad4e6ee | ||
![]() |
d1e401cb0c | ||
![]() |
018479fae9 | ||
![]() |
1c18e72539 | ||
![]() |
779e710045 | ||
![]() |
089a981f6e | ||
![]() |
3b24bbee5f | ||
![]() |
f9a597bed9 | ||
![]() |
a637b3bb24 | ||
![]() |
3e520820d8 | ||
![]() |
3a71929821 | ||
![]() |
a08629c503 | ||
![]() |
cfc30c1234 | ||
![]() |
ddbd486500 | ||
![]() |
f5794a5bae | ||
![]() |
bb1bf87fe2 | ||
![]() |
acc59523e0 | ||
![]() |
38d7ea16b4 | ||
![]() |
6e3147c5f5 | ||
![]() |
1b09f225ff | ||
![]() |
3cf8c4f8a8 | ||
![]() |
30be4b473f | ||
![]() |
6908034a86 | ||
![]() |
cba43f675a | ||
![]() |
6ff826bc3a | ||
![]() |
c7afd10ec0 | ||
![]() |
b39d5174f9 | ||
![]() |
501bc0ab3f | ||
![]() |
688d28b5ea | ||
![]() |
27d2c7b078 | ||
![]() |
2fb12ccf65 | ||
![]() |
cb92d159c1 | ||
![]() |
64bdf4237c | ||
![]() |
fd7b4ec7e3 | ||
![]() |
57eb57d4d7 | ||
![]() |
7974e9505b | ||
![]() |
7498fb37b5 | ||
![]() |
2cc3e88e6c | ||
![]() |
5fd8cfeb80 | ||
![]() |
b295566a4e | ||
![]() |
e0943a2d55 | ||
![]() |
3015740c3e | ||
![]() |
ec9ff2f803 | ||
![]() |
ec8aae9122 | ||
![]() |
52e608cc43 | ||
![]() |
8213f270e5 | ||
![]() |
7085042b0d | ||
![]() |
6a411d2458 | ||
![]() |
38e2fbabb8 | ||
![]() |
85709f754a | ||
![]() |
623a1e8a91 | ||
![]() |
de69945ebe | ||
![]() |
7095fa6ac6 | ||
![]() |
a59e8298fd | ||
![]() |
2737d52279 | ||
![]() |
0ac1ad4386 | ||
![]() |
2db328ac31 | ||
![]() |
b6de4ad054 | ||
![]() |
cfea7164b7 | ||
![]() |
7e7e5a6be4 | ||
![]() |
df57f4c009 | ||
![]() |
c2185c4ce5 | ||
![]() |
08714436c3 | ||
![]() |
f65f5d07c0 | ||
![]() |
a9b10c4560 | ||
![]() |
589fbd3158 | ||
![]() |
0ffc8c5d19 | ||
![]() |
7498617b74 | ||
![]() |
f21d505ab8 | ||
![]() |
7b16af0585 | ||
![]() |
a83108282a | ||
![]() |
1c4d01d6ec | ||
![]() |
22e6d4067d | ||
![]() |
1046b29c1a | ||
![]() |
d6127e28f3 | ||
![]() |
25a949356d | ||
![]() |
72a012b817 | ||
![]() |
f439bd639c | ||
![]() |
91476a420a | ||
![]() |
96c0f9cad5 | ||
![]() |
df50559495 | ||
![]() |
6d35bd7947 | ||
![]() |
d27356bbba | ||
![]() |
3054a824ce |
8
.dockerignore
Normal file
@@ -0,0 +1,8 @@
|
||||
.git
|
||||
.github
|
||||
.gitignore
|
||||
contrib
|
||||
init-scripts
|
||||
pylintrc
|
||||
*.md
|
||||
!CHANGELOG*.md
|
3
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
github: JonnyWong16
|
||||
patreon: Tautulli
|
||||
custom: ["https://bit.ly/2InPp15"]
|
83
.github/workflows/publish-docker.yml
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
name: Publish Docker
|
||||
on:
|
||||
push:
|
||||
branches: [master, beta, nightly]
|
||||
tags: [v*]
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Prepare
|
||||
id: prepare
|
||||
run: |
|
||||
if [[ $GITHUB_REF == refs/tags/* ]]; then
|
||||
echo ::set-output name=tag::${GITHUB_REF#refs/tags/}
|
||||
elif [[ $GITHUB_REF == refs/heads/master ]]; then
|
||||
echo ::set-output name=tag::latest
|
||||
else
|
||||
echo ::set-output name=tag::${GITHUB_REF#refs/heads/}
|
||||
fi
|
||||
if [[ $GITHUB_REF == refs/tags/* ]]; then
|
||||
echo ::set-output name=branch::master
|
||||
else
|
||||
echo ::set-output name=branch::${GITHUB_REF#refs/heads/}
|
||||
fi
|
||||
echo ::set-output name=commit::${GITHUB_SHA}
|
||||
echo ::set-output name=build_date::$(date -u +'%Y-%m-%dT%H:%M:%SZ')
|
||||
echo ::set-output name=docker_platforms::linux/amd64,linux/arm64,linux/arm
|
||||
echo ::set-output name=docker_image::tautulli/tautulli
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: crazy-max/ghaction-docker-buildx@v1
|
||||
with:
|
||||
version: latest
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Docker Buildx (no push)
|
||||
run: |
|
||||
docker buildx build \
|
||||
--platform ${{ steps.prepare.outputs.docker_platforms }} \
|
||||
--output "type=image,push=false" \
|
||||
--build-arg "TAG=${{ steps.prepare.outputs.tag }}" \
|
||||
--build-arg "BRANCH=${{ steps.prepare.outputs.branch }}" \
|
||||
--build-arg "COMMIT=${{ steps.prepare.outputs.commit }}" \
|
||||
--build-arg "BUILD_DATE=${{ steps.prepare.outputs.build_date }}" \
|
||||
--tag "${{ steps.prepare.outputs.docker_image }}:${{ steps.prepare.outputs.tag }}" \
|
||||
--file Dockerfile .
|
||||
|
||||
- name: Docker Login
|
||||
if: success()
|
||||
env:
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
run: |
|
||||
echo "${DOCKER_PASSWORD}" | docker login --username "${{ secrets.DOCKER_USERNAME }}" --password-stdin
|
||||
|
||||
- name: Docker Buildx (push)
|
||||
if: success()
|
||||
run: |
|
||||
docker buildx build \
|
||||
--platform ${{ steps.prepare.outputs.docker_platforms }} \
|
||||
--output "type=image,push=true" \
|
||||
--build-arg "TAG=${{ steps.prepare.outputs.tag }}" \
|
||||
--build-arg "BRANCH=${{ steps.prepare.outputs.branch }}" \
|
||||
--build-arg "COMMIT=${{ steps.prepare.outputs.commit }}" \
|
||||
--build-arg "BUILD_DATE=${{ steps.prepare.outputs.build_date }}" \
|
||||
--tag "${{ steps.prepare.outputs.docker_image }}:${{ steps.prepare.outputs.tag }}" \
|
||||
--file Dockerfile .
|
||||
|
||||
- name: Clear
|
||||
if: always()
|
||||
run: |
|
||||
rm -f ${HOME}/.docker/config.json
|
||||
|
||||
- name: Post Status to Discord
|
||||
uses: sarisia/actions-status-discord@v1
|
||||
if: always()
|
||||
with:
|
||||
webhook: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
status: ${{ job.status }}
|
||||
job: ${{ github.workflow }}
|
||||
nofail: true
|
28
.github/workflows/publish-release.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
name: Publish Release
|
||||
on:
|
||||
push:
|
||||
tags: [v*]
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@master
|
||||
- name: Get Release Version
|
||||
run: echo ::set-env name=RELEASE_VERSION::${GITHUB_REF#refs/tags/}
|
||||
- name: Get Changelog
|
||||
run: echo ::set-env name=CHANGELOG::"$( sed -n '/^## /{p; :loop n; p; /^## /q; b loop}' CHANGELOG.md | sed '$d' | sed '$d' | sed '$d' | sed ':a;N;$!ba;s/\n/%0A/g' )"
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ env.RELEASE_VERSION }}
|
||||
release_name: Tautulli ${{ env.RELEASE_VERSION }}
|
||||
body: |
|
||||
## Changelog
|
||||
|
||||
##${{ env.CHANGELOG }}
|
||||
draft: false
|
||||
prerelease: ${{ endsWith(env.RELEASE_VERSION, '-beta') }}
|
203
API.md
@@ -88,7 +88,8 @@ Required parameters:
|
||||
section_id (str): The id of the Plex library section
|
||||
|
||||
Optional parameters:
|
||||
None
|
||||
server_id (str): The Plex server identifier of the library section
|
||||
row_ids (str): Comma separated row ids to delete, e.g. "2,3,8"
|
||||
|
||||
Returns:
|
||||
None
|
||||
@@ -103,7 +104,7 @@ Required parameters:
|
||||
user_id (str): The id of the Plex user
|
||||
|
||||
Optional parameters:
|
||||
None
|
||||
row_ids (str): Comma separated row ids to delete, e.g. "2,3,8"
|
||||
|
||||
Returns:
|
||||
None
|
||||
@@ -114,6 +115,21 @@ Returns:
|
||||
Delete and recreate the cache directory.
|
||||
|
||||
|
||||
### delete_history
|
||||
Delete history rows from Tautulli.
|
||||
|
||||
```
|
||||
Required parameters:
|
||||
row_ids (str): Comma separated row ids to delete, e.g. "65,110,2,3645"
|
||||
|
||||
Optional parameters:
|
||||
None
|
||||
|
||||
Returns:
|
||||
None
|
||||
```
|
||||
|
||||
|
||||
### delete_hosted_images
|
||||
Delete the images uploaded to image hosting services.
|
||||
|
||||
@@ -146,7 +162,8 @@ Required parameters:
|
||||
section_id (str): The id of the Plex library section
|
||||
|
||||
Optional parameters:
|
||||
None
|
||||
server_id (str): The Plex server identifier of the library section
|
||||
row_ids (str): Comma separated row ids to delete, e.g. "2,3,8"
|
||||
|
||||
Returns:
|
||||
None
|
||||
@@ -173,11 +190,14 @@ Delete the 3rd party API lookup info.
|
||||
|
||||
```
|
||||
Required parameters:
|
||||
rating_key (int): 1234
|
||||
(Note: Must be the movie, show, or artist rating key)
|
||||
Optional parameters:
|
||||
None
|
||||
|
||||
Optional parameters:
|
||||
rating_key (int): 1234
|
||||
(Note: Must be the movie, show, artist, album, or track rating key)
|
||||
service (str): 'themoviedb' or 'tvmaze' or 'musicbrainz'
|
||||
delete_all (bool): 'true' to delete all images form the service
|
||||
|
||||
Returns:
|
||||
json:
|
||||
{"result": "success",
|
||||
@@ -275,6 +295,10 @@ Returns:
|
||||
```
|
||||
|
||||
|
||||
### delete_recently_added
|
||||
Flush out all of the recently added items in the database.
|
||||
|
||||
|
||||
### delete_temp_sessions
|
||||
Flush out all of the temporary sessions in the database.
|
||||
|
||||
@@ -287,7 +311,7 @@ Required parameters:
|
||||
user_id (str): The id of the Plex user
|
||||
|
||||
Optional parameters:
|
||||
None
|
||||
row_ids (str): Comma separated row ids to delete, e.g. "2,3,8"
|
||||
|
||||
Returns:
|
||||
None
|
||||
@@ -327,6 +351,7 @@ Required parameters:
|
||||
|
||||
Optional parameters:
|
||||
custom_thumb (str): The URL for the custom library thumbnail
|
||||
custom_art (str): The URL for the custom library background art
|
||||
keep_history (int): 0 or 1
|
||||
|
||||
Returns:
|
||||
@@ -395,7 +420,11 @@ Returns:
|
||||
"banner": "/library/metadata/1219/banner/1503306930",
|
||||
"bif_thumb": "/library/parts/274169/indexes/sd/1000",
|
||||
"bitrate": "10617",
|
||||
"channel_call_sign": "",
|
||||
"channel_identifier": "",
|
||||
"channel_stream": 0,
|
||||
"channel_thumb": "",
|
||||
"children_count": "",
|
||||
"collections": [],
|
||||
"container": "mkv",
|
||||
"content_rating": "TV-MA",
|
||||
@@ -416,6 +445,7 @@ Returns:
|
||||
"Drama",
|
||||
"Fantasy"
|
||||
],
|
||||
"grandparent_guid": "com.plexapp.agents.thetvdb://121361?lang=en",
|
||||
"grandparent_rating_key": "1219",
|
||||
"grandparent_thumb": "/library/metadata/1219/thumb/1503306930",
|
||||
"grandparent_title": "Game of Thrones",
|
||||
@@ -426,13 +456,15 @@ Returns:
|
||||
"ip_address": "10.10.10.1",
|
||||
"ip_address_public": "64.123.23.111",
|
||||
"is_admin": 1,
|
||||
"is_allow_sync": null,
|
||||
"is_allow_sync": 1,
|
||||
"is_home_user": 1,
|
||||
"is_restricted": 0,
|
||||
"keep_history": 1,
|
||||
"labels": [],
|
||||
"last_viewed_at": "1462165717",
|
||||
"library_name": "TV Shows",
|
||||
"live": 0,
|
||||
"live_uuid": "",
|
||||
"local": "1",
|
||||
"location": "lan",
|
||||
"machine_id": "lmd93nkn12k29j2lnm",
|
||||
@@ -441,8 +473,9 @@ Returns:
|
||||
"optimized_version": 0,
|
||||
"optimized_version_profile": "",
|
||||
"optimized_version_title": "",
|
||||
"originally_available_at": "2016-04-24",
|
||||
"original_title": "",
|
||||
"originally_available_at": "2016-04-24",
|
||||
"parent_guid": "com.plexapp.agents.thetvdb://121361/6?lang=en",
|
||||
"parent_media_index": "6",
|
||||
"parent_rating_key": "153036",
|
||||
"parent_thumb": "/library/metadata/153036/thumb/1503889210",
|
||||
@@ -461,6 +494,7 @@ Returns:
|
||||
"rating_key": "153037",
|
||||
"relay": 0,
|
||||
"section_id": "2",
|
||||
"secure": 1,
|
||||
"session_id": "helf15l3rxgw01xxe0jf3l3d",
|
||||
"session_key": "27",
|
||||
"shared_libraries": [
|
||||
@@ -499,15 +533,23 @@ Returns:
|
||||
"stream_subtitle_location": "",
|
||||
"stream_video_bit_depth": "8",
|
||||
"stream_video_bitrate": "10233",
|
||||
"stream_video_chroma_subsampling": "4:2:0",
|
||||
"stream_video_codec": "h264",
|
||||
"stream_video_codec_level": "41",
|
||||
"stream_video_color_primaries": "",
|
||||
"stream_video_color_range": "tv",
|
||||
"stream_video_color_space": "bt709",
|
||||
"stream_video_color_trc": "",
|
||||
"stream_video_decision": "direct play",
|
||||
"stream_video_dynamic_range": "SDR",
|
||||
"stream_video_framerate": "24p",
|
||||
"stream_video_full_resolution": "1080p",
|
||||
"stream_video_height": "1078",
|
||||
"stream_video_language": "",
|
||||
"stream_video_language_code": "",
|
||||
"stream_video_ref_frames": "4",
|
||||
"stream_video_resolution": "1080",
|
||||
"stream_video_scan_type": "progressive",
|
||||
"stream_video_width": "1920",
|
||||
"studio": "HBO",
|
||||
"subtitle_codec": "",
|
||||
@@ -555,17 +597,25 @@ Returns:
|
||||
"username": "LordCommanderSnow",
|
||||
"video_bit_depth": "8",
|
||||
"video_bitrate": "10233",
|
||||
"video_chroma_subsampling": "4:2:0",
|
||||
"video_codec": "h264",
|
||||
"video_codec_level": "41",
|
||||
"video_color_primaries": "",
|
||||
"video_color_range": "tv",
|
||||
"video_color_space": "bt709",
|
||||
"video_color_trc": ",
|
||||
"video_decision": "direct play",
|
||||
"video_dynamic_range": "SDR",
|
||||
"video_frame_rate": "23.976",
|
||||
"video_framerate": "24p",
|
||||
"video_full_resolution": "1080p",
|
||||
"video_height": "1078",
|
||||
"video_language": "",
|
||||
"video_language_code": "",
|
||||
"video_profile": "high",
|
||||
"video_ref_frames": "4",
|
||||
"video_resolution": "1080",
|
||||
"video_scan_type": "progressive",
|
||||
"video_width": "1920",
|
||||
"view_offset": "1000",
|
||||
"width": "1920",
|
||||
@@ -665,8 +715,9 @@ Optional parameters:
|
||||
grandparent_rating_key (int): 351
|
||||
start_date (str): "YYYY-MM-DD"
|
||||
section_id (int): 2
|
||||
media_type (str): "movie", "episode", "track"
|
||||
media_type (str): "movie", "episode", "track", "live"
|
||||
transcode_decision (str): "direct play", "copy", "transcode",
|
||||
guid (str): Plex guid for an item, e.g. "com.plexapp.agents.thetvdb://121361/6/1"
|
||||
order_column (str): "date", "friendly_name", "ip_address", "platform", "player",
|
||||
"full_title", "started", "paused_counter", "stopped", "duration"
|
||||
order_dir (str): "desc" or "asc"
|
||||
@@ -691,19 +742,23 @@ Returns:
|
||||
"original_title": "",
|
||||
"group_count": 1,
|
||||
"group_ids": "1124",
|
||||
"id": 1124,
|
||||
"guid": "com.plexapp.agents.thetvdb://121361/6/1?lang=en",
|
||||
"ip_address": "xxx.xxx.xxx.xxx",
|
||||
"live": 0,
|
||||
"media_index": 17,
|
||||
"media_type": "episode",
|
||||
"originally_available_at": "2016-04-24",
|
||||
"parent_media_index": 7,
|
||||
"parent_rating_key": 544,
|
||||
"parent_title": "",
|
||||
"paused_counter": 0,
|
||||
"percent_complete": 84,
|
||||
"platform": "Chrome",
|
||||
"player": "Plex Web (Chrome)",
|
||||
"platform": "Windows",
|
||||
"product": "Plex for Windows",
|
||||
"player": "Castle-PC",
|
||||
"rating_key": 4348,
|
||||
"reference_id": 1123,
|
||||
"row_id": 1124,
|
||||
"session_key": null,
|
||||
"started": 1462688107,
|
||||
"state": null,
|
||||
@@ -751,8 +806,10 @@ Returns:
|
||||
[{"content_rating": "TV-MA",
|
||||
"friendly_name": "",
|
||||
"grandparent_thumb": "/library/metadata/1219/thumb/1462175063",
|
||||
"guid": "com.plexapp.agents.thetvdb://121361/6/1?lang=en",
|
||||
"labels": [],
|
||||
"last_play": 1462380698,
|
||||
"live": 0,
|
||||
"media_type": "episode",
|
||||
"platform": "",
|
||||
"platform_type": "",
|
||||
@@ -813,6 +870,7 @@ Returns:
|
||||
[{"art": "/:/resources/show-fanart.jpg",
|
||||
"child_count": "3745",
|
||||
"count": "62",
|
||||
"is_active": 1,
|
||||
"parent_count": "240",
|
||||
"section_id": "2",
|
||||
"section_name": "TV Shows",
|
||||
@@ -833,6 +891,7 @@ Required parameters:
|
||||
None
|
||||
|
||||
Optional parameters:
|
||||
grouping (int): 0 or 1
|
||||
order_column (str): "library_thumb", "section_name", "section_type", "count", "parent_count",
|
||||
"child_count", "last_accessed", "last_played", "plays", "duration"
|
||||
order_dir (str): "desc" or "asc"
|
||||
@@ -852,23 +911,29 @@ Returns:
|
||||
"do_notify": "Checked",
|
||||
"do_notify_created": "Checked",
|
||||
"duration": 1578037,
|
||||
"id": 1128,
|
||||
"guid": "com.plexapp.agents.thetvdb://121361/6/1?lang=en",
|
||||
"histroy_row_id": 1128,
|
||||
"is_active": 1,
|
||||
"keep_history": "Checked",
|
||||
"labels": [],
|
||||
"last_accessed": 1462693216,
|
||||
"last_played": "Game of Thrones - The Red Woman",
|
||||
"library_art": "/:/resources/show-fanart.jpg",
|
||||
"library_thumb": "",
|
||||
"library_thumb": "/:/resources/show.png",
|
||||
"live": 0,
|
||||
"media_index": 1,
|
||||
"media_type": "episode",
|
||||
"originally_available_at": "2016-04-24",
|
||||
"parent_count": 240,
|
||||
"parent_media_index": 6,
|
||||
"parent_title": "",
|
||||
"plays": 772,
|
||||
"rating_key": 153037,
|
||||
"row_id": 1,
|
||||
"section_id": 2,
|
||||
"section_name": "TV Shows",
|
||||
"section_type": "Show",
|
||||
"server_id": "ds48g4r354a8v9byrrtr697g3g79w",
|
||||
"thumb": "/library/metadata/153036/thumb/1462175062",
|
||||
"year": 2016
|
||||
},
|
||||
@@ -896,13 +961,16 @@ Returns:
|
||||
"deleted_section": 0,
|
||||
"do_notify": 1,
|
||||
"do_notify_created": 1,
|
||||
"is_active": 1,
|
||||
"keep_history": 1,
|
||||
"library_art": "/:/resources/movie-fanart.jpg",
|
||||
"library_thumb": "/:/resources/movie.png",
|
||||
"parent_count": null,
|
||||
"row_id": 1,
|
||||
"section_id": 1,
|
||||
"section_name": "Movies",
|
||||
"section_type": "movie"
|
||||
"section_type": "movie",
|
||||
"server_id": "ds48g4r354a8v9byrrtr697g3g79w"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -950,6 +1018,7 @@ Returns:
|
||||
"rating_key": "1219",
|
||||
"section_id": 2,
|
||||
"section_type": "show",
|
||||
"sort_title": "Game of Thrones",
|
||||
"thumb": "/library/metadata/1219/thumb/1436265995",
|
||||
"title": "Game of Thrones",
|
||||
"video_codec": "",
|
||||
@@ -1017,10 +1086,11 @@ Get a library's watch time statistics.
|
||||
|
||||
```
|
||||
Required parameters:
|
||||
section_id (str): The id of the Plex library section
|
||||
section_id (str): The id of the Plex library section
|
||||
|
||||
Optional parameters:
|
||||
grouping (int): 0 or 1
|
||||
query_days (str): Comma separated days, e.g. "1,7,30,0"
|
||||
|
||||
Returns:
|
||||
json:
|
||||
@@ -1108,6 +1178,7 @@ Returns:
|
||||
"Drama",
|
||||
"Fantasy"
|
||||
],
|
||||
"grandparent_guid": "com.plexapp.agents.thetvdb://121361?lang=en",
|
||||
"grandparent_rating_key": "1219",
|
||||
"grandparent_thumb": "/library/metadata/1219/thumb/1462175063",
|
||||
"grandparent_title": "Game of Thrones",
|
||||
@@ -1115,6 +1186,7 @@ Returns:
|
||||
"labels": [],
|
||||
"last_viewed_at": "1462165717",
|
||||
"library_name": "TV Shows",
|
||||
"live": 0,
|
||||
"media_index": "1",
|
||||
"media_info": [
|
||||
{
|
||||
@@ -1124,6 +1196,9 @@ Returns:
|
||||
"audio_codec": "ac3",
|
||||
"audio_profile": "",
|
||||
"bitrate": "10617",
|
||||
"channel_call_sign": "",
|
||||
"channel_identifier": "",
|
||||
"channel_thumb": "",
|
||||
"container": "mkv",
|
||||
"height": "1078",
|
||||
"id": "257925",
|
||||
@@ -1142,12 +1217,17 @@ Returns:
|
||||
"video_bitrate": "10233",
|
||||
"video_codec": "h264",
|
||||
"video_codec_level": "41",
|
||||
"video_color_primaries": "",
|
||||
"video_color_range": "tv",
|
||||
"video_color_space": "bt709",
|
||||
"video_color_trc": "",
|
||||
"video_frame_rate": "23.976",
|
||||
"video_height": "1078",
|
||||
"video_language": "",
|
||||
"video_language_code": "",
|
||||
"video_profile": "high",
|
||||
"video_ref_frames": "4",
|
||||
"video_scan_type": "progressive",
|
||||
"video_width": "1920",
|
||||
"selected": 0
|
||||
},
|
||||
@@ -1182,6 +1262,7 @@ Returns:
|
||||
],
|
||||
"video_codec": "h264",
|
||||
"video_framerate": "24p",
|
||||
"video_full_resolution": "1080p",
|
||||
"video_profile": "high",
|
||||
"video_resolution": "1080",
|
||||
"width": "1920"
|
||||
@@ -1190,6 +1271,7 @@ Returns:
|
||||
"media_type": "episode",
|
||||
"original_title": "",
|
||||
"originally_available_at": "2016-04-24",
|
||||
"parent_guid": "com.plexapp.agents.thetvdb://121361/6?lang=en",
|
||||
"parent_media_index": "6",
|
||||
"parent_rating_key": "153036",
|
||||
"parent_thumb": "/library/metadata/153036/thumb/1462175062",
|
||||
@@ -1198,7 +1280,7 @@ Returns:
|
||||
"rating_image": "rottentomatoes://image.rating.ripe",
|
||||
"rating_key": "153037",
|
||||
"section_id": "2",
|
||||
"sort_title": "Game of Thrones",
|
||||
"sort_title": "Red Woman",
|
||||
"studio": "HBO",
|
||||
"summary": "Jon Snow is dead. Daenerys meets a strong man. Cersei sees her daughter again.",
|
||||
"tagline": "",
|
||||
@@ -1494,7 +1576,8 @@ Returns:
|
||||
"series":
|
||||
[{"name": "Movies", "data": [...]}
|
||||
{"name": "TV", "data": [...]},
|
||||
{"name": "Music", "data": [...]}
|
||||
{"name": "Music", "data": [...]},
|
||||
{"name": "Live TV", "data": [...]}
|
||||
]
|
||||
}
|
||||
```
|
||||
@@ -1520,7 +1603,8 @@ Returns:
|
||||
"series":
|
||||
[{"name": "Movies", "data": [...]}
|
||||
{"name": "TV", "data": [...]},
|
||||
{"name": "Music", "data": [...]}
|
||||
{"name": "Music", "data": [...]},
|
||||
{"name": "Live TV", "data": [...]}
|
||||
]
|
||||
}
|
||||
```
|
||||
@@ -1546,7 +1630,8 @@ Returns:
|
||||
"series":
|
||||
[{"name": "Movies", "data": [...]}
|
||||
{"name": "TV", "data": [...]},
|
||||
{"name": "Music", "data": [...]}
|
||||
{"name": "Music", "data": [...]},
|
||||
{"name": "Live TV", "data": [...]}
|
||||
]
|
||||
}
|
||||
```
|
||||
@@ -1650,7 +1735,8 @@ Returns:
|
||||
"series":
|
||||
[{"name": "Movies", "data": [...]}
|
||||
{"name": "TV", "data": [...]},
|
||||
{"name": "Music", "data": [...]}
|
||||
{"name": "Music", "data": [...]},
|
||||
{"name": "Live TV", "data": [...]}
|
||||
]
|
||||
}
|
||||
```
|
||||
@@ -1676,7 +1762,8 @@ Returns:
|
||||
"series":
|
||||
[{"name": "Movies", "data": [...]}
|
||||
{"name": "TV", "data": [...]},
|
||||
{"name": "Music", "data": [...]}
|
||||
{"name": "Music", "data": [...]},
|
||||
{"name": "Live TV", "data": [...]}
|
||||
]
|
||||
}
|
||||
```
|
||||
@@ -1702,7 +1789,8 @@ Returns:
|
||||
"series":
|
||||
[{"name": "Movies", "data": [...]}
|
||||
{"name": "TV", "data": [...]},
|
||||
{"name": "Music", "data": [...]}
|
||||
{"name": "Music", "data": [...]},
|
||||
{"name": "Live TV", "data": [...]}
|
||||
]
|
||||
}
|
||||
```
|
||||
@@ -1790,22 +1878,59 @@ Optional parameters:
|
||||
Returns:
|
||||
json:
|
||||
{"recently_added":
|
||||
[{"added_at": "1461572396",
|
||||
[{"actors": [
|
||||
"Kit Harington",
|
||||
"Emilia Clarke",
|
||||
"Isaac Hempstead-Wright",
|
||||
"Maisie Williams",
|
||||
"Liam Cunningham",
|
||||
],
|
||||
"added_at": "1461572396",
|
||||
"art": "/library/metadata/1219/art/1462175063",
|
||||
"audience_rating": "8",
|
||||
"audience_rating_image": "rottentomatoes://image.rating.upright",
|
||||
"banner": "/library/metadata/1219/banner/1462175063",
|
||||
"directors": [
|
||||
"Jeremy Podeswa"
|
||||
],
|
||||
"duration": "2998290",
|
||||
"full_title": "Game of Thrones - The Red Woman",
|
||||
"genres": [
|
||||
"Adventure",
|
||||
"Drama",
|
||||
"Fantasy"
|
||||
],
|
||||
"grandparent_rating_key": "1219",
|
||||
"grandparent_thumb": "/library/metadata/1219/thumb/1462175063",
|
||||
"grandparent_title": "Game of Thrones",
|
||||
"library_name": "",
|
||||
"guid": "com.plexapp.agents.thetvdb://121361/6/1?lang=en",
|
||||
"labels": [],
|
||||
"last_viewed_at": "1462165717",
|
||||
"library_name": "TV Shows",
|
||||
"media_index": "1",
|
||||
"media_type": "episode",
|
||||
"original_title": "",
|
||||
"originally_available_at": "2016-04-24",
|
||||
"parent_media_index": "6",
|
||||
"parent_rating_key": "153036",
|
||||
"parent_thumb": "/library/metadata/153036/thumb/1462175062",
|
||||
"parent_title": "",
|
||||
"rating": "7.8",
|
||||
"rating_image": "rottentomatoes://image.rating.ripe",
|
||||
"rating_key": "153037",
|
||||
"section_id": "2",
|
||||
"sort_title": "Red Woman",
|
||||
"studio": "HBO",
|
||||
"summary": "Jon Snow is dead. Daenerys meets a strong man. Cersei sees her daughter again.",
|
||||
"tagline": "",
|
||||
"thumb": "/library/metadata/153037/thumb/1462175060",
|
||||
"title": "The Red Woman",
|
||||
"user_rating": "9.0",
|
||||
"updated_at": "1462175060",
|
||||
"writers": [
|
||||
"David Benioff",
|
||||
"D. B. Weiss"
|
||||
],
|
||||
"year": "2016"
|
||||
},
|
||||
{...},
|
||||
@@ -1987,6 +2112,7 @@ Returns:
|
||||
"stream_video_bitrate": 527,
|
||||
"stream_video_codec": "h264",
|
||||
"stream_video_decision": "transcode",
|
||||
"stream_video_dynamic_range": "SDR",
|
||||
"stream_video_framerate": "24p",
|
||||
"stream_video_height": 306,
|
||||
"stream_video_resolution": "SD",
|
||||
@@ -2001,6 +2127,7 @@ Returns:
|
||||
"video_bitrate": 2500,
|
||||
"video_codec": "h264",
|
||||
"video_decision": "transcode",
|
||||
"video_dynamic_range": "SDR",
|
||||
"video_framerate": "24p",
|
||||
"video_height": 816,
|
||||
"video_resolution": "1080",
|
||||
@@ -2120,10 +2247,13 @@ Returns:
|
||||
"do_notify": 1,
|
||||
"email": "Jon.Snow.1337@CastleBlack.com",
|
||||
"friendly_name": "Jon Snow",
|
||||
"is_active": 1,
|
||||
"is_admin": 0,
|
||||
"is_allow_sync": 1,
|
||||
"is_home_user": 1,
|
||||
"is_restricted": 0,
|
||||
"keep_history": 1,
|
||||
"row_id": 1,
|
||||
"shared_libraries": ["10", "1", "4", "5", "15", "20", "2"],
|
||||
"user_id": 133788,
|
||||
"user_thumb": "https://plex.tv/users/k10w42309cynaopq/avatar",
|
||||
@@ -2154,12 +2284,15 @@ Returns:
|
||||
"recordsFiltered": 10,
|
||||
"data":
|
||||
[{"friendly_name": "Jon Snow",
|
||||
"guid": "com.plexapp.agents.thetvdb://121361/6/1?lang=en",
|
||||
"id": 1121,
|
||||
"ip_address": "xxx.xxx.xxx.xxx",
|
||||
"last_played": "Game of Thrones - The Red Woman",
|
||||
"last_seen": 1462591869,
|
||||
"live": 0,
|
||||
"media_index": 1,
|
||||
"media_type": "episode",
|
||||
"originally_available_at": "2016-04-24",
|
||||
"parent_media_index": 6,
|
||||
"parent_title": "",
|
||||
"platform": "Chrome",
|
||||
@@ -2273,6 +2406,7 @@ Required parameters:
|
||||
|
||||
Optional parameters:
|
||||
grouping (int): 0 or 1
|
||||
query_days (str): Comma separated days, e.g. "1,7,30,0"
|
||||
|
||||
Returns:
|
||||
json:
|
||||
@@ -2316,11 +2450,13 @@ Returns:
|
||||
"filter_music": "",
|
||||
"filter_photos": "",
|
||||
"filter_tv": "",
|
||||
"is_active": 1,
|
||||
"is_admin": 0,
|
||||
"is_allow_sync": 1,
|
||||
"is_home_user": 1,
|
||||
"is_restricted": 0,
|
||||
"keep_history": 1,
|
||||
"row_id": 1,
|
||||
"server_token": "PU9cMuQZxJKFBtGqHk68",
|
||||
"shared_libraries": "1;2;3",
|
||||
"thumb": "https://plex.tv/users/k10w42309cynaopq/avatar",
|
||||
@@ -2341,6 +2477,7 @@ Required parameters:
|
||||
None
|
||||
|
||||
Optional parameters:
|
||||
grouping (int): 0 or 1
|
||||
order_column (str): "user_thumb", "friendly_name", "last_seen", "ip_address", "platform",
|
||||
"player", "last_played", "plays", "duration"
|
||||
order_dir (str): "desc" or "asc"
|
||||
@@ -2358,19 +2495,24 @@ Returns:
|
||||
"do_notify": "Checked",
|
||||
"duration": 2998290,
|
||||
"friendly_name": "Jon Snow",
|
||||
"id": 1121,
|
||||
"guid": "com.plexapp.agents.thetvdb://121361/6/1?lang=en",
|
||||
"history_row_id": 1121,
|
||||
"ip_address": "xxx.xxx.xxx.xxx",
|
||||
"is_active": 1,
|
||||
"keep_history": "Checked",
|
||||
"last_played": "Game of Thrones - The Red Woman",
|
||||
"last_seen": 1462591869,
|
||||
"live": 0,
|
||||
"media_index": 1,
|
||||
"media_type": "episode",
|
||||
"originally_available_at": "2016-04-24",
|
||||
"parent_media_index": 6,
|
||||
"parent_title": "",
|
||||
"platform": "Chrome",
|
||||
"player": "Plex Web (Chrome)",
|
||||
"plays": 487,
|
||||
"rating_key": 153037,
|
||||
"row_id": 1,
|
||||
"thumb": "/library/metadata/153036/thumb/1462175062",
|
||||
"transcode_decision": "transcode",
|
||||
"user_id": 133788,
|
||||
@@ -2446,6 +2588,7 @@ Required parameters:
|
||||
body (str): The body of the message
|
||||
|
||||
Optional parameters:
|
||||
headers (str): The JSON headers for webhook notifications
|
||||
script_args (str): The arguments for script notifications
|
||||
|
||||
Returns:
|
||||
@@ -2502,10 +2645,10 @@ Optional parameters:
|
||||
width (str): 300
|
||||
height (str): 450
|
||||
opacity (str): 25
|
||||
background (str): 282828
|
||||
background (str): Hex color, e.g. 282828
|
||||
blur (str): 3
|
||||
img_format (str): png
|
||||
fallback (str): "poster", "cover", "art"
|
||||
fallback (str): "poster", "cover", "art", "poster-live", "art-live", "art-live-full"
|
||||
refresh (bool): True or False whether to refresh the image cache
|
||||
return_hash (bool): True or False to return the self-hosted image hash instead of the image
|
||||
|
||||
@@ -2631,7 +2774,7 @@ Returns:
|
||||
### sql
|
||||
Query the Tautulli database with raw SQL. Automatically makes a backup of
|
||||
the database if the latest backup is older then 24h. `api_sql` must be
|
||||
manually enabled in the config file.
|
||||
manually enabled in the config file while Tautulli is shut down.
|
||||
|
||||
```
|
||||
Required parameters:
|
||||
|
241
CHANGELOG.md
@@ -1,5 +1,244 @@
|
||||
# Changelog
|
||||
|
||||
## v2.2.2-beta (2020-04-12)
|
||||
|
||||
* Notifications:
|
||||
* New: Added notification trigger for Tautulli database corruption.
|
||||
* New: Added TAUTULLI_PYTHON_VERSION to script notification environment variables.
|
||||
* Fix: Notification grouping by season/album and show/artist not enabled by default.
|
||||
* Change: The file size notification parameter is now reported in SI units. (Thanks @aaronldunlap)
|
||||
* UI:
|
||||
* Fix: Delete lookup info from the media info page failing.
|
||||
* New: Added icon on the users table to indicate if the user is not on the Plex server.
|
||||
* New: Added icon on the libraries table to indicate if the library is not on the Plex server.
|
||||
* Fix: XBMC platform icon not being redirected to the Kodi platform icon.
|
||||
* Change: Improved deleting libraries so libraries with the same section ID are not also deleted.
|
||||
* API:
|
||||
* Fix: Returning XML for the API failing due to unicode characters.
|
||||
* Fix: Grouping parameter for various API commands not falling back to default setting.
|
||||
* New: Added time_queries parameter to get_library_watch_time_stats and get_user_watch_time_stats API command. (Thanks @KaasKop97)
|
||||
* New: Added an "is_active" return value to the get_user, get_users, get_library, and get_libraries API commands which indicates if the user or library is on the Plex server.
|
||||
* New: Added delete_history API command.
|
||||
* Change: Added optional parameter for row_ids for delete_library, delete_user, delete_all_library_history, and delete_all_user_history API commands.
|
||||
* Mobile App:
|
||||
* Fix: Temporary device token not being invalidated after cancelling device registration.
|
||||
* Other:
|
||||
* Fix: Update failing on CentOS due to an older git version.
|
||||
* Fix: Manifest file for creating a web app had incorrect info.
|
||||
* New: Docker images updated to support ARM platforms.
|
||||
|
||||
|
||||
## v2.2.1 (2020-03-28)
|
||||
|
||||
* Notifications:
|
||||
* Fix: File size notification parameter incorrectly truncated to an integer.
|
||||
* Fix: Notification grouping by season/album not enabled by default.
|
||||
* New: Added transcode decision counts to notification parameters.
|
||||
* Change: Tags (<>) are no longer stripped from from Webhook notification text.
|
||||
* Newsletter:
|
||||
* New: Added favicon to newsletter template when viewing as a web page.
|
||||
* UI:
|
||||
* Fix: Username missing from the Synced Items table.
|
||||
* Fix: Windows system tray icon not enabled by default.
|
||||
* Fix: Saving a mobile device with a blank friendly name caused an error.
|
||||
* New: Added IMDb and Rotten Tomato Ratings to info pages.
|
||||
* New: Added button in settings to delete all 3rd party metadata lookup info in the database.
|
||||
* New: Added button in settings to flush recently added items in the database.
|
||||
* API:
|
||||
* New: Added delete_recenly_added API command to flush recently added items.
|
||||
* Change: Updated delete_lookup_info API command parameters to allow deleteing all 3rd party metadata lookup info.
|
||||
|
||||
|
||||
## v2.2.0 (2020-03-08)
|
||||
|
||||
* Important Note!
|
||||
* All Live TV changes requires Plex Media Server 1.18.7 or higher.
|
||||
* Monitoring:
|
||||
* Fix: Improved IPv6 display on the activity cards. (Thanks @felixbuenemann)
|
||||
* New: Added Live TV metadata and posters to the activity cards.
|
||||
* Change: Show bandwidth in Gbps when greater than 1000 Mbps.
|
||||
* History:
|
||||
* New: Added history logging for Live TV sessions.
|
||||
* New: Added a fake "Live TV" library to collect Live TV history.
|
||||
* Note: This library will show up the first time that Live TV is played.
|
||||
* New: Added the ability to filter history by Live TV.
|
||||
* Graphs:
|
||||
* New: Added Live TV series to the "Plays by Period" and "Play Totals" graphs.
|
||||
* Change: Media type series on the graphs are only shown if the corresponding library type is present.
|
||||
* Notifications:
|
||||
* Fix: Race condition causing stream count to be incorrect for playback stop notifications.
|
||||
* New: Added Live TV channel notification parameters.
|
||||
* New: Added Plex background art notification parameter.
|
||||
* Note: This is the Plex API endpoint to retrieve the background art, not the actual image.
|
||||
* New: Added poster images for clip notifications.
|
||||
* Change: Default Webhook notification method to POST.
|
||||
* UI:
|
||||
* Fix: Windows platform showing up twice on the Most Active Platforms statistics card.
|
||||
* New: Added option to change the background art for library sections when editing a library.
|
||||
* New: Added button to reset Tautulli git installation in settings to fix failed git updates.
|
||||
* API:
|
||||
* New: Added ability to filter history using a "live" media type and by guid for the get_history API command.
|
||||
* New: Added cutsom_art parameter to the edit_library API command.
|
||||
* Other:
|
||||
* Change: Add crossorigin use-credentials attribute to manifest tags. (Thanks @pkoenig10)
|
||||
* Change: Disable automatic updates for Docker containers. Updates are now handled by updating the Docker container.
|
||||
* Note: If you are using an old Docker container created before v2.2.0, then you may need to completely remove and recreate the container to update for the first time.
|
||||
* Note: Use the ":latest" Docker tag for the newest stable release, or the ":beta" or ":nightly" tags to access the beta or nightly branches.
|
||||
|
||||
|
||||
## v2.1.44 (2020-02-05)
|
||||
|
||||
* Monitoring:
|
||||
* Fix: SDR source video being identified as HDR stream video.
|
||||
* Notifications:
|
||||
* Fix: Unable to select condition operator for video color parameters.
|
||||
* UI:
|
||||
* Fix: Capitalization for platforms on history tables.
|
||||
|
||||
|
||||
## v2.1.43 (2020-02-03)
|
||||
|
||||
* Monitoring:
|
||||
* New: Added HDR indicator on activity card.
|
||||
* New: Added dynamic range to history steam info modal.
|
||||
* Notifications:
|
||||
* Fix: Webhook notification body sent as incorrect data type when Content-Type header is overridden.
|
||||
* Fix: Telegram notification character limit incorrect for unicode characters.
|
||||
* New: Added color and dynamic range notification parameters.
|
||||
* Newsletters:
|
||||
* Fix: Episodes and Albums plural spelling on recently added newsletter section headers.
|
||||
* UI:
|
||||
* Fix: Windows and macOS platform capitalization.
|
||||
* Fix: Season number 0 not shown for episodes on history tables.
|
||||
* Other:
|
||||
* Change: Mask email addresses in logs.
|
||||
* Change: Update deprecated GitHub access token URL parameter to Authorization header.
|
||||
|
||||
|
||||
## v2.1.42 (2020-01-04)
|
||||
|
||||
* Other:
|
||||
* Fix: SSL certificate error when installing GeoLite2 database.
|
||||
* Change: Verify MaxMind license key and GeoLite2 database path before installing.
|
||||
* Change: Disable GeoLite2 database uninstall button when it is not installed.
|
||||
|
||||
|
||||
## v2.1.41 (2019-12-30)
|
||||
|
||||
* Other:
|
||||
* Fix: Failing to extract the GeoLite2 database on Windows.
|
||||
|
||||
|
||||
## v2.1.40 (2019-12-30)
|
||||
|
||||
* UI:
|
||||
* Change: Moved 3rd Party API settings to new tab in the settings.
|
||||
* Graphs:
|
||||
* Change: Improve calculating month ranges for Play Totals graphs.
|
||||
* Other:
|
||||
* Fix: Failing to verify a Plex Media Server using a hostname.
|
||||
* Change: A license key is now required to install the MaxMind GeoLite2 database for IP geolocation. Please follow the guide in the wiki to reinstall the GeoLite2 database.
|
||||
* Change: The GeoLite2 database will now automatically update periodically if installed.
|
||||
|
||||
|
||||
## v2.1.39 (2019-12-08)
|
||||
|
||||
* UI:
|
||||
* New: Added creating admin username and password to setup wizard.
|
||||
* API:
|
||||
* Change: Remove default notification subject and body for notify API command.
|
||||
* Other:
|
||||
* Change: Check for database corruption when making backup.
|
||||
|
||||
|
||||
## v2.1.38 (2019-11-17)
|
||||
|
||||
* Notifications:
|
||||
* New: Added custom JSON headers to the webhook notification agent.
|
||||
* UI:
|
||||
* Fix: Homepage recently watched card not showing grouped history.
|
||||
* Other:
|
||||
* New: Added GitHub sponsor donation option.
|
||||
* Change: Improve resolving hostnames.
|
||||
|
||||
|
||||
## v2.1.37 (2019-10-11)
|
||||
|
||||
* Notifications:
|
||||
* Fix: Last.fm URLs linking to artist page instead of the album page.
|
||||
* New: Added option for MusicBrainz lookup for music notifications. Option must be enabled under 3rd Party APIs in the settings.
|
||||
* New: Added MusicBrainz ID and MusicBrainz URL notification parameters.
|
||||
* Change: Automatically truncate Discord description summary to 2048 characters.
|
||||
|
||||
|
||||
## v2.1.36-beta (2019-10-05)
|
||||
|
||||
* Monitoring:
|
||||
* Fix: Activity card title not updating after pre-rolls or auto-play.
|
||||
* History:
|
||||
* Fix: Display correct interlaced or progressive video scan type on stream data modal.
|
||||
* Graphs:
|
||||
* New: Separate interlaced and progressive video scan type on source and stream resolution graphs.
|
||||
* API:
|
||||
* New: Added parent_guid and grandparent_guid to get_activity and get_metadata commands.
|
||||
|
||||
|
||||
## v2.1.35-beta (2019-09-24)
|
||||
|
||||
* Monitoring:
|
||||
* Fix: Audio shown as blank on activity cards when changing audio tracks during direct play.
|
||||
* Fix: Display correct interlaced or progressive video scan type on activity cards.
|
||||
* New: Added flag for Nvidia hardware decoding on activity cards.
|
||||
* Notifications:
|
||||
* Fix: Notification parameter prefix and suffix were not substituted correctly.
|
||||
* Fix: Release Date notification parameter was incorrectly casted to an integer instead of a string.
|
||||
* New: Added video scan type and full resolution notification parameters.
|
||||
* UI:
|
||||
* Fix: Movies with the same title but different year being grouped on the homepage stats cards.
|
||||
* API:
|
||||
* New: Added video scan type and full resolution values to get_activity command.
|
||||
* Other:
|
||||
* Fix: Tautulli logging out every time after saving settings and restarting.
|
||||
|
||||
|
||||
## v2.1.34 (2019-09-03)
|
||||
|
||||
* History:
|
||||
* New: Added Product column to history tables.
|
||||
* Notifications:
|
||||
* Fix: IMDB/TMDb/TVDB/TVmaze ID notification parameters showing blank values after lookup.
|
||||
* UI:
|
||||
* Fix: Libraries and Users tables did not respect the group history setting.
|
||||
* API:
|
||||
* Fix: Title field was not searchable in get_library_media_info command.
|
||||
* New: Added grouping option to get_libraries_table and get_users_table commands.
|
||||
* New: Added product value to get_history command.
|
||||
* Other:
|
||||
* Fix: Could not verify Plex Media Server with unpublished hostnames.
|
||||
* Change: Automatically logout all Tautulli instances when changing the admin password.
|
||||
|
||||
|
||||
## v2.1.33 (2019-07-27)
|
||||
|
||||
* Notifications:
|
||||
* Change: Mask notification agent password fields.
|
||||
* Change: Enable searching by email address in dropdown menu.
|
||||
* Other:
|
||||
* Fix: Version number being overwritten with "None" which prevented updating in some instances.
|
||||
* Change: Update Plex OAuth request headers.
|
||||
|
||||
|
||||
## v2.1.32 (2019-06-26)
|
||||
|
||||
* Newsletters:
|
||||
* Fix: Newsletter scheduler issue for QNAP devices using an invalid "local" timezone preventing Tautulli from starting.
|
||||
|
||||
|
||||
## v2.1.31 (2019-06-24)
|
||||
|
||||
* No additional changes from v2.1.31-beta.
|
||||
|
||||
|
||||
## v2.1.31-beta (2019-06-13)
|
||||
|
||||
* Monitoring:
|
||||
@@ -23,6 +262,7 @@
|
||||
|
||||
|
||||
## v2.1.29 (2019-05-11)
|
||||
|
||||
* No additional changes from v2.1.29-beta.
|
||||
|
||||
|
||||
@@ -167,6 +407,7 @@
|
||||
|
||||
|
||||
## v2.1.20 (2018-09-05)
|
||||
|
||||
* No additional changes from v2.1.20-beta.
|
||||
|
||||
|
||||
|
23
Dockerfile
Normal file
@@ -0,0 +1,23 @@
|
||||
FROM tautulli/tautulli-baseimage:latest
|
||||
|
||||
LABEL maintainer="Tautulli"
|
||||
|
||||
ARG BRANCH
|
||||
ARG COMMIT
|
||||
|
||||
ENV TAUTULLI_DOCKER=True
|
||||
ENV TZ=UTC
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN \
|
||||
echo ${BRANCH} > /app/branch.txt && \
|
||||
echo ${COMMIT} > /app/version.txt
|
||||
|
||||
COPY . /app
|
||||
|
||||
CMD [ "python", "Tautulli.py", "--datadir", "/config" ]
|
||||
|
||||
VOLUME /config /plex_logs
|
||||
EXPOSE 8181
|
||||
HEALTHCHECK --start-period=90s CMD curl -ILfSs http://localhost:8181/status > /dev/null || curl -ILfkSs https://localhost:8181/status > /dev/null || exit 1
|
@@ -1,41 +0,0 @@
|
||||
<!---
|
||||
Reporting Issues:
|
||||
* To ensure that a developer has enough information to work with please include all of the information below.
|
||||
Please provide as much detail as possible. Screenshots can be very useful to see the problem.
|
||||
* Use proper markdown syntax to structure your post (i.e. code/log in code blocks).
|
||||
See: https://help.github.com/articles/basic-writing-and-formatting-syntax/
|
||||
* Include a link to your **FULL** log file that has the error(not just a few lines!).
|
||||
Please use [Gist](http://gist.github.com) or [Pastebin](http://pastebin.com/).
|
||||
|
||||
Feature Requests:
|
||||
* Feature requests are handled on FeatHub: http://feathub.com/Tautulli/Tautulli
|
||||
* Do not post them on the GitHub issues tracker.
|
||||
-->
|
||||
|
||||
**Version:**
|
||||
|
||||
**Branch:**
|
||||
|
||||
**Commit hash:**
|
||||
|
||||
**Operating system:**
|
||||
|
||||
**Python version:**
|
||||
|
||||
**What you did?**
|
||||
|
||||
**What happened?**
|
||||
|
||||
**What you expected?**
|
||||
|
||||
**How can we reproduce your issue?**
|
||||
<!-- Provide a detailed step-by-step. -->
|
||||
|
||||
**What are your (relevant) settings?**
|
||||
|
||||
**Link to logs:**
|
||||
|
||||
|
||||
<!--
|
||||
Close your issue when it's solved! If you found the solution yourself please comment so that others benefit from it.
|
||||
-->
|
25
README.md
@@ -1,9 +1,5 @@
|
||||
# Tautulli
|
||||
|
||||
[](https://tautulli.com/discord)
|
||||
[](https://www.reddit.com/r/Tautulli/)
|
||||
[](https://forums.plex.tv/t/tautulli-monitor-your-plex-media-server/225242)
|
||||
|
||||
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).
|
||||
@@ -31,7 +27,21 @@ This project is based on code from [Headphones](https://github.com/rembo10/headp
|
||||
|
||||

|
||||
|
||||
## Installation and Support
|
||||
## Installation & Support
|
||||
|
||||
[](https://python.org/downloads/release/python-2717/)
|
||||
[](https://hub.docker.com/r/tautulli/tautulli)
|
||||
[](https://hub.docker.com/r/tautulli/tautulli)
|
||||
|
||||
| Status | Branch: `master` | Branch: `beta` | Branch: `nightly` |
|
||||
| --- | --- | --- | --- |
|
||||
| Release | [](https://github.com/Tautulli/Tautulli/releases/latest) <br> [](https://github.com/Tautulli/Tautulli/releases/latest) | [](https://github.com/Tautulli/Tautulli/releases) <br> [](https://github.com/Tautulli/Tautulli/commits/beta) | [](https://github.com/Tautulli/Tautulli/commits/nightly) <br> [](https://github.com/Tautulli/Tautulli/commits/nightly) |
|
||||
| Docker | [](https://hub.docker.com/r/tautulli/tautulli) <br> [](https://github.com/Tautulli/Tautulli/actions?query=workflow%3A"Publish+Docker"+branch%3Amaster) | [](https://hub.docker.com/r/tautulli/tautulli) <br> [](https://github.com/Tautulli/Tautulli/actions?query=workflow%3A"Publish+Docker"+branch%3Abeta) | [](https://hub.docker.com/r/tautulli/tautulli) <br> [](https://github.com/Tautulli/Tautulli/actions?query=workflow%3A"Publish+Docker"+branch%3Anightly) |
|
||||
|
||||
[](https://github.com/Tautulli/Tautulli-Wiki/wiki)
|
||||
[](https://tautulli.com/discord)
|
||||
[](https://www.reddit.com/r/Tautulli/)
|
||||
[](https://forums.plex.tv/t/tautulli-monitor-your-plex-media-server/225242)
|
||||
|
||||
* Read the [Installation Guides](https://github.com/Tautulli/Tautulli-Wiki/wiki/Installation) for instructions to install Tautulli.
|
||||
* The [Frequently Asked Questions](https://github.com/Tautulli/Tautulli-Wiki/wiki/Frequently-Asked-Questions) in the wiki can help you with common problems.
|
||||
@@ -39,10 +49,15 @@ This project is based on code from [Headphones](https://github.com/rembo10/headp
|
||||
|
||||
## Issues & Feature Requests
|
||||
|
||||
[](https://github.com/Tautulli/Tautulli-Issues)
|
||||
[](https://feathub.com/Tautulli/Tautulli)
|
||||
|
||||
* Please see the [Issues Repository](https://github.com/Tautulli/Tautulli-Issues).
|
||||
|
||||
## License
|
||||
|
||||
[](https://github.com/Tautulli/Tautulli/blob/master/LICENSE)
|
||||
|
||||
This is free software under the GPL v3 open source license. Feel free to do with it what you wish, but any modification must be open sourced. A copy of the license is included.
|
||||
|
||||
This software includes Highsoft software libraries which you may freely distribute for non-commercial use. Commerical users must licence this software, for more information visit https://shop.highsoft.com/faq/non-commercial#non-commercial-redistribution.
|
36
Tautulli.py
@@ -36,7 +36,7 @@ import time
|
||||
import tzlocal
|
||||
|
||||
import plexpy
|
||||
from plexpy import config, database, logger, webstart
|
||||
from plexpy import config, database, helpers, logger, webstart
|
||||
|
||||
|
||||
# Register signals, such as CTRL + C
|
||||
@@ -106,8 +106,8 @@ def main():
|
||||
plexpy.QUIET = True
|
||||
|
||||
# Do an intial setup of the logger.
|
||||
logger.initLogger(console=not plexpy.QUIET, log_dir=False,
|
||||
verbose=plexpy.VERBOSE)
|
||||
# Require verbose for pre-initilization to see critical errors
|
||||
logger.initLogger(console=not plexpy.QUIET, log_dir=False, verbose=True)
|
||||
|
||||
try:
|
||||
plexpy.SYS_TIMEZONE = tzlocal.get_localzone()
|
||||
@@ -117,7 +117,7 @@ def main():
|
||||
|
||||
plexpy.SYS_UTC_OFFSET = datetime.datetime.now(plexpy.SYS_TIMEZONE).strftime('%z')
|
||||
|
||||
if os.getenv('TAUTULLI_DOCKER', False) == 'True':
|
||||
if helpers.bool_true(os.getenv('TAUTULLI_DOCKER', False)):
|
||||
plexpy.DOCKER = True
|
||||
|
||||
if args.dev:
|
||||
@@ -234,31 +234,19 @@ def main():
|
||||
plexpy.CONFIG.ENABLE_HTTPS = False
|
||||
|
||||
# Try to start the server. Will exit here is address is already in use.
|
||||
web_config = {
|
||||
'http_port': plexpy.HTTP_PORT,
|
||||
'http_host': plexpy.CONFIG.HTTP_HOST,
|
||||
'http_root': plexpy.CONFIG.HTTP_ROOT,
|
||||
'http_environment': plexpy.CONFIG.HTTP_ENVIRONMENT,
|
||||
'http_proxy': plexpy.CONFIG.HTTP_PROXY,
|
||||
'enable_https': plexpy.CONFIG.ENABLE_HTTPS,
|
||||
'https_cert': plexpy.CONFIG.HTTPS_CERT,
|
||||
'https_cert_chain': plexpy.CONFIG.HTTPS_CERT_CHAIN,
|
||||
'https_key': plexpy.CONFIG.HTTPS_KEY,
|
||||
'http_username': plexpy.CONFIG.HTTP_USERNAME,
|
||||
'http_password': plexpy.CONFIG.HTTP_PASSWORD,
|
||||
'http_basic_auth': plexpy.CONFIG.HTTP_BASIC_AUTH
|
||||
}
|
||||
webstart.initialize(web_config)
|
||||
webstart.start()
|
||||
|
||||
# Windows system tray icon
|
||||
if os.name == 'nt' and plexpy.CONFIG.WIN_SYS_TRAY:
|
||||
plexpy.win_system_tray()
|
||||
|
||||
logger.info("Tautulli is ready!")
|
||||
|
||||
# Open webbrowser
|
||||
if plexpy.CONFIG.LAUNCH_BROWSER and not args.nolaunch and not plexpy.DEV:
|
||||
plexpy.launch_browser(plexpy.CONFIG.HTTP_HOST, plexpy.HTTP_PORT,
|
||||
plexpy.HTTP_ROOT)
|
||||
|
||||
# Windows system tray icon
|
||||
if os.name == 'nt' and plexpy.CONFIG.WIN_SYS_TRAY:
|
||||
plexpy.win_system_tray()
|
||||
|
||||
# Wait endlessy for a signal to happen
|
||||
while True:
|
||||
if not plexpy.SIGNAL:
|
||||
@@ -275,6 +263,8 @@ def main():
|
||||
plexpy.shutdown(restart=True)
|
||||
elif plexpy.SIGNAL == 'checkout':
|
||||
plexpy.shutdown(restart=True, checkout=True)
|
||||
elif plexpy.SIGNAL == 'reset':
|
||||
plexpy.shutdown(restart=True, reset=True)
|
||||
else:
|
||||
plexpy.shutdown(restart=True, update=True)
|
||||
|
||||
|
@@ -28,7 +28,7 @@
|
||||
|
||||
<!-- ICONS -->
|
||||
<!-- Android -->
|
||||
<link rel="manifest" href="${http_root}images/favicon/manifest.json?v=2.0.5">
|
||||
<link rel="manifest" href="${http_root}images/favicon/manifest.json?v=2.0.5" crossorigin="use-credentials">
|
||||
<meta name="theme-color" content="#282a2d">
|
||||
<!-- Apple -->
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="${http_root}images/favicon/apple-touch-icon.png?v=2.0.5">
|
||||
@@ -43,23 +43,23 @@
|
||||
<div class="container">
|
||||
<div id="ajaxMsg" class="ajaxMsg"></div>
|
||||
% if _session['user_group'] == 'admin':
|
||||
% if plexpy.CONFIG.CHECK_GITHUB and plexpy.UPDATE_AVAILABLE is None:
|
||||
% if plexpy.CONFIG.CHECK_GITHUB and plexpy.UPDATE_AVAILABLE is not False:
|
||||
<div id="updatebar" style="display: none;">
|
||||
% if plexpy.UPDATE_AVAILABLE is None:
|
||||
You are running an unknown version of Tautulli.<br />
|
||||
<a href="update">Update</a> or <a href="#" id="updateDismiss">Dismiss</a>
|
||||
</div>
|
||||
% elif plexpy.CONFIG.CHECK_GITHUB and plexpy.UPDATE_AVAILABLE == 'release':
|
||||
<div id="updatebar" style="display: none;">
|
||||
% elif plexpy.UPDATE_AVAILABLE == 'release':
|
||||
A <a href="${anon_url('https://github.com/%s/%s/releases/tag/%s' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO, plexpy.LATEST_RELEASE))}" target="_blank">
|
||||
new release (${plexpy.LATEST_RELEASE})</a> of Tautulli is available!<br />
|
||||
<a href="update">Update</a> or <a href="#" id="updateDismiss">Dismiss</a>
|
||||
</div>
|
||||
% elif plexpy.CONFIG.CHECK_GITHUB and plexpy.UPDATE_AVAILABLE == 'commit':
|
||||
<div id="updatebar" style="display: none;">
|
||||
% elif plexpy.UPDATE_AVAILABLE == 'commit':
|
||||
A <a href="${anon_url('https://github.com/%s/%s/compare/%s...%s' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO, plexpy.CURRENT_VERSION, plexpy.LATEST_VERSION))}" target="_blank">
|
||||
newer version</a> of Tautulli is available!<br />
|
||||
You are ${plexpy.COMMITS_BEHIND} commit${'s' if plexpy.COMMITS_BEHIND > 1 else ''} behind.<br />
|
||||
% endif
|
||||
% if plexpy.DOCKER:
|
||||
Update your Docker container or <a href="#" id="updateDismiss">Dismiss</a>
|
||||
% else:
|
||||
<a href="update">Update</a> or <a href="#" id="updateDismiss">Dismiss</a>
|
||||
% endif
|
||||
</div>
|
||||
% else:
|
||||
<div id="updatebar" style="display: none;"></div>
|
||||
@@ -229,6 +229,7 @@ ${next.modalIncludes()}
|
||||
</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="#patreon-donation" role="tab" data-toggle="tab">Patreon</a></li>
|
||||
<li><a href="#github-donation" role="tab" data-toggle="tab">GitHub</a></li>
|
||||
<li><a href="#paypal-donation" role="tab" data-toggle="tab">PayPal</a></li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
@@ -240,6 +241,14 @@ ${next.modalIncludes()}
|
||||
<img src="images/become_a_patron_button.png" alt="Become a Patron" height="40">
|
||||
</a>
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane" id="github-donation" style="text-align: center">
|
||||
<p>
|
||||
Click the button below to continue to GitHub.
|
||||
</p>
|
||||
<a href="${anon_url('https://github.com/sponsors/JonnyWong16')}" target="_blank" class="btn btn-sm btn-default" style="font-weight: 600;">
|
||||
<i class="fa fa-heart fa-sm" style="color: #ea4aaa;"></i> Sponsor
|
||||
</a>
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane" id="paypal-donation" style="text-align: center">
|
||||
<p>
|
||||
Click the button below to continue to PayPal.
|
||||
@@ -282,6 +291,7 @@ ${next.modalIncludes()}
|
||||
<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/platform.min.js"></script>
|
||||
<script src="${http_root}js/ipaddr.min.js"></script>
|
||||
<script src="${http_root}js/script.js${cache_param}"></script>
|
||||
<script src="${http_root}js/jquery.tripleclick.min.js"></script>
|
||||
% if _session['user_group'] == 'admin' and BROWSER_NOTIFIERS:
|
||||
@@ -309,21 +319,23 @@ ${next.modalIncludes()}
|
||||
complete: function (xhr, status) {
|
||||
var result = $.parseJSON(xhr.responseText);
|
||||
var msg = '';
|
||||
if (result.update === null) {
|
||||
msg = 'You are running an unknown version of Tautulli.<br />' +
|
||||
'<a href="update">Update</a> or <a href="#" id="updateDismiss">Dismiss</a>';
|
||||
$('#updatebar').html(msg).fadeIn();
|
||||
} else if (result.update === true && result.release === true) {
|
||||
msg = 'A <a href="' + result.release_url + '" target="_blank">new release (' + result.latest_release + ')</a> of Tautulli is available!<br />' +
|
||||
'<a href="update">Update</a> or <a href="#" id="updateDismiss">Dismiss</a>';
|
||||
$('#updatebar').html(msg).fadeIn();
|
||||
} else if (result.update === true && result.release === false) {
|
||||
msg = 'A <a href="' + result.compare_url + '" target="_blank">newer version</a> of Tautulli is available!<br />' +
|
||||
'You are '+ result.commits_behind + ' commit' + (result.commits_behind > 1 ? 's' : '') + ' behind.<br />' +
|
||||
'<a href="update">Update</a> or <a href="#" id="updateDismiss">Dismiss</a>';
|
||||
$('#updatebar').html(msg).fadeIn();
|
||||
} else if (result.update === false) {
|
||||
if (result.update === false) {
|
||||
showMsg('<i class="fa fa-check"></i> ' + result.message, false, true, 2000);
|
||||
} else {
|
||||
if (result.update === null) {
|
||||
msg = 'You are running an unknown version of Tautulli.<br />';
|
||||
} else if (result.update === true && result.release === true) {
|
||||
msg = 'A <a href="' + result.release_url + '" target="_blank">new release (' + result.latest_release + ')</a> of Tautulli is available!<br />';
|
||||
} else if (result.update === true && result.release === false) {
|
||||
msg = 'A <a href="' + result.compare_url + '" target="_blank">newer version</a> of Tautulli is available!<br />' +
|
||||
'You are '+ result.commits_behind + ' commit' + (result.commits_behind > 1 ? 's' : '') + ' behind.<br />';
|
||||
}
|
||||
if (result.docker) {
|
||||
msg += 'Update your Docker container or <a href="#" id="updateDismiss">Dismiss</a>';
|
||||
} else {
|
||||
msg += '<a href="update">Update</a> or <a href="#" id="updateDismiss">Dismiss</a>';
|
||||
}
|
||||
$('#updatebar').html(msg).fadeIn();
|
||||
}
|
||||
|
||||
if (_callback) {
|
||||
|
@@ -53,14 +53,6 @@ DOCUMENTATION :: END
|
||||
<td>Newsletter Directory:</td>
|
||||
<td>${plexpy.CONFIG.NEWSLETTER_DIR}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>GeoLite2 Database:</td>
|
||||
% if plexpy.CONFIG.GEOIP_DB:
|
||||
<td>${plexpy.CONFIG.GEOIP_DB} | <a class="no-highlight" href="#" id="reinstall_geoip_db">Reinstall / Update</a> | <a class="no-highlight" href="#" id="uninstall_geoip_db">Uninstall</a></td>
|
||||
% else:
|
||||
<td><a class="no-highlight" href="#" id="install_geoip_db">Click here to install the GeoLite2 database.</a></td>
|
||||
% endif
|
||||
</tr>
|
||||
% if plexpy.ARGS:
|
||||
<tr>
|
||||
<td>Arguments:</td>
|
||||
@@ -69,7 +61,7 @@ DOCUMENTATION :: END
|
||||
% endif
|
||||
<tr>
|
||||
<td>Platform:</td>
|
||||
<td>${common.PLATFORM} ${common.PLATFORM_RELEASE} (${common.PLATFORM_VERSION + (' - {}'.format(common.PLATFORM_LINUX_DISTRO) if common.PLATFORM_LINUX_DISTRO else '')})</td>
|
||||
<td>${'[Docker] ' if plexpy.DOCKER else ''}${common.PLATFORM} ${common.PLATFORM_RELEASE} (${common.PLATFORM_VERSION + (' - {}'.format(common.PLATFORM_LINUX_DISTRO) if common.PLATFORM_LINUX_DISTRO else '')})</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>System Timezone:</td>
|
||||
@@ -102,22 +94,6 @@ DOCUMENTATION :: END
|
||||
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$("#install_geoip_db, #reinstall_geoip_db").click(function () {
|
||||
var msg = 'Are you sure you want to install the GeoLite2 database?<br /><br />' +
|
||||
'The database is used to lookup IP address geolocation info.<br />' +
|
||||
'The database will be downloaded from <a href="${anon_url("https://dev.maxmind.com/geoip/geoip2/geolite2/")}" target="_blank">MaxMind</a>, <br />' +
|
||||
'and requires <strong>100MB</strong> of free space to install in your Tautulli directory.<br />'
|
||||
var url = 'install_geoip_db';
|
||||
confirmAjaxCall(url, msg, null, 'Installing GeoLite2 database.', getConfigurationTable);
|
||||
});
|
||||
|
||||
$("#uninstall_geoip_db").click(function () {
|
||||
var msg = 'Are you sure you want to uninstall the GeoLite2 database?<br /><br />' +
|
||||
'You will not be able to lookup IP address geolocation info.';
|
||||
var url = 'uninstall_geoip_db';
|
||||
confirmAjaxCall(url, msg, null, 'Uninstalling GeoLite2 database.', getConfigurationTable);
|
||||
});
|
||||
|
||||
$('.guidelines-modal-link').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
$('#guidelines-type').text($(this).data('id'))
|
||||
|
@@ -21,7 +21,7 @@ ul.ColVis_collection li {
|
||||
|
||||
.ColVis_Button:hover,
|
||||
ul.ColVis_collection li:hover {
|
||||
color: #F9AA03;
|
||||
color: #E5A00D;
|
||||
}
|
||||
|
||||
button.ColVis_Button {
|
||||
|
@@ -101,7 +101,7 @@ table.display tr:hover a {
|
||||
text-decoration:none;
|
||||
}
|
||||
table.display td:hover a {
|
||||
color: #F9AA03;
|
||||
color: #E5A00D;
|
||||
}
|
||||
table.display thead tr:hover {
|
||||
background-color: #212121;
|
||||
|
@@ -523,7 +523,7 @@ fieldset[disabled] .btn-bright.active {
|
||||
color: #eee;
|
||||
}
|
||||
.modal-body i {
|
||||
color: #F9AA03;
|
||||
color: #E5A00D;
|
||||
}
|
||||
.modal-body i.fa {
|
||||
color: #fff;
|
||||
@@ -534,7 +534,7 @@ fieldset[disabled] .btn-bright.active {
|
||||
}
|
||||
.modal-body strong,
|
||||
.modal-body strong i.fa {
|
||||
color: #F9AA03;
|
||||
color: #E5A00D;
|
||||
}
|
||||
.modal-footer {
|
||||
padding: 15px 20px;
|
||||
@@ -673,7 +673,7 @@ textarea.form-control:focus {
|
||||
color: #fff;
|
||||
}
|
||||
.form-control-feedback {
|
||||
color: #F9AA03;
|
||||
color: #E5A00D;
|
||||
margin: 5px 40px 5px 0;
|
||||
}
|
||||
.form-control[disabled],
|
||||
@@ -711,7 +711,6 @@ fieldset[disabled] .form-control {
|
||||
box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1);
|
||||
}
|
||||
.users-poster-face {
|
||||
overflow: hidden;
|
||||
float: left;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
@@ -857,7 +856,6 @@ a .users-poster-face:hover {
|
||||
z-index: 2;
|
||||
}
|
||||
.dashboard-activity-info-platform {
|
||||
padding: 6px !important;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
width: 50px;
|
||||
@@ -973,7 +971,7 @@ a .users-poster-face:hover {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 140px;
|
||||
max-width: 125px;
|
||||
}
|
||||
.dashboard-activity-info-time {
|
||||
position: absolute;
|
||||
@@ -1036,13 +1034,13 @@ a .users-poster-face:hover {
|
||||
}
|
||||
.dashboard-activity-container:hover .progress-bar {
|
||||
color: rgba(255, 255, 255, 1);
|
||||
background-image: -webkit-linear-gradient(left,rgba(0,0,0,0.25),0%,rgba(0,0,0,0),50px);
|
||||
background-image: -webkit-linear-gradient(left,rgba(0,0,0,0.25) 0%,rgba(0,0,0,0) 50px);
|
||||
background-image: -moz-linear-gradient(left,rgba(0,0,0,0.25) 0%,rgba(0,0,0,0) 50px);
|
||||
background-image: linear-gradient(to left,rgba(0,0,0,0.25) 0%,rgba(0,0,0,0) 50px);
|
||||
}
|
||||
.dashboard-activity-container:hover .buffer-bar {
|
||||
color: rgba(255, 255, 255, 1);
|
||||
background-image: -webkit-linear-gradient(left,rgba(0,0,0,0.25),0%,rgba(0,0,0,0),50px);
|
||||
background-image: -webkit-linear-gradient(left,rgba(0,0,0,0.25) 0%,rgba(0,0,0,0) 50px);
|
||||
background-image: -moz-linear-gradient(left,rgba(0,0,0,0.25) 0%,rgba(0,0,0,0) 50px);
|
||||
background-image: linear-gradient(to left,rgba(0,0,0,0.25) 0%,rgba(0,0,0,0) 50px);
|
||||
}
|
||||
@@ -1742,7 +1740,7 @@ a:hover .dashboard-recent-media-cover {
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
background-image: -webkit-gradient(linear,left 0,left 100%,from(rgba(0,0,0,.7)),to(rgba(0,0,0,.9)));
|
||||
background-image: -webkit-linear-gradient(top,rgba(0,0,0,.7),0,rgba(0,0,0,.9),100%);
|
||||
background-image: -webkit-linear-gradient(top,rgba(0,0,0,.7) 0,rgba(0,0,0,.9) 100%);
|
||||
background-image: -moz-linear-gradient(top,rgba(0,0,0,.7) 0,rgba(0,0,0,.9) 100%);
|
||||
background-image: linear-gradient(to bottom,rgba(0,0,0,.7) 0,rgba(0,0,0,.9) 100%);
|
||||
background-repeat: repeat-x;
|
||||
@@ -1921,6 +1919,16 @@ a:hover .summary-poster-face-track .summary-poster-face-overlay span {
|
||||
margin-left: 2px;
|
||||
color: #999;
|
||||
}
|
||||
.critic-rating {
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
margin-top: 2px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
float: right;
|
||||
}
|
||||
.children-list,
|
||||
.search-results-list {
|
||||
position: relative;
|
||||
@@ -2177,7 +2185,7 @@ li.advanced-setting {
|
||||
font-size: 24px;
|
||||
color: #fff;
|
||||
padding-top: 27px;
|
||||
padding-left: 110px;
|
||||
padding-left: 105px;
|
||||
}
|
||||
.user-info-nav {
|
||||
margin-top: 15px;
|
||||
@@ -3102,12 +3110,28 @@ div.dataTables_info {
|
||||
border-left-color: #fff;
|
||||
}
|
||||
.tooltip-inner {
|
||||
max-width: 250px;
|
||||
color: #000;
|
||||
background: #fff;
|
||||
border: 0;
|
||||
font-weight: bold;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.inactive-library-tooltip,
|
||||
.inactive-user-tooltip {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.inactive-library-tooltip i.fa,
|
||||
.inactive-user-tooltip i.fa {
|
||||
color: #E5A00D;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
text-shadow: 0 0 2px rgba(0,0,0,.5);
|
||||
}
|
||||
.history-thumbnail-popover {
|
||||
z-index: 2000;
|
||||
padding: 0;
|
||||
@@ -3134,6 +3158,37 @@ div.dataTables_info {
|
||||
-moz-box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1);
|
||||
box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1);
|
||||
}
|
||||
.channel-thumbnail-popover {
|
||||
z-index: 2000;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
.channel-thumbnail-popover.popover.left {
|
||||
margin-left: -15px;
|
||||
}
|
||||
.channel-thumbnail-popover.popover.right {
|
||||
margin-left: 15px;
|
||||
}
|
||||
.channel-thumbnail-popover .popover-content {
|
||||
color: #000;
|
||||
padding: 0;
|
||||
}
|
||||
.channel-thumbnail {
|
||||
background-color: #868b8b;
|
||||
background-position: center;
|
||||
background-size: contain;
|
||||
background-origin: content-box;
|
||||
background-repeat: no-repeat;
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
padding: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.channel-thumbnail-popover .arrow:after {
|
||||
border-right-color: #868b8b !important;
|
||||
}
|
||||
.edit-user-toggles,
|
||||
.edit-library-toggles {
|
||||
padding-right: 10px;
|
||||
@@ -3766,9 +3821,8 @@ a:hover .overlay-refresh-image:hover {
|
||||
}
|
||||
|
||||
.svg-icon {
|
||||
padding: 10px;
|
||||
background-size: calc(100% - 20px) calc(100% - 20px) !important;
|
||||
background-origin: content-box !important;
|
||||
background-size: contain !important;
|
||||
background-repeat: no-repeat !important;
|
||||
background-position: center !important;
|
||||
}
|
||||
@@ -3878,7 +3932,7 @@ a:hover .overlay-refresh-image:hover {
|
||||
}
|
||||
.platform-xbmc {
|
||||
background-color: #3b4872;
|
||||
background-image: url(../images/platforms/xbmc.svg);
|
||||
background-image: url(../images/platforms/kodi.svg);
|
||||
}
|
||||
.platform-xbox {
|
||||
background-color: #107c10;
|
||||
@@ -3983,10 +4037,39 @@ a:hover .overlay-refresh-image:hover {
|
||||
.library-video {
|
||||
background-image: url(../images/libraries/video.svg);
|
||||
}
|
||||
.library-live {
|
||||
background-image: url(../images/libraries/live.svg);
|
||||
}
|
||||
.stats-most_concurrent {
|
||||
background-image: url(../images/icons/most-concurrent-streams.svg);
|
||||
}
|
||||
|
||||
.rating-image {
|
||||
width: 51px;
|
||||
height: 20px;
|
||||
margin-left: 10px;
|
||||
display: inline-block;
|
||||
background-origin: content-box !important;
|
||||
background-size: contain !important;
|
||||
background-repeat: no-repeat !important;
|
||||
background-position: left !important;
|
||||
text-align: right;
|
||||
}
|
||||
.rating-imdb {
|
||||
width: 62px !important;
|
||||
background-image: url(../images/rating/imdb.svg);
|
||||
}
|
||||
.rating-rottentomatos-ripe {
|
||||
background-image: url(../images/rating/tomato-ripe.svg);
|
||||
}
|
||||
.rating-rottentomatos-rotten {
|
||||
background-image: url(../images/rating/tomato-rotten.svg);
|
||||
}
|
||||
.rating-rottentomatos-upright {
|
||||
background-image: url(../images/rating/popcorn-upright.svg);
|
||||
}
|
||||
.rating-rottentomatos-spilled {
|
||||
background-image: url(../images/rating/popcorn-spilled.svg);
|
||||
}
|
||||
.transparent {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
@@ -4033,7 +4116,7 @@ a:hover .overlay-refresh-image:hover {
|
||||
table-layout: fixed;
|
||||
}
|
||||
.stream-info .heading {
|
||||
color: #F9AA03;
|
||||
color: #E5A00D;
|
||||
text-transform: uppercase;
|
||||
font-size: 15px;
|
||||
font-weight: bold !important;
|
||||
|
@@ -62,8 +62,7 @@ DOCUMENTATION :: END
|
||||
% if session is not None:
|
||||
<%
|
||||
from collections import defaultdict
|
||||
from urllib import quote
|
||||
from plexpy import helpers
|
||||
from plexpy.helpers import cast_to_int, page
|
||||
from plexpy.common import VIDEO_RESOLUTION_OVERRIDES, AUDIO_CODEC_OVERRIDES, EXTRA_TYPES
|
||||
import plexpy
|
||||
%>
|
||||
@@ -71,62 +70,67 @@ DOCUMENTATION :: END
|
||||
data = defaultdict(lambda: 'Unknown', **session)
|
||||
sk = data['session_key']
|
||||
|
||||
href = 'info?rating_key={}'.format(data['rating_key']) if data['rating_key'] else '#'
|
||||
parent_href = 'info?rating_key={}'.format(data['parent_rating_key']) if data['parent_rating_key'] else '#'
|
||||
grandparent_href = 'info?rating_key={}'.format(data['grandparent_rating_key']) if data['grandparent_rating_key'] else '#'
|
||||
user_href = 'user?user_id={}'.format(data['user_id']) if data['user_id'] else '#'
|
||||
href = page('info', data['rating_key'])
|
||||
parent_href = page('info', data['parent_rating_key'])
|
||||
grandparent_href = page('info', data['grandparent_rating_key'])
|
||||
user_href = page('user', data['user_id']) if data['user_id'] else '#'
|
||||
%>
|
||||
<div class="dashboard-activity-instance" id="activity-instance-${sk}" data-key="${sk}" data-id="${data['session_id']}"
|
||||
data-rating_key="${data['rating_key']}" data-parent_rating_key="${data['parent_rating_key']}" data-grandparent_rating_key="${data['grandparent_rating_key']}">
|
||||
data-rating_key="${data['rating_key']}" data-parent_rating_key="${data['parent_rating_key']}" data-grandparent_rating_key="${data['grandparent_rating_key']}"
|
||||
data-guid="${data['guid']}">
|
||||
<div class="dashboard-activity-container">
|
||||
<%
|
||||
if data['live'] == 1:
|
||||
background_url = 'images/art-live.png'
|
||||
if data['live']:
|
||||
background_url = page('pms_image_proxy', data['art'], data['rating_key'], 500, 280, 40, '282828', 3, fallback='art-live', refresh=True)
|
||||
elif data['channel_stream'] == 0:
|
||||
background_url = 'pms_image_proxy?img=' + data['art'] + '&width=500&height=280&opacity=40&background=282828&blur=3&fallback=art&refresh=true'
|
||||
background_url = page('pms_image_proxy', data['art'], data['rating_key'], 500, 280, 40, '282828', 3, fallback='art', refresh=True)
|
||||
else:
|
||||
if (data['art'] and data['art'].startswith('http')) or (data['thumb'] and data['thumb'].startswith('http')):
|
||||
background_url = data['art']
|
||||
else:
|
||||
background_url = 'pms_image_proxy?img=' + quote(data['art'] or data['thumb']) + '&width=500&height=280&fallback=art&refresh=true&clip=true'
|
||||
background_url = page('pms_image_proxy', data['art'] or data['thumb'], data['rating_key'], 500, 280, 40, '282828', 3, fallback='art', refresh=True, clip=True)
|
||||
%>
|
||||
<div id="background-${sk}" class="dashboard-activity-background" style="background-image: url(${background_url});">
|
||||
<div class="dashboard-activity-poster-container hidden-xs">
|
||||
% if data['media_type'] == 'track':
|
||||
<div id="poster-${sk}-bg" class="dashboard-activity-poster" style="background-image: url(pms_image_proxy?img=${data['parent_thumb']}&width=300&height=300&opacity=60&background=282828&blur=3&fallback=cover&refresh=true);"></div>
|
||||
<div id="poster-${sk}-bg" class="dashboard-activity-poster" style="background-image: url(${page('pms_image_proxy', data['parent_thumb'], data['parent_rating_key'], 300, 300, 60, '282828', 3, fallback='cover', refresh=True)});"></div>
|
||||
% endif
|
||||
% if data['live'] == 1:
|
||||
<div id="poster-${sk}" class="dashboard-activity-poster" style="background-image: url(images/poster-live.png);"></div>
|
||||
% if data['live']:
|
||||
% if data['media_type'] == 'movie':
|
||||
<a id="poster-url-${sk}" href="${href}" title="${data['title']}">
|
||||
<div id="poster-${sk}" class="dashboard-activity-poster" style="background-image: url(${page('pms_image_proxy', data['thumb'], data['rating_key'], 300, 450, fallback='poster-live', refresh=True)});"></div>
|
||||
</a>
|
||||
% elif data['media_type'] == 'episode':
|
||||
<a id="poster-url-${sk}" href="${href}" title="${data['grandparent_title']}">
|
||||
<div id="poster-${sk}" class="dashboard-activity-poster" style="background-image: url(${page('pms_image_proxy', data['grandparent_thumb'], data['rating_key'], 300, 450, fallback='poster-live', refresh=True)});"></div>
|
||||
</a>
|
||||
% else:
|
||||
<div id="poster-${sk}" class="dashboard-activity-poster" style="background-image: url(${page('pms_image_proxy', data['grandparent_thumb'] or data['thumb'], data['rating_key'], 300, 450, fallback='poster-live', refresh=True)});"></div>
|
||||
% endif
|
||||
% elif data['channel_stream'] == 0:
|
||||
% if data['media_type'] == 'movie':
|
||||
<a id="poster-url-${sk}" href="${href}" title="${data['title']}">
|
||||
<div id="poster-${sk}" class="dashboard-activity-poster" style="background-image: url(pms_image_proxy?img=${data['thumb']}&width=300&height=450&fallback=poster&refresh=true);"></div>
|
||||
<div id="poster-${sk}" class="dashboard-activity-poster" style="background-image: url(${page('pms_image_proxy', data['thumb'], data['rating_key'], 300, 450, fallback='poster', refresh=True)});"></div>
|
||||
</a>
|
||||
% elif data['media_type'] == 'episode':
|
||||
<a id="poster-url-${sk}" href="${grandparent_href}" title="${data['grandparent_title']}">
|
||||
<div id="poster-${sk}" class="dashboard-activity-poster" style="background-image: url(pms_image_proxy?img=${data['grandparent_thumb']}&width=300&height=450&fallback=poster&refresh=true);"></div>
|
||||
<div id="poster-${sk}" class="dashboard-activity-poster" style="background-image: url(${page('pms_image_proxy', data['grandparent_thumb'], data['grandparent_rating_key'], 300, 450, fallback='poster', refresh=True)});"></div>
|
||||
</a>
|
||||
% elif data['media_type'] == 'track':
|
||||
<a id="poster-url-${sk}" href="${parent_href}" title="${data['parent_title']}">
|
||||
<div id="poster-${sk}" class="dashboard-activity-cover" style="background-image: url(pms_image_proxy?img=${data['parent_thumb']}&width=300&height=300&fallback=cover&refresh=true);"></div>
|
||||
<div id="poster-${sk}" class="dashboard-activity-cover" style="background-image: url(${page('pms_image_proxy', data['parent_thumb'], data['parent_rating_key'], 300, 300, fallback='cover', refresh=True)});"></div>
|
||||
</a>
|
||||
% elif data['media_type'] in ('photo', 'clip'):
|
||||
% if data['extra_type']:
|
||||
<div id="poster-${sk}" class="dashboard-activity-poster" style="background-image: url(pms_image_proxy?img=${data['art'].replace('/art', '/thumb') or data['thumb']}&width=300&height=450&fallback=poster&refresh=true);"></div>
|
||||
<div id="poster-${sk}" class="dashboard-activity-poster" style="background-image: url(${page('pms_image_proxy', data['art'].replace('/art', '/thumb') or data['thumb'], data['rating_key'], 300, 450, fallback='poster', refresh=True)});"></div>
|
||||
% elif data['parent_thumb']:
|
||||
<div id="poster-${sk}" class="dashboard-activity-poster" style="background-image: url(${page('pms_image_proxy', data['parent_thumb'], data['parent_rating_key'], 300, 450, fallback='poster', refresh=True)});"></div>
|
||||
% else:
|
||||
<div id="poster-${sk}" class="dashboard-activity-poster" style="background-image: url(pms_image_proxy?img=${data['parent_thumb'] or data['thumb']}&width=300&height=450&fallback=poster&refresh=true);"></div>
|
||||
<div id="poster-${sk}" class="dashboard-activity-poster" style="background-image: url(${page('pms_image_proxy', data['thumb'], data['rating_key'], 300, 450, fallback='poster', refresh=True)});"></div>
|
||||
% endif
|
||||
% else:
|
||||
<div id="poster-${sk}" class="dashboard-activity-poster" style="background-image: url(images/poster.png);"></div>
|
||||
% endif
|
||||
% else:
|
||||
% if data['channel_icon'].startswith('http'):
|
||||
<div id="poster-${sk}" class="dashboard-activity-poster-blur" style="background-image: url(${data['channel_icon']});"></div>
|
||||
<div id="poster-${sk}" class="dashboard-activity-cover" style="background-image: url(${data['channel_icon']});"></div>
|
||||
% else:
|
||||
<div id="poster-${sk}" class="dashboard-activity-poster" style="background-image: url(pms_image_proxy?img=${data['channel_icon']}&width=300&height=300&opacity=60&background=282828&blur=3&fallback=cover&refresh=true);"></div>
|
||||
<div id="poster-${sk}" class="dashboard-activity-cover" style="background-image: url(pms_image_proxy?img=${data['channel_icon']}&width=300&height=300&fallback=cover&refresh=true);"></div>
|
||||
% endif
|
||||
<div id="poster-${sk}" class="dashboard-activity-poster" style="background-image: url(${page('pms_image_proxy', data['channel_icon'], data['rating_key'], 300, 300, 60, '282828', 3, fallback='cover', refresh=True)});"></div>
|
||||
<div id="poster-${sk}" class="dashboard-activity-cover" style="background-image: url(${page('pms_image_proxy', data['channel_icon'], data['rating_key'], 300, 300, fallback='cover', refresh=True)});"></div>
|
||||
% endif
|
||||
</div>
|
||||
<div class="dashboard-activity-info-icon">
|
||||
@@ -139,7 +143,7 @@ DOCUMENTATION :: END
|
||||
<div id="platform-${sk}" class="dashboard-activity-info-platform${no_terminate} svg-icon platform-${data['platform_name']}" title="${data['platform']}"></div>
|
||||
% if _session['user_group'] == 'admin' and plexpy.CONFIG.PMS_PLEXPASS and data['session_id']:
|
||||
<div class="dashboard-activity-terminate-session" id="terminate-button-${sk}" data-key="${sk}" data-id="${data['session_id']}" data-toggle="tooltip" title="Terminate Stream">
|
||||
<i class="fa fa-times" style="padding-top: 8px;"></i>
|
||||
<i class="fa fa-times" style="padding-top: 10px;"></i>
|
||||
</div>
|
||||
% endif
|
||||
</div>
|
||||
@@ -160,7 +164,7 @@ DOCUMENTATION :: END
|
||||
<div class="sub-value platform-right" id="stream_quality-${sk}">
|
||||
% if data['media_type'] != 'photo' and data['quality_profile'] != 'Unknown':
|
||||
<%
|
||||
br = helpers.cast_to_int(data['stream_bitrate']) or ''
|
||||
br = cast_to_int(data['stream_bitrate']) or ''
|
||||
if br:
|
||||
if br > 1000:
|
||||
br = '(' + str(round(br / 1000.0, 1)) + ' Mbps)'
|
||||
@@ -226,17 +230,24 @@ DOCUMENTATION :: END
|
||||
<li class="dashboard-activity-info-item">
|
||||
<div class="sub-heading">Video</div>
|
||||
<div class="sub-value" id="video_decision-${sk}">
|
||||
% if data['media_type'] in ('movie', 'episode', 'clip'):
|
||||
% if data['media_type'] in ('movie', 'episode', 'clip') and data['stream_video_decision']:
|
||||
<%
|
||||
if data['video_dynamic_range'] == 'HDR':
|
||||
video_dynamic_range = ' ' + data['video_dynamic_range']
|
||||
stream_video_dynamic_range = ' ' + data['stream_video_dynamic_range']
|
||||
else:
|
||||
video_dynamic_range = stream_video_dynamic_range = ''
|
||||
%>
|
||||
% if data['stream_video_decision'] == 'transcode':
|
||||
<%
|
||||
hw_d = ' (HW)' if data['transcode_hw_decoding'] else ''
|
||||
hw_e = ' (HW)' if data['transcode_hw_encoding'] else ''
|
||||
%>
|
||||
Transcode (${data['video_codec'].upper()}${hw_d} ${VIDEO_RESOLUTION_OVERRIDES.get(data['video_resolution'], data['video_resolution'])} <i class="fa fa-long-arrow-right"></i> ${data['stream_video_codec'].upper()}${hw_e} ${VIDEO_RESOLUTION_OVERRIDES.get(data['stream_video_resolution'], data['stream_video_resolution'])})
|
||||
Transcode (${data['video_codec'].upper()}${hw_d} ${data['video_full_resolution']}${video_dynamic_range} <i class="fa fa-long-arrow-right"></i> ${data['stream_video_codec'].upper()}${hw_e} ${data['stream_video_full_resolution']}${stream_video_dynamic_range})
|
||||
% elif data['stream_video_decision'] == 'copy':
|
||||
Direct Stream (${data['stream_video_codec'].upper()} ${VIDEO_RESOLUTION_OVERRIDES.get(data['stream_video_resolution'], data['stream_video_resolution'])})
|
||||
Direct Stream (${data['stream_video_codec'].upper()} ${data['stream_video_full_resolution']}${stream_video_dynamic_range})
|
||||
% else:
|
||||
Direct Play (${data['stream_video_codec'].upper()} ${VIDEO_RESOLUTION_OVERRIDES.get(data['stream_video_resolution'], data['stream_video_resolution'])})
|
||||
Direct Play (${data['stream_video_codec'].upper()} ${data['stream_video_full_resolution']}${stream_video_dynamic_range})
|
||||
% endif
|
||||
% elif data['media_type'] == 'photo':
|
||||
Direct Play (${data['width']}x${data['height']})
|
||||
@@ -248,12 +259,14 @@ DOCUMENTATION :: END
|
||||
<li class="dashboard-activity-info-item">
|
||||
<div class="sub-heading">Audio</div>
|
||||
<div class="sub-value" id="audio_decision-${sk}">
|
||||
% if data['stream_audio_decision'] == 'transcode':
|
||||
Transcode (${AUDIO_CODEC_OVERRIDES.get(data['audio_codec'], data['audio_codec'].upper())} ${data['audio_channel_layout'].split('(')[0].capitalize()} <i class="fa fa-long-arrow-right"></i> ${AUDIO_CODEC_OVERRIDES.get(data['stream_audio_codec'], data['stream_audio_codec'].upper())} ${data['stream_audio_channel_layout'].split('(')[0].capitalize()})
|
||||
% elif data['stream_audio_decision'] == 'copy':
|
||||
Direct Stream (${AUDIO_CODEC_OVERRIDES.get(data['stream_audio_codec'], data['stream_audio_codec'].upper())} ${data['stream_audio_channel_layout'].split('(')[0].capitalize()})
|
||||
% else:
|
||||
Direct Play (${AUDIO_CODEC_OVERRIDES.get(data['stream_audio_codec'], data['stream_audio_codec'].upper())} ${data['stream_audio_channel_layout'].split('(')[0].capitalize()})
|
||||
% if data['stream_audio_decision']:
|
||||
% if data['stream_audio_decision'] == 'transcode':
|
||||
Transcode (${AUDIO_CODEC_OVERRIDES.get(data['audio_codec'], data['audio_codec'].upper())} ${data['audio_channel_layout'].split('(')[0].capitalize()} <i class="fa fa-long-arrow-right"></i> ${AUDIO_CODEC_OVERRIDES.get(data['stream_audio_codec'], data['stream_audio_codec'].upper())} ${data['stream_audio_channel_layout'].split('(')[0].capitalize()})
|
||||
% elif data['stream_audio_decision'] == 'copy':
|
||||
Direct Stream (${AUDIO_CODEC_OVERRIDES.get(data['stream_audio_codec'], data['stream_audio_codec'].upper())} ${data['stream_audio_channel_layout'].split('(')[0].capitalize()})
|
||||
% else:
|
||||
Direct Play (${AUDIO_CODEC_OVERRIDES.get(data['stream_audio_codec'], data['stream_audio_codec'].upper())} ${data['stream_audio_channel_layout'].split('(')[0].capitalize()})
|
||||
% endif
|
||||
% endif
|
||||
</div>
|
||||
</li>
|
||||
@@ -292,7 +305,11 @@ DOCUMENTATION :: END
|
||||
% endif
|
||||
<span id="location-${sk}">${data['location'].upper()}</span>:
|
||||
% if data['ip_address'] != 'N/A':
|
||||
<span class="ip-container"><span class="ip-address">${data['ip_address']}</span></span>
|
||||
% if len(data['ip_address']) > 20:
|
||||
<span class="ip-container"><span class="ip-address" data-toggle="tooltip" title="${data['ip_address']}">${data['ip_address']}</span></span>
|
||||
% else:
|
||||
<span class="ip-container"><span class="ip-address">${data['ip_address']}</span></span>
|
||||
% endif
|
||||
% if data['relayed']:
|
||||
<span data-toggle="tooltip" title="Plex Relay"><i class="fa fa-exclamation-circle"></i></span>
|
||||
% else:
|
||||
@@ -317,8 +334,10 @@ DOCUMENTATION :: END
|
||||
<div class="sub-value time-right">
|
||||
% if data['media_type'] != 'photo' and data['bandwidth'] != 'Unknown':
|
||||
<%
|
||||
bw = helpers.cast_to_int(data['bandwidth'])
|
||||
if bw > 1000:
|
||||
bw = cast_to_int(data['bandwidth'])
|
||||
if bw > 1000000:
|
||||
bw = str(round(bw / 1000000.0, 1)) + ' Gbps'
|
||||
elif bw > 1000:
|
||||
bw = str(round(bw / 1000.0, 1)) + ' Mbps'
|
||||
else:
|
||||
bw = str(bw) + ' kbps'
|
||||
@@ -337,8 +356,8 @@ DOCUMENTATION :: END
|
||||
</div>
|
||||
% if data['media_type'] != 'photo':
|
||||
<div class="dashboard-activity-info-time">
|
||||
% if data['live'] == 1:
|
||||
<br />Live
|
||||
% if data['live']:
|
||||
<br /><span class="thumb-tooltip" data-toggle="popover" data-img="${data['channel_thumb']}" data-height="40" data-width="40">${data['channel_call_sign']} ${data['channel_identifier']}</span>
|
||||
% elif data['view_offset']:
|
||||
ETA:
|
||||
<span id="stream-eta-${sk}">
|
||||
@@ -367,8 +386,8 @@ DOCUMENTATION :: END
|
||||
</div>
|
||||
<div class="dashboard-activity-progress">
|
||||
<div class="dashboard-activity-progress-bar">
|
||||
% if data['live'] == 1:
|
||||
<div id="progress-bar-${sk}" class="progress-bar" style="width: 100%" data-toggle="tooltip" title="Stream Progress Live">Live</div>
|
||||
% if data['live']:
|
||||
<div id="progress-bar-${sk}" class="progress-bar" style="width: 100%" data-state="live" data-toggle="tooltip" title="Stream Progress Live">Live</div>
|
||||
% else:
|
||||
<div id="buffer-bar-${sk}" class="buffer-bar" style="width: ${data['transcode_progress']}%" data-toggle="tooltip" title="Transcoder Progress ${data['transcode_progress']}%">${data['transcode_progress']}%</div>
|
||||
<div id="progress-bar-${sk}" class="progress-bar" style="width: ${data['progress_percent']}%" data-last_view_offset="${data['view_offset']}" data-view_offset="${data['view_offset']}" data-stream_duration="${data['stream_duration']}" data-state="${data['state']}" data-toggle="tooltip" title="Stream Progress ${data['progress_percent']}%">${data['progress_percent']}%</div>
|
||||
@@ -391,7 +410,16 @@ DOCUMENTATION :: END
|
||||
% endif
|
||||
</div>
|
||||
<div class="dashboard-activity-metadata-title">
|
||||
% if data['channel_stream'] == 0:
|
||||
% if data['live']:
|
||||
% if data['media_type'] == 'movie':
|
||||
<a href="${href}" title="${data['title']}">${data['title']}</a>
|
||||
% elif data['media_type'] == 'episode':
|
||||
<a href="${href}" title="${data['grandparent_title']}">${data['grandparent_title']}</a>
|
||||
- <a href="${href}" title="${data['title']}">${data['title']}</a>
|
||||
% else:
|
||||
<span title="${data['title']}">${data['title']}</span>
|
||||
% endif
|
||||
% elif data['channel_stream'] == 0:
|
||||
% if data['media_type'] == 'movie':
|
||||
<a href="${href}" title="${data['title']}">${data['title']}</a>
|
||||
% elif data['media_type'] == 'episode':
|
||||
@@ -416,9 +444,9 @@ DOCUMENTATION :: END
|
||||
</div>
|
||||
</div>
|
||||
<div class="dashboard-activity-metadata-subtitle-container">
|
||||
% if data['live'] == 1:
|
||||
<div id="media-type-${sk}" class="dashboard-activity-metadata-media_type-icon" title="Plex Live TV">
|
||||
<i class="fa fa-fw fa-television"></i>
|
||||
% if data['live']:
|
||||
<div id="media-type-${sk}" class="dashboard-activity-metadata-media_type-icon" title="Live TV">
|
||||
<i class="fa fa-fw fa-broadcast-tower"></i>
|
||||
</div>
|
||||
% elif data['channel_stream'] == 0:
|
||||
<div id="media-type-${sk}" class="dashboard-activity-metadata-media_type-icon" title="${data['media_type'].capitalize()}">
|
||||
@@ -440,8 +468,19 @@ DOCUMENTATION :: END
|
||||
</div>
|
||||
% endif
|
||||
<div class="dashboard-activity-metadata-subtitle">
|
||||
% if data['live'] == 1:
|
||||
<span title="Plex Live TV" class="sub-heading">Plex Live TV</span>
|
||||
% if data['live']:
|
||||
% if data['media_type'] == 'movie':
|
||||
<span title="${data['year']}" class="sub-heading">${data['year']}</span>
|
||||
% elif data['media_type'] == 'episode':
|
||||
% if data['media_index']:
|
||||
<a href="${href}" title="Season ${data['parent_media_index']}" class="sub-heading">S${data['parent_media_index']}</a>
|
||||
· <a href="${href}" title="Episode ${data['media_index']}" class="sub-heading">E${data['media_index']}</a>
|
||||
% else:
|
||||
<a href="${href}" title="${data['originally_available_at']}" class="sub-heading">${data['originally_available_at']}</a>
|
||||
% endif
|
||||
% else:
|
||||
<span title="Live TV" class="sub-heading">Live TV</span>
|
||||
% endif
|
||||
% elif data['channel_stream'] == 0:
|
||||
% if data['media_type'] == 'movie':
|
||||
<span title="${data['year']}" class="sub-heading">${data['year']}</span>
|
||||
|
@@ -40,13 +40,22 @@ DOCUMENTATION :: END
|
||||
<div class="modal-body" id="modal-text">
|
||||
<fieldset>
|
||||
<div class="form-group">
|
||||
<label for="profile_url">Library Picture URL</label>
|
||||
<label for="profile_url">Library Thumbnail URL</label>
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<input type="text" class="form-control" id="custom_thumb_url" name="custom_thumb_url" value="${data['library_thumb']}">
|
||||
</div>
|
||||
</div>
|
||||
<p class="help-block">Change the library's picture in Tautulli. To reset to default, leave this field empty and save.</p>
|
||||
<p class="help-block">Change the library's thumbnail in Tautulli. To reset to default, leave this field empty and save.</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="profile_url">Library Background Art URL</label>
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<input type="text" class="form-control" id="custom_art_url" name="custom_art_url" value="${data['library_art']}">
|
||||
</div>
|
||||
</div>
|
||||
<p class="help-block">Change the library's background art in Tautulli. To reset to default, leave this field empty and save.</p>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
@@ -80,6 +89,7 @@ DOCUMENTATION :: END
|
||||
// Save library options
|
||||
$("#save_library").on('click', function () {
|
||||
var custom_thumb = $("#custom_thumb_url").val();
|
||||
var custom_art = $("#custom_art_url").val();
|
||||
var keep_history = 0;
|
||||
if ($("#keep_history").is(":checked")) {
|
||||
keep_history = 1;
|
||||
@@ -90,6 +100,7 @@ DOCUMENTATION :: END
|
||||
data: {
|
||||
section_id: '${data["section_id"]}',
|
||||
custom_thumb: custom_thumb,
|
||||
custom_art: custom_art,
|
||||
keep_history: keep_history
|
||||
},
|
||||
cache: false,
|
||||
|
@@ -252,6 +252,7 @@
|
||||
case "TV": media_type = 'episode'; break;
|
||||
case "Movies": media_type = 'movie'; break;
|
||||
case "Music": media_type = 'track'; break;
|
||||
case "Live TV": media_type = 'live'; break;
|
||||
case "Direct Play": transcode_decision = 'direct play'; break;
|
||||
case "Direct Stream": transcode_decision = 'copy'; break;
|
||||
case "Transcode": transcode_decision = 'transcode'; break;
|
||||
@@ -304,6 +305,23 @@
|
||||
|
||||
setLocalStorage(chart_key, JSON.stringify(chart_visibility));
|
||||
}
|
||||
|
||||
function getGraphColors(data_series) {
|
||||
var colors = {
|
||||
'TV': '#E5A00D',
|
||||
'Movies': '#FFFFFF',
|
||||
'Music': '#F06464',
|
||||
'Live TV': '#19A0D7',
|
||||
'Direct Play': '#E5A00D',
|
||||
'Direct Stream': '#FFFFFF',
|
||||
'Transcode': '#F06464'
|
||||
};
|
||||
var series_colors = [];
|
||||
$.each(data_series, function(index, series) {
|
||||
series_colors.push(colors[series.name]);
|
||||
});
|
||||
return series_colors;
|
||||
}
|
||||
</script>
|
||||
<script src="${http_root}js/graphs/plays_by_day.js${cache_param}"></script>
|
||||
<script src="${http_root}js/graphs/plays_by_dayofweek.js${cache_param}"></script>
|
||||
@@ -390,6 +408,7 @@
|
||||
hc_plays_by_day_options.yAxis.min = 0;
|
||||
hc_plays_by_day_options.xAxis.categories = dateArray;
|
||||
hc_plays_by_day_options.series = getGraphVisibility(hc_plays_by_day_options.chart.renderTo, data.series);
|
||||
hc_plays_by_day_options.colors = getGraphColors(data.series);
|
||||
var hc_plays_by_day = new Highcharts.Chart(hc_plays_by_day_options);
|
||||
}
|
||||
});
|
||||
@@ -403,6 +422,7 @@
|
||||
if (yaxis === 'duration') { dataSecondsToHours(data); }
|
||||
hc_plays_by_dayofweek_options.xAxis.categories = data.categories;
|
||||
hc_plays_by_dayofweek_options.series = getGraphVisibility(hc_plays_by_dayofweek_options.chart.renderTo, data.series);
|
||||
hc_plays_by_dayofweek_options.colors = getGraphColors(data.series);
|
||||
var hc_plays_by_dayofweek = new Highcharts.Chart(hc_plays_by_dayofweek_options);
|
||||
}
|
||||
});
|
||||
@@ -416,6 +436,7 @@
|
||||
if (yaxis === 'duration') { dataSecondsToHours(data); }
|
||||
hc_plays_by_hourofday_options.xAxis.categories = data.categories;
|
||||
hc_plays_by_hourofday_options.series = getGraphVisibility(hc_plays_by_hourofday_options.chart.renderTo, data.series);
|
||||
hc_plays_by_hourofday_options.colors = getGraphColors(data.series);
|
||||
var hc_plays_by_hourofday = new Highcharts.Chart(hc_plays_by_hourofday_options);
|
||||
}
|
||||
});
|
||||
@@ -429,6 +450,7 @@
|
||||
if (yaxis === 'duration') { dataSecondsToHours(data); }
|
||||
hc_plays_by_platform_options.xAxis.categories = data.categories;
|
||||
hc_plays_by_platform_options.series = getGraphVisibility(hc_plays_by_platform_options.chart.renderTo, data.series);
|
||||
hc_plays_by_platform_options.colors = getGraphColors(data.series);
|
||||
var hc_plays_by_platform = new Highcharts.Chart(hc_plays_by_platform_options);
|
||||
}
|
||||
});
|
||||
@@ -442,6 +464,7 @@
|
||||
if (yaxis === 'duration') { dataSecondsToHours(data); }
|
||||
hc_plays_by_user_options.xAxis.categories = data.categories;
|
||||
hc_plays_by_user_options.series = getGraphVisibility(hc_plays_by_user_options.chart.renderTo, data.series);
|
||||
hc_plays_by_user_options.colors = getGraphColors(data.series);
|
||||
var hc_plays_by_user = new Highcharts.Chart(hc_plays_by_user_options);
|
||||
}
|
||||
});
|
||||
@@ -478,6 +501,7 @@
|
||||
hc_plays_by_stream_type_options.yAxis.min = 0;
|
||||
hc_plays_by_stream_type_options.xAxis.categories = dateArray;
|
||||
hc_plays_by_stream_type_options.series = getGraphVisibility(hc_plays_by_stream_type_options.chart.renderTo, data.series);
|
||||
hc_plays_by_stream_type_options.colors = getGraphColors(data.series);
|
||||
var hc_plays_by_stream_type = new Highcharts.Chart(hc_plays_by_stream_type_options);
|
||||
}
|
||||
});
|
||||
@@ -491,6 +515,7 @@
|
||||
if (yaxis === 'duration') { dataSecondsToHours(data); }
|
||||
hc_plays_by_source_resolution_options.xAxis.categories = data.categories;
|
||||
hc_plays_by_source_resolution_options.series = getGraphVisibility(hc_plays_by_source_resolution_options.chart.renderTo, data.series);
|
||||
hc_plays_by_source_resolution_options.colors = getGraphColors(data.series);
|
||||
var hc_plays_by_source_resolution = new Highcharts.Chart(hc_plays_by_source_resolution_options);
|
||||
}
|
||||
});
|
||||
@@ -504,6 +529,7 @@
|
||||
if (yaxis === 'duration') { dataSecondsToHours(data); }
|
||||
hc_plays_by_stream_resolution_options.xAxis.categories = data.categories;
|
||||
hc_plays_by_stream_resolution_options.series = getGraphVisibility(hc_plays_by_stream_resolution_options.chart.renderTo, data.series);
|
||||
hc_plays_by_stream_resolution_options.colors = getGraphColors(data.series);
|
||||
var hc_plays_by_stream_resolution = new Highcharts.Chart(hc_plays_by_stream_resolution_options);
|
||||
}
|
||||
});
|
||||
@@ -517,6 +543,7 @@
|
||||
if (yaxis === 'duration') { dataSecondsToHours(data); }
|
||||
hc_plays_by_platform_by_stream_type_options.xAxis.categories = data.categories;
|
||||
hc_plays_by_platform_by_stream_type_options.series = getGraphVisibility(hc_plays_by_platform_by_stream_type_options.chart.renderTo, data.series);
|
||||
hc_plays_by_platform_by_stream_type_options.colors = getGraphColors(data.series);
|
||||
var hc_plays_by_platform_by_stream_type = new Highcharts.Chart(hc_plays_by_platform_by_stream_type_options);
|
||||
}
|
||||
});
|
||||
@@ -530,6 +557,7 @@
|
||||
if (yaxis === 'duration') { dataSecondsToHours(data); }
|
||||
hc_plays_by_user_by_stream_type_options.xAxis.categories = data.categories;
|
||||
hc_plays_by_user_by_stream_type_options.series = getGraphVisibility(hc_plays_by_user_by_stream_type_options.chart.renderTo, data.series);
|
||||
hc_plays_by_user_by_stream_type_options.colors = getGraphColors(data.series);
|
||||
var hc_plays_by_user_by_stream_type = new Highcharts.Chart(hc_plays_by_user_by_stream_type_options);
|
||||
}
|
||||
});
|
||||
@@ -553,6 +581,7 @@
|
||||
hc_plays_by_month_options.yAxis.min = 0;
|
||||
hc_plays_by_month_options.xAxis.categories = data.categories;
|
||||
hc_plays_by_month_options.series = getGraphVisibility(hc_plays_by_month_options.chart.renderTo, data.series);
|
||||
hc_plays_by_month_options.colors = getGraphColors(data.series);
|
||||
var hc_plays_by_month = new Highcharts.Chart(hc_plays_by_month_options);
|
||||
}
|
||||
});
|
||||
|
@@ -44,6 +44,9 @@
|
||||
<label class="btn btn-dark">
|
||||
<input type="radio" name="media_type-filter" id="history-track" value="track" autocomplete="off"> Music
|
||||
</label>
|
||||
<label class="btn btn-dark">
|
||||
<input type="radio" name="media_type-filter" id="history-live" value="live" autocomplete="off"> Live TV
|
||||
</label>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-dark refresh-history-button" id="refresh-history-list"><i class="fa fa-refresh"></i> Refresh history</button>
|
||||
@@ -60,7 +63,8 @@
|
||||
<th align="left" id="friendly_name">User</th>
|
||||
<th align="left" id="ip_address">IP Address</th>
|
||||
<th align="left" id="platform">Platform</th>
|
||||
<th align="left" id="device">Player</th>
|
||||
<th align="left" id="product">Product</th>
|
||||
<th align="left" id="player">Player</th>
|
||||
<th align="left" id="title">Title</th>
|
||||
<th align="left" id="started">Started</th>
|
||||
<th align="left" id="paused_counter">Paused</th>
|
||||
@@ -143,7 +147,7 @@
|
||||
var colvis = new $.fn.dataTable.ColVis(history_table, {
|
||||
buttonText: '<i class="fa fa-columns"></i> Select columns',
|
||||
buttonClass: 'btn btn-dark',
|
||||
exclude: [0, 11]
|
||||
exclude: [0, 12]
|
||||
});
|
||||
$(colvis.button()).appendTo('div.colvis-button-bar');
|
||||
|
||||
@@ -181,17 +185,15 @@
|
||||
$('#deleteCount').text(history_to_delete.length);
|
||||
$('#confirm-modal-delete').modal();
|
||||
$('#confirm-modal-delete').one('click', '#confirm-delete', function () {
|
||||
history_to_delete.forEach(function(row, idx) {
|
||||
$.ajax({
|
||||
url: 'delete_history_rows',
|
||||
type: 'POST',
|
||||
data: { row_id: row },
|
||||
async: true,
|
||||
success: function (data) {
|
||||
var msg = "History deleted";
|
||||
showMsg(msg, false, true, 2000);
|
||||
}
|
||||
});
|
||||
$.ajax({
|
||||
url: 'delete_history_rows',
|
||||
type: 'POST',
|
||||
data: { row_ids: history_to_delete.join(',') },
|
||||
async: true,
|
||||
success: function (data) {
|
||||
var msg = "History deleted";
|
||||
showMsg(msg, false, true, 2000);
|
||||
}
|
||||
});
|
||||
history_table.draw();
|
||||
});
|
||||
|
@@ -6,7 +6,7 @@
|
||||
<h4 class="modal-title" id="myModalLabel">
|
||||
<strong><span id="modal_header_ip_address">
|
||||
% if data.get('media_type'):
|
||||
<% h = {'episode': 'TV Show', 'track': 'Music'} %>
|
||||
<% h = {'episode': 'TV Show', 'track': 'Music', 'live': 'Live TV'} %>
|
||||
<i class="fa fa-history"></i> ${h.get(data['media_type'], data['media_type'].title())} History for <span id="date-header">${data['start_date']}</span>
|
||||
% elif data.get('transcode_decision'):
|
||||
<% h = {'copy': 'Direct Stream'} %>
|
||||
@@ -26,6 +26,7 @@
|
||||
<th align="left" id="friendly_name">User</th>
|
||||
<th align="left" id="ip_address">IP Address</th>
|
||||
<th align="left" id="platform">Platform</th>
|
||||
<th align="left" id="product">Product</th>
|
||||
<th align="left" id="device">Player</th>
|
||||
<th align="left" id="title">Title</th>
|
||||
<th align="left" id="started">Started</th>
|
||||
@@ -61,7 +62,7 @@
|
||||
};
|
||||
|
||||
history_table = $('#history_table_modal').DataTable(history_table_options);
|
||||
history_table.columns([0, 3, 4, 8, 10, 11]).visible(false);
|
||||
history_table.columns([0, 3, 4, 5, 9, 11, 12]).visible(false);
|
||||
|
||||
clearSearchButton('history_table_modal', history_table);
|
||||
|
||||
|
@@ -53,11 +53,11 @@ DOCUMENTATION :: END
|
||||
</%doc>
|
||||
|
||||
<%!
|
||||
from plexpy import helpers
|
||||
from plexpy.helpers import cast_to_int, page
|
||||
|
||||
# Human readable duration
|
||||
def hd(seconds):
|
||||
m, s = divmod(helpers.cast_to_int(seconds), 60)
|
||||
m, s = divmod(cast_to_int(seconds), 60)
|
||||
h, m = divmod(m, 60)
|
||||
return str(h).zfill(1) + ':' + str(m).zfill(2)
|
||||
%>
|
||||
@@ -72,11 +72,8 @@ DOCUMENTATION :: END
|
||||
<div class="dashboard-stats-instance" id="stats-instance-${stat_id}" data-stat_id="${stat_id}">
|
||||
<div class="dashboard-stats-container">
|
||||
% if stat_id in ('top_movies', 'popular_movies', 'top_tv', 'popular_tv', 'top_music', 'popular_music', 'last_watched'):
|
||||
% if row0['art']:
|
||||
<div id="stats-background-${stat_id}" class="dashboard-stats-background" style="background-image: url(pms_image_proxy?img=${row0['art']}&width=500&height=280&opacity=40&background=282828&blur=3&fallback=art);">
|
||||
% else:
|
||||
<div id="stats-background-${stat_id}" class="dashboard-stats-background" style="background-image: url(images/art.png);">
|
||||
% endif
|
||||
<% fallback = 'art-live' if row0['live'] else 'art' %>
|
||||
<div id="stats-background-${stat_id}" class="dashboard-stats-background" style="background-image: url(${page('pms_image_proxy', row0['art'], row0['rating_key'], 500, 280, 40, '282828', 3, fallback=fallback)});">
|
||||
% elif stat_id == 'top_platforms':
|
||||
<div id="stats-background-${stat_id}" class="dashboard-stats-background platform-${row0['platform_name']}-rgba no-image">
|
||||
% else:
|
||||
@@ -85,20 +82,28 @@ DOCUMENTATION :: END
|
||||
% if stat_id in ('top_movies', 'popular_movies', 'top_tv', 'popular_tv', 'top_music', 'popular_music', 'last_watched'):
|
||||
<div class="dashboard-stats-poster-container hidden-xs">
|
||||
% if stat_id in ('top_music', 'popular_music'):
|
||||
<div id="stats-thumb-${stat_id}-bg" class="dashboard-stats-poster" style="background-image: url(pms_image_proxy?img=${row0['thumb']}&width=300&height=300&opacity=60&background=282828&blur=3&fallback=cover);"></div>
|
||||
<div id="stats-thumb-${stat_id}-bg" class="dashboard-stats-poster" style="background-image: url(${page('pms_image_proxy', row0['thumb'], row0['rating_key'], 300, 300, 60, '282828', 3, fallback='cover')});"></div>
|
||||
% endif
|
||||
<% height, type = ('300', 'cover') if stat_id in ('top_music', 'popular_music') else ('450', 'poster') %>
|
||||
<% href = 'info?rating_key={}'.format(row0['rating_key']) if row0['rating_key'] else '#' %>
|
||||
<%
|
||||
height, fallback = ('450', 'poster')
|
||||
if stat_id in ('top_music', 'popular_music'):
|
||||
height, fallback = ('300', 'cover')
|
||||
elif row0['live']:
|
||||
height, fallback = ('450', 'poster-live')
|
||||
|
||||
href = '#'
|
||||
if row0['rating_key']:
|
||||
if row0['live']:
|
||||
href = page('info', row0['rating_key'], row0['guid'], history=True, live=row0['live'])
|
||||
else:
|
||||
href = page('info', row0['rating_key'])
|
||||
%>
|
||||
<a id="stats-thumb-url-${stat_id}" href="${href}" title="${row0['title']}">
|
||||
% if row0['thumb']:
|
||||
<div id="stats-thumb-${stat_id}" class="dashboard-stats-${type}" style="background-image: url(pms_image_proxy?img=${row0['thumb']}&width=300&height=${height}&fallback=${type});"></div>
|
||||
% else:
|
||||
<div id="stats-thumb-${stat_id}" class="dashboard-stats-${type}" style="background-image: url(images/${type}.png);"></div>
|
||||
% endif
|
||||
<div id="stats-thumb-${stat_id}" class="dashboard-stats-${fallback.split('-')[0]}" style="background-image: url(${page('pms_image_proxy', row0['thumb'], row0['rating_key'], 300, height, fallback=fallback)});"></div>
|
||||
</a>
|
||||
</div>
|
||||
% elif stat_id == 'top_users':
|
||||
<% user_href = 'user?user_id={}'.format(row0['user_id']) if row0['user_id'] else '#' %>
|
||||
<% user_href = page('user', row0['user_id']) if row0['user_id'] else '#' %>
|
||||
<a id="stats-thumb-url-${stat_id}" href="${user_href}" title="${row0['friendly_name']}" class="hidden-xs">
|
||||
<div id="stats-thumb-${stat_id}" class="dashboard-stats-circle" style="background-image: url(${row0['user_thumb'] or 'images/gravatar-default.png'})"></div>
|
||||
</a>
|
||||
@@ -126,19 +131,27 @@ DOCUMENTATION :: END
|
||||
<div class="dashboard-stats-info scoller-content">
|
||||
<ul class="list-unstyled dashboard-stats-info-list">
|
||||
% for row in top_stat['rows']:
|
||||
<li class="dashboard-stats-info-item ${'expanded' if loop.index == 0 else ''}" data-stat_id="${stat_id}" data-rating_key="${row.get('rating_key')}" data-title="${row.get('title')}"
|
||||
<li class="dashboard-stats-info-item ${'expanded' if loop.index == 0 else ''}" data-stat_id="${stat_id}"
|
||||
data-rating_key="${row.get('rating_key')}" data-guid="${row.get('guid')}" data-title="${row.get('title')}"
|
||||
data-art="${row.get('art')}" data-thumb="${row.get('thumb')}" data-platform="${row.get('platform_name')}"
|
||||
data-user_id="${row.get('user_id')}" data-friendly_name="${row.get('friendly_name')}" data-user_thumb="${row.get('user_thumb')}"
|
||||
data-last_watch="${row.get('last_watch')}" data-started="${row.get('started')}">
|
||||
data-last_watch="${row.get('last_watch')}" data-started="${row.get('started')}" data-live="${row.get('live')}">
|
||||
<div class="sub-list">${loop.index + 1}</div>
|
||||
<div class="sub-value">
|
||||
% if stat_id in ('top_movies', 'popular_movies', 'top_tv', 'popular_tv', 'top_music', 'popular_music', 'last_watched'):
|
||||
<% href = 'info?rating_key={}'.format(row['rating_key']) if row['rating_key'] else '#' %>
|
||||
<%
|
||||
href = '#'
|
||||
if row['rating_key']:
|
||||
if row['live']:
|
||||
href = page('info', row['rating_key'], row['guid'], history=True, live=row['live'])
|
||||
else:
|
||||
href = page('info', row['rating_key'])
|
||||
%>
|
||||
<a href="${href}" title="${row['title']}">
|
||||
${row['title']}
|
||||
</a>
|
||||
% elif stat_id == 'top_users':
|
||||
<% user_href = 'user?user_id={}'.format(row['user_id']) if row['user_id'] else '#' %>
|
||||
<% user_href = page('user', row['user_id']) if row['user_id'] else '#' %>
|
||||
<a href="${user_href}" title="${row['friendly_name']}">
|
||||
${row['friendly_name']}
|
||||
</a>
|
||||
@@ -170,78 +183,6 @@ DOCUMENTATION :: END
|
||||
</div>
|
||||
% endif
|
||||
% endfor
|
||||
<script>
|
||||
$('.dashboard-stats-instance .dashboard-stats-info-scroller').scrollbar()
|
||||
|
||||
function changeImages(elem) {
|
||||
var stat_id = $(elem).data('stat_id');
|
||||
var art = $(elem).data('art');
|
||||
var thumb = $(elem).data('thumb');
|
||||
var user_id = $(elem).data('user_id');
|
||||
var user_thumb = $(elem).data('user_thumb');
|
||||
var rating_key = $(elem).data('rating_key');
|
||||
var [height, fallback] = ($.inArray(stat_id, ['top_music', 'popular_music']) > -1) ? [300, 'cover'] : [450, 'poster'];
|
||||
var href;
|
||||
|
||||
if (stat_id == 'most_concurrent') {
|
||||
return
|
||||
} else if (stat_id == 'top_users') {
|
||||
$('#stats-thumb-' + stat_id).css('background-image', 'url(' + (user_thumb || 'images/gravatar-default.png') + ')');
|
||||
if (user_id) {
|
||||
href = 'user?user_id=' + user_id;
|
||||
} else {
|
||||
href = '#';
|
||||
}
|
||||
$('#stats-thumb-url-' + stat_id).attr('href', href).prop('title', $(elem).data('friendly_name'));
|
||||
} else if (stat_id == 'top_platforms') {
|
||||
$('#stats-thumb-' + stat_id).removeClass(function (index, className) {
|
||||
return (className.match (/(^|\s)platform-\S+/g) || []).join(' ');
|
||||
}).addClass('platform-' + $(elem).data('platform'));
|
||||
$('#stats-background-' + stat_id).removeClass(function (index, className) {
|
||||
return (className.match (/(^|\s)platform-\S+/g) || []).join(' ');
|
||||
}).addClass('platform-' + $(elem).data('platform') + '-rgba');
|
||||
} else {
|
||||
if (rating_key) {
|
||||
href = 'info?rating_key=' + rating_key;
|
||||
} else {
|
||||
href = '#';
|
||||
}
|
||||
$('#stats-thumb-url-' + stat_id).attr('href', href).prop('title', $(elem).data('title'));
|
||||
if (art) {
|
||||
$('#stats-background-' + stat_id).css('background-image', 'url(pms_image_proxy?img=' + art + '&width=500&height=280&opacity=40&background=282828&blur=3&fallback=art)');
|
||||
} else {
|
||||
$('#stats-background-' + stat_id).css('background-image', 'url(images/art.png)');
|
||||
}
|
||||
if (thumb) {
|
||||
$('#stats-thumb-' + stat_id).css('background-image', 'url(pms_image_proxy?img=' + thumb + '&width=300&height=' + height + '&fallback=' + fallback + ')');
|
||||
$('#stats-thumb-' + stat_id + '-bg').css('background-image', 'url(pms_image_proxy?img=' + thumb + '&width=300&height=' + height + '&opacity=60&background=282828&blur=3&fallback=' + fallback + ')');
|
||||
} else {
|
||||
$('#stats-thumb-' + stat_id).css('background-image', 'url(images/' + fallback + '.png)');
|
||||
$('#stats-thumb-' + stat_id + '-bg').css('background-image', 'url(images/' + fallback + '.png)');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$('.dashboard-stats-info-item').mouseenter(function () {
|
||||
changeImages(this)
|
||||
if ($(this).data('stat_id') == 'last_watched') {
|
||||
var friendly_name = $(this).data('friendly_name');
|
||||
var last_watch = moment($(this).data('last_watch'), 'X').format(date_format);
|
||||
$('#last-watched-header-info').html(friendly_name);
|
||||
} else if ($(this).data('stat_id') == 'most_concurrent') {
|
||||
var started = moment($(this).data('started'), 'X').format(date_format + ' ' + time_format);
|
||||
$('#most-concurrent-header-info').html(started);
|
||||
}
|
||||
});
|
||||
$('.dashboard-stats-instance').mouseleave(function () {
|
||||
changeImages($(this).find('.dashboard-stats-info-item').first())
|
||||
if ($(this).data('stat_id') == 'last_watched') {
|
||||
$('#last-watched-header-info').text($(this).find('.dashboard-stats-info-item').first().data('friendly_name'));
|
||||
} else if ($(this).data('stat_id') == 'most_concurrent') {
|
||||
$('#most-concurrent-header-info').text('streams');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
% else:
|
||||
<div class="text-muted">No stats to show for the selected period.</div><br>
|
||||
% endif
|
BIN
data/interfaces/default/images/art-live-full.png
Normal file
After Width: | Height: | Size: 786 KiB |
@@ -2,7 +2,7 @@
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="${http_root}images/favicon/mstile-150x150.png?v=2.0.5"/>
|
||||
<square150x150logo src="mstile-150x150.png?v=2.0.5"/>
|
||||
<TileColor>#282a2d</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
|
@@ -1,18 +1,23 @@
|
||||
{
|
||||
"name": "Tautulli",
|
||||
"name": "Tautulli: Monitor your Plex Media Server",
|
||||
"short_name": "Tautulli",
|
||||
"Description": "A Python based monitoring and tracking tool for Plex Media Server.",
|
||||
"start_url": "../../",
|
||||
"scope": "../../",
|
||||
"icons": [
|
||||
{
|
||||
"src": "${http_root}images/favicon/android-chrome-192x192.png?v=2.0.5",
|
||||
"src": "android-chrome-192x192.png?v=2.0.5",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "${http_root}images/favicon/android-chrome-256x256.png?v=2.0.5",
|
||||
"src": "android-chrome-256x256.png?v=2.0.5",
|
||||
"sizes": "256x256",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#282a2d",
|
||||
"background_color": "#282a2d",
|
||||
"display": "standalone"
|
||||
"display": "standalone",
|
||||
"orientation": "any"
|
||||
}
|
BIN
data/interfaces/default/images/libraries/live.png
Normal file
After Width: | Height: | Size: 6.2 KiB |
9
data/interfaces/default/images/libraries/live.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>live</title>
|
||||
<path fill="#fff" d="M9.636 10.115c-0.829 0.544-1.243 0.816-2.072 1.361-2.331-3.547-2.331-6.195 0-9.749 0.829 0.546 1.244 0.819 2.072 1.361-1.68 2.557-1.68 4.464 0 7.027z"></path>
|
||||
<path fill="#fff" d="M4.374 11.662c-0.828 0.542-1.243 0.815-2.072 1.359-3.069-4.676-3.069-8.159 0-12.838 0.829 0.546 1.244 0.817 2.072 1.362-2.418 3.684-2.418 6.426 0 10.117z"></path>
|
||||
<path fill="#fff" d="M22.365 10.115c0.826 0.544 1.242 0.816 2.070 1.361 2.334-3.547 2.334-6.195 0-9.749-0.828 0.546-1.244 0.819-2.070 1.361 1.677 2.557 1.677 4.464 0 7.027z"></path>
|
||||
<path fill="#fff" d="M27.627 11.662c0.827 0.542 1.243 0.815 2.070 1.359 3.070-4.676 3.070-8.159 0-12.838-0.827 0.546-1.243 0.817-2.070 1.362 2.419 3.684 2.419 6.426 0 10.117z"></path>
|
||||
<path fill="#fff" d="M25.211 31.982l2.611-0.95-8.172-22.45c0.32-0.589 0.502-1.263 0.502-1.979 0-2.293-1.859-4.152-4.152-4.152s-4.151 1.858-4.151 4.152c0 0.672 0.16 1.305 0.443 1.868l-8.212 22.561 2.612 0.95 1.952-5.362h14.616l1.951 5.362zM17.396 10.513l3.945 10.834-7.903-7.9 1.080-2.966c0.46 0.176 0.96 0.272 1.481 0.272 0.49 0.001 0.961-0.084 1.397-0.24zM12.39 16.329l7.51 7.512h-10.245l2.735-7.512z"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
1
data/interfaces/default/images/rating/imdb.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg viewBox="0 0 1000 560" xmlns="http://www.w3.org/2000/svg" stroke-miterlimit="1.414" clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round"><path d="M0 89.996C0 62.384 22.378 40 49.997 40h900.006C977.616 40 1000 62.388 1000 89.996v380.008c0 27.612-22.378 49.996-49.997 49.996H49.997C22.384 520 0 497.612 0 470.004V89.996z" fill="#e1be00"/><path d="M769.68 134.76v94.64c6.03-6.976 12.753-12.181 20.17-15.61 7.419-3.428 18.552-5.157 27.24-5.157 10.01 0 18.685 1.552 26.04 4.667 7.362 3.109 12.967 7.471 16.829 13.08 3.857 5.614 6.172 11.11 6.962 16.485.781 5.377 1.176 16.843 1.176 34.41v81.63c0 17.448-1.176 30.434-3.528 38.981-2.357 8.543-7.881 15.958-16.567 22.23-8.691 6.267-19 9.405-30.952 9.405-8.567 0-19.648-1.857-27.07-5.581-7.424-3.724-14.21-9.314-20.362-16.767l-4.709 18.538h-68.04v-290.95h72.809m-631.58 290.95h75.58v-290.95h-75.58v290.95m199.38-290.95c2.881 17.615 5.9 38.29 9.06 62.01l10.829 73.915 17.505-135.92h98.73v290.95h-65.99l-.239-196.38-26.433 196.38h-47.15l-27.862-192.11-.238 192.11h-66.2v-290.95h97.99m218.36 0c36.581 0 57.629 1.681 70.52 5.03 12.895 3.347 22.705 8.847 29.419 16.504 6.719 7.657 10.915 16.181 12.595 25.567 1.677 9.39 2.752 27.843 2.752 55.36v102.18c0 26.08-1.461 43.519-3.918 52.31-2.462 8.8-6.748 15.676-12.862 20.638-6.124 4.962-13.676 8.433-22.672 10.404-9 1.977-22.551 2.962-40.657 2.962h-91.57v-290.95h56.39m239.33 220.35c0 14.08-.7 22.977-2.096 26.677-1.4 3.704-7.485 5.566-12.1 5.566-4.5 0-7.5-1.786-9.02-5.371-1.519-3.581-2.272-11.757-2.272-24.538v-76.891c0-13.257.667-21.519 2-24.809 1.333-3.277 4.248-4.924 8.743-4.924 4.609 0 10.796 1.871 12.376 5.633 1.576 3.762 2.367 11.795 2.367 24.09v74.57m-203.37-167.99c2.986 1.728 4.901 4.457 5.734 8.157.833 3.709 1.257 12.138 1.257 25.29v112.8c0 19.371-1.257 31.23-3.767 35.595-2.509 4.371-9.2 6.548-20.06 6.548v-190.99c8.234 0 13.852.866 16.838 2.6"/></svg>
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="560" height="560" viewBox="0 0 560 560">
|
||||
<g fill="none" transform="translate(33 140)">
|
||||
<path fill="#FFF" d="M43.8020066,267.3152 L281.745403,290.797105 C286.539148,305.7344 292.894623,320.31421 302.004138,331.0528 L71.7380852,300.927619 C61.4905377,294.175695 50.8770689,281.050362 43.8020066,267.3152 Z M266.684852,192.017143 C267.285384,212.923048 270.116459,239.981562 275.03101,263.034133 L33.8766098,243.950705 C26.3585902,221.21181 24.03,207.991848 22.1723803,189.066133 L266.684852,192.017143 Z M275.03101,89.3083429 C270.116459,112.360914 267.285384,139.419429 266.684852,160.325333 L22.1723803,163.276343 C24.03,144.350629 26.3585902,131.130667 33.8766098,108.391771 L275.03101,89.3083429 Z M302.004138,21.2896762 C292.894623,32.030019 286.539148,46.6080762 281.745403,61.5471238 L43.8020066,85.0272762 C50.8770689,71.2921143 61.4905377,58.1685333 71.7380852,51.4148571 L302.004138,21.2896762 Z"/>
|
||||
<path fill="#00641E" d="M303.565869,264.667352 C306.720846,256.846476 317.903331,252.081752 325.93259,252.63901 C334.515108,253.234819 343.631626,262.264838 345.224872,271.145905 C345.520761,270.823467 345.830656,270.518552 346.145803,270.218895 C348.901593,267.583314 352.35421,265.834438 356.132479,265.352533 C355.554708,262.790552 355.416393,260.048076 355.812079,257.233752 C357.207482,247.3328 365.145698,239.907962 374.234203,239.981562 C380.099449,240.028876 385.245108,243.032457 388.597928,247.646476 C388.897318,247.271467 389.228223,246.928 389.550374,246.577524 C393.384669,226.586362 395.814807,203.999924 396.368066,180.030857 C398.267705,97.6111238 377.375174,30.2776381 349.70522,29.6397714 C322.033515,29.0001524 298.061292,95.2962286 296.161652,177.715962 C296.161652,177.715962 294.696216,207.778057 303.565869,264.667352"/>
|
||||
<path fill="#FFD700" d="M490.910577,354.797562 C492.545843,352.0656 493.45977,348.7904 493.396741,345.310171 C493.927239,334.065143 486.214879,323.871543 475.484105,325.000076 C475.794,323.713829 475.997095,322.376762 476.075882,320.997638 C476.718433,309.733333 468.957049,300.025143 458.739266,299.315429 C458.515161,299.30141 458.294557,299.2944 458.072203,299.28739 C459.134951,296.492343 459.661948,293.371352 459.47461,290.06461 C458.945862,280.692876 452.488839,272.796648 444.088407,271.242286 C441.054236,270.681524 438.111108,270.960152 435.416597,271.894171 C432.870905,265.617143 427.525652,260.961067 421.023108,259.977981 C420.508367,249.786133 413.153174,241.397486 403.68299,240.740343 C397.728452,240.326781 392.257141,243.064 388.597928,247.646476 C385.245108,243.032457 380.099449,240.030629 374.234203,239.983314 C365.145698,239.907962 357.207482,247.3328 355.812079,257.235505 C355.416393,260.048076 355.554708,262.790552 356.132479,265.354286 C352.35421,265.834438 348.901593,267.585067 346.145803,270.218895 C345.830656,270.518552 345.520761,270.823467 345.224872,271.145905 C343.631626,262.264838 334.515108,253.236571 325.93259,252.63901 C317.903331,252.081752 306.575528,256.963886 303.565869,264.667352 C304.885987,278.101105 313.275915,314.72061 343.621121,347.570743 L343.890748,347.590019 C346.816367,350.239619 350.636656,351.699352 354.709062,351.33661 C357.233744,351.110552 359.558833,350.206324 361.56177,348.811429 L362.050249,348.844724 C364.720249,350.700495 367.929502,351.671314 371.320839,351.369905 C372.616446,351.254248 373.852525,350.942324 375.027325,350.497219 C377.933685,356.51139 384.41522,360.398171 391.649607,359.760305 C397.25223,359.266133 402.014459,356.164419 404.81402,351.799238 L405.720944,351.862324 C408.517003,354.636343 412.233993,356.252038 416.24337,356.131124 C419.557672,361.149943 425.645272,364.237638 432.363167,363.647086 C434.893102,363.424533 437.254957,362.692038 439.361193,361.582781 C442.875089,365.90941 448.642289,368.481905 454.969751,367.924648 C461.22543,367.376152 466.509403,363.904686 469.384249,359.104914 C472.208321,361.374248 475.746728,362.592152 479.503987,362.257448 C483.149193,361.931505 486.38821,360.208914 488.821849,357.603124 L489.228039,357.631162 C489.781298,356.828571 490.25402,356.006705 490.693475,355.177829 C490.70398,355.162057 490.712734,355.144533 490.721489,355.128762 C490.781016,355.018362 490.854551,354.909714 490.910577,354.797562"/>
|
||||
<path fill="#04A53C" d="M281.745403,61.5471238 L43.8020066,85.0272762 C50.8770689,71.2921143 61.4905377,58.1685333 71.7380852,51.4148571 L302.004138,21.2896762 C292.894623,32.030019 286.539148,46.6080762 281.745403,61.5471238 Z M302.004138,331.0528 L71.7380852,300.927619 C61.4905377,294.175695 50.8770689,281.050362 43.8020066,267.316952 L281.745403,290.797105 C286.539148,305.7344 292.894623,320.31421 302.004138,331.0528 Z M33.8766098,243.950705 C26.3585902,221.21181 24.03,207.9936 22.1723803,189.066133 L266.684852,192.017143 C267.285384,212.923048 270.116459,239.981562 275.03101,263.034133 L33.8766098,243.950705 Z M33.8766098,108.391771 L275.03101,89.3083429 C270.116459,112.360914 267.285384,139.419429 266.684852,160.327086 L22.1723803,163.276343 C24.03,144.350629 26.3585902,131.130667 33.8766098,108.391771 Z M378.597246,25.7126857 C363.342354,7.93478095 352.390977,-0.411809524 343.010085,0.672914286 C341.261016,0.895466667 76.1168852,37.8952381 76.1168852,37.8952381 C34.0429377,42.1780571 0.416695082,103.32739 0,176.172114 C0.416695082,249.015086 34.0429377,310.164419 76.1168852,314.44899 C76.1168852,314.44899 341.758249,351.583695 343.010085,351.669562 C345.228374,351.655543 347.418649,351.357638 349.57741,350.803886 C347.476426,350.178286 345.538269,349.083048 343.890748,347.590019 L343.621121,347.570743 C313.275915,314.722362 304.885987,278.101105 303.565869,264.667352 C303.56937,264.656838 303.576374,264.648076 303.579875,264.637562 C303.576374,264.648076 303.56937,264.656838 303.565869,264.667352 C294.696216,207.778057 296.161652,177.715962 296.161652,177.715962 C298.061292,95.2962286 322.033515,29.0001524 349.70522,29.638019 C377.375174,30.2776381 398.267705,97.6111238 396.368066,180.030857 C395.814807,203.999924 393.384669,226.586362 389.550374,246.577524 C393.489718,242.226362 398.773692,240.393371 403.68299,240.740343 C404.586413,240.805181 405.465325,240.957638 406.326728,241.155657 C423.131095,149.125867 405.514348,59.262019 378.597246,25.7126857 Z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 6.1 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="560" height="560"><g fill="none"><path fill="#FFF" d="M370.57 474.214l23.466-237.956c14.93-4.796 29.498-11.15 40.23-20.262L404.16 446.278c-6.748 10.248-19.863 20.86-33.59 27.936zm-78.197 21.631l2.947-244.528c20.894-.599 47.933-3.43 70.97-8.346l-19.07 241.17c-22.724 7.518-35.934 9.848-54.847 11.704zm-99.694-252.874c23.038 4.916 50.077 7.747 70.971 8.346l2.948 244.528c-18.914-1.856-32.123-4.186-54.847-11.705l-19.072-241.17zm-67.974-26.975c10.732 9.112 25.3 15.466 40.23 20.262l23.464 237.956c-13.726-7.075-26.84-17.688-33.59-27.936l-30.104-230.282z"/><path fill="gold" d="M118.905 157.445c1.357 28.827 72.771 51.677 160.578 51.176 76.687-.438 140.659-18.546 156.329-42.336a22.976 22.976 0 00-14.058-7.426c.06-.7.098-1.406.095-2.122-.065-11.4-8.429-20.788-19.327-22.54.287-1.474.438-2.999.43-4.559-.072-12.696-10.426-22.928-23.124-22.856-.287.001-.568.036-.853.049a22.911 22.911 0 001.254-7.56c-.074-12.697-10.425-22.93-23.123-22.858a22.914 22.914 0 00-8.247 1.6c-3.632-6.835-10.606-11.6-18.737-12.149-1.416-11.4-11.157-20.195-22.93-20.129-7.41.042-13.963 3.6-18.136 9.065-4.233-4.605-10.3-7.494-17.047-7.456-12.698.072-22.932 10.424-22.86 23.118a22.983 22.983 0 001.115 6.946 22.918 22.918 0 00-13.07 7.459c-2.644-9.847-11.637-17.084-22.314-17.024-9.975.057-18.406 6.47-21.537 15.366-8.474 3.426-14.439 11.738-14.383 21.433.012 2.154.342 4.227.907 6.202a22.876 22.876 0 00-9.328-1.932c-10.012.058-18.47 6.516-21.574 15.465a22.83 22.83 0 00-9.788-2.149c-12.698.072-22.934 10.422-22.86 23.118a22.833 22.833 0 003.159 11.463c-.202.203-.379.426-.571.636"/><path fill="#FA320A" d="M404.161 446.278c-6.749 10.248-19.864 20.86-33.59 27.936l23.465-237.956c14.93-4.796 29.498-11.15 40.23-20.262L404.16 446.278zM347.22 484.14c-22.723 7.519-35.934 9.85-54.847 11.705l2.947-244.528c20.894-.599 47.933-3.43 70.973-8.346L347.22 484.14zm-135.47 0l-19.07-241.17c23.037 4.917 50.076 7.748 70.97 8.347l2.948 244.528c-18.914-1.856-32.123-4.186-54.847-11.705zm-56.94-37.862l-30.105-230.282c10.732 9.112 25.3 15.466 40.23 20.262l23.464 237.956c-13.726-7.075-26.84-17.688-33.588-27.936zm247.668-321.143c.298 1.453.465 2.955.473 4.498a23.018 23.018 0 01-.43 4.56c10.9 1.749 19.263 11.137 19.328 22.54a23.59 23.59 0 01-.095 2.12 22.976 22.976 0 0114.058 7.425c-15.669 23.792-79.642 41.9-156.327 42.34-87.807.502-159.221-22.346-160.58-51.175.192-.208.37-.433.57-.634-1.355-2.311-2.29-4.887-2.773-7.62-8.408 7.979-13.495 14.412-12.6 23.78.085 1.251 37.196 266.911 37.196 266.911 4.282 42.075 65.391 75.703 138.187 76.12 72.796-.417 133.907-34.045 138.187-76.12 0 0 37.11-265.66 37.197-266.912 1.777-18.736-20.15-35.745-52.39-47.833z"/></g></svg>
|
After Width: | Height: | Size: 2.6 KiB |
1
data/interfaces/default/images/rating/tomato-ripe.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="560" height="560"><g fill="none"><path fill="#FA320A" d="M478.29 296.976c-3.99-63.966-36.52-111.823-85.468-138.579.278 1.56-1.109 3.508-2.688 2.818-32.016-14.006-86.328 31.32-124.282 7.584.285 8.519-1.378 50.072-59.914 52.483-1.382.056-2.142-1.355-1.268-2.354 7.828-8.929 15.732-31.535 4.367-43.586-24.338 21.81-38.472 30.017-85.138 19.186-29.878 31.241-46.809 74-43.485 127.265 6.78 108.735 108.63 170.89 211.193 164.49 102.556-6.395 193.466-80.572 186.683-189.307"/><path fill="#00912D" d="M291.375 132.293c21.075-5.023 81.693-.49 101.114 25.274 1.166 1.545-.475 4.468-2.355 3.648-32.016-14.006-86.328 31.32-124.282 7.584.285 8.519-1.378 50.072-59.914 52.483-1.382.056-2.142-1.355-1.268-2.354 7.828-8.929 15.73-31.535 4.367-43.586-26.512 23.758-40.884 31.392-98.426 15.838-1.883-.508-1.241-3.535.762-4.298 10.876-4.157 35.515-22.361 58.824-30.385 4.438-1.526 8.862-2.71 13.18-3.4-25.665-2.293-37.235-5.862-53.559-3.4-1.789.27-3.004-1.813-1.895-3.241 21.995-28.332 62.513-36.888 87.512-21.837-15.41-19.094-27.48-34.321-27.48-34.321l28.601-16.246s11.817 26.4 20.414 45.614c21.275-31.435 60.86-34.336 77.585-12.033.992 1.326-.045 3.21-1.702 3.171-13.612-.331-21.107 12.05-21.675 21.466l.197.023"/></g></svg>
|
After Width: | Height: | Size: 1.2 KiB |
1
data/interfaces/default/images/rating/tomato-rotten.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="560" height="560"><path fill="#0AC855" d="M445.185 444.684c-79.369 4.167-95.587-86.652-126.726-86.006-13.268.279-23.726 14.151-19.133 30.32 2.525 8.888 9.53 21.923 13.944 30.011 15.57 28.544-7.447 60.845-34.383 63.577-44.76 4.54-63.433-21.426-62.278-48.007 1.3-29.84 26.6-60.331.65-73.305-27.194-13.597-49.301 39.572-75.325 51.439-23.553 10.741-56.248 2.413-67.872-23.741-8.164-18.379-6.68-53.768 29.67-67.27 22.706-8.433 73.305 11.029 75.9-13.623 2.992-28.416-53.155-30.812-70.06-37.626-29.912-12.055-47.567-37.85-33.734-65.522 10.378-20.757 40.915-29.203 64.223-20.11 27.922 10.892 32.404 39.853 46.71 51.897 12.324 10.38 29.19 11.68 40.22 4.543 8.135-5.265 10.843-16.828 7.774-27.39-4.07-14.023-14.875-22.773-25.415-31.346-18.758-15.249-45.24-28.36-29.222-69.983 13.13-34.11 51.642-35.34 51.642-35.34 15.3-1.72 29.002 2.9 40.167 12.875 14.927 13.335 17.834 31.16 15.336 50.176-2.283 17.358-8.426 32.56-11.63 49.759-3.717 19.966 6.954 40.086 27.249 40.869 26.694 1.031 34.698-19.486 37.964-32.492 4.782-19.028 11.058-36.694 28.718-47.82 25.346-15.97 60.552-12.47 76.886 18.222 12.92 24.284 8.772 57.715-11.047 75.97-8.892 8.188-19.584 11.075-31.148 11.156-16.585.117-33.162-.29-48.556 7.471-10.48 5.281-15.047 13.888-15.045 25.423 0 11.242 5.853 18.585 15.336 23.363 17.86 9.003 37.577 10.843 56.871 14.222 27.98 4.9 52.581 14.755 68.375 40.72.142.228.28.458.415.69 18.139 30.741-.831 75.005-36.476 76.878"/></svg>
|
After Width: | Height: | Size: 1.4 KiB |
@@ -334,13 +334,13 @@
|
||||
streams_header = streams_header.replace(/, $/, '') + ')';
|
||||
$('#currentActivityHeader-streams').text(streams_header);
|
||||
|
||||
var bandwidth_header = ((total_bw > 1000) ? ((total_bw / 1000).toFixed(1) + ' Mbps') : (total_bw + ' kbps'));
|
||||
var bandwidth_header = ((total_bw > 1000000) ? ((total_bw / 1000000).toFixed(1) + ' Gbps') : ((total_bw > 1000) ? ((total_bw / 1000).toFixed(1) + ' Mbps') : (total_bw + ' kbps')));
|
||||
var lan_wan_bandwidth_header = '';
|
||||
if (lan_bw) {
|
||||
lan_wan_bandwidth_header += 'LAN: ' + ((lan_bw > 1000) ? ((lan_bw / 1000).toFixed(1) + ' Mbps') : (lan_bw + ' kbps')) + ', ';
|
||||
lan_wan_bandwidth_header += 'LAN: ' + ((lan_bw > 1000000) ? ((lan_bw / 1000000).toFixed(1) + ' Gbps') : ((lan_bw > 1000) ? ((lan_bw / 1000).toFixed(1) + ' Mbps') : (lan_bw + ' kbps'))) + ', ';
|
||||
}
|
||||
if (wan_bw) {
|
||||
lan_wan_bandwidth_header += 'WAN: ' + ((wan_bw > 1000) ? ((wan_bw / 1000).toFixed(1) + ' Mbps') : (wan_bw + ' kbps')) + ', ';
|
||||
lan_wan_bandwidth_header += 'WAN: ' + ((wan_bw > 1000000) ? ((wan_bw / 1000000).toFixed(1) + ' Gbps') : ((wan_bw > 1000) ? ((wan_bw / 1000).toFixed(1) + ' Mbps') : (wan_bw + ' kbps'))) + ', ';
|
||||
}
|
||||
if (lan_wan_bandwidth_header) {
|
||||
bandwidth_header += ' (' + lan_wan_bandwidth_header.replace(/, $/, '') + ')';
|
||||
@@ -355,8 +355,11 @@
|
||||
var session_id = s.session_id;
|
||||
var instance = $('#activity-instance-' + key);
|
||||
|
||||
// Create a new instance if it doesn't exist
|
||||
if (!(instance.length)) {
|
||||
// Create a new instance if it doesn't exist or recreate the entire instance
|
||||
// if the rating key changed (for movies or episodes) of guid changed (for live tv) with the same session key
|
||||
if (!(instance.length) ||
|
||||
(s.media_type !== 'track' && s.rating_key !== instance.data('rating_key').toString()) ||
|
||||
(s.live === 1 && s.guid !== instance.data('guid'))) {
|
||||
create_instances.push(key);
|
||||
getActivityInstance(key);
|
||||
return;
|
||||
@@ -382,33 +385,33 @@
|
||||
// Switching tracks can be under the same session key, so need to update the info.
|
||||
if (s.media_type === 'track') {
|
||||
// Update if artist changed
|
||||
if (s.grandparent_rating_key !== instance.data('grandparent_rating_key')) {
|
||||
$('#background-' + key).css('background-image', 'url(pms_image_proxy?img=' + s.art + '&width=500&height=280&opacity=40&background=282828&blur=3&fallback=art&refresh=true)');
|
||||
if (s.grandparent_rating_key !== instance.data('grandparent_rating_key').toString()) {
|
||||
$('#background-' + key).css('background-image', 'url(' + page('pms_image_proxy', s.art, s.rating_key, 500, 280, 40, '282828', 3, 'art', true) + ')');
|
||||
$('#metadata-grandparent_title-' + key)
|
||||
.attr('href', 'info?rating_key=' + s.grandparent_rating_key)
|
||||
.attr('href', page('info', s.grandparent_rating_key))
|
||||
.attr('title', s.original_title || s.grandparent_title)
|
||||
.text(s.original_title || s.grandparent_title);
|
||||
}
|
||||
// Update cover if album changed
|
||||
if (s.parent_rating_key !== instance.data('parent_rating_key')) {
|
||||
$('#poster-' + key).css('background-image', 'url(pms_image_proxy?img=' + s.parent_thumb + '&width=300&height=300&fallback=poster&refresh=true)');
|
||||
$('#poster-' + key + '-bg').css('background-image', 'url(pms_image_proxy?img=' + s.parent_thumb + '&width=300&height=300&opacity=60&background=282828&blur=3&fallback=poster&refresh=true)');
|
||||
if (s.parent_rating_key !== instance.data('parent_rating_key').toString()) {
|
||||
$('#poster-' + key).css('background-image', 'url(' + page('pms_image_proxy', s.parent_thumb, s.parent_rating_key, 300, 300, null, null, null, 'poster', true) + ')');
|
||||
$('#poster-' + key + '-bg').css('background-image', 'url(' + page('pms_image_proxy', s.parent_thumb, s.parent_rating_key, 300, 300, 60, '282828', 3, 'poster', true) + ')');
|
||||
$('#poster-url-' + key)
|
||||
.attr('href', 'info?rating_key=' + s.parent_rating_key)
|
||||
.attr('href', page('info', s.parent_rating_key))
|
||||
.attr('title', s.parent_title);
|
||||
$('#metadata-parent_title-' + key)
|
||||
.attr('href', 'info?rating_key=' + s.parent_rating_key)
|
||||
.attr('href', page('info', s.parent_rating_key))
|
||||
.attr('title', s.parent_title)
|
||||
.text(s.parent_title);
|
||||
}
|
||||
// Update cover if track changed
|
||||
if (s.rating_key !== instance.data('rating_key')) {
|
||||
if (s.rating_key !== instance.data('rating_key').toString()) {
|
||||
$('#metadata-grandparent_title-' + key)
|
||||
.attr('href', 'info?rating_key=' + s.grandparent_rating_key)
|
||||
.attr('href', page('info', s.grandparent_rating_key))
|
||||
.attr('title', s.original_title || s.grandparent_title)
|
||||
.text(s.original_title || s.grandparent_title);
|
||||
$('#metadata-title-' + key)
|
||||
.attr('href', 'info?rating_key=' + s.rating_key)
|
||||
.attr('href', page('info', s.rating_key))
|
||||
.attr('title', s.title)
|
||||
.text(s.title);
|
||||
}
|
||||
@@ -436,6 +439,8 @@
|
||||
|
||||
var video_decision = '';
|
||||
if (['movie', 'episode', 'clip'].indexOf(s.media_type) > -1 && s.stream_video_decision) {
|
||||
var v_bd = (s.video_dynamic_range === 'HDR') ? ' ' + s.video_dynamic_range : '';
|
||||
var sv_bd = (s.video_dynamic_range === 'HDR') ? ' ' + s.stream_video_dynamic_range : '';
|
||||
var v_res= '';
|
||||
switch (s.video_resolution.toLowerCase()) {
|
||||
case 'sd':
|
||||
@@ -445,7 +450,7 @@
|
||||
v_res = '4k';
|
||||
break;
|
||||
default:
|
||||
v_res = s.video_resolution + 'p'
|
||||
v_res = s.video_full_resolution;
|
||||
}
|
||||
var sv_res = '';
|
||||
switch (s.stream_video_resolution.toLowerCase()) {
|
||||
@@ -456,16 +461,16 @@
|
||||
sv_res = '4k';
|
||||
break;
|
||||
default:
|
||||
sv_res = s.stream_video_resolution + 'p'
|
||||
sv_res = s.stream_video_full_resolution;
|
||||
}
|
||||
if (s.stream_video_decision === 'transcode') {
|
||||
var hw_d = (s.transcode_hw_decoding === 1) ? ' (HW)' : '';
|
||||
var hw_e = (s.transcode_hw_encoding === 1) ? ' (HW)' : '';
|
||||
video_decision = 'Transcode (' + s.video_codec.toUpperCase() + hw_d + ' ' + v_res + ' <i class="fa fa-long-arrow-right"></i> ' + s.stream_video_codec.toUpperCase() + hw_e + ' ' + sv_res + ')';
|
||||
video_decision = 'Transcode (' + s.video_codec.toUpperCase() + hw_d + ' ' + v_res + v_bd + ' <i class="fa fa-long-arrow-right"></i> ' + s.stream_video_codec.toUpperCase() + hw_e + ' ' + sv_res + sv_bd + ')';
|
||||
} else if (s.stream_video_decision === 'copy') {
|
||||
video_decision = 'Direct Stream (' + s.stream_video_codec.toUpperCase() + ' ' + sv_res + ')';
|
||||
video_decision = 'Direct Stream (' + s.stream_video_codec.toUpperCase() + ' ' + sv_res + sv_bd + ')';
|
||||
} else {
|
||||
video_decision = 'Direct Play (' + s.stream_video_codec.toUpperCase() + ' ' + sv_res + ')';
|
||||
video_decision = 'Direct Play (' + s.stream_video_codec.toUpperCase() + ' ' + sv_res + sv_bd + ')';
|
||||
}
|
||||
} else if (s.media_type === 'photo') {
|
||||
video_decision = 'Direct Play (' + s.width + 'x' + s.height + ')';
|
||||
@@ -521,7 +526,9 @@
|
||||
|
||||
if (s.media_type !== 'photo' && s.bandwidth !== 'Unknown') {
|
||||
var bw = parseInt(s.bandwidth) || 0;
|
||||
if (bw > 1000) {
|
||||
if (bw > 1000000) {
|
||||
bw = (bw / 1000000).toFixed(1) + ' Gbps';
|
||||
} else if (bw > 1000) {
|
||||
bw = (bw / 1000).toFixed(1) + ' Mbps';
|
||||
} else {
|
||||
bw = bw + ' kbps'
|
||||
@@ -540,10 +547,12 @@
|
||||
// Update the progress bars, percent - 3 because of 3px padding-right
|
||||
$('#buffer-bar-' + key).width(parseInt(s.transcode_progress) - 3 + '%').html(s.transcode_progress + '%')
|
||||
.attr('data-original-title', 'Transcoder Progress ' + s.transcode_progress + '%');
|
||||
var progress_bar = $('#progress-bar-' + key);
|
||||
progress_bar.data('state', s.state);
|
||||
if (progress_bar.data('last_view_offset') !== s.view_offset) {
|
||||
progress_bar.data('last_view_offset', s.view_offset).data('view_offset', s.view_offset);
|
||||
if (s.live !== 1) {
|
||||
var progress_bar = $('#progress-bar-' + key);
|
||||
progress_bar.data('state', s.state);
|
||||
if (progress_bar.data('last_view_offset') !== s.view_offset) {
|
||||
progress_bar.data('last_view_offset', s.view_offset).data('view_offset', s.view_offset);
|
||||
}
|
||||
}
|
||||
|
||||
// Add temporary class so we know which instances are still active
|
||||
@@ -556,6 +565,7 @@
|
||||
$(instance).removeClass('updated-temp');
|
||||
} else {
|
||||
$(instance).find('[data-toggle=tooltip]').tooltip('destroy');
|
||||
$(instance).find('[data-toggle=popover]').popover('destroy');
|
||||
$(instance).remove();
|
||||
}
|
||||
});
|
||||
@@ -580,9 +590,27 @@
|
||||
session_key: session_key
|
||||
},
|
||||
complete: function(xhr, status) {
|
||||
$('#currentActivity').append(xhr.responseText);
|
||||
var instance = $('#activity-instance-' + session_key);
|
||||
|
||||
if (instance.length) {
|
||||
instance.replaceWith(xhr.responseText);
|
||||
} else {
|
||||
$('#currentActivity').append(xhr.responseText);
|
||||
}
|
||||
|
||||
$('#activity-instance-' + session_key + ' .dashboard-activity-info-scroller').scrollbar();
|
||||
$('#activity-instance-' + session_key + ' [data-toggle=tooltip]').tooltip({ container: 'body', placement: 'right', delay: 50 });
|
||||
$('#activity-instance-' + session_key + ' [data-toggle=popover]').popover({
|
||||
html: true,
|
||||
container: 'body',
|
||||
trigger: 'hover',
|
||||
placement: 'right',
|
||||
delay: 50,
|
||||
template: '<div class="popover channel-thumbnail-popover" role="tooltip"><div class="arrow" style="top: 50%;"></div><div class="popover-content"></div></div>',
|
||||
content: function () {
|
||||
return '<div class="channel-thumbnail" style="background-image: url(' + $(this).data('img') + ');" />';
|
||||
}
|
||||
});
|
||||
$('#terminate-button-' + session_key).tooltip('destroy').tooltip({ container: 'body', placement: 'left', delay: 50 });
|
||||
lockScroll('#activity-instance-' + session_key + ' .dashboard-activity-info-scroller');
|
||||
|
||||
@@ -698,6 +726,88 @@
|
||||
% endif
|
||||
</script>
|
||||
% endif
|
||||
% if 'watch_stats' in config['home_sections'] or 'library_stats' in config['home_sections']:
|
||||
<script>
|
||||
function statsCardCallback() {
|
||||
$('.dashboard-stats-instance .dashboard-stats-info-scroller').scrollbar();
|
||||
|
||||
function changeImages(elem) {
|
||||
var stat_id = $(elem).data('stat_id');
|
||||
var art = $(elem).data('art');
|
||||
var thumb = $(elem).data('thumb');
|
||||
var user_id = $(elem).data('user_id');
|
||||
var user_thumb = $(elem).data('user_thumb');
|
||||
var rating_key = $(elem).data('rating_key');
|
||||
var guid = $(elem).data('guid');
|
||||
var live = $(elem).data('live');
|
||||
var [height, fallback_poster, fallback_art] = [450, 'poster', 'art'];
|
||||
if ($.inArray(stat_id, ['top_music', 'popular_music']) > -1) {
|
||||
[height, fallback_poster, fallback_art] = [300, 'cover', 'art'];
|
||||
} else if (live) {
|
||||
[height, fallback_poster, fallback_art] = [450, 'poster-live', 'art-live'];
|
||||
}
|
||||
var href = '#';
|
||||
|
||||
if (stat_id === 'most_concurrent') {
|
||||
return
|
||||
} else if (stat_id === 'top_users') {
|
||||
$('#stats-thumb-' + stat_id).css('background-image', 'url(' + (user_thumb || 'images/gravatar-default.png') + ')');
|
||||
if (user_id) {
|
||||
href = page('user', user_id);
|
||||
}
|
||||
$('#stats-thumb-url-' + stat_id).attr('href', href).prop('title', $(elem).data('friendly_name'));
|
||||
} else if (stat_id === 'top_platforms') {
|
||||
$('#stats-thumb-' + stat_id).removeClass(function (index, className) {
|
||||
return (className.match (/(^|\s)platform-\S+/g) || []).join(' ');
|
||||
}).addClass('platform-' + $(elem).data('platform'));
|
||||
$('#stats-background-' + stat_id).removeClass(function (index, className) {
|
||||
return (className.match (/(^|\s)platform-\S+/g) || []).join(' ');
|
||||
}).addClass('platform-' + $(elem).data('platform') + '-rgba');
|
||||
} else {
|
||||
if (rating_key) {
|
||||
if (live) {
|
||||
href = page('info', rating_key, guid, true, live);
|
||||
} else {
|
||||
href = page('info', rating_key);
|
||||
}
|
||||
}
|
||||
$('#stats-thumb-url-' + stat_id).attr('href', href).prop('title', $(elem).data('title'));
|
||||
$('#stats-background-' + stat_id).css('background-image', 'url(' + page('pms_image_proxy', art, rating_key, 500, 280, 40, '282828', 3, fallback_art) + ')');
|
||||
$('#stats-thumb-' + stat_id).css('background-image', 'url(' + page('pms_image_proxy', thumb, rating_key, 300, height, null, null, null, fallback_poster) + ')');
|
||||
$('#stats-thumb-' + stat_id + '-bg').css('background-image', 'url(' + page('pms_image_proxy', thumb, rating_key, 300, height, 60, '282828', 3, fallback_poster) + ')');
|
||||
$('#library-stats-background-' + stat_id).css('background-image', 'url(' + page('pms_image_proxy', art, rating_key, 500, 280, 40, '282828', 3, fallback_art) + ')');
|
||||
if (thumb.startsWith('http')) {
|
||||
$('#library-stats-thumb-' + stat_id).css('background-image', 'url(' + page('pms_image_proxy', thumb, rating_key, 300, 300, null, null, null, 'cover') + ')')
|
||||
.removeClass('svg-icon library-' + stat_id);
|
||||
} else {
|
||||
$('#library-stats-thumb-' + stat_id).css('background-image', '')
|
||||
.addClass('svg-icon library-' + stat_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$('.dashboard-stats-info-item').mouseenter(function () {
|
||||
changeImages(this);
|
||||
if ($(this).data('stat_id') === 'last_watched') {
|
||||
var friendly_name = $(this).data('friendly_name');
|
||||
var last_watch = moment($(this).data('last_watch'), 'X').format(date_format);
|
||||
$('#last-watched-header-info').html(friendly_name);
|
||||
} else if ($(this).data('stat_id') === 'most_concurrent') {
|
||||
var started = moment($(this).data('started'), 'X').format(date_format + ' ' + time_format);
|
||||
$('#most-concurrent-header-info').html(started);
|
||||
}
|
||||
});
|
||||
$('.dashboard-stats-instance').mouseleave(function () {
|
||||
changeImages($(this).find('.dashboard-stats-info-item').first());
|
||||
if ($(this).data('stat_id') === 'last_watched') {
|
||||
$('#last-watched-header-info').text($(this).find('.dashboard-stats-info-item').first().data('friendly_name'));
|
||||
} else if ($(this).data('stat_id') === 'most_concurrent') {
|
||||
$('#most-concurrent-header-info').text('streams');
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
% endif
|
||||
% if 'watch_stats' in config['home_sections']:
|
||||
<script>
|
||||
function getHomeStats(time_range, stats_type) {
|
||||
@@ -716,6 +826,7 @@
|
||||
$("#home-stats").html(xhr.responseText);
|
||||
$('#ajaxMsg').fadeOut();
|
||||
lockScroll('#home-stats .dashboard-stats-info-scroller');
|
||||
statsCardCallback();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -755,6 +866,7 @@
|
||||
data: { },
|
||||
complete: function (xhr, status) {
|
||||
$("#library-stats").html(xhr.responseText);
|
||||
statsCardCallback();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -873,7 +985,10 @@
|
||||
async: true,
|
||||
complete: function (xhr, status) {
|
||||
$("#changelog-modal .modal-body").html(xhr.responseText);
|
||||
$('#changelog-modal').modal();
|
||||
$('#changelog-modal').modal({
|
||||
backdrop: 'static',
|
||||
keyboard: false
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@@ -36,10 +36,12 @@ DOCUMENTATION :: END
|
||||
</%doc>
|
||||
|
||||
<%!
|
||||
from collections import defaultdict
|
||||
import re
|
||||
|
||||
from plexpy import notifiers
|
||||
from plexpy.common import MEDIA_TYPE_HEADERS, MEDIA_FLAGS_AUDIO, MEDIA_FLAGS_VIDEO
|
||||
from plexpy.helpers import page, get_percent
|
||||
|
||||
# Get audio codec file
|
||||
def af(codec):
|
||||
@@ -48,13 +50,20 @@ DOCUMENTATION :: END
|
||||
return file_type
|
||||
return codec
|
||||
|
||||
# Get audio codec file
|
||||
# Get video codec file
|
||||
def vf(codec):
|
||||
for pattern, file_type in MEDIA_FLAGS_VIDEO.iteritems():
|
||||
if re.match(pattern, codec):
|
||||
return file_type
|
||||
return codec
|
||||
|
||||
# Get video resolution file
|
||||
def vr(resolution):
|
||||
if resolution in ('1080i', '576i', '480i'):
|
||||
return resolution
|
||||
else:
|
||||
return resolution.lower().rstrip('ip')
|
||||
|
||||
def br(text):
|
||||
return text.replace('\n', '<br /><br />')
|
||||
%>
|
||||
@@ -68,11 +77,15 @@ DOCUMENTATION :: END
|
||||
</%def>
|
||||
|
||||
<%def name="body()">
|
||||
% if data:
|
||||
<% media_info = data['media_info'][0] if data['media_info'] else {} %>
|
||||
% if metadata:
|
||||
<%
|
||||
data = defaultdict(lambda: None, **metadata)
|
||||
media_info = defaultdict(lambda: None, **(data['media_info'][0] if data['media_info'] else {}))
|
||||
%>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="art-face" style="background-image:url(pms_image_proxy?img=${data['art']}&width=1920&height=1080)"></div>
|
||||
<% fallback = 'art-live-full' if data['live'] else None %>
|
||||
<div class="art-face" style="background-image:url(${page('pms_image_proxy', data['art'], data['rating_key'], 1920, 1080, fallback=fallback)})"></div>
|
||||
% if _session['user_group'] == 'admin':
|
||||
<span class="overlay-refresh-image info-art" title="Refresh background image"><i class="fa fa-refresh refresh_pms_image"></i></span>
|
||||
% endif
|
||||
@@ -81,44 +94,60 @@ DOCUMENTATION :: END
|
||||
<div class="col-md-12">
|
||||
<div class="summary-navbar-list">
|
||||
<ul class="list-unstyled breadcrumb">
|
||||
% if data['media_type'] in ('movie', 'collection'):
|
||||
<li><a href="library?section_id=${data['section_id']}">${data['library_name']}</a></li>
|
||||
% if data['live']:
|
||||
<li><a href="${page('library', data['section_id'])}">${data['library_name']}</a></li>
|
||||
<span class="breadcrumb-arrow"><i class="fa fa-chevron-right"></i></span>
|
||||
% if data['media_type'] == 'movie':
|
||||
<li class="active metadata-xml">${data['title']}</li>
|
||||
% elif data['media_type'] == 'episode':
|
||||
<li class="hidden-xs hidden-sm">${data['grandparent_title']}</li>
|
||||
<span class="breadcrumb-arrow"><i class="fa fa-chevron-right"></i></span>
|
||||
% if data['media_index']:
|
||||
<li>Season ${data['parent_media_index']}</li>
|
||||
<span class="breadcrumb-arrow"><i class="fa fa-chevron-right"></i></span>
|
||||
<li class="active metadata-xml">Episode ${data['media_index']} - ${data['title']}</li>
|
||||
% else:
|
||||
<li class="active metadata-xml">${data['title']}</li>
|
||||
% endif
|
||||
% endif
|
||||
% elif data['media_type'] in ('movie', 'collection'):
|
||||
<li><a href="${page('library', data['section_id'])}">${data['library_name']}</a></li>
|
||||
<span class="breadcrumb-arrow"><i class="fa fa-chevron-right"></i></span>
|
||||
<li class="active metadata-xml">${data['title']}</li>
|
||||
% elif data['media_type'] == 'show':
|
||||
<li><a href="library?section_id=${data['section_id']}">${data['library_name']}</a></li>
|
||||
<li><a href="${page('library', data['section_id'])}">${data['library_name']}</a></li>
|
||||
<span class="breadcrumb-arrow"><i class="fa fa-chevron-right"></i></span>
|
||||
<li class="active metadata-xml">${data['title']}</li>
|
||||
% elif data['media_type'] == 'season':
|
||||
<li class="hidden-xs hidden-sm"><a href="library?section_id=${data['section_id']}">${data['library_name']}</a></li>
|
||||
<li class="hidden-xs hidden-sm"><a href="${page('library', data['section_id'])}">${data['library_name']}</a></li>
|
||||
<span class="breadcrumb-arrow"><i class="fa fa-chevron-right"></i></span>
|
||||
<li><a href="info?rating_key=${data['parent_rating_key']}">${data['parent_title']}</a></li>
|
||||
<li><a href="${page('info', data['parent_rating_key'])}">${data['parent_title']}</a></li>
|
||||
<span class="breadcrumb-arrow"><i class="fa fa-chevron-right"></i></span>
|
||||
<li class="active metadata-xml">Season ${data['media_index']}</li>
|
||||
% elif data['media_type'] == 'episode':
|
||||
<li class="hidden-xs hidden-sm"><a href="library?section_id=${data['section_id']}">${data['library_name']}</a></li>
|
||||
<li class="hidden-xs hidden-sm"><a href="${page('library', data['section_id'])}">${data['library_name']}</a></li>
|
||||
<span class="breadcrumb-arrow"><i class="fa fa-chevron-right"></i></span>
|
||||
<li class="hidden-xs hidden-sm"><a href="info?rating_key=${data['grandparent_rating_key']}">${data['grandparent_title']}</a></li>
|
||||
<li class="hidden-xs hidden-sm"><a href="${page('info', data['grandparent_rating_key'])}">${data['grandparent_title']}</a></li>
|
||||
<span class="breadcrumb-arrow"><i class="fa fa-chevron-right"></i></span>
|
||||
<li><a href="info?rating_key=${data['parent_rating_key']}">Season ${data['parent_media_index']}</a></li>
|
||||
<li><a href="${page('info', data['parent_rating_key'])}">Season ${data['parent_media_index']}</a></li>
|
||||
<span class="breadcrumb-arrow"><i class="fa fa-chevron-right"></i></span>
|
||||
<li class="active metadata-xml">Episode ${data['media_index']} - ${data['title']}</li>
|
||||
% elif data['media_type'] == 'artist':
|
||||
<li><a href="library?section_id=${data['section_id']}">${data['library_name']}</a></li>
|
||||
<li><a href="${page('library', data['section_id'])}">${data['library_name']}</a></li>
|
||||
<span class="breadcrumb-arrow"><i class="fa fa-chevron-right"></i></span>
|
||||
<li class="active metadata-xml">${data['title']}</li>
|
||||
% elif data['media_type'] == 'album':
|
||||
<li class="hidden-xs hidden-sm"><a href="library?section_id=${data['section_id']}">${data['library_name']}</a></li>
|
||||
<li class="hidden-xs hidden-sm"><a href="${page('library', data['section_id'])}">${data['library_name']}</a></li>
|
||||
<span class="breadcrumb-arrow"><i class="fa fa-chevron-right"></i></span>
|
||||
<li><a href="info?rating_key=${data['parent_rating_key']}">${data['parent_title']}</a></li>
|
||||
<li><a href="${page('info', data['parent_rating_key'])}">${data['parent_title']}</a></li>
|
||||
<span class="breadcrumb-arrow"><i class="fa fa-chevron-right"></i></span>
|
||||
<li class="active metadata-xml">${data['title']}</li>
|
||||
% elif data['media_type'] == 'track':
|
||||
<li class="hidden-xs hidden-sm"><a href="library?section_id=${data['section_id']}">${data['library_name']}</a></li>
|
||||
<li class="hidden-xs hidden-sm"><a href="${page('library', data['section_id'])}">${data['library_name']}</a></li>
|
||||
<span class="breadcrumb-arrow"><i class="fa fa-chevron-right"></i></span>
|
||||
<li class="hidden-xs hidden-sm"><a href="info?rating_key=${data['grandparent_rating_key']}">${data['grandparent_title']}</a></li>
|
||||
<li class="hidden-xs hidden-sm"><a href="${page('info', data['grandparent_rating_key'])}">${data['grandparent_title']}</a></li>
|
||||
<span class="breadcrumb-arrow"><i class="fa fa-chevron-right"></i></span>
|
||||
<li><a href="info?rating_key=${data['parent_rating_key']}">${data['parent_title']}</a></li>
|
||||
<li><a href="${page('info', data['parent_rating_key'])}">${data['parent_title']}</a></li>
|
||||
<span class="breadcrumb-arrow"><i class="fa fa-chevron-right"></i></span>
|
||||
<li class="active metadata-xml">Track ${data['media_index']} - ${data['title']}</li>
|
||||
% endif
|
||||
@@ -131,11 +160,18 @@ DOCUMENTATION :: END
|
||||
<div class="summary-content-poster hidden-xs hidden-sm">
|
||||
% if data['media_type'] == 'track':
|
||||
<a href="${config['pms_web_url']}#!/server/${config['pms_identifier']}/details?key=%2Flibrary%2Fmetadata%2F${data['parent_rating_key']}" target="_blank" title="View on Plex Web">
|
||||
% else:
|
||||
% elif not data['live']:
|
||||
<a href="${config['pms_web_url']}#!/server/${config['pms_identifier']}/details?key=%2Flibrary%2Fmetadata%2F${data['rating_key']}" target="_blank" title="View on Plex Web">
|
||||
% endif
|
||||
% if data['live']:
|
||||
<div class="summary-poster-face" style="background-image: url(${page('pms_image_proxy', data['grandparent_thumb'] or data['thumb'], data['rating_key'], 300, 450, fallback='poster-live')});">
|
||||
<div class="summary-poster-face-overlay">
|
||||
<span></span>
|
||||
</div>
|
||||
</div>
|
||||
% else:
|
||||
% 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(${page('pms_image_proxy', data['thumb'], data['rating_key'], 500, 280, fallback='art')});">
|
||||
<div class="summary-poster-face-overlay">
|
||||
<span></span>
|
||||
</div>
|
||||
@@ -144,7 +180,7 @@ DOCUMENTATION :: END
|
||||
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
|
||||
% endif
|
||||
% elif data['media_type'] == 'artist' or data['media_type'] == 'album' or data['media_type'] == 'track':
|
||||
<div class="summary-poster-face-track" style="background-image: url(pms_image_proxy?img=${data['thumb']}&width=500&height=500&fallback=cover);">
|
||||
<div class="summary-poster-face-track" style="background-image: url(${page('pms_image_proxy', data['thumb'], data['rating_key'], 500, 500, fallback='cover')});">
|
||||
<div class="summary-poster-face-overlay">
|
||||
<span></span>
|
||||
</div>
|
||||
@@ -153,7 +189,7 @@ DOCUMENTATION :: END
|
||||
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
|
||||
% endif
|
||||
% else:
|
||||
<div class="summary-poster-face" style="background-image: url(pms_image_proxy?img=${data['thumb']}&width=300&height=450&fallback=poster);">
|
||||
<div class="summary-poster-face" style="background-image: url(${page('pms_image_proxy', data['thumb'], data['rating_key'], 300, 450, fallback='poster')});">
|
||||
<div class="summary-poster-face-overlay">
|
||||
<span></span>
|
||||
</div>
|
||||
@@ -162,24 +198,37 @@ DOCUMENTATION :: END
|
||||
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
|
||||
% endif
|
||||
% endif
|
||||
% endif
|
||||
% if not data['live']:
|
||||
</a>
|
||||
% endif
|
||||
</div>
|
||||
<div class="summary-content-title">
|
||||
% if data['media_type'] in ('movie', 'show', 'artist', 'collection'):
|
||||
% if data['live']:
|
||||
% if data['media_type'] == 'movie':
|
||||
<h1> </h1><h1>${data['title']}</h1>
|
||||
% elif data['media_type'] == 'episode':
|
||||
<h1>${data['grandparent_title']}</h1>
|
||||
<h2>${data['title']}</h2>
|
||||
% if data['media_index']:
|
||||
<h3 class="hidden-xs">S${data['parent_media_index']} · E${data['media_index']}</h3>
|
||||
% endif
|
||||
% endif
|
||||
% elif data['media_type'] in ('movie', 'show', 'artist', 'collection'):
|
||||
<h1> </h1><h1>${data['title']}</h1>
|
||||
% elif data['media_type'] == 'season':
|
||||
<h1> </h1><h1><a href="info?rating_key=${data['parent_rating_key']}">${data['parent_title']}</a></h1>
|
||||
<h1> </h1><h1><a href="${page('info', data['parent_rating_key'])}">${data['parent_title']}</a></h1>
|
||||
<h3 class="hidden-xs">S${data['media_index']}</h3>
|
||||
% elif data['media_type'] == 'episode':
|
||||
<h1><a href="info?rating_key=${data['grandparent_rating_key']}">${data['grandparent_title']}</a></h1>
|
||||
<h1><a href="${page('info', data['grandparent_rating_key'])}">${data['grandparent_title']}</a></h1>
|
||||
<h2>${data['title']}</h2>
|
||||
<h3 class="hidden-xs">S${data['parent_media_index']} · E${data['media_index']}</h3>
|
||||
% elif data['media_type'] == 'album':
|
||||
<h1><a href="info?rating_key=${data['parent_rating_key']}">${data['parent_title']}</a></h1>
|
||||
<h1><a href="${page('info', data['parent_rating_key'])}">${data['parent_title']}</a></h1>
|
||||
<h2>${data['title']}</h2>
|
||||
% elif data['media_type'] == 'track':
|
||||
<h1><a href="info?rating_key=${data['grandparent_rating_key']}">${data['original_title'] or data['grandparent_title']}</a></h1>
|
||||
<h2><a href="info?rating_key=${data['parent_rating_key']}">${data['parent_title']}</a> - ${data['title']}</h2>
|
||||
<h1><a href="${page('info', data['grandparent_rating_key'])}">${data['original_title'] or data['grandparent_title']}</a></h1>
|
||||
<h2><a href="${page('info', data['parent_rating_key'])}">${data['parent_title']}</a> - ${data['title']}</h2>
|
||||
<h3 class="hidden-xs">T${data['media_index']}</h3>
|
||||
% endif
|
||||
</div>
|
||||
@@ -187,7 +236,7 @@ DOCUMENTATION :: END
|
||||
</div>
|
||||
<div class="summary-content-wrapper">
|
||||
<div class="col-md-9">
|
||||
% if data['media_type'] == 'movie':
|
||||
% if data['media_type'] == 'movie' or data['live']:
|
||||
<div class="summary-content-padding hidden-xs hidden-sm" style="height: 305px;">
|
||||
% elif data['media_type'] in ('show', 'season', 'collection'):
|
||||
<div class="summary-content-padding hidden-xs hidden-sm" style="height: 270px;">
|
||||
@@ -206,7 +255,7 @@ DOCUMENTATION :: END
|
||||
<img class="summary-content-media-flag" title="${media_info['video_codec']}" src="${http_root}images/media_flags/video_codec/${media_info['video_codec'] | vf}.png" />
|
||||
% endif
|
||||
% if data['media_type'] != 'track' and media_info['video_resolution']:
|
||||
<img class="summary-content-media-flag" title="${media_info['video_resolution']}" src="${http_root}images/media_flags/video_resolution/${media_info['video_resolution']}.png" />
|
||||
<img class="summary-content-media-flag" title="${media_info['video_resolution']}" src="${http_root}images/media_flags/video_resolution/${media_info['video_full_resolution'] | vr}.png" />
|
||||
% endif
|
||||
% if media_info['audio_codec']:
|
||||
<img class="summary-content-media-flag" title="${media_info['audio_codec']}" src="${http_root}images/media_flags/audio_codec/${media_info['audio_codec'] | af}.png" />
|
||||
@@ -220,16 +269,28 @@ DOCUMENTATION :: END
|
||||
<div class="summary-content">
|
||||
<div class="summary-content-details-wrapper">
|
||||
% if data['rating']:
|
||||
<div class="star-rating hidden-xs hidden-sm" title="${data['rating']}">
|
||||
% for i in range(0,5):
|
||||
% if round(float(data['rating']) / 2) > i:
|
||||
<i class="star-icon fa fa-star"></i>
|
||||
% else:
|
||||
<i class="star-icon-o fa fa-star-o"></i>
|
||||
% endif
|
||||
% endfor
|
||||
% if data['rating_image']:
|
||||
% if data['rating_image'].startswith('imdb://'):
|
||||
<div class="critic-rating hidden-xs hidden-sm" title="${data['rating']}">
|
||||
<span class="rating-image rating-imdb"><strong>${data['rating']}</strong></span>
|
||||
</div>
|
||||
% endif
|
||||
% if data['audience_rating_image'].startswith('rottentomatoes://'):
|
||||
<div class="critic-rating hidden-xs hidden-sm" title="${data['audience_rating']}">
|
||||
<span class="rating-image rating-rottentomatos-${data['audience_rating_image'].rsplit('.')[-1]}"><strong>${get_percent(data['audience_rating'], 10)}%</strong></span>
|
||||
</div>
|
||||
% endif
|
||||
% if data['rating_image'].startswith('rottentomatoes://'):
|
||||
<div class="critic-rating hidden-xs hidden-sm" title="${data['rating']}">
|
||||
<span class="rating-image rating-rottentomatos-${data['rating_image'].rsplit('.')[-1]}"><strong>${get_percent(data['rating'], 10)}%</strong></span>
|
||||
</div>
|
||||
% endif
|
||||
% else:
|
||||
<div class="critic-rating hidden-xs hidden-sm" title="${data['rating']}">
|
||||
<i class="star-icon fa fa-star"></i> <strong>${get_percent(data['rating'], 10)}%</strong>
|
||||
</div>
|
||||
% endif
|
||||
% endif
|
||||
<div class="summary-content-details-tag">
|
||||
% if data['directors']:
|
||||
Directed by <strong> ${data['directors'][0]}</strong>
|
||||
@@ -251,6 +312,8 @@ DOCUMENTATION :: END
|
||||
Released <strong> ${data['year']}</strong>
|
||||
% elif data['media_type'] == 'collection':
|
||||
Year <strong> ${data['min_year']} - ${data['max_year']}</strong>
|
||||
% elif data['year']:
|
||||
Year <strong> ${data['year']}</strong>
|
||||
% endif
|
||||
</div>
|
||||
<div class="summary-content-details-tag">
|
||||
@@ -263,6 +326,11 @@ DOCUMENTATION :: END
|
||||
Rated <strong> ${data['content_rating']} </strong>
|
||||
% endif
|
||||
</div>
|
||||
<div class="summary-content-details-tag" id="channel-icon">
|
||||
% if media_info['channel_identifier']:
|
||||
Channel <strong> <span class="thumb-tooltip" data-toggle="popover" data-img="${media_info['channel_thumb']}" data-height="40" data-width="40">${media_info['channel_call_sign']} ${media_info['channel_identifier']}</span> </strong>
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
% if data['tagline']:
|
||||
<div class="summary-content-summary">
|
||||
@@ -405,17 +473,17 @@ DOCUMENTATION :: END
|
||||
</a>
|
||||
</div>
|
||||
% endif
|
||||
% if data.get('tvmaze_id') or data.get('themoviedb_id'):
|
||||
% if data.get('tvmaze_id') or data.get('themoviedb_id') or data.get('musicbrainz_id'):
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-danger btn-edit" data-toggle="modal" aria-pressed="false" autocomplete="off" id="delete-lookup-info"
|
||||
data-id="${data['grandparent_rating_key'] if data['media_type'] in ('episode', 'track') else data['parent_rating_key'] if data['media_type'] in ('season', 'album') else data['rating_key']}"
|
||||
data-title="${data['grandparent_title'] if data['media_type'] in ('episode', 'track') else data['parent_title'] if data['media_type'] in ('season', 'album') else data['title']}">
|
||||
data-id="${data['grandparent_rating_key'] if data['media_type'] == 'episode' else data['parent_rating_key'] if data['media_type'] == 'season' else data['rating_key']}"
|
||||
data-title="${data['grandparent_title'] if data['media_type'] == 'episode' else data['parent_title'] if data['media_type'] == 'season' else data['title']}">
|
||||
<i class="fa fa-search"></i> Delete Lookup Info
|
||||
</button>
|
||||
</div>
|
||||
% endif
|
||||
% if data.get('poster_url'):
|
||||
<div class="btn-group">
|
||||
<div class="btn-group" id="hosted-poster">
|
||||
% if data['media_type'] == 'artist' or data['media_type'] == 'album' or data['media_type'] == 'track':
|
||||
<span class="hosted-poster-tooltip" data-toggle="popover" data-img="${data['poster_url']}" data-height="80" data-width="80" style="display: inline-flex;">
|
||||
% else:
|
||||
@@ -429,6 +497,7 @@ DOCUMENTATION :: END
|
||||
</span>
|
||||
</div>
|
||||
% endif
|
||||
% if not data['live']:
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-dark" data-toggle="modal" aria-pressed="false" autocomplete="off" id="send-recently-added-notification"
|
||||
data-id="${data['rating_key']}">
|
||||
@@ -436,6 +505,7 @@ DOCUMENTATION :: END
|
||||
</button>
|
||||
</div>
|
||||
% endif
|
||||
% endif
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-dark refresh-history-button" id="refresh-history-list"><i class="fa fa-refresh"></i> Refresh history</button>
|
||||
</div>
|
||||
@@ -451,6 +521,7 @@ DOCUMENTATION :: END
|
||||
<th align="left" id="friendly_name">User</th>
|
||||
<th align="left" id="ip_address">IP Address</th>
|
||||
<th align="left" id="platform">Platform</th>
|
||||
<th align="left" id="product">Product</th>
|
||||
<th align="left" id="player">Player</th>
|
||||
<th align="left" id="title">Title</th>
|
||||
<th align="left" id="started">Started</th>
|
||||
@@ -473,6 +544,10 @@ DOCUMENTATION :: END
|
||||
</%def>
|
||||
|
||||
<%def name="modalIncludes()">
|
||||
% if metadata:
|
||||
<%
|
||||
data = defaultdict(None, **metadata)
|
||||
%>
|
||||
<div class="modal fade" id="info-modal" tabindex="-1" role="dialog" aria-labelledby="info-modal">
|
||||
</div>
|
||||
<div class="modal fade" id="ip-info-modal" tabindex="-1" role="dialog" aria-labelledby="ip-info-modal">
|
||||
@@ -548,6 +623,7 @@ DOCUMENTATION :: END
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="javascriptIncludes()">
|
||||
@@ -557,9 +633,28 @@ DOCUMENTATION :: END
|
||||
<script src="${http_root}js/dataTables.bootstrap.pagination.js"></script>
|
||||
<script src="${http_root}js/moment-with-locale.js"></script>
|
||||
|
||||
% if data:
|
||||
% if metadata:
|
||||
<%
|
||||
data = defaultdict(None, **metadata)
|
||||
%>
|
||||
<script src="${http_root}js/tables/history_table.js${cache_param}"></script>
|
||||
% if data['media_type'] in ('show', 'artist'):
|
||||
% if data['live']:
|
||||
<script>
|
||||
function get_history() {
|
||||
history_table_options.ajax = {
|
||||
url: 'get_history',
|
||||
type: 'POST',
|
||||
data: function ( d ) {
|
||||
return {
|
||||
json_data: JSON.stringify( d ),
|
||||
guid: "${data['guid']}",
|
||||
user_id: "${_session['user_group']}" == "admin" ? null : "${_session['user_id']}"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
% elif data['media_type'] in ('show', 'artist'):
|
||||
<script>
|
||||
function get_history() {
|
||||
history_table_options.ajax = {
|
||||
@@ -613,7 +708,7 @@ DOCUMENTATION :: END
|
||||
$(document).ready(function () {
|
||||
get_history();
|
||||
history_table = $('#history_table-RK-${data["rating_key"]}').DataTable(history_table_options);
|
||||
var colvis = new $.fn.dataTable.ColVis(history_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark', exclude: [0, 11] });
|
||||
var colvis = new $.fn.dataTable.ColVis(history_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark', exclude: [0, 12] });
|
||||
$(colvis.button()).appendTo('div.colvis-button-bar');
|
||||
|
||||
clearSearchButton('history_table-RK-${data["rating_key"]}', history_table);
|
||||
@@ -626,17 +721,15 @@ DOCUMENTATION :: END
|
||||
$('#deleteCount').text(history_to_delete.length);
|
||||
$('#confirm-modal-delete').modal();
|
||||
$('#confirm-modal-delete').one('click', '#confirm-delete', function () {
|
||||
history_to_delete.forEach(function (row, idx) {
|
||||
$.ajax({
|
||||
url: 'delete_history_rows',
|
||||
type: 'POST',
|
||||
data: { row_id: row },
|
||||
async: true,
|
||||
success: function (data) {
|
||||
var msg = "History deleted";
|
||||
showMsg(msg, false, true, 2000);
|
||||
}
|
||||
});
|
||||
$.ajax({
|
||||
url: 'delete_history_rows',
|
||||
type: 'POST',
|
||||
data: { row_ids: history_to_delete.join(',') },
|
||||
async: true,
|
||||
success: function (data) {
|
||||
var msg = "History deleted";
|
||||
showMsg(msg, false, true, 2000);
|
||||
}
|
||||
});
|
||||
history_table.draw();
|
||||
});
|
||||
@@ -723,10 +816,22 @@ DOCUMENTATION :: END
|
||||
$("#airdate").html(moment($("#airdate").text()).format('MMM DD, YYYY'));
|
||||
$("#runtime").html(millisecondsToMinutes($("#runtime").text(), true));
|
||||
$('div.art-face').animate({ opacity: 0.2 }, { duration: 1000 });
|
||||
$('#channel-icon').popover({
|
||||
selector: '[data-toggle=popover]',
|
||||
html: true,
|
||||
container: 'body',
|
||||
trigger: 'hover',
|
||||
placement: 'right',
|
||||
template: '<div class="popover channel-thumbnail-popover" role="tooltip"><div class="arrow" style="top: 50%;"></div><div class="popover-content"></div></div>',
|
||||
content: function () {
|
||||
return '<div class="channel-thumbnail" style="background-image: url(' + $(this).data('img') + ');" />';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
% if data.get('poster_url'):
|
||||
<script>
|
||||
$('.hosted-poster-tooltip').popover({
|
||||
$('#hosted-poster').popover({
|
||||
selector: '[data-toggle=popover]',
|
||||
html: true,
|
||||
container: 'body',
|
||||
trigger: 'hover',
|
||||
@@ -750,13 +855,13 @@ DOCUMENTATION :: END
|
||||
});
|
||||
</script>
|
||||
% endif
|
||||
% if data.get('tvmaze_id') or data.get('themoviedb_id'):
|
||||
% if data.get('tvmaze_id') or data.get('themoviedb_id') or data.get('musicbrainz_id'):
|
||||
<script>
|
||||
$('#delete-lookup-info').on('click', function () {
|
||||
var msg = 'Are you sure you want to delete the 3rd party API lookup for <strong>' + $(this).data('title') + '</strong>?<br><br>' +
|
||||
'The info will be looked up again the next time a notification is sent.';
|
||||
var msg = 'Are you sure you want to delete all the metadata lookup info for <strong>' + $(this).data('title') + '</strong>?' +
|
||||
'<br /><br />Tautulli will lookup the metadata info again the next time a notification is sent.';
|
||||
var url = 'delete_lookup_info';
|
||||
var data = { rating_key: $(this).data('id'), title: $(this).data('title') };
|
||||
var data = { rating_key: $(this).data('id') };
|
||||
var callback = function () {
|
||||
$('#delete-lookup-info').closest('.btn-group').remove();
|
||||
};
|
||||
|
@@ -27,6 +27,9 @@ DOCUMENTATION :: END
|
||||
</%doc>
|
||||
|
||||
% if data != None:
|
||||
<%
|
||||
from plexpy.helpers import page
|
||||
%>
|
||||
% if data['children_count'] > 0:
|
||||
<div class="item-children-wrapper">
|
||||
<ul class="item-children-instance list-unstyled">
|
||||
@@ -38,9 +41,9 @@ DOCUMENTATION :: END
|
||||
<li>
|
||||
% endif
|
||||
% if data['children_type'] == 'movie':
|
||||
<a href="info?rating_key=${child['rating_key']}" title="${child['title']}">
|
||||
<a href="${page('info', child['rating_key'])}" title="${child['title']}">
|
||||
<div class="item-children-poster">
|
||||
<div class="item-children-poster-face poster-item" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=450&fallback=poster);"></div>
|
||||
<div class="item-children-poster-face poster-item" style="background-image: url(${page('pms_image_proxy', child['thumb'], child['rating_key'], 300, 450, fallback='poster')});"></div>
|
||||
% if _session['user_group'] == 'admin':
|
||||
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
|
||||
% endif
|
||||
@@ -48,14 +51,14 @@ DOCUMENTATION :: END
|
||||
</a>
|
||||
<div class="item-children-instance-text-wrapper poster-item">
|
||||
<h3>
|
||||
<a href="info?rating_key=${child['rating_key']}" title="${child['title']}">${child['title']}</a>
|
||||
<a href="${page('info', child['rating_key'])}" title="${child['title']}">${child['title']}</a>
|
||||
</h3>
|
||||
<h3 class="text-muted">${child['year']}</h3>
|
||||
</div>
|
||||
% elif data['children_type'] == 'show':
|
||||
<a href="info?rating_key=${child['rating_key']}" title="${child['title']}">
|
||||
<a href="${page('info', child['rating_key'])}" title="${child['title']}">
|
||||
<div class="item-children-poster">
|
||||
<div class="item-children-poster-face poster-item" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=450&fallback=poster);"></div>
|
||||
<div class="item-children-poster-face poster-item" style="background-image: url(${page('pms_image_proxy', child['thumb'], child['rating_key'], 300, 450, fallback='poster')});"></div>
|
||||
% if _session['user_group'] == 'admin':
|
||||
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
|
||||
% endif
|
||||
@@ -63,16 +66,16 @@ DOCUMENTATION :: END
|
||||
</a>
|
||||
<div class="item-children-instance-text-wrapper poster-item">
|
||||
<h3>
|
||||
<a href="info?rating_key=${child['rating_key']}" title="${child['title']}">${child['title']}</a>
|
||||
<a href="${page('info', child['rating_key'])}" title="${child['title']}">${child['title']}</a>
|
||||
</h3>
|
||||
</div>
|
||||
% elif data['children_type'] == 'season':
|
||||
<a href="info?rating_key=${child['rating_key']}" title="Season ${child['media_index']}">
|
||||
<a href="${page('info', child['rating_key'])}" title="Season ${child['media_index']}">
|
||||
<div class="item-children-poster">
|
||||
% if child['thumb']:
|
||||
<div class="item-children-poster-face poster-item" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=450&fallback=poster);">
|
||||
<div class="item-children-poster-face poster-item" style="background-image: url(${page('pms_image_proxy', child['thumb'], child['rating_key'], 300, 450, fallback='poster')});">
|
||||
% else:
|
||||
<div class="item-children-poster-face poster-item" style="background-image: url(pms_image_proxy?img=${child['parent_thumb']}&width=300&height=450&fallback=poster);">
|
||||
<div class="item-children-poster-face poster-item" style="background-image: url(${page('pms_image_proxy', child['parent_thumb'], child['parent_rating_key'], 300, 450, fallback='poster')});">
|
||||
% endif
|
||||
<div class="item-children-card-overlay">
|
||||
<div class="item-children-overlay-text">
|
||||
@@ -86,9 +89,9 @@ DOCUMENTATION :: END
|
||||
</div>
|
||||
</a>
|
||||
% elif data['children_type'] == 'episode':
|
||||
<a href="info?rating_key=${child['rating_key']}" title="Episode ${child['media_index']}">
|
||||
<a href="${page('info', child['rating_key'])}" title="Episode ${child['media_index']}">
|
||||
<div class="item-children-poster">
|
||||
<div class="item-children-poster-face episode-item" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=500&height=250&fallback=art);">
|
||||
<div class="item-children-poster-face episode-item" style="background-image: url(${page('pms_image_proxy', child['thumb'], child['rating_key'], 500, 280, fallback='art')});">
|
||||
<div class="item-children-card-overlay">
|
||||
<div class="item-children-overlay-text">
|
||||
Episode ${child['media_index'] or child['originally_available_at']}
|
||||
@@ -102,13 +105,13 @@ DOCUMENTATION :: END
|
||||
</a>
|
||||
<div class="item-children-instance-text-wrapper episode-item">
|
||||
<h3>
|
||||
<a href="info?rating_key=${child['rating_key']}" title="${child['title']}">${child['title']}</a>
|
||||
<a href="${page('info', child['rating_key'])}" title="${child['title']}">${child['title']}</a>
|
||||
</h3>
|
||||
</div>
|
||||
% elif data['children_type'] == 'album':
|
||||
<a href="info?rating_key=${child['rating_key']}" title="${child['title']}">
|
||||
<a href="${page('info', child['rating_key'])}" title="${child['title']}">
|
||||
<div class="item-children-poster">
|
||||
<div class="item-children-poster-face cover-item" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=300&fallback=cover);"></div>
|
||||
<div class="item-children-poster-face cover-item" style="background-image: url(${page('pms_image_proxy', child['thumb'], child['rating_key'], 300, 300, fallback='cover')});"></div>
|
||||
% if _session['user_group'] == 'admin':
|
||||
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
|
||||
% endif
|
||||
@@ -116,14 +119,14 @@ DOCUMENTATION :: END
|
||||
</a>
|
||||
<div class="item-children-instance-text-wrapper cover-item">
|
||||
<h3>
|
||||
<a href="info?rating_key=${child['rating_key']}" title="${child['title']}">${child['title']}</a>
|
||||
<a href="${page('info', child['rating_key'])}" title="${child['title']}">${child['title']}</a>
|
||||
</h3>
|
||||
</div>
|
||||
% elif data['children_type'] == 'track':
|
||||
% if loop.index % 2 == 0:
|
||||
<div class="item-children-list-item-even">
|
||||
<span class="item-children-list-item-index"> ${child['media_index']}</span>
|
||||
<span class="item-children-list-item-title"><a href="info?rating_key=${child['rating_key']}" title="${child['title']}">${child['title']}</a>
|
||||
<span class="item-children-list-item-title"><a href="${page('info', child['rating_key'])}" title="${child['title']}">${child['title']}</a>
|
||||
% if child['original_title']:
|
||||
<span class="text-muted"> - ${child['original_title']}</span>
|
||||
% endif
|
||||
@@ -135,7 +138,7 @@ DOCUMENTATION :: END
|
||||
% else:
|
||||
<div class="item-children-list-item-odd">
|
||||
<span class="item-children-list-item-index"> ${child['media_index']}</span>
|
||||
<span class="item-children-list-item-title"><a href="info?rating_key=${child['rating_key']}" title="${child['title']}">${child['title']}</a>
|
||||
<span class="item-children-list-item-title"><a href="${page('info', child['rating_key'])}" title="${child['title']}">${child['title']}</a>
|
||||
% if child['original_title']:
|
||||
<span class="text-muted"> - ${child['original_title']}</span>
|
||||
% endif
|
||||
|
@@ -29,6 +29,7 @@ DOCUMENTATION :: END
|
||||
% if data != None:
|
||||
<%
|
||||
from plexpy.common import MEDIA_TYPE_HEADERS
|
||||
from plexpy.helpers import page
|
||||
types = ('movie', 'show', 'artist', 'album')
|
||||
%>
|
||||
% for media_type in types:
|
||||
@@ -45,12 +46,12 @@ DOCUMENTATION :: END
|
||||
<ul class="item-children-instance list-unstyled">
|
||||
% for child in data['results_list'][media_type]:
|
||||
<li>
|
||||
<a href="info?rating_key=${child['rating_key']}" title="${child['title']}">
|
||||
<a href="${page('info', child['rating_key'])}" title="${child['title']}">
|
||||
<div class="item-children-poster">
|
||||
% if media_type in ('artist', 'album'):
|
||||
<div class="item-children-poster-face cover-item" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=300&fallback=cover);"></div>
|
||||
<div class="item-children-poster-face cover-item" style="background-image: url(${page('pms_image_proxy', child['thumb'], child['rating_key'], 300, 300, fallback='cover')});"></div>
|
||||
% else:
|
||||
<div class="item-children-poster-face poster-item" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=450&fallback=poster);"></div>
|
||||
<div class="item-children-poster-face poster-item" style="background-image: url(${page('pms_image_proxy', child['thumb'], child['rating_key'], 300, 450, fallback='poster')});"></div>
|
||||
% endif
|
||||
% if _session['user_group'] == 'admin':
|
||||
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
|
||||
@@ -60,22 +61,22 @@ DOCUMENTATION :: END
|
||||
% if media_type == 'artist':
|
||||
<div class="item-children-instance-text-wrapper cover-item">
|
||||
<h3>
|
||||
<a href="info?rating_key=${child['rating_key']}" title="${child['title']}">${child['title']}</a>
|
||||
<a href="${page('info', child['rating_key'])}" title="${child['title']}">${child['title']}</a>
|
||||
</h3>
|
||||
</div>
|
||||
% elif media_type == 'album':
|
||||
<div class="item-children-instance-text-wrapper cover-item">
|
||||
<h3>
|
||||
<a href="info?rating_key=${child['parent_rating_key']}" title="${child['parent_title']}">${child['parent_title']}</a>
|
||||
<a href="${page('info', child['parent_rating_key'])}" title="${child['parent_title']}">${child['parent_title']}</a>
|
||||
</h3>
|
||||
<h3>
|
||||
<a href="info?rating_key=${child['rating_key']}" title="${child['title']}">${child['title']}</a>
|
||||
<a href="${page('info', child['rating_key'])}" title="${child['title']}">${child['title']}</a>
|
||||
</h3>
|
||||
</div>
|
||||
% else:
|
||||
<div class="item-children-instance-text-wrapper poster-item">
|
||||
<h3>
|
||||
<a href="info?rating_key=${child['rating_key']}" title="${child['title']}">${child['title']}</a>
|
||||
<a href="${page('info', child['rating_key'])}" title="${child['title']}">${child['title']}</a>
|
||||
</h3>
|
||||
<h3 class="text-muted">${child['year']}</h3>
|
||||
</div>
|
||||
|
@@ -53,6 +53,9 @@ DOCUMENTATION :: END
|
||||
</%doc>
|
||||
|
||||
% if data != None:
|
||||
<%
|
||||
from plexpy.helpers import page
|
||||
%>
|
||||
% if data['results_count'] > 0:
|
||||
% if 'collection' in data['results_list'] and data['results_list']['collection']:
|
||||
<div class="item-children-wrapper">
|
||||
@@ -62,9 +65,9 @@ DOCUMENTATION :: END
|
||||
<ul class="item-children-instance list-unstyled">
|
||||
% for child in data['results_list']['collection']:
|
||||
<li>
|
||||
<a href="info?rating_key=${child['rating_key']}" id="${child['rating_key']}">
|
||||
<a href="${page('info', child['rating_key'])}" id="${child['rating_key']}">
|
||||
<div class="item-children-poster">
|
||||
<div class="item-children-poster-face poster-item" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=450&fallback=poster);"></div>
|
||||
<div class="item-children-poster-face poster-item" style="background-image: url(${page('pms_image_proxy', child['thumb'], child['rating_key'], 300, 450, fallback='poster')});"></div>
|
||||
% if _session['user_group'] == 'admin':
|
||||
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
|
||||
% endif
|
||||
@@ -87,9 +90,9 @@ DOCUMENTATION :: END
|
||||
<ul class="item-children-instance list-unstyled">
|
||||
% for child in data['results_list']['movie']:
|
||||
<li>
|
||||
<a href="info?rating_key=${child['rating_key']}" id="${child['rating_key']}">
|
||||
<a href="${page('info', child['rating_key'])}" id="${child['rating_key']}">
|
||||
<div class="item-children-poster">
|
||||
<div class="item-children-poster-face poster-item" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=450&fallback=poster);"></div>
|
||||
<div class="item-children-poster-face poster-item" style="background-image: url(${page('pms_image_proxy', child['thumb'], child['rating_key'], 300, 450, fallback='poster')});"></div>
|
||||
% if _session['user_group'] == 'admin':
|
||||
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
|
||||
% endif
|
||||
@@ -112,9 +115,9 @@ DOCUMENTATION :: END
|
||||
<ul class="item-children-instance list-unstyled">
|
||||
% for child in data['results_list']['show']:
|
||||
<li>
|
||||
<a href="info?rating_key=${child['rating_key']}" id="${child['rating_key']}">
|
||||
<a href="${page('info', child['rating_key'])}" id="${child['rating_key']}">
|
||||
<div class="item-children-poster">
|
||||
<div class="item-children-poster-face poster-item" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=450&fallback=poster);"></div>
|
||||
<div class="item-children-poster-face poster-item" style="background-image: url(${page('pms_image_proxy', child['thumb'], child['rating_key'], 300, 450, fallback='poster')});"></div>
|
||||
% if _session['user_group'] == 'admin':
|
||||
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
|
||||
% endif
|
||||
@@ -137,9 +140,9 @@ DOCUMENTATION :: END
|
||||
<ul class="item-children-instance list-unstyled">
|
||||
% for child in data['results_list']['season']:
|
||||
<li>
|
||||
<a href="info?rating_key=${child['rating_key']}" id="${child['rating_key']}">
|
||||
<a href="${page('info', child['rating_key'])}" id="${child['rating_key']}">
|
||||
<div class="item-children-poster">
|
||||
<div class="item-children-poster-face poster-item" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=450&fallback=poster);"></div>
|
||||
<div class="item-children-poster-face poster-item" style="background-image: url(${page('pms_image_proxy', child['thumb'], child['rating_key'], 300, 450, fallback='poster')});"></div>
|
||||
% if _session['user_group'] == 'admin':
|
||||
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
|
||||
% endif
|
||||
@@ -162,9 +165,9 @@ DOCUMENTATION :: END
|
||||
<ul class="item-children-instance list-unstyled">
|
||||
% for child in data['results_list']['episode']:
|
||||
<li>
|
||||
<a href="info?rating_key=${child['rating_key']}" id="${child['rating_key']}">
|
||||
<a href="${page('info', child['rating_key'])}" id="${child['rating_key']}">
|
||||
<div class="item-children-poster">
|
||||
<div class="item-children-poster-face episode-item" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=500&height=250&fallback=art);"></div>
|
||||
<div class="item-children-poster-face episode-item" style="background-image: url(${page('pms_image_proxy', child['thumb'], child['rating_key'], 500, 280, fallback='art')});"></div>
|
||||
% if _session['user_group'] == 'admin':
|
||||
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
|
||||
% endif
|
||||
@@ -188,9 +191,9 @@ DOCUMENTATION :: END
|
||||
<ul class="item-children-instance list-unstyled">
|
||||
% for child in data['results_list']['artist']:
|
||||
<li>
|
||||
<a href="info?rating_key=${child['rating_key']}" id="${child['rating_key']}">
|
||||
<a href="${page('info', child['rating_key'])}" id="${child['rating_key']}">
|
||||
<div class="item-children-poster">
|
||||
<div class="item-children-poster-face cover-item" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=300&fallback=cover);"></div>
|
||||
<div class="item-children-poster-face cover-item" style="background-image: url(${page('pms_image_proxy', child['thumb'], child['rating_key'], 300, 300, fallback='cover')});"></div>
|
||||
% if _session['user_group'] == 'admin':
|
||||
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
|
||||
% endif
|
||||
@@ -212,9 +215,9 @@ DOCUMENTATION :: END
|
||||
<ul class="item-children-instance list-unstyled">
|
||||
% for child in data['results_list']['album']:
|
||||
<li>
|
||||
<a href="info?rating_key=${child['rating_key']}" id="${child['rating_key']}">
|
||||
<a href="${page('info', child['rating_key'])}" id="${child['rating_key']}">
|
||||
<div class="item-children-poster">
|
||||
<div class="item-children-poster-face cover-item" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=300&fallback=cover);"></div>
|
||||
<div class="item-children-poster-face cover-item" style="background-image: url(${page('pms_image_proxy', child['thumb'], child['rating_key'], 300, 300, fallback='cover')});"></div>
|
||||
% if _session['user_group'] == 'admin':
|
||||
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
|
||||
% endif
|
||||
@@ -237,9 +240,9 @@ DOCUMENTATION :: END
|
||||
<ul class="item-children-instance list-unstyled">
|
||||
% for child in data['results_list']['track']:
|
||||
<li>
|
||||
<a href="info?rating_key=${child['rating_key']}" id="${child['rating_key']}">
|
||||
<a href="${page('info', child['rating_key'])}" id="${child['rating_key']}">
|
||||
<div class="item-children-poster">
|
||||
<div class="item-children-poster-face cover-item" style="background-image: url(pms_image_proxy?img=${child['parent_thumb']}&width=300&height=300&fallback=cover);">
|
||||
<div class="item-children-poster-face cover-item" style="background-image: url(${page('pms_image_proxy', child['parent_thumb'], child['parent_rating_key'], 300, 300, fallback='cover')});">
|
||||
<div class="item-children-card-overlay">
|
||||
<div class="item-children-overlay-text">
|
||||
Track ${child['media_index']}
|
||||
|
@@ -40,7 +40,6 @@ var hc_plays_by_day_options = {
|
||||
}
|
||||
}
|
||||
},
|
||||
colors: ['#F9AA03', '#FFFFFF', '#FF4747'],
|
||||
xAxis: {
|
||||
type: 'datetime',
|
||||
labels: {
|
||||
|
@@ -23,7 +23,6 @@ var hc_plays_by_dayofweek_options = {
|
||||
credits: {
|
||||
enabled: false
|
||||
},
|
||||
colors: ['#F9AA03', '#FFFFFF', '#FF4747'],
|
||||
xAxis: {
|
||||
categories: [{}],
|
||||
labels: {
|
||||
|
@@ -23,7 +23,6 @@ var hc_plays_by_hourofday_options = {
|
||||
credits: {
|
||||
enabled: false
|
||||
},
|
||||
colors: ['#F9AA03', '#FFFFFF', '#FF4747'],
|
||||
xAxis: {
|
||||
categories: [{}],
|
||||
labels: {
|
||||
|
@@ -23,7 +23,6 @@ var hc_plays_by_month_options = {
|
||||
credits: {
|
||||
enabled: false
|
||||
},
|
||||
colors: ['#F9AA03', '#FFFFFF', '#FF4747'],
|
||||
xAxis: {
|
||||
labels: {
|
||||
style: {
|
||||
|
@@ -23,7 +23,6 @@ var hc_plays_by_platform_options = {
|
||||
credits: {
|
||||
enabled: false
|
||||
},
|
||||
colors: ['#F9AA03', '#FFFFFF', '#FF4747'],
|
||||
xAxis: {
|
||||
categories: [{}],
|
||||
labels: {
|
||||
|
@@ -23,7 +23,6 @@ var hc_plays_by_platform_by_stream_type_options = {
|
||||
credits: {
|
||||
enabled: false
|
||||
},
|
||||
colors: ['#F9AA03', '#FFFFFF', '#FF4747'],
|
||||
xAxis: {
|
||||
categories: [{}],
|
||||
labels: {
|
||||
|
@@ -23,7 +23,6 @@ var hc_plays_by_source_resolution_options = {
|
||||
credits: {
|
||||
enabled: false
|
||||
},
|
||||
colors: ['#F9AA03', '#FFFFFF', '#FF4747'],
|
||||
xAxis: {
|
||||
categories: [{}],
|
||||
labels: {
|
||||
|
@@ -23,7 +23,6 @@ var hc_plays_by_stream_resolution_options = {
|
||||
credits: {
|
||||
enabled: false
|
||||
},
|
||||
colors: ['#F9AA03', '#FFFFFF', '#FF4747'],
|
||||
xAxis: {
|
||||
categories: [{}],
|
||||
labels: {
|
||||
|
@@ -40,7 +40,6 @@ var hc_plays_by_stream_type_options = {
|
||||
}
|
||||
}
|
||||
},
|
||||
colors: ['#F9AA03', '#FFFFFF', '#FF4747'],
|
||||
xAxis: {
|
||||
type: 'datetime',
|
||||
labels: {
|
||||
|
@@ -23,7 +23,6 @@ var hc_plays_by_user_options = {
|
||||
credits: {
|
||||
enabled: false
|
||||
},
|
||||
colors: ['#F9AA03', '#FFFFFF', '#FF4747'],
|
||||
xAxis: {
|
||||
categories: [{}],
|
||||
labels: {
|
||||
|
@@ -23,7 +23,6 @@ var hc_plays_by_user_by_stream_type_options = {
|
||||
credits: {
|
||||
enabled: false
|
||||
},
|
||||
colors: ['#F9AA03', '#FFFFFF', '#FF4747'],
|
||||
xAxis: {
|
||||
categories: [{}],
|
||||
labels: {
|
||||
|
@@ -258,33 +258,31 @@ $.cachedScript = function (url) {
|
||||
function isPrivateIP(ip_address) {
|
||||
var defer = $.Deferred();
|
||||
|
||||
$.cachedScript('js/ipaddr.min.js').done(function () {
|
||||
if (ipaddr.isValid(ip_address)) {
|
||||
var addr = ipaddr.process(ip_address);
|
||||
if (ipaddr.isValid(ip_address)) {
|
||||
var addr = ipaddr.process(ip_address);
|
||||
|
||||
var rangeList = [];
|
||||
if (addr.kind() === 'ipv4') {
|
||||
rangeList = [
|
||||
ipaddr.parseCIDR('127.0.0.0/8'),
|
||||
ipaddr.parseCIDR('10.0.0.0/8'),
|
||||
ipaddr.parseCIDR('172.16.0.0/12'),
|
||||
ipaddr.parseCIDR('192.168.0.0/16')
|
||||
];
|
||||
} else {
|
||||
rangeList = [
|
||||
ipaddr.parseCIDR('fd00::/8')
|
||||
];
|
||||
}
|
||||
|
||||
if (ipaddr.subnetMatch(addr, rangeList, -1) >= 0) {
|
||||
defer.resolve();
|
||||
} else {
|
||||
defer.reject();
|
||||
}
|
||||
var rangeList = [];
|
||||
if (addr.kind() === 'ipv4') {
|
||||
rangeList = [
|
||||
ipaddr.parseCIDR('127.0.0.0/8'),
|
||||
ipaddr.parseCIDR('10.0.0.0/8'),
|
||||
ipaddr.parseCIDR('172.16.0.0/12'),
|
||||
ipaddr.parseCIDR('192.168.0.0/16')
|
||||
];
|
||||
} else {
|
||||
defer.resolve('n/a');
|
||||
rangeList = [
|
||||
ipaddr.parseCIDR('fd00::/8')
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
if (ipaddr.subnetMatch(addr, rangeList, -1) >= 0) {
|
||||
defer.resolve();
|
||||
} else {
|
||||
defer.reject();
|
||||
}
|
||||
} else {
|
||||
defer.resolve('n/a');
|
||||
}
|
||||
|
||||
return defer.promise();
|
||||
}
|
||||
@@ -463,8 +461,9 @@ $('*').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;
|
||||
function humanFileSize(bytes, si = true) {
|
||||
//var thresh = si ? 1000 : 1024;
|
||||
var thresh = 1024; // Always divide by 2^10 but display SI units
|
||||
if (Math.abs(bytes) < thresh) {
|
||||
return bytes + ' B';
|
||||
}
|
||||
@@ -568,8 +567,11 @@ function getPlexHeaders() {
|
||||
'X-Plex-Client-Identifier': getLocalStorage('Tautulli_ClientID', uuidv4(), false),
|
||||
'X-Plex-Platform': p.name,
|
||||
'X-Plex-Platform-Version': p.version,
|
||||
'X-Plex-Model': 'Plex OAuth',
|
||||
'X-Plex-Device': p.os,
|
||||
'X-Plex-Device-Name': p.name
|
||||
'X-Plex-Device-Name': p.name,
|
||||
'X-Plex-Device-Screen-Resolution': window.screen.width + 'x' + window.screen.height,
|
||||
'X-Plex-Language': 'en'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -655,7 +657,21 @@ function PlexOAuth(success, error, pre) {
|
||||
const pin = data.pin;
|
||||
const code = data.code;
|
||||
|
||||
plex_oauth_window.location = 'https://app.plex.tv/auth/#!?clientID=' + x_plex_headers['X-Plex-Client-Identifier'] + '&code=' + code;
|
||||
var oauth_params = {
|
||||
'clientID': x_plex_headers['X-Plex-Client-Identifier'],
|
||||
'context[device][product]': x_plex_headers['X-Plex-Product'],
|
||||
'context[device][version]': x_plex_headers['X-Plex-Version'],
|
||||
'context[device][platform]': x_plex_headers['X-Plex-Platform'],
|
||||
'context[device][platformVersion]': x_plex_headers['X-Plex-Platform-Version'],
|
||||
'context[device][device]': x_plex_headers['X-Plex-Device'],
|
||||
'context[device][deviceName]': x_plex_headers['X-Plex-Device-Name'],
|
||||
'context[device][model]': x_plex_headers['X-Plex-Model'],
|
||||
'context[device][screenResolution]': x_plex_headers['X-Plex-Device-Screen-Resolution'],
|
||||
'context[device][layout]': 'desktop',
|
||||
'code': code
|
||||
}
|
||||
|
||||
plex_oauth_window.location = 'https://app.plex.tv/auth/#!?' + encodeData(oauth_params);
|
||||
polling = pin;
|
||||
|
||||
(function poll() {
|
||||
@@ -693,4 +709,76 @@ function PlexOAuth(success, error, pre) {
|
||||
error()
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function encodeData(data) {
|
||||
return Object.keys(data).map(function(key) {
|
||||
return [key, data[key]].map(encodeURIComponent).join("=");
|
||||
}).join("&");
|
||||
}
|
||||
|
||||
function page(endpoint, ...args) {
|
||||
let endpoints = {
|
||||
'pms_image_proxy': pms_image_proxy,
|
||||
'info': info_page,
|
||||
'library': library_page,
|
||||
'user': user_page
|
||||
};
|
||||
|
||||
var params = {};
|
||||
|
||||
if (endpoint in endpoints) {
|
||||
params = endpoints[endpoint](...args);
|
||||
}
|
||||
|
||||
return endpoint + '?' + $.param(params).replace(/'/g, '%27');
|
||||
}
|
||||
|
||||
function pms_image_proxy(img, rating_key, width, height, opacity, background, blur, fallback, refresh, clip, img_format) {
|
||||
var params = {};
|
||||
|
||||
if (img != null) { params.img = img; }
|
||||
if (rating_key != null) { params.rating_key = rating_key; }
|
||||
if (width != null) { params.width = width; }
|
||||
if (height != null) { params.height = height; }
|
||||
if (opacity != null) { params.opacity = opacity; }
|
||||
if (background != null) { params.background = background; }
|
||||
if (blur != null) { params.blur = blur; }
|
||||
if (fallback != null) { params.fallback = fallback; }
|
||||
if (refresh != null) { params.refresh = true; }
|
||||
if (clip != null) { params.clip = true; }
|
||||
if (img_format != null) { params.img_format = img_format; }
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
function info_page(rating_key, guid, history, live) {
|
||||
var params = {};
|
||||
|
||||
if (live && history) {
|
||||
params.guid = guid;
|
||||
} else {
|
||||
params.rating_key = rating_key;
|
||||
}
|
||||
|
||||
if (history) { params.source = 'history'; }
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
function library_page(section_id) {
|
||||
var params = {};
|
||||
|
||||
if (section_id != null) { params.section_id = section_id; }
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
function user_page(user_id, user) {
|
||||
var params = {};
|
||||
|
||||
if (user_id != null) { params.user_id = user_id; }
|
||||
if (user != null) { params.user = user; }
|
||||
|
||||
return params;
|
||||
}
|
||||
|
@@ -36,10 +36,10 @@ history_table_options = {
|
||||
"targets": [0],
|
||||
"data": null,
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (rowData['id'] === null) {
|
||||
if (rowData['row_id'] === null) {
|
||||
$(td).html('');
|
||||
} else {
|
||||
$(td).html('<button class="btn btn-xs btn-warning" data-id="' + rowData['id'] + '"><i class="fa fa-trash-o fa-fw"></i> Delete</button>');
|
||||
$(td).html('<button class="btn btn-xs btn-warning" data-id="' + rowData['row_id'] + '"><i class="fa fa-trash-o fa-fw"></i> Delete</button>');
|
||||
}
|
||||
},
|
||||
"width": "5%",
|
||||
@@ -49,7 +49,7 @@ history_table_options = {
|
||||
},
|
||||
{
|
||||
"targets": [1],
|
||||
"data":"date",
|
||||
"data": "date",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
var date = moment(cellData, "X").format(date_format);
|
||||
if (rowData['state'] !== null) {
|
||||
@@ -77,13 +77,13 @@ history_table_options = {
|
||||
},
|
||||
{
|
||||
"targets": [2],
|
||||
"data":"friendly_name",
|
||||
"data": "friendly_name",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData !== '') {
|
||||
if (rowData['user_id']) {
|
||||
$(td).html('<a href="user?user_id=' + rowData['user_id'] + '">' + cellData + '</a>');
|
||||
$(td).html('<a href="' + page('user', rowData['user_id']) + '">' + cellData + '</a>');
|
||||
} else {
|
||||
$(td).html('<a href="user?user=' + rowData['user'] + '">' + cellData + '</a>');
|
||||
$(td).html('<a href="' + page('user', null, rowData['user']) + '">' + cellData + '</a>');
|
||||
}
|
||||
} else {
|
||||
$(td).html(cellData);
|
||||
@@ -112,7 +112,7 @@ history_table_options = {
|
||||
},
|
||||
{
|
||||
"targets": [4],
|
||||
"data":"platform",
|
||||
"data": "platform",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData !== '') {
|
||||
$(td).html(cellData);
|
||||
@@ -123,6 +123,17 @@ history_table_options = {
|
||||
},
|
||||
{
|
||||
"targets": [5],
|
||||
"data": "product",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData !== '') {
|
||||
$(td).html(cellData);
|
||||
}
|
||||
},
|
||||
"width": "10%",
|
||||
"className": "no-wrap"
|
||||
},
|
||||
{
|
||||
"targets": [6],
|
||||
"data": "player",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData !== '') {
|
||||
@@ -137,46 +148,54 @@ history_table_options = {
|
||||
$(td).html('<div><a href="#" data-target="#info-modal" data-toggle="modal"><div style="float: left;">' + transcode_dec + ' ' + cellData + '</div></a></div>');
|
||||
}
|
||||
},
|
||||
"width": "12%",
|
||||
"width": "10%",
|
||||
"className": "no-wrap modal-control"
|
||||
},
|
||||
{
|
||||
"targets": [6],
|
||||
"data":"full_title",
|
||||
"targets": [7],
|
||||
"data": "full_title",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData !== '') {
|
||||
var icon = '';
|
||||
var icon_title = '';
|
||||
var parent_info = '';
|
||||
var media_type = '';
|
||||
var thumb_popover = '';
|
||||
var source = (rowData['state'] === null) ? 'source=history&' : '';
|
||||
var fallback = (rowData['live']) ? 'poster-live' : 'poster';
|
||||
var history = (rowData['state'] === null);
|
||||
if (rowData['media_type'] === 'movie') {
|
||||
icon = (rowData['live']) ? 'fa-broadcast-tower' : 'fa-film';
|
||||
icon_title = (rowData['live']) ? 'Live TV' : 'Movie';
|
||||
if (rowData['year']) { parent_info = ' (' + rowData['year'] + ')'; }
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Movie"><i class="fa fa-film fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=poster" data-height="120" data-width="80">' + cellData + parent_info + '</span>'
|
||||
$(td).html('<div class="history-title"><a href="info?' + source + 'rating_key=' + rowData['rating_key'] + '"><div style="float: left;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="' + icon_title + '"><i class="fa ' + icon + ' fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="' + page('pms_image_proxy', rowData['thumb'], rowData['rating_key'], 300, 450, null, null, null, fallback) + '" data-height="120" data-width="80">' + cellData + parent_info + '</span>';
|
||||
$(td).html('<div class="history-title"><a href="' + page('info', rowData['rating_key'], rowData['guid'], history, rowData['live']) + '"><div style="float: left;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else if (rowData['media_type'] === 'episode') {
|
||||
if (rowData['parent_media_index'] && rowData['media_index']) { parent_info = ' (S' + rowData['parent_media_index'] + '· E' + rowData['media_index'] + ')'; }
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Episode"><i class="fa fa-television fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=poster" data-height="120" data-width="80">' + cellData + parent_info + '</span>'
|
||||
$(td).html('<div class="history-title"><a href="info?' + source + 'rating_key=' + rowData['rating_key'] + '"><div style="float: left;" >' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
icon = (rowData['live']) ? 'fa-broadcast-tower' : 'fa-television';
|
||||
icon_title = (rowData['live']) ? 'Live TV' : 'Episode';
|
||||
if (!isNaN(parseInt(rowData['parent_media_index'])) && !isNaN(parseInt(rowData['media_index']))) { parent_info = ' (S' + rowData['parent_media_index'] + ' · E' + rowData['media_index'] + ')'; }
|
||||
else if (rowData['live'] && rowData['originally_available_at']) { parent_info = ' (' + rowData['originally_available_at'] + ')'; }
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="' + icon_title + '"><i class="fa ' + icon + ' fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="' + page('pms_image_proxy', rowData['thumb'], rowData['rating_key'], 300, 450, null, null, null, fallback) + '" data-height="120" data-width="80">' + cellData + parent_info + '</span>';
|
||||
$(td).html('<div class="history-title"><a href="' + page('info', rowData['rating_key'], rowData['guid'], history, rowData['live']) + '"><div style="float: left;" >' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else if (rowData['media_type'] === 'track') {
|
||||
if (rowData['parent_title']) { parent_info = ' (' + rowData['parent_title'] + ')'; }
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Track"><i class="fa fa-music fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=300&fallback=cover" data-height="80" data-width="80">' + cellData + parent_info + '</span>'
|
||||
$(td).html('<div class="history-title"><a href="info?' + source + 'rating_key=' + rowData['rating_key'] + '"><div style="float: left;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="' + page('pms_image_proxy', rowData['thumb'], rowData['rating_key'], 300, 300, null, null, null, 'cover') + '" data-height="80" data-width="80">' + cellData + parent_info + '</span>';
|
||||
$(td).html('<div class="history-title"><a href="' + page('info', rowData['rating_key'], rowData['guid'], history, rowData['live']) + '"><div style="float: left;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else if (rowData['media_type'] === 'clip') {
|
||||
$(td).html(cellData);
|
||||
} else {
|
||||
$(td).html('<a href="info?rating_key=' + rowData['rating_key'] + '">' + cellData + '</a>');
|
||||
$(td).html('<a href="' + page('info', rowData['rating_key']) + '">' + cellData + '</a>');
|
||||
}
|
||||
}
|
||||
},
|
||||
"width": "33%",
|
||||
"width": "25%",
|
||||
"className": "datatable-wrap"
|
||||
},
|
||||
{
|
||||
"targets": [7],
|
||||
"data":"started",
|
||||
"targets": [8],
|
||||
"data": "started",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData === null) {
|
||||
$(td).html('n/a');
|
||||
@@ -189,8 +208,8 @@ history_table_options = {
|
||||
"className": "no-wrap"
|
||||
},
|
||||
{
|
||||
"targets": [8],
|
||||
"data":"paused_counter",
|
||||
"targets": [9],
|
||||
"data": "paused_counter",
|
||||
"render": function (data, type, full) {
|
||||
if (data !== null) {
|
||||
return Math.round(moment.duration(data, 'seconds').as('minutes')) + ' mins';
|
||||
@@ -203,8 +222,8 @@ history_table_options = {
|
||||
"className": "no-wrap"
|
||||
},
|
||||
{
|
||||
"targets": [9],
|
||||
"data":"stopped",
|
||||
"targets": [10],
|
||||
"data": "stopped",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData === null || (rowData['state'] != null && rowData['state'] != "stopped")) {
|
||||
$(td).html('n/a');
|
||||
@@ -217,8 +236,8 @@ history_table_options = {
|
||||
"className": "no-wrap"
|
||||
},
|
||||
{
|
||||
"targets": [10],
|
||||
"data":"duration",
|
||||
"targets": [11],
|
||||
"data": "duration",
|
||||
"render": function (data, type, full) {
|
||||
if (data !== null) {
|
||||
return Math.round(moment.duration(data, 'seconds').as('minutes')) + ' mins';
|
||||
@@ -231,7 +250,7 @@ history_table_options = {
|
||||
"className": "no-wrap"
|
||||
},
|
||||
{
|
||||
"targets": [11],
|
||||
"targets": [12],
|
||||
"data": "watched_status",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData == 1) {
|
||||
@@ -298,19 +317,19 @@ history_table_options = {
|
||||
"rowCallback": function (row, rowData, rowIndex) {
|
||||
if (rowData['group_count'] == 1) {
|
||||
// if no grouped rows simply toggle the delete button
|
||||
if ($.inArray(rowData['id'], history_to_delete) !== -1) {
|
||||
$(row).find('button[data-id="' + rowData['id'] + '"]').toggleClass('btn-warning').toggleClass('btn-danger');
|
||||
if ($.inArray(rowData['row_id'], history_to_delete) !== -1) {
|
||||
$(row).find('button[data-id="' + rowData['row_id'] + '"]').toggleClass('btn-warning').toggleClass('btn-danger');
|
||||
}
|
||||
} else if (rowData['id'] !== null) {
|
||||
} else if (rowData['row_id'] !== null) {
|
||||
// if grouped rows
|
||||
// toggle the parent button to danger
|
||||
$(row).find('button[data-id="' + rowData['id'] + '"]').toggleClass('btn-warning').toggleClass('btn-danger');
|
||||
$(row).find('button[data-id="' + rowData['row_id'] + '"]').toggleClass('btn-warning').toggleClass('btn-danger');
|
||||
// check if any child rows are not selected
|
||||
var group_ids = rowData['group_ids'].split(',').map(Number);
|
||||
group_ids.forEach(function (id) {
|
||||
var index = $.inArray(id, history_to_delete);
|
||||
if (index == -1) {
|
||||
$(row).find('button[data-id="' + rowData['id'] + '"]').addClass('btn-warning').removeClass('btn-danger');
|
||||
$(row).find('button[data-id="' + rowData['row_id'] + '"]').addClass('btn-warning').removeClass('btn-danger');
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -334,7 +353,7 @@ $('.history_table').on('click', '> tbody > tr > td.modal-control', function () {
|
||||
var rowData = row.data();
|
||||
|
||||
$.get('get_stream_data', {
|
||||
row_id: rowData['id'],
|
||||
row_id: rowData['row_id'],
|
||||
session_key: rowData['session_key'],
|
||||
user: rowData['friendly_name']
|
||||
}).then(function (jqXHR) {
|
||||
@@ -363,9 +382,9 @@ $('.history_table').on('click', '> tbody > tr > td.delete-control > button', fun
|
||||
|
||||
if (rowData['group_count'] == 1) {
|
||||
// if no grouped rows simply add or remove row from history_to_delete
|
||||
var index = $.inArray(rowData['id'], history_to_delete);
|
||||
var index = $.inArray(rowData['row_id'], history_to_delete);
|
||||
if (index === -1) {
|
||||
history_to_delete.push(rowData['id']);
|
||||
history_to_delete.push(rowData['row_id']);
|
||||
} else {
|
||||
history_to_delete.splice(index, 1);
|
||||
}
|
||||
@@ -489,7 +508,8 @@ function childTableFormat(rowData) {
|
||||
'<th align="left" id="friendly_name">User</th>' +
|
||||
'<th align="left" id="ip_address">IP Address</th>' +
|
||||
'<th align="left" id="platform">Platform</th>' +
|
||||
'<th align="left" id="platform">Player</th>' +
|
||||
'<th align="left" id="product">Product</th>' +
|
||||
'<th align="left" id="player">Player</th>' +
|
||||
'<th align="left" id="title">Title</th>' +
|
||||
'<th align="left" id="started">Started</th>' +
|
||||
'<th align="left" id="paused_counter">Paused</th>' +
|
||||
@@ -529,7 +549,7 @@ function createChildTable(row, rowData) {
|
||||
var childRowData = childRow.data();
|
||||
|
||||
$.get('get_stream_data', {
|
||||
row_id: childRowData['id'],
|
||||
row_id: childRowData['row_id'],
|
||||
user: childRowData['friendly_name']
|
||||
}).then(function (jqXHR) {
|
||||
$("#info-modal").html(jqXHR);
|
||||
@@ -556,9 +576,9 @@ function createChildTable(row, rowData) {
|
||||
var childRowData = childRow.data();
|
||||
|
||||
// add or remove row from history_to_delete
|
||||
var index = $.inArray(childRowData['id'], history_to_delete);
|
||||
var index = $.inArray(childRowData['row_id'], history_to_delete);
|
||||
if (index === -1) {
|
||||
history_to_delete.push(childRowData['id']);
|
||||
history_to_delete.push(childRowData['row_id']);
|
||||
} else {
|
||||
history_to_delete.splice(index, 1);
|
||||
}
|
||||
|
@@ -63,9 +63,9 @@ history_table_modal_options = {
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData !== '') {
|
||||
if (rowData['user_id']) {
|
||||
$(td).html('<a href="user?user_id=' + rowData['user_id'] + '">' + cellData + '</a>');
|
||||
$(td).html('<a href="' + page('user', rowData['user_id']) + '">' + cellData + '</a>');
|
||||
} else {
|
||||
$(td).html('<a href="user?user=' + rowData['user'] + '">' + cellData + '</a>');
|
||||
$(td).html('<a href="' + page('user', null, rowData['user']) + '">' + cellData + '</a>');
|
||||
}
|
||||
} else {
|
||||
$(td).html(cellData);
|
||||
@@ -98,26 +98,34 @@ history_table_modal_options = {
|
||||
"data":"full_title",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData !== '') {
|
||||
var icon = '';
|
||||
var icon_title = '';
|
||||
var parent_info = '';
|
||||
var media_type = '';
|
||||
var thumb_popover = '';
|
||||
var fallback = (rowData['live']) ? 'poster-live' : 'poster';
|
||||
if (rowData['media_type'] === 'movie') {
|
||||
icon = (rowData['live']) ? 'fa-broadcast-tower' : 'fa-film';
|
||||
icon_title = (rowData['live']) ? 'Live TV' : 'Movie';
|
||||
if (rowData['year']) { parent_info = ' (' + rowData['year'] + ')'; }
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Movie"><i class="fa fa-film fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=poster" data-height="120" data-width="80">' + cellData + parent_info + '</span>'
|
||||
$(td).html('<div class="history-title"><a href="info?source=history&rating_key=' + rowData['rating_key'] + '"><div style="float: left;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="' + icon_title + '"><i class="fa ' + icon + ' fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="' + page('pms_image_proxy', rowData['thumb'], rowData['rating_key'], 300, 450, null, null, null, fallback) + '" data-height="120" data-width="80">' + cellData + parent_info + '</span>';
|
||||
$(td).html('<div class="history-title"><a href="' + page('info', rowData['rating_key'], rowData['guid'], true, rowData['live']) + '"><div style="float: left;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else if (rowData['media_type'] === 'episode') {
|
||||
if (rowData['parent_media_index'] && rowData['media_index']) { parent_info = ' (S' + rowData['parent_media_index'] + '· E' + rowData['media_index'] + ')'; }
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Episode"><i class="fa fa-television fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=poster" data-height="120" data-width="80">' + cellData + parent_info + '</span>'
|
||||
$(td).html('<div class="history-title"><a href="info?source=history&rating_key=' + rowData['rating_key'] + '"><div style="float: left;" >' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
icon = (rowData['live']) ? 'fa-broadcast-tower' : 'fa-television';
|
||||
icon_title = (rowData['live']) ? 'Live TV' : 'Episode';
|
||||
if (!isNaN(parseInt(rowData['parent_media_index'])) && !isNaN(parseInt(rowData['media_index']))) { parent_info = ' (S' + rowData['parent_media_index'] + ' · E' + rowData['media_index'] + ')'; }
|
||||
else if (rowData['live'] && rowData['originally_available_at']) { parent_info = ' (' + rowData['originally_available_at'] + ')'; }
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="' + icon_title + '"><i class="fa ' + icon + ' fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="' + page('pms_image_proxy', rowData['thumb'], rowData['rating_key'], 300, 450, null, null, null, fallback) + '" data-height="120" data-width="80">' + cellData + parent_info + '</span>';
|
||||
$(td).html('<div class="history-title"><a href="' + page('info', rowData['rating_key'], rowData['guid'], true, rowData['live']) + '"><div style="float: left;" >' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else if (rowData['media_type'] === 'track') {
|
||||
if (rowData['parent_title']) { parent_info = ' (' + rowData['parent_title'] + ')'; }
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Track"><i class="fa fa-music fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=300&fallback=cover" data-height="80" data-width="80">' + cellData + parent_info + '</span>'
|
||||
$(td).html('<div class="history-title"><a href="info?source=history&rating_key=' + rowData['rating_key'] + '"><div style="float: left;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="' + page('pms_image_proxy', rowData['thumb'], rowData['rating_key'], 300, 300, null, null, null, 'cover') + '" data-height="80" data-width="80">' + cellData + parent_info + '</span>';
|
||||
$(td).html('<div class="history-title"><a href="' + page('info', rowData['rating_key'], rowData['guid'], true, rowData['live']) + '"><div style="float: left;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else {
|
||||
$(td).html('<a href="info?rating_key=' + rowData['rating_key'] + '">' + cellData + '</a>');
|
||||
$(td).html('<a href="' + page('info', rowData['rating_key']) + '">' + cellData + '</a>');
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -161,7 +169,7 @@ $('.history_table').on('click', 'td.modal-control', function () {
|
||||
function showStreamDetails() {
|
||||
$.ajax({
|
||||
url: 'get_stream_data',
|
||||
data: { row_id: rowData['id'], user: rowData['friendly_name'] },
|
||||
data: { row_id: rowData['row_id'], user: rowData['friendly_name'] },
|
||||
cache: false,
|
||||
async: true,
|
||||
complete: function (xhr, status) {
|
||||
|
@@ -27,8 +27,8 @@ libraries_list_table_options = {
|
||||
"data": null,
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
$(td).html('<div class="edit-library-toggles">' +
|
||||
'<button class="btn btn-xs btn-warning delete-library" data-id="' + rowData['section_id'] + '" data-toggle="button"><i class="fa fa-trash-o fa-fw"></i> Delete</button> ' +
|
||||
'<button class="btn btn-xs btn-warning purge-library" data-id="' + rowData['section_id'] + '" data-toggle="button"><i class="fa fa-eraser fa-fw"></i> Purge</button>   ' +
|
||||
'<button class="btn btn-xs btn-warning delete-library" data-id="' + rowData['row_id'] + '" data-toggle="button"><i class="fa fa-trash-o fa-fw"></i> Delete</button> ' +
|
||||
'<button class="btn btn-xs btn-warning purge-library" data-id="' + rowData['row_id'] + '" data-toggle="button"><i class="fa fa-eraser fa-fw"></i> Purge</button>   ' +
|
||||
'<input type="checkbox" id="keep_history-' + rowData['section_id'] + '" name="keep_history" value="1" ' + rowData['keep_history'] + '><label class="edit-tooltip" for="keep_history-' + rowData['section_id'] + '" data-toggle="tooltip" title="Toggle History"><i class="fa fa-history fa-lg fa-fw"></i></label> ' +
|
||||
'</div>');
|
||||
},
|
||||
@@ -41,14 +41,16 @@ libraries_list_table_options = {
|
||||
"targets": [1],
|
||||
"data": "library_thumb",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
var inactive = '';
|
||||
if (!rowData['is_active']) { inactive = '<span class="inactive-library-tooltip" data-toggle="tooltip" title="Library not on Plex server"><i class="fa fa-exclamation-triangle"></i></span>'; }
|
||||
if (cellData !== null && cellData !== '') {
|
||||
if (rowData['library_thumb'].substring(0, 4) == "http") {
|
||||
$(td).html('<a href="library?section_id=' + rowData['section_id'] + '"><div class="libraries-poster-face" style="background-image: url(' + rowData['library_thumb'] + ');"></div></a>');
|
||||
$(td).html('<a href="' + page('library', rowData['section_id']) + '"><div class="libraries-poster-face" style="background-image: url(' + rowData['library_thumb'] + ');">' + inactive + '</div></a>');
|
||||
} else {
|
||||
$(td).html('<a href="library?section_id=' + rowData['section_id'] + '"><div class="libraries-poster-face svg-icon library-' + rowData['section_type'] + '"></div></a>');
|
||||
$(td).html('<a href="' + page('library', rowData['section_id']) + '"><div class="libraries-poster-face svg-icon library-' + rowData['section_type'] + '">' + inactive + '</div></a>');
|
||||
}
|
||||
} else {
|
||||
$(td).html('<a href="library?section_id=' + rowData['section_id'] + '"><div class="libraries-poster-face" style="background-image: url(../../images/cover.png);"></div></a>');
|
||||
$(td).html('<a href="' + page('library', rowData['section_id']) + '"><div class="libraries-poster-face" style="background-image: url(../../images/cover.png);">' + inactive + '</div></a>');
|
||||
}
|
||||
},
|
||||
"orderable": false,
|
||||
@@ -61,8 +63,8 @@ libraries_list_table_options = {
|
||||
"data": "section_name",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData !== null && cellData !== '') {
|
||||
$(td).html('<div data-id="' + rowData['section_id'] + '">' +
|
||||
'<a href="library?section_id=' + rowData['section_id'] + '">' + cellData + '</a>' +
|
||||
$(td).html('<div data-id="' + rowData['row_id'] + '">' +
|
||||
'<a href="' + page('library', rowData['section_id']) + '">' + cellData + '</a>' +
|
||||
'</div>');
|
||||
} else {
|
||||
$(td).html('n/a');
|
||||
@@ -137,45 +139,34 @@ libraries_list_table_options = {
|
||||
"data":"last_played",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData !== null && cellData !== '') {
|
||||
var icon = '';
|
||||
var icon_title = '';
|
||||
var parent_info = '';
|
||||
var media_type = '';
|
||||
var thumb_popover = '';
|
||||
var fallback = (rowData['live']) ? 'poster-live' : 'poster';
|
||||
if (rowData['media_type'] === 'movie') {
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Movie"><i class="fa fa-film fa-fw"></i></span>';
|
||||
if (rowData['rating_key']) {
|
||||
if (rowData['year']) { parent_info = ' (' + rowData['year'] + ')'; }
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=poster" data-height="120" data-width="80">' + cellData + parent_info + '</span>'
|
||||
$(td).html('<div class="history-title"><a href="info?source=history&rating_key=' + rowData['rating_key'] + '"><div style="float: left;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else {
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="images/poster.png" data-height="120" data-width="80">' + cellData + parent_info + '</span>'
|
||||
$(td).html('<div class="history-title"><div style="float: left;">' + media_type + ' ' + thumb_popover + '</div></div>');
|
||||
}
|
||||
icon = (rowData['live']) ? 'fa-broadcast-tower' : 'fa-film';
|
||||
icon_title = (rowData['live']) ? 'Live TV' : 'Movie';
|
||||
if (rowData['year']) { parent_info = ' (' + rowData['year'] + ')'; }
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="' + icon_title + '"><i class="fa ' + icon + ' fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="' + page('pms_image_proxy', rowData['thumb'], rowData['rating_key'], 300, 450, null, null, null, fallback) + '" data-height="120" data-width="80">' + cellData + parent_info + '</span>';
|
||||
$(td).html('<div class="history-title"><a href="' + page('info', rowData['rating_key'], rowData['guid'], true, rowData['live']) + '"><div style="float: left;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else if (rowData['media_type'] === 'episode') {
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Episode"><i class="fa fa-television fa-fw"></i></span>';
|
||||
if (rowData['rating_key']) {
|
||||
if (rowData['parent_media_index'] && rowData['media_index']) { parent_info = ' (S' + rowData['parent_media_index'] + '· E' + rowData['media_index'] + ')'; }
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=poster" data-height="120" data-width="80">' + cellData + parent_info + '</span>'
|
||||
$(td).html('<div class="history-title"><a href="info?source=history&rating_key=' + rowData['rating_key'] + '"><div style="float: left;" >' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else {
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="images/poster.png" data-height="120" data-width="80">' + cellData + parent_info + '</span>'
|
||||
$(td).html('<div class="history-title"><div style="float: left;" >' + media_type + ' ' + thumb_popover + '</div></div>');
|
||||
}
|
||||
icon = (rowData['live']) ? 'fa-broadcast-tower' : 'fa-television';
|
||||
icon_title = (rowData['live']) ? 'Live TV' : 'Episode';
|
||||
if (!isNaN(parseInt(rowData['parent_media_index'])) && !isNaN(parseInt(rowData['media_index']))) { parent_info = ' (S' + rowData['parent_media_index'] + ' · E' + rowData['media_index'] + ')'; }
|
||||
else if (rowData['live'] && rowData['originally_available_at']) { parent_info = ' (' + rowData['originally_available_at'] + ')'; }
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="' + icon_title + '"><i class="fa ' + icon + ' fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="' + page('pms_image_proxy', rowData['thumb'], rowData['rating_key'], 300, 450, null, null, null, fallback) + '" data-height="120" data-width="80">' + cellData + parent_info + '</span>';
|
||||
$(td).html('<div class="history-title"><a href="' + page('info', rowData['rating_key'], rowData['guid'], true, rowData['live']) + '"><div style="float: left;" >' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else if (rowData['media_type'] === 'track') {
|
||||
if (rowData['parent_title']) { parent_info = ' (' + rowData['parent_title'] + ')'; }
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Track"><i class="fa fa-music fa-fw"></i></span>';
|
||||
if (rowData['rating_key']) {
|
||||
if (rowData['parent_title']) { parent_info = ' (' + rowData['parent_title'] + ')'; }
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=300&fallback=cover" data-height="80" data-width="80">' + cellData + parent_info + '</span>'
|
||||
$(td).html('<div class="history-title"><a href="info?source=history&rating_key=' + rowData['rating_key'] + '"><div style="float: left;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else {
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="images/cover.png" data-height="80" data-width="80">' + cellData + parent_info + '</span>'
|
||||
$(td).html('<div class="history-title"><div style="float: left;">' + media_type + ' ' + thumb_popover + '</div></div>');
|
||||
}
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="' + page('pms_image_proxy', rowData['thumb'], rowData['rating_key'], 300, 300, null, null, null, 'cover') + '" data-height="80" data-width="80">' + cellData + parent_info + '</span>';
|
||||
$(td).html('<div class="history-title"><a href="' + page('info', rowData['rating_key'], rowData['guid'], true, rowData['live']) + '"><div style="float: left;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else if (rowData['media_type']) {
|
||||
if (rowData['rating_key']) {
|
||||
$(td).html('<a href="info?rating_key=' + rowData['rating_key'] + '">' + cellData + '</a>');
|
||||
} else {
|
||||
$(td).html(cellData);
|
||||
}
|
||||
$(td).html('<a href="' + page('info', rowData['rating_key']) + '">' + cellData + '</a>');
|
||||
}
|
||||
} else {
|
||||
$(td).html('n/a');
|
||||
@@ -243,11 +234,11 @@ libraries_list_table_options = {
|
||||
showMsg(msg, false, false, 0)
|
||||
},
|
||||
"rowCallback": function (row, rowData) {
|
||||
if ($.inArray(rowData['section_id'], libraries_to_delete) !== -1) {
|
||||
$(row).find('button.delete-library[data-id="' + rowData['section_id'] + '"]').toggleClass('btn-warning').toggleClass('btn-danger');
|
||||
if ($.inArray(rowData['row_id'], libraries_to_delete) !== -1) {
|
||||
$(row).find('button.delete-library[data-id="' + rowData['row_id'] + '"]').toggleClass('btn-warning').toggleClass('btn-danger');
|
||||
}
|
||||
if ($.inArray(rowData['section_id'], libraries_to_purge) !== -1) {
|
||||
$(row).find('button.purge-library[data-id="' + rowData['section_id'] + '"]').toggleClass('btn-warning').toggleClass('btn-danger');
|
||||
if ($.inArray(rowData['row_id'], libraries_to_purge) !== -1) {
|
||||
$(row).find('button.purge-library[data-id="' + rowData['row_id'] + '"]').toggleClass('btn-warning').toggleClass('btn-danger');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -288,11 +279,11 @@ $('#libraries_list_table').on('click', 'td.edit-control > .edit-library-toggles
|
||||
var row = libraries_list_table.row(tr);
|
||||
var rowData = row.data();
|
||||
|
||||
var index_delete = $.inArray(rowData['section_id'], libraries_to_delete);
|
||||
var index_purge = $.inArray(rowData['section_id'], libraries_to_purge);
|
||||
var index_delete = $.inArray(rowData['row_id'], libraries_to_delete);
|
||||
var index_purge = $.inArray(rowData['row_id'], libraries_to_purge);
|
||||
|
||||
if (index_delete === -1) {
|
||||
libraries_to_delete.push(rowData['section_id']);
|
||||
libraries_to_delete.push(rowData['row_id']);
|
||||
if (index_purge === -1) {
|
||||
tr.find('button.purge-library').click();
|
||||
}
|
||||
@@ -311,11 +302,11 @@ $('#libraries_list_table').on('click', 'td.edit-control > .edit-library-toggles
|
||||
var row = libraries_list_table.row(tr);
|
||||
var rowData = row.data();
|
||||
|
||||
var index_delete = $.inArray(rowData['section_id'], libraries_to_delete);
|
||||
var index_purge = $.inArray(rowData['section_id'], libraries_to_purge);
|
||||
var index_delete = $.inArray(rowData['row_id'], libraries_to_delete);
|
||||
var index_purge = $.inArray(rowData['row_id'], libraries_to_purge);
|
||||
|
||||
if (index_purge === -1) {
|
||||
libraries_to_purge.push(rowData['section_id']);
|
||||
libraries_to_purge.push(rowData['row_id']);
|
||||
} else {
|
||||
libraries_to_purge.splice(index_purge, 1);
|
||||
if (index_delete != -1) {
|
||||
|
@@ -50,7 +50,7 @@ media_info_table_options = {
|
||||
expand_details = '<span class="expand-media-info-tooltip" data-toggle="tooltip" title="Show Episodes"><i class="fa fa-plus-circle fa-fw"></i></span>';
|
||||
$(td).html('<div><a href="#"><div style="float: left;">' + expand_details + ' ' + date + '</div></a></div>');
|
||||
} else if (rowData['media_type'] === 'artist') {
|
||||
expand_details = '<span class="expand-media-info-tooltip" data-toggle="tooltip" title="Show Albumns"><i class="fa fa-plus-circle fa-fw"></i></span>';
|
||||
expand_details = '<span class="expand-media-info-tooltip" data-toggle="tooltip" title="Show Albums"><i class="fa fa-plus-circle fa-fw"></i></span>';
|
||||
$(td).html('<div><a href="#"><div style="float: left;">' + expand_details + ' ' + date + '</div></a></div>');
|
||||
} else if (rowData['media_type'] === 'album') {
|
||||
expand_details = '<span class="expand-media-info-tooltip" data-toggle="tooltip" title="Show Tracks"><i class="fa fa-plus-circle fa-fw"></i></span>';
|
||||
@@ -78,43 +78,43 @@ media_info_table_options = {
|
||||
if (rowData['media_type'] === 'movie') {
|
||||
if (rowData['year']) { parent_info = ' (' + rowData['year'] + ')'; }
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Movie"><i class="fa fa-film fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=poster" data-height="120" data-width="80">' + rowData['title'] + parent_info + '</span>';
|
||||
$(td).html('<div class="history-title"><a href="info?rating_key=' + rowData['rating_key'] + '"><div style="float: left;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="' + page('pms_image_proxy', rowData['thumb'], rowData['rating_key'], 300, 450, null, null, null, 'poster') + '" data-height="120" data-width="80">' + rowData['title'] + parent_info + '</span>';
|
||||
$(td).html('<div class="history-title"><a href="' + page('info', rowData['rating_key']) + '"><div style="float: left;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else if (rowData['media_type'] === 'show') {
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="TV Show"><i class="fa fa-television fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=poster" data-height="120" data-width="80">' + rowData['title'] + '</span>';
|
||||
$(td).html('<div class="history-title"><a href="info?rating_key=' + rowData['rating_key'] + '"><div style="float: left;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="' + page('pms_image_proxy', rowData['thumb'], rowData['rating_key'], 300, 450, null, null, null, 'poster') + '" data-height="120" data-width="80">' + rowData['title'] + '</span>';
|
||||
$(td).html('<div class="history-title"><a href="' + page('info', rowData['rating_key']) + '"><div style="float: left;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else if (rowData['media_type'] === 'season') {
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Season"><i class="fa fa-television fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=poster" data-height="120" data-width="80">' + rowData['title'] + '</span>';
|
||||
$(td).html('<div class="history-title"><a href="info?rating_key=' + rowData['rating_key'] + '"><div style="float: left; padding-left: 15px;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="' + page('pms_image_proxy', rowData['thumb'], rowData['rating_key'], 300, 450, null, null, null, 'poster') + '" data-height="120" data-width="80">' + rowData['title'] + '</span>';
|
||||
$(td).html('<div class="history-title"><a href="' + page('info', rowData['rating_key']) + '"><div style="float: left; padding-left: 15px;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else if (rowData['media_type'] === 'episode') {
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Episode"><i class="fa fa-television fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=art" data-height="80" data-width="140">E' + rowData['media_index'] + ' - ' + rowData['title'] + '</span>';
|
||||
$(td).html('<div class="history-title"><a href="info?rating_key=' + rowData['rating_key'] + '"><div style="float: left; padding-left: 30px;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="' + page('pms_image_proxy', rowData['thumb'], rowData['rating_key'], 500, 280, null, null, null, 'art') + '" data-height="80" data-width="140">E' + rowData['media_index'] + ' - ' + rowData['title'] + '</span>';
|
||||
$(td).html('<div class="history-title"><a href="' + page('info', rowData['rating_key']) + '"><div style="float: left; padding-left: 30px;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else if (rowData['media_type'] === 'artist') {
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Artist"><i class="fa fa-music fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=300&fallback=cover" data-height="80" data-width="80">' + rowData['title'] + '</span>';
|
||||
$(td).html('<div class="history-title"><a href="info?rating_key=' + rowData['rating_key'] + '"><div style="float: left;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="' + page('pms_image_proxy', rowData['thumb'], rowData['rating_key'], 300, 300, null, null, null, 'cover') + '" data-height="80" data-width="80">' + rowData['title'] + '</span>';
|
||||
$(td).html('<div class="history-title"><a href="' + page('info', rowData['rating_key']) + '"><div style="float: left;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else if (rowData['media_type'] === 'album') {
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Album"><i class="fa fa-music fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=300&fallback=cover" data-height="80" data-width="80">' + rowData['title'] + '</span>';
|
||||
$(td).html('<div class="history-title"><a href="info?rating_key=' + rowData['rating_key'] + '"><div style="float: left; padding-left: 15px;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="' + page('pms_image_proxy', rowData['thumb'], rowData['rating_key'], 300, 300, null, null, null, 'cover') + '" data-height="80" data-width="80">' + rowData['title'] + '</span>';
|
||||
$(td).html('<div class="history-title"><a href="' + page('info', rowData['rating_key']) + '"><div style="float: left; padding-left: 15px;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else if (rowData['media_type'] === 'track') {
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Track"><i class="fa fa-music fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=300&fallback=cover" data-height="80" data-width="80">T' + rowData['media_index'] + ' - ' + rowData['title'] + '</span>';
|
||||
$(td).html('<div class="history-title"><a href="info?rating_key=' + rowData['rating_key'] + '"><div style="float: left; padding-left: 30px;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="' + page('pms_image_proxy', rowData['thumb'], rowData['rating_key'], 300, 300, null, null, null, 'cover') + '" data-height="80" data-width="80">T' + rowData['media_index'] + ' - ' + rowData['title'] + '</span>';
|
||||
$(td).html('<div class="history-title"><a href="' + page('info', rowData['rating_key']) + '"><div style="float: left; padding-left: 30px;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else if (rowData['media_type'] === 'photo_album') {
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Photo Album"><i class="fa fa-camera fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=poster" data-height="120" data-width="80">' + rowData['title'] + '</span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="' + page('pms_image_proxy', rowData['thumb'], rowData['rating_key'], 300, 450, null, null, null, 'poster') + '" data-height="120" data-width="80">' + rowData['title'] + '</span>';
|
||||
$(td).html('<div class="history-title"><div style="float: left; padding-left: 15px;">' + media_type + ' ' + thumb_popover + '</div></div>');
|
||||
} else if (rowData['media_type'] === 'photo') {
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Photo"><i class="fa fa-picture-o fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=poster" data-height="120" data-width="80">' + rowData['title'] + '</span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="' + page('pms_image_proxy', rowData['thumb'], rowData['rating_key'], 300, 450, null, null, null, 'poster') + '" data-height="120" data-width="80">' + rowData['title'] + '</span>';
|
||||
$(td).html('<div class="history-title"><div style="float: left; padding-left: 15px;">' + media_type + ' ' + thumb_popover + '</div></div>');
|
||||
} else if (rowData['media_type'] === 'clip') {
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Video"><i class="fa fa-video-camera fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=art" data-height="80" data-width="140">' + rowData['title'] + '</span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="' + page('pms_image_proxy', rowData['thumb'], rowData['rating_key'], 500, 280, null, null, null, 'art') + '" data-height="80" data-width="140">' + rowData['title'] + '</span>';
|
||||
$(td).html('<div class="history-title"><div style="float: left; padding-left: 15px;">' + media_type + ' ' + thumb_popover + '</div></div>');
|
||||
} else {
|
||||
$(td).html(cellData);
|
||||
|
@@ -51,9 +51,9 @@ sync_table_options = {
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData !== '') {
|
||||
if (rowData['user_id']) {
|
||||
$(td).html('<a href="user?user_id=' + rowData['user_id'] + '">' + cellData + '</a>');
|
||||
$(td).html('<a href="' + page('user', rowData['user_id']) + '">' + cellData + '</a>');
|
||||
} else {
|
||||
$(td).html('<a href="user?user=' + rowData['user'] + '">' + cellData + '</a>');
|
||||
$(td).html('<a href="' + page('user', null, rowData['user']) + '">' + cellData + '</a>');
|
||||
}
|
||||
} else {
|
||||
$(td).html(cellData);
|
||||
@@ -67,7 +67,7 @@ sync_table_options = {
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData !== '') {
|
||||
if (rowData['rating_key']) {
|
||||
$(td).html('<a href="info?rating_key=' + rowData['rating_key'] + '">' + cellData + '</a>');
|
||||
$(td).html('<a href="' + page('info', rowData['rating_key']) + '">' + cellData + '</a>');
|
||||
} else {
|
||||
$(td).html(cellData);
|
||||
}
|
||||
|
@@ -82,29 +82,37 @@ user_ip_table_options = {
|
||||
"data": "last_played",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData !== '') {
|
||||
var icon = '';
|
||||
var icon_title = '';
|
||||
var parent_info = '';
|
||||
var media_type = '';
|
||||
var thumb_popover = '';
|
||||
var fallback = (rowData['live']) ? 'poster-live' : 'poster';
|
||||
if (rowData['media_type'] === 'movie') {
|
||||
icon = (rowData['live']) ? 'fa-broadcast-tower' : 'fa-film';
|
||||
icon_title = (rowData['live']) ? 'Live TV' : 'Movie';
|
||||
if (rowData['year']) { parent_info = ' (' + rowData['year'] + ')'; }
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Movie"><i class="fa fa-film fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=poster" data-height="120" data-width="80">' + cellData + parent_info + '</span>'
|
||||
$(td).html('<div class="history-title"><a href="info?source=history&rating_key=' + rowData['rating_key'] + '"><div style="float: left;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="' + icon_title + '"><i class="fa ' + icon + ' fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="' + page('pms_image_proxy', rowData['thumb'], rowData['rating_key'], 300, 450, null, null, null, fallback) + '" data-height="120" data-width="80">' + cellData + parent_info + '</span>';
|
||||
$(td).html('<div class="history-title"><a href="' + page('info', rowData['rating_key'], rowData['guid'], true, rowData['live']) + '"><div style="float: left;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else if (rowData['media_type'] === 'episode') {
|
||||
if (rowData['parent_media_index'] && rowData['media_index']) { parent_info = ' (S' + rowData['parent_media_index'] + '· E' + rowData['media_index'] + ')'; }
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Episode"><i class="fa fa-television fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=poster" data-height="120" data-width="80">' + cellData + parent_info + '</span>'
|
||||
$(td).html('<div class="history-title"><a href="info?source=history&rating_key=' + rowData['rating_key'] + '"><div style="float: left;" >' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
icon = (rowData['live']) ? 'fa-broadcast-tower' : 'fa-television';
|
||||
icon_title = (rowData['live']) ? 'Live TV' : 'Episode';
|
||||
if (!isNaN(parseInt(rowData['parent_media_index'])) && !isNaN(parseInt(rowData['media_index']))) { parent_info = ' (S' + rowData['parent_media_index'] + ' · E' + rowData['media_index'] + ')'; }
|
||||
else if (rowData['live'] && rowData['originally_available_at']) { parent_info = ' (' + rowData['originally_available_at'] + ')'; }
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="' + icon_title + '"><i class="fa ' + icon + ' fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="' + page('pms_image_proxy', rowData['thumb'], rowData['rating_key'], 300, 450, null, null, null, fallback) + '" data-height="120" data-width="80">' + cellData + parent_info + '</span>';
|
||||
$(td).html('<div class="history-title"><a href="' + page('info', rowData['rating_key'], rowData['guid'], true, rowData['live']) + '"><div style="float: left;" >' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else if (rowData['media_type'] === 'track') {
|
||||
if (rowData['parent_title']) { parent_info = ' (' + rowData['parent_title'] + ')'; }
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Track"><i class="fa fa-music fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=300&fallback=cover" data-height="80" data-width="80">' + cellData + parent_info + '</span>'
|
||||
$(td).html('<div class="history-title"><a href="info?source=history&rating_key=' + rowData['rating_key'] + '"><div style="float: left;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="' + page('pms_image_proxy', rowData['thumb'], rowData['rating_key'], 300, 300, null, null, null, 'cover') + '" data-height="80" data-width="80">' + cellData + parent_info + '</span>';
|
||||
$(td).html('<div class="history-title"><a href="' + page('info', rowData['rating_key'], rowData['guid'], true, rowData['live']) + '"><div style="float: left;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else if (rowData['media_type']) {
|
||||
$(td).html('<a href="info?rating_key=' + rowData['rating_key'] + '">' + cellData + '</a>');
|
||||
} else {
|
||||
$(td).html('n/a');
|
||||
$(td).html('<a href="' + page('info', rowData['rating_key']) + '">' + cellData + '</a>');
|
||||
}
|
||||
} else {
|
||||
$(td).html('n/a');
|
||||
}
|
||||
},
|
||||
"width": "30%",
|
||||
@@ -159,7 +167,7 @@ $('.user_ip_table').on('click', 'td.modal-control', function () {
|
||||
function showStreamDetails() {
|
||||
$.ajax({
|
||||
url: 'get_stream_data',
|
||||
data: { row_id: rowData['id'], user: rowData['friendly_name'] },
|
||||
data: { row_id: rowData['history_row_id'], user: rowData['friendly_name'] },
|
||||
cache: false,
|
||||
async: true,
|
||||
complete: function (xhr, status) {
|
||||
|
@@ -44,8 +44,8 @@ users_list_table_options = {
|
||||
"data": null,
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
$(td).html('<div class="edit-user-toggles">' +
|
||||
'<button class="btn btn-xs btn-warning delete-user" data-id="' + rowData['user_id'] + '" data-toggle="button"><i class="fa fa-trash-o fa-fw"></i> Delete</button> ' +
|
||||
'<button class="btn btn-xs btn-warning purge-user" data-id="' + rowData['user_id'] + '" data-toggle="button"><i class="fa fa-eraser fa-fw"></i> Purge</button>   ' +
|
||||
'<button class="btn btn-xs btn-warning delete-user" data-id="' + rowData['row_id'] + '" data-toggle="button"><i class="fa fa-trash-o fa-fw"></i> Delete</button> ' +
|
||||
'<button class="btn btn-xs btn-warning purge-user" data-id="' + rowData['row_id'] + '" data-toggle="button"><i class="fa fa-eraser fa-fw"></i> Purge</button>   ' +
|
||||
'<input type="checkbox" id="keep_history-' + rowData['user_id'] + '" name="keep_history" value="1" ' + rowData['keep_history'] + '><label class="edit-tooltip" for="keep_history-' + rowData['user_id'] + '" data-toggle="tooltip" title="Toggle History"><i class="fa fa-history fa-lg fa-fw"></i></label> ' +
|
||||
'<input type="checkbox" id="allow_guest-' + rowData['user_id'] + '" name="allow_guest" value="1" ' + rowData['allow_guest'] + '><label class="edit-tooltip" for="allow_guest-' + rowData['user_id'] + '" data-toggle="tooltip" title="Toggle Guest Access"><i class="fa fa-unlock-alt fa-lg fa-fw"></i></label> ' +
|
||||
'</div>');
|
||||
@@ -59,10 +59,12 @@ users_list_table_options = {
|
||||
"targets": [1],
|
||||
"data": "user_thumb",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
var inactive = '';
|
||||
if (!rowData['is_active']) { inactive = '<span class="inactive-user-tooltip" data-toggle="tooltip" title="User not on Plex server"><i class="fa fa-exclamation-triangle"></i></span>'; }
|
||||
if (cellData === '') {
|
||||
$(td).html('<a href="user?user_id=' + rowData['user_id'] + '"><div class="users-poster-face" style="background-image: url(../../images/gravatar-default-80x80.png);"></div></a>');
|
||||
$(td).html('<a href="' + page('user', rowData['user_id']) + '"><div class="users-poster-face" style="background-image: url(../../images/gravatar-default-80x80.png);">' + inactive + '</div></a>');
|
||||
} else {
|
||||
$(td).html('<a href="user?user_id=' + rowData['user_id'] + '"><div class="users-poster-face" style="background-image: url(' + rowData['user_thumb'] + ');"></div></a>');
|
||||
$(td).html('<a href="' + page('user', rowData['user_id']) + '"><div class="users-poster-face" style="background-image: url(' + rowData['user_thumb'] + ');">' + inactive + '</div></a>');
|
||||
}
|
||||
},
|
||||
"orderable": false,
|
||||
@@ -75,8 +77,8 @@ users_list_table_options = {
|
||||
"data": "friendly_name",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData !== null && cellData !== '') {
|
||||
$(td).html('<div class="edit-user-name" data-id="' + rowData['user_id'] + '">' +
|
||||
'<a href="user?user_id=' + rowData['user_id'] + '">' + cellData + '</a>' +
|
||||
$(td).html('<div class="edit-user-name" data-id="' + rowData['row_id'] + '">' +
|
||||
'<a href="' + page('user', rowData['user_id']) + '">' + cellData + '</a>' +
|
||||
'<input type="text" class="hidden" value="' + cellData + '">' +
|
||||
'</div>');
|
||||
} else {
|
||||
@@ -157,26 +159,34 @@ users_list_table_options = {
|
||||
"data":"last_played",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData !== null && cellData !== '') {
|
||||
var icon = '';
|
||||
var icon_title = '';
|
||||
var parent_info = '';
|
||||
var media_type = '';
|
||||
var thumb_popover = '';
|
||||
var fallback = (rowData['live']) ? 'poster-live' : 'poster';
|
||||
if (rowData['media_type'] === 'movie') {
|
||||
icon = (rowData['live']) ? 'fa-broadcast-tower' : 'fa-film';
|
||||
icon_title = (rowData['live']) ? 'Live TV' : 'Movie';
|
||||
if (rowData['year']) { parent_info = ' (' + rowData['year'] + ')'; }
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Movie"><i class="fa fa-film fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=poster" data-height="120" data-width="80">' + cellData + parent_info + '</span>'
|
||||
$(td).html('<div class="history-title"><a href="info?source=history&rating_key=' + rowData['rating_key'] + '"><div style="float: left;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="' + icon_title + '"><i class="fa ' + icon + ' fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="' + page('pms_image_proxy', rowData['thumb'], rowData['rating_key'], 300, 450, null, null, null, fallback) + '" data-height="120" data-width="80">' + cellData + parent_info + '</span>';
|
||||
$(td).html('<div class="history-title"><a href="' + page('info', rowData['rating_key'], rowData['guid'], true, rowData['live']) + '"><div style="float: left;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else if (rowData['media_type'] === 'episode') {
|
||||
if (rowData['parent_media_index'] && rowData['media_index']) { parent_info = ' (S' + rowData['parent_media_index'] + '· E' + rowData['media_index'] + ')'; }
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Episode"><i class="fa fa-television fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=poster" data-height="120" data-width="80">' + cellData + parent_info + '</span>'
|
||||
$(td).html('<div class="history-title"><a href="info?source=history&rating_key=' + rowData['rating_key'] + '"><div style="float: left;" >' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
icon = (rowData['live']) ? 'fa-broadcast-tower' : 'fa-television';
|
||||
icon_title = (rowData['live']) ? 'Live TV' : 'Episode';
|
||||
if (!isNaN(parseInt(rowData['parent_media_index'])) && !isNaN(parseInt(rowData['media_index']))) { parent_info = ' (S' + rowData['parent_media_index'] + ' · E' + rowData['media_index'] + ')'; }
|
||||
else if (rowData['live'] && rowData['originally_available_at']) { parent_info = ' (' + rowData['originally_available_at'] + ')'; }
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="' + icon_title + '"><i class="fa ' + icon + ' fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="' + page('pms_image_proxy', rowData['thumb'], rowData['rating_key'], 300, 450, null, null, null, fallback) + '" data-height="120" data-width="80">' + cellData + parent_info + '</span>';
|
||||
$(td).html('<div class="history-title"><a href="' + page('info', rowData['rating_key'], rowData['guid'], true, rowData['live']) + '"><div style="float: left;" >' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else if (rowData['media_type'] === 'track') {
|
||||
if (rowData['parent_title']) { parent_info = ' (' + rowData['parent_title'] + ')'; }
|
||||
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Track"><i class="fa fa-music fa-fw"></i></span>';
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=300&fallback=cover" data-height="80" data-width="80">' + cellData + parent_info + '</span>'
|
||||
$(td).html('<div class="history-title"><a href="info?source=history&rating_key=' + rowData['rating_key'] + '"><div style="float: left;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="' + page('pms_image_proxy', rowData['thumb'], rowData['rating_key'], 300, 300, null, null, null, 'cover') + '" data-height="80" data-width="80">' + cellData + parent_info + '</span>';
|
||||
$(td).html('<div class="history-title"><a href="' + page('info', rowData['rating_key'], rowData['guid'], true, rowData['live']) + '"><div style="float: left;">' + media_type + ' ' + thumb_popover + '</div></a></div>');
|
||||
} else if (rowData['media_type']) {
|
||||
$(td).html('<a href="info?rating_key=' + rowData['rating_key'] + '">' + cellData + '</a>');
|
||||
$(td).html('<a href="' + page('info', rowData['rating_key']) + '">' + cellData + '</a>');
|
||||
}
|
||||
} else {
|
||||
$(td).html('n/a');
|
||||
@@ -246,10 +256,10 @@ users_list_table_options = {
|
||||
},
|
||||
"rowCallback": function (row, rowData) {
|
||||
if ($.inArray(rowData['user_id'], users_to_delete) !== -1) {
|
||||
$(row).find('button.delete-user[data-id="' + rowData['user_id'] + '"]').toggleClass('btn-warning').toggleClass('btn-danger');
|
||||
$(row).find('button.delete-user[data-id="' + rowData['row_id'] + '"]').toggleClass('btn-warning').toggleClass('btn-danger');
|
||||
}
|
||||
if ($.inArray(rowData['user_id'], users_to_purge) !== -1) {
|
||||
$(row).find('button.purge-user[data-id="' + rowData['user_id'] + '"]').toggleClass('btn-warning').toggleClass('btn-danger');
|
||||
$(row).find('button.purge-user[data-id="' + rowData['row_id'] + '"]').toggleClass('btn-warning').toggleClass('btn-danger');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -260,7 +270,7 @@ $('#users_list_table').on('click', 'td.modal-control', function () {
|
||||
var rowData = row.data();
|
||||
|
||||
$.get('get_stream_data', {
|
||||
row_id: rowData['id'],
|
||||
row_id: rowData['history_row_id'],
|
||||
user: rowData['friendly_name']
|
||||
}).then(function (jqXHR) {
|
||||
$("#info-modal").html(jqXHR);
|
||||
@@ -318,11 +328,11 @@ $('#users_list_table').on('click', 'td.edit-control > .edit-user-toggles > butto
|
||||
var row = users_list_table.row(tr);
|
||||
var rowData = row.data();
|
||||
|
||||
var index_delete = $.inArray(rowData['user_id'], users_to_delete);
|
||||
var index_purge = $.inArray(rowData['user_id'], users_to_purge);
|
||||
var index_delete = $.inArray(rowData['row_id'], users_to_delete);
|
||||
var index_purge = $.inArray(rowData['row_id'], users_to_purge);
|
||||
|
||||
if (index_delete === -1) {
|
||||
users_to_delete.push(rowData['user_id']);
|
||||
users_to_delete.push(rowData['row_id']);
|
||||
if (index_purge === -1) {
|
||||
tr.find('button.purge-user').click();
|
||||
}
|
||||
@@ -341,11 +351,11 @@ $('#users_list_table').on('click', 'td.edit-control > .edit-user-toggles > butto
|
||||
var row = users_list_table.row(tr);
|
||||
var rowData = row.data();
|
||||
|
||||
var index_delete = $.inArray(rowData['user_id'], users_to_delete);
|
||||
var index_purge = $.inArray(rowData['user_id'], users_to_purge);
|
||||
var index_delete = $.inArray(rowData['row_id'], users_to_delete);
|
||||
var index_purge = $.inArray(rowData['row_id'], users_to_purge);
|
||||
|
||||
if (index_purge === -1) {
|
||||
users_to_purge.push(rowData['user_id']);
|
||||
users_to_purge.push(rowData['row_id']);
|
||||
} else {
|
||||
users_to_purge.splice(index_purge, 1);
|
||||
if (index_delete != -1) {
|
||||
|
@@ -116,14 +116,14 @@
|
||||
});
|
||||
|
||||
if (libraries_to_delete.length > 0) {
|
||||
$('#libraries-to-delete').prepend('<p>Are you REALLY sure you want to delete the following libraries:</p>')
|
||||
$('#libraries-to-delete').prepend('<p>Are you REALLY sure you want to delete the following libraries:</p>');
|
||||
for (var i = 0; i < libraries_to_delete.length; i++) {
|
||||
$('#libraries-to-delete').append('<li>' + $('div[data-id=' + libraries_to_delete[i] + ']').text() + '</li>');
|
||||
}
|
||||
}
|
||||
|
||||
if (libraries_to_purge.length > 0) {
|
||||
$('#libraries-to-purge').prepend('<p>Are you REALLY sure you want to purge all history for the following libraries:</p>')
|
||||
$('#libraries-to-purge').prepend('<p>Are you REALLY sure you want to purge all history for the following libraries:</p>');
|
||||
for (var i = 0; i < libraries_to_purge.length; i++) {
|
||||
$('#libraries-to-purge').append('<li>' + $('div[data-id=' + libraries_to_purge[i] + ']').text() + '</li>');
|
||||
}
|
||||
@@ -131,33 +131,30 @@
|
||||
|
||||
$('#confirm-modal-delete').modal();
|
||||
$('#confirm-modal-delete').one('click', '#confirm-delete', function () {
|
||||
libraries_to_delete.forEach(function(row, idx) {
|
||||
$.ajax({
|
||||
url: 'delete_library',
|
||||
type: 'POST',
|
||||
data: { section_id: row },
|
||||
cache: false,
|
||||
async: true,
|
||||
success: function (data) {
|
||||
var msg = "Library deleted";
|
||||
showMsg(msg, false, true, 2000);
|
||||
}
|
||||
});
|
||||
$.ajax({
|
||||
url: 'delete_all_library_history',
|
||||
type: 'POST',
|
||||
data: { row_ids: libraries_to_purge.join(',') },
|
||||
cache: false,
|
||||
async: true,
|
||||
success: function (data) {
|
||||
var msg = "Library history purged";
|
||||
showMsg(msg, false, true, 2000);
|
||||
libraries_list_table.draw();
|
||||
}
|
||||
});
|
||||
libraries_to_purge.forEach(function(row, idx) {
|
||||
$.ajax({
|
||||
url: 'delete_all_library_history',
|
||||
type: 'POST',
|
||||
data: { section_id: row },
|
||||
cache: false,
|
||||
async: true,
|
||||
success: function (data) {
|
||||
var msg = "Library history purged";
|
||||
showMsg(msg, false, true, 2000);
|
||||
}
|
||||
});
|
||||
$.ajax({
|
||||
url: 'delete_library',
|
||||
type: 'POST',
|
||||
data: { row_ids: libraries_to_delete.join(',') },
|
||||
cache: false,
|
||||
async: true,
|
||||
success: function (data) {
|
||||
var msg = "Library deleted";
|
||||
showMsg(msg, false, true, 2000);
|
||||
libraries_list_table.draw();
|
||||
}
|
||||
});
|
||||
libraries_list_table.draw();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -188,7 +185,7 @@
|
||||
complete: function (xhr, status) {
|
||||
var result = $.parseJSON(xhr.responseText);
|
||||
var msg = result.message;
|
||||
if (result.result == 'success') {
|
||||
if (result.result === 'success') {
|
||||
showMsg('<i class="fa fa-check"></i> ' + msg, false, true, 2000, false);
|
||||
libraries_list_table.draw();
|
||||
} else {
|
||||
|
@@ -35,10 +35,14 @@ DOCUMENTATION :: END
|
||||
|
||||
<%def name="body()">
|
||||
% if data:
|
||||
<%
|
||||
from plexpy.common import LIVE_TV_SECTION_ID
|
||||
from plexpy.helpers import page
|
||||
%>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
% if data['library_art']:
|
||||
<div class="art-face" style="background-image:url(pms_image_proxy?img=${data['library_art']}&width=1920&height=1080)"></div>
|
||||
<div class="art-face" style="background-image:url(${page('pms_image_proxy', data['library_art'], None, 1920, 1080)})"></div>
|
||||
% if _session['user_group'] == 'admin':
|
||||
<span class="overlay-refresh-image info-art" title="Refresh background image"><i class="fa fa-refresh refresh_pms_image"></i></span>
|
||||
% endif
|
||||
@@ -57,10 +61,22 @@ DOCUMENTATION :: END
|
||||
<div class="col-md-12">
|
||||
<div class="table-card-back">
|
||||
<div class="user-info-wrapper">
|
||||
% if data['library_thumb'][:4] == 'http' or data['library_thumb'][:10] == 'interfaces':
|
||||
<div class="library-info-poster-face" style="background-image: url(${data['library_thumb']});"></div>
|
||||
% if data['library_thumb'].startswith('http'):
|
||||
<div class="library-info-poster-face" style="background-image: url(${page('pms_image_proxy', data['library_thumb'], None, 80, 80)});">
|
||||
% if not data['is_active']:
|
||||
<span class="inactive-library-tooltip" data-toggle="tooltip" title="Library not on Plex server">
|
||||
<i class="fa fa-2x fa-exclamation-triangle"></i>
|
||||
</span>
|
||||
% endif
|
||||
</div>
|
||||
% else:
|
||||
<div class="library-info-poster-face svg-icon library-${data['section_type']}"></div>
|
||||
<div class="library-info-poster-face svg-icon library-${data['section_type']}">
|
||||
% if not data['is_active']:
|
||||
<span class="inactive-library-tooltip" data-toggle="tooltip" title="Library not on Plex server">
|
||||
<i class="fa fa-2x fa-exclamation-triangle"></i>
|
||||
</span>
|
||||
% endif
|
||||
</div>
|
||||
% endif
|
||||
<div class="user-info-username">
|
||||
<span class="set-username">${data['section_name']}</span>
|
||||
@@ -75,8 +91,10 @@ DOCUMENTATION :: END
|
||||
<li class="active"><a href="#tabs-profile" role="tab" data-toggle="tab">Profile</a></li>
|
||||
<li><a id="history-tab-btn" href="#tabs-history" role="tab" data-toggle="tab">History</a></li>
|
||||
% if _session['user_group'] == 'admin':
|
||||
% if data['section_id'] != LIVE_TV_SECTION_ID:
|
||||
<li><a id="media-info-tab-btn" href="#tabs-mediainfo" role="tab" data-toggle="tab">Media Info</a></li>
|
||||
% endif
|
||||
% endif
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -143,6 +161,7 @@ DOCUMENTATION :: END
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
% if data['section_id'] != LIVE_TV_SECTION_ID:
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
@@ -168,6 +187,7 @@ DOCUMENTATION :: END
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
% endif
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane" id="tabs-history">
|
||||
<div class="container-fluid">
|
||||
@@ -205,6 +225,7 @@ DOCUMENTATION :: END
|
||||
<th align="left" id="friendly_name">User</th>
|
||||
<th align="left" id="ip_address">IP Address</th>
|
||||
<th align="left" id="platform">Platform</th>
|
||||
<th align="left" id="product">Product</th>
|
||||
<th align="left" id="player">Player</th>
|
||||
<th align="left" id="title">Title</th>
|
||||
<th align="left" id="started">Started</th>
|
||||
@@ -347,6 +368,7 @@ DOCUMENTATION :: END
|
||||
<script src="${http_root}js/dataTables.bootstrap.min.js"></script>
|
||||
<script src="${http_root}js/dataTables.bootstrap.pagination.js"></script>
|
||||
% if data:
|
||||
<% from plexpy.common import LIVE_TV_SECTION_ID %>
|
||||
<script>
|
||||
% if str(data['section_id']).isdigit():
|
||||
var section_id = ${data['section_id']};
|
||||
@@ -385,7 +407,7 @@ DOCUMENTATION :: END
|
||||
};
|
||||
history_table = $('#history_table-SID-${data["section_id"]}').DataTable(history_table_options);
|
||||
|
||||
var colvis = new $.fn.dataTable.ColVis(history_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark', exclude: [0, 11] });
|
||||
var colvis = new $.fn.dataTable.ColVis(history_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark', exclude: [0, 12] });
|
||||
$(colvis.button()).appendTo('#button-bar-history');
|
||||
|
||||
clearSearchButton('history_table-SID-${data["section_id"]}', history_table);
|
||||
@@ -401,6 +423,8 @@ DOCUMENTATION :: END
|
||||
history_table.draw();
|
||||
});
|
||||
|
||||
$(".inactive-library-tooltip").tooltip();
|
||||
|
||||
% if _session['user_group'] == 'admin':
|
||||
function loadMediaInfoTable() {
|
||||
// Build media info table
|
||||
@@ -461,17 +485,15 @@ DOCUMENTATION :: END
|
||||
$('#deleteCount').text(history_to_delete.length);
|
||||
$('#confirm-modal-delete').modal();
|
||||
$('#confirm-modal-delete').one('click', '#confirm-delete', function () {
|
||||
history_to_delete.forEach(function(row, idx) {
|
||||
$.ajax({
|
||||
url: 'delete_history_rows',
|
||||
type: 'POST',
|
||||
data: { row_id: row },
|
||||
async: true,
|
||||
success: function (data) {
|
||||
var msg = "History deleted";
|
||||
showMsg(msg, false, true, 2000);
|
||||
}
|
||||
});
|
||||
$.ajax({
|
||||
url: 'delete_history_rows',
|
||||
type: 'POST',
|
||||
data: { row_ids: history_to_delete.join(',') },
|
||||
async: true,
|
||||
success: function (data) {
|
||||
var msg = "History deleted";
|
||||
showMsg(msg, false, true, 2000);
|
||||
}
|
||||
});
|
||||
history_table.draw();
|
||||
});
|
||||
@@ -525,7 +547,9 @@ DOCUMENTATION :: END
|
||||
}
|
||||
|
||||
recentlyWatched();
|
||||
% if data['section_id'] != LIVE_TV_SECTION_ID:
|
||||
recentlyAdded();
|
||||
% endif
|
||||
|
||||
function highlightWatchedScrollerButton() {
|
||||
var scroller = $("#recently-watched-row-scroller");
|
||||
|
@@ -31,6 +31,9 @@ DOCUMENTATION :: END
|
||||
</%doc>
|
||||
|
||||
% if data:
|
||||
<%
|
||||
from plexpy.helpers import page
|
||||
%>
|
||||
<div class="dashboard-recent-media-row">
|
||||
<div id="recently-added-row-scroller" style="left: 0;">
|
||||
<ul class="dashboard-recent-media list-unstyled">
|
||||
@@ -38,19 +41,19 @@ DOCUMENTATION :: END
|
||||
<li>
|
||||
% if item['media_type'] == 'episode' or item['media_type'] == 'movie':
|
||||
% if item['media_type'] == 'movie':
|
||||
<a href="info?rating_key=${item['rating_key']}" title="${item['title']}">
|
||||
<a href="${page('info', item['rating_key'])}" title="${item['title']}">
|
||||
% elif item['media_type'] == 'episode':
|
||||
<a href="info?rating_key=${item['rating_key']}" title="${item['grandparent_title']}">
|
||||
<a href="${page('info', item['rating_key'])}" title="${item['grandparent_title']}">
|
||||
% endif
|
||||
<div class="dashboard-recent-media-poster">
|
||||
% if item['media_type'] == 'episode':
|
||||
% if item['parent_thumb']:
|
||||
<div class="dashboard-recent-media-poster-face" style="background-image: url(pms_image_proxy?img=${item['parent_thumb']}&width=300&height=450&fallback=poster);">
|
||||
<div class="dashboard-recent-media-poster-face" style="background-image: url(${page('pms_image_proxy', item['parent_thumb'], item['parent_rating_key'], 300, 450, fallback='poster')});">
|
||||
% else:
|
||||
<div class="dashboard-recent-media-poster-face" style="background-image: url(pms_image_proxy?img=${item['grandparent_thumb']}&width=300&height=450&fallback=poster);">
|
||||
<div class="dashboard-recent-media-poster-face" style="background-image: url(${page('pms_image_proxy', item['grandparent_thumb'], item['grandparent_rating_key'], 300, 450, fallback='poster')});">
|
||||
% endif
|
||||
% elif item['media_type'] == 'movie':
|
||||
<div class="dashboard-recent-media-poster-face" style="background-image: url(pms_image_proxy?img=${item['thumb']}&width=300&height=450&fallback=poster);">
|
||||
<div class="dashboard-recent-media-poster-face" style="background-image: url(${page('pms_image_proxy', item['thumb'], item['rating_key'], 300, 450, fallback='poster')});">
|
||||
% endif
|
||||
<div class="dashboard-recent-media-overlay">
|
||||
<div class="dashboard-recent-media-overlay-text" id="added_at-${item['rating_key']}">
|
||||
@@ -68,27 +71,27 @@ DOCUMENTATION :: END
|
||||
<div class="dashboard-recent-media-metacontainer">
|
||||
% if item['media_type'] == 'episode':
|
||||
<h3>
|
||||
<a href="info?rating_key=${item['grandparent_rating_key']}" title="${item['grandparent_title']}">${item['grandparent_title']}</a>
|
||||
<a href="${page('info', item['grandparent_rating_key'])}" title="${item['grandparent_title']}">${item['grandparent_title']}</a>
|
||||
</h3>
|
||||
<h3 class="text-muted">
|
||||
<a href="info?rating_key=${item['rating_key']}" title="${item['title']}">${item['title']}</a>
|
||||
<a href="${page('info', item['rating_key'])}" title="${item['title']}">${item['title']}</a>
|
||||
</h3>
|
||||
<h3 class="text-muted">
|
||||
<a href="info?rating_key=${item['parent_rating_key']}" title="Season ${item['parent_media_index']}">S${item['parent_media_index']}</a>
|
||||
· <a href="info?rating_key=${item['rating_key']}" title="Episode ${item['media_index']}">E${item['media_index']}</a>
|
||||
<a href="${page('info', item['parent_rating_key'])}" title="Season ${item['parent_media_index']}">S${item['parent_media_index']}</a>
|
||||
· <a href="${page('info', item['rating_key'])}" title="Episode ${item['media_index']}">E${item['media_index']}</a>
|
||||
</h3>
|
||||
% elif item['media_type'] == 'movie':
|
||||
<h3>
|
||||
<a href="info?rating_key=${item['rating_key']}" title="${item['title']}">${item['title']}</a>
|
||||
<a href="${page('info', item['rating_key'])}" title="${item['title']}">${item['title']}</a>
|
||||
</h3>
|
||||
<h3 class="text-muted">${item['year']}</h3>
|
||||
<h3 class="text-muted"> </h3>
|
||||
% endif
|
||||
</div>
|
||||
% elif item['media_type'] == 'album':
|
||||
<a href="info?rating_key=${item['rating_key']}" title="${item['title']}">
|
||||
<a href="${page('info', item['rating_key'])}" title="${item['title']}">
|
||||
<div class="dashboard-recent-media-cover">
|
||||
<div class="dashboard-recent-media-cover-face" style="background-image: url(pms_image_proxy?img=${item['thumb']}&width=300&height=300&fallback=cover);">
|
||||
<div class="dashboard-recent-media-cover-face" style="background-image: url(${page('pms_image_proxy', item['thumb'], item['rating_key'], 300, 300, fallback='cover')});">
|
||||
<div class="dashboard-recent-media-overlay">
|
||||
<div class="dashboard-recent-media-overlay-text" id="added_at-${item['rating_key']}">
|
||||
<script>
|
||||
@@ -100,10 +103,10 @@ DOCUMENTATION :: END
|
||||
</div>
|
||||
<div class="dashboard-recent-media-metacontainer">
|
||||
<h3>
|
||||
<a href="info?rating_key=${item['parent_rating_key']}" title="${item['parent_title']}">${item['parent_title']}</a>
|
||||
<a href="${page('info', item['parent_rating_key'])}" title="${item['parent_title']}">${item['parent_title']}</a>
|
||||
</h3>
|
||||
<h3 class="text-muted">
|
||||
<a class="text-muted" href="info?rating_key=${item['rating_key']}" title="${item['title']}">${item['title']}</a>
|
||||
<a class="text-muted" href="${page('info', item['rating_key'])}" title="${item['title']}">${item['title']}</a>
|
||||
</h3>
|
||||
<h3 class="text-muted"> </h3>
|
||||
</div>
|
||||
|
@@ -25,6 +25,8 @@ DOCUMENTATION :: END
|
||||
|
||||
% if data:
|
||||
<%
|
||||
from plexpy.helpers import page
|
||||
|
||||
types = ('movie', 'show', 'artist', 'photo')
|
||||
headers = {'movie': ('Movie Libraries', ('Movies', '', '')),
|
||||
'show': ('TV Show Libraries', ('Shows', 'Seasons', 'Episodes')),
|
||||
@@ -33,10 +35,17 @@ DOCUMENTATION :: END
|
||||
%>
|
||||
% for section_type in types:
|
||||
% if section_type in data:
|
||||
<%
|
||||
row0 = data[section_type][0]
|
||||
%>
|
||||
<div class="dashboard-stats-instance" id="library-stats-instance-${section_type}" data-section_type="${section_type}">
|
||||
<div class="dashboard-stats-container">
|
||||
<div id="library-stats-background-${section_type}" class="dashboard-stats-background" style="background-image: url(pms_image_proxy?img=/:/resources/${section_type}-fanart.jpg&width=500&height=280&opacity=40&background=282828&blur=3&fallback=art);">
|
||||
<div id="library-stats-background-${section_type}" class="dashboard-stats-background" style="background-image: url(${page('pms_image_proxy', row0['art'], None, 500, 280, 40, '282828', 3, fallback='art')});">
|
||||
% if row0['thumb'].startswith('http'):
|
||||
<div id="library-stats-thumb-${section_type}" class="dashboard-stats-flat hidden-xs" style="background-image: url(${page('pms_image_proxy', row0['thumb'], None, 80, 80)});"></div>
|
||||
% else:
|
||||
<div id="library-stats-thumb-${section_type}" class="dashboard-stats-flat svg-icon library-${section_type} hidden-xs"></div>
|
||||
% endif
|
||||
<div class="dashboard-stats-info-container">
|
||||
<div id="library-stats-title-${section_type}" class="dashboard-stats-info-title">
|
||||
<h4>${headers[section_type][0]}</h4>
|
||||
@@ -46,10 +55,11 @@ DOCUMENTATION :: END
|
||||
<div class="dashboard-stats-info scoller-content">
|
||||
<ul class="list-unstyled dashboard-stats-info-list">
|
||||
% for section in data[section_type]:
|
||||
<li class="dashboard-stats-info-item ${'expanded' if loop.index == 0 else ''}">
|
||||
<li class="dashboard-stats-info-item ${'expanded' if loop.index == 0 else ''}" data-stat_id="${section_type}"
|
||||
data-art="${section.get('art')}" data-thumb="${section.get('thumb')}">
|
||||
<div class="sub-list">${loop.index + 1}</div>
|
||||
<div class="sub-value">
|
||||
<a href="library?section_id=${section['section_id']}" title="${section['section_name']}">
|
||||
<a href="${page('library', section['section_id'])}" title="${section['section_name']}">
|
||||
${section['section_name']}
|
||||
</a>
|
||||
</div>
|
||||
|
@@ -19,16 +19,17 @@ DOCUMENTATION :: END
|
||||
</%doc>
|
||||
|
||||
% if data:
|
||||
<% from plexpy.helpers import page %>
|
||||
% for a in data:
|
||||
<ul class="list-unstyled">
|
||||
<div class="user-player-instance">
|
||||
<li>
|
||||
% if a['user_id']:
|
||||
<a href="user?user_id=${a['user_id']}" title="${a['friendly_name']}">
|
||||
<a href="${page('user', a['user_id'])}" title="${a['friendly_name']}">
|
||||
<div class="library-user-instance-box" style="background-image: url(${a['user_thumb']});"></div>
|
||||
</a>
|
||||
<div class=" user-player-instance-name">
|
||||
<a href="user?user_id=${a['user_id']}" title="${a['friendly_name']}">${a['friendly_name']}</a>
|
||||
<a href="${page('user', a['user_id'])}" title="${a['friendly_name']}">${a['friendly_name']}</a>
|
||||
</div>
|
||||
% else:
|
||||
<div class="library-user-instance-box" style="background-image: url(${a['user_thumb']});"></div>
|
||||
|
@@ -24,7 +24,7 @@
|
||||
|
||||
<!-- ICONS -->
|
||||
<!-- Android -->
|
||||
<link rel="manifest" href="${http_root}images/favicon/manifest.json?v=2.0.5">
|
||||
<link rel="manifest" href="${http_root}images/favicon/manifest.json?v=2.0.5" crossorigin="use-credentials>
|
||||
<meta name="theme-color" content="#282a2d">
|
||||
<!-- Apple -->
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="${http_root}images/favicon/apple-touch-icon.png?v=2.0.5">
|
||||
@@ -183,4 +183,4 @@
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
@@ -271,7 +271,7 @@
|
||||
</div>
|
||||
<p class="help-block">
|
||||
Select an existing notification agent where the subject and body text will be sent.<br>
|
||||
Note: Self-hosted newsletters must be enabled under <a data-tab-destination="tabs-notifications" data-dismiss="modal" data-target="#newsletter_self_hosted">Newsletters</a> to include a link to the newsletter.
|
||||
Note: Self-hosted newsletters must be enabled under <a data-tab-destination="notifications" data-dismiss="modal" data-target="newsletter_self_hosted">Newsletters</a> to include a link to the newsletter.
|
||||
</p>
|
||||
</div>
|
||||
<div id="newsletter-email-config">
|
||||
@@ -584,6 +584,7 @@
|
||||
var $email_selectors = $('#newsletter_email_to, #newsletter_email_cc, #newsletter_email_bcc').selectize({
|
||||
plugins: ['remove_button'],
|
||||
maxItems: null,
|
||||
searchField: ['text', 'value'],
|
||||
render: {
|
||||
item: function(item, escape) {
|
||||
return '<div>' +
|
||||
|
@@ -8,6 +8,9 @@
|
||||
<meta charset="utf-8">
|
||||
<title>Tautulli - ${title} | ${server_name}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="${http_root}images/favicon/favicon-32x32.png?v=2.0.5">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="${http_root}images/favicon/favicon-16x16.png?v=2.0.5">
|
||||
<link rel="shortcut icon" href="${http_root}images/favicon/favicon.ico?v=2.0.5">
|
||||
<link href="${http_root}css/tautulli.css${cache_param}" rel="stylesheet">
|
||||
<style>
|
||||
* {
|
||||
|
@@ -243,6 +243,11 @@
|
||||
</div>
|
||||
<ul class="submenu">
|
||||
<li>
|
||||
<div class="form-group">
|
||||
<label for="${action['name']}_subject">JSON Headers</label>
|
||||
<textarea class="form-control" id="${action['name']}_subject" name="${action['name']}_subject" data-parsley-trigger="change" data-autoresize required>${notifier['notify_text'][action['name']]['subject']}</textarea>
|
||||
<p class="help-block">Set custom JSON headers.</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="${action['name']}_body">JSON Data</label>
|
||||
<textarea class="form-control" id="${action['name']}_body" name="${action['name']}_body" data-parsley-trigger="change" data-autoresize required>${notifier['notify_text'][action['name']]['body']}</textarea>
|
||||
@@ -326,6 +331,15 @@
|
||||
<p class="help-block">Set custom arguments passed to the script.</p>
|
||||
</div>
|
||||
% elif notifier['agent_name'] == 'webhook':
|
||||
<div class="form-group">
|
||||
<label for="test_subject">JSON Headers</label>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<textarea class="form-control" id="test_subject" name="test_subject" data-autoresize></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<p class="help-block">Set custom JSON headers sent to the webhook.</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="test_body">JSON Data</label>
|
||||
<div class="row">
|
||||
@@ -471,7 +485,7 @@
|
||||
'<div class="form-group">' +
|
||||
'<label>Warning</label>' +
|
||||
'<p class="help-block" style="color: #eb8600;">Facebook requires HTTPS for authorization. ' +
|
||||
'Please enable HTTPS for Tautulli under <a data-tab-destination="tabs-web_interface" data-dismiss="modal" data-target="#enable_https">Web Interface</a>.</p>' +
|
||||
'Please enable HTTPS for Tautulli under <a data-tab-destination="web_interface" data-dismiss="modal" data-target="enable_https">Web Interface</a>.</p>' +
|
||||
'</div>'
|
||||
);
|
||||
$('#facebook_redirect_uri').val('HTTPS not enabled');
|
||||
@@ -566,6 +580,7 @@
|
||||
var $email_selectors = $('#email_to, #email_cc, #email_bcc').selectize({
|
||||
plugins: ['remove_button'],
|
||||
maxItems: null,
|
||||
searchField: ['text', 'value'],
|
||||
render: {
|
||||
item: function(item, escape) {
|
||||
return '<div>' +
|
||||
|
@@ -31,6 +31,9 @@ DOCUMENTATION :: END
|
||||
</%doc>
|
||||
|
||||
% if data != None:
|
||||
<%
|
||||
from plexpy.helpers import page
|
||||
%>
|
||||
% if data:
|
||||
<div class="dashboard-recent-media-row">
|
||||
<div id="recently-added-row-scroller" style="left: 0;">
|
||||
@@ -39,9 +42,9 @@ DOCUMENTATION :: END
|
||||
<div class="dashboard-recent-media-instance">
|
||||
<li data-type="${item['media_type']}">
|
||||
% if item['media_type'] == 'movie':
|
||||
<a href="info?rating_key=${item['rating_key']}" title="${item['title']}">
|
||||
<a href="${page('info', item['rating_key'])}" title="${item['title']}">
|
||||
<div class="dashboard-recent-media-poster">
|
||||
<div class="dashboard-recent-media-poster-face" style="background-image: url(pms_image_proxy?img=${item['thumb']}&width=300&height=450&fallback=poster);">
|
||||
<div class="dashboard-recent-media-poster-face" style="background-image: url(${page('pms_image_proxy', item['thumb'], item['rating_key'], 300, 450, fallback='poster')});">
|
||||
<div class="dashboard-recent-media-overlay">
|
||||
<div class="dashboard-recent-media-overlay-text" id="added_at-${item['rating_key']}">
|
||||
<script>
|
||||
@@ -57,15 +60,15 @@ DOCUMENTATION :: END
|
||||
</a>
|
||||
<div class="dashboard-recent-media-metacontainer">
|
||||
<h3>
|
||||
<a href="info?rating_key=${item['rating_key']}" title="${item['title']}">${item['title']}</a>
|
||||
<a href="${page('info', item['rating_key'])}" title="${item['title']}">${item['title']}</a>
|
||||
</h3>
|
||||
<h3 class="text-muted">${item['year']}</h3>
|
||||
<h3 class="text-muted"> </h3>
|
||||
</div>
|
||||
% elif item['media_type'] == 'show':
|
||||
<a href="info?rating_key=${item['rating_key']}" title="${item['title']}">
|
||||
<a href="${page('info', item['rating_key'])}" title="${item['title']}">
|
||||
<div class="dashboard-recent-media-poster">
|
||||
<div class="dashboard-recent-media-poster-face" style="background-image: url(pms_image_proxy?img=${item['thumb']}&width=300&height=450&fallback=poster);">
|
||||
<div class="dashboard-recent-media-poster-face" style="background-image: url(${page('pms_image_proxy', item['thumb'], item['rating_key'], 300, 450, fallback='poster')});">
|
||||
<div class="dashboard-recent-media-overlay">
|
||||
<div class="dashboard-recent-media-overlay-text" id="added_at-${item['rating_key']}">
|
||||
<script>
|
||||
@@ -81,7 +84,7 @@ DOCUMENTATION :: END
|
||||
</a>
|
||||
<div class="dashboard-recent-media-metacontainer">
|
||||
<h3>
|
||||
<a href="info?rating_key=${item['rating_key']}" title="${item['title']}">${item['title']}</a>
|
||||
<a href="${page('info', item['rating_key'])}" title="${item['title']}">${item['title']}</a>
|
||||
</h3>
|
||||
<h3 class="text-muted">
|
||||
${item['child_count']} Seasons
|
||||
@@ -89,9 +92,13 @@ DOCUMENTATION :: END
|
||||
<h3 class="text-muted"> </h3>
|
||||
</div>
|
||||
% elif item['media_type'] == 'season':
|
||||
<a href="info?rating_key=${item['rating_key']}" title="${item['parent_title']}">
|
||||
<a href="${page('info', item['rating_key'])}" title="${item['parent_title']}">
|
||||
<div class="dashboard-recent-media-poster">
|
||||
<div class="dashboard-recent-media-poster-face" style="background-image: url(pms_image_proxy?img=${item['thumb'] or item['parent_thumb']}&width=300&height=450&fallback=poster);">
|
||||
% if item['thumb']:
|
||||
<div class="dashboard-recent-media-poster-face" style="background-image: url(${page('pms_image_proxy', item['thumb'], item['rating_key'], 300, 450, fallback='poster')});">
|
||||
% else:
|
||||
<div class="dashboard-recent-media-poster-face" style="background-image: url(${page('pms_image_proxy', item['parent_thumb'], item['parent_rating_key'], 300, 450, fallback='poster')});">
|
||||
% endif
|
||||
<div class="dashboard-recent-media-overlay">
|
||||
<div class="dashboard-recent-media-overlay-text" id="added_at-${item['rating_key']}">
|
||||
<script>
|
||||
@@ -107,17 +114,17 @@ DOCUMENTATION :: END
|
||||
</a>
|
||||
<div class="dashboard-recent-media-metacontainer">
|
||||
<h3>
|
||||
<a href="info?rating_key=${item['parent_rating_key']}" title="${item['parent_title']}">${item['parent_title']}</a>
|
||||
<a href="${page('info', item['parent_rating_key'])}" title="${item['parent_title']}">${item['parent_title']}</a>
|
||||
</h3>
|
||||
<h3 class="text-muted">
|
||||
<a class="text-muted" href="info?rating_key=${item['rating_key']}" title="${item['title']}">${item['title']}</a>
|
||||
<a class="text-muted" href="${page('info', item['rating_key'])}" title="${item['title']}">${item['title']}</a>
|
||||
</h3>
|
||||
<h3 class="text-muted"> </h3>
|
||||
</div>
|
||||
% elif item['media_type'] == 'episode':
|
||||
<a href="info?rating_key=${item['rating_key']}" title="${item['title']}">
|
||||
<a href="${page('info', item['rating_key'])}" title="${item['title']}">
|
||||
<div class="dashboard-recent-media-poster">
|
||||
<div class="dashboard-recent-media-poster-face" style="background-image: url(pms_image_proxy?img=${item['grandparent_thumb']}&width=300&height=450&fallback=poster);">
|
||||
<div class="dashboard-recent-media-poster-face" style="background-image: url(${page('pms_image_proxy', item['grandparent_thumb'], item['grandparent_rating_key'], 300, 450, fallback='poster')});">
|
||||
<div class="dashboard-recent-media-overlay">
|
||||
<div class="dashboard-recent-media-overlay-text" id="added_at-${item['rating_key']}">
|
||||
<script>
|
||||
@@ -133,21 +140,21 @@ DOCUMENTATION :: END
|
||||
</a>
|
||||
<div class="dashboard-recent-media-metacontainer">
|
||||
<h3>
|
||||
<a href="info?rating_key=${item['grandparent_rating_key']}" title="${item['grandparent_title']}">${item['grandparent_title']}</a>
|
||||
<a href="${page('info', item['grandparent_rating_key'])}" title="${item['grandparent_title']}">${item['grandparent_title']}</a>
|
||||
</h3>
|
||||
<h3 class="text-muted">
|
||||
<a href="info?rating_key=${item['rating_key']}" title="${item['title']}">${item['title']}</a>
|
||||
<a href="${page('info', item['rating_key'])}" title="${item['title']}">${item['title']}</a>
|
||||
</h3>
|
||||
<h3 class="text-muted">
|
||||
<a href="info?rating_key=${item['parent_rating_key']}" title="Season ${item['parent_media_index']}">S${item['parent_media_index']}</a>
|
||||
<a href="${page('info', item['parent_rating_key'])}" title="Season ${item['parent_media_index']}">S${item['parent_media_index']}</a>
|
||||
·
|
||||
<a href="info?rating_key=${item['rating_key']}" title="Episode ${item['media_index']}">E${item['media_index']}</a>
|
||||
<a href="${page('info', item['rating_key'])}" title="Episode ${item['media_index']}">E${item['media_index']}</a>
|
||||
</h3>
|
||||
</div>
|
||||
% elif item['media_type'] == 'album':
|
||||
<a href="info?rating_key=${item['rating_key']}" title="${item['parent_title']}">
|
||||
<a href="${page('info', item['rating_key'])}" title="${item['parent_title']}">
|
||||
<div class="dashboard-recent-media-cover">
|
||||
<div class="dashboard-recent-media-cover-face" style="background-image: url(pms_image_proxy?img=${item['thumb']}&width=300&height=300&fallback=cover);">
|
||||
<div class="dashboard-recent-media-cover-face" style="background-image: url(${page('pms_image_proxy', item['thumb'], item['rating_key'], 300, 300, fallback='cover')});">
|
||||
<div class="dashboard-recent-media-overlay">
|
||||
<div class="dashboard-recent-media-overlay-text" id="added_at-${item['rating_key']}">
|
||||
<script>
|
||||
@@ -163,10 +170,10 @@ DOCUMENTATION :: END
|
||||
</a>
|
||||
<div class="dashboard-recent-media-metacontainer">
|
||||
<h3>
|
||||
<a href="info?rating_key=${item['parent_rating_key']}" title="${item['parent_title']}">${item['parent_title']}</a>
|
||||
<a href="${page('info', item['parent_rating_key'])}" title="${item['parent_title']}">${item['parent_title']}</a>
|
||||
</h3>
|
||||
<h3 class="text-muted">
|
||||
<a class="text-muted" href="info?rating_key=${item['rating_key']}" title="${item['title']}">${item['title']}</a>
|
||||
<a class="text-muted" href="${page('info', item['rating_key'])}" title="${item['title']}">${item['title']}</a>
|
||||
</h3>
|
||||
<h3 class="text-muted"> </h3>
|
||||
</div>
|
||||
|
@@ -56,6 +56,7 @@
|
||||
<li role="presentation"><a href="#tabs-notifications" aria-controls="tabs-notifications" role="tab" data-toggle="tab">Notifications & Newsletters</a></li>
|
||||
<li role="presentation"><a href="#tabs-notification_agents" aria-controls="tabs-notification_agents" role="tab" data-toggle="tab">Notification Agents</a></li>
|
||||
<li role="presentation"><a href="#tabs-newsletter_agents" aria-controls="tabs-newsletter_agents" role="tab" data-toggle="tab">Newsletter Agents</a></li>
|
||||
<li role="presentation"><a href="#tabs-3rd_party_apis" aria-controls="tabs-3rd_party_apis" role="tab" data-toggle="tab">3rd Party APIs</a></li>
|
||||
<li role="presentation"><a href="#tabs-import_backups" aria-controls="tabs-import_backups" role="tab" data-toggle="tab">Import & Backups</a></li>
|
||||
<li role="presentation"><a href="#tabs-android_app" aria-controls="tabs-android_app" role="tab" data-toggle="tab">Tautulli Remote Android App <sup><small>beta</small></sup></a></li>
|
||||
</ul>
|
||||
@@ -216,7 +217,7 @@
|
||||
<div id="git_update_options">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="plexpy_auto_update" name="plexpy_auto_update" value="1" ${config['plexpy_auto_update']}> Update Automatically
|
||||
<input type="checkbox" id="plexpy_auto_update" name="plexpy_auto_update" value="1" ${config['plexpy_auto_update']} ${docker_setting}> Update Automatically ${docker_msg | n}
|
||||
</label>
|
||||
<p class="help-block">Update Tautulli automatically if an update is available.</p>
|
||||
</div>
|
||||
@@ -264,6 +265,20 @@
|
||||
</div>
|
||||
<p class="help-block">Optional: The path to your git environment variable. Leave blank for default.</p>
|
||||
</div>
|
||||
<div class="form-group advanced-setting">
|
||||
<label>Repair Git Install</label>
|
||||
<p class="help-block">
|
||||
Attempt to fix updating by resetting your Tautulli installation back to <strong>${common.RELEASE}</strong>.<br />
|
||||
Note: This will not affect any saved history or settings.
|
||||
</p>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-form" type="button" id="reset_git_install">Reset</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
% endif
|
||||
|
||||
<p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p>
|
||||
@@ -284,7 +299,7 @@
|
||||
</div>
|
||||
<div id="home_refresh_interval_error" class="alert alert-danger settings-alert" role="alert"></div>
|
||||
</div>
|
||||
<p class="help-block">Set the interval (in seconds) to refresh the current activity on the homepage. Minimum 2.</p>
|
||||
<p class="help-block">Set the interval (in seconds) to refresh the current activity on the homepage. Minimum 2, default 10.</p>
|
||||
</div>
|
||||
|
||||
<div class="padded-header">
|
||||
@@ -983,6 +998,20 @@
|
||||
</div>
|
||||
<p class="help-block">Set the delay (in seconds) to wait for consecutive recently added items to group together and to allow metadata to be processed before sending the notification. Minimum 60 seconds.</p>
|
||||
</div>
|
||||
<div class="form-group advanced-setting">
|
||||
<label>Flush Recently Added</label>
|
||||
<p class="help-block">
|
||||
Attempt to fix recently added notifications by flushing out all of the recently added items in the database.<br />
|
||||
Warning: This will reset all recently added notifications. For emergency use only when recently added notifications are stuck!
|
||||
</p>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-form" type="button" id="delete_recently_added">Flush</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" name="notify_recently_added_upgrade" id="notify_recently_added_upgrade" value="1" ${config['notify_recently_added_upgrade']}> Send a Notification for New Versions <span class="settings-warning">[Not working]</span>
|
||||
@@ -1008,7 +1037,7 @@
|
||||
<p class="help-block" id="self_host_newsletter_message">
|
||||
Note: The <span class="inline-pre">${http_root}newsletter</span> endpoint on your domain must be publicly accessible from the internet.
|
||||
</p>
|
||||
<p class="help-block settings-warning base-url-warning">Warning: Public Tautulli domain not set under <a data-tab-destination="tabs-web_interface" data-target="#http_base_url">Web Interface</a>.</p>
|
||||
<p class="help-block settings-warning base-url-warning">Warning: Public Tautulli domain not set under <a data-tab-destination="web_interface" data-target="http_base_url">Web Interface</a>.</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="newsletter_auth">Newsletter Authentication</label>
|
||||
@@ -1022,7 +1051,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<p class="help-block">Select the authentication method to use for self-hosted newsletters.</p>
|
||||
<p class="help-block settings-warning newsletter-guest-access-warning">Warning: Guest Access is not enabled under <a data-tab-destination="tabs-web_interface" data-target="#allow_guest_access">Web Interface</a>.</p>
|
||||
<p class="help-block settings-warning newsletter-guest-access-warning">Warning: Guest Access is not enabled under <a data-tab-destination="web_interface" data-target="allow_guest_access">Web Interface</a>.</p>
|
||||
</div>
|
||||
<div class="form-group" id="newsletter_password_option">
|
||||
<label for="newsletter_password">Newsletter Password</label>
|
||||
@@ -1063,12 +1092,60 @@
|
||||
<p class="help-block">Enter the full path to where newsletter files will be saved.</p>
|
||||
</div>
|
||||
|
||||
<p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p>
|
||||
|
||||
</div>
|
||||
|
||||
<div role="tabpanel" class="tab-pane" id="tabs-notification_agents">
|
||||
|
||||
<div class="padded-header">
|
||||
<h3>3rd Party APIs</h3>
|
||||
<h3>Notification Agents</h3>
|
||||
</div>
|
||||
|
||||
<p class="help-block">
|
||||
Add a new notification agent, or configure an existing notification agent by clicking the settings icon on the right.
|
||||
</p>
|
||||
<p class="help-block">
|
||||
Please see the <a target='_blank' href='${anon_url('https://github.com/%s/%s-Wiki/wiki/Notification-Agents-Guide' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO))}'>Notification Agents Guide</a> for instructions on setting up each notification agent.
|
||||
</p>
|
||||
<br />
|
||||
<div id="plexpy-notifiers-table">
|
||||
<div class='text-muted'><i class="fa fa-refresh fa-spin"></i> Loading notification agents...</div>
|
||||
<br>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div role="tabpanel" class="tab-pane" id="tabs-newsletter_agents">
|
||||
|
||||
<div class="padded-header">
|
||||
<h3>Newsletter Agents</h3>
|
||||
</div>
|
||||
|
||||
<p class="help-block">
|
||||
Add a new newsletter agent, or configure an existing newsletter agent by clicking the settings icon on the right.
|
||||
</p>
|
||||
<p class="help-block settings-warning" id="newsletter_upload_warning">
|
||||
Warning: The <a data-tab-destination="3rd_party_apis" data-target="notify_upload_posters">Image Hosting</a> setting must be enabled for images to display on the newsletter.</span>
|
||||
</p>
|
||||
<br/>
|
||||
<div id="plexpy-newsletters-table">
|
||||
<div class='text-muted'><i class="fa fa-refresh fa-spin"></i> Loading newsletter agents...</div>
|
||||
<br>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div role="tabpanel" class="tab-pane" id="tabs-3rd_party_apis">
|
||||
|
||||
<div class="padded-header">
|
||||
<h3>Image Hosting</h3>
|
||||
</div>
|
||||
|
||||
<p class="help-block">Image hosting is used to provide posters and artwork for some notification agents and newsletters.</p>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="notify_upload_posters">Image Hosting</label>
|
||||
<label for="notify_upload_posters">Image Host</label>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="${'input-group' if config['notify_upload_posters'] in (1, 3) else ''}">
|
||||
@@ -1090,8 +1167,8 @@
|
||||
</div>
|
||||
<div id="imgur_upload_options" style="overlfow: hidden; display: ${'none' if config['notify_upload_posters'] != 1 else 'block'}">
|
||||
<div class="form-group">
|
||||
<p class="help-block" id="imgur_upload_message">
|
||||
You can register a new Imgur application <a href="${anon_url('https://api.imgur.com/oauth2/addclient')}" target="_blank">here</a>.<br>
|
||||
<p class="help-block">
|
||||
Please see the <a target='_blank' href='${anon_url('https://github.com/%s/%s-Wiki/wiki/3rd-Party-APIs-Guide' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO))}'>3rd Party APIs Guide</a> for instructions on setting up Imgur.<br>
|
||||
Warning: Imgur uploads are rate-limited and newsletters may exceed the limit. Please use Cloudinary for newsletters instead.
|
||||
</p>
|
||||
</div>
|
||||
@@ -1108,13 +1185,13 @@
|
||||
<div id="self_host_image_options" style="overlfow: hidden; display: ${'none' if config['notify_upload_posters'] != 2 else 'block'}">
|
||||
<div class="form-group">
|
||||
<p class="help-block" id="self_host_image_message">Note: The <span class="inline-pre">${http_root}image</span> endpoint on your domain must be publicly accessible from the internet.</p>
|
||||
<p class="help-block settings-warning base-url-warning">Warning: Public Tautulli domain not set under <a data-tab-destination="tabs-web_interface" data-target="#http_base_url">Web Interface</a>.</p>
|
||||
<p class="help-block settings-warning base-url-warning">Warning: Public Tautulli domain not set under <a data-tab-destination="web_interface" data-target="http_base_url">Web Interface</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="cloudinary_upload_options" style="overlfow: hidden; display: ${'none' if config['notify_upload_posters'] != 3 else 'block'}">
|
||||
<div class="form-group">
|
||||
<p class="help-block" id="imgur_upload_message">
|
||||
You can sign up for Cloudinary <a href="${anon_url('https://cloudinary.com')}" target="_blank">here</a>.<br>
|
||||
<p class="help-block">
|
||||
Please see the <a target='_blank' href='${anon_url('https://github.com/%s/%s-Wiki/wiki/3rd-Party-APIs-Guide' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO))}'>3rd Party APIs Guide</a> for instructions on setting up Cloudinary.
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
@@ -1151,6 +1228,13 @@
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="padded-header">
|
||||
<h3>Metadata Lookups</h3>
|
||||
</div>
|
||||
|
||||
<p class="help-block">Metadata lookups are used to provide additional links for notifications when available.</p>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" name="themoviedb_lookup" id="themoviedb_lookup" value="1" ${config['themoviedb_lookup']}> Lookup TheMovieDB Links
|
||||
@@ -1163,51 +1247,78 @@
|
||||
</label>
|
||||
<p class="help-block">Enable to lookup links to TVmaze (and IMDb if needed) for TV shows when available.</p>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" name="musicbrainz_lookup" id="musicbrainz_lookup" value="1" ${config['musicbrainz_lookup']}> Lookup MusicBrainz Links
|
||||
</label>
|
||||
<p class="help-block">Enable to lookup links to MusicBrainz for music when available.</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="maxmind_license_key">Delete Lookup Info</label>
|
||||
<p class="help-block">Delete all cached metadata lookup info in Tautulli.</p>
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-form delete_all_lookups" type="button" data-service="themoviedb">TheMovieDB</button>
|
||||
<button class="btn btn-form delete_all_lookups" type="button" data-service="tvmaze">TVmaze</button>
|
||||
<button class="btn btn-form delete_all_lookups" type="button" data-service="musicbrainz">MusicBrainz</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="padded-header">
|
||||
<h3>Geolocation Database</h3>
|
||||
</div>
|
||||
|
||||
<p class="help-block">The GeoLite2 database is used to geolocate IP addresses.</p>
|
||||
<p class="help-block">
|
||||
Please see the <a target='_blank' href='${anon_url('https://github.com/%s/%s-Wiki/wiki/3rd-Party-APIs-Guide' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO))}'>3rd Party APIs Guide</a> for instructions on setting up MaxMind.<br>
|
||||
</p>
|
||||
<div class="form-group">
|
||||
<label for="maxmind_license_key">MaxMind License Key</label>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="maxmind_license_key" name="maxmind_license_key" value="${config['maxmind_license_key']}" data-parsley-trigger="change">
|
||||
</div>
|
||||
</div>
|
||||
<p class="help-block">
|
||||
Enter your MaxMind License Key to install the GeoLite2 database.
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="geoip_db">GeoLite2 Database File</label> ${docker_msg | n}
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" id="geoip_db" name="geoip_db" value="${config['geoip_db']}" ${docker_setting} data-parsley-trigger="change" data-parsley-pattern=".+\.mmdb$" data-parsley-errors-container="#geoip_db_error" data-parsley-error-message="Must end with '.mmdb'">
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-form" type="button" id="install_geoip_db">${'Update' if config["geoip_db_installed"] else 'Install'}</button>
|
||||
<button class="btn btn-form" type="button" id="uninstall_geoip_db" ${'disabled' if not config['geoip_db_installed'] else ''}>Uninstall</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="geoip_db_error" class="alert alert-danger settings-alert" role="alert"></div>
|
||||
</div>
|
||||
<p class="help-block">
|
||||
Leave blank to install in the default location. GeoLite2 database last updated <strong><span id="geoip_db_updated">never</span></strong>.
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group advanced-setting">
|
||||
<label for="geoip_db_update_days">GeoLite2 Database Update Interval</label>
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<input type="text" class="form-control" data-parsley-type="integer" id="geoip_db_update_days" name="geoip_db_update_days" value="${config['geoip_db_update_days']}" size="5" data-parsley-range="[7, 30]" data-parsley-trigger="change" data-parsley-errors-container="#geoip_db_update_days_error" required>
|
||||
</div>
|
||||
<div id="geoip_db_update_days_error" class="alert alert-danger settings-alert" role="alert"></div>
|
||||
</div>
|
||||
<p class="help-block">The interval (in days) Tautulli will automatically update the GeoLite2 database. Minimum 7, maximum 30, default 30.</p>
|
||||
</div>
|
||||
|
||||
<p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p>
|
||||
|
||||
</div>
|
||||
|
||||
<div role="tabpanel" class="tab-pane" id="tabs-notification_agents">
|
||||
|
||||
<div class="padded-header">
|
||||
<h3>Notification Agents</h3>
|
||||
</div>
|
||||
|
||||
<p class="help-block">
|
||||
Add a new notification agent, or configure an existing notification agent by clicking the settings icon on the right.
|
||||
</p>
|
||||
<p class="help-block">
|
||||
Please see the <a target='_blank' href='${anon_url('https://github.com/%s/%s-Wiki/wiki/Notification-Agents-Guide' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO))}'>Notification Agents Guide</a> for instructions on setting up each notification agent.
|
||||
</p>
|
||||
<br />
|
||||
<div id="plexpy-notifiers-table">
|
||||
<div class='text-muted'><i class="fa fa-refresh fa-spin"></i> Loading notification agents...</div>
|
||||
<br>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div role="tabpanel" class="tab-pane" id="tabs-newsletter_agents">
|
||||
|
||||
<div class="padded-header">
|
||||
<h3>Newsletter Agents</h3>
|
||||
</div>
|
||||
|
||||
<p class="help-block">
|
||||
Add a new newsletter agent, or configure an existing newsletter agent by clicking the settings icon on the right.
|
||||
</p>
|
||||
<p class="help-block settings-warning" id="newsletter_upload_warning">
|
||||
Warning: The <a data-tab-destination="tabs-notifications" data-target="#notify_upload_posters">Image Hosting</a> setting must be enabled for images to display on the newsletter.</span>
|
||||
</p>
|
||||
<br/>
|
||||
<div id="plexpy-newsletters-table">
|
||||
<div class='text-muted'><i class="fa fa-refresh fa-spin"></i> Loading newsletter agents...</div>
|
||||
<br>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div role="tabpanel" class="tab-pane" id="tabs-import_backups">
|
||||
|
||||
<div class="padded-header">
|
||||
@@ -1310,7 +1421,7 @@
|
||||
<div class="form-group">
|
||||
<label>Registered Devices</label>
|
||||
<p class="help-block">Register a new device using a QR code, or configure an existing device by clicking the settings icon on the right.</p>
|
||||
<p id="app_api_msg" style="color: #eb8600;">The API must be enabled under <a data-tab-destination="tabs-web_interface" data-target="#api_enabled">Web Interface</a> to use the app.</p>
|
||||
<p id="app_api_msg" style="color: #eb8600;">Warning: The API must be enabled under <a data-tab-destination="web_interface" data-target="api_enabled">Web Interface</a> to use the app.</p>
|
||||
<div class="row">
|
||||
<div id="plexpy-mobile-devices-table" class="col-md-12">
|
||||
<div class='text-muted'><i class="fa fa-refresh fa-spin"></i> Loading registered devices...</div>
|
||||
@@ -1790,11 +1901,6 @@ Rating: {rating}/10 --> Rating: /10
|
||||
async: true,
|
||||
complete: function(xhr, status) {
|
||||
$("#plexpy-configuration-table").html(xhr.responseText);
|
||||
if ("${kwargs.get('install_geoip')}" == 'true') {
|
||||
$('#install_geoip_db').removeClass('no-highlight').css('color','#e9a049');
|
||||
} else if ("${kwargs.get('reinstall_geoip')}" == 'true') {
|
||||
$('#reinstall_geoip_db').removeClass('no-highlight').css('color','#e9a049');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1915,12 +2021,10 @@ $(document).ready(function() {
|
||||
}
|
||||
|
||||
function preSaveChecks(_callback) {
|
||||
if (serverChanged) {
|
||||
verifyServer();
|
||||
}
|
||||
verifyPMSWebURL();
|
||||
|
||||
if (_callback) {
|
||||
if (serverChanged) {
|
||||
verifyServer(_callback);
|
||||
} else if (typeof _callback === "function") {
|
||||
_callback();
|
||||
}
|
||||
}
|
||||
@@ -1945,12 +2049,13 @@ $(document).ready(function() {
|
||||
settingsChanged = true;
|
||||
});
|
||||
|
||||
function saveSettings() {
|
||||
function saveSettings(showMsg, _callback) {
|
||||
if (configForm.parsley().validate()) {
|
||||
doAjaxCall('configUpdate', $(this), 'tabs', true, true, postSaveChecks);
|
||||
return false;
|
||||
doAjaxCall('configUpdate', $(this), 'tabs', true, showMsg, _callback);
|
||||
return true;
|
||||
} 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);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1964,7 +2069,7 @@ $(document).ready(function() {
|
||||
}
|
||||
|
||||
$('.save-button').click(function() {
|
||||
preSaveChecks(function () { saveSettings() });
|
||||
preSaveChecks(function () { saveSettings(true, postSaveChecks) });
|
||||
});
|
||||
|
||||
initConfigCheckbox('#api_enabled');
|
||||
@@ -2065,11 +2170,17 @@ $(document).ready(function() {
|
||||
});
|
||||
|
||||
$("#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 msg = 'Are you sure you want to flush the temporary sessions?<br /><br /><strong>This will reset all currently active sessions.</strong>';
|
||||
var url = 'delete_temp_sessions';
|
||||
confirmAjaxCall(url, msg);
|
||||
});
|
||||
|
||||
$("#delete_recently_added").click(function () {
|
||||
var msg = 'Are you sure you want to flush the recently added items?<br /><br /><strong>This will reset all recently added notifications.</strong>';
|
||||
var url = 'delete_recently_added';
|
||||
confirmAjaxCall(url, msg);
|
||||
});
|
||||
|
||||
$("#switch_git_branch").click(function () {
|
||||
var current_remote = "${config['git_remote']}";
|
||||
var current_branch = "${config['git_branch']}";
|
||||
@@ -2090,6 +2201,17 @@ $(document).ready(function() {
|
||||
}
|
||||
});
|
||||
|
||||
$("#reset_git_install").click(function () {
|
||||
var msg = 'Are you sure you want to reset your Tautulli installtion back to <strong>${common.RELEASE}</strong>?' +
|
||||
'<br /><br />Tautulli will restart.';
|
||||
$('#confirm-message').html(msg);
|
||||
$('#confirm-modal').modal();
|
||||
$('#confirm-modal').one('click', '#confirm-button', function () {
|
||||
settingsChanged = false;
|
||||
window.location.href = 'reset_git_install';
|
||||
});
|
||||
});
|
||||
|
||||
$('#api_key').click(function(){ $('#api_key').select() });
|
||||
$("#generate_api").click(function() {
|
||||
$.get('generate_api_key',
|
||||
@@ -2273,6 +2395,7 @@ $(document).ready(function() {
|
||||
|
||||
if (($("#pms_ip").val() !== '') || ($("#pms_port").val() !== '')) {
|
||||
$("#pms_verify").html('<i class="fa fa-refresh fa-spin"></i>').fadeIn('fast');
|
||||
showMsg('Verifying Plex server...', true, true, 10000, false);
|
||||
$.ajax({
|
||||
url: 'get_server_id',
|
||||
data: {
|
||||
@@ -2310,10 +2433,11 @@ $(document).ready(function() {
|
||||
} else {
|
||||
$("#pms_verify").html('<i class="fa fa-check"></i>').fadeIn('fast');
|
||||
$("#pms_ip_group").removeClass("has-error");
|
||||
showMsg('<i class="fa fa-check"></i> Server verified.', false, true, 5000);
|
||||
serverChanged = false;
|
||||
}
|
||||
|
||||
if (_callback) {
|
||||
if (typeof _callback === "function") {
|
||||
_callback();
|
||||
}
|
||||
} else {
|
||||
@@ -2465,6 +2589,7 @@ $(document).ready(function() {
|
||||
for (var i in libraries_list) {
|
||||
var title = libraries_list[i].section_name;
|
||||
var key = libraries_list[i].section_id;
|
||||
if (key === 999999) { continue; } // Don't show Live TV library
|
||||
$('#sortable_home_library_cards').append(
|
||||
'<li class="card card-sortable">' +
|
||||
'<div class="card-handle"><i class="fa fa-bars"></i></div>' +
|
||||
@@ -2714,12 +2839,23 @@ $(document).ready(function() {
|
||||
var name = image_hosting_option.text();
|
||||
|
||||
var msg = 'Are you sure you want to delete all uploaded images on <strong>' + name + '</strong>?' +
|
||||
'<br />All previous links to the images will no longer work. This cannot be undone!';
|
||||
'<br /><br />All previous links to the images will no longer work. This cannot be undone!';
|
||||
var url = 'delete_hosted_images';
|
||||
var data = { service: name, delete_all: true };
|
||||
confirmAjaxCall(url, msg, data, false);
|
||||
});
|
||||
|
||||
$('body').on('click', '.delete_all_lookups', function () {
|
||||
var service = $(this).data('service');
|
||||
var name = $(this).text();
|
||||
|
||||
var msg = 'Are you sure you want to delete all the metadata lookup info from <strong>' + name + '</strong>?' +
|
||||
'<br /><br />Tautulli will lookup the metadata info again the next time a notification is sent.';
|
||||
var url = 'delete_lookup_info';
|
||||
var data = { service: service, delete_all: true };
|
||||
confirmAjaxCall(url, msg, data, false);
|
||||
});
|
||||
|
||||
function baseURLSet() {
|
||||
if ($('#http_base_url').val()) {
|
||||
$('.base-url-warning').hide();
|
||||
@@ -2767,25 +2903,81 @@ $(document).ready(function() {
|
||||
|
||||
$('#allow_guest_access').click(function () {
|
||||
newsletterPasswordEnabled();
|
||||
})
|
||||
});
|
||||
|
||||
function gotoSetting(tab, setting){
|
||||
$("a[href=#tabs-" + tab + "]").click();
|
||||
if (setting) {
|
||||
_setting = '#' + setting;
|
||||
if ($(_setting).closest('.advanced-setting').length && !$('#menu_link_show_advanced_settings').hasClass('active')) {
|
||||
$('#menu_link_show_advanced_settings').click()
|
||||
}
|
||||
var body_container = $('.body-container');
|
||||
var scroll_pos = setting ? body_container.scrollTop() + $(_setting).offset().top - 100 : 0;
|
||||
body_container.animate({scrollTop: scroll_pos});
|
||||
$(_setting).closest('.form-group, .checkbox').delay(500).fadeOut().fadeIn('slow').fadeOut().fadeIn('slow');
|
||||
}
|
||||
}
|
||||
|
||||
$('body').on('click', 'a[data-tab-destination]', function () {
|
||||
var tab = $(this).data('tab-destination');
|
||||
$("a[href=#" + tab + "]").click();
|
||||
var scroll_destination = $(this).data('target');
|
||||
if (scroll_destination) {
|
||||
if ($(scroll_destination).closest('.advanced-setting').length && !$('#menu_link_show_advanced_settings').hasClass('active')) {
|
||||
$('#menu_link_show_advanced_settings').click()
|
||||
}
|
||||
var body_container = $('.body-container')
|
||||
var scroll_pos = scroll_destination ? body_container.scrollTop() + $(scroll_destination).offset().top - 100 : 0;
|
||||
body_container.animate({scrollTop: scroll_pos});
|
||||
}
|
||||
var setting = $(this).data('target');
|
||||
gotoSetting(tab, setting)
|
||||
});
|
||||
|
||||
$('#resources-xml').on('tripleclick', function () {
|
||||
openPlexXML('/api/resources', true, {includeHttps: 1});
|
||||
});
|
||||
|
||||
if ("${kwargs.get('install_geoip')}" === 'true') {
|
||||
gotoSetting('3rd_party_apis', 'geoip_db')
|
||||
}
|
||||
|
||||
if ("${config['geoip_db_installed']}" > "0") {
|
||||
$("#geoip_db_updated").text(moment("${config['geoip_db_installed']}", "X").fromNow());
|
||||
}
|
||||
|
||||
$("#install_geoip_db").click(function () {
|
||||
var maxmind_license_key = $("#maxmind_license_key");
|
||||
maxmind_license_key.val($.trim(maxmind_license_key.val()));
|
||||
if (maxmind_license_key.val() === "") {
|
||||
maxmind_license_key.focus();
|
||||
showMsg('<i class="fa fa-exclamation-circle"></i> Maxmind License Key is required.', false, true, 5000, true);
|
||||
return false;
|
||||
} else if (!(saveSettings())) {
|
||||
return false;
|
||||
}
|
||||
var msg = 'Are you sure you want to install the GeoLite2 database?<br /><br />' +
|
||||
'The database is used to lookup IP address geolocation info.<br />' +
|
||||
'The database will be downloaded from <a href="${anon_url("https://dev.maxmind.com/geoip/geoip2/geolite2/")}" target="_blank">MaxMind</a>, <br />' +
|
||||
'and requires <strong>100MB</strong> of free space to install.<br />';
|
||||
var url = 'install_geoip_db';
|
||||
if ($(this).text() === 'Update') {
|
||||
url += '?update=true';
|
||||
}
|
||||
confirmAjaxCall(url, msg, null, 'Installing GeoLite2 database.', function (result) {
|
||||
if (result.result === "success") {
|
||||
$('#install_geoip_db').text('Update');
|
||||
$('#uninstall_geoip_db').prop('disabled', false);
|
||||
$('#geoip_db_updated').text(moment(result.updated, "X").fromNow());
|
||||
}
|
||||
getSchedulerTable();
|
||||
});
|
||||
});
|
||||
|
||||
$("#uninstall_geoip_db").click(function () {
|
||||
var msg = 'Are you sure you want to uninstall the GeoLite2 database?<br /><br />' +
|
||||
'You will not be able to lookup IP address geolocation info.';
|
||||
var url = 'uninstall_geoip_db';
|
||||
confirmAjaxCall(url, msg, null, 'Uninstalling GeoLite2 database.', function (result) {
|
||||
if (result.result === "success") {
|
||||
$('#install_geoip_db').text('Install');
|
||||
$('#uninstall_geoip_db').prop('disabled', true);
|
||||
$('#geoip_db_updated').text('never');
|
||||
}
|
||||
getSchedulerTable();
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</%def>
|
||||
|
@@ -96,8 +96,8 @@ DOCUMENTATION :: END
|
||||
% if data['media_type'] != 'track':
|
||||
<tr>
|
||||
<td>Resolution</td>
|
||||
<td>${VIDEO_RESOLUTION_OVERRIDES.get(data['stream_video_resolution'], data['stream_video_resolution'])}</td>
|
||||
<td>${VIDEO_RESOLUTION_OVERRIDES.get(data['video_resolution'], data['video_resolution'])}</td>
|
||||
<td>${data['stream_video_full_resolution']}</td>
|
||||
<td>${data['video_full_resolution']}</td>
|
||||
</tr>
|
||||
% endif
|
||||
<tr>
|
||||
@@ -178,6 +178,11 @@ DOCUMENTATION :: END
|
||||
<td>${data['stream_video_framerate']}</td>
|
||||
<td>${data['video_framerate']}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Dynamic Range</td>
|
||||
<td>${data['stream_video_dynamic_range']}</td>
|
||||
<td>${data['video_dynamic_range']}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Aspect Ratio</td>
|
||||
<td>-</td>
|
||||
|
@@ -39,30 +39,43 @@ DOCUMENTATION :: END
|
||||
<ul class="list-unstyled breadcrumb">
|
||||
% if query['media_type'] == 'movie':
|
||||
<li>Movies</li>
|
||||
<span class="breadcrumb-arrow"><i class="fa fa-chevron-right"></i></span>
|
||||
<li class="active">${query['title']}</li>
|
||||
% elif query['media_type'] == 'show':
|
||||
<li>TV Shows</li>
|
||||
<span class="breadcrumb-arrow"><i class="fa fa-chevron-right"></i></span>
|
||||
<li class="active">${query['grandparent_title']}</li>
|
||||
% elif query['media_type'] == 'season':
|
||||
<li class="hidden-xs hidden-sm">TV Shows</li>
|
||||
<span class="breadcrumb-arrow"><i class="fa fa-chevron-right"></i></span>
|
||||
<li class="hidden-xs hidden-sm">${query['grandparent_title']}</li>
|
||||
<span class="breadcrumb-arrow"><i class="fa fa-chevron-right"></i></span>
|
||||
<li class="active">Season ${query['parent_media_index']}</li>
|
||||
% elif query['media_type'] == 'episode':
|
||||
<li class="hidden-xs hidden-sm">TV Shows</li>
|
||||
<span class="breadcrumb-arrow"><i class="fa fa-chevron-right"></i></span>
|
||||
<li class="hidden-xs hidden-sm">${query['grandparent_title']}</li>
|
||||
<span class="breadcrumb-arrow"><i class="fa fa-chevron-right"></i></span>
|
||||
<li>Season ${query['parent_media_index']}</li>
|
||||
<span class="breadcrumb-arrow"><i class="fa fa-chevron-right"></i></span>
|
||||
<li class="active">Episode ${query['media_index']} - ${query['title']}</li>
|
||||
% elif query['media_type'] == 'artist':
|
||||
<li><Music</li>
|
||||
<li>Music</li>
|
||||
<span class="breadcrumb-arrow"><i class="fa fa-chevron-right"></i></span>
|
||||
<li class="active">${query['grandparent_title']}</li>
|
||||
% elif query['media_type'] == 'album':
|
||||
<li class="hidden-xs hidden-sm">Music</li>
|
||||
<span class="breadcrumb-arrow"><i class="fa fa-chevron-right"></i></span>
|
||||
<li>${query['grandparent_title']}</li>
|
||||
<span class="breadcrumb-arrow"><i class="fa fa-chevron-right"></i></span>
|
||||
<li class="active">${query['parent_title']}</li>
|
||||
% elif query['media_type'] == 'track':
|
||||
<li class="hidden-xs hidden-sm">Music</li>
|
||||
<span class="breadcrumb-arrow"><i class="fa fa-chevron-right"></i></span>
|
||||
<li class="hidden-xs hidden-sm">${query['grandparent_title']}</li>
|
||||
<span class="breadcrumb-arrow"><i class="fa fa-chevron-right"></i></span>
|
||||
<li>${query['parent_title']}</li>
|
||||
<span class="breadcrumb-arrow"><i class="fa fa-chevron-right"></i></span>
|
||||
<li class="active">Track ${query['media_index']} - ${query['title']}</li>
|
||||
% endif
|
||||
</ul>
|
||||
@@ -127,6 +140,7 @@ DOCUMENTATION :: END
|
||||
</%def>
|
||||
|
||||
<%def name="modalIncludes()">
|
||||
% if query:
|
||||
<div class="modal fade" id="confirm-modal-update" tabindex="-1" role="dialog" aria-labelledby="confirm-modal-update">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
@@ -169,6 +183,7 @@ DOCUMENTATION :: END
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="javascriptIncludes()">
|
||||
|
@@ -51,7 +51,13 @@ DOCUMENTATION :: END
|
||||
<div class="col-md-12">
|
||||
<div class="table-card-back">
|
||||
<div class="user-info-wrapper">
|
||||
<div class="user-info-poster-face" style="background-image: url(${data['user_thumb']});"></div>
|
||||
<div class="user-info-poster-face" style="background-image: url(${data['user_thumb']});">
|
||||
% if not data['is_active']:
|
||||
<span class="inactive-user-tooltip" data-toggle="tooltip" title="User not on Plex server">
|
||||
<i class="fa fa-2x fa-exclamation-triangle"></i>
|
||||
</span>
|
||||
% endif
|
||||
</div>
|
||||
<div class="user-info-username">
|
||||
<span class="set-username">${data['friendly_name']}</span>
|
||||
% if _session['user_group'] == 'admin':
|
||||
@@ -168,6 +174,9 @@ DOCUMENTATION :: END
|
||||
<label class="btn btn-dark">
|
||||
<input type="radio" name="media_type-filter" id="history-track" value="track" autocomplete="off"> Music
|
||||
</label>
|
||||
<label class="btn btn-dark">
|
||||
<input type="radio" name="media_type-filter" id="history-live" value="live" autocomplete="off"> Live TV
|
||||
</label>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-dark refresh-history-button" id="refresh-history-list"><i class="fa fa-refresh"></i> Refresh history</button>
|
||||
@@ -184,6 +193,7 @@ DOCUMENTATION :: END
|
||||
<th align="left" id="friendly_name">User</th>
|
||||
<th align="left" id="ip_address">IP Address</th>
|
||||
<th align="left" id="platform">Platform</th>
|
||||
<th align="left" id="product">Product</th>
|
||||
<th align="left" id="player">Player</th>
|
||||
<th align="left" id="title">Title</th>
|
||||
<th align="left" id="started">Started</th>
|
||||
@@ -425,7 +435,7 @@ DOCUMENTATION :: END
|
||||
history_table = $('#history_table-UID-${data["user_id"]}').DataTable(history_table_options);
|
||||
history_table.column(2).visible(false);
|
||||
|
||||
var colvis = new $.fn.dataTable.ColVis(history_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark', exclude: [0, 11] });
|
||||
var colvis = new $.fn.dataTable.ColVis(history_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark', exclude: [0, 12] });
|
||||
$(colvis.button()).appendTo('#button-bar-history');
|
||||
|
||||
clearSearchButton('history_table-UID-${data["user_id"]}', history_table);
|
||||
@@ -536,6 +546,8 @@ DOCUMENTATION :: END
|
||||
login_log_table.draw();
|
||||
});
|
||||
|
||||
$(".inactive-user-tooltip").tooltip();
|
||||
|
||||
% if _session['user_group'] == 'admin':
|
||||
$("#edit-user-tooltip").tooltip();
|
||||
|
||||
@@ -562,17 +574,15 @@ DOCUMENTATION :: END
|
||||
$('#deleteType').text('history');
|
||||
$('#confirm-modal-delete').modal();
|
||||
$('#confirm-modal-delete').one('click', '#confirm-delete', function () {
|
||||
history_to_delete.forEach(function(row, idx) {
|
||||
$.ajax({
|
||||
url: 'delete_history_rows',
|
||||
type: 'POST',
|
||||
data: { row_id: row },
|
||||
async: true,
|
||||
success: function (data) {
|
||||
var msg = "History deleted";
|
||||
showMsg(msg, false, true, 2000);
|
||||
}
|
||||
});
|
||||
$.ajax({
|
||||
url: 'delete_history_rows',
|
||||
type: 'POST',
|
||||
data: { row_ids: history_to_delete.join(',') },
|
||||
async: true,
|
||||
success: function (data) {
|
||||
var msg = "History deleted";
|
||||
showMsg(msg, false, true, 2000);
|
||||
}
|
||||
});
|
||||
history_table.draw();
|
||||
});
|
||||
|
@@ -27,6 +27,9 @@ DOCUMENTATION :: END
|
||||
</%doc>
|
||||
|
||||
% if data:
|
||||
<%
|
||||
from plexpy.helpers import page
|
||||
%>
|
||||
<div class="dashboard-recent-media-row">
|
||||
<div id="recently-watched-row-scroller" style="left: 0;">
|
||||
<ul class="dashboard-recent-media list-unstyled">
|
||||
@@ -35,12 +38,12 @@ DOCUMENTATION :: END
|
||||
% if item['media_type'] == 'episode' or item['media_type'] == 'movie':
|
||||
% if item['rating_key']:
|
||||
% if item['media_type'] == 'movie':
|
||||
<a href="info?source=history&rating_key=${item['rating_key']}" title="${item['title']}">
|
||||
<a href="${page('info', item['rating_key'], history=True, live=item['live'])}" title="${item['title']}">
|
||||
% elif item['media_type'] == 'episode':
|
||||
<a href="info?source=history&rating_key=${item['rating_key']}" title="${item['grandparent_title']}">
|
||||
<a href="${page('info', item['rating_key'], history=True, live=item['live'])}" title="${item['grandparent_title']}">
|
||||
% endif
|
||||
<div class="dashboard-recent-media-poster">
|
||||
<div class="dashboard-recent-media-poster-face" style="background-image: url(pms_image_proxy?img=${item['thumb']}&width=300&height=450&fallback=poster);">
|
||||
<div class="dashboard-recent-media-poster-face" style="background-image: url(${page('pms_image_proxy', item['thumb'], item['rating_key'], 300, 450, fallback='poster')});">
|
||||
<div class="dashboard-recent-media-overlay">
|
||||
<div class="dashboard-recent-media-overlay-text" id="time-${item['time']}">
|
||||
<script>
|
||||
@@ -56,19 +59,38 @@ DOCUMENTATION :: END
|
||||
</a>
|
||||
<div class="dashboard-recent-media-metacontainer">
|
||||
% if item['media_type'] == 'episode':
|
||||
% if item['live']:
|
||||
<h3>
|
||||
<a href="info?rating_key=${item['grandparent_rating_key']}" title="${item['grandparent_title']}">${item['grandparent_title']}</a>
|
||||
<a href="${page('info', item['rating_key'], history=True, live=item['live'])}" title="${item['grandparent_title']}">${item['grandparent_title']}</a>
|
||||
</h3>
|
||||
<h3 class="text-muted" title="${item['title']}">
|
||||
<a href="info?source=history&rating_key=${item['rating_key']}" title="${item['title']}">${item['title']}</a>
|
||||
<a href="${page('info', item['rating_key'], history=True, live=item['live'])}" title="${item['title']}">${item['title']}</a>
|
||||
</h3>
|
||||
% if item['media_index']:
|
||||
<h3 class="text-muted">
|
||||
<a href="${page('info', item['rating_key'], history=True, live=item['live'])}" title="Season ${item['parent_media_index']}">S${item['parent_media_index']}</a>
|
||||
· <a href="${page('info', item['rating_key'], history=True, live=item['live'])}" title="Episode ${item['media_index']}">E${item['media_index']}</a>
|
||||
</h3>
|
||||
% else:
|
||||
<h3 class="text-muted">
|
||||
<a href="${page('info', item['rating_key'], history=True, live=item['live'])}" title="${item['originally_available_at']}">${item['originally_available_at']}</a>
|
||||
</h3>
|
||||
% endif
|
||||
% else:
|
||||
<h3>
|
||||
<a href="${page('info', item['grandparent_rating_key'])}" title="${item['grandparent_title']}">${item['grandparent_title']}</a>
|
||||
</h3>
|
||||
<h3 class="text-muted" title="${item['title']}">
|
||||
<a href="${page('info', item['rating_key'], history=True, live=item['live'])}" title="${item['title']}">${item['title']}</a>
|
||||
</h3>
|
||||
<h3 class="text-muted">
|
||||
<a href="info?rating_key=${item['parent_rating_key']}" title="Season ${item['parent_media_index']}">S${item['parent_media_index']}</a>
|
||||
· <a href="info?source=history&rating_key=${item['rating_key']}" title="Episode ${item['media_index']}">E${item['media_index']}</a>
|
||||
<a href="${page('info', item['parent_rating_key'])}" title="Season ${item['parent_media_index']}">S${item['parent_media_index']}</a>
|
||||
· <a href="${page('info', item['rating_key'], history=True, live=item['live'])}" title="Episode ${item['media_index']}">E${item['media_index']}</a>
|
||||
</h3>
|
||||
% endif
|
||||
% elif item['media_type'] == 'movie':
|
||||
<h3 title="${item['title']}">
|
||||
<a href="info?source=history&rating_key=${item['rating_key']}" title="${item['title']}">${item['title']}</a>
|
||||
<a href="${page('info', item['rating_key'], history=True, live=item['live'])}" title="${item['title']}">${item['title']}</a>
|
||||
</h3>
|
||||
<h3 class="text-muted">${item['year']}</h3>
|
||||
<h3 class="text-muted"> </h3>
|
||||
@@ -94,9 +116,9 @@ DOCUMENTATION :: END
|
||||
% endif
|
||||
% elif item['media_type'] == 'track':
|
||||
% if item['rating_key']:
|
||||
<a href="info?source=history&rating_key=${item['rating_key']}" title="${item['parent_title']}">
|
||||
<a href="${page('info', item['rating_key'], history=True, live=item['live'])}" title="${item['parent_title']}">
|
||||
<div class="dashboard-recent-media-cover">
|
||||
<div class="dashboard-recent-media-cover-face" style="background-image: url(pms_image_proxy?img=${item['thumb']}&width=300&height=300&fallback=cover);">
|
||||
<div class="dashboard-recent-media-cover-face" style="background-image: url(${page('pms_image_proxy', item['thumb'], item['rating_key'], 300, 300, fallback='cover')});">
|
||||
<div class="dashboard-recent-media-overlay">
|
||||
<div class="dashboard-recent-media-overlay-text" id="time-${item['time']}">
|
||||
<script>
|
||||
@@ -109,13 +131,13 @@ DOCUMENTATION :: END
|
||||
</a>
|
||||
<div class="dashboard-recent-media-metacontainer">
|
||||
<h3 title="${item['original_title'] or item['grandparent_title']}">
|
||||
<a href="info?rating_key=${item['grandparent_rating_key']}" title="${item['original_title'] or item['grandparent_title']}">${item['original_title'] or item['grandparent_title']}</a>
|
||||
<a href="${page('info', item['grandparent_rating_key'])}" title="${item['original_title'] or item['grandparent_title']}">${item['original_title'] or item['grandparent_title']}</a>
|
||||
</h3>
|
||||
<h3 class="text-muted" title="${item['title']}">
|
||||
<a href="info?source=history&rating_key=${item['rating_key']}" title="${item['title']}">${item['title']}</a>
|
||||
<a href="${page('info', item['rating_key'], history=True, live=item['live'])}" title="${item['title']}">${item['title']}</a>
|
||||
</h3>
|
||||
<h3 class="text-muted">
|
||||
<a href="info?rating_key=${item['parent_rating_key']}" title="${item['parent_title']}">${item['parent_title']}</a>
|
||||
<a href="${page('info', item['parent_rating_key'])}" title="${item['parent_title']}">${item['parent_title']}</a>
|
||||
</h3>
|
||||
</div>
|
||||
% else:
|
||||
|
@@ -119,14 +119,14 @@
|
||||
});
|
||||
|
||||
if (users_to_delete.length > 0) {
|
||||
$('#users-to-delete').prepend('<p>Are you REALLY sure you want to delete and purge all history for the following users:</p>')
|
||||
$('#users-to-delete').prepend('<p>Are you REALLY sure you want to delete and purge all history for the following users:</p>');
|
||||
for (var i = 0; i < users_to_delete.length; i++) {
|
||||
$('#users-to-delete').append('<li>' + $('div[data-id=' + users_to_delete[i] + '] > input').val() + '</li>');
|
||||
}
|
||||
}
|
||||
|
||||
if (users_to_purge.length > 0) {
|
||||
$('#users-to-purge').prepend('<p>Are you REALLY sure you want to purge all history for the following users:</p>')
|
||||
$('#users-to-purge').prepend('<p>Are you REALLY sure you want to purge all history for the following users:</p>');
|
||||
for (var i = 0; i < users_to_purge.length; i++) {
|
||||
$('#users-to-purge').append('<li>' + $('div[data-id=' + users_to_purge[i] + '] > input').val() + '</li>');
|
||||
}
|
||||
@@ -134,33 +134,30 @@
|
||||
|
||||
$('#confirm-modal-delete').modal();
|
||||
$('#confirm-modal-delete').one('click', '#confirm-delete', function () {
|
||||
users_to_delete.forEach(function(row, idx) {
|
||||
$.ajax({
|
||||
url: 'delete_user',
|
||||
type: 'POST',
|
||||
data: { user_id: row },
|
||||
cache: false,
|
||||
async: true,
|
||||
success: function (data) {
|
||||
var msg = "User deleted";
|
||||
showMsg(msg, false, true, 2000);
|
||||
}
|
||||
});
|
||||
$.ajax({
|
||||
url: 'delete_all_user_history',
|
||||
type: 'POST',
|
||||
data: { row_ids: users_to_purge.join(',') },
|
||||
cache: false,
|
||||
async: true,
|
||||
success: function (data) {
|
||||
var msg = "User history purged";
|
||||
showMsg(msg, false, true, 2000);
|
||||
users_list_table.draw();
|
||||
}
|
||||
});
|
||||
users_to_purge.forEach(function(row, idx) {
|
||||
$.ajax({
|
||||
url: 'delete_all_user_history',
|
||||
type: 'POST',
|
||||
data: { user_id: row },
|
||||
cache: false,
|
||||
async: true,
|
||||
success: function (data) {
|
||||
var msg = "User history purged";
|
||||
showMsg(msg, false, true, 2000);
|
||||
}
|
||||
});
|
||||
$.ajax({
|
||||
url: 'delete_user',
|
||||
type: 'POST',
|
||||
data: { row_ids: users_to_delete.join(',') },
|
||||
cache: false,
|
||||
async: true,
|
||||
success: function (data) {
|
||||
var msg = "User deleted";
|
||||
showMsg(msg, false, true, 2000);
|
||||
users_list_table.draw();
|
||||
}
|
||||
});
|
||||
users_list_table.draw();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -192,7 +189,7 @@
|
||||
complete: function (xhr, status) {
|
||||
var result = $.parseJSON(xhr.responseText);
|
||||
var msg = result.message;
|
||||
if (result.result == 'success') {
|
||||
if (result.result === 'success') {
|
||||
showMsg('<i class="fa fa-check"></i> ' + msg, false, true, 2000, false);
|
||||
users_list_table.draw();
|
||||
} else {
|
||||
|
@@ -21,28 +21,21 @@
|
||||
<link href="${http_root}css/font-awesome.v4-shims.min.css" rel="stylesheet">
|
||||
|
||||
<!-- Favicons -->
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="${http_root}images/favicon/favicon-32x32.png?v=2.0.0">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="${http_root}images/favicon/favicon-16x16.png?v=2.0.0">
|
||||
<link rel="shortcut icon" href="${http_root}images/favicon/favicon.ico?v=2.0.0">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="${http_root}images/favicon/favicon-32x32.png?v=2.0.5">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="${http_root}images/favicon/favicon-16x16.png?v=2.0.5">
|
||||
<link rel="shortcut icon" href="${http_root}images/favicon/favicon.ico?v=2.0.5">
|
||||
|
||||
<!-- ICONS -->
|
||||
<!-- Android >M39 icon -->
|
||||
<link rel="icon" type="image/png" sizes="192x192" href="${http_root}images/favicon/android-chrome-192x192.png?v=2.0.0">
|
||||
<link rel="manifest" href="${http_root}json/Android-manifest.json?v=2.0.0">
|
||||
<meta name="theme-color" content="#1f1f1f">
|
||||
<!-- Android -->
|
||||
<link rel="manifest" href="${http_root}images/favicon/manifest.json?v=2.0.5" crossorigin="use-credentials">
|
||||
<meta name="theme-color" content="#282a2d">
|
||||
<!-- Apple -->
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="${http_root}images/favicon/apple-touch-icon.png?v=2.0.0">
|
||||
<link rel="mask-icon" href="${http_root}images/favicon/safari-pinned-tab.svg?v=2.0.0" color="#1f1f1f">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="${http_root}images/favicon/apple-touch-icon.png?v=2.0.5">
|
||||
<link rel="mask-icon" href="${http_root}images/favicon/safari-pinned-tab.svg?v=2.0.5" color="#282a2d">
|
||||
<meta name="apple-mobile-web-app-title" content="Tautulli">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
<meta name="viewport" content="initial-scale=1">
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
<!-- IE10 icon -->
|
||||
<!-- Microsoft -->
|
||||
<meta name="application-name" content="Tautulli">
|
||||
<meta name="msapplication-TileColor" content="#1f1f1f">
|
||||
<meta name="msapplication-TileImage" content="${http_root}images/favicon/mstile-144x144.png?v=2.0.0">
|
||||
<meta name="msapplication-config" content="${http_root}xml/IEconfig.xml?v=2.0.0" />
|
||||
<meta name="msapplication-config" content="${http_root}images/favicon/browserconfig.xml?v=2.0.5">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -67,8 +60,38 @@
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wizard-card" data-cardname="card2">
|
||||
<h3>Plex Authentication</h3>
|
||||
<h3>Authentication</h3>
|
||||
<div class="wizard-input-section">
|
||||
<p class="help-block">
|
||||
Please setup an admin username and password for Tautulli.
|
||||
</p>
|
||||
</div>
|
||||
<div class="wizard-input-section">
|
||||
<label for="http_username">HTTP Username</label>
|
||||
<div class="row">
|
||||
<div class="col-xs-8">
|
||||
<input type="text" class="form-control auth-settings" id="http_username" name="http_username" value="" size="30">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wizard-input-section">
|
||||
<label for="http_password">HTTP Password</label>
|
||||
<div class="row">
|
||||
<div class="col-xs-8">
|
||||
<input type="password" class="form-control auth-settings" id="http_password" name="http_password" value="" size="30" autocomplete="new-password">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" class="form-control" name="http_hash_password" id="http_hash_password" value="1">
|
||||
<input type="hidden" class="form-control" name="http_plex_admin" id="http_plex_admin" value="1">
|
||||
<input type="hidden" id="authentication_valid" data-validate="validateAuthentication" value="">
|
||||
<span style="display: none;" id="authentication-status"></span>
|
||||
</div>
|
||||
|
||||
<div class="wizard-card" data-cardname="card3">
|
||||
<h3>Plex Account</h3>
|
||||
<div class="wizard-input-section">
|
||||
<p class="help-block">
|
||||
Tautulli requires a Plex.tv account. Click the button below to sign in on Plex.tv. You may need to allow popups in your browser.
|
||||
@@ -78,7 +101,8 @@
|
||||
<a class="btn btn-dark" id="sign-in-plex" href="#" role="button">Sign In with Plex</a>
|
||||
<span style="margin-left: 10px; display: none;" id="pms-token-status"></span>
|
||||
</div>
|
||||
<div class="wizard-card" data-cardname="card3">
|
||||
|
||||
<div class="wizard-card" data-cardname="card4">
|
||||
<h3>Plex Media Server</h3>
|
||||
<div class="wizard-input-section">
|
||||
<p class="help-block">
|
||||
@@ -137,7 +161,7 @@
|
||||
<span style="margin-left: 10px; display: none;" id="pms-verify-status"></span>
|
||||
</div>
|
||||
|
||||
<div class="wizard-card" data-cardname="card4">
|
||||
<div class="wizard-card" data-cardname="card5">
|
||||
<h3>Activity Logging</h3>
|
||||
<div class="wizard-input-section">
|
||||
<p class="help-block">
|
||||
@@ -162,7 +186,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wizard-card" data-cardname="card4">
|
||||
<div class="wizard-card" data-cardname="card6">
|
||||
<h3>Notifications</h3>
|
||||
<div class="wizard-input-section">
|
||||
<p class="help-block">
|
||||
@@ -175,7 +199,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wizard-card" data-cardname="card5">
|
||||
<div class="wizard-card" data-cardname="card7">
|
||||
<h3>Database Import</h3>
|
||||
<div class="wizard-input-section">
|
||||
<p class="help-block">
|
||||
@@ -192,6 +216,7 @@
|
||||
<input type="checkbox" name="first_run" id="first_run" value="1" checked>
|
||||
<input type="checkbox" name="group_history_tables" id="group_history_tables" value="1" checked>
|
||||
<input type="checkbox" name="history_table_activity" id="history_table_activity" value="1" checked>
|
||||
<input type="checkbox" name="win_sys_tray" id="win_sys_tray" value="1" checked>
|
||||
<input type="checkbox" name="launch_browser" id="launch_browser" value="1" checked>
|
||||
<input type="checkbox" name="api_enabled" id="api_enabled" value="1" checked>
|
||||
<input type="checkbox" name="refresh_users_on_startup" id="refresh_users_on_startup" value="1" checked>
|
||||
@@ -199,6 +224,8 @@
|
||||
<input type="checkbox" name="check_github" id="check_github" value="1" checked>
|
||||
<input type="checkbox" name="log_blacklist" id="log_blacklist" value="1" checked>
|
||||
<input type="checkbox" name="cache_images" id="cache_images" value="1" checked>
|
||||
<input type="checkbox" name="notify_group_recently_added_grandparent" id="notify_group_recently_added_grandparent" value="1" checked>
|
||||
<input type="checkbox" name="notify_group_recently_added_parent" id="notify_group_recently_added_parent" value="1" checked>
|
||||
<input type="checkbox" name="server_changed" id="server_changed" value="1" checked>
|
||||
<input type="checkbox" name="first_run_complete" id="first_run_complete" value="1" checked>
|
||||
<input type="text" name="home_stats_cards" id="home_stats_cards" value="first_run_wizard">
|
||||
@@ -227,11 +254,29 @@
|
||||
<script src="${http_root}js/script.js${cache_param}"></script>
|
||||
<script src="${http_root}js/bootstrap-wizard.min.js"></script>
|
||||
<script>
|
||||
function validateAuthentication(el) {
|
||||
var http_username = $("#http_username").val();
|
||||
var http_password = $("#http_password").val();
|
||||
var valid_authentication = el.val();
|
||||
var retValue = {};
|
||||
|
||||
if (http_username === "" || http_password === "") {
|
||||
retValue.status = false;
|
||||
retValue.msg = "Please enter a username and password.";
|
||||
$("#authentication-status").html('<i class="fa fa-exclamation-circle"></i> Please enter a username and password.');
|
||||
$('#authentication-status').fadeIn('fast').delay(2000).fadeOut('fast');
|
||||
} else {
|
||||
retValue.status = true;
|
||||
}
|
||||
|
||||
return retValue;
|
||||
}
|
||||
|
||||
function validatePMSip(el) {
|
||||
var valid_pms_ip = el.val();
|
||||
var retValue = {};
|
||||
|
||||
if (valid_pms_ip == "") {
|
||||
if (valid_pms_ip === "") {
|
||||
retValue.status = false;
|
||||
retValue.msg = "Please verify your server.";
|
||||
$("#pms-verify-status").html('<i class="fa fa-exclamation-circle"></i> Please verify your server.');
|
||||
@@ -247,7 +292,7 @@
|
||||
var valid_pms_token = el.val();
|
||||
var retValue = {};
|
||||
|
||||
if (valid_pms_token == "") {
|
||||
if (valid_pms_token === "") {
|
||||
retValue.status = false;
|
||||
retValue.msg = "Please authenticate.";
|
||||
$("#pms-token-status").html('<i class="fa fa-exclamation-circle"></i> Please authenticate.');
|
||||
@@ -284,7 +329,7 @@ $(document).ready(function() {
|
||||
$.fn.wizard.logging = false;
|
||||
var options = {
|
||||
keyboard : false,
|
||||
contentHeight : 400,
|
||||
contentHeight : 450,
|
||||
contentWidth : 700,
|
||||
backdrop: 'static',
|
||||
buttons: {submitText: 'Finish'},
|
||||
|
@@ -26,6 +26,7 @@
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title>Tautulli Newsletter - ${subject}</title>
|
||||
<link rel="shortcut icon" href="${base_url_image + 'images/favicon/favicon.ico' if base_url_image else 'https://tautulli.com/images/favicon.ico'}">
|
||||
<style>
|
||||
/* -------------------------------------
|
||||
GLOBAL RESETS
|
||||
@@ -694,7 +695,7 @@
|
||||
<div class="sub-header-count" style="margin-left: auto;margin-right: auto;font-size: 30px;text-align: center;">
|
||||
<span class="count" style="color: #E5A00D;">${len(recently_added['show'])}</span> <span class="count-units" style="color: #aaaaaa;font-size: 20px;text-transform: uppercase;">show${'s' if len(recently_added['show']) > 1 else ''}</span> /
|
||||
<% total_episodes = sum(season['episode_count'] for show in recently_added['show'] for season in show['season']) %>
|
||||
<span class="count" style="color: #E5A00D;">${total_episodes}</span> <span class="count-units" style="color: #aaaaaa;font-size: 20px;text-transform: uppercase;">episode${'s' if total > 1 else ''}</span>
|
||||
<span class="count" style="color: #E5A00D;">${total_episodes}</span> <span class="count-units" style="color: #aaaaaa;font-size: 20px;text-transform: uppercase;">episode${'s' if total_episodes > 1 else ''}</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -847,7 +848,7 @@
|
||||
<div class="sub-header-count" style="margin-left: auto;margin-right: auto;font-size: 30px;text-align: center;">
|
||||
<span class="count" style="color: #E5A00D;">${len(recently_added['artist'])}</span> <span class="count-units" style="color: #aaaaaa;font-size: 20px;text-transform: uppercase;">artist${'s' if len(recently_added['artist']) > 1 else ''}</span> /
|
||||
<% total_albums = sum(artist['album_count'] for artist in recently_added['artist']) %>
|
||||
<span class="count" style="color: #E5A00D;">${total_albums}</span> <span class="count-units" style="color: #aaaaaa;font-size: 20px;text-transform: uppercase;">album${'s' if total > 1 else ''}</span>
|
||||
<span class="count" style="color: #E5A00D;">${total_albums}</span> <span class="count-units" style="color: #aaaaaa;font-size: 20px;text-transform: uppercase;">album${'s' if total_albums > 1 else ''}</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
@@ -26,6 +26,7 @@
|
||||
<meta name="viewport" content="width=device-width"/>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<title>Tautulli Newsletter - ${subject}</title>
|
||||
<link rel="shortcut icon" href="${base_url_image + 'images/favicon/favicon.ico' if base_url_image else 'https://tautulli.com/images/favicon.ico'}">
|
||||
<style>
|
||||
/* -------------------------------------
|
||||
GLOBAL RESETS
|
||||
@@ -694,7 +695,7 @@
|
||||
<div class="sub-header-count">
|
||||
<span class="count">${len(recently_added['show'])}</span> <span class="count-units">show${'s' if len(recently_added['show']) > 1 else ''}</span> /
|
||||
<% total_episodes = sum(season['episode_count'] for show in recently_added['show'] for season in show['season']) %>
|
||||
<span class="count">${total_episodes}</span> <span class="count-units">episode${'s' if total > 1 else ''}</span>
|
||||
<span class="count">${total_episodes}</span> <span class="count-units">episode${'s' if total_episodes > 1 else ''}</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -847,7 +848,7 @@
|
||||
<div class="sub-header-count">
|
||||
<span class="count">${len(recently_added['artist'])}</span> <span class="count-units">artist${'s' if len(recently_added['artist']) > 1 else ''}</span> /
|
||||
<% total_albums = sum(artist['album_count'] for artist in recently_added['artist']) %>
|
||||
<span class="count">${total_albums}</span> <span class="count-units">album${'s' if total > 1 else ''}</span>
|
||||
<span class="count">${total_albums}</span> <span class="count-units">album${'s' if total_albums > 1 else ''}</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
@@ -1,3 +1,3 @@
|
||||
from .core import where
|
||||
|
||||
__version__ = "2019.03.09"
|
||||
__version__ = "2019.11.28"
|
||||
|
@@ -771,36 +771,6 @@ vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
|
||||
+OkuE6N36B9K
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=Class 2 Primary CA O=Certplus
|
||||
# Subject: CN=Class 2 Primary CA O=Certplus
|
||||
# Label: "Certplus Class 2 Primary CA"
|
||||
# Serial: 177770208045934040241468760488327595043
|
||||
# MD5 Fingerprint: 88:2c:8c:52:b8:a2:3c:f3:f7:bb:03:ea:ae:ac:42:0b
|
||||
# SHA1 Fingerprint: 74:20:74:41:72:9c:dd:92:ec:79:31:d8:23:10:8d:c2:81:92:e2:bb
|
||||
# SHA256 Fingerprint: 0f:99:3c:8a:ef:97:ba:af:56:87:14:0e:d5:9a:d1:82:1b:b4:af:ac:f0:aa:9a:58:b5:d5:7a:33:8a:3a:fb:cb
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAw
|
||||
PTELMAkGA1UEBhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFz
|
||||
cyAyIFByaW1hcnkgQ0EwHhcNOTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9
|
||||
MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2VydHBsdXMxGzAZBgNVBAMTEkNsYXNz
|
||||
IDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANxQ
|
||||
ltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR5aiR
|
||||
VhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyL
|
||||
kcAbmXuZVg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCd
|
||||
EgETjdyAYveVqUSISnFOYFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yas
|
||||
H7WLO7dDWWuwJKZtkIvEcupdM5i3y95ee++U8Rs+yskhwcWYAqqi9lt3m/V+llU0
|
||||
HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRMECDAGAQH/AgEKMAsGA1Ud
|
||||
DwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJYIZIAYb4
|
||||
QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMu
|
||||
Y29tL0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/
|
||||
AN9WM2K191EBkOvDP9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8
|
||||
yfFC82x/xXp8HVGIutIKPidd3i1RTtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMR
|
||||
FcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+7UCmnYR0ObncHoUW2ikbhiMA
|
||||
ybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW//1IMwrh3KWB
|
||||
kJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7
|
||||
l7+ijrRU
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=DST Root CA X3 O=Digital Signature Trust Co.
|
||||
# Subject: CN=DST Root CA X3 O=Digital Signature Trust Co.
|
||||
# Label: "DST Root CA X3"
|
||||
@@ -1219,36 +1189,6 @@ t0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw
|
||||
WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=Deutsche Telekom Root CA 2 O=Deutsche Telekom AG OU=T-TeleSec Trust Center
|
||||
# Subject: CN=Deutsche Telekom Root CA 2 O=Deutsche Telekom AG OU=T-TeleSec Trust Center
|
||||
# Label: "Deutsche Telekom Root CA 2"
|
||||
# Serial: 38
|
||||
# MD5 Fingerprint: 74:01:4a:91:b1:08:c4:58:ce:47:cd:f0:dd:11:53:08
|
||||
# SHA1 Fingerprint: 85:a4:08:c0:9c:19:3e:5d:51:58:7d:cd:d6:13:30:fd:8c:de:37:bf
|
||||
# SHA256 Fingerprint: b6:19:1a:50:d0:c3:97:7f:7d:a9:9b:cd:aa:c8:6a:22:7d:ae:b9:67:9e:c7:0b:a3:b0:c9:d9:22:71:c1:70:d3
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEc
|
||||
MBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2Vj
|
||||
IFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENB
|
||||
IDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5MjM1OTAwWjBxMQswCQYDVQQGEwJE
|
||||
RTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxl
|
||||
U2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290
|
||||
IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEU
|
||||
ha88EOQ5bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhC
|
||||
QN/Po7qCWWqSG6wcmtoIKyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1Mjwr
|
||||
rFDa1sPeg5TKqAyZMg4ISFZbavva4VhYAUlfckE8FQYBjl2tqriTtM2e66foai1S
|
||||
NNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aKSe5TBY8ZTNXeWHmb0moc
|
||||
QqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTVjlsB9WoH
|
||||
txa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAP
|
||||
BgNVHRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOC
|
||||
AQEAlGRZrTlk5ynrE/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756Abrsp
|
||||
tJh6sTtU6zkXR34ajgv8HzFZMQSyzhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpa
|
||||
IzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8rZ7/gFnkm0W09juwzTkZmDLl
|
||||
6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4Gdyd1Lx+4ivn+
|
||||
xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU
|
||||
Cm26OWMohpLzGITY+9HPBVZkVw==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=Cybertrust Global Root O=Cybertrust, Inc
|
||||
# Subject: CN=Cybertrust Global Root O=Cybertrust, Inc
|
||||
# Label: "Cybertrust Global Root"
|
||||
@@ -3453,46 +3393,6 @@ AAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3CekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ
|
||||
5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=Certinomis - Root CA O=Certinomis OU=0002 433998903
|
||||
# Subject: CN=Certinomis - Root CA O=Certinomis OU=0002 433998903
|
||||
# Label: "Certinomis - Root CA"
|
||||
# Serial: 1
|
||||
# MD5 Fingerprint: 14:0a:fd:8d:a8:28:b5:38:69:db:56:7e:61:22:03:3f
|
||||
# SHA1 Fingerprint: 9d:70:bb:01:a5:a4:a0:18:11:2e:f7:1c:01:b9:32:c5:34:e7:88:a8
|
||||
# SHA256 Fingerprint: 2a:99:f5:bc:11:74:b7:3c:bb:1d:62:08:84:e0:1c:34:e5:1c:cb:39:78:da:12:5f:0e:33:26:88:83:bf:41:58
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFkjCCA3qgAwIBAgIBATANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJGUjET
|
||||
MBEGA1UEChMKQ2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxHTAb
|
||||
BgNVBAMTFENlcnRpbm9taXMgLSBSb290IENBMB4XDTEzMTAyMTA5MTcxOFoXDTMz
|
||||
MTAyMTA5MTcxOFowWjELMAkGA1UEBhMCRlIxEzARBgNVBAoTCkNlcnRpbm9taXMx
|
||||
FzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMR0wGwYDVQQDExRDZXJ0aW5vbWlzIC0g
|
||||
Um9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANTMCQosP5L2
|
||||
fxSeC5yaah1AMGT9qt8OHgZbn1CF6s2Nq0Nn3rD6foCWnoR4kkjW4znuzuRZWJfl
|
||||
LieY6pOod5tK8O90gC3rMB+12ceAnGInkYjwSond3IjmFPnVAy//ldu9n+ws+hQV
|
||||
WZUKxkd8aRi5pwP5ynapz8dvtF4F/u7BUrJ1Mofs7SlmO/NKFoL21prbcpjp3vDF
|
||||
TKWrteoB4owuZH9kb/2jJZOLyKIOSY008B/sWEUuNKqEUL3nskoTuLAPrjhdsKkb
|
||||
5nPJWqHZZkCqqU2mNAKthH6yI8H7KsZn9DS2sJVqM09xRLWtwHkziOC/7aOgFLSc
|
||||
CbAK42C++PhmiM1b8XcF4LVzbsF9Ri6OSyemzTUK/eVNfaoqoynHWmgE6OXWk6Ri
|
||||
wsXm9E/G+Z8ajYJJGYrKWUM66A0ywfRMEwNvbqY/kXPLynNvEiCL7sCCeN5LLsJJ
|
||||
wx3tFvYk9CcbXFcx3FXuqB5vbKziRcxXV4p1VxngtViZSTYxPDMBbRZKzbgqg4SG
|
||||
m/lg0h9tkQPTYKbVPZrdd5A9NaSfD171UkRpucC63M9933zZxKyGIjK8e2uR73r4
|
||||
F2iw4lNVYC2vPsKD2NkJK/DAZNuHi5HMkesE/Xa0lZrmFAYb1TQdvtj/dBxThZng
|
||||
WVJKYe2InmtJiUZ+IFrZ50rlau7SZRFDAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIB
|
||||
BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTvkUz1pcMw6C8I6tNxIqSSaHh0
|
||||
2TAfBgNVHSMEGDAWgBTvkUz1pcMw6C8I6tNxIqSSaHh02TANBgkqhkiG9w0BAQsF
|
||||
AAOCAgEAfj1U2iJdGlg+O1QnurrMyOMaauo++RLrVl89UM7g6kgmJs95Vn6RHJk/
|
||||
0KGRHCwPT5iVWVO90CLYiF2cN/z7ZMF4jIuaYAnq1fohX9B0ZedQxb8uuQsLrbWw
|
||||
F6YSjNRieOpWauwK0kDDPAUwPk2Ut59KA9N9J0u2/kTO+hkzGm2kQtHdzMjI1xZS
|
||||
g081lLMSVX3l4kLr5JyTCcBMWwerx20RoFAXlCOotQqSD7J6wWAsOMwaplv/8gzj
|
||||
qh8c3LigkyfeY+N/IZ865Z764BNqdeuWXGKRlI5nU7aJ+BIJy29SWwNyhlCVCNSN
|
||||
h4YVH5Uk2KRvms6knZtt0rJ2BobGVgjF6wnaNsIbW0G+YSrjcOa4pvi2WsS9Iff/
|
||||
ql+hbHY5ZtbqTFXhADObE5hjyW/QASAJN1LnDE8+zbz1X5YnpyACleAu6AdBBR8V
|
||||
btaw5BngDwKTACdyxYvRVB9dSsNAl35VpnzBMwQUAR1JIGkLGZOdblgi90AMRgwj
|
||||
Y/M50n92Uaf0yKHxDHYiI0ZSKS3io0EHVmmY0gUJvGnHWmHNj4FgFU2A3ZDifcRQ
|
||||
8ow7bkrHxuaAKzyBvBGAFhAn1/DNP3nMcyrDflOR1m749fPH0FFNjkulW+YZFzvW
|
||||
gQncItzujrnEj1PhZ7szuIgVRs/taTX/dQ1G885x4cVrhkIGuUE=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=OISTE WISeKey Global Root GB CA O=WISeKey OU=OISTE Foundation Endorsed
|
||||
# Subject: CN=OISTE WISeKey Global Root GB CA O=WISeKey OU=OISTE Foundation Endorsed
|
||||
# Label: "OISTE WISeKey Global Root GB CA"
|
||||
@@ -4656,3 +4556,47 @@ L5/ndtFhKvshuzHQqp9HpLIiyhY6UFfEW0NnxWViA0kB60PZ2Pierc+xYw5F9KBa
|
||||
LJstxabArahH9CdMOA0uG0k7UvToiIMrVCjU8jVStDKDYmlkDJGcn5fqdBb9HxEG
|
||||
mpv0
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=Entrust Root Certification Authority - G4 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2015 Entrust, Inc. - for authorized use only
|
||||
# Subject: CN=Entrust Root Certification Authority - G4 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2015 Entrust, Inc. - for authorized use only
|
||||
# Label: "Entrust Root Certification Authority - G4"
|
||||
# Serial: 289383649854506086828220374796556676440
|
||||
# MD5 Fingerprint: 89:53:f1:83:23:b7:7c:8e:05:f1:8c:71:38:4e:1f:88
|
||||
# SHA1 Fingerprint: 14:88:4e:86:26:37:b0:26:af:59:62:5c:40:77:ec:35:29:ba:96:01
|
||||
# SHA256 Fingerprint: db:35:17:d1:f6:73:2a:2d:5a:b9:7c:53:3e:c7:07:79:ee:32:70:a6:2f:b4:ac:42:38:37:24:60:e6:f0:1e:88
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIGSzCCBDOgAwIBAgIRANm1Q3+vqTkPAAAAAFVlrVgwDQYJKoZIhvcNAQELBQAw
|
||||
gb4xCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQL
|
||||
Ex9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykg
|
||||
MjAxNSBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMjAw
|
||||
BgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEc0
|
||||
MB4XDTE1MDUyNzExMTExNloXDTM3MTIyNzExNDExNlowgb4xCzAJBgNVBAYTAlVT
|
||||
MRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1
|
||||
c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxNSBFbnRydXN0LCBJ
|
||||
bmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMjAwBgNVBAMTKUVudHJ1c3Qg
|
||||
Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEc0MIICIjANBgkqhkiG9w0B
|
||||
AQEFAAOCAg8AMIICCgKCAgEAsewsQu7i0TD/pZJH4i3DumSXbcr3DbVZwbPLqGgZ
|
||||
2K+EbTBwXX7zLtJTmeH+H17ZSK9dE43b/2MzTdMAArzE+NEGCJR5WIoV3imz/f3E
|
||||
T+iq4qA7ec2/a0My3dl0ELn39GjUu9CH1apLiipvKgS1sqbHoHrmSKvS0VnM1n4j
|
||||
5pds8ELl3FFLFUHtSUrJ3hCX1nbB76W1NhSXNdh4IjVS70O92yfbYVaCNNzLiGAM
|
||||
C1rlLAHGVK/XqsEQe9IFWrhAnoanw5CGAlZSCXqc0ieCU0plUmr1POeo8pyvi73T
|
||||
DtTUXm6Hnmo9RR3RXRv06QqsYJn7ibT/mCzPfB3pAqoEmh643IhuJbNsZvc8kPNX
|
||||
wbMv9W3y+8qh+CmdRouzavbmZwe+LGcKKh9asj5XxNMhIWNlUpEbsZmOeX7m640A
|
||||
2Vqq6nPopIICR5b+W45UYaPrL0swsIsjdXJ8ITzI9vF01Bx7owVV7rtNOzK+mndm
|
||||
nqxpkCIHH2E6lr7lmk/MBTwoWdPBDFSoWWG9yHJM6Nyfh3+9nEg2XpWjDrk4JFX8
|
||||
dWbrAuMINClKxuMrLzOg2qOGpRKX/YAr2hRC45K9PvJdXmd0LhyIRyk0X+IyqJwl
|
||||
N4y6mACXi0mWHv0liqzc2thddG5msP9E36EYxr5ILzeUePiVSj9/E15dWf10hkNj
|
||||
c0kCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD
|
||||
VR0OBBYEFJ84xFYjwznooHFs6FRM5Og6sb9nMA0GCSqGSIb3DQEBCwUAA4ICAQAS
|
||||
5UKme4sPDORGpbZgQIeMJX6tuGguW8ZAdjwD+MlZ9POrYs4QjbRaZIxowLByQzTS
|
||||
Gwv2LFPSypBLhmb8qoMi9IsabyZIrHZ3CL/FmFz0Jomee8O5ZDIBf9PD3Vht7LGr
|
||||
hFV0d4QEJ1JrhkzO3bll/9bGXp+aEJlLdWr+aumXIOTkdnrG0CSqkM0gkLpHZPt/
|
||||
B7NTeLUKYvJzQ85BK4FqLoUWlFPUa19yIqtRLULVAJyZv967lDtX/Zr1hstWO1uI
|
||||
AeV8KEsD+UmDfLJ/fOPtjqF/YFOOVZ1QNBIPt5d7bIdKROf1beyAN/BYGW5KaHbw
|
||||
H5Lk6rWS02FREAutp9lfx1/cH6NcjKF+m7ee01ZvZl4HliDtC3T7Zk6LERXpgUl+
|
||||
b7DUUH8i119lAg2m9IUe2K4GS0qn0jFmwvjO5QimpAKWRGhXxNUzzxkvFMSUHHuk
|
||||
2fCfDrGA4tGeEWSpiBE6doLlYsKA2KSD7ZPvfC+QsDJMlhVoSFLUmQjAJOgc47Ol
|
||||
IQ6SwJAfzyBfyjs4x7dtOvPmRLgOMWuIjnDrnBdSqEGULoe256YSxXXfW8AKbnuk
|
||||
5F6G+TaU33fD6Q3AOfF5u0aOq0NZJ7cguyPpVkAh7DE9ZapD8j3fcEThuk0mEDuY
|
||||
n/PIjhs4ViFqUZPTkcpG2om3PVODLAgfi49T3f+sHw==
|
||||
-----END CERTIFICATE-----
|
||||
|
2
lib/musicbrainzngs/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
from musicbrainzngs.musicbrainz import *
|
||||
from musicbrainzngs.caa import *
|
187
lib/musicbrainzngs/caa.py
Normal file
@@ -0,0 +1,187 @@
|
||||
# This file is part of the musicbrainzngs library
|
||||
# Copyright (C) Alastair Porter, Wieland Hoffmann, and others
|
||||
# This file is distributed under a BSD-2-Clause type license.
|
||||
# See the COPYING file for more information.
|
||||
|
||||
__all__ = [
|
||||
'set_caa_hostname', 'get_image_list', 'get_release_group_image_list',
|
||||
'get_release_group_image_front', 'get_image_front', 'get_image_back',
|
||||
'get_image'
|
||||
]
|
||||
|
||||
import json
|
||||
|
||||
from musicbrainzngs import compat
|
||||
from musicbrainzngs import musicbrainz
|
||||
from musicbrainzngs.util import _unicode
|
||||
|
||||
hostname = "coverartarchive.org"
|
||||
https = True
|
||||
|
||||
|
||||
def set_caa_hostname(new_hostname, use_https=False):
|
||||
"""Set the base hostname for Cover Art Archive requests.
|
||||
Defaults to 'coverartarchive.org', accessing over https.
|
||||
For backwards compatibility, `use_https` is False by default.
|
||||
|
||||
:param str new_hostname: The hostname (and port) of the CAA server to connect to
|
||||
:param bool use_https: `True` if the host should be accessed using https. Default is `False`
|
||||
"""
|
||||
global hostname
|
||||
global https
|
||||
hostname = new_hostname
|
||||
https = use_https
|
||||
|
||||
|
||||
def _caa_request(mbid, imageid=None, size=None, entitytype="release"):
|
||||
""" Make a CAA request.
|
||||
|
||||
:param imageid: ``front``, ``back`` or a number from the listing obtained
|
||||
with :meth:`get_image_list`.
|
||||
:type imageid: str
|
||||
|
||||
:param size: "250", "500", "1200"
|
||||
:type size: str or None
|
||||
|
||||
:param entitytype: ``release`` or ``release-group``
|
||||
:type entitytype: str
|
||||
"""
|
||||
# Construct the full URL for the request, including hostname and
|
||||
# query string.
|
||||
path = [entitytype, mbid]
|
||||
if imageid and size:
|
||||
path.append("%s-%s" % (imageid, size))
|
||||
elif imageid:
|
||||
path.append(imageid)
|
||||
url = compat.urlunparse((
|
||||
'https' if https else 'http',
|
||||
hostname,
|
||||
'/%s' % '/'.join(path),
|
||||
'',
|
||||
'',
|
||||
''
|
||||
))
|
||||
musicbrainz._log.debug("GET request for %s" % (url, ))
|
||||
|
||||
# Set up HTTP request handler and URL opener.
|
||||
httpHandler = compat.HTTPHandler(debuglevel=0)
|
||||
handlers = [httpHandler]
|
||||
|
||||
opener = compat.build_opener(*handlers)
|
||||
|
||||
# Make request.
|
||||
req = musicbrainz._MusicbrainzHttpRequest("GET", url, None)
|
||||
# Useragent isn't needed for CAA, but we'll add it if it exists
|
||||
if musicbrainz._useragent != "":
|
||||
req.add_header('User-Agent', musicbrainz._useragent)
|
||||
musicbrainz._log.debug("requesting with UA %s" % musicbrainz._useragent)
|
||||
|
||||
resp = musicbrainz._safe_read(opener, req, None)
|
||||
|
||||
# TODO: The content type declared by the CAA for JSON files is
|
||||
# 'applicaiton/octet-stream'. This is not useful to detect whether the
|
||||
# content is JSON, so default to decoding JSON if no imageid was supplied.
|
||||
# http://tickets.musicbrainz.org/browse/CAA-75
|
||||
if imageid:
|
||||
# If we asked for an image, return the image
|
||||
return resp
|
||||
else:
|
||||
# Otherwise it's json
|
||||
data = _unicode(resp)
|
||||
return json.loads(data)
|
||||
|
||||
|
||||
def get_image_list(releaseid):
|
||||
"""Get the list of cover art associated with a release.
|
||||
|
||||
The return value is the deserialized response of the `JSON listing
|
||||
<http://musicbrainz.org/doc/Cover_Art_Archive/API#.2Frelease.2F.7Bmbid.7D.2F>`_
|
||||
returned by the Cover Art Archive API.
|
||||
|
||||
If an error occurs then a :class:`~musicbrainzngs.ResponseError` will
|
||||
be raised with one of the following HTTP codes:
|
||||
|
||||
* 400: `Releaseid` is not a valid UUID
|
||||
* 404: No release exists with an MBID of `releaseid`
|
||||
* 503: Ratelimit exceeded
|
||||
"""
|
||||
return _caa_request(releaseid)
|
||||
|
||||
|
||||
def get_release_group_image_list(releasegroupid):
|
||||
"""Get the list of cover art associated with a release group.
|
||||
|
||||
The return value is the deserialized response of the `JSON listing
|
||||
<http://musicbrainz.org/doc/Cover_Art_Archive/API#.2Frelease-group.2F.7Bmbid.7D.2F>`_
|
||||
returned by the Cover Art Archive API.
|
||||
|
||||
If an error occurs then a :class:`~musicbrainzngs.ResponseError` will
|
||||
be raised with one of the following HTTP codes:
|
||||
|
||||
* 400: `Releaseid` is not a valid UUID
|
||||
* 404: No release exists with an MBID of `releaseid`
|
||||
* 503: Ratelimit exceeded
|
||||
"""
|
||||
return _caa_request(releasegroupid, entitytype="release-group")
|
||||
|
||||
|
||||
def get_release_group_image_front(releasegroupid, size=None):
|
||||
"""Download the front cover art for a release group.
|
||||
The `size` argument and the possible error conditions are the same as for
|
||||
:meth:`get_image`.
|
||||
"""
|
||||
return get_image(releasegroupid, "front", size=size,
|
||||
entitytype="release-group")
|
||||
|
||||
|
||||
def get_image_front(releaseid, size=None):
|
||||
"""Download the front cover art for a release.
|
||||
The `size` argument and the possible error conditions are the same as for
|
||||
:meth:`get_image`.
|
||||
"""
|
||||
return get_image(releaseid, "front", size=size)
|
||||
|
||||
|
||||
def get_image_back(releaseid, size=None):
|
||||
"""Download the back cover art for a release.
|
||||
The `size` argument and the possible error conditions are the same as for
|
||||
:meth:`get_image`.
|
||||
"""
|
||||
return get_image(releaseid, "back", size=size)
|
||||
|
||||
|
||||
def get_image(mbid, coverid, size=None, entitytype="release"):
|
||||
"""Download cover art for a release. The coverart file to download
|
||||
is specified by the `coverid` argument.
|
||||
|
||||
If `size` is not specified, download the largest copy present, which can be
|
||||
very large.
|
||||
|
||||
If an error occurs then a :class:`~musicbrainzngs.ResponseError`
|
||||
will be raised with one of the following HTTP codes:
|
||||
|
||||
* 400: `Releaseid` is not a valid UUID or `coverid` is invalid
|
||||
* 404: No release exists with an MBID of `releaseid`
|
||||
* 503: Ratelimit exceeded
|
||||
|
||||
:param coverid: ``front``, ``back`` or a number from the listing obtained with
|
||||
:meth:`get_image_list`
|
||||
:type coverid: int or str
|
||||
|
||||
:param size: "250", "500", "1200" or None. If it is None, the largest
|
||||
available picture will be downloaded. If the image originally
|
||||
uploaded to the Cover Art Archive was smaller than the
|
||||
requested size, only the original image will be returned.
|
||||
:type size: str or None
|
||||
|
||||
:param entitytype: The type of entity for which to download the cover art.
|
||||
This is either ``release`` or ``release-group``.
|
||||
:type entitytype: str
|
||||
:return: The binary image data
|
||||
:type: str
|
||||
"""
|
||||
if isinstance(coverid, int):
|
||||
coverid = "%d" % (coverid, )
|
||||
if isinstance(size, int):
|
||||
size = "%d" % (size, )
|
||||
return _caa_request(mbid, coverid, size=size, entitytype=entitytype)
|
61
lib/musicbrainzngs/compat.py
Normal file
@@ -0,0 +1,61 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2012 Kenneth Reitz.
|
||||
|
||||
# Permission to use, copy, modify, and/or distribute this software for any
|
||||
# purpose with or without fee is hereby granted, provided that the above
|
||||
# copyright notice and this permission notice appear in all copies.
|
||||
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
"""
|
||||
pythoncompat
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
|
||||
# -------
|
||||
# Pythons
|
||||
# -------
|
||||
|
||||
# Syntax sugar.
|
||||
_ver = sys.version_info
|
||||
|
||||
#: Python 2.x?
|
||||
is_py2 = (_ver[0] == 2)
|
||||
|
||||
#: Python 3.x?
|
||||
is_py3 = (_ver[0] == 3)
|
||||
|
||||
# ---------
|
||||
# Specifics
|
||||
# ---------
|
||||
|
||||
if is_py2:
|
||||
from StringIO import StringIO
|
||||
from urllib2 import HTTPPasswordMgr, HTTPDigestAuthHandler, Request,\
|
||||
HTTPHandler, build_opener, HTTPError, URLError
|
||||
from httplib import BadStatusLine, HTTPException
|
||||
from urlparse import urlunparse
|
||||
from urllib import urlencode, quote_plus
|
||||
|
||||
bytes = str
|
||||
unicode = unicode
|
||||
basestring = basestring
|
||||
elif is_py3:
|
||||
from io import StringIO
|
||||
from urllib.request import HTTPPasswordMgr, HTTPDigestAuthHandler, Request,\
|
||||
HTTPHandler, build_opener
|
||||
from urllib.error import HTTPError, URLError
|
||||
from http.client import HTTPException, BadStatusLine
|
||||
from urllib.parse import urlunparse, urlencode, quote_plus
|
||||
|
||||
unicode = str
|
||||
bytes = bytes
|
||||
basestring = (str,bytes)
|
817
lib/musicbrainzngs/mbxml.py
Normal file
@@ -0,0 +1,817 @@
|
||||
# This file is part of the musicbrainzngs library
|
||||
# Copyright (C) Alastair Porter, Adrian Sampson, and others
|
||||
# This file is distributed under a BSD-2-Clause type license.
|
||||
# See the COPYING file for more information.
|
||||
|
||||
import re
|
||||
import xml.etree.ElementTree as ET
|
||||
import logging
|
||||
|
||||
from . import util
|
||||
|
||||
|
||||
def fixtag(tag, namespaces):
|
||||
# given a decorated tag (of the form {uri}tag), return prefixed
|
||||
# tag and namespace declaration, if any
|
||||
if isinstance(tag, ET.QName):
|
||||
tag = tag.text
|
||||
namespace_uri, tag = tag[1:].split("}", 1)
|
||||
prefix = namespaces.get(namespace_uri)
|
||||
if prefix is None:
|
||||
prefix = "ns%d" % len(namespaces)
|
||||
namespaces[namespace_uri] = prefix
|
||||
if prefix == "xml":
|
||||
xmlns = None
|
||||
else:
|
||||
xmlns = ("xmlns:%s" % prefix, namespace_uri)
|
||||
else:
|
||||
xmlns = None
|
||||
return "%s:%s" % (prefix, tag), xmlns
|
||||
|
||||
|
||||
NS_MAP = {"http://musicbrainz.org/ns/mmd-2.0#": "ws2",
|
||||
"http://musicbrainz.org/ns/ext#-2.0": "ext"}
|
||||
_log = logging.getLogger("musicbrainzngs")
|
||||
|
||||
def get_error_message(error):
|
||||
""" Given an error XML message from the webservice containing
|
||||
<error><text>x</text><text>y</text></error>, return a list
|
||||
of [x, y]"""
|
||||
try:
|
||||
tree = util.bytes_to_elementtree(error)
|
||||
root = tree.getroot()
|
||||
errors = []
|
||||
if root.tag == "error":
|
||||
for ch in root:
|
||||
if ch.tag == "text":
|
||||
errors.append(ch.text)
|
||||
return errors
|
||||
except ET.ParseError:
|
||||
return None
|
||||
|
||||
def make_artist_credit(artists):
|
||||
names = []
|
||||
for artist in artists:
|
||||
if isinstance(artist, dict):
|
||||
if "name" in artist:
|
||||
names.append(artist.get("name", ""))
|
||||
else:
|
||||
names.append(artist.get("artist", {}).get("name", ""))
|
||||
else:
|
||||
names.append(artist)
|
||||
return "".join(names)
|
||||
|
||||
def parse_elements(valid_els, inner_els, element):
|
||||
""" Extract single level subelements from an element.
|
||||
For example, given the element:
|
||||
<element>
|
||||
<subelement>Text</subelement>
|
||||
</element>
|
||||
and a list valid_els that contains "subelement",
|
||||
return a dict {'subelement': 'Text'}
|
||||
|
||||
Delegate the parsing of multi-level subelements to another function.
|
||||
For example, given the element:
|
||||
<element>
|
||||
<subelement>
|
||||
<a>Foo</a><b>Bar</b>
|
||||
</subelement>
|
||||
</element>
|
||||
and a dictionary {'subelement': parse_subelement},
|
||||
call parse_subelement(<subelement>) and
|
||||
return a dict {'subelement': <result>}
|
||||
if parse_subelement returns a tuple of the form
|
||||
(True, {'subelement-key': <result>})
|
||||
then merge the second element of the tuple into the
|
||||
result (which may have a key other than 'subelement' or
|
||||
more than 1 key)
|
||||
"""
|
||||
result = {}
|
||||
for sub in element:
|
||||
t = fixtag(sub.tag, NS_MAP)[0]
|
||||
if ":" in t:
|
||||
t = t.split(":")[1]
|
||||
if t in valid_els:
|
||||
result[t] = sub.text or ""
|
||||
elif t in inner_els.keys():
|
||||
inner_result = inner_els[t](sub)
|
||||
if isinstance(inner_result, tuple) and inner_result[0]:
|
||||
result.update(inner_result[1])
|
||||
else:
|
||||
result[t] = inner_result
|
||||
# add counts for lists when available
|
||||
m = re.match(r'([a-z0-9-]+)-list', t)
|
||||
if m and "count" in sub.attrib:
|
||||
result["%s-count" % m.group(1)] = int(sub.attrib["count"])
|
||||
else:
|
||||
_log.info("in <%s>, uncaught <%s>",
|
||||
fixtag(element.tag, NS_MAP)[0], t)
|
||||
return result
|
||||
|
||||
def parse_attributes(attributes, element):
|
||||
""" Extract attributes from an element.
|
||||
For example, given the element:
|
||||
<element type="Group" />
|
||||
and a list attributes that contains "type",
|
||||
return a dict {'type': 'Group'}
|
||||
"""
|
||||
result = {}
|
||||
for attr in element.attrib:
|
||||
if "{" in attr:
|
||||
a = fixtag(attr, NS_MAP)[0]
|
||||
else:
|
||||
a = attr
|
||||
if a in attributes:
|
||||
result[a] = element.attrib[attr]
|
||||
else:
|
||||
_log.info("in <%s>, uncaught attribute %s", fixtag(element.tag, NS_MAP)[0], attr)
|
||||
|
||||
return result
|
||||
|
||||
def parse_message(message):
|
||||
tree = util.bytes_to_elementtree(message)
|
||||
root = tree.getroot()
|
||||
result = {}
|
||||
valid_elements = {"area": parse_area,
|
||||
"artist": parse_artist,
|
||||
"instrument": parse_instrument,
|
||||
"label": parse_label,
|
||||
"place": parse_place,
|
||||
"event": parse_event,
|
||||
"release": parse_release,
|
||||
"release-group": parse_release_group,
|
||||
"series": parse_series,
|
||||
"recording": parse_recording,
|
||||
"work": parse_work,
|
||||
"url": parse_url,
|
||||
|
||||
"disc": parse_disc,
|
||||
"cdstub": parse_cdstub,
|
||||
"isrc": parse_isrc,
|
||||
|
||||
"annotation-list": parse_annotation_list,
|
||||
"area-list": parse_area_list,
|
||||
"artist-list": parse_artist_list,
|
||||
"label-list": parse_label_list,
|
||||
"place-list": parse_place_list,
|
||||
"event-list": parse_event_list,
|
||||
"instrument-list": parse_instrument_list,
|
||||
"release-list": parse_release_list,
|
||||
"release-group-list": parse_release_group_list,
|
||||
"series-list": parse_series_list,
|
||||
"recording-list": parse_recording_list,
|
||||
"work-list": parse_work_list,
|
||||
"url-list": parse_url_list,
|
||||
|
||||
"collection-list": parse_collection_list,
|
||||
"collection": parse_collection,
|
||||
|
||||
"message": parse_response_message
|
||||
}
|
||||
result.update(parse_elements([], valid_elements, root))
|
||||
return result
|
||||
|
||||
def parse_response_message(message):
|
||||
return parse_elements(["text"], {}, message)
|
||||
|
||||
def parse_collection_list(cl):
|
||||
return [parse_collection(c) for c in cl]
|
||||
|
||||
def parse_collection(collection):
|
||||
result = {}
|
||||
attribs = ["id", "type", "entity-type"]
|
||||
elements = ["name", "editor"]
|
||||
inner_els = {"release-list": parse_release_list,
|
||||
"artist-list": parse_artist_list,
|
||||
"event-list": parse_event_list,
|
||||
"place-list": parse_place_list,
|
||||
"recording-list": parse_recording_list,
|
||||
"work-list": parse_work_list}
|
||||
result.update(parse_attributes(attribs, collection))
|
||||
result.update(parse_elements(elements, inner_els, collection))
|
||||
|
||||
return result
|
||||
|
||||
def parse_annotation_list(al):
|
||||
return [parse_annotation(a) for a in al]
|
||||
|
||||
def parse_annotation(annotation):
|
||||
result = {}
|
||||
attribs = ["type", "ext:score"]
|
||||
elements = ["entity", "name", "text"]
|
||||
result.update(parse_attributes(attribs, annotation))
|
||||
result.update(parse_elements(elements, {}, annotation))
|
||||
return result
|
||||
|
||||
def parse_lifespan(lifespan):
|
||||
parts = parse_elements(["begin", "end", "ended"], {}, lifespan)
|
||||
|
||||
return parts
|
||||
|
||||
def parse_area_list(al):
|
||||
return [parse_area(a) for a in al]
|
||||
|
||||
def parse_area(area):
|
||||
result = {}
|
||||
attribs = ["id", "type", "ext:score"]
|
||||
elements = ["name", "sort-name", "disambiguation"]
|
||||
inner_els = {"life-span": parse_lifespan,
|
||||
"alias-list": parse_alias_list,
|
||||
"relation-list": parse_relation_list,
|
||||
"annotation": parse_annotation,
|
||||
"iso-3166-1-code-list": parse_element_list,
|
||||
"iso-3166-2-code-list": parse_element_list,
|
||||
"iso-3166-3-code-list": parse_element_list}
|
||||
|
||||
result.update(parse_attributes(attribs, area))
|
||||
result.update(parse_elements(elements, inner_els, area))
|
||||
|
||||
return result
|
||||
|
||||
def parse_artist_list(al):
|
||||
return [parse_artist(a) for a in al]
|
||||
|
||||
def parse_artist(artist):
|
||||
result = {}
|
||||
attribs = ["id", "type", "ext:score"]
|
||||
elements = ["name", "sort-name", "country", "user-rating",
|
||||
"disambiguation", "gender", "ipi"]
|
||||
inner_els = {"area": parse_area,
|
||||
"begin-area": parse_area,
|
||||
"end-area": parse_area,
|
||||
"life-span": parse_lifespan,
|
||||
"recording-list": parse_recording_list,
|
||||
"relation-list": parse_relation_list,
|
||||
"release-list": parse_release_list,
|
||||
"release-group-list": parse_release_group_list,
|
||||
"work-list": parse_work_list,
|
||||
"tag-list": parse_tag_list,
|
||||
"user-tag-list": parse_tag_list,
|
||||
"rating": parse_rating,
|
||||
"ipi-list": parse_element_list,
|
||||
"isni-list": parse_element_list,
|
||||
"alias-list": parse_alias_list,
|
||||
"annotation": parse_annotation}
|
||||
|
||||
result.update(parse_attributes(attribs, artist))
|
||||
result.update(parse_elements(elements, inner_els, artist))
|
||||
|
||||
return result
|
||||
|
||||
def parse_coordinates(c):
|
||||
return parse_elements(['latitude', 'longitude'], {}, c)
|
||||
|
||||
def parse_place_list(pl):
|
||||
return [parse_place(p) for p in pl]
|
||||
|
||||
def parse_place(place):
|
||||
result = {}
|
||||
attribs = ["id", "type", "ext:score"]
|
||||
elements = ["name", "address",
|
||||
"ipi", "disambiguation"]
|
||||
inner_els = {"area": parse_area,
|
||||
"coordinates": parse_coordinates,
|
||||
"life-span": parse_lifespan,
|
||||
"tag-list": parse_tag_list,
|
||||
"user-tag-list": parse_tag_list,
|
||||
"alias-list": parse_alias_list,
|
||||
"relation-list": parse_relation_list,
|
||||
"annotation": parse_annotation}
|
||||
|
||||
result.update(parse_attributes(attribs, place))
|
||||
result.update(parse_elements(elements, inner_els, place))
|
||||
|
||||
return result
|
||||
|
||||
def parse_event_list(el):
|
||||
return [parse_event(e) for e in el]
|
||||
|
||||
def parse_event(event):
|
||||
result = {}
|
||||
attribs = ["id", "type", "ext:score"]
|
||||
elements = ["name", "time", "setlist", "cancelled", "disambiguation", "user-rating"]
|
||||
inner_els = {"life-span": parse_lifespan,
|
||||
"relation-list": parse_relation_list,
|
||||
"alias-list": parse_alias_list,
|
||||
"tag-list": parse_tag_list,
|
||||
"user-tag-list": parse_tag_list,
|
||||
"rating": parse_rating}
|
||||
|
||||
result.update(parse_attributes(attribs, event))
|
||||
result.update(parse_elements(elements, inner_els, event))
|
||||
|
||||
return result
|
||||
|
||||
def parse_instrument(instrument):
|
||||
result = {}
|
||||
attribs = ["id", "type", "ext:score"]
|
||||
elements = ["name", "description", "disambiguation"]
|
||||
inner_els = {"relation-list": parse_relation_list,
|
||||
"tag-list": parse_tag_list,
|
||||
"alias-list": parse_alias_list,
|
||||
"annotation": parse_annotation}
|
||||
result.update(parse_attributes(attribs, instrument))
|
||||
result.update(parse_elements(elements, inner_els, instrument))
|
||||
|
||||
return result
|
||||
|
||||
def parse_label_list(ll):
|
||||
return [parse_label(l) for l in ll]
|
||||
|
||||
def parse_label(label):
|
||||
result = {}
|
||||
attribs = ["id", "type", "ext:score"]
|
||||
elements = ["name", "sort-name", "country", "label-code", "user-rating",
|
||||
"ipi", "disambiguation"]
|
||||
inner_els = {"area": parse_area,
|
||||
"life-span": parse_lifespan,
|
||||
"release-list": parse_release_list,
|
||||
"tag-list": parse_tag_list,
|
||||
"user-tag-list": parse_tag_list,
|
||||
"rating": parse_rating,
|
||||
"ipi-list": parse_element_list,
|
||||
"alias-list": parse_alias_list,
|
||||
"relation-list": parse_relation_list,
|
||||
"annotation": parse_annotation}
|
||||
|
||||
result.update(parse_attributes(attribs, label))
|
||||
result.update(parse_elements(elements, inner_els, label))
|
||||
|
||||
return result
|
||||
|
||||
def parse_relation_target(tgt):
|
||||
attributes = parse_attributes(['id'], tgt)
|
||||
if 'id' in attributes:
|
||||
return (True, {'target-id': attributes['id']})
|
||||
else:
|
||||
return (True, {'target-id': tgt.text})
|
||||
|
||||
def parse_relation_list(rl):
|
||||
attribs = ["target-type"]
|
||||
ttype = parse_attributes(attribs, rl)
|
||||
key = "%s-relation-list" % ttype["target-type"]
|
||||
return (True, {key: [parse_relation(r) for r in rl]})
|
||||
|
||||
def parse_relation(relation):
|
||||
result = {}
|
||||
attribs = ["type", "type-id"]
|
||||
elements = ["target", "direction", "begin", "end", "ended", "ordering-key"]
|
||||
inner_els = {"area": parse_area,
|
||||
"artist": parse_artist,
|
||||
"instrument": parse_instrument,
|
||||
"label": parse_label,
|
||||
"place": parse_place,
|
||||
"event": parse_event,
|
||||
"recording": parse_recording,
|
||||
"release": parse_release,
|
||||
"release-group": parse_release_group,
|
||||
"series": parse_series,
|
||||
"attribute-list": parse_element_list,
|
||||
"work": parse_work,
|
||||
"target": parse_relation_target
|
||||
}
|
||||
result.update(parse_attributes(attribs, relation))
|
||||
result.update(parse_elements(elements, inner_els, relation))
|
||||
# We parse attribute-list again to get attributes that have both
|
||||
# text and attribute values
|
||||
result.update(parse_elements(['target-credit'], {"attribute-list": parse_relation_attribute_list}, relation))
|
||||
|
||||
return result
|
||||
|
||||
def parse_relation_attribute_list(attributelist):
|
||||
ret = []
|
||||
for attribute in attributelist:
|
||||
ret.append(parse_relation_attribute_element(attribute))
|
||||
return (True, {"attributes": ret})
|
||||
|
||||
def parse_relation_attribute_element(element):
|
||||
# Parses an attribute into a dictionary containing an element
|
||||
# {"attribute": <text value>} and also an additional element
|
||||
# containing any xml attributes.
|
||||
# e.g <attribute value="BuxWV 1">number</attribute>
|
||||
# -> {"attribute": "number", "value": "BuxWV 1"}
|
||||
result = {}
|
||||
for attr in element.attrib:
|
||||
if "{" in attr:
|
||||
a = fixtag(attr, NS_MAP)[0]
|
||||
else:
|
||||
a = attr
|
||||
result[a] = element.attrib[attr]
|
||||
result["attribute"] = element.text
|
||||
return result
|
||||
|
||||
def parse_release(release):
|
||||
result = {}
|
||||
attribs = ["id", "ext:score"]
|
||||
elements = ["title", "status", "disambiguation", "quality", "country",
|
||||
"barcode", "date", "packaging", "asin"]
|
||||
inner_els = {"text-representation": parse_text_representation,
|
||||
"artist-credit": parse_artist_credit,
|
||||
"label-info-list": parse_label_info_list,
|
||||
"medium-list": parse_medium_list,
|
||||
"release-group": parse_release_group,
|
||||
"tag-list": parse_tag_list,
|
||||
"user-tag-list": parse_tag_list,
|
||||
"relation-list": parse_relation_list,
|
||||
"annotation": parse_annotation,
|
||||
"cover-art-archive": parse_caa,
|
||||
"release-event-list": parse_release_event_list}
|
||||
|
||||
result.update(parse_attributes(attribs, release))
|
||||
result.update(parse_elements(elements, inner_els, release))
|
||||
if "artist-credit" in result:
|
||||
result["artist-credit-phrase"] = make_artist_credit(
|
||||
result["artist-credit"])
|
||||
|
||||
return result
|
||||
|
||||
def parse_medium_list(ml):
|
||||
"""medium-list results from search have an additional
|
||||
<track-count> element containing the number of tracks
|
||||
over all mediums. Optionally add this"""
|
||||
medium_list = []
|
||||
track_count = None
|
||||
for m in ml:
|
||||
tag = fixtag(m.tag, NS_MAP)[0]
|
||||
if tag == "ws2:medium":
|
||||
medium_list.append(parse_medium(m))
|
||||
elif tag == "ws2:track-count":
|
||||
track_count = int(m.text)
|
||||
ret = {"medium-list": medium_list}
|
||||
if track_count is not None:
|
||||
ret["medium-track-count"] = track_count
|
||||
|
||||
return (True, ret)
|
||||
|
||||
def parse_release_event_list(rel):
|
||||
return [parse_release_event(re) for re in rel]
|
||||
|
||||
def parse_release_event(event):
|
||||
result = {}
|
||||
elements = ["date"]
|
||||
inner_els = {"area": parse_area}
|
||||
|
||||
result.update(parse_elements(elements, inner_els, event))
|
||||
return result
|
||||
|
||||
def parse_medium(medium):
|
||||
result = {}
|
||||
elements = ["position", "format", "title"]
|
||||
inner_els = {"disc-list": parse_disc_list,
|
||||
"pregap": parse_track,
|
||||
"track-list": parse_track_list,
|
||||
"data-track-list": parse_track_list}
|
||||
|
||||
result.update(parse_elements(elements, inner_els, medium))
|
||||
return result
|
||||
|
||||
def parse_disc_list(dl):
|
||||
return [parse_disc(d) for d in dl]
|
||||
|
||||
def parse_text_representation(textr):
|
||||
return parse_elements(["language", "script"], {}, textr)
|
||||
|
||||
def parse_release_group(rg):
|
||||
result = {}
|
||||
attribs = ["id", "type", "ext:score"]
|
||||
elements = ["title", "user-rating", "first-release-date", "primary-type",
|
||||
"disambiguation"]
|
||||
inner_els = {"artist-credit": parse_artist_credit,
|
||||
"release-list": parse_release_list,
|
||||
"tag-list": parse_tag_list,
|
||||
"user-tag-list": parse_tag_list,
|
||||
"secondary-type-list": parse_element_list,
|
||||
"relation-list": parse_relation_list,
|
||||
"rating": parse_rating,
|
||||
"annotation": parse_annotation}
|
||||
|
||||
result.update(parse_attributes(attribs, rg))
|
||||
result.update(parse_elements(elements, inner_els, rg))
|
||||
if "artist-credit" in result:
|
||||
result["artist-credit-phrase"] = make_artist_credit(result["artist-credit"])
|
||||
|
||||
return result
|
||||
|
||||
def parse_recording(recording):
|
||||
result = {}
|
||||
attribs = ["id", "ext:score"]
|
||||
elements = ["title", "length", "user-rating", "disambiguation", "video"]
|
||||
inner_els = {"artist-credit": parse_artist_credit,
|
||||
"release-list": parse_release_list,
|
||||
"tag-list": parse_tag_list,
|
||||
"user-tag-list": parse_tag_list,
|
||||
"rating": parse_rating,
|
||||
"isrc-list": parse_external_id_list,
|
||||
"relation-list": parse_relation_list,
|
||||
"annotation": parse_annotation}
|
||||
|
||||
result.update(parse_attributes(attribs, recording))
|
||||
result.update(parse_elements(elements, inner_els, recording))
|
||||
if "artist-credit" in result:
|
||||
result["artist-credit-phrase"] = make_artist_credit(result["artist-credit"])
|
||||
|
||||
return result
|
||||
|
||||
def parse_series_list(sl):
|
||||
return [parse_series(s) for s in sl]
|
||||
|
||||
def parse_series(series):
|
||||
result = {}
|
||||
attribs = ["id", "type", "ext:score"]
|
||||
elements = ["name", "disambiguation"]
|
||||
inner_els = {"alias-list": parse_alias_list,
|
||||
"relation-list": parse_relation_list,
|
||||
"annotation": parse_annotation}
|
||||
|
||||
result.update(parse_attributes(attribs, series))
|
||||
result.update(parse_elements(elements, inner_els, series))
|
||||
|
||||
return result
|
||||
|
||||
def parse_external_id_list(pl):
|
||||
return [parse_attributes(["id"], p)["id"] for p in pl]
|
||||
|
||||
def parse_element_list(el):
|
||||
return [e.text for e in el]
|
||||
|
||||
def parse_work_list(wl):
|
||||
return [parse_work(w) for w in wl]
|
||||
|
||||
def parse_work(work):
|
||||
result = {}
|
||||
attribs = ["id", "ext:score", "type"]
|
||||
elements = ["title", "user-rating", "language", "iswc", "disambiguation"]
|
||||
inner_els = {"tag-list": parse_tag_list,
|
||||
"user-tag-list": parse_tag_list,
|
||||
"rating": parse_rating,
|
||||
"alias-list": parse_alias_list,
|
||||
"iswc-list": parse_element_list,
|
||||
"relation-list": parse_relation_list,
|
||||
"annotation": parse_response_message,
|
||||
"attribute-list": parse_work_attribute_list
|
||||
}
|
||||
|
||||
result.update(parse_attributes(attribs, work))
|
||||
result.update(parse_elements(elements, inner_els, work))
|
||||
|
||||
return result
|
||||
|
||||
def parse_work_attribute_list(wal):
|
||||
return [parse_work_attribute(wa) for wa in wal]
|
||||
|
||||
def parse_work_attribute(wa):
|
||||
attribs = ["type"]
|
||||
typeinfo = parse_attributes(attribs, wa)
|
||||
result = {}
|
||||
if typeinfo:
|
||||
result = {"attribute": typeinfo["type"],
|
||||
"value": wa.text}
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def parse_url_list(ul):
|
||||
return [parse_url(u) for u in ul]
|
||||
|
||||
def parse_url(url):
|
||||
result = {}
|
||||
attribs = ["id"]
|
||||
elements = ["resource"]
|
||||
inner_els = {"relation-list": parse_relation_list}
|
||||
|
||||
result.update(parse_attributes(attribs, url))
|
||||
result.update(parse_elements(elements, inner_els, url))
|
||||
|
||||
return result
|
||||
|
||||
def parse_disc(disc):
|
||||
result = {}
|
||||
attribs = ["id"]
|
||||
elements = ["sectors"]
|
||||
inner_els = {"release-list": parse_release_list,
|
||||
"offset-list": parse_offset_list
|
||||
}
|
||||
|
||||
result.update(parse_attributes(attribs, disc))
|
||||
result.update(parse_elements(elements, inner_els, disc))
|
||||
|
||||
return result
|
||||
|
||||
def parse_cdstub(cdstub):
|
||||
result = {}
|
||||
attribs = ["id"]
|
||||
elements = ["title", "artist", "barcode"]
|
||||
inner_els = {"track-list": parse_track_list}
|
||||
|
||||
result.update(parse_attributes(attribs, cdstub))
|
||||
result.update(parse_elements(elements, inner_els, cdstub))
|
||||
|
||||
return result
|
||||
|
||||
def parse_offset_list(ol):
|
||||
return [int(o.text) for o in ol]
|
||||
|
||||
def parse_instrument_list(rl):
|
||||
result = []
|
||||
for r in rl:
|
||||
result.append(parse_instrument(r))
|
||||
return result
|
||||
|
||||
def parse_release_list(rl):
|
||||
result = []
|
||||
for r in rl:
|
||||
result.append(parse_release(r))
|
||||
return result
|
||||
|
||||
def parse_release_group_list(rgl):
|
||||
result = []
|
||||
for rg in rgl:
|
||||
result.append(parse_release_group(rg))
|
||||
return result
|
||||
|
||||
def parse_isrc(isrc):
|
||||
result = {}
|
||||
attribs = ["id"]
|
||||
inner_els = {"recording-list": parse_recording_list}
|
||||
|
||||
result.update(parse_attributes(attribs, isrc))
|
||||
result.update(parse_elements([], inner_els, isrc))
|
||||
|
||||
return result
|
||||
|
||||
def parse_recording_list(recs):
|
||||
result = []
|
||||
for r in recs:
|
||||
result.append(parse_recording(r))
|
||||
return result
|
||||
|
||||
def parse_artist_credit(ac):
|
||||
result = []
|
||||
for namecredit in ac:
|
||||
result.append(parse_name_credit(namecredit))
|
||||
join = parse_attributes(["joinphrase"], namecredit)
|
||||
if "joinphrase" in join:
|
||||
result.append(join["joinphrase"])
|
||||
return result
|
||||
|
||||
def parse_name_credit(nc):
|
||||
result = {}
|
||||
elements = ["name"]
|
||||
inner_els = {"artist": parse_artist}
|
||||
|
||||
result.update(parse_elements(elements, inner_els, nc))
|
||||
|
||||
return result
|
||||
|
||||
def parse_label_info_list(lil):
|
||||
result = []
|
||||
|
||||
for li in lil:
|
||||
result.append(parse_label_info(li))
|
||||
return result
|
||||
|
||||
def parse_label_info(li):
|
||||
result = {}
|
||||
elements = ["catalog-number"]
|
||||
inner_els = {"label": parse_label}
|
||||
|
||||
result.update(parse_elements(elements, inner_els, li))
|
||||
return result
|
||||
|
||||
def parse_track_list(tl):
|
||||
result = []
|
||||
for t in tl:
|
||||
result.append(parse_track(t))
|
||||
return result
|
||||
|
||||
def parse_track(track):
|
||||
result = {}
|
||||
attribs = ["id"]
|
||||
elements = ["number", "position", "title", "length"]
|
||||
inner_els = {"recording": parse_recording,
|
||||
"artist-credit": parse_artist_credit}
|
||||
|
||||
result.update(parse_attributes(attribs, track))
|
||||
result.update(parse_elements(elements, inner_els, track))
|
||||
if "artist-credit" in result.get("recording", {}) and "artist-credit" not in result:
|
||||
result["artist-credit"] = result["recording"]["artist-credit"]
|
||||
if "artist-credit" in result:
|
||||
result["artist-credit-phrase"] = make_artist_credit(result["artist-credit"])
|
||||
# Make a length field that contains track length or recording length
|
||||
track_or_recording = None
|
||||
if "length" in result:
|
||||
track_or_recording = result["length"]
|
||||
elif result.get("recording", {}).get("length"):
|
||||
track_or_recording = result.get("recording", {}).get("length")
|
||||
if track_or_recording:
|
||||
result["track_or_recording_length"] = track_or_recording
|
||||
return result
|
||||
|
||||
def parse_tag_list(tl):
|
||||
return [parse_tag(t) for t in tl]
|
||||
|
||||
def parse_tag(tag):
|
||||
result = {}
|
||||
attribs = ["count"]
|
||||
elements = ["name"]
|
||||
|
||||
result.update(parse_attributes(attribs, tag))
|
||||
result.update(parse_elements(elements, {}, tag))
|
||||
|
||||
return result
|
||||
|
||||
def parse_rating(rating):
|
||||
result = {}
|
||||
attribs = ["votes-count"]
|
||||
|
||||
result.update(parse_attributes(attribs, rating))
|
||||
result["rating"] = rating.text
|
||||
|
||||
return result
|
||||
|
||||
def parse_alias_list(al):
|
||||
return [parse_alias(a) for a in al]
|
||||
|
||||
def parse_alias(alias):
|
||||
result = {}
|
||||
attribs = ["locale", "sort-name", "type", "primary",
|
||||
"begin-date", "end-date"]
|
||||
|
||||
result.update(parse_attributes(attribs, alias))
|
||||
result["alias"] = alias.text
|
||||
|
||||
return result
|
||||
|
||||
def parse_caa(caa_element):
|
||||
result = {}
|
||||
elements = ["artwork", "count", "front", "back", "darkened"]
|
||||
|
||||
result.update(parse_elements(elements, {}, caa_element))
|
||||
return result
|
||||
|
||||
|
||||
###
|
||||
|
||||
def make_barcode_request(release2barcode):
|
||||
NS = "http://musicbrainz.org/ns/mmd-2.0#"
|
||||
root = ET.Element("{%s}metadata" % NS)
|
||||
rel_list = ET.SubElement(root, "{%s}release-list" % NS)
|
||||
for release, barcode in release2barcode.items():
|
||||
rel_xml = ET.SubElement(rel_list, "{%s}release" % NS)
|
||||
bar_xml = ET.SubElement(rel_xml, "{%s}barcode" % NS)
|
||||
rel_xml.set("{%s}id" % NS, release)
|
||||
bar_xml.text = barcode
|
||||
|
||||
return ET.tostring(root, "utf-8")
|
||||
|
||||
def make_tag_request(**kwargs):
|
||||
NS = "http://musicbrainz.org/ns/mmd-2.0#"
|
||||
root = ET.Element("{%s}metadata" % NS)
|
||||
for entity_type in ['artist', 'label', 'place', 'recording', 'release', 'release_group', 'work']:
|
||||
entity_tags = kwargs.pop(entity_type + '_tags', None)
|
||||
if entity_tags is not None:
|
||||
e_list = ET.SubElement(root, "{%s}%s-list" % (NS, entity_type.replace('_', '-')))
|
||||
for e, tags in entity_tags.items():
|
||||
e_xml = ET.SubElement(e_list, "{%s}%s" % (NS, entity_type.replace('_', '-')))
|
||||
e_xml.set("{%s}id" % NS, e)
|
||||
taglist = ET.SubElement(e_xml, "{%s}user-tag-list" % NS)
|
||||
for tag in tags:
|
||||
usertag_xml = ET.SubElement(taglist, "{%s}user-tag" % NS)
|
||||
name_xml = ET.SubElement(usertag_xml, "{%s}name" % NS)
|
||||
name_xml.text = tag
|
||||
if kwargs.keys():
|
||||
raise TypeError("make_tag_request() got an unexpected keyword argument '%s'" % kwargs.popitem()[0])
|
||||
|
||||
return ET.tostring(root, "utf-8")
|
||||
|
||||
def make_rating_request(**kwargs):
|
||||
NS = "http://musicbrainz.org/ns/mmd-2.0#"
|
||||
root = ET.Element("{%s}metadata" % NS)
|
||||
for entity_type in ['artist', 'label', 'recording', 'release_group', 'work']:
|
||||
entity_ratings = kwargs.pop(entity_type + '_ratings', None)
|
||||
if entity_ratings is not None:
|
||||
e_list = ET.SubElement(root, "{%s}%s-list" % (NS, entity_type.replace('_', '-')))
|
||||
for e, rating in entity_ratings.items():
|
||||
e_xml = ET.SubElement(e_list, "{%s}%s" % (NS, entity_type.replace('_', '-')))
|
||||
e_xml.set("{%s}id" % NS, e)
|
||||
rating_xml = ET.SubElement(e_xml, "{%s}user-rating" % NS)
|
||||
rating_xml.text = str(rating)
|
||||
if kwargs.keys():
|
||||
raise TypeError("make_rating_request() got an unexpected keyword argument '%s'" % kwargs.popitem()[0])
|
||||
|
||||
return ET.tostring(root, "utf-8")
|
||||
|
||||
def make_isrc_request(recording2isrcs):
|
||||
NS = "http://musicbrainz.org/ns/mmd-2.0#"
|
||||
root = ET.Element("{%s}metadata" % NS)
|
||||
rec_list = ET.SubElement(root, "{%s}recording-list" % NS)
|
||||
for rec, isrcs in recording2isrcs.items():
|
||||
if len(isrcs) > 0:
|
||||
rec_xml = ET.SubElement(rec_list, "{%s}recording" % NS)
|
||||
rec_xml.set("{%s}id" % NS, rec)
|
||||
isrc_list_xml = ET.SubElement(rec_xml, "{%s}isrc-list" % NS)
|
||||
isrc_list_xml.set("{%s}count" % NS, str(len(isrcs)))
|
||||
for isrc in isrcs:
|
||||
isrc_xml = ET.SubElement(isrc_list_xml, "{%s}isrc" % NS)
|
||||
isrc_xml.set("{%s}id" % NS, isrc)
|
||||
return ET.tostring(root, "utf-8")
|
1312
lib/musicbrainzngs/musicbrainz.py
Normal file
44
lib/musicbrainzngs/util.py
Normal file
@@ -0,0 +1,44 @@
|
||||
# This file is part of the musicbrainzngs library
|
||||
# Copyright (C) Alastair Porter, Adrian Sampson, and others
|
||||
# This file is distributed under a BSD-2-Clause type license.
|
||||
# See the COPYING file for more information.
|
||||
|
||||
import sys
|
||||
import locale
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from . import compat
|
||||
|
||||
def _unicode(string, encoding=None):
|
||||
"""Try to decode byte strings to unicode.
|
||||
This can only be a guess, but this might be better than failing.
|
||||
It is safe to use this on numbers or strings that are already unicode.
|
||||
"""
|
||||
if isinstance(string, compat.unicode):
|
||||
unicode_string = string
|
||||
elif isinstance(string, compat.bytes):
|
||||
# use given encoding, stdin, preferred until something != None is found
|
||||
if encoding is None:
|
||||
encoding = sys.stdin.encoding
|
||||
if encoding is None:
|
||||
encoding = locale.getpreferredencoding()
|
||||
unicode_string = string.decode(encoding, "ignore")
|
||||
else:
|
||||
unicode_string = compat.unicode(string)
|
||||
return unicode_string.replace('\x00', '').strip()
|
||||
|
||||
def bytes_to_elementtree(bytes_or_file):
|
||||
"""Given a bytestring or a file-like object that will produce them,
|
||||
parse and return an ElementTree.
|
||||
"""
|
||||
if isinstance(bytes_or_file, compat.basestring):
|
||||
s = bytes_or_file
|
||||
else:
|
||||
s = bytes_or_file.read()
|
||||
|
||||
if compat.is_py3:
|
||||
s = _unicode(s, "utf-8")
|
||||
|
||||
f = compat.StringIO(s)
|
||||
tree = ET.ElementTree(file=f)
|
||||
return tree
|
@@ -22,8 +22,8 @@ from pytz.tzfile import build_tzinfo
|
||||
|
||||
|
||||
# The IANA (nee Olson) database is updated several times a year.
|
||||
OLSON_VERSION = '2019a'
|
||||
VERSION = '2019.1' # pip compatible version number.
|
||||
OLSON_VERSION = '2019c'
|
||||
VERSION = '2019.3' # pip compatible version number.
|
||||
__version__ = VERSION
|
||||
|
||||
OLSEN_VERSION = OLSON_VERSION # Old releases had this misspelling
|
||||
@@ -188,8 +188,14 @@ def _unmunge_zone(zone):
|
||||
return zone.replace('_plus_', '+').replace('_minus_', '-')
|
||||
|
||||
|
||||
_all_timezones_lower_to_standard = None
|
||||
|
||||
|
||||
def _case_insensitive_zone_lookup(zone):
|
||||
"""case-insensitively matching timezone, else return zone unchanged"""
|
||||
global _all_timezones_lower_to_standard
|
||||
if _all_timezones_lower_to_standard is None:
|
||||
_all_timezones_lower_to_standard = dict((tz.lower(), tz) for tz in all_timezones) # noqa
|
||||
return _all_timezones_lower_to_standard.get(zone.lower()) or zone # noqa
|
||||
|
||||
|
||||
@@ -1098,7 +1104,6 @@ all_timezones = LazyList(
|
||||
tz for tz in all_timezones if resource_exists(tz))
|
||||
|
||||
all_timezones_set = LazySet(all_timezones)
|
||||
_all_timezones_lower_to_standard = dict((tz.lower(), tz) for tz in all_timezones)
|
||||
common_timezones = \
|
||||
['Africa/Abidjan',
|
||||
'Africa/Accra',
|
||||
|
@@ -27,8 +27,8 @@ from pytz.tzinfo import DstTzInfo, StaticTzInfo # noqa
|
||||
|
||||
# I test for expected version to ensure the correct version of pytz is
|
||||
# actually being tested.
|
||||
EXPECTED_VERSION = '2019.1'
|
||||
EXPECTED_OLSON_VERSION = '2019a'
|
||||
EXPECTED_VERSION = '2019.3'
|
||||
EXPECTED_OLSON_VERSION = '2019c'
|
||||
|
||||
fmt = '%Y-%m-%d %H:%M:%S %Z%z'
|
||||
|
||||
@@ -183,8 +183,14 @@ class PicklingTest(unittest.TestCase):
|
||||
# Python 3 introduced a new pickle protocol where numbers are stored in
|
||||
# hexadecimal representation. Here we extract the pickle
|
||||
# representation of the number for the current Python version.
|
||||
old_pickle_pattern = pickle.dumps(tz._utcoffset.seconds)[3:-1]
|
||||
new_pickle_pattern = pickle.dumps(new_utcoffset)[3:-1]
|
||||
#
|
||||
# Test protocol 3 on Python 3 and protocol 0 on Python 2.
|
||||
if sys.version_info >= (3,):
|
||||
protocol = 3
|
||||
else:
|
||||
protocol = 0
|
||||
old_pickle_pattern = pickle.dumps(tz._utcoffset.seconds, protocol)[3:-1]
|
||||
new_pickle_pattern = pickle.dumps(new_utcoffset, protocol)[3:-1]
|
||||
hacked_p = p.replace(old_pickle_pattern, new_pickle_pattern)
|
||||
|
||||
self.assertNotEqual(p, hacked_p)
|
||||
|