Compare commits

...

710 commits

Author SHA1 Message Date
JonnyWong16
1144bba580
v2.15.3
Some checks failed
Publish Snap / Build Snap Package (armhf) (push) Has been cancelled
Publish Docker / Build Docker Image (push) Has been cancelled
Publish Snap / Build Snap Package (arm64) (push) Has been cancelled
CodeQL / CodeQL Analysis (push) Has been cancelled
Publish Snap / Build Snap Package (amd64) (push) Has been cancelled
Publish Installers / Build MacOS Installer (push) Has been cancelled
Publish Installers / Build Windows Installer (push) Has been cancelled
Publish Snap / Discord Notification (push) Has been cancelled
Publish Docker / Discord Notification (push) Has been cancelled
Publish Installers / Discord Notification (push) Has been cancelled
Publish Installers / VirusTotal Scan (push) Has been cancelled
Publish Installers / Release Installers (push) Has been cancelled
2025-08-03 10:17:15 -07:00
JonnyWong16
443eb8da15
Disable browser autocomplete in notification and newsletter configs
Fixes #2557
2025-08-03 10:10:01 -07:00
Komu Wairagu
5ecd570f95
Allow users to set config values through environment variables. (#2543)
* - Allow users to set config values through environment variables.
- Fixes: https://github.com/Tautulli/Tautulli/issues/2309

* Prefix environment variables with `TAUTULLI_`
For: https://github.com/Tautulli/Tautulli/issues/2309

* Update handling Tautulli environment variables

* Add log message when environment variable not saved to config

---------

Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>
2025-08-03 09:42:58 -07:00
Tom Niget
b8589513c1
Remove duplicate "Total" entry in graph tooltips (#2534) 2025-08-03 09:42:44 -07:00
JonnyWong16
d46710962c
Add ability to return svg files using pms_image_proxy 2025-08-03 09:35:06 -07:00
JonnyWong16
5921a7d83f
Fix rounding of minutes in global stats play duration
Some checks failed
CodeQL / CodeQL Analysis (push) Has been cancelled
Publish Docker / Build Docker Image (push) Has been cancelled
Publish Installers / Build MacOS Installer (push) Has been cancelled
Publish Installers / Build Windows Installer (push) Has been cancelled
Publish Snap / Build Snap Package (amd64) (push) Has been cancelled
Publish Snap / Build Snap Package (arm64) (push) Has been cancelled
Publish Snap / Build Snap Package (armhf) (push) Has been cancelled
Publish Docker / Discord Notification (push) Has been cancelled
Publish Installers / VirusTotal Scan (push) Has been cancelled
Publish Installers / Release Installers (push) Has been cancelled
Publish Installers / Discord Notification (push) Has been cancelled
Publish Snap / Discord Notification (push) Has been cancelled
2025-07-14 19:54:45 -07:00
JonnyWong16
9a6253d775
Enable jquery.scrollbar on macosx and webkit
Fixes #2221

Ref: gromo/jquery.scrollbar#134
2025-05-10 16:30:02 -07:00
JonnyWong16
43fc7eebfe
Update is_hdr helper function 2025-05-10 16:26:12 -07:00
JonnyWong16
c4f8a81190
Add hearingImpaired and visualImparied to exporter fields
* `hearingImpaired` for `SubtitleStreams`
* `visualImpaired` for `AudioStreams`
2025-05-10 16:25:39 -07:00
JonnyWong16
f6bffe1850
Update plexapi==4.17.0 2025-05-10 16:13:23 -07:00
dependabot[bot]
3cb71f94a3
Bump plexapi from 4.16.1 to 4.17.0 (#2538)
Bumps [plexapi](https://github.com/pushingkarmaorg/python-plexapi) from 4.16.1 to 4.17.0.
- [Release notes](https://github.com/pushingkarmaorg/python-plexapi/releases)
- [Commits](https://github.com/pushingkarmaorg/python-plexapi/compare/4.16.1...4.17.0)

---
updated-dependencies:
- dependency-name: plexapi
  dependency-version: 4.17.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2025-05-10 16:10:03 -07:00
JonnyWong16
ff5edc06fe
Update crypto donation 2025-05-10 15:56:52 -07:00
JonnyWong16
cc88cffc1f
Fix retrieving history for collections/playlists with over 1000 items 2025-05-03 16:08:17 -07:00
JonnyWong16
e735294e1c
Uppercase ZIP archive export download tooltip 2025-04-18 18:21:27 -07:00
JonnyWong16
889026b092
Add auto sync winget fork to workflow 2025-04-12 16:56:10 -07:00
JonnyWong16
76f6a2da6b
v2.15.2 2025-04-12 16:02:46 -07:00
Tom Niget
d2a14ea6c0
Add hidden-by-default Total curve to the daily stream graph (#2497)
* Add hidden-by-default Total curve to the daily stream graph

* Update curve color

Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

---------

Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>
2025-04-12 15:58:28 -07:00
JonnyWong16
e6c0a12dd5
Add stream count to tab title on homepage
Closes #2517
2025-03-30 20:30:01 -07:00
JonnyWong16
24dd403a72
Activity card only link to library if section_id available 2025-03-29 20:42:53 -07:00
JonnyWong16
a876e006d6
Fix Trakt URL redirect to media page
Fixes #2513
2025-03-29 20:42:44 -07:00
dependabot[bot]
74786f0ed1
Bump cryptography from 43.0.3 to 44.0.2 (#2519)
Bumps [cryptography](https://github.com/pyca/cryptography) from 43.0.3 to 44.0.2.
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/43.0.3...44.0.2)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-24 14:05:05 -07:00
dependabot[bot]
99e575383c
Bump pyopenssl from 24.2.1 to 25.0.0 (#2482)
Bumps [pyopenssl](https://github.com/pyca/pyopenssl) from 24.2.1 to 25.0.0.
- [Changelog](https://github.com/pyca/pyopenssl/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/pyopenssl/compare/24.2.1...25.0.0)

---
updated-dependencies:
- dependency-name: pyopenssl
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2025-03-24 14:04:46 -07:00
JonnyWong16
3e784c7495
Check stream watched status before stopped status
Fixes #2506
2025-03-23 20:41:30 -07:00
JonnyWong16
68dc095c83
Do not redirect API requests to login page
Fixes #2490
2025-03-23 20:10:43 -07:00
JonnyWong16
ad2ec0e2bf
Fix CherryPy CORS response headers
Fixes #2279
2025-03-23 19:44:10 -07:00
JonnyWong16
09c28e434d
Check Pushover attachment under 5MB limit
Fixes #2396
2025-03-23 18:12:08 -07:00
JonnyWong16
cfc7b817b3
Downgrade pyinstaller to 6.10.0 2025-03-23 16:19:54 -07:00
JonnyWong16
b3aa29c677
Swap source and stream columns in steam info modal 2025-03-23 16:05:01 -07:00
JonnyWong16
e4d181ba5b
Add PATCH method for webhooks 2025-03-16 12:26:34 -07:00
JonnyWong16
53e5f89725
Add audio profile notification parameters 2025-03-16 12:26:33 -07:00
JonnyWong16
0879b848b9
Add link to library page from activity card media type icon 2025-03-16 12:26:32 -07:00
JonnyWong16
c70381c3ff
Fix ntfy notifications not sending if provider link is blank 2025-03-16 12:26:30 -07:00
JonnyWong16
f23d3eb81c
Fix changelog username 2025-03-16 12:26:28 -07:00
luzpaz
2ed603f288
Fix typos (#2520)
Found via codespell
2025-03-16 12:25:29 -07:00
JonnyWong16
a96fd23d72
v2.15.1 2025-01-11 15:27:24 -08:00
JonnyWong16
65dc466c07
Add Github token to release virus scan 2025-01-09 15:13:20 -08:00
JonnyWong16
0a4730625c
Update copyright year 2025-01-09 15:12:49 -08:00
JonnyWong16
67fa4ca645
Add logos to season and episode exports 2025-01-09 10:45:49 -08:00
JonnyWong16
078c293bd7
Update plexapi=4.16.1 2025-01-09 10:45:29 -08:00
JonnyWong16
85e9237608
Update pyjwt==2.10.1 2025-01-09 10:16:47 -08:00
dependabot[bot]
f9b3631745
Bump pyjwt from 2.10.0 to 2.10.1 (#2445)
Bumps [pyjwt](https://github.com/jpadilla/pyjwt) from 2.10.0 to 2.10.1.
- [Release notes](https://github.com/jpadilla/pyjwt/releases)
- [Changelog](https://github.com/jpadilla/pyjwt/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/jpadilla/pyjwt/compare/2.10.0...2.10.1)

---
updated-dependencies:
- dependency-name: pyjwt
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2025-01-09 10:14:37 -08:00
JonnyWong16
8f03e27617
Add VirusTotal scan to installer build CI 2025-01-09 09:57:58 -08:00
JonnyWong16
63fe386057
Disable basic auth for /newsletter and /image endpoints
Fixes #2472
2025-01-09 09:13:57 -08:00
chrisdecker08
b7c4f2eefe
detect HDR transcodes via colorTrc attribute (#2466) 2024-12-14 12:24:08 -08:00
JonnyWong16
37ef098718
Flip docker container healthcheck https first 2024-11-28 16:36:03 -08:00
JonnyWong16
78864d7a97
v2.15.0 2024-11-24 14:57:29 -08:00
JonnyWong16
62a05712f8
Add logos to exporter 2024-11-24 14:55:51 -08:00
JonnyWong16
ca0e1c321d
Convert CustomArrow to string in newsletter raw json 2024-11-24 14:17:35 -08:00
JonnyWong16
b9cb7102c4
Add plex_slug and plex_watch_url to nofication parameters 2024-11-19 10:58:55 -08:00
JonnyWong16
6e6fe1fb65
Add slugs to metadata details 2024-11-19 10:36:34 -08:00
peagravel
9c473c6528
Add friendly name to the top bar of config modals (#2432) 2024-11-19 10:14:45 -08:00
Castle
5c38de0dfb
Allow Telegram blockquote expandable (#2427)
* Allow Telegram blockquote expandable

Blockquote is not yet supported, this feature adds support along with expandable functionality.

* Add support for tg-emoji in Telegram HTML

---------

Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>
2024-11-19 10:12:00 -08:00
JonnyWong16
ea66f6713b
Add hasVoiceActivity to exporter fields 2024-11-19 10:09:02 -08:00
dependabot[bot]
dd9a35df51
Bump pyjwt from 2.9.0 to 2.10.0 (#2441)
* Bump pyjwt from 2.9.0 to 2.10.0

Bumps [pyjwt](https://github.com/jpadilla/pyjwt) from 2.9.0 to 2.10.0.
- [Release notes](https://github.com/jpadilla/pyjwt/releases)
- [Changelog](https://github.com/jpadilla/pyjwt/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/jpadilla/pyjwt/compare/2.9.0...2.10.0)

---
updated-dependencies:
- dependency-name: pyjwt
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update pyjwt==2.10.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-11-19 10:01:27 -08:00
dependabot[bot]
feca713b76
Bump dnspython from 2.6.1 to 2.7.0 (#2440)
* Bump dnspython from 2.6.1 to 2.7.0

Bumps [dnspython](https://github.com/rthalley/dnspython) from 2.6.1 to 2.7.0.
- [Release notes](https://github.com/rthalley/dnspython/releases)
- [Changelog](https://github.com/rthalley/dnspython/blob/main/doc/whatsnew.rst)
- [Commits](https://github.com/rthalley/dnspython/compare/v2.6.1...v2.7.0)

---
updated-dependencies:
- dependency-name: dnspython
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update dnspython==2.7.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-11-19 10:00:50 -08:00
dependabot[bot]
0836fb902c
Bump plexapi from 4.15.16 to 4.16.0 (#2439)
* Bump plexapi from 4.15.16 to 4.16.0

Bumps [plexapi](https://github.com/pkkid/python-plexapi) from 4.15.16 to 4.16.0.
- [Release notes](https://github.com/pkkid/python-plexapi/releases)
- [Commits](https://github.com/pkkid/python-plexapi/compare/4.15.16...4.16.0)

---
updated-dependencies:
- dependency-name: plexapi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update plexapi==4.16.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-11-19 10:00:37 -08:00
dependabot[bot]
eb2c372d82
Bump bleach from 6.1.0 to 6.2.0 (#2438)
* Bump bleach from 6.1.0 to 6.2.0

Bumps [bleach](https://github.com/mozilla/bleach) from 6.1.0 to 6.2.0.
- [Changelog](https://github.com/mozilla/bleach/blob/main/CHANGES)
- [Commits](https://github.com/mozilla/bleach/compare/v6.1.0...v6.2.0)

---
updated-dependencies:
- dependency-name: bleach
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update bleach==6.2.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-11-19 10:00:24 -08:00
dependabot[bot]
be2e63e7e0
Bump pyparsing from 3.1.4 to 3.2.0 (#2437)
* Bump pyparsing from 3.1.4 to 3.2.0

Bumps [pyparsing](https://github.com/pyparsing/pyparsing) from 3.1.4 to 3.2.0.
- [Release notes](https://github.com/pyparsing/pyparsing/releases)
- [Changelog](https://github.com/pyparsing/pyparsing/blob/master/CHANGES)
- [Commits](https://github.com/pyparsing/pyparsing/compare/3.1.4...3.2.0)

---
updated-dependencies:
- dependency-name: pyparsing
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update pyparsing==3.2.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-11-19 10:00:11 -08:00
dependabot[bot]
2fe3f039cc
Bump tokenize-rt from 6.0.0 to 6.1.0 (#2436)
* Bump tokenize-rt from 6.0.0 to 6.1.0

Bumps [tokenize-rt](https://github.com/asottile/tokenize-rt) from 6.0.0 to 6.1.0.
- [Commits](https://github.com/asottile/tokenize-rt/compare/v6.0.0...v6.1.0)

---
updated-dependencies:
- dependency-name: tokenize-rt
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update tokenize-rt==6.1.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-11-19 09:59:58 -08:00
dependabot[bot]
baf926e5db
Bump markupsafe from 2.1.5 to 3.0.2 (#2435)
Bumps [markupsafe](https://github.com/pallets/markupsafe) from 2.1.5 to 3.0.2.
- [Release notes](https://github.com/pallets/markupsafe/releases)
- [Changelog](https://github.com/pallets/markupsafe/blob/main/CHANGES.rst)
- [Commits](https://github.com/pallets/markupsafe/compare/2.1.5...3.0.2)

---
updated-dependencies:
- dependency-name: markupsafe
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2024-11-19 09:59:40 -08:00
JonnyWong16
85b63fb61a
Remove backports.zoneinfo 2024-11-19 09:34:49 -08:00
JonnyWong16
afc29604cc
Bump zipp==3.21.0
Closes #2433
2024-11-19 09:34:48 -08:00
JonnyWong16
5b47cebdc7
Bump minimum Python version to 3.9 2024-11-16 15:21:41 -08:00
JonnyWong16
d9f38f9390
Fix artist title for fixing metadata match
Fixes #2429
2024-11-16 15:19:57 -08:00
JonnyWong16
86d775a586
Update OneSignal API calls 2024-11-16 15:19:56 -08:00
dependabot[bot]
ddb4f6131b
Bump pyinstaller from 6.8.0 to 6.11.1 (#2431)
Bumps [pyinstaller](https://github.com/pyinstaller/pyinstaller) from 6.8.0 to 6.11.1.
- [Release notes](https://github.com/pyinstaller/pyinstaller/releases)
- [Changelog](https://github.com/pyinstaller/pyinstaller/blob/develop/doc/CHANGES.rst)
- [Commits](https://github.com/pyinstaller/pyinstaller/compare/v6.8.0...v6.11.1)

---
updated-dependencies:
- dependency-name: pyinstaller
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-16 15:02:52 -08:00
dependabot[bot]
599e52de6a
Bump cryptography from 43.0.0 to 43.0.3 (#2421)
Bumps [cryptography](https://github.com/pyca/cryptography) from 43.0.0 to 43.0.3.
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/43.0.0...43.0.3)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2024-11-16 14:53:44 -08:00
dependabot[bot]
84be60cb36
Bump pywin32 from 306 to 308 (#2417)
Bumps [pywin32](https://github.com/mhammond/pywin32) from 306 to 308.
- [Release notes](https://github.com/mhammond/pywin32/releases)
- [Changelog](https://github.com/mhammond/pywin32/blob/main/CHANGES.txt)
- [Commits](https://github.com/mhammond/pywin32/commits)

---
updated-dependencies:
- dependency-name: pywin32
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2024-11-16 14:53:33 -08:00
dependabot[bot]
d9a87f9726
Bump packaging from 24.1 to 24.2 (#2428)
* Bump packaging from 24.1 to 24.2

Bumps [packaging](https://github.com/pypa/packaging) from 24.1 to 24.2.
- [Release notes](https://github.com/pypa/packaging/releases)
- [Changelog](https://github.com/pypa/packaging/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pypa/packaging/compare/24.1...24.2)

---
updated-dependencies:
- dependency-name: packaging
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update packaging==24.2

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-11-16 14:52:11 -08:00
dependabot[bot]
9289ead996
Bump mako from 1.3.5 to 1.3.6 (#2423)
* Bump mako from 1.3.5 to 1.3.6

Bumps [mako](https://github.com/sqlalchemy/mako) from 1.3.5 to 1.3.6.
- [Release notes](https://github.com/sqlalchemy/mako/releases)
- [Changelog](https://github.com/sqlalchemy/mako/blob/main/CHANGES)
- [Commits](https://github.com/sqlalchemy/mako/commits)

---
updated-dependencies:
- dependency-name: mako
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update mako==1.3.6

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-11-16 14:51:58 -08:00
dependabot[bot]
af752e0acc
Bump xmltodict from 0.13.0 to 0.14.2 (#2418)
* Bump xmltodict from 0.13.0 to 0.14.2

Bumps [xmltodict](https://github.com/martinblech/xmltodict) from 0.13.0 to 0.14.2.
- [Changelog](https://github.com/martinblech/xmltodict/blob/master/CHANGELOG.md)
- [Commits](https://github.com/martinblech/xmltodict/compare/v0.13.0...v0.14.2)

---
updated-dependencies:
- dependency-name: xmltodict
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update xmltodict==0.14.2

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-11-16 14:51:45 -08:00
dependabot[bot]
86abd130b0
Bump profilehooks from 1.12.0 to 1.13.0 (#2414)
* Bump profilehooks from 1.12.0 to 1.13.0

Bumps [profilehooks](https://github.com/mgedmin/profilehooks) from 1.12.0 to 1.13.0.
- [Changelog](https://github.com/mgedmin/profilehooks/blob/master/CHANGES.rst)
- [Commits](https://github.com/mgedmin/profilehooks/compare/1.12.0...1.13.0)

---
updated-dependencies:
- dependency-name: profilehooks
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update profilehooks==1.13.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-11-16 14:51:34 -08:00
dependabot[bot]
fc2c7cc871
Bump tzdata from 2024.1 to 2024.2 (#2409)
* Bump tzdata from 2024.1 to 2024.2

Bumps [tzdata](https://github.com/python/tzdata) from 2024.1 to 2024.2.
- [Release notes](https://github.com/python/tzdata/releases)
- [Changelog](https://github.com/python/tzdata/blob/master/NEWS.md)
- [Commits](https://github.com/python/tzdata/compare/2024.1...2024.2)

---
updated-dependencies:
- dependency-name: tzdata
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update tzdata==2024.2

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-11-16 14:51:22 -08:00
dependabot[bot]
025e8bcf58
Bump platformdirs from 4.2.2 to 4.3.6 (#2403)
* Bump platformdirs from 4.2.2 to 4.3.6

Bumps [platformdirs](https://github.com/tox-dev/platformdirs) from 4.2.2 to 4.3.6.
- [Release notes](https://github.com/tox-dev/platformdirs/releases)
- [Changelog](https://github.com/tox-dev/platformdirs/blob/main/CHANGES.rst)
- [Commits](https://github.com/tox-dev/platformdirs/compare/4.2.2...4.3.6)

---
updated-dependencies:
- dependency-name: platformdirs
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update platformdirs==4.3.6

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-11-16 14:51:10 -08:00
dependabot[bot]
bf07912711
Bump idna from 3.7 to 3.10 (#2400)
* Bump idna from 3.7 to 3.10

Bumps [idna](https://github.com/kjd/idna) from 3.7 to 3.10.
- [Release notes](https://github.com/kjd/idna/releases)
- [Changelog](https://github.com/kjd/idna/blob/master/HISTORY.rst)
- [Commits](https://github.com/kjd/idna/compare/v3.7...v3.10)

---
updated-dependencies:
- dependency-name: idna
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update idna==3.10

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-11-16 14:50:57 -08:00
dependabot[bot]
48b1c7b522
Bump pytz from 2024.1 to 2024.2 (#2398)
* Bump pytz from 2024.1 to 2024.2

Bumps [pytz](https://github.com/stub42/pytz) from 2024.1 to 2024.2.
- [Release notes](https://github.com/stub42/pytz/releases)
- [Commits](https://github.com/stub42/pytz/compare/release_2024.1...release_2024.2)

---
updated-dependencies:
- dependency-name: pytz
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update pytz==2024.2

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-11-16 14:49:08 -08:00
dependabot[bot]
e69852fa0e
Bump importlib-metadata from 8.2.0 to 8.5.0 (#2397)
* Bump importlib-metadata from 8.2.0 to 8.5.0

Bumps [importlib-metadata](https://github.com/python/importlib_metadata) from 8.2.0 to 8.5.0.
- [Release notes](https://github.com/python/importlib_metadata/releases)
- [Changelog](https://github.com/python/importlib_metadata/blob/main/NEWS.rst)
- [Commits](https://github.com/python/importlib_metadata/compare/v8.2.0...v8.5.0)

---
updated-dependencies:
- dependency-name: importlib-metadata
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update importlib-metadata==8.5.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-11-16 14:48:53 -08:00
dependabot[bot]
01589cb8b0
Bump importlib-resources from 6.4.0 to 6.4.5 (#2394)
* Bump importlib-resources from 6.4.0 to 6.4.5

Bumps [importlib-resources](https://github.com/python/importlib_resources) from 6.4.0 to 6.4.5.
- [Release notes](https://github.com/python/importlib_resources/releases)
- [Changelog](https://github.com/python/importlib_resources/blob/main/NEWS.rst)
- [Commits](https://github.com/python/importlib_resources/compare/v6.4.0...v6.4.5)

---
updated-dependencies:
- dependency-name: importlib-resources
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update importlib-resources==6.4.5

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-11-16 14:48:10 -08:00
dependabot[bot]
f3a2c02e96
Bump certifi from 2024.7.4 to 2024.8.30 (#2391)
* Bump certifi from 2024.7.4 to 2024.8.30

Bumps [certifi](https://github.com/certifi/python-certifi) from 2024.7.4 to 2024.8.30.
- [Commits](https://github.com/certifi/python-certifi/compare/2024.07.04...2024.08.30)

---
updated-dependencies:
- dependency-name: certifi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update certifi==2024.8.30

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-11-16 14:47:58 -08:00
dependabot[bot]
d3f7eef84f
Bump pyparsing from 3.1.2 to 3.1.4 (#2388)
* Bump pyparsing from 3.1.2 to 3.1.4

Bumps [pyparsing](https://github.com/pyparsing/pyparsing) from 3.1.2 to 3.1.4.
- [Release notes](https://github.com/pyparsing/pyparsing/releases)
- [Changelog](https://github.com/pyparsing/pyparsing/blob/master/CHANGES)
- [Commits](https://github.com/pyparsing/pyparsing/compare/pyparsing_3.1.2...3.1.4)

---
updated-dependencies:
- dependency-name: pyparsing
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update pyparsing==3.1.4

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-11-16 14:47:48 -08:00
dependabot[bot]
2f3d24a0e7
Bump simplejson from 3.19.2 to 3.19.3 (#2379)
* Bump simplejson from 3.19.2 to 3.19.3

Bumps [simplejson](https://github.com/simplejson/simplejson) from 3.19.2 to 3.19.3.
- [Release notes](https://github.com/simplejson/simplejson/releases)
- [Changelog](https://github.com/simplejson/simplejson/blob/master/CHANGES.txt)
- [Commits](https://github.com/simplejson/simplejson/compare/v3.19.2...v3.19.3)

---
updated-dependencies:
- dependency-name: simplejson
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update simplejson==3.19.3

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-11-16 14:47:38 -08:00
JonnyWong16
2d3271376b
Remove unused cache_image function
Fixes #2426
2024-11-02 11:13:37 -07:00
JonnyWong16
940c2ae6cd
v2.14.6 2024-10-12 16:55:38 -07:00
JonnyWong16
1cdfd5f30a
Refactor scroller code 2024-09-28 14:30:40 -07:00
JonnyWong16
e3f4851883
Make recent rows touch scrollable 2024-09-28 13:17:09 -07:00
JonnyWong16
1353247b55
Allow formatting newsletter date parameters 2024-09-25 15:11:21 -07:00
JonnyWong16
3cf6560de3
Support apscheduler cron expressions 2024-09-25 11:54:55 -07:00
JonnyWong16
9ca8d59372
Fix auto-update not running 2024-09-23 16:52:17 -07:00
JonnyWong16
921a3a0af9
Round human duration to nearest significant base 2024-09-22 18:03:28 -07:00
JonnyWong16
3bb53f480e
Change snap package to cryptography 2024-09-21 14:17:08 -07:00
JonnyWong16
6979a4025f
v2.14.5 2024-09-20 20:29:15 -07:00
dependabot[bot]
cc1a325eac
Bump plexapi from 4.15.15 to 4.15.16 (#2383)
* Bump plexapi from 4.15.15 to 4.15.16

Bumps [plexapi](https://github.com/pkkid/python-plexapi) from 4.15.15 to 4.15.16.
- [Release notes](https://github.com/pkkid/python-plexapi/releases)
- [Commits](https://github.com/pkkid/python-plexapi/compare/4.15.15...4.15.16)

---
updated-dependencies:
- dependency-name: plexapi
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update plexapi==4.15.16

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-09-20 20:24:00 -07:00
JonnyWong16
de697cb2ca
Add 2k resolution override 2024-09-15 14:27:05 -07:00
JonnyWong16
596cf57d61
Do no initialize db connection in ActivityProcessor 2024-09-15 14:26:45 -07:00
JonnyWong16
ac32297160
Close database connection on garbage collection 2024-09-04 13:28:54 -07:00
JonnyWong16
330b8a3a82
Encode ntfy payload 2024-08-31 16:10:00 -07:00
JonnyWong16
5cf39cb097
Add Docker image labels 2024-08-17 11:27:17 -07:00
JonnyWong16
14c9c7a393
Replace PyCryptodome with Cryptography 2024-08-16 20:40:31 -07:00
JonnyWong16
cf8fb2e65d
Catch exception trying to remove PID file 2024-08-16 19:32:53 -07:00
JonnyWong16
623a9f2919
v2.14.4 2024-08-10 19:41:23 -07:00
JonnyWong16
3fb46a9ab7
Update workflow snapcraft actions branch 2024-08-10 19:30:12 -07:00
Teodor-Stelian Baltaretu
cfd81684b7
Removed deprecated getdefaultlocale (#2345) (#2364)
* Removed deprecated getdefaultlocale (#2345)

* Added Special Case For Windows (#2345)

* Refactored the changes into a cleaner code with comments (#2345)

* Changed the encoding method used and the selection of language

* Removed hardcoded encoding in Windows handling
2024-08-10 19:24:06 -07:00
dependabot[bot]
fb4f0046f3
Bump pyopenssl from 24.1.0 to 24.2.1 (#2368)
Bumps [pyopenssl](https://github.com/pyca/pyopenssl) from 24.1.0 to 24.2.1.
- [Changelog](https://github.com/pyca/pyopenssl/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/pyopenssl/compare/24.1.0...24.2.1)

---
updated-dependencies:
- dependency-name: pyopenssl
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2024-08-10 19:20:21 -07:00
dependabot[bot]
7d4efac75d
Bump tokenize-rt from 5.2.0 to 6.0.0 (#2376)
* Bump tokenize-rt from 5.2.0 to 6.0.0

Bumps [tokenize-rt](https://github.com/asottile/tokenize-rt) from 5.2.0 to 6.0.0.
- [Commits](https://github.com/asottile/tokenize-rt/compare/v5.2.0...v6.0.0)

---
updated-dependencies:
- dependency-name: tokenize-rt
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update tokenize-rt==6.0.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-08-10 19:19:28 -07:00
dependabot[bot]
509d18801b
Bump cloudinary from 1.40.0 to 1.41.0 (#2375)
* Bump cloudinary from 1.40.0 to 1.41.0

Bumps [cloudinary](https://github.com/cloudinary/pycloudinary) from 1.40.0 to 1.41.0.
- [Release notes](https://github.com/cloudinary/pycloudinary/releases)
- [Changelog](https://github.com/cloudinary/pycloudinary/blob/master/CHANGELOG.md)
- [Commits](https://github.com/cloudinary/pycloudinary/compare/1.40.0...1.41.0)

---
updated-dependencies:
- dependency-name: cloudinary
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update cloudinary==1.41.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-08-10 19:18:37 -07:00
dependabot[bot]
da501df846
Bump pyjwt from 2.8.0 to 2.9.0 (#2374)
* Bump pyjwt from 2.8.0 to 2.9.0

Bumps [pyjwt](https://github.com/jpadilla/pyjwt) from 2.8.0 to 2.9.0.
- [Release notes](https://github.com/jpadilla/pyjwt/releases)
- [Changelog](https://github.com/jpadilla/pyjwt/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/jpadilla/pyjwt/compare/2.8.0...2.9.0)

---
updated-dependencies:
- dependency-name: pyjwt
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update pyjwt==2.9.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-08-10 19:18:24 -07:00
dependabot[bot]
43cb027592
Bump tempora from 5.6.0 to 5.7.0 (#2371)
* Bump tempora from 5.6.0 to 5.7.0

Bumps [tempora](https://github.com/jaraco/tempora) from 5.6.0 to 5.7.0.
- [Release notes](https://github.com/jaraco/tempora/releases)
- [Changelog](https://github.com/jaraco/tempora/blob/main/NEWS.rst)
- [Commits](https://github.com/jaraco/tempora/compare/v5.6.0...v5.7.0)

---
updated-dependencies:
- dependency-name: tempora
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update tempora==5.7.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-08-10 19:16:20 -07:00
dependabot[bot]
2e6f541ec2
Bump importlib-metadata from 8.0.0 to 8.2.0 (#2370)
* Bump importlib-metadata from 8.0.0 to 8.2.0

Bumps [importlib-metadata](https://github.com/python/importlib_metadata) from 8.0.0 to 8.2.0.
- [Release notes](https://github.com/python/importlib_metadata/releases)
- [Changelog](https://github.com/python/importlib_metadata/blob/main/NEWS.rst)
- [Commits](https://github.com/python/importlib_metadata/compare/v8.0.0...v8.2.0)

---
updated-dependencies:
- dependency-name: importlib-metadata
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update importlib-metadata==8.2.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-08-10 19:16:03 -07:00
JonnyWong16
822d5a452c
Capitalize macOS platform 2024-08-10 19:13:56 -07:00
dependabot[bot]
7696d031d3
Bump certifi from 2024.6.2 to 2024.7.4 (#2361)
* Bump certifi from 2024.6.2 to 2024.7.4

Bumps [certifi](https://github.com/certifi/python-certifi) from 2024.6.2 to 2024.7.4.
- [Commits](https://github.com/certifi/python-certifi/compare/2024.06.02...2024.07.04)

---
updated-dependencies:
- dependency-name: certifi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update certifi==2024.7.4

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>
2024-07-06 11:05:03 -07:00
dependabot[bot]
50ced86ba5
Bump importlib-metadata from 7.1.0 to 8.0.0 (#2360)
* Bump importlib-metadata from 7.1.0 to 8.0.0

Bumps [importlib-metadata](https://github.com/python/importlib_metadata) from 7.1.0 to 8.0.0.
- [Release notes](https://github.com/python/importlib_metadata/releases)
- [Changelog](https://github.com/python/importlib_metadata/blob/main/NEWS.rst)
- [Commits](https://github.com/python/importlib_metadata/compare/v7.1.0...v8.0.0)

---
updated-dependencies:
- dependency-name: importlib-metadata
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update importlib-metadata==8.0.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>
2024-07-06 11:04:54 -07:00
JonnyWong16
e934d09eff
Update plexapi==4.15.15 requirements.txt 2024-07-06 11:03:05 -07:00
JonnyWong16
96c5cb216c
Update plexapi==4.15.15 2024-07-06 11:02:21 -07:00
Nate Harris
2ee2ab652c
[FEAT] Add ntfy as a notifier (#2356)
* - Add ntfy as a notifier

* - Fix media poster attachment in ntfy
2024-07-06 10:02:36 -07:00
JonnyWong16
193b82c54a
Update Slack notification attachment 2024-06-30 14:15:11 -07:00
JonnyWong16
7d00383d1c
v2.14.3 2024-06-19 19:14:20 -07:00
dependabot[bot]
6f84ce8048
Bump docker/build-push-action from 5 to 6 (#2354)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5 to 6.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v5...v6)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-19 00:06:18 -07:00
dependabot[bot]
709db66b10
Bump pyinstaller from 6.6.0 to 6.8.0 (#2346)
Bumps [pyinstaller](https://github.com/pyinstaller/pyinstaller) from 6.6.0 to 6.8.0.
- [Release notes](https://github.com/pyinstaller/pyinstaller/releases)
- [Changelog](https://github.com/pyinstaller/pyinstaller/blob/develop/doc/CHANGES.rst)
- [Commits](https://github.com/pyinstaller/pyinstaller/compare/v6.6.0...v6.8.0)

---
updated-dependencies:
- dependency-name: pyinstaller
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2024-06-19 00:06:02 -07:00
dependabot[bot]
2f1607b96b
Bump packaging from 24.0 to 24.1 (#2347)
* Bump packaging from 24.0 to 24.1

Bumps [packaging](https://github.com/pypa/packaging) from 24.0 to 24.1.
- [Release notes](https://github.com/pypa/packaging/releases)
- [Changelog](https://github.com/pypa/packaging/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pypa/packaging/compare/24.0...24.1)

---
updated-dependencies:
- dependency-name: packaging
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update packaging==24.1

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-06-19 00:04:34 -07:00
dependabot[bot]
28ad2716ba
Bump pyobjc-framework-cocoa from 10.2 to 10.3.1 (#2348)
Bumps [pyobjc-framework-cocoa](https://github.com/ronaldoussoren/pyobjc) from 10.2 to 10.3.1.
- [Release notes](https://github.com/ronaldoussoren/pyobjc/releases)
- [Changelog](https://github.com/ronaldoussoren/pyobjc/blob/master/docs/changelog.rst)
- [Commits](https://github.com/ronaldoussoren/pyobjc/compare/v10.2...v10.3.1)

---
updated-dependencies:
- dependency-name: pyobjc-framework-cocoa
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-06-19 00:04:15 -07:00
dependabot[bot]
f1a8164b94
Bump pyobjc-core from 10.2 to 10.3.1 (#2349)
Bumps [pyobjc-core](https://github.com/ronaldoussoren/pyobjc) from 10.2 to 10.3.1.
- [Release notes](https://github.com/ronaldoussoren/pyobjc/releases)
- [Changelog](https://github.com/ronaldoussoren/pyobjc/blob/master/docs/changelog.rst)
- [Commits](https://github.com/ronaldoussoren/pyobjc/compare/v10.2...v10.3.1)

---
updated-dependencies:
- dependency-name: pyobjc-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2024-06-19 00:03:23 -07:00
dependabot[bot]
3bc94cad6c
Bump tempora from 5.5.1 to 5.6.0 (#2355)
* Bump tempora from 5.5.1 to 5.6.0

Bumps [tempora](https://github.com/jaraco/tempora) from 5.5.1 to 5.6.0.
- [Release notes](https://github.com/jaraco/tempora/releases)
- [Changelog](https://github.com/jaraco/tempora/blob/main/NEWS.rst)
- [Commits](https://github.com/jaraco/tempora/compare/v5.5.1...v5.6.0)

---
updated-dependencies:
- dependency-name: tempora
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update tempora==5.6.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-06-19 00:02:54 -07:00
dependabot[bot]
a528f052b9
Bump cherrypy from 18.9.0 to 18.10.0 (#2353)
* Bump cherrypy from 18.9.0 to 18.10.0

Bumps [cherrypy](https://github.com/cherrypy/cherrypy) from 18.9.0 to 18.10.0.
- [Changelog](https://github.com/cherrypy/cherrypy/blob/main/CHANGES.rst)
- [Commits](https://github.com/cherrypy/cherrypy/compare/v18.9.0...v18.10.0)

---
updated-dependencies:
- dependency-name: cherrypy
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update cherrypy==18.10.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-06-19 00:02:35 -07:00
dependabot[bot]
5e977c044a
Bump zipp from 3.18.2 to 3.19.2 (#2343)
* Bump zipp from 3.18.2 to 3.19.2

Bumps [zipp](https://github.com/jaraco/zipp) from 3.18.2 to 3.19.2.
- [Release notes](https://github.com/jaraco/zipp/releases)
- [Changelog](https://github.com/jaraco/zipp/blob/main/NEWS.rst)
- [Commits](https://github.com/jaraco/zipp/compare/v3.18.2...v3.19.2)

---
updated-dependencies:
- dependency-name: zipp
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update zipp==3.19.2

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-06-19 00:02:06 -07:00
dependabot[bot]
afa25d45f6
Bump certifi from 2024.2.2 to 2024.6.2 (#2342)
* Bump certifi from 2024.2.2 to 2024.6.2

Bumps [certifi](https://github.com/certifi/python-certifi) from 2024.2.2 to 2024.6.2.
- [Commits](https://github.com/certifi/python-certifi/compare/2024.02.02...2024.06.02)

---
updated-dependencies:
- dependency-name: certifi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update certifi==2024.6.2

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-06-19 00:01:47 -07:00
dependabot[bot]
43e71d836a
Bump requests from 2.31.0 to 2.32.3 (#2338)
* Bump requests from 2.31.0 to 2.32.3

Bumps [requests](https://github.com/psf/requests) from 2.31.0 to 2.32.3.
- [Release notes](https://github.com/psf/requests/releases)
- [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md)
- [Commits](https://github.com/psf/requests/compare/v2.31.0...v2.32.3)

---
updated-dependencies:
- dependency-name: requests
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update requests==2.32.3

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-06-19 00:01:34 -07:00
JonnyWong16
55573d26ea
Ignore shutdown exception in cheroot 2024-06-18 19:19:18 -07:00
JonnyWong16
f1d44c051d
Fix loading of scheduled tasks when tasks are disabled 2024-06-06 21:39:33 -07:00
JonnyWong16
a3af8ed362
Fix width of use secure connection checkbox in wizard 2024-06-03 23:14:19 -07:00
JonnyWong16
912fd75a2f
Remove pms_is_remote setting
* Automatically determine if a server is local or remote
2024-06-03 23:14:19 -07:00
JonnyWong16
5778672dab
Fix webserver restarting 2024-05-25 18:41:17 -07:00
JonnyWong16
dcdf5a2992
Fix SQLite quotes for history date filters 2024-05-21 10:40:00 -07:00
JonnyWong16
73cfa8e0c0
Add git clean to reset git install 2024-05-18 14:29:53 -07:00
JonnyWong16
795d568df2
v2.14.2 2024-05-18 13:56:06 -07:00
JonnyWong16
8396a04ce8
Update plexapi==4.15.13 2024-05-18 13:52:52 -07:00
JonnyWong16
8419eee4b2
Catch exception when decoding server response message 2024-05-18 13:50:37 -07:00
dependabot[bot]
c505e26656
Bump zipp from 3.18.1 to 3.18.2 (#2326)
* Bump zipp from 3.18.1 to 3.18.2

Bumps [zipp](https://github.com/jaraco/zipp) from 3.18.1 to 3.18.2.
- [Release notes](https://github.com/jaraco/zipp/releases)
- [Changelog](https://github.com/jaraco/zipp/blob/main/NEWS.rst)
- [Commits](https://github.com/jaraco/zipp/compare/v3.18.1...v3.18.2)

---
updated-dependencies:
- dependency-name: zipp
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update zipp==3.18.2

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-05-18 11:17:26 -07:00
dependabot[bot]
37ffe68ce2
Bump mako from 1.3.3 to 1.3.5 (#2325)
* Bump mako from 1.3.3 to 1.3.5

Bumps [mako](https://github.com/sqlalchemy/mako) from 1.3.3 to 1.3.5.
- [Release notes](https://github.com/sqlalchemy/mako/releases)
- [Changelog](https://github.com/sqlalchemy/mako/blob/main/CHANGES)
- [Commits](https://github.com/sqlalchemy/mako/commits)

---
updated-dependencies:
- dependency-name: mako
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update mako==1.3.5

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-05-18 11:17:13 -07:00
dependabot[bot]
dc9e778111
Bump platformdirs from 4.2.1 to 4.2.2 (#2324)
* Bump platformdirs from 4.2.1 to 4.2.2

Bumps [platformdirs](https://github.com/platformdirs/platformdirs) from 4.2.1 to 4.2.2.
- [Release notes](https://github.com/platformdirs/platformdirs/releases)
- [Changelog](https://github.com/platformdirs/platformdirs/blob/main/CHANGES.rst)
- [Commits](https://github.com/platformdirs/platformdirs/compare/4.2.1...4.2.2)

---
updated-dependencies:
- dependency-name: platformdirs
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update platformdirs==4.2.2

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-05-18 11:17:01 -07:00
JonnyWong16
68bf1c70f7
Overwrite image provider export field for uploaded assets 2024-05-13 20:56:40 -07:00
JonnyWong16
ee0b4c0602
Add artProvider and thumbProvider to exporter fields 2024-05-12 22:38:05 -07:00
JonnyWong16
5c115dec68
Fix uploading MacOS installer release asset 2024-05-11 09:51:05 -07:00
JonnyWong16
1d77f32665
v2.14.1-beta 2024-05-11 09:33:49 -07:00
JonnyWong16
af01b8c6cc
Fix appending user ids 2024-05-09 22:43:57 -07:00
JonnyWong16
dd9d3b97a2
Update workflow joncloud/makensis-action@v4.1
[skip ci]
2024-05-09 22:41:07 -07:00
JonnyWong16
96c20ad893
Update cloudinary==1.40.0 2024-05-09 22:31:06 -07:00
dependabot[bot]
5e90f3bb31
Bump paho-mqtt from 2.0.0 to 2.1.0 (#2316)
* Bump paho-mqtt from 2.0.0 to 2.1.0

Bumps [paho-mqtt](https://github.com/eclipse/paho.mqtt.python) from 2.0.0 to 2.1.0.
- [Release notes](https://github.com/eclipse/paho.mqtt.python/releases)
- [Changelog](https://github.com/eclipse/paho.mqtt.python/blob/master/ChangeLog.txt)
- [Commits](https://github.com/eclipse/paho.mqtt.python/compare/v2.0.0...v2.1.0)

---
updated-dependencies:
- dependency-name: paho-mqtt
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update paho-mqtt==2.1.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-05-09 22:28:50 -07:00
dependabot[bot]
dab46249f2
Bump websocket-client from 1.7.0 to 1.8.0 (#2313)
* Bump websocket-client from 1.7.0 to 1.8.0

Bumps [websocket-client](https://github.com/websocket-client/websocket-client) from 1.7.0 to 1.8.0.
- [Release notes](https://github.com/websocket-client/websocket-client/releases)
- [Changelog](https://github.com/websocket-client/websocket-client/blob/master/ChangeLog)
- [Commits](https://github.com/websocket-client/websocket-client/compare/v1.7.0...v1.8.0)

---
updated-dependencies:
- dependency-name: websocket-client
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update websocket-client==1.8.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-05-09 22:28:14 -07:00
dependabot[bot]
5d0ba8b222
Bump platformdirs from 4.2.0 to 4.2.1 (#2312)
* Bump platformdirs from 4.2.0 to 4.2.1

Bumps [platformdirs](https://github.com/platformdirs/platformdirs) from 4.2.0 to 4.2.1.
- [Release notes](https://github.com/platformdirs/platformdirs/releases)
- [Changelog](https://github.com/platformdirs/platformdirs/blob/main/CHANGES.rst)
- [Commits](https://github.com/platformdirs/platformdirs/compare/4.2.0...4.2.1)

---
updated-dependencies:
- dependency-name: platformdirs
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update platformdirs==4.2.1

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-05-09 22:27:59 -07:00
dependabot[bot]
3e8a5663a3
Bump plexapi from 4.15.11 to 4.15.12 (#2311)
* Bump plexapi from 4.15.11 to 4.15.12

Bumps [plexapi](https://github.com/pkkid/python-plexapi) from 4.15.11 to 4.15.12.
- [Release notes](https://github.com/pkkid/python-plexapi/releases)
- [Commits](https://github.com/pkkid/python-plexapi/compare/4.15.11...4.15.12)

---
updated-dependencies:
- dependency-name: plexapi
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update plexapi==4.15.12

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-05-09 22:27:15 -07:00
dependabot[bot]
6414a0ba12
Bump cheroot from 10.0.0 to 10.0.1 (#2310)
* Bump cheroot from 10.0.0 to 10.0.1

Bumps [cheroot](https://github.com/cherrypy/cheroot) from 10.0.0 to 10.0.1.
- [Release notes](https://github.com/cherrypy/cheroot/releases)
- [Changelog](https://github.com/cherrypy/cheroot/blob/main/CHANGES.rst)
- [Commits](https://github.com/cherrypy/cheroot/compare/v10.0.0...v10.0.1)

---
updated-dependencies:
- dependency-name: cheroot
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update cheroot==10.0.1

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-05-09 22:27:04 -07:00
dependabot[bot]
bcac5b7897
Bump cloudinary from 1.39.1 to 1.40.0 (#2308)
Bumps [cloudinary](https://github.com/cloudinary/pycloudinary) from 1.39.1 to 1.40.0.
- [Release notes](https://github.com/cloudinary/pycloudinary/releases)
- [Changelog](https://github.com/cloudinary/pycloudinary/blob/master/CHANGELOG.md)
- [Commits](https://github.com/cloudinary/pycloudinary/compare/1.39.1...1.40.0)

---
updated-dependencies:
- dependency-name: cloudinary
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2024-05-09 22:20:53 -07:00
Tom Niget
de3393d62b
Remove Python 2 handling code (#2098)
* Remove Python 2 update modal

* Remove Python 2 handling code

* Remove backports dependencies

* Remove uses of future and __future__

* Fix import

* Remove requirements

* Update lib folder

* Clean up imports and blank lines

---------

Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>
2024-05-09 22:18:08 -07:00
JonnyWong16
dcec1f6f5f
Update snapcraft git data folder 2024-05-09 21:55:26 -07:00
JonnyWong16
65905a6647
Fix escaping regex string 2024-05-09 21:52:39 -07:00
JonnyWong16
5de2cf85c3
Workaround users remaining in friends list without shared libraries 2024-05-09 20:49:06 -07:00
JonnyWong16
a7660d5c03
v2.14.0-beta 2024-04-18 22:41:23 -07:00
JonnyWong16
5e02db897f
Remove anonymous redirect 2024-04-18 13:12:56 -07:00
JonnyWong16
52e2950aa9
Add no-referrer policy 2024-04-18 13:12:47 -07:00
dependabot[bot]
acf1b2384a
Bump pyinstaller from 6.5.0 to 6.6.0 (#2307)
Bumps [pyinstaller](https://github.com/pyinstaller/pyinstaller) from 6.5.0 to 6.6.0.
- [Release notes](https://github.com/pyinstaller/pyinstaller/releases)
- [Changelog](https://github.com/pyinstaller/pyinstaller/blob/develop/doc/CHANGES.rst)
- [Commits](https://github.com/pyinstaller/pyinstaller/compare/v6.5.0...v6.6.0)

---
updated-dependencies:
- dependency-name: pyinstaller
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-18 13:03:30 -07:00
dependabot[bot]
07166ae6b2
Bump idna from 3.6 to 3.7 (#2304)
* Bump idna from 3.6 to 3.7

Bumps [idna](https://github.com/kjd/idna) from 3.6 to 3.7.
- [Release notes](https://github.com/kjd/idna/releases)
- [Changelog](https://github.com/kjd/idna/blob/master/HISTORY.rst)
- [Commits](https://github.com/kjd/idna/compare/v3.6...v3.7)

---
updated-dependencies:
- dependency-name: idna
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update idna==3.7

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-04-18 13:01:25 -07:00
dependabot[bot]
80984bd296
Bump mako from 1.3.2 to 1.3.3 (#2303)
* Bump mako from 1.3.2 to 1.3.3

Bumps [mako](https://github.com/sqlalchemy/mako) from 1.3.2 to 1.3.3.
- [Release notes](https://github.com/sqlalchemy/mako/releases)
- [Changelog](https://github.com/sqlalchemy/mako/blob/main/CHANGES)
- [Commits](https://github.com/sqlalchemy/mako/commits)

---
updated-dependencies:
- dependency-name: mako
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update mako==1.3.3

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-04-18 13:01:09 -07:00
JonnyWong16
6a9e532805
Change path join for session metadata cache 2024-04-07 15:58:28 -07:00
JonnyWong16
2258a88168
Use metadata from session for stale live tv sessions 2024-04-07 15:58:01 -07:00
JonnyWong16
282810e9ca
Increase Remote app PBKDF2 iterations to 600,000 and SHA256 hash
OWASP Cheat Sheet recommends 600,000 iterations for SHA256.

https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2
2024-04-04 22:57:17 -07:00
JonnyWong16
4582ff4a56
Fix grouping live tv history 2024-04-01 21:47:33 -07:00
JonnyWong16
10e62ca42d
Fix stats on live tv info pages 2024-04-01 19:02:52 -07:00
JonnyWong16
e3245bc126
Add live tv channel keys notification parameters 2024-04-01 19:02:52 -07:00
JonnyWong16
3dc6d226f8
Add live tv channel keys to database 2024-04-01 19:02:52 -07:00
JonnyWong16
177962d626
Change live tv text to channel title 2024-04-01 19:02:52 -07:00
JonnyWong16
5e4f656155
Add space to session start live tv log message 2024-04-01 19:02:52 -07:00
JonnyWong16
980fc8a43f
Fix live tv history watched status 2024-04-01 19:02:52 -07:00
JonnyWong16
458e89b8d7
Get live TV metadata from epg 2024-04-01 19:02:52 -07:00
JonnyWong16
b8185afdf9
Temporary fix for live tv session missing duration 2024-03-31 17:09:35 -07:00
JonnyWong16
85519b1b45
Update plexapi==4.15.11 2024-03-31 16:11:00 -07:00
dependabot[bot]
653a6d5c12
Bump pyinstaller from 6.4.0 to 6.5.0 (#2298)
Bumps [pyinstaller](https://github.com/pyinstaller/pyinstaller) from 6.4.0 to 6.5.0.
- [Release notes](https://github.com/pyinstaller/pyinstaller/releases)
- [Changelog](https://github.com/pyinstaller/pyinstaller/blob/develop/doc/CHANGES.rst)
- [Commits](https://github.com/pyinstaller/pyinstaller/compare/v6.4.0...v6.5.0)

---
updated-dependencies:
- dependency-name: pyinstaller
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-30 15:46:44 -07:00
dependabot[bot]
b2a8601c70
Bump pyobjc-framework-cocoa from 10.1 to 10.2 (#2299)
Bumps [pyobjc-framework-cocoa](https://github.com/ronaldoussoren/pyobjc) from 10.1 to 10.2.
- [Release notes](https://github.com/ronaldoussoren/pyobjc/releases)
- [Changelog](https://github.com/ronaldoussoren/pyobjc/blob/master/docs/changelog.rst)
- [Commits](https://github.com/ronaldoussoren/pyobjc/compare/v10.1...v10.2)

---
updated-dependencies:
- dependency-name: pyobjc-framework-cocoa
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2024-03-30 15:30:09 -07:00
dependabot[bot]
938b48c5aa
Bump pyobjc-core from 10.1 to 10.2 (#2294)
Bumps [pyobjc-core](https://github.com/ronaldoussoren/pyobjc) from 10.1 to 10.2.
- [Release notes](https://github.com/ronaldoussoren/pyobjc/releases)
- [Changelog](https://github.com/ronaldoussoren/pyobjc/blob/master/docs/changelog.rst)
- [Commits](https://github.com/ronaldoussoren/pyobjc/compare/v10.1...v10.2)

---
updated-dependencies:
- dependency-name: pyobjc-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2024-03-30 15:29:37 -07:00
dependabot[bot]
cdfffda877
Bump pyopenssl from 24.0.0 to 24.1.0 (#2290)
Bumps [pyopenssl](https://github.com/pyca/pyopenssl) from 24.0.0 to 24.1.0.
- [Changelog](https://github.com/pyca/pyopenssl/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/pyopenssl/compare/24.0.0...24.1.0)

---
updated-dependencies:
- dependency-name: pyopenssl
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2024-03-30 15:29:25 -07:00
dependabot[bot]
b6eca379fa
Bump idna from 3.4 to 3.6 (#2300)
* Bump idna from 3.4 to 3.6

Bumps [idna](https://github.com/kjd/idna) from 3.4 to 3.6.
- [Changelog](https://github.com/kjd/idna/blob/master/HISTORY.rst)
- [Commits](https://github.com/kjd/idna/compare/v3.4...v3.6)

---
updated-dependencies:
- dependency-name: idna
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update idna==3.6

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-03-30 15:29:14 -07:00
dependabot[bot]
aa2006c2cc
Bump markupsafe from 2.1.3 to 2.1.5 (#2297)
Bumps [markupsafe](https://github.com/pallets/markupsafe) from 2.1.3 to 2.1.5.
- [Release notes](https://github.com/pallets/markupsafe/releases)
- [Changelog](https://github.com/pallets/markupsafe/blob/main/CHANGES.rst)
- [Commits](https://github.com/pallets/markupsafe/compare/2.1.3...2.1.5)

---
updated-dependencies:
- dependency-name: markupsafe
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2024-03-30 15:29:02 -07:00
dependabot[bot]
26358427ce
Bump pyparsing from 3.1.1 to 3.1.2 (#2296)
* Bump pyparsing from 3.1.1 to 3.1.2

Bumps [pyparsing](https://github.com/pyparsing/pyparsing) from 3.1.1 to 3.1.2.
- [Release notes](https://github.com/pyparsing/pyparsing/releases)
- [Changelog](https://github.com/pyparsing/pyparsing/blob/master/CHANGES)
- [Commits](https://github.com/pyparsing/pyparsing/compare/3.1.1...pyparsing_3.1.2)

---
updated-dependencies:
- dependency-name: pyparsing
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update pyparsing==3.1.2

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-03-30 15:28:45 -07:00
dependabot[bot]
1d96e0f859
Bump tzdata from 2023.3 to 2024.1 (#2295)
* Bump tzdata from 2023.3 to 2024.1

Bumps [tzdata](https://github.com/python/tzdata) from 2023.3 to 2024.1.
- [Release notes](https://github.com/python/tzdata/releases)
- [Changelog](https://github.com/python/tzdata/blob/master/NEWS.md)
- [Commits](https://github.com/python/tzdata/compare/2023.3...2024.1)

---
updated-dependencies:
- dependency-name: tzdata
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update tzdata==2024.1

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-03-30 15:28:14 -07:00
dependabot[bot]
0d1d2a3e6b
Bump requests-oauthlib from 1.3.1 to 2.0.0 (#2293)
* Bump requests-oauthlib from 1.3.1 to 2.0.0

Bumps [requests-oauthlib](https://github.com/requests/requests-oauthlib) from 1.3.1 to 2.0.0.
- [Release notes](https://github.com/requests/requests-oauthlib/releases)
- [Changelog](https://github.com/requests/requests-oauthlib/blob/master/HISTORY.rst)
- [Commits](https://github.com/requests/requests-oauthlib/compare/v1.3.1...v2.0.0)

---
updated-dependencies:
- dependency-name: requests-oauthlib
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update requests-oauthlib==2.0.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-03-30 15:28:02 -07:00
dependabot[bot]
452a4afdcf
Bump tempora from 5.5.0 to 5.5.1 (#2292)
Bumps [tempora](https://github.com/jaraco/tempora) from 5.5.0 to 5.5.1.
- [Release notes](https://github.com/jaraco/tempora/releases)
- [Changelog](https://github.com/jaraco/tempora/blob/main/NEWS.rst)
- [Commits](https://github.com/jaraco/tempora/compare/v5.5.0...v5.5.1)

---
updated-dependencies:
- dependency-name: tempora
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2024-03-30 15:27:42 -07:00
dependabot[bot]
bfc4f66739
Bump python-dateutil from 2.8.2 to 2.9.0.post0 (#2291)
Bumps [python-dateutil](https://github.com/dateutil/dateutil) from 2.8.2 to 2.9.0.post0.
- [Release notes](https://github.com/dateutil/dateutil/releases)
- [Changelog](https://github.com/dateutil/dateutil/blob/master/NEWS)
- [Commits](https://github.com/dateutil/dateutil/compare/2.8.2...2.9.0.post0)

---
updated-dependencies:
- dependency-name: python-dateutil
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2024-03-30 15:27:31 -07:00
dependabot[bot]
f82aecb88c
Bump paho-mqtt from 1.6.1 to 2.0.0 (#2288)
* Bump paho-mqtt from 1.6.1 to 2.0.0

Bumps [paho-mqtt](https://github.com/eclipse/paho.mqtt.python) from 1.6.1 to 2.0.0.
- [Release notes](https://github.com/eclipse/paho.mqtt.python/releases)
- [Changelog](https://github.com/eclipse/paho.mqtt.python/blob/master/ChangeLog.txt)
- [Commits](https://github.com/eclipse/paho.mqtt.python/compare/v1.6.1...v2.0.0)

---
updated-dependencies:
- dependency-name: paho-mqtt
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update paho-mqtt==2.0.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-03-30 15:27:16 -07:00
dependabot[bot]
75a1750a4e
Bump cherrypy from 18.8.0 to 18.9.0 (#2289)
Bumps [cherrypy](https://github.com/cherrypy/cherrypy) from 18.8.0 to 18.9.0.
- [Changelog](https://github.com/cherrypy/cherrypy/blob/main/CHANGES.rst)
- [Commits](https://github.com/cherrypy/cherrypy/compare/v18.8.0...v18.9.0)

---
updated-dependencies:
- dependency-name: cherrypy
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2024-03-26 15:10:51 -07:00
JonnyWong16
51196a7fb1
Update cherrypy==18.9.0 2024-03-24 17:55:28 -07:00
JonnyWong16
2fc618c01f
Revert "Bump cherrypy from 18.8.0 to 18.9.0 (#2266)"
This reverts commit faef9a94c4.
2024-03-24 17:55:28 -07:00
JonnyWong16
fcd8ef11f4
Add before and after parameters to get_home_stats API
Closes #2231
2024-03-24 17:26:18 -07:00
JonnyWong16
c737161164
Add dovi notification parameters
Closes #2240
2024-03-24 16:37:46 -07:00
JonnyWong16
de3121cba9
Add Dolby Vision info to media info 2024-03-24 16:36:18 -07:00
JonnyWong16
9fe58a6d86
Add lan streams and wan streams notification parameters
Closes #2276
2024-03-24 16:09:41 -07:00
JonnyWong16
cfdb6975f0
Add platform version and product version notification parameters
Closes #2244
2024-03-24 16:09:21 -07:00
JonnyWong16
41693ee5f1
Remove synced items from UI 2024-03-24 15:46:57 -07:00
JonnyWong16
7a11b10947
Fix Cloudinary delete all limit of 1000 2024-03-24 15:44:43 -07:00
JonnyWong16
4430c14374
Fix incorrect reachability websocket event key 2024-03-24 15:31:14 -07:00
dependabot[bot]
e248c13c15
Bump importlib-metadata from 6.8.0 to 7.1.0 (#2286)
* Bump importlib-metadata from 6.8.0 to 7.1.0

Bumps [importlib-metadata](https://github.com/python/importlib_metadata) from 6.8.0 to 7.1.0.
- [Release notes](https://github.com/python/importlib_metadata/releases)
- [Changelog](https://github.com/python/importlib_metadata/blob/main/NEWS.rst)
- [Commits](https://github.com/python/importlib_metadata/compare/v6.8.0...v7.1.0)

---
updated-dependencies:
- dependency-name: importlib-metadata
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update importlib-metadata==7.1.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-03-24 15:29:52 -07:00
dependabot[bot]
b01b21ae05
Bump importlib-resources from 6.0.1 to 6.4.0 (#2285)
* Bump importlib-resources from 6.0.1 to 6.4.0

Bumps [importlib-resources](https://github.com/python/importlib_resources) from 6.0.1 to 6.4.0.
- [Release notes](https://github.com/python/importlib_resources/releases)
- [Changelog](https://github.com/python/importlib_resources/blob/main/NEWS.rst)
- [Commits](https://github.com/python/importlib_resources/compare/v6.0.1...v6.4.0)

---
updated-dependencies:
- dependency-name: importlib-resources
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update importlib-resources==6.4.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-03-24 15:27:55 -07:00
dependabot[bot]
6c6fa34ba4
Bump cloudinary from 1.34.0 to 1.39.1 (#2283)
* Bump cloudinary from 1.34.0 to 1.39.1

Bumps [cloudinary](https://github.com/cloudinary/pycloudinary) from 1.34.0 to 1.39.1.
- [Release notes](https://github.com/cloudinary/pycloudinary/releases)
- [Changelog](https://github.com/cloudinary/pycloudinary/blob/master/CHANGELOG.md)
- [Commits](https://github.com/cloudinary/pycloudinary/compare/1.34.0...1.39.1)

---
updated-dependencies:
- dependency-name: cloudinary
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update cloudinary==1.39.1

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-03-24 15:27:42 -07:00
dependabot[bot]
24fff60ed4
Bump zipp from 3.16.2 to 3.18.1 (#2281)
* Bump zipp from 3.16.2 to 3.18.1

Bumps [zipp](https://github.com/jaraco/zipp) from 3.16.2 to 3.18.1.
- [Release notes](https://github.com/jaraco/zipp/releases)
- [Changelog](https://github.com/jaraco/zipp/blob/main/NEWS.rst)
- [Commits](https://github.com/jaraco/zipp/compare/v3.16.2...v3.18.1)

---
updated-dependencies:
- dependency-name: zipp
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update zipp==3.18.1

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-03-24 15:27:03 -07:00
dependabot[bot]
4398dfa821
Bump packaging from 23.1 to 24.0 (#2274)
* Bump packaging from 23.1 to 24.0

Bumps [packaging](https://github.com/pypa/packaging) from 23.1 to 24.0.
- [Release notes](https://github.com/pypa/packaging/releases)
- [Changelog](https://github.com/pypa/packaging/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pypa/packaging/compare/23.1...24.0)

---
updated-dependencies:
- dependency-name: packaging
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update packaging==24.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-03-24 15:26:46 -07:00
dependabot[bot]
a0170a6f3d
Bump beautifulsoup4 from 4.12.2 to 4.12.3 (#2267)
* Bump beautifulsoup4 from 4.12.2 to 4.12.3

Bumps [beautifulsoup4](https://www.crummy.com/software/BeautifulSoup/bs4/) from 4.12.2 to 4.12.3.

---
updated-dependencies:
- dependency-name: beautifulsoup4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update beautifulsoup4==4.12.3

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-03-24 15:26:22 -07:00
dependabot[bot]
faef9a94c4
Bump cherrypy from 18.8.0 to 18.9.0 (#2266)
* Bump cherrypy from 18.8.0 to 18.9.0

Bumps [cherrypy](https://github.com/cherrypy/cherrypy) from 18.8.0 to 18.9.0.
- [Changelog](https://github.com/cherrypy/cherrypy/blob/main/CHANGES.rst)
- [Commits](https://github.com/cherrypy/cherrypy/compare/v18.8.0...v18.9.0)

---
updated-dependencies:
- dependency-name: cherrypy
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update cherrypy==18.9.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-03-24 15:25:44 -07:00
dependabot[bot]
cfefa928be
Bump dnspython from 2.4.2 to 2.6.1 (#2264)
* Bump dnspython from 2.4.2 to 2.6.1

Bumps [dnspython](https://github.com/rthalley/dnspython) from 2.4.2 to 2.6.1.
- [Release notes](https://github.com/rthalley/dnspython/releases)
- [Changelog](https://github.com/rthalley/dnspython/blob/main/doc/whatsnew.rst)
- [Commits](https://github.com/rthalley/dnspython/compare/v2.4.2...v2.6.1)

---
updated-dependencies:
- dependency-name: dnspython
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update dnspython==2.6.1

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-03-24 15:25:23 -07:00
dependabot[bot]
aca7e72715
Bump distro from 1.8.0 to 1.9.0 (#2262)
* Bump distro from 1.8.0 to 1.9.0

Bumps [distro](https://github.com/python-distro/distro) from 1.8.0 to 1.9.0.
- [Release notes](https://github.com/python-distro/distro/releases)
- [Changelog](https://github.com/python-distro/distro/blob/master/CHANGELOG.md)
- [Commits](https://github.com/python-distro/distro/compare/v1.8.0...v1.9.0)

---
updated-dependencies:
- dependency-name: distro
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update distro==1.9.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-03-24 15:24:31 -07:00
dependabot[bot]
b7836102a9
Bump backports-functools-lru-cache from 1.6.6 to 2.0.0 (#2263)
* Bump backports-functools-lru-cache from 1.6.6 to 2.0.0

Bumps [backports-functools-lru-cache](https://github.com/jaraco/backports.functools_lru_cache) from 1.6.6 to 2.0.0.
- [Release notes](https://github.com/jaraco/backports.functools_lru_cache/releases)
- [Changelog](https://github.com/jaraco/backports.functools_lru_cache/blob/main/NEWS.rst)
- [Commits](https://github.com/jaraco/backports.functools_lru_cache/compare/v1.6.6...v2.0.0)

---
updated-dependencies:
- dependency-name: backports-functools-lru-cache
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update backports-functools-lru-cache==2.0.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-03-24 15:24:05 -07:00
dependabot[bot]
13eb0fd6db
Bump platformdirs from 3.11.0 to 4.2.0 (#2258)
* Bump platformdirs from 3.11.0 to 4.2.0

Bumps [platformdirs](https://github.com/platformdirs/platformdirs) from 3.11.0 to 4.2.0.
- [Release notes](https://github.com/platformdirs/platformdirs/releases)
- [Changelog](https://github.com/platformdirs/platformdirs/blob/main/CHANGES.rst)
- [Commits](https://github.com/platformdirs/platformdirs/compare/3.11.0...4.2.0)

---
updated-dependencies:
- dependency-name: platformdirs
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update platformdirs==4.2.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-03-24 15:23:44 -07:00
dependabot[bot]
a4cfc6323d
Bump certifi from 2023.7.22 to 2024.2.2 (#2257)
* Bump certifi from 2023.7.22 to 2024.2.2

Bumps [certifi](https://github.com/certifi/python-certifi) from 2023.7.22 to 2024.2.2.
- [Commits](https://github.com/certifi/python-certifi/compare/2023.07.22...2024.02.02)

---
updated-dependencies:
- dependency-name: certifi
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update certifi==2024.2.2

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-03-24 15:23:15 -07:00
dependabot[bot]
4468c3e4af
Bump mako from 1.2.4 to 1.3.2 (#2256)
* Bump mako from 1.2.4 to 1.3.2

Bumps [mako](https://github.com/sqlalchemy/mako) from 1.2.4 to 1.3.2.
- [Release notes](https://github.com/sqlalchemy/mako/releases)
- [Changelog](https://github.com/sqlalchemy/mako/blob/main/CHANGES)
- [Commits](https://github.com/sqlalchemy/mako/commits)

---
updated-dependencies:
- dependency-name: mako
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update mako==1.3.2

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-03-24 15:22:16 -07:00
dependabot[bot]
52819f7da6
Bump pytz from 2023.3 to 2024.1 (#2254)
* Bump pytz from 2023.3 to 2024.1

Bumps [pytz](https://github.com/stub42/pytz) from 2023.3 to 2024.1.
- [Release notes](https://github.com/stub42/pytz/releases)
- [Commits](https://github.com/stub42/pytz/compare/release_2023.3...release_2024.1)

---
updated-dependencies:
- dependency-name: pytz
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update pytz==2024.1

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-03-24 15:22:05 -07:00
dependabot[bot]
24b6d37bbe
Bump websocket-client from 1.6.2 to 1.7.0 (#2207)
* Bump websocket-client from 1.6.2 to 1.7.0

Bumps [websocket-client](https://github.com/websocket-client/websocket-client) from 1.6.2 to 1.7.0.
- [Release notes](https://github.com/websocket-client/websocket-client/releases)
- [Changelog](https://github.com/websocket-client/websocket-client/blob/master/ChangeLog)
- [Commits](https://github.com/websocket-client/websocket-client/compare/v1.6.2...v1.7.0)

---
updated-dependencies:
- dependency-name: websocket-client
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update websocket-client==1.7.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-03-24 15:21:51 -07:00
dependabot[bot]
dbffb519f5
Bump bleach from 6.0.0 to 6.1.0 (#2177)
* Bump bleach from 6.0.0 to 6.1.0

Bumps [bleach](https://github.com/mozilla/bleach) from 6.0.0 to 6.1.0.
- [Changelog](https://github.com/mozilla/bleach/blob/main/CHANGES)
- [Commits](https://github.com/mozilla/bleach/compare/v6.0.0...v6.1.0)

---
updated-dependencies:
- dependency-name: bleach
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update bleach==6.1.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-03-24 15:21:33 -07:00
dependabot[bot]
e307796475
Bump simplejson from 3.19.1 to 3.19.2 (#2176)
* Bump simplejson from 3.19.1 to 3.19.2

Bumps [simplejson](https://github.com/simplejson/simplejson) from 3.19.1 to 3.19.2.
- [Release notes](https://github.com/simplejson/simplejson/releases)
- [Changelog](https://github.com/simplejson/simplejson/blob/master/CHANGES.txt)
- [Commits](https://github.com/simplejson/simplejson/compare/v3.19.1...v3.19.2)

---
updated-dependencies:
- dependency-name: simplejson
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update simplejson==3.19.2

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-03-24 15:21:17 -07:00
dependabot[bot]
c1b8be0227
Bump arrow from 1.2.3 to 1.3.0 (#2172)
* Bump arrow from 1.2.3 to 1.3.0

Bumps [arrow](https://github.com/arrow-py/arrow) from 1.2.3 to 1.3.0.
- [Release notes](https://github.com/arrow-py/arrow/releases)
- [Changelog](https://github.com/arrow-py/arrow/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/arrow-py/arrow/compare/1.2.3...1.3.0)

---
updated-dependencies:
- dependency-name: arrow
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update arrow==1.3.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-03-24 15:20:52 -07:00
dependabot[bot]
d3504e8a3c
Bump softprops/action-gh-release from 1 to 2 (#2273)
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 1 to 2.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](https://github.com/softprops/action-gh-release/compare/v1...v2)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2024-03-24 14:53:44 -07:00
dependabot[bot]
344f19c9d6
Bump actions/cache from 3 to 4 (#2261)
Bumps [actions/cache](https://github.com/actions/cache) from 3 to 4.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2024-03-24 14:53:10 -07:00
JonnyWong16
901c484f89
Include deleted usernames in log filter 2024-03-14 12:15:02 -07:00
JonnyWong16
149c7fa7a0
Bump snap to core22 2024-03-02 17:03:56 -08:00
JonnyWong16
b73a2e9acc
Update workflow statuses 2024-03-02 16:29:01 -08:00
JonnyWong16
b48e9f4074
Update installer workflow for universal macOS binary
Bump python to 3.11
2024-03-02 16:29:01 -08:00
dependabot[bot]
36fbff5503
Bump actions/download-artifact from 3 to 4 (#2223)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 3 to 4.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2024-03-02 14:35:56 -08:00
dependabot[bot]
355be99512
Bump actions/upload-artifact from 3 to 4 (#2224)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2024-03-02 14:35:43 -08:00
dependabot[bot]
a397b90c23
Bump github/codeql-action from 2 to 3 (#2222)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2 to 3.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2024-03-02 14:35:11 -08:00
dependabot[bot]
6a19beb476
Bump actions/stale from 8 to 9 (#2214)
Bumps [actions/stale](https://github.com/actions/stale) from 8 to 9.
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/stale/compare/v8...v9)

---
updated-dependencies:
- dependency-name: actions/stale
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2024-03-02 14:34:58 -08:00
dependabot[bot]
71e6ea00c9
Bump actions/setup-python from 4 to 5 (#2211)
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 5.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2024-03-02 14:34:18 -08:00
dependabot[bot]
7e4f7c0c56
Bump dessant/label-actions from 3 to 4 (#2195)
Bumps [dessant/label-actions](https://github.com/dessant/label-actions) from 3 to 4.
- [Release notes](https://github.com/dessant/label-actions/releases)
- [Changelog](https://github.com/dessant/label-actions/blob/main/CHANGELOG.md)
- [Commits](https://github.com/dessant/label-actions/compare/v3...v4)

---
updated-dependencies:
- dependency-name: dessant/label-actions
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2024-03-02 14:33:52 -08:00
JonnyWong16
36ed4bfa3c
Set pyinstaller --contents-directory
Revert back to old one-dir behaviour

Ref.: pyinstaller/pyinstaller#7713
2024-03-02 14:31:44 -08:00
JonnyWong16
7ee2c59075
Add playlist sourceURI to exporter 2024-03-02 14:10:31 -08:00
JonnyWong16
82089fdb7b
Add track genres to exporter 2024-03-02 14:10:31 -08:00
JonnyWong16
cc070cfc6b
Add slug attribute to exporter 2024-03-02 14:10:31 -08:00
dependabot[bot]
78a7a48587
Bump pyinstaller from 5.13.0 to 6.4.0 (#2253)
Bumps [pyinstaller](https://github.com/pyinstaller/pyinstaller) from 5.13.0 to 6.4.0.
- [Release notes](https://github.com/pyinstaller/pyinstaller/releases)
- [Changelog](https://github.com/pyinstaller/pyinstaller/blob/develop/doc/CHANGES.rst)
- [Commits](https://github.com/pyinstaller/pyinstaller/compare/v5.13.0...v6.4.0)

---
updated-dependencies:
- dependency-name: pyinstaller
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2024-03-02 14:10:17 -08:00
dependabot[bot]
f403fdcc5b
Bump pyobjc-framework-cocoa from 9.2 to 10.1 (#2218)
Bumps [pyobjc-framework-cocoa](https://github.com/ronaldoussoren/pyobjc) from 9.2 to 10.1.
- [Release notes](https://github.com/ronaldoussoren/pyobjc/releases)
- [Changelog](https://github.com/ronaldoussoren/pyobjc/blob/master/docs/changelog.rst)
- [Commits](https://github.com/ronaldoussoren/pyobjc/compare/v9.2...v10.1)

---
updated-dependencies:
- dependency-name: pyobjc-framework-cocoa
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2024-03-02 14:10:07 -08:00
dependabot[bot]
50dba6bf19
Bump pyobjc-core from 9.2 to 10.1 (#2219)
Bumps [pyobjc-core](https://github.com/ronaldoussoren/pyobjc) from 9.2 to 10.1.
- [Release notes](https://github.com/ronaldoussoren/pyobjc/releases)
- [Changelog](https://github.com/ronaldoussoren/pyobjc/blob/master/docs/changelog.rst)
- [Commits](https://github.com/ronaldoussoren/pyobjc/compare/v9.2...v10.1)

---
updated-dependencies:
- dependency-name: pyobjc-core
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2024-03-02 14:08:25 -08:00
dependabot[bot]
23591a0435
Bump pyopenssl from 23.2.0 to 24.0.0 (#2252)
Bumps [pyopenssl](https://github.com/pyca/pyopenssl) from 23.2.0 to 24.0.0.
- [Changelog](https://github.com/pyca/pyopenssl/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/pyopenssl/compare/23.2.0...24.0.0)

---
updated-dependencies:
- dependency-name: pyopenssl
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2024-03-02 14:07:39 -08:00
dependabot[bot]
60f29b0fa4
Bump pycryptodomex from 3.18.0 to 3.20.0 (#2237)
Bumps [pycryptodomex](https://github.com/Legrandin/pycryptodome) from 3.18.0 to 3.20.0.
- [Release notes](https://github.com/Legrandin/pycryptodome/releases)
- [Changelog](https://github.com/Legrandin/pycryptodome/blob/master/Changelog.rst)
- [Commits](https://github.com/Legrandin/pycryptodome/compare/v3.18.0...v3.20.0)

---
updated-dependencies:
- dependency-name: pycryptodomex
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2024-03-02 14:02:24 -08:00
dependabot[bot]
b1c0972077
Bump plexapi from 4.15.4 to 4.15.10 (#2251)
* Bump plexapi from 4.15.4 to 4.15.10

Bumps [plexapi](https://github.com/pkkid/python-plexapi) from 4.15.4 to 4.15.10.
- [Release notes](https://github.com/pkkid/python-plexapi/releases)
- [Commits](https://github.com/pkkid/python-plexapi/compare/4.15.4...4.15.10)

---
updated-dependencies:
- dependency-name: plexapi
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update plexapi==4.15.10

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2024-03-02 13:52:45 -08:00
JonnyWong16
040972bcba
Increase PBKDF2 iterations to 600,000
OWASP Cheat Sheet recommends 600,000 iterations.

https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2
2024-02-19 17:31:28 -08:00
JonnyWong16
c172965ec8
Don't need to refresh Pushover config after entering token 2024-02-17 15:23:14 -08:00
JonnyWong16
d0c07326ab
Change cookie expires to max-age 2023-12-07 23:05:34 -08:00
JonnyWong16
d019efcf91
v2.13.4 2023-12-06 23:18:16 -08:00
JonnyWong16
fe7a59c7f9
Fix use UTC for JWT expiry and max-age for cookie 2023-12-06 23:09:32 -08:00
JonnyWong16
e3113ebd30
Fix configuration table None system language 2023-12-06 13:20:33 -08:00
JonnyWong16
5525b9851c
Fix issue number in changelog 2023-12-03 14:02:51 -08:00
JonnyWong16
8cb74f7480
v2.13.3 2023-12-03 13:49:55 -08:00
JonnyWong16
98ceb0a81d
Add time formats to order of notification text modifiers 2023-12-03 13:45:13 -08:00
JonnyWong16
325271a88e
Update the stream duration on activity cards
* Fixes #2206

Some clients like Plexamp use the same sessionKey when the track changes.
2023-12-03 13:33:37 -08:00
JonnyWong16
ddc8a08fc7
Replace usage of utcnow()
`datetime.utcnow()` deprecated in Python 3.12
2023-11-21 11:36:08 -08:00
JonnyWong16
d0c1e467bd
Add support for thetvdb_url for movies 2023-11-19 13:46:57 -08:00
JonnyWong16
380cbd232c
Add file_size_bytes notification parameter
* Change type of file_size parameter and update description to indicate it is in human readable format.
2023-11-19 13:05:16 -08:00
JonnyWong16
98c363f559
Update Notification Text Modifiers modal with Time Formats 2023-11-13 11:28:53 -08:00
JonnyWong16
5abdfd7377
Make datestamp and timestamp formattable 2023-11-13 11:25:39 -08:00
JonnyWong16
8fd62e30b3
Fix duration_time typo 2023-11-09 17:17:24 -08:00
JonnyWong16
89aad6952b
Fix activity card overflow due to screen scaling
* Fixes #2033
2023-11-09 16:43:41 -08:00
JonnyWong16
2da3714dd1
Add CustomArrow date/time formatter 2023-11-07 17:18:48 -08:00
JonnyWong16
ab5836a65b
Add duration_time notification parameter 2023-11-07 17:12:19 -08:00
JonnyWong16
ae17d2dde0
Switch actions/create-release to softprops/action-gh-release
* actions/create-release deprecated
2023-11-04 13:44:49 -07:00
JonnyWong16
f1c12c0bbe
Switch appdirs to platformdirs 2023-11-04 13:29:40 -07:00
JonnyWong16
32cf26884b
Add system language and sqlite version to configuration table 2023-10-26 11:05:51 -07:00
JonnyWong16
dd380b583f
Add system language to startup logs 2023-10-26 11:05:34 -07:00
JonnyWong16
c215afbf84
v2.13.2 2023-10-26 09:27:31 -07:00
JonnyWong16
d63c0cb469
Guard against None transcode_key 2023-10-26 09:24:48 -07:00
JonnyWong16
98583d139a
Add config override for PMS_LANGUAGE 2023-10-26 09:05:47 -07:00
JonnyWong16
ab16adcffc
Add link from concurrent stream stats card to graphs page 2023-10-23 16:04:29 -07:00
herby2212
a31dcb5508
Quater values for History Watch Status (#2179)
* initial release

* change fillup orientation to clockwise

* fix indentation for css

* fix 50 percent circle orientation

* optimize colors and padding
2023-10-23 15:51:35 -07:00
JonnyWong16
1e4fc05ecf
Add ping method to refresh token 2023-10-17 11:23:04 -07:00
JonnyWong16
efdd4156d8
Update IP helper function 2023-10-16 15:04:19 -07:00
JonnyWong16
7245e97726
Fix concurrent streams graph query 2023-10-10 21:27:13 -07:00
JonnyWong16
d8d1f75605
Use group_by_keys helper for library stats 2023-10-10 19:32:30 -07:00
JonnyWong16
b32183b7b6
Speed up graphs data lookup 2023-10-10 19:18:47 -07:00
JonnyWong16
a59e07c07d
Add metadataDirectory to exporter fields 2023-10-10 14:23:26 -07:00
dependabot[bot]
aa4d98ee34
Bump plexapi from 4.15.0 to 4.15.4 (#2175)
* Bump plexapi from 4.15.0 to 4.15.4

Bumps [plexapi](https://github.com/pkkid/python-plexapi) from 4.15.0 to 4.15.4.
- [Release notes](https://github.com/pkkid/python-plexapi/releases)
- [Commits](https://github.com/pkkid/python-plexapi/compare/4.15.0...4.15.4)

---
updated-dependencies:
- dependency-name: plexapi
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update plexapi==4.15.4

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-10-10 14:22:10 -07:00
JonnyWong16
fdc1dd3525
Add trigger time to notification and newsletter tables 2023-10-09 11:37:56 -07:00
JonnyWong16
62be48df9c
Change colour of max. concurrent stream series 2023-10-09 11:33:40 -07:00
JonnyWong16
982c893c49
Use helper function to cast play duration to int 2023-10-09 11:27:46 -07:00
JonnyWong16
0fa7553d97
Fix right float cog icon on mobile devices table 2023-10-09 11:27:46 -07:00
JonnyWong16
b18c31f431
Update table right flow overflow 2023-10-09 11:27:46 -07:00
JonnyWong16
a668932ea8
Remove banners from exports 2023-10-09 11:27:46 -07:00
herby2212
59fe34982e
Concurrent Streams per Day Graph (#2046)
* initial commit

* fix grouping in webserve

* remove event handler and adapt cursor

* optimize most concurrent calculation

* update branch from nightly and user filter

* max concurrent streams in graph

* made several changes mentioned in review
2023-10-07 13:45:11 -07:00
dependabot[bot]
4938954c61
Bump docker/setup-buildx-action from 2 to 3 (#2152)
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 2 to 3.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2023-09-18 12:59:23 -07:00
dependabot[bot]
76f1335a55
Bump docker/build-push-action from 4 to 5 (#2151)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 4 to 5.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v4...v5)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2023-09-18 12:57:59 -07:00
dependabot[bot]
5e75d0ce73
Bump docker/setup-qemu-action from 2 to 3 (#2150)
Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 2 to 3.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2023-09-18 12:57:43 -07:00
dependabot[bot]
26419f4610
Bump docker/login-action from 2 to 3 (#2149)
Bumps [docker/login-action](https://github.com/docker/login-action) from 2 to 3.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2023-09-18 12:57:32 -07:00
dependabot[bot]
aaea924aaa
Bump actions/checkout from 3 to 4 (#2145)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2023-09-18 12:57:18 -07:00
JonnyWong16
6d858367a4
v2.13.1 2023-08-25 11:49:55 -07:00
JonnyWong16
85bc9c39ae
Update tzlocal==5.0.1 2023-08-25 11:33:56 -07:00
dependabot[bot]
80e6131a0d
Revert "Bump apscheduler from 3.10.1 to 3.10.4 (#2133)"
This reverts commit 2c42150799.
2023-08-25 11:23:49 -07:00
JonnyWong16
1b26775ec6
Revert "Remove importlib-metadata and importlib-resources"
This reverts commit 38435ae81c.
2023-08-25 11:05:53 -07:00
JonnyWong16
fa510792f1
Revert "Remove importlib-metadata and importlib-resources"
This reverts commit 47b2f55c97.
2023-08-25 11:05:46 -07:00
JonnyWong16
289a8a2334
v2.13.0 2023-08-25 09:11:08 -07:00
JonnyWong16
47b2f55c97
Remove importlib-metadata and importlib-resources 2023-08-24 14:18:46 -07:00
JonnyWong16
38435ae81c
Remove importlib-metadata and importlib-resources 2023-08-24 13:49:16 -07:00
JonnyWong16
b133904b48
Drop support for Python 3.7 2023-08-24 12:20:50 -07:00
JonnyWong16
67842cfa02
Add subtitleStream exporter fields for on-demand subtitles 2023-08-24 12:17:53 -07:00
JonnyWong16
24fbc9a17a
Add track chapter exporter fields 2023-08-24 12:17:21 -07:00
dependabot[bot]
b2c16eba07
Bump plexapi from 4.13.4 to 4.15.0 (#2132)
* Bump plexapi from 4.13.4 to 4.15.0

Bumps [plexapi](https://github.com/pkkid/python-plexapi) from 4.13.4 to 4.15.0.
- [Release notes](https://github.com/pkkid/python-plexapi/releases)
- [Commits](https://github.com/pkkid/python-plexapi/compare/4.13.4...4.15.0)

---
updated-dependencies:
- dependency-name: plexapi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update plexapi==4.15.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-08-24 12:10:56 -07:00
dependabot[bot]
2c42150799
Bump apscheduler from 3.10.1 to 3.10.4 (#2133)
* Bump apscheduler from 3.10.1 to 3.10.4

Bumps [apscheduler](https://github.com/agronholm/apscheduler) from 3.10.1 to 3.10.4.
- [Changelog](https://github.com/agronholm/apscheduler/blob/3.10.4/docs/versionhistory.rst)
- [Commits](https://github.com/agronholm/apscheduler/compare/3.10.1...3.10.4)

---
updated-dependencies:
- dependency-name: apscheduler
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update apscheduler==3.10.4

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-08-24 12:10:08 -07:00
dependabot[bot]
3debeada2a
Bump pyparsing from 3.0.9 to 3.1.1 (#2131)
* Bump pyparsing from 3.0.9 to 3.1.1

Bumps [pyparsing](https://github.com/pyparsing/pyparsing) from 3.0.9 to 3.1.1.
- [Release notes](https://github.com/pyparsing/pyparsing/releases)
- [Changelog](https://github.com/pyparsing/pyparsing/blob/master/CHANGES)
- [Commits](https://github.com/pyparsing/pyparsing/compare/pyparsing_3.0.9...3.1.1)

---
updated-dependencies:
- dependency-name: pyparsing
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update pyparsing==3.1.1

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-08-24 12:09:51 -07:00
dependabot[bot]
d0c7f25a3f
Bump markupsafe from 2.1.2 to 2.1.3 (#2130)
* Bump markupsafe from 2.1.2 to 2.1.3

Bumps [markupsafe](https://github.com/pallets/markupsafe) from 2.1.2 to 2.1.3.
- [Release notes](https://github.com/pallets/markupsafe/releases)
- [Changelog](https://github.com/pallets/markupsafe/blob/main/CHANGES.rst)
- [Commits](https://github.com/pallets/markupsafe/compare/2.1.2...2.1.3)

---
updated-dependencies:
- dependency-name: markupsafe
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update markupsave==2.1.3

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-08-24 12:09:39 -07:00
dependabot[bot]
371d35433c
Bump tokenize-rt from 5.0.0 to 5.2.0 (#2129)
* Bump tokenize-rt from 5.0.0 to 5.2.0

Bumps [tokenize-rt](https://github.com/asottile/tokenize-rt) from 5.0.0 to 5.2.0.
- [Commits](https://github.com/asottile/tokenize-rt/compare/v5.0.0...v5.2.0)

---
updated-dependencies:
- dependency-name: tokenize-rt
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update tokenize-rt==5.2.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-08-24 12:09:28 -07:00
dependabot[bot]
4033114175
Bump cheroot from 9.0.0 to 10.0.0 (#2128)
* Bump cheroot from 9.0.0 to 10.0.0

Bumps [cheroot](https://github.com/cherrypy/cheroot) from 9.0.0 to 10.0.0.
- [Release notes](https://github.com/cherrypy/cheroot/releases)
- [Changelog](https://github.com/cherrypy/cheroot/blob/main/CHANGES.rst)
- [Commits](https://github.com/cherrypy/cheroot/compare/v9.0.0...v10.0.0)

---
updated-dependencies:
- dependency-name: cheroot
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update cheroot==10.0.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-08-24 12:09:15 -07:00
dependabot[bot]
9423f65a90
Bump backports-functools-lru-cache from 1.6.4 to 1.6.6 (#2127)
* Bump backports-functools-lru-cache from 1.6.4 to 1.6.6

Bumps [backports-functools-lru-cache](https://github.com/jaraco/backports.functools_lru_cache) from 1.6.4 to 1.6.6.
- [Release notes](https://github.com/jaraco/backports.functools_lru_cache/releases)
- [Changelog](https://github.com/jaraco/backports.functools_lru_cache/blob/main/NEWS.rst)
- [Commits](https://github.com/jaraco/backports.functools_lru_cache/compare/v1.6.4...v1.6.6)

---
updated-dependencies:
- dependency-name: backports-functools-lru-cache
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update backports-functools-lru-cache==1.6.6

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-08-24 12:06:08 -07:00
dependabot[bot]
72f1ce7865
Bump importlib-resources from 5.12.0 to 6.0.1 (#2126)
* Bump importlib-resources from 5.12.0 to 6.0.1

Bumps [importlib-resources](https://github.com/python/importlib_resources) from 5.12.0 to 6.0.1.
- [Release notes](https://github.com/python/importlib_resources/releases)
- [Changelog](https://github.com/python/importlib_resources/blob/main/NEWS.rst)
- [Commits](https://github.com/python/importlib_resources/compare/v5.12.0...v6.0.1)

---
updated-dependencies:
- dependency-name: importlib-resources
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update importlib-resources==6.0.1

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-08-24 12:05:53 -07:00
dependabot[bot]
9383d5120c
Bump portend from 3.1.0 to 3.2.0 (#2125)
Bumps [portend](https://github.com/jaraco/portend) from 3.1.0 to 3.2.0.
- [Release notes](https://github.com/jaraco/portend/releases)
- [Changelog](https://github.com/jaraco/portend/blob/main/NEWS.rst)
- [Commits](https://github.com/jaraco/portend/compare/v3.1.0...v3.2.0)

---
updated-dependencies:
- dependency-name: portend
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2023-08-24 12:05:41 -07:00
dependabot[bot]
69d052f758
Bump zipp from 3.15.0 to 3.16.2 (#2124)
* Bump zipp from 3.15.0 to 3.16.2

Bumps [zipp](https://github.com/jaraco/zipp) from 3.15.0 to 3.16.2.
- [Release notes](https://github.com/jaraco/zipp/releases)
- [Changelog](https://github.com/jaraco/zipp/blob/main/NEWS.rst)
- [Commits](https://github.com/jaraco/zipp/compare/v3.15.0...v3.16.2)

---
updated-dependencies:
- dependency-name: zipp
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update zipp==3.16.2

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-08-24 12:05:25 -07:00
dependabot[bot]
c0aa4e4996
Bump dnspython from 2.3.0 to 2.4.2 (#2123)
* Bump dnspython from 2.3.0 to 2.4.2

Bumps [dnspython](https://github.com/rthalley/dnspython) from 2.3.0 to 2.4.2.
- [Release notes](https://github.com/rthalley/dnspython/releases)
- [Changelog](https://github.com/rthalley/dnspython/blob/master/doc/whatsnew.rst)
- [Commits](https://github.com/rthalley/dnspython/compare/v2.3.0...v2.4.2)

---
updated-dependencies:
- dependency-name: dnspython
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update dnspython==2.4.2

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-08-24 12:05:11 -07:00
JonnyWong16
9f00f5dafa
Fix watched notification trigger description
Fixes #2104
2023-08-23 22:04:27 -07:00
dependabot[bot]
7266a60343
Bump cloudinary from 1.32.0 to 1.34.0 (#2109)
* Bump cloudinary from 1.32.0 to 1.34.0

Bumps [cloudinary](https://github.com/cloudinary/pycloudinary) from 1.32.0 to 1.34.0.
- [Release notes](https://github.com/cloudinary/pycloudinary/releases)
- [Changelog](https://github.com/cloudinary/pycloudinary/blob/master/CHANGELOG.md)
- [Commits](https://github.com/cloudinary/pycloudinary/compare/1.32.0...1.34.0)

---
updated-dependencies:
- dependency-name: cloudinary
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update cloudinary==1.34.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-08-23 21:56:41 -07:00
JonnyWong16
edd8c5fdc3
Pin urllib3<2 in requirements.txt
[skip ci]
2023-08-23 21:54:59 -07:00
JonnyWong16
d6b3ed178e
Downgraade urllib3==1.26.16 2023-08-23 21:52:33 -07:00
dependabot[bot]
eac78a3047
Bump websocket-client from 1.5.1 to 1.6.2 (#2122)
* Bump websocket-client from 1.5.1 to 1.6.2

Bumps [websocket-client](https://github.com/websocket-client/websocket-client) from 1.5.1 to 1.6.2.
- [Release notes](https://github.com/websocket-client/websocket-client/releases)
- [Changelog](https://github.com/websocket-client/websocket-client/blob/master/ChangeLog)
- [Commits](https://github.com/websocket-client/websocket-client/compare/v1.5.1...v1.6.2)

---
updated-dependencies:
- dependency-name: websocket-client
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update websocket-client==1.6.2

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-08-23 21:45:28 -07:00
dependabot[bot]
c93f470371
Bump pyjwt from 2.6.0 to 2.8.0 (#2115)
* Bump pyjwt from 2.6.0 to 2.8.0

Bumps [pyjwt](https://github.com/jpadilla/pyjwt) from 2.6.0 to 2.8.0.
- [Release notes](https://github.com/jpadilla/pyjwt/releases)
- [Changelog](https://github.com/jpadilla/pyjwt/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/jpadilla/pyjwt/compare/2.6.0...2.8.0)

---
updated-dependencies:
- dependency-name: pyjwt
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update pyjwt==2.8.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-08-23 21:45:15 -07:00
dependabot[bot]
77f38bbf93 Bump urllib3 from 1.26.15 to 2.0.4 (#2113)
* Bump urllib3 from 1.26.15 to 2.0.4

Bumps [urllib3](https://github.com/urllib3/urllib3) from 1.26.15 to 2.0.4.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/1.26.15...2.0.4)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update urllib3==2.0.4

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-08-23 21:44:36 -07:00
dependabot[bot]
1e903b164b
Bump tempora from 5.2.1 to 5.5.0 (#2111)
* Bump tempora from 5.2.1 to 5.5.0

Bumps [tempora](https://github.com/jaraco/tempora) from 5.2.1 to 5.5.0.
- [Release notes](https://github.com/jaraco/tempora/releases)
- [Changelog](https://github.com/jaraco/tempora/blob/main/NEWS.rst)
- [Commits](https://github.com/jaraco/tempora/compare/v5.2.1...v5.5.0)

---
updated-dependencies:
- dependency-name: tempora
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update tempora==5.5.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-08-23 21:43:27 -07:00
dependabot[bot]
9a196f3dca
Bump certifi from 2022.12.7 to 2023.7.22 (#2110)
* Bump certifi from 2022.12.7 to 2023.7.22

Bumps [certifi](https://github.com/certifi/python-certifi) from 2022.12.7 to 2023.7.22.
- [Commits](https://github.com/certifi/python-certifi/compare/2022.12.07...2023.07.22)

---
updated-dependencies:
- dependency-name: certifi
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update certifi==2023.7.22

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-08-23 21:42:53 -07:00
dependabot[bot]
6b6d43ef43
Bump requests from 2.28.2 to 2.31.0 (#2078)
* Bump requests from 2.28.2 to 2.31.0

Bumps [requests](https://github.com/psf/requests) from 2.28.2 to 2.31.0.
- [Release notes](https://github.com/psf/requests/releases)
- [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md)
- [Commits](https://github.com/psf/requests/compare/v2.28.2...v2.31.0)

---
updated-dependencies:
- dependency-name: requests
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update requests==2.31.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-08-23 21:40:02 -07:00
dependabot[bot]
478d9e6aa5
Bump soupsieve from 2.4 to 2.4.1 (#2051)
* Bump soupsieve from 2.4 to 2.4.1

Bumps [soupsieve](https://github.com/facelessuser/soupsieve) from 2.4 to 2.4.1.
- [Release notes](https://github.com/facelessuser/soupsieve/releases)
- [Commits](https://github.com/facelessuser/soupsieve/compare/2.4...2.4.1)

---
updated-dependencies:
- dependency-name: soupsieve
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Remove soupsieve

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-08-23 21:39:21 -07:00
dependabot[bot]
c5bbaaf39c
Bump packaging from 23.0 to 23.1 (#2043)
* Bump packaging from 23.0 to 23.1

Bumps [packaging](https://github.com/pypa/packaging) from 23.0 to 23.1.
- [Release notes](https://github.com/pypa/packaging/releases)
- [Changelog](https://github.com/pypa/packaging/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pypa/packaging/compare/23.0...23.1)

---
updated-dependencies:
- dependency-name: packaging
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update packaging==23.1

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-08-23 21:38:59 -07:00
dependabot[bot]
e70e08c3f5
Bump beautifulsoup4 from 4.11.2 to 4.12.2 (#2037)
* Bump beautifulsoup4 from 4.11.2 to 4.12.2

Bumps [beautifulsoup4](https://www.crummy.com/software/BeautifulSoup/bs4/) from 4.11.2 to 4.12.2.

---
updated-dependencies:
- dependency-name: beautifulsoup4
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update beautifulsoup4==4.12.2

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-08-23 21:38:49 -07:00
dependabot[bot]
1798594569
Bump simplejson from 3.18.3 to 3.19.1 (#2036)
* Bump simplejson from 3.18.3 to 3.19.1

Bumps [simplejson](https://github.com/simplejson/simplejson) from 3.18.3 to 3.19.1.
- [Release notes](https://github.com/simplejson/simplejson/releases)
- [Changelog](https://github.com/simplejson/simplejson/blob/master/CHANGES.txt)
- [Commits](https://github.com/simplejson/simplejson/compare/v3.18.3...v3.19.1)

---
updated-dependencies:
- dependency-name: simplejson
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update simplejson==3.19.1

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-08-23 21:38:39 -07:00
dependabot[bot]
70fb00280b
Bump tzdata from 2022.7 to 2023.3 (#2032)
* Bump tzdata from 2022.7 to 2023.3

Bumps [tzdata](https://github.com/python/tzdata) from 2022.7 to 2023.3.
- [Release notes](https://github.com/python/tzdata/releases)
- [Changelog](https://github.com/python/tzdata/blob/master/NEWS.md)
- [Commits](https://github.com/python/tzdata/compare/2022.7...2023.3)

---
updated-dependencies:
- dependency-name: tzdata
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update tzdata==2023.3

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-08-23 21:38:28 -07:00
dependabot[bot]
835ea34bea
Bump pytz from 2022.7.1 to 2023.3 (#2031)
* Bump pytz from 2022.7.1 to 2023.3

Bumps [pytz](https://github.com/stub42/pytz) from 2022.7.1 to 2023.3.
- [Release notes](https://github.com/stub42/pytz/releases)
- [Commits](https://github.com/stub42/pytz/compare/release_2022.7.1...release_2023.3)

---
updated-dependencies:
- dependency-name: pytz
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update pytz==2023.3

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-08-23 21:38:17 -07:00
dependabot[bot]
a21fffd227
Bump pyinstaller from 5.8.0 to 5.13.0 (#2114)
Bumps [pyinstaller](https://github.com/pyinstaller/pyinstaller) from 5.8.0 to 5.13.0.
- [Release notes](https://github.com/pyinstaller/pyinstaller/releases)
- [Changelog](https://github.com/pyinstaller/pyinstaller/blob/develop/doc/CHANGES.rst)
- [Commits](https://github.com/pyinstaller/pyinstaller/compare/v5.8.0...v5.13.0)

---
updated-dependencies:
- dependency-name: pyinstaller
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-23 21:23:35 -07:00
dependabot[bot]
f80cd73982
Bump importlib-metadata from 6.0.0 to 6.8.0 (#2112)
Bumps [importlib-metadata](https://github.com/python/importlib_metadata) from 6.0.0 to 6.8.0.
- [Release notes](https://github.com/python/importlib_metadata/releases)
- [Changelog](https://github.com/python/importlib_metadata/blob/main/NEWS.rst)
- [Commits](https://github.com/python/importlib_metadata/compare/v6.0.0...v6.8.0)

---
updated-dependencies:
- dependency-name: importlib-metadata
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2023-08-23 21:22:24 -07:00
dependabot[bot]
e11a4c50ba
Bump pyobjc-framework-cocoa from 9.0.1 to 9.2 (#2083)
Bumps [pyobjc-framework-cocoa](https://github.com/ronaldoussoren/pyobjc) from 9.0.1 to 9.2.
- [Release notes](https://github.com/ronaldoussoren/pyobjc/releases)
- [Changelog](https://github.com/ronaldoussoren/pyobjc/blob/master/docs/changelog.rst)
- [Commits](https://github.com/ronaldoussoren/pyobjc/compare/v9.0.1...v9.2)

---
updated-dependencies:
- dependency-name: pyobjc-framework-cocoa
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-08-23 21:21:55 -07:00
dependabot[bot]
a84b5b51ed
Bump pyobjc-core from 9.0.1 to 9.2 (#2082)
Bumps [pyobjc-core](https://github.com/ronaldoussoren/pyobjc) from 9.0.1 to 9.2.
- [Release notes](https://github.com/ronaldoussoren/pyobjc/releases)
- [Changelog](https://github.com/ronaldoussoren/pyobjc/blob/master/docs/changelog.rst)
- [Commits](https://github.com/ronaldoussoren/pyobjc/compare/v9.0.1...v9.2)

---
updated-dependencies:
- dependency-name: pyobjc-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2023-08-23 21:21:16 -07:00
dependabot[bot]
b7c0b104e9
Bump pycryptodomex from 3.17 to 3.18.0 (#2076)
Bumps [pycryptodomex](https://github.com/Legrandin/pycryptodome) from 3.17 to 3.18.0.
- [Release notes](https://github.com/Legrandin/pycryptodome/releases)
- [Changelog](https://github.com/Legrandin/pycryptodome/blob/master/Changelog.rst)
- [Commits](https://github.com/Legrandin/pycryptodome/compare/v3.17.0...v3.18.0)

---
updated-dependencies:
- dependency-name: pycryptodomex
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-08-23 21:20:54 -07:00
dependabot[bot]
6fa8bb3768
Bump pyopenssl from 23.0.0 to 23.2.0 (#2081)
Bumps [pyopenssl](https://github.com/pyca/pyopenssl) from 23.0.0 to 23.2.0.
- [Changelog](https://github.com/pyca/pyopenssl/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/pyopenssl/compare/23.0.0...23.2.0)

---
updated-dependencies:
- dependency-name: pyopenssl
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2023-08-23 21:20:05 -07:00
dependabot[bot]
31543d267f
Bump pywin32 from 305 to 306 (#2028)
Bumps [pywin32](https://github.com/mhammond/pywin32) from 305 to 306.
- [Release notes](https://github.com/mhammond/pywin32/releases)
- [Changelog](https://github.com/mhammond/pywin32/blob/main/CHANGES.txt)
- [Commits](https://github.com/mhammond/pywin32/commits)

---
updated-dependencies:
- dependency-name: pywin32
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2023-08-23 21:19:39 -07:00
dependabot[bot]
842e36485a
Bump actions/stale from 7 to 8 (#2025)
Bumps [actions/stale](https://github.com/actions/stale) from 7 to 8.
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/stale/compare/v7...v8)

---
updated-dependencies:
- dependency-name: actions/stale
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2023-08-15 08:42:01 -07:00
JonnyWong16
e2cb15ef49
Add notification image option for iOS Tautulli Remote App 2023-08-02 16:51:20 -07:00
JonnyWong16
b984a99d51
Update workflows action version refs 2023-07-27 20:04:37 -07:00
JonnyWong16
d701d18a81
Update workflows action version refs 2023-07-27 20:04:03 -07:00
JonnyWong16
765804c93b
Don't expose do_state_change 2023-07-20 14:19:05 -07:00
JonnyWong16
b953b951fb
v2.12.6 2023-07-13 15:50:39 -07:00
JonnyWong16
571a6b6d2d
Cast view_offset to int for regrouping history 2023-07-10 08:58:03 -07:00
David Pooley
6010e406c8
Fix simultaneous streams per IP not behaving as expected with IPv6 (#2096)
* Fix IPv6 comparisson for concurrent streams

* Update regex to allow numbers in config variables

* Remove additional logging for local testing

* Update plexpy/notification_handler.py

Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

---------

Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>
2023-07-08 16:32:42 -07:00
JonnyWong16
d91e561a56
Regroup history in separate thread and improve logging 2023-07-07 17:47:38 -07:00
Tom Niget
343a3e9281
Multiselect user filters (#2090)
* Extract user filter generation code into method

* Extend make_user_cond to allow lists of user IDs

* Update documentation for stats APIs to indicate handling of ID lists

* Use multiselect dropdown for user filter on graphs page

Use standard concatenation

Fix select style

Move settings to JS constructor

Change text for no users checked

Don't call selectAll on page init

Add it back

Remove attributes

Fix emptiness check

Allow deselect all

Only refresh if user id changed

* Show "N users" starting at 2 users

Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

* Use helper function split_strip

Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

* Move make_user_cond at bottom and make private

* Add new user picker to history page

* Fix copy-paste error

* Again

* Add CSS for bootstrap-select

---------

Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>
2023-07-07 17:15:16 -07:00
JonnyWong16
b144e6527f
Add button to regroup play history 2023-07-07 14:36:44 -07:00
JonnyWong16
1fe6d1505f
Add method to regroup history 2023-07-07 14:36:44 -07:00
JonnyWong16
085cfa4bef
Fix history grouping incorrect for watched content 2023-07-07 14:36:44 -07:00
JonnyWong16
c761e6e8d0
Fix template_name argument for login page 2023-06-29 00:07:43 -07:00
JonnyWong16
7ff3abe8b7
Rename template_name argument 2023-06-27 17:20:21 -07:00
JonnyWong16
d9b3b311b9
Only initialize mako TemplateLookup once
* Ref: sqlalchemy/mako#378
2023-06-27 14:23:09 -07:00
JonnyWong16
2a48e3375a
Add d3d11va hardware decoder 2023-06-25 17:35:16 -07:00
JonnyWong16
ea6c6078df
v2.12.4 2023-05-23 10:03:36 -07:00
JonnyWong16
f39b9f9087
Fix SQLite Double-Quoted Strings (#2057)
* Fix __init__.py

* Fix activity_pinger.py

* Fix activity_processor.py

* Fix database.py

* Fix datafactory.py

* Fix exporter.py

* Fix graphs.py

* Fix libraries.py

* Fix mobile_app.py

* Fix newsletter_handler.py

* Fix newsletters.py

* Fix notification_handler.py

* Fix notifiers.py

* Fix plexivity_import.py

* Fix plexwatch_import.py

* Fix users.py

* Fix webauth.py
2023-05-15 11:03:26 -07:00
JonnyWong16
3a1d6322ae
Add return ID for async API calls
* export_id, notification_id, and newsletter_notification_d
2023-05-05 15:57:33 -07:00
JonnyWong16
fe4fba353e
Catch KeyError on import db version 2023-04-20 08:50:41 -07:00
JonnyWong16
3b3c59c4bb
Set view offset equal to duration if stopped within the last 10 sec
* Plex reports the view offset every 10 seconds, so the view offset at the end of stream can be short by up to 10 seconds.
2023-04-17 12:50:52 -07:00
JonnyWong16
e9b1db139e
v2.12.3 2023-04-14 11:50:55 -07:00
JonnyWong16
99afb7392b
Use separate log file for script PlexAPI 2023-04-14 11:29:53 -07:00
JonnyWong16
14648a4604
Fix live tv thumb hover on top libraries statistics card 2023-04-13 21:48:31 -07:00
JonnyWong16
07715c6a49
Rename API get_history response duration to play_duration 2023-04-13 15:17:41 -07:00
JonnyWong16
b0921b5f4a
Fix history table sorting by play duration 2023-04-13 15:17:22 -07:00
JonnyWong16
2921c1fc30
Fix live tv thumb and art for top libraries statistics card 2023-04-13 14:48:19 -07:00
JonnyWong16
fa8b51bfd9
Add .vscode to .gitignore 2023-04-13 14:46:30 -07:00
JonnyWong16
eb7a4fb4bf
Fix calculating media info file sizes 2023-03-22 16:48:42 -07:00
JonnyWong16
18110206d6
Fallback subtitle decision if transcoding subtitles 2023-03-16 22:02:44 -07:00
JonnyWong16
7836dc8403
v2.12.2 2023-03-16 18:29:42 -07:00
JonnyWong16
ab7e559f39
Limit platform version to 100 characters 2023-03-15 17:14:53 -07:00
JonnyWong16
710dd7955f
v2.12.1 2023-03-14 13:05:49 -07:00
JonnyWong16
93081e3f6d
Trim analytic event platform version for macOS 2023-03-14 13:02:49 -07:00
JonnyWong16
8664570a78
Don't show audio language for music on activity card 2023-03-14 12:21:45 -07:00
JonnyWong16
e5f55d7fe7
Remove checking for synced items for sessions 2023-03-14 11:19:50 -07:00
JonnyWong16
41f935e1ed
v2.12.0 2023-03-13 12:16:44 -07:00
JonnyWong16
ea5b43419d
Add last_refreshed to get_library_media_info doc string 2023-03-13 12:16:44 -07:00
JonnyWong16
2dd9272f56
Remove extra space in doc strings 2023-03-13 12:05:40 -07:00
dependabot[bot]
705fd2d3e8
Bump urllib3 from 1.26.14 to 1.26.15 (#2017)
* Bump urllib3 from 1.26.14 to 1.26.15

Bumps [urllib3](https://github.com/urllib3/urllib3) from 1.26.14 to 1.26.15.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/1.26.14...1.26.15)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update urllib3==1.26.15

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-03-13 11:53:56 -07:00
JonnyWong16
93d1f10ad3
Add credits markers and audio/subtitle settings to exporter fields 2023-03-10 11:06:44 -08:00
JonnyWong16
eb7495e930
Update plexapi==4.13.4 2023-03-10 11:06:44 -08:00
JonnyWong16
b7da2dedf3
Add hidden setting to override server name 2023-03-07 20:25:49 -08:00
JonnyWong16
000656be00
Add last_refreshed time to get_library_media_info response 2023-03-07 20:16:08 -08:00
JonnyWong16
c90469b0b7
URI encode download logfile parameter 2023-03-03 22:37:57 -08:00
JonnyWong16
3d2959ee51
Use .setAttribute() for QR code address parser 2023-03-03 22:25:01 -08:00
JonnyWong16
4274a29bd6
Use .text() instead of .html() to set graph range values 2023-03-03 22:22:29 -08:00
JonnyWong16
22c5e59ee9
Only run CodeQL on nightly branch 2023-03-03 11:20:29 -08:00
JonnyWong16
39047ea7c3
Add codeql config file
[skip ci]
2023-03-03 11:17:59 -08:00
JonnyWong16
0925234f06
Create .github/workflows/codeql.yml 2023-03-03 11:07:45 -08:00
JonnyWong16
d240984c27
Fix multiline changelog in publish installer workflow
* Ref: https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#multiline-strings

[skip ci]
2023-03-03 10:59:43 -08:00
JonnyWong16
8f06b39a07
Fix submit winget workflow
[skip ci]
2023-03-03 10:59:22 -08:00
JonnyWong16
aa7606d576
v2.12.0-beta 2023-03-03 10:26:31 -08:00
JonnyWong16
78a3d15039
Add history table to collection and playlist info pages 2023-03-03 10:22:02 -08:00
dependabot[bot]
ec323abb92
Bump apscheduler from 3.10.0 to 3.10.1 (#2013)
* Bump apscheduler from 3.10.0 to 3.10.1

Bumps [apscheduler](https://github.com/agronholm/apscheduler) from 3.10.0 to 3.10.1.
- [Release notes](https://github.com/agronholm/apscheduler/releases)
- [Changelog](https://github.com/agronholm/apscheduler/blob/3.10.1/docs/versionhistory.rst)
- [Commits](https://github.com/agronholm/apscheduler/compare/3.10.0...3.10.1)

---
updated-dependencies:
- dependency-name: apscheduler
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update apscheduler==3.10.1

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-03-03 10:21:48 -08:00
dependabot[bot]
69e1b43aa1
Bump soupsieve from 2.3.2.post1 to 2.4 (#2014)
Bumps [soupsieve](https://github.com/facelessuser/soupsieve) from 2.3.2.post1 to 2.4.
- [Release notes](https://github.com/facelessuser/soupsieve/releases)
- [Commits](https://github.com/facelessuser/soupsieve/compare/2.3.2.post1...2.4)

---
updated-dependencies:
- dependency-name: soupsieve
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2023-03-03 10:21:35 -08:00
JonnyWong16
dffa353966
Don't submit to winget for pre-release (beta)
[skip ci]
2023-03-02 22:59:58 -08:00
dependabot[bot]
a241de6dc2
Bump pyinstaller from 5.7.0 to 5.8.0 (#1998)
Bumps [pyinstaller](https://github.com/pyinstaller/pyinstaller) from 5.7.0 to 5.8.0.
- [Release notes](https://github.com/pyinstaller/pyinstaller/releases)
- [Changelog](https://github.com/pyinstaller/pyinstaller/blob/develop/doc/CHANGES.rst)
- [Commits](https://github.com/pyinstaller/pyinstaller/compare/v5.7.0...v5.8.0)

---
updated-dependencies:
- dependency-name: pyinstaller
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-02 21:10:48 -08:00
dependabot[bot]
3ba3ea9b7e
Bump pycryptodomex from 3.16.0 to 3.17 (#1983)
Bumps [pycryptodomex](https://github.com/Legrandin/pycryptodome) from 3.16.0 to 3.17.
- [Release notes](https://github.com/Legrandin/pycryptodome/releases)
- [Changelog](https://github.com/Legrandin/pycryptodome/blob/master/Changelog.rst)
- [Commits](https://github.com/Legrandin/pycryptodome/compare/v3.16.0...v3.17.0)

---
updated-dependencies:
- dependency-name: pycryptodomex
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2023-03-02 21:04:49 -08:00
dependabot[bot]
33f9c70181
Bump urllib3 from 1.26.13 to 1.26.14 (#1965)
* Bump urllib3 from 1.26.13 to 1.26.14

Bumps [urllib3](https://github.com/urllib3/urllib3) from 1.26.13 to 1.26.14.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/1.26.14/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/1.26.13...1.26.14)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update urllib3==1.26.14

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-03-02 20:58:20 -08:00
dependabot[bot]
eb9c9ffd4e
Bump pyopenssl from 22.1.0 to 23.0.0 (#1955)
Bumps [pyopenssl](https://github.com/pyca/pyopenssl) from 22.1.0 to 23.0.0.
- [Release notes](https://github.com/pyca/pyopenssl/releases)
- [Changelog](https://github.com/pyca/pyopenssl/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/pyopenssl/compare/22.1.0...23.0.0)

---
updated-dependencies:
- dependency-name: pyopenssl
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2023-03-02 20:57:58 -08:00
dependabot[bot]
133c8a3363
Bump cloudinary from 1.30.0 to 1.32.0 (#1996)
* Bump cloudinary from 1.30.0 to 1.32.0

Bumps [cloudinary](https://github.com/cloudinary/pycloudinary) from 1.30.0 to 1.32.0.
- [Release notes](https://github.com/cloudinary/pycloudinary/releases)
- [Changelog](https://github.com/cloudinary/pycloudinary/blob/master/CHANGELOG.md)
- [Commits](https://github.com/cloudinary/pycloudinary/compare/1.30.0...1.32.0)

---
updated-dependencies:
- dependency-name: cloudinary
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update cloudinary==1.32.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-03-02 20:57:02 -08:00
dependabot[bot]
66fa93cfaf
Bump websocket-client from 1.4.2 to 1.5.1 (#1993)
* Bump websocket-client from 1.4.2 to 1.5.1

Bumps [websocket-client](https://github.com/websocket-client/websocket-client) from 1.4.2 to 1.5.1.
- [Release notes](https://github.com/websocket-client/websocket-client/releases)
- [Changelog](https://github.com/websocket-client/websocket-client/blob/master/ChangeLog)
- [Commits](https://github.com/websocket-client/websocket-client/compare/v1.4.2...v1.5.1)

---
updated-dependencies:
- dependency-name: websocket-client
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update websocket-client==1.5.1

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-03-02 20:56:49 -08:00
dependabot[bot]
d0f3f96709
Bump simplejson from 3.18.0 to 3.18.3 (#1992)
* Bump simplejson from 3.18.0 to 3.18.3

Bumps [simplejson](https://github.com/simplejson/simplejson) from 3.18.0 to 3.18.3.
- [Release notes](https://github.com/simplejson/simplejson/releases)
- [Changelog](https://github.com/simplejson/simplejson/blob/master/CHANGES.txt)
- [Commits](https://github.com/simplejson/simplejson/compare/v3.18.0...v3.18.3)

---
updated-dependencies:
- dependency-name: simplejson
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update simplejson==3.18.3

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-03-02 20:56:39 -08:00
dependabot[bot]
8e42757b2d
Bump beautifulsoup4 from 4.11.1 to 4.11.2 (#1987)
* Bump beautifulsoup4 from 4.11.1 to 4.11.2

Bumps [beautifulsoup4](https://www.crummy.com/software/BeautifulSoup/bs4/) from 4.11.1 to 4.11.2.

---
updated-dependencies:
- dependency-name: beautifulsoup4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update beautifulsoup4==4.11.2

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-03-02 20:56:24 -08:00
dependabot[bot]
ded93ef2f5
Bump apscheduler from 3.9.1.post1 to 3.10.0 (#1986)
* Bump apscheduler from 3.9.1.post1 to 3.10.0

Bumps [apscheduler](https://github.com/agronholm/apscheduler) from 3.9.1.post1 to 3.10.0.
- [Release notes](https://github.com/agronholm/apscheduler/releases)
- [Changelog](https://github.com/agronholm/apscheduler/blob/3.10.0/docs/versionhistory.rst)
- [Commits](https://github.com/agronholm/apscheduler/compare/3.9.1.post1...3.10.0)

---
updated-dependencies:
- dependency-name: apscheduler
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update apscheduler==3.10.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-03-02 20:55:46 -08:00
dependabot[bot]
1466a391d1
Bump bleach from 5.0.1 to 6.0.0 (#1979)
* Bump bleach from 5.0.1 to 6.0.0

Bumps [bleach](https://github.com/mozilla/bleach) from 5.0.1 to 6.0.0.
- [Release notes](https://github.com/mozilla/bleach/releases)
- [Changelog](https://github.com/mozilla/bleach/blob/main/CHANGES)
- [Commits](https://github.com/mozilla/bleach/compare/v5.0.1...v6.0.0)

---
updated-dependencies:
- dependency-name: bleach
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update bleach==6.0.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-03-02 20:55:01 -08:00
dependabot[bot]
6b1b6d0f32
Bump tempora from 5.1.0 to 5.2.1 (#1977)
* Bump tempora from 5.1.0 to 5.2.1

Bumps [tempora](https://github.com/jaraco/tempora) from 5.1.0 to 5.2.1.
- [Release notes](https://github.com/jaraco/tempora/releases)
- [Changelog](https://github.com/jaraco/tempora/blob/main/CHANGES.rst)
- [Commits](https://github.com/jaraco/tempora/compare/v5.1.0...v5.2.1)

---
updated-dependencies:
- dependency-name: tempora
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update tempora==5.2.1

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-03-02 20:54:54 -08:00
dependabot[bot]
2fda916331
Bump markupsafe from 2.1.1 to 2.1.2 (#1976)
* Bump markupsafe from 2.1.1 to 2.1.2

Bumps [markupsafe](https://github.com/pallets/markupsafe) from 2.1.1 to 2.1.2.
- [Release notes](https://github.com/pallets/markupsafe/releases)
- [Changelog](https://github.com/pallets/markupsafe/blob/main/CHANGES.rst)
- [Commits](https://github.com/pallets/markupsafe/compare/2.1.1...2.1.2)

---
updated-dependencies:
- dependency-name: markupsafe
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update markupsafe==2.1.2

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-03-02 20:54:41 -08:00
dependabot[bot]
32c06a8b72
Bump dnspython from 2.2.1 to 2.3.0 (#1975)
* Bump dnspython from 2.2.1 to 2.3.0

Bumps [dnspython](https://github.com/rthalley/dnspython) from 2.2.1 to 2.3.0.
- [Release notes](https://github.com/rthalley/dnspython/releases)
- [Changelog](https://github.com/rthalley/dnspython/blob/master/doc/whatsnew.rst)
- [Commits](https://github.com/rthalley/dnspython/compare/v2.2.1...v2.3.0)

---
updated-dependencies:
- dependency-name: dnspython
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update dnspython==2.3.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-03-02 20:54:32 -08:00
dependabot[bot]
6910079330
Bump pytz from 2022.7 to 2022.7.1 (#1974)
* Bump pytz from 2022.7 to 2022.7.1

Bumps [pytz](https://github.com/stub42/pytz) from 2022.7 to 2022.7.1.
- [Release notes](https://github.com/stub42/pytz/releases)
- [Commits](https://github.com/stub42/pytz/compare/release_2022.7...release_2022.7.1)

---
updated-dependencies:
- dependency-name: pytz
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update pytz==2022.7.1

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-03-02 20:54:08 -08:00
dependabot[bot]
9f727d0086
Bump future from 0.18.2 to 0.18.3 (#1970)
* Bump future from 0.18.2 to 0.18.3

Bumps [future](https://github.com/PythonCharmers/python-future) from 0.18.2 to 0.18.3.
- [Release notes](https://github.com/PythonCharmers/python-future/releases)
- [Changelog](https://github.com/PythonCharmers/python-future/blob/master/docs/changelog.rst)
- [Commits](https://github.com/PythonCharmers/python-future/compare/v0.18.2...v0.18.3)

---
updated-dependencies:
- dependency-name: future
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update future==0.18.3

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-03-02 20:53:26 -08:00
dependabot[bot]
cc78f17be5
Bump requests from 2.28.1 to 2.28.2 (#1968)
* Bump requests from 2.28.1 to 2.28.2

Bumps [requests](https://github.com/psf/requests) from 2.28.1 to 2.28.2.
- [Release notes](https://github.com/psf/requests/releases)
- [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md)
- [Commits](https://github.com/psf/requests/compare/v2.28.1...v2.28.2)

---
updated-dependencies:
- dependency-name: requests
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update requests==2.28.2

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-03-02 20:53:15 -08:00
dependabot[bot]
70e09582da
Bump packaging from 22.0 to 23.0 (#1964)
* Bump packaging from 22.0 to 23.0

Bumps [packaging](https://github.com/pypa/packaging) from 22.0 to 23.0.
- [Release notes](https://github.com/pypa/packaging/releases)
- [Changelog](https://github.com/pypa/packaging/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pypa/packaging/compare/22.0...23.0)

---
updated-dependencies:
- dependency-name: packaging
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update packaging==23.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-03-02 20:53:00 -08:00
dependabot[bot]
1f59171dcb
Bump zipp from 3.11.0 to 3.15.0 (#2011)
* Bump zipp from 3.11.0 to 3.15.0

Bumps [zipp](https://github.com/jaraco/zipp) from 3.11.0 to 3.15.0.
- [Release notes](https://github.com/jaraco/zipp/releases)
- [Changelog](https://github.com/jaraco/zipp/blob/main/CHANGES.rst)
- [Commits](https://github.com/jaraco/zipp/compare/v3.11.0...v3.15.0)

---
updated-dependencies:
- dependency-name: zipp
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update zipp==3.15.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-03-02 20:50:33 -08:00
dependabot[bot]
c8f43825f9
Bump importlib-resources from 5.10.1 to 5.12.0 (#2004)
* Bump importlib-resources from 5.10.1 to 5.12.0

Bumps [importlib-resources](https://github.com/python/importlib_resources) from 5.10.1 to 5.12.0.
- [Release notes](https://github.com/python/importlib_resources/releases)
- [Changelog](https://github.com/python/importlib_resources/blob/main/CHANGES.rst)
- [Commits](https://github.com/python/importlib_resources/compare/v5.10.1...v5.12.0)

---
updated-dependencies:
- dependency-name: importlib-resources
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update importlib-resources==5.12.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-03-02 20:50:15 -08:00
dependabot[bot]
eaa294b2a2
Bump importlib-metadata from 5.2.0 to 6.0.0 (#1954)
* Bump importlib-metadata from 5.2.0 to 6.0.0

Bumps [importlib-metadata](https://github.com/python/importlib_metadata) from 5.2.0 to 6.0.0.
- [Release notes](https://github.com/python/importlib_metadata/releases)
- [Changelog](https://github.com/python/importlib_metadata/blob/main/CHANGES.rst)
- [Commits](https://github.com/python/importlib_metadata/compare/v5.2.0...v6.0.0)

---
updated-dependencies:
- dependency-name: importlib-metadata
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update importlib-metadata==6.0.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2023-03-02 20:49:14 -08:00
dependabot[bot]
5c43daf067
Bump docker/build-push-action from 3 to 4 (#1985)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 3 to 4.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2023-03-02 20:01:45 -08:00
herby2212
0522253194
Add watch time & user stats for playlists (#2012) 2023-02-28 17:33:13 -08:00
herby2212
ae3d75bbe3
watch time & user stats for collections (#1982)
* user_stats for collection

* watch_time_stats for collection

* check for media_type to be compatible with API

* update API and datafactory optimizations

* beautify webserve class

* fix sql query build

* filter on suitable collections

* stats for collections of sub media type

* optimize array creation
2023-02-26 17:32:50 -08:00
JonnyWong16
993909fa08
Use private shared-memory for Snap 2023-02-26 16:17:58 -08:00
JonnyWong16
ce45321e3f
Revert "Add multiprocessing semaphore prefix"
This reverts commit 731d5c9baf.
2023-02-26 16:13:25 -08:00
JonnyWong16
cd3ff6eed7
Remove extra pass 2023-02-26 16:06:44 -08:00
JonnyWong16
731d5c9baf
Add multiprocessing semaphore prefix
* Fixes #2007
2023-02-26 15:17:04 -08:00
JonnyWong16
af3e5574f5
Migrate to Google Analytics 4 2023-02-26 15:09:51 -08:00
JonnyWong16
42eeb90532
Add ga4mp library
* Remove UniversalAnalytics
2023-02-26 15:09:12 -08:00
JonnyWong16
ecb6d8b743
Move track artist to details tag on info page 2023-02-25 16:43:14 -08:00
JonnyWong16
e6d1712afd
Fix styling on collection info pages 2023-02-25 16:35:48 -08:00
JonnyWong16
ebe570d42f
Allow setting a custom Pushover sound
* Closes #2005
2023-02-21 11:12:56 -08:00
JonnyWong16
2a1bf7847b
Last watched statistics card based on credits markers 2023-02-20 18:36:15 -08:00
JonnyWong16
928e1d4b5e
History table watched status based on credits markers 2023-02-20 18:36:15 -08:00
JonnyWong16
c5005c1ea9
Group watched history sessions based on credits markers 2023-02-20 18:36:15 -08:00
JonnyWong16
b2b12044e3
Trigger on_watched based on credits markers 2023-02-20 18:36:15 -08:00
JonnyWong16
b1dd28e39b
Add setting to change video watched completion behaviour 2023-02-20 16:33:19 -08:00
JonnyWong16
c2abfce8e1
Save credits markers offsets to session history 2023-02-19 17:41:48 -08:00
JonnyWong16
6807cebe51
Strip whitespace from condition values 2023-02-17 18:55:28 -08:00
JonnyWong16
87d3c0ae81
Fix missing on_commercial columns in database 2023-02-17 18:54:52 -08:00
JonnyWong16
6b0b3a476f
Add support for commercial marker triggers 2023-02-17 10:01:57 -08:00
JonnyWong16
32bb98e8c1
Update get_metadata_details docs 2023-02-17 09:29:08 -08:00
JonnyWong16
9c6b8f1af5
Simplify metadata credits marker first flag 2023-02-17 09:28:44 -08:00
JonnyWong16
7cc78d448d
Simplify set marker in database 2023-02-16 17:06:46 -08:00
JonnyWong16
599c54c9e1
Refactor activity handler 2023-02-16 17:02:30 -08:00
JonnyWong16
9be3bbbf0f
Update marker notification parameters 2023-02-16 16:20:25 -08:00
JonnyWong16
97af214ac1
Handle seeking through intro/credits markers 2023-02-16 16:19:29 -08:00
JonnyWong16
71bc063155
Add notification triggers for intro/credit markers 2023-02-16 11:31:35 -08:00
JonnyWong16
9a152932ee
Monitor stream intro/credits marker activity 2023-02-16 11:30:33 -08:00
JonnyWong16
a8539b2927
Retrieve intro/credits markers for metadata details 2023-02-16 11:30:33 -08:00
JonnyWong16
0db9548995
Revert "Add prvenance: false to docker/build-push-action"
This reverts commit 548264d51a.
2023-02-14 18:26:50 -08:00
JonnyWong16
e263f0b8a3
Refactor notification image upload code 2023-02-14 18:21:19 -08:00
JonnyWong16
5ab9315f16
Upload notification images directly to Discord 2023-02-14 18:19:35 -08:00
JonnyWong16
b6ff45138f
Add section_id and user_id parameters to get_home_stats API command
* Closes #1944
2023-01-28 14:10:47 -08:00
JonnyWong16
b0a55df862
Add anidb_id and anidb_url notification parameters
* Closes #1973
2023-01-28 14:10:47 -08:00
JonnyWong16
c51ee673e8
Add support for Telegram group topics
* Closes #1980
2023-01-28 14:10:47 -08:00
herby2212
0959f28e95
Add edition detail field for movie info (#1957)
* edition addition for movie info

* swap position to match plex order
2023-01-28 14:09:11 -08:00
JonnyWong16
1e02c26a9a
Relax workflow action versions 2023-01-20 17:09:20 -08:00
JonnyWong16
548264d51a
Add prvenance: false to docker/build-push-action
Ref: docker/buildx#1533
2023-01-20 17:09:20 -08:00
JonnyWong16
460a463be1
Update notification parameter description for Plex API image paths 2023-01-19 13:54:55 -08:00
JonnyWong16
2f6869ed2a
Update CI badges in README
* Ref: badges/shields#8671

[skip ci]
2022-12-23 11:11:04 -08:00
JonnyWong16
3c996f01a9
v2.11.1 2022-12-22 17:58:38 -08:00
JonnyWong16
d9343730da
Downgrade snap to core20 2022-12-22 17:50:24 -08:00
JonnyWong16
7386ca1c42
Add backports.cached-property==1.0.2 2022-12-22 17:28:28 -08:00
James Tufarelli
2a0e07aca5
Fix update check failing when git is not found (#1943)
* Update versioncheck to cast commit_hash to string

After the most recent v2.11.0 update, Tautulli would not start until I made this change to the codebase.

* Update plexpy/versioncheck.py

Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>
2022-12-22 17:28:09 -08:00
JonnyWong16
1e58847bcd
Update snap to core22
* Fixes #1941
2022-12-22 16:24:51 -08:00
JonnyWong16
b0ba73cf46
Fix no started parameter for recently added notifications
* Fixes #1940
2022-12-22 13:52:11 -08:00
JonnyWong16
6181aa178d
Fix publish installer changelog sed command 2022-12-22 12:01:52 -08:00
JonnyWong16
680c8f1ee3
Update publish installer workflow token 2022-12-22 11:44:48 -08:00
JonnyWong16
e8af221e99
Rename submit winget job name 2022-12-22 11:44:30 -08:00
JonnyWong16
808ca348e8
Use source language instead of stream language on activity cards 2022-12-22 11:24:41 -08:00
JonnyWong16
f6dd38ad12
v2.11.0 2022-12-22 11:15:59 -08:00
dependabot[bot]
4064ac083e
Bump actions/setup-python from 4.3.1 to 4.4.0 (#1938)
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4.3.1 to 4.4.0.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v4.3.1...v4.4.0)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-12-22 10:45:05 -08:00
dependabot[bot]
31f6b02149
Bump plexapi from 4.13.1 to 4.13.2 (#1939)
* Bump plexapi from 4.13.1 to 4.13.2

Bumps [plexapi](https://github.com/pkkid/python-plexapi) from 4.13.1 to 4.13.2.
- [Release notes](https://github.com/pkkid/python-plexapi/releases)
- [Commits](https://github.com/pkkid/python-plexapi/compare/4.13.1...4.13.2)

---
updated-dependencies:
- dependency-name: plexapi
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update plexapi==4.13.2

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-12-22 10:44:51 -08:00
JonnyWong16
8cd5b0b775
Use system language for requests 2022-12-21 17:25:40 -08:00
JonnyWong16
d426c3e12e
Don't capitalize language and display "Unknown" on activity cards 2022-12-21 17:25:18 -08:00
JonnyWong16
8eed162367
Override direct play subtitle decision in stream data modal 2022-12-21 17:24:41 -08:00
JonnyWong16
fe1d850e1c
Display "Unknown" language in stream data modal 2022-12-21 17:24:14 -08:00
JonnyWong16
bcb9a9d6c6
Display subtitle forced in stream data modal 2022-12-21 17:02:10 -08:00
JonnyWong16
5a9ace0b19
Log subtitle forced 2022-12-21 17:01:50 -08:00
JonnyWong16
185a5cb0ea
Add subtitle_forced to database 2022-12-21 17:01:24 -08:00
JonnyWong16
44419fb0bf
Display subtitle language in stream data modal 2022-12-21 16:35:15 -08:00
JonnyWong16
c00dfca2f0
Log subtitle language 2022-12-21 16:35:15 -08:00
JonnyWong16
023fde7a84
Add subtitle_language to database 2022-12-21 16:35:15 -08:00
JonnyWong16
7c030ef362
Link watch statistics to info page using history metadata
* Closes #1882
2022-12-21 16:35:15 -08:00
FSCorrupt
8664b95ca1
Adding stream audio/sub language to activity card. (#1900)
* Update current_activity_instance.html

* Update index.html

* Make sure language is capitalized on activity card

Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>
2022-12-21 16:26:32 -08:00
Jagandeep Brar
74d8bc15ca
Add session_key to LunaSea notifier payload (#1929) 2022-12-21 16:08:14 -08:00
dependabot[bot]
cbe33e93c9
Bump pyinstaller from 5.6.2 to 5.7.0 (#1917)
Bumps [pyinstaller](https://github.com/pyinstaller/pyinstaller) from 5.6.2 to 5.7.0.
- [Release notes](https://github.com/pyinstaller/pyinstaller/releases)
- [Changelog](https://github.com/pyinstaller/pyinstaller/blob/develop/doc/CHANGES.rst)
- [Commits](https://github.com/pyinstaller/pyinstaller/compare/v5.6.2...v5.7.0)

---
updated-dependencies:
- dependency-name: pyinstaller
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-21 16:06:32 -08:00
Eric Nemchik
8cf12a2ab5
backports.zoneinfo environment marker (#1926)
https://github.com/pganssle/zoneinfo#installation-and-depending-on-this-library notes:
> Support for `backports.zoneinfo` in Python 3.9+ is currently minimal, since it is expected that you would use the standard library `zoneinfo` module instead.
2022-12-21 16:05:55 -08:00
dependabot[bot]
497ceb8cbb
Bump pyobjc-core from 9.0 to 9.0.1 (#1935)
Bumps [pyobjc-core](https://github.com/ronaldoussoren/pyobjc) from 9.0 to 9.0.1.
- [Release notes](https://github.com/ronaldoussoren/pyobjc/releases)
- [Changelog](https://github.com/ronaldoussoren/pyobjc/blob/master/docs/changelog.rst)
- [Commits](https://github.com/ronaldoussoren/pyobjc/commits)

---
updated-dependencies:
- dependency-name: pyobjc-core
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-12-21 16:05:11 -08:00
dependabot[bot]
18a5962026
Bump pyobjc-framework-cocoa from 9.0 to 9.0.1 (#1933)
Bumps [pyobjc-framework-cocoa](https://github.com/ronaldoussoren/pyobjc) from 9.0 to 9.0.1.
- [Release notes](https://github.com/ronaldoussoren/pyobjc/releases)
- [Changelog](https://github.com/ronaldoussoren/pyobjc/blob/master/docs/changelog.rst)
- [Commits](https://github.com/ronaldoussoren/pyobjc/commits)

---
updated-dependencies:
- dependency-name: pyobjc-framework-cocoa
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-12-21 16:03:50 -08:00
dependabot[bot]
d8150813f1
Bump pycryptodomex from 3.15.0 to 3.16.0 (#1911)
Bumps [pycryptodomex](https://github.com/Legrandin/pycryptodome) from 3.15.0 to 3.16.0.
- [Release notes](https://github.com/Legrandin/pycryptodome/releases)
- [Changelog](https://github.com/Legrandin/pycryptodome/blob/master/Changelog.rst)
- [Commits](https://github.com/Legrandin/pycryptodome/compare/v3.15.0...v3.16.0)

---
updated-dependencies:
- dependency-name: pycryptodomex
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-12-21 16:03:24 -08:00
dependabot[bot]
4398ece2da
Bump pytz from 2022.6 to 2022.7 (#1934)
* Bump pytz from 2022.6 to 2022.7

Bumps [pytz](https://github.com/stub42/pytz) from 2022.6 to 2022.7.
- [Release notes](https://github.com/stub42/pytz/releases)
- [Commits](https://github.com/stub42/pytz/compare/release_2022.6...release_2022.7)

---
updated-dependencies:
- dependency-name: pytz
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update pytz==2022.7

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-12-21 16:02:40 -08:00
dependabot[bot]
8159bf456c
Bump importlib-metadata from 5.0.0 to 5.2.0 (#1932)
* Bump importlib-metadata from 5.0.0 to 5.2.0

Bumps [importlib-metadata](https://github.com/python/importlib_metadata) from 5.0.0 to 5.2.0.
- [Release notes](https://github.com/python/importlib_metadata/releases)
- [Changelog](https://github.com/python/importlib_metadata/blob/main/CHANGES.rst)
- [Commits](https://github.com/python/importlib_metadata/compare/v5.0.0...v5.2.0)

---
updated-dependencies:
- dependency-name: importlib-metadata
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update importlib-metadata==5.2.0

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-12-21 16:02:25 -08:00
dependabot[bot]
56780677fb
Bump packaging from 21.3 to 22.0 (#1922)
* Bump packaging from 21.3 to 22.0

Bumps [packaging](https://github.com/pypa/packaging) from 21.3 to 22.0.
- [Release notes](https://github.com/pypa/packaging/releases)
- [Changelog](https://github.com/pypa/packaging/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pypa/packaging/compare/21.3...22.0)

---
updated-dependencies:
- dependency-name: packaging
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update packaging==22.0

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-12-21 16:01:24 -08:00
dependabot[bot]
dbe0018e24
Bump certifi from 2022.9.24 to 2022.12.7 (#1920)
* Bump certifi from 2022.9.24 to 2022.12.7

Bumps [certifi](https://github.com/certifi/python-certifi) from 2022.9.24 to 2022.12.7.
- [Release notes](https://github.com/certifi/python-certifi/releases)
- [Commits](https://github.com/certifi/python-certifi/compare/2022.09.24...2022.12.07)

---
updated-dependencies:
- dependency-name: certifi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update certifi==2022.12.7

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-12-21 16:00:56 -08:00
dependabot[bot]
483245506b
Bump importlib-resources from 5.10.0 to 5.10.1 (#1918)
* Bump importlib-resources from 5.10.0 to 5.10.1

Bumps [importlib-resources](https://github.com/python/importlib_resources) from 5.10.0 to 5.10.1.
- [Release notes](https://github.com/python/importlib_resources/releases)
- [Changelog](https://github.com/python/importlib_resources/blob/main/CHANGES.rst)
- [Commits](https://github.com/python/importlib_resources/compare/v5.10.0...v5.10.1)

---
updated-dependencies:
- dependency-name: importlib-resources
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update importlib-resources==5.10.1

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-12-21 16:00:12 -08:00
dependabot[bot]
c61f854318
Bump tzdata from 2022.6 to 2022.7 (#1915)
* Bump tzdata from 2022.6 to 2022.7

Bumps [tzdata](https://github.com/python/tzdata) from 2022.6 to 2022.7.
- [Release notes](https://github.com/python/tzdata/releases)
- [Changelog](https://github.com/python/tzdata/blob/master/NEWS.md)
- [Commits](https://github.com/python/tzdata/compare/2022.6...2022.7)

---
updated-dependencies:
- dependency-name: tzdata
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update tzdata==2022.7

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-12-21 15:59:53 -08:00
dependabot[bot]
e646c0e6eb
Bump zipp from 3.10.0 to 3.11.0 (#1912)
* Bump zipp from 3.10.0 to 3.11.0

Bumps [zipp](https://github.com/jaraco/zipp) from 3.10.0 to 3.11.0.
- [Release notes](https://github.com/jaraco/zipp/releases)
- [Changelog](https://github.com/jaraco/zipp/blob/main/CHANGES.rst)
- [Commits](https://github.com/jaraco/zipp/compare/v3.10.0...v3.11.0)

---
updated-dependencies:
- dependency-name: zipp
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update zipp==3.11.0

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-12-21 15:59:38 -08:00
dependabot[bot]
d596b86c8d
Bump urllib3 from 1.26.12 to 1.26.13 (#1908)
* Bump urllib3 from 1.26.12 to 1.26.13

Bumps [urllib3](https://github.com/urllib3/urllib3) from 1.26.12 to 1.26.13.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/1.26.13/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/1.26.12...1.26.13)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update urllib3==1.26.13

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-12-21 15:59:10 -08:00
dependabot[bot]
3d378eb583
Bump cheroot from 8.6.0 to 9.0.0 (#1903)
* Bump cheroot from 8.6.0 to 9.0.0

Bumps [cheroot](https://github.com/cherrypy/cheroot) from 8.6.0 to 9.0.0.
- [Release notes](https://github.com/cherrypy/cheroot/releases)
- [Changelog](https://github.com/cherrypy/cheroot/blob/main/CHANGES.rst)
- [Commits](https://github.com/cherrypy/cheroot/compare/v8.6.0...v9.0.0)

---
updated-dependencies:
- dependency-name: cheroot
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update cheroot==9.0.0

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-12-21 15:58:54 -08:00
dependabot[bot]
0a5edebea3
Bump tempora from 5.0.2 to 5.1.0 (#1902)
* Bump tempora from 5.0.2 to 5.1.0

Bumps [tempora](https://github.com/jaraco/tempora) from 5.0.2 to 5.1.0.
- [Release notes](https://github.com/jaraco/tempora/releases)
- [Changelog](https://github.com/jaraco/tempora/blob/main/CHANGES.rst)
- [Commits](https://github.com/jaraco/tempora/compare/v5.0.2...v5.1.0)

---
updated-dependencies:
- dependency-name: tempora
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update tempora==5.1.0

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-12-21 15:58:39 -08:00
dependabot[bot]
d736fab432
Bump actions/stale from 6 to 7 (#1937)
Bumps [actions/stale](https://github.com/actions/stale) from 6 to 7.
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/stale/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/stale
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-12-21 15:42:30 -08:00
dependabot[bot]
ff81b08491
Bump actions/cache from 3.0.11 to 3.2.0 (#1936)
Bumps [actions/cache](https://github.com/actions/cache) from 3.0.11 to 3.2.0.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3.0.11...v3.2.0)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-12-21 15:42:02 -08:00
dependabot[bot]
d00916331d
Bump actions/checkout from 3.1.0 to 3.2.0 (#1924)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3.1.0 to 3.2.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3.1.0...v3.2.0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-12-21 15:41:48 -08:00
dependabot[bot]
ec08c887f4
Bump actions/setup-python from 4.3.0 to 4.3.1 (#1921)
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4.3.0 to 4.3.1.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v4.3.0...v4.3.1)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-12-21 15:41:32 -08:00
dependabot[bot]
d7aa1ced53
Bump dessant/label-actions from 2 to 3 (#1916)
Bumps [dessant/label-actions](https://github.com/dessant/label-actions) from 2 to 3.
- [Release notes](https://github.com/dessant/label-actions/releases)
- [Changelog](https://github.com/dessant/label-actions/blob/master/CHANGELOG.md)
- [Commits](https://github.com/dessant/label-actions/compare/v2...v3)

---
updated-dependencies:
- dependency-name: dessant/label-actions
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-12-21 15:41:04 -08:00
JonnyWong16
8a1cd2841b
Revert "Pin diddlesnaps/snapcraft-multiarch-action@v1.5.0"
This reverts commit 6156edf5fe.
2022-12-21 15:14:22 -08:00
JonnyWong16
5f8fbe1230
Add meta color-scheme to newsletter template
* Allows CSS to support light and dark themes
2022-12-21 15:13:14 -08:00
JonnyWong16
8cfe60b485
Add started and stopped notification parameters
* Closes #1931
2022-12-21 14:55:33 -08:00
JonnyWong16
6156edf5fe
Pin diddlesnaps/snapcraft-multiarch-action@v1.5.0 2022-11-29 20:55:14 -08:00
JonnyWong16
e2326cd06a
Mask more user and metadata fields for guest access
Fixes #1913
2022-11-29 14:04:24 -08:00
JonnyWong16
94eb5cdef3
Add movie editionTitle to exporter 2022-11-24 09:15:07 -08:00
JonnyWong16
8c552d3086
Only validate condition operator if parameter is present 2022-11-21 20:48:47 -08:00
JonnyWong16
5a8797a6dd
Shorten TLS log message 2022-11-15 15:56:11 -08:00
dependabot[bot]
192dd64dbe
Bump mako from 1.2.3 to 1.2.4 (#1899)
* Bump mako from 1.2.3 to 1.2.4

Bumps [mako](https://github.com/sqlalchemy/mako) from 1.2.3 to 1.2.4.
- [Release notes](https://github.com/sqlalchemy/mako/releases)
- [Changelog](https://github.com/sqlalchemy/mako/blob/main/CHANGES)
- [Commits](https://github.com/sqlalchemy/mako/commits)

---
updated-dependencies:
- dependency-name: mako
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update mako==1.2.4

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-11-15 15:56:03 -08:00
dependabot[bot]
259a96995a
Bump simplejson from 3.17.6 to 3.18.0 (#1898)
* Bump simplejson from 3.17.6 to 3.18.0

Bumps [simplejson](https://github.com/simplejson/simplejson) from 3.17.6 to 3.18.0.
- [Release notes](https://github.com/simplejson/simplejson/releases)
- [Changelog](https://github.com/simplejson/simplejson/blob/master/CHANGES.txt)
- [Commits](https://github.com/simplejson/simplejson/compare/v3.17.6...v3.18.0)

---
updated-dependencies:
- dependency-name: simplejson
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update simplejson==3.18.0

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-11-15 15:55:50 -08:00
dependabot[bot]
60da559332
Bump pyjwt from 2.4.0 to 2.6.0 (#1897)
* Bump pyjwt from 2.4.0 to 2.6.0

Bumps [pyjwt](https://github.com/jpadilla/pyjwt) from 2.4.0 to 2.6.0.
- [Release notes](https://github.com/jpadilla/pyjwt/releases)
- [Changelog](https://github.com/jpadilla/pyjwt/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/jpadilla/pyjwt/compare/2.4.0...2.6.0)

---
updated-dependencies:
- dependency-name: pyjwt
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update pyjwt==2.6.0

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-11-14 11:27:25 -08:00
dependabot[bot]
79cf61c53e
Bump rumps from 0.3.0 to 0.4.0 (#1896)
* Bump rumps from 0.3.0 to 0.4.0

Bumps [rumps](https://github.com/jaredks/rumps) from 0.3.0 to 0.4.0.
- [Release notes](https://github.com/jaredks/rumps/releases)
- [Changelog](https://github.com/jaredks/rumps/blob/master/CHANGES.rst)
- [Commits](https://github.com/jaredks/rumps/commits)

---
updated-dependencies:
- dependency-name: rumps
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update rumps==0.4.0

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-11-14 11:27:16 -08:00
dependabot[bot]
b31d75aeee
Bump distro from 1.7.0 to 1.8.0 (#1895)
* Bump distro from 1.7.0 to 1.8.0

Bumps [distro](https://github.com/python-distro/distro) from 1.7.0 to 1.8.0.
- [Release notes](https://github.com/python-distro/distro/releases)
- [Changelog](https://github.com/python-distro/distro/blob/master/CHANGELOG.md)
- [Commits](https://github.com/python-distro/distro/compare/v1.7.0...v1.8.0)

---
updated-dependencies:
- dependency-name: distro
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update distro==1.8.0

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-11-14 11:27:01 -08:00
dependabot[bot]
6b835226cd
Bump cloudinary from 1.29.0 to 1.30.0 (#1890)
* Bump cloudinary from 1.29.0 to 1.30.0

Bumps [cloudinary](https://github.com/cloudinary/pycloudinary) from 1.29.0 to 1.30.0.
- [Release notes](https://github.com/cloudinary/pycloudinary/releases)
- [Changelog](https://github.com/cloudinary/pycloudinary/blob/master/CHANGELOG.md)
- [Commits](https://github.com/cloudinary/pycloudinary/compare/1.29.0...1.30.0)

---
updated-dependencies:
- dependency-name: cloudinary
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update cloudinary==1.30.0

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-11-14 11:26:24 -08:00
dependabot[bot]
7f132439be
Bump tokenize-rt from 4.2.1 to 5.0.0 (#1889)
* Bump tokenize-rt from 4.2.1 to 5.0.0

Bumps [tokenize-rt](https://github.com/asottile/tokenize-rt) from 4.2.1 to 5.0.0.
- [Release notes](https://github.com/asottile/tokenize-rt/releases)
- [Commits](https://github.com/asottile/tokenize-rt/compare/v4.2.1...v5.0.0)

---
updated-dependencies:
- dependency-name: tokenize-rt
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update tokenize-rt==5.0.0

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-11-14 11:26:09 -08:00
dependabot[bot]
6365327595
Bump pyopenssl from 22.0.0 to 22.1.0 (#1893)
Bumps [pyopenssl](https://github.com/pyca/pyopenssl) from 22.0.0 to 22.1.0.
- [Release notes](https://github.com/pyca/pyopenssl/releases)
- [Changelog](https://github.com/pyca/pyopenssl/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/pyopenssl/compare/22.0.0...22.1.0)

---
updated-dependencies:
- dependency-name: pyopenssl
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-11-14 11:25:40 -08:00
dependabot[bot]
8d40224d61
Bump pywin32 from 304 to 305 (#1894)
Bumps [pywin32](https://github.com/mhammond/pywin32) from 304 to 305.
- [Release notes](https://github.com/mhammond/pywin32/releases)
- [Changelog](https://github.com/mhammond/pywin32/blob/main/CHANGES.txt)
- [Commits](https://github.com/mhammond/pywin32/commits)

---
updated-dependencies:
- dependency-name: pywin32
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-11-14 11:24:36 -08:00
dependabot[bot]
b3091d7366
Bump pyobjc-framework-cocoa from 8.5 to 9.0 (#1891)
Bumps [pyobjc-framework-cocoa](https://github.com/ronaldoussoren/pyobjc) from 8.5 to 9.0.
- [Release notes](https://github.com/ronaldoussoren/pyobjc/releases)
- [Changelog](https://github.com/ronaldoussoren/pyobjc/blob/master/docs/changelog.rst)
- [Commits](https://github.com/ronaldoussoren/pyobjc/compare/v8.5...v9.0)

---
updated-dependencies:
- dependency-name: pyobjc-framework-cocoa
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-11-14 11:24:22 -08:00
dependabot[bot]
fb21815ca0
Bump apscheduler from 3.9.1 to 3.9.1.post1 (#1892)
Bumps [apscheduler](https://github.com/agronholm/apscheduler) from 3.9.1 to 3.9.1.post1.
- [Release notes](https://github.com/agronholm/apscheduler/releases)
- [Changelog](https://github.com/agronholm/apscheduler/blob/master/docs/versionhistory.rst)
- [Commits](https://github.com/agronholm/apscheduler/compare/3.9.1...3.9.1.post1)

---
updated-dependencies:
- dependency-name: apscheduler
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-11-14 11:24:06 -08:00
JonnyWong16
0f39171f93
Make webserver TLS version advanced config
Ref: #1870
2022-11-14 11:09:22 -08:00
JonnyWong16
100fdd1df1
Change export m3u8 to m3u extension
* m3u files are still encoded using UTF-8
2022-11-13 19:09:58 -08:00
JonnyWong16
1cdb55a6a9
Set SSLContext.minimum_version to TLSv1.2
`ssl.OP_NO_TLSv1` and `ssl.OP_NO_TLSv1_1` deprecated since Python 3.7 and OpenSSL 1.1.0.

Ref: #1870
2022-11-13 01:03:36 -08:00
JonnyWong16
977eec8a66
Disable TLS 1.0 and TLS 1.1 in webserver
Fixes #1870
2022-11-13 00:19:02 -08:00
JonnyWong16
05739ea959
pyOpenSSL only required for generating self-signed certificates 2022-11-13 00:17:28 -08:00
JonnyWong16
ed735e5fa3
Shorten winget workflow name 2022-11-12 17:57:44 -08:00
dependabot[bot]
76cc56a215
Bump cherrypy from 18.6.1 to 18.8.0 (#1796)
* Bump cherrypy from 18.6.1 to 18.8.0

Bumps [cherrypy](https://github.com/cherrypy/cherrypy) from 18.6.1 to 18.8.0.
- [Release notes](https://github.com/cherrypy/cherrypy/releases)
- [Changelog](https://github.com/cherrypy/cherrypy/blob/main/CHANGES.rst)
- [Commits](https://github.com/cherrypy/cherrypy/compare/v18.6.1...v18.8.0)

---
updated-dependencies:
- dependency-name: cherrypy
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update cherrypy==18.8.0

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>
2022-11-12 17:53:03 -08:00
dependabot[bot]
e79da07973
Bump plexapi from 4.12.1 to 4.13.1 (#1888)
Bumps [plexapi](https://github.com/pkkid/python-plexapi) from 4.12.1 to 4.13.1.
- [Release notes](https://github.com/pkkid/python-plexapi/releases)
- [Commits](https://github.com/pkkid/python-plexapi/compare/4.12.1...4.13.1)

---
updated-dependencies:
- dependency-name: plexapi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-11-12 17:29:35 -08:00
dependabot[bot]
3af08f0d07
Bump websocket-client from 1.3.2 to 1.4.2 (#1886)
* Bump websocket-client from 1.3.2 to 1.4.2

Bumps [websocket-client](https://github.com/websocket-client/websocket-client) from 1.3.2 to 1.4.2.
- [Release notes](https://github.com/websocket-client/websocket-client/releases)
- [Changelog](https://github.com/websocket-client/websocket-client/blob/master/ChangeLog)
- [Commits](https://github.com/websocket-client/websocket-client/compare/v1.3.2...v1.4.2)

---
updated-dependencies:
- dependency-name: websocket-client
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update websocket-client==1.4.2

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-11-12 17:29:02 -08:00
dependabot[bot]
a5ccc9a1a3
Bump pytz from 2022.1 to 2022.6 (#1880)
* Bump pytz from 2022.1 to 2022.6

Bumps [pytz](https://github.com/stub42/pytz) from 2022.1 to 2022.6.
- [Release notes](https://github.com/stub42/pytz/releases)
- [Commits](https://github.com/stub42/pytz/compare/release_2022.1...release_2022.6)

---
updated-dependencies:
- dependency-name: pytz
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update pytz==2022.6

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-11-12 17:26:45 -08:00
dependabot[bot]
8a4cb13b18
Bump tzdata from 2022.1 to 2022.6 (#1878)
* Bump tzdata from 2022.1 to 2022.6

Bumps [tzdata](https://github.com/python/tzdata) from 2022.1 to 2022.6.
- [Release notes](https://github.com/python/tzdata/releases)
- [Changelog](https://github.com/python/tzdata/blob/master/NEWS.md)
- [Commits](https://github.com/python/tzdata/compare/2022.1...2022.6)

---
updated-dependencies:
- dependency-name: tzdata
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update tzdata==2022.6

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-11-12 17:25:54 -08:00
dependabot[bot]
c2f47a52b4
Bump httpagentparser from 1.9.2 to 1.9.5 (#1869)
* Bump httpagentparser from 1.9.2 to 1.9.5

Bumps [httpagentparser](https://github.com/shon/httpagentparser) from 1.9.2 to 1.9.5.
- [Release notes](https://github.com/shon/httpagentparser/releases)
- [Commits](https://github.com/shon/httpagentparser/commits)

---
updated-dependencies:
- dependency-name: httpagentparser
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update httpagentparser==1.9.5

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-11-12 17:23:34 -08:00
dependabot[bot]
d26ce224ce
Bump mako from 1.2.0 to 1.2.3 (#1845)
* Bump mako from 1.2.0 to 1.2.3

Bumps [mako](https://github.com/sqlalchemy/mako) from 1.2.0 to 1.2.3.
- [Release notes](https://github.com/sqlalchemy/mako/releases)
- [Changelog](https://github.com/sqlalchemy/mako/blob/main/CHANGES)
- [Commits](https://github.com/sqlalchemy/mako/commits)

---
updated-dependencies:
- dependency-name: mako
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update mako==1.2.3

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-11-12 17:19:17 -08:00
dependabot[bot]
58aab24c28
Bump arrow from 1.2.2 to 1.2.3 (#1829)
* Bump arrow from 1.2.2 to 1.2.3

Bumps [arrow](https://github.com/arrow-py/arrow) from 1.2.2 to 1.2.3.
- [Release notes](https://github.com/arrow-py/arrow/releases)
- [Changelog](https://github.com/arrow-py/arrow/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/arrow-py/arrow/compare/1.2.2...1.2.3)

---
updated-dependencies:
- dependency-name: arrow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update arrow==1.2.3

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-11-12 17:18:11 -08:00
dependabot[bot]
76649be6e4
Bump tempora from 5.0.1 to 5.0.2 (#1795)
Bumps [tempora](https://github.com/jaraco/tempora) from 5.0.1 to 5.0.2.
- [Release notes](https://github.com/jaraco/tempora/releases)
- [Changelog](https://github.com/jaraco/tempora/blob/main/CHANGES.rst)
- [Commits](https://github.com/jaraco/tempora/compare/v5.0.1...v5.0.2)

---
updated-dependencies:
- dependency-name: tempora
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-11-12 17:14:13 -08:00
dependabot[bot]
af1aed0b6b
Bump requests from 2.27.1 to 2.28.1 (#1781)
* Bump requests from 2.27.1 to 2.28.1

Bumps [requests](https://github.com/psf/requests) from 2.27.1 to 2.28.1.
- [Release notes](https://github.com/psf/requests/releases)
- [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md)
- [Commits](https://github.com/psf/requests/compare/v2.27.1...v2.28.1)

---
updated-dependencies:
- dependency-name: requests
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update requests==2.28.1

* Update urllib3==1.26.12

* Update certifi==2022.9.24

* Update idna==3.4

* Update charset-normalizer==2.1.1

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-11-12 17:12:19 -08:00
dependabot[bot]
baa0e08c2a
Bump zipp from 3.8.0 to 3.10.0 (#1872)
* Bump zipp from 3.8.0 to 3.10.0

Bumps [zipp](https://github.com/jaraco/zipp) from 3.8.0 to 3.10.0.
- [Release notes](https://github.com/jaraco/zipp/releases)
- [Changelog](https://github.com/jaraco/zipp/blob/main/CHANGES.rst)
- [Commits](https://github.com/jaraco/zipp/compare/v3.8.0...v3.10.0)

---
updated-dependencies:
- dependency-name: zipp
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update zipp==3.10.0

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-11-12 17:12:06 -08:00
dependabot[bot]
d889e810f4
Bump bleach from 5.0.0 to 5.0.1 (#1777)
* Bump bleach from 5.0.0 to 5.0.1

Bumps [bleach](https://github.com/mozilla/bleach) from 5.0.0 to 5.0.1.
- [Release notes](https://github.com/mozilla/bleach/releases)
- [Changelog](https://github.com/mozilla/bleach/blob/main/CHANGES)
- [Commits](https://github.com/mozilla/bleach/commits)

---
updated-dependencies:
- dependency-name: bleach
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update bleach==5.0.1

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-11-12 17:11:49 -08:00
dependabot[bot]
21f5fee403
Bump idna from 3.3 to 3.4 (#1835)
Bumps [idna](https://github.com/kjd/idna) from 3.3 to 3.4.
- [Release notes](https://github.com/kjd/idna/releases)
- [Changelog](https://github.com/kjd/idna/blob/master/HISTORY.rst)
- [Commits](https://github.com/kjd/idna/compare/v3.3...v3.4)

---
updated-dependencies:
- dependency-name: idna
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-11-12 17:11:08 -08:00
dependabot[bot]
fbb016a7fd
Bump certifi from 2022.5.18.1 to 2022.9.24 (#1849)
Bumps [certifi](https://github.com/certifi/python-certifi) from 2022.5.18.1 to 2022.9.24.
- [Release notes](https://github.com/certifi/python-certifi/releases)
- [Commits](https://github.com/certifi/python-certifi/compare/2022.05.18.1...2022.09.24)

---
updated-dependencies:
- dependency-name: certifi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-11-12 17:10:37 -08:00
dependabot[bot]
cc0c4b638d
Bump urllib3 from 1.26.9 to 1.26.12 (#1823)
Bumps [urllib3](https://github.com/urllib3/urllib3) from 1.26.9 to 1.26.12.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/1.26.9...1.26.12)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-11-12 17:10:26 -08:00
JonnyWong16
c73450c0db
Update importlib-resources==5.10.0
[skip ci]
2022-11-12 17:05:05 -08:00
JonnyWong16
5bb9dbd0f6
Update importlib-metadata==5.0.0
[skip ci]
2022-11-12 17:04:54 -08:00
dependabot[bot]
20be945e8c
Bump mshick/add-pr-comment from 1 to 2 (#1885)
Bumps [mshick/add-pr-comment](https://github.com/mshick/add-pr-comment) from 1 to 2.
- [Release notes](https://github.com/mshick/add-pr-comment/releases)
- [Commits](https://github.com/mshick/add-pr-comment/compare/v1...v2)

---
updated-dependencies:
- dependency-name: mshick/add-pr-comment
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-11-12 17:03:24 -08:00
dependabot[bot]
b11b7e62e9
Bump pyinstaller from 5.1 to 5.6.2 (#1879)
Bumps [pyinstaller](https://github.com/pyinstaller/pyinstaller) from 5.1 to 5.6.2.
- [Release notes](https://github.com/pyinstaller/pyinstaller/releases)
- [Changelog](https://github.com/pyinstaller/pyinstaller/blob/develop/doc/CHANGES.rst)
- [Commits](https://github.com/pyinstaller/pyinstaller/compare/v5.1...v5.6.2)

---
updated-dependencies:
- dependency-name: pyinstaller
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>
2022-11-12 17:00:58 -08:00
dependabot[bot]
76a3b3d2a5
Bump importlib-resources from 5.7.1 to 5.10.0 (#1862)
Bumps [importlib-resources](https://github.com/python/importlib_resources) from 5.7.1 to 5.10.0.
- [Release notes](https://github.com/python/importlib_resources/releases)
- [Changelog](https://github.com/python/importlib_resources/blob/main/CHANGES.rst)
- [Commits](https://github.com/python/importlib_resources/compare/v5.7.1...v5.10.0)

---
updated-dependencies:
- dependency-name: importlib-resources
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-11-12 17:00:33 -08:00
dependabot[bot]
65452f08ce
Bump importlib-metadata from 4.11.4 to 5.0.0 (#1856)
Bumps [importlib-metadata](https://github.com/python/importlib_metadata) from 4.11.4 to 5.0.0.
- [Release notes](https://github.com/python/importlib_metadata/releases)
- [Changelog](https://github.com/python/importlib_metadata/blob/main/CHANGES.rst)
- [Commits](https://github.com/python/importlib_metadata/compare/v4.11.4...v5.0.0)

---
updated-dependencies:
- dependency-name: importlib-metadata
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-12 16:52:22 -08:00
dependabot[bot]
75e492556b
Bump pyobjc-core from 8.5 to 9.0 (#1887)
Bumps [pyobjc-core](https://github.com/ronaldoussoren/pyobjc) from 8.5 to 9.0.
- [Release notes](https://github.com/ronaldoussoren/pyobjc/releases)
- [Changelog](https://github.com/ronaldoussoren/pyobjc/blob/master/docs/changelog.rst)
- [Commits](https://github.com/ronaldoussoren/pyobjc/compare/v8.5...v9.0)

---
updated-dependencies:
- dependency-name: pyobjc-core
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-11-12 16:52:16 -08:00
dependabot[bot]
a58a4ed632
Bump pycryptodomex from 3.14.1 to 3.15.0 (#1773)
Bumps [pycryptodomex](https://github.com/Legrandin/pycryptodome) from 3.14.1 to 3.15.0.
- [Release notes](https://github.com/Legrandin/pycryptodome/releases)
- [Changelog](https://github.com/Legrandin/pycryptodome/blob/master/Changelog.rst)
- [Commits](https://github.com/Legrandin/pycryptodome/compare/v3.14.1...v3.15.0)

---
updated-dependencies:
- dependency-name: pycryptodomex
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-11-12 16:51:59 -08:00
Paarth Shah
2ea85f31e7
Bump minimum python version in CONTRIBUTING.md. (#1854)
* Bump minimum python version in CONTRIBUTING.md.

* Fix random typo.

* More touch-ups.

[skip ci]
2022-11-12 16:48:40 -08:00
JonnyWong16
38cc3bc7f2
Update winget workflow token
[skip ci]
2022-11-07 13:34:25 -08:00
JonnyWong16
04d6bfbd78
Fix publish installer changelog 2022-11-07 13:22:37 -08:00
JonnyWong16
d7f6a1b186
v2.10.5 2022-11-07 13:14:21 -08:00
JonnyWong16
b1e2a30dc1
Fix publish installer workflow 2022-11-07 12:31:17 -08:00
JonnyWong16
defbbbff92
Patch set-output to environment files in GitHub workflow
* https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/
2022-11-07 12:22:30 -08:00
JonnyWong16
7c8cea74e4
Fix padding on condition operator dropdown on small screens 2022-11-07 12:05:04 -08:00
JonnyWong16
a3ad40122d
Add months timeframe for newsletters
* Closes #1876
2022-11-07 11:26:13 -08:00
JonnyWong16
b74a1a3c32
Separate stdout and stderr console logging
* Closes #1874
2022-11-07 10:41:49 -08:00
JonnyWong16
894eaf0365
Check IPv6 HTTP host when retrieving app URL 2022-11-07 10:41:49 -08:00
JonnyWong16
ed53d66aa7
Launch browser with IPv6 http_host 2022-11-07 10:41:49 -08:00
dependabot[bot]
c53566225c
Bump actions/cache from 3.0.8 to 3.0.11 (#1864)
Bumps [actions/cache](https://github.com/actions/cache) from 3.0.8 to 3.0.11.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3.0.8...v3.0.11)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-11-07 09:30:51 -08:00
dependabot[bot]
a1b6c35bd2
Bump actions/setup-python from 4.2.0 to 4.3.0 (#1859)
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4.2.0 to 4.3.0.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v4.2.0...v4.3.0)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-11-07 09:30:40 -08:00
dependabot[bot]
1977ca7db2
Bump actions/checkout from 3.0.2 to 3.1.0 (#1858)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3.0.2 to 3.1.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3.0.2...v3.1.0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-11-07 09:30:30 -08:00
dependabot[bot]
4b97382b7c
Bump actions/stale from 5 to 6 (#1844)
Bumps [actions/stale](https://github.com/actions/stale) from 5 to 6.
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/stale/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/stale
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-11-07 09:30:19 -08:00
JonnyWong16
5975b59c93
Add user_thumb to get_history response 2022-11-05 20:03:48 +00:00
JonnyWong16
571bbb2db1
Fallback season thumb to show thumb in get_metadata 2022-11-01 16:37:36 +00:00
JonnyWong16
08bc365a7c
Add collections to get_children_metadata API data 2022-10-25 16:11:37 +00:00
JonnyWong16
e6829310ad
Validate notifier custom conditions before saving
* Fixes #1846
2022-10-10 19:05:13 +00:00
JonnyWong16
a9949a07da
Update filterer
* Clear condition operator if type changes
2022-10-10 18:05:14 +00:00
JonnyWong16
5faeafedd5
Fix API 400 response code 2022-10-01 19:24:57 +00:00
JonnyWong16
0f872ab440
Fix broken link on library stats cards 2022-09-27 23:41:27 +00:00
JonnyWong16
15afbe3001
Add edition_title notification parameter 2022-09-27 23:28:48 +00:00
JonnyWong16
a8be53e0dc
Add edition_title to metadata details 2022-09-27 21:18:52 +00:00
JonnyWong16
90cf863305
Update pip cache in publish-installers workflow 2022-09-15 10:05:57 -07:00
JonnyWong16
5ace9e163d
Set default library count to 0 2022-09-12 16:47:06 -07:00
JonnyWong16
54af528f6c
Add submit-winget.yml workflow 2022-09-10 09:17:01 -07:00
JonnyWong16
0a6b12329c
Link to MusicBrainz track for notifications 2022-09-09 16:14:31 -07:00
JonnyWong16
41b796e007
v2.10.4 2022-09-05 11:07:25 -07:00
dependabot[bot]
925efe0db7
Bump actions/cache from 3.0.6 to 3.0.8 (#1822)
Bumps [actions/cache](https://github.com/actions/cache) from 3.0.6 to 3.0.8.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3.0.6...v3.0.8)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-08-26 09:17:13 -07:00
JonnyWong16
806c8814b6
Unpin QEMU in snap workflow 2022-08-23 11:22:54 -07:00
JonnyWong16
70256dd0b9
Update snap login in workflow 2022-08-23 10:48:01 -07:00
JonnyWong16
ac520d656b
Add "does not begin with" and "does not end with" condition operators 2022-08-19 10:41:02 -07:00
JonnyWong16
3c40f83738
Update filterer.jquery.js 2022-08-19 10:36:10 -07:00
JonnyWong16
b2777e30f2
Add git safe directory to snapcraft.yml 2022-08-18 16:40:38 -07:00
JonnyWong16
84fb1a2dc2
Add quality profile tooltip 2022-08-18 16:35:06 -07:00
JonnyWong16
b73f8cc30e
Remove unused architectures in snap workflow 2022-08-18 10:14:14 -07:00
JonnyWong16
c3572f3212
Add workflow dispatch to workflows
[skip ci]
2022-08-18 10:11:47 -07:00
JonnyWong16
85dda97e52
Fix library stats not shown for libraries without history
* Fixes #1818
2022-08-18 10:02:32 -07:00
JonnyWong16
8114079eac
Fix album count showing 0 2022-08-11 09:46:15 -07:00
JonnyWong16
444b138e97
Update v2.10.3 changelog 2022-08-09 17:22:49 -07:00
JonnyWong16
24f05a03c0
v2.10.3 2022-08-09 17:20:12 -07:00
JonnyWong16
b83e9e3257
Fix channel identifier overflow on activity card
* Fixes #1802
2022-08-09 17:16:07 -07:00
JonnyWong16
2a83502ca1
Username log filter longer than 3 characters 2022-08-09 17:02:47 -07:00
JonnyWong16
0bf1329d03
Add guides to Artist, Album, and Track export attributes 2022-08-09 17:02:47 -07:00
JonnyWong16
c1f433ab60
Add languageTag to export attribute 2022-08-09 16:44:41 -07:00
dependabot[bot]
23c881f4d1
Bump plexapi from 4.11.2 to 4.12.1 (#1811)
Bumps [plexapi](https://github.com/pkkid/python-plexapi) from 4.11.2 to 4.12.1.
- [Release notes](https://github.com/pkkid/python-plexapi/releases)
- [Commits](https://github.com/pkkid/python-plexapi/compare/4.11.2...4.12.1)

---
updated-dependencies:
- dependency-name: plexapi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-09 16:38:19 -07:00
dependabot[bot]
e85834706a
Bump actions/cache from 3.0.5 to 3.0.6 (#1808)
Bumps [actions/cache](https://github.com/actions/cache) from 3.0.5 to 3.0.6.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3.0.5...v3.0.6)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-08-09 16:36:16 -07:00
dependabot[bot]
8327c48111
Bump actions/setup-python from 4.1.0 to 4.2.0 (#1805)
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4.1.0 to 4.2.0.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v4.1.0...v4.2.0)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-08-09 16:36:03 -07:00
JonnyWong16
e7b96ed9ae
Only filter usernames >= 3 characters
* Fixes #1806
2022-08-03 10:52:58 -07:00
JonnyWong16
3ab0c8fe22
Use last played artwork on library stats cards 2022-08-01 15:02:51 -07:00
JonnyWong16
4ae1f6cef9
Allow fallback to another pms image 2022-08-01 14:48:35 -07:00
JonnyWong16
3631e723bc
Add last played metadata to library stats 2022-08-01 14:47:16 -07:00
JonnyWong16
ddcf350353
Add last played metadata to home stats top_libraries and top_users 2022-08-01 14:46:32 -07:00
JonnyWong16
2b339f60bc
Update PayPal URL 2022-07-29 20:04:08 -07:00
JonnyWong16
52e399144a
Add Stripe donation 2022-07-29 19:40:50 -07:00
JonnyWong16
c45ded5a98
Implement X-Plex-Container-Size for retrieving library items
PMS 1.28 log warning
```
Missing X-Plex-Container-Size header. This will fail with status code 400 in the future.
```
2022-07-24 14:13:22 -07:00
JonnyWong16
85eaed57e0
Clean up lookup table unique constraints on start up 2022-07-18 15:33:37 -07:00
JonnyWong16
6e6de11aaa
Cache usernames for log filter
* Fixes #1705
2022-07-18 15:31:23 -07:00
JonnyWong16
9f1b7de623
Add show_year notification parameter 2022-07-14 18:45:00 -07:00
JonnyWong16
aeb572f211
Add parent_year and grandparent_year to get_metadata_details 2022-07-14 18:39:59 -07:00
JonnyWong16
b598e9fc0c
Make sure token is saved before populating server dropdown list 2022-07-14 16:14:10 -07:00
JonnyWong16
b6cb946ae2
Add json support for MQTT notifications 2022-07-14 12:29:12 -07:00
dependabot[bot]
39afabf1b7
Bump actions/cache from 3.0.4 to 3.0.5 (#1792)
Bumps [actions/cache](https://github.com/actions/cache) from 3.0.4 to 3.0.5.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3.0.4...v3.0.5)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-07-14 12:14:34 -07:00
dependabot[bot]
8cbf93fda4
Bump actions/setup-python from 4.0.0 to 4.1.0 (#1788)
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4.0.0 to 4.1.0.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v4.0.0...v4.1.0)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-07-14 12:14:16 -07:00
dependabot[bot]
03938ebf2c
Bump actions/setup-python from 3.1.2 to 4.0.0 (#1760)
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 3.1.2 to 4.0.0.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v3.1.2...v4.0.0)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-03 15:41:07 -07:00
dependabot[bot]
167a914e20
Bump joncloud/makensis-action from 3.6 to 3.7 (#1775)
Bumps [joncloud/makensis-action](https://github.com/joncloud/makensis-action) from 3.6 to 3.7.
- [Release notes](https://github.com/joncloud/makensis-action/releases)
- [Commits](https://github.com/joncloud/makensis-action/compare/v3.6...v3.7)

---
updated-dependencies:
- dependency-name: joncloud/makensis-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-07-03 15:40:56 -07:00
dependabot[bot]
7f15c00f7e
Bump actions/cache from 3.0.3 to 3.0.4 (#1759)
Bumps [actions/cache](https://github.com/actions/cache) from 3.0.3 to 3.0.4.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3.0.3...v3.0.4)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-07-03 15:40:36 -07:00
dependabot[bot]
22070ea549
Bump technote-space/workflow-conclusion-action from 2.2 to 3.0 (#1758)
Bumps [technote-space/workflow-conclusion-action](https://github.com/technote-space/workflow-conclusion-action) from 2.2 to 3.0.
- [Release notes](https://github.com/technote-space/workflow-conclusion-action/releases)
- [Changelog](https://github.com/technote-space/workflow-conclusion-action/blob/main/.releasegarc)
- [Commits](https://github.com/technote-space/workflow-conclusion-action/compare/v2.2...v3.0)

---
updated-dependencies:
- dependency-name: technote-space/workflow-conclusion-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-07-03 15:40:24 -07:00
JonnyWong16
746d44922e
v2.10.2 2022-07-03 15:32:18 -07:00
JonnyWong16
ec99a2f3d9
Fix updating cron default value in newsletter table 2022-07-03 15:22:25 -07:00
JonnyWong16
d482177342
Update Python version in readme 2022-07-03 15:22:25 -07:00
JonnyWong16
285fa3a0f5
Clear user friendly name if equal to username 2022-07-03 15:22:25 -07:00
JonnyWong16
98433a7c54
Fix username not showing on home stats cards 2022-07-03 15:22:25 -07:00
JonnyWong16
d4def8e5ae
Fix cover size on info page 2022-07-03 15:22:25 -07:00
TheMeanCanEHdian
ed103bc5de
Update library icons to latest Plex style. (#1779)
* Update library icons to latest Plex style.

* Add back mistakenly removed live icons.

* Update live icon to match new style.

* Adjust icon size.

* Better center icon.
2022-07-03 13:31:33 -07:00
JonnyWong16
15fb052716
Fix stream audio info when playing a secondary track 2022-06-04 17:57:57 -07:00
JonnyWong16
ebbd66d056
Fix empty recently added error message 2022-06-03 09:41:36 -07:00
JonnyWong16
a2c589fca4
v2.10.1 2022-06-01 10:18:41 -07:00
JonnyWong16
1bd5168156
Fix OneSignal device validation
* OneSignal changed to only accept `app_id` as a query parameter instead of in the payload
2022-06-01 09:42:47 -07:00
JonnyWong16
0576920b89
Add support for mbid:// guids in notification parameters
* Requires PMS 1.27.0.5849
2022-05-31 10:57:44 -07:00
JonnyWong16
ca3c2b09cf
Add grandparent_guids and parent_guids to get_metadata_details 2022-05-31 10:57:44 -07:00
dependabot[bot]
695a00350b
Bump actions/cache from 3.0.2 to 3.0.3 (#1755)
Bumps [actions/cache](https://github.com/actions/cache) from 3.0.2 to 3.0.3.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3.0.2...v3.0.3)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-05-31 10:52:15 -07:00
dependabot[bot]
b7ca9c3502
Bump plexapi from 4.11.1 to 4.11.2 (#1753)
* Bump plexapi from 4.11.1 to 4.11.2

Bumps [plexapi](https://github.com/pkkid/python-plexapi) from 4.11.1 to 4.11.2.
- [Release notes](https://github.com/pkkid/python-plexapi/releases)
- [Commits](https://github.com/pkkid/python-plexapi/compare/4.11.1...4.11.2)

---
updated-dependencies:
- dependency-name: plexapi
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update plexapi==4.11.2

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-05-31 10:51:53 -07:00
JonnyWong16
fffd936b0f
Change shared_libraries to list for get_user 2022-05-28 15:03:52 -07:00
JonnyWong16
bd83c8d55c
Remove server_token from get_users 2022-05-28 15:03:24 -07:00
JonnyWong16
b905e0139c
Add continent to geoip lookup 2022-05-28 08:51:10 -07:00
JonnyWong16
b9b844dd38
Check for a blank user_id when retrieving user details 2022-05-25 11:37:32 -07:00
JonnyWong16
e9d370a4b4
v2.10.0 2022-05-23 10:57:21 -07:00
dependabot[bot]
248fb6724a
Bump importlib-metadata from 4.11.3 to 4.11.4 (#1750)
* Bump importlib-metadata from 4.11.3 to 4.11.4

Bumps [importlib-metadata](https://github.com/python/importlib_metadata) from 4.11.3 to 4.11.4.
- [Release notes](https://github.com/python/importlib_metadata/releases)
- [Changelog](https://github.com/python/importlib_metadata/blob/main/CHANGES.rst)
- [Commits](https://github.com/python/importlib_metadata/compare/v4.11.3...v4.11.4)

---
updated-dependencies:
- dependency-name: importlib-metadata
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update importlib-metadata==4.11.4

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-05-23 10:45:11 -07:00
JonnyWong16
bb01141cfc
Fix parsing Windows path on Linux for notification filename parameter 2022-05-20 10:57:58 -07:00
dependabot[bot]
775375b8b4
Bump plexapi from 4.11.0 to 4.11.1 (#1749)
* Bump plexapi from 4.11.0 to 4.11.1

Bumps [plexapi](https://github.com/pkkid/python-plexapi) from 4.11.0 to 4.11.1.
- [Release notes](https://github.com/pkkid/python-plexapi/releases)
- [Commits](https://github.com/pkkid/python-plexapi/compare/4.11.0...4.11.1)

---
updated-dependencies:
- dependency-name: plexapi
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update plexapi==4.11.1

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-05-20 10:57:35 -07:00
dependabot[bot]
010377870d
Bump certifi from 2021.10.8 to 2022.5.18.1 (#1748)
* Bump certifi from 2021.10.8 to 2022.5.18.1

Bumps [certifi](https://github.com/certifi/python-certifi) from 2021.10.8 to 2022.5.18.1.
- [Release notes](https://github.com/certifi/python-certifi/releases)
- [Commits](https://github.com/certifi/python-certifi/compare/2021.10.08...2022.05.18.1)

---
updated-dependencies:
- dependency-name: certifi
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update certifi==2022.5.18.1

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-05-20 10:57:24 -07:00
JonnyWong16
9967786997
Add new exporter fields for PlexAPI 4.11.0 2022-05-18 11:25:25 -07:00
JonnyWong16
07eba40096
Update default user thumb image 2022-05-18 11:24:41 -07:00
dependabot[bot]
399fd6ff91
Bump plexapi from 4.9.2 to 4.11.0 (#1690)
* Bump plexapi from 4.9.2 to 4.10.1

Bumps [plexapi](https://github.com/pkkid/python-plexapi) from 4.9.2 to 4.10.1.
- [Release notes](https://github.com/pkkid/python-plexapi/releases)
- [Commits](https://github.com/pkkid/python-plexapi/compare/4.9.2...4.10.1)

---
updated-dependencies:
- dependency-name: plexapi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update plexapi==4.11.0

* Update requirements.txt

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-05-18 11:24:15 -07:00
dependabot[bot]
f1b95f5837
Bump pyinstaller from 5.0.1 to 5.1 (#1746)
Bumps [pyinstaller](https://github.com/pyinstaller/pyinstaller) from 5.0.1 to 5.1.
- [Release notes](https://github.com/pyinstaller/pyinstaller/releases)
- [Changelog](https://github.com/pyinstaller/pyinstaller/blob/develop/doc/CHANGES.rst)
- [Commits](https://github.com/pyinstaller/pyinstaller/compare/v5.0.1...v5.1)

---
updated-dependencies:
- dependency-name: pyinstaller
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-05-18 10:54:11 -07:00
dependabot[bot]
abc01bb64d
Bump actions/cache from 3.0.1 to 3.0.2 (#1745)
Bumps [actions/cache](https://github.com/actions/cache) from 3.0.1 to 3.0.2.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3.0.1...v3.0.2)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-05-18 10:45:00 -07:00
dependabot[bot]
bdc907ee67
Bump actions/setup-python from 3.1.1 to 3.1.2 (#1744)
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 3.1.1 to 3.1.2.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v3.1.1...v3.1.2)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-05-18 10:44:47 -07:00
JonnyWong16
1e99d51fe2
Add importlib-metadata to requirements-package.txt 2022-05-16 22:37:28 -07:00
JonnyWong16
27b72663d4
Fix platform in get_home_stats and get_user_player_stats API docs 2022-05-16 21:25:18 -07:00
dependabot[bot]
bff4a3f6ba
Bump pyinstaller from 4.9 to 5.0.1 (#1730)
Bumps [pyinstaller](https://github.com/pyinstaller/pyinstaller) from 4.9 to 5.0.1.
- [Release notes](https://github.com/pyinstaller/pyinstaller/releases)
- [Changelog](https://github.com/pyinstaller/pyinstaller/blob/develop/doc/CHANGES.rst)
- [Commits](https://github.com/pyinstaller/pyinstaller/compare/v4.9...v5.0.1)

---
updated-dependencies:
- dependency-name: pyinstaller
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-05-16 21:24:56 -07:00
dependabot[bot]
ba3275fed7
Bump pywin32 from 303 to 304 (#1732)
Bumps [pywin32](https://github.com/mhammond/pywin32) from 303 to 304.
- [Release notes](https://github.com/mhammond/pywin32/releases)
- [Changelog](https://github.com/mhammond/pywin32/blob/main/CHANGES.txt)
- [Commits](https://github.com/mhammond/pywin32/commits)

---
updated-dependencies:
- dependency-name: pywin32
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-05-16 21:23:40 -07:00
dependabot[bot]
df58950585
Bump pyobjc-core from 8.2 to 8.5 (#1725)
Bumps [pyobjc-core](https://github.com/ronaldoussoren/pyobjc) from 8.2 to 8.5.
- [Release notes](https://github.com/ronaldoussoren/pyobjc/releases)
- [Changelog](https://github.com/ronaldoussoren/pyobjc/blob/master/docs/changelog.rst)
- [Commits](https://github.com/ronaldoussoren/pyobjc/compare/v8.2...v8.5)

---
updated-dependencies:
- dependency-name: pyobjc-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-05-16 21:23:21 -07:00
dependabot[bot]
fbf22b5ea2
Bump pyobjc-framework-cocoa from 8.2 to 8.5 (#1724)
Bumps [pyobjc-framework-cocoa](https://github.com/ronaldoussoren/pyobjc) from 8.2 to 8.5.
- [Release notes](https://github.com/ronaldoussoren/pyobjc/releases)
- [Changelog](https://github.com/ronaldoussoren/pyobjc/blob/master/docs/changelog.rst)
- [Commits](https://github.com/ronaldoussoren/pyobjc/compare/v8.2...v8.5)

---
updated-dependencies:
- dependency-name: pyobjc-framework-cocoa
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-05-16 21:21:44 -07:00
dependabot[bot]
1af3c43320
Bump docker/login-action from 1 to 2 (#1738)
Bumps [docker/login-action](https://github.com/docker/login-action) from 1 to 2.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/v1...v2)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-05-16 21:21:18 -07:00
dependabot[bot]
228101fd48
Bump docker/setup-buildx-action from 1 to 2 (#1737)
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 1 to 2.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v1...v2)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-05-16 21:21:09 -07:00
dependabot[bot]
54a878f7b1
Bump docker/setup-qemu-action from 1 to 2 (#1736)
Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 1 to 2.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/v1...v2)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-05-16 21:21:01 -07:00
dependabot[bot]
9df184d7f4
Bump docker/build-push-action from 2 to 3 (#1735)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 2 to 3.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Upgrade guide](https://github.com/docker/build-push-action/blob/master/UPGRADE.md)
- [Commits](https://github.com/docker/build-push-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-05-16 21:20:50 -07:00
dependabot[bot]
86dd3f0db7
Bump actions/checkout from 2 to 3.0.2 (#1726)
Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3.0.2.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2...v3.0.2)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-05-16 21:20:39 -07:00
dependabot[bot]
89d9fcac66
Bump actions/stale from 4 to 5 (#1714)
Bumps [actions/stale](https://github.com/actions/stale) from 4 to 5.
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/stale/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/stale
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-05-16 21:20:19 -07:00
dependabot[bot]
f6214c9a98
Bump actions/upload-artifact from 2 to 3 (#1712)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 2 to 3.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-05-16 21:20:05 -07:00
dependabot[bot]
988c6750aa
Bump actions/download-artifact from 2 to 3 (#1711)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 2 to 3.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-05-16 21:19:54 -07:00
dependabot[bot]
04b745b584
Bump actions/setup-python from 2 to 3.1.1 (#1709)
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 2 to 3.1.1.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v2...v3.1.1)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-05-16 21:19:14 -07:00
dependabot[bot]
f068bcca01
Bump actions/cache from 2 to 3.0.1 (#1701)
Bumps [actions/cache](https://github.com/actions/cache) from 2 to 3.0.1.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v2...v3.0.1)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

[skip ci]
2022-05-16 21:17:59 -07:00
dependabot[bot]
b3aeaafd00
Bump pyjwt from 2.3.0 to 2.4.0 (#1743)
* Bump pyjwt from 2.3.0 to 2.4.0

Bumps [pyjwt](https://github.com/jpadilla/pyjwt) from 2.3.0 to 2.4.0.
- [Release notes](https://github.com/jpadilla/pyjwt/releases)
- [Changelog](https://github.com/jpadilla/pyjwt/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/jpadilla/pyjwt/compare/2.3.0...2.4.0)

---
updated-dependencies:
- dependency-name: pyjwt
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update pyjwt==2.4.0

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-05-16 20:56:13 -07:00
dependabot[bot]
d17015de44
Bump pyparsing from 3.0.7 to 3.0.9 (#1741)
* Bump pyparsing from 3.0.7 to 3.0.9

Bumps [pyparsing](https://github.com/pyparsing/pyparsing) from 3.0.7 to 3.0.9.
- [Release notes](https://github.com/pyparsing/pyparsing/releases)
- [Changelog](https://github.com/pyparsing/pyparsing/blob/master/CHANGES)
- [Commits](https://github.com/pyparsing/pyparsing/compare/pyparsing_3.0.7...pyparsing_3.0.9)

---
updated-dependencies:
- dependency-name: pyparsing
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update pyparsing==3.0.9

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-05-16 20:55:40 -07:00
dependabot[bot]
93b6370759
Bump xmltodict from 0.12.0 to 0.13.0 (#1739)
* Bump xmltodict from 0.12.0 to 0.13.0

Bumps [xmltodict](https://github.com/martinblech/xmltodict) from 0.12.0 to 0.13.0.
- [Release notes](https://github.com/martinblech/xmltodict/releases)
- [Changelog](https://github.com/martinblech/xmltodict/blob/master/CHANGELOG.md)
- [Commits](https://github.com/martinblech/xmltodict/compare/v0.12.0...v0.13.0)

---
updated-dependencies:
- dependency-name: xmltodict
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update xmltodict==0.13.0

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-05-16 20:55:30 -07:00
dependabot[bot]
55812ce2f0
Bump importlib-resources from 5.6.0 to 5.7.1 (#1723)
* Bump importlib-resources from 5.6.0 to 5.7.1

Bumps [importlib-resources](https://github.com/python/importlib_resources) from 5.6.0 to 5.7.1.
- [Release notes](https://github.com/python/importlib_resources/releases)
- [Changelog](https://github.com/python/importlib_resources/blob/main/CHANGES.rst)
- [Commits](https://github.com/python/importlib_resources/compare/v5.6.0...v5.7.1)

---
updated-dependencies:
- dependency-name: importlib-resources
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update importlib-resources==5.7.1

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-05-16 20:48:17 -07:00
dependabot[bot]
467ae352f5
Bump beautifulsoup4 from 4.10.0 to 4.11.1 (#1717)
* Bump beautifulsoup4 from 4.10.0 to 4.11.1

Bumps [beautifulsoup4](https://www.crummy.com/software/BeautifulSoup/bs4/) from 4.10.0 to 4.11.1.

---
updated-dependencies:
- dependency-name: beautifulsoup4
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update beautifulsoup4==4.11.1

* Update soupsieve==2.3.2.post1

* Update requirements.txt

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-05-16 20:46:21 -07:00
dependabot[bot]
a1fe0b04d7
Bump bleach from 4.1.0 to 5.0.0 (#1708)
* Bump bleach from 4.1.0 to 5.0.0

Bumps [bleach](https://github.com/mozilla/bleach) from 4.1.0 to 5.0.0.
- [Release notes](https://github.com/mozilla/bleach/releases)
- [Changelog](https://github.com/mozilla/bleach/blob/main/CHANGES)
- [Commits](https://github.com/mozilla/bleach/compare/v4.1.0...v5.0.0)

---
updated-dependencies:
- dependency-name: bleach
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update bleach==5.0.0

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-05-16 20:41:47 -07:00
dependabot[bot]
d510e0f600
Bump websocket-client from 1.2.3 to 1.3.2 (#1700)
* Bump websocket-client from 1.2.3 to 1.3.2

Bumps [websocket-client](https://github.com/websocket-client/websocket-client) from 1.2.3 to 1.3.2.
- [Release notes](https://github.com/websocket-client/websocket-client/releases)
- [Changelog](https://github.com/websocket-client/websocket-client/blob/master/ChangeLog)
- [Commits](https://github.com/websocket-client/websocket-client/compare/v1.2.3...v1.3.2)

---
updated-dependencies:
- dependency-name: websocket-client
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update websocket-client==1.3.2

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-05-16 20:40:13 -07:00
dependabot[bot]
67baf9a260
Bump urllib3 from 1.26.8 to 1.26.9 (#1688)
* Bump urllib3 from 1.26.8 to 1.26.9

Bumps [urllib3](https://github.com/urllib3/urllib3) from 1.26.8 to 1.26.9.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/1.26.9/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/1.26.8...1.26.9)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update urllib3==1.26.9

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-05-16 20:35:58 -07:00
dependabot[bot]
238afb4794
Bump mako from 1.1.6 to 1.2.0 (#1684)
* Bump mako from 1.1.6 to 1.2.0

Bumps [mako](https://github.com/sqlalchemy/mako) from 1.1.6 to 1.2.0.
- [Release notes](https://github.com/sqlalchemy/mako/releases)
- [Changelog](https://github.com/sqlalchemy/mako/blob/main/CHANGES)
- [Commits](https://github.com/sqlalchemy/mako/commits)

---
updated-dependencies:
- dependency-name: mako
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update mako==1.2.0

* Update MarkupSafe==2.1.1

* Add importlib-metadata==4.11.3

* Update requirements.txt

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-05-16 20:33:50 -07:00
dependabot[bot]
aa0c58ef0e
Bump dnspython from 2.2.0 to 2.2.1 (#1679)
* Bump dnspython from 2.2.0 to 2.2.1

Bumps [dnspython](https://github.com/rthalley/dnspython) from 2.2.0 to 2.2.1.
- [Release notes](https://github.com/rthalley/dnspython/releases)
- [Changelog](https://github.com/rthalley/dnspython/blob/master/doc/whatsnew.rst)
- [Commits](https://github.com/rthalley/dnspython/compare/v2.2.0...v2.2.1)

---
updated-dependencies:
- dependency-name: dnspython
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update dnspython==2.2.1

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-05-16 20:33:29 -07:00
dependabot[bot]
54c9214b03
Bump apscheduler from 3.8.0 to 3.9.1 (#1675)
* Bump apscheduler from 3.8.0 to 3.9.1

Bumps [apscheduler](https://github.com/agronholm/apscheduler) from 3.8.0 to 3.9.1.
- [Release notes](https://github.com/agronholm/apscheduler/releases)
- [Changelog](https://github.com/agronholm/apscheduler/blob/3.9.1/docs/versionhistory.rst)
- [Commits](https://github.com/agronholm/apscheduler/compare/3.8.0...3.9.1)

---
updated-dependencies:
- dependency-name: apscheduler
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update apscheduler==3.9.1

* Update pytz==2022.1

* Add pytz-deprecation-shim==0.1.0.post0

* Update tzdata==2022.1

* Update tzlocal==4.2

* Update requirements.txt

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-05-16 20:32:37 -07:00
dependabot[bot]
942e09e59e
Bump distro from 1.6.0 to 1.7.0 (#1658)
* Bump distro from 1.6.0 to 1.7.0

Bumps [distro](https://github.com/python-distro/distro) from 1.6.0 to 1.7.0.
- [Release notes](https://github.com/python-distro/distro/releases)
- [Changelog](https://github.com/python-distro/distro/blob/master/CHANGELOG.md)
- [Commits](https://github.com/python-distro/distro/compare/v1.6.0...v1.7.0)

---
updated-dependencies:
- dependency-name: distro
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update distro==1.7.0

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-05-16 20:32:27 -07:00
JonnyWong16
f08687b960
Fix Dolby Vision detection
* Plex renamed "Dolby Vision" to "DoVi" in the display title.
2022-05-12 13:11:14 -07:00
JonnyWong16
1f7ddd4d01
Disable PlexAPI auto-reloading when fetching collections/playlists 2022-05-12 13:11:14 -07:00
JonnyWong16
df54df5e65
Set PlexAPI log level to debug 2022-04-26 11:02:07 -07:00
JonnyWong16
1371cfa889
Trigger on_stop notification after successful db write
* Prevent race condition from querying the db using the notification trigger
2022-04-14 20:54:07 -07:00
dependabot[bot]
dd59a79005
Bump zipp from 3.7.0 to 3.8.0 (#1706)
* Bump zipp from 3.7.0 to 3.8.0

Bumps [zipp](https://github.com/jaraco/zipp) from 3.7.0 to 3.8.0.
- [Release notes](https://github.com/jaraco/zipp/releases)
- [Changelog](https://github.com/jaraco/zipp/blob/main/CHANGES.rst)
- [Commits](https://github.com/jaraco/zipp/compare/v3.7.0...v3.8.0)

---
updated-dependencies:
- dependency-name: zipp
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update zipp==3.8.0

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-04-12 18:42:59 -07:00
dependabot[bot]
fa52d99691
Bump importlib-resources from 5.4.0 to 5.6.0 (#1699)
* Bump importlib-resources from 5.4.0 to 5.6.0

Bumps [importlib-resources](https://github.com/python/importlib_resources) from 5.4.0 to 5.6.0.
- [Release notes](https://github.com/python/importlib_resources/releases)
- [Changelog](https://github.com/python/importlib_resources/blob/main/CHANGES.rst)
- [Commits](https://github.com/python/importlib_resources/compare/v5.4.0...v5.6.0)

---
updated-dependencies:
- dependency-name: importlib-resources
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update importlib-resources==5.6.0

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
2022-04-12 18:42:48 -07:00
JonnyWong16
c54c811eec
Update jaraco.text==3.7.0
[skip ci]
2022-04-12 18:41:08 -07:00
JonnyWong16
d5afac4104
Add jaraco.context==4.1.1
[skip ci]
2022-04-12 18:40:59 -07:00
JonnyWong16
3e8ef3c1b4
Update jaraco.collections==3.5.1
[skip ci]
2022-04-12 18:40:47 -07:00
JonnyWong16
addf9ea324
Update jaraco.classes==3.2.1
[skip ci]
2022-04-12 18:40:33 -07:00
JonnyWong16
ab24116b1b
Fix get_users_table API docs 2022-04-12 13:28:13 -07:00
JonnyWong16
d4002b2e2b
Change values to int instead of "Checked" in users and libraries table
* get_users_table: allow_guest, do_notify, keep_history
* get_libraries_table: do_notify, do_notify_created, keep_history
2022-04-12 12:43:12 -07:00
JonnyWong16
b0950abdd9
v2.9.7 2022-04-11 15:26:25 -07:00
JonnyWong16
a9860fe62a
Fix managed users no username 2022-04-11 15:10:53 -07:00
JonnyWong16
dd389b0f94
v2.9.6 2022-04-10 15:48:32 -07:00
JonnyWong16
1ce292a0b6
Only build snap on amd64, arm64, armhf 2022-04-10 15:40:57 -07:00
JonnyWong16
a2fac2b11c
Improve display of dynamic range on activity cards
* Ref #1654. Thanks @herby2212.
2022-04-09 17:52:17 -07:00
JonnyWong16
4862aee9a1
Add hidden user title (Full Name) column to users table 2022-04-08 20:03:05 -07:00
JonnyWong16
aa6592eec7
Fix importing Plex usernames
* Fixes #1710
* Also import the user title (Full Name)
2022-04-08 20:03:05 -07:00
JonnyWong16
e996c4b375
Change default update check interval to hours instead of minutes 2022-04-04 21:06:25 -07:00
JonnyWong16
73b94782f9
Add hidden username and email columns to users table 2022-04-01 21:31:06 -07:00
JonnyWong16
b737ab96b4
Change include summary option for Discord and Slack for all media types 2022-04-01 21:08:10 -07:00
JonnyWong16
5c06accca0
Mask emails and longer usernames first 2022-04-01 10:19:41 -07:00
JonnyWong16
3cd98457c2
Apply PMS_TIMEOUT setting to websocket connection 2022-03-26 15:47:53 -07:00
JonnyWong16
944a231e5b
Change websocket header to a dict 2022-03-26 15:47:17 -07:00
JonnyWong16
5d84587ee2
Fix validating Plex login in setup wizard
* Fixes #1697
2022-03-26 15:40:28 -07:00
JonnyWong16
9a4ca69590
v2.9.5 2022-03-26 15:11:20 -07:00
JonnyWong16
9c8e8b4351
Simplify default settings in setup wizard 2022-03-26 14:57:44 -07:00
JonnyWong16
87389320fb
Fix live tv filter in history query
* Fixes #1691
2022-03-18 15:50:01 -07:00
JonnyWong16
eb1d724417
Add Tautulli to X-Plex-Device-Name header 2022-03-16 16:59:58 -07:00
JonnyWong16
50d90d16bf
Update App Store link to specify iphone 2022-03-11 20:18:10 -08:00
JonnyWong16
35aca11feb
Escape characters in username log filter
* Remove filter for friendly names
2022-02-27 17:03:11 -08:00
JonnyWong16
84a14c0f25
Colour active filter buttons 2022-02-27 16:50:29 -08:00
JonnyWong16
bd9fe54fd7
Enable dynamic anonymous redirect by default in setup wizard 2022-02-24 19:11:30 -08:00
JonnyWong16
74cd494162
Pass media_type when retrieving show and artist children for newsletter
* Fix different album types not shown on newsletter. (#1559)
2022-02-21 12:14:14 -08:00
JonnyWong16
297620f4a9
Reorder db migrations 2022-02-21 09:42:50 -08:00
JonnyWong16
d657a609ff
Reschedule tasks after changing backup interval
* Fixes #1662
2022-02-19 16:37:03 -08:00
JonnyWong16
3c48ea49f3
Retrieve season summary and fallback to show summary
* Fixes #1657
2022-02-18 23:27:02 -08:00
JonnyWong16
ec363b2ef3
Add transcode offset keys to get_activity API docs 2022-02-13 12:17:57 -08:00
JonnyWong16
6d84d1b4c9
Improve calculation for transcode progress bar 2022-02-13 11:59:06 -08:00
1601 changed files with 148699 additions and 55374 deletions

4
.github/codeql-config.yml vendored Normal file
View file

@ -0,0 +1,4 @@
name: CodeQL Config
paths-ignore:
- lib

38
.github/workflows/codeql.yml vendored Normal file
View file

@ -0,0 +1,38 @@
name: CodeQL
on:
push:
branches: [nightly]
pull_request:
branches: [nightly]
schedule:
- cron: '05 10 * * 1'
jobs:
codeql-analysis:
name: CodeQL Analysis
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: ['javascript', 'python']
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
config-file: ./.github/codeql-config.yml
languages: ${{ matrix.language }}
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{matrix.language}}"

View file

@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Stale - name: Stale
uses: actions/stale@v4 uses: actions/stale@v9
with: with:
stale-issue-message: > stale-issue-message: >
This issue is stale because it has been open for 30 days with no activity. This issue is stale because it has been open for 30 days with no activity.
@ -30,7 +30,7 @@ jobs:
days-before-close: 5 days-before-close: 5
- name: Invalid Template - name: Invalid Template
uses: actions/stale@v4 uses: actions/stale@v9
with: with:
stale-issue-message: > stale-issue-message: >
Invalid issues template. Invalid issues template.

View file

@ -10,6 +10,6 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Label Issues - name: Label Issues
uses: dessant/label-actions@v2 uses: dessant/label-actions@v4
with: with:
github-token: ${{ github.token }} github-token: ${{ github.token }}

View file

@ -1,6 +1,7 @@
name: Publish Docker name: Publish Docker
on: on:
workflow_dispatch: ~
push: push:
branches: [master, beta, nightly] branches: [master, beta, nightly]
tags: [v*] tags: [v*]
@ -12,41 +13,40 @@ jobs:
if: ${{ !contains(github.event.head_commit.message, '[skip ci]') }} if: ${{ !contains(github.event.head_commit.message, '[skip ci]') }}
steps: steps:
- name: Checkout Code - name: Checkout Code
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Prepare - name: Prepare
id: prepare id: prepare
run: | run: |
if [[ $GITHUB_REF == refs/tags/* ]]; then if [[ $GITHUB_REF == refs/tags/* ]]; then
echo ::set-output name=tag::${GITHUB_REF#refs/tags/} echo "tag=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
elif [[ $GITHUB_REF == refs/heads/master ]]; then elif [[ $GITHUB_REF == refs/heads/master ]]; then
echo ::set-output name=tag::latest echo "tag=latest" >> $GITHUB_OUTPUT
else else
echo ::set-output name=tag::${GITHUB_REF#refs/heads/} echo "tag=${GITHUB_REF#refs/heads/}" >> $GITHUB_OUTPUT
fi fi
if [[ $GITHUB_REF == refs/tags/*-beta ]]; then if [[ $GITHUB_REF == refs/tags/*-beta ]]; then
echo ::set-output name=branch::beta echo "branch=beta" >> $GITHUB_OUTPUT
elif [[ $GITHUB_REF == refs/tags/* ]]; then elif [[ $GITHUB_REF == refs/tags/* ]]; then
echo ::set-output name=branch::master echo "branch=master" >> $GITHUB_OUTPUT
else else
echo ::set-output name=branch::${GITHUB_REF#refs/heads/} echo "branch=${GITHUB_REF#refs/heads/}" >> $GITHUB_OUTPUT
fi fi
echo ::set-output name=commit::${GITHUB_SHA} echo "commit=${GITHUB_SHA}" >> $GITHUB_OUTPUT
echo ::set-output name=build_date::$(date -u +'%Y-%m-%dT%H:%M:%SZ') echo "docker_platforms=linux/amd64,linux/arm64/v8,linux/arm/v7,linux/arm/v6" >> $GITHUB_OUTPUT
echo ::set-output name=docker_platforms::linux/amd64,linux/arm64/v8,linux/arm/v7,linux/arm/v6 echo "docker_image=${{ secrets.DOCKER_REPO }}/tautulli" >> $GITHUB_OUTPUT
echo ::set-output name=docker_image::${{ secrets.DOCKER_REPO }}/tautulli
- name: Set Up QEMU - name: Set Up QEMU
uses: docker/setup-qemu-action@v1 uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1 uses: docker/setup-buildx-action@v3
id: buildx id: buildx
with: with:
version: latest version: latest
- name: Cache Docker Layers - name: Cache Docker Layers
uses: actions/cache@v2 uses: actions/cache@v4
with: with:
path: /tmp/.buildx-cache path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }} key: ${{ runner.os }}-buildx-${{ github.sha }}
@ -54,22 +54,28 @@ jobs:
${{ runner.os }}-buildx- ${{ runner.os }}-buildx-
- name: Login to DockerHub - name: Login to DockerHub
uses: docker/login-action@v1 uses: docker/login-action@v3
if: success() if: success()
with: with:
username: ${{ secrets.DOCKER_USERNAME }} username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }} password: ${{ secrets.DOCKER_TOKEN }}
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@v1 uses: docker/login-action@v3
if: success() if: success()
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ secrets.DOCKER_USERNAME }} username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.GHCR_TOKEN }} password: ${{ secrets.GHCR_TOKEN }}
- name: Extract Docker Metadata
id: metadata
uses: docker/metadata-action@v5
with:
images: ${{ steps.prepare.outputs.docker_image }}
- name: Docker Build and Push - name: Docker Build and Push
uses: docker/build-push-action@v2 uses: docker/build-push-action@v6
if: success() if: success()
with: with:
context: . context: .
@ -80,10 +86,10 @@ jobs:
TAG=${{ steps.prepare.outputs.tag }} TAG=${{ steps.prepare.outputs.tag }}
BRANCH=${{ steps.prepare.outputs.branch }} BRANCH=${{ steps.prepare.outputs.branch }}
COMMIT=${{ steps.prepare.outputs.commit }} COMMIT=${{ steps.prepare.outputs.commit }}
BUILD_DATE=${{ steps.prepare.outputs.build_date }}
tags: | tags: |
${{ steps.prepare.outputs.docker_image }}:${{ steps.prepare.outputs.tag }} ${{ steps.prepare.outputs.docker_image }}:${{ steps.prepare.outputs.tag }}
ghcr.io/${{ steps.prepare.outputs.docker_image }}:${{ steps.prepare.outputs.tag }} ghcr.io/${{ steps.prepare.outputs.docker_image }}:${{ steps.prepare.outputs.tag }}
labels: ${{ steps.metadata.outputs.labels }}
cache-from: type=local,src=/tmp/.buildx-cache cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache cache-to: type=local,dest=/tmp/.buildx-cache
@ -93,23 +99,10 @@ jobs:
if: always() && !contains(github.event.head_commit.message, '[skip ci]') if: always() && !contains(github.event.head_commit.message, '[skip ci]')
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Get Build Job Status
uses: technote-space/workflow-conclusion-action@v2.2
- name: Combine Job Status
id: status
run: |
failures=(neutral, skipped, timed_out, action_required)
if [[ ${array[@]} =~ $WORKFLOW_CONCLUSION ]]; then
echo ::set-output name=status::failure
else
echo ::set-output name=status::$WORKFLOW_CONCLUSION
fi
- name: Post Status to Discord - name: Post Status to Discord
uses: sarisia/actions-status-discord@v1 uses: sarisia/actions-status-discord@v1
with: with:
webhook: ${{ secrets.DISCORD_WEBHOOK }} webhook: ${{ secrets.DISCORD_WEBHOOK }}
status: ${{ steps.status.outputs.status }} status: ${{ needs.build-docker.result == 'success' && 'success' || contains(needs.*.result, 'failure') && 'failure' || 'cancelled' }}
title: ${{ github.workflow }} title: ${{ github.workflow }}
nofail: true nofail: true

View file

@ -1,14 +1,18 @@
name: Publish Installers name: Publish Installers
on: on:
workflow_dispatch: ~
push: push:
branches: [master, beta, nightly] branches: [master, beta, nightly]
tags: [v*] tags: [v*]
env:
PYTHON_VERSION: '3.11'
jobs: jobs:
build-installer: build-installer:
name: Build ${{ matrix.os_upper }} Installer name: Build ${{ matrix.os_upper }} Installer
runs-on: ${{ matrix.os }}-latest runs-on: ${{ matrix.os }}-${{ matrix.os_version }}
if: ${{ !contains(github.event.head_commit.message, '[skip ci]') }} if: ${{ !contains(github.event.head_commit.message, '[skip ci]') }}
strategy: strategy:
fail-fast: false fail-fast: false
@ -16,14 +20,18 @@ jobs:
include: include:
- os: 'windows' - os: 'windows'
os_upper: 'Windows' os_upper: 'Windows'
os_version: 'latest'
arch: 'x64'
ext: 'exe' ext: 'exe'
- os: 'macos' - os: 'macos'
os_upper: 'MacOS' os_upper: 'MacOS'
os_version: '14'
arch: 'universal'
ext: 'pkg' ext: 'pkg'
steps: steps:
- name: Checkout Code - name: Checkout Code
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Set Release Version - name: Set Release Version
id: get_version id: get_version
@ -32,14 +40,14 @@ jobs:
if [[ $GITHUB_REF == refs/tags/* ]]; then if [[ $GITHUB_REF == refs/tags/* ]]; then
echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV
VERSION_NSIS=${GITHUB_REF#refs/tags/v}.1 VERSION_NSIS=${GITHUB_REF#refs/tags/v}.1
echo ::set-output name=VERSION_NSIS::${VERSION_NSIS/%-beta.1/.0} echo "VERSION_NSIS=${VERSION_NSIS/%-beta.1/.0}" >> $GITHUB_OUTPUT
echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/v} echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
echo ::set-output name=RELEASE_VERSION::${GITHUB_REF#refs/tags/} echo "RELEASE_VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
else else
echo "VERSION=0.0.0" >> $GITHUB_ENV echo "VERSION=0.0.0" >> $GITHUB_ENV
echo ::set-output name=VERSION_NSIS::0.0.0.0 echo "VERSION_NSIS=0.0.0.0" >> $GITHUB_OUTPUT
echo ::set-output name=VERSION::0.0.0 echo "VERSION=0.0.0" >> $GITHUB_OUTPUT
echo ::set-output name=RELEASE_VERSION::${GITHUB_SHA::7} echo "RELEASE_VERSION=${GITHUB_SHA::7}" >> $GITHUB_OUTPUT
fi fi
if [[ $GITHUB_REF == refs/tags/*-beta ]]; then if [[ $GITHUB_REF == refs/tags/*-beta ]]; then
echo "beta" > branch.txt echo "beta" > branch.txt
@ -51,34 +59,29 @@ jobs:
echo $GITHUB_SHA > version.txt echo $GITHUB_SHA > version.txt
- name: Set Up Python - name: Set Up Python
uses: actions/setup-python@v2 uses: actions/setup-python@v5
with: with:
python-version: 3.9 python-version: ${{ env.PYTHON_VERSION }}
cache: pip
- name: Cache Dependencies cache-dependency-path: '**/requirements*.txt'
uses: actions/cache@v2
with:
path: ~\AppData\Local\pip\Cache
key: ${{ runner.os }}-pip-${{ hashFiles('package/requirements-package.txt') }}
restore-keys: ${{ runner.os }}-pip-
- name: Install Dependencies - name: Install Dependencies
run: | run: |
python -m pip install --upgrade pip python -m pip install --upgrade pip
pip install -r package/requirements-package.txt pip install -r package/requirements-package.txt --no-binary cffi
- name: Build Package - name: Build Package
run: | run: |
pyinstaller -y ./package/Tautulli-${{ matrix.os }}.spec pyinstaller -y ./package/Tautulli-${{ matrix.os }}.spec
- name: Create Windows Installer - name: Create Windows Installer
uses: joncloud/makensis-action@v3.6 uses: joncloud/makensis-action@v4.1
if: matrix.os == 'windows' if: matrix.os == 'windows'
with: with:
script-file: ./package/Tautulli.nsi script-file: ./package/Tautulli.nsi
arguments: > arguments: >
/DVERSION=${{ steps.get_version.outputs.VERSION_NSIS }} /DVERSION=${{ steps.get_version.outputs.VERSION_NSIS }}
/DINSTALLER_NAME=..\Tautulli-windows-${{ steps.get_version.outputs.RELEASE_VERSION }}-x64.exe /DINSTALLER_NAME=..\Tautulli-${{ matrix.os }}-${{ steps.get_version.outputs.RELEASE_VERSION }}-${{ matrix.arch }}.${{ matrix.ext }}
additional-plugin-paths: package/nsis-plugins additional-plugin-paths: package/nsis-plugins
- name: Create MacOS Installer - name: Create MacOS Installer
@ -89,13 +92,31 @@ jobs:
--version ${{ steps.get_version.outputs.VERSION }} \ --version ${{ steps.get_version.outputs.VERSION }} \
--component ./dist/Tautulli.app \ --component ./dist/Tautulli.app \
--scripts ./package/macos-scripts \ --scripts ./package/macos-scripts \
Tautulli-macos-${{ steps.get_version.outputs.RELEASE_VERSION }}-x64.pkg Tautulli-${{ matrix.os }}-${{ steps.get_version.outputs.RELEASE_VERSION }}-${{ matrix.arch }}.${{ matrix.ext }}
- name: Upload Installer - name: Upload Installer
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v4
with: with:
name: Tautulli-${{ matrix.os }}-installer name: Tautulli-${{ matrix.os }}-installer
path: Tautulli-${{ matrix.os }}-${{ steps.get_version.outputs.RELEASE_VERSION }}-x64.${{ matrix.ext }} path: Tautulli-${{ matrix.os }}-${{ steps.get_version.outputs.RELEASE_VERSION }}-${{ matrix.arch }}.${{ matrix.ext }}
virus-total:
name: VirusTotal Scan
needs: build-installer
if: needs.build-installer.result == 'success' && !contains(github.event.head_commit.message, '[skip ci]')
runs-on: ubuntu-latest
steps:
- name: Download Installers
if: needs.build-installer.result == 'success'
uses: actions/download-artifact@v4
- name: Upload to VirusTotal
uses: crazy-max/ghaction-virustotal@v4
with:
vt_api_key: ${{ secrets.VT_API_KEY }}
files: |
Tautulli-windows-installer/Tautulli-windows-*-x64.exe
Tautulli-macos-installer/Tautulli-macos-*-universal.pkg
release: release:
name: Release Installers name: Release Installers
@ -103,63 +124,44 @@ jobs:
if: always() && startsWith(github.ref, 'refs/tags/') && !contains(github.event.head_commit.message, '[skip ci]') if: always() && startsWith(github.ref, 'refs/tags/') && !contains(github.event.head_commit.message, '[skip ci]')
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Get Build Job Status
uses: technote-space/workflow-conclusion-action@v2.2
- name: Checkout Code - name: Checkout Code
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Set Release Version - name: Set Release Version
id: get_version id: get_version
run: | run: |
echo ::set-output name=RELEASE_VERSION::${GITHUB_REF#refs/tags/} echo "RELEASE_VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
- name: Download Installers - name: Download Installers
if: env.WORKFLOW_CONCLUSION == 'success' if: needs.build-installer.result == 'success'
uses: actions/download-artifact@v2 uses: actions/download-artifact@v4
- name: Get Changelog - name: Get Changelog
id: get_changelog id: get_changelog
run: | run: |
echo ::set-output name=CHANGELOG::"$( sed -n '/^## /{p; :loop n; p; /^## /q; b loop}' CHANGELOG.md \ 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' )" | sed '$d' | sed '$d' | sed '$d' )"
EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
echo "CHANGELOG<<$EOF" >> $GITHUB_OUTPUT
echo "$CHANGELOG" >> $GITHUB_OUTPUT
echo "$EOF" >> $GITHUB_OUTPUT
- name: Create Release - name: Create Release
uses: actions/create-release@v1 uses: softprops/action-gh-release@v2
id: create_release id: create_release
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GHACTIONS_TOKEN }}
with: with:
tag_name: ${{ steps.get_version.outputs.RELEASE_VERSION }} tag_name: ${{ steps.get_version.outputs.RELEASE_VERSION }}
release_name: Tautulli ${{ steps.get_version.outputs.RELEASE_VERSION }} name: Tautulli ${{ steps.get_version.outputs.RELEASE_VERSION }}
body: | body: |
## Changelog ## Changelog
##${{ steps.get_changelog.outputs.CHANGELOG }} ##${{ steps.get_changelog.outputs.CHANGELOG }}
draft: false
prerelease: ${{ endsWith(steps.get_version.outputs.RELEASE_VERSION, '-beta') }} prerelease: ${{ endsWith(steps.get_version.outputs.RELEASE_VERSION, '-beta') }}
files: |
- name: Upload Windows Installer Tautulli-windows-installer/Tautulli-windows-${{ steps.get_version.outputs.RELEASE_VERSION }}-x64.exe
uses: actions/upload-release-asset@v1 Tautulli-macos-installer/Tautulli-macos-${{ steps.get_version.outputs.RELEASE_VERSION }}-universal.pkg
if: env.WORKFLOW_CONCLUSION == 'success'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: Tautulli-windows-installer/Tautulli-windows-${{ steps.get_version.outputs.RELEASE_VERSION }}-x64.exe
asset_name: Tautulli-windows-${{ steps.get_version.outputs.RELEASE_VERSION }}-x64.exe
asset_content_type: application/vnd.microsoft.portable-executable
- name: Upload MacOS Installer
uses: actions/upload-release-asset@v1
if: env.WORKFLOW_CONCLUSION == 'success'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: Tautulli-macos-installer/Tautulli-macos-${{ steps.get_version.outputs.RELEASE_VERSION }}-x64.pkg
asset_name: Tautulli-macos-${{ steps.get_version.outputs.RELEASE_VERSION }}-x64.pkg
asset_content_type: application/vnd.apple.installer+xml
discord: discord:
name: Discord Notification name: Discord Notification
@ -167,23 +169,10 @@ jobs:
if: always() && !contains(github.event.head_commit.message, '[skip ci]') if: always() && !contains(github.event.head_commit.message, '[skip ci]')
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Get Build Job Status
uses: technote-space/workflow-conclusion-action@v2.2
- name: Combine Job Status
id: status
run: |
failures=(neutral, skipped, timed_out, action_required)
if [[ ${array[@]} =~ $WORKFLOW_CONCLUSION ]]; then
echo ::set-output name=status::failure
else
echo ::set-output name=status::$WORKFLOW_CONCLUSION
fi
- name: Post Status to Discord - name: Post Status to Discord
uses: sarisia/actions-status-discord@v1 uses: sarisia/actions-status-discord@v1
with: with:
webhook: ${{ secrets.DISCORD_WEBHOOK }} webhook: ${{ secrets.DISCORD_WEBHOOK }}
status: ${{ steps.status.outputs.status }} status: ${{ needs.build-installer.result == 'success' && 'success' || contains(needs.*.result, 'failure') && 'failure' || 'cancelled' }}
title: ${{ github.workflow }} title: ${{ github.workflow }}
nofail: true nofail: true

View file

@ -1,6 +1,7 @@
name: Publish Snap name: Publish Snap
on: on:
workflow_dispatch: ~
push: push:
branches: [master, beta, nightly] branches: [master, beta, nightly]
tags: [v*] tags: [v*]
@ -14,55 +15,51 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
architecture: architecture:
- i386
- amd64 - amd64
- arm64 - arm64
- armhf - armhf
- ppc64el
#- s390x # broken at the moment
steps: steps:
- name: Checkout Code - name: Checkout Code
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Prepare - name: Prepare
id: prepare id: prepare
run: | run: |
git fetch --prune --unshallow --tags git fetch --prune --unshallow --tags
if [[ $GITHUB_REF == refs/tags/*-beta || $GITHUB_REF == refs/heads/beta ]]; then if [[ $GITHUB_REF == refs/tags/*-beta || $GITHUB_REF == refs/heads/beta ]]; then
echo ::set-output name=RELEASE::beta echo "RELEASE=beta" >> $GITHUB_OUTPUT
elif [[ $GITHUB_REF == refs/tags/* || $GITHUB_REF == refs/heads/master ]]; then elif [[ $GITHUB_REF == refs/tags/* || $GITHUB_REF == refs/heads/master ]]; then
echo ::set-output name=RELEASE::stable echo "RELEASE=stable" >> $GITHUB_OUTPUT
else else
echo ::set-output name=RELEASE::edge echo "RELEASE=edge" >> $GITHUB_OUTPUT
fi fi
- name: Set Up QEMU - name: Set Up QEMU
uses: docker/setup-qemu-action@v1 uses: docker/setup-qemu-action@v3
with:
image: tonistiigi/binfmt@sha256:df15403e06a03c2f461c1f7938b171fda34a5849eb63a70e2a2109ed5a778bde
- name: Build Snap Package - name: Build Snap Package
uses: diddlesnaps/snapcraft-multiarch-action@v1 uses: diddlesnaps/snapcraft-multiarch-action@master
id: build id: build
with: with:
architecture: ${{ matrix.architecture }} architecture: ${{ matrix.architecture }}
- name: Upload Snap Package - name: Upload Snap Package
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v4
with: with:
name: Tautulli-snap-package-${{ matrix.architecture }} name: Tautulli-snap-package-${{ matrix.architecture }}
path: ${{ steps.build.outputs.snap }} path: ${{ steps.build.outputs.snap }}
- name: Review Snap Package - name: Review Snap Package
uses: diddlesnaps/snapcraft-review-tools-action@v1 uses: diddlesnaps/snapcraft-review-tools-action@master
with: with:
snap: ${{ steps.build.outputs.snap }} snap: ${{ steps.build.outputs.snap }}
- name: Publish Snap Package - name: Publish Snap Package
uses: snapcore/action-publish@v1 uses: snapcore/action-publish@v1
if: startsWith(github.ref, 'refs/tags/') || github.ref == 'refs/heads/nightly' if: startsWith(github.ref, 'refs/tags/') || github.ref == 'refs/heads/nightly'
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_LOGIN }}
with: with:
store_login: ${{ secrets.SNAP_LOGIN }}
snap: ${{ steps.build.outputs.snap }} snap: ${{ steps.build.outputs.snap }}
release: ${{ steps.prepare.outputs.RELEASE }} release: ${{ steps.prepare.outputs.RELEASE }}
@ -72,23 +69,10 @@ jobs:
if: always() && !contains(github.event.head_commit.message, '[skip ci]') if: always() && !contains(github.event.head_commit.message, '[skip ci]')
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Get Build Job Status
uses: technote-space/workflow-conclusion-action@v2.2
- name: Combine Job Status
id: status
run: |
failures=(neutral, skipped, timed_out, action_required)
if [[ ${array[@]} =~ $WORKFLOW_CONCLUSION ]]; then
echo ::set-output name=status::failure
else
echo ::set-output name=status::$WORKFLOW_CONCLUSION
fi
- name: Post Status to Discord - name: Post Status to Discord
uses: sarisia/actions-status-discord@v1 uses: sarisia/actions-status-discord@v1
with: with:
webhook: ${{ secrets.DISCORD_WEBHOOK }} webhook: ${{ secrets.DISCORD_WEBHOOK }}
status: ${{ steps.status.outputs.status }} status: ${{ needs.build-snap.result == 'success' && 'success' || contains(needs.*.result, 'failure') && 'failure' || 'cancelled' }}
title: ${{ github.workflow }} title: ${{ github.workflow }}
nofail: true nofail: true

View file

@ -10,15 +10,14 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout Code - name: Checkout Code
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Comment on Pull Request - name: Comment on Pull Request
uses: mshick/add-pr-comment@v1 uses: mshick/add-pr-comment@v2
if: github.base_ref != 'nightly' if: github.base_ref != 'nightly'
with: with:
message: Pull requests must be made to the `nightly` branch. Thanks. message: Pull requests must be made to the `nightly` branch. Thanks.
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
repo-token-user-login: 'github-actions[bot]'
- name: Fail Workflow - name: Fail Workflow
if: github.base_ref != 'nightly' if: github.base_ref != 'nightly'

44
.github/workflows/submit-winget.yml vendored Normal file
View file

@ -0,0 +1,44 @@
name: Submit winget
on:
workflow_dispatch: ~
release:
types: [published]
jobs:
winget:
name: Submit Winget Package
runs-on: windows-latest
if: ${{ !github.event.release.prerelease }}
steps:
- name: Sync Winget Fork
run: gh repo sync ${{ secrets.WINGET_USERNAME }}/winget-pkgs -b master
env:
GH_TOKEN: ${{ secrets.WINGET_TOKEN }}
- name: Submit package to Windows Package Manager Community Repository
run: |
$wingetPackage = "Tautulli.Tautulli"
$gitToken = "${{ secrets.WINGET_TOKEN }}"
$github = Invoke-RestMethod -uri "https://api.github.com/repos/Tautulli/Tautulli/releases/latest"
$installerUrl = $github | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match "Tautulli-windows-.*-x64.exe" | Select -ExpandProperty browser_download_url
$version = "$($github.tag_name.Trim('v')).1"
# getting latest wingetcreate file
iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe
.\wingetcreate.exe update $wingetPackage -s -v $version -u $installerUrl -t $gitToken
virus-total:
name: VirusTotal Scan
runs-on: ubuntu-latest
steps:
- name: Upload to VirusTotal
uses: crazy-max/ghaction-virustotal@v4
with:
vt_api_key: ${{ secrets.VT_API_KEY }}
github_token: ${{ secrets.GHACTIONS_TOKEN }}
update_release_body: true
files: |
.exe$
.pkg$

3
.gitignore vendored
View file

@ -53,6 +53,9 @@ Thumbs.db
#Ignore files generated by PyCharm #Ignore files generated by PyCharm
*.idea/* *.idea/*
#Ignore files generated by VSCode
*.vscode/*
#Ignore files generated by vi #Ignore files generated by vi
*.swp *.swp

View file

@ -1,5 +1,478 @@
# Changelog # Changelog
## v2.15.3 (2025-08-03)
* Exporter:
* New: Added hearingImpaired for subtitles and visualImpaired for audio attributes to exporter fields.
* Graphs:
* Fix: Remove duplicate "Total" entry in graph tooltips. (Thanks @zdimension) (#2534)
* UI:
* Fix: Failing to retrieve collections / playlists with over 1000 items.
* Fix: Scrollbar not showing on macosx and webkit browsers. (#2221)
* Fix: Incorrect rounding of minutes in global stats play duration.
* Fix: Disable browser autocomplete for notification agent and newsletter agent configurations. (#2557)
* API:
* New: Added ability to return svg files using pms_image_proxy API command.
* Other:
* New: Added ability to set config values using environment variables. (Thanks @komuw) (#2309, #2543)
## v2.15.2 (2025-04-12)
* Activity:
* New: Added link to library by clicking media type icon.
* New: Added stream count to tab title on homepage. (#2517)
* History:
* Fix: Check stream watched status before stream stopped status. (#2506)
* Notifications:
* Fix: ntfy notifications failing to send if provider link is blank.
* Fix: Check Pushover notification attachment is under 5MB limit. (#2396)
* Fix: Track URLs redirecting to the correct media page. (#2513)
* New: Added audio profile notification parameters.
* New: Added PATCH method for Webhook notifications.
* Graphs:
* New: Added Total line to daily streams graph. (Thanks @zdimension) (#2497)
* UI:
* Fix: Do not redirect API requests to the login page. (#2490)
* Change: Swap source and stream columns in stream info modal.
* Other:
* Fix: Various typos. (Thanks @luzpaz) (#2520)
* Fix: CherryPy CORS response header not being set correctly. (#2279)
## v2.15.1 (2025-01-11)
* Activity:
* Fix: Detection of HDR transcodes. (Thanks @cdecker08) (#2412, #2466)
* Newsletters:
* Fix: Disable basic authentication for /newsletter and /image endpoints. (#2472)
* Exporter:
* New: Added logos to season and episode exports.
* Other:
* Fix: Docker container https health check.
## v2.15.0 (2024-11-24)
* Notes:
* Support for Python 3.8 has been dropped. The minimum Python version is now 3.9.
* Notifications:
* New: Allow Telegram blockquote and tg-emoji HTML tags. (Thanks @MythodeaLoL) (#2427)
* New: Added Plex slug and Plex Watch URL notification parameters. (#2420)
* Change: Update OneSignal API calls to use the new API endpoint for Tautulli Remote App notifications.
* Newsletters:
* Fix: Dumping custom dates in raw newsletter json.
* History:
* Fix: Unable to fix match for artists. (#2429)
* Exporter:
* New: Added movie and episode hasVoiceActivity attribute to exporter fields.
* New: Added subtitle canAutoSync attribute to exporter fields.
* New: Added logos to the exporter fields.
* UI:
* New: Add friendly name to the top bar of config modals. (Thanks @peagravel) (#2432)
* API:
* New: Added plex slugs to metadata in the get_metadata API command.
* Other:
* Fix: Tautulli failing to start with Python 3.13. (#2426)
## v2.14.6 (2024-10-12)
* Newsletters:
* Fix: Allow formatting newsletter date parameters.
* Change: Support apscheduler compatible cron expressions.
* UI:
* Fix: Round runtime before converting to human duration.
* Fix: Make recently added/watched rows touch scrollable.
* Other:
* Fix: Auto-updater not running.
## v2.14.5 (2024-09-20)
* Activity:
* Fix: Display of 2k resolution on activity card.
* Notifications:
* Fix: ntfy notifications with special characters failing to send.
* Other:
* Fix: Memory leak with database closing. (#2404)
## v2.14.4 (2024-08-10)
* Notifications:
* Fix: Update Slack notification info card.
* New: Added ntfy notification agent. (Thanks @nwithan8) (#2356, #2000)
* UI:
* Fix: macOS platform capitalization.
* Other:
* Fix: Remove deprecated getdefaultlocale. (Thanks @teodorstelian) (#2364, #2345)
## v2.14.3 (2024-06-19)
* Graphs:
* Fix: History table not loading when clicking on the graphs in some instances.
* UI:
* Fix: Scheduled tasks table not loading when certain tasks are disabled.
* Removed: Unnecessary Remote Server checkbox from the settings page.
* Other:
* Fix: Webserver not restarting after the setup wizard.
* Fix: Workaround webserver crashing in some instances.
## v2.14.2 (2024-05-18)
* History:
* Fix: Live TV activity not logging to history.
* Fix: Incorrect grouping of live TV history.
* Notifications:
* Fix: Pushover configuration settings refreshing after entering a token.
* Fix: Plex remote access down notifications not triggering.
* Fix: Deleting all images from Cloudinary only deleting 1000 images.
* New: Added platform version and product version notification parameters. (#2244)
* New: Added LAN streams and WAN streams notification parameters. (#2276)
* New: Added Dolby Vision notification parameters. (#2240)
* New: Added live TV channel notification parameters.
* Change: Improved Tautulli Remote App notification encryption method.
* Note: Requires Tautulli Remote App version 3.2.4.
* Exporter:
* New: Added slug attribute to exporter fields.
* New: Added track genres to exporter fields.
* New: Added playlist source URI to exporter fields.
* New: Added artProvider and thumbProvider to exporter fields.
* UI:
* Fix: Mask deleted usernames in the logs.
* Fix: Live TV watch stats not showing on the media info page.
* Fix: Users without access to Plex server not showing as inactive.
* Removed: Deprecated synced item pages.
* Removed: Anonymous redirect settings. Links now use browser no-referrer policy instead.
* API:
* New: Added Dolby Vision info to the get_metadata API command.
* New: Added before and after parameters to the get_home_stats API command. (#2231)
* Packages:
* New: Universal binary for macOS for Apple silicon.
* New: Bump Snap package to core22.
* Other:
* Change: Login cookie expires changed to max-age.
* Change: Improved key generation for login password. It is recommended to reenter your HTTP Password in the settings after upgrading.
* Removed: Python 2 compatibility code. (#2098, #2226) (Thanks @zdimension)
## v2.13.4 (2023-12-07)
* UI:
* Fix: Tautulli configuration settings page not loading when system language is None.
* Fix: Login cookie expiring too quickly.
## v2.13.3 (2023-12-03)
* Notifications:
* New: Added duration_time notification parameter.
* New: Added file_size_bytes notification parameter.
* New: Added time formats notification text modifiers.
* New: Added support for thetvdb_url for movies.
* UI:
* Fix: Activity card overflowing due to screen scaling. (#2033)
* Fix: Stream duration on activity card not being updated on track changes in some cases. (#2206)
## v2.13.2 (2023-10-26)
* History:
* New: Added quarter values icons for history watch status. (#2179, #2156) (Thanks @herby2212)
* Graphs:
* New: Added concurrent streams per day graph. (#2046) (Thanks @herby2212)
* Exporter:
* New: Added metadata directory to exporter fields.
* Removed: Banner exporter fields for tv shows.
* UI:
* New: Added last triggered time to notification agents and newsletter agent lists.
* Other:
* New: Added X-Plex-Language header override to config file.
## v2.13.1 (2023-08-25)
* Notes:
* Support for Python 3.7 has been dropped. The minimum Python version is now 3.8.
* Other:
* Fix: Tautulli failing to start on some systems.
## v2.13.0 (2023-08-25)
* Notes:
* Support for Python 3.7 has been dropped. The minimum Python version is now 3.8.
* Notifications:
* Fix: Improved watched notification trigger description. (#2104)
* New: Added notification image option for iOS Tautulli Remote app.
* Exporter:
* New: Added track chapter export fields.
* New: Added on-demand subtitle export fields.
## v2.12.5 (2023-07-13)
* Activity:
* New: Added d3d11va to list of hardware decoders.
* History:
* Fix: Incorrect grouping of play history.
* New: Added button in settings to regroup play history.
* Notifications:
* Fix: Incorrect concurrent streams notifications by IP addresss for IPv6 addresses (#2096) (Thanks @pooley182)
* UI:
* Fix: Occasional UI crashing on Python 3.11.
* New: Added multiselect user filters to History and Graphs pages. (#2090) (Thanks @zdimension)
* API:
* New: Added regroup_history API command.
* Change: Updated graph API commands to accept a comma separated list of user IDs.
## v2.12.4 (2023-05-23)
* History:
* Fix: Set view offset equal to duration if a stream is stopped within the last 10 sec.
* Other:
* Fix: Database import may fail for some older databases.
* Fix: Double-quoted strings for newer versions of SQLite. (#2015, #2057)
* API:
* Change: Return the ID for async API calls (export_metadata, notify, notify_newsletter).
## v2.12.3 (2023-04-14)
* Activity:
* Fix: Incorrect subtitle decision shown when subtitles are transcoded.
* History:
* Fix: Incorrect order when sorting by the duration column in the history tables.
* Notifications:
* Fix: Logging error when running scripts that use PlexAPI.
* UI:
* Fix: Calculate file sizes setting causing the media info table to fail to load.
* Fix: Incorrect artwork and thumbnail shown for Live TV on the Most Active Libraries statistics card.
* API:
* Change: Renamed duration to play_duration in the get_history API response. (Note: duration kept for backwards compatibility.)
## v2.12.2 (2023-03-16)
* Other:
* Fix: Tautulli not starting on FreeBSD jails.
## v2.12.1 (2023-03-14)
* Activity:
* Fix: Stop checking for deprecated sync items sessions.
* Change: Do not show audio language on activity cards for music.
* Other:
* Fix: Tautulli not starting on macOS.
## v2.12.0 (2023-03-13)
* Notifications:
* New: Added support for Telegram group topics. (#1980)
* New: Added anidb_id and anidb_url notification parameters. (#1973)
* New: Added notification triggers for Intro Marker, Commercial Marker, and Credits Marker.
* New: Added various intro, commercial, and credits marker notification parameters.
* New: Allow setting a custom Pushover notification sound. (#2005)
* Change: Notification images are now uploaded directly to Discord without the need for a 3rd party image hosting service.
* Change: Automatically strip whitespace from notification condition values.
* Change: Trigger watched notifications based on the video watched completion behaviour setting.
* Exporter:
* Fix: Unable to run exporter when using the Snap package. (#2007)
* New: Added credits marker, and audio/subtitle settings to export fields.
* UI:
* Fix: Incorrect styling and missing content for collection media info pages.
* New: Added edition details field on movie media info pages. (#1957) (Thanks @herby2212)
* New: Added setting to change the video watched completion behaviour.
* New: Added watch time and user statistics to collection and playlist media info pages. (#1982, #2012) (Thanks @herby2212)
* New: Added history table to collection and playlist media info pages.
* New: Dynamically change watched status in the UI based on video watched completion behaviour setting.
* New: Added hidden setting to override server name.
* Change: Move track artist to a details field instead of in the title on track media info pages.
* API:
* New: Added section_id and user_id parameters to get_home_stats API command. (#1944)
* New: Added marker info to get_metadata API command results.
* New: Added media_type parameter to get_item_watch_time_stats and get_item_user_stats API commands. (#1982) (Thanks @herby2212)
* New: Added last_refreshed timestamp to get_library_media_info API command response.
* Other:
* Change: Migrate analytics to Google Analytics 4.
## v2.11.1 (2022-12-22)
* Activity:
* Fix: Use source language instead of stream language on activity cards.
* Notifications:
* Fix: Blank start time notification parameters causing recently added notifications to fail. (#1940)
* Other:
* Fix: Tautulli failing to start when using python 3.7.
* Fix: Snap install failing to start. (#1941)
* Fix: Update check crashing when git is missing. (#1943) (Thanks @Minituff)
## v2.11.0 (2022-12-22)
* Activity:
* New: Added audio and subtitle language to activity cards. (#1831, #1900) (Thanks @fscorrupt)
* History:
* New: Log subtitle language and subtitle forced to database. (#1826)
* Notifications:
* Fix: Validating condition operators would fail with a blank parameter.
* New: Added start time and stop time notification parameters. (#1931)
* New: Added session_key to LunaSea notification payload. (#1929) (Thanks @JagandeepBrar)
* Newsletters:
* Fix: Allow CSS to support light and dark themes.
* Exporter:
* New: Added editionTitle to movie exporter fields.
* Change: m3u8 export changed to .m3u file extension. File is still encoded using UTF-8.
* UI:
* Fix: Link watch statistics to media page using metadata from history. (#1882)
* New: Show subtitle language and subtitle forced flag in stream data modal.
* Other:
* Fix: Mask more user and metadata fields for guest access. (#1913)
* Change: Disable TLS 1.0 and 1.1 for the webserver. Minimum TLS version is 1.2. (#1870)
* Change: Use system language for requests to Plex Media Server.
## v2.10.5 (2022-11-07)
* Notifications:
* New: Added edition_title notification parameter. (#1838)
* Change: Track notifications link to MusicBrainz track instead of album.
* Newsletters:
* New: Added months time frame for newsletters. (#1876)
* UI:
* Fix: Broken link on library statistic cards. (#1852)
* Fix: Check for IPv6 host when generating QR code for app registration.
* Fix: Missing padding on condition operator dropdown on small screens.
* Other:
* Fix: Launching browser when webserver is bound to IPv6.
* New: Tautulli can be installed via the Windows Package Manager (winget).
* Change: Separate stdout and stderr console logging. (#1874)
* API:
* Fix: API not returning 400 response code.
* New: Added edition_title to get_metadata API response.
* New: Added collections to get_children_metadata API response.
* New: Added user_thumb to get_history API response.
* New: Validate custom notification conditions before saving notification agents. (#1846)
* Change: Fallback to parent_thumb for seasons in get_metadata API response.
## v2.10.4 (2022-09-05)
* Activity:
* New: Added tooltip for quality profile on activity cards.
* Notifications:
* New: Added "does not begin with" and "does not end with" condition operators.
* UI:
* Fix: Album count showing 0 on library statistics.
* Fix: Library statistics not showing up for libraries without any history.
## v2.10.3 (2022-08-09)
* Notifications:
* New: Added JSON support for MQTT notifications. (#1763)
* New: Added show year notification parameter.
* Exporter:
* New: Added guids to artist, album, and track metadata export fields.
* New: Added languageTag to stream media info export fields.
* UI:
* Fix: Long channel identifier overflowing activity card. (#1802)
* Change: Use the last played item's artwork for library statistics cards.
* Other:
* Fix: Username log filter causing database to lock up. (#1705)
* Change: Username log filter only applies to usernames longer than 3 characters. (#1806)
* API:
* New: Added parent_year and grandparent_year to get_metadata_details API command.
* New: Added last played metadata to top_libraries and top_users in get_home_stats API command.
* New: Allow fallback to another PMS image in pms_image_proxy API command.
## v2.10.2 (2022-07-03)
* Activity:
* Fix: Incorrect audio stream info shown on the activity card when playing a secondary audio track.
* UI:
* Fix: Usernames not showing on the home statistics cards.
* Fix: Do not save a user's friendly name if it is the same as the username.
* Change: Update library icons to the latest Plex style.
## v2.10.1 (2022-06-01)
* Notifications:
* New: Added support for MusicBrainz (mbid://) guids in notification parameters without MusicBrainz lookup enabled. Requires Plex Media Server 1.27.0 or newer with refreshed Plex Music agent metadata.
* Mobile App:
* Fix: OneSignal validation failing when registering a device.
* API:
* New: Added grandparent_guids and parent_guids to get_metadata API command.
* Change: Updated continent in get_geoip_lookup API command.
* Change: Removed server_token from from get_users API command.
* Change: shared_libraries changed to a list instead of a string for get_users API command.
## v2.10.0 (2022-05-23)
* Activity:
* Fix: Detection of Dolby Vision missing for PMS 1.26.1.
* Notifications:
* Fix: Parsing of filename notification parameter incorrect for Windows PMS.
* Exporter:
* New: Added additional theme and label export fields.
* UI:
* Fix: Slow loading of collections and playlists tables.
* Change: Update default user thumbnail image to match Plex Web.
* API:
* Change: Values for get_users_table and get_libraries_table return an integer instead of "Checked".
## v2.9.7 (2022-04-11)
* UI:
* Fix: Managed user missing the username in the Users table.
## v2.9.6 (2022-04-10)
* Activity:
* New: Improved display of dynamic range on the activity cards. (Thanks @herby2212)
* Notifications:
* Change: Make include summary option apply to all media types for Discord and Slack notifications.
* UI:
* Fix: Validating Plex login in the setup wizard. (#1697)
* New: Added hidden username, email, and full name columns to users table.
* Other:
* Fix: Apply pms_timeout setting to websocket connection.
* Fix: Importing of Plex username instead of the full name. (#1710)
## v2.9.5 (2022-03-26)
* Note:
* Updated Snap packages are currently unavailable due to an upstream issue.
* Activity:
* Change: Improve calculation for transcode progress bar percentage on the activity cards.
* History:
* Fix: Live TV history filter not working. (#1691)
* Newsletter:
* Fix: Newsletter not showing different album types. (#1559)
* UI:
* Fix: Display season summary on the media info page if available with a fallback to show summary. (#1657)
* Change: Colour active filter buttons to improve contrast. (#1663)
* API:
* New: Added transcode offset keys to get_activity command.
* Other:
* Fix: Reschedule backup task after changing backup interval. (#1662)
* Fix: Dynamic anonymous redirect setting not being enabled by default after the setup wizard.
* Fix: Usernames with special characters not being filtered in the logs.
## v2.9.4 (2022-02-12) ## v2.9.4 (2022-02-12)
* UI: * UI:

View file

@ -9,12 +9,12 @@ All pull requests should be based on the `nightly` branch, to minimize cross mer
### Python Code ### Python Code
#### Compatibility #### Compatibility
The code should work with Python 3.6+. Note that Tautulli runs on many different platforms. The code should work with Python 3.8+. Note that Tautulli runs on many different platforms.
Re-use existing code. Do not hesitate to add logging in your code. You can the logger module `plexpy.logger.*` for this. Web requests are invoked via `plexpy.request.*` and derived ones. Use these methods to automatically add proper and meaningful error handling. Re-use existing code. Do not hesitate to add logging in your code. You can the logger module `plexpy.logger.*` for this. Web requests are invoked via `plexpy.request.*` and derived ones. Use these methods to automatically add proper and meaningful error handling.
#### Code conventions #### Code conventions
Although Tautulli did not adapt a code convention in the past, we try to follow the [PEP8](http://legacy.python.org/dev/peps/pep-0008/) conventions for future code. A short summary to remind you (copied from http://wiki.ros.org/PyStyleGuide): Although Tautulli did not adopt a code convention in the past, we try to follow [PEP8](http://legacy.python.org/dev/peps/pep-0008/) conventions for future code. A short summary to remind you (copied from http://wiki.ros.org/PyStyleGuide):
* 4 space indentation * 4 space indentation
* 80 characters per line * 80 characters per line

View file

@ -25,4 +25,4 @@ CMD [ "python", "Tautulli.py", "--datadir", "/config" ]
ENTRYPOINT [ "./start.sh" ] ENTRYPOINT [ "./start.sh" ]
EXPOSE 8181 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 HEALTHCHECK --start-period=90s CMD curl -ILfks https://localhost:8181/status > /dev/null || curl -ILfs http://localhost:8181/status > /dev/null || exit 1

View file

@ -36,7 +36,7 @@ and [PlexWatchWeb](https://github.com/ecleese/plexWatchWeb).
[![Docker Stars][badge-docker-stars]][DockerHub] [![Docker Stars][badge-docker-stars]][DockerHub]
[![Downloads][badge-downloads]][Releases Latest] [![Downloads][badge-downloads]][Releases Latest]
[badge-python]: https://img.shields.io/badge/python->=3.6-blue?style=flat-square [badge-python]: https://img.shields.io/badge/python->=3.9-blue?style=flat-square
[badge-docker-pulls]: https://img.shields.io/docker/pulls/tautulli/tautulli?style=flat-square [badge-docker-pulls]: https://img.shields.io/docker/pulls/tautulli/tautulli?style=flat-square
[badge-docker-stars]: https://img.shields.io/docker/stars/tautulli/tautulli?style=flat-square [badge-docker-stars]: https://img.shields.io/docker/stars/tautulli/tautulli?style=flat-square
[badge-downloads]: https://img.shields.io/github/downloads/Tautulli/Tautulli/total?style=flat-square [badge-downloads]: https://img.shields.io/github/downloads/Tautulli/Tautulli/total?style=flat-square
@ -57,24 +57,24 @@ Read the [Installation Guides][Installation] for instructions on how to install
[badge-release-nightly-last-commit]: https://img.shields.io/github/last-commit/Tautulli/Tautulli/nightly?style=flat-square&color=blue [badge-release-nightly-last-commit]: https://img.shields.io/github/last-commit/Tautulli/Tautulli/nightly?style=flat-square&color=blue
[badge-release-nightly-commits]: https://img.shields.io/github/commits-since/Tautulli/Tautulli/latest/nightly?style=flat-square&color=blue [badge-release-nightly-commits]: https://img.shields.io/github/commits-since/Tautulli/Tautulli/latest/nightly?style=flat-square&color=blue
[badge-docker-master]: https://img.shields.io/badge/docker-latest-blue?style=flat-square [badge-docker-master]: https://img.shields.io/badge/docker-latest-blue?style=flat-square
[badge-docker-master-ci]: https://img.shields.io/github/workflow/status/Tautulli/Tautulli/Publish%20Docker/master?style=flat-square [badge-docker-master-ci]: https://img.shields.io/github/actions/workflow/status/Tautulli/Tautulli/.github/workflows/publish-docker.yml?style=flat-square&branch=master
[badge-docker-beta]: https://img.shields.io/badge/docker-beta-blue?style=flat-square [badge-docker-beta]: https://img.shields.io/badge/docker-beta-blue?style=flat-square
[badge-docker-beta-ci]: https://img.shields.io/github/workflow/status/Tautulli/Tautulli/Publish%20Docker/beta?style=flat-square [badge-docker-beta-ci]: https://img.shields.io/github/actions/workflow/status/Tautulli/Tautulli/.github/workflows/publish-docker.yml?style=flat-square&branch=beta
[badge-docker-nightly]: https://img.shields.io/badge/docker-nightly-blue?style=flat-square [badge-docker-nightly]: https://img.shields.io/badge/docker-nightly-blue?style=flat-square
[badge-docker-nightly-ci]: https://img.shields.io/github/workflow/status/Tautulli/Tautulli/Publish%20Docker/nightly?style=flat-square [badge-docker-nightly-ci]: https://img.shields.io/github/actions/workflow/status/Tautulli/Tautulli/.github/workflows/publish-docker.yml?style=flat-square&branch=nightly
[badge-snap-master]: https://img.shields.io/badge/snap-stable-blue?style=flat-square [badge-snap-master]: https://img.shields.io/badge/snap-stable-blue?style=flat-square
[badge-snap-master-ci]: https://img.shields.io/github/workflow/status/Tautulli/Tautulli/Publish%20Snap/master?style=flat-square [badge-snap-master-ci]: https://img.shields.io/github/actions/workflow/status/Tautulli/Tautulli/.github/workflows/publish-snap.yml?style=flat-square&branch=master
[badge-snap-beta]: https://img.shields.io/badge/snap-beta-blue?style=flat-square [badge-snap-beta]: https://img.shields.io/badge/snap-beta-blue?style=flat-square
[badge-snap-beta-ci]: https://img.shields.io/github/workflow/status/Tautulli/Tautulli/Publish%20Snap/beta?style=flat-square [badge-snap-beta-ci]: https://img.shields.io/github/actions/workflow/status/Tautulli/Tautulli/.github/workflows/publish-snap.yml?style=flat-square&branch=beta
[badge-snap-nightly]: https://img.shields.io/badge/snap-edge-blue?style=flat-square [badge-snap-nightly]: https://img.shields.io/badge/snap-edge-blue?style=flat-square
[badge-snap-nightly-ci]: https://img.shields.io/github/workflow/status/Tautulli/Tautulli/Publish%20Snap/nightly?style=flat-square [badge-snap-nightly-ci]: https://img.shields.io/github/actions/workflow/status/Tautulli/Tautulli/.github/workflows/publish-snap.yml?style=flat-square&branch=nightly
[badge-installer-master-win]: https://img.shields.io/github/v/release/Tautulli/Tautulli?label=windows&style=flat-square [badge-installer-master-win]: https://img.shields.io/github/v/release/Tautulli/Tautulli?label=windows&style=flat-square
[badge-installer-master-macos]: https://img.shields.io/github/v/release/Tautulli/Tautulli?label=macos&style=flat-square [badge-installer-master-macos]: https://img.shields.io/github/v/release/Tautulli/Tautulli?label=macos&style=flat-square
[badge-installer-master-ci]: https://img.shields.io/github/workflow/status/Tautulli/Tautulli/Publish%20Installers/master?style=flat-square [badge-installer-master-ci]: https://img.shields.io/github/actions/workflow/status/Tautulli/Tautulli/.github/workflows/publish-installers.yml?style=flat-square&branch=master
[badge-installer-beta-win]: https://img.shields.io/github/v/release/Tautulli/Tautulli?label=windows&include_prereleases&style=flat-square [badge-installer-beta-win]: https://img.shields.io/github/v/release/Tautulli/Tautulli?label=windows&include_prereleases&style=flat-square
[badge-installer-beta-macos]: https://img.shields.io/github/v/release/Tautulli/Tautulli?label=macos&include_prereleases&style=flat-square [badge-installer-beta-macos]: https://img.shields.io/github/v/release/Tautulli/Tautulli?label=macos&include_prereleases&style=flat-square
[badge-installer-beta-ci]: https://img.shields.io/github/workflow/status/Tautulli/Tautulli/Publish%20Installers/beta?style=flat-square [badge-installer-beta-ci]: https://img.shields.io/github/actions/workflow/status/Tautulli/Tautulli/.github/workflows/publish-installers.yml?style=flat-square&branch=beta
[badge-installer-nightly-ci]: https://img.shields.io/github/workflow/status/Tautulli/Tautulli/Publish%20Installers/nightly?style=flat-square [badge-installer-nightly-ci]: https://img.shields.io/github/actions/workflow/status/Tautulli/Tautulli/.github/workflows/publish-installers.yml?style=flat-square&branch=nightly
## Support ## Support
@ -129,7 +129,7 @@ This is free software under the GPL v3 open source license. Feel free to do with
but any modification must be open sourced. A copy of the license is included. 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 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 non-commercial use. Commercial users must licence this software, for more information visit
https://shop.highsoft.com/faq/non-commercial#non-commercial-redistribution. https://shop.highsoft.com/faq/non-commercial#non-commercial-redistribution.

View file

@ -23,18 +23,18 @@ import sys
# Ensure lib added to path, before any other imports # Ensure lib added to path, before any other imports
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), 'lib')) sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), 'lib'))
from future.builtins import str
import appdirs
import argparse import argparse
import datetime import datetime
import locale import locale
import platformdirs
import pytz import pytz
import signal import signal
import shutil import shutil
import time import time
import threading import threading
import tzlocal import tzlocal
import ctypes
import plexpy import plexpy
from plexpy import common, config, database, helpers, logger, webstart from plexpy import common, config, database, helpers, logger, webstart
@ -70,8 +70,26 @@ def main():
plexpy.SYS_ENCODING = None plexpy.SYS_ENCODING = None
try: try:
locale.setlocale(locale.LC_ALL, "")
plexpy.SYS_LANGUAGE, plexpy.SYS_ENCODING = locale.getdefaultlocale() # Attempt to get the system's locale settings
language_code, encoding = locale.getlocale()
# Special handling for Windows platform
if sys.platform == 'win32':
# Get the user's current language settings on Windows
windll = ctypes.windll.kernel32
lang_id = windll.GetUserDefaultLCID()
# Map Windows language ID to locale identifier
language_code = locale.windows_locale.get(lang_id, '')
# Get the preferred encoding
encoding = locale.getpreferredencoding()
# Assign values to application-specific variable
plexpy.SYS_LANGUAGE = language_code
plexpy.SYS_ENCODING = encoding
except (locale.Error, IOError): except (locale.Error, IOError):
pass pass
@ -111,7 +129,7 @@ def main():
if args.quiet: if args.quiet:
plexpy.QUIET = True plexpy.QUIET = True
# Do an intial setup of the logger. # Do an initial setup of the logger.
# Require verbose for pre-initilization to see critical errors # Require verbose for pre-initilization to see critical errors
logger.initLogger(console=not plexpy.QUIET, log_dir=False, verbose=True) logger.initLogger(console=not plexpy.QUIET, log_dir=False, verbose=True)
@ -186,7 +204,7 @@ def main():
if args.datadir: if args.datadir:
plexpy.DATA_DIR = args.datadir plexpy.DATA_DIR = args.datadir
elif plexpy.FROZEN: elif plexpy.FROZEN:
plexpy.DATA_DIR = appdirs.user_data_dir("Tautulli", False) plexpy.DATA_DIR = platformdirs.user_data_dir("Tautulli", False)
else: else:
plexpy.DATA_DIR = plexpy.PROG_DIR plexpy.DATA_DIR = plexpy.PROG_DIR
@ -246,23 +264,13 @@ def main():
# Start the background threads # Start the background threads
plexpy.start() plexpy.start()
# Force the http port if neccessary # Force the http port if necessary
if args.port: if args.port:
plexpy.HTTP_PORT = args.port plexpy.HTTP_PORT = args.port
logger.info('Using forced web server port: %i', plexpy.HTTP_PORT) logger.info('Using forced web server port: %i', plexpy.HTTP_PORT)
else: else:
plexpy.HTTP_PORT = int(plexpy.CONFIG.HTTP_PORT) plexpy.HTTP_PORT = int(plexpy.CONFIG.HTTP_PORT)
# Check if pyOpenSSL is installed. It is required for certificate generation
# and for CherryPy.
if plexpy.CONFIG.ENABLE_HTTPS:
try:
import OpenSSL
except ImportError:
logger.warn("The pyOpenSSL module is missing. Install this "
"module to enable HTTPS. HTTPS will be disabled.")
plexpy.CONFIG.ENABLE_HTTPS = False
# Try to start the server. Will exit here is address is already in use. # Try to start the server. Will exit here is address is already in use.
webstart.start() webstart.start()

View file

@ -13,6 +13,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content=""> <meta name="description" content="">
<meta name="author" content=""> <meta name="author" content="">
<meta name="referrer" content="no-referrer">
<link href="${http_root}css/bootstrap3/bootstrap.min.css" rel="stylesheet"> <link href="${http_root}css/bootstrap3/bootstrap.min.css" rel="stylesheet">
<link href="${http_root}css/pnotify.custom.min.css" rel="stylesheet" /> <link href="${http_root}css/pnotify.custom.min.css" rel="stylesheet" />
<link href="${http_root}css/selectize.bootstrap3.css" rel="stylesheet" /> <link href="${http_root}css/selectize.bootstrap3.css" rel="stylesheet" />
@ -123,11 +124,6 @@
% else: % else:
<li><a href="graphs">Graphs</a></li> <li><a href="graphs">Graphs</a></li>
% endif % endif
% if title == "Synced Items":
<li class="active"><a href="sync">Synced Items</a></li>
% else:
<li><a href="sync">Synced Items</a></li>
% endif
% if title == "Settings": % if title == "Settings":
<li class="dropdown active"> <li class="dropdown active">
% else: % else:
@ -236,8 +232,9 @@ ${next.modalIncludes()}
<ul id="donation_type" class="nav nav-pills" role="tablist" style="display: flex; justify-content: center; margin: 10px 0;"> <ul id="donation_type" class="nav nav-pills" role="tablist" style="display: flex; justify-content: center; margin: 10px 0;">
<li class="active"><a href="#github-donation" role="tab" data-toggle="tab">GitHub</a></li> <li class="active"><a href="#github-donation" role="tab" data-toggle="tab">GitHub</a></li>
<li><a href="#patreon-donation" role="tab" data-toggle="tab">Patreon</a></li> <li><a href="#patreon-donation" role="tab" data-toggle="tab">Patreon</a></li>
<li><a href="#stripe-donation" role="tab" data-toggle="tab">Stripe</a></li>
<li><a href="#paypal-donation" role="tab" data-toggle="tab">PayPal</a></li> <li><a href="#paypal-donation" role="tab" data-toggle="tab">PayPal</a></li>
<li><a href="#crypto-donation" role="tab" data-toggle="tab">Crypto</a></li> <li><a href="#crypto-donation" role="tab" data-toggle="tab" id="crypto-donation-tab">Crypto</a></li>
</ul> </ul>
<div class="tab-content"> <div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="github-donation" style="text-align: center"> <div role="tabpanel" class="tab-pane active" id="github-donation" style="text-align: center">
@ -262,12 +259,23 @@ ${next.modalIncludes()}
</p> </p>
<p class="small-muted">(Patreon has a fee)</p> <p class="small-muted">(Patreon has a fee)</p>
</div> </div>
<div role="tabpanel" class="tab-pane" id="stripe-donation" style="text-align: center">
<p>
Click the button below to continue to Stripe.
</p>
<p>
<a href="${anon_url('https://donate.stripe.com/5kA7vnb7dczVbxC9AA')}" target="_blank" rel="noreferrer">
<img src="images/Stripe_wordmark_-_white_small_28px.png" alt="Stripe" style="background-color: #7068fe; border-radius: 3px; padding: 3px;">
</a>
</p>
<p class="small-muted">(Stripe has a fee)</p>
</div>
<div role="tabpanel" class="tab-pane" id="paypal-donation" style="text-align: center"> <div role="tabpanel" class="tab-pane" id="paypal-donation" style="text-align: center">
<p> <p>
Click the button below to continue to PayPal. Click the button below to continue to PayPal.
</p> </p>
<p> <p>
<a href="${anon_url('https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=6XPPKTDSX9QFL&lc=US&item_name=Tautulli&currency_code=USD&bn=PP%2dDonationsBF%3abtn_donate_LG%2egif%3aNonHosted')}" target="_blank" rel="noreferrer"> <a href="${anon_url('https://www.paypal.com/donate/?hosted_button_id=CUHSQ99KAKC5Q')}" target="_blank" rel="noreferrer">
<img src="images/gold-rect-paypal-34px.png" alt="PayPal"> <img src="images/gold-rect-paypal-34px.png" alt="PayPal">
</a> </a>
</p> </p>
@ -275,7 +283,16 @@ ${next.modalIncludes()}
</div> </div>
<div role="tabpanel" class="tab-pane" id="crypto-donation" style="text-align: center"> <div role="tabpanel" class="tab-pane" id="crypto-donation" style="text-align: center">
<p> <p>
Click the button below to continue to Coinbase. Select a cryptocurrency.
</p>
<select class="form-control" id="crypto-select"></select>
<div id="crypto-qrcode"></div>
<div id="crypto-address" class="form-group">
<label>Address:</label>
<span class="inline-pre" id="crypto-address-value"></span>
</div>
<p>
Or click the button below to continue to Coinbase.
</p> </p>
<a href="${anon_url('https://commerce.coinbase.com/checkout/8a9fa08c-8a38-409e-9220-868124c4ba0c')}" target="_blank" rel="noreferrer" class="donate-with-crypto"> <a href="${anon_url('https://commerce.coinbase.com/checkout/8a9fa08c-8a38-409e-9220-868124c4ba0c')}" target="_blank" rel="noreferrer" class="donate-with-crypto">
<span>Donate with Crypto</span> <span>Donate with Crypto</span>
@ -323,6 +340,7 @@ ${next.modalIncludes()}
<script src="${http_root}js/blurhash_pure_js_port.min.js"></script> <script src="${http_root}js/blurhash_pure_js_port.min.js"></script>
<script src="${http_root}js/script.js${cache_param}"></script> <script src="${http_root}js/script.js${cache_param}"></script>
<script src="${http_root}js/ajaxNotifications.js"></script> <script src="${http_root}js/ajaxNotifications.js"></script>
<script src="${http_root}js/kjua.min.js"></script>
<script> <script>
% if _session['user_group'] == 'admin': % if _session['user_group'] == 'admin':
$('body').on('click', '#updateDismiss', function() { $('body').on('click', '#updateDismiss', function() {
@ -396,6 +414,42 @@ ${next.modalIncludes()}
checkUpdate(function () { $('#nav-update').html('<i class="fa fa-fw fa-arrow-alt-circle-up"></i> Check for Updates'); }); checkUpdate(function () { $('#nav-update').html('<i class="fa fa-fw fa-arrow-alt-circle-up"></i> Check for Updates'); });
}); });
$('#crypto-donation-tab').one('shown.bs.tab', function (e) {
$.ajax({
url: 'https://tautulli.com/donate/crypto-addresses.json',
type: 'GET',
dataType: 'json',
cache: false,
async: true,
success: function (data) {
$('#crypto-select').empty().append('<option selected disabled>Select Cryptocurrency</option>');
$.each(data, function (index, crypto) {
$('<option/>', {
text: crypto.name + ' (' + crypto.symbol + ')',
value: crypto.address
}).appendTo('#crypto-select');
});
},
error: function () {
$('#crypto-select').empty().append('<option selected disabled>Error: Unable to load addresses</option>');
}
});
});
$('#crypto-select').change(function() {
var address = $(this).val();
$('#crypto-qrcode').empty().kjua({
text: address,
render: 'canvas',
ecLevel: 'H',
size: 256,
fill: '#000',
back: '#eee'
}).show();
$('#crypto-address-value').text(address);
$('#crypto-address').show();
})
% endif % endif
$('.dropdown-toggle').click(function (e) { $('.dropdown-toggle').click(function (e) {

View file

@ -11,6 +11,7 @@ DOCUMENTATION :: END
<%! <%!
import os import os
import sqlite3
import sys import sys
import plexpy import plexpy
from plexpy import common, logger from plexpy import common, logger
@ -71,10 +72,18 @@ DOCUMENTATION :: END
<td>System Timezone:</td> <td>System Timezone:</td>
<td>${str(plexpy.SYS_TIMEZONE)} (${'UTC{}'.format(plexpy.SYS_UTC_OFFSET)}) <td>${str(plexpy.SYS_TIMEZONE)} (${'UTC{}'.format(plexpy.SYS_UTC_OFFSET)})
</tr> </tr>
<tr>
<td>System Language:</td>
<td>${plexpy.SYS_LANGUAGE}${' (override {})'.format(plexpy.CONFIG.PMS_LANGUAGE) if plexpy.CONFIG.PMS_LANGUAGE else ''}</td>
</tr>
<tr> <tr>
<td>Python Version:</td> <td>Python Version:</td>
<td>${sys.version}</td> <td>${sys.version}</td>
</tr> </tr>
<tr>
<td>SQLite Version:</td>
<td>${sqlite3.sqlite_version}</td>
</tr>
<tr> <tr>
<td class="top-line">Resources:</td> <td class="top-line">Resources:</td>
<td class="top-line"> <td class="top-line">

File diff suppressed because one or more lines are too long

View file

@ -79,7 +79,6 @@ select.form-control {
color: #eee !important; color: #eee !important;
border: 0px solid #444 !important; border: 0px solid #444 !important;
background: #555 !important; background: #555 !important;
padding: 1px 2px;
transition: background-color .3s; transition: background-color .3s;
} }
.selectize-control.form-control .selectize-input { .selectize-control.form-control .selectize-input {
@ -87,7 +86,6 @@ select.form-control {
align-items: center; align-items: center;
flex-wrap: wrap; flex-wrap: wrap;
margin-bottom: 4px; margin-bottom: 4px;
padding-left: 5px;
} }
.selectize-control.form-control.selectize-pms-ip .selectize-input { .selectize-control.form-control.selectize-pms-ip .selectize-input {
padding-left: 12px !important; padding-left: 12px !important;
@ -122,6 +120,16 @@ select.form-control {
#condition-widget .fa-minus { #condition-widget .fa-minus {
cursor: pointer; cursor: pointer;
} }
#condition-widget .condition-operator-col {
padding-left: 0;
padding-right: 0;
}
@media (max-width: 767px) {
#condition-widget .condition-operator-col {
padding-left: 15px;
padding-right: 15px;
}
}
.react-selectize.root-node .react-selectize-control .react-selectize-placeholder { .react-selectize.root-node .react-selectize-control .react-selectize-placeholder {
color: #eee !important; color: #eee !important;
} }
@ -680,6 +688,13 @@ textarea.form-control:focus {
.btn-form:focus { .btn-form:focus {
color: #eee; color: #eee;
} }
.btn-filter.active,
.btn-filter.active.focus {
background-color: #b7800a !important;
}
.btn-filter.active:hover {
background-color: #896007 !important;
}
.form-control-feedback { .form-control-feedback {
color: #E5A00D; color: #E5A00D;
margin: 5px 40px 5px 0; margin: 5px 40px 5px 0;
@ -950,7 +965,7 @@ a .users-poster-face:hover {
font-size: 10px; font-size: 10px;
text-align: right; text-align: right;
text-transform: uppercase; text-transform: uppercase;
line-height: 14px; line-height: 10px;
-webkit-flex-shrink: 0; -webkit-flex-shrink: 0;
flex-shrink: 0; flex-shrink: 0;
} }
@ -990,6 +1005,12 @@ a .users-poster-face:hover {
font-size: 10px; font-size: 10px;
z-index: 2; z-index: 2;
} }
.dashboard-activity-info-channel {
display: inline-block;
max-width: 75px;
text-overflow: ellipsis;
overflow: hidden;
}
.dashboard-activity-progress { .dashboard-activity-progress {
width: 100%; width: 100%;
height: 5px; height: 5px;
@ -1457,7 +1478,8 @@ a:hover .dashboard-stats-square {
text-align: center; text-align: center;
position: relative; position: relative;
z-index: 0; z-index: 0;
overflow: hidden; overflow: auto;
scrollbar-width: none;
} }
.dashboard-recent-media { .dashboard-recent-media {
width: 100%; width: 100%;
@ -2833,6 +2855,30 @@ a .home-platforms-list-cover-face:hover
overflow: hidden; overflow: hidden;
max-width: 350px; max-width: 350px;
} }
.circle {
width: 1.55rem;
height: 1.55rem;
border-radius: 50%;
border: 0.2rem solid #eeeeee;
}
.circle-quarter {
background-image:
linear-gradient(00deg, #2b2b2b 50%, transparent 50%),
linear-gradient(270deg, #eeeeee 50%, transparent 50%);
}
.circle-half {
background-image:
linear-gradient(90deg, #2b2b2b 50%, transparent 50%),
linear-gradient(-90deg, #eeeeee 50%, transparent 50%);
}
.circle-three-quarter {
background-image:
linear-gradient(180deg, transparent 50%, #eeeeee 50%),
linear-gradient(-90deg, #eeeeee 50%, transparent 50%);
}
.circle-full {
background: #eeeeee;
}
#graph-tabs { #graph-tabs {
padding-bottom: 10px; padding-bottom: 10px;
float: none; float: none;
@ -2893,7 +2939,7 @@ a .home-platforms-list-cover-face:hover
margin-bottom: -20px; margin-bottom: -20px;
width: 100%; width: 100%;
max-width: 1750px; max-width: 1750px;
overflow: hidden; display: flow-root;
} }
.table-card-back td { .table-card-back td {
font-size: 12px; font-size: 12px;
@ -2963,7 +3009,8 @@ a .home-platforms-list-cover-face:hover
max-width: 900px; max-width: 900px;
} }
.stacked-configs > li > span { .stacked-configs > li > span {
display: block; display: inline-block;
width: inherit;
padding: 8px 20px 8px 15px; padding: 8px 20px 8px 15px;
color: #eee; color: #eee;
border-left: 2px solid #444; border-left: 2px solid #444;
@ -4278,6 +4325,10 @@ a:hover .overlay-refresh-image:hover {
.stream-info tr:nth-child(even) td { .stream-info tr:nth-child(even) td {
background-color: rgba(255,255,255,0.010); background-color: rgba(255,255,255,0.010);
} }
.stream-info td:nth-child(3),
.stream-info th:nth-child(3) {
width: 25px;
}
.number-input { .number-input {
margin: 0 !important; margin: 0 !important;
width: 55px !important; width: 55px !important;
@ -4524,12 +4575,32 @@ a.donate-with-crypto::after {
top: 0; top: 0;
left: 0; left: 0;
} }
#crypto-select {
width: 280px;
margin: 15px auto;
}
#crypto-qrcode {
width: 258px;
padding: 0;
margin: 15px auto;
line-height: 0;
text-align: center;
background-color: #eee;
border: 1px solid #ccc;
border-radius: 4px;
display: none;
}
#crypto-address {
margin: 15px auto;
text-align: center;
display: none;
}
#api_qr_code { #api_qr_code {
width: 100%; width: 100%;
padding: 0; padding: 0;
margin: 0 0 10px; margin: 0 0 10px;
line-height: 1; line-height: 0;
text-align: center; text-align: center;
background-color: #eee; background-color: #eee;
border: 1px solid #ccc; border: 1px solid #ccc;

View file

@ -62,7 +62,7 @@ DOCUMENTATION :: END
% if session is not None: % if session is not None:
<% <%
from collections import defaultdict from collections import defaultdict
from plexpy.helpers import cast_to_int, page, short_season from plexpy.helpers import cast_to_int, get_percent, page, short_season
from plexpy.common import VIDEO_RESOLUTION_OVERRIDES, AUDIO_CODEC_OVERRIDES, EXTRA_TYPES from plexpy.common import VIDEO_RESOLUTION_OVERRIDES, AUDIO_CODEC_OVERRIDES, EXTRA_TYPES
import plexpy import plexpy
%> %>
@ -74,6 +74,7 @@ DOCUMENTATION :: END
parent_href = page('info', data['parent_rating_key']) parent_href = page('info', data['parent_rating_key'])
grandparent_href = page('info', data['grandparent_rating_key']) grandparent_href = page('info', data['grandparent_rating_key'])
user_href = page('user', data['user_id']) if data['user_id'] else '#' user_href = page('user', data['user_id']) if data['user_id'] else '#'
library_href = page('library', data['section_id']) if data['section_id'] else '#'
season = short_season(data['parent_title']) season = short_season(data['parent_title'])
%> %>
<div class="dashboard-activity-instance" id="activity-instance-${sk}" data-key="${sk}" data-id="${data['session_id']}" <div class="dashboard-activity-instance" id="activity-instance-${sk}" data-key="${sk}" data-id="${data['session_id']}"
@ -160,7 +161,8 @@ DOCUMENTATION :: END
</li> </li>
<li class="dashboard-activity-info-item"> <li class="dashboard-activity-info-item">
<div class="sub-heading">Quality</div> <div class="sub-heading">Quality</div>
<div class="sub-value platform-right" id="stream_quality-${sk}"> <div class="sub-value platform-right">
<span id="stream_quality-${sk}">
% if data['media_type'] != 'photo' and data['quality_profile'] != 'Unknown': % if data['media_type'] != 'photo' and data['quality_profile'] != 'Unknown':
<% <%
br = cast_to_int(data['stream_bitrate']) or '' br = cast_to_int(data['stream_bitrate']) or ''
@ -174,6 +176,8 @@ DOCUMENTATION :: END
% else: % else:
${data['quality_profile']} ${data['quality_profile']}
% endif % endif
</span>
<span data-toggle="tooltip" title="Quality profile is only an estimate based on bitrate and may be incorrect"><i class="fa fa-exclamation-circle"></i></span>
</div> </div>
</li> </li>
% if data['optimized_version'] == 1: % if data['optimized_version'] == 1:
@ -231,11 +235,14 @@ DOCUMENTATION :: END
<div class="sub-value" id="video_decision-${sk}"> <div class="sub-value" id="video_decision-${sk}">
% if data['media_type'] in ('movie', 'episode', 'clip') and data['stream_video_decision']: % if data['media_type'] in ('movie', 'episode', 'clip') and data['stream_video_decision']:
<% <%
if data['video_dynamic_range'] == 'HDR': if data['video_dynamic_range'] != 'SDR':
video_dynamic_range = ' ' + data['video_dynamic_range'] video_dynamic_range = ' ' + data['video_dynamic_range']
else:
video_dynamic_range = ''
if data['stream_video_dynamic_range'] != 'SDR' or video_dynamic_range:
stream_video_dynamic_range = ' ' + data['stream_video_dynamic_range'] stream_video_dynamic_range = ' ' + data['stream_video_dynamic_range']
else: else:
video_dynamic_range = stream_video_dynamic_range = '' stream_video_dynamic_range = ''
%> %>
% if data['stream_video_decision'] == 'transcode': % if data['stream_video_decision'] == 'transcode':
<% <%
@ -259,12 +266,15 @@ DOCUMENTATION :: END
<div class="sub-heading">Audio</div> <div class="sub-heading">Audio</div>
<div class="sub-value" id="audio_decision-${sk}"> <div class="sub-value" id="audio_decision-${sk}">
% if data['stream_audio_decision']: % if data['stream_audio_decision']:
<%
audio_language = (data['audio_language'] or 'Unknown') + ' - ' if data['media_type'] != 'track' else ''
%>
% if data['stream_audio_decision'] == 'transcode': % 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()}) Transcode (${audio_language}${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': % 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()}) Direct Stream (${audio_language}${AUDIO_CODEC_OVERRIDES.get(data['stream_audio_codec'], data['stream_audio_codec'].upper())} ${data['stream_audio_channel_layout'].split('(')[0].capitalize()})
% else: % else:
Direct Play (${AUDIO_CODEC_OVERRIDES.get(data['stream_audio_codec'], data['stream_audio_codec'].upper())} ${data['stream_audio_channel_layout'].split('(')[0].capitalize()}) Direct Play (${audio_language}${AUDIO_CODEC_OVERRIDES.get(data['stream_audio_codec'], data['stream_audio_codec'].upper())} ${data['stream_audio_channel_layout'].split('(')[0].capitalize()})
% endif % endif
% endif % endif
</div> </div>
@ -279,13 +289,13 @@ DOCUMENTATION :: END
subtitle_codec = 'None' if data['stream_subtitle_codec'] and data['stream_subtitle_transient'] else data['subtitle_codec'].upper() subtitle_codec = 'None' if data['stream_subtitle_codec'] and data['stream_subtitle_transient'] else data['subtitle_codec'].upper()
%> %>
% if data['stream_subtitle_decision'] == 'transcode': % if data['stream_subtitle_decision'] == 'transcode':
Transcode (${subtitle_codec} <i class="fa fa-long-arrow-right"></i> ${data['stream_subtitle_codec'].upper()}) Transcode (${data['subtitle_language'] or 'Unknown'} - ${subtitle_codec} <i class="fa fa-long-arrow-right"></i> ${data['stream_subtitle_codec'].upper()})
% elif data['stream_subtitle_decision'] == 'copy': % elif data['stream_subtitle_decision'] == 'copy':
Direct Stream (${subtitle_codec}) Direct Stream (${data['subtitle_language'] or 'Unknown'} - ${subtitle_codec})
% elif data['stream_subtitle_decision'] == 'burn': % elif data['stream_subtitle_decision'] == 'burn':
Burn (${subtitle_codec}) Burn (${data['subtitle_language'] or 'Unknown'} - ${subtitle_codec})
% else: % else:
Direct Play (${subtitle_codec if data['synced_version'] else data['stream_subtitle_codec'].upper()}) Direct Play (${data['subtitle_language'] or 'Unknown'} - ${subtitle_codec if data['synced_version'] else data['stream_subtitle_codec'].upper()})
% endif % endif
% else: % else:
None None
@ -360,7 +370,7 @@ DOCUMENTATION :: END
% if data['media_type'] != 'photo': % if data['media_type'] != 'photo':
<div class="dashboard-activity-info-time"> <div class="dashboard-activity-info-time">
% if data['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> <br /><span class="thumb-tooltip dashboard-activity-info-channel" data-toggle="popover" data-img="${data['channel_thumb']}" data-height="40" data-width="40">${data['channel_title'] or (data['channel_vcn'] + ' ' + data['channel_call_sign'])}</span>
% elif data['view_offset']: % elif data['view_offset']:
ETA: ETA:
<span id="stream-eta-${sk}"> <span id="stream-eta-${sk}">
@ -392,7 +402,8 @@ DOCUMENTATION :: END
% if data['live']: % 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> <div id="progress-bar-${sk}" class="progress-bar" style="width: 100%" data-state="live" data-toggle="tooltip" title="Stream Progress Live">Live</div>
% else: % 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> <% transcode_progress = get_percent(data['transcode_max_offset_available'] * 1000, data['duration']) or data['transcode_progress'] %>
<div id="buffer-bar-${sk}" class="buffer-bar" style="width: ${transcode_progress}%" data-toggle="tooltip" title="Transcoder Progress ${transcode_progress}%">${transcode_progress}%</div>
<div id="progress-bar-${sk}" class="progress-bar" style="width: ${data['progress_percent']}%" data-last_view_offset="${data['view_offset']}" data-view_offset="${data['view_offset']}" data-stream_duration="${data['stream_duration']}" data-state="${data['state']}" data-toggle="tooltip" title="Stream Progress ${data['progress_percent']}%">${data['progress_percent']}%</div> <div 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>
% endif % endif
</div> </div>
@ -453,21 +464,27 @@ DOCUMENTATION :: END
<div class="dashboard-activity-metadata-subtitle-container"> <div class="dashboard-activity-metadata-subtitle-container">
% if data['live']: % if data['live']:
<div id="media-type-${sk}" class="dashboard-activity-metadata-media_type-icon" title="Live TV"> <div id="media-type-${sk}" class="dashboard-activity-metadata-media_type-icon" title="Live TV">
<i class="fa fa-fw fa-broadcast-tower"></i>&nbsp; <a href="${library_href}">
<i class="fa fa-fw fa-broadcast-tower"></i>
</a>&nbsp;
</div> </div>
% elif data['channel_stream'] == 0: % elif data['channel_stream'] == 0:
<div id="media-type-${sk}" class="dashboard-activity-metadata-media_type-icon" title="${data['media_type'].capitalize()}"> <div id="media-type-${sk}" class="dashboard-activity-metadata-media_type-icon" title="${data['media_type'].capitalize()}">
<a href="${library_href}">
% if data['media_type'] == 'movie': % if data['media_type'] == 'movie':
<i class="fa fa-fw fa-film"></i>&nbsp; <i class="fa fa-fw fa-film"></i>
% elif data['media_type'] == 'episode': % elif data['media_type'] == 'episode':
<i class="fa fa-fw fa-television"></i>&nbsp; <i class="fa fa-fw fa-television"></i>
% elif data['media_type'] == 'track': % elif data['media_type'] == 'track':
<i class="fa fa-fw fa-music"></i>&nbsp; <i class="fa fa-fw fa-music"></i>
% elif data['media_type'] == 'photo': % elif data['media_type'] == 'photo':
<i class="fa fa-fw fa-picture-o"></i>&nbsp; <i class="fa fa-fw fa-picture-o"></i>
% elif data['media_type'] == 'clip': % elif data['media_type'] == 'clip':
<i class="fa fa-fw fa-video-camera"></i>&nbsp; <i class="fa fa-fw fa-video-camera"></i>
% else:
<i class="fa fa-fw fa-question-circle"></i>
% endif % endif
</a>&nbsp;
</div> </div>
% else: % else:
<div id="media-type-${sk}" class="dashboard-activity-metadata-media_type-icon" title="Channel"> <div id="media-type-${sk}" class="dashboard-activity-metadata-media_type-icon" title="Channel">

View file

@ -20,6 +20,7 @@ DOCUMENTATION :: END
export = exporter.Export() export = exporter.Export()
thumb_media_types = ', '.join([export.PLURAL_MEDIA_TYPES[k] for k, v in export.MEDIA_TYPES.items() if v[0]]) thumb_media_types = ', '.join([export.PLURAL_MEDIA_TYPES[k] for k, v in export.MEDIA_TYPES.items() if v[0]])
art_media_types = ', '.join([export.PLURAL_MEDIA_TYPES[k] for k, v in export.MEDIA_TYPES.items() if v[1]]) art_media_types = ', '.join([export.PLURAL_MEDIA_TYPES[k] for k, v in export.MEDIA_TYPES.items() if v[1]])
logo_media_types = ', '.join([export.PLURAL_MEDIA_TYPES[k] for k, v in export.MEDIA_TYPES.items() if v[2]])
%> %>
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
<div class="modal-content"> <div class="modal-content">
@ -144,6 +145,22 @@ DOCUMENTATION :: END
Select the level to export background artwork image files.<br>Note: Only applies to ${art_media_types}. Select the level to export background artwork image files.<br>Note: Only applies to ${art_media_types}.
</p> </p>
</div> </div>
<div class="form-group">
<label for="export_logo_level">Logo Image Export Level</label>
<div class="row">
<div class="col-md-12">
<select class="form-control" id="export_logo_level" name="export_logo_level">
<option value="0" selected>Level 0 - None / Custom</option>
<option value="1">Level 1 - Uploaded and Selected Logos Only</option>
<option value="2">Level 2 - Selected and Locked Logos Only</option>
<option value="9">Level 9 - All Selected Logos</option>
</select>
</div>
</div>
<p class="help-block">
Select the level to export logo image files.<br>Note: Only applies to ${logo_media_types}.
</p>
</div>
<p class="help-block"> <p class="help-block">
Warning: Exporting images may take a long time! Images will be saved to a folder alongside the data file. Warning: Exporting images may take a long time! Images will be saved to a folder alongside the data file.
</p> </p>
@ -226,11 +243,12 @@ DOCUMENTATION :: END
getExportFields(); getExportFields();
$('#export_file_format').on('change', function() { $('#export_file_format').on('change', function() {
if ($(this).val() === 'm3u8') { if ($(this).val() === 'm3u') {
$('#export_metadata_level').prop('disabled', true); $('#export_metadata_level').prop('disabled', true);
$('#export_media_info_level').prop('disabled', true); $('#export_media_info_level').prop('disabled', true);
$("#export_thumb_level").prop('disabled', true); $("#export_thumb_level").prop('disabled', true);
$("#export_art_level").prop('disabled', true); $("#export_art_level").prop('disabled', true);
$("#export_logo_level").prop('disabled', true);
export_custom_metadata_fields.disable(); export_custom_metadata_fields.disable();
export_custom_media_info_fields.disable(); export_custom_media_info_fields.disable();
} else { } else {
@ -238,6 +256,7 @@ DOCUMENTATION :: END
$('#export_media_info_level').prop('disabled', false); $('#export_media_info_level').prop('disabled', false);
$("#export_thumb_level").prop('disabled', false); $("#export_thumb_level").prop('disabled', false);
$("#export_art_level").prop('disabled', false); $("#export_art_level").prop('disabled', false);
$("#export_logo_level").prop('disabled', false);
export_custom_metadata_fields.enable(); export_custom_metadata_fields.enable();
export_custom_media_info_fields.enable(); export_custom_media_info_fields.enable();
} }
@ -252,6 +271,7 @@ DOCUMENTATION :: END
var file_format = $('#export_file_format option:selected').val(); var file_format = $('#export_file_format option:selected').val();
var thumb_level = $("#export_thumb_level option:selected").val(); var thumb_level = $("#export_thumb_level option:selected").val();
var art_level = $("#export_art_level option:selected").val(); var art_level = $("#export_art_level option:selected").val();
var logo_level = $("#export_logo_level option:selected").val();
var custom_fields = [ var custom_fields = [
$('#export_custom_metadata_fields').val(), $('#export_custom_metadata_fields').val(),
$('#export_custom_media_info_fields').val() $('#export_custom_media_info_fields').val()
@ -270,6 +290,7 @@ DOCUMENTATION :: END
file_format: file_format, file_format: file_format,
thumb_level: thumb_level, thumb_level: thumb_level,
art_level: art_level, art_level: art_level,
logo_level: logo_level,
custom_fields: custom_fields, custom_fields: custom_fields,
export_type: export_type, export_type: export_type,
individual_files: individual_files individual_files: individual_files

View file

@ -1,6 +1,7 @@
<%inherit file="base.html"/> <%inherit file="base.html"/>
<%def name="headIncludes()"> <%def name="headIncludes()">
<link rel="stylesheet" href="${http_root}css/bootstrap-select.min.css">
<link rel="stylesheet" href="${http_root}css/dataTables.bootstrap.min.css"> <link rel="stylesheet" href="${http_root}css/dataTables.bootstrap.min.css">
<link rel="stylesheet" href="${http_root}css/tautulli-dataTables.css"> <link rel="stylesheet" href="${http_root}css/tautulli-dataTables.css">
</%def> </%def>
@ -14,17 +15,15 @@
<div class="button-bar"> <div class="button-bar">
<div class="btn-group" id="user-selection"> <div class="btn-group" id="user-selection">
<label> <label>
<select name="graph-user" id="graph-user" class="btn" style="color: inherit;"> <select name="graph-user" id="graph-user" multiple>
<option value="">All Users</option>
<option disabled>&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;</option>
</select> </select>
</label> </label>
</div> </div>
<div class="btn-group" style="margin-right: 2px;" data-toggle="buttons" id="yaxis-selection"> <div class="btn-group" style="margin-right: 2px;" data-toggle="buttons" id="yaxis-selection">
<label class="btn btn-dark"> <label class="btn btn-dark btn-filter">
<input type="radio" name="yaxis-options" id="yaxis-plays" value="plays" autocomplete="off"> Play Count <input type="radio" name="yaxis-options" id="yaxis-plays" value="plays" autocomplete="off"> Play Count
</label> </label>
<label class="btn btn-dark"> <label class="btn btn-dark btn-filter">
<input type="radio" name="yaxis-options" id="yaxis-duration" value="duration" autocomplete="off"> Play Duration <input type="radio" name="yaxis-options" id="yaxis-duration" value="duration" autocomplete="off"> Play Duration
</label> </label>
</div> </div>
@ -138,6 +137,20 @@
</div> </div>
</div> </div>
</div> </div>
<div class="row" id="concurrent-graph">
<div class="col-md-12">
<h4><i class="fa fa-video-camera"></i> Daily concurrent stream count</span> by stream type <small>Last <span class="days">30</span> days</small></h4>
<p class="help-block">
The total count of concurrent streams of tv, movies, and music by the transcode decision.
</p>
<div class="graphs-instance">
<div class="watch-chart" id="graph_concurrent_streams_by_stream_type">
<div class="graphs-load"><i class="fa fa-refresh fa-spin"></i> Loading chart...</div>
<br>
</div>
</div>
</div>
</div>
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<h4><i class="fa fa-expand-arrows-alt"></i> <span class="yaxis-text">Play count</span> by source resolution <small>Last <span class="days">30</span> days</small></h4> <h4><i class="fa fa-expand-arrows-alt"></i> <span class="yaxis-text">Play count</span> by source resolution <small>Last <span class="days">30</span> days</small></h4>
@ -225,6 +238,7 @@
</%def> </%def>
<%def name="javascriptIncludes()"> <%def name="javascriptIncludes()">
<script src="${http_root}js/bootstrap-select.min.js"></script>
<script src="${http_root}js/highcharts.min.js"></script> <script src="${http_root}js/highcharts.min.js"></script>
<script src="${http_root}js/jquery.dataTables.min.js"></script> <script src="${http_root}js/jquery.dataTables.min.js"></script>
<script src="${http_root}js/dataTables.bootstrap.min.js"></script> <script src="${http_root}js/dataTables.bootstrap.min.js"></script>
@ -287,6 +301,10 @@
return obj; return obj;
}, {}); }, {});
if (!("Total" in chart_visibility)) {
chart_visibility["Total"] = false;
}
return data_series.map(function(s) { return data_series.map(function(s) {
var obj = Object.assign({}, s); var obj = Object.assign({}, s);
obj.visible = (chart_visibility[s.name] !== false); obj.visible = (chart_visibility[s.name] !== false);
@ -312,7 +330,9 @@
'Live TV': '#19A0D7', 'Live TV': '#19A0D7',
'Direct Play': '#E5A00D', 'Direct Play': '#E5A00D',
'Direct Stream': '#FFFFFF', 'Direct Stream': '#FFFFFF',
'Transcode': '#F06464' 'Transcode': '#F06464',
'Max. Concurrent Streams': '#96C83C',
'Total': '#96C83C'
}; };
var series_colors = []; var series_colors = [];
$.each(data_series, function(index, series) { $.each(data_series, function(index, series) {
@ -327,6 +347,7 @@
<script src="${http_root}js/graphs/plays_by_platform.js${cache_param}"></script> <script src="${http_root}js/graphs/plays_by_platform.js${cache_param}"></script>
<script src="${http_root}js/graphs/plays_by_user.js${cache_param}"></script> <script src="${http_root}js/graphs/plays_by_user.js${cache_param}"></script>
<script src="${http_root}js/graphs/plays_by_stream_type.js${cache_param}"></script> <script src="${http_root}js/graphs/plays_by_stream_type.js${cache_param}"></script>
<script src="${http_root}js/graphs/concurrent_streams_by_stream_type.js${cache_param}"></script>
<script src="${http_root}js/graphs/plays_by_source_resolution.js${cache_param}"></script> <script src="${http_root}js/graphs/plays_by_source_resolution.js${cache_param}"></script>
<script src="${http_root}js/graphs/plays_by_stream_resolution.js${cache_param}"></script> <script src="${http_root}js/graphs/plays_by_stream_resolution.js${cache_param}"></script>
<script src="${http_root}js/graphs/plays_by_platform_by_stream_type.js${cache_param}"></script> <script src="${http_root}js/graphs/plays_by_platform_by_stream_type.js${cache_param}"></script>
@ -356,6 +377,10 @@
break break
} }
if (window.location.hash === '#concurrent-graph') {
current_tab = '#tabs-stream';
}
$('#yaxis-' + yaxis).prop('checked', true); $('#yaxis-' + yaxis).prop('checked', true);
$('#yaxis-' + yaxis).closest('label').addClass('active'); $('#yaxis-' + yaxis).closest('label').addClass('active');
$('#graph-days').val(current_day_range); $('#graph-days').val(current_day_range);
@ -364,8 +389,8 @@
//$(current_tab).addClass('active'); //$(current_tab).addClass('active');
$('.days').html(current_day_range); $('.days').text(current_day_range);
$('.months').html(current_month_range); $('.months').text(current_month_range);
// Load user ids and names (for the selector) // Load user ids and names (for the selector)
$.ajax({ $.ajax({
@ -373,14 +398,35 @@
type: 'get', type: 'get',
dataType: "json", dataType: "json",
success: function (data) { success: function (data) {
var select = $('#graph-user'); let select = $('#graph-user');
let by_id = {};
data.sort(function(a, b) { data.sort(function(a, b) {
return a.friendly_name.localeCompare(b.friendly_name); return a.friendly_name.localeCompare(b.friendly_name);
}); });
data.forEach(function(item) { data.forEach(function(item) {
select.append('<option value="' + item.user_id + '">' + select.append('<option value="' + item.user_id + '">' +
item.friendly_name + '</option>'); item.friendly_name + '</option>');
by_id[item.user_id] = item.friendly_name;
}); });
select.selectpicker({
countSelectedText: function(sel, total) {
if (sel === 0 || sel === total) {
return 'All users';
} else if (sel > 1) {
return sel + ' users';
} else {
return select.val().map(function(id) {
return by_id[id];
}).join(', ');
}
},
style: 'btn-dark',
actionsBox: true,
selectedTextFormat: 'count',
noneSelectedText: 'All users'
});
select.selectpicker('render');
select.selectpicker('selectAll');
} }
}); });
@ -519,6 +565,33 @@
} }
}); });
$.ajax({
url: "get_concurrent_streams_by_stream_type",
type: 'get',
data: { time_range: time_range, user_id: selected_user_id },
dataType: "json",
success: function(data) {
var dateArray = [];
$.each(data.categories, function (i, day) {
dateArray.push(moment(day, 'YYYY-MM-DD').valueOf());
// Highlight the weekend
if ((moment(day, 'YYYY-MM-DD').format('ddd') == 'Sat') ||
(moment(day, 'YYYY-MM-DD').format('ddd') == 'Sun')) {
hc_plays_by_day_options.xAxis.plotBands.push({
from: i-0.5,
to: i+0.5,
color: 'rgba(80,80,80,0.3)'
});
}
});
hc_concurrent_streams_by_stream_type_options.yAxis.min = 0;
hc_concurrent_streams_by_stream_type_options.xAxis.categories = dateArray;
hc_concurrent_streams_by_stream_type_options.series = getGraphVisibility(hc_concurrent_streams_by_stream_type_options.chart.renderTo, data.series);
hc_concurrent_streams_by_stream_type_options.colors = getGraphColors(data.series);
var hc_plays_by_stream_type = new Highcharts.Chart(hc_concurrent_streams_by_stream_type_options);
}
});
$.ajax({ $.ajax({
url: "get_plays_by_source_resolution", url: "get_plays_by_source_resolution",
type: 'get', type: 'get',
@ -575,7 +648,7 @@
} }
}); });
$('#nav-tabs-2').tab('show'); $('#nav-tabs-stream').tab('show');
} }
function loadGraphsTab3(time_range, yaxis) { function loadGraphsTab3(time_range, yaxis) {
@ -602,11 +675,6 @@
$('#nav-tabs-total').tab('show'); $('#nav-tabs-total').tab('show');
} }
// Set initial state
if (current_tab === '#tabs-plays') { loadGraphsTab1(current_day_range, yaxis); }
if (current_tab === '#tabs-stream') { loadGraphsTab2(current_day_range, yaxis); }
if (current_tab === '#tabs-total') { loadGraphsTab3(current_month_range, yaxis); }
// Tab1 opened // Tab1 opened
$('#nav-tabs-plays').on('shown.bs.tab', function (e) { $('#nav-tabs-plays').on('shown.bs.tab', function (e) {
e.preventDefault(); e.preventDefault();
@ -639,7 +707,7 @@
setLocalStorage('graph_days', current_day_range); setLocalStorage('graph_days', current_day_range);
if (current_tab === '#tabs-plays') { loadGraphsTab1(current_day_range, yaxis); } if (current_tab === '#tabs-plays') { loadGraphsTab1(current_day_range, yaxis); }
if (current_tab === '#tabs-stream') { loadGraphsTab2(current_day_range, yaxis); } if (current_tab === '#tabs-stream') { loadGraphsTab2(current_day_range, yaxis); }
$('.days').html(current_day_range); $('.days').text(current_day_range);
}); });
// Month range changed // Month range changed
@ -649,12 +717,23 @@
current_month_range = $(this).val(); current_month_range = $(this).val();
setLocalStorage('graph_months', current_month_range); setLocalStorage('graph_months', current_month_range);
if (current_tab === '#tabs-total') { loadGraphsTab3(current_month_range, yaxis); } if (current_tab === '#tabs-total') { loadGraphsTab3(current_month_range, yaxis); }
$('.months').html(current_month_range); $('.months').text(current_month_range);
}); });
let graph_user_last_id = undefined;
// User changed // User changed
$('#graph-user').on('change', function() { $('#graph-user').on('change', function() {
selected_user_id = $(this).val() || null; let val = $(this).val();
if (val.length === 0 || val.length === $(this).children().length) {
selected_user_id = null; // if all users are selected, just send an empty list
} else {
selected_user_id = val.join(",");
}
if (selected_user_id === graph_user_last_id) {
return;
}
graph_user_last_id = selected_user_id;
if (current_tab === '#tabs-plays') { loadGraphsTab1(current_day_range, yaxis); } if (current_tab === '#tabs-plays') { loadGraphsTab1(current_day_range, yaxis); }
if (current_tab === '#tabs-stream') { loadGraphsTab2(current_day_range, yaxis); } if (current_tab === '#tabs-stream') { loadGraphsTab2(current_day_range, yaxis); }
if (current_tab === '#tabs-total') { loadGraphsTab3(current_month_range, yaxis); } if (current_tab === '#tabs-total') { loadGraphsTab3(current_month_range, yaxis); }
@ -681,6 +760,7 @@
if (this.points.length > 1) { if (this.points.length > 1) {
var total = 0; var total = 0;
$.each(this.points, function(i, point) { $.each(this.points, function(i, point) {
if (point.series.name === 'Total') return;
s += '<br/>'+point.series.name+': '+point.y; s += '<br/>'+point.series.name+': '+point.y;
total += point.y; total += point.y;
}); });
@ -707,6 +787,7 @@
if (this.points.length > 1) { if (this.points.length > 1) {
var total = 0; var total = 0;
$.each(this.points, function(i, point) { $.each(this.points, function(i, point) {
if (point.series.name === 'Total') return;
s += '<br/>'+point.series.name+': '+moment.duration(point.y, 'hours').format('D [days] H [hrs] m [mins]'); s += '<br/>'+point.series.name+': '+moment.duration(point.y, 'hours').format('D [days] H [hrs] m [mins]');
total += point.y; total += point.y;
}); });
@ -727,6 +808,7 @@
hc_plays_by_day_options.xAxis.plotBands = []; hc_plays_by_day_options.xAxis.plotBands = [];
hc_plays_by_stream_type_options.xAxis.plotBands = []; hc_plays_by_stream_type_options.xAxis.plotBands = [];
hc_concurrent_streams_by_stream_type_options.xAxis.plotBands = [];
hc_plays_by_day_options.yAxis.labels.formatter = yaxis_format; hc_plays_by_day_options.yAxis.labels.formatter = yaxis_format;
hc_plays_by_dayofweek_options.yAxis.labels.formatter = yaxis_format; hc_plays_by_dayofweek_options.yAxis.labels.formatter = yaxis_format;

View file

@ -1,6 +1,7 @@
<%inherit file="base.html"/> <%inherit file="base.html"/>
<%def name="headIncludes()"> <%def name="headIncludes()">
<link rel="stylesheet" href="${http_root}css/bootstrap-select.min.css">
<link rel="stylesheet" href="${http_root}css/dataTables.bootstrap.min.css"> <link rel="stylesheet" href="${http_root}css/dataTables.bootstrap.min.css">
<link rel="stylesheet" href="${http_root}css/dataTables.colVis.css"> <link rel="stylesheet" href="${http_root}css/dataTables.colVis.css">
<link rel="stylesheet" href="${http_root}css/tautulli-dataTables.css"> <link rel="stylesheet" href="${http_root}css/tautulli-dataTables.css">
@ -31,35 +32,33 @@
% if _session['user_group'] == 'admin': % if _session['user_group'] == 'admin':
<div class="btn-group" id="user-selection"> <div class="btn-group" id="user-selection">
<label> <label>
<select name="history-user" id="history-user" class="btn" style="color: inherit;"> <select name="history-user" id="history-user" multiple>
<option value="">All Users</option>
<option disabled>&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;</option>
</select> </select>
</label> </label>
</div> </div>
% endif % endif
<div class="btn-group" data-toggle="buttons" id="media_type-selection"> <div class="btn-group" data-toggle="buttons" id="media_type-selection">
<label class="btn btn-dark"> <label class="btn btn-dark btn-filter">
<input type="checkbox" name="media_type-filter" id="history-media_type-movie" value="movie" autocomplete="off"><i class="fa fa-film"></i> Movies <input type="checkbox" name="media_type-filter" id="history-media_type-movie" value="movie" autocomplete="off"><i class="fa fa-film"></i> Movies
</label> </label>
<label class="btn btn-dark"> <label class="btn btn-dark btn-filter">
<input type="checkbox" name="media_type-filter" id="history-media_type-episode" value="episode" autocomplete="off"><i class="fa fa-television"></i> TV Shows <input type="checkbox" name="media_type-filter" id="history-media_type-episode" value="episode" autocomplete="off"><i class="fa fa-television"></i> TV Shows
</label> </label>
<label class="btn btn-dark"> <label class="btn btn-dark btn-filter">
<input type="checkbox" name="media_type-filter" id="history-media_type-track" value="track" autocomplete="off"><i class="fa fa-music"></i> Music <input type="checkbox" name="media_type-filter" id="history-media_type-track" value="track" autocomplete="off"><i class="fa fa-music"></i> Music
</label> </label>
<label class="btn btn-dark"> <label class="btn btn-dark btn-filter">
<input type="checkbox" name="media_type-filter" id="history-media_type-live" value="live" autocomplete="off"><i class="fa fa-broadcast-tower"></i> Live TV <input type="checkbox" name="media_type-filter" id="history-media_type-live" value="live" autocomplete="off"><i class="fa fa-broadcast-tower"></i> Live TV
</label> </label>
</div> </div>
<div class="btn-group" data-toggle="buttons" id="transcode_decision-selection"> <div class="btn-group" data-toggle="buttons" id="transcode_decision-selection">
<label class="btn btn-dark"> <label class="btn btn-dark btn-filter">
<input type="checkbox" name="transcode_decision-filter" id="history-transcode_decision-direct_play" value="direct play" autocomplete="off"><i class="fa fa-play-circle"></i> Direct Play <input type="checkbox" name="transcode_decision-filter" id="history-transcode_decision-direct_play" value="direct play" autocomplete="off"><i class="fa fa-play-circle"></i> Direct Play
</label> </label>
<label class="btn btn-dark"> <label class="btn btn-dark btn-filter">
<input type="checkbox" name="transcode_decision-filter" id="history-transcode_decision-copy" value="copy" autocomplete="off"><i class="fa fa-stream"></i> Direct Stream <input type="checkbox" name="transcode_decision-filter" id="history-transcode_decision-copy" value="copy" autocomplete="off"><i class="fa fa-stream"></i> Direct Stream
</label> </label>
<label class="btn btn-dark"> <label class="btn btn-dark btn-filter">
<input type="checkbox" name="transcode_decision-filter" id="history-transcode_decision-transcode" value="transcode" autocomplete="off"><i class="fa fa-server"></i> Transcode <input type="checkbox" name="transcode_decision-filter" id="history-transcode_decision-transcode" value="transcode" autocomplete="off"><i class="fa fa-server"></i> Transcode
</label> </label>
</div> </div>
@ -84,7 +83,7 @@
<th align="left" id="started">Started</th> <th align="left" id="started">Started</th>
<th align="left" id="paused_counter">Paused</th> <th align="left" id="paused_counter">Paused</th>
<th align="left" id="stopped">Stopped</th> <th align="left" id="stopped">Stopped</th>
<th align="left" id="duration">Duration</th> <th align="left" id="play_duration">Duration</th>
<th align="left" id="percent_complete"></th> <th align="left" id="percent_complete"></th>
</tr> </tr>
</thead> </thead>
@ -121,6 +120,7 @@
</%def> </%def>
<%def name="javascriptIncludes()"> <%def name="javascriptIncludes()">
<script src="${http_root}js/bootstrap-select.min.js"></script>
<script src="${http_root}js/jquery.dataTables.min.js"></script> <script src="${http_root}js/jquery.dataTables.min.js"></script>
<script src="${http_root}js/dataTables.colVis.js"></script> <script src="${http_root}js/dataTables.colVis.js"></script>
<script src="${http_root}js/dataTables.bootstrap.min.js"></script> <script src="${http_root}js/dataTables.bootstrap.min.js"></script>
@ -134,17 +134,40 @@
type: 'GET', type: 'GET',
dataType: 'json', dataType: 'json',
success: function (data) { success: function (data) {
var select = $('#history-user'); let select = $('#history-user');
let by_id = {};
data.sort(function (a, b) { data.sort(function (a, b) {
return a.friendly_name.localeCompare(b.friendly_name); return a.friendly_name.localeCompare(b.friendly_name);
}); });
data.forEach(function (item) { data.forEach(function (item) {
select.append('<option value="' + item.user_id + '">' + select.append('<option value="' + item.user_id + '">' +
item.friendly_name + '</option>'); item.friendly_name + '</option>');
by_id[item.user_id] = item.friendly_name;
}); });
select.selectpicker({
countSelectedText: function(sel, total) {
if (sel === 0 || sel === total) {
return 'All users';
} else if (sel > 1) {
return sel + ' users';
} else {
return select.val().map(function(id) {
return by_id[id];
}).join(', ');
}
},
style: 'btn-dark',
actionsBox: true,
selectedTextFormat: 'count',
noneSelectedText: 'All users'
});
select.selectpicker('render');
select.selectpicker('selectAll');
} }
}); });
let history_user_last_id = undefined;
function loadHistoryTable(media_type, transcode_decision, selected_user_id) { function loadHistoryTable(media_type, transcode_decision, selected_user_id) {
history_table_options.ajax = { history_table_options.ajax = {
url: 'get_history', url: 'get_history',
@ -187,7 +210,16 @@
}); });
$('#history-user').on('change', function () { $('#history-user').on('change', function () {
selected_user_id = $(this).val() || null; let val = $(this).val();
if (val.length === 0 || val.length === $(this).children().length) {
selected_user_id = null; // if all users are selected, just send an empty list
} else {
selected_user_id = val.join(",");
}
if (selected_user_id === history_user_last_id) {
return;
}
history_user_last_id = selected_user_id;
history_table.draw(); history_table.draw();
}); });
} }

View file

@ -32,7 +32,7 @@
<th align="left" id="started">Started</th> <th align="left" id="started">Started</th>
<th align="left" id="paused_counter">Paused</th> <th align="left" id="paused_counter">Paused</th>
<th align="left" id="stopped">Stopped</th> <th align="left" id="stopped">Stopped</th>
<th align="left" id="duration">Duration</th> <th align="left" id="play_duration">Duration</th>
<th align="left" id="percent_complete"></th> <th align="left" id="percent_complete"></th>
</tr> </tr>
</thead> </thead>

View file

@ -77,9 +77,10 @@ DOCUMENTATION :: END
<% fallback = 'art-live' if row0['live'] else 'art' %> <% 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)});"> <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_libraries': % elif stat_id == 'top_libraries':
<div id="stats-background-${stat_id}" class="dashboard-stats-background" style="background-image: url(${page('pms_image_proxy', row0['art'], None, 500, 280, 40, '282828', 3, fallback='art')});"> <% fallback = 'art-live' if row0['live'] else row0['library_art'] %>
<div id="stats-background-${stat_id}" class="dashboard-stats-background" style="background-image: url(${page('pms_image_proxy', row0['art'] or row0['library_art'], None, 500, 280, 40, '282828', 3, fallback=fallback)});" data-library_art="${row0['library_art']}">
% elif stat_id == 'top_users': % elif stat_id == 'top_users':
<div id="stats-background-${stat_id}" class="dashboard-stats-background" data-blurhash="${page('pms_image_proxy', row0['user_thumb'] or 'interfaces/default/images/gravatar-default.png', None, 80, 80, 40, '282828', 0, fallback='user')}"> <div id="stats-background-${stat_id}" class="dashboard-stats-background" data-blurhash="${page('pms_image_proxy', row0['user_thumb'] or 'interfaces/default/images/gravatar-default.png', None, 100, 100, 40, '282828', 0, fallback='user')}">
% elif stat_id == 'top_platforms': % elif stat_id == 'top_platforms':
<div id="stats-background-${stat_id}" class="dashboard-stats-background platform-${row0['platform_name']}-rgba no-image"> <div id="stats-background-${stat_id}" class="dashboard-stats-background platform-${row0['platform_name']}-rgba no-image">
% else: % else:
@ -102,22 +103,22 @@ DOCUMENTATION :: END
if row0['live']: if row0['live']:
href = page('info', row0['rating_key'], row0['guid'], history=True, live=row0['live']) href = page('info', row0['rating_key'], row0['guid'], history=True, live=row0['live'])
else: else:
href = page('info', row0['rating_key']) href = page('info', row0['rating_key'], history=True)
%> %>
<a id="stats-thumb-url-${stat_id}" href="${href}" title="${row0['title']}"> <a id="stats-thumb-url-${stat_id}" href="${href}" title="${row0['title']}">
<div id="stats-thumb-${stat_id}" class="dashboard-stats-${fallback.split('-')[0]}" style="background-image: url(${page('pms_image_proxy', row0['thumb'], row0['grandparent_rating_key'] or row0['rating_key'], 300, height, fallback=fallback)});"></div> <div id="stats-thumb-${stat_id}" class="dashboard-stats-${fallback.split('-')[0]}" style="background-image: url(${page('pms_image_proxy', row0['thumb'], row0['grandparent_rating_key'] or row0['rating_key'], 300, height, fallback=fallback)});"></div>
</a> </a>
</div> </div>
% elif stat_id == 'top_libraries': % elif stat_id == 'top_libraries':
% if row0['thumb'].startswith('http'): % if row0['library_thumb'].startswith('http'):
<div id="stats-thumb-${stat_id}" class="dashboard-stats-flat hidden-xs" style="background-image: url(${page('pms_image_proxy', row0['thumb'], None, 80, 80)});"></div> <div id="stats-thumb-${stat_id}" class="dashboard-stats-flat hidden-xs" style="background-image: url(${page('pms_image_proxy', row0['library_thumb'], None, 100, 100, fallback='cover')});"></div>
% else: % else:
<div id="stats-thumb-${stat_id}" class="dashboard-stats-flat svg-icon library-${row0['section_type']} hidden-xs"></div> <div id="stats-thumb-${stat_id}" class="dashboard-stats-flat svg-icon library-${row0['section_type']} hidden-xs"></div>
% endif % endif
% elif stat_id == 'top_users': % elif stat_id == 'top_users':
<% user_href = page('user', 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['user']}" class="hidden-xs"> <a id="stats-thumb-url-${stat_id}" href="${user_href}" title="${row0['user']}" class="hidden-xs">
<div id="stats-thumb-${stat_id}" class="dashboard-stats-circle" style="background-image: url(${page('pms_image_proxy', row0['user_thumb'] or 'interfaces/default/images/gravatar-default.png', None, 80, 80, fallback='user')})"></div> <div id="stats-thumb-${stat_id}" class="dashboard-stats-circle" style="background-image: url(${page('pms_image_proxy', row0['user_thumb'] or 'interfaces/default/images/gravatar-default.png', None, 100, 100, fallback='user')})"></div>
</a> </a>
% elif stat_id == 'top_platforms': % elif stat_id == 'top_platforms':
<div id="stats-thumb-${stat_id}" class="dashboard-stats-flat svg-icon platform-${row0['platform_name']} transparent hidden-xs"></div> <div id="stats-thumb-${stat_id}" class="dashboard-stats-flat svg-icon platform-${row0['platform_name']} transparent hidden-xs"></div>
@ -147,7 +148,8 @@ DOCUMENTATION :: END
data-rating_key="${row.get('rating_key')}" data-grandparent_rating_key="${row.get('grandparent_rating_key')}" data-guid="${row.get('guid')}" data-title="${row.get('title')}" data-rating_key="${row.get('rating_key')}" data-grandparent_rating_key="${row.get('grandparent_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-library-type="${row.get('section_type')}" data-art="${row.get('art')}" data-thumb="${row.get('thumb')}" data-platform="${row.get('platform_name')}" data-library-type="${row.get('section_type')}"
data-user_id="${row.get('user_id')}" data-user="${row.get('user')}" data-friendly_name="${row.get('friendly_name')}" data-user_thumb="${row.get('user_thumb')}" data-user_id="${row.get('user_id')}" data-user="${row.get('user')}" 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-live="${row.get('live')}"> data-last_watch="${row.get('last_watch')}" data-started="${row.get('started')}" data-live="${row.get('live')}"
data-library_art="${row.get('library_art', '')}" data-library_thumb="${row.get('library_thumb', '')}">
<div class="sub-list">${loop.index + 1}</div> <div class="sub-list">${loop.index + 1}</div>
<div class="sub-value"> <div class="sub-value">
% if stat_id in ('top_movies', 'popular_movies', 'top_tv', 'popular_tv', 'top_music', 'popular_music', 'last_watched'): % if stat_id in ('top_movies', 'popular_movies', 'top_tv', 'popular_tv', 'top_music', 'popular_music', 'last_watched'):
@ -157,7 +159,7 @@ DOCUMENTATION :: END
if row['live']: if row['live']:
href = page('info', row['rating_key'], row['guid'], history=True, live=row['live']) href = page('info', row['rating_key'], row['guid'], history=True, live=row['live'])
else: else:
href = page('info', row['rating_key']) href = page('info', row['rating_key'], history=True)
%> %>
<a href="${href}" title="${row['title']}"> <a href="${href}" title="${row['title']}">
${row['title']} ${row['title']}
@ -175,7 +177,9 @@ DOCUMENTATION :: END
% elif stat_id == 'top_platforms': % elif stat_id == 'top_platforms':
${row['platform']} ${row['platform']}
% elif stat_id == 'most_concurrent': % elif stat_id == 'most_concurrent':
<a href="graphs#concurrent-graph" title="${row['title']}">
${row['title']} ${row['title']}
</a>
% endif % endif
</div> </div>
<div class="sub-count"> <div class="sub-count">

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="120px" height="120px" viewBox="0 0 120 120" enable-background="new 0 0 120 120" xml:space="preserve">
<g>
<path fill="#BEBEBE" d="M31.161,105.119c4.747-3.413,10.392-5.561,16.939-6.444c2.645,1.983,6.611,2.976,11.9,2.976
c5.288,0,9.255-0.992,11.9-2.976c6.546,0.884,12.192,3.031,16.938,6.444c-8.325,5.336-18.223,8.431-28.838,8.431
C49.385,113.55,39.487,110.455,31.161,105.119z"/>
<path fill="#D6D6D6" d="M48.1,98.675l2.737-8.553C42.241,84.96,36.2,72.758,36.2,58.526c0-7.478,1.669-14.397,4.498-20.029
c3.436,1.059,7.504,1.669,11.864,1.669c10.86,0,19.907-3.795,21.903-8.822c5.676,6.256,9.334,16.104,9.334,27.182
c0,14.232-6.041,26.434-14.637,31.596l2.737,8.553c-2.646,1.983-6.612,2.976-11.9,2.976c-5.248,0-9.194-0.977-11.838-2.929
L48.1,98.675z"/>
<path fill="#A6A6A6" d="M38.763,73.994C33.496,68.09,30.25,60,30.25,51.075C30.25,33.002,43.57,18.35,60,18.35
c16.431,0,29.75,14.65,29.75,32.724c0,8.925-3.247,17.016-8.514,22.92c1.64-4.647,2.563-9.901,2.563-15.468
c0-11.078-3.658-20.925-9.334-27.182c-1.996,5.027-11.043,8.822-21.903,8.822c-4.36,0-8.428-0.611-11.864-1.669
C37.868,44.128,36.2,51.048,36.2,58.526C36.2,64.093,37.124,69.348,38.763,73.994z"/>
<path fill="#D6D6D6" d="M60,113.55c29.575,0,53.55-23.974,53.55-53.55c0-29.575-23.975-53.55-53.55-53.55
C30.425,6.45,6.45,30.425,6.45,60C6.45,89.576,30.425,113.55,60,113.55z M60,119.5C27.138,119.5,0.5,92.86,0.5,60
S27.138,0.5,60,0.5c32.86,0,59.5,26.64,59.5,59.499C119.5,92.86,92.86,119.5,60,119.5z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Before After
Before After

View file

@ -1,5 +1,11 @@
<!-- Generated by IcoMoon.io --> <?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"> <!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="32px" height="32px" viewBox="0 0 32 32" enable-background="new 0 0 32 32" xml:space="preserve">
<title>artist</title> <title>artist</title>
<path fill="#fff" d="M9.201 24.681c0-6.699 0-13.358 0-20.035 7.594-1.505 15.157-3.004 22.768-4.513 0 0.172 0 0.319 0 0.465 0 7.067-0.026 14.135 0.021 21.202 0.010 1.498-0.59 2.57-1.716 3.423-1.999 1.512-4.26 2.145-6.751 1.88-0.504-0.054-1.020-0.205-1.481-0.418-1.502-0.695-1.856-2.122-0.908-3.48 0.826-1.184 1.99-1.924 3.302-2.433 1.362-0.528 2.774-0.843 4.252-0.719 0.324 0.027 0.646 0.084 0.994 0.13 0-4.345 0-8.679 0-13.050-6.062 1.204-12.099 2.404-18.16 3.608 0 0.196 0 0.345 0 0.495 0 5.174-0.006 10.349 0.004 15.523 0.003 1.409-0.802 2.302-1.854 3.056-0.889 0.637-1.859 1.114-2.906 1.426-1.524 0.453-3.067 0.627-4.619 0.169-0.952-0.281-1.789-0.736-2.010-1.83-0.136-0.673 0.098-1.269 0.459-1.822 0.772-1.183 1.947-1.853 3.193-2.388 1.662-0.714 3.394-1.043 5.207-0.698 0.048 0.009 0.099 0.005 0.206 0.010z"></path> <path fill="#FFFFFF" d="M29.369,0.72H10.271C8.864,0.722,7.726,1.861,7.724,3.267v18.534c-0.771-0.459-1.649-0.703-2.546-0.709
c-2.813,0-5.094,2.281-5.094,5.094s2.281,5.094,5.094,5.094s5.093-2.281,5.093-5.094V10.907h19.099v10.894
c-0.771-0.459-1.649-0.703-2.546-0.709c-2.813,0-5.094,2.281-5.094,5.094s2.281,5.094,5.094,5.094s5.093-2.281,5.093-5.094V3.267
C31.914,1.861,30.775,0.722,29.369,0.72z M10.271,3.267h19.099V8.36H10.271V3.267z"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 979 B

After

Width:  |  Height:  |  Size: 931 B

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Before After
Before After

View file

@ -1,9 +1,36 @@
<!-- Generated by IcoMoon.io --> <?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"> <!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<title>live</title> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<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> <svg version="1.1" id="Layer_2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
<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> width="32px" height="32px" viewBox="0 0 32 32" enable-background="new 0 0 32 32" xml:space="preserve">
<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> <g>
<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> <polygon fill="none" points="12.252,15.364 9.287,23.717 20.6,23.709 "/>
<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> <path fill="none" d="M13.122,12.92l8.846,8.846l-4.158-11.71c-0.558,0.271-1.175,0.437-1.839,0.437
c-0.66,0-1.276-0.164-1.833-0.434L13.122,12.92z"/>
<path fill="#FFFFFF" d="M19.663,8.28c0.33-0.601,0.529-1.277,0.529-2.008c0-2.333-1.892-4.223-4.222-4.223s-4.222,1.89-4.222,4.223
c0,0.733,0.203,1.412,0.532,2.013l-7.56,21.292c-0.248,0.697,0.047,1.434,0.656,1.654c0.608,0.215,1.305-0.177,1.549-0.865
l1.53-4.309l15.034-0.007l1.532,4.315c0.247,0.688,0.941,1.08,1.549,0.865c0.61-0.221,0.904-0.957,0.658-1.654L19.663,8.28z
M17.81,10.057l4.158,11.71l-8.846-8.846l1.016-2.861c0.557,0.27,1.173,0.434,1.833,0.434
C16.635,10.493,17.252,10.328,17.81,10.057z M12.252,15.364l8.347,8.345L9.287,23.717L12.252,15.364z"/>
<g>
<path fill="#FFFFFF" d="M3.638,1.458C3.189,0.996,2.563,0.875,2.279,1.211c-3.043,3.578-3.045,8.859-0.002,12.44
c0.284,0.335,0.913,0.214,1.362-0.25c0.452-0.462,0.628-1.022,0.424-1.264C1.76,9.428,1.762,5.433,4.064,2.726
C4.267,2.484,4.089,1.924,3.638,1.458z"/>
</g>
<g>
<path fill="#FFFFFF" d="M28.303,1.46c-0.45,0.464-0.626,1.021-0.421,1.261c2.303,2.71,2.3,6.707-0.003,9.412
c-0.205,0.24-0.025,0.802,0.424,1.266c0.448,0.466,1.075,0.586,1.361,0.25c3.044-3.578,3.046-8.86,0.003-12.441
C29.38,0.873,28.753,0.996,28.303,1.46z"/>
</g>
<g>
<path fill="#FFFFFF" d="M8.847,11.926c0.453-0.465,0.687-0.966,0.546-1.132C7.78,8.897,7.781,6.1,9.392,4.205
c0.141-0.167-0.094-0.672-0.545-1.138c-0.448-0.463-1.019-0.639-1.24-0.376c-2.351,2.766-2.353,6.848,0,9.616
C7.826,12.569,8.398,12.39,8.847,11.926z"/>
</g>
<g>
<path fill="#FFFFFF" d="M23.182,11.858c0.45,0.466,1.017,0.642,1.24,0.379c2.353-2.766,2.354-6.849,0.001-9.617
C24.199,2.362,23.63,2.54,23.18,3.004c-0.449,0.464-0.685,0.967-0.542,1.132c1.611,1.896,1.61,4.692-0.001,6.587
C22.495,10.888,22.73,11.393,23.182,11.858z"/>
</g>
</g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Before After
Before After

View file

@ -1,5 +1,17 @@
<!-- Generated by IcoMoon.io --> <?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"> <!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<title>movie</title> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<path fill="#fff" d="M0 1.416c10.695 0 21.315 0 32 0 0 9.719 0 19.417 0 29.168-10.635 0-21.293 0-32 0 0-9.699 0-19.399 0-29.168zM9.202 15.129c4.535 0 9.065 0 13.609 0 0-3.798 0-7.579 0-11.355-4.563 0-9.075 0-13.609 0 0 3.801 0 7.551 0 11.355zM9.215 28.909c4.581 0 9.093 0 13.61 0 0-3.818 0-7.585 0-11.382-4.549 0-9.062 0-13.61 0 0 3.803 0 7.57 0 11.382zM6.813 5.983c0-0.753 0-1.467 0-2.209-1.138 0-2.235 0-3.33 0 0 0.766 0 1.48 0 2.209 1.131 0 2.21 0 3.33 0zM25.231 3.754c0 0.783 0 1.494 0 2.219 1.141 0 2.235 0 3.33 0 0-0.763 0-1.476 0-2.219-1.12 0-2.198 0-3.33 0zM25.233 12.938c0 0.777 0 1.492 0 2.19 1.149 0 2.248 0 3.335 0 0-0.754 0-1.452 0-2.19-1.116 0-2.197 0-3.335 0zM25.227 22.074c0 0.783 0 1.496 0 2.218 1.139 0 2.235 0 3.335 0 0-0.758 0-1.471 0-2.218-1.119 0-2.196 0-3.335 0zM3.472 26.689c0 0.768 0 1.481 0 2.221 1.133 0 2.226 0 3.34 0 0-0.763 0-1.475 0-2.221-1.119 0-2.197 0-3.34 0zM28.579 26.711c-1.112 0-2.225 0-3.353 0 0 0.749 0 1.462 0 2.202 1.137 0 2.233 0 3.353 0 0-0.748 0-1.447 0-2.202zM6.817 15.155c0-0.774 0-1.468 0-2.21-1.133 0-2.229 0-3.338 0 0 0.761 0 1.473 0 2.21 1.127 0 2.207 0 3.338 0zM3.463 19.73c1.165 0 2.242 0 3.346 0 0-0.759 0-1.469 0-2.189-1.143 0-2.239 0-3.346 0 0 0.748 0 1.446 0 2.189zM28.559 19.733c0-0.764 0-1.479 0-2.188-1.144 0-2.241 0-3.34 0 0 0.752 0 1.448 0 2.188 1.114 0 2.195 0 3.34 0zM6.791 24.304c0-0.798 0-1.511 0-2.215-1.145 0-2.225 0-3.312 0 0 0.766 0 1.481 0 2.215 1.129 0 2.211 0 3.312 0zM3.489 8.378c0 0.771 0 1.464 0 2.145 1.138 0 2.219 0 3.319 0 0-0.74 0-1.431 0-2.145-1.129 0-2.211 0-3.319 0zM28.554 10.538c0-0.775 0-1.464 0-2.137-1.146 0-2.242 0-3.319 0 0 0.747 0 1.438 0 2.137 1.131 0 2.21 0 3.319 0z"></path> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="32px"
height="32px" viewBox="0 0 32 32" enable-background="new 0 0 32 32" xml:space="preserve">
<g id="Layer_1">
<title>artist</title>
</g>
<g id="Layer_2">
<path fill="#FFFFFF" d="M29.508,0.241H2.492c-1.242,0-2.251,1.008-2.251,2.251v27.015c0,1.242,1.009,2.252,2.251,2.252h27.016
c1.242,0,2.251-1.01,2.251-2.252V2.493C31.759,1.25,30.75,0.241,29.508,0.241z M4.744,29.508H2.492v-3.002h2.252V29.508z
M4.744,24.255H2.492v-3.753h2.252V24.255z M4.744,18.252H2.492V14.5h2.252V18.252z M4.744,12.248H2.492V8.496h2.252V12.248z
M4.744,6.245H2.492V2.493h2.252V6.245z M25.005,29.508H6.995V16.75h18.01V29.508z M25.005,14.5H6.995V2.493h18.01V14.5z
M29.508,29.508h-2.252v-3.002h2.252V29.508z M29.508,24.255h-2.252v-3.753h2.252V24.255z M29.508,18.252h-2.252V14.5h2.252V18.252
z M29.508,12.248h-2.252V8.496h2.252V12.248z M29.508,6.245h-2.252V2.493h2.252V6.245z"/>
</g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Before After
Before After

View file

@ -1,6 +1,25 @@
<!-- Generated by IcoMoon.io --> <?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"> <!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<title>photo</title> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<path fill="#fff" d="M25.208 2.266c0 1.514 0 2.976 0 4.513 2.281 0 4.505 0 6.792 0 0 7.681 0 15.287 0 22.955-10.643 0-21.3 0-32 0 0-7.626 0-15.236 0-22.913 2.258 0 4.486 0 6.792 0 0-1.546 0-3.029 0-4.555 6.152 0 12.226 0 18.415 0zM16.008 9.209c-5.026-0.004-9.12 4.079-9.123 9.099-0.004 4.961 4.099 9.069 9.074 9.085 5.022 0.016 9.124-4.047 9.159-9.070 0.035-4.985-4.087-9.109-9.109-9.114z"></path> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="32px"
<path fill="#fff" d="M20.601 18.292c0.003 2.551-2.070 4.626-4.61 4.613-2.558-0.013-4.595-2.069-4.591-4.634 0.003-2.524 2.038-4.557 4.577-4.572 2.562-0.015 4.621 2.030 4.624 4.593z"></path> height="32px" viewBox="0 0 32 32" enable-background="new 0 0 32 32" xml:space="preserve">
<g id="Layer_2">
</g>
<g id="Layer_3">
<g>
<path fill="#FFFFFF" d="M30.807,8.975c-0.587-0.596-1.388-0.933-2.224-0.934h-5.158l-0.855-2.599
c-0.207-0.633-0.607-1.184-1.145-1.577c-0.536-0.389-1.18-0.6-1.84-0.6h-7.171c-0.661,0-1.306,0.211-1.84,0.602
C10.037,4.259,9.637,4.811,9.43,5.442L8.573,8.041H3.416C2.579,8.042,1.779,8.379,1.192,8.975c-0.591,0.6-0.923,1.408-0.923,2.25
V25.55c0,0.843,0.332,1.65,0.923,2.25c0.587,0.596,1.387,0.932,2.224,0.934h25.167c0.836-0.002,1.637-0.338,2.224-0.934
c0.591-0.6,0.923-1.407,0.923-2.25V11.225C31.729,10.383,31.397,9.574,30.807,8.975z M29.482,25.55
c0,0.258-0.103,0.497-0.273,0.672c-0.165,0.168-0.391,0.265-0.627,0.266H3.418c-0.237-0.001-0.462-0.098-0.628-0.266
c-0.176-0.181-0.274-0.422-0.273-0.674v-14.32c0-0.258,0.103-0.499,0.273-0.673c0.172-0.173,0.396-0.266,0.627-0.267h6.782
l1.365-4.144c0.063-0.189,0.181-0.351,0.334-0.463c0.152-0.11,0.332-0.169,0.514-0.169h7.175c0.182,0,0.361,0.059,0.514,0.169
c0.153,0.112,0.271,0.273,0.335,0.463l1.363,4.144h6.783c0.23,0,0.456,0.094,0.627,0.267c0.171,0.174,0.272,0.413,0.273,0.671
V25.55z"/>
<path fill="#FFFFFF" d="M16,10.7c-3.764,0-6.813,3.049-6.813,6.812c0,3.765,3.05,6.815,6.813,6.815
c3.763,0,6.813-3.051,6.813-6.815C22.813,13.749,19.763,10.7,16,10.7z M15.999,22.082c-2.523,0-4.568-2.045-4.568-4.568
s2.045-4.569,4.568-4.569c2.524,0,4.569,2.046,4.569,4.569S18.523,22.082,15.999,22.082z"/>
</g>
</g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 746 B

After

Width:  |  Height:  |  Size: 1.8 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 875 B

Before After
Before After

View file

@ -1,12 +1,9 @@
<!-- Generated by IcoMoon.io --> <?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"> <!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<title>playlist</title> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<path fill="#fff" d="M9.167 10.268c7.653 0 15.208 0 22.82 0 0 1.147 0 2.268 0 3.451-7.583 0-15.172 0-22.82 0 0-1.134 0-2.274 0-3.451z"></path> <svg version="1.1" id="Layer_4" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
<path fill="#fff" d="M9.14 21.73c0-1.152 0-2.257 0-3.424 7.609 0 15.212 0 22.86 0 0 1.132 0 2.252 0 3.424-7.607 0-15.191 0-22.86 0z"></path> width="32px" height="32px" viewBox="0 0 32 32" enable-background="new 0 0 32 32" xml:space="preserve">
<path fill="#fff" d="M9.217 5.679c0-1.145 0-2.23 0-3.363 7.562 0 15.101 0 22.683 0 0 1.113 0 2.213 0 3.363-7.539 0-15.080 0-22.683 0z"></path> <path fill="#FFFFFF" d="M0.27,2.892h2.621v2.622H0.27V2.892z M31.729,2.892H8.135v2.622h23.595V2.892z M31.729,10.757H8.135v2.622
<path fill="#fff" d="M9.225 29.708c0-1.132 0-2.214 0-3.336 7.558 0 15.086 0 22.668 0 0 1.086 0 2.186 0 3.336-7.526 0-15.071 0-22.668 0z"></path> h23.595V10.757z M8.135,18.622h23.595v2.621H8.135V18.622z M31.729,26.487H8.135v2.621h23.595V26.487z M2.891,10.757H0.27v2.622
<path fill="#fff" d="M0.007 10.279c1.58 0 3.043 0 4.551 0 0 1.153 0 2.272 0 3.444-1.511 0-3.011 0-4.551 0 0-1.157 0-2.292 0-3.444z"></path> h2.621V10.757z M0.27,18.622h2.621v2.621H0.27V18.622z M2.891,26.487H0.27v2.621h2.621V26.487z"/>
<path fill="#fff" d="M4.527 2.292c0 1.183 0 2.284 0 3.424-1.496 0-2.957 0-4.479 0 0-1.142 0-2.276 0-3.424 1.51 0 2.971 0 4.479 0z"></path>
<path fill="#fff" d="M4.571 18.3c0 1.151 0 2.254 0 3.416-1.515 0-3.019 0-4.571 0 0-1.127 0-2.247 0-3.416 1.513 0 3.001 0 4.571 0z"></path>
<path fill="#fff" d="M4.54 26.352c0 1.137 0 2.218 0 3.354-1.494 0-2.975 0-4.506 0 0-1.094 0-2.192 0-3.354 1.489 0 2.965 0 4.506 0z"></path>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 819 B

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Before After
Before After

View file

@ -1,6 +1,12 @@
<!-- Generated by IcoMoon.io --> <?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"> <!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<title>show</title> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<path fill="#fff" d="M0 26.565c0-8.38 0-16.706 0-25.089 10.661 0 21.307 0 32 0 0 8.343 0 16.686 0 25.089-10.637 0-21.283 0-32 0zM3.514 4.937c0 5.355 0 10.634 0 15.901 8.375 0 16.691 0 24.994 0 0-5.331 0-10.612 0-15.901-8.356 0-16.656 0-24.994 0z"></path> <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
<path fill="#fff" d="M6.874 30.524c0-0.553 0-1.056 0-1.602 6.084 0 12.136 0 18.25 0 0 0.509 0 1.029 0 1.602-6.050 0-12.12 0-18.25 0z"></path> width="32px" height="32px" viewBox="0 0 32 32" enable-background="new 0 0 32 32" xml:space="preserve">
<title>artist</title>
<g>
<path fill="#FFFFFF" d="M2.359,1.602h27.281c1.255,0,2.273,1.018,2.273,2.273v19.703c0,1.256-1.019,2.273-2.273,2.273H2.359
c-1.255,0-2.273-1.018-2.273-2.273V3.875C0.086,2.62,1.104,1.602,2.359,1.602z M2.359,23.578h27.281V3.875H2.359V23.578z"/>
<path fill="#FFFFFF" d="M25.094,30.398v-2.273H6.906v2.273H25.094z"/>
</g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 555 B

After

Width:  |  Height:  |  Size: 816 B

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Before After
Before After

View file

@ -1,5 +1,13 @@
<!-- Generated by IcoMoon.io --> <?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"> <!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<title>video</title> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<path fill="#fff" d="M20.662 27.439c-6.971 0-13.797 0-20.662 0 0-7.639 0-15.235 0-22.878 6.846 0 13.672 0 20.588 0 0 2.962 0 5.932 0 9.091 3.87-2.316 7.591-4.542 11.412-6.828 0 6.153 0 12.176 0 18.33-3.769-2.257-7.494-4.488-11.338-6.789 0 3.070 0 6.035 0 9.075z"></path> <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="32px" height="32px" viewBox="0 0 32 32" enable-background="new 0 0 32 32" xml:space="preserve">
<title>artist</title>
<path fill="#FFFFFF" d="M21.619,27.237H2.515c-1.241,0-2.247-1.006-2.247-2.247V7.011c0-1.242,1.006-2.248,2.247-2.248h19.104
c1.24,0,2.247,1.006,2.247,2.248v4.562l6.091-4.349c0.506-0.36,1.207-0.242,1.568,0.263c0.134,0.19,0.206,0.415,0.207,0.647v15.732
c-0.002,0.621-0.509,1.123-1.128,1.119c-0.232-0.001-0.458-0.075-0.647-0.209l-6.091-4.349v4.563
C23.866,26.231,22.859,27.237,21.619,27.237z M2.515,7.011V24.99h19.104v-6.742c0.003-0.621,0.509-1.122,1.129-1.118
c0.231,0,0.457,0.074,0.646,0.208l6.091,4.349V10.314l-6.091,4.349c-0.506,0.36-1.207,0.242-1.568-0.263
c-0.133-0.189-0.206-0.415-0.207-0.647V7.011H2.515z"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 430 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Before After
Before After

View file

@ -42,10 +42,10 @@
<h3 class="pull-left">Watch Statistics</h3> <h3 class="pull-left">Watch Statistics</h3>
<div class="button-bar"> <div class="button-bar">
<div class="btn-group pull-left" data-toggle="buttons" id="watch-stats-toggles" style="margin-right: 3px"> <div class="btn-group pull-left" data-toggle="buttons" id="watch-stats-toggles" style="margin-right: 3px">
<label class="btn btn-dark"> <label class="btn btn-dark btn-filter">
<input type="radio" class="watched-stats-toggle" name="watched-stats-type" id="watched-stats-plays" value="plays" autocomplete="off"> Play Count <input type="radio" class="watched-stats-toggle" name="watched-stats-type" id="watched-stats-plays" value="plays" autocomplete="off"> Play Count
</label> </label>
<label class="btn btn-dark"> <label class="btn btn-dark btn-filter">
<input type="radio" class="watched-stats-toggle" name="watched-stats-type" id="watched-stats-duration" value="duration" autocomplete="off"> Play Duration <input type="radio" class="watched-stats-toggle" name="watched-stats-type" id="watched-stats-duration" value="duration" autocomplete="off"> Play Duration
</label> </label>
</div> </div>
@ -92,27 +92,27 @@
<h3 class="pull-left"><span id="recently-added-xml">Recently Added</span></h3> <h3 class="pull-left"><span id="recently-added-xml">Recently Added</span></h3>
<ul class="nav nav-header nav-dashboard pull-right" style="margin-top: -3px;"> <ul class="nav nav-header nav-dashboard pull-right" style="margin-top: -3px;">
<li> <li>
<a href="#" id="recently-added-page-left" class="paginate btn-gray disabled" data-id="+1"><i class="fa fa-lg fa-chevron-left"></i></a> <a href="#" id="recently-added-page-left" class="paginate-added btn-gray disabled" data-id="-1"><i class="fa fa-lg fa-chevron-left"></i></a>
</li> </li>
<li> <li>
<a href="#" id="recently-added-page-right" class="paginate btn-gray disabled" data-id="-1"><i class="fa fa-lg fa-chevron-right"></i></a> <a href="#" id="recently-added-page-right" class="paginate-added btn-gray disabled" data-id="+1"><i class="fa fa-lg fa-chevron-right"></i></a>
</li> </li>
</ul> </ul>
<div class="button-bar"> <div class="button-bar">
<div class="btn-group pull-left" data-toggle="buttons" id="recently-added-toggles" style="margin-right: 3px"> <div class="btn-group pull-left" data-toggle="buttons" id="recently-added-toggles" style="margin-right: 3px">
<label class="btn btn-dark" id="recently-added-label-all"> <label class="btn btn-dark btn-filter" id="recently-added-label-all">
<input type="radio" name="recently-added-toggle" id="recently-added-toggle-all" value="all" autocomplete="off"> All <input type="radio" name="recently-added-toggle" id="recently-added-toggle-all" value="all" autocomplete="off"> All
</label> </label>
<label class="btn btn-dark" id="recently-added-label-movies"> <label class="btn btn-dark btn-filter" id="recently-added-label-movies">
<input type="radio" name="recently-added-toggle" id="recently-added-toggle-movie" value="movie" autocomplete="off"> Movies <input type="radio" name="recently-added-toggle" id="recently-added-toggle-movie" value="movie" autocomplete="off"> Movies
</label> </label>
<label class="btn btn-dark" id="recently-added-label-tv"> <label class="btn btn-dark btn-filter" id="recently-added-label-tv">
<input type="radio" name="recently-added-toggle" id="recently-added-toggle-show" value="show" autocomplete="off"> TV Shows <input type="radio" name="recently-added-toggle" id="recently-added-toggle-show" value="show" autocomplete="off"> TV Shows
</label> </label>
<label class="btn btn-dark" id="recently-added-label-music"> <label class="btn btn-dark btn-filter" id="recently-added-label-music">
<input type="radio" name="recently-added-toggle" id="recently-added-toggle-artist" value="artist" autocomplete="off"> Music <input type="radio" name="recently-added-toggle" id="recently-added-toggle-artist" value="artist" autocomplete="off"> Music
</label> </label>
<label class="btn btn-dark" id="recently-added-label-other_video"> <label class="btn btn-dark btn-filter" id="recently-added-label-other_video">
<input type="radio" name="recently-added-toggle" id="recently-added-toggle-other_video" value="other_video" autocomplete="off"> Videos <input type="radio" name="recently-added-toggle" id="recently-added-toggle-other_video" value="other_video" autocomplete="off"> Videos
</label> </label>
</div> </div>
@ -212,28 +212,6 @@
</div> </div>
</div> </div>
</div> </div>
<% from plexpy.helpers import anon_url %>
<div id="python2-modal" class="modal fade wide" tabindex="-1" role="dialog" aria-labelledby="python2-modal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
<h4 class="modal-title">Unable to Update</h4>
</div>
<div class="modal-body" style="text-align: center;">
<p>Tautulli is still running using Python 2 and cannot be updated past v2.6.3.</p>
<p>Python 3 is required to continue receiving updates.</p>
<p>
<strong>Please see the <a href="${anon_url('https://github.com/Tautulli/Tautulli/wiki/Upgrading-to-Python-3-%28Tautulli-v2.5%29')}" target="_blank" rel="noreferrer">wiki</a>
for instructions on how to upgrade to Python 3.</strong>
</p>
</div>
<div class="modal-footer">
<input type="button" class="btn btn-bright" data-dismiss="modal" value="Close">
</div>
</div>
</div>
</div>
% endif % endif
<div class="modal fade" id="ip-info-modal" tabindex="-1" role="dialog" aria-labelledby="ip-info-modal"> <div class="modal fade" id="ip-info-modal" tabindex="-1" role="dialog" aria-labelledby="ip-info-modal">
@ -320,6 +298,8 @@
$('#currentActivityHeader-bandwidth-tooltip').tooltip({ container: 'body', placement: 'right', delay: 50 }); $('#currentActivityHeader-bandwidth-tooltip').tooltip({ container: 'body', placement: 'right', delay: 50 });
var title = document.title;
function getCurrentActivity() { function getCurrentActivity() {
activity_ready = false; activity_ready = false;
@ -390,6 +370,8 @@
$('#currentActivityHeader').show(); $('#currentActivityHeader').show();
document.title = stream_count + ' stream' + (stream_count > 1 ? 's' : '') + ' | ' + title;
sessions.forEach(function (session) { sessions.forEach(function (session) {
var s = (typeof Proxy === "function") ? new Proxy(session, defaultHandler) : session; var s = (typeof Proxy === "function") ? new Proxy(session, defaultHandler) : session;
var key = s.session_key; var key = s.session_key;
@ -483,8 +465,8 @@
var video_decision = ''; var video_decision = '';
if (['movie', 'episode', 'clip'].indexOf(s.media_type) > -1 && s.stream_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 v_bd = (s.video_dynamic_range !== 'SDR') ? ' ' + s.video_dynamic_range : '';
var sv_bd = (s.video_dynamic_range === 'HDR') ? ' ' + s.stream_video_dynamic_range : ''; var sv_bd = (s.stream_video_dynamic_range !== 'SDR' || v_bd) ? ' ' + s.stream_video_dynamic_range : '';
var v_res= ''; var v_res= '';
switch (s.video_resolution.toLowerCase()) { switch (s.video_resolution.toLowerCase()) {
case 'sd': case 'sd':
@ -523,14 +505,15 @@
var audio_decision = ''; var audio_decision = '';
if (['movie', 'episode', 'clip', 'track'].indexOf(s.media_type) > -1 && s.stream_audio_decision) { if (['movie', 'episode', 'clip', 'track'].indexOf(s.media_type) > -1 && s.stream_audio_decision) {
var audio_language = (s.media_type !== 'track') ? (s.audio_language || 'Unknown') + ' - ' : '';
var a_codec = (s.audio_codec === 'truehd') ? 'TrueHD' : s.audio_codec.toUpperCase(); var a_codec = (s.audio_codec === 'truehd') ? 'TrueHD' : s.audio_codec.toUpperCase();
var sa_codec = (s.stream_audio_codec === 'truehd') ? 'TrueHD' : s.stream_audio_codec.toUpperCase(); var sa_codec = (s.stream_audio_codec === 'truehd') ? 'TrueHD' : s.stream_audio_codec.toUpperCase();
if (s.stream_audio_decision === 'transcode') { if (s.stream_audio_decision === 'transcode') {
audio_decision = 'Transcode (' + a_codec + ' ' + capitalizeFirstLetter(s.audio_channel_layout.split('(')[0]) + ' <i class="fa fa-long-arrow-right"></i> ' + sa_codec + ' ' + capitalizeFirstLetter(s.stream_audio_channel_layout.split('(')[0]) + ')'; audio_decision = 'Transcode (' + audio_language + a_codec + ' ' + capitalizeFirstLetter(s.audio_channel_layout.split('(')[0]) + ' <i class="fa fa-long-arrow-right"></i> ' + sa_codec + ' ' + capitalizeFirstLetter(s.stream_audio_channel_layout.split('(')[0]) + ')';
} else if (s.stream_audio_decision === 'copy') { } else if (s.stream_audio_decision === 'copy') {
audio_decision = 'Direct Stream (' + sa_codec + ' ' + capitalizeFirstLetter(s.stream_audio_channel_layout.split('(')[0]) + ')'; audio_decision = 'Direct Stream (' + audio_language + sa_codec + ' ' + capitalizeFirstLetter(s.stream_audio_channel_layout.split('(')[0]) + ')';
} else { } else {
audio_decision = 'Direct Play (' + sa_codec + ' ' + capitalizeFirstLetter(s.stream_audio_channel_layout.split('(')[0]) + ')'; audio_decision = 'Direct Play (' + audio_language + sa_codec + ' ' + capitalizeFirstLetter(s.stream_audio_channel_layout.split('(')[0]) + ')';
} }
} }
$('#audio_decision-' + key).html(audio_decision); $('#audio_decision-' + key).html(audio_decision);
@ -539,13 +522,13 @@
if (['movie', 'episode', 'clip'].indexOf(s.media_type) > -1 && s.subtitles === 1) { if (['movie', 'episode', 'clip'].indexOf(s.media_type) > -1 && s.subtitles === 1) {
var subtitle_codec = (s.stream_subtitle_codec && s.stream_subtitle_transient) ? 'None' : s.subtitle_codec.toUpperCase(); var subtitle_codec = (s.stream_subtitle_codec && s.stream_subtitle_transient) ? 'None' : s.subtitle_codec.toUpperCase();
if (s.stream_subtitle_decision === 'transcode') { if (s.stream_subtitle_decision === 'transcode') {
subtitle_decision = 'Transcode (' + subtitle_codec + ' <i class="fa fa-long-arrow-right"></i> ' + s.stream_subtitle_codec.toUpperCase() + ')'; subtitle_decision = 'Transcode ('+ (s.subtitle_language || 'Unknown')+ ' - ' + subtitle_codec + ' <i class="fa fa-long-arrow-right"></i> ' + s.stream_subtitle_codec.toUpperCase() + ')';
} else if (s.stream_subtitle_decision === 'copy') { } else if (s.stream_subtitle_decision === 'copy') {
subtitle_decision = 'Direct Stream (' + subtitle_codec + ')'; subtitle_decision = 'Direct Stream ('+ (s.subtitle_language || 'Unknown')+ ' - ' + subtitle_codec + ')';
} else if (s.stream_subtitle_decision === 'burn') { } else if (s.stream_subtitle_decision === 'burn') {
subtitle_decision = 'Burn (' + subtitle_codec + ')'; subtitle_decision = 'Burn ('+ (s.subtitle_language || 'Unknown')+ ' - ' + subtitle_codec + ')';
} else { } else {
subtitle_decision = 'Direct Play (' + ((s.synced_version === '1') ? subtitle_codec : s.stream_subtitle_codec.toUpperCase()) + ')'; subtitle_decision = 'Direct Play ('+ (s.subtitle_language || 'Unknown')+ ' - ' + ((s.synced_version === '1') ? subtitle_codec : s.stream_subtitle_codec.toUpperCase()) + ')';
} }
} }
$('#subtitle_decision-' + key).html(subtitle_decision); $('#subtitle_decision-' + key).html(subtitle_decision);
@ -583,6 +566,7 @@
// Update the stream progress times // Update the stream progress times
$('#stream-eta-' + key).html(moment().add(parseInt(s.duration) - parseInt(s.view_offset), 'milliseconds').format(time_format)); $('#stream-eta-' + key).html(moment().add(parseInt(s.duration) - parseInt(s.view_offset), 'milliseconds').format(time_format));
$('#stream-duration-' + key).html(millisecondsToMinutes(parseInt(s.stream_duration), false));
var stream_view_offset = $('#stream-view-offset-' + key); var stream_view_offset = $('#stream-view-offset-' + key);
stream_view_offset.data('state', s.state); stream_view_offset.data('state', s.state);
if (stream_view_offset.data('last_view_offset') !== s.view_offset) { if (stream_view_offset.data('last_view_offset') !== s.view_offset) {
@ -590,8 +574,10 @@
} }
// Update the progress bars // Update the progress bars
$('#buffer-bar-' + key).css({width: parseInt(s.transcode_progress) + '%'}).html(s.transcode_progress + '%') var duration = parseInt(s.duration);
.attr('data-original-title', 'Transcoder Progress ' + s.transcode_progress + '%'); var transcode_progress = duration ? Math.round(s.transcode_max_offset_available * 1000 / duration * 100) : s.transcode_progress;
$('#buffer-bar-' + key).css({width: parseInt(transcode_progress) + '%'}).html(transcode_progress + '%')
.attr('data-original-title', 'Transcoder Progress ' + transcode_progress + '%');
if (s.live !== 1) { if (s.live !== 1) {
var progress_bar = $('#progress-bar-' + key); var progress_bar = $('#progress-bar-' + key);
progress_bar.data('state', s.state); progress_bar.data('state', s.state);
@ -618,6 +604,8 @@
} else { } else {
$('#currentActivityHeader').hide(); $('#currentActivityHeader').hide();
$('#currentActivity').html('<div id="dashboard-no-activity" class="text-muted">Nothing is currently being played.</div>'); $('#currentActivity').html('<div id="dashboard-no-activity" class="text-muted">Nothing is currently being played.</div>');
document.title = title;
} }
activity_ready = true; activity_ready = true;
@ -794,6 +782,8 @@
var grandparent_rating_key = $(elem).data('grandparent_rating_key'); var grandparent_rating_key = $(elem).data('grandparent_rating_key');
var guid = $(elem).data('guid'); var guid = $(elem).data('guid');
var live = $(elem).data('live'); var live = $(elem).data('live');
var library_art = $(elem).data('library_art');
var library_thumb = $(elem).data('library_thumb');
var [height, fallback_poster, fallback_art] = [450, 'poster', 'art']; var [height, fallback_poster, fallback_art] = [450, 'poster', 'art'];
if ($.inArray(stat_id, ['top_music', 'popular_music']) > -1) { if ($.inArray(stat_id, ['top_music', 'popular_music']) > -1) {
[height, fallback_poster, fallback_art] = [300, 'cover', 'art']; [height, fallback_poster, fallback_art] = [300, 'cover', 'art'];
@ -805,18 +795,18 @@
if (stat_id === 'most_concurrent') { if (stat_id === 'most_concurrent') {
return return
} else if (stat_id === 'top_libraries') { } else if (stat_id === 'top_libraries') {
$('#stats-background-' + stat_id).css('background-image', 'url(' + page('pms_image_proxy', art, null, 500, 280, 40, '282828', 3, fallback_art) + ')'); $('#stats-background-' + stat_id).css('background-image', 'url(' + page('pms_image_proxy', art || library_art, null, 500, 280, 40, '282828', 3, fallback_art) + ')');
$('#stats-thumb-' + stat_id).removeClass(function (index, className) { $('#stats-thumb-' + stat_id).removeClass(function (index, className) {
return (className.match (/(^|\s)svg-icon library-\S+/g) || []).join(' ')}); return (className.match (/(^|\s)svg-icon library-\S+/g) || []).join(' ')});
if (thumb.startsWith('http')) { if (library_thumb.startsWith('http')) {
$('#stats-thumb-' + stat_id).css('background-image', 'url(' + page('pms_image_proxy', thumb, null, 300, 300, null, null, null, 'cover') + ')'); $('#stats-thumb-' + stat_id).css('background-image', 'url(' + page('pms_image_proxy', library_thumb, null, 100, 100, null, null, null, 'cover') + ')');
} else { } else {
$('#stats-thumb-' + stat_id).css('background-image', '') $('#stats-thumb-' + stat_id).css('background-image', '')
.addClass('svg-icon library-' + library_type); .addClass('svg-icon library-' + library_type);
} }
} else if (stat_id === 'top_users') { } else if (stat_id === 'top_users') {
loadBlurHash($('#stats-background-' + stat_id), page('pms_image_proxy', user_thumb || 'interfaces/default/images/gravatar-default.png', null, 80, 80, 40, '282828', 0, 'user')); loadBlurHash($('#stats-background-' + stat_id), page('pms_image_proxy', user_thumb || 'interfaces/default/images/gravatar-default.png', null, 100, 100, 40, '282828', 0, 'user'));
$('#stats-thumb-' + stat_id).css('background-image', 'url(' + page('pms_image_proxy', user_thumb || 'interfaces/default/images/gravatar-default.png', null, 80, 80, null, null, null, 'user') + ')'); $('#stats-thumb-' + stat_id).css('background-image', 'url(' + page('pms_image_proxy', user_thumb || 'interfaces/default/images/gravatar-default.png', null, 100, 100, null, null, null, 'user') + ')');
if (user_id) { if (user_id) {
href = page('user', user_id); href = page('user', user_id);
} }
@ -841,7 +831,7 @@
$('#stats-background-' + stat_id).css('background-image', 'url(' + page('pms_image_proxy', art, img_rating_key, 500, 280, 40, '282828', 3, fallback_art) + ')'); $('#stats-background-' + stat_id).css('background-image', 'url(' + page('pms_image_proxy', art, img_rating_key, 500, 280, 40, '282828', 3, fallback_art) + ')');
$('#stats-thumb-' + stat_id).css('background-image', 'url(' + page('pms_image_proxy', thumb, img_rating_key, 300, height, null, null, null, fallback_poster) + ')'); $('#stats-thumb-' + stat_id).css('background-image', 'url(' + page('pms_image_proxy', thumb, img_rating_key, 300, height, null, null, null, fallback_poster) + ')');
$('#stats-thumb-' + stat_id + '-bg').css('background-image', 'url(' + page('pms_image_proxy', thumb, img_rating_key, 300, height, 60, '282828', 3, fallback_poster) + ')'); $('#stats-thumb-' + stat_id + '-bg').css('background-image', 'url(' + page('pms_image_proxy', thumb, img_rating_key, 300, height, 60, '282828', 3, fallback_poster) + ')');
$('#library-stats-background-' + stat_id).css('background-image', 'url(' + page('pms_image_proxy', art, img_rating_key, 500, 280, 40, '282828', 3, fallback_art) + ')'); $('#library-stats-background-' + stat_id).css('background-image', 'url(' + page('pms_image_proxy', art || library_art, img_rating_key, 500, 280, 40, '282828', 3, library_art || fallback_art) + ')');
if (thumb.startsWith('http')) { if (thumb.startsWith('http')) {
$('#library-stats-thumb-' + stat_id).css('background-image', 'url(' + page('pms_image_proxy', thumb, img_rating_key, 300, 300, null, null, null, 'cover') + ')') $('#library-stats-thumb-' + stat_id).css('background-image', 'url(' + page('pms_image_proxy', thumb, img_rating_key, 300, 300, null, null, null, 'cover') + ')')
.removeClass('svg-icon library-' + stat_id); .removeClass('svg-icon library-' + stat_id);
@ -952,10 +942,14 @@
count: recently_added_count, count: recently_added_count,
media_type: recently_added_type media_type: recently_added_type
}, },
beforeSend: function () {
$(".dashboard-recent-media-row").animate({ scrollLeft: 0 }, 1000);
},
complete: function (xhr, status) { complete: function (xhr, status) {
$("#recentlyAdded").html(xhr.responseText); $("#recentlyAdded").html(xhr.responseText);
$('#ajaxMsg').fadeOut(); $('#ajaxMsg').fadeOut();
highlightAddedScrollerButton(); highlightScrollerButton("#recently-added");
paginateScroller("#recently-added", ".paginate-added");
} }
}); });
} }
@ -971,57 +965,11 @@
recentlyAdded(recently_added_count, recently_added_type); recentlyAdded(recently_added_count, recently_added_type);
} }
function highlightAddedScrollerButton() {
var scroller = $("#recently-added-row-scroller");
var numElems = scroller.find("li:visible").length;
scroller.width(numElems * 175);
if (scroller.width() > $("body").find(".container-fluid").width()) {
$("#recently-added-page-right").removeClass("disabled");
} else {
$("#recently-added-page-right").addClass("disabled");
}
}
$(window).resize(function () {
highlightAddedScrollerButton();
});
function resetScroller() {
leftTotal = 0;
$("#recently-added-row-scroller").animate({ left: leftTotal }, 1000);
$("#recently-added-page-left").addClass("disabled").blur();
}
var leftTotal = 0;
$(".paginate").click(function (e) {
e.preventDefault();
var scroller = $("#recently-added-row-scroller");
var containerWidth = $("body").find(".container-fluid").width();
var scrollAmount = $(this).data("id") * parseInt((containerWidth - 15) / 175) * 175;
var leftMax = Math.min(-parseInt(scroller.width()) + Math.abs(scrollAmount), 0);
leftTotal = Math.max(Math.min(leftTotal + scrollAmount, 0), leftMax);
scroller.animate({ left: leftTotal }, 250);
if (leftTotal === 0) {
$("#recently-added-page-left").addClass("disabled").blur();
} else {
$("#recently-added-page-left").removeClass("disabled");
}
if (leftTotal === leftMax) {
$("#recently-added-page-right").addClass("disabled").blur();
} else {
$("#recently-added-page-right").removeClass("disabled");
}
});
$('#recently-added-toggles').on('change', function () { $('#recently-added-toggles').on('change', function () {
$('#recently-added-toggles > label').removeClass('active'); $('#recently-added-toggles > label').removeClass('active');
selected_filter = $('input[name=recently-added-toggle]:checked', '#recently-added-toggles'); selected_filter = $('input[name=recently-added-toggle]:checked', '#recently-added-toggles');
$(selected_filter).closest('label').addClass('active'); $(selected_filter).closest('label').addClass('active');
recently_added_type = $(selected_filter).val(); recently_added_type = $(selected_filter).val();
resetScroller();
setLocalStorage('home_stats_recently_added_type', recently_added_type); setLocalStorage('home_stats_recently_added_type', recently_added_type);
recentlyAdded(recently_added_count, recently_added_type); recentlyAdded(recently_added_count, recently_added_type);
}); });
@ -1029,7 +977,6 @@
$('#recently-added-count').change(function () { $('#recently-added-count').change(function () {
forceMinMax($(this)); forceMinMax($(this));
recently_added_count = $(this).val(); recently_added_count = $(this).val();
resetScroller();
setLocalStorage('home_stats_recently_added_count', recently_added_count); setLocalStorage('home_stats_recently_added_count', recently_added_count);
recentlyAdded(recently_added_count, recently_added_type); recentlyAdded(recently_added_count, recently_added_type);
}); });
@ -1061,16 +1008,4 @@
}); });
</script> </script>
% endif % endif
% if _session['user_group'] == 'admin':
<script>
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
if (urlParams.get('update') === 'python2') {
$("#python2-modal").modal({
backdrop: 'static',
keyboard: false
});
}
</script>
% endif
</%def> </%def>

View file

@ -12,8 +12,10 @@ data :: Usable parameters (if not applicable for media type, blank value will be
== Global keys == == Global keys ==
rating_key Returns the unique identifier for the media item. rating_key Returns the unique identifier for the media item.
media_type Returns the type of media. Either 'movie', 'show', 'season', 'episode', 'artist', 'album', or 'track'. media_type Returns the type of media. Either 'movie', 'show', 'season', 'episode', 'artist', 'album', or 'track'.
sub_media_type Returns the subtype of media. Either 'movie', 'show', 'season', 'episode', 'artist', 'album', or 'track'.
art Returns the location of the item's artwork art Returns the location of the item's artwork
title Returns the name of the movie, show, episode, artist, album, or track. title Returns the name of the movie, show, episode, artist, album, or track.
edition_title Returns the edition title of a movie.
duration Returns the standard runtime of the media. duration Returns the standard runtime of the media.
content_rating Returns the age rating for the media. content_rating Returns the age rating for the media.
summary Returns a brief description of the media plot. summary Returns a brief description of the media plot.
@ -212,8 +214,8 @@ DOCUMENTATION :: END
% if _session['user_group'] == 'admin': % if _session['user_group'] == 'admin':
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span> <span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
% endif % endif
% elif data['media_type'] in ('artist', 'album', 'track', 'playlist', 'photo_album', 'photo', 'clip'): % elif data['media_type'] in ('artist', 'album', 'track', 'playlist', 'photo_album', 'photo', 'clip') or data['sub_media_type'] in ('artist', 'album', 'track'):
<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-track" style="background-image: url(${page('pms_image_proxy', data['thumb'], data['rating_key'], 300, 300, fallback='cover')});">
<div class="summary-poster-face-overlay"> <div class="summary-poster-face-overlay">
<span></span> <span></span>
</div> </div>
@ -266,7 +268,7 @@ DOCUMENTATION :: END
<h1><a href="${page('info', 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> <h2>${data['title']}</h2>
% elif data['media_type'] == 'track': % elif data['media_type'] == 'track':
<h1><a href="${page('info', data['grandparent_rating_key'])}">${data['original_title'] or data['grandparent_title']}</a></h1> <h1><a href="${page('info', data['grandparent_rating_key'])}">${data['grandparent_title']}</a></h1>
<h2><a href="${page('info', data['parent_rating_key'])}">${data['parent_title']}</a> - ${data['title']}</h2> <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> <h3 class="hidden-xs">T${data['media_index']}</h3>
% elif data['media_type'] in ('photo', 'clip'): % elif data['media_type'] in ('photo', 'clip'):
@ -282,14 +284,14 @@ DOCUMENTATION :: END
padding_height = '' padding_height = ''
if data['media_type'] == 'movie' or data['live']: if data['media_type'] == 'movie' or data['live']:
padding_height = 'height: 305px;' padding_height = 'height: 305px;'
elif data['media_type'] in ('show', 'season', 'collection'): elif data['media_type'] in ('artist', 'album', 'playlist', 'photo_album', 'photo') or data['sub_media_type'] in ('artist', 'album', 'track'):
padding_height = 'height: 270px;'
elif data['media_type'] == 'episode':
padding_height = 'height: 70px;'
elif data['media_type'] in ('artist', 'album', 'playlist', 'photo_album', 'photo'):
padding_height = 'height: 150px;' padding_height = 'height: 150px;'
elif data['media_type'] in ('track', 'clip'): elif data['media_type'] in ('track', 'clip'):
padding_height = 'height: 180px;' padding_height = 'height: 180px;'
elif data['media_type'] == 'episode':
padding_height = 'height: 70px;'
elif data['media_type'] in ('show', 'season', 'collection'):
padding_height = 'height: 270px;'
%> %>
<div class="summary-content-padding hidden-xs hidden-sm" style="${padding_height}"> <div class="summary-content-padding hidden-xs hidden-sm" style="${padding_height}">
% if data['media_type'] in ('movie', 'episode', 'track', 'clip'): % if data['media_type'] in ('movie', 'episode', 'track', 'clip'):
@ -368,6 +370,11 @@ DOCUMENTATION :: END
Studio <strong> ${data['studio']}</strong> Studio <strong> ${data['studio']}</strong>
% endif % endif
</div> </div>
<div class="summary-content-details-tag">
% if data['media_type'] == 'track' and data['original_title']:
Track Artists <strong> ${data['original_title']}</strong>
% endif
</div>
<div class="summary-content-details-tag"> <div class="summary-content-details-tag">
% if data['media_type'] == 'movie': % if data['media_type'] == 'movie':
Year <strong> ${data['year']}</strong> Year <strong> ${data['year']}</strong>
@ -390,14 +397,19 @@ DOCUMENTATION :: END
Runtime <strong> <span id="runtime">${data['duration']}</span></strong> Runtime <strong> <span id="runtime">${data['duration']}</span></strong>
% endif % endif
</div> </div>
% if data['edition_title']:
<div class="summary-content-details-tag">
Edition <strong> ${data['edition_title']} </strong>
</div>
% endif
<div class="summary-content-details-tag"> <div class="summary-content-details-tag">
% if data['content_rating']: % if data['content_rating']:
Rated <strong> ${data['content_rating']} </strong> Rated <strong> ${data['content_rating']} </strong>
% endif % endif
</div> </div>
<div class="summary-content-details-tag" id="channel-icon"> <div class="summary-content-details-tag" id="channel-icon">
% if media_info['channel_identifier']: % if media_info['channel_vcn']:
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> Channel <strong> <span class="thumb-tooltip" data-toggle="popover" data-img="${media_info['channel_thumb']}" data-height="40" data-width="40">${media_info['channel_title'] or (media_info['channel_vcn'] + ' ' + media_info['channel_call_sign'])}</span> </strong>
% endif % endif
</div> </div>
</div> </div>
@ -542,7 +554,7 @@ DOCUMENTATION :: END
</div> </div>
</div> </div>
% endif % endif
% if data['media_type'] in ('movie', 'show', 'season', 'episode', 'artist', 'album', 'track'): % if data['media_type'] in ('movie', 'show', 'season', 'episode', 'artist', 'album', 'track', 'collection', 'playlist'):
<div class="col-md-12"> <div class="col-md-12">
<div class="table-card-header"> <div class="table-card-header">
<div class="header-bar"> <div class="header-bar">
@ -571,7 +583,7 @@ DOCUMENTATION :: END
</div> </div>
% endif % endif
<% <%
history_type = data['media_type'] in ('movie', 'show', 'season', 'episode', 'artist', 'album', 'track') history_type = data['media_type'] in ('movie', 'show', 'season', 'episode', 'artist', 'album', 'track', 'collection', 'playlist')
history_active = 'active' if history_type else '' history_active = 'active' if history_type else ''
export_active = 'active' if not history_type else '' export_active = 'active' if not history_type else ''
%> %>
@ -634,7 +646,7 @@ DOCUMENTATION :: END
<div class="col-md-12"> <div class="col-md-12">
<div class="table-card-header"> <div class="table-card-header">
<div class="header-bar"> <div class="header-bar">
% if data['media_type'] in ('artist', 'album', 'track'): % if data['media_type'] in ('artist', 'album', 'track', 'playlist'):
<span>Play History for <strong>${data['title']}</strong></span> <span>Play History for <strong>${data['title']}</strong></span>
% else: % else:
<span>Watch History for <strong>${data['title']}</strong></span> <span>Watch History for <strong>${data['title']}</strong></span>
@ -649,13 +661,13 @@ DOCUMENTATION :: END
</div> </div>
% endif % endif
<div class="btn-group" data-toggle="buttons" id="transcode_decision-selection"> <div class="btn-group" data-toggle="buttons" id="transcode_decision-selection">
<label class="btn btn-dark"> <label class="btn btn-dark btn-filter">
<input type="checkbox" name="transcode_decision-filter" id="history-transcode_decision-direct_play" value="direct play" autocomplete="off"><i class="fa fa-play-circle"></i> Direct Play <input type="checkbox" name="transcode_decision-filter" id="history-transcode_decision-direct_play" value="direct play" autocomplete="off"><i class="fa fa-play-circle"></i> Direct Play
</label> </label>
<label class="btn btn-dark"> <label class="btn btn-dark btn-filter">
<input type="checkbox" name="transcode_decision-filter" id="history-transcode_decision-direct_stream" value="copy" autocomplete="off"><i class="fa fa-stream"></i> Direct Stream <input type="checkbox" name="transcode_decision-filter" id="history-transcode_decision-direct_stream" value="copy" autocomplete="off"><i class="fa fa-stream"></i> Direct Stream
</label> </label>
<label class="btn btn-dark"> <label class="btn btn-dark btn-filter">
<input type="checkbox" name="transcode_decision-filter" id="history-transcode_decision-transcode" value="transcode" autocomplete="off"><i class="fa fa-server"></i> Transcode <input type="checkbox" name="transcode_decision-filter" id="history-transcode_decision-transcode" value="transcode" autocomplete="off"><i class="fa fa-server"></i> Transcode
</label> </label>
</div> </div>
@ -680,7 +692,7 @@ DOCUMENTATION :: END
<th align="left" id="started">Started</th> <th align="left" id="started">Started</th>
<th align="left" id="paused_counter">Paused</th> <th align="left" id="paused_counter">Paused</th>
<th align="left" id="stopped">Stopped</th> <th align="left" id="stopped">Stopped</th>
<th align="left" id="duration">Duration</th> <th align="left" id="play_duration">Duration</th>
<th align="left" id="percent_complete"></th> <th align="left" id="percent_complete"></th>
</tr> </tr>
</thead> </thead>
@ -806,7 +818,7 @@ DOCUMENTATION :: END
% elif data['media_type'] == 'album': % elif data['media_type'] == 'album':
${data['parent_title']}<br />${data['title']} ${data['parent_title']}<br />${data['title']}
% elif data['media_type'] == 'track': % elif data['media_type'] == 'track':
${data['original_title'] or data['grandparent_title']}<br />${data['title']}<br />${data['parent_title']} ${data['grandparent_title']}<br />${data['title']}<br />${data['parent_title']}
% endif % endif
</strong> </strong>
</p> </p>
@ -853,7 +865,7 @@ DOCUMENTATION :: END
%> %>
<script src="${http_root}js/tables/history_table.js${cache_param}"></script> <script src="${http_root}js/tables/history_table.js${cache_param}"></script>
<script src="${http_root}js/tables/export_table.js${cache_param}"></script> <script src="${http_root}js/tables/export_table.js${cache_param}"></script>
% if data['media_type'] in ('movie', 'show', 'season', 'episode', 'artist', 'album', 'track'): % if data['media_type'] in ('movie', 'show', 'season', 'episode', 'artist', 'album', 'track', 'collection', 'playlist'):
<script> <script>
function loadHistoryTable(transcode_decision) { function loadHistoryTable(transcode_decision) {
// Build watch history table // Build watch history table
@ -866,13 +878,16 @@ DOCUMENTATION :: END
transcode_decision: transcode_decision, transcode_decision: transcode_decision,
user_id: "${history_user_id}", user_id: "${history_user_id}",
% if data['live']: % if data['live']:
guid: "${data['guid']} guid: "${data['guid']}"
% elif data['media_type'] in ('show', 'artist'): % elif data['media_type'] in ('show', 'artist'):
grandparent_rating_key: "${data['rating_key']}" grandparent_rating_key: "${data['rating_key']}"
% elif data['media_type'] in ('season', 'album'): % elif data['media_type'] in ('season', 'album'):
parent_rating_key: "${data['rating_key']}" parent_rating_key: "${data['rating_key']}"
% elif data['media_type'] in ('movie', 'episode', 'track'): % elif data['media_type'] in ('movie', 'episode', 'track'):
rating_key: "${data['rating_key']}" rating_key: "${data['rating_key']}"
% elif data['media_type'] in ('collection', 'playlist'):
media_type: "${data['media_type']}",
rating_key: "${data['rating_key']}"
% endif % endif
}; };
} }
@ -925,13 +940,20 @@ DOCUMENTATION :: END
}); });
</script> </script>
% endif % endif
% if data['media_type'] in ('movie', 'show', 'season', 'episode', 'artist', 'album', 'track'): % if data['media_type'] in ('movie', 'show', 'season', 'episode', 'artist', 'album', 'track', 'collection', 'playlist'):
<script> <script>
// Populate watch time stats // Populate watch time stats
$.ajax({ $.ajax({
url: 'item_watch_time_stats', url: 'item_watch_time_stats',
async: true, async: true,
data: { rating_key: "${data['rating_key']}" }, data: {
% if data['live']:
guid: "${data['guid']}"
% else:
rating_key: "${data['rating_key']}",
media_type: "${data['media_type']}"
% endif
},
complete: function(xhr, status) { complete: function(xhr, status) {
$("#watch-time-stats").html(xhr.responseText); $("#watch-time-stats").html(xhr.responseText);
} }
@ -940,7 +962,14 @@ DOCUMENTATION :: END
$.ajax({ $.ajax({
url: 'item_user_stats', url: 'item_user_stats',
async: true, async: true,
data: { rating_key: "${data['rating_key']}" }, data: {
% if data['live']:
guid: "${data['guid']}"
% else:
rating_key: "${data['rating_key']}",
media_type: "${data['media_type']}"
% endif
},
complete: function(xhr, status) { complete: function(xhr, status) {
$("#user-stats").html(xhr.responseText); $("#user-stats").html(xhr.responseText);
} }

View file

@ -160,6 +160,16 @@ DOCUMENTATION :: END
% endif % endif
</div> </div>
</a> </a>
<div class="item-children-instance-text-wrapper poster-item">
<h3>
<a href="${page('info', child['rating_key'])}" title="${child['title']}">${child['title']}</a>
</h3>
% if media_type == 'collection':
<h3 class="text-muted">
<a class="text-muted" href="${page('info', child['parent_rating_key'])}" title="${child['parent_title']}">${child['parent_title']}</a>
</h3>
% endif
</div>
% elif child['media_type'] == 'episode': % elif child['media_type'] == 'episode':
<a href="${page('info', 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">
@ -179,6 +189,29 @@ DOCUMENTATION :: END
<h3> <h3>
<a href="${page('info', child['rating_key'])}" title="${child['title']}">${child['title']}</a> <a href="${page('info', child['rating_key'])}" title="${child['title']}">${child['title']}</a>
</h3> </h3>
% if media_type == 'collection':
<h3 class="text-muted">
<a href="${page('info', child['grandparent_rating_key'])}" title="${child['grandparent_title']}">${child['grandparent_title']}</a>
</h3>
<h3 class="text-muted">
<a href="${page('info', child['parent_rating_key'])}" title="${child['parent_title']}">${short_season(child['parent_title'])}</a>
&middot; <a href="${page('info', child['rating_key'])}" title="Episode ${child['media_index']}">E${child['media_index']}</a>
</h3>
% endif
</div>
% elif child['media_type'] == 'artist':
<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(${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
</div>
</a>
<div class="item-children-instance-text-wrapper cover-item">
<h3>
<a href="${page('info', child['rating_key'])}" title="${child['title']}">${child['title']}</a>
</h3>
</div> </div>
% elif child['media_type'] == 'album': % elif child['media_type'] == 'album':
<a href="${page('info', child['rating_key'])}" title="${child['title']}"> <a href="${page('info', child['rating_key'])}" title="${child['title']}">
@ -193,6 +226,11 @@ DOCUMENTATION :: END
<h3> <h3>
<a href="${page('info', child['rating_key'])}" title="${child['title']}">${child['title']}</a> <a href="${page('info', child['rating_key'])}" title="${child['title']}">${child['title']}</a>
</h3> </h3>
% if media_type == 'collection':
<h3 class="text-muted">
<a class="text-muted" href="${page('info', child['parent_rating_key'])}" title="${child['parent_title']}">${child['parent_title']}</a>
</h3>
% endif
</div> </div>
% elif child['media_type'] == 'track': % elif child['media_type'] == 'track':
<% e = 'even' if loop.index % 2 == 0 else 'odd' %> <% e = 'even' if loop.index % 2 == 0 else 'odd' %>
@ -205,7 +243,15 @@ DOCUMENTATION :: END
${child['title']} ${child['title']}
</span> </span>
</a> </a>
% if child['original_title']: % if media_type == 'collection':
-
<a href="${page('info', child['grandparent_rating_key'])}" title="${child['grandparent_title']}">
<span class="thumb-tooltip" data-toggle="popover" data-img="${page('pms_image_proxy', child['grandparent_thumb'], child['grandparent_rating_key'], 300, 300, fallback='cover')}" data-height="80" data-width="80">
${child['grandparent_title']}
</span>
</a>
<span class="text-muted"> (<a class="no-highlight" href="${page('info', child['parent_rating_key'])}" title="${child['parent_title']}">${child['parent_title']}</a>)</span>
% elif child['original_title']:
<span class="text-muted"> - ${child['original_title']}</span> <span class="text-muted"> - ${child['original_title']}</span>
% endif % endif
</span> </span>

View file

@ -40,6 +40,7 @@
<div id="ip_error" class="col-sm-12 text-muted"></div> <div id="ip_error" class="col-sm-12 text-muted"></div>
<div class="col-sm-6"> <div class="col-sm-6">
<ul class="list-unstyled"> <ul class="list-unstyled">
<li>Continent: <strong><span id="continent"></span></strong></li>
<li>Country: <strong><span id="country"></span></strong></li> <li>Country: <strong><span id="country"></span></strong></li>
<li>Region: <strong><span id="region"></span></strong></li> <li>Region: <strong><span id="region"></span></strong></li>
<li>City: <strong><span id="city"></span></strong></li> <li>City: <strong><span id="city"></span></strong></li>
@ -103,6 +104,7 @@
$('#ip_error').html('<i class="fa fa-exclamation-circle"></i> ' + result.message).show(); $('#ip_error').html('<i class="fa fa-exclamation-circle"></i> ' + result.message).show();
} else { } else {
var data = result.data; var data = result.data;
$('#continent').html(data.continent);
$('#country').html(data.country); $('#country').html(data.country);
$('#region').html(data.region); $('#region').html(data.region);
$('#city').html(data.city); $('#city').html(data.city);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,76 @@
var formatter_function = function() {
if (moment(this.x, 'X').isValid() && (this.x > 946684800)) {
var s = '<b>'+ moment(this.x).format('ddd MMM D') +'</b>';
} else {
var s = '<b>'+ this.x +'</b>';
}
$.each(this.points, function(i, point) {
s += '<br/>'+point.series.name+': '+point.y;
});
return s;
};
var hc_concurrent_streams_by_stream_type_options = {
chart: {
type: 'line',
backgroundColor: 'rgba(0,0,0,0)',
renderTo: 'graph_concurrent_streams_by_stream_type'
},
title: {
text: ''
},
legend: {
enabled: true,
itemStyle: {
font: '9pt "Open Sans", sans-serif',
color: '#A0A0A0'
},
itemHoverStyle: {
color: '#FFF'
},
itemHiddenStyle: {
color: '#444'
}
},
credits: {
enabled: false
},
plotOptions: {
series: {
events: {
legendItemClick: function() {
setGraphVisibility(this.chart.renderTo.id, this.chart.series, this.name);
}
}
}
},
xAxis: {
type: 'datetime',
labels: {
formatter: function() {
return moment(this.value).format("MMM D");
},
style: {
color: '#aaa'
}
},
categories: [{}],
plotBands: []
},
yAxis: {
title: {
text: null
},
labels: {
style: {
color: '#aaa'
}
}
},
tooltip: {
shared: true,
crosshairs: true,
formatter: formatter_function
},
series: [{}]
};

File diff suppressed because one or more lines are too long

View file

@ -288,23 +288,10 @@ function isPrivateIP(ip_address) {
} }
function humanTime(seconds) { function humanTime(seconds) {
var d = Math.floor(moment.duration(seconds, 'seconds').asDays()); if (seconds > 0) {
var h = Math.floor(moment.duration((seconds % 86400), 'seconds').asHours()); return humanDuration(seconds * 1000).replaceAll(/(\d+) (\w+)/g, '<h3>$1</h3><p>$2</p>')
var m = Math.round(moment.duration(((seconds % 86400) % 3600), 'seconds').asMinutes());
var text = '';
if (d > 0) {
text = '<h3>' + d + '</h3><p> day' + ((d > 1) ? 's' : '') + '</p>'
+ '<h3>' + h + '</h3><p> hr' + ((h > 1) ? 's' : '') + '</p>'
+ '<h3>' + m + '</h3><p> min' + ((m > 1) ? 's' : '') + '</p>';
} else if (h > 0) {
text = '<h3>' + h + '</h3><p> hr' + ((h > 1) ? 's' : '') + '</p>'
+ '<h3>' + m + '</h3><p> min' + ((m > 1) ? 's' : '') + '</p>';
} else {
text = '<h3>' + m + '</h3><p> min' + ((m > 1) ? 's' : '') + '</p>';
} }
return "<h3>0</h3><p>mins</p>";
return text
} }
String.prototype.toProperCase = function () { String.prototype.toProperCase = function () {
@ -360,7 +347,8 @@ function humanDuration(ms, sig='dhm', units='ms', return_seconds=300000) {
sig = 'dhms' sig = 'dhms'
} }
ms = ms * factors[units]; r = factors[sig.slice(-1)];
ms = Math.round(ms * factors[units] / r) * r;
h = ms % factors['d']; h = ms % factors['d'];
d = Math.trunc(ms / factors['d']); d = Math.trunc(ms / factors['d']);
@ -619,7 +607,7 @@ function getPlexHeaders(clientID) {
'X-Plex-Platform-Version': p.version, 'X-Plex-Platform-Version': p.version,
'X-Plex-Model': 'Plex OAuth', 'X-Plex-Model': 'Plex OAuth',
'X-Plex-Device': p.os, 'X-Plex-Device': p.os,
'X-Plex-Device-Name': p.name, 'X-Plex-Device-Name': p.name + ' (Tautulli)',
'X-Plex-Device-Screen-Resolution': window.screen.width + 'x' + window.screen.height, 'X-Plex-Device-Screen-Resolution': window.screen.width + 'x' + window.screen.height,
'X-Plex-Language': 'en' 'X-Plex-Language': 'en'
}; };
@ -929,3 +917,50 @@ $('.modal').on('hide.bs.modal', function (e) {
$.fn.hasScrollBar = function() { $.fn.hasScrollBar = function() {
return this.get(0).scrollHeight > this.get(0).clientHeight; return this.get(0).scrollHeight > this.get(0).clientHeight;
} }
function paginateScroller(scrollerId, buttonClass) {
$(buttonClass).click(function (e) {
e.preventDefault();
var scroller = $(scrollerId + "-row-scroller");
var scrollerParent = scroller.parent();
var containerWidth = scrollerParent.width();
var scrollCurrent = scrollerParent.scrollLeft();
var scrollAmount = $(this).data("id") * parseInt(containerWidth / 175) * 175;
var scrollMax = scroller.width() - Math.abs(scrollAmount);
var scrollTotal = Math.min(parseInt(scrollCurrent / 175) * 175 + scrollAmount, scrollMax);
scrollerParent.animate({ scrollLeft: scrollTotal }, 250);
});
}
function highlightScrollerButton(scrollerId) {
var scroller = $(scrollerId + "-row-scroller");
var scrollerParent = scroller.parent();
var buttonLeft = $(scrollerId + "-page-left");
var buttonRight = $(scrollerId + "-page-right");
var numElems = scroller.find("li").length;
scroller.width(numElems * 175);
$(buttonLeft).addClass("disabled").blur();
if (scroller.width() > scrollerParent.width()) {
$(buttonRight).removeClass("disabled");
} else {
$(buttonRight).addClass("disabled");
}
scrollerParent.scroll(function () {
var scrollCurrent = $(this).scrollLeft();
var scrollMax = scroller.width() - $(this).width();
if (scrollCurrent == 0) {
$(buttonLeft).addClass("disabled").blur();
} else {
$(buttonLeft).removeClass("disabled");
}
if (scrollCurrent >= scrollMax) {
$(buttonRight).addClass("disabled").blur();
} else {
$(buttonRight).removeClass("disabled");
}
});
}

View file

@ -32,7 +32,12 @@ collections_table_options = {
if (rowData['smart']) { if (rowData['smart']) {
smart = '<span class="media-type-tooltip" data-toggle="tooltip" title="Smart Collection"><i class="fa fa-cog fa-fw"></i></span>&nbsp;' smart = '<span class="media-type-tooltip" data-toggle="tooltip" title="Smart Collection"><i class="fa fa-cog fa-fw"></i></span>&nbsp;'
} }
console.log(rowData['subtype'])
if (rowData['subtype'] === 'artist' || rowData['subtype'] === 'album' || rowData['subtype'] === 'track') {
var thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="' + page('pms_image_proxy', rowData['thumb'], rowData['ratingKey'], 300, 300, null, null, null, 'cover') + '" data-height="80" data-width="80">' + rowData['title'] + '</span>';
} else {
var thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="' + page('pms_image_proxy', rowData['thumb'], rowData['ratingKey'], 300, 450, null, null, null, 'poster') + '" data-height="120" data-width="80">' + rowData['title'] + '</span>'; var thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="' + page('pms_image_proxy', rowData['thumb'], rowData['ratingKey'], 300, 450, null, null, null, 'poster') + '" data-height="120" data-width="80">' + rowData['title'] + '</span>';
}
$(td).html(smart + '<a href="' + page('info', rowData['ratingKey']) + '">' + thumb_popover + '</a>'); $(td).html(smart + '<a href="' + page('info', rowData['ratingKey']) + '">' + thumb_popover + '</a>');
} }
}, },

View file

@ -100,7 +100,7 @@ export_table_options = {
"createdCell": function (td, cellData, rowData, row, col) { "createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== '') { if (cellData !== '') {
var images = ''; var images = '';
if (rowData['thumb_level'] || rowData['art_level']) { if (rowData['thumb_level'] || rowData['art_level'] || rowData['logo_level']) {
images = ' + images'; images = ' + images';
} }
$(td).html(cellData + images); $(td).html(cellData + images);
@ -161,14 +161,14 @@ export_table_options = {
if (cellData === 1 && rowData['exists']) { if (cellData === 1 && rowData['exists']) {
var tooltip_title = ''; var tooltip_title = '';
var icon = ''; var icon = '';
if (rowData['thumb_level'] || rowData['art_level'] || rowData['individual_files']) { if (rowData['thumb_level'] || rowData['art_level'] || rowData['logo_level'] || rowData['individual_files']) {
tooltip_title = 'Zip Archive'; tooltip_title = 'ZIP Archive';
icon = 'fa-file-archive'; icon = 'fa-file-archive';
} else { } else {
tooltip_title = rowData['file_format'].toUpperCase() + ' File'; tooltip_title = rowData['file_format'].toUpperCase() + ' File';
icon = 'fa-file-download'; icon = 'fa-file-download';
} }
var icon = (rowData['thumb_level'] || rowData['art_level'] || rowData['individual_files']) ? 'fa-file-archive' : 'fa-file-download'; var icon = (rowData['thumb_level'] || rowData['art_level'] || rowData['logo_level'] || rowData['individual_files']) ? 'fa-file-archive' : 'fa-file-download';
$(td).html('<button class="btn btn-xs btn-success pull-left" data-id="' + rowData['export_id'] + '"><span data-toggle="tooltip" data-placement="left" title="' + tooltip_title + '"><i class="fa ' + icon + ' fa-fw"></i> Download</span></button>'); $(td).html('<button class="btn btn-xs btn-success pull-left" data-id="' + rowData['export_id'] + '"><span data-toggle="tooltip" data-placement="left" title="' + tooltip_title + '"><i class="fa ' + icon + ' fa-fw"></i> Download</span></button>');
} else if (cellData === 0) { } else if (cellData === 0) {
var percent = Math.min(getPercent(rowData['exported_items'], rowData['total_items']), 99) var percent = Math.min(getPercent(rowData['exported_items'], rowData['total_items']), 99)

View file

@ -247,7 +247,7 @@ history_table_options = {
}, },
{ {
"targets": [11], "targets": [11],
"data": "duration", "data": "play_duration",
"render": function (data, type, full) { "render": function (data, type, full) {
if (data !== null) { if (data !== null) {
return Math.round(moment.duration(data, 'seconds').as('minutes')) + ' mins'; return Math.round(moment.duration(data, 'seconds').as('minutes')) + ' mins';
@ -263,13 +263,17 @@ history_table_options = {
"targets": [12], "targets": [12],
"data": "watched_status", "data": "watched_status",
"createdCell": function (td, cellData, rowData, row, col) { "createdCell": function (td, cellData, rowData, row, col) {
var circleValue = "";
if (cellData == 1) { if (cellData == 1) {
$(td).html('<span class="watched-tooltip" data-toggle="tooltip" title="' + rowData['percent_complete'] + '%"><i class="fa fa-lg fa-circle"></i></span>'); circleValue = " circle-full";
} else if (cellData == 0.75) {
circleValue = " circle-three-quarter";
} else if (cellData == 0.5) { } else if (cellData == 0.5) {
$(td).html('<span class="watched-tooltip" data-toggle="tooltip" title="' + rowData['percent_complete'] + '%"><i class="fa fa-lg fa-adjust fa-rotate-180"></i></span>'); circleValue = " circle-half";
} else { } else if (cellData == 0.25) {
$(td).html('<span class="watched-tooltip" data-toggle="tooltip" title="' + rowData['percent_complete'] + '%"><i class="fa fa-lg fa-circle-o"></i></span>'); circleValue = " circle-quarter";
} }
$(td).html('<span class="watched-tooltip" data-toggle="tooltip" title="' + rowData['percent_complete'] + '%"><div class="circle' + circleValue + '" /></span>');
}, },
"searchable": false, "searchable": false,
"orderable": false, "orderable": false,
@ -529,7 +533,7 @@ function childTableFormat(rowData) {
'<th align="left" id="started">Started</th>' + '<th align="left" id="started">Started</th>' +
'<th align="left" id="paused_counter">Paused</th>' + '<th align="left" id="paused_counter">Paused</th>' +
'<th align="left" id="stopped">Stopped</th>' + '<th align="left" id="stopped">Stopped</th>' +
'<th align="left" id="duration">Duration</th>' + '<th align="left" id="play_duration">Duration</th>' +
'<th align="left" id="percent_complete"></th>' + '<th align="left" id="percent_complete"></th>' +
'</tr>' + '</tr>' +
'</thead>' + '</thead>' +

View file

@ -33,7 +33,7 @@ libraries_list_table_options = {
$(td).html('<div class="edit-library-toggles">' + $(td).html('<div class="edit-library-toggles">' +
'<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>&nbsp' + '<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>&nbsp' +
'<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>&nbsp&nbsp&nbsp' + '<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>&nbsp&nbsp&nbsp' +
'<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>&nbsp' + '<input type="checkbox" id="keep_history-' + rowData['section_id'] + '" name="keep_history" value="1" ' + (rowData['keep_history'] ? 'checked' : '') + '><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>&nbsp' +
'</div>'); '</div>');
}, },
"width": "7%", "width": "7%",

View file

@ -50,8 +50,8 @@ users_list_table_options = {
$(td).html('<div class="edit-user-toggles">' + $(td).html('<div class="edit-user-toggles">' +
'<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>&nbsp' + '<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>&nbsp' +
'<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>&nbsp&nbsp&nbsp' + '<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>&nbsp&nbsp&nbsp' +
'<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>&nbsp' + '<input type="checkbox" id="keep_history-' + rowData['user_id'] + '" name="keep_history" value="1" ' + (rowData['keep_history'] ? 'checked' : '') + '><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>&nbsp' +
'<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>&nbsp' + '<input type="checkbox" id="allow_guest-' + rowData['user_id'] + '" name="allow_guest" value="1" ' + (rowData['allow_guest'] ? 'checked' : '') + '><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>&nbsp' +
'</div>'); '</div>');
}, },
"width": "7%", "width": "7%",
@ -90,6 +90,42 @@ users_list_table_options = {
}, },
{ {
"targets": [3], "targets": [3],
"data": "username",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
$(td).html(cellData);
}
},
"visible": false,
"width": "10%",
"className": "no-wrap"
},
{
"targets": [4],
"data": "title",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
$(td).html(cellData);
}
},
"visible": false,
"width": "10%",
"className": "no-wrap"
},
{
"targets": [5],
"data": "email",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
$(td).html(cellData);
}
},
"visible": false,
"width": "10%",
"className": "no-wrap"
},
{
"targets": [6],
"data": "last_seen", "data": "last_seen",
"createdCell": function (td, cellData, rowData, row, col) { "createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') { if (cellData !== null && cellData !== '') {
@ -103,7 +139,7 @@ users_list_table_options = {
"className": "no-wrap" "className": "no-wrap"
}, },
{ {
"targets": [4], "targets": [7],
"data": "ip_address", "data": "ip_address",
"createdCell": function (td, cellData, rowData, row, col) { "createdCell": function (td, cellData, rowData, row, col) {
if (cellData) { if (cellData) {
@ -121,7 +157,7 @@ users_list_table_options = {
"className": "no-wrap modal-control-ip" "className": "no-wrap modal-control-ip"
}, },
{ {
"targets": [5], "targets": [8],
"data": "platform", "data": "platform",
"createdCell": function (td, cellData, rowData, row, col) { "createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') { if (cellData !== null && cellData !== '') {
@ -134,7 +170,7 @@ users_list_table_options = {
"className": "no-wrap modal-control" "className": "no-wrap modal-control"
}, },
{ {
"targets": [6], "targets": [9],
"data":"player", "data":"player",
"createdCell": function (td, cellData, rowData, row, col) { "createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') { if (cellData !== null && cellData !== '') {
@ -155,7 +191,7 @@ users_list_table_options = {
"className": "no-wrap modal-control" "className": "no-wrap modal-control"
}, },
{ {
"targets": [7], "targets": [10],
"data":"last_played", "data":"last_played",
"createdCell": function (td, cellData, rowData, row, col) { "createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') { if (cellData !== null && cellData !== '') {
@ -196,7 +232,7 @@ users_list_table_options = {
"className": "datatable-wrap" "className": "datatable-wrap"
}, },
{ {
"targets": [8], "targets": [11],
"data": "plays", "data": "plays",
"createdCell": function (td, cellData, rowData, row, col) { "createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') { if (cellData !== null && cellData !== '') {
@ -208,7 +244,7 @@ users_list_table_options = {
"className": "no-wrap" "className": "no-wrap"
}, },
{ {
"targets": [9], "targets": [12],
"data": "duration", "data": "duration",
"createdCell": function (td, cellData, rowData, row, col) { "createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') { if (cellData !== null && cellData !== '') {

View file

@ -149,10 +149,10 @@ DOCUMENTATION :: END
<div class="table-card-header"> <div class="table-card-header">
<ul class="nav nav-header nav-dashboard pull-right"> <ul class="nav nav-header nav-dashboard pull-right">
<li> <li>
<a href="#" id="recently-watched-page-left" class="paginate-watched btn-gray disabled" data-id="+1"><i class="fa fa-lg fa-chevron-left"></i></a> <a href="#" id="recently-watched-page-left" class="paginate-watched btn-gray disabled" data-id="-1"><i class="fa fa-lg fa-chevron-left"></i></a>
</li> </li>
<li> <li>
<a href="#" id="recently-watched-page-right" class="paginate-watched btn-gray disabled" data-id="-1"><i class="fa fa-lg fa-chevron-right"></i></a> <a href="#" id="recently-watched-page-right" class="paginate-watched btn-gray disabled" data-id="+1"><i class="fa fa-lg fa-chevron-right"></i></a>
</li> </li>
</ul> </ul>
<div class="header-bar"> <div class="header-bar">
@ -175,10 +175,10 @@ DOCUMENTATION :: END
<div class="table-card-header"> <div class="table-card-header">
<ul class="nav nav-header nav-dashboard pull-right"> <ul class="nav nav-header nav-dashboard pull-right">
<li> <li>
<a href="#" id="recently-added-page-left" class="paginate-added btn-gray disabled" data-id="+1"><i class="fa fa-lg fa-chevron-left"></i></a> <a href="#" id="recently-added-page-left" class="paginate-added btn-gray disabled" data-id="-1"><i class="fa fa-lg fa-chevron-left"></i></a>
</li> </li>
<li> <li>
<a href="#" id="recently-added-page-right" class="paginate-added btn-gray disabled" data-id="-1"><i class="fa fa-lg fa-chevron-right"></i></a> <a href="#" id="recently-added-page-right" class="paginate-added btn-gray disabled" data-id="+1"><i class="fa fa-lg fa-chevron-right"></i></a>
</li> </li>
</ul> </ul>
<div class="header-bar"> <div class="header-bar">
@ -217,13 +217,13 @@ DOCUMENTATION :: END
</div> </div>
% endif % endif
<div class="btn-group" data-toggle="buttons" id="transcode_decision-selection"> <div class="btn-group" data-toggle="buttons" id="transcode_decision-selection">
<label class="btn btn-dark"> <label class="btn btn-dark btn-filter">
<input type="checkbox" name="transcode_decision-filter" id="history-transcode_decision-direct_play" value="direct play" autocomplete="off"><i class="fa fa-play-circle"></i> Direct Play <input type="checkbox" name="transcode_decision-filter" id="history-transcode_decision-direct_play" value="direct play" autocomplete="off"><i class="fa fa-play-circle"></i> Direct Play
</label> </label>
<label class="btn btn-dark"> <label class="btn btn-dark btn-filter">
<input type="checkbox" name="transcode_decision-filter" id="history-transcode_decision-direct_stream" value="copy" autocomplete="off"><i class="fa fa-stream"></i> Direct Stream <input type="checkbox" name="transcode_decision-filter" id="history-transcode_decision-direct_stream" value="copy" autocomplete="off"><i class="fa fa-stream"></i> Direct Stream
</label> </label>
<label class="btn btn-dark"> <label class="btn btn-dark btn-filter">
<input type="checkbox" name="transcode_decision-filter" id="history-transcode_decision-transcode" value="transcode" autocomplete="off"><i class="fa fa-server"></i> Transcode <input type="checkbox" name="transcode_decision-filter" id="history-transcode_decision-transcode" value="transcode" autocomplete="off"><i class="fa fa-server"></i> Transcode
</label> </label>
</div> </div>
@ -248,7 +248,7 @@ DOCUMENTATION :: END
<th align="left" id="started">Started</th> <th align="left" id="started">Started</th>
<th align="left" id="paused_counter">Paused</th> <th align="left" id="paused_counter">Paused</th>
<th align="left" id="stopped">Stopped</th> <th align="left" id="stopped">Stopped</th>
<th align="left" id="duration">Duration</th> <th align="left" id="play_duration">Duration</th>
<th align="left" id="percent_complete"></th> <th align="left" id="percent_complete"></th>
</tr> </tr>
</thead> </thead>
@ -690,7 +690,8 @@ DOCUMENTATION :: END
}, },
complete: function(xhr, status) { complete: function(xhr, status) {
$("#library-recently-watched").html(xhr.responseText); $("#library-recently-watched").html(xhr.responseText);
highlightWatchedScrollerButton(); highlightScrollerButton("#recently-watched");
paginateScroller("#recently-watched", ".paginate-watched");
} }
}); });
} }
@ -706,7 +707,8 @@ DOCUMENTATION :: END
}, },
complete: function(xhr, status) { complete: function(xhr, status) {
$("#library-recently-added").html(xhr.responseText); $("#library-recently-added").html(xhr.responseText);
highlightAddedScrollerButton(); highlightScrollerButton("#recently-added");
paginateScroller("#recently-added", ".paginate-added");
} }
}); });
} }
@ -716,83 +718,8 @@ DOCUMENTATION :: END
recentlyAdded(); recentlyAdded();
% endif % endif
function highlightWatchedScrollerButton() {
var scroller = $("#recently-watched-row-scroller");
var numElems = scroller.find("li").length;
scroller.width(numElems * 175);
if (scroller.width() > $("#library-recently-watched").width()) {
$("#recently-watched-page-right").removeClass("disabled");
} else {
$("#recently-watched-page-right").addClass("disabled");
}
}
function highlightAddedScrollerButton() {
var scroller = $("#recently-added-row-scroller");
var numElems = scroller.find("li").length;
scroller.width(numElems * 175);
if (scroller.width() > $("#library-recently-added").width()) {
$("#recently-added-page-right").removeClass("disabled");
} else {
$("#recently-added-page-right").addClass("disabled");
}
}
$(window).resize(function() {
highlightWatchedScrollerButton();
highlightAddedScrollerButton();
});
$('div.art-face').animate({ opacity: 0.2 }, { duration: 1000 }); $('div.art-face').animate({ opacity: 0.2 }, { duration: 1000 });
var leftTotalWatched = 0;
$(".paginate-watched").click(function (e) {
e.preventDefault();
var scroller = $("#recently-watched-row-scroller");
var containerWidth = $("#library-recently-watched").width();
var scrollAmount = $(this).data("id") * parseInt(containerWidth / 175) * 175;
var leftMax = Math.min(-parseInt(scroller.width()) + Math.abs(scrollAmount), 0);
leftTotalWatched = Math.max(Math.min(leftTotalWatched + scrollAmount, 0), leftMax);
scroller.animate({ left: leftTotalWatched }, 250);
if (leftTotalWatched == 0) {
$("#recently-watched-page-left").addClass("disabled").blur();
} else {
$("#recently-watched-page-left").removeClass("disabled");
}
if (leftTotalWatched == leftMax) {
$("#recently-watched-page-right").addClass("disabled").blur();
} else {
$("#recently-watched-page-right").removeClass("disabled");
}
});
var leftTotalAdded = 0;
$(".paginate-added").click(function (e) {
e.preventDefault();
var scroller = $("#recently-added-row-scroller");
var containerWidth = $("#library-recently-added").width();
var scrollAmount = $(this).data("id") * parseInt(containerWidth / 175) * 175;
var leftMax = Math.min(-parseInt(scroller.width()) + Math.abs(scrollAmount), 0);
leftTotalAdded = Math.max(Math.min(leftTotalAdded + scrollAmount, 0), leftMax);
scroller.animate({ left: leftTotalAdded }, 250);
if (leftTotalAdded == 0) {
$("#recently-added-page-left").addClass("disabled").blur();
} else {
$("#recently-added-page-left").removeClass("disabled");
}
if (leftTotalAdded == leftMax) {
$("#recently-added-page-right").addClass("disabled").blur();
} else {
$("#recently-added-page-right").removeClass("disabled");
}
});
$(document).ready(function () { $(document).ready(function () {
// Javascript to enable link to tab // Javascript to enable link to tab

View file

@ -36,7 +36,7 @@ DOCUMENTATION :: END
%> %>
<div class="dashboard-recent-media-row"> <div class="dashboard-recent-media-row">
<div id="recently-added-row-scroller" style="left: 0;"> <div id="recently-added-row-scroller">
<ul class="dashboard-recent-media list-unstyled"> <ul class="dashboard-recent-media list-unstyled">
% for item in data: % for item in data:
<li> <li>

View file

@ -40,7 +40,7 @@ DOCUMENTATION :: END
%> %>
<div class="dashboard-stats-instance" id="library-stats-instance-${section_type}" data-section_type="${section_type}"> <div class="dashboard-stats-instance" id="library-stats-instance-${section_type}" data-section_type="${section_type}">
<div class="dashboard-stats-container"> <div class="dashboard-stats-container">
<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')});"> <div id="library-stats-background-${section_type}" class="dashboard-stats-background" style="background-image: url(${page('pms_image_proxy', row0['art'] or row0['library_art'], None, 500, 280, 40, '282828', 3, fallback=row0['library_art'])});" data-library_art="${row0['library_art']}">
% if row0['thumb'].startswith('http'): % 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> <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: % else:
@ -56,7 +56,7 @@ DOCUMENTATION :: END
<ul class="list-unstyled dashboard-stats-info-list"> <ul class="list-unstyled dashboard-stats-info-list">
% for section in data[section_type]: % for section in data[section_type]:
<li class="dashboard-stats-info-item ${'expanded' if loop.index == 0 else ''}" data-stat_id="${section_type}" <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')}"> data-art="${section.get('art')}" data-thumb="${section.get('thumb')}" data-library_art="${section.get('library_art')}">
<div class="sub-list">${loop.index + 1}</div> <div class="sub-list">${loop.index + 1}</div>
<div class="sub-value"> <div class="sub-value">
<a href="${page('library', section['section_id'])}" title="${section['section_name']}"> <a href="${page('library', section['section_id'])}" title="${section['section_name']}">

View file

@ -11,6 +11,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content=""> <meta name="description" content="">
<meta name="author" content=""> <meta name="author" content="">
<meta name="referrer" content="no-referrer">
<link href="${http_root}css/bootstrap3/bootstrap.min.css" rel="stylesheet"> <link href="${http_root}css/bootstrap3/bootstrap.min.css" rel="stylesheet">
<link href="${http_root}css/tautulli.css${cache_param}" rel="stylesheet"> <link href="${http_root}css/tautulli.css${cache_param}" rel="stylesheet">
<link href="${http_root}css/opensans.min.css" rel="stylesheet"> <link href="${http_root}css/opensans.min.css" rel="stylesheet">

View file

@ -453,12 +453,12 @@
$("#download-tautullilog").click(function () { $("#download-tautullilog").click(function () {
var logfile = $(".tab-pane.active").data('logfile'); var logfile = $(".tab-pane.active").data('logfile');
window.location.href = "download_log?logfile=" + logfile; window.location.href = "download_log?logfile=" + window.encodeURIComponent(logfile);
}); });
$("#download-plexserverlog").click(function () { $("#download-plexserverlog").click(function () {
var logfile = $("option:selected", "#plex-log-files").val(); var logfile = $("option:selected", "#plex-log-files").val();
window.location.href = "download_plex_log?logfile=" + logfile; window.location.href = "download_plex_log?logfile=" + window.encodeURIComponent(logfile);
}); });
$("#clear-notify-logs").click(function () { $("#clear-notify-logs").click(function () {

View file

@ -3,7 +3,7 @@
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button> <button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
<h4 class="modal-title" id="mobile-device-config-modal-header">${device['device_name']} Settings &nbsp;<small><span class="device_id">(Device ID: ${device['id']})</span></small></h4> <h4 class="modal-title" id="mobile-device-config-modal-header">${device['device_name']} Settings &nbsp;<small><span class="device_id">(Device ID: ${device['id']}${' - ' + device['friendly_name'] if device['friendly_name'] else ''})</span></small></h4>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="container-fluid"> <div class="container-fluid">

View file

@ -28,15 +28,17 @@ DOCUMENTATION :: END
<span class="toggle-left official-tooltip" data-toggle="tooltip" data-placement="top" title="Unofficial or Unknown App"><i class="fa fa-lg fa-fw fa-exclamation-triangle"></i></span> <span class="toggle-left official-tooltip" data-toggle="tooltip" data-placement="top" title="Unofficial or Unknown App"><i class="fa fa-lg fa-fw fa-exclamation-triangle"></i></span>
% endif % endif
${device['friendly_name'] or device['device_name']} &nbsp;<span class="friendly_name">(${device['id']})</span> ${device['friendly_name'] or device['device_name']} &nbsp;<span class="friendly_name">(${device['id']})</span>
<span class="toggle-right"><i class="fa fa-lg fa-fw fa-cog"></i></span> <span class="toggle-right friendly_name">
<span class="toggle-right friendly_name" id="device-last_seen-${device['id']}">
% if device['last_seen']: % if device['last_seen']:
<span id="device-last_seen-${device['id']}">
<script> <script>
$("#device-last_seen-${device['id']}").text(moment("${device['last_seen']}", "X").fromNow()) $("#device-last_seen-${device['id']}").text(moment("${device['last_seen']}", "X").fromNow())
</script> </script>
</span>
% else: % else:
never never
% endif % endif
<i class="fa fa-lg fa-fw fa-cog"></i></span>
</span> </span>
</span> </span>
</li> </li>
@ -58,7 +60,7 @@ DOCUMENTATION :: END
getPlexPyURL = function () { getPlexPyURL = function () {
var deferred = $.Deferred(); var deferred = $.Deferred();
if (location.hostname !== "localhost" && location.hostname !== "127.0.0.1") { if (location.hostname !== "localhost" && location.hostname !== "127.0.0.1" && location.hostname !== "[::1]") {
deferred.resolve(location.href.split('/settings')[0]); deferred.resolve(location.href.split('/settings')[0]);
} else { } else {
$.get('get_plexpy_url').then(function (url) { $.get('get_plexpy_url').then(function (url) {
@ -70,11 +72,11 @@ DOCUMENTATION :: END
function checkQRAddress(url) { function checkQRAddress(url) {
var parser = document.createElement('a'); var parser = document.createElement('a');
parser.href = url; parser.setAttribute('href', url);
var hostname = parser.hostname; var hostname = parser.hostname;
var protocol = parser.protocol; var protocol = parser.protocol;
if (hostname === '127.0.0.1' || hostname === 'localhost') { if (hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '[::1]') {
$('#api_qr_localhost').toggle(true); $('#api_qr_localhost').toggle(true);
$('#api_qr_private').toggle(false); $('#api_qr_private').toggle(false);
} else { } else {

View file

@ -13,7 +13,7 @@
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button> <button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
<h4 class="modal-title" id="newsletter-config-modal-header">${newsletter['agent_label']} Newsletter Settings &nbsp;<small><span class="newsletter_id">(Newsletter ID: ${newsletter['id']})</span></small></h4> <h4 class="modal-title" id="newsletter-config-modal-header">${newsletter['agent_label']} Newsletter Settings &nbsp;<small><span class="newsletter_id">(Newsletter ID: ${newsletter['id']}${' - ' + newsletter['friendly_name'] if newsletter['friendly_name'] else ''})</span></small></h4>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="container-fluid"> <div class="container-fluid">
@ -32,7 +32,7 @@
<div class="col-md-12"> <div class="col-md-12">
<div class="checkbox" style="margin-bottom: 20px;"> <div class="checkbox" style="margin-bottom: 20px;">
<label> <label>
<input type="checkbox" data-id="active_value" class="checkboxes" value="1" ${checked(newsletter['active'])}> Enable the Newsletter <input type="checkbox" data-id="active_value" class="checkboxes" value="1" autocomplete="off" ${checked(newsletter['active'])}> Enable the Newsletter
</label> </label>
<input type="hidden" id="active_value" name="active" value="${newsletter['active']}"> <input type="hidden" id="active_value" name="active" value="${newsletter['active']}">
</div> </div>
@ -40,27 +40,31 @@
<label for="custom_cron">Schedule</label> <label for="custom_cron">Schedule</label>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<select class="form-control" id="custom_cron" name="newsletter_config_custom_cron"> <select class="form-control" id="custom_cron" name="newsletter_config_custom_cron" autocomplete="off">
<option value="0" ${'selected' if newsletter['config']['custom_cron'] == 0 else ''}>Simple</option> <option value="0" ${'selected' if newsletter['config']['custom_cron'] == 0 else ''}>Simple</option>
<option value="1" ${'selected' if newsletter['config']['custom_cron'] == 1 else ''}>Custom</option> <option value="1" ${'selected' if newsletter['config']['custom_cron'] == 1 else ''}>Custom</option>
</select> </select>
<input type="text" id="cron_value" name="cron" value="${newsletter['cron']}" /> <input type="text" id="cron_value" name="cron" value="${newsletter['cron']}" autocomplete="off" />
<div id="cron-widget"></div> <div id="cron-widget"></div>
</div> </div>
</div> </div>
<p class="help-block"> <p class="help-block">
<span id="simple_cron_message">Set the schedule for the newsletter.</span> <span id="simple_cron_message">Set the schedule for the newsletter.</span>
<span id="custom_cron_message">Set the schedule for the newsletter using a <a href="${anon_url('https://crontab.guru')}" target="_blank" rel="noreferrer">custom crontab</a>. Only standard cron values are valid.</span> <span id="custom_cron_message">
Set the schedule for the newsletter using a <a href="${anon_url('https://crontab.guru')}" target="_blank" rel="noreferrer">custom crontab</a>.
<a href="${anon_url('https://apscheduler.readthedocs.io/en/3.x/modules/triggers/cron.html#expression-types')}" target="_blank" rel="noreferrer">Click here</a> for a list of supported expressions.
</span>
</p> </p>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="time_frame">Time Frame</label> <label for="time_frame">Time Frame</label>
<div class="row"> <div class="row">
<div class="col-md-4"> <div class="col-md-5">
<div class="input-group newsletter-time_frame"> <div class="input-group newsletter-time_frame">
<span class="input-group-addon form-control btn-dark inactive">Last</span> <span class="input-group-addon form-control btn-dark inactive">Last</span>
<input type="number" class="form-control" id="newsletter_config_time_frame" name="newsletter_config_time_frame" value="${newsletter['config']['time_frame']}"> <input type="number" class="form-control" id="newsletter_config_time_frame" name="newsletter_config_time_frame" value="${newsletter['config']['time_frame']}" autocomplete="off">
<select class="form-control" id="newsletter_config_time_frame_units" name="newsletter_config_time_frame_units"> <select class="form-control" id="newsletter_config_time_frame_units" name="newsletter_config_time_frame_units" autocomplete="off">
<option value="months" ${'selected' if newsletter['config']['time_frame_units'] == 'months' else ''}>months</option>
<option value="days" ${'selected' if newsletter['config']['time_frame_units'] == 'days' else ''}>days</option> <option value="days" ${'selected' if newsletter['config']['time_frame_units'] == 'days' else ''}>days</option>
<option value="hours" ${'selected' if newsletter['config']['time_frame_units'] == 'hours' else ''}>hours</option> <option value="hours" ${'selected' if newsletter['config']['time_frame_units'] == 'hours' else ''}>hours</option>
</select> </select>
@ -84,7 +88,7 @@
<label for="${item['name']}">${item['label']}</label> <label for="${item['name']}">${item['label']}</label>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<input type="${item['input_type']}" class="form-control" id="${item['name']}" name="${item['name']}" value="${item['value']}" size="30" ${'readonly' if item.get('readonly') else ''}> <input type="${item['input_type']}" class="form-control" id="${item['name']}" name="${item['name']}" value="${item['value']}" size="30" autocomplete="off" ${'readonly' if item.get('readonly') else ''}>
</div> </div>
</div> </div>
<p class="help-block">${item['description'] | n}</p> <p class="help-block">${item['description'] | n}</p>
@ -94,7 +98,7 @@
<label for="${item['name']}">${item['label']}</label> <label for="${item['name']}">${item['label']}</label>
<div class="row"> <div class="row">
<div class="col-md-3"> <div class="col-md-3">
<input type="${item['input_type']}" class="form-control" id="${item['name']}" name="${item['name']}" value="${item['value']}" size="30"> <input type="${item['input_type']}" class="form-control" id="${item['name']}" name="${item['name']}" value="${item['value']}" size="30" autocomplete="off">
</div> </div>
</div> </div>
<p class="help-block">${item['description'] | n}</p> <p class="help-block">${item['description'] | n}</p>
@ -112,7 +116,7 @@
% elif item['input_type'] == 'checkbox': % elif item['input_type'] == 'checkbox':
<div class="checkbox"> <div class="checkbox">
<label> <label>
<input type="checkbox" data-id="${item['name']}" class="checkboxes" value="1" ${checked(item['value'])}> ${item['label']} <input type="checkbox" data-id="${item['name']}" class="checkboxes" value="1" autocomplete="off" ${checked(item['value'])}> ${item['label']}
</label> </label>
<p class="help-block">${item['description'] | n}</p> <p class="help-block">${item['description'] | n}</p>
<input type="hidden" id="${item['name']}" name="${item['name']}" value="${item['value']}"> <input type="hidden" id="${item['name']}" name="${item['name']}" value="${item['value']}">
@ -122,7 +126,7 @@
<label for="${item['name']}">${item['label']}</label> <label for="${item['name']}">${item['label']}</label>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<select class="form-control" id="${item['name']}" name="${item['name']}"> <select class="form-control" id="${item['name']}" name="${item['name']}" autocomplete="off">
% for key, value in sorted(item['select_options'].items()): % for key, value in sorted(item['select_options'].items()):
% if key == item['value']: % if key == item['value']:
<option value="${key}" selected>${value}</option> <option value="${key}" selected>${value}</option>
@ -140,9 +144,11 @@
<label for="${item['name']}">${item['label']}</label> <label for="${item['name']}">${item['label']}</label>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<select class="form-control" id="${item['name']}" name="${item['name']}"> <select class="form-control" id="${item['name']}" name="${item['name']}" autocomplete="off">
% if item['select_all']:
<option value="select-all">Select All</option> <option value="select-all">Select All</option>
<option value="remove-all">Remove All</option> <option value="remove-all">Remove All</option>
% endif
% if isinstance(item['select_options'], dict): % if isinstance(item['select_options'], dict):
% for section, options in item['select_options'].items(): % for section, options in item['select_options'].items():
<optgroup label="${section}"> <optgroup label="${section}">
@ -152,7 +158,9 @@
</optgroup> </optgroup>
% endfor % endfor
% else: % else:
% if item['select_all']:
<option value="border-all"></option> <option value="border-all"></option>
% endif
% for option in sorted(item['select_options'], key=lambda x: x['text'].lower()): % for option in sorted(item['select_options'], key=lambda x: x['text'].lower()):
<option value="${option['value']}">${option['text']}</option> <option value="${option['value']}">${option['text']}</option>
% endfor % endfor
@ -170,7 +178,7 @@
<label for="id_name">Unique ID Name</label> <label for="id_name">Unique ID Name</label>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<input type="text" class="form-control" id="id_name" name="id_name" value="${newsletter['id_name']}" size="30"> <input type="text" class="form-control" id="id_name" name="id_name" value="${newsletter['id_name']}" size="30" autocomplete="off">
</div> </div>
</div> </div>
<p class="help-block"> <p class="help-block">
@ -183,7 +191,7 @@
<label for="friendly_name">Description</label> <label for="friendly_name">Description</label>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<input type="text" class="form-control" id="friendly_name" name="friendly_name" value="${newsletter['friendly_name']}" size="30"> <input type="text" class="form-control" id="friendly_name" name="friendly_name" value="${newsletter['friendly_name']}" size="30" autocomplete="off">
</div> </div>
</div> </div>
<p class="help-block">Optional: Enter a description to help identify this newsletter in the newsletters list.</p> <p class="help-block">Optional: Enter a description to help identify this newsletter in the newsletters list.</p>
@ -197,7 +205,7 @@
<label>Saving</label> <label>Saving</label>
<div class="checkbox"> <div class="checkbox">
<label> <label>
<input type="checkbox" id="newsletter_config_save_only_checkbox" data-id="newsletter_config_save_only" class="checkboxes" value="1" ${checked(newsletter['config']['save_only'])}> Save HTML File Only <input type="checkbox" id="newsletter_config_save_only_checkbox" data-id="newsletter_config_save_only" class="checkboxes" value="1" autocomplete="off" ${checked(newsletter['config']['save_only'])}> Save HTML File Only
</label> </label>
<p class="help-block">Enable to save the newsletter HTML file without sending it to any notification agent.</p> <p class="help-block">Enable to save the newsletter HTML file without sending it to any notification agent.</p>
<input type="hidden" id="newsletter_config_save_only" name="newsletter_config_save_only" value="${newsletter['config']['save_only']}"> <input type="hidden" id="newsletter_config_save_only" name="newsletter_config_save_only" value="${newsletter['config']['save_only']}">
@ -206,7 +214,7 @@
<label for="newsletter_config_filename">HTML File Name</label> <label for="newsletter_config_filename">HTML File Name</label>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<input type="text" class="form-control" id="newsletter_config_filename" name="newsletter_config_filename" value="${newsletter['config']['filename']}" size="30"> <input type="text" class="form-control" id="newsletter_config_filename" name="newsletter_config_filename" value="${newsletter['config']['filename']}" size="30" autocomplete="off">
</div> </div>
</div> </div>
<p class="help-block">Optional: Enter the file name to use when saving the newsletter (ending with <span class="inline-pre">.html</span>). You may use any of the <a href="#newsletter-text-sub-modal" data-toggle="modal">newsletter text parameters</a>. Leave blank for default.</p> <p class="help-block">Optional: Enter the file name to use when saving the newsletter (ending with <span class="inline-pre">.html</span>). You may use any of the <a href="#newsletter-text-sub-modal" data-toggle="modal">newsletter text parameters</a>. Leave blank for default.</p>
@ -216,7 +224,7 @@
<label>Sending</label> <label>Sending</label>
<div class="checkbox"> <div class="checkbox">
<label> <label>
<input type="checkbox" id="newsletter_config_formatted_checkbox" data-id="newsletter_config_formatted" class="checkboxes" value="1" ${checked(newsletter['config']['formatted'])}> Send Newsletter as an HTML Formatted Email <input type="checkbox" id="newsletter_config_formatted_checkbox" data-id="newsletter_config_formatted" class="checkboxes" value="1" autocomplete="off" ${checked(newsletter['config']['formatted'])}> Send Newsletter as an HTML Formatted Email
</label> </label>
<p class="help-block">Enable to send the newsletter as an HTML formatted Email. Disable to only send a subject and body message to a different notification agent.</p> <p class="help-block">Enable to send the newsletter as an HTML formatted Email. Disable to only send a subject and body message to a different notification agent.</p>
<input type="hidden" id="newsletter_config_formatted" name="newsletter_config_formatted" value="${newsletter['config']['formatted']}"> <input type="hidden" id="newsletter_config_formatted" name="newsletter_config_formatted" value="${newsletter['config']['formatted']}">
@ -224,7 +232,7 @@
<div class="form-group" id="email_notifier_select"> <div class="form-group" id="email_notifier_select">
<div class="checkbox"> <div class="checkbox">
<label> <label>
<input type="checkbox" id="newsletter_config_threaded_checkbox" data-id="newsletter_config_threaded" class="checkboxes" value="1" ${checked(newsletter['config']['threaded'])}> Enable Grouped Email Thread <input type="checkbox" id="newsletter_config_threaded_checkbox" data-id="newsletter_config_threaded" class="checkboxes" value="1" autocomplete="off" ${checked(newsletter['config']['threaded'])}> Enable Grouped Email Thread
</label> </label>
<p class="help-block">Enable to group this newsletter together in a single Email thread. Disable to send a new Email for each newsletter.</p> <p class="help-block">Enable to group this newsletter together in a single Email thread. Disable to send a new Email for each newsletter.</p>
<input type="hidden" id="newsletter_config_threaded" name="newsletter_config_threaded" value="${newsletter['config']['threaded']}"> <input type="hidden" id="newsletter_config_threaded" name="newsletter_config_threaded" value="${newsletter['config']['threaded']}">
@ -232,7 +240,7 @@
<label for="newsletter_email_notifier_id">Email Notification Agent</label> <label for="newsletter_email_notifier_id">Email Notification Agent</label>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<select class="form-control" id="newsletter_email_notifier_id" name="newsletter_email_notifier_id"> <select class="form-control" id="newsletter_email_notifier_id" name="newsletter_email_notifier_id" autocomplete="off">
% for notifier in email_notifiers: % for notifier in email_notifiers:
<% selected = 'selected' if notifier['id'] == newsletter['email_config']['notifier_id'] else '' %> <% selected = 'selected' if notifier['id'] == newsletter['email_config']['notifier_id'] else '' %>
% if notifier['friendly_name']: % if notifier['friendly_name']:
@ -255,7 +263,7 @@
<label for="newsletter_config_notifier_id">Notification Agent</label> <label for="newsletter_config_notifier_id">Notification Agent</label>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<select class="form-control" id="newsletter_config_notifier_id" name="newsletter_config_notifier_id"> <select class="form-control" id="newsletter_config_notifier_id" name="newsletter_config_notifier_id" autocomplete="off">
% for notifier in other_notifiers: % for notifier in other_notifiers:
<% selected = 'selected' if notifier['id'] == newsletter['config']['notifier_id'] else '' %> <% selected = 'selected' if notifier['id'] == newsletter['config']['notifier_id'] else '' %>
% if notifier['friendly_name']: % if notifier['friendly_name']:
@ -286,7 +294,7 @@
<label for="${item['name']}">${item['label']}</label> <label for="${item['name']}">${item['label']}</label>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<input type="${item['input_type']}" class="form-control" id="${item['name']}" name="${item['name']}" value="${item['value']}" size="30" ${'readonly' if item.get('readonly') else ''}> <input type="${item['input_type']}" class="form-control" id="${item['name']}" name="${item['name']}" value="${item['value']}" size="30" autocomplete="off" ${'readonly' if item.get('readonly') else ''}>
</div> </div>
</div> </div>
<p class="help-block">${item['description'] | n}</p> <p class="help-block">${item['description'] | n}</p>
@ -296,7 +304,7 @@
<label for="${item['name']}">${item['label']}</label> <label for="${item['name']}">${item['label']}</label>
<div class="row"> <div class="row">
<div class="col-md-3"> <div class="col-md-3">
<input type="${item['input_type']}" class="form-control" id="${item['name']}" name="${item['name']}" value="${item['value']}" size="30"> <input type="${item['input_type']}" class="form-control" id="${item['name']}" name="${item['name']}" value="${item['value']}" size="30" autocomplete="off">
</div> </div>
</div> </div>
<p class="help-block">${item['description'] | n}</p> <p class="help-block">${item['description'] | n}</p>
@ -314,7 +322,7 @@
% elif item['input_type'] == 'checkbox' and item['name'] != 'newsletter_email_html_support': % elif item['input_type'] == 'checkbox' and item['name'] != 'newsletter_email_html_support':
<div class="checkbox"> <div class="checkbox">
<label> <label>
<input type="checkbox" data-id="${item['name']}" class="checkboxes" value="1" ${checked(item['value'])}> ${item['label']} <input type="checkbox" data-id="${item['name']}" class="checkboxes" value="1" autocomplete="off" ${checked(item['value'])}> ${item['label']}
</label> </label>
<p class="help-block">${item['description'] | n}</p> <p class="help-block">${item['description'] | n}</p>
<input type="hidden" id="${item['name']}" name="${item['name']}" value="${item['value']}"> <input type="hidden" id="${item['name']}" name="${item['name']}" value="${item['value']}">
@ -324,7 +332,7 @@
<label for="${item['name']}">${item['label']}</label> <label for="${item['name']}">${item['label']}</label>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<select class="form-control" id="${item['name']}" name="${item['name']}"> <select class="form-control" id="${item['name']}" name="${item['name']}" autocomplete="off">
% for key, value in sorted(item['select_options'].items()): % for key, value in sorted(item['select_options'].items()):
% if isinstance(value, list): % if isinstance(value, list):
<optgroup label="${key}"> <optgroup label="${key}">
@ -346,7 +354,7 @@
<label for="${item['name']}">${item['label']}</label> <label for="${item['name']}">${item['label']}</label>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<select class="form-control" id="${item['name']}" name="${item['name']}"> <select class="form-control" id="${item['name']}" name="${item['name']}" autocomplete="off">
<option value="select-all">Select All</option> <option value="select-all">Select All</option>
<option value="remove-all">Remove All</option> <option value="remove-all">Remove All</option>
% if isinstance(item['select_options'], dict): % if isinstance(item['select_options'], dict):
@ -391,7 +399,7 @@
<label for="subject">Subject</label> <label for="subject">Subject</label>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<input type="text" class="form-control" id="subject" name="subject" value="${newsletter['subject']}" size="30"> <input type="text" class="form-control" id="subject" name="subject" value="${newsletter['subject']}" size="30" autocomplete="off">
</div> </div>
</div> </div>
<p class="help-block"> <p class="help-block">
@ -402,7 +410,7 @@
<label for="body">Body</label> <label for="body">Body</label>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<textarea class="form-control" id="body" name="body" data-autoresize>${newsletter['body']}</textarea> <textarea class="form-control" id="body" name="body" autocomplete="off" data-autoresize>${newsletter['body']}</textarea>
</div> </div>
</div> </div>
<p class="help-block"> <p class="help-block">
@ -413,7 +421,7 @@
<label for="message">Message</label> <label for="message">Message</label>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<textarea class="form-control" id="message" name="message" data-autoresize>${newsletter['message']}</textarea> <textarea class="form-control" id="message" name="message" autocomplete="off" data-autoresize>${newsletter['message']}</textarea>
</div> </div>
</div> </div>
<p class="help-block"> <p class="help-block">
@ -476,7 +484,7 @@
}); });
if (${newsletter['config']['custom_cron']}) { if (${newsletter['config']['custom_cron']}) {
$('#cron_value').val('${newsletter['cron']}'); $('#cron_value').val('${newsletter['cron'] | n}');
} else { } else {
try { try {
cron_widget.cron('value', '${newsletter['cron']}'); cron_widget.cron('value', '${newsletter['cron']}');

View file

@ -1,5 +1,5 @@
<% <%
from six.moves.urllib.parse import urlencode from urllib.parse import urlencode
%> %>
<!doctype html> <!doctype html>

View file

@ -20,13 +20,28 @@ DOCUMENTATION :: END
% else: % else:
${newsletter['agent_label']} &nbsp;<span class="friendly_name">(${newsletter['id']})</span> ${newsletter['agent_label']} &nbsp;<span class="friendly_name">(${newsletter['id']})</span>
% endif % endif
<span class="toggle-right"><i class="fa fa-lg fa-fw fa-cog"></i></span> <span class="toggle-right friendly_name">
<span class="toggle-right friendly_name" id="newsletter-next_run-${newsletter['id']}">
% if newsletter_handler.NEWSLETTER_SCHED.get_job('newsletter-{}'.format(newsletter['id'])): % if newsletter_handler.NEWSLETTER_SCHED.get_job('newsletter-{}'.format(newsletter['id'])):
<% job = newsletter_handler.NEWSLETTER_SCHED.get_job('newsletter-{}'.format(newsletter['id'])) %> <% job = newsletter_handler.NEWSLETTER_SCHED.get_job('newsletter-{}'.format(newsletter['id'])) %>
<span id="newsletter-next_run-${newsletter['id']}">
<script> <script>
$("#newsletter-next_run-${newsletter['id']}").text(moment("${job.next_run_time}", "YYYY-MM-DD HH:mm:ssZ").fromNow()) $("#newsletter-next_run-${newsletter['id']}").text(
"Next: " + moment("${job.next_run_time}", "YYYY-MM-DD HH:mm:ssZ").fromNow() + " | ")
</script> </script>
</span>
% endif
% if newsletter['last_triggered']:
<% icon, icon_tooltip = ('fa-check', 'Success') if newsletter['last_success'] else ('fa-times', 'Failed') %>
<span id="newsletter-last_triggered-${newsletter['id']}">
<script>
$("#newsletter-last_triggered-${newsletter['id']}").html(
"Last: " + moment("${newsletter['last_triggered']}", "X").fromNow() + ' <i class="fa fa-lg fa-fw ${icon}" data-toggle="tooltip" data-placement="top" title="${icon_tooltip}"></i>'
)
</script>
</span>
% else:
Last: never
<i class="fa fa-lg fa-fw fa-minus"></i>
% endif % endif
</span> </span>
</span> </span>

View file

@ -12,7 +12,7 @@
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button> <button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
<h4 class="modal-title" id="notifier-config-modal-header">${notifier['agent_label']} Settings &nbsp;<small><span class="notifier_id">(Notifier ID: ${notifier['id']})</span></small></h4> <h4 class="modal-title" id="notifier-config-modal-header">${notifier['agent_label']} Settings &nbsp;<small><span class="notifier_id">(Notifier ID: ${notifier['id']}${' - ' + notifier['friendly_name'] if notifier['friendly_name'] else ''})</span></small></h4>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="container-fluid"> <div class="container-fluid">
@ -51,13 +51,13 @@
<div class="col-md-12"> <div class="col-md-12">
% if notifier['agent_name'] == 'scripts' and item['name'] == 'scripts_script_folder': % if notifier['agent_name'] == 'scripts' and item['name'] == 'scripts_script_folder':
<div class="input-group"> <div class="input-group">
<input type="${item['input_type']}" class="form-control" id="${item['name']}" name="${item['name']}" value="${item['value']}" size="30" ${'readonly' if item.get('readonly') else ''}> <input type="${item['input_type']}" class="form-control" id="${item['name']}" name="${item['name']}" value="${item['value']}" size="30" autocomplete="off" ${'readonly' if item.get('readonly') else ''}>
<span class="input-group-btn"> <span class="input-group-btn">
<button class="btn btn-form" type="button" id="${item['name']}_browse" data-toggle="browse" data-filter=".folderonly" data-target="#${item['name']}">Browse</button> <button class="btn btn-form" type="button" id="${item['name']}_browse" data-toggle="browse" data-filter=".folderonly" data-target="#${item['name']}">Browse</button>
</span> </span>
</div> </div>
% else: % else:
<input type="${item['input_type']}" class="form-control" id="${item['name']}" name="${item['name']}" value="${item['value']}" size="30" ${'readonly' if item.get('readonly') else ''}> <input type="${item['input_type']}" class="form-control" id="${item['name']}" name="${item['name']}" value="${item['value']}" size="30" autocomplete="off" ${'readonly' if item.get('readonly') else ''}>
% endif % endif
</div> </div>
</div> </div>
@ -72,7 +72,7 @@
<span class="input-group-btn"> <span class="input-group-btn">
<button class="btn btn-form reveal-token" type="button"><i class="fa fa-eye-slash"></i></button> <button class="btn btn-form reveal-token" type="button"><i class="fa fa-eye-slash"></i></button>
</span> </span>
<input type="password" class="form-control" id="${item['name']}" name="${item['name']}" value="${item['value']}" size="30" ${'readonly' if item.get('readonly') else ''}> <input type="password" class="form-control" id="${item['name']}" name="${item['name']}" value="${item['value']}" size="30" autocomplete="off" ${'readonly' if item.get('readonly') else ''}>
</div> </div>
</div> </div>
</div> </div>
@ -83,7 +83,7 @@
<label for="${item['name']}">${item['label']}</label> <label for="${item['name']}">${item['label']}</label>
<div class="row"> <div class="row">
<div class="col-md-3"> <div class="col-md-3">
<input type="${item['input_type']}" class="form-control" id="${item['name']}" name="${item['name']}" value="${item['value']}" size="30"> <input type="${item['input_type']}" class="form-control" id="${item['name']}" name="${item['name']}" value="${item['value']}" size="30" autocomplete="off">
</div> </div>
</div> </div>
<p class="help-block">${item['description'] | n}</p> <p class="help-block">${item['description'] | n}</p>
@ -101,7 +101,7 @@
% elif item['input_type'] == 'checkbox': % elif item['input_type'] == 'checkbox':
<div class="checkbox"> <div class="checkbox">
<label> <label>
<input type="checkbox" data-id="${item['name']}" class="checkboxes" value="1" ${checked(item['value'])}> ${item['label']} <input type="checkbox" data-id="${item['name']}" class="checkboxes" value="1" autocomplete="off" ${checked(item['value'])}> ${item['label']}
</label> </label>
<p class="help-block">${item['description'] | n}</p> <p class="help-block">${item['description'] | n}</p>
<input type="hidden" id="${item['name']}" name="${item['name']}" value="${item['value']}"> <input type="hidden" id="${item['name']}" name="${item['name']}" value="${item['value']}">
@ -111,7 +111,7 @@
<label for="${item['name']}">${item['label']}</label> <label for="${item['name']}">${item['label']}</label>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<select class="form-control" id="${item['name']}" name="${item['name']}"> <select class="form-control" id="${item['name']}" name="${item['name']}" autocomplete="off">
% for key, value in sorted(item['select_options'].items()): % for key, value in sorted(item['select_options'].items()):
% if isinstance(value, list): % if isinstance(value, list):
<optgroup label="${key}"> <optgroup label="${key}">
@ -133,9 +133,11 @@
<label for="${item['name']}">${item['label']}</label> <label for="${item['name']}">${item['label']}</label>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<select class="form-control" id="${item['name']}" name="${item['name']}"> <select class="form-control" id="${item['name']}" name="${item['name']}" autocomplete="off">
% if item['select_all']:
<option value="select-all">Select All</option> <option value="select-all">Select All</option>
<option value="remove-all">Remove All</option> <option value="remove-all">Remove All</option>
% endif
% if isinstance(item['select_options'], dict): % if isinstance(item['select_options'], dict):
% for section, options in item['select_options'].items(): % for section, options in item['select_options'].items():
<optgroup label="${section}"> <optgroup label="${section}">
@ -145,7 +147,9 @@
</optgroup> </optgroup>
% endfor % endfor
% else: % else:
% if item['select_all']:
<option value="border-all"></option> <option value="border-all"></option>
% endif
% for option in sorted(item['select_options'], key=lambda x: x['text'].lower()): % for option in sorted(item['select_options'], key=lambda x: x['text'].lower()):
<option value="${option['value']}">${option['text']}</option> <option value="${option['value']}">${option['text']}</option>
% endfor % endfor
@ -163,7 +167,7 @@
<label for="friendly_name">Description</label> <label for="friendly_name">Description</label>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<input type="text" class="form-control" id="friendly_name" name="friendly_name" value="${notifier['friendly_name']}" size="30"> <input type="text" class="form-control" id="friendly_name" name="friendly_name" value="${notifier['friendly_name']}" size="30" autocomplete="off">
</div> </div>
</div> </div>
<p class="help-block">Optional: Enter a description to help identify this agent in the notification agents list.</p> <p class="help-block">Optional: Enter a description to help identify this agent in the notification agents list.</p>
@ -181,7 +185,7 @@
% for action in available_notification_actions: % for action in available_notification_actions:
<div class="checkbox"> <div class="checkbox">
<label> <label>
<input type="checkbox" data-id="${action['name']}" class="checkboxes" value="1" ${checked(notifier['actions'][action['name']])}> ${action['label']} <input type="checkbox" data-id="${action['name']}" class="checkboxes" value="1" autocomplete="off" ${checked(notifier['actions'][action['name']])}> ${action['label']}
</label> </label>
<p class="help-block">${action['description'] | n}</p> <p class="help-block">${action['description'] | n}</p>
<input type="hidden" id="${action['name']}" name="${action['name']}" value="${notifier['actions'][action['name']]}"> <input type="hidden" id="${action['name']}" name="${action['name']}" value="${notifier['actions'][action['name']]}">
@ -204,7 +208,7 @@
<div class="form-group"> <div class="form-group">
<label for="custom_conditions_logic">Condition Logic</label> <label for="custom_conditions_logic">Condition Logic</label>
<input type="text" class="form-control" name="custom_conditions_logic" id="custom_conditions_logic" value="${notifier['custom_conditions_logic']}" /> <input type="text" class="form-control" name="custom_conditions_logic" id="custom_conditions_logic" value="${notifier['custom_conditions_logic']}" autocomplete="off" />
<div id="custom_conditions_logic_error" class="alert alert-danger" role="alert" style="padding-top: 5px; padding-bottom: 5px; margin: 0; display: none;"><i class="fa fa-exclamation-triangle" style="color: #a94442;"></i> <span></span></div> <div id="custom_conditions_logic_error" class="alert alert-danger" role="alert" style="padding-top: 5px; padding-bottom: 5px; margin: 0; display: none;"><i class="fa fa-exclamation-triangle" style="color: #a94442;"></i> <span></span></div>
<p class="help-block"> <p class="help-block">
Optional: Enter custom logic to use when evaluating the conditions (e.g. <span class="inline-pre">{1} and ({2} or {3})</span>). Optional: Enter custom logic to use when evaluating the conditions (e.g. <span class="inline-pre">{1} and ({2} or {3})</span>).
@ -250,7 +254,7 @@
<li> <li>
<div class="form-group"> <div class="form-group">
<label for="${action['name']}_subject">Script Arguments</label> <label for="${action['name']}_subject">Script Arguments</label>
<input class="form-control" type="text" id="${action['name']}_subject" name="${action['name']}_subject" value="${notifier['notify_text'][action['name']]['subject']}" data-parsley-trigger="change" required> <input class="form-control" type="text" id="${action['name']}_subject" name="${action['name']}_subject" value="${notifier['notify_text'][action['name']]['subject']}" autocomplete="off" data-parsley-trigger="change" required>
<p class="help-block">Set custom arguments passed to the script.</p> <p class="help-block">Set custom arguments passed to the script.</p>
</div> </div>
<div class="form-group"> <div class="form-group">
@ -276,12 +280,12 @@
<li> <li>
<div class="form-group"> <div class="form-group">
<label for="${action['name']}_subject">JSON Headers</label> <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> <textarea class="form-control" id="${action['name']}_subject" name="${action['name']}_subject" data-parsley-trigger="change" autocomplete="off" data-autoresize required>${notifier['notify_text'][action['name']]['subject']}</textarea>
<p class="help-block">Set custom JSON headers.</p> <p class="help-block">Set custom JSON headers.</p>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="${action['name']}_body">JSON Data</label> <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> <textarea class="form-control" id="${action['name']}_body" name="${action['name']}_body" data-parsley-trigger="change" autocomplete="off" data-autoresize required>${notifier['notify_text'][action['name']]['body']}</textarea>
<p class="help-block">Set custom JSON data.</p> <p class="help-block">Set custom JSON data.</p>
</div> </div>
<div class="form-group"> <div class="form-group">
@ -307,12 +311,12 @@
<li> <li>
<div class="form-group"> <div class="form-group">
<label for="${action['name']}_subject">Subject Line</label> <label for="${action['name']}_subject">Subject Line</label>
<input class="form-control" type="text" id="${action['name']}_subject" name="${action['name']}_subject" value="${notifier['notify_text'][action['name']]['subject']}" data-parsley-trigger="change" required> <input class="form-control" type="text" id="${action['name']}_subject" name="${action['name']}_subject" value="${notifier['notify_text'][action['name']]['subject']}" autocomplete="off" data-parsley-trigger="change" required>
<p class="help-block">Set a custom subject line.</p> <p class="help-block">Set a custom subject line.</p>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="${action['name']}_body">Message Body</label> <label for="${action['name']}_body">Message Body</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> <textarea class="form-control" id="${action['name']}_body" name="${action['name']}_body" data-parsley-trigger="change" autocomplete="off" data-autoresize required>${notifier['notify_text'][action['name']]['body']}</textarea>
<p class="help-block">Set a custom body.</p> <p class="help-block">Set a custom body.</p>
</div> </div>
<div class="form-group"> <div class="form-group">
@ -343,7 +347,7 @@
<label for="test_script">Script</label> <label for="test_script">Script</label>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<select class="form-control" id="test_script" name="test_script"> <select class="form-control" id="test_script" name="test_script" autocomplete="off">
% for key, value in sorted(notifier['config_options'][2]['select_options'].items()): % for key, value in sorted(notifier['config_options'][2]['select_options'].items()):
<option value="${key}">${value}</option> <option value="${key}">${value}</option>
% endfor % endfor
@ -356,7 +360,7 @@
<label for="test_script_args">Script Arguments</label> <label for="test_script_args">Script Arguments</label>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<input class="form-control" type="text" id="test_script_args" name="test_script_args" value=""> <input class="form-control" type="text" id="test_script_args" name="test_script_args" value="" autocomplete="off">
</div> </div>
</div> </div>
<p class="help-block">Set custom arguments passed to the script.</p> <p class="help-block">Set custom arguments passed to the script.</p>
@ -366,7 +370,7 @@
<label for="test_subject">JSON Headers</label> <label for="test_subject">JSON Headers</label>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<textarea class="form-control" id="test_subject" name="test_subject" data-autoresize></textarea> <textarea class="form-control" id="test_subject" name="test_subject" autocomplete="off" data-autoresize></textarea>
</div> </div>
</div> </div>
<p class="help-block">Set custom JSON headers sent to the webhook.</p> <p class="help-block">Set custom JSON headers sent to the webhook.</p>
@ -375,7 +379,7 @@
<label for="test_body">JSON Data</label> <label for="test_body">JSON Data</label>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<textarea class="form-control" id="test_body" name="test_body" data-autoresize></textarea> <textarea class="form-control" id="test_body" name="test_body" autocomplete="off" data-autoresize></textarea>
</div> </div>
</div> </div>
<p class="help-block">Set custom JSON data sent to the webhook.</p> <p class="help-block">Set custom JSON data sent to the webhook.</p>
@ -385,7 +389,7 @@
<label for="test_subject">Subject Line</label> <label for="test_subject">Subject Line</label>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<input class="form-control" type="text" id="test_subject" name="test_subject" value="Tautulli"> <input class="form-control" type="text" id="test_subject" name="test_subject" value="Tautulli" autocomplete="off">
</div> </div>
</div> </div>
<p class="help-block">Set a custom subject line.</p> <p class="help-block">Set a custom subject line.</p>
@ -394,7 +398,7 @@
<label for="test_body">Message Body</label> <label for="test_body">Message Body</label>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<textarea class="form-control" id="test_body" name="test_body" data-autoresize>Test Notification</textarea> <textarea class="form-control" id="test_body" name="test_body" autocomplete="off" data-autoresize>Test Notification</textarea>
</div> </div>
</div> </div>
<p class="help-block">Set a custom body.</p> <p class="help-block">Set a custom body.</p>
@ -719,6 +723,12 @@
pushoverPriority(); pushoverPriority();
}); });
var $pushover_sound = $('#pushover_sound').selectize({
create: true
});
var pushover_sound = $pushover_sound[0].selectize;
pushover_sound.setValue(${json.dumps(next((c['value'] for c in notifier['config_options'] if c['name'] == 'pushover_sound'), [])) | n});
% elif notifier['agent_name'] == 'plexmobileapp': % elif notifier['agent_name'] == 'plexmobileapp':
var $plexmobileapp_user_ids = $('#plexmobileapp_user_ids').selectize({ var $plexmobileapp_user_ids = $('#plexmobileapp_user_ids').selectize({
plugins: ['remove_button'], plugins: ['remove_button'],

View file

@ -19,7 +19,20 @@ DOCUMENTATION :: END
% else: % else:
${notifier['agent_label']} &nbsp;<span class="friendly_name">(${notifier['id']})</span> ${notifier['agent_label']} &nbsp;<span class="friendly_name">(${notifier['id']})</span>
% endif % endif
<span class="toggle-right"><i class="fa fa-lg fa-fw fa-cog"></i></span> <span class="toggle-right friendly_name">
% if notifier['last_triggered']:
<% icon, icon_tooltip = ('fa-check', 'Success') if notifier['last_success'] else ('fa-times', 'Failed') %>
<span id="notifier-last_triggered-${notifier['id']}">
<script>
$("#notifier-last_triggered-${notifier['id']}").html(
moment("${notifier['last_triggered']}", "X").fromNow() + ' <i class="fa fa-lg fa-fw ${icon}" data-toggle="tooltip" data-placement="top" title="${icon_tooltip}"></i>'
)
</script>
</span>
% else:
never
<i class="fa fa-lg fa-fw fa-minus"></i>
% endif
</span> </span>
</li> </li>
% endfor % endfor

View file

@ -36,7 +36,7 @@ DOCUMENTATION :: END
%> %>
% if data: % if data:
<div class="dashboard-recent-media-row"> <div class="dashboard-recent-media-row">
<div id="recently-added-row-scroller" style="left: 0;"> <div id="recently-added-row-scroller">
<ul class="dashboard-recent-media list-unstyled"> <ul class="dashboard-recent-media list-unstyled">
% for item in data: % for item in data:
<div class="dashboard-recent-media-instance"> <div class="dashboard-recent-media-instance">

View file

@ -13,8 +13,6 @@ DOCUMENTATION :: END
import datetime import datetime
import plexpy import plexpy
from plexpy import common, helpers from plexpy import common, helpers
scheduled_jobs = [j.id for j in plexpy.SCHED.get_jobs()]
%> %>
<table class="config-scheduler-table small-muted"> <table class="config-scheduler-table small-muted">
@ -29,16 +27,15 @@ DOCUMENTATION :: END
</thead> </thead>
<tbody> <tbody>
% for job, job_type in common.SCHEDULER_LIST.items(): % for job, job_type in common.SCHEDULER_LIST.items():
% if job in scheduled_jobs:
<% <%
sched_job = plexpy.SCHED.get_job(job) sched_job = plexpy.SCHED.get_job(job)
now = datetime.datetime.now(sched_job.next_run_time.tzinfo)
%> %>
% if sched_job:
<tr> <tr>
<td>${sched_job.id}</td> <td>${sched_job.id}</td>
<td><i class="fa fa-sm fa-fw fa-check"></i> Active</td> <td><i class="fa fa-sm fa-fw fa-check"></i> Active</td>
<td>${helpers.format_timedelta_Hms(sched_job.trigger.interval)}</td> <td>${helpers.format_timedelta_Hms(sched_job.trigger.interval)}</td>
<td>${helpers.format_timedelta_Hms(sched_job.next_run_time - now)}</td> <td>${helpers.format_timedelta_Hms(sched_job.next_run_time - datetime.datetime.now(sched_job.next_run_time.tzinfo))}</td>
<td>${sched_job.next_run_time.astimezone(plexpy.SYS_TIMEZONE).strftime('%Y-%m-%d %H:%M:%S')}</td> <td>${sched_job.next_run_time.astimezone(plexpy.SYS_TIMEZONE).strftime('%Y-%m-%d %H:%M:%S')}</td>
</tr> </tr>
% elif job_type == 'websocket' and plexpy.WS_CONNECTED: % elif job_type == 'websocket' and plexpy.WS_CONNECTED:

View file

@ -132,12 +132,6 @@
</label> </label>
<p class="help-block">Change the "<em>Play by day of week</em>" graph to start on Monday. Default is start on Sunday.</p> <p class="help-block">Change the "<em>Play by day of week</em>" graph to start on Monday. Default is start on Sunday.</p>
</div> </div>
<div class="checkbox advanced-setting">
<label>
<input type="checkbox" id="group_history_tables" name="group_history_tables" value="1" ${config['group_history_tables']}> Group Play History
</label>
<p class="help-block">Group play history for the same item and user as a single entry when progress is less than the watched percent.</p>
</div>
<div class="checkbox advanced-setting"> <div class="checkbox advanced-setting">
<label> <label>
<input type="checkbox" id="history_table_activity" name="history_table_activity" value="1" ${config['history_table_activity']}> Current Activity in History Tables <input type="checkbox" id="history_table_activity" name="history_table_activity" value="1" ${config['history_table_activity']}> Current Activity in History Tables
@ -164,7 +158,7 @@
<input type="checkbox" id="log_blacklist_usernames" name="log_blacklist_usernames" value="1" ${config['log_blacklist_usernames']}> Mask Usernames in Logs <input type="checkbox" id="log_blacklist_usernames" name="log_blacklist_usernames" value="1" ${config['log_blacklist_usernames']}> Mask Usernames in Logs
</label> </label>
<p class="help-block"> <p class="help-block">
Enable to mask Plex usernames and Tautulli friendly names with asterisks (*) in the logs.<br /> Enable to mask Plex usernames with asterisks (*) in the logs.<br />
Note: Only logs from the time this setting is enabled will be masked. Note: Only logs from the time this setting is enabled will be masked.
</p> </p>
</div> </div>
@ -213,6 +207,39 @@
</div> </div>
<p class="help-block">Set the percentage for a music track to be considered as listened. Minimum 50, Maximum 95.</p> <p class="help-block">Set the percentage for a music track to be considered as listened. Minimum 50, Maximum 95.</p>
</div> </div>
<div class="form-group">
<label for="music_watched_percent">Video Watched Completion Behaviour</label>
<div class="row">
<div class="col-md-7">
<select class="form-control" id="watched_marker" name="watched_marker">
<option value="0" ${'selected' if config['watched_marker'] == 0 else ''}>At selected threshold percentage</option>
<option value="1" ${'selected' if config['watched_marker'] == 1 else ''}>At final credits marker position</option>
<option value="2" ${'selected' if config['watched_marker'] == 2 else ''}>At first credits marker position</option>
<option value="3" ${'selected' if config['watched_marker'] == 3 else ''}>Earliest between threshold percent and first credits marker</option>
</select>
</div>
</div>
<p class="help-block">Decide whether to use end credits markers to determine the 'watched' state of video items. When markers are not available the selected threshold percentage will be used.</p>
</div>
<div class="checkbox advanced-setting">
<label>
<input type="checkbox" id="group_history_tables" name="group_history_tables" value="1" ${config['group_history_tables']}> Group Play History
</label>
<p class="help-block">Group play history for the same item and user as a single entry when progress is less than the watched percent.</p>
</div>
<div class="form-group advanced-setting">
<label>Regroup Play History</label>
<p class="help-block">
Fix grouping of play history in the database.<br />
</p>
<div class="row">
<div class="col-md-4">
<div class="btn-group">
<button class="btn btn-form" type="button" id="regroup_history">Regroup</button>
</div>
</div>
</div>
</div>
<div class="form-group advanced-setting"> <div class="form-group advanced-setting">
<label>Flush Temporary Sessions</label> <label>Flush Temporary Sessions</label>
<p class="help-block"> <p class="help-block">
@ -649,27 +676,6 @@
</div> </div>
</div> </div>
<div class="checkbox advanced-setting">
<label>
<input type="checkbox" name="anon_redirect_dynamic" id="anon_redirect_dynamic" value="1" ${config['anon_redirect_dynamic']} /> Use Dynamic Anonymous Redirect Service
</label>
<p class="help-block">
Allow Tautulli to use the dynamic anonymous redirect service listed <a href="${anon_url('https://tautulli.com/anonymizer.txt')}" target="_blank" rel="noreferrer">here</a>.
Disable to specify your own service.
</p>
</div>
<div id="anon_redirect_options">
<div class="form-group advanced-setting">
<label for="anon_redirect">Anonymous Redirect Service</label>
<div class="row">
<div class="col-md-4">
<input type="text" class="form-control" id="anon_redirect" name="anon_redirect" value="${config['anon_redirect']}" size="30">
</div>
</div>
<p class="help-block">Backlink protection via anonymizer service, must end in "?". Leave blank to disable.</p>
</div>
</div>
<div class="padded-header"> <div class="padded-header">
<h3>Authentication</h3> <h3>Authentication</h3>
</div> </div>
@ -761,7 +767,6 @@
data-identifier="${config['pms_identifier']}" data-identifier="${config['pms_identifier']}"
data-ip="${config['pms_ip']}" data-ip="${config['pms_ip']}"
data-port="${config['pms_port']}" data-port="${config['pms_port']}"
data-local="${int(not int(config['pms_is_remote']))}"
data-ssl="${config['pms_ssl']}" data-ssl="${config['pms_ssl']}"
data-is_cloud="${config['pms_is_cloud']}" data-is_cloud="${config['pms_is_cloud']}"
data-label="${config['pms_name'] or 'Local'}" data-label="${config['pms_name'] or 'Local'}"
@ -794,13 +799,6 @@
</label> </label>
<p class="help-block">Connect to your Plex server using HTTPS if you have <a href="${anon_url('https://support.plex.tv/articles/206225077-how-to-use-secure-server-connections')}" target="_blank" rel="noreferrer">secure connections</a> enabled.</p> <p class="help-block">Connect to your Plex server using HTTPS if you have <a href="${anon_url('https://support.plex.tv/articles/206225077-how-to-use-secure-server-connections')}" target="_blank" rel="noreferrer">secure connections</a> enabled.</p>
</div> </div>
<div class="checkbox">
<label>
<input type="checkbox" id="pms_is_remote_checkbox" class="checkbox-toggle pms-settings" data-id="pms_is_remote" value="1" ${checked(config['pms_is_remote'])}> Remote Server
<input type="hidden" id="pms_is_remote" name="pms_is_remote" value="${config['pms_is_remote']}">
</label>
<p class="help-block">Check this if your Plex Server is not on the same local network as Tautulli.</p>
</div>
<div class="form-group"> <div class="form-group">
<label for="pms_url">Plex Server URL</label> <label for="pms_url">Plex Server URL</label>
<div class="row"> <div class="row">
@ -1251,7 +1249,7 @@
</div> </div>
<p class="help-block"> <p class="help-block">
Add a new notification agent, or configure an existing notification agent by clicking the settings icon on the right. Add a new notification agent, or configure an existing notification agent by clicking on the item below.
</p> </p>
<p class="help-block"> <p class="help-block">
Please see the <a href="${anon_url('https://github.com/%s/%s/wiki/Notification-Agents-Guide' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO))}" target="_blank" rel="noreferrer">Notification Agents Guide</a> for instructions on setting up each notification agent. Please see the <a href="${anon_url('https://github.com/%s/%s/wiki/Notification-Agents-Guide' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO))}" target="_blank" rel="noreferrer">Notification Agents Guide</a> for instructions on setting up each notification agent.
@ -1271,7 +1269,7 @@
</div> </div>
<p class="help-block"> <p class="help-block">
Add a new newsletter agent, or configure an existing newsletter agent by clicking the settings icon on the right. Add a new newsletter agent, or configure an existing newsletter agent by clicking on the item below.
</p> </p>
<p class="help-block settings-warning" id="newsletter_upload_warning"> <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> 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>
@ -1582,7 +1580,7 @@
Get the Tautulli Remote app on Get the Tautulli Remote app on
<a href="${anon_url('https://play.google.com/store/apps/details?id=com.tautulli.tautulli_remote')}" target="_blank" rel="noreferrer">Google Play</a> <a href="${anon_url('https://play.google.com/store/apps/details?id=com.tautulli.tautulli_remote')}" target="_blank" rel="noreferrer">Google Play</a>
or the or the
<a href="${anon_url('https://apps.apple.com/us/app/tautulli-remote/id1570909086')}" target="_blank" rel="noreferrer">App Store</a> <a href="${anon_url('https://apps.apple.com/us/app/tautulli-remote/id1570909086?platform=iphone')}" target="_blank" rel="noreferrer">App Store</a>
to access Tautulli from your mobile device. to access Tautulli from your mobile device.
<br /> <br />
</p> </p>
@ -1591,7 +1589,7 @@
<a href="${anon_url('https://play.google.com/store/apps/details?id=com.tautulli.tautulli_remote')}" target="_blank" rel="noreferrer"><img alt="Get it on Google Play" src="images/google-play-badge.svg" /></a> <a href="${anon_url('https://play.google.com/store/apps/details?id=com.tautulli.tautulli_remote')}" target="_blank" rel="noreferrer"><img alt="Get it on Google Play" src="images/google-play-badge.svg" /></a>
</span> </span>
<span class="app-badge"> <span class="app-badge">
<a href="${anon_url('https://apps.apple.com/us/app/tautulli-remote/id1570909086')}" target="_blank" rel="noreferrer"><img alt="Download on the App Store" src="images/app-store-badge.svg" /></a> <a href="${anon_url('https://apps.apple.com/us/app/tautulli-remote/id1570909086?platform=iphone')}" target="_blank" rel="noreferrer"><img alt="Download on the App Store" src="images/app-store-badge.svg" /></a>
</span> </span>
<span class="app-badge"> <span class="app-badge">
<small>Google Play and the Google Play logo are trademarks of Google LLC.</small> <small>Google Play and the Google Play logo are trademarks of Google LLC.</small>
@ -1603,7 +1601,7 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label>Registered Devices</label> <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 class="help-block">Register a new device using a QR code, or configure an existing device by clicking on the item below.</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> <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>
<br /> <br />
<div class="row"> <div class="row">
@ -1896,6 +1894,21 @@
<p><strong style="color: #fff;">Example:</strong></p> <p><strong style="color: #fff;">Example:</strong></p>
<pre>{media_type} --> movie <pre>{media_type} --> movie
{media_type!c} --> Movie</pre> {media_type!c} --> Movie</pre>
</div>
<div>
<h4>Time Formats</h4>
</div>
<div style="padding-bottom: 10px;">
<p class="help-block">
Notification parameters which are "in date format" or "in time format" can be formatted using the
<a href="javascript:void(0)" data-target="#dateTimeOptionsModal" data-toggle="modal">Date & Time Format Options</a>
by adding a <span class="inline-pre">:format</span> specifier.
If no format is specified, the default Date Format and Time Format under Settings > General will be used.
</p>
<p><strong style="color: #fff;">Example:</strong></p>
<pre>{started_datestamp:ddd, MMMM DD, YYYY} --> Mon, December 25, 2023
{stopped_timestamp:h:mm a} --> 9:56 pm
{duration_time:HH:mm:ss} --> 01:42:20</pre>
</div> </div>
<div> <div>
<h4>List Slicing</h4> <h4>List Slicing</h4>
@ -1967,7 +1980,7 @@ Rating: {rating}/10 --> Rating: /10
<li>Evaluation</li> <li>Evaluation</li>
<li>Parameter</li> <li>Parameter</li>
<li>Case Modifier</li> <li>Case Modifier</li>
<li>List Slicing</li> <li>Time Formats / List Slicing</li>
<li>Suffix</li> <li>Suffix</li>
</ol> </ol>
<p><strong style="color: #fff;">Example:</strong></p> <p><strong style="color: #fff;">Example:</strong></p>
@ -2142,7 +2155,6 @@ Rating: {rating}/10 --> Rating: /10
<script src="${http_root}js/parsley.min.js"></script> <script src="${http_root}js/parsley.min.js"></script>
<script src="${http_root}js/Sortable.min.js"></script> <script src="${http_root}js/Sortable.min.js"></script>
<script src="${http_root}js/jquery.inputaffix.min.js"></script> <script src="${http_root}js/jquery.inputaffix.min.js"></script>
<script src="${http_root}js/kjua.min.js"></script>
<script> <script>
function getConfigurationTable() { function getConfigurationTable() {
$.ajax({ $.ajax({
@ -2374,7 +2386,6 @@ $(document).ready(function() {
initConfigCheckbox('#api_enabled'); initConfigCheckbox('#api_enabled');
initConfigCheckbox('#enable_https'); initConfigCheckbox('#enable_https');
initConfigCheckbox('#anon_redirect_dynamic', null, true);
initConfigCheckbox('#https_create_cert'); initConfigCheckbox('#https_create_cert');
initConfigCheckbox('#check_github'); initConfigCheckbox('#check_github');
initConfigCheckbox('#monitor_pms_updates'); initConfigCheckbox('#monitor_pms_updates');
@ -2470,6 +2481,12 @@ $(document).ready(function() {
confirmAjaxCall(url, msg); confirmAjaxCall(url, msg);
}); });
$("#regroup_history").click(function () {
var msg = 'Are you sure you want to regroup play history in the database?<br /><br /><strong>This make take a long time for large databases.<br />Regrouping will continue in the background.</strong>';
var url = 'regroup_history';
confirmAjaxCall(url, msg);
});
$("#delete_temp_sessions").click(function () { $("#delete_temp_sessions").click(function () {
var msg = 'Are you sure you want to flush the temporary sessions?<br /><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'; var url = 'delete_temp_sessions';
@ -2571,7 +2588,6 @@ $(document).ready(function() {
return '<div data-identifier="' + item.clientIdentifier + return '<div data-identifier="' + item.clientIdentifier +
'" data-ip="' + item.ip + '" data-ip="' + item.ip +
'" data-port="' + item.port + '" data-port="' + item.port +
'" data-local="' + item.local +
'" data-ssl="' + item.httpsRequired + '" data-ssl="' + item.httpsRequired +
'" data-is_cloud="' + item.is_cloud + '" data-is_cloud="' + item.is_cloud +
'" data-label="' + item.label + '">' + '" data-label="' + item.label + '">' +
@ -2585,7 +2601,6 @@ $(document).ready(function() {
return '<div data-identifier="' + item.clientIdentifier + return '<div data-identifier="' + item.clientIdentifier +
'" data-ip="' + item.ip + '" data-ip="' + item.ip +
'" data-port="' + item.port + '" data-port="' + item.port +
'" data-local="' + item.local +
'" data-ssl="' + item.httpsRequired + '" data-ssl="' + item.httpsRequired +
'" data-is_cloud="' + item.is_cloud + '" data-is_cloud="' + item.is_cloud +
'" data-label="' + item.label + '">' + '" data-label="' + item.label + '">' +
@ -2608,7 +2623,6 @@ $(document).ready(function() {
var identifier = $(pms_ip_selected).data('identifier'); var identifier = $(pms_ip_selected).data('identifier');
var ip = $(pms_ip_selected).data('ip'); var ip = $(pms_ip_selected).data('ip');
var port = $(pms_ip_selected).data('port'); var port = $(pms_ip_selected).data('port');
var local = $(pms_ip_selected).data('local');
var ssl = $(pms_ip_selected).data('ssl'); var ssl = $(pms_ip_selected).data('ssl');
var is_cloud = $(pms_ip_selected).data('is_cloud'); var is_cloud = $(pms_ip_selected).data('is_cloud');
var value = $(pms_ip_selected).data('value'); var value = $(pms_ip_selected).data('value');
@ -2616,8 +2630,6 @@ $(document).ready(function() {
$("#pms_identifier").val(identifier !== 'undefined' ? identifier : ''); $("#pms_identifier").val(identifier !== 'undefined' ? identifier : '');
$('#pms_ip').val(ip !== 'undefined' ? ip : value); $('#pms_ip').val(ip !== 'undefined' ? ip : value);
$('#pms_port').val(port !== 'undefined' ? port : 32400); $('#pms_port').val(port !== 'undefined' ? port : 32400);
$('#pms_is_remote_checkbox').prop('checked', (local !== 'undefined' && local === 0));
$('#pms_is_remote').val(local !== 'undefined' && local === 0 ? 1 : 0);
$('#pms_ssl_checkbox').prop('checked', (ssl !== 'undefined' && ssl === 1)); $('#pms_ssl_checkbox').prop('checked', (ssl !== 'undefined' && ssl === 1));
$('#pms_ssl').val(ssl !== 'undefined' && ssl === 1 ? 1 : 0); $('#pms_ssl').val(ssl !== 'undefined' && ssl === 1 ? 1 : 0);
$('#pms_is_cloud').val(is_cloud !== 'undefined' && is_cloud === true ? 1 : 0); $('#pms_is_cloud').val(is_cloud !== 'undefined' && is_cloud === true ? 1 : 0);
@ -2655,7 +2667,6 @@ $(document).ready(function() {
var pms_port = $("#pms_port").val(); var pms_port = $("#pms_port").val();
var pms_identifier = $("#pms_identifier").val(); var pms_identifier = $("#pms_identifier").val();
var pms_ssl = $("#pms_ssl").val(); var pms_ssl = $("#pms_ssl").val();
var pms_is_remote = $("#pms_is_remote").val();
var pms_url_manual = $("#pms_url_manual").is(':checked') ? 1 : 0; var pms_url_manual = $("#pms_url_manual").is(':checked') ? 1 : 0;
if (($("#pms_ip").val() !== '') || ($("#pms_port").val() !== '')) { if (($("#pms_ip").val() !== '') || ($("#pms_port").val() !== '')) {
@ -2667,7 +2678,6 @@ $(document).ready(function() {
hostname: pms_ip, hostname: pms_ip,
port: pms_port, port: pms_port,
ssl: pms_ssl, ssl: pms_ssl,
remote: pms_is_remote,
manual: pms_url_manual, manual: pms_url_manual,
get_url: true, get_url: true,
test_websocket: true test_websocket: true
@ -2757,10 +2767,10 @@ $(document).ready(function() {
function OAuthSuccessCallback(authToken) { function OAuthSuccessCallback(authToken) {
$.post('save_pms_token', { token: authToken, client_id: $('#pms_client_id').val() }, function() { $.post('save_pms_token', { token: authToken, client_id: $('#pms_client_id').val() }, function() {
showMsg('<i class="fa fa-check"></i> Saved new Plex.tv token.', false, true, 5000); showMsg('<i class="fa fa-check"></i> Saved new Plex.tv token.', false, true, 5000);
getServerOptions();
}); });
$("#pms-token-status").html('<i class="fa fa-check"></i>&nbsp; Authentication successful.').fadeIn('fast'); $("#pms-token-status").html('<i class="fa fa-check"></i>&nbsp; Authentication successful.').fadeIn('fast');
$("#token_error_bar").hide(); $("#token_error_bar").hide();
getServerOptions(authToken);
} }
function OAuthErrorCallback() { function OAuthErrorCallback() {
$("#pms-token-status").html('<i class="fa fa-exclamation-circle"></i>&nbsp; Error communicating with Plex.tv.').fadeIn('fast'); $("#pms-token-status").html('<i class="fa fa-exclamation-circle"></i>&nbsp; Error communicating with Plex.tv.').fadeIn('fast');

View file

@ -68,14 +68,14 @@ DOCUMENTATION :: END
<table class="stream-info" style="margin-top: 0;"> <table class="stream-info" style="margin-top: 0;">
<thead> <thead>
<tr> <tr>
<th> <th></th>
</th>
<th class="heading">
Stream Details
</th>
<th class="heading"> <th class="heading">
Source Details Source Details
</th> </th>
<th><i class="fa fa-long-arrow-right"></i></th>
<th class="heading">
Stream Details
</th>
</tr> </tr>
</thead> </thead>
</table> </table>
@ -85,38 +85,46 @@ DOCUMENTATION :: END
<th> <th>
Media Media
</th> </th>
<th></th>
<th></th>
<th></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td>Bitrate</td> <td>Bitrate</td>
<td>${data['stream_bitrate']} ${'kbps' if data['stream_bitrate'] else ''}</td>
<td>${data['bitrate']} ${'kbps' if data['bitrate'] else ''}</td> <td>${data['bitrate']} ${'kbps' if data['bitrate'] else ''}</td>
<td><i class="fa fa-long-arrow-right"></i></td>
<td>${data['stream_bitrate']} ${'kbps' if data['stream_bitrate'] else ''}</td>
</tr> </tr>
% if data['media_type'] != 'track': % if data['media_type'] != 'track':
<tr> <tr>
<td>Resolution</td> <td>Resolution</td>
<td>${data['stream_video_full_resolution']}</td>
<td>${data['video_full_resolution']}</td> <td>${data['video_full_resolution']}</td>
<td><i class="fa fa-long-arrow-right"></i></td>
<td>${data['stream_video_full_resolution']}</td>
</tr> </tr>
% endif % endif
<tr> <tr>
<td>Quality</td> <td>Quality</td>
<td>${data['quality_profile']}</td>
<td>-</td> <td>-</td>
<td></td>
<td>${data['quality_profile']}</td>
</tr> </tr>
% if data['optimized_version'] == 1: % if data['optimized_version'] == 1:
<tr> <tr>
<td>Optimized Version</td> <td>Optimized Version</td>
<td>-</td>
<td>${data['optimized_version_profile']}<br>(${data['optimized_version_title']})</td> <td>${data['optimized_version_profile']}<br>(${data['optimized_version_title']})</td>
<td></td>
<td>-</td>
</tr> </tr>
% endif % endif
% if data['synced_version'] == 1: % if data['synced_version'] == 1:
<tr> <tr>
<td>Synced Version</td> <td>Synced Version</td>
<td>-</td>
<td>${data['synced_version_profile']}</td> <td>${data['synced_version_profile']}</td>
<td></td>
<td>-</td>
</tr> </tr>
% endif % endif
</tbody> </tbody>
@ -127,6 +135,8 @@ DOCUMENTATION :: END
<th> <th>
Container Container
</th> </th>
<th></th>
<th></th>
<th> <th>
${data['stream_container_decision']} ${data['stream_container_decision']}
</th> </th>
@ -135,8 +145,9 @@ DOCUMENTATION :: END
<tbody> <tbody>
<tr> <tr>
<td>Container</td> <td>Container</td>
<td>${data['stream_container'].upper()}</td>
<td>${data['container'].upper()}</td> <td>${data['container'].upper()}</td>
<td><i class="fa fa-long-arrow-right"></i></td>
<td>${data['stream_container'].upper()}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@ -147,6 +158,8 @@ DOCUMENTATION :: END
<th> <th>
Video Video
</th> </th>
<th></th>
<th></th>
<th> <th>
${data['stream_video_decision']} ${data['stream_video_decision']}
</th> </th>
@ -155,38 +168,45 @@ DOCUMENTATION :: END
<tbody> <tbody>
<tr> <tr>
<td>Codec</td> <td>Codec</td>
<td>${data['stream_video_codec'].upper()} ${'(HW)' if data['transcode_hw_encoding'] else ''}</td>
<td>${data['video_codec'].upper()} ${'(HW)' if data['transcode_hw_decoding'] else ''}</td> <td>${data['video_codec'].upper()} ${'(HW)' if data['transcode_hw_decoding'] else ''}</td>
<td><i class="fa fa-long-arrow-right"></i></td>
<td>${data['stream_video_codec'].upper()} ${'(HW)' if data['transcode_hw_encoding'] else ''}</td>
</tr> </tr>
<tr> <tr>
<td>Bitrate</td> <td>Bitrate</td>
<td>${data['stream_video_bitrate']} ${'kbps' if data['stream_video_bitrate'] else ''}</td>
<td>${data['video_bitrate']} ${'kbps' if data['video_bitrate'] else ''}</td> <td>${data['video_bitrate']} ${'kbps' if data['video_bitrate'] else ''}</td>
<td><i class="fa fa-long-arrow-right"></i></td>
<td>${data['stream_video_bitrate']} ${'kbps' if data['stream_video_bitrate'] else ''}</td>
</tr> </tr>
<tr> <tr>
<td>Width</td> <td>Width</td>
<td>${data['stream_video_width']}</td>
<td>${data['video_width']}</td> <td>${data['video_width']}</td>
<td><i class="fa fa-long-arrow-right"></i></td>
<td>${data['stream_video_width']}</td>
</tr> </tr>
<tr> <tr>
<td>Height</td> <td>Height</td>
<td>${data['stream_video_height']}</td>
<td>${data['video_height']}</td> <td>${data['video_height']}</td>
<td><i class="fa fa-long-arrow-right"></i></td>
<td>${data['stream_video_height']}</td>
</tr> </tr>
<tr> <tr>
<td>Framerate</td> <td>Framerate</td>
<td>${data['stream_video_framerate']}</td>
<td>${data['video_framerate']}</td> <td>${data['video_framerate']}</td>
<td><i class="fa fa-long-arrow-right"></i></td>
<td>${data['stream_video_framerate']}</td>
</tr> </tr>
<tr> <tr>
<td>Dynamic Range</td> <td>Dynamic Range</td>
<td>${data['stream_video_dynamic_range']}</td>
<td>${data['video_dynamic_range']}</td> <td>${data['video_dynamic_range']}</td>
<td><i class="fa fa-long-arrow-right"></i></td>
<td>${data['stream_video_dynamic_range']}</td>
</tr> </tr>
<tr> <tr>
<td>Aspect Ratio</td> <td>Aspect Ratio</td>
<td>-</td>
<td>${data['aspect_ratio']}</td> <td>${data['aspect_ratio']}</td>
<td></td>
<td>-</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@ -197,6 +217,8 @@ DOCUMENTATION :: END
<th> <th>
Audio Audio
</th> </th>
<th></th>
<th></th>
<th> <th>
${data['stream_audio_decision']} ${data['stream_audio_decision']}
</th> </th>
@ -205,26 +227,29 @@ DOCUMENTATION :: END
<tbody> <tbody>
<tr> <tr>
<td>Codec</td> <td>Codec</td>
<td>${AUDIO_CODEC_OVERRIDES.get(data['stream_audio_codec'], data['stream_audio_codec'].upper())}</td>
<td>${AUDIO_CODEC_OVERRIDES.get(data['audio_codec'], data['audio_codec'].upper())}</td> <td>${AUDIO_CODEC_OVERRIDES.get(data['audio_codec'], data['audio_codec'].upper())}</td>
<td><i class="fa fa-long-arrow-right"></i></td>
<td>${AUDIO_CODEC_OVERRIDES.get(data['stream_audio_codec'], data['stream_audio_codec'].upper())}</td>
</tr> </tr>
<tr> <tr>
<td>Bitrate</td> <td>Bitrate</td>
<td>${data['stream_audio_bitrate']} ${'kbps' if data['stream_audio_bitrate'] else ''}</td>
<td>${data['audio_bitrate']} ${'kbps' if data['audio_bitrate'] else ''}</td> <td>${data['audio_bitrate']} ${'kbps' if data['audio_bitrate'] else ''}</td>
<td><i class="fa fa-long-arrow-right"></i></td>
<td>${data['stream_audio_bitrate']} ${'kbps' if data['stream_audio_bitrate'] else ''}</td>
</tr> </tr>
<tr> <tr>
<td>Channels</td> <td>Channels</td>
<td>${data['stream_audio_channels']}</td>
<td>${data['audio_channels']}</td> <td>${data['audio_channels']}</td>
<td><i class="fa fa-long-arrow-right"></i></td>
<td>${data['stream_audio_channels']}</td>
</tr> </tr>
% if data['stream_audio_language'] != '':
<tr> <tr>
<td>Language</td> <td>Language</td>
<td>${data['stream_audio_language']}</td> <td>${data['audio_language'] or 'Unknown'}</td>
<td>${data['audio_language']}</td> <td></td>
<td>-</td>
</tr> </tr>
% endif
</tbody> </tbody>
</table> </table>
% if data['subtitles'] == 1: % if data['subtitles'] == 1:
@ -234,17 +259,34 @@ DOCUMENTATION :: END
<th> <th>
Subtitles Subtitles
</th> </th>
<th></th>
<th></th>
<th> <th>
${data['stream_subtitle_decision']} ${'direct play' if data['stream_subtitle_decision'] not in ('transcode', 'copy', 'burn') else data['stream_subtitle_decision']}
</th> </th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td>Codec</td> <td>Codec</td>
<td>${data['stream_subtitle_codec'].upper() or '-'}</td>
<td>${data['subtitle_codec'].upper()}</td> <td>${data['subtitle_codec'].upper()}</td>
<td><i class="fa fa-long-arrow-right"></i></td>
<td>${data['stream_subtitle_codec'].upper() or '-'}</td>
</tr> </tr>
<tr>
<td>Language</td>
<td>${data['subtitle_language'] or 'Unknown'}</td>
<td></td>
<td>-</td>
</tr>
% if data['subtitle_forced']:
<tr>
<td>Forced</td>
<td>${bool(data['subtitle_forced'])}</td>
<td></td>
<td>-</td>
</tr>
% endif
</tbody> </tbody>
</table> </table>
% endif % endif

View file

@ -30,7 +30,7 @@
<div class="iframe-container"> <div class="iframe-container">
<iframe class="iframe" allowfullscreen="true" id="support-iframe" data-name="Tautulli-Support" data-src="https://support.tautulli.com" <iframe class="iframe" allowfullscreen="true" id="support-iframe" data-name="Tautulli-Support" data-src="https://support.tautulli.com"
sandbox="allow-presentation allow-forms allow-same-origin allow-pointer-lock allow-scripts allow-popups allow-modals allow-top-navigation" sandbox="allow-presentation allow-forms allow-same-origin allow-pointer-lock allow-scripts allow-popups allow-modals allow-top-navigation"
style="display: none;"> style="display: none;" rel="noreferrer">
</iframe> </iframe>
<div class="iframe-overlay"> <div class="iframe-overlay">
<div class="iframe-button-container"> <div class="iframe-button-container">

View file

@ -76,7 +76,6 @@ DOCUMENTATION :: END
% if _session['user_group'] == 'admin': % if _session['user_group'] == 'admin':
<li><a id="nav-tabs-export" href="#tabs-export" role="tab" data-toggle="tab">Export</a></li> <li><a id="nav-tabs-export" href="#tabs-export" role="tab" data-toggle="tab">Export</a></li>
% endif % endif
<li><a id="nav-tabs-synceditems" href="#tabs-synceditems" role="tab" data-toggle="tab">Synced Items</a></li>
<li><a id="nav-tabs-ipaddresses" href="#tabs-ipaddresses" role="tab" data-toggle="tab">IP Addresses</a></li> <li><a id="nav-tabs-ipaddresses" href="#tabs-ipaddresses" role="tab" data-toggle="tab">IP Addresses</a></li>
<li><a id="nav-tabs-tautullilogins" href="#tabs-tautullilogins" role="tab" data-toggle="tab">Tautulli Logins</a></li> <li><a id="nav-tabs-tautullilogins" href="#tabs-tautullilogins" role="tab" data-toggle="tab">Tautulli Logins</a></li>
</ul> </ul>
@ -126,10 +125,10 @@ DOCUMENTATION :: END
<div class="table-card-header"> <div class="table-card-header">
<ul class="nav nav-header nav-dashboard pull-right"> <ul class="nav nav-header nav-dashboard pull-right">
<li> <li>
<a href="#" id="recently-watched-page-left" class="paginate btn-gray disabled" data-id="+1"><i class="fa fa-lg fa-chevron-left"></i></a> <a href="#" id="recently-watched-page-left" class="paginate-watched btn-gray disabled" data-id="-1"><i class="fa fa-lg fa-chevron-left"></i></a>
</li> </li>
<li> <li>
<a href="#" id="recently-watched-page-right" class="paginate btn-gray" data-id="-1"><i class="fa fa-lg fa-chevron-right"></i></a> <a href="#" id="recently-watched-page-right" class="paginate-watched btn-gray" data-id="+1"><i class="fa fa-lg fa-chevron-right"></i></a>
</li> </li>
</ul> </ul>
<div class="header-bar"> <div class="header-bar">
@ -167,27 +166,27 @@ DOCUMENTATION :: END
</div> </div>
% endif % endif
<div class="btn-group" data-toggle="buttons" id="media_type-selection"> <div class="btn-group" data-toggle="buttons" id="media_type-selection">
<label class="btn btn-dark"> <label class="btn btn-dark btn-filter">
<input type="checkbox" name="media_type-filter" id="history-media_type-movie" value="movie" autocomplete="off"><i class="fa fa-film"></i> Movies <input type="checkbox" name="media_type-filter" id="history-media_type-movie" value="movie" autocomplete="off"><i class="fa fa-film"></i> Movies
</label> </label>
<label class="btn btn-dark"> <label class="btn btn-dark btn-filter">
<input type="checkbox" name="media_type-filter" id="history-media_type-episode" value="episode" autocomplete="off"><i class="fa fa-television"></i> TV Shows <input type="checkbox" name="media_type-filter" id="history-media_type-episode" value="episode" autocomplete="off"><i class="fa fa-television"></i> TV Shows
</label> </label>
<label class="btn btn-dark"> <label class="btn btn-dark btn-filter">
<input type="checkbox" name="media_type-filter" id="history-media_type-track" value="track" autocomplete="off"><i class="fa fa-music"></i> Music <input type="checkbox" name="media_type-filter" id="history-media_type-track" value="track" autocomplete="off"><i class="fa fa-music"></i> Music
</label> </label>
<label class="btn btn-dark"> <label class="btn btn-dark btn-filter">
<input type="checkbox" name="media_type-filter" id="history-media_type-live" value="live" autocomplete="off"><i class="fa fa-broadcast-tower"></i> Live TV <input type="checkbox" name="media_type-filter" id="history-media_type-live" value="live" autocomplete="off"><i class="fa fa-broadcast-tower"></i> Live TV
</label> </label>
</div> </div>
<div class="btn-group" data-toggle="buttons" id="transcode_decision-selection"> <div class="btn-group" data-toggle="buttons" id="transcode_decision-selection">
<label class="btn btn-dark"> <label class="btn btn-dark btn-filter">
<input type="checkbox" name="transcode_decision-filter" id="history-transcode_decision-direct_play" value="direct play" autocomplete="off"><i class="fa fa-play-circle"></i> Direct Play <input type="checkbox" name="transcode_decision-filter" id="history-transcode_decision-direct_play" value="direct play" autocomplete="off"><i class="fa fa-play-circle"></i> Direct Play
</label> </label>
<label class="btn btn-dark"> <label class="btn btn-dark btn-filter">
<input type="checkbox" name="transcode_decision-filter" id="history-transcode_decision-direct_stream" value="copy" autocomplete="off"><i class="fa fa-stream"></i> Direct Stream <input type="checkbox" name="transcode_decision-filter" id="history-transcode_decision-direct_stream" value="copy" autocomplete="off"><i class="fa fa-stream"></i> Direct Stream
</label> </label>
<label class="btn btn-dark"> <label class="btn btn-dark btn-filter">
<input type="checkbox" name="transcode_decision-filter" id="history-transcode_decision-transcode" value="transcode" autocomplete="off"><i class="fa fa-server"></i> Transcode <input type="checkbox" name="transcode_decision-filter" id="history-transcode_decision-transcode" value="transcode" autocomplete="off"><i class="fa fa-server"></i> Transcode
</label> </label>
</div> </div>
@ -212,7 +211,7 @@ DOCUMENTATION :: END
<th align="left" id="started">Started</th> <th align="left" id="started">Started</th>
<th align="left" id="paused_counter">Paused</th> <th align="left" id="paused_counter">Paused</th>
<th align="left" id="stopped">Stopped</th> <th align="left" id="stopped">Stopped</th>
<th align="left" id="duration">Duration</th> <th align="left" id="play_duration">Duration</th>
<th align="left" id="percent_complete"></th> <th align="left" id="percent_complete"></th>
</tr> </tr>
</thead> </thead>
@ -316,57 +315,6 @@ DOCUMENTATION :: END
</div> </div>
</div> </div>
% endif % endif
<div role="tabpanel" class="tab-pane" id="tabs-synceditems">
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
<div class='table-card-header'>
<div class="header-bar">
<span>
<i class="fa fa-cloud-download"></i> Synced Items for <strong>
<span class="set-username">${data['friendly_name']}</span>
</strong>
</span>
</div>
<div class="button-bar">
% if _session['user_group'] == 'admin':
<div class="btn-group">
<button class="btn btn-danger btn-edit" data-toggle="button" aria-pressed="false" autocomplete="off" id="sync-row-edit-mode">
<i class="fa fa-trash-o"></i> Delete mode
</button>
</div>
% endif
<div class="btn-group">
<button class="btn btn-dark refresh-syncs-button" id="refresh-syncs-list"><i class="fa fa-refresh"></i> Refresh synced items</button>
</div>
<div class="btn-group colvis-button-bar" id="button-bar-sync"></div>
</div>
</div>
<div class="table-card-back">
<table class="display sync_table" id="sync_table-UID-${data['user_id']}" width="100%">
<thead>
<tr>
<th align="left" id="delete_row">Delete</th>
<th align="left" id="state">State</th>
<th align="left" id="username">Username</th>
<th align="left" id="sync_title">Title</th>
<th align="left" id="type">Type</th>
<th align="left" id="sync_platform">Platform</th>
<th align="left" id="device">Device</th>
<th align="left" id="size">Total Size</th>
<th align="left" id="items">Total Items</th>
<th align="left" id="converted">Converted</th>
<th align="left" id="downloaded">Downloaded</th>
<th align="left" id="sync_percent_complete">Complete</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="tabs-ipaddresses"> <div role="tabpanel" class="tab-pane" id="tabs-ipaddresses">
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
@ -642,12 +590,6 @@ DOCUMENTATION :: END
clearSearchButton('sync_table-UID-${data["user_id"]}', sync_table); clearSearchButton('sync_table-UID-${data["user_id"]}', sync_table);
} }
$('#nav-tabs-synceditems').on('shown.bs.tab', function() {
if (typeof(sync_table) === 'undefined') {
loadSyncTable(user_id);
}
});
$("#refresh-syncs-list").click(function() { $("#refresh-syncs-list").click(function() {
sync_table.ajax.reload(); sync_table.ajax.reload();
}); });
@ -724,52 +666,14 @@ DOCUMENTATION :: END
}, },
complete: function(xhr, status) { complete: function(xhr, status) {
$("#user-recently-watched").html(xhr.responseText); $("#user-recently-watched").html(xhr.responseText);
highlightWatchedScrollerButton(); highlightScrollerButton("#recently-watched");
paginateScroller("#recently-watched", ".paginate-watched");
} }
}); });
} }
recentlyWatched(); recentlyWatched();
function highlightWatchedScrollerButton() {
var scroller = $("#recently-watched-row-scroller");
var numElems = scroller.find("li").length;
scroller.width(numElems * 175);
if (scroller.width() > $("#user-recently-watched").width()) {
$("#recently-watched-page-right").removeClass("disabled");
} else {
$("#recently-watched-page-right").addClass("disabled");
}
}
$(window).resize(function() {
highlightWatchedScrollerButton();
});
var leftTotal = 0;
$(".paginate").click(function (e) {
e.preventDefault();
var scroller = $("#recently-watched-row-scroller");
var containerWidth = $("#user-recently-watched").width();
var scrollAmount = $(this).data("id") * parseInt(containerWidth / 175) * 175;
var leftMax = Math.min(-parseInt(scroller.width()) + Math.abs(scrollAmount), 0);
leftTotal = Math.max(Math.min(leftTotal + scrollAmount, 0), leftMax);
scroller.animate({ left: leftTotal }, 250);
if (leftTotal == 0) {
$("#recently-watched-page-left").addClass("disabled").blur();
} else {
$("#recently-watched-page-left").removeClass("disabled");
}
if (leftTotal == leftMax) {
$("#recently-watched-page-right").addClass("disabled").blur();
} else {
$("#recently-watched-page-right").removeClass("disabled");
}
});
$(document).ready(function () { $(document).ready(function () {
// Javascript to enable link to tab // Javascript to enable link to tab
var hash = document.location.hash; var hash = document.location.hash;

View file

@ -31,7 +31,7 @@ DOCUMENTATION :: END
from plexpy.helpers import page, short_season from plexpy.helpers import page, short_season
%> %>
<div class="dashboard-recent-media-row"> <div class="dashboard-recent-media-row">
<div id="recently-watched-row-scroller" style="left: 0;"> <div id="recently-watched-row-scroller">
<ul class="dashboard-recent-media list-unstyled"> <ul class="dashboard-recent-media list-unstyled">
% for item in data: % for item in data:
<li> <li>

View file

@ -33,6 +33,9 @@
<th align="left" id="edit_row">Edit</th> <th align="left" id="edit_row">Edit</th>
<th align="right" id="avatar"></th> <th align="right" id="avatar"></th>
<th align="left" id="friendly_name">User</th> <th align="left" id="friendly_name">User</th>
<th align="left" id="username">Username</th>
<th align="left" id="title">Full Name</th>
<th align="left" id="email">Email</th>
<th align="left" id="last_seen">Last Streamed</th> <th align="left" id="last_seen">Last Streamed</th>
<th align="left" id="last_known_ip">Last Known IP</th> <th align="left" id="last_known_ip">Last Known IP</th>
<th align="left" id="last_platform">Last Platform</th> <th align="left" id="last_platform">Last Platform</th>

View file

@ -12,6 +12,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content=""> <meta name="description" content="">
<meta name="author" content=""> <meta name="author" content="">
<meta name="referrer" content="no-referrer">
<link href="${http_root}css/bootstrap3/bootstrap.min.css" rel="stylesheet"> <link href="${http_root}css/bootstrap3/bootstrap.min.css" rel="stylesheet">
<link href="${http_root}css/bootstrap-wizard.css" rel="stylesheet"> <link href="${http_root}css/bootstrap-wizard.css" rel="stylesheet">
<link href="${http_root}css/tautulli.css${cache_param}" rel="stylesheet"> <link href="${http_root}css/tautulli.css${cache_param}" rel="stylesheet">
@ -134,7 +135,6 @@
data-identifier="${config['pms_identifier']}" data-identifier="${config['pms_identifier']}"
data-ip="${config['pms_ip']}" data-ip="${config['pms_ip']}"
data-port="${config['pms_port']}" data-port="${config['pms_port']}"
data-local="${int(not int(config['pms_is_remote']))}"
data-ssl="${config['pms_ssl']}" data-ssl="${config['pms_ssl']}"
data-is_cloud="${config['pms_is_cloud']}" data-is_cloud="${config['pms_is_cloud']}"
data-label="${config['pms_name'] or 'Local'}" data-label="${config['pms_name'] or 'Local'}"
@ -150,7 +150,7 @@
<div class="col-xs-3"> <div class="col-xs-3">
<input type="text" class="form-control pms-settings" name="pms_port" id="pms_port" placeholder="32400" value="${config['pms_port']}" required> <input type="text" class="form-control pms-settings" name="pms_port" id="pms_port" placeholder="32400" value="${config['pms_port']}" required>
</div> </div>
<div class="col-xs-4"> <div class="col-xs-9">
<div class="checkbox"> <div class="checkbox">
<label> <label>
<input type="checkbox" id="pms_ssl_checkbox" class="checkbox-toggle pms-settings" data-id="pms_ssl" value="1" ${helpers.checked(config['pms_ssl'])}> Use Secure Connection <input type="checkbox" id="pms_ssl_checkbox" class="checkbox-toggle pms-settings" data-id="pms_ssl" value="1" ${helpers.checked(config['pms_ssl'])}> Use Secure Connection
@ -158,14 +158,6 @@
</label> </label>
</div> </div>
</div> </div>
<div class="col-xs-4">
<div class="checkbox">
<label>
<input type="checkbox" id="pms_is_remote_checkbox" class="checkbox-toggle pms-settings" data-id="pms_is_remote" value="1" ${helpers.checked(config['pms_is_remote'])}> Remote Server
<input type="hidden" id="pms_is_remote" name="pms_is_remote" value="${config['pms_is_remote']}">
</label>
</div>
</div>
</div> </div>
</div> </div>
<input type="hidden" id="pms_valid" data-validate="validatePMSip" value=""> <input type="hidden" id="pms_valid" data-validate="validatePMSip" value="">
@ -229,23 +221,6 @@
<!-- Required fields but hidden --> <!-- Required fields but hidden -->
<div style="display: none;"> <div style="display: none;">
<input type="checkbox" name="first_run" id="first_run" value="1" checked> <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="sys_tray_icon" id="sys_tray_icon" value="1" checked>
<input type="checkbox" name="launch_startup" id="launch_startup" 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>
<input type="checkbox" name="refresh_libraries_on_startup" id="refresh_libraries_on_startup" value="1" checked>
<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="log_blacklist_usernames" 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="text" name="home_stats_cards" id="home_stats_cards" value="first_run_wizard">
<input type="text" name="home_library_cards" id="home_library_cards" value="first_run_wizard">
</div> </div>
</div> </div>
@ -308,7 +283,7 @@
var valid_pms_token = el.val(); var valid_pms_token = el.val();
var retValue = {}; var retValue = {};
if (valid_pms_token === 0) { if (valid_pms_token !== "1") {
retValue.status = false; retValue.status = false;
retValue.msg = "Please authenticate."; retValue.msg = "Please authenticate.";
$("#pms-token-status").html('<i class="fa fa-exclamation-circle"></i> Please authenticate.'); $("#pms-token-status").html('<i class="fa fa-exclamation-circle"></i> Please authenticate.');
@ -407,7 +382,6 @@ $(document).ready(function() {
return '<div data-identifier="' + item.clientIdentifier + return '<div data-identifier="' + item.clientIdentifier +
'" data-ip="' + item.ip + '" data-ip="' + item.ip +
'" data-port="' + item.port + '" data-port="' + item.port +
'" data-local="' + item.local +
'" data-ssl="' + item.httpsRequired + '" data-ssl="' + item.httpsRequired +
'" data-is_cloud="' + item.is_cloud + '" data-is_cloud="' + item.is_cloud +
'" data-label="' + item.label + '">' + '" data-label="' + item.label + '">' +
@ -421,7 +395,6 @@ $(document).ready(function() {
return '<div data-identifier="' + item.clientIdentifier + return '<div data-identifier="' + item.clientIdentifier +
'" data-ip="' + item.ip + '" data-ip="' + item.ip +
'" data-port="' + item.port + '" data-port="' + item.port +
'" data-local="' + item.local +
'" data-ssl="' + item.httpsRequired + '" data-ssl="' + item.httpsRequired +
'" data-is_cloud="' + item.is_cloud + '" data-is_cloud="' + item.is_cloud +
'" data-label="' + item.label + '">' + '" data-label="' + item.label + '">' +
@ -444,7 +417,6 @@ $(document).ready(function() {
var identifier = $(pms_ip_selected).data('identifier'); var identifier = $(pms_ip_selected).data('identifier');
var ip = $(pms_ip_selected).data('ip'); var ip = $(pms_ip_selected).data('ip');
var port = $(pms_ip_selected).data('port'); var port = $(pms_ip_selected).data('port');
var local = $(pms_ip_selected).data('local');
var ssl = $(pms_ip_selected).data('ssl'); var ssl = $(pms_ip_selected).data('ssl');
var is_cloud = $(pms_ip_selected).data('is_cloud'); var is_cloud = $(pms_ip_selected).data('is_cloud');
var value = $(pms_ip_selected).data('value'); var value = $(pms_ip_selected).data('value');
@ -455,19 +427,15 @@ $(document).ready(function() {
$("#pms_identifier").val(identifier !== 'undefined' ? identifier : ''); $("#pms_identifier").val(identifier !== 'undefined' ? identifier : '');
$('#pms_ip').val(ip !== 'undefined' ? ip : value); $('#pms_ip').val(ip !== 'undefined' ? ip : value);
$('#pms_port').val(port !== 'undefined' ? port : 32400); $('#pms_port').val(port !== 'undefined' ? port : 32400);
$('#pms_is_remote_checkbox').prop('checked', (local !== 'undefined' && local === 0));
$('#pms_is_remote').val(local !== 'undefined' && local === 0 ? 1 : 0);
$('#pms_ssl_checkbox').prop('checked', (ssl !== 'undefined' && ssl === 1)); $('#pms_ssl_checkbox').prop('checked', (ssl !== 'undefined' && ssl === 1));
$('#pms_ssl').val(ssl !== 'undefined' && ssl === 1 ? 1 : 0); $('#pms_ssl').val(ssl !== 'undefined' && ssl === 1 ? 1 : 0);
$('#pms_is_cloud').val(is_cloud !== 'undefined' && is_cloud === true ? 1 : 0); $('#pms_is_cloud').val(is_cloud !== 'undefined' && is_cloud === true ? 1 : 0);
if (is_cloud === true) { if (is_cloud === true) {
$('#pms_port').prop('readonly', true); $('#pms_port').prop('readonly', true);
$('#pms_is_remote_checkbox').prop('disabled', true);
$('#pms_ssl_checkbox').prop('disabled', true); $('#pms_ssl_checkbox').prop('disabled', true);
} else { } else {
$('#pms_port').prop('readonly', false); $('#pms_port').prop('readonly', false);
$('#pms_is_remote_checkbox').prop('disabled', false);
$('#pms_ssl_checkbox').prop('disabled', false); $('#pms_ssl_checkbox').prop('disabled', false);
} }
}, },
@ -504,7 +472,6 @@ $(document).ready(function() {
var pms_port = $("#pms_port").val().trim(); var pms_port = $("#pms_port").val().trim();
var pms_identifier = $("#pms_identifier").val(); var pms_identifier = $("#pms_identifier").val();
var pms_ssl = $("#pms_ssl").val(); var pms_ssl = $("#pms_ssl").val();
var pms_is_remote = $("#pms_is_remote").val();
if ((pms_ip !== '') || (pms_port !== '')) { if ((pms_ip !== '') || (pms_port !== '')) {
$("#pms-verify-status").html('<i class="fa fa-refresh fa-spin"></i>&nbsp; Verifying server...'); $("#pms-verify-status").html('<i class="fa fa-refresh fa-spin"></i>&nbsp; Verifying server...');
$('#pms-verify-status').fadeIn('fast'); $('#pms-verify-status').fadeIn('fast');
@ -514,8 +481,7 @@ $(document).ready(function() {
hostname: pms_ip, hostname: pms_ip,
port: pms_port, port: pms_port,
identifier: pms_identifier, identifier: pms_identifier,
ssl: pms_ssl, ssl: pms_ssl
remote: pms_is_remote
}, },
cache: true, cache: true,
async: true, async: true,
@ -558,10 +524,10 @@ $(document).ready(function() {
function OAuthSuccessCallback(authToken) { function OAuthSuccessCallback(authToken) {
$.post('save_pms_token', { token: authToken, client_id: $('#pms_client_id').val() }, function () { $.post('save_pms_token', { token: authToken, client_id: $('#pms_client_id').val() }, function () {
$("#pms_token_validated").val(1); $("#pms_token_validated").val(1);
getServerOptions();
}); });
$("#pms-token-status").html('<i class="fa fa-check"></i>&nbsp; Authentication successful.').fadeIn('fast'); $("#pms-token-status").html('<i class="fa fa-check"></i>&nbsp; Authentication successful.').fadeIn('fast');
authenticated = true; authenticated = true;
getServerOptions(authToken);
} }
function OAuthErrorCallback() { function OAuthErrorCallback() {
$("#pms-token-status").html('<i class="fa fa-exclamation-circle"></i>&nbsp; Error communicating with Plex.tv.').fadeIn('fast'); $("#pms-token-status").html('<i class="fa fa-exclamation-circle"></i>&nbsp; Error communicating with Plex.tv.').fadeIn('fast');

View file

@ -24,6 +24,7 @@
<html> <html>
<head> <head>
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="color-scheme" content="light dark">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Tautulli Newsletter - ${subject}</title> <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'}"> <link rel="shortcut icon" href="${base_url_image + 'images/favicon/favicon.ico' if base_url_image else 'https://tautulli.com/images/favicon.ico'}">

View file

@ -24,6 +24,7 @@
<html> <html>
<head> <head>
<meta name="viewport" content="width=device-width"/> <meta name="viewport" content="width=device-width"/>
<meta name="color-scheme" content="light dark">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>Tautulli Newsletter - ${subject}</title> <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'}"> <link rel="shortcut icon" href="${base_url_image + 'images/favicon/favicon.ico' if base_url_image else 'https://tautulli.com/images/favicon.ico'}">

View file

@ -1,121 +0,0 @@
#!/usr/bin/python
###############################################################################
# Formatting filter for urllib2's HTTPHandler(debuglevel=1) output
# Copyright (c) 2013, Analytics Pros
#
# This project is free software, distributed under the BSD license.
# Analytics Pros offers consulting and integration services if your firm needs
# assistance in strategy, implementation, or auditing existing work.
###############################################################################
import sys, re, os
from io import StringIO
class BufferTranslator(object):
""" Provides a buffer-compatible interface for filtering buffer content.
"""
parsers = []
def __init__(self, output):
self.output = output
self.encoding = getattr(output, 'encoding', None)
def write(self, content):
content = self.translate(content)
self.output.write(content)
@staticmethod
def stripslashes(content):
return content.decode('string_escape')
@staticmethod
def addslashes(content):
return content.encode('string_escape')
def translate(self, line):
for pattern, method in self.parsers:
match = pattern.match(line)
if match:
return method(match)
return line
class LineBufferTranslator(BufferTranslator):
""" Line buffer implementation supports translation of line-format input
even when input is not already line-buffered. Caches input until newlines
occur, and then dispatches translated input to output buffer.
"""
def __init__(self, *a, **kw):
self._linepending = []
super(LineBufferTranslator, self).__init__(*a, **kw)
def write(self, _input):
lines = _input.splitlines(True)
for i in range(0, len(lines)):
last = i
if lines[i].endswith('\n'):
prefix = len(self._linepending) and ''.join(self._linepending) or ''
self.output.write(self.translate(prefix + lines[i]))
del self._linepending[0:]
last = -1
if last >= 0:
self._linepending.append(lines[ last ])
def __del__(self):
if len(self._linepending):
self.output.write(self.translate(''.join(self._linepending)))
class HTTPTranslator(LineBufferTranslator):
""" Translates output from |urllib2| HTTPHandler(debuglevel = 1) into
HTTP-compatible, readible text structures for human analysis.
"""
RE_LINE_PARSER = re.compile(r'^(?:([a-z]+):)\s*(\'?)([^\r\n]*)\2(?:[\r\n]*)$')
RE_LINE_BREAK = re.compile(r'(\r?\n|(?:\\r)?\\n)')
RE_HTTP_METHOD = re.compile(r'^(POST|GET|HEAD|DELETE|PUT|TRACE|OPTIONS)')
RE_PARAMETER_SPACER = re.compile(r'&([a-z0-9]+)=')
@classmethod
def spacer(cls, line):
return cls.RE_PARAMETER_SPACER.sub(r' &\1= ', line)
def translate(self, line):
parsed = self.RE_LINE_PARSER.match(line)
if parsed:
value = parsed.group(3)
stage = parsed.group(1)
if stage == 'send': # query string is rendered here
return '\n# HTTP Request:\n' + self.stripslashes(value)
elif stage == 'reply':
return '\n\n# HTTP Response:\n' + self.stripslashes(value)
elif stage == 'header':
return value + '\n'
else:
return value
return line
def consume(outbuffer = None): # Capture standard output
sys.stdout = HTTPTranslator(outbuffer or sys.stdout)
return sys.stdout
if __name__ == '__main__':
consume(sys.stdout).write(sys.stdin.read())
print('\n')
# vim: set nowrap tabstop=4 shiftwidth=4 softtabstop=0 expandtab textwidth=0 filetype=python foldmethod=indent foldcolumn=4

View file

@ -1,424 +0,0 @@
from future.moves.urllib.request import urlopen, build_opener, install_opener
from future.moves.urllib.request import Request, HTTPSHandler
from future.moves.urllib.error import URLError, HTTPError
from future.moves.urllib.parse import urlencode
import random
import datetime
import time
import uuid
import hashlib
import socket
def generate_uuid(basedata=None):
""" Provides a _random_ UUID with no input, or a UUID4-format MD5 checksum of any input data provided """
if basedata is None:
return str(uuid.uuid4())
elif isinstance(basedata, str):
checksum = hashlib.md5(str(basedata).encode('utf-8')).hexdigest()
return '%8s-%4s-%4s-%4s-%12s' % (
checksum[0:8], checksum[8:12], checksum[12:16], checksum[16:20], checksum[20:32])
class Time(datetime.datetime):
""" Wrappers and convenience methods for processing various time representations """
@classmethod
def from_unix(cls, seconds, milliseconds=0):
""" Produce a full |datetime.datetime| object from a Unix timestamp """
base = list(time.gmtime(seconds))[0:6]
base.append(milliseconds * 1000) # microseconds
return cls(*base)
@classmethod
def to_unix(cls, timestamp):
""" Wrapper over time module to produce Unix epoch time as a float """
if not isinstance(timestamp, datetime.datetime):
raise TypeError('Time.milliseconds expects a datetime object')
base = time.mktime(timestamp.timetuple())
return base
@classmethod
def milliseconds_offset(cls, timestamp, now=None):
""" Offset time (in milliseconds) from a |datetime.datetime| object to now """
if isinstance(timestamp, (int, float)):
base = timestamp
else:
base = cls.to_unix(timestamp)
base = base + (timestamp.microsecond / 1000000)
if now is None:
now = time.time()
return (now - base) * 1000
class HTTPRequest(object):
""" URL Construction and request handling abstraction.
This is not intended to be used outside this module.
Automates mapping of persistent state (i.e. query parameters)
onto transcient datasets for each query.
"""
endpoint = 'https://www.google-analytics.com/collect'
@staticmethod
def debug():
""" Activate debugging on urllib2 """
handler = HTTPSHandler(debuglevel=1)
opener = build_opener(handler)
install_opener(opener)
# Store properties for all requests
def __init__(self, user_agent=None, *args, **opts):
self.user_agent = user_agent or 'Analytics Pros - Universal Analytics (Python)'
@classmethod
def fixUTF8(cls, data): # Ensure proper encoding for UA's servers...
""" Convert all strings to UTF-8 """
for key in data:
if isinstance(data[key], str):
data[key] = data[key].encode('utf-8')
return data
# Apply stored properties to the given dataset & POST to the configured endpoint
def send(self, data):
request = Request(
self.endpoint + '?' + urlencode(self.fixUTF8(data)).encode('utf-8'),
headers={
'User-Agent': self.user_agent
}
)
self.open(request)
def open(self, request):
try:
return urlopen(request)
except HTTPError as e:
return False
except URLError as e:
self.cache_request(request)
return False
def cache_request(self, request):
# TODO: implement a proper caching mechanism here for re-transmitting hits
# record = (Time.now(), request.get_full_url(), request.get_data(), request.headers)
pass
class HTTPPost(HTTPRequest):
# Apply stored properties to the given dataset & POST to the configured endpoint
def send(self, data):
request = Request(
self.endpoint,
data=urlencode(self.fixUTF8(data)).encode('utf-8'),
headers={
'User-Agent': self.user_agent
}
)
self.open(request)
class Tracker(object):
""" Primary tracking interface for Universal Analytics """
params = None
parameter_alias = {}
valid_hittypes = ('pageview', 'event', 'social', 'screenview', 'transaction', 'item', 'exception', 'timing')
@classmethod
def alias(cls, typemap, base, *names):
""" Declare an alternate (humane) name for a measurement protocol parameter """
cls.parameter_alias[base] = (typemap, base)
for i in names:
cls.parameter_alias[i] = (typemap, base)
@classmethod
def coerceParameter(cls, name, value=None):
if isinstance(name, str) and name[0] == '&':
return name[1:], str(value)
elif name in cls.parameter_alias:
typecast, param_name = cls.parameter_alias.get(name)
return param_name, typecast(value)
else:
raise KeyError('Parameter "{0}" is not recognized'.format(name))
def payload(self, data):
for key, value in data.items():
try:
yield self.coerceParameter(key, value)
except KeyError:
continue
option_sequence = {
'pageview': [(str, 'dp')],
'event': [(str, 'ec'), (str, 'ea'), (str, 'el'), (int, 'ev')],
'social': [(str, 'sn'), (str, 'sa'), (str, 'st')],
'timing': [(str, 'utc'), (str, 'utv'), (str, 'utt'), (str, 'utl')]
}
@classmethod
def consume_options(cls, data, hittype, args):
""" Interpret sequential arguments related to known hittypes based on declared structures """
opt_position = 0
data['t'] = hittype # integrate hit type parameter
if hittype in cls.option_sequence:
for expected_type, optname in cls.option_sequence[hittype]:
if opt_position < len(args) and isinstance(args[opt_position], expected_type):
data[optname] = args[opt_position]
opt_position += 1
@classmethod
def hittime(cls, timestamp=None, age=None, milliseconds=None):
""" Returns an integer represeting the milliseconds offset for a given hit (relative to now) """
if isinstance(timestamp, (int, float)):
return int(Time.milliseconds_offset(Time.from_unix(timestamp, milliseconds=milliseconds)))
if isinstance(timestamp, datetime.datetime):
return int(Time.milliseconds_offset(timestamp))
if isinstance(age, (int, float)):
return int(age * 1000) + (milliseconds or 0)
@property
def account(self):
return self.params.get('tid', None)
def __init__(self, account, name=None, client_id=None, hash_client_id=False, user_id=None, user_agent=None,
use_post=True):
if use_post is False:
self.http = HTTPRequest(user_agent=user_agent)
else:
self.http = HTTPPost(user_agent=user_agent)
self.params = {'v': 1, 'tid': account}
if client_id is None:
client_id = generate_uuid()
self.params['cid'] = client_id
self.hash_client_id = hash_client_id
if user_id is not None:
self.params['uid'] = user_id
def set_timestamp(self, data):
""" Interpret time-related options, apply queue-time parameter as needed """
if 'hittime' in data: # an absolute timestamp
data['qt'] = self.hittime(timestamp=data.pop('hittime', None))
if 'hitage' in data: # a relative age (in seconds)
data['qt'] = self.hittime(age=data.pop('hitage', None))
def send(self, hittype, *args, **data):
""" Transmit HTTP requests to Google Analytics using the measurement protocol """
if hittype not in self.valid_hittypes:
raise KeyError('Unsupported Universal Analytics Hit Type: {0}'.format(repr(hittype)))
self.set_timestamp(data)
self.consume_options(data, hittype, args)
for item in args: # process dictionary-object arguments of transcient data
if isinstance(item, dict):
for key, val in self.payload(item):
data[key] = val
for k, v in self.params.items(): # update only absent parameters
if k not in data:
data[k] = v
data = dict(self.payload(data))
if self.hash_client_id:
data['cid'] = generate_uuid(data['cid'])
# Transmit the hit to Google...
self.http.send(data)
# Setting persistent attibutes of the session/hit/etc (inc. custom dimensions/metrics)
def set(self, name, value=None):
if isinstance(name, dict):
for key, value in name.items():
try:
param, value = self.coerceParameter(key, value)
self.params[param] = value
except KeyError:
pass
elif isinstance(name, str):
try:
param, value = self.coerceParameter(name, value)
self.params[param] = value
except KeyError:
pass
def __getitem__(self, name):
param, value = self.coerceParameter(name, None)
return self.params.get(param, None)
def __setitem__(self, name, value):
param, value = self.coerceParameter(name, value)
self.params[param] = value
def __delitem__(self, name):
param, value = self.coerceParameter(name, None)
if param in self.params:
del self.params[param]
def safe_unicode(obj):
""" Safe convertion to the Unicode string version of the object """
try:
return str(obj)
except UnicodeDecodeError:
return obj.decode('utf-8')
# Declaring name mappings for Measurement Protocol parameters
MAX_CUSTOM_DEFINITIONS = 200
MAX_EC_LISTS = 11 # 1-based index
MAX_EC_PRODUCTS = 11 # 1-based index
MAX_EC_PROMOTIONS = 11 # 1-based index
Tracker.alias(int, 'v', 'protocol-version')
Tracker.alias(safe_unicode, 'cid', 'client-id', 'clientId', 'clientid')
Tracker.alias(safe_unicode, 'tid', 'trackingId', 'account')
Tracker.alias(safe_unicode, 'uid', 'user-id', 'userId', 'userid')
Tracker.alias(safe_unicode, 'uip', 'user-ip', 'userIp', 'ipaddr')
Tracker.alias(safe_unicode, 'ua', 'userAgent', 'userAgentOverride', 'user-agent')
Tracker.alias(safe_unicode, 'dp', 'page', 'path')
Tracker.alias(safe_unicode, 'dt', 'title', 'pagetitle', 'pageTitle' 'page-title')
Tracker.alias(safe_unicode, 'dl', 'location')
Tracker.alias(safe_unicode, 'dh', 'hostname')
Tracker.alias(safe_unicode, 'sc', 'sessioncontrol', 'session-control', 'sessionControl')
Tracker.alias(safe_unicode, 'dr', 'referrer', 'referer')
Tracker.alias(int, 'qt', 'queueTime', 'queue-time')
Tracker.alias(safe_unicode, 't', 'hitType', 'hittype')
Tracker.alias(int, 'aip', 'anonymizeIp', 'anonIp', 'anonymize-ip')
Tracker.alias(safe_unicode, 'ds', 'dataSource', 'data-source')
# Campaign attribution
Tracker.alias(safe_unicode, 'cn', 'campaign', 'campaignName', 'campaign-name')
Tracker.alias(safe_unicode, 'cs', 'source', 'campaignSource', 'campaign-source')
Tracker.alias(safe_unicode, 'cm', 'medium', 'campaignMedium', 'campaign-medium')
Tracker.alias(safe_unicode, 'ck', 'keyword', 'campaignKeyword', 'campaign-keyword')
Tracker.alias(safe_unicode, 'cc', 'content', 'campaignContent', 'campaign-content')
Tracker.alias(safe_unicode, 'ci', 'campaignId', 'campaignID', 'campaign-id')
# Technical specs
Tracker.alias(safe_unicode, 'sr', 'screenResolution', 'screen-resolution', 'resolution')
Tracker.alias(safe_unicode, 'vp', 'viewport', 'viewportSize', 'viewport-size')
Tracker.alias(safe_unicode, 'de', 'encoding', 'documentEncoding', 'document-encoding')
Tracker.alias(int, 'sd', 'colors', 'screenColors', 'screen-colors')
Tracker.alias(safe_unicode, 'ul', 'language', 'user-language', 'userLanguage')
# Mobile app
Tracker.alias(safe_unicode, 'an', 'appName', 'app-name', 'app')
Tracker.alias(safe_unicode, 'cd', 'contentDescription', 'screenName', 'screen-name', 'content-description')
Tracker.alias(safe_unicode, 'av', 'appVersion', 'app-version', 'version')
Tracker.alias(safe_unicode, 'aid', 'appID', 'appId', 'application-id', 'app-id', 'applicationId')
Tracker.alias(safe_unicode, 'aiid', 'appInstallerId', 'app-installer-id')
# Ecommerce
Tracker.alias(safe_unicode, 'ta', 'affiliation', 'transactionAffiliation', 'transaction-affiliation')
Tracker.alias(safe_unicode, 'ti', 'transaction', 'transactionId', 'transaction-id')
Tracker.alias(float, 'tr', 'revenue', 'transactionRevenue', 'transaction-revenue')
Tracker.alias(float, 'ts', 'shipping', 'transactionShipping', 'transaction-shipping')
Tracker.alias(float, 'tt', 'tax', 'transactionTax', 'transaction-tax')
Tracker.alias(safe_unicode, 'cu', 'currency', 'transactionCurrency',
'transaction-currency') # Currency code, e.g. USD, EUR
Tracker.alias(safe_unicode, 'in', 'item-name', 'itemName')
Tracker.alias(float, 'ip', 'item-price', 'itemPrice')
Tracker.alias(float, 'iq', 'item-quantity', 'itemQuantity')
Tracker.alias(safe_unicode, 'ic', 'item-code', 'sku', 'itemCode')
Tracker.alias(safe_unicode, 'iv', 'item-variation', 'item-category', 'itemCategory', 'itemVariation')
# Events
Tracker.alias(safe_unicode, 'ec', 'event-category', 'eventCategory', 'category')
Tracker.alias(safe_unicode, 'ea', 'event-action', 'eventAction', 'action')
Tracker.alias(safe_unicode, 'el', 'event-label', 'eventLabel', 'label')
Tracker.alias(int, 'ev', 'event-value', 'eventValue', 'value')
Tracker.alias(int, 'ni', 'noninteractive', 'nonInteractive', 'noninteraction', 'nonInteraction')
# Social
Tracker.alias(safe_unicode, 'sa', 'social-action', 'socialAction')
Tracker.alias(safe_unicode, 'sn', 'social-network', 'socialNetwork')
Tracker.alias(safe_unicode, 'st', 'social-target', 'socialTarget')
# Exceptions
Tracker.alias(safe_unicode, 'exd', 'exception-description', 'exceptionDescription', 'exDescription')
Tracker.alias(int, 'exf', 'exception-fatal', 'exceptionFatal', 'exFatal')
# User Timing
Tracker.alias(safe_unicode, 'utc', 'timingCategory', 'timing-category')
Tracker.alias(safe_unicode, 'utv', 'timingVariable', 'timing-variable')
Tracker.alias(float, 'utt', 'time', 'timingTime', 'timing-time')
Tracker.alias(safe_unicode, 'utl', 'timingLabel', 'timing-label')
Tracker.alias(float, 'dns', 'timingDNS', 'timing-dns')
Tracker.alias(float, 'pdt', 'timingPageLoad', 'timing-page-load')
Tracker.alias(float, 'rrt', 'timingRedirect', 'timing-redirect')
Tracker.alias(safe_unicode, 'tcp', 'timingTCPConnect', 'timing-tcp-connect')
Tracker.alias(safe_unicode, 'srt', 'timingServerResponse', 'timing-server-response')
# Custom dimensions and metrics
for i in range(0, 200):
Tracker.alias(safe_unicode, 'cd{0}'.format(i), 'dimension{0}'.format(i))
Tracker.alias(int, 'cm{0}'.format(i), 'metric{0}'.format(i))
# Content groups
for i in range(0, 5):
Tracker.alias(safe_unicode, 'cg{0}'.format(i), 'contentGroup{0}'.format(i))
# Enhanced Ecommerce
Tracker.alias(str, 'pa') # Product action
Tracker.alias(str, 'tcc') # Coupon code
Tracker.alias(str, 'pal') # Product action list
Tracker.alias(int, 'cos') # Checkout step
Tracker.alias(str, 'col') # Checkout step option
Tracker.alias(str, 'promoa') # Promotion action
for product_index in range(1, MAX_EC_PRODUCTS):
Tracker.alias(str, 'pr{0}id'.format(product_index)) # Product SKU
Tracker.alias(str, 'pr{0}nm'.format(product_index)) # Product name
Tracker.alias(str, 'pr{0}br'.format(product_index)) # Product brand
Tracker.alias(str, 'pr{0}ca'.format(product_index)) # Product category
Tracker.alias(str, 'pr{0}va'.format(product_index)) # Product variant
Tracker.alias(str, 'pr{0}pr'.format(product_index)) # Product price
Tracker.alias(int, 'pr{0}qt'.format(product_index)) # Product quantity
Tracker.alias(str, 'pr{0}cc'.format(product_index)) # Product coupon code
Tracker.alias(int, 'pr{0}ps'.format(product_index)) # Product position
for custom_index in range(MAX_CUSTOM_DEFINITIONS):
Tracker.alias(str, 'pr{0}cd{1}'.format(product_index, custom_index)) # Product custom dimension
Tracker.alias(int, 'pr{0}cm{1}'.format(product_index, custom_index)) # Product custom metric
for list_index in range(1, MAX_EC_LISTS):
Tracker.alias(str, 'il{0}pi{1}id'.format(list_index, product_index)) # Product impression SKU
Tracker.alias(str, 'il{0}pi{1}nm'.format(list_index, product_index)) # Product impression name
Tracker.alias(str, 'il{0}pi{1}br'.format(list_index, product_index)) # Product impression brand
Tracker.alias(str, 'il{0}pi{1}ca'.format(list_index, product_index)) # Product impression category
Tracker.alias(str, 'il{0}pi{1}va'.format(list_index, product_index)) # Product impression variant
Tracker.alias(int, 'il{0}pi{1}ps'.format(list_index, product_index)) # Product impression position
Tracker.alias(int, 'il{0}pi{1}pr'.format(list_index, product_index)) # Product impression price
for custom_index in range(MAX_CUSTOM_DEFINITIONS):
Tracker.alias(str, 'il{0}pi{1}cd{2}'.format(list_index, product_index,
custom_index)) # Product impression custom dimension
Tracker.alias(int, 'il{0}pi{1}cm{2}'.format(list_index, product_index,
custom_index)) # Product impression custom metric
for list_index in range(1, MAX_EC_LISTS):
Tracker.alias(str, 'il{0}nm'.format(list_index)) # Product impression list name
for promotion_index in range(1, MAX_EC_PROMOTIONS):
Tracker.alias(str, 'promo{0}id'.format(promotion_index)) # Promotion ID
Tracker.alias(str, 'promo{0}nm'.format(promotion_index)) # Promotion name
Tracker.alias(str, 'promo{0}cr'.format(promotion_index)) # Promotion creative
Tracker.alias(str, 'promo{0}ps'.format(promotion_index)) # Promotion position
# Shortcut for creating trackers
def create(account, *args, **kwargs):
return Tracker(account, *args, **kwargs)
# vim: set nowrap tabstop=4 shiftwidth=4 softtabstop=0 expandtab textwidth=0 filetype=python foldmethod=indent foldcolumn=4

View file

@ -1 +0,0 @@
from . import Tracker

View file

@ -0,0 +1,396 @@
import math
import sys
from dataclasses import dataclass
from datetime import timezone
from typing import TYPE_CHECKING, Any, Callable, Iterator, Optional, SupportsFloat, SupportsIndex, TypeVar, Union
if sys.version_info < (3, 8):
from typing_extensions import Protocol, runtime_checkable
else:
from typing import Protocol, runtime_checkable
if sys.version_info < (3, 9):
from typing_extensions import Annotated, Literal
else:
from typing import Annotated, Literal
if sys.version_info < (3, 10):
EllipsisType = type(Ellipsis)
KW_ONLY = {}
SLOTS = {}
else:
from types import EllipsisType
KW_ONLY = {"kw_only": True}
SLOTS = {"slots": True}
__all__ = (
'BaseMetadata',
'GroupedMetadata',
'Gt',
'Ge',
'Lt',
'Le',
'Interval',
'MultipleOf',
'MinLen',
'MaxLen',
'Len',
'Timezone',
'Predicate',
'LowerCase',
'UpperCase',
'IsDigits',
'IsFinite',
'IsNotFinite',
'IsNan',
'IsNotNan',
'IsInfinite',
'IsNotInfinite',
'doc',
'DocInfo',
'__version__',
)
__version__ = '0.6.0'
T = TypeVar('T')
# arguments that start with __ are considered
# positional only
# see https://peps.python.org/pep-0484/#positional-only-arguments
class SupportsGt(Protocol):
def __gt__(self: T, __other: T) -> bool:
...
class SupportsGe(Protocol):
def __ge__(self: T, __other: T) -> bool:
...
class SupportsLt(Protocol):
def __lt__(self: T, __other: T) -> bool:
...
class SupportsLe(Protocol):
def __le__(self: T, __other: T) -> bool:
...
class SupportsMod(Protocol):
def __mod__(self: T, __other: T) -> T:
...
class SupportsDiv(Protocol):
def __div__(self: T, __other: T) -> T:
...
class BaseMetadata:
"""Base class for all metadata.
This exists mainly so that implementers
can do `isinstance(..., BaseMetadata)` while traversing field annotations.
"""
__slots__ = ()
@dataclass(frozen=True, **SLOTS)
class Gt(BaseMetadata):
"""Gt(gt=x) implies that the value must be greater than x.
It can be used with any type that supports the ``>`` operator,
including numbers, dates and times, strings, sets, and so on.
"""
gt: SupportsGt
@dataclass(frozen=True, **SLOTS)
class Ge(BaseMetadata):
"""Ge(ge=x) implies that the value must be greater than or equal to x.
It can be used with any type that supports the ``>=`` operator,
including numbers, dates and times, strings, sets, and so on.
"""
ge: SupportsGe
@dataclass(frozen=True, **SLOTS)
class Lt(BaseMetadata):
"""Lt(lt=x) implies that the value must be less than x.
It can be used with any type that supports the ``<`` operator,
including numbers, dates and times, strings, sets, and so on.
"""
lt: SupportsLt
@dataclass(frozen=True, **SLOTS)
class Le(BaseMetadata):
"""Le(le=x) implies that the value must be less than or equal to x.
It can be used with any type that supports the ``<=`` operator,
including numbers, dates and times, strings, sets, and so on.
"""
le: SupportsLe
@runtime_checkable
class GroupedMetadata(Protocol):
"""A grouping of multiple BaseMetadata objects.
`GroupedMetadata` on its own is not metadata and has no meaning.
All it the the constraint and metadata should be fully expressable
in terms of the `BaseMetadata`'s returned by `GroupedMetadata.__iter__()`.
Concrete implementations should override `GroupedMetadata.__iter__()`
to add their own metadata.
For example:
>>> @dataclass
>>> class Field(GroupedMetadata):
>>> gt: float | None = None
>>> description: str | None = None
...
>>> def __iter__(self) -> Iterable[BaseMetadata]:
>>> if self.gt is not None:
>>> yield Gt(self.gt)
>>> if self.description is not None:
>>> yield Description(self.gt)
Also see the implementation of `Interval` below for an example.
Parsers should recognize this and unpack it so that it can be used
both with and without unpacking:
- `Annotated[int, Field(...)]` (parser must unpack Field)
- `Annotated[int, *Field(...)]` (PEP-646)
""" # noqa: trailing-whitespace
@property
def __is_annotated_types_grouped_metadata__(self) -> Literal[True]:
return True
def __iter__(self) -> Iterator[BaseMetadata]:
...
if not TYPE_CHECKING:
__slots__ = () # allow subclasses to use slots
def __init_subclass__(cls, *args: Any, **kwargs: Any) -> None:
# Basic ABC like functionality without the complexity of an ABC
super().__init_subclass__(*args, **kwargs)
if cls.__iter__ is GroupedMetadata.__iter__:
raise TypeError("Can't subclass GroupedMetadata without implementing __iter__")
def __iter__(self) -> Iterator[BaseMetadata]: # noqa: F811
raise NotImplementedError # more helpful than "None has no attribute..." type errors
@dataclass(frozen=True, **KW_ONLY, **SLOTS)
class Interval(GroupedMetadata):
"""Interval can express inclusive or exclusive bounds with a single object.
It accepts keyword arguments ``gt``, ``ge``, ``lt``, and/or ``le``, which
are interpreted the same way as the single-bound constraints.
"""
gt: Union[SupportsGt, None] = None
ge: Union[SupportsGe, None] = None
lt: Union[SupportsLt, None] = None
le: Union[SupportsLe, None] = None
def __iter__(self) -> Iterator[BaseMetadata]:
"""Unpack an Interval into zero or more single-bounds."""
if self.gt is not None:
yield Gt(self.gt)
if self.ge is not None:
yield Ge(self.ge)
if self.lt is not None:
yield Lt(self.lt)
if self.le is not None:
yield Le(self.le)
@dataclass(frozen=True, **SLOTS)
class MultipleOf(BaseMetadata):
"""MultipleOf(multiple_of=x) might be interpreted in two ways:
1. Python semantics, implying ``value % multiple_of == 0``, or
2. JSONschema semantics, where ``int(value / multiple_of) == value / multiple_of``
We encourage users to be aware of these two common interpretations,
and libraries to carefully document which they implement.
"""
multiple_of: Union[SupportsDiv, SupportsMod]
@dataclass(frozen=True, **SLOTS)
class MinLen(BaseMetadata):
"""
MinLen() implies minimum inclusive length,
e.g. ``len(value) >= min_length``.
"""
min_length: Annotated[int, Ge(0)]
@dataclass(frozen=True, **SLOTS)
class MaxLen(BaseMetadata):
"""
MaxLen() implies maximum inclusive length,
e.g. ``len(value) <= max_length``.
"""
max_length: Annotated[int, Ge(0)]
@dataclass(frozen=True, **SLOTS)
class Len(GroupedMetadata):
"""
Len() implies that ``min_length <= len(value) <= max_length``.
Upper bound may be omitted or ``None`` to indicate no upper length bound.
"""
min_length: Annotated[int, Ge(0)] = 0
max_length: Optional[Annotated[int, Ge(0)]] = None
def __iter__(self) -> Iterator[BaseMetadata]:
"""Unpack a Len into zone or more single-bounds."""
if self.min_length > 0:
yield MinLen(self.min_length)
if self.max_length is not None:
yield MaxLen(self.max_length)
@dataclass(frozen=True, **SLOTS)
class Timezone(BaseMetadata):
"""Timezone(tz=...) requires a datetime to be aware (or ``tz=None``, naive).
``Annotated[datetime, Timezone(None)]`` must be a naive datetime.
``Timezone[...]`` (the ellipsis literal) expresses that the datetime must be
tz-aware but any timezone is allowed.
You may also pass a specific timezone string or timezone object such as
``Timezone(timezone.utc)`` or ``Timezone("Africa/Abidjan")`` to express that
you only allow a specific timezone, though we note that this is often
a symptom of poor design.
"""
tz: Union[str, timezone, EllipsisType, None]
@dataclass(frozen=True, **SLOTS)
class Predicate(BaseMetadata):
"""``Predicate(func: Callable)`` implies `func(value)` is truthy for valid values.
Users should prefer statically inspectable metadata, but if you need the full
power and flexibility of arbitrary runtime predicates... here it is.
We provide a few predefined predicates for common string constraints:
``IsLower = Predicate(str.islower)``, ``IsUpper = Predicate(str.isupper)``, and
``IsDigit = Predicate(str.isdigit)``. Users are encouraged to use methods which
can be given special handling, and avoid indirection like ``lambda s: s.lower()``.
Some libraries might have special logic to handle certain predicates, e.g. by
checking for `str.isdigit` and using its presence to both call custom logic to
enforce digit-only strings, and customise some generated external schema.
We do not specify what behaviour should be expected for predicates that raise
an exception. For example `Annotated[int, Predicate(str.isdigit)]` might silently
skip invalid constraints, or statically raise an error; or it might try calling it
and then propogate or discard the resulting exception.
"""
func: Callable[[Any], bool]
@dataclass
class Not:
func: Callable[[Any], bool]
def __call__(self, __v: Any) -> bool:
return not self.func(__v)
_StrType = TypeVar("_StrType", bound=str)
LowerCase = Annotated[_StrType, Predicate(str.islower)]
"""
Return True if the string is a lowercase string, False otherwise.
A string is lowercase if all cased characters in the string are lowercase and there is at least one cased character in the string.
""" # noqa: E501
UpperCase = Annotated[_StrType, Predicate(str.isupper)]
"""
Return True if the string is an uppercase string, False otherwise.
A string is uppercase if all cased characters in the string are uppercase and there is at least one cased character in the string.
""" # noqa: E501
IsDigits = Annotated[_StrType, Predicate(str.isdigit)]
"""
Return True if the string is a digit string, False otherwise.
A string is a digit string if all characters in the string are digits and there is at least one character in the string.
""" # noqa: E501
IsAscii = Annotated[_StrType, Predicate(str.isascii)]
"""
Return True if all characters in the string are ASCII, False otherwise.
ASCII characters have code points in the range U+0000-U+007F. Empty string is ASCII too.
"""
_NumericType = TypeVar('_NumericType', bound=Union[SupportsFloat, SupportsIndex])
IsFinite = Annotated[_NumericType, Predicate(math.isfinite)]
"""Return True if x is neither an infinity nor a NaN, and False otherwise."""
IsNotFinite = Annotated[_NumericType, Predicate(Not(math.isfinite))]
"""Return True if x is one of infinity or NaN, and False otherwise"""
IsNan = Annotated[_NumericType, Predicate(math.isnan)]
"""Return True if x is a NaN (not a number), and False otherwise."""
IsNotNan = Annotated[_NumericType, Predicate(Not(math.isnan))]
"""Return True if x is anything but NaN (not a number), and False otherwise."""
IsInfinite = Annotated[_NumericType, Predicate(math.isinf)]
"""Return True if x is a positive or negative infinity, and False otherwise."""
IsNotInfinite = Annotated[_NumericType, Predicate(Not(math.isinf))]
"""Return True if x is neither a positive or negative infinity, and False otherwise."""
try:
from typing_extensions import DocInfo, doc # type: ignore [attr-defined]
except ImportError:
@dataclass(frozen=True, **SLOTS)
class DocInfo: # type: ignore [no-redef]
""" "
The return value of doc(), mainly to be used by tools that want to extract the
Annotated documentation at runtime.
"""
documentation: str
"""The documentation string passed to doc()."""
def doc(
documentation: str,
) -> DocInfo:
"""
Add documentation to a type annotation inside of Annotated.
For example:
>>> def hi(name: Annotated[int, doc("The name of the user")]) -> None: ...
"""
return DocInfo(documentation)

View file

@ -0,0 +1,147 @@
import math
import sys
from datetime import date, datetime, timedelta, timezone
from decimal import Decimal
from typing import Any, Dict, Iterable, Iterator, List, NamedTuple, Set, Tuple
if sys.version_info < (3, 9):
from typing_extensions import Annotated
else:
from typing import Annotated
import annotated_types as at
class Case(NamedTuple):
"""
A test case for `annotated_types`.
"""
annotation: Any
valid_cases: Iterable[Any]
invalid_cases: Iterable[Any]
def cases() -> Iterable[Case]:
# Gt, Ge, Lt, Le
yield Case(Annotated[int, at.Gt(4)], (5, 6, 1000), (4, 0, -1))
yield Case(Annotated[float, at.Gt(0.5)], (0.6, 0.7, 0.8, 0.9), (0.5, 0.0, -0.1))
yield Case(
Annotated[datetime, at.Gt(datetime(2000, 1, 1))],
[datetime(2000, 1, 2), datetime(2000, 1, 3)],
[datetime(2000, 1, 1), datetime(1999, 12, 31)],
)
yield Case(
Annotated[datetime, at.Gt(date(2000, 1, 1))],
[date(2000, 1, 2), date(2000, 1, 3)],
[date(2000, 1, 1), date(1999, 12, 31)],
)
yield Case(
Annotated[datetime, at.Gt(Decimal('1.123'))],
[Decimal('1.1231'), Decimal('123')],
[Decimal('1.123'), Decimal('0')],
)
yield Case(Annotated[int, at.Ge(4)], (4, 5, 6, 1000, 4), (0, -1))
yield Case(Annotated[float, at.Ge(0.5)], (0.5, 0.6, 0.7, 0.8, 0.9), (0.4, 0.0, -0.1))
yield Case(
Annotated[datetime, at.Ge(datetime(2000, 1, 1))],
[datetime(2000, 1, 2), datetime(2000, 1, 3)],
[datetime(1998, 1, 1), datetime(1999, 12, 31)],
)
yield Case(Annotated[int, at.Lt(4)], (0, -1), (4, 5, 6, 1000, 4))
yield Case(Annotated[float, at.Lt(0.5)], (0.4, 0.0, -0.1), (0.5, 0.6, 0.7, 0.8, 0.9))
yield Case(
Annotated[datetime, at.Lt(datetime(2000, 1, 1))],
[datetime(1999, 12, 31), datetime(1999, 12, 31)],
[datetime(2000, 1, 2), datetime(2000, 1, 3)],
)
yield Case(Annotated[int, at.Le(4)], (4, 0, -1), (5, 6, 1000))
yield Case(Annotated[float, at.Le(0.5)], (0.5, 0.0, -0.1), (0.6, 0.7, 0.8, 0.9))
yield Case(
Annotated[datetime, at.Le(datetime(2000, 1, 1))],
[datetime(2000, 1, 1), datetime(1999, 12, 31)],
[datetime(2000, 1, 2), datetime(2000, 1, 3)],
)
# Interval
yield Case(Annotated[int, at.Interval(gt=4)], (5, 6, 1000), (4, 0, -1))
yield Case(Annotated[int, at.Interval(gt=4, lt=10)], (5, 6), (4, 10, 1000, 0, -1))
yield Case(Annotated[float, at.Interval(ge=0.5, le=1)], (0.5, 0.9, 1), (0.49, 1.1))
yield Case(
Annotated[datetime, at.Interval(gt=datetime(2000, 1, 1), le=datetime(2000, 1, 3))],
[datetime(2000, 1, 2), datetime(2000, 1, 3)],
[datetime(2000, 1, 1), datetime(2000, 1, 4)],
)
yield Case(Annotated[int, at.MultipleOf(multiple_of=3)], (0, 3, 9), (1, 2, 4))
yield Case(Annotated[float, at.MultipleOf(multiple_of=0.5)], (0, 0.5, 1, 1.5), (0.4, 1.1))
# lengths
yield Case(Annotated[str, at.MinLen(3)], ('123', '1234', 'x' * 10), ('', '1', '12'))
yield Case(Annotated[str, at.Len(3)], ('123', '1234', 'x' * 10), ('', '1', '12'))
yield Case(Annotated[List[int], at.MinLen(3)], ([1, 2, 3], [1, 2, 3, 4], [1] * 10), ([], [1], [1, 2]))
yield Case(Annotated[List[int], at.Len(3)], ([1, 2, 3], [1, 2, 3, 4], [1] * 10), ([], [1], [1, 2]))
yield Case(Annotated[str, at.MaxLen(4)], ('', '1234'), ('12345', 'x' * 10))
yield Case(Annotated[str, at.Len(0, 4)], ('', '1234'), ('12345', 'x' * 10))
yield Case(Annotated[List[str], at.MaxLen(4)], ([], ['a', 'bcdef'], ['a', 'b', 'c']), (['a'] * 5, ['b'] * 10))
yield Case(Annotated[List[str], at.Len(0, 4)], ([], ['a', 'bcdef'], ['a', 'b', 'c']), (['a'] * 5, ['b'] * 10))
yield Case(Annotated[str, at.Len(3, 5)], ('123', '12345'), ('', '1', '12', '123456', 'x' * 10))
yield Case(Annotated[str, at.Len(3, 3)], ('123',), ('12', '1234'))
yield Case(Annotated[Dict[int, int], at.Len(2, 3)], [{1: 1, 2: 2}], [{}, {1: 1}, {1: 1, 2: 2, 3: 3, 4: 4}])
yield Case(Annotated[Set[int], at.Len(2, 3)], ({1, 2}, {1, 2, 3}), (set(), {1}, {1, 2, 3, 4}))
yield Case(Annotated[Tuple[int, ...], at.Len(2, 3)], ((1, 2), (1, 2, 3)), ((), (1,), (1, 2, 3, 4)))
# Timezone
yield Case(
Annotated[datetime, at.Timezone(None)], [datetime(2000, 1, 1)], [datetime(2000, 1, 1, tzinfo=timezone.utc)]
)
yield Case(
Annotated[datetime, at.Timezone(...)], [datetime(2000, 1, 1, tzinfo=timezone.utc)], [datetime(2000, 1, 1)]
)
yield Case(
Annotated[datetime, at.Timezone(timezone.utc)],
[datetime(2000, 1, 1, tzinfo=timezone.utc)],
[datetime(2000, 1, 1), datetime(2000, 1, 1, tzinfo=timezone(timedelta(hours=6)))],
)
yield Case(
Annotated[datetime, at.Timezone('Europe/London')],
[datetime(2000, 1, 1, tzinfo=timezone(timedelta(0), name='Europe/London'))],
[datetime(2000, 1, 1), datetime(2000, 1, 1, tzinfo=timezone(timedelta(hours=6)))],
)
# predicate types
yield Case(at.LowerCase[str], ['abc', 'foobar'], ['', 'A', 'Boom'])
yield Case(at.UpperCase[str], ['ABC', 'DEFO'], ['', 'a', 'abc', 'AbC'])
yield Case(at.IsDigits[str], ['123'], ['', 'ab', 'a1b2'])
yield Case(at.IsAscii[str], ['123', 'foo bar'], ['£100', '😊', 'whatever 👀'])
yield Case(Annotated[int, at.Predicate(lambda x: x % 2 == 0)], [0, 2, 4], [1, 3, 5])
yield Case(at.IsFinite[float], [1.23], [math.nan, math.inf, -math.inf])
yield Case(at.IsNotFinite[float], [math.nan, math.inf], [1.23])
yield Case(at.IsNan[float], [math.nan], [1.23, math.inf])
yield Case(at.IsNotNan[float], [1.23, math.inf], [math.nan])
yield Case(at.IsInfinite[float], [math.inf], [math.nan, 1.23])
yield Case(at.IsNotInfinite[float], [math.nan, 1.23], [math.inf])
# check stacked predicates
yield Case(at.IsInfinite[Annotated[float, at.Predicate(lambda x: x > 0)]], [math.inf], [-math.inf, 1.23, math.nan])
# doc
yield Case(Annotated[int, at.doc("A number")], [1, 2], [])
# custom GroupedMetadata
class MyCustomGroupedMetadata(at.GroupedMetadata):
def __iter__(self) -> Iterator[at.Predicate]:
yield at.Predicate(lambda x: float(x).is_integer())
yield Case(Annotated[float, MyCustomGroupedMetadata()], [0, 2.0], [0.01, 1.5])

View file

@ -1,608 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2005-2010 ActiveState Software Inc.
# Copyright (c) 2013 Eddy Petrișor
"""Utilities for determining application-specific dirs.
See <http://github.com/ActiveState/appdirs> for details and usage.
"""
# Dev Notes:
# - MSDN on where to store app data files:
# http://support.microsoft.com/default.aspx?scid=kb;en-us;310294#XSLTH3194121123120121120120
# - Mac OS X: http://developer.apple.com/documentation/MacOSX/Conceptual/BPFileSystem/index.html
# - XDG spec for Un*x: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
__version__ = "1.4.4"
__version_info__ = tuple(int(segment) for segment in __version__.split("."))
import sys
import os
PY3 = sys.version_info[0] == 3
if PY3:
unicode = str
if sys.platform.startswith('java'):
import platform
os_name = platform.java_ver()[3][0]
if os_name.startswith('Windows'): # "Windows XP", "Windows 7", etc.
system = 'win32'
elif os_name.startswith('Mac'): # "Mac OS X", etc.
system = 'darwin'
else: # "Linux", "SunOS", "FreeBSD", etc.
# Setting this to "linux2" is not ideal, but only Windows or Mac
# are actually checked for and the rest of the module expects
# *sys.platform* style strings.
system = 'linux2'
else:
system = sys.platform
def user_data_dir(appname=None, appauthor=None, version=None, roaming=False):
r"""Return full path to the user-specific data dir for this application.
"appname" is the name of application.
If None, just the system directory is returned.
"appauthor" (only used on Windows) is the name of the
appauthor or distributing body for this application. Typically
it is the owning company name. This falls back to appname. You may
pass False to disable it.
"version" is an optional version path element to append to the
path. You might want to use this if you want multiple versions
of your app to be able to run independently. If used, this
would typically be "<major>.<minor>".
Only applied when appname is present.
"roaming" (boolean, default False) can be set True to use the Windows
roaming appdata directory. That means that for users on a Windows
network setup for roaming profiles, this user data will be
sync'd on login. See
<http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx>
for a discussion of issues.
Typical user data directories are:
Mac OS X: ~/Library/Application Support/<AppName>
Unix: ~/.local/share/<AppName> # or in $XDG_DATA_HOME, if defined
Win XP (not roaming): C:\Documents and Settings\<username>\Application Data\<AppAuthor>\<AppName>
Win XP (roaming): C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>
Win 7 (not roaming): C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>
Win 7 (roaming): C:\Users\<username>\AppData\Roaming\<AppAuthor>\<AppName>
For Unix, we follow the XDG spec and support $XDG_DATA_HOME.
That means, by default "~/.local/share/<AppName>".
"""
if system == "win32":
if appauthor is None:
appauthor = appname
const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA"
path = os.path.normpath(_get_win_folder(const))
if appname:
if appauthor is not False:
path = os.path.join(path, appauthor, appname)
else:
path = os.path.join(path, appname)
elif system == 'darwin':
path = os.path.expanduser('~/Library/Application Support/')
if appname:
path = os.path.join(path, appname)
else:
path = os.getenv('XDG_DATA_HOME', os.path.expanduser("~/.local/share"))
if appname:
path = os.path.join(path, appname)
if appname and version:
path = os.path.join(path, version)
return path
def site_data_dir(appname=None, appauthor=None, version=None, multipath=False):
r"""Return full path to the user-shared data dir for this application.
"appname" is the name of application.
If None, just the system directory is returned.
"appauthor" (only used on Windows) is the name of the
appauthor or distributing body for this application. Typically
it is the owning company name. This falls back to appname. You may
pass False to disable it.
"version" is an optional version path element to append to the
path. You might want to use this if you want multiple versions
of your app to be able to run independently. If used, this
would typically be "<major>.<minor>".
Only applied when appname is present.
"multipath" is an optional parameter only applicable to *nix
which indicates that the entire list of data dirs should be
returned. By default, the first item from XDG_DATA_DIRS is
returned, or '/usr/local/share/<AppName>',
if XDG_DATA_DIRS is not set
Typical site data directories are:
Mac OS X: /Library/Application Support/<AppName>
Unix: /usr/local/share/<AppName> or /usr/share/<AppName>
Win XP: C:\Documents and Settings\All Users\Application Data\<AppAuthor>\<AppName>
Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.)
Win 7: C:\ProgramData\<AppAuthor>\<AppName> # Hidden, but writeable on Win 7.
For Unix, this is using the $XDG_DATA_DIRS[0] default.
WARNING: Do not use this on Windows. See the Vista-Fail note above for why.
"""
if system == "win32":
if appauthor is None:
appauthor = appname
path = os.path.normpath(_get_win_folder("CSIDL_COMMON_APPDATA"))
if appname:
if appauthor is not False:
path = os.path.join(path, appauthor, appname)
else:
path = os.path.join(path, appname)
elif system == 'darwin':
path = os.path.expanduser('/Library/Application Support')
if appname:
path = os.path.join(path, appname)
else:
# XDG default for $XDG_DATA_DIRS
# only first, if multipath is False
path = os.getenv('XDG_DATA_DIRS',
os.pathsep.join(['/usr/local/share', '/usr/share']))
pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)]
if appname:
if version:
appname = os.path.join(appname, version)
pathlist = [os.sep.join([x, appname]) for x in pathlist]
if multipath:
path = os.pathsep.join(pathlist)
else:
path = pathlist[0]
return path
if appname and version:
path = os.path.join(path, version)
return path
def user_config_dir(appname=None, appauthor=None, version=None, roaming=False):
r"""Return full path to the user-specific config dir for this application.
"appname" is the name of application.
If None, just the system directory is returned.
"appauthor" (only used on Windows) is the name of the
appauthor or distributing body for this application. Typically
it is the owning company name. This falls back to appname. You may
pass False to disable it.
"version" is an optional version path element to append to the
path. You might want to use this if you want multiple versions
of your app to be able to run independently. If used, this
would typically be "<major>.<minor>".
Only applied when appname is present.
"roaming" (boolean, default False) can be set True to use the Windows
roaming appdata directory. That means that for users on a Windows
network setup for roaming profiles, this user data will be
sync'd on login. See
<http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx>
for a discussion of issues.
Typical user config directories are:
Mac OS X: same as user_data_dir
Unix: ~/.config/<AppName> # or in $XDG_CONFIG_HOME, if defined
Win *: same as user_data_dir
For Unix, we follow the XDG spec and support $XDG_CONFIG_HOME.
That means, by default "~/.config/<AppName>".
"""
if system in ["win32", "darwin"]:
path = user_data_dir(appname, appauthor, None, roaming)
else:
path = os.getenv('XDG_CONFIG_HOME', os.path.expanduser("~/.config"))
if appname:
path = os.path.join(path, appname)
if appname and version:
path = os.path.join(path, version)
return path
def site_config_dir(appname=None, appauthor=None, version=None, multipath=False):
r"""Return full path to the user-shared data dir for this application.
"appname" is the name of application.
If None, just the system directory is returned.
"appauthor" (only used on Windows) is the name of the
appauthor or distributing body for this application. Typically
it is the owning company name. This falls back to appname. You may
pass False to disable it.
"version" is an optional version path element to append to the
path. You might want to use this if you want multiple versions
of your app to be able to run independently. If used, this
would typically be "<major>.<minor>".
Only applied when appname is present.
"multipath" is an optional parameter only applicable to *nix
which indicates that the entire list of config dirs should be
returned. By default, the first item from XDG_CONFIG_DIRS is
returned, or '/etc/xdg/<AppName>', if XDG_CONFIG_DIRS is not set
Typical site config directories are:
Mac OS X: same as site_data_dir
Unix: /etc/xdg/<AppName> or $XDG_CONFIG_DIRS[i]/<AppName> for each value in
$XDG_CONFIG_DIRS
Win *: same as site_data_dir
Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.)
For Unix, this is using the $XDG_CONFIG_DIRS[0] default, if multipath=False
WARNING: Do not use this on Windows. See the Vista-Fail note above for why.
"""
if system in ["win32", "darwin"]:
path = site_data_dir(appname, appauthor)
if appname and version:
path = os.path.join(path, version)
else:
# XDG default for $XDG_CONFIG_DIRS
# only first, if multipath is False
path = os.getenv('XDG_CONFIG_DIRS', '/etc/xdg')
pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)]
if appname:
if version:
appname = os.path.join(appname, version)
pathlist = [os.sep.join([x, appname]) for x in pathlist]
if multipath:
path = os.pathsep.join(pathlist)
else:
path = pathlist[0]
return path
def user_cache_dir(appname=None, appauthor=None, version=None, opinion=True):
r"""Return full path to the user-specific cache dir for this application.
"appname" is the name of application.
If None, just the system directory is returned.
"appauthor" (only used on Windows) is the name of the
appauthor or distributing body for this application. Typically
it is the owning company name. This falls back to appname. You may
pass False to disable it.
"version" is an optional version path element to append to the
path. You might want to use this if you want multiple versions
of your app to be able to run independently. If used, this
would typically be "<major>.<minor>".
Only applied when appname is present.
"opinion" (boolean) can be False to disable the appending of
"Cache" to the base app data dir for Windows. See
discussion below.
Typical user cache directories are:
Mac OS X: ~/Library/Caches/<AppName>
Unix: ~/.cache/<AppName> (XDG default)
Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Cache
Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Cache
On Windows the only suggestion in the MSDN docs is that local settings go in
the `CSIDL_LOCAL_APPDATA` directory. This is identical to the non-roaming
app data dir (the default returned by `user_data_dir` above). Apps typically
put cache data somewhere *under* the given dir here. Some examples:
...\Mozilla\Firefox\Profiles\<ProfileName>\Cache
...\Acme\SuperApp\Cache\1.0
OPINION: This function appends "Cache" to the `CSIDL_LOCAL_APPDATA` value.
This can be disabled with the `opinion=False` option.
"""
if system == "win32":
if appauthor is None:
appauthor = appname
path = os.path.normpath(_get_win_folder("CSIDL_LOCAL_APPDATA"))
if appname:
if appauthor is not False:
path = os.path.join(path, appauthor, appname)
else:
path = os.path.join(path, appname)
if opinion:
path = os.path.join(path, "Cache")
elif system == 'darwin':
path = os.path.expanduser('~/Library/Caches')
if appname:
path = os.path.join(path, appname)
else:
path = os.getenv('XDG_CACHE_HOME', os.path.expanduser('~/.cache'))
if appname:
path = os.path.join(path, appname)
if appname and version:
path = os.path.join(path, version)
return path
def user_state_dir(appname=None, appauthor=None, version=None, roaming=False):
r"""Return full path to the user-specific state dir for this application.
"appname" is the name of application.
If None, just the system directory is returned.
"appauthor" (only used on Windows) is the name of the
appauthor or distributing body for this application. Typically
it is the owning company name. This falls back to appname. You may
pass False to disable it.
"version" is an optional version path element to append to the
path. You might want to use this if you want multiple versions
of your app to be able to run independently. If used, this
would typically be "<major>.<minor>".
Only applied when appname is present.
"roaming" (boolean, default False) can be set True to use the Windows
roaming appdata directory. That means that for users on a Windows
network setup for roaming profiles, this user data will be
sync'd on login. See
<http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx>
for a discussion of issues.
Typical user state directories are:
Mac OS X: same as user_data_dir
Unix: ~/.local/state/<AppName> # or in $XDG_STATE_HOME, if defined
Win *: same as user_data_dir
For Unix, we follow this Debian proposal <https://wiki.debian.org/XDGBaseDirectorySpecification#state>
to extend the XDG spec and support $XDG_STATE_HOME.
That means, by default "~/.local/state/<AppName>".
"""
if system in ["win32", "darwin"]:
path = user_data_dir(appname, appauthor, None, roaming)
else:
path = os.getenv('XDG_STATE_HOME', os.path.expanduser("~/.local/state"))
if appname:
path = os.path.join(path, appname)
if appname and version:
path = os.path.join(path, version)
return path
def user_log_dir(appname=None, appauthor=None, version=None, opinion=True):
r"""Return full path to the user-specific log dir for this application.
"appname" is the name of application.
If None, just the system directory is returned.
"appauthor" (only used on Windows) is the name of the
appauthor or distributing body for this application. Typically
it is the owning company name. This falls back to appname. You may
pass False to disable it.
"version" is an optional version path element to append to the
path. You might want to use this if you want multiple versions
of your app to be able to run independently. If used, this
would typically be "<major>.<minor>".
Only applied when appname is present.
"opinion" (boolean) can be False to disable the appending of
"Logs" to the base app data dir for Windows, and "log" to the
base cache dir for Unix. See discussion below.
Typical user log directories are:
Mac OS X: ~/Library/Logs/<AppName>
Unix: ~/.cache/<AppName>/log # or under $XDG_CACHE_HOME if defined
Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Logs
Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Logs
On Windows the only suggestion in the MSDN docs is that local settings
go in the `CSIDL_LOCAL_APPDATA` directory. (Note: I'm interested in
examples of what some windows apps use for a logs dir.)
OPINION: This function appends "Logs" to the `CSIDL_LOCAL_APPDATA`
value for Windows and appends "log" to the user cache dir for Unix.
This can be disabled with the `opinion=False` option.
"""
if system == "darwin":
path = os.path.join(
os.path.expanduser('~/Library/Logs'),
appname)
elif system == "win32":
path = user_data_dir(appname, appauthor, version)
version = False
if opinion:
path = os.path.join(path, "Logs")
else:
path = user_cache_dir(appname, appauthor, version)
version = False
if opinion:
path = os.path.join(path, "log")
if appname and version:
path = os.path.join(path, version)
return path
class AppDirs(object):
"""Convenience wrapper for getting application dirs."""
def __init__(self, appname=None, appauthor=None, version=None,
roaming=False, multipath=False):
self.appname = appname
self.appauthor = appauthor
self.version = version
self.roaming = roaming
self.multipath = multipath
@property
def user_data_dir(self):
return user_data_dir(self.appname, self.appauthor,
version=self.version, roaming=self.roaming)
@property
def site_data_dir(self):
return site_data_dir(self.appname, self.appauthor,
version=self.version, multipath=self.multipath)
@property
def user_config_dir(self):
return user_config_dir(self.appname, self.appauthor,
version=self.version, roaming=self.roaming)
@property
def site_config_dir(self):
return site_config_dir(self.appname, self.appauthor,
version=self.version, multipath=self.multipath)
@property
def user_cache_dir(self):
return user_cache_dir(self.appname, self.appauthor,
version=self.version)
@property
def user_state_dir(self):
return user_state_dir(self.appname, self.appauthor,
version=self.version)
@property
def user_log_dir(self):
return user_log_dir(self.appname, self.appauthor,
version=self.version)
#---- internal support stuff
def _get_win_folder_from_registry(csidl_name):
"""This is a fallback technique at best. I'm not sure if using the
registry for this guarantees us the correct answer for all CSIDL_*
names.
"""
if PY3:
import winreg as _winreg
else:
import _winreg
shell_folder_name = {
"CSIDL_APPDATA": "AppData",
"CSIDL_COMMON_APPDATA": "Common AppData",
"CSIDL_LOCAL_APPDATA": "Local AppData",
}[csidl_name]
key = _winreg.OpenKey(
_winreg.HKEY_CURRENT_USER,
r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
)
dir, type = _winreg.QueryValueEx(key, shell_folder_name)
return dir
def _get_win_folder_with_pywin32(csidl_name):
from win32com.shell import shellcon, shell
dir = shell.SHGetFolderPath(0, getattr(shellcon, csidl_name), 0, 0)
# Try to make this a unicode path because SHGetFolderPath does
# not return unicode strings when there is unicode data in the
# path.
try:
dir = unicode(dir)
# Downgrade to short path name if have highbit chars. See
# <http://bugs.activestate.com/show_bug.cgi?id=85099>.
has_high_char = False
for c in dir:
if ord(c) > 255:
has_high_char = True
break
if has_high_char:
try:
import win32api
dir = win32api.GetShortPathName(dir)
except ImportError:
pass
except UnicodeError:
pass
return dir
def _get_win_folder_with_ctypes(csidl_name):
import ctypes
csidl_const = {
"CSIDL_APPDATA": 26,
"CSIDL_COMMON_APPDATA": 35,
"CSIDL_LOCAL_APPDATA": 28,
}[csidl_name]
buf = ctypes.create_unicode_buffer(1024)
ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf)
# Downgrade to short path name if have highbit chars. See
# <http://bugs.activestate.com/show_bug.cgi?id=85099>.
has_high_char = False
for c in buf:
if ord(c) > 255:
has_high_char = True
break
if has_high_char:
buf2 = ctypes.create_unicode_buffer(1024)
if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024):
buf = buf2
return buf.value
def _get_win_folder_with_jna(csidl_name):
import array
from com.sun import jna
from com.sun.jna.platform import win32
buf_size = win32.WinDef.MAX_PATH * 2
buf = array.zeros('c', buf_size)
shell = win32.Shell32.INSTANCE
shell.SHGetFolderPath(None, getattr(win32.ShlObj, csidl_name), None, win32.ShlObj.SHGFP_TYPE_CURRENT, buf)
dir = jna.Native.toString(buf.tostring()).rstrip("\0")
# Downgrade to short path name if have highbit chars. See
# <http://bugs.activestate.com/show_bug.cgi?id=85099>.
has_high_char = False
for c in dir:
if ord(c) > 255:
has_high_char = True
break
if has_high_char:
buf = array.zeros('c', buf_size)
kernel = win32.Kernel32.INSTANCE
if kernel.GetShortPathName(dir, buf, buf_size):
dir = jna.Native.toString(buf.tostring()).rstrip("\0")
return dir
if system == "win32":
try:
import win32com.shell
_get_win_folder = _get_win_folder_with_pywin32
except ImportError:
try:
from ctypes import windll
_get_win_folder = _get_win_folder_with_ctypes
except ImportError:
try:
import com.sun.jna
_get_win_folder = _get_win_folder_with_jna
except ImportError:
_get_win_folder = _get_win_folder_from_registry
#---- self test code
if __name__ == "__main__":
appname = "MyApp"
appauthor = "MyCompany"
props = ("user_data_dir",
"user_config_dir",
"user_cache_dir",
"user_state_dir",
"user_log_dir",
"site_data_dir",
"site_config_dir")
print("-- app dirs %s --" % __version__)
print("-- app dirs (with optional 'version')")
dirs = AppDirs(appname, appauthor, version="1.0")
for prop in props:
print("%s: %s" % (prop, getattr(dirs, prop)))
print("\n-- app dirs (without optional 'version')")
dirs = AppDirs(appname, appauthor)
for prop in props:
print("%s: %s" % (prop, getattr(dirs, prop)))
print("\n-- app dirs (without optional 'appauthor')")
dirs = AppDirs(appname)
for prop in props:
print("%s: %s" % (prop, getattr(dirs, prop)))
print("\n-- app dirs (with disabled 'appauthor')")
dirs = AppDirs(appname, appauthor=False)
for prop in props:
print("%s: %s" % (prop, getattr(dirs, prop)))

View file

@ -3,13 +3,9 @@ from __future__ import absolute_import
import sys import sys
from apscheduler.executors.base import BaseExecutor, run_job from apscheduler.executors.base import BaseExecutor, run_job
from apscheduler.executors.base_py3 import run_coroutine_job
from apscheduler.util import iscoroutinefunction_partial from apscheduler.util import iscoroutinefunction_partial
try:
from apscheduler.executors.base_py3 import run_coroutine_job
except ImportError:
run_coroutine_job = None
class AsyncIOExecutor(BaseExecutor): class AsyncIOExecutor(BaseExecutor):
""" """
@ -46,11 +42,8 @@ class AsyncIOExecutor(BaseExecutor):
self._run_job_success(job.id, events) self._run_job_success(job.id, events)
if iscoroutinefunction_partial(job.func): if iscoroutinefunction_partial(job.func):
if run_coroutine_job is not None:
coro = run_coroutine_job(job, job._jobstore_alias, run_times, self._logger.name) coro = run_coroutine_job(job, job._jobstore_alias, run_times, self._logger.name)
f = self._eventloop.create_task(coro) f = self._eventloop.create_task(coro)
else:
raise Exception('Executing coroutine based jobs is not supported with Trollius')
else: else:
f = self._eventloop.run_in_executor(None, run_job, job, job._jobstore_alias, run_times, f = self._eventloop.run_in_executor(None, run_job, job, job._jobstore_alias, run_times,
self._logger.name) self._logger.name)

View file

@ -106,7 +106,7 @@ class MongoDBJobStore(BaseJobStore):
raise JobLookupError(job_id) raise JobLookupError(job_id)
def remove_all_jobs(self): def remove_all_jobs(self):
self.collection.remove() self.collection.delete_many({})
def shutdown(self): def shutdown(self):
self.client.close() self.client.close()
@ -133,7 +133,7 @@ class MongoDBJobStore(BaseJobStore):
# Remove all the jobs we failed to restore # Remove all the jobs we failed to restore
if failed_job_ids: if failed_job_ids:
self.collection.remove({'_id': {'$in': failed_job_ids}}) self.collection.delete_many({'_id': {'$in': failed_job_ids}})
return jobs return jobs

View file

@ -57,7 +57,7 @@ class SQLAlchemyJobStore(BaseJobStore):
# 25 = precision that translates to an 8-byte float # 25 = precision that translates to an 8-byte float
self.jobs_t = Table( self.jobs_t = Table(
tablename, metadata, tablename, metadata,
Column('id', Unicode(191, _warn_on_bytestring=False), primary_key=True), Column('id', Unicode(191), primary_key=True),
Column('next_run_time', Float(25), index=True), Column('next_run_time', Float(25), index=True),
Column('job_state', LargeBinary, nullable=False), Column('job_state', LargeBinary, nullable=False),
schema=tableschema schema=tableschema
@ -68,8 +68,9 @@ class SQLAlchemyJobStore(BaseJobStore):
self.jobs_t.create(self.engine, True) self.jobs_t.create(self.engine, True)
def lookup_job(self, job_id): def lookup_job(self, job_id):
selectable = select([self.jobs_t.c.job_state]).where(self.jobs_t.c.id == job_id) selectable = select(self.jobs_t.c.job_state).where(self.jobs_t.c.id == job_id)
job_state = self.engine.execute(selectable).scalar() with self.engine.begin() as connection:
job_state = connection.execute(selectable).scalar()
return self._reconstitute_job(job_state) if job_state else None return self._reconstitute_job(job_state) if job_state else None
def get_due_jobs(self, now): def get_due_jobs(self, now):
@ -77,10 +78,11 @@ class SQLAlchemyJobStore(BaseJobStore):
return self._get_jobs(self.jobs_t.c.next_run_time <= timestamp) return self._get_jobs(self.jobs_t.c.next_run_time <= timestamp)
def get_next_run_time(self): def get_next_run_time(self):
selectable = select([self.jobs_t.c.next_run_time]).\ selectable = select(self.jobs_t.c.next_run_time).\
where(self.jobs_t.c.next_run_time != null()).\ where(self.jobs_t.c.next_run_time != null()).\
order_by(self.jobs_t.c.next_run_time).limit(1) order_by(self.jobs_t.c.next_run_time).limit(1)
next_run_time = self.engine.execute(selectable).scalar() with self.engine.begin() as connection:
next_run_time = connection.execute(selectable).scalar()
return utc_timestamp_to_datetime(next_run_time) return utc_timestamp_to_datetime(next_run_time)
def get_all_jobs(self): def get_all_jobs(self):
@ -94,8 +96,9 @@ class SQLAlchemyJobStore(BaseJobStore):
'next_run_time': datetime_to_utc_timestamp(job.next_run_time), 'next_run_time': datetime_to_utc_timestamp(job.next_run_time),
'job_state': pickle.dumps(job.__getstate__(), self.pickle_protocol) 'job_state': pickle.dumps(job.__getstate__(), self.pickle_protocol)
}) })
with self.engine.begin() as connection:
try: try:
self.engine.execute(insert) connection.execute(insert)
except IntegrityError: except IntegrityError:
raise ConflictingIdError(job.id) raise ConflictingIdError(job.id)
@ -104,19 +107,22 @@ class SQLAlchemyJobStore(BaseJobStore):
'next_run_time': datetime_to_utc_timestamp(job.next_run_time), 'next_run_time': datetime_to_utc_timestamp(job.next_run_time),
'job_state': pickle.dumps(job.__getstate__(), self.pickle_protocol) 'job_state': pickle.dumps(job.__getstate__(), self.pickle_protocol)
}).where(self.jobs_t.c.id == job.id) }).where(self.jobs_t.c.id == job.id)
result = self.engine.execute(update) with self.engine.begin() as connection:
result = connection.execute(update)
if result.rowcount == 0: if result.rowcount == 0:
raise JobLookupError(job.id) raise JobLookupError(job.id)
def remove_job(self, job_id): def remove_job(self, job_id):
delete = self.jobs_t.delete().where(self.jobs_t.c.id == job_id) delete = self.jobs_t.delete().where(self.jobs_t.c.id == job_id)
result = self.engine.execute(delete) with self.engine.begin() as connection:
result = connection.execute(delete)
if result.rowcount == 0: if result.rowcount == 0:
raise JobLookupError(job_id) raise JobLookupError(job_id)
def remove_all_jobs(self): def remove_all_jobs(self):
delete = self.jobs_t.delete() delete = self.jobs_t.delete()
self.engine.execute(delete) with self.engine.begin() as connection:
connection.execute(delete)
def shutdown(self): def shutdown(self):
self.engine.dispose() self.engine.dispose()
@ -132,11 +138,12 @@ class SQLAlchemyJobStore(BaseJobStore):
def _get_jobs(self, *conditions): def _get_jobs(self, *conditions):
jobs = [] jobs = []
selectable = select([self.jobs_t.c.id, self.jobs_t.c.job_state]).\ selectable = select(self.jobs_t.c.id, self.jobs_t.c.job_state).\
order_by(self.jobs_t.c.next_run_time) order_by(self.jobs_t.c.next_run_time)
selectable = selectable.where(and_(*conditions)) if conditions else selectable selectable = selectable.where(and_(*conditions)) if conditions else selectable
failed_job_ids = set() failed_job_ids = set()
for row in self.engine.execute(selectable): with self.engine.begin() as connection:
for row in connection.execute(selectable):
try: try:
jobs.append(self._reconstitute_job(row.job_state)) jobs.append(self._reconstitute_job(row.job_state))
except BaseException: except BaseException:
@ -146,7 +153,7 @@ class SQLAlchemyJobStore(BaseJobStore):
# Remove all the jobs we failed to restore # Remove all the jobs we failed to restore
if failed_job_ids: if failed_job_ids:
delete = self.jobs_t.delete().where(self.jobs_t.c.id.in_(failed_job_ids)) delete = self.jobs_t.delete().where(self.jobs_t.c.id.in_(failed_job_ids))
self.engine.execute(delete) connection.execute(delete)
return jobs return jobs

View file

@ -1,18 +1,10 @@
from __future__ import absolute_import from __future__ import absolute_import
import asyncio
from functools import wraps, partial from functools import wraps, partial
from apscheduler.schedulers.base import BaseScheduler from apscheduler.schedulers.base import BaseScheduler
from apscheduler.util import maybe_ref from apscheduler.util import maybe_ref
try:
import asyncio
except ImportError: # pragma: nocover
try:
import trollius as asyncio
except ImportError:
raise ImportError(
'AsyncIOScheduler requires either Python 3.4 or the asyncio package installed')
def run_in_event_loop(func): def run_in_event_loop(func):
@wraps(func) @wraps(func)

View file

@ -191,12 +191,11 @@ class BaseScheduler(six.with_metaclass(ABCMeta)):
self.state = STATE_STOPPED self.state = STATE_STOPPED
# Shut down all executors # Shut down all executors
with self._executors_lock: with self._executors_lock, self._jobstores_lock:
for executor in six.itervalues(self._executors): for executor in six.itervalues(self._executors):
executor.shutdown(wait) executor.shutdown(wait)
# Shut down all job stores # Shut down all job stores
with self._jobstores_lock:
for jobstore in six.itervalues(self._jobstores): for jobstore in six.itervalues(self._jobstores):
jobstore.shutdown() jobstore.shutdown()

View file

@ -7,6 +7,9 @@ try:
except (ImportError, RuntimeError): # pragma: nocover except (ImportError, RuntimeError): # pragma: nocover
try: try:
from PyQt4.QtCore import QObject, QTimer from PyQt4.QtCore import QObject, QTimer
except ImportError:
try:
from PySide6.QtCore import QObject, QTimer # noqa
except ImportError: except ImportError:
try: try:
from PySide2.QtCore import QObject, QTimer # noqa from PySide2.QtCore import QObject, QTimer # noqa
@ -14,7 +17,7 @@ except (ImportError, RuntimeError): # pragma: nocover
try: try:
from PySide.QtCore import QObject, QTimer # noqa from PySide.QtCore import QObject, QTimer # noqa
except ImportError: except ImportError:
raise ImportError('QtScheduler requires either PyQt5, PyQt4, PySide2 ' raise ImportError('QtScheduler requires either PyQt5, PyQt4, PySide6, PySide2 '
'or PySide installed') 'or PySide installed')
@ -30,7 +33,7 @@ class QtScheduler(BaseScheduler):
def _start_timer(self, wait_seconds): def _start_timer(self, wait_seconds):
self._stop_timer() self._stop_timer()
if wait_seconds is not None: if wait_seconds is not None:
wait_time = min(wait_seconds * 1000, 2147483647) wait_time = min(int(wait_seconds * 1000), 2147483647)
self._timer = QTimer.singleShot(wait_time, self._process_jobs) self._timer = QTimer.singleShot(wait_time, self._process_jobs)
def _stop_timer(self): def _stop_timer(self):

View file

@ -6,7 +6,8 @@ import six
from apscheduler.triggers.base import BaseTrigger from apscheduler.triggers.base import BaseTrigger
from apscheduler.triggers.cron.fields import ( from apscheduler.triggers.cron.fields import (
BaseField, MonthField, WeekField, DayOfMonthField, DayOfWeekField, DEFAULT_VALUES) BaseField, MonthField, WeekField, DayOfMonthField, DayOfWeekField, DEFAULT_VALUES)
from apscheduler.util import datetime_ceil, convert_to_datetime, datetime_repr, astimezone from apscheduler.util import (
datetime_ceil, convert_to_datetime, datetime_repr, astimezone, localize, normalize)
class CronTrigger(BaseTrigger): class CronTrigger(BaseTrigger):
@ -143,7 +144,7 @@ class CronTrigger(BaseTrigger):
i += 1 i += 1
difference = datetime(**values) - dateval.replace(tzinfo=None) difference = datetime(**values) - dateval.replace(tzinfo=None)
return self.timezone.normalize(dateval + difference), fieldnum return normalize(dateval + difference), fieldnum
def _set_field_value(self, dateval, fieldnum, new_value): def _set_field_value(self, dateval, fieldnum, new_value):
values = {} values = {}
@ -156,7 +157,7 @@ class CronTrigger(BaseTrigger):
else: else:
values[field.name] = new_value values[field.name] = new_value
return self.timezone.localize(datetime(**values)) return localize(datetime(**values), self.timezone)
def get_next_fire_time(self, previous_fire_time, now): def get_next_fire_time(self, previous_fire_time, now):
if previous_fire_time: if previous_fire_time:

View file

@ -4,7 +4,9 @@ from math import ceil
from tzlocal import get_localzone from tzlocal import get_localzone
from apscheduler.triggers.base import BaseTrigger from apscheduler.triggers.base import BaseTrigger
from apscheduler.util import convert_to_datetime, timedelta_seconds, datetime_repr, astimezone from apscheduler.util import (
convert_to_datetime, normalize, timedelta_seconds, datetime_repr,
astimezone)
class IntervalTrigger(BaseTrigger): class IntervalTrigger(BaseTrigger):
@ -63,7 +65,7 @@ class IntervalTrigger(BaseTrigger):
next_fire_time = self._apply_jitter(next_fire_time, self.jitter, now) next_fire_time = self._apply_jitter(next_fire_time, self.jitter, now)
if not self.end_date or next_fire_time <= self.end_date: if not self.end_date or next_fire_time <= self.end_date:
return self.timezone.normalize(next_fire_time) return normalize(next_fire_time)
def __getstate__(self): def __getstate__(self):
return { return {

View file

@ -2,6 +2,7 @@
from __future__ import division from __future__ import division
from asyncio import iscoroutinefunction
from datetime import date, datetime, time, timedelta, tzinfo from datetime import date, datetime, time, timedelta, tzinfo
from calendar import timegm from calendar import timegm
from functools import partial from functools import partial
@ -22,19 +23,10 @@ try:
except ImportError: except ImportError:
TIMEOUT_MAX = 4294967 # Maximum value accepted by Event.wait() on Windows TIMEOUT_MAX = 4294967 # Maximum value accepted by Event.wait() on Windows
try:
from asyncio import iscoroutinefunction
except ImportError:
try:
from trollius import iscoroutinefunction
except ImportError:
def iscoroutinefunction(func):
return False
__all__ = ('asint', 'asbool', 'astimezone', 'convert_to_datetime', 'datetime_to_utc_timestamp', __all__ = ('asint', 'asbool', 'astimezone', 'convert_to_datetime', 'datetime_to_utc_timestamp',
'utc_timestamp_to_datetime', 'timedelta_seconds', 'datetime_ceil', 'get_callable_name', 'utc_timestamp_to_datetime', 'timedelta_seconds', 'datetime_ceil', 'get_callable_name',
'obj_to_ref', 'ref_to_obj', 'maybe_ref', 'repr_escape', 'check_callable_args', 'obj_to_ref', 'ref_to_obj', 'maybe_ref', 'repr_escape', 'check_callable_args',
'TIMEOUT_MAX') 'normalize', 'localize', 'TIMEOUT_MAX')
class _Undefined(object): class _Undefined(object):
@ -90,9 +82,7 @@ def astimezone(obj):
if isinstance(obj, six.string_types): if isinstance(obj, six.string_types):
return timezone(obj) return timezone(obj)
if isinstance(obj, tzinfo): if isinstance(obj, tzinfo):
if not hasattr(obj, 'localize') or not hasattr(obj, 'normalize'): if obj.tzname(None) == 'local':
raise TypeError('Only timezones from the pytz library are supported')
if obj.zone == 'local':
raise ValueError( raise ValueError(
'Unable to determine the name of the local timezone -- you must explicitly ' 'Unable to determine the name of the local timezone -- you must explicitly '
'specify the name of the local timezone. Please refrain from using timezones like ' 'specify the name of the local timezone. Please refrain from using timezones like '
@ -162,11 +152,7 @@ def convert_to_datetime(input, tz, arg_name):
if isinstance(tz, six.string_types): if isinstance(tz, six.string_types):
tz = timezone(tz) tz = timezone(tz)
try: return localize(datetime_, tz)
return tz.localize(datetime_, is_dst=None)
except AttributeError:
raise TypeError(
'Only pytz timezones are supported (need the localize() and normalize() methods)')
def datetime_to_utc_timestamp(timeval): def datetime_to_utc_timestamp(timeval):
@ -431,3 +417,14 @@ def iscoroutinefunction_partial(f):
# The asyncio version of iscoroutinefunction includes testing for @coroutine # The asyncio version of iscoroutinefunction includes testing for @coroutine
# decorations vs. the inspect version which does not. # decorations vs. the inspect version which does not.
return iscoroutinefunction(f) return iscoroutinefunction(f)
def normalize(dt):
return datetime.fromtimestamp(dt.timestamp(), dt.tzinfo)
def localize(dt, tzinfo):
if hasattr(tzinfo, 'localize'):
return tzinfo.localize(dt)
return normalize(dt.replace(tzinfo=tzinfo))

View file

@ -1 +1 @@
__version__ = "1.2.2" __version__ = "1.3.0"

View file

@ -168,9 +168,9 @@ class Arrow:
isinstance(tzinfo, dt_tzinfo) isinstance(tzinfo, dt_tzinfo)
and hasattr(tzinfo, "localize") and hasattr(tzinfo, "localize")
and hasattr(tzinfo, "zone") and hasattr(tzinfo, "zone")
and tzinfo.zone # type: ignore[attr-defined] and tzinfo.zone
): ):
tzinfo = parser.TzinfoParser.parse(tzinfo.zone) # type: ignore[attr-defined] tzinfo = parser.TzinfoParser.parse(tzinfo.zone)
elif isinstance(tzinfo, str): elif isinstance(tzinfo, str):
tzinfo = parser.TzinfoParser.parse(tzinfo) tzinfo = parser.TzinfoParser.parse(tzinfo)
@ -495,7 +495,7 @@ class Arrow:
yield current yield current
values = [getattr(current, f) for f in cls._ATTRS] values = [getattr(current, f) for f in cls._ATTRS]
current = cls(*values, tzinfo=tzinfo).shift( # type: ignore current = cls(*values, tzinfo=tzinfo).shift( # type: ignore[misc]
**{frame_relative: relative_steps} **{frame_relative: relative_steps}
) )
@ -578,7 +578,7 @@ class Arrow:
for _ in range(3 - len(values)): for _ in range(3 - len(values)):
values.append(1) values.append(1)
floor = self.__class__(*values, tzinfo=self.tzinfo) # type: ignore floor = self.__class__(*values, tzinfo=self.tzinfo) # type: ignore[misc]
if frame_absolute == "week": if frame_absolute == "week":
# if week_start is greater than self.isoweekday() go back one week by setting delta = 7 # if week_start is greater than self.isoweekday() go back one week by setting delta = 7
@ -792,7 +792,6 @@ class Arrow:
return self._datetime.isoformat() return self._datetime.isoformat()
def __format__(self, formatstr: str) -> str: def __format__(self, formatstr: str) -> str:
if len(formatstr) > 0: if len(formatstr) > 0:
return self.format(formatstr) return self.format(formatstr)
@ -804,7 +803,6 @@ class Arrow:
# attributes and properties # attributes and properties
def __getattr__(self, name: str) -> int: def __getattr__(self, name: str) -> int:
if name == "week": if name == "week":
return self.isocalendar()[1] return self.isocalendar()[1]
@ -965,7 +963,6 @@ class Arrow:
absolute_kwargs = {} absolute_kwargs = {}
for key, value in kwargs.items(): for key, value in kwargs.items():
if key in self._ATTRS: if key in self._ATTRS:
absolute_kwargs[key] = value absolute_kwargs[key] = value
elif key in ["week", "quarter"]: elif key in ["week", "quarter"]:
@ -1022,7 +1019,6 @@ class Arrow:
additional_attrs = ["weeks", "quarters", "weekday"] additional_attrs = ["weeks", "quarters", "weekday"]
for key, value in kwargs.items(): for key, value in kwargs.items():
if key in self._ATTRS_PLURAL or key in additional_attrs: if key in self._ATTRS_PLURAL or key in additional_attrs:
relative_kwargs[key] = value relative_kwargs[key] = value
else: else:
@ -1259,11 +1255,10 @@ class Arrow:
) )
if trunc(abs(delta)) != 1: if trunc(abs(delta)) != 1:
granularity += "s" # type: ignore granularity += "s" # type: ignore[assignment]
return locale.describe(granularity, delta, only_distance=only_distance) return locale.describe(granularity, delta, only_distance=only_distance)
else: else:
if not granularity: if not granularity:
raise ValueError( raise ValueError(
"Empty granularity list provided. " "Empty granularity list provided. "
@ -1314,7 +1309,7 @@ class Arrow:
def dehumanize(self, input_string: str, locale: str = "en_us") -> "Arrow": def dehumanize(self, input_string: str, locale: str = "en_us") -> "Arrow":
"""Returns a new :class:`Arrow <arrow.arrow.Arrow>` object, that represents """Returns a new :class:`Arrow <arrow.arrow.Arrow>` object, that represents
the time difference relative to the attrbiutes of the the time difference relative to the attributes of the
:class:`Arrow <arrow.arrow.Arrow>` object. :class:`Arrow <arrow.arrow.Arrow>` object.
:param timestring: a ``str`` representing a humanized relative time. :param timestring: a ``str`` representing a humanized relative time.
@ -1367,7 +1362,6 @@ class Arrow:
# Search input string for each time unit within locale # Search input string for each time unit within locale
for unit, unit_object in locale_obj.timeframes.items(): for unit, unit_object in locale_obj.timeframes.items():
# Need to check the type of unit_object to create the correct dictionary # Need to check the type of unit_object to create the correct dictionary
if isinstance(unit_object, Mapping): if isinstance(unit_object, Mapping):
strings_to_search = unit_object strings_to_search = unit_object
@ -1378,13 +1372,12 @@ class Arrow:
# Needs to cycle all through strings as some locales have strings that # Needs to cycle all through strings as some locales have strings that
# could overlap in a regex match, since input validation isn't being performed. # could overlap in a regex match, since input validation isn't being performed.
for time_delta, time_string in strings_to_search.items(): for time_delta, time_string in strings_to_search.items():
# Replace {0} with regex \d representing digits # Replace {0} with regex \d representing digits
search_string = str(time_string) search_string = str(time_string)
search_string = search_string.format(r"\d+") search_string = search_string.format(r"\d+")
# Create search pattern and find within string # Create search pattern and find within string
pattern = re.compile(fr"(^|\b|\d){search_string}") pattern = re.compile(rf"(^|\b|\d){search_string}")
match = pattern.search(input_string) match = pattern.search(input_string)
# If there is no match continue to next iteration # If there is no match continue to next iteration
@ -1419,19 +1412,19 @@ class Arrow:
# Assert error if string does not modify any units # Assert error if string does not modify any units
if not any([True for k, v in unit_visited.items() if v]): if not any([True for k, v in unit_visited.items() if v]):
raise ValueError( raise ValueError(
"Input string not valid. Note: Some locales do not support the week granulairty in Arrow. " "Input string not valid. Note: Some locales do not support the week granularity in Arrow. "
"If you are attempting to use the week granularity on an unsupported locale, this could be the cause of this error." "If you are attempting to use the week granularity on an unsupported locale, this could be the cause of this error."
) )
# Sign logic # Sign logic
future_string = locale_obj.future future_string = locale_obj.future
future_string = future_string.format(".*") future_string = future_string.format(".*")
future_pattern = re.compile(fr"^{future_string}$") future_pattern = re.compile(rf"^{future_string}$")
future_pattern_match = future_pattern.findall(input_string) future_pattern_match = future_pattern.findall(input_string)
past_string = locale_obj.past past_string = locale_obj.past
past_string = past_string.format(".*") past_string = past_string.format(".*")
past_pattern = re.compile(fr"^{past_string}$") past_pattern = re.compile(rf"^{past_string}$")
past_pattern_match = past_pattern.findall(input_string) past_pattern_match = past_pattern.findall(input_string)
# If a string contains the now unit, there will be no relative units, hence the need to check if the now unit # If a string contains the now unit, there will be no relative units, hence the need to check if the now unit
@ -1718,7 +1711,6 @@ class Arrow:
# math # math
def __add__(self, other: Any) -> "Arrow": def __add__(self, other: Any) -> "Arrow":
if isinstance(other, (timedelta, relativedelta)): if isinstance(other, (timedelta, relativedelta)):
return self.fromdatetime(self._datetime + other, self._datetime.tzinfo) return self.fromdatetime(self._datetime + other, self._datetime.tzinfo)
@ -1736,7 +1728,6 @@ class Arrow:
pass # pragma: no cover pass # pragma: no cover
def __sub__(self, other: Any) -> Union[timedelta, "Arrow"]: def __sub__(self, other: Any) -> Union[timedelta, "Arrow"]:
if isinstance(other, (timedelta, relativedelta)): if isinstance(other, (timedelta, relativedelta)):
return self.fromdatetime(self._datetime - other, self._datetime.tzinfo) return self.fromdatetime(self._datetime - other, self._datetime.tzinfo)
@ -1749,7 +1740,6 @@ class Arrow:
return NotImplemented return NotImplemented
def __rsub__(self, other: Any) -> timedelta: def __rsub__(self, other: Any) -> timedelta:
if isinstance(other, dt_datetime): if isinstance(other, dt_datetime):
return other - self._datetime return other - self._datetime
@ -1758,42 +1748,36 @@ class Arrow:
# comparisons # comparisons
def __eq__(self, other: Any) -> bool: def __eq__(self, other: Any) -> bool:
if not isinstance(other, (Arrow, dt_datetime)): if not isinstance(other, (Arrow, dt_datetime)):
return False return False
return self._datetime == self._get_datetime(other) return self._datetime == self._get_datetime(other)
def __ne__(self, other: Any) -> bool: def __ne__(self, other: Any) -> bool:
if not isinstance(other, (Arrow, dt_datetime)): if not isinstance(other, (Arrow, dt_datetime)):
return True return True
return not self.__eq__(other) return not self.__eq__(other)
def __gt__(self, other: Any) -> bool: def __gt__(self, other: Any) -> bool:
if not isinstance(other, (Arrow, dt_datetime)): if not isinstance(other, (Arrow, dt_datetime)):
return NotImplemented return NotImplemented
return self._datetime > self._get_datetime(other) return self._datetime > self._get_datetime(other)
def __ge__(self, other: Any) -> bool: def __ge__(self, other: Any) -> bool:
if not isinstance(other, (Arrow, dt_datetime)): if not isinstance(other, (Arrow, dt_datetime)):
return NotImplemented return NotImplemented
return self._datetime >= self._get_datetime(other) return self._datetime >= self._get_datetime(other)
def __lt__(self, other: Any) -> bool: def __lt__(self, other: Any) -> bool:
if not isinstance(other, (Arrow, dt_datetime)): if not isinstance(other, (Arrow, dt_datetime)):
return NotImplemented return NotImplemented
return self._datetime < self._get_datetime(other) return self._datetime < self._get_datetime(other)
def __le__(self, other: Any) -> bool: def __le__(self, other: Any) -> bool:
if not isinstance(other, (Arrow, dt_datetime)): if not isinstance(other, (Arrow, dt_datetime)):
return NotImplemented return NotImplemented
@ -1865,7 +1849,6 @@ class Arrow:
def _get_iteration_params(cls, end: Any, limit: Optional[int]) -> Tuple[Any, int]: def _get_iteration_params(cls, end: Any, limit: Optional[int]) -> Tuple[Any, int]:
"""Sets default end and limit values for range method.""" """Sets default end and limit values for range method."""
if end is None: if end is None:
if limit is None: if limit is None:
raise ValueError("One of 'end' or 'limit' is required.") raise ValueError("One of 'end' or 'limit' is required.")

View file

@ -21,7 +21,7 @@ except (OverflowError, ValueError, OSError): # pragma: no cover
# Must get max value of ctime on Windows based on architecture (x32 vs x64) # Must get max value of ctime on Windows based on architecture (x32 vs x64)
# https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/ctime-ctime32-ctime64-wctime-wctime32-wctime64 # https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/ctime-ctime32-ctime64-wctime-wctime32-wctime64
# Note: this may occur on both 32-bit Linux systems (issue #930) along with Windows systems # Note: this may occur on both 32-bit Linux systems (issue #930) along with Windows systems
is_64bits = sys.maxsize > 2 ** 32 is_64bits = sys.maxsize > 2**32
_MAX_TIMESTAMP = ( _MAX_TIMESTAMP = (
datetime(3000, 1, 1, 23, 59, 59, 999999).timestamp() datetime(3000, 1, 1, 23, 59, 59, 999999).timestamp()
if is_64bits if is_64bits
@ -162,6 +162,16 @@ DEHUMANIZE_LOCALES = {
"ta-lk", "ta-lk",
"ur", "ur",
"ur-pk", "ur-pk",
"ka",
"ka-ge",
"kk", "kk",
"kk-kz", "kk-kz",
# "lo",
# "lo-la",
"am",
"am-et",
"hy-am",
"hy",
"uz",
"uz-uz",
} }

View file

@ -267,11 +267,9 @@ class ArrowFactory:
raise TypeError(f"Cannot parse single argument of type {type(arg)!r}.") raise TypeError(f"Cannot parse single argument of type {type(arg)!r}.")
elif arg_count == 2: elif arg_count == 2:
arg_1, arg_2 = args[0], args[1] arg_1, arg_2 = args[0], args[1]
if isinstance(arg_1, datetime): if isinstance(arg_1, datetime):
# (datetime, tzinfo/str) -> fromdatetime @ tzinfo # (datetime, tzinfo/str) -> fromdatetime @ tzinfo
if isinstance(arg_2, (dt_tzinfo, str)): if isinstance(arg_2, (dt_tzinfo, str)):
return self.type.fromdatetime(arg_1, tzinfo=arg_2) return self.type.fromdatetime(arg_1, tzinfo=arg_2)
@ -281,7 +279,6 @@ class ArrowFactory:
) )
elif isinstance(arg_1, date): elif isinstance(arg_1, date):
# (date, tzinfo/str) -> fromdate @ tzinfo # (date, tzinfo/str) -> fromdate @ tzinfo
if isinstance(arg_2, (dt_tzinfo, str)): if isinstance(arg_2, (dt_tzinfo, str)):
return self.type.fromdate(arg_1, tzinfo=arg_2) return self.type.fromdate(arg_1, tzinfo=arg_2)

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